diff --git a/CMakeHeaderFileList.cmake b/CMakeHeaderFileList.cmake index 25ecc09d48ac17cb43f90da87c93d381da700a62..68a3429306a823b5770ed299a636199be22e97ca 100644 --- a/CMakeHeaderFileList.cmake +++ b/CMakeHeaderFileList.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeHeaderFileList.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeHeaderFileList.cmake 5255 2015-02-03 14:52:43Z strinh $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -66,12 +66,18 @@ SET (HEADER_DATA_STRUCTURE data-structure/vpList.h ) +set(HEADER_DETECTION + detection/vpDetectorBase.h + detection/barcode/vpDetectorDataMatrixCode.h + detection/barcode/vpDetectorQRCode.h + detection/face/vpDetectorFace.h +) + SET (HEADER_EXCEPTION exceptions/vpException.h ) SET (HEADER_DEVICE_FRAMEGRABBER - device/framegrabber/1394/vp1394Grabber.h device/framegrabber/1394/vp1394TwoGrabber.h device/framegrabber/1394/vp1394CMUGrabber.h device/framegrabber/disk/vpDiskGrabber.h @@ -103,6 +109,8 @@ SET (HEADER_KEY_POINT key-point/vpKeyPointSurf.h key-point/vpPlanarObjectDetector.h key-point/vpFernClassifier.h + key-point/vpKeyPoint.h + key-point/vpXmlConfigParserKeyPoint.h ) SET (HEADER_DEVICE_KINECT @@ -149,6 +157,7 @@ SET (HEADER_MATH math/transformation/vpRxyzVector.h math/transformation/vpRzyxVector.h math/transformation/vpRzyzVector.h + math/transformation/vpXmlParserHomogeneousMatrix.h math/transformation/vpThetaUVector.h math/transformation/vpTranslationVector.h math/transformation/vpVelocityTwistMatrix.h @@ -166,7 +175,6 @@ SET (HEADER_ROBOT robot/real-robot/biclops/vpBiclops.h robot/real-robot/biclops/vpRobotBiclopsController.h robot/real-robot/biclops/vpRobotBiclops.h - robot/real-robot/cycab/vpRobotCycab.h robot/real-robot/pioneer/vpUnicycle.h robot/real-robot/pioneer/vpPioneer.h robot/real-robot/pioneer/vpPioneerPan.h @@ -189,7 +197,6 @@ SET (HEADER_ROBOT ) SET (HEADER_SERVO - servo/vpAdaptativeGain.h servo/vpAdaptiveGain.h servo/vpServoData.h servo/vpServoDisplay.h @@ -222,6 +229,7 @@ SET (HEADER_SIMULATOR ) SET (HEADER_TOOLS + tools/convert/vpConvert.h tools/geometry/vpPlane.h tools/geometry/vpRect.h tools/geometry/vpTriangle.h @@ -265,19 +273,22 @@ SET (HEADER_TRACKING tracking/moving-edges/vpMeNurbs.h tracking/mbt/vpMbTracker.h - tracking/mbt/vpMbHiddenFaces.h + tracking/mbt/vpMbHiddenFaces.h + tracking/mbt/vpMbXmlParser.h + tracking/mbt/vpMbtPolygon.h + tracking/mbt/edge/vpMbtDistanceCircle.h + tracking/mbt/edge/vpMbtDistanceCylinder.h tracking/mbt/edge/vpMbtDistanceLine.h - tracking/mbt/edge/vpMbtPolygon.h + tracking/mbt/edge/vpMbtMeEllipse.h tracking/mbt/edge/vpMbtMeLine.h tracking/mbt/edge/vpMbEdgeTracker.h tracking/mbt/edge/vpMbtXmlParser.h - tracking/mbt/edge/vpMbtDistanceCylinder.h tracking/mbt/hybrid/vpMbEdgeKltTracker.h - tracking/mbt/klt/vpMbtKltPolygon.h + tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.h + tracking/mbt/klt/vpMbtDistanceKltPoints.h tracking/mbt/klt/vpMbKltTracker.h tracking/mbt/klt/vpMbtKltXmlParser.h - tracking/moments/vpMomentObject.h tracking/moments/vpMomentAlpha.h tracking/moments/vpMomentBasic.h tracking/moments/vpMomentCentered.h @@ -290,6 +301,26 @@ SET (HEADER_TRACKING tracking/moments/vpMomentObject.h tracking/moments/vpMomentAreaNormalized.h tracking/moments/vpMomentArea.h + + tracking/template-tracker/vpTemplateTracker.h + tracking/template-tracker/ssd/vpTemplateTrackerSSD.h + tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.h + tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.h + tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.h + tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.h + tracking/template-tracker/zncc/vpTemplateTrackerZNCC.h + tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.h + tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.h + tracking/template-tracker/tools/vpTemplateTrackerBSpline.h + tracking/template-tracker/tools/vpTemplateTrackerHeader.h + tracking/template-tracker/tools/vpTemplateTrackerZone.h + tracking/template-tracker/tools/vpTemplateTrackerTriangle.h + tracking/template-tracker/warp/vpTemplateTrackerWarp.h + tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.h + tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.h + tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.h + tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.h + tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.h ) SET (HEADER_VIDEO @@ -322,26 +353,24 @@ SET (HEADER_VISUAL_FEATURE visual-feature/vpFeatureEllipse.h visual-feature/vpFeatureException.h visual-feature/vpFeatureLine.h + visual-feature/vpFeatureLuminance.h + visual-feature/vpFeatureMoment.h + visual-feature/vpFeatureMomentAlpha.h + visual-feature/vpFeatureMomentArea.h + visual-feature/vpFeatureMomentAreaNormalized.h + visual-feature/vpFeatureMomentBasic.h + visual-feature/vpFeatureMomentCentered.h + visual-feature/vpFeatureMomentCInvariant.h + visual-feature/vpFeatureMomentCommon.h + visual-feature/vpFeatureMomentDatabase.h + visual-feature/vpFeatureMomentGravityCenter.h + visual-feature/vpFeatureMomentGravityCenterNormalized.h visual-feature/vpFeaturePoint3D.h visual-feature/vpFeaturePoint.h visual-feature/vpFeaturePointPolar.h visual-feature/vpFeatureThetaU.h visual-feature/vpFeatureTranslation.h visual-feature/vpFeatureVanishingPoint.h - visual-feature/vpFeatureMoment.h - visual-feature/vpFeatureMomentDatabase.h - visual-feature/vpFeatureMomentCommon.h - visual-feature/vpFeatureMomentAlpha.h - visual-feature/vpFeatureMomentGravityCenter.h - visual-feature/vpFeatureMomentBasic.h - visual-feature/vpFeatureMomentGravityCenterNormalized.h - visual-feature/vpFeatureMomentCentered.h - visual-feature/vpFeatureMomentCInvariant.h - visual-feature/vpFeatureMomentCommon.h - visual-feature/vpFeatureMomentAreaNormalized.h - visual-feature/vpFeatureMomentArea.h - - visual-feature/vpFeatureLuminance.h visual-feature/vpFeatureSegment.h visual-feature/vpGenericFeature.h ) @@ -357,6 +386,7 @@ SET (HEADER_ALL ${HEADER_CAMERA} ${HEADER_COMPUTER_VISION} ${HEADER_DATA_STRUCTURE} + ${HEADER_DETECTION} ${HEADER_DEVICE_DISPLAY} ${HEADER_DEVICE_FRAMEGRABBER} ${HEADER_DEVICE_KINECT} diff --git a/CMakeLists.txt b/CMakeLists.txt index f83aaf5ae2db903a698addb1a61485f2b424b404..d10e231c1316113f4ef4f24bcdd847c4cf2313de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4340 2013-07-23 20:11:12Z fspindle $ +# $Id: CMakeLists.txt 5287 2015-02-09 15:29:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,14 +38,19 @@ # ############################################################################# -PROJECT(VISP C CXX) +# Need to be befor project(VISP) to work +if(WIN32) + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory") +endif() -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +project(VISP C CXX) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) #----------------------------------------------------------------------------- # VISP version number. An even minor number corresponds to releases. SET(VISP_VERSION_MAJOR "2") -SET(VISP_VERSION_MINOR "8") +SET(VISP_VERSION_MINOR "10") SET(VISP_VERSION_PATCH "0") SET(VISP_VERSION "${VISP_VERSION_MAJOR}.${VISP_VERSION_MINOR}.${VISP_VERSION_PATCH}") # Package revision number @@ -54,7 +59,7 @@ SET(VISP_REVISION "1") # where are user-specific cmake modules set(VISP_CMAKE_MODULE_PATH ${VISP_SOURCE_DIR}/CMakeModules) -find_file(GNU_INSTALL_DIRS_FROM_CMAKE ${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) +find_file(GNU_INSTALL_DIRS_FROM_CMAKE NAMES GNUInstallDirs.cmake PATHS ${CMAKE_ROOT}/Modules) mark_as_advanced(GNU_INSTALL_DIRS_FROM_CMAKE) if(GNU_INSTALL_DIRS_FROM_CMAKE) include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) @@ -84,8 +89,12 @@ SET(VISP_EXTERN_LIBRARIES "") SET(VISP_DEFS "") if(WIN32) - # Postfix of .lib and .dll - set(CMAKE_DEBUG_POSTFIX "d") + # Postfix of .lib and .dll + set(VISP_DEBUG_POSTFIX "d") + set(VISP_DLLVERSION "${VISP_VERSION_MAJOR}${VISP_VERSION_MINOR}${VISP_VERSION_PATCH}") +else() + set(VISP_DEBUG_POSTFIX "") + set(VISP_DLLVERSION "") endif() # Get the OS @@ -133,15 +142,31 @@ endif() #-------------------------------------------------------------------- # Choose static or shared libraries. -OPTION(BUILD_SHARED_LIBS "Build ViSP shared libraries (.dll/.so) instead of static ones (.lib/.a)." OFF) +OPTION(BUILD_SHARED_LIBS "Build ViSP shared libraries (.dll/.so) instead of static ones (.lib/.a)." ON) # Build examples as an option. OPTION(BUILD_EXAMPLES "Build ViSP examples." ON) +# Build examples as an option. +OPTION(BUILD_TESTS "Build ViSP tests." ON) # Build demos as an option. OPTION(BUILD_DEMOS "Build ViSP demos." ON) # Build demos as an option. -OPTION(BUILD_TUTORIAL "Build ViSP tutorial." ON) +OPTION(BUILD_TUTORIALS "Build ViSP tutorials." ON) # Build deprecated functions as an option. OPTION(BUILD_DEPRECATED_FUNCTIONS "Build deprecated functionalities." ON) +# Debug and trace cflags +option(ACTIVATE_DEBUG_TRACE "Enable debug and trace printings" ON) + +if(ACTIVATE_DEBUG_TRACE) + list(APPEND VISP_DEFS "-DVP_TRACE") + list(APPEND VISP_DEFS "-DVP_DEBUG") +else() + string(REPLACE "-DVP_TRACE" "" " " VISP_DEFS "${VISP_DEFS}") + string(REPLACE "-DVP_DEBUG" "" " " VISP_DEFS "${VISP_DEFS}") +endif() + +if(MSVC) + option(BUILD_WITH_STATIC_CRT "Enables use of staticaly linked CRT for staticaly linked ViSP" ON) +endif() # Note that it is better to set MOMENTS_COMBINE_MATRICES to OFF OPTION(MOMENTS_COMBINE_MATRICES "Use linear combination of matrices instead of linear combination of moments to compute interaction matrices." OFF) @@ -164,17 +189,12 @@ ENDIF() IF (UNIX) - FIND_PACKAGE(DC1394) - IF(DC1394_2_FOUND) - OPTION(USE_DC1394_2 "Compile ViSP with the libdc1394.2 library" ON) - ELSE(DC1394_2_FOUND) - SET(USE_DC1394_2 OFF) - ENDIF(DC1394_2_FOUND) - IF(DC1394_1_FOUND) - OPTION(USE_DC1394_1 "Compile ViSP with the libdc1394.1 library" ON) - ELSE(DC1394_1_FOUND) - SET(USE_DC1394_1 OFF) - ENDIF(DC1394_1_FOUND) + find_package(DC1394) + if(DC1394_FOUND) + option(USE_DC1394 "Compile ViSP with the libdc1394.2 library" ON) + else() + set(USE_DC1394 OFF) + endif() FIND_PACKAGE(V4L2) IF(V4L2_FOUND) @@ -215,13 +235,6 @@ IF (UNIX) SET(USE_PTU46 OFF) ENDIF(PTU46_FOUND) - FIND_PACKAGE(CycabTk) - IF(CycabTk_FOUND) - OPTION(USE_CYCAB "Compile ViSP for Cycab car-like mobile robot at Irisa" OFF) - ELSE(CycabTk_FOUND) - SET(USE_CYCAB OFF) - ENDIF(CycabTk_FOUND) - ENDIF(UNIX) IF(WIN32) @@ -278,16 +291,15 @@ ELSE(LAPACK_FOUND) SET(USE_LAPACK OFF) ENDIF(LAPACK_FOUND) -IF(NOT USE_LAPACK) - FIND_PACKAGE(GSL) - IF(GSL_FOUND) - OPTION(USE_GSL "Compile ViSP with the GSL library" ON) - ELSE(GSL_FOUND) - SET(USE_GSL OFF) - ENDIF(GSL_FOUND) -ENDIF() +#IF(NOT USE_LAPACK) # line removed since the template tracker needs gsl + find_package(GSL) + if(GSL_FOUND) + option(USE_GSL "Compile ViSP with the GSL library" ON) + else() + set(USE_GSL OFF) + endif() +#ENDIF() -INCLUDE (${CMAKE_ROOT}/Modules/FindOpenGL.cmake) FIND_PACKAGE(OpenGL) FIND_PACKAGE(Coin3D) if (NOT COIN3D_FOUND) @@ -467,12 +479,16 @@ else(PNG_FOUND) endif() endif() -FIND_PACKAGE(FFMPEG) -IF(FFMPEG_FOUND) - OPTION(USE_FFMPEG "Compile ViSP with the ffmpeg library" ON) -ELSE(FFMPEG_FOUND) - SET(USE_FFMPEG OFF) -ENDIF(FFMPEG_FOUND) +find_package(FFMPEG) +if(FFMPEG_FOUND) + if(USE_OPENCV) + option(USE_FFMPEG "Compile ViSP with the ffmpeg library" OFF) + else() + option(USE_FFMPEG "Compile ViSP with the ffmpeg library" ON) + endif() +else() + set(USE_FFMPEG OFF) +endif() # To control Pioneer mobile robots, under UNIX we need Aria, pthread, rt and dl 3rd party libraries find_package(ARIA) @@ -495,6 +511,19 @@ else() SET(USE_DL OFF) endif() +find_package(ZBAR) +if(ZBAR_FOUND) + OPTION(USE_ZBAR "Compile ViSP with zbar library" ON) +else() + SET(USE_ZBAR OFF) +endif() + +find_package(DMTX) +if(DMTX_FOUND) + OPTION(USE_DMTX "Compile ViSP with zbar library" ON) +else() + SET(USE_DMTX OFF) +endif() # Set other options to default value #SET(USE_X11 ON) # For Linux/OSX display @@ -509,8 +538,7 @@ endif() #SET(USE_GTK2 ON) # For Linux/OSX/Windows display with gtk-2.x #SET(USE_DIRECT3D ON) # For Windows display #SET(USE_GDI ON) # For Windows display -#SET(USE_DC1394_1 ON) # For firewire grabber under Linux and OSX ? -#SET(USE_DC1394_2 ON) # For firewire grabber under Linux and OSX ? +#SET(USE_DC1394 ON) # For firewire grabber under Linux and OSX ? #SET(USE_V4L2 ON) # For Video 4 Linux 2 grabber under Linux #SET(USE_DIRECTSHOW ON) # For Windows direct show grabber #SET(USE_LIBJPEG ON) # For reading jpeg files @@ -523,12 +551,64 @@ IF(BUILD_SHARED_LIBS) SET(VISP_BUILD_SHARED_LIBS TRUE) # for header vpConfig.h ENDIF(BUILD_SHARED_LIBS) +#---------------------------------------------------------------------- +# Try to find doxygen for documentation generation +# Use "make visp_doc" target to generate the documentation +#---------------------------------------------------------------------- +find_package(Doxygen) +if(DOXYGEN_FOUND) + set(VISP_HAVE_DOXYGEN "yes") # for header vpConfig.h + set(VISP_HAVE_DOXYGEN_FOUND "yes") # for ViSP-third-party.txt + if(DOXYGEN_DOT_EXECUTABLE) + set(VISP_HAVE_DOT "yes") # for header vpConfig.h + set(VISP_HAVE_DOT_FOUND "yes") # for ViSP-third-party.txt + else() + set(VISP_HAVE_DOT "no") # for header vpConfig.h + set(VISP_HAVE_DOT_FOUND "no") # for ViSP-third-party.txt + endif() + ## we need latex for doxygen because of the formulas + find_package(LATEX) + if(NOT LATEX_COMPILER) + message(STATUS "latex command LATEX_COMPILER not found but usually required. You will probably get warnings and user interaction on doxy run.") + endif() + if(NOT MAKEINDEX_COMPILER) + message(STATUS "makeindex command MAKEINDEX_COMPILER not found but usually required.") + endif() + if(NOT DVIPS_CONVERTER) + message(STATUS "dvips command DVIPS_CONVERTER not found but usually required.") + endif() + + configure_file(${VISP_SOURCE_DIR}/doc/config-doxygen.in + ${VISP_DOC_DIR}/config-doxygen + @ONLY ) + + configure_file(${VISP_SOURCE_DIR}/doc/mainpage.doc.in + ${VISP_DOC_DIR}/mainpage.doc + @ONLY ) +else() + set(VISP_HAVE_DOXYGEN "no") # for header vpConfig.h + set(VISP_HAVE_DOXYGEN_FOUND "no") # for ViSP-third-party.txt + set(VISP_HAVE_DOT_FOUND "no") # for ViSP-third-party.txt +endif() + +# ---------------------------------------------------------------------------- +# Solution folders: +# ---------------------------------------------------------------------------- +if(MSVC_IDE OR CMAKE_GENERATOR MATCHES Xcode) + option(ENABLE_SOLUTION_FOLDERS "Solution folder in Visual Studio or in other IDEs" ON) +endif() + +if(ENABLE_SOLUTION_FOLDERS) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets") +endif() + +# Extra ViSP targets: uninstall, etc. +include(${VISP_CMAKE_MODULE_PATH}/VISPExtraTargets.cmake) + INCLUDE(CheckLibraryExists) -#-------------------------------------------------------------------- -# yarp lib -#-------------------------------------------------------------------- #-------------------------------------------------------------------- # yarp lib #-------------------------------------------------------------------- @@ -568,10 +648,12 @@ IF(USE_YARP) ENDFOREACH() ENDFOREACH() - # Remove Yarp libraries since they were added previously with full absolute library path and name - FOREACH(lib ${YARP_LIBRARIES}) - LIST(REMOVE_ITEM YARP_LINK_LIBS ${lib}) - ENDFOREACH() + # Remove Yarp libraries since they were added previously with full absolute library path and name + if(YARP_LINK_LIBS) + foreach(lib ${YARP_LIBRARIES}) + list(REMOVE_ITEM YARP_LINK_LIBS ${lib}) + endforeach() + endif() # Add 3rd party libraries requested by Yarp LIST(APPEND VISP_EXTERN_LIBRARIES ${YARP_LINK_LIBS}) @@ -594,8 +676,9 @@ ENDIF(NAS_FOUND) # Platform dependent #---------------------------------------------------------------------- IF(WIN32 AND NOT CYGWIN) - LIST(APPEND VISP_DEFS "-DWIN32") - LIST(APPEND VISP_DEFS "-DGX") # To support exceptions + if(BUILD_DEPRECATED_FUNCTIONS) + list(APPEND VISP_DEFS "-DWIN32") # only for compat with previous versions + endif() CHECK_LIBRARY_EXISTS("winmm.lib" getch "" HAVE_LIBWINMM) # for timeGetTime() IF(HAVE_LIBWINMM) #MESSAGE("have winmm.lib") @@ -608,34 +691,36 @@ IF(WIN32 AND NOT CYGWIN) if(MINGW) CHECK_LIBRARY_EXISTS("ws2_32.a" getch "" HAVE_LIBWS2_32) # for inet_ntoa() and socket functionalities if(HAVE_LIBWS2_32) - #message("have ws2_32.a") list(APPEND VISP_EXTERN_LIBRARIES "ws2_32.a") + else() + find_library(WS2_32_LIBRARY "libws2_32.a" + "$ENV{MINGW_DIR}/lib" + "$ENV{MINGW_DIR}/mingw/lib" + C:/mingw/mingw/lib) + mark_as_advanced(WS2_32_LIBRARY) + if(WS2_32_LIBRARY) + list(APPEND VISP_EXTERN_LIBRARIES "${WS2_32_LIBRARY}") + endif() endif() else() # pure WIN32 CHECK_LIBRARY_EXISTS("ws2_32.lib" getch "" HAVE_LIBWS2_32) # for inet_ntoa() and socket functionalities if(HAVE_LIBWS2_32) #message("have ws2_32.lib") list(APPEND VISP_EXTERN_LIBRARIES "ws2_32.lib") - endif() + endif() endif() ENDIF(WIN32 AND NOT CYGWIN) - -IF(UNIX) - LIST(APPEND VISP_DEFS "-DUNIX") - IF(APPLE) - LIST(APPEND VISP_DEFS "-DAPPLE") - ENDIF(APPLE) -ENDIF(UNIX) - -IF(UNIX) - INCLUDE(TestBigEndian) - TEST_BIG_ENDIAN(BIGENDIAN) - #SET(BIGENDIAN FALSE) -ENDIF() - -IF(BIGENDIAN) - LIST(APPEND VISP_DEFS "-DBIGENDIAN") -ENDIF(BIGENDIAN) +if(UNIX) + if(BUILD_DEPRECATED_FUNCTIONS) + list(APPEND VISP_DEFS "-DUNIX") + if(APPLE) + list(APPEND VISP_DEFS "-DAPPLE") + endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + list(APPEND VISP_DEFS "-DSOLARIS") + endif() + endif() +endif() #-------------------------------------------------------------------- # OpenMP @@ -685,15 +770,32 @@ ENDIF(USE_LAPACK) #-------------------------------------------------------------------- # X11 lib #-------------------------------------------------------------------- -SET(VISP_HAVE_X11_FOUND "no") # for ViSP-third-party.txt -IF(USE_X11) - MESSAGE(STATUS "X11 found") - SET(VISP_HAVE_X11 TRUE) # for header vpConfig.h - SET(VISP_HAVE_X11_FOUND "yes") # for ViSP-third-party.txt - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${X11_INCLUDE_DIR}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${X11_LIBRARIES} -lm) +set(VISP_HAVE_X11_FOUND "no") # for ViSP-third-party.txt +if(USE_X11) + message(STATUS "X11 found") + set(VISP_HAVE_X11 TRUE) # for header vpConfig.h + set(VISP_HAVE_X11_FOUND "yes") # for ViSP-third-party.txt + # try to found -lm requested on some platforms to link with X11 + list(APPEND VISP_EXTERN_INCLUDE_DIRS ${X11_INCLUDE_DIR}) + list(APPEND VISP_EXTERN_LIBRARIES ${X11_LIBRARIES}) + find_library(M_LIBRARY NAMES m) + mark_as_advanced(M_LIBRARY) + if(M_LIBRARY) + list(APPEND VISP_EXTERN_LIBRARIES ${M_LIBRARY}) + endif() #MESSAGE("X11: ${X11_LIBRARIES}") -ENDIF(USE_X11) +else() + # try to found -lsocket -lnsl requested for vpNetwork and vpSickLDMRS + find_library(SOCKET_LIBRARY NAMES socket) + find_library(NSL_LIBRARY NAMES nsl) + if (SOCKET_LIBRARY) + list(APPEND VISP_EXTERN_LIBRARIES ${SOCKET_LIBRARY}) + endif() + if (NSL_LIBRARY) + list(APPEND VISP_EXTERN_LIBRARIES ${NSL_LIBRARY}) + endif() + mark_as_advanced(SOCKET_LIBRARY NSL_LIBRARY) +endif() #-------------------------------------------------------------------- @@ -726,13 +828,19 @@ IF(USE_OGRE) # search names with "lib". This is the workaround. set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "") endif () - set(Boost_ADDITIONAL_VERSIONS "1.53.0" "1.52.0" "1.51.0" "1.50.0" "1.49.0" "1.48.0" "1.47.0" "1.46.0" "1.45.0" "1.44.0" "1.44" "1.44.0" "1.42" "1.42.0" "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" ) + #set(Boost_ADDITIONAL_VERSIONS "1.53.0" "1.52.0" "1.51.0" "1.50.0" "1.49.0" "1.48.0" "1.47.0" "1.46.0" "1.45.0" "1.44.0" "1.44" "1.44.0" "1.42" "1.42.0" "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" ) # Components that need to be linked with. Since Ogre 1.9 we need not only boost_thread and boost_date_time, but also boost_system set(OGRE_BOOST_COMPONENTS thread system date_time) + if(WIN32) + list(APPEND OGRE_BOOST_COMPONENTS chrono) + endif() find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET) - mark_as_advanced(Boost_LIB_DIAGNOSTIC_DEFINITIONS Boost_DIR) + mark_as_advanced(Boost_LIB_DIAGNOSTIC_DEFINITIONS Boost_DIR BOOST_THREAD_LIBRARY) if (NOT Boost_FOUND) set(OGRE_BOOST_COMPONENTS thread date_time) + if(WIN32) + list(APPEND OGRE_BOOST_COMPONENTS chrono) + endif() find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET) endif() if (NOT Boost_FOUND) @@ -743,7 +851,7 @@ IF(USE_OGRE) if (Boost_FOUND) # Set up referencing of Boost - LIST(APPEND VISP_DEFS "-DBOOST_ALL_NO_LIB") + #LIST(APPEND VISP_DEFS "-DBOOST_ALL_NO_LIB") LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) LIST(APPEND VISP_EXTERN_LIBRARIES ${Boost_LIBRARIES}) endif() @@ -772,6 +880,19 @@ IF(USE_OGRE) SET(VISP_HAVE_OGRE_FOUND "yes") # for ViSP-third-party.txt SET(VISP_HAVE_OGRE TRUE) # for header vpConfig.h + # If Ogre media are not available we provide the minimal material to run the examples: + # - resources.cfg + # - plugins.cfg + # - media/materials/... + # - media/models/... + # + # We need to introduce OGRE_MEDIA_NOT_AVAILABLE to memorize when OGRE_MEDIA_DIR is not set. + # Because in that case, OGRE_MEDIA_DIR should be set first to VISP_HAVE_OGRE_RESOURCES_PATH + # (for the "make all" case) then to VISP_INSTALL_DIR_OGRE_RESOURCES (for the "make install" case) + if(NOT OGRE_MEDIA_DIR) + set(OGRE_MEDIA_NOT_AVAILABLE "TRUE") + endif() + # Try to search for an existing plugins.cfg file # Here we cannot use OGRE_PLUGIN_DIR_REL or OGRE_PLUGIN_DIR_DBG where # we may find an existing plugins.cfg file, since under Windows in these @@ -794,18 +915,12 @@ IF(USE_OGRE) # case 2: install or packaging case #-------------- - IF(UNIX) - set(VISP_INSTALL_DIR_OGRE_PLUGINS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/visp/data/ogre-simulator") - ELSE() - set(VISP_INSTALL_DIR_OGRE_PLUGINS "${CMAKE_INSTALL_PREFIX}/data/ogre-simulator") - ENDIF() - # install rule for plugins.cfg: IF(UNIX) if(OGRE_PLUGIN_DIR_REL) INSTALL(FILES ${VISP_HAVE_OGRE_PLUGINS_PATH}/plugins.cfg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/data/ogre-simulator + DESTINATION ${CMAKE_INSTALL_LIBDIR}/visp/data/ogre-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) @@ -813,7 +928,7 @@ IF(USE_OGRE) if(OGRE_PLUGIN_DIR_DBG) INSTALL(FILES ${VISP_HAVE_OGRE_PLUGINS_PATH}/plugins_d.cfg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/data/ogre-simulator + DESTINATION ${CMAKE_INSTALL_LIBDIR}/visp/data/ogre-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) @@ -846,48 +961,113 @@ IF(USE_OGRE) NO_SYSTEM_ENVIRONMENT_PATH ) + # Here we copy all the minimal media files + # - media/materials/... + # - media/models/... + if(OGRE_MEDIA_NOT_AVAILABLE) + file(COPY data/ogre-simulator/media DESTINATION ${VISP_BINARY_DIR}/data/ogre-simulator) + endif() + + + # Here we create a resources.cfg if it was not found IF(NOT VISP_HAVE_OGRE_RESOURCES_PATH) # we create a resources.cfg file for vpAROgre.cpp # case 1: normal case + # If OGRE_MEDIA_DIR is not found, we set it to VISP_HAVE_OGRE_RESOURCES_PATH in order to use + # the minimal requested media to run the examples #-------------- SET(VISP_HAVE_OGRE_RESOURCES_PATH ${VISP_BINARY_DIR}/data/ogre-simulator) + if(OGRE_MEDIA_NOT_AVAILABLE) + set(OGRE_MEDIA_DIR ${VISP_HAVE_OGRE_RESOURCES_PATH}/media) + endif() + + # On Fedora 20 when ogre-devel and ogre-samples packages are installed, AROgre and + # AROgreBasic examples produce a segfault when the folowing line in resource.cf.in + # is commented: + # '#FileSystem=@OGRE_MEDIA_DIR@/materials/programs' + # Here we check if @OGRE_MEDIA_DIR@/materials/programs forder is empty. + # If empty, we comment the line to avoid lintian warnings. + # If not empty we remove the comment to use the available programs. + file(GLOB OGRE_PROGRAM_CONTENT ${OGRE_MEDIA_DIR}/materials/programs/*.*) + if(OGRE_PROGRAM_CONTENT) + set(OGRE_COMMENT_LINE "") + else() + set(OGRE_COMMENT_LINE "#") + endif() CONFIGURE_FILE( ${VISP_CMAKE_MODULE_PATH}/resources.cfg.in ${VISP_HAVE_OGRE_RESOURCES_PATH}/resources.cfg IMMEDIATE @ONLY) # case 2: install or packaging case + # If OGRE_MEDIA_DIR is not found, we set it to VISP_INSTALL_DIR_OGRE_RESOURCES in order to use + # the minimal requested media to run the examples #-------------- IF(UNIX) - set(VISP_INSTALL_DIR_OGRE_RESOURCES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/visp/data/ogre-simulator") + set(VISP_INSTALL_DIR_OGRE_RESOURCES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/data/ogre-simulator") ELSE() set(VISP_INSTALL_DIR_OGRE_RESOURCES "${CMAKE_INSTALL_PREFIX}/data/ogre-simulator") ENDIF() - # install rule for resources.cfg: - IF(UNIX) - INSTALL(FILES - ${VISP_HAVE_OGRE_RESOURCES_PATH}/resources.cfg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/data/ogre-simulator + if(OGRE_MEDIA_NOT_AVAILABLE) + set(OGRE_MEDIA_DIR ${VISP_INSTALL_DIR_OGRE_RESOURCES}/media) + endif() + + # install rule for resources.cfg and Ogre media if they are not available: + if(UNIX) + configure_file( + ${VISP_CMAKE_MODULE_PATH}/resources.cfg.in + ${VISP_BINARY_DIR}/unix-install/resources.cfg + IMMEDIATE @ONLY) + install(FILES + ${VISP_BINARY_DIR}/unix-install/resources.cfg + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/data/ogre-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) - ELSE() - INSTALL(FILES - ${VISP_HAVE_OGRE_RESOURCES_PATH}/resources.cfg + if(OGRE_MEDIA_NOT_AVAILABLE) + install(DIRECTORY + data/ogre-simulator/media + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/data/ogre-simulator + FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE + COMPONENT libraries + ) + endif() + else() + configure_file( + ${VISP_CMAKE_MODULE_PATH}/resources.cfg.in + ${VISP_BINARY_DIR}/win-install/resources.cfg + IMMEDIATE @ONLY) + install(FILES + ${VISP_BINARY_DIR}/win-install/resources.cfg DESTINATION data/ogre-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) - ENDIF() - ENDIF() + if(OGRE_MEDIA_NOT_AVAILABLE) + install(DIRECTORY + data/ogre-simulator/media + DESTINATION data/ogre-simulator + FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE + COMPONENT libraries + ) + endif() + endif() + endif() MARK_AS_ADVANCED(VISP_HAVE_OGRE_PLUGINS_PATH) MARK_AS_ADVANCED(VISP_HAVE_OGRE_RESOURCES_PATH) #message("VISP_HAVE_OGRE_PLUGINS_PATH: ${VISP_HAVE_OGRE_PLUGINS_PATH}") #message("VISP_HAVE_OGRE_RESOURCES_PATH: ${VISP_HAVE_OGRE_RESOURCES_PATH}") - - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS}) + + # hack to fix possible presence of NOTFOUND in OGRE_INCLUDE_DIRS + #message("OGRE_INCLUDE_DIRS: ${OGRE_INCLUDE_DIRS}") + #LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS}) + foreach(inc_ ${OGRE_INCLUDE_DIRS}) + if(NOT ${inc_} MATCHES "NOTFOUND") + LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${inc_}) + endif() + endforeach() LIST(APPEND VISP_EXTERN_LIBRARIES ${OGRE_LIBRARIES}) ENDIF(USE_OGRE) @@ -914,9 +1094,15 @@ IF(USE_COIN) IF(WIN32) LIST(APPEND VISP_DEFS "-DCOIN_DLL") ENDIF(WIN32) - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR}) - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${COIN3D_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${OPENGL_LIBRARIES}) + LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${COIN3D_INCLUDE_DIRS}) + # On OSX cmake 2.8 found OpenGL but OPENGL_INCLUDE_DIR was set to NOT_FOUND + # We add a test to be sure that the OPENGL vars exist. + if(OPENGL_INCLUDE_DIR) + list(APPEND VISP_EXTERN_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR}) + endif() + if(OPENGL_LIBRARIES) + list(APPEND VISP_EXTERN_LIBRARIES ${OPENGL_LIBRARIES}) + endif() #MESSAGE("COIN3D_INCLUDE_DIRS = ${COIN3D_INCLUDE_DIRS}") #MESSAGE("COIN3D_LIBRARIES = ${COIN3D_LIBRARIES}") @@ -1073,6 +1259,7 @@ IF(USE_LIBUSB_1) MESSAGE(STATUS "libusb-1.0 found") + LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${LIBUSB_1_INCLUDE_DIRS}) LIST(APPEND VISP_EXTERN_LIBRARIES ${LIBUSB_1_LIBRARIES}) ELSE() MESSAGE(STATUS "libusb-1.0 not found") @@ -1089,7 +1276,7 @@ IF(USE_LIBFREENECT AND USE_LIBUSB_1 AND USE_PTHREAD) # The material is found. Check if libfreenect is an old version include(CheckCXXSourceCompiles) SET(CMAKE_REQUIRED_LIBRARIES ${LIBFREENECT_LIBRARIES} ${PTHREAD_LIBRARIES} ${LIBUSB_1_LIBRARIES}) - SET(CMAKE_REQUIRED_INCLUDES ${LIBFREENECT_INCLUDE_DIRS} ${PTHREAD_INCLUDE_DIRS}) + SET(CMAKE_REQUIRED_INCLUDES ${LIBFREENECT_INCLUDE_DIRS} ${PTHREAD_INCLUDE_DIRS} ${LIBUSB_1_INCLUDE_DIRS}) CHECK_CXX_SOURCE_COMPILES(" #include <libfreenect.hpp> @@ -1157,56 +1344,152 @@ SET(VISP_HAVE_ZLIB_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_FFMPEG_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_CMU1394_FOUND "no") # for ViSP-third-party.txt -# open CV -IF(USE_OPENCV) +# OpenCV +if(USE_OPENCV) #SET(OpenCV_FIND_QUIETLY TRUE) #FIND_PACKAGE(OpenCV) - IF(OpenCV_FOUND) - MESSAGE(STATUS "OpenCV found") - IF(VISP_BUILD_SHARED_LIBS AND VISP_USE_MSVC) - ADD_DEFINITIONS("-DCVAPI_EXPORTS") - ENDIF(VISP_BUILD_SHARED_LIBS AND VISP_USE_MSVC) - SET(VISP_HAVE_OPENCV TRUE) # for header vpConfig.h - SET(VISP_HAVE_OPENCV_FOUND "yes") # for ViSP-third-party.txt - - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LINK_DIR ${OpenCV_LIB_DIR}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${OpenCV_LIBS}) + if(OpenCV_FOUND) + message(STATUS "OpenCV found") + set(VISP_HAVE_OPENCV TRUE) # for header vpConfig.h + set(VISP_HAVE_OPENCV_FOUND "yes") # for ViSP-third-party.txt + # On win32 since OpenCV 2.4.7 and on OSX with OpenCV 2.4.10 we cannot use OpenCV_LIBS to set ViSP 3rd party libraries. + # Using OpenCV_LIBS works to build visp library, examples, demos and test thanks to the components, + # but not tutorials that are stand alone Cmake project that use ViSP as a 3rd party. + # To be clear OpenCV_LIBS contains opencv_core and not c:\...\opencv_core248.lib full path as requested + # to use ViSP. This was not the case with OpenCV 2.4.6. + # For the build of ViSP it works with OpenCV_LIBS: in that case thanks to opencv_core properties, CMake + # is able to find the real name and location of the libraries. + # But when ViSP is used as a 3rd party where it should import OpenCV libraries, it doesn't work with + # OpenCV_LIBS. + # The solution here is to get the real name of OpenCV libraries thanks to the properties and link + # with these names. + # An other way could be to include OpenCVConfig.cmake, but in that case, visp-config and visp.pc + # will be not able to give the names of OpenCV libraries when used without CMake. + #message("OpenCV_LIB_COMPONENTS: ${OpenCV_LIB_COMPONENTS}") + #message("OpenCV_LIBS: ${OpenCV_LIBS}") + #if(WIN32 AND OpenCV_LIB_COMPONENTS AND OpenCV_VERSION AND OpenCV_VERSION VERSION_GREATER 2.4.6.1) + if(OpenCV_LIB_COMPONENTS AND OpenCV_VERSION AND OpenCV_VERSION VERSION_GREATER 2.4.6.1) + set(config_ "NONE" "RELEASE" "DEBUG" "RELEASEWITHDEBINFO") # RelWithDebugInfo was requested under Fedora 20 + + if(POLICY CMP0045) + # Fix Error on non-existent target in get_target_property for 3rd party location extraction + cmake_policy(PUSH) + cmake_policy(SET CMP0045 OLD) + endif() + + foreach(component_ ${OpenCV_LIB_COMPONENTS}) + foreach(imp_config_ ${config_}) + if(OpenCV_SHARED) + get_target_property(component_property_${imp_config_}_ ${component_} IMPORTED_IMPLIB_${imp_config_}) + # particular case of opencv_ts that doesn't have an implib + if(NOT EXISTS "${component_property_${imp_config_}_}") + get_target_property(component_property_${imp_config_}_ ${component_} IMPORTED_LOCATION_${imp_config_}) + endif() + else() + get_target_property(component_property_${imp_config_}_ ${component_} IMPORTED_LOCATION_${imp_config_}) + endif() + get_target_property(component_property_3rdparty_${imp_config_}_ ${component_} IMPORTED_LINK_INTERFACE_LIBRARIES_${imp_config_}) + #message("component_property_${imp_config_}_: ${component_property_${imp_config_}_}") + #message("component_property_3rdparty_${imp_config_}_: ${component_property_3rdparty_${imp_config_}_}") + + # Under Unix, there is no specific suffix for OpenCV libraries. If one is found we add it + # Under Windows, we add the "optimized", "debug" specific keywords + if(WIN32 AND EXISTS "${component_property_${imp_config_}_}" AND "${imp_config_}" MATCHES "RELEASE") # also valid for RELEASEWITHDEBINFO + list(APPEND VISP_EXTERN_LIBRARIES optimized "${component_property_${imp_config_}_}") + elseif(WIN32 AND EXISTS "${component_property_${imp_config_}_}" AND "${imp_config_}" MATCHES "DEBUG") + list(APPEND VISP_EXTERN_LIBRARIES debug "${component_property_${imp_config_}_}") + elseif(EXISTS "${component_property_${imp_config_}_}") + list(APPEND VISP_EXTERN_LIBRARIES "${component_property_${imp_config_}_}") + endif() + + if(component_property_3rdparty_${imp_config_}_) + foreach(3rdparty_ ${component_property_3rdparty_${imp_config_}_}) + #message("3rdparty_ ${3rdparty_}") + list(FIND OpenCV_LIB_COMPONENTS ${3rdparty_} 3rdparty_is_opencv_component_) + if(3rdparty_is_opencv_component_ LESS 0) + #message("${3rdparty_} is not an opencv component") + get_target_property(3rdparty_opt_location_ ${3rdparty_} IMPORTED_LOCATION_${imp_config_}) + if(NOT EXISTS "${3rdparty_opt_location_}") + #message("3rdparty_: ${3rdparty_} location doesn't exist in ${imp_config_}") + get_target_property(3rdparty_opt_location_ ${3rdparty_} IMPORTED_LOCATION) + #message("3rdparty_: ${3rdparty_} location : ${3rdparty_opt_location_}") + endif() + if(EXISTS "${3rdparty_opt_location_}") + #message("3rdparty_opt_location_: ${3rdparty_opt_location_} with config ${imp_config_}") + if(WIN32 AND "${imp_config_}" MATCHES "RELEASE") + #message("is release") + list(APPEND VISP_EXTERN_LIBRARIES optimized ${3rdparty_opt_location_}) + elseif(WIN32 AND "${imp_config_}" MATCHES "DEBUG") + list(APPEND VISP_EXTERN_LIBRARIES debug ${3rdparty_opt_location_}) + else() + list(APPEND VISP_EXTERN_LIBRARIES ${3rdparty_opt_location_}) + endif() + else() + find_library(3rdparty_location_ NAMES ${3rdparty_}) + mark_as_advanced(3rdparty_location_) + if(3rdparty_location_) + #message(${3rdparty_location_}) + list(APPEND VISP_EXTERN_LIBRARIES ${3rdparty_location_}) # should be a system dependency + else() + list(APPEND VISP_EXTERN_LIBRARIES ${3rdparty_}) # should be a system dependency + endif() + endif() + endif() + endforeach() + endif() + endforeach() + endforeach() + + if(POLICY CMP0045) + # Fix Error on non-existent target in get_target_property for 3rd party location extraction + cmake_policy(POP) + endif() + + else() + # this should be an old OpenCV version that doesn't have the previous behavior + list(APPEND VISP_EXTERN_LIBRARIES ${OpenCV_LIBS}) + endif() + list(APPEND VISP_EXTERN_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}) + #LIST(APPEND VISP_EXTERN_LIBRARIES ${OpenCV_LIBS}) #MESSAGE("OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}") #MESSAGE("OpenCV_LIBS = ${OpenCV_LIBS}") #MESSAGE("OpenCV_LIB_DIR = ${OpenCV_LIB_DIR}") - SET(VISP_HAVE_OPENCV_VERSION "(${OpenCV_VERSION_MAJOR}*65536 + ${OpenCV_VERSION_MINOR}*256 + ${OpenCV_VERSION_PATCH})") # for ViSP-third-party.txt + set(VISP_HAVE_OPENCV_VERSION "(${OpenCV_VERSION_MAJOR}*65536 + ${OpenCV_VERSION_MINOR}*256 + ${OpenCV_VERSION_PATCH})") # for ViSP-third-party.txt #MESSAGE("VISP_HAVE_OPENCV_VERSION = ${VISP_HAVE_OPENCV_VERSION}") + #message("VISP_EXTERN_LIBRARIES = ${VISP_EXTERN_LIBRARIES}") # additional check to see if opencv_nonfree module introduced in OpenCV 2.4 is available #message("OpenCV_VERSION = ${OpenCV_VERSION}") #message("OpenCV_NONFREE_FOUND: ${OPENCV_NONFREE_FOUND}") - if(${OpenCV_VERSION}) - if(${OpenCV_VERSION} VERSION_LESS "2.4.2") + if(OpenCV_VERSION) + if(OpenCV_VERSION VERSION_LESS "2.4.0") + message(STATUS "OpenCV nonfree module found") set(VISP_HAVE_OPENCV_NONFREE TRUE) # for header vpConfig.h else() # Version is greater or equal to 2.4.0 - if(OPENCV_NONFREE_FOUND) - message(STATUS "OpenCV nonfree found") + if(OPENCV_NONFREE_FOUND) # OpenCV < 3.0.0 + message(STATUS "OpenCV xfeatures2d module found") set(VISP_HAVE_OPENCV_NONFREE TRUE) # for header vpConfig.h + elseif(OPENCV_XFEATURES2D_FOUND) # OpenCV >= 3.0.0 + set(VISP_HAVE_OPENCV_XFEATURES2D TRUE) # for header vpConfig.h else() - message(STATUS "OpenCV nonfree not found") + message(STATUS "OpenCV nonfree or xfeature2d module not found") endif() endif() else() + message("OpenCV_VERSION is false") message(STATUS "OpenCV nonfree not found") endif() - ELSE(OpenCV_FOUND) - MESSAGE(STATUS "OpenCV not found") - SET(VISP_HAVE_OPENCV_FOUND "no") # for ViSP-third-party.txt - SET(VISP_HAVE_OPENCV_VERSION "(0)") # for ViSP-third-party.txt - ENDIF(OpenCV_FOUND) -ELSE(USE_OPENCV) - SET(OpenCV_VERSION_MAJOR 0) - SET(OpenCV_VERSION_MINOR 0) + else(OpenCV_FOUND) + message(STATUS "OpenCV not found") + set(VISP_HAVE_OPENCV_FOUND "no") # for ViSP-third-party.txt + set(VISP_HAVE_OPENCV_VERSION "(0)") # for ViSP-third-party.txt + endif(OpenCV_FOUND) +else(USE_OPENCV) + set(OpenCV_VERSION_MAJOR 0) + set(OpenCV_VERSION_MINOR 0) SET(OpenCV_VERSION_PATCH 0) - SET(VISP_HAVE_OPENCV_VERSION "(0)") # for ViSP-third-party.txt -ENDIF(USE_OPENCV) - + set(VISP_HAVE_OPENCV_VERSION "(0)") # for ViSP-third-party.txt +endif(USE_OPENCV) # gnu scientific library IF(USE_GSL) @@ -1216,12 +1499,8 @@ IF(USE_GSL) SET(VISP_HAVE_GSL TRUE) # for header vpConfig.h SET(VISP_HAVE_GSL_FOUND "yes") # for ViSP-third-party.txt LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${GSL_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LINK_DIR ${GSL_EXE_LINKER_FLAGS}) - LIST(APPEND VISP_EXTERN_LINK_DIR ${GSL_LINK_DIRECTORIES}) LIST(APPEND VISP_EXTERN_LIBRARIES ${GSL_LIBRARIES}) - #LINK_DIRECTORIES(${GSL_LINK_DIRECTORIES}) #MESSAGE("GSL_INCLUDE_DIRS = ${GSL_INCLUDE_DIRS}") - #MESSAGE("GSL_LINK_DIRECTORIES = ${GSL_LINK_DIRECTORIES}") #MESSAGE("GSL_LIBRARIES = ${GSL_LIBRARIES}") ELSE(GSL_FOUND) MESSAGE(STATUS "GSL not found") @@ -1372,63 +1651,43 @@ ENDIF(USE_FFMPEG) # Specific hardware : framegrabbers and cameras #---------------------------------------------------------------------- # default initialisation -SET(VISP_HAVE_DC1394_1_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_DC1394_2_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_V4L2_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_DIRECTSHOW_FOUND "no") # for ViSP-third-party.txt # library for firewire cameras -IF(USE_DC1394_1 OR USE_DC1394_2) +IF(USE_DC1394) #FIND_PACKAGE(DC1394) - IF(DC1394_FOUND) - SET(VISP_HAVE_DC1394 TRUE) # for header vpConfig.h - IF(DC1394_2_FOUND AND USE_DC1394_2) - MESSAGE(STATUS "libdc1394-2.x found") - SET(VISP_HAVE_DC1394_2 TRUE) # for header vpConfig.h - SET(VISP_HAVE_DC1394_2_FOUND "yes") # for ViSP-third-party.txt - IF(DC1394_CAMERA_ENUMERATE_FOUND) - SET(VISP_HAVE_DC1394_2_CAMERA_ENUMERATE TRUE) # for header vpConfig.h - ENDIF(DC1394_CAMERA_ENUMERATE_FOUND) - IF(DC1394_FIND_CAMERAS_FOUND) - SET(VISP_HAVE_DC1394_2_FIND_CAMERAS TRUE) # for header vpConfig.h - ENDIF(DC1394_FIND_CAMERAS_FOUND) - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${DC1394_2_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${DC1394_2_LIBRARY}) - - #MESSAGE("DBG VISP_HAVE_DC1394_2= ${VISP_HAVE_DC1394_2}") - ELSE(DC1394_2_FOUND AND USE_DC1394_2) - MESSAGE(STATUS "libdc1394-2.x not found") - SET(VISP_HAVE_DC1394_2_FOUND "no") # for ViSP-third-party.txt - ENDIF(DC1394_2_FOUND AND USE_DC1394_2) - - IF(DC1394_1_FOUND AND USE_DC1394_1) - MESSAGE(STATUS "libdc1394-1.x found") - SET(VISP_HAVE_DC1394_1 TRUE) # for header vpConfig.h - SET(VISP_HAVE_DC1394_1_FOUND "yes") # for ViSP-third-party.txt - #MESSAGE("DBG VISP_HAVE_DC1394_1= ${VISP_HAVE_DC1394_1}") - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${DC1394_1_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${DC1394_1_LIBRARY}) - ELSE(DC1394_1_FOUND AND USE_DC1394_1) - MESSAGE(STATUS "libdc1394-1.x not found") - SET(VISP_HAVE_DC1394_1_FOUND "no") # for ViSP-third-party.txt - ENDIF(DC1394_1_FOUND AND USE_DC1394_1) + if(DC1394_FOUND) + set(VISP_HAVE_DC1394 TRUE) # for header vpConfig.h + + MESSAGE(STATUS "libdc1394-2.x found") + SET(VISP_HAVE_DC1394_2 TRUE) # for header vpConfig.h + SET(VISP_HAVE_DC1394_2_FOUND "yes") # for ViSP-third-party.txt + IF(DC1394_CAMERA_ENUMERATE_FOUND) + SET(VISP_HAVE_DC1394_2_CAMERA_ENUMERATE TRUE) # for header vpConfig.h + ENDIF(DC1394_CAMERA_ENUMERATE_FOUND) + IF(DC1394_FIND_CAMERAS_FOUND) + SET(VISP_HAVE_DC1394_2_FIND_CAMERAS TRUE) # for header vpConfig.h + ENDIF(DC1394_FIND_CAMERAS_FOUND) + LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${DC1394_INCLUDE_DIRS}) + LIST(APPEND VISP_EXTERN_LIBRARIES ${DC1394_LIBRARY}) + + #MESSAGE("DBG VISP_HAVE_DC1394_2= ${VISP_HAVE_DC1394_2}") #MESSAGE("DBG DC1394_INCLUDE_DIRS=${DC1394_INCLUDE_DIRS}") - IF(APPLE) #SET(VISP_EXTERN_LIBRARIES ${VISP_EXTERN_LIBRARIES} IOkit "-framework Carbon") LIST(APPEND VISP_EXTERN_LIBRARIES "-framework IOkit -framework Carbon") ENDIF(APPLE) - #MESSAGE("DC1394_LIBRARIES = ${DC1394_LIBRARIES}") + #MESSAGE("DC1394_LIBRARIES = ${DC1394_LIBRARIES}") ELSE(DC1394_FOUND) - MESSAGE(STATUS "libdc1394-1.x not found") MESSAGE(STATUS "libdc1394-2.x not found") - SET(VISP_HAVE_DC1394_1_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_DC1394_2_FOUND "no") # for ViSP-third-party.txt #MESSAGE("Firewire dc1394 requested but not found. Turn off USE_DC1394!") # SET(USE_DC1394 OFF) ENDIF(DC1394_FOUND) -ENDIF(USE_DC1394_1 OR USE_DC1394_2) +ENDIF(USE_DC1394) # library for CMU1394 pan-tilt head IF(USE_CMU1394) @@ -1604,7 +1863,6 @@ SET(VISP_HAVE_PTU46_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_PIONEER_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_VIPER650_FOUND "no") # for ViSP-third-party.txt SET(VISP_HAVE_VIPER850_FOUND "no") # for ViSP-third-party.txt -SET(VISP_HAVE_CYCAB_FOUND "no") # for ViSP-third-party.txt # library for Irisa's Afma4 cylindrical robot IF(USE_AFMA4) @@ -1766,26 +2024,6 @@ IF(USE_PTU46) ENDIF(PTU46_FOUND) ENDIF(USE_PTU46) -# library for Cycab car-like mobile robot -IF(USE_CYCAB) - IF(CycabTk_FOUND) - IF(CycabTk_NEW_FOUND) - SET(VISP_HAVE_CYCABTK TRUE) # for header vpConfig.h - ENDIF(CycabTk_NEW_FOUND) - IF(CycabTk_OLD_FOUND) - SET(VISP_HAVE_CYCABTK_OLD TRUE) # for header vpConfig.h - ENDIF(CycabTk_OLD_FOUND) - SET(VISP_HAVE_CYCAB TRUE) # for header vpConfig.h - SET(VISP_HAVE_CYCAB_FOUND "yes") # for ViSP-third-party.txt - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS ${CycabTk_INCLUDE_DIRS}) - LIST(APPEND VISP_EXTERN_LIBRARIES ${CycabTk_LIBRARIES}) - ELSE(CycabTk_FOUND) - MESSAGE(STATUS "Cycab interface not found") - MESSAGE("Cycab requested but not found. Turn off USE_CYCAB option!") - SET(VISP_HAVE_CYCAB_FOUND "no") # for ViSP-third-party.txt - ENDIF(CycabTk_FOUND) -ENDIF(USE_CYCAB) - # libraries for Pioneer mobile robots if(USE_ARIA) if(UNIX) @@ -1814,46 +2052,23 @@ if(USE_ARIA) endif(UNIX) endif() +if(USE_ZBAR) + set(VISP_HAVE_ZBAR TRUE) # for header vpConfig.h + set(VISP_HAVE_ZBAR_FOUND "yes") # for ViSP-third-party.txt + list(APPEND VISP_EXTERN_INCLUDE_DIRS ${ZBAR_INCLUDE_DIRS}) + list(APPEND VISP_EXTERN_LIBRARIES ${ZBAR_LIBRARIES}) +else() + set(VISP_HAVE_ZBAR_FOUND "no") # for ViSP-third-party.txt +endif() -#---------------------------------------------------------------------- -# add html-doc target for doxygen documentation building -#---------------------------------------------------------------------- -FIND_PACKAGE(Doxygen) -IF (DOXYGEN_FOUND) - SET(VISP_HAVE_DOXYGEN "yes") # for header vpConfig.h - SET(VISP_HAVE_DOXYGEN_FOUND "yes") # for ViSP-third-party.txt - IF(DOXYGEN_DOT_EXECUTABLE) - SET(VISP_HAVE_DOT "yes") # for header vpConfig.h - SET(VISP_HAVE_DOT_FOUND "yes") # for ViSP-third-party.txt - ELSE(DOXYGEN_DOT_EXECUTABLE) - SET(VISP_HAVE_DOT "no") # for header vpConfig.h - SET(VISP_HAVE_DOT_FOUND "no") # for ViSP-third-party.txt - ENDIF(DOXYGEN_DOT_EXECUTABLE) - ## we need latex for doxygen because of the formulas - FIND_PACKAGE(LATEX) - IF (NOT LATEX_COMPILER) - MESSAGE(STATUS "latex command LATEX_COMPILER not found but usually required. You will probably get warnings and user interaction on doxy run.") - ENDIF (NOT LATEX_COMPILER) - IF (NOT MAKEINDEX_COMPILER) - MESSAGE(STATUS "makeindex command MAKEINDEX_COMPILER not found but usually required.") - ENDIF (NOT MAKEINDEX_COMPILER) - IF (NOT DVIPS_CONVERTER) - MESSAGE(STATUS "dvips command DVIPS_CONVERTER not found but usually required.") - ENDIF (NOT DVIPS_CONVERTER) - - CONFIGURE_FILE(${VISP_SOURCE_DIR}/doc/config-doxygen.in - ${VISP_DOC_DIR}/config-doxygen - @ONLY ) - - CONFIGURE_FILE(${VISP_SOURCE_DIR}/doc/mainpage.doc.in - ${VISP_SOURCE_DIR}/doc/mainpage.doc - @ONLY ) - ADD_CUSTOM_TARGET(html-doc ${DOXYGEN_EXECUTABLE} ${VISP_DOC_DIR}/config-doxygen) -ELSE(DOXYGEN_FOUND) - SET(VISP_HAVE_DOXYGEN "no") # for header vpConfig.h - SET(VISP_HAVE_DOXYGEN_FOUND "no") # for ViSP-third-party.txt - SET(VISP_HAVE_DOT_FOUND "no") # for ViSP-third-party.txt -ENDIF(DOXYGEN_FOUND) +if(USE_DMTX) + set(VISP_HAVE_DMTX TRUE) # for header vpConfig.h + set(VISP_HAVE_DMTX_FOUND "yes") # for ViSP-third-party.txt + list(APPEND VISP_EXTERN_INCLUDE_DIRS ${DMTX_INCLUDE_DIRS}) + list(APPEND VISP_EXTERN_LIBRARIES ${DMTX_LIBRARIES}) +else() + set(VISP_HAVE_DMTX_FOUND "no") # for ViSP-third-party.txt +endif() #---------------------------------------------------------------------- # Add definitions @@ -1865,8 +2080,7 @@ ENDIF(DOXYGEN_FOUND) # warnings. To suppress it, we add the _CRT_SECURE_NO_DEPRECATE preprocessor # definition if(WIN32 AND MSVC) - #MESSAGE("Add -D_CRT_SECURE_NO_DEPRECATE ") - list(APPEND VISP_DEFS "-D_CRT_SECURE_NO_DEPRECATE") + add_definitions("-D_CRT_SECURE_NO_DEPRECATE") endif() #MESSAGE("Definitions: ${VISP_DEFS}") @@ -1874,7 +2088,7 @@ IF(VISP_DEFS) LIST(REMOVE_DUPLICATES VISP_DEFS) ENDIF(VISP_DEFS) #MESSAGE("Definitions without duplicates: ${VISP_DEFS}") -ADD_DEFINITIONS(${VISP_DEFS}) +add_definitions(${VISP_DEFS}) #---------------------------------------------------------------------- # Add intern and third party include dirs @@ -1883,38 +2097,55 @@ ADD_DEFINITIONS(${VISP_DEFS}) IF(VISP_EXTERN_INCLUDE_DIRS) LIST(REMOVE_DUPLICATES VISP_EXTERN_INCLUDE_DIRS) ENDIF(VISP_EXTERN_INCLUDE_DIRS) -#MESSAGE("VISP_EXTERN_INCLUDE_DIRS without duplicates: ${VISP_EXTERN_INCLUDE_DIRS}") -INCLUDE_DIRECTORIES(${VISP_INTERN_INCLUDE_DIR} ${VISP_EXTERN_INCLUDE_DIRS}) +#MESSAGE("VISP_INTERN_INCLUDE_DIR: ${VISP_INTERN_INCLUDE_DIR}") +#MESSAGE("VISP_EXTERN_INCLUDE_DIRS: ${VISP_EXTERN_INCLUDE_DIRS}") +include_directories(BEFORE ${VISP_INTERN_INCLUDE_DIR}) +include_directories(${VISP_EXTERN_INCLUDE_DIRS}) #---------------------------------------------------------------------- -# Add link directories +# Use statically or dynamically linked CRT? +# Default: dynamic #---------------------------------------------------------------------- -#MESSAGE("VISP_EXTERN_LINK_DIR: ${VISP_EXTERN_LINK_DIR}") -IF(VISP_EXTERN_LINK_DIR) - LIST(REMOVE_DUPLICATES VISP_EXTERN_LINK_DIR) -ENDIF(VISP_EXTERN_LINK_DIR) -#MESSAGE("VISP_EXTERN_LINK_DIR without duplicates: ${VISP_EXTERN_LINK_DIR}") -LINK_DIRECTORIES(${VISP_EXTERN_LINK_DIR}) +if(MSVC) + include(${VISP_CMAKE_MODULE_PATH}/VISPCRTLinkage.cmake) +endif(MSVC) #---------------------------------------------------------------------- -# Cleanify the libraries list +# Platform specific #---------------------------------------------------------------------- -IF(APPLE) - LIST(REMOVE_DUPLICATES VISP_EXTERN_LIBRARIES) -ENDIF() +include(${VISP_CMAKE_MODULE_PATH}/VISPDetectPlatform.cmake) -#---------------------------------------------------------------------- -# Generate the package dependent visp-config shell script for projects which -# are not using CMake: -# Usage: -# visp-config --cflags ... -#---------------------------------------------------------------------- -INCLUDE(${VISP_CMAKE_MODULE_PATH}/GenerateConfigScript.cmake) +# Set the path where to install the lib +if(WIN32) + if(DEFINED VISP_RUNTIME AND DEFINED VISP_ARCH) + set(VISP_INSTALL_BINARIES_PREFIX "${VISP_ARCH}/${VISP_RUNTIME}/") + else() + message(STATUS "Can't detect runtime and/or arch") + set(VISP_INSTALL_BINARIES_PREFIX "") + endif() +else() + set(VISP_INSTALL_BINARIES_PREFIX "") +endif() + +# where to install the library +if(WIN32) + if(VISP_STATIC) + set(VISP_LIB_INSTALL_PATH "${VISP_INSTALL_BINARIES_PREFIX}static${CMAKE_INSTALL_LIBDIR}") + else() + set(VISP_LIB_INSTALL_PATH "${VISP_INSTALL_BINARIES_PREFIX}${CMAKE_INSTALL_LIBDIR}") + endif() + set(VISP_BIN_INSTALL_PATH "${VISP_INSTALL_BINARIES_PREFIX}${CMAKE_INSTALL_BINDIR}") +else() + set(VISP_LIB_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}) + set(VISP_BIN_INSTALL_PATH ${CMAKE_INSTALL_BINDIR}) +endif() #---------------------------------------------------------------------- -# add distclean target +# Cleanify the libraries list #---------------------------------------------------------------------- -INCLUDE(${VISP_CMAKE_MODULE_PATH}/TargetDistclean.cmake) +IF(APPLE) + LIST(REMOVE_DUPLICATES VISP_EXTERN_LIBRARIES) +ENDIF() #---------------------------------------------------------------------- # customize clean target @@ -1932,40 +2163,18 @@ SET_DIRECTORY_PROPERTIES(PROPERTIES ) #---------------------------------------------------------------------- -# customize uninstall target +# Propagation in src to build the library #---------------------------------------------------------------------- -CONFIGURE_FILE( - "${VISP_CMAKE_MODULE_PATH}/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) - -ADD_CUSTOM_TARGET(uninstall - "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") +add_subdirectory(src) #---------------------------------------------------------------------- -# Configure the file describing how to use ViSP. VISPConfig.cmake -# is the main file configuring a CMake package. -# . Exports build settings and dependencies for projects using ViSP as a -# third party project. -# . Create and install files for simple use of FIND_PACKAGE(VISP) -# by other cmakified "user" projects and libraries depending on ViSP. -# (see "Mastering CMake", pp.72) -# . To use ViSP in a third party project based on CMake: -# FIND_PACKAGE(VISP REQUIRED) -# IF(VISP_FOUND) -# INCLUDE(${VISP_USE_FILE}) -# ENDIF(VISP_FOUND) +# Configure the files that depend on the build <binary dir> or +# installation <install dir> usage. This is the case of: +# vpConfig.h #---------------------------------------------------------------------- -# Installation for ViSPConfig.cmake -# case 1: <binary dir>/ViSPConfig.cmake for use with make -# case 2: <install dir>/ViSPConfig.cmake for use with make install - -# case 1 +# case 1: when ViSP is build with make; files are used in <binary dir> #-------------- - -set(VISP_ROOT_DIR_CONFIGCMAKE "${VISP_BINARY_DIR}") -set(VISP_ROOT_DIR_CMAKE_CONFIGCMAKE "${VISP_BINARY_DIR}") set(VISP_ROOT_DIR_DATA_CONFIGCMAKE "${VISP_BINARY_DIR}") SET(VISP_SCENES_DIR ${VISP_ROOT_DIR_DATA_CONFIGCMAKE}/data/wireframe-simulator) @@ -1976,32 +2185,11 @@ CONFIGURE_FILE(${VISP_SOURCE_DIR}/include/vpConfig.h.cmake ${VISP_INCLUDE_DIR}/vpConfig.h ) -CONFIGURE_FILE( - ${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake.in - ${VISP_BINARY_DIR}/VISPConfig.cmake - IMMEDIATE @ONLY) - -# Just to copy ./CMakeModule/VISPUse.cmake.in <binary dir>/UseVISP.cmake -CONFIGURE_FILE( - ${VISP_CMAKE_MODULE_PATH}/VISPUse.cmake.in - ${VISP_BINARY_DIR}/VISPUse.cmake - IMMEDIATE @ONLY) - -# Generate the package dependent file include/visp/vpConfig.h -CONFIGURE_FILE(${VISP_SOURCE_DIR}/ViSP-third-party.txt.cmake - ${VISP_BINARY_DIR}/ViSP-third-party.txt -) - - -# case 2 +# case 2: when ViSP is build with make install; files are used in <install dir> #-------------- - -set(VISP_ROOT_DIR_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}") IF(UNIX) - set(VISP_ROOT_DIR_CMAKE_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/visp") - set(VISP_ROOT_DIR_DATA_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/visp") + set(VISP_ROOT_DIR_DATA_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}") ELSE() - set(VISP_ROOT_DIR_CMAKE_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}") set(VISP_ROOT_DIR_DATA_CONFIGCMAKE "${CMAKE_INSTALL_PREFIX}") ENDIF() @@ -2013,40 +2201,19 @@ if(VISP_INSTALL_DIR_OGRE_RESOURCES) endif() # Only if plugins.cfg created by hand, we change the path to plugins.cfg in install/vpConfig.h if(VISP_INSTALL_DIR_OGRE_RESOURCES) - SET(VISP_HAVE_OGRE_PLUGINS_PATH ${VISP_ROOT_DIR_DATA_CONFIGCMAKE}/data/ogre-simulator) + SET(VISP_HAVE_OGRE_PLUGINS_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/visp/data/ogre-simulator) endif() -# Generate the package dependent file include/visp/vpConfig.h -CONFIGURE_FILE(${VISP_SOURCE_DIR}/include/vpConfig.h.cmake - ${VISP_BINARY_DIR}/install/vpConfig.h +#---------------------------------------------------------------------- +# Generate the ViSP-third-party.txt information file +#---------------------------------------------------------------------- +CONFIGURE_FILE(${VISP_SOURCE_DIR}/ViSP-third-party.txt.cmake + ${VISP_BINARY_DIR}/ViSP-third-party.txt ) -CONFIGURE_FILE( - ${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake.in - ${VISP_BINARY_DIR}/install/VISPConfig.cmake - IMMEDIATE @ONLY) - -# Just to copy ./CMakeModule/VISPUse.cmake.in in <install dir>/UseVISP.cmake -CONFIGURE_FILE( - ${VISP_CMAKE_MODULE_PATH}/VISPUse.cmake.in - ${VISP_BINARY_DIR}/install/VISPUse.cmake - IMMEDIATE @ONLY) - -# Export our build settings and library dependencies for use by the third party -# user projects -INCLUDE(${CMAKE_ROOT}/Modules/CMakeExportBuildSettings.cmake) -CMAKE_EXPORT_BUILD_SETTINGS( - ${VISP_BINARY_DIR}/install/VISPBuildSettings.cmake - ) - -# Install vpConfig.h that may differ from the one in include/visp -INSTALL(FILES ${VISP_BINARY_DIR}/install/vpConfig.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/visp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE - COMPONENT headers - ) - +#---------------------------------------------------------------------- # Create and install visp-config.1.gz man page +#---------------------------------------------------------------------- if(UNIX) FIND_PROGRAM(GZIP gzip) file(MAKE_DIRECTORY ${VISP_BINARY_DIR}/doc/man/man1) @@ -2067,46 +2234,31 @@ if(UNIX) MARK_AS_ADVANCED(GZIP) endif() -# Install the packaging files for use by FIND_PACKAGE(VISP) in user projects -# install rule for vpConfig.h: +#---------------------------------------------------------------------- +# Export the library +#---------------------------------------------------------------------- # we need to adapt VISP_SCENES_DIR and VISP_ROBOT_ARMS_DIR # here set to /usr/share/visp/data -IF(UNIX) - INSTALL(FILES - ${VISP_BINARY_DIR}/install/VISPUse.cmake - ${VISP_BINARY_DIR}/install/VISPConfig.cmake - ${VISP_BINARY_DIR}/install/VISPBuildSettings.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/visp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE - COMPONENT libraries - ) +if(UNIX) foreach(SCENE ${WIREFRAME_SCENES}) - INSTALL(FILES + install(FILES "${VISP_SOURCE_DIR}/src/simulator/wireframe-simulator/scene/${SCENE}" - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/data/wireframe-simulator + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/data/wireframe-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) endforeach() foreach(SCENE ${ROBOT_ARMS_SCENES}) - INSTALL(FILES + install(FILES "${VISP_SOURCE_DIR}/src/robot/simulator-robot/arms/${SCENE}" - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/data/robot-simulator + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/data/robot-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) endforeach() -ELSE() - INSTALL(FILES - ${VISP_BINARY_DIR}/install/VISPUse.cmake - ${VISP_BINARY_DIR}/install/VISPConfig.cmake - ${VISP_BINARY_DIR}/install/VISPBuildSettings.cmake - DESTINATION . - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE - COMPONENT libraries - ) +else() foreach(SCENE ${WIREFRAME_SCENES}) - INSTALL(FILES + install(FILES "${VISP_SOURCE_DIR}/src/simulator/wireframe-simulator/scene/${SCENE}" DESTINATION data/wireframe-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE @@ -2114,23 +2266,14 @@ ELSE() ) endforeach() foreach(SCENE ${ROBOT_ARMS_SCENES}) - INSTALL(FILES + install(FILES "${VISP_SOURCE_DIR}/src/robot/simulator-robot/arms/${SCENE}" DESTINATION data/robot-simulator PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT libraries ) endforeach() -ENDIF() - -#---------------------------------------------------------------------- -# For Dart server and tests -# We use CDash set through CTestConfig.cmake file -# Dashboards are sent to http://cdash.irisa.fr/CDash/index.php?project=ViSP -#---------------------------------------------------------------------- -ENABLE_TESTING() -INCLUDE(Dart) -MARK_AS_ADVANCED(DART_ROOT) +endif() #---------------------------------------------------------------------- # For CPack packaging tool @@ -2146,43 +2289,71 @@ if (BUILD_PACKAGE) include (${VISP_SOURCE_DIR}/CMakeModules/CPackConfig.cmake) endif(BUILD_PACKAGE) +#---------------------------------------------------------------------- +# Configure the file describing how to use ViSP. VISPConfig.cmake +# is the main file configuring a CMake package. +# . Exports build settings and dependencies for projects using ViSP as a +# third party project. +# . Create and install files for simple use of find_package(VISP) +# by other cmakified "user" projects and libraries depending on ViSP. +# (see "Mastering CMake", pp.72) +# . To use ViSP in a third party project based on CMake: +# find_package(VISP REQUIRED) +# include_directories(${VISP_INCLUDE_DIRS}) +# target_link_libraries(<target> ${VISP_LIBRARIES}) +#---------------------------------------------------------------------- +include(${VISP_CMAKE_MODULE_PATH}/VISPGenerateConfig.cmake) #---------------------------------------------------------------------- -# Propagation in sub dirs +# Generate the package dependent visp-config shell script for projects which +# are not using CMake: +# Usage: +# visp-config --cflags ... #---------------------------------------------------------------------- -SUBDIRS(src) -if(BUILD_DEMOS) - subdirs(demo) -endif() -if(BUILD_EXAMPLES) - subdirs(example) -endif() -if(BUILD_TUTORIAL) - set(VISP_DIR ${PROJECT_BINARY_DIR}) - mark_as_advanced(VISP_DIR) - mark_as_advanced(VISP_INCLUDE_DIRS) - mark_as_advanced(VISP_LIBRARY_DIRS) - subdirs(tutorial) -endif() +include(${VISP_CMAKE_MODULE_PATH}/VISPGeneratePkgConfigScript.cmake) -IF(BUILD_TESTING) +#---------------------------------------------------------------------- +# Propagation in sub dirs to build demo, example, test, tutorial +#---------------------------------------------------------------------- +if(BUILD_TESTS) + #---------------------------------------------------------------------- + # For Dart server and tests + # We use CDash set through CTestConfig.cmake file + # Dashboards are sent to http://cdash.irisa.fr/CDash/index.php?project=ViSP + #---------------------------------------------------------------------- + enable_testing() + include(Dart) + mark_as_advanced(DART_ROOT) + mark_as_advanced(BUILD_TESTING) # # Test coverage specific code # - IF(CMAKE_COMPILER_IS_GNUCXX AND NOT BUILD_SHARED_LIBS AND CMAKE_BUILD_TYPE MATCHES "Debug") - - OPTION(BUILD_TEST_COVERAGE "Enable test coverage." OFF) - ENDIF() + if(CMAKE_COMPILER_IS_GNUCXX AND NOT BUILD_SHARED_LIBS AND CMAKE_BUILD_TYPE MATCHES "Debug") + option(BUILD_TEST_COVERAGE "Enable test coverage." OFF) + endif() - IF(BUILD_TEST_COVERAGE) + if(BUILD_TEST_COVERAGE) # Add build options for test coverage. Currently coverage is only supported # on gcc compiler # Because using -fprofile-arcs with shared lib can cause problems like: # hidden symbol `__bb_init_func', we add this option only for static # library build - MESSAGE(STATUS "Add -ftest-coverage -fprofile-arcs compiler options") - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ftest-coverage -fprofile-arcs") - ENDIF(BUILD_TEST_COVERAGE) + message(STATUS "Add -ftest-coverage -fprofile-arcs compiler options") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ftest-coverage -fprofile-arcs") + endif() + + add_subdirectory(test) +endif() - SUBDIRS(test) -ENDIF(BUILD_TESTING) +if(BUILD_DEMOS) + add_subdirectory(demo) +endif() +if(BUILD_EXAMPLES) + add_subdirectory(example) +endif() +if(BUILD_TUTORIALS) + set(VISP_DIR ${PROJECT_BINARY_DIR}) + mark_as_advanced(VISP_DIR) + mark_as_advanced(VISP_INCLUDE_DIRS) + add_subdirectory(tutorial) +endif() diff --git a/CMakeModules/AddExtraCompilationFlags.cmake b/CMakeModules/AddExtraCompilationFlags.cmake index f496f28c3f68dc439443479fdad658fcad7f6971..c75a69408173780fd15d3e62fb877e443609da69 100644 --- a/CMakeModules/AddExtraCompilationFlags.cmake +++ b/CMakeModules/AddExtraCompilationFlags.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: AddExtraCompilationFlags.cmake 4308 2013-07-08 08:47:09Z fspindle $ +# $Id: AddExtraCompilationFlags.cmake 4608 2014-01-21 16:37:58Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -146,6 +146,12 @@ MACRO(ADD_EXTRA_COMPILATION_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") endif() + if(UNIX) + if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() + endif() + # Remove duplicates compilation flags separate_arguments(CMAKE_CXX_FLAGS) list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS) diff --git a/CMakeModules/CPackConfig.cmake b/CMakeModules/CPackConfig.cmake index 85cf0c3e8648a4ee6dcd8b7f16fce892879fb7c9..cb4e00d1deed8cff98279d0bc9eb4762665f0231 100644 --- a/CMakeModules/CPackConfig.cmake +++ b/CMakeModules/CPackConfig.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CPackConfig.cmake 4335 2013-07-23 09:53:54Z fspindle $ +# $Id: CPackConfig.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/CPackConfigDeb.cmake b/CMakeModules/CPackConfigDeb.cmake index 729f71131bc736373cf59ff48c6de093ad85f9b0..b7aaa40063454bc10bd432cc10adbb0e9142b484 100644 --- a/CMakeModules/CPackConfigDeb.cmake +++ b/CMakeModules/CPackConfigDeb.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CPackConfigDeb.cmake 4132 2013-02-11 21:04:12Z fspindle $ +# $Id: CPackConfigDeb.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/CPackConfigNsis.cmake b/CMakeModules/CPackConfigNsis.cmake index dfd71e2fc7bdf7d99066d23475f5e7e842df0b1a..9ccb97cd6b280c26f3cc39d3e63bb9154b6c3350 100644 --- a/CMakeModules/CPackConfigNsis.cmake +++ b/CMakeModules/CPackConfigNsis.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CPackConfigNsis.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CPackConfigNsis.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/CPackConfigRpm.cmake b/CMakeModules/CPackConfigRpm.cmake index ca677cde6c604e863bff15800c6efef94292b269..ec624e3359f8b8da02d8251d59d57a8d98a19939 100644 --- a/CMakeModules/CPackConfigRpm.cmake +++ b/CMakeModules/CPackConfigRpm.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CPackConfigRpm.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CPackConfigRpm.cmake 4972 2014-11-16 13:03:24Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -135,7 +135,7 @@ ENDIF() set(CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_DEPENDS}") -SET(CPACK_RPM_PACKAGE_DESCRIPTION "Visual Servoing Platform development files +set(CPACK_RPM_PACKAGE_DESCRIPTION "Visual Servoing Platform development files ViSP, standing for Visual Servoing Platform, is unique. This software is a complete cross-platform solution that allows prototyping and developing applications in visual tracking and visual servoing. @@ -145,3 +145,5 @@ SET(CPACK_RPM_PACKAGE_DESCRIPTION "Visual Servoing Platform development files . This package contains development files (headers and shared library symbolic link).") + +set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/share/man" "/usr/share/man/man1" "/usr/lib64/pkgconfig" "/usr/lib/pkgconfig" "/usr/lib64/cmake" "/usr/lib/cmake") diff --git a/CMakeModules/FindARIA.cmake b/CMakeModules/FindARIA.cmake index 3de03f214f84dae658ca5a99053bd738994e0099..e3c32aa108f28f23e944c71f2e26bda6709c1bbe 100644 --- a/CMakeModules/FindARIA.cmake +++ b/CMakeModules/FindARIA.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindARIA.cmake 4288 2013-06-27 11:56:18Z fspindle $ +# $Id: FindARIA.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindBICLOPS.cmake b/CMakeModules/FindBICLOPS.cmake index 754b4827335521b0373c6a39f08e6756e430df52..c5fa981f26d2d7461835cdeb29552b772a64e671 100644 --- a/CMakeModules/FindBICLOPS.cmake +++ b/CMakeModules/FindBICLOPS.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindBICLOPS.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindBICLOPS.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindCALINUX.cmake b/CMakeModules/FindCALINUX.cmake index 617dcfbdafde4ffeef27ff791f3975fc93586bc1..61571bc05fbb2954f45c6507533fa8a4928bcf74 100644 --- a/CMakeModules/FindCALINUX.cmake +++ b/CMakeModules/FindCALINUX.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindCALINUX.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindCALINUX.cmake 5309 2015-02-11 11:08:15Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -50,6 +50,7 @@ ELSE(NOT UNIX) FIND_PATH(CALINUX_INCLUDE_DIR calinux.h $ENV{CALINUX_HOME}/include /local/soft/Cerebellum/CALinux/current/include + /home/soft/Cerebellum/CALinux/current/include ) #MESSAGE("DBG CALINUX_INCLUDE_DIR=${CALINUX_INCLUDE_DIR}") @@ -58,6 +59,7 @@ ELSE(NOT UNIX) PATHS $ENV{CALINUX_HOME}/lib /local/soft/Cerebellum/CALinux/current/lib + /home/soft/Cerebellum/CALinux/current/lib ) #MESSAGE("DBG CALINUX_LIBRARY=${CALINUX_LIBRARY}") diff --git a/CMakeModules/FindCMU1394.cmake b/CMakeModules/FindCMU1394.cmake index 09a2cbffa08909d875ff549a2d7e8018da6a8d58..a01b813aa29f6c4e3fab31053b9a832433f7336d 100644 --- a/CMakeModules/FindCMU1394.cmake +++ b/CMakeModules/FindCMU1394.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindCMU1394.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindCMU1394.cmake 4603 2014-01-21 13:40:58Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -52,22 +52,40 @@ if(WIN32) ) #MESSAGE("DBG CMU1394_INCLUDE_DIR=${CMU1394_INCLUDE_DIR}") - FIND_LIBRARY(CMU1394_LIBRARY_DEBUG - NAMES 1394camerad - PATHS - $ENV{CMU1394_HOME}/lib - "C:/Program Files (x86)/CMU/1394Camera/lib" - "C:/Program Files/CMU/1394Camera/lib" - ) - #MESSAGE("DBG CMU1394_LIBRARY_DEBUG=${CMU1394_LIBRARY_DEBUG}") + if(CMAKE_CL_64) + FIND_LIBRARY(CMU1394_LIBRARY_DEBUG + NAMES 1394camerad + PATHS + $ENV{CMU1394_HOME}/lib64/x64 + "C:/Program Files (x86)/CMU/1394Camera/lib64/x64" + "C:/Program Files/CMU/1394Camera/lib64/x64" + ) - FIND_LIBRARY(CMU1394_LIBRARY_RELEASE - NAMES 1394camera - PATHS - $ENV{CMU1394_HOME}/lib - "C:/Program Files (x86)/CMU/1394Camera/lib" - "C:/Program Files/CMU/1394Camera/lib" - ) + FIND_LIBRARY(CMU1394_LIBRARY_RELEASE + NAMES 1394camera + PATHS + $ENV{CMU1394_HOME}/lib64/x64 + "C:/Program Files (x86)/CMU/1394Camera/lib64/x64" + "C:/Program Files/CMU/1394Camera/lib64/x64" + ) + else() + FIND_LIBRARY(CMU1394_LIBRARY_DEBUG + NAMES 1394camerad + PATHS + $ENV{CMU1394_HOME}/lib + "C:/Program Files (x86)/CMU/1394Camera/lib" + "C:/Program Files/CMU/1394Camera/lib" + ) + + FIND_LIBRARY(CMU1394_LIBRARY_RELEASE + NAMES 1394camera + PATHS + $ENV{CMU1394_HOME}/lib + "C:/Program Files (x86)/CMU/1394Camera/lib" + "C:/Program Files/CMU/1394Camera/lib" + ) + endif() + #MESSAGE("DBG CMU1394_LIBRARY_DEBUG=${CMU1394_LIBRARY_DEBUG}") #MESSAGE("DBG CMU1394_LIBRARY_RELEASE=${CMU1394_LIBRARY_RELEASE}") set(CMU1394_LIBRARIES "") diff --git a/CMakeModules/FindCPP11.cmake b/CMakeModules/FindCPP11.cmake index 4e45c223cc4fd0dc8af48dd05bb97da42163cd8e..69001799a57ec8763732a3e470c2d7916c2f1772 100644 --- a/CMakeModules/FindCPP11.cmake +++ b/CMakeModules/FindCPP11.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindCPP11.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindCPP11.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindCycabTk.cmake b/CMakeModules/FindCycabTk.cmake deleted file mode 100644 index 6eac6c5b88f9fd3f2be97194647761df8f5527d6..0000000000000000000000000000000000000000 --- a/CMakeModules/FindCycabTk.cmake +++ /dev/null @@ -1,146 +0,0 @@ -############################################################################# -# -# $Id: FindCycabTk.cmake 4056 2013-01-05 13:04:42Z fspindle $ -# -# This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. -# -# This software is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# ("GPL") version 2 as published by the Free Software Foundation. -# See the file LICENSE.txt at the root directory of this source -# distribution for additional information about the GNU GPL. -# -# For using ViSP with software that can not be combined with the GNU -# GPL, please contact INRIA about acquiring a ViSP Professional -# Edition License. -# -# See http://www.irisa.fr/lagadic/visp/visp.html for more information. -# -# This software was developed at: -# INRIA Rennes - Bretagne Atlantique -# Campus Universitaire de Beaulieu -# 35042 Rennes Cedex -# France -# http://www.irisa.fr/lagadic -# -# If you have questions regarding the use of this file, please contact -# INRIA at visp@inria.fr -# -# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -# -# Description: -# Try to find CycabTk toolkit to control the Cycab car-like mobile robot. -# Once run this will define: -# -# CycabTk_FOUND -# CycabTk_INCLUDE_DIRS -# CycabTk_LIBRARIES -# -# Additional var set when the new version of cycabtk exists (to use) -# CycabTk_NEW_FOUND -# CycabTk_NEW_INCLUDE_DIRS -# CycabTk_NEW_LIBRARIES -# -# Additional var set when the old version of cycabtk exists (obsolete) -# CycabTk_OLD_FOUND -# CycabTk_OLD_INCLUDE_DIRS -# CycabTk_OLD_LIBRARIES -# -# Authors: -# Fabien Spindler -# -############################################################################# - -# -# check first if the old version of cycabtk exists -# This old version is to use with the Syndex base low level control of the Cycab -# running on cycab-hf1 computer -# -FIND_PATH(CycabTk_OLD_INCLUDE_DIR EtherCycab/EtherCycab.hpp - $ENV{CycabTk_OLD_HOME}/include - $ENV{HOME}/soft/Cycab/third-party/cycabtk-old/cycabtk-old/include - /local/fspindle/soft/Cycab/third-party/cycabtk-old/cycabtk-old/include -) -#MESSAGE("DBG CycabTk_OLD_INCLUDE_DIR=${CycabTk_OLD_INCLUDE_DIR}") - -FIND_LIBRARY(CycabTk_OLD_LIBRARY - NAMES cycabtk - PATHS - $ENV{CycabTk_OLD_HOME}/lib - $ENV{HOME}/soft/Cycab/third-party/cycabtk-old/cycabtk-old/lib - /local/fspindle/soft/Cycab/third-party/cycabtk-old/cycabtk-old/lib -) -#MESSAGE("DBG CycabTk_OLD_LIBRARY=${CycabTk_OLD_LIBRARY}") - - -IF(CycabTk_OLD_INCLUDE_DIR AND CycabTk_OLD_LIBRARY) - SET(CycabTk_OLD_FOUND TRUE) - SET(CycabTk_OLD_INCLUDE_DIRS ${CycabTk_OLD_INCLUDE_DIR}) - SET(CycabTk_OLD_LIBRARIES ${CycabTk_OLD_LIBRARY}) -ELSE(CycabTk_OLD_INCLUDE_DIR AND CycabTk_OLD_LIBRARY) - SET(CycabTk_OLD_FOUND FALSE) -ENDIF(CycabTk_OLD_INCLUDE_DIR AND CycabTk_OLD_LIBRARY) - -# -# check secondly if the new version of cycabtk exists -# This new version is to use with the C base low level control of the Cycab -# throw the Peak CAN dongle -# -# --- Check library dependency -include(FindPkgConfig) - -set( Boost_USE_MULTITHREADED ON ) -find_package( Boost COMPONENTS serialization) - -FIND_LIBRARY(HUGR_LIBRARY - NAMES hugr - PATHS - /usr/lib - /usr/local/lib -) - -IF(Boost_SERIALIZATION_FOUND AND HUGR_LIBRARY) - SET(CycabTk_NEW_FOUND TRUE) - SET(CycabTk_NEW_LIBRARIES ${HUGR_LIBRARY} ${Boost_SERIALIZATION_LIBRARIES}) -ELSE(Boost_SERIALIZATION_FOUND AND HUGR_LIBRARY) - SET(CycabTk_NEW_FOUND FALSE) -ENDIF(Boost_SERIALIZATION_FOUND AND HUGR_LIBRARY) - -#MESSAGE("CycabTk new: ${CycabTk_NEW_FOUND} old: ${CycabTk_OLD_FOUND}") - -IF(CycabTk_NEW_FOUND AND CycabTk_OLD_FOUND) - OPTION(USE_CYCAB_WITH_OLD_CYCABTK "Use the old and obsolete CycabTk" FALSE) -ENDIF(CycabTk_NEW_FOUND AND CycabTk_OLD_FOUND) - -IF(USE_CYCAB_WITH_OLD_CYCABTK) - # either old or new cycabtk is detected - SET(CycabTk_FOUND TRUE) - SET(CycabTk_NEW_FOUND FALSE) - SET(CycabTk_INCLUDE_DIRS ${CycabTk_OLD_INCLUDE_DIRS}) - SET(CycabTk_LIBRARIES ${CycabTk_OLD_LIBRARIES}) -ELSE(USE_CYCAB_WITH_OLD_CYCABTK) - IF(CycabTk_NEW_FOUND) - SET(CycabTk_FOUND TRUE) - SET(CycabTk_OLD_FOUND FALSE) - SET(CycabTk_LIBRARIES ${CycabTk_NEW_LIBRARIES}) - ELSE(CycabTk_NEW_FOUND) - IF(CycabTk_OLD_FOUND) - SET(CycabTk_FOUND TRUE) - SET(CycabTk_INCLUDE_DIRS ${CycabTk_OLD_INCLUDE_DIRS}) - SET(CycabTk_LIBRARIES ${CycabTk_OLD_LIBRARIES}) - ENDIF(CycabTk_OLD_FOUND) - ENDIF(CycabTk_NEW_FOUND) -ENDIF(USE_CYCAB_WITH_OLD_CYCABTK) - -MARK_AS_ADVANCED( - CycabTk_INCLUDE_DIR - CycabTk_LIBRARIES - CycabTk_NEW_INCLUDE_DIR - CycabTk_NEW_LIBRARIES - CycabTk_OLD_INCLUDE_DIR - CycabTk_OLD_LIBRARY - CycabTk_OLD_LIBRARIES - HUGR_LIBRARY -) \ No newline at end of file diff --git a/CMakeModules/FindDC1394.cmake b/CMakeModules/FindDC1394.cmake index 797a754534984d71b968889cc30efcb9f7800577..01b4db77f9e374b8a286de9d60409aa7ea949e85 100644 --- a/CMakeModules/FindDC1394.cmake +++ b/CMakeModules/FindDC1394.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindDC1394.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindDC1394.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -36,15 +36,8 @@ # Once run this will define: # # DC1394_FOUND -# DC1394_1_FOUND -# DC1394_2_FOUND -# DC1394_VERSION # DC1394_INCLUDE_DIRS -# DC1394_1_INCLUDE_DIRS -# DC1394_2_INCLUDE_DIRS # DC1394_LIBRARIES -# DC1394_1_LIBRARY -# DC1394_2_LIBRARY # # The two defines below are only useful to compile with libdc1394-2.x. In # that case DC1394_VERSION=2. Since the libdc1394-2.x API is not stable, we @@ -64,53 +57,24 @@ IF(NOT UNIX) # MESSAGE("FindDC1394.cmake: libdc1394 only available for Unix.") SET(DC1394_FOUND FALSE) -ELSE(NOT UNIX) -# Search for libdc1394-1.x - - FIND_PATH(DC1394_1_INCLUDE_DIR libdc1394/dc1394_control.h - $ENV{DC1394_HOME}/include - $ENV{DC1394_DIR}/include - /usr/include ) -#MESSAGE("DBG DC1394_1_INCLUDE_DIR=${DC1394_1_INCLUDE_DIR}") - - FIND_LIBRARY(DC1394_1_LIBRARY - NAMES dc1394_control - PATHS - $ENV{DC1394_HOME}/lib - $ENV{DC1394_DIR}/lib - /usr/lib - ) -#MESSAGE("DBG DC1394_1_LIBRARY=${DC1394_LIBRARY}") - - IF(DC1394_1_LIBRARY AND DC1394_1_INCLUDE_DIR) - SET(DC1394_FOUND TRUE) - SET(DC1394_1_FOUND TRUE) - SET(DC1394_VERSION 1) - SET(DC1394_LIBRARIES ${DC1394_1_LIBRARY}) - SET(DC1394_1_INCLUDE_DIRS ${DC1394_1_INCLUDE_DIR}) - SET(DC1394_INCLUDE_DIRS ${DC1394_1_INCLUDE_DIRS}) - ELSE(DC1394_1_LIBRARY AND DC1394_1_INCLUDE_DIR) - SET(DC1394_FOUND FALSE) - SET(DC1394_1_FOUND FALSE) - ENDIF(DC1394_1_LIBRARY AND DC1394_1_INCLUDE_DIR) - +ELSE(NOT UNIX) # Search for libdc1394-2.x - FIND_PATH(DC1394_2_INCLUDE_DIR dc1394/control.h + FIND_PATH(DC1394_INCLUDE_DIR dc1394/control.h $ENV{DC1394_HOME}/include $ENV{DC1394_DIR}/include /usr/include ) - #MESSAGE("DBG DC1394_2_INCLUDE_DIR=${DC1394_2_INCLUDE_DIR}") + #MESSAGE("DBG DC1394_INCLUDE_DIR=${DC1394_INCLUDE_DIR}") - FIND_LIBRARY(DC1394_2_LIBRARY + FIND_LIBRARY(DC1394_LIBRARY NAMES dc1394 PATHS $ENV{DC1394_HOME}/lib $ENV{DC1394_DIR}/lib /usr/lib ) - #MESSAGE("DBG DC1394_2_LIBRARY=${DC1394_2_LIBRARY}") + #MESSAGE("DBG DC1394_LIBRARY=${DC1394_LIBRARY}") - IF(DC1394_2_LIBRARY AND DC1394_2_INCLUDE_DIR) + IF(DC1394_LIBRARY AND DC1394_INCLUDE_DIR) # Since the libdc1394-2.x API is not stable, try to compile a # sample code to determine if we have to use dc1394_find_cameras() or @@ -119,8 +83,8 @@ ELSE(NOT UNIX) include(CheckCXXSourceCompiles) - SET(CMAKE_REQUIRED_LIBRARIES ${DC1394_2_LIBRARY}) - SET(CMAKE_REQUIRED_INCLUDES ${DC1394_2_INCLUDE_DIR}) + SET(CMAKE_REQUIRED_LIBRARIES ${DC1394_LIBRARY}) + SET(CMAKE_REQUIRED_INCLUDES ${DC1394_INCLUDE_DIR}) CHECK_CXX_SOURCE_COMPILES(" #include <dc1394/control.h> @@ -153,28 +117,23 @@ ELSE(NOT UNIX) ENDIF(NOT DC1394_CAMERA_ENUMERATE_FOUND) IF(NOT DC1394_CAMERA_ENUMERATE_FOUND AND NOT DC1394_FIND_CAMERAS_FOUND) - SET(DC1394_2_FOUND FALSE) + SET(DC1394_FOUND FALSE) MESSAGE("libdc1394-2.x found but not compatible with ViSP...") ELSE(NOT DC1394_CAMERA_ENUMERATE_FOUND AND NOT DC1394_FIND_CAMERAS_FOUND) SET(DC1394_FOUND TRUE) - SET(DC1394_2_FOUND TRUE) ENDIF(NOT DC1394_CAMERA_ENUMERATE_FOUND AND NOT DC1394_FIND_CAMERAS_FOUND) - SET(DC1394_VERSION 2) - SET(DC1394_LIBRARIES ${DC1394_2_LIBRARY}) - SET(DC1394_2_INCLUDE_DIRS ${DC1394_2_INCLUDE_DIR}) - SET(DC1394_INCLUDE_DIRS ${DC1394_2_INCLUDE_DIRS}) - ENDIF(DC1394_2_LIBRARY AND DC1394_2_INCLUDE_DIR) + SET(DC1394_LIBRARIES ${DC1394_LIBRARY}) + SET(DC1394_INCLUDE_DIRS ${DC1394_INCLUDE_DIR}) + ENDIF(DC1394_LIBRARY AND DC1394_INCLUDE_DIR) ## -------------------------------- MARK_AS_ADVANCED( - DC1394_1_LIBRARY - DC1394_1_INCLUDE_DIR - DC1394_2_LIBRARY - DC1394_2_INCLUDE_DIR + DC1394_LIBRARY + DC1394_INCLUDE_DIR DC1394_INCLUDE_DIR DC1394_LIBRARIES DC1394_LIBRARY diff --git a/CMakeModules/FindDIRECT3D.cmake b/CMakeModules/FindDIRECT3D.cmake index f6bb4a4f8b2e58eaa515037f116991205ee8bf5a..18eb6a173ea0096faeaadbc7d42e24b1cb87f5f4 100755 --- a/CMakeModules/FindDIRECT3D.cmake +++ b/CMakeModules/FindDIRECT3D.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindDIRECT3D.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindDIRECT3D.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindDIRECTSHOW.cmake b/CMakeModules/FindDIRECTSHOW.cmake index 7d3d62602e83a4f77ee89f7d45c9970525a55072..95ecea8eacb0fb0fab8850a251c79d8eaaead042 100644 --- a/CMakeModules/FindDIRECTSHOW.cmake +++ b/CMakeModules/FindDIRECTSHOW.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindDIRECTSHOW.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindDIRECTSHOW.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindDL.cmake b/CMakeModules/FindDL.cmake index 62b5704672ae52c89631baee69fca5532c0ac28c..f62ab6b041f3c02d2e972fcb5f785341a7d470bb 100644 --- a/CMakeModules/FindDL.cmake +++ b/CMakeModules/FindDL.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindDL.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindDL.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/TargetDistclean.cmake b/CMakeModules/FindDMTX.cmake similarity index 64% rename from CMakeModules/TargetDistclean.cmake rename to CMakeModules/FindDMTX.cmake index c46819a2237e8ce078bf439398b616b32f9784c9..db31d64b4e146c1fb9aab7312e6206434ce91bf4 100644 --- a/CMakeModules/TargetDistclean.cmake +++ b/CMakeModules/FindDMTX.cmake @@ -1,64 +1,71 @@ -############################################################################# -# -# $Id: TargetDistclean.cmake 4056 2013-01-05 13:04:42Z fspindle $ -# -# This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. -# -# This software is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# ("GPL") version 2 as published by the Free Software Foundation. -# See the file LICENSE.txt at the root directory of this source -# distribution for additional information about the GNU GPL. -# -# For using ViSP with software that can not be combined with the GNU -# GPL, please contact INRIA about acquiring a ViSP Professional -# Edition License. -# -# See http://www.irisa.fr/lagadic/visp/visp.html for more information. -# -# This software was developed at: -# INRIA Rennes - Bretagne Atlantique -# Campus Universitaire de Beaulieu -# 35042 Rennes Cedex -# France -# http://www.irisa.fr/lagadic -# -# If you have questions regarding the use of this file, please contact -# INRIA at visp@inria.fr -# -# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -# -# Description: -# Add custom target distclean: cleans and removes cmake generated files, etc. -# -# Authors: -# Fabien Spindler -# -############################################################################# - -ADD_CUSTOM_TARGET( distclean DEPENDS clean) - -SET(DISTCLEAN_FILES - cmake.check_cache - */cmake.check_cache - cmake_install.cmake - */cmake_install.cmake - */*/cmake_install.cmake - CMakeCache.txt - core core.* - gmon - *~ - *% - SunWS_cache - ii_files - ) - -## for 1.8.x: -ADD_CUSTOM_COMMAND( - TARGET distclean - COMMAND - ${CMAKE_COMMAND} -E remove ${DISTCLEAN_FILES} - COMMENT - ) +############################################################################# +# +# $Id: FindV4L2.cmake 4574 2014-01-09 08:48:51Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# Try to find dmtx library. +# Once run this will define: +# +# DMTX_FOUND +# DMTX_INCLUDE_DIRS +# DMTX_LIBRARIES +# +# Authors: +# Fabien Spindler +# +############################################################################# + + +find_path(DMTX_INCLUDE_DIRS dmtx.h + $ENV{DMTX_DIR}/include + /usr/include + /usr/local/include +) + +find_library(DMTX_LIBRARIES + NAMES dmtx + PATHS + $ENV{DMTX_DIR}/lib + /usr/lib + /usr/local/lib +) + +if(DMTX_INCLUDE_DIRS AND DMTX_LIBRARIES) + set(DMTX_FOUND TRUE) +else() + set(DMTX_FOUND FALSE) +endif() + +mark_as_advanced( + DMTX_INCLUDE_DIRS + DMTX_LIBRARIES +) + diff --git a/CMakeModules/FindFFMPEG.cmake b/CMakeModules/FindFFMPEG.cmake index 017bfc35db169ef5ced5ab790ad2964061fed09b..ff8dea34fb59ec5dcde6bd506b6d62c1c3929bf0 100644 --- a/CMakeModules/FindFFMPEG.cmake +++ b/CMakeModules/FindFFMPEG.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindFFMPEG.cmake 4160 2013-03-12 08:34:49Z fspindle $ +# $Id: FindFFMPEG.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -44,7 +44,88 @@ ############################################################################# # detection of the FFMPEG headers location - FIND_PATH(FFMPEG_INCLUDE_DIR_AVCODEC +if(MINGW) + find_path(FFMPEG_INCLUDE_DIR_AVCODEC + NAMES + libavcodec/avcodec.h + PATHS + "$ENV{MINGW_DIR}/include" + C:/mingw/include + PATH_SUFFIXES + ffmpeg + ) + + find_path(FFMPEG_INCLUDE_DIR_AVFORMAT + NAMES + libavformat/avformat.h + PATHS + "$ENV{MINGW_DIR}/include" + C:/mingw/include + PATH_SUFFIXES + ffmpeg + ) + + find_path(FFMPEG_INCLUDE_DIR_AVUTIL + NAMES + libavutil/avutil.h + PATHS + "$ENV{MINGW_DIR}/include" + C:/mingw/include + PATH_SUFFIXES + ffmpeg + ) + + find_path(FFMPEG_INCLUDE_DIR_SWSCALE + NAMES + libswscale/swscale.h + PATHS + "$ENV{MINGW_DIR}/include" + C:/mingw/include + PATH_SUFFIXES + libswscale + ffmpeg + ) + + # Detection of the FFMPEG library on Unix + find_library(FFMPEG_AVUTIL_LIBRARY + NAMES + avutil + PATHS + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) + find_library(FFMPEG_AVCODEC_LIBRARY + NAMES + avcodec + PATHS + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) + find_library(FFMPEG_AVFORMAT_LIBRARY + NAMES + avformat + PATHS + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) + + find_library(FFMPEG_AVCORE_LIBRARY + NAMES + avcore + PATHS + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) + + find_library(FFMPEG_SWSCALE_LIBRARY + NAMES + swscale + PATHS + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) +else(MINGW) + find_path(FFMPEG_INCLUDE_DIR_AVCODEC NAMES libavcodec/avcodec.h PATHS @@ -56,7 +137,7 @@ ffmpeg ) - FIND_PATH(FFMPEG_INCLUDE_DIR_AVFORMAT + find_path(FFMPEG_INCLUDE_DIR_AVFORMAT NAMES libavformat/avformat.h PATHS @@ -68,7 +149,7 @@ ffmpeg ) - FIND_PATH(FFMPEG_INCLUDE_DIR_AVUTIL + find_path(FFMPEG_INCLUDE_DIR_AVUTIL NAMES libavutil/avutil.h PATHS @@ -80,7 +161,7 @@ ffmpeg ) - FIND_PATH(FFMPEG_INCLUDE_DIR_SWSCALE + find_path(FFMPEG_INCLUDE_DIR_SWSCALE NAMES libswscale/swscale.h PATHS @@ -94,7 +175,7 @@ ) # Detection of the FFMPEG library on Unix - FIND_LIBRARY(FFMPEG_AVUTIL_LIBRARY + find_library(FFMPEG_AVUTIL_LIBRARY NAMES avutil PATHS @@ -105,7 +186,7 @@ $ENV{FFMPEG_DIR}/Release $ENV{FFMPEG_DIR} ) - FIND_LIBRARY(FFMPEG_AVCODEC_LIBRARY + find_library(FFMPEG_AVCODEC_LIBRARY NAMES avcodec PATHS @@ -116,7 +197,7 @@ $ENV{FFMPEG_DIR}/Release $ENV{FFMPEG_DIR} ) - FIND_LIBRARY(FFMPEG_AVFORMAT_LIBRARY + find_library(FFMPEG_AVFORMAT_LIBRARY NAMES avformat PATHS @@ -128,7 +209,7 @@ $ENV{FFMPEG_DIR} ) - FIND_LIBRARY(FFMPEG_AVCORE_LIBRARY + find_library(FFMPEG_AVCORE_LIBRARY NAMES avcore PATHS @@ -140,7 +221,7 @@ $ENV{FFMPEG_DIR} ) - FIND_LIBRARY(FFMPEG_SWSCALE_LIBRARY + find_library(FFMPEG_SWSCALE_LIBRARY NAMES swscale PATHS @@ -151,6 +232,7 @@ $ENV{FFMPEG_DIR}/Release $ENV{FFMPEG_DIR} ) +endif(MINGW) # FFMpeg depend son Zlib FIND_PACKAGE(ZLIB) @@ -196,6 +278,29 @@ IF(FFMPEG_INCLUDE_DIR_AVCODEC AND FFMPEG_INCLUDE_DIR_AVFORMAT AND FFMPEG_INCLUDE list(APPEND FFMPEG_LIBRARIES ${ICONV_LIBRARIES}) endif() +elseif(MINGW AND FFMPEG_INCLUDE_DIR_AVCODEC AND FFMPEG_INCLUDE_DIR_AVFORMAT AND FFMPEG_INCLUDE_DIR_AVUTIL AND FFMPEG_INCLUDE_DIR_SWSCALE AND FFMPEG_SWSCALE_LIBRARY AND FFMPEG_AVFORMAT_LIBRARY AND FFMPEG_AVCODEC_LIBRARY AND FFMPEG_AVUTIL_LIBRARY AND ZLIB_LIBRARIES) + # Bzip2 is nor requested with mingw-w64 + SET(FFMPEG_FOUND TRUE) + SET(FFMPEG_INCLUDE_DIRS + ${FFMPEG_INCLUDE_DIR_AVCODEC} + ${FFMPEG_INCLUDE_DIR_AVFORMAT} + ${FFMPEG_INCLUDE_DIR_AVUTIL} + ${FFMPEG_INCLUDE_DIR_SWSCALE} + ) + SET(FFMPEG_LIBRARIES + ${FFMPEG_SWSCALE_LIBRARY} + ${FFMPEG_AVFORMAT_LIBRARY} + ${FFMPEG_AVCODEC_LIBRARY} + ${FFMPEG_AVUTIL_LIBRARY} + ) + if(FFMPEG_AVCORE_LIBRARY) + LIST(APPEND FFMPEG_LIBRARIES ${FFMPEG_AVCORE_LIBRARY}) + endif() + list(APPEND FFMPEG_LIBRARIES ${ZLIB_LIBRARIES}) + if(ICONV_FOUND) + list(APPEND FFMPEG_LIBRARIES ${ICONV_LIBRARIES}) + endif() + ELSE() SET(FFMPEG_FOUND FALSE) ENDIF () diff --git a/CMakeModules/FindGDI.cmake b/CMakeModules/FindGDI.cmake index c39a3e1c92335cd11d846a217063568da9e33461..3db6543c709aac1b83e9467a282ed1bff0c66a74 100644 --- a/CMakeModules/FindGDI.cmake +++ b/CMakeModules/FindGDI.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindGDI.cmake 4293 2013-07-01 16:05:01Z fspindle $ +# $Id: FindGDI.cmake 5286 2015-02-09 14:36:35Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -49,6 +49,9 @@ IF(WIN32) IF(MINGW) FIND_LIBRARY(GDI_LIBRARY gdi32 "C:/MinGW/lib" + "C:/mingw/mingw/lib" + "$ENV{MINGW_DIR}/lib" + "$ENV{MINGW_DIR}/mingw/lib" DOC "Where can the GDI (Graphics Device Interface) library be found" NO_DEFAULT_PATH ) @@ -59,15 +62,17 @@ IF(WIN32) "$ENV{WINSDK_DIR}/Lib/x64" "$ENV{WINSDK_HOME}/Lib/x64" "$ENV{DXSDK_DIR}/Lib/x64" - "C:/Program Files/Microsoft SDKs/Windows/v6.1/Lib/x64" "C:/Program Files/Microsoft SDKs/Windows/v6.0/Lib/x64" + "C:/Program Files/Microsoft SDKs/Windows/v6.0a/Lib/x64" + "C:/Program Files/Microsoft SDKs/Windows/v6.1/Lib/x64" "C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib/x64" "C:/Program Files/Microsoft SDKs/Windows/v7.1/Lib/x64" + "C:/Program Files/Microsoft SDKs/Windows/v7.1A/Lib/x64" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib/x64" + "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib/x64" + "C:/Program Files (x86)/Windows Kits/8.0/Lib/win8/um/x64" "C:/Program Files/Microsoft Platform SDK/Lib/x64" "C:/DXSDK/Include/Lib/x64" - "C:/Program Files/Microsoft SDKs/Windows/v6.0a/Lib/x64" - "C:/Program Files (x86)/Windows Kits/8.0/Lib/win8/um/x64" DOC "Where can the GDI (Graphics Device Interface) library be found" ) @@ -102,15 +107,16 @@ IF(WIN32) "$ENV{WINSDK_DIR}/Lib" "$ENV{WINSDK_HOME}/Lib" "$ENV{DXSDK_DIR}/Lib" - "C:/Program Files/Microsoft SDKs/Windows/v6.1/Lib" + "C:/Program Files/Microsoft SDKs/Windows/v6.0a/Lib" "C:/Program Files/Microsoft SDKs/Windows/v6.0/Lib" + "C:/Program Files/Microsoft SDKs/Windows/v6.1/Lib" "C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1/Lib" + "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Lib" + "C:/Program Files (x86)/Windows Kits/8.0/Lib/win8/um/x86" "C:/Program Files/Microsoft Platform SDK/Lib" "C:/DXSDK/Include/Lib" - "C:/Program Files/Microsoft SDKs/Windows/v6.0a/Lib" - "C:/Program Files (x86)/Windows Kits/8.0/Lib/win8/um/x86" DOC "Where can the GDI (Graphics Device Interface) library be found" ) diff --git a/CMakeModules/FindGSL.cmake b/CMakeModules/FindGSL.cmake index 406888c77fd695dfc82400de4ab70d24b078b6de..91a0801ee17bb04d460ff46d2a262e45e5626004 100644 --- a/CMakeModules/FindGSL.cmake +++ b/CMakeModules/FindGSL.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindGSL.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindGSL.cmake 4815 2014-07-31 14:00:05Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -35,201 +35,159 @@ # (see http://www.gnu.org/software/gsl/) # Once run this will define: # -# GSL_FOUND = system has GSL lib -# -# GSL_LIBRARIES = full path to the libraries -# on Unix/Linux with additional linker flags from "gsl-config --libs" -# -# GSL_INCLUDE_DIRS = where to find headers -# -# GSL_LINK_DIRECTORIES = link directories, useful for rpath on Unix -# GSL_EXE_LINKER_FLAGS = rpath on Unix +# GSL_FOUND : system has GSL lib +# GSL_LIBRARIES : full path to the libraries +# GSL_INCLUDE_DIRS : where to find headers # # Authors: # Fabien Spindler # ############################################################################# -IF(WIN32) - FIND_LIBRARY(GSL_gsl_LIBRARY - NAMES gsl - PATHS - "$ENV{GSL_HOME}/lib" - "$ENV{GSL_DIR}/lib" - DOC "Where can the GSL (gsl.lib) library be found" - ) - FIND_LIBRARY(GSL_cblas_LIBRARY - NAMES cblas - PATHS - "$ENV{GSL_HOME}/lib" - "$ENV{GSL_DIR}/lib" - DOC "Where can the GSL (cblas.lib) library be found" - ) - FIND_LIBRARY(GSL_gsl_LIBRARY_DEBUG - NAMES gsl_d - PATHS - "$ENV{GSL_HOME}/lib" - "$ENV{GSL_DIR}/lib" - DOC "Where can the GSL (gsl.lib) library be found" - ) - FIND_LIBRARY(GSL_cblas_LIBRARY_DEBUG - NAMES cblas_d - PATHS - "$ENV{GSL_HOME}/lib" - "$ENV{GSL_DIR}/lib" - DOC "Where can the GSL (cblas.lib) library be found" - ) - SET(GSL_LIBRARIES "optimized" ${GSL_cblas_LIBRARY} - "optimized" ${GSL_gsl_LIBRARY} - "debug" ${GSL_cblas_LIBRARY_DEBUG} - "debug" ${GSL_gsl_LIBRARY_DEBUG}) - - FIND_PATH(GSL_INCLUDE_DIR gsl/gsl_linalg.h - $ENV{GSL_HOME}/include - $ENV{GSL_DIR}/include - ) - - IF(GSL_INCLUDE_DIR AND GSL_gsl_LIBRARY AND GSL_cblas_LIBRARY - AND GSL_gsl_LIBRARY_DEBUG AND GSL_cblas_LIBRARY_DEBUG) - SET(GSL_FOUND TRUE) - ELSE(GSL_INCLUDE_DIR AND GSL_gsl_LIBRARY AND GSL_cblas_LIBRARY - AND GSL_gsl_LIBRARY_DEBUG AND GSL_cblas_LIBRARY_DEBUG) - SET(GSL_FOUND FALSE) - ENDIF(GSL_INCLUDE_DIR AND GSL_gsl_LIBRARY AND GSL_cblas_LIBRARY - AND GSL_gsl_LIBRARY_DEBUG AND GSL_cblas_LIBRARY_DEBUG) - - MARK_AS_ADVANCED( - GSL_gsl_LIBRARY - GSL_cblas_LIBRARY - GSL_gsl_LIBRARY_DEBUG - GSL_cblas_LIBRARY_DEBUG - GSL_INCLUDE_DIR - GSL_LIBRARIES - GSL_LINK_DIRECTORIES - ) -ELSE(WIN32) - IF(UNIX) - FIND_PROGRAM(GSL_CONFIG gsl-config - $ENV{GSL_HOME}/bin - $ENV{GSL_DIR}/bin - /usr/bin - /usr/local/bin - ) - #MESSAGE("DBG GSL_CONFIG ${GSL_CONFIG}") - - IF (GSL_CONFIG) - # set INCLUDE_DIRS to prefix+include - EXEC_PROGRAM(${GSL_CONFIG} - ARGS --prefix - OUTPUT_VARIABLE GSL_PREFIX) - SET(GSL_INCLUDE_DIR ${GSL_PREFIX}/include) - #MESSAGE(STATUS "Using GSL from ${GSL_PREFIX}") - #MESSAGE("GSL_INCLUDE_DIR ${GSL_INCLUDE_DIR}") - - ## extract link lib path and name for rpath - EXEC_PROGRAM(${GSL_CONFIG} - ARGS --libs - OUTPUT_VARIABLE GSL_CONFIG_LIBS ) - #MESSAGE("GSL_CONFIG_LIBS: ${GSL_CONFIG_LIBS}") - - ## split off the link dirs (for rpath) - ## use regular expression to match wildcard equivalent "-L*<endchar>" - ## with <endchar> is a space or a semicolon - STRING(REGEX MATCHALL "[-][L]([^ ;])+" - GSL_LINK_DIRECTORIES_WITH_PREFIX - "${GSL_CONFIG_LIBS}" ) - SET(GSL_LINK_DIRECTORIES "") - - #MESSAGE("DBG GSL_LINK_DIRECTORIES_WITH_PREFIX=${GSL_LINK_DIRECTORIES_WITH_PREFIX}") - - ## remove prefix -L because we need the pure directory for LINK_DIRECTORIES - - IF (GSL_LINK_DIRECTORIES_WITH_PREFIX) - STRING(REGEX REPLACE "[-][L]" "" GSL_LINK_DIRECTORIES ${GSL_LINK_DIRECTORIES_WITH_PREFIX} ) - ENDIF (GSL_LINK_DIRECTORIES_WITH_PREFIX) - ## Check if link directory is empty. This can occur with GSL-1.6 - ## In that case we force the link directory to be ${GSL_PREFIX}/lib - IF(GSL_LINK_DIRECTORIES MATCHES "") - #MESSAGE("DBG GSL_LINK_DIRECTORIES is empty") - SET(GSL_LINK_DIRECTORIES "${GSL_PREFIX}/lib") - ENDIF(GSL_LINK_DIRECTORIES MATCHES "") - - #MESSAGE("DBG GSL_LINK_DIRECTORIES=${GSL_LINK_DIRECTORIES}") -# IF(NOT APPLE) -# SET(GSL_EXE_LINKER_FLAGS "-Wl,-rpath,${GSL_LINK_DIRECTORIES}") -# ENDIF(NOT APPLE) - - #MESSAGE("DBG GSL_EXE_LINKER_FLAGS=${GSL_EXE_LINKER_FLAGS}") - ## use regular expression to match wildcard equivalent "-l*<endchar>" - ## with <endchar> is a space or a semicolon - STRING(REGEX MATCHALL "[-][l]([^ ;])+" - GSL_LINK_LIBRARIES - "${GSL_CONFIG_LIBS}" ) - #MESSAGE("DBG GSL_LINK_LIBRARIES=${GSL_LINK_LIBRARIES}") - # Because in GSL_LINK_LIBRARIES defs are separated by ";", parse the - # GSL_LINK_LIBRARIES in order to build a space separated string - #message(GSL_LINK_LIBRARIES ${GSL_LINK_LIBRARIES}) - SET(GSL_LIB_ONES "") - FOREACH(libs ${GSL_LINK_LIBRARIES}) - STRING(REGEX REPLACE "[-][l]" "" GSL_LIB_ONES ${libs}) - #MESSAGE("GSL_LIB_ONES=${GSL_LIB_ONES} - ${libs}") - MARK_AS_ADVANCED(LIBGSL_${GSL_LIB_ONES}) - FIND_LIBRARY(LIBGSL_${GSL_LIB_ONES} - NAMES ${GSL_LIB_ONES} - PATHS - ${GSL_LINK_DIRECTORIES} - /usr/lib - /usr/local/lib - ) - #MESSAGE("LIBGSL_${GSL_LIB_ONES} ${LIBGSL_${GSL_LIB_ONES}}") - IF(LIBGSL_${GSL_LIB_ONES}) - #MESSAGE("LIB_GSL=${LIB_GSL} - ${libs}") - LIST(APPEND GSL_LIBRARIES ${LIBGSL_${GSL_LIB_ONES}}) - ENDIF() - ENDFOREACH(libs) +# it seems that a macro() can't accept a list as input. That's why we have +# MY_LIBRARY1 and MY_LIBRARY2 +macro(CheckCompilation_gsl2 MY_INCLUDE_DIR MY_LIBRARY1 MY_LIBRARY2 MY_BUILD_SUCCEED2) + # The material is found. Check if it works on the requested architecture + include(CheckCXXSourceCompiles) + #message("Test compilation: ${MY_INCLUDE_DIR} + ${MY_LIBRARY1} + ${MY_LIBRARY2}") - #MESSAGE("GSL_LIBRARIES=${GSL_LIBRARIES}") - - #ELSE(GSL_CONFIG) - # MESSAGE("FindGSL.cmake: gsl-config not found. Please set it manually. GSL_CONFIG=${GSL_CONFIG}") - ENDIF(GSL_CONFIG) + set(CMAKE_REQUIRED_LIBRARIES ${MY_LIBRARY1} ${MY_LIBRARY2}) + set(CMAKE_REQUIRED_INCLUDES ${MY_INCLUDE_DIR}) + check_cxx_source_compiles(" + #include <gsl/gsl_linalg.h> // Contrib for GSL library + #include <gsl/gsl_math.h> + #include <gsl/gsl_eigen.h> + int main() + { + gsl_matrix *A2 = gsl_matrix_alloc(6, 6) ; + } + " BUILD_SUCCEED2) + #message("BUILD_SUCCEED: ${BUILD_SUCCEED}") + set(${MY_BUILD_SUCCEED2} ${BUILD_SUCCEED2}) +endmacro() - IF(GSL_INCLUDE_DIR AND GSL_LIBRARIES) +macro(CheckCompilation_gsl1 MY_INCLUDE_DIR MY_LIBRARY MY_BUILD_SUCCEED) # The material is found. Check if it works on the requested architecture include(CheckCXXSourceCompiles) - - SET(CMAKE_REQUIRED_LIBRARIES ${GSL_LIBRARIES}) - SET(CMAKE_REQUIRED_INCLUDES ${GSL_INCLUDE_DIR}) - CHECK_CXX_SOURCE_COMPILES(" + #message("Test compilation: ${MY_INCLUDE_DIR} + ${MY_LIBRARY}") + + set(CMAKE_REQUIRED_LIBRARIES ${MY_LIBRARY}) + set(CMAKE_REQUIRED_INCLUDES ${MY_INCLUDE_DIR}) + check_cxx_source_compiles(" #include <gsl/gsl_linalg.h> // Contrib for GSL library #include <gsl/gsl_math.h> #include <gsl/gsl_eigen.h> - int main() + int main() { - gsl_matrix *A = gsl_matrix_alloc(6, 6) ; + gsl_matrix *A1 = gsl_matrix_alloc(6, 6) ; } - " GSL_BUILD_TEST) - #MESSAGE("GSL_BUILD_TEST: ${GSL_BUILD_TEST}") - IF(GSL_BUILD_TEST) - SET(GSL_INCLUDE_DIRS ${GSL_INCLUDE_DIR}) - SET(GSL_LIBRARIES ${GSL_LIBRARIES}) - SET(GSL_FOUND TRUE) - ELSE() - SET(GSL_FOUND FALSE) - #MESScAGE("libgsl library found but not compatible with architecture.") - ENDIF() - ELSE(GSL_INCLUDE_DIR AND GSL_LIBRARIES) - SET(GSL_FOUND FALSE) - ENDIF(GSL_INCLUDE_DIR AND GSL_LIBRARIES) + " BUILD_SUCCEED) + #message("BUILD_SUCCEED: ${BUILD_SUCCEED}") + set(${MY_BUILD_SUCCEED} ${BUILD_SUCCEED}) +endmacro() + +find_path(GSL_INCLUDE_DIR gsl/gsl_linalg.h + $ENV{GSL_HOME}/include + $ENV{GSL_DIR}/include + /usr/include + /usr/local/include +) + +find_library(GSL_gsl_LIBRARY + NAMES gsl + PATHS + "$ENV{GSL_HOME}/lib" + "$ENV{GSL_DIR}/lib" + /usr/lib + /usr/local/lib +) + +if(WIN32) + find_library(GSL_cblas_LIBRARY + NAMES cblas + PATHS + "$ENV{GSL_HOME}/lib" + "$ENV{GSL_DIR}/lib" + /usr/lib + /usr/local/lib + ) - MARK_AS_ADVANCED( - GSL_INCLUDE_DIR - GSL_INCLUDE_DIRS - GSL_LIBRARIES - GSL_LINK_DIRECTORIES - GSL_CONFIG + find_library(GSL_gsl_LIBRARY_DEBUG + NAMES gsl_d + PATHS + "$ENV{GSL_HOME}/lib" + "$ENV{GSL_DIR}/lib" + /usr/lib + /usr/local/lib ) + find_library(GSL_cblas_LIBRARY_DEBUG + NAMES cblas_d + PATHS + "$ENV{GSL_HOME}/lib" + "$ENV{GSL_DIR}/lib" + /usr/lib + /usr/local/lib + ) + + if(GSL_INCLUDE_DIR AND GSL_gsl_LIBRARY AND GSL_cblas_LIBRARY + AND GSL_gsl_LIBRARY_DEBUG AND GSL_cblas_LIBRARY_DEBUG) + set(GSL_LIBRARIES "optimized" ${GSL_cblas_LIBRARY} + "optimized" ${GSL_gsl_LIBRARY} + "debug" ${GSL_cblas_LIBRARY_DEBUG} + "debug" ${GSL_gsl_LIBRARY_DEBUG}) + set(GSL_INCLUDE_DIRS ${GSL_INCLUDE_DIR}) + set(GSL_FOUND TRUE) + else() + set(GSL_FOUND FALSE) + endif() + + mark_as_advanced( + GSL_gsl_LIBRARY_DEBUG + GSL_cblas_LIBRARY_DEBUG + ) +else() + if(GSL_INCLUDE_DIR AND GSL_gsl_LIBRARY) + # Check if gsl library is sufficient + set(GSL_INCLUDE_DIRS ${GSL_INCLUDE_DIR}) + set(GSL_LIBRARIES ${GSL_gsl_LIBRARY}) + + CheckCompilation_gsl1(${GSL_INCLUDE_DIRS} ${GSL_gsl_LIBRARY} BUILD_SUCCEED1) + #message("BUILD_STATUS 1: ${BUILD_SUCCEED1}") + if(BUILD_SUCCEED1) + set(GSL_FOUND TRUE) + else() + # Try to add gslcblas library if requested + + find_library(GSL_cblas_LIBRARY + NAMES gslcblas + PATHS + "$ENV{GSL_HOME}/lib" + "$ENV{GSL_DIR}/lib" + /usr/lib + /usr/local/lib + ) + if(GSL_cblas_LIBRARY) + list(APPEND GSL_LIBRARIES ${GSL_cblas_LIBRARY}) + #message("add cblas to GSL_LIBRARIES: ${GSL_LIBRARIES}") + CheckCompilation_gsl2(${GSL_INCLUDE_DIRS} ${GSL_gsl_LIBRARY} ${GSL_cblas_LIBRARY} BUILD_SUCCEED2) + #message("BUILD_STATUS 2: ${BUILD_SUCCEED2}") + if(BUILD_SUCCEED2) + set(GSL_FOUND TRUE) + else() + set(GSL_FOUND FALSE) + endif() + else() + set(GSL_FOUND FALSE) + endif() + endif() + else() + set(GSL_FOUND FALSE) + endif() +endif() - ENDIF(UNIX) -ENDIF(WIN32) +mark_as_advanced( + GSL_gsl_LIBRARY + GSL_cblas_LIBRARY + GSL_INCLUDE_DIR +) diff --git a/CMakeModules/FindICONV.cmake b/CMakeModules/FindICONV.cmake index 23579cf161c627e4c91bc2669d634533a4dd52c9..2a5ac18c0c7572956ccec1cac87acc3fa113ec33 100755 --- a/CMakeModules/FindICONV.cmake +++ b/CMakeModules/FindICONV.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindICONV.cmake 4160 2013-03-12 08:34:49Z fspindle $ +# $Id: FindICONV.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,36 +43,46 @@ # ############################################################################# - -FIND_PATH(ICONV_INCLUDE_DIR iconv.h +if(MINGW) + find_path(ICONV_INCLUDE_DIR iconv.h + "$ENV{MINGW_DIR}/include" + C:/mingw/include + ) + find_library(ICONV_LIBRARY iconv + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) +else() + find_path(ICONV_INCLUDE_DIR iconv.h $ENV{ICONV_DIR}/include $ENV{ICONV_HOME}/include $ENV{XML2_DIR}/include $ENV{XML2_HOME}/include "c:/libxml2/include" "c:/iconv/include" -) -FIND_LIBRARY(ICONV_LIBRARY iconv + ) + find_library(ICONV_LIBRARY iconv $ENV{ICONV_DIR}/lib $ENV{ICONV_HOME}/lib $ENV{XML2_DIR}/lib $ENV{XML2_HOME}/lib "c:/libxml2/lib" "c:/iconv/lib" -) + ) +endif() -IF(ICONV_LIBRARY) +if(ICONV_LIBRARY) SET(ICONV_LIBRARIES ${ICONV_LIBRARY}) -ENDIF(ICONV_LIBRARY) +endif() -IF(ICONV_LIBRARIES AND ICONV_INCLUDE_DIR) +if(ICONV_LIBRARIES AND ICONV_INCLUDE_DIR) SET(ICONV_INCLUDE_DIRS ${ICONV_INCLUDE_DIR}) SET(ICONV_FOUND TRUE) -ELSE(ICONV_LIBRARIES AND ICONV_INCLUDE_DIR) +else() SET(ICONV_FOUND FALSE) -ENDIF(ICONV_LIBRARIES AND ICONV_INCLUDE_DIR) +endif() -MARK_AS_ADVANCED( +mark_as_advanced( ICONV_INCLUDE_DIR ICONV_LIBRARIES ICONV_LIBRARY diff --git a/CMakeModules/FindIRISA.cmake b/CMakeModules/FindIRISA.cmake index fdb37f3377fd7e850032c7ee1aa250011491ade2..f7d9993d728b40d444e92d40c43a0ac5fe270336 100644 --- a/CMakeModules/FindIRISA.cmake +++ b/CMakeModules/FindIRISA.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindIRISA.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindIRISA.cmake 5309 2015-02-11 11:08:15Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -50,6 +50,7 @@ ELSE(NOT UNIX) FIND_PATH(IRISA_INCLUDE_DIR irisa_Afma6.h $ENV{IRISA_HOME}/include /local/soft/Cerebellum/Irisa/current/src + /home/soft/Cerebellum/Irisa/current/src ) #MESSAGE("DBG IRISA_INCLUDE_DIR=${IRISA_INCLUDE_DIR}") @@ -58,6 +59,7 @@ ELSE(NOT UNIX) PATHS $ENV{IRISA_HOME}/lib /local/soft/Cerebellum/Irisa/current/src + /home/soft/Cerebellum/Irisa/current/src ) #MESSAGE("DBG IRISA_LIBRARY=${IRISA_LIBRARY}") diff --git a/CMakeModules/FindLAPACK_C.cmake b/CMakeModules/FindLAPACK_C.cmake index 5deff69bca3ed1ca1178654926f3d41a4ab84618..577944d749c73e69aed98db9dcb6f5e6f1c99c66 100644 --- a/CMakeModules/FindLAPACK_C.cmake +++ b/CMakeModules/FindLAPACK_C.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindLAPACK_C.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindLAPACK_C.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindLIBFREENECT.cmake b/CMakeModules/FindLIBFREENECT.cmake index 0ce94fb1d7cd7ba3f2dad950d4487fac9003a7c4..a9325f88cb81b5ddc5acb5d0e8be35fde4c27ade 100644 --- a/CMakeModules/FindLIBFREENECT.cmake +++ b/CMakeModules/FindLIBFREENECT.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindLIBFREENECT.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindLIBFREENECT.cmake 4695 2014-03-15 11:28:48Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -62,6 +62,8 @@ ELSE() $ENV{LIBFREENECT_HOME}/include /usr/include /usr/local/include + /usr/include/libfreenect + /usr/local/include/libfreenect ) FIND_PATH(LIBFREENECT_H_INCLUDE_DIR libfreenect.h $ENV{LIBFREENECT_HOME}/include diff --git a/CMakeModules/FindLIBUSB-1.cmake b/CMakeModules/FindLIBUSB-1.cmake index 79d57a4164a20e7c4b98005ffb79351e1f490d45..4a9432c0b9b5e744cd6b800240fea896eff90ed1 100644 --- a/CMakeModules/FindLIBUSB-1.cmake +++ b/CMakeModules/FindLIBUSB-1.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindLIBUSB-1.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindLIBUSB-1.cmake 4678 2014-02-19 09:52:35Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -35,6 +35,7 @@ # # LIBUSB_1_FOUND # LIBUSB_1_LIBRARIES +# LIBUSB_1_INCLUDE_DIRS # # Authors: # Celine Teuliere @@ -55,11 +56,18 @@ ELSE(WIN32) /usr/lib /usr/local/lib ) + FIND_PATH(LIBUSB_1_INCLUDE_DIR libusb.h + $ENV{LIBUSB_1_HOME}/include/libusb-1.0 + $ENV{LIBUSB_1_HOME}/build/include/libusb-1.0 + /usr/include/libusb-1.0 + /usr/local/include/libusb-1.0 + ) ENDIF(WIN32) ## -------------------------------- -IF(LIBUSB_1_LIBRARY) +IF(LIBUSB_1_LIBRARY AND LIBUSB_1_INCLUDE_DIR) + SET(LIBUSB_1_INCLUDE_DIRS ${LIBUSB_1_INCLUDE_DIR}) SET(LIBUSB_1_LIBRARIES ${LIBUSB_1_LIBRARY}) SET(LIBUSB_1_FOUND TRUE) ELSE() @@ -67,6 +75,8 @@ ELSE() ENDIF() MARK_AS_ADVANCED( + LIBUSB_1_INCLUDE_DIRS + LIBUSB_1_INCLUDE_DIR LIBUSB_1_LIBRARIES LIBUSB_1_LIBRARY ) diff --git a/CMakeModules/FindMyCoin3D.cmake b/CMakeModules/FindMyCoin3D.cmake index 60c5a28693c028a62cb9361cb6ee2407a3f49741..1cefd1610f2967ca1a308490c68b1244e84f5b81 100644 --- a/CMakeModules/FindMyCoin3D.cmake +++ b/CMakeModules/FindMyCoin3D.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindMyCoin3D.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindMyCoin3D.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindMyGTK2.cmake b/CMakeModules/FindMyGTK2.cmake index a6c95b05a4fce2a3400df7414614680668700bf2..674de22d44067d38bdf862df4f76dd9ddd3e5ba3 100644 --- a/CMakeModules/FindMyGTK2.cmake +++ b/CMakeModules/FindMyGTK2.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindMyGTK2.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindMyGTK2.cmake 4637 2014-02-04 09:48:33Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -69,7 +69,7 @@ IF(UNIX OR WIN32) /usr/lib/i386-linux-gnu/glib-2.0/include /usr/lib/x86_64-linux-gnu/glib-2.0/include /opt/gnome/lib/glib-2.0/include - "C:/GTK/lib/glib-2.0/include"glib + C:/GTK/lib/glib-2.0/include /sw/lib/glib-2.0/include ) @@ -78,7 +78,7 @@ IF(UNIX OR WIN32) $ENV{GTK2_HOME}/include/glib-2.0 /usr/include/glib-2.0 /opt/gnome/include/glib-2.0 - "C:/GTK/include/glib-2.0" + C:/GTK/include/glib-2.0 /sw/include/glib-2.0 ) @@ -87,7 +87,7 @@ IF(UNIX OR WIN32) $ENV{GTK2_HOME}/include/pango-1.0 /usr/include/pango-1.0 /opt/gnome/include/pango-1.0 - "C:/GTK/include/pango-1.0" + C:/GTK/include/pango-1.0 /sw/include/pango-1.0 ) @@ -96,7 +96,7 @@ IF(UNIX OR WIN32) $ENV{GTK2_HOME}/include/cairo /usr/include/cairo /opt/gnome/include/cairo - "C:/GTK/include/cairo" + C:/GTK/include/cairo /sw/include/cairo ) @@ -106,7 +106,7 @@ IF(UNIX OR WIN32) /usr/lib/gtk-2.0/include /usr/lib64/gtk-2.0/include /opt/gnome/lib/gtk-2.0/include - "C:/GTK/lib/gtk-2.0/include" + C:/GTK/lib/gtk-2.0/include /sw/lib/gtk-2.0/include /usr/lib/i386-linux-gnu/gtk-2.0/include /usr/lib/x86_64-linux-gnu/gtk-2.0/include @@ -117,7 +117,7 @@ IF(UNIX OR WIN32) $ENV{GTK2_HOME}/gdk-pixbuf-2.0 /usr/include/gdk-pixbuf-2.0 /usr/include/gtk-2.0 - "C:/GTK/include/gtk-2.0" + C:/GTK/include/gtk-2.0 /sw/include/gtk-2.0 ) @@ -128,7 +128,7 @@ IF(UNIX OR WIN32) $ENV{GTK2_HOME}/include/atk-1.0 /usr/include/atk-1.0 /opt/gnome/include/atk-1.0 - "C:/GTK/include/atk-1.0" + C:/GTK/include/atk-1.0 /sw/include/atk-1.0 ) @@ -141,7 +141,7 @@ IF(UNIX OR WIN32) /usr/openwin/lib /usr/X11R6/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) @@ -154,7 +154,7 @@ IF(UNIX OR WIN32) /usr/openwin/lib /usr/X11R6/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) @@ -167,7 +167,7 @@ IF(UNIX OR WIN32) /usr/openwin/lib /usr/X11R6/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) @@ -180,7 +180,7 @@ IF(UNIX OR WIN32) /usr/openwin/lib /usr/X11R6/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) @@ -193,7 +193,7 @@ IF(UNIX OR WIN32) /usr/openwin/lib /usr/X11R6/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) @@ -203,7 +203,7 @@ IF(UNIX OR WIN32) PATHS $ENV{GTK2_HOME}/lib /usr/lib /opt/gnome/lib - "C:/GTK/lib" + C:/GTK/lib /sw/lib ) diff --git a/CMakeModules/FindMyJPEG.cmake b/CMakeModules/FindMyJPEG.cmake index d62689708bb805ce030c47e2d757da23567110e8..8364005a63d99ef3f5431da49d64c214bfdc9e7e 100644 --- a/CMakeModules/FindMyJPEG.cmake +++ b/CMakeModules/FindMyJPEG.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindMyJPEG.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindMyJPEG.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -45,7 +45,25 @@ # detection of the Libjpeg headers location - FIND_PATH(JPEG_INCLUDE_DIR +if(MINGW) + find_path(JPEG_INCLUDE_DIR + NAMES + jpeglib.h + PATHS + "$ENV{MINGW_DIR}/include" + C:/mingw/include + ) + # Detection of the Libjpeg library on Unix + find_library(JPEG_LIBRARY + NAMES + jpeg libjpeg + PATHS + "$ENV{MINGW_DIR}/lib" + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) +else() + find_path(JPEG_INCLUDE_DIR NAMES jpeglib.h PATHS @@ -55,10 +73,8 @@ $ENV{LIBJPEG_DIR} "C:/Program Files/GnuWin32/include" ) - #MESSAGE("JPEG_INCLUDE_DIR=${JPEG_INCLUDE_DIR}") - # Detection of the Libjpeg library on Unix - FIND_LIBRARY(JPEG_LIBRARY + find_library(JPEG_LIBRARY NAMES jpeg libjpeg PATHS @@ -70,6 +86,8 @@ $ENV{LIBJPEG_DIR} "C:/Program Files/GnuWin32/lib" ) +endif() + #MESSAGE("JPEG_INCLUDE_DIR=${JPEG_INCLUDE_DIR}") #MESSAGE("JPEG_LIBRARY=${JPEG_LIBRARY}") diff --git a/CMakeModules/FindMyPNG.cmake b/CMakeModules/FindMyPNG.cmake index b23f1b9c14f1c9b96cfcaa7ba822694217e4f0ec..1a70f440c49972d067eecf3eb592c359cd97cabc 100644 --- a/CMakeModules/FindMyPNG.cmake +++ b/CMakeModules/FindMyPNG.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindMyPNG.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindMyPNG.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -46,36 +46,55 @@ # detection of the Libpng headers location -FIND_PATH(PNG_INCLUDE_DIR - NAMES - png.h - PATHS - $ENV{LIBPNG_DIR}/include - $ENV{LIBPNG_DIR} - $ENV{LIBPNG_INCLUDE_DIR} - "/usr/include" - "/usr/local/include" - "C:/Program Files/libpng/include" +if(MINGW) + find_path(PNG_INCLUDE_DIR + NAMES + png.h + PATHS + "C:/mingw/include/libpng14" + "$ENV{MINGW_DIR}/include/libpng14" + ) +else() + find_path(PNG_INCLUDE_DIR + NAMES + png.h + PATHS + "$ENV{LIBPNG_DIR}/include" + "$ENV{LIBPNG_DIR}" + "$ENV{LIBPNG_INCLUDE_DIR}" + "/usr/include" + "/usr/local/include" + "C:/Program Files/libpng/include" ) +endif() #MESSAGE("PNG_INCLUDE_DIR=${PNG_INCLUDE_DIR}") -IF(UNIX) +if(UNIX) # Detection of the Libpng library on Unix - FIND_LIBRARY(PNG_LIBRARY + find_library(PNG_LIBRARY NAMES - png15 libpng15 png12 libpng12 png libpng + png15 libpng15 libpng14 png12 libpng12 png libpng PATHS - $ENV{LIBPNG_DIR}/lib - $ENV{LIBPNG_DIR}/Release - $ENV{LIBPNG_DIR} - $ENV{LIBPNG_LIBRARY_DIR} + "$ENV{LIBPNG_DIR}/lib" + "$ENV{LIBPNG_DIR}/Release" + "$ENV{LIBPNG_DIR}" + "$ENV{LIBPNG_LIBRARY_DIR}" /usr/lib /usr/local/lib /lib "C:/Program Files/libpng/lib" ) +elseif(MINGW) + # Detection of the Libpng library on mingw + find_library(PNG_LIBRARY + NAMES + png15 libpng15 libpng14 png12 libpng12 png libpng + PATHS + "C:/mingw/lib64" + "$ENV{MINGW_DIR}/lib64" + ) #MESSAGE("PNG_LIBRARY=${PNG_LIBRARY}") -ELSE(UNIX) +else() FIND_LIBRARY(PNG_LIBRARY_RELEASE NAMES png15 libpng15 png12 libpng12 png libpng @@ -106,7 +125,7 @@ ELSE(UNIX) #MESSAGE("PNG_LIBRARY_DEBUG=${PNG_LIBRARY_DEBUG}") ENDIF(UNIX) ## -------------------------------- - + SET(PNG_FOUND FALSE) FIND_PACKAGE(ZLIB) diff --git a/CMakeModules/FindMyZLIB.cmake b/CMakeModules/FindMyZLIB.cmake index 8cc7163ab1aa8ccf8ef40b0bf868d7e60cdfc274..1d55ae6c7a0f1774d426af96fdd0630ac641ade2 100644 --- a/CMakeModules/FindMyZLIB.cmake +++ b/CMakeModules/FindMyZLIB.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindMyZLIB.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindMyZLIB.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -44,16 +44,23 @@ # Authors: # Fabien Spindler -FIND_PATH(ZLIB_INCLUDE_DIR zlib.h - $ENV{ZLIB_DIR}/include - $ENV{ZLIB_INCLUDE_DIR} - /usr/include - /usr/local/include - "C:/Program Files/zlib/include" - ) +if(MINGW) + find_path(ZLIB_INCLUDE_DIR zlib.h + "$ENV{MINGW_DIR}/include" + C:/mingw/include + ) +else() + find_path(ZLIB_INCLUDE_DIR zlib.h + $ENV{ZLIB_DIR}/include + $ENV{ZLIB_INCLUDE_DIR} + /usr/include + /usr/local/include + "C:/Program Files/zlib/include" + ) +endif() -IF(UNIX) - FIND_LIBRARY(ZLIB_LIBRARY z zlib +if(UNIX) + find_library(ZLIB_LIBRARY z zlib $ENV{ZLIB_DIR}/lib $ENV{ZLIB_LIBRARY_DIR} /lib @@ -63,14 +70,21 @@ IF(UNIX) ) #MESSAGE("ZLIB_LIBRARY=${ZLIB_LIBRARY}") #MESSAGE("ZLIB_INCLUDE_DIRS=${ZLIB_INCLUDE_DIRS}") -ELSE(UNIX) - FIND_LIBRARY(ZLIB_LIBRARY_RELEASE zlib +elseif(MINGW) + find_library(ZLIB_LIBRARY z zlib + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) +else() + find_library(ZLIB_LIBRARY_RELEASE z zlib $ENV{ZLIB_DIR}/lib $ENV{ZLIB_LIBRARY_RELEASE_DIR} /lib /usr/lib /usr/local/lib "C:/Program Files/zlib/lib" + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 ) FIND_LIBRARY(ZLIB_LIBRARY_DEBUG zlibd @@ -83,8 +97,7 @@ ELSE(UNIX) ) #MESSAGE("ZLIB_LIBRARY_RELEASE=${ZLIB_LIBRARY_RELEASE}") #MESSAGE("ZLIB_LIBRARY_DEBUG=${ZLIB_LIBRARY_DEBUG}") - -ENDIF(UNIX) +endif() ## -------------------------------- diff --git a/CMakeModules/FindNAS.cmake b/CMakeModules/FindNAS.cmake index 71324eff0f749c4d9a4ce1508bb221b1d16f724f..4e927af45cde72f647a9de703d35d76bc677a991 100644 --- a/CMakeModules/FindNAS.cmake +++ b/CMakeModules/FindNAS.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindNAS.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindNAS.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindOpenCV2.cmake b/CMakeModules/FindOpenCV2.cmake index 28fe6300567e6dfcada1390ee21aa56bddd80c70..bd9b99d0ca05a0eca1c503532bdbe28d2567d44c 100644 --- a/CMakeModules/FindOpenCV2.cmake +++ b/CMakeModules/FindOpenCV2.cmake @@ -257,7 +257,7 @@ IF(NOT OpenCV_FOUND) "OpenCV required but some headers or libs not found. Please specify it's location with OpenCV_ROOT_DIR env. variable.") ELSE(OpenCV_FIND_REQUIRED) MESSAGE(STATUS - "ERROR: OpenCV was not found.") + "OpenCV not found.") ENDIF(OpenCV_FIND_REQUIRED) ENDIF(NOT OpenCV_FIND_QUIETLY) ENDIF(NOT OpenCV_FOUND) diff --git a/CMakeModules/FindPARPORT.cmake b/CMakeModules/FindPARPORT.cmake index 58f414d86aad8f6f5a822045bbd28448ad609ebe..8450d1ebf136f54a05d43da178547d85f9d7bf4e 100644 --- a/CMakeModules/FindPARPORT.cmake +++ b/CMakeModules/FindPARPORT.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindPARPORT.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindPARPORT.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindPTHREAD.cmake b/CMakeModules/FindPTHREAD.cmake index 0627c6d0a441ff42cc711d47350fd856e80aa720..683d7bf6ba5cdd7b3ad3e55a9589208be9e665dc 100644 --- a/CMakeModules/FindPTHREAD.cmake +++ b/CMakeModules/FindPTHREAD.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindPTHREAD.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindPTHREAD.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,17 +43,29 @@ # ############################################################################# - - FIND_PATH(PTHREAD_INCLUDE_DIR pthread.h +if(MINGW) + find_path(PTHREAD_INCLUDE_DIR pthread.h + "$ENV{MINGW_DIR}/include" + "$ENV{MINGW_DIR}/mingw/include" + C:/mingw/mingw/include + ) + + # pthreadVSE pthreadGCE pthreadGC pthreadVC1 pthreadVC2 are comming from web + find_library(PTHREAD_LIBRARY + NAMES pthread pthreadGC2 pthreadVSE pthreadGCE pthreadGC pthreadVC1 pthreadVC2 + PATHS + "$ENV{MINGW_DIR}/lib" + "$ENV{MINGW_DIR}/mingw/lib" + C:/mingw/mingw/lib + ) +else() + find_path(PTHREAD_INCLUDE_DIR pthread.h "$ENV{PTHREAD_HOME}/include" "$ENV{PTHREAD_DIR}/include" /usr/include - "C:/MinGW/include" ) - #MESSAGE("DBG PTHREAD_INCLUDE_DIR=${PTHREAD_INCLUDE_DIR}") - # pthreadVSE pthreadGCE pthreadGC pthreadVC1 pthreadVC2 are comming from web - FIND_LIBRARY(PTHREAD_LIBRARY + find_library(PTHREAD_LIBRARY NAMES pthread pthreadGC2 pthreadVSE pthreadGCE pthreadGC pthreadVC1 pthreadVC2 PATHS "$ENV{PTHREAD_HOME}/lib" @@ -61,9 +73,9 @@ /usr/lib /usr/local/lib /lib - "C:/MinGW/lib" ) - +endif() + #MESSAGE("DBG PTHREAD_INCLUDE_DIR=${PTHREAD_INCLUDE_DIR}") #MESSAGE(STATUS "DBG PTHREAD_LIBRARY=${PTHREAD_LIBRARY}") ## -------------------------------- diff --git a/CMakeModules/FindPTU46.cmake b/CMakeModules/FindPTU46.cmake index 9960f5d59414630c8d5d6b0f62c9a576e0e3050b..46fdae34b427fef83583be84088e76caf3e56018 100644 --- a/CMakeModules/FindPTU46.cmake +++ b/CMakeModules/FindPTU46.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindPTU46.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindPTU46.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindRAW1394.cmake b/CMakeModules/FindRAW1394.cmake index 1c7a7f754bd1a6bb776e5608dcfb7b6a2bd66880..db33295810f070768da6375e646b15af6d14d8f7 100644 --- a/CMakeModules/FindRAW1394.cmake +++ b/CMakeModules/FindRAW1394.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindRAW1394.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindRAW1394.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindRT.cmake b/CMakeModules/FindRT.cmake index 87d75694b686a413d2ea32b8b891cec861885de0..4554ea78f2e69195f471473578cf1eb1d7722d3a 100644 --- a/CMakeModules/FindRT.cmake +++ b/CMakeModules/FindRT.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindRT.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindRT.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindSOQT.cmake b/CMakeModules/FindSOQT.cmake index f52e23e45fb340ce0c6d5e83c16381440a58417e..ad7d73ca93c1bad1aa1aa6f4aa990af6f2ac6f4e 100644 --- a/CMakeModules/FindSOQT.cmake +++ b/CMakeModules/FindSOQT.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindSOQT.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindSOQT.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindSOWIN.cmake b/CMakeModules/FindSOWIN.cmake index 4c224c4da0b750527d318b31439c723754118968..632fb721711e0d69a531adc18f7e05a5d533401f 100644 --- a/CMakeModules/FindSOWIN.cmake +++ b/CMakeModules/FindSOWIN.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindSOWIN.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindSOWIN.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindSOXT.cmake b/CMakeModules/FindSOXT.cmake index d41d911aa1ad144f8958dcbe6135ba0b330d51ab..2d178304027951366a4d0783d1e55f0b40f031d5 100644 --- a/CMakeModules/FindSOXT.cmake +++ b/CMakeModules/FindSOXT.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindSOXT.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindSOXT.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindV4L2.cmake b/CMakeModules/FindV4L2.cmake index 884dd5c43a9cc12d0f62e45ae14f28e08fe956ef..fe068c3d40298119526aa5f7d7c4e855a6b2ba3c 100644 --- a/CMakeModules/FindV4L2.cmake +++ b/CMakeModules/FindV4L2.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindV4L2.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: FindV4L2.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/FindXML2.cmake b/CMakeModules/FindXML2.cmake index 1e0e73971a8328df49fcdd9142d98f5d71b9d9a7..d03d955a06c5536a158afc46d1050a0c8f38891d 100755 --- a/CMakeModules/FindXML2.cmake +++ b/CMakeModules/FindXML2.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: FindXML2.cmake 4069 2013-01-21 14:28:56Z fspindle $ +# $Id: FindXML2.cmake 5316 2015-02-12 10:58:18Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -44,37 +44,47 @@ ############################################################################# -IF(WIN32) - FIND_PACKAGE(ICONV) - IF(ICONV_FOUND) - FIND_PATH(XML2_INCLUDE_DIR libxml/xmlmemory.h - $ENV{XML2_DIR}/include - $ENV{XML2_DIR}/include/libxml2 - $ENV{XML2_HOME}/include - $ENV{XML2_HOME}/include/libxml2 - ) - FIND_LIBRARY(XML2_LIBRARY libxml2 - $ENV{XML2_DIR}/lib - $ENV{XML2_HOME}/lib +if(WIN32) + find_package(ICONV) + if(MINGW) + find_path(XML2_INCLUDE_DIR libxml/xmlmemory.h + "$ENV{MINGW_DIR}/include/libxml2" + C:/mingw/include/libxml2 + ) + find_library(XML2_LIBRARY libxml2 + "$ENV{MINGW_DIR}/lib" + "$ENV{MINGW_DIR}/lib64" + C:/mingw/lib64 + ) + else() + find_path(XML2_INCLUDE_DIR libxml/xmlmemory.h + "$ENV{XML2_DIR}/include" + "$ENV{XML2_DIR}/include/libxml2" + "$ENV{XML2_HOME}/include" + "$ENV{XML2_HOME}/include/libxml2" + ) + find_library(XML2_LIBRARY libxml2 + "$ENV{XML2_DIR}/lib" + "$ENV{XML2_HOME}/lib" /usr/lib /usr/local/lib "c:/libxml2/lib" - ) - ENDIF(ICONV_FOUND) -ELSE(WIN32) - FIND_PATH(XML2_INCLUDE_DIR libxml/xmlmemory.h - $ENV{XML2_DIR}/include/libxml2 - $ENV{XML2_HOME}/include/libxml2 + ) + endif() +else(WIN32) + find_path(XML2_INCLUDE_DIR libxml/xmlmemory.h + "$ENV{XML2_DIR}/include/libxml2" + "$ENV{XML2_HOME}/include/libxml2" /usr/include/libxml2 /usr/local/include/libxml2 ) - FIND_LIBRARY(XML2_LIBRARY xml2 - $ENV{XML2_DIR}/lib - $ENV{XML2_HOME}/lib + find_library(XML2_LIBRARY xml2 + "$ENV{XML2_DIR}/lib" + "$ENV{XML2_HOME}/lib" /usr/lib /usr/local/lib ) -ENDIF(WIN32) +endif(WIN32) #MESSAGE("DBG XML2_INCLUDE_DIR=${XML2_INCLUDE_DIR}") @@ -97,10 +107,10 @@ IF(XML2_LIBRARIES AND XML2_INCLUDE_DIR) SET(XML2_INCLUDE_DIRS ${XML2_INCLUDE_DIR}) SET(XML2_FOUND TRUE) - IF(WIN32) + IF(WIN32 AND ICONV_FOUND) LIST(APPEND XML2_INCLUDE_DIRS ${ICONV_INCLUDE_DIRS}) SET(XML2_LIBRARIES ${XML2_LIBRARIES} ${ICONV_LIBRARIES}) - ENDIF(WIN32) + ENDIF() ELSE(XML2_LIBRARIES AND XML2_INCLUDE_DIR) SET(XML2_FOUND FALSE) diff --git a/CMakeModules/FindZBAR.cmake b/CMakeModules/FindZBAR.cmake new file mode 100644 index 0000000000000000000000000000000000000000..21c31be5e4d5fc69104a8ffdf8655c2f9148486c --- /dev/null +++ b/CMakeModules/FindZBAR.cmake @@ -0,0 +1,71 @@ +############################################################################# +# +# $Id: FindV4L2.cmake 4574 2014-01-09 08:48:51Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# Try to find zbar library. +# Once run this will define: +# +# ZBAR_FOUND +# ZBAR_INCLUDE_DIRS +# ZBAR_LIBRARIES +# +# Authors: +# Fabien Spindler +# +############################################################################# + + +find_path(ZBAR_INCLUDE_DIRS zbar.h + $ENV{ZBAR_DIR}/include + /usr/include + /usr/local/include +) + +find_library(ZBAR_LIBRARIES + NAMES zbar + PATHS + $ENV{ZBAR_DIR}/lib + /usr/lib + /usr/local/lib +) + +if(ZBAR_INCLUDE_DIRS AND ZBAR_LIBRARIES) + set(ZBAR_FOUND TRUE) +else() + set(ZBAR_FOUND FALSE) +endif() + +mark_as_advanced( + ZBAR_INCLUDE_DIRS + ZBAR_LIBRARIES +) + diff --git a/CMakeModules/GenerateConfigScript.cmake b/CMakeModules/GenerateConfigScript.cmake deleted file mode 100644 index cb21dd3b528daf7fb2cdf7e67893fb162769222a..0000000000000000000000000000000000000000 --- a/CMakeModules/GenerateConfigScript.cmake +++ /dev/null @@ -1,384 +0,0 @@ -############################################################################# -# -# $Id: GenerateConfigScript.cmake 4209 2013-04-16 07:04:20Z fspindle $ -# -# This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. -# -# This software is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# ("GPL") version 2 as published by the Free Software Foundation. -# See the file LICENSE.txt at the root directory of this source -# distribution for additional information about the GNU GPL. -# -# For using ViSP with software that can not be combined with the GNU -# GPL, please contact INRIA about acquiring a ViSP Professional -# Edition License. -# -# See http://www.irisa.fr/lagadic/visp/visp.html for more information. -# -# This software was developed at: -# INRIA Rennes - Bretagne Atlantique -# Campus Universitaire de Beaulieu -# 35042 Rennes Cedex -# France -# http://www.irisa.fr/lagadic -# -# If you have questions regarding the use of this file, please contact -# INRIA at visp@inria.fr -# -# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -# -# Description: -# This file generates the ViSP library config shell scripts: -# - visp-config in <build dir>/bin from visp-config.in -# - visp-config in <build dir>/install from visp-config.install.in -# When make install, this file is copied in <install dir>/bin -# - visp.pc in <build dir>/install from visp.pc.in -# When make install, this file is copied in <install dir>/lib/pkgconfig -# -# Authors: -# Fabien Spindler -# -############################################################################# - - -IF (UNIX) - ####################################################################### - # - # for Unix platforms: Linux, OSX - # - ####################################################################### - SET(FILE_VISP_CONFIG_SCRIPT_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.in") - SET(FILE_VISP_CONFIG_SCRIPT "${BINARY_OUTPUT_PATH}/visp-config") - - SET(FILE_VISP_CONFIG_SCRIPT_INSTALL_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.install.in") - SET(FILE_VISP_CONFIG_SCRIPT_INSTALL "${VISP_BINARY_DIR}/install/visp-config") - - SET(FILE_VISP_CONFIG_PC_INSTALL_IN "${VISP_SOURCE_DIR}/CMakeModules/visp.pc.in") - SET(FILE_VISP_CONFIG_PC_INSTALL "${VISP_BINARY_DIR}/install/visp.pc") - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_PREFIX - #---------------------------------------------------------------------- - SET(VISP_CONFIG_SCRIPT_PREFIX "${CMAKE_INSTALL_PREFIX}") - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_CFLAGS - #---------------------------------------------------------------------- - SET(VISP_CONFIG_CFLAGS ${VISP_OPENMP_FLAGS}) - LIST(APPEND VISP_CONFIG_CFLAGS "${VISP_DEFS}") - - FOREACH(INCDIR ${VISP_EXTERN_INCLUDE_DIRS}) - LIST(APPEND VISP_CONFIG_CFLAGS "-I${INCDIR}") - ENDFOREACH(INCDIR) - - # Suppress twins - LIST(REMOVE_DUPLICATES VISP_CONFIG_CFLAGS) - - # Format the string to suppress CMake separators ";" - SET(VISP_CONFIG_CFLAGS_REFORMATED "") - FOREACH(element ${VISP_CONFIG_CFLAGS}) - SET(VISP_CONFIG_CFLAGS_REFORMATED "${VISP_CONFIG_CFLAGS_REFORMATED} ${element}") - ENDFOREACH(element) - SET(VISP_CONFIG_CFLAGS ${VISP_CONFIG_CFLAGS_REFORMATED}) -# MESSAGE(": ${VISP_CONFIG_CFLAGS}") - - - IF(BUILD_TEST_COVERAGE) - # Add build options for test coverage. Currently coverage is only supported - # on gcc compiler - # Because using -fprofile-arcs with shared lib can cause problems like: - # hidden symbol `__bb_init_func', we add this option only for static - # library build - SET(VISP_CONFIG_CFLAGS "${VISP_CONFIG_CFLAGS} -ftest-coverage -fprofile-arcs") - ENDIF(BUILD_TEST_COVERAGE) - - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_LIBS - # - # add "-l" to library names - # skip *.so, *.a, *.dylib, -framework*, -l* - # - #---------------------------------------------------------------------- - - # Manage the lib path - SET(VISP_CONFIG_SCRIPT_TMP_LDFLAGS) - FOREACH(dir ${VISP_EXTERN_LINK_DIR}) - LIST(APPEND VISP_CONFIG_SCRIPT_TMP_LDFLAGS "-L${dir}") - ENDFOREACH(dir) - #MESSAGE("VISP_EXTERN_LINK_DIR: ${VISP_EXTERN_LINK_DIR}") - #MESSAGE("VISP_CONFIG_SCRIPT_TMP_LDFLAGS: ${VISP_CONFIG_SCRIPT_TMP_LDFLAGS}") - # convert semicolon-separated vector to space-separated string - FOREACH(val ${VISP_CONFIG_SCRIPT_TMP_LDFLAGS}) - SET(VISP_CONFIG_LIBS "${VISP_CONFIG_LIBS} ${val}") - ENDFOREACH(val) - - # Manage the libs - LIST(REMOVE_ITEM VISP_EXTERN_LIBRARIES "debug") - LIST(REMOVE_ITEM VISP_EXTERN_LIBRARIES "optimized") - SET(TMP_LIBS) - FOREACH(lib ${VISP_EXTERN_LIBRARIES}) - IF("${lib}" MATCHES "[-][f][r][a][m][e][w][o][r][k]+.") - # does nothing - LIST(APPEND TMP_LIBS ${lib}) - ELSEIF("${lib}" MATCHES ".[.][f][r][a][m][e][w][o][r][k]+$") - # replace /path/name.framework by -framework name - GET_FILENAME_COMPONENT(FRAMEWORK ${lib} NAME_WE) - #MESSAGE("add -framework ${FRAMEWORK}") - LIST(APPEND TMP_LIBS "-framework ${FRAMEWORK}") - ELSEIF("${lib}" MATCHES ".[.][s][o]+$" OR "${lib}" MATCHES ".[.][a]+$") - # does nothing - LIST(APPEND TMP_LIBS ${lib}) - ELSEIF("${lib}" MATCHES ".[.][d][y][l][i][b]+$") - # does nothing - LIST(APPEND TMP_LIBS ${lib}) - ELSEIF("${lib}" MATCHES "[-][l]+.") - # does nothing - LIST(APPEND TMP_LIBS ${lib}) - ELSE() - # add -l prefix - #MESSAGE("add -l${lib}") - LIST(APPEND TMP_LIBS "-l${lib}") - ENDIF() - ENDFOREACH(lib) - - FOREACH(val ${TMP_LIBS}) - SET(VISP_CONFIG_LIBS "${VISP_CONFIG_LIBS} ${val}") - ENDFOREACH(val) - - #MESSAGE("EXTERN LIBS : ${VISP_CONFIG_LIBS}") - - #--------------------------------------------------------------------- - # Updates the <build dir>/bin/visp-config shell script - # Updates VISP_CONFIG_LIBS_SCRIPT (for visp-config) - # Updates VISP_CONFIG_CFLAGS_SCRIPT (for visp-config) - #---------------------------------------------------------------------- - - # prepend with ViSP own include dir - set(VISP_CONFIG_CFLAGS_SCRIPT "-I$PREFIX/${CMAKE_INSTALL_INCLUDEDIR} ${VISP_CONFIG_CFLAGS}") - - # prepend with ViSP own lib dir - SET(VISP_CONFIG_LIBS_SCRIPT "-L$PREFIX/${CMAKE_INSTALL_LIBDIR} -l${VISP_INTERN_LIBRARY} ${VISP_CONFIG_LIBS}") - IF(UNIX) - IF(NOT APPLE) - SET(VISP_CONFIG_LIBS_SCRIPT "-Wl,-rpath,$PREFIX/${CMAKE_INSTALL_LIBDIR} ${VISP_CONFIG_LIBS_SCRIPT}") - ENDIF(NOT APPLE) - ENDIF(UNIX) - - SET(VISP_ECHO_NO_NEWLINE_CHARACTER "") - SET(VISP_ECHO_NO_NEWLINE_OPTION "") - IF(APPLE) - SET(VISP_ECHO_NO_NEWLINE_CHARACTER "\\c") - ELSE(APPLE) - SET(VISP_ECHO_NO_NEWLINE_OPTION "-n") - ENDIF(APPLE) - - CONFIGURE_FILE(${FILE_VISP_CONFIG_SCRIPT_IN} ${FILE_VISP_CONFIG_SCRIPT}) - - #--------------------------------------------------------------------- - # Updates the <build dir>/install/visp-config shell script - #---------------------------------------------------------------------- - - CONFIGURE_FILE(${FILE_VISP_CONFIG_SCRIPT_INSTALL_IN} ${FILE_VISP_CONFIG_SCRIPT_INSTALL}) - - #--------------------------------------------------------------------- - # Updates the <build dir>/install/visp.pc pkg-config file - # Updates VISP_CONFIG_CFLAGS_PC (for libvisp.pc used by pkg-config) - # Updates VISP_CONFIG_LIBS_PC (for libvisp.pc used by pkg-config) - #---------------------------------------------------------------------- - set(exec_prefix "\${prefix}") - set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") - set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}") - - # prepend with ViSP own include dir - set(VISP_CONFIG_CFLAGS_PC "-I\${includedir} ${VISP_CONFIG_CFLAGS}") - - # prepend with ViSP own lib dir - SET(VISP_CONFIG_LIBS_PC "-L\${libdir} -l${VISP_INTERN_LIBRARY} ${VISP_CONFIG_LIBS}") - IF(UNIX) - IF(NOT APPLE) - SET(VISP_CONFIG_LIBS_PC "-Wl,-rpath,\${libdir} ${VISP_CONFIG_LIBS_PC}") - ENDIF(NOT APPLE) - ENDIF(UNIX) - CONFIGURE_FILE(${FILE_VISP_CONFIG_PC_INSTALL_IN} ${FILE_VISP_CONFIG_PC_INSTALL}) - -ELSE(UNIX) - ####################################################################### - # - # for windows platforms - # - ####################################################################### - SET(FILE_VISP_CONFIG_SCRIPT_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.bat.in") - SET(FILE_VISP_CONFIG_SCRIPT "${BINARY_OUTPUT_PATH}/visp-config.bat") - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_PREFIX - #---------------------------------------------------------------------- - SET(VISP_CONFIG_SCRIPT_PREFIX "${CMAKE_INSTALL_PREFIX}") - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_DEF - #---------------------------------------------------------------------- - SET(VISP_CONFIG_SCRIPT_DEFS "") - FOREACH(def ${VISP_DEFS}) - #MESSAGE("def to process: ${def}") - IF("${def}" MATCHES "[-][D]+.") - #MESSAGE("${def} matches -D") - STRING(REGEX REPLACE "[-][D]" "" def ${def}) - STRING(REGEX REPLACE "[ ]" ";" def ${def}) - #MESSAGE("new ${def} without -D") - ENDIF("${def}" MATCHES "[-][D]+.") - SET(VISP_CONFIG_SCRIPT_DEFS "${def}, ${VISP_CONFIG_SCRIPT_DEFS}") - ENDFOREACH(def) - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_INCLUDE - #---------------------------------------------------------------------- - LIST(APPEND VISP_EXTERN_INCLUDE_DIRS "%PREFIX%/${CMAKE_INSTALL_INCLUDEDIR}") - LIST(REMOVE_DUPLICATES VISP_EXTERN_INCLUDE_DIRS) - - # Format the string - FOREACH(element ${VISP_EXTERN_INCLUDE_DIRS}) - SET(VISP_CONFIG_SCRIPT_INC "\"${element}\"; ${VISP_CONFIG_SCRIPT_INC}") - ENDFOREACH(element) - -# MESSAGE(VISP_CONFIG_SCRIPT_INC ${VISP_CONFIG_SCRIPT_INC}) - - #--------------------------------------------------------------------- - # Updates VISP_OPENMP_SUPPORT - #---------------------------------------------------------------------- - IF(VISP_OPENMP_FLAGS) - SET(VISP_OPENMP_SUPPORT "OpenMP support: Yes") - ELSE() - SET(VISP_OPENMP_SUPPORT "OpenMP support: No") - ENDIF() - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_LIBDIR - #---------------------------------------------------------------------- - SET(TMP_SCRIPT_LIBDIR "%PREFIX%\\lib") - #SET(TMP_SCRIPT_LIBDIR ${TMP_SCRIPT_LIBDIR} "%PREFIX%\\lib\\$(Outdir)") - SET(TMP_SCRIPT_LIBDIR ${TMP_SCRIPT_LIBDIR} "%PREFIX%\\lib\\$(ConfigurationName)") - #MESSAGE("VISP_EXTERN_LINK_DIR ${VISP_EXTERN_LINK_DIR}") - FOREACH(var ${VISP_EXTERN_LINK_DIR}) - #MESSAGE("var to process: ${var}") - IF("${var}" MATCHES "[-][L]+.") - #MESSAGE("${var} matches -L") - STRING(REGEX REPLACE "[-][L]" "" var ${var}) - #MESSAGE("new ${newvar} without -L") - ENDIF("${var}" MATCHES "[-][L]+.") - LIST(APPEND TMP_SCRIPT_LIBDIR ${var}) - LIST(APPEND TMP_SCRIPT_LIBDIR "${var}\\$(ConfigurationName)") - ENDFOREACH(var) - - FOREACH(lib ${VISP_EXTERN_LIBRARIES}) - # Get the library path - GET_FILENAME_COMPONENT(libpath ${lib} PATH) - #MESSAGE("library path: ${libpath}") - - LIST(APPEND TMP_SCRIPT_LIBDIR ${libpath}) - ENDFOREACH(lib) - - #MESSAGE("TMP_SCRIPT_LIBDIR: ${TMP_SCRIPT_LIBDIR}") - - # Suppress twins - IF(TMP_SCRIPT_LIBDIR) - LIST(REMOVE_DUPLICATES TMP_SCRIPT_LIBDIR) - ENDIF() - # Format the string - FOREACH(element ${TMP_SCRIPT_LIBDIR}) - SET(VISP_CONFIG_SCRIPT_LIBDIR "\"${element}\"; ${VISP_CONFIG_SCRIPT_LIBDIR}") - ENDFOREACH(element) - -# MESSAGE("VISP_CONFIG_SCRIPT_LIBDIR: ${VISP_CONFIG_SCRIPT_LIBDIR}") - - #--------------------------------------------------------------------- - # Updates VISP_CONFIG_SCRIPT_LIBS - #---------------------------------------------------------------------- - SET(TMP_SCRIPT_LIBS_DEBUG "${VISP_INTERN_LIBRARY}${CMAKE_DEBUG_POSTFIX}.lib") - SET(TMP_SCRIPT_LIBS_OPTIMIZED "${VISP_INTERN_LIBRARY}.lib") - - #MESSAGE(VISP_EXTERN_LIBRARIES: ${VISP_EXTERN_LIBRARIES}) - SET(TMP_IS_DEBUG FALSE) - SET(TMP_IS_OPTIMIZED FALSE) - FOREACH(lib ${VISP_EXTERN_LIBRARIES}) - IF("${lib}" MATCHES "[d][e][b][u][g]") - SET(TMP_IS_DEBUG TRUE) - ELSEIF("${lib}" MATCHES "[o][p][t][i][m][i][z][e][d]") - SET(TMP_IS_OPTIMIZED TRUE) - ELSE() - - # Get the library name - GET_FILENAME_COMPONENT(libname ${lib} NAME) - IF("${libname}" MATCHES ".+[.][l][i][b]" OR "${libname}" MATCHES ".+[.][L][i][b]") - #MESSAGE("${libname} matches .lib or .Lib") - ELSE("${libname}" MATCHES ".+[.][l][i][b]" OR "${libname}" MATCHES ".+[.][L][i][b]") - # We need to add .lib suffix - #MESSAGE("For ${libname} we add .lib suffix") - SET(libname "${libname}.lib") - ENDIF("${libname}" MATCHES ".+[.][l][i][b]" OR "${libname}" MATCHES ".+[.][L][i][b]") - - IF(TMP_IS_DEBUG) - SET(TMP_IS_DEBUG FALSE) - LIST(APPEND TMP_SCRIPT_LIBS_DEBUG ${libname}) - ELSEIF(TMP_IS_OPTIMIZED) - SET(TMP_IS_OPTIMIZED FALSE) - LIST(APPEND TMP_SCRIPT_LIBS_OPTIMIZED ${libname}) - ELSE() - LIST(APPEND TMP_SCRIPT_LIBS_DEBUG ${libname}) - LIST(APPEND TMP_SCRIPT_LIBS_OPTIMIZED ${libname}) - ENDIF() - ENDIF() - - ENDFOREACH(lib) - - # Format the string - FOREACH(element ${TMP_SCRIPT_LIBS_DEBUG}) - SET(VISP_CONFIG_SCRIPT_LIBS_DEBUG "${VISP_CONFIG_SCRIPT_LIBS_DEBUG}; ${element}") - ENDFOREACH(element) - FOREACH(element ${TMP_SCRIPT_LIBS_OPTIMIZED}) - SET(VISP_CONFIG_SCRIPT_LIBS_OPTIMIZED "${VISP_CONFIG_SCRIPT_LIBS_OPTIMIZED}; ${element}") - ENDFOREACH(element) - - #MESSAGE(VISP_CONFIG_SCRIPT_LIBS: ${VISP_CONFIG_SCRIPT_LIBS}) - - #--------------------------------------------------------------------- - # Updates the visp-config shell script - #---------------------------------------------------------------------- - CONFIGURE_FILE(${FILE_VISP_CONFIG_SCRIPT_IN} ${FILE_VISP_CONFIG_SCRIPT}) -ENDIF(UNIX) - -#---------------------------------------------------------------------- -# customize install target -#---------------------------------------------------------------------- -# install rule for visp-config shell script -IF (UNIX) - INSTALL(FILES ${FILE_VISP_CONFIG_SCRIPT_INSTALL} - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - OWNER_WRITE - COMPONENT libraries) -ELSE(UNIX) - INSTALL(FILES ${BINARY_OUTPUT_PATH}/visp-config.bat - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - OWNER_WRITE - COMPONENT libraries) -ENDIF (UNIX) -# install rule for visp.pc pkg-config file -IF (UNIX) - INSTALL(FILES ${FILE_VISP_CONFIG_PC_INSTALL} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - OWNER_WRITE - COMPONENT libraries) -ELSE(UNIX) - # not implemented yet -ENDIF (UNIX) - - diff --git a/CMakeModules/OgreTools.cmake b/CMakeModules/OgreTools.cmake index 301b73778e5e9d8735857c0c9a124be76c56dfcf..fd714dedc4fb3740482f4cac179f034bb6513ce7 100755 --- a/CMakeModules/OgreTools.cmake +++ b/CMakeModules/OgreTools.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: OgreTools.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: OgreTools.cmake 4769 2014-07-08 20:25:50Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,18 +38,72 @@ # ############################################################################# +######################################################### +# Find Ogre plugins +# +# This is a modified version of the macro provided with Ogre +# except that it should be used only in a desperate way when the original +# one doesn't detect anything +######################################################### + +macro(ogre_find_plugin_lib_visp PLUGIN) + # On Unix, the plugins might have no prefix + if (CMAKE_FIND_LIBRARY_PREFIXES) + set(TMP_CMAKE_LIB_PREFIX ${CMAKE_FIND_LIBRARY_PREFIXES}) + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "") + endif() + + # strip RenderSystem_ or Plugin_ prefix from plugin name + string(REPLACE "RenderSystem_" "" PLUGIN_TEMP ${PLUGIN}) + string(REPLACE "Plugin_" "" PLUGIN_NAME ${PLUGIN_TEMP}) + + set(OGRE_PLUGIN_PATH_SUFFIXES + PlugIns PlugIns/${PLUGIN_NAME} Plugins Plugins/${PLUGIN_NAME} ${PLUGIN} + RenderSystems RenderSystems/${PLUGIN_NAME} ${ARGN}) + # find link libraries for plugins + set(OGRE_${PLUGIN}_LIBRARY_NAMES "${PLUGIN}${OGRE_LIB_SUFFIX}") + get_debug_names(OGRE_${PLUGIN}_LIBRARY_NAMES) + find_library(OGRE_${PLUGIN}_LIBRARY_REL NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES} + HINTS ${OGRE_LIBRARY_DIRS} ${OGRE_LIBRARY_DIRS}/OGRE ${OGRE_LIBRARY_DIRS}/OGRE-${OGRE_VERSION_MAJOR}.${OGRE_VERSION_MINOR}.${OGRE_VERSION_PATCH} + PATH_SUFFIXES "" OGRE opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt) + find_library(OGRE_${PLUGIN}_LIBRARY_DBG NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES_DBG} + HINTS ${OGRE_LIBRARY_DIRS} ${OGRE_LIBRARY_DIRS}/OGRE ${OGRE_LIBRARY_DIRS}/OGRE-${OGRE_VERSION_MAJOR}.${OGRE_VERSION_MINOR}.${OGRE_VERSION_PATCH} + PATH_SUFFIXES "" OGRE opt debug debug/opt) + make_library_set(OGRE_${PLUGIN}_LIBRARY) + + if (OGRE_${PLUGIN}_LIBRARY) + set(OGRE_${PLUGIN}_FOUND TRUE) + endif () + + mark_as_advanced(OGRE_${PLUGIN}_LIBRARY_REL OGRE_${PLUGIN}_LIBRARY_DBG OGRE_${PLUGIN}_LIBRARY_FWK) + +endmacro(ogre_find_plugin_lib_visp) + MACRO(CREATE_OGRE_PLUGIN_CONFIG_FILE) SET(VISP_HAVE_OGRE_PLUGINS_PATH ${VISP_BINARY_DIR}/data/ogre-simulator) # If OGRE_PLUGIN_DIR_REL and OGRE_PLUGIN_DIR_DBG are not defined we - # try to find these path manually + # try to find them manually IF(NOT OGRE_PLUGIN_DIR_REL AND NOT OGRE_PLUGIN_DIR_DBG) - IF(OGRE_RenderSystem_GL_LIBRARY_REL) + ogre_find_plugin_lib_visp(RenderSystem_Direct3D9) + ogre_find_plugin_lib_visp(RenderSystem_Direct3D10) + ogre_find_plugin_lib_visp(RenderSystem_Direct3D11) + ogre_find_plugin_lib_visp(RenderSystem_GL) + ogre_find_plugin_lib_visp(RenderSystem_GLES) + ogre_find_plugin_lib_visp(Plugin_ParticleFX) + ogre_find_plugin_lib_visp(Plugin_BSPSceneManager) + ogre_find_plugin_lib_visp(Plugin_CgProgramManager) + ogre_find_plugin_lib_visp(Plugin_PCZSceneManager) + ogre_find_plugin_lib_visp(Plugin_OctreeSceneManager) + ogre_find_plugin_lib_visp(Plugin_OctreeZone) + + + IF(OGRE_RenderSystem_GL_LIBRARY_REL) GET_FILENAME_COMPONENT(OGRE_PLUGIN_DIR_REL ${OGRE_RenderSystem_GL_LIBRARY_REL} PATH) - #message("set manually OGRE_PLUGIN_DIR_REL to ${OGRE_PLUGIN_DIR_REL}") + #message("set manually OGRE_PLUGIN_DIR_REL to ${OGRE_PLUGIN_DIR_REL}") ELSEIF(OGRE_RenderSystem_GL_LIBRARY_DBG) GET_FILENAME_COMPONENT(OGRE_PLUGIN_DIR_DBG ${OGRE_RenderSystem_GL_LIBRARY_DBG} PATH) - #message("set manually OGRE_PLUGIN_DIR_DBG to ${OGRE_PLUGIN_DIR_DBG}") + #message("set manually OGRE_PLUGIN_DIR_DBG to ${OGRE_PLUGIN_DIR_DBG}") ENDIF() ENDIF() diff --git a/CMakeModules/VISPCRTLinkage.cmake b/CMakeModules/VISPCRTLinkage.cmake new file mode 100644 index 0000000000000000000000000000000000000000..20675fa78f4fbd1648ed148905c50c1bc840124d --- /dev/null +++ b/CMakeModules/VISPCRTLinkage.cmake @@ -0,0 +1,44 @@ +if(NOT MSVC) + message(FATAL_ERROR "CRT options are available only for MSVC") +endif() + +if(NOT BUILD_SHARED_LIBS AND BUILD_WITH_STATIC_CRT) + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif() + if(${flag_var} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:msvcrtd.lib") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt.lib") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libcmtd.lib") +else() + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MT") + string(REGEX REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") + endif() + if(${flag_var} MATCHES "/MTd") + string(REGEX REPLACE "/MTd" "/MDd" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) +endif() + +if(NOT ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.8 AND NOT ${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} LESS 8.6) + include(ProcessorCount) + ProcessorCount(N) + if(NOT N EQUAL 0) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP${N} ") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP${N} ") + endif() +endif() diff --git a/CMakeModules/VISPConfig.cmake b/CMakeModules/VISPConfig.cmake new file mode 100644 index 0000000000000000000000000000000000000000..f4857511bed65951c4720b65f536335e4548500c --- /dev/null +++ b/CMakeModules/VISPConfig.cmake @@ -0,0 +1,149 @@ +############################################################################# +# +# $Id: VISPConfig.cmake.in 4806 2014-07-30 08:55:55Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# CMake package config file for ViSP. +# +# ** File generated automatically, do not modify ** +# +# This file will define the following CMake variables: +# - VISP_INCLUDE_DIRS : ViSP and third-party include directories +# - VISP_LIBRARIES : ViSP library to link against. Third-party libraries are +# linked automatically thanks to cmake export file VISPTargets.cmake +# - VISP_VERSION_STRING : Full ViSP version that is build. Example: "2.10.0" +# - VISP_VERSION_MAJOR : Major version part of VISP_VERSION. Example: "2" +# - VISP_VERSION_MINOR : Minor version part of VISP_VERSION. Example: "10" +# - VISP_VERSION_PATCH : Patch version part of VISP_VERSION. Example: "0" +# +# Advanced variables: +# - VISP_SHARED : Use ViSP as shared library +# - VISP_CONFIG_PATH : Path to this VISPConfig.cmake +# - VISP_FIND_QUIETLY : If set to TRUE turn off messages during configuration +# - VISP_USE_FILE : File to include to use ViSP without specific cmake code +# +# Windows specific variables: +# - VISP_STATIC : If set to TRUE uses ViSP static library (.lib) rather then dynamic (.dll) +# +# Typical usage in user project: +# +# find_package(VISP) +# include_directories(${VISP_INCLUDE_DIRS}) +# target_link_libraries(MY_TARGET_NAME ${VISP_LIBRARIES}) +# +# It is also possible to build your project using VISP_USE_FILE. +# +# find_package(VISP) +# if(VISP_FOUND) +# include(${VISP_USE_FILE}) +# endif() +# +# Authors: +# Fabien Spindler +# +############################################################################# + +# similar code exist in VISPDetectPlatform.cmake +if(MSVC) + if(CMAKE_CL_64) + set(VISP_ARCH x64) + else() + set(VISP_ARCH x86) + endif() + if(MSVC_VERSION EQUAL 1400) + set(VISP_RUNTIME vc8) + elseif(MSVC_VERSION EQUAL 1500) + set(VISP_RUNTIME vc9) + elseif(MSVC_VERSION EQUAL 1600) + set(VISP_RUNTIME vc10) + elseif(MSVC_VERSION EQUAL 1700) + set(VISP_RUNTIME vc11) + elseif(MSVC_VERSION EQUAL 1800) + set(VISP_RUNTIME vc12) + endif() +elseif(MINGW) + set(VISP_RUNTIME mingw) + + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpmachine + OUTPUT_VARIABLE VISP_GCC_TARGET_MACHINE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(VISP_GCC_TARGET_MACHINE MATCHES "64") + set(MINGW64 1) + set(VISP_ARCH x64) + else() + set(VISP_ARCH x86) + endif() +endif() + +if(CMAKE_VERSION VERSION_GREATER 2.6.2) + unset(VISP_CONFIG_PATH CACHE) +endif() + +if(NOT VISP_FIND_QUIETLY) + message(STATUS "ViSP ARCH: ${VISP_ARCH}") + message(STATUS "ViSP RUNTIME: ${VISP_RUNTIME}") +endif() + +get_filename_component(VISP_CONFIG_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH CACHE) +if(VISP_RUNTIME AND VISP_ARCH) + if(NOT DEFINED VISP_STATIC AND EXISTS "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/lib/VISPConfig.cmake") + set(VISP_LIB_PATH "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/lib") + elseif(NOT DEFINED VISP_STATIC AND EXISTS "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/staticlib/VISPConfig.cmake") + set(VISP_LIB_PATH "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/staticlib") + elseif(VISP_STATIC AND EXISTS "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/staticlib/VISPConfig.cmake") + set(VISP_LIB_PATH "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/staticlib") + elseif(VISP_STATIC EXISTS "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/lib/VISPConfig.cmake") + set(VISP_LIB_PATH "${VISP_CONFIG_PATH}/${VISP_ARCH}/${VISP_RUNTIME}/lib") + endif() +endif() + +if(VISP_LIB_PATH AND EXISTS "${VISP_LIB_PATH}/VISPConfig.cmake") + include("${VISP_LIB_PATH}/VISPConfig.cmake") + + set(VISP_FOUND TRUE CACHE BOOL "" FORCE) + + if(NOT VISP_FIND_QUIETLY) + message(STATUS "Found VISP ${VISP_VERSION} in ${VISP_LIB_PATH}") + if(NOT VISP_LIB_PATH MATCHES "/staticlib") + get_filename_component(_VISP_LIB_PATH "${VISP_LIB_PATH}/../bin" ABSOLUTE) + file(TO_NATIVE_PATH "${_VISP_LIB_PATH}" _VISP_LIB_PATH) + message(STATUS "You might need to add ${_VISP_LIB_PATH} to your PATH to be able to run your applications.") + endif() + endif() +else() + if(NOT VISP_FIND_QUIETLY) + message(WARNING +"Found ViSP for Windows but it has no binaries compatible with your configuration. +You should manually point CMake variable VISP_DIR to your build of ViSP library." + ) + endif() + set(VISP_FOUND FALSE CACHE BOOL "" FORCE) +endif() diff --git a/CMakeModules/VISPConfig.cmake.in b/CMakeModules/VISPConfig.cmake.in index bb63a359090a135effc79aa8900c08bc2afe3ff0..f42084305771c6a012db589447110a0063c36c06 100644 --- a/CMakeModules/VISPConfig.cmake.in +++ b/CMakeModules/VISPConfig.cmake.in @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: VISPConfig.cmake.in 4221 2013-04-17 10:13:34Z fspindle $ +# $Id: VISPConfig.cmake.in 5011 2014-11-28 10:11:48Z ayol $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -33,106 +33,111 @@ # Description: # CMake package config file for ViSP. # -# This file will define the following CMake variables: -# VISP_ROOT_DIR : ViSP base directory location -# VISP_INCLUDE_DIRS : ViSP and third-party headers location -# VISP_LIBRARY_DIRS : ViSP and third-party libraries location -# VISP_LIBRARY : ViSP library name -# VISP_LIBRARIES : ViSP and third-party libraries name -# VISP_DEFINITIONS : Defines required to build ViSP +# ** File generated automatically, do not modify ** # -# VISP_SOURCE_DIR : Location of ViSP source code +# This file will define the following CMake variables: +# - VISP_INCLUDE_DIRS : ViSP and third-party include directories +# - VISP_LIBRARIES : ViSP library to link against. Third-party libraries are +# linked automatically thanks to cmake export file VISPTargets.cmake +# - VISP_VERSION_STRING : Full ViSP version that is build, "@VISP_VERSION@" +# - VISP_VERSION_MAJOR : Major version part of VISP_VERSION: "@VISP_VERSION_MAJOR@" +# - VISP_VERSION_MINOR : Minor version part of VISP_VERSION: "@VISP_VERSION_MINOR@" +# - VISP_VERSION_PATCH : Patch version part of VISP_VERSION: "@VISP_VERSION_PATCH@" # -# VISP_VERSION_STRING : Full ViSP version, eg 2.6.1 -# VISP_VERSION_MAJOR : Major version part of VISP_VERSION, eg 2. -# VISP_VERSION_MINOR : Minor version part of VISP_VERSION, eg 6. -# VISP_VERSION_PATCH : Patch version part of VISP_VERSION, eg 1. +# Advanced variables: +# - VISP_SHARED : Use ViSP as shared library +# - VISP_CONFIG_PATH : Path to this VISPConfig.cmake +# - VISP_USE_FILE : File to include to use ViSP without specific cmake code # # Typical usage in user project: -# FIND_PACKAGE(VISP) -# IF (VISP_FOUND) -# INCLUDE(${VISP_USE_FILE}) -# ENDIF (VISP_FOUND) +# +# find_package(VISP) +# include_directories(${VISP_INCLUDE_DIRS}) +# target_link_libraries(MY_TARGET_NAME ${VISP_LIBRARIES}) +# +# It is also possible to build your project using VISP_USE_FILE. +# +# find_package(VISP) +# if(VISP_FOUND) +# include(${VISP_USE_FILE}) +# endif() # # Authors: # Fabien Spindler # ############################################################################# -SET(VISP_LIBRARY "@VISP_INTERN_LIBRARY@") +#if(VISP_FOUND) +# return() +#endif() -# Set VISP_ROOT_DIR -SET(VISP_ROOT_DIR "@VISP_ROOT_DIR_CONFIGCMAKE@") -#MESSAGE("VISP_ROOT_DIR: ${VISP_ROOT_DIR}") -SET(VISP_ROOT_DIR_CMAKE "@VISP_ROOT_DIR_CMAKE_CONFIGCMAKE@") -#MESSAGE("VISP_ROOT_DIR_CMAKE: ${VISP_ROOT_DIR_CMAKE}") +#set(VISP_FOUND TRUE) # Set the version numbers -SET(VISP_VERSION_STRING "@VISP_VERSION@") -SET(VISP_VERSION_MAJOR "@VISP_VERSION_MAJOR@") -SET(VISP_VERSION_MINOR "@VISP_VERSION_MINOR@") -SET(VISP_VERSION_PATCH "@VISP_VERSION_PATCH@") +set(VISP_VERSION_STRING "@VISP_VERSION@") +set(VISP_VERSION_MAJOR "@VISP_VERSION_MAJOR@") +set(VISP_VERSION_MINOR "@VISP_VERSION_MINOR@") +set(VISP_VERSION_PATCH "@VISP_VERSION_PATCH@") + +# Some additional settings are required if ViSP is built as static libs +set(VISP_SHARED @BUILD_SHARED_LIBS@) + +# Extract the directory where *this* file has been installed (determined at cmake run-time) +get_filename_component(VISP_CONFIG_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH CACHE) +mark_as_advanced(VISP_CONFIG_PATH) + +if(NOT WIN32) + # Since this file is installed in ./${VISP_INSTALL_LIBDIR}/cmake/visp + # the first "../../" are here to go to ${VISP_INSTALL_LIBDIR}, and then + # VISP_INSTALL_LIBDIR_TO_PARENT goes to the parent dir "." + set(VISP_INSTALL_PATH "${VISP_CONFIG_PATH}/../../@VISP_INSTALL_LIBDIR_TO_PARENT@") + # Get the absolute path with no ../.. relative marks, to eliminate implicit linker warnings + if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 2.8) + get_filename_component(VISP_INSTALL_PATH "${VISP_INSTALL_PATH}" ABSOLUTE) + else() + get_filename_component(VISP_INSTALL_PATH "${VISP_INSTALL_PATH}" REALPATH) + endif() +endif() # Tells the user project where to find ViSP headers -SET(VISP_INCLUDE_DIRS "${VISP_ROOT_DIR}/@CMAKE_INSTALL_INCLUDEDIR@" CACHE FILEPATH "Location of ViSP includes") -#MESSAGE("VISP_INCLUDE_DIRS: ${VISP_INCLUDE_DIRS}") - -# Get ViSP specific definitions -SET(VISP_DEFINITIONS "@VISP_DEFS@") -#MESSAGE("VISP_DEFINITIONS: ${VISP_DEFINITIONS}") - -# Tells the user project where to find ViSP third party headers used -# to compile ViSP. These locations are concatenate to VISP_INCLUDE_DIRS -SET(VISP_EXTERN_INCLUDE_DIRS "@VISP_EXTERN_INCLUDE_DIRS@") -#MESSAGE("VISP_EXTERN_INCLUDE_DIRS: ${VISP_EXTERN_INCLUDE_DIRS}") -FOREACH(val ${VISP_EXTERN_INCLUDE_DIRS}) - LIST(APPEND VISP_INCLUDE_DIRS "${val}") -ENDFOREACH(val ${VISP_EXTERN_INCLUDE_DIRS}) - -#MESSAGE("VISP_INCLUDE_DIRS: ${VISP_INCLUDE_DIRS}") - -# Tells the user project where to find ViSP library -SET(VISP_LIBRARY_DIRS "${VISP_ROOT_DIR}/@CMAKE_INSTALL_LIBDIR@" CACHE FILEPATH "Location of ViSP library") - -# Tells the user project where to find third party libraries used to -# compile ViSP -SET(VISP_EXTERN_LINK_DIR "@VISP_EXTERN_LINK_DIR@") -#MESSAGE("VISP_EXTERN_LINK_DIR: ${VISP_EXTERN_LINK_DIR}") -FOREACH(val ${VISP_EXTERN_LINK_DIR}) - LIST(APPEND VISP_LIBRARY_DIRS "${val}") -ENDFOREACH(val ${VISP_EXTERN_LINK_DIR}) -#MESSAGE("VISP_LIBRARY_DIRS: ${VISP_LIBRARY_DIRS}") - -# export source dir for doxygen eventually -SET(VISP_SOURCE_DIR "@VISP_SOURCE_DIR@") +set(VISP_INCLUDE_DIRS @VISP_INCLUDE_DIRS_CONFIGCMAKE@) # Tells the user project ViSP library name -SET(VISP_LIBRARIES debug "@VISP_INTERN_LIBRARY@@CMAKE_DEBUG_POSTFIX@" optimized "@VISP_INTERN_LIBRARY@") - -# Tells the user project libraries name used to build ViSP library -SET(VISP_EXTERN_LIBRARIES "@VISP_EXTERN_LIBRARIES@") -FOREACH(val ${VISP_EXTERN_LIBRARIES}) - LIST(APPEND VISP_LIBRARIES "${val}") -ENDFOREACH(val ${VISP_EXTERN_LIBRARIES}) -#MESSAGE("VISP_LIBRARIES: ${VISP_LIBRARIES}") +set(VISP_LIBRARIES "@VISP_INTERN_LIBRARY@") + +# need to be improved +if(POLICY CMP0024) + # Fix to prevent multiple includes + if(NOT TARGET @VISP_INTERN_LIBRARY@) + cmake_policy(PUSH) + cmake_policy(SET CMP0024 OLD) + # Our library dependencies (contains definitions for IMPORTED targets) + include("${CMAKE_CURRENT_LIST_DIR}/VISPTargets.cmake") + cmake_policy(POP) + endif() +else() + # Fix for cmake 2.8.7 to prevent multiple includes + if(NOT TARGET @VISP_INTERN_LIBRARY@) + # Our library dependencies (contains definitions for IMPORTED targets) + include("${CMAKE_CURRENT_LIST_DIR}/VISPTargets.cmake") + endif() +endif() # where to find the USE file to be used by user project -SET(VISP_USE_FILE "${VISP_ROOT_DIR_CMAKE}/VISPUse.cmake") +set(VISP_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/VISPUse.cmake") -IF(BUILD_TEST_COVERAGE) +if(BUILD_TEST_COVERAGE) # Add build options for test coverage. Currently coverage is only supported # on gcc compiler # Because using -fprofile-arcs with shared lib can cause problems like: # hidden symbol `__bb_init_func', we add this option only for static # library build - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ftest-coverage -fprofile-arcs") -ENDIF(BUILD_TEST_COVERAGE) - - -# -# Remember VISP' third party libs configuration: -# + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ftest-coverage -fprofile-arcs") +endif() +#---------------------------------------------------------------------- +# Remember VISP' third party libs configuration: +#---------------------------------------------------------------------- SET(VISP_HAVE_X11 "@VISP_HAVE_X11@") SET(VISP_HAVE_PTHREAD "@VISP_HAVE_PTHREAD@") SET(VISP_HAVE_GTK "@VISP_HAVE_GTK@") @@ -157,7 +162,6 @@ SET(VISP_HAVE_QT "@VISP_HAVE_QT@") SET(VISP_HAVE_SOQT "@VISP_HAVE_SOQT@") SET(VISP_HAVE_SOWIN "@VISP_HAVE_SOWIN@") SET(VISP_HAVE_SOXT "@VISP_HAVE_SOXT@") -SET(VISP_HAVE_DC1394_1 "@VISP_HAVE_DC1394_1@") SET(VISP_HAVE_DC1394_2 "@VISP_HAVE_DC1394_2@") SET(VISP_HAVE_CMU1394 "@VISP_HAVE_CMU1394@") SET(VISP_HAVE_V4L2 "@VISP_HAVE_V4L2@") @@ -168,9 +172,6 @@ SET(VISP_HAVE_BICLOPS "@VISP_HAVE_BICLOPS@") SET(VISP_HAVE_PTU46 "@VISP_HAVE_PTU46@") SET(VISP_HAVE_VIPER650 "@VISP_HAVE_VIPER650@") SET(VISP_HAVE_VIPER850 "@VISP_HAVE_VIPER850@") -SET(VISP_HAVE_CYCAB "@VISP_HAVE_CYCAB@") -SET(VISP_HAVE_CYCABTK "@VISP_HAVE_CYCABTK@") -SET(VISP_HAVE_CYCABTK_OLD "@VISP_HAVE_CYCABTK_OLD@") SET(VISP_HAVE_PIONEER "@VISP_HAVE_PIONEER@") SET(VISP_HAVE_PARPORT "@VISP_HAVE_PARPORT@") SET(VISP_HAVE_XML2 "@VISP_HAVE_XML2@") @@ -181,3 +182,57 @@ SET(VISP_HAVE_YARP "@VISP_HAVE_YARP@") SET(VISP_HAVE_OPENMP "@VISP_HAVE_OPENMP@") SET(VISP_HAVE_ACCESS_TO_NAS "@VISP_HAVE_ACCESS_TO_NAS@") SET(VISP_HAVE_CPP11_COMPATIBILITY "@VISP_HAVE_CPP11_COMPATIBILITY@") + +#---------------------------------------------------------------------- +# Some useful macro to be able to build the tutorials along side ViSP +#---------------------------------------------------------------------- +# Create a target from the *.cpp file, link against ViSP libraries and add a dependency to ViSP library +# tu ensure that the library is build before this target. +macro(visp_add_target file_cpp) + get_filename_component(target ${file_cpp} NAME_WE) + include_directories(${VISP_INCLUDE_DIRS}) + add_executable(${target} ${file_cpp}) + target_link_libraries(${target} ${VISP_LIBRARIES}) + if(VISP_INTERN_LIBRARY) + add_dependencies(${target} ${VISP_INTERN_LIBRARY}) + endif() +endmacro() + +# Create a dependency to the target extracted from the *.cpp file and put the target in the solution dependency folder. +macro(visp_add_dependency file_cpp dependency) + get_filename_component(target ${file_cpp} NAME_WE) + if(TARGET visp_${dependency}) + add_dependencies(visp_${dependency} ${target}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${target} PROPERTIES FOLDER "${dependency}") + endif() + endif() +endmacro() + +# Copy the data files to the same location than the target associated to the cpp files +# Since CMake 3.0.0 policy CMP0026 was introduced to disallow location property on target. +# If CMake 3.0.0 is used, we use $<TARGET_FILE_DIR:tgt> to get the target location +if (CMAKE_VERSION VERSION_GREATER 2.8.12) + macro(visp_copy_data file_cpp file_data) + get_filename_component(target ${file_cpp} NAME_WE) + #get_target_property(target_location ${target} LOCATION) + get_filename_component(target_location "${target_location}" PATH) + add_custom_command( + TARGET ${target} + POST_BUILD + #COMMAND ${CMAKE_COMMAND} -E copy "${file_data}" "${target_location}" + COMMAND ${CMAKE_COMMAND} -E copy "${file_data}" "$<TARGET_FILE_DIR:${target}>" + ) + endmacro() +else() + macro(visp_copy_data file_cpp file_data) + get_filename_component(target ${file_cpp} NAME_WE) + get_target_property(target_location ${target} LOCATION) + get_filename_component(target_location "${target_location}" PATH) + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${file_data}" "${target_location}" + ) + endmacro() +endif() diff --git a/CMakeModules/VISPConfigVersion.cmake.in b/CMakeModules/VISPConfigVersion.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..fd298708453860821eefc2500a5258f5304aa982 --- /dev/null +++ b/CMakeModules/VISPConfigVersion.cmake.in @@ -0,0 +1,31 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version. +# The variable VISP_VERSION must be set before calling configure_file(). + +set(PACKAGE_VERSION @VISP_VERSION@) + +# There is a bug in CMake (at least in 2.7.0) whereby calling "find_package(FOO)" within +# "find_package(FOO)" results in the version being checked in the +# second version no matter if it was set. To get around this, check +# "PACKAGE_FIND_VERSION" and if empty set return variables to TRUE to +# make CMake happy. Not the best solution, but it does the trick. +if(NOT PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + set(PACKAGE_VERSION_EXACT TRUE) + return() +endif() + +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + diff --git a/CMakeModules/VISPDetectPlatform.cmake b/CMakeModules/VISPDetectPlatform.cmake new file mode 100644 index 0000000000000000000000000000000000000000..50ef61f9be7ad42089f98325bcc6c83bab59765d --- /dev/null +++ b/CMakeModules/VISPDetectPlatform.cmake @@ -0,0 +1,41 @@ +# Similar code exist in VISPConfig.cmake + +if(NOT DEFINED VISP_STATIC) + # look for global setting + if(NOT DEFINED BUILD_SHARED_LIBS OR BUILD_SHARED_LIBS) + set(VISP_STATIC OFF) + else() + set(VISP_STATIC ON) + endif() +endif() + +if(MSVC) + if(CMAKE_CL_64) + set(VISP_ARCH x64) + else() + set(VISP_ARCH x86) + endif() + if(MSVC_VERSION EQUAL 1400) + set(VISP_RUNTIME vc8) + elseif(MSVC_VERSION EQUAL 1500) + set(VISP_RUNTIME vc9) + elseif(MSVC_VERSION EQUAL 1600) + set(VISP_RUNTIME vc10) + elseif(MSVC_VERSION EQUAL 1700) + set(VISP_RUNTIME vc11) + elseif(MSVC_VERSION EQUAL 1800) + set(VISP_RUNTIME vc12) + endif() +elseif(MINGW) + set(VISP_RUNTIME mingw) + + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpmachine + OUTPUT_VARIABLE VISP_GCC_TARGET_MACHINE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(VISP_GCC_TARGET_MACHINE MATCHES "64") + set(MINGW64 1) + set(VISP_ARCH x64) + else() + set(VISP_ARCH x86) + endif() +endif() diff --git a/CMakeModules/VISPExtraTargets.cmake b/CMakeModules/VISPExtraTargets.cmake new file mode 100644 index 0000000000000000000000000000000000000000..b1ff04568e7a7d0c7b2909161ac24c3358633bbd --- /dev/null +++ b/CMakeModules/VISPExtraTargets.cmake @@ -0,0 +1,74 @@ +# ---------------------------------------------------------------------------- +# Uninstall target, for "make uninstall" +# ---------------------------------------------------------------------------- +configure_file( + "${VISP_CMAKE_MODULE_PATH}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(uninstall PROPERTIES FOLDER "CMakeTargets") +endif() + +# ---------------------------------------------------------------------------- +# Doxygen documentation target, for "make visp_doc" and "make html-doc" (to keep compat with previous versions) +# ---------------------------------------------------------------------------- +if(DOXYGEN_FOUND) + add_custom_target(html-doc ${DOXYGEN_EXECUTABLE} ${VISP_DOC_DIR}/config-doxygen) # for compat with previous versions + add_custom_target(visp_doc ${DOXYGEN_EXECUTABLE} ${VISP_DOC_DIR}/config-doxygen) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_doc PROPERTIES FOLDER "extra") + set_target_properties(html-doc PROPERTIES FOLDER "extra") + endif() +endif() + +# ---------------------------------------------------------------------------- +# Tests target, for make visp_library +# ---------------------------------------------------------------------------- +add_custom_target(visp_library) +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_library PROPERTIES FOLDER "extra") +endif() + +# ---------------------------------------------------------------------------- +# Tests target, for make visp_tests +# ---------------------------------------------------------------------------- +if(BUILD_TESTS) + add_custom_target(visp_tests) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_tests PROPERTIES FOLDER "extra") + endif() +endif() + +# ---------------------------------------------------------------------------- +# Tests target, for make visp_examples +# ---------------------------------------------------------------------------- +if(BUILD_EXAMPLES) + add_custom_target(visp_examples) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_examples PROPERTIES FOLDER "extra") + endif() +endif() + +# ---------------------------------------------------------------------------- +# Tests target, for make visp_demos +# ---------------------------------------------------------------------------- +if(BUILD_DEMOS) + add_custom_target(visp_demos) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_demos PROPERTIES FOLDER "extra") + endif() +endif() + +# ---------------------------------------------------------------------------- +# Tests target, for make visp_tutorials +# ---------------------------------------------------------------------------- +if(BUILD_TUTORIALS) + add_custom_target(visp_tutorials) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(visp_tutorials PROPERTIES FOLDER "extra") + endif() +endif() diff --git a/CMakeModules/VISPGenerateConfig.cmake b/CMakeModules/VISPGenerateConfig.cmake new file mode 100755 index 0000000000000000000000000000000000000000..d7d2ef502d1a1813ce8fd5249931f8688b770959 --- /dev/null +++ b/CMakeModules/VISPGenerateConfig.cmake @@ -0,0 +1,196 @@ +############################################################################# +# +# $Id: GenerateConfigt.cmake 4676 2014-02-17 22:08:37Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# This file generates the ViSPConfig.cmake file: +# Part 1/3: ${BIN_DIR}/VISPConfig.cmake -> For use *without* "make install" +# Part 2/3: ${BIN_DIR}/unix-install/VISPConfig.cmake -> For use with "make install" +# Part 3/3: ${BIN_DIR}/win-install/VISPConfig.cmake -> For use within binary installers/packages +# +# Authors: +# Fabien Spindler +# +############################################################################# + +# Macro that returns the relative path to go from a child folder to the parent folder +# input: path_to_child +# output: path_to_parent, the relative path to go from path_to_child to parent +# example: if input =lib/x86_64-linux-gnu, then output=../.. +macro(get_path_to_parent path_to_child path_to_parent) + set(${path_to_parent} "") + set(input_ "${path_to_child}") + while(input_) + if(input_) + set(${path_to_parent} "${${path_to_parent}}../") + endif() + get_filename_component(input_ "${input_}" PATH) + endwhile(input_) +endmacro() + +# Here we determine the relative path from ./${CMAKE_INSTALL_LIBDIR} to its parent folder +# if CMAKE_INSTALL_LIBDIR=lib, then VISP_INSTALL_LIBDIR_TO_PARENT=../ +# if CMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu, then VISP_INSTALL_LIBDIR_TO_PARENT=../.. +get_path_to_parent(${CMAKE_INSTALL_LIBDIR} VISP_INSTALL_LIBDIR_TO_PARENT) + +# ------------------------------------------------------------------------------------------- +# Part 1/3: ${BIN_DIR}/VISPConfig.cmake -> For use *without* "make install" +# ------------------------------------------------------------------------------------------- + +# Export the library +export(TARGETS ${VISP_INTERN_LIBRARY} FILE "${PROJECT_BINARY_DIR}/VISPTargets.cmake") + +# Update include dirs +set(VISP_INCLUDE_DIRS_CONFIGCMAKE_ "${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}") +list(APPEND VISP_INCLUDE_DIRS_CONFIGCMAKE_ ${VISP_EXTERN_INCLUDE_DIRS}) +set(VISP_INCLUDE_DIRS_CONFIGCMAKE "") +foreach(val ${VISP_INCLUDE_DIRS_CONFIGCMAKE_}) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE "${VISP_INCLUDE_DIRS_CONFIGCMAKE} \"${val}\"") +endforeach() +#message("VISP_INCLUDE_DIRS_CONFIGCMAKE: ${VISP_INCLUDE_DIRS_CONFIGCMAKE}") + +configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake.in + ${VISP_BINARY_DIR}/VISPConfig.cmake + IMMEDIATE @ONLY) + +configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfigVersion.cmake.in + ${VISP_BINARY_DIR}/VISPConfigVersion.cmake + IMMEDIATE @ONLY) + +configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPUse.cmake.in + ${VISP_BINARY_DIR}/VISPUse.cmake + IMMEDIATE @ONLY) + +# -------------------------------------------------------------------------------------------- +# Part 2/3: ${BIN_DIR}/unix-install/VISPConfig.cmake -> For use *with* "make install" +# ------------------------------------------------------------------------------------------- + +if(UNIX) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE_ "\${VISP_INSTALL_PATH}/${CMAKE_INSTALL_INCLUDEDIR}") + list(APPEND VISP_INCLUDE_DIRS_CONFIGCMAKE_ ${VISP_EXTERN_INCLUDE_DIRS}) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE "") + foreach(val ${VISP_INCLUDE_DIRS_CONFIGCMAKE_}) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE "${VISP_INCLUDE_DIRS_CONFIGCMAKE} \"${val}\"") + endforeach() + #message("VISP_INCLUDE_DIRS_CONFIGCMAKE: ${VISP_INCLUDE_DIRS_CONFIGCMAKE}") + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake.in + ${VISP_BINARY_DIR}/unix-install/VISPConfig.cmake + IMMEDIATE @ONLY) + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfigVersion.cmake.in + ${VISP_BINARY_DIR}/unix-install/VISPConfigVersion.cmake + IMMEDIATE @ONLY) + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPUse.cmake.in + ${VISP_BINARY_DIR}/unix-install/VISPUse.cmake + IMMEDIATE @ONLY) + + configure_file(${VISP_SOURCE_DIR}/include/vpConfig.h.cmake + ${VISP_BINARY_DIR}/unix-install/vpConfig.h @ONLY) + + install(FILES + ${VISP_BINARY_DIR}/unix-install/VISPConfig.cmake + ${VISP_BINARY_DIR}/unix-install/VISPConfigVersion.cmake + ${VISP_BINARY_DIR}/unix-install/VISPUse.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/visp" + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE + COMPONENT libraries) + + # Install the export set for use with the install-tree + install(EXPORT VISPTargets + FILE VISPTargets.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/visp" + COMPONENT libraries) +endif() + +# -------------------------------------------------------------------------------------------- +# Part 3/3: ${BIN_DIR}/win-install/VISPConfig.cmake -> For use within binary installers/packages +# -------------------------------------------------------------------------------------------- +if(WIN32) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE_ "\${VISP_CONFIG_PATH}/${CMAKE_INSTALL_INCLUDEDIR}") + list(APPEND VISP_INCLUDE_DIRS_CONFIGCMAKE_ ${VISP_EXTERN_INCLUDE_DIRS}) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE "") + foreach(val ${VISP_INCLUDE_DIRS_CONFIGCMAKE_}) + set(VISP_INCLUDE_DIRS_CONFIGCMAKE "${VISP_INCLUDE_DIRS_CONFIGCMAKE} \"${val}\"") + endforeach() + #message("VISP_INCLUDE_DIRS_CONFIGCMAKE: ${VISP_INCLUDE_DIRS_CONFIGCMAKE}") + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake.in + ${VISP_BINARY_DIR}/win-install/VISPConfig.cmake + IMMEDIATE @ONLY) + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPConfigVersion.cmake.in + ${VISP_BINARY_DIR}/win-install/VISPConfigVersion.cmake + IMMEDIATE @ONLY) + + configure_file( + ${VISP_CMAKE_MODULE_PATH}/VISPUse.cmake.in + ${VISP_BINARY_DIR}/win-install/VISPUse.cmake + IMMEDIATE @ONLY) + + if(BUILD_SHARED_LIBS) + install(FILES + "${CMAKE_BINARY_DIR}/win-install/ViSPConfig.cmake" + "${CMAKE_BINARY_DIR}/win-install/ViSPUse.cmake" + DESTINATION "${VISP_INSTALL_BINARIES_PREFIX}${CMAKE_INSTALL_LIBDIR}" + COMPONENT libraries) + install(EXPORT VISPTargets + DESTINATION "${VISP_INSTALL_BINARIES_PREFIX}${CMAKE_INSTALL_LIBDIR}" + FILE VISPTargets.cmake + COMPONENT libraries) + else() + install(FILES + "${CMAKE_BINARY_DIR}/win-install/ViSPConfig.cmake" + "${CMAKE_BINARY_DIR}/win-install/ViSPUse.cmake" + DESTINATION "${VISP_INSTALL_BINARIES_PREFIX}static${CMAKE_INSTALL_LIBDIR}" + COMPONENT libraries) + install(EXPORT VISPTargets + DESTINATION "${VISP_INSTALL_BINARIES_PREFIX}static${CMAKE_INSTALL_LIBDIR}" + FILE VISPTargets.cmake + COMPONENT libraries) + endif() + + install(FILES + "${VISP_CMAKE_MODULE_PATH}/VISPConfig.cmake" + "${VISP_BINARY_DIR}/win-install/VISPConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_PREFIX}" + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE + COMPONENT libraries) +endif() diff --git a/CMakeModules/VISPGeneratePkgConfigScript.cmake b/CMakeModules/VISPGeneratePkgConfigScript.cmake new file mode 100644 index 0000000000000000000000000000000000000000..f33c2b2cd99fb6c98ed17d710a38fbd2a7737a45 --- /dev/null +++ b/CMakeModules/VISPGeneratePkgConfigScript.cmake @@ -0,0 +1,332 @@ +############################################################################# +# +# $Id: VISPGeneratePkgConfigScript.cmake 5314 2015-02-12 08:32:30Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# This file generates the ViSP library config shell scripts: +# - visp-config in <build dir>/bin from visp-config.in +# - visp-config in <build dir>/install from visp-config.install.in +# When make install, this file is copied in <install dir>/bin +# - visp.pc in <build dir>/install from visp.pc.in +# When make install, this file is copied in <install dir>/lib/pkgconfig +# +# Authors: +# Fabien Spindler +# +############################################################################# + + +if (UNIX) + ####################################################################### + # + # for Unix platforms: Linux, OSX + # + ####################################################################### + set(FILE_VISP_CONFIG_SCRIPT_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.in") + set(FILE_VISP_CONFIG_SCRIPT "${BINARY_OUTPUT_PATH}/visp-config") + + set(FILE_VISP_CONFIG_SCRIPT_INSTALL_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.install.in") + set(FILE_VISP_CONFIG_SCRIPT_INSTALL "${VISP_BINARY_DIR}/unix-install/visp-config") + + set(FILE_VISP_CONFIG_PC_INSTALL_IN "${VISP_SOURCE_DIR}/CMakeModules/visp.pc.in") + set(FILE_VISP_CONFIG_PC_INSTALL "${VISP_BINARY_DIR}/unix-install/visp.pc") + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_SCRIPT_PREFIX + #---------------------------------------------------------------------- + set(VISP_CONFIG_SCRIPT_PREFIX "${CMAKE_INSTALL_PREFIX}") + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_CFLAGS + #---------------------------------------------------------------------- + foreach(INCDIR ${VISP_EXTERN_INCLUDE_DIRS}) + list(APPEND VISP_CONFIG_CFLAGS "-I${INCDIR}") + endforeach() + + # Suppress twins + if(VISP_CONFIG_CFLAGS) + list(REMOVE_DUPLICATES VISP_CONFIG_CFLAGS) + endif() + + # Format the string to suppress CMake separators ";" + set(VISP_CONFIG_CFLAGS_REFORMATED "") + foreach(element ${VISP_CONFIG_CFLAGS}) + set(VISP_CONFIG_CFLAGS_REFORMATED "${VISP_CONFIG_CFLAGS_REFORMATED} ${element}") + endforeach() + set(VISP_CONFIG_CFLAGS ${VISP_CONFIG_CFLAGS_REFORMATED}) +# message(": ${VISP_CONFIG_CFLAGS}") + + if(BUILD_TEST_COVERAGE) + # Add build options for test coverage. Currently coverage is only supported + # on gcc compiler + # Because using -fprofile-arcs with shared lib can cause problems like: + # hidden symbol `__bb_init_func', we add this option only for static + # library build + set(VISP_CONFIG_CFLAGS "${VISP_CONFIG_CFLAGS} -ftest-coverage -fprofile-arcs") + endif() + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_LIBS + # + # add "-l" to library names + # skip *.so, *.a, *.dylib, -framework*, -l* + # + #---------------------------------------------------------------------- + + # need to be improved + if(POLICY CMP0026) + cmake_policy(PUSH) + cmake_policy(SET CMP0026 OLD) + get_target_property(visp_libpath ${VISP_INTERN_LIBRARY} LOCATION_Release) + cmake_policy(POP) + else() + get_target_property(visp_libpath ${VISP_INTERN_LIBRARY} LOCATION_Release) + endif() + #message("ViSP libpath: ${vip_libpath}") + get_filename_component(visp_libname "${visp_libpath}" NAME) + + #message("ViSP libname: ${visp_libname}") + # Manage the libs + list(REMOVE_ITEM VISP_EXTERN_LIBRARIES "debug") + list(REMOVE_ITEM VISP_EXTERN_LIBRARIES "optimized") + set(TMP_LIBS) + foreach(lib ${VISP_EXTERN_LIBRARIES}) + if("${lib}" MATCHES "[-][f][r][a][m][e][w][o][r][k]+.") + # does nothing + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES ".[.][f][r][a][m][e][w][o][r][k]+$") + # replace /path/name.framework by -framework name + get_filename_component(FRAMEWORK ${lib} NAME_WE) + #message("add -framework ${FRAMEWORK}") + list(APPEND TMP_LIBS "-framework ${FRAMEWORK}") + elseif("${lib}" MATCHES ".[.][s][o]+$" OR "${lib}" MATCHES ".[.][a]+$") + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES ".[.][s][o][.][0123456789]+$") + # does nothing + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES ".[.][s][o][.][0123456789]*[.][0123456789]+$") + # does nothing + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES ".[.][s][o][.][0123456789]*[.][0123456789]*[.][0123456789]+$") + # does nothing + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES ".[.][d][y][l][i][b]+$") + # does nothing + list(APPEND TMP_LIBS ${lib}) + elseif("${lib}" MATCHES "^(-l)") + # does nothing + list(APPEND TMP_LIBS ${lib}) + else() + # add -l prefix + #MESSAGE("add -l${lib}") + list(APPEND TMP_LIBS "${lib}") + endif() + endforeach() + + foreach(val ${TMP_LIBS}) + set(VISP_CONFIG_LIBS "${VISP_CONFIG_LIBS} ${val}") + endforeach(val) + + #--------------------------------------------------------------------- + # Updates the <build dir>/bin/visp-config shell script + # Updates VISP_CONFIG_LIBS_SCRIPT (for visp-config) + # Updates VISP_CONFIG_CFLAGS_SCRIPT (for visp-config) + #---------------------------------------------------------------------- + + # prepend with ViSP own include dir + set(VISP_CONFIG_CFLAGS_SCRIPT "-I$PREFIX/${CMAKE_INSTALL_INCLUDEDIR} ${VISP_CONFIG_CFLAGS}") + + # prepend with ViSP own lib + set(VISP_CONFIG_LIBS_SCRIPT "$PREFIX/${CMAKE_INSTALL_LIBDIR}/${visp_libname} ${VISP_CONFIG_LIBS}") + + set(VISP_ECHO_NO_NEWLINE_CHARACTER "") + set(VISP_ECHO_NO_NEWLINE_OPTION "") + if(APPLE) + set(VISP_ECHO_NO_NEWLINE_CHARACTER "\\c") + else() + set(VISP_ECHO_NO_NEWLINE_OPTION "-n") + endif() + + configure_file(${FILE_VISP_CONFIG_SCRIPT_IN} ${FILE_VISP_CONFIG_SCRIPT}) + + #--------------------------------------------------------------------- + # Updates the <build dir>/install/visp-config shell script + #---------------------------------------------------------------------- + + configure_file(${FILE_VISP_CONFIG_SCRIPT_INSTALL_IN} ${FILE_VISP_CONFIG_SCRIPT_INSTALL}) + + #--------------------------------------------------------------------- + # Updates the <build dir>/install/visp.pc pkg-config file + # Updates VISP_CONFIG_CFLAGS_PC (for libvisp.pc used by pkg-config) + # Updates VISP_CONFIG_LIBS_PC (for libvisp.pc used by pkg-config) + #---------------------------------------------------------------------- + set(exec_prefix "\${prefix}") + set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}") + + # prepend with ViSP own include dir + set(VISP_CONFIG_CFLAGS_PC "-I\${includedir} ${VISP_CONFIG_CFLAGS}") + + # prepend with ViSP own lib dir and append -L<lib dir> + set(VISP_CONFIG_LIBS_PC "\${libdir}/${visp_libname} ${VISP_CONFIG_LIBS}") + configure_file(${FILE_VISP_CONFIG_PC_INSTALL_IN} ${FILE_VISP_CONFIG_PC_INSTALL}) + +else(UNIX) + ####################################################################### + # + # for windows platforms + # + ####################################################################### + set(FILE_VISP_CONFIG_SCRIPT_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.bat.in") + set(FILE_VISP_CONFIG_SCRIPT "${BINARY_OUTPUT_PATH}/visp-config.bat") + + set(FILE_VISP_CONFIG_SCRIPT_INSTALL_IN "${VISP_SOURCE_DIR}/CMakeModules/visp-config.bat.in") + set(FILE_VISP_CONFIG_SCRIPT_INSTALL "${VISP_BINARY_DIR}/win-install/visp-config-${VISP_ARCH}-${VISP_RUNTIME}.bat") + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_SCRIPT_PREFIX + #---------------------------------------------------------------------- + set(VISP_CONFIG_SCRIPT_PREFIX "${CMAKE_INSTALL_PREFIX}") + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_SCRIPT_DEF + #---------------------------------------------------------------------- + set(VISP_CONFIG_SCRIPT_DEFS "") + set(VISP_OPENMP_SUPPORT "no") + if(NOT ${VISP_OPENMP_FLAGS} STREQUAL "") + set(VISP_CONFIG_SCRIPT_DEFS "${VISP_OPENMP_FLAGS}") + set(VISP_OPENMP_SUPPORT "yes") + endif() + if(NOT ${VISP_CPP11_FLAGS} STREQUAL "") + set(VISP_CONFIG_SCRIPT_DEFS "${VISP_CPP11_FLAGS}, ${VISP_CONFIG_SCRIPT_DEFS}") + endif() + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_SCRIPT_INCLUDE + #---------------------------------------------------------------------- + list(APPEND VISP_EXTERN_INCLUDE_DIRS "%PREFIX%/${CMAKE_INSTALL_INCLUDEDIR}") + list(REMOVE_DUPLICATES VISP_EXTERN_INCLUDE_DIRS) + + # Format the string + set(VISP_CONFIG_SCRIPT_INC ${VISP_EXTERN_INCLUDE_DIRS}) + #message(VISP_CONFIG_SCRIPT_INC ${VISP_CONFIG_SCRIPT_INC}) + + #--------------------------------------------------------------------- + # Updates VISP_CONFIG_SCRIPT_LIBDIR + # 1/ For usage with the build tree + # 2/ For usage with the install tree + # + # and updates VISP_CONFIG_SCRIPT_LIBS_${config} + #---------------------------------------------------------------------- + set(TMP_SCRIPT_LIBS_DEBUG "${VISP_INTERN_LIBRARY}${VISP_DLLVERSION}${VISP_DEBUG_POSTFIX}.lib") + set(TMP_SCRIPT_LIBS_OPTIMIZED "${VISP_INTERN_LIBRARY}${VISP_DLLVERSION}.lib") + + #MESSAGE(VISP_EXTERN_LIBRARIES: ${VISP_EXTERN_LIBRARIES}) + set(TMP_IS_DEBUG FALSE) + set(TMP_IS_OPTIMIZED FALSE) + foreach(lib ${VISP_EXTERN_LIBRARIES}) + if("${lib}" MATCHES "[d][e][b][u][g]") + set(TMP_IS_DEBUG TRUE) + elseif("${lib}" MATCHES "[o][p][t][i][m][i][z][e][d]") + set(TMP_IS_OPTIMIZED TRUE) + else() + # Get the library name + get_filename_component(libname ${lib} NAME) + if("${libname}" MATCHES ".+[.][l][i][b]" OR "${libname}" MATCHES ".+[.][L][i][b]") + #MESSAGE("${libname} matches .lib or .Lib") + else() + # We need to add .lib suffix + #MESSAGE("For ${libname} we add .lib suffix") + set(libname "${libname}.lib") + endif() + + # Get the library path + get_filename_component(libpath ${lib} PATH) + list(APPEND VISP_CONFIG_SCRIPT_LIBDIR_ "${libpath}") + + if(TMP_IS_DEBUG) + set(TMP_IS_DEBUG FALSE) + list(APPEND TMP_SCRIPT_LIBS_DEBUG ${libname}) + elseif(TMP_IS_OPTIMIZED) + set(TMP_IS_OPTIMIZED FALSE) + list(APPEND TMP_SCRIPT_LIBS_OPTIMIZED ${libname}) + else() + list(APPEND TMP_SCRIPT_LIBS_DEBUG ${libname}) + list(APPEND TMP_SCRIPT_LIBS_OPTIMIZED ${libname}) + endif() + endif() + endforeach(lib) + + # Format the string + set(VISP_CONFIG_SCRIPT_LIBS_DEBUG "${TMP_SCRIPT_LIBS_DEBUG}") + set(VISP_CONFIG_SCRIPT_LIBS_OPTIMIZED "${TMP_SCRIPT_LIBS_OPTIMIZED}") + + # Format the string + string(REGEX REPLACE "lib/Release" "lib/$(ConfigurationName)" VISP_CONFIG_SCRIPT_LIBDIR_ "${VISP_CONFIG_SCRIPT_LIBDIR_}") + string(REGEX REPLACE "lib/Debug" "lib/$(ConfigurationName)" VISP_CONFIG_SCRIPT_LIBDIR_ "${VISP_CONFIG_SCRIPT_LIBDIR_}") + + # 1/ For usage with the build tree + set(VISP_CONFIG_SCRIPT_LIBDIR "%PREFIX%/lib") + list(APPEND VISP_CONFIG_SCRIPT_LIBDIR "%PREFIX%/lib/$(ConfigurationName)") + list(APPEND VISP_CONFIG_SCRIPT_LIBDIR ${VISP_CONFIG_SCRIPT_LIBDIR_}) + list(REMOVE_DUPLICATES VISP_CONFIG_SCRIPT_LIBDIR) + configure_file(${FILE_VISP_CONFIG_SCRIPT_IN} ${FILE_VISP_CONFIG_SCRIPT}) + + # 2/ For usage with the install tree + set(VISP_CONFIG_SCRIPT_LIBDIR "%PREFIX%/${VISP_ARCH}/${VISP_RUNTIME}/lib") + list(APPEND VISP_CONFIG_SCRIPT_LIBDIR ${VISP_CONFIG_SCRIPT_LIBDIR_}) + list(REMOVE_DUPLICATES VISP_CONFIG_SCRIPT_LIBDIR) + configure_file(${FILE_VISP_CONFIG_SCRIPT_INSTALL_IN} ${FILE_VISP_CONFIG_SCRIPT_INSTALL}) +endif(UNIX) + +#---------------------------------------------------------------------- +# customize install target +#---------------------------------------------------------------------- +# install rule for visp-config shell script +install(FILES ${FILE_VISP_CONFIG_SCRIPT_INSTALL} + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + OWNER_WRITE + COMPONENT libraries) + +# install rule for visp.pc pkg-config file +if(UNIX) + install(FILES ${FILE_VISP_CONFIG_PC_INSTALL} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + OWNER_WRITE + COMPONENT libraries) +else() + # not implemented yet +endif() + + diff --git a/CMakeModules/VISPUse.cmake.in b/CMakeModules/VISPUse.cmake.in index 0d04dd8495a745655f60ff2992cc8a3413ff0137..36ab38f845875325b47193bdc934166d72da0f3a 100644 --- a/CMakeModules/VISPUse.cmake.in +++ b/CMakeModules/VISPUse.cmake.in @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: VISPUse.cmake.in 4057 2013-01-05 13:10:29Z fspindle $ +# $Id: VISPUse.cmake.in 4616 2014-01-24 18:43:25Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,26 +38,12 @@ # ############################################################################# -# Add extra build flags to be able to consider especially for OpenMP -list(APPEND CMAKE_CXX_FLAGS "@VISP_OPENMP_FLAGS@") -list(APPEND CMAKE_CXX_FLAGS "@VISP_CPP11_FLAGS@") - -# Remove duplicates compilation flags -separate_arguments(CMAKE_CXX_FLAGS) -list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS) -string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "common C++ build flags" FORCE) # Tell the compiler where to find ViSP's header files # and the third party headers we depend on -INCLUDE_DIRECTORIES(${VISP_INCLUDE_DIRS}) - -# Add the required definitions -ADD_DEFINITIONS(${VISP_DEFINITIONS}) +include_directories(${VISP_INCLUDE_DIRS}) # Tell the compiler where to find ViSP's libraries # and the third party libraries we depend on -LINK_DIRECTORIES(${VISP_LIBRARY_DIRS}) - -LINK_LIBRARIES(${VISP_LIBRARIES}) +link_libraries(${VISP_LIBRARIES}) diff --git a/CMakeModules/checkForItifg8Version.cpp b/CMakeModules/checkForItifg8Version.cpp deleted file mode 100644 index 8a96303c8a23a4caef18936cf0f50ab5f81c7188..0000000000000000000000000000000000000000 --- a/CMakeModules/checkForItifg8Version.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include <itifgExt.h> -#include <libitifg.h> - - -int main() -{ - struct iti_setup_t setup; - char file_name[FILENAME_MAX]; - int error; - int version = 0; - - strcpy (file_name, ITI_PFS_PREFIX); - strcat (file_name, ITI_PFS_STRING); - - if ((error = iti_parse_info (file_name, &setup))) - { - switch (error) - { - case -ITI_EARG: - fprintf (stderr, "read_procfs: Wrong call argument\n" - "(file_name:%s, setup%p).\n", file_name, &setup); - exit (-1); - break; - case -ITI_EFMT: - fprintf (stderr, "read_procfs: Wrong data format (%s%s).\n", - ITI_PFS_PREFIX, ITI_PFS_STRING); - exit (-1); - break; - case -ITI_EENT: - fprintf (stderr, "read_procfs: File not found (%s%s).\n", - ITI_PFS_PREFIX, ITI_PFS_STRING); - exit (-1); - break; - case -ITI_ESYS: - fprintf (stderr, "read_procfs: Error while system call (%s).\n", - strerror (errno)); - exit (-1); - break; - default: - fprintf (stderr, "read_procfs: Error not specified!\n"); - exit (-1); - } - } -// fprintf (stderr, "Driver version %0x = %d: %d.%d.%d-%d.\n", -// setup.version, setup.version, -// (setup.version & 0xFF000000) >> 24, -// (setup.version & 0x00FF0000) >> 16, -// (setup.version & 0x0000FF00) >> 8, -// setup.version & 0x000000FF); - - int major = (setup.version & 0xFF000000) >> 24; - int minor = (setup.version & 0x00FF0000) >> 16; - - return (major*10 + minor); - -} diff --git a/CMakeModules/cmake_uninstall.cmake.in b/CMakeModules/cmake_uninstall.cmake.in index e788ba1d63b87fe1f239a070998195aceafd4d87..fb9072b671d2bdb00d0560539f6247c29976c411 100644 --- a/CMakeModules/cmake_uninstall.cmake.in +++ b/CMakeModules/cmake_uninstall.cmake.in @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: cmake_uninstall.cmake.in 4057 2013-01-05 13:10:29Z fspindle $ +# $Id: cmake_uninstall.cmake.in 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/CMakeModules/resources.cfg.in b/CMakeModules/resources.cfg.in old mode 100755 new mode 100644 index 759b13ad465d4bd33af6c33000af957a78ea79ef..dc38028ea60fa14b05af69e1fc4cb1e653ac133d --- a/CMakeModules/resources.cfg.in +++ b/CMakeModules/resources.cfg.in @@ -1,11 +1,11 @@ # Resources required by the sample browser and most samples. [Essential] -Zip=@OGRE_MEDIA_DIR@/packs/SdkTrays.zip +#Zip=@OGRE_MEDIA_DIR@/packs/SdkTrays.zip # Resource locations to be added to the default path [General] FileSystem=@OGRE_MEDIA_DIR@ -FileSystem=@OGRE_MEDIA_DIR@/materials/programs +@OGRE_COMMENT_LINE@FileSystem=@OGRE_MEDIA_DIR@/materials/programs FileSystem=@OGRE_MEDIA_DIR@/materials/scripts FileSystem=@OGRE_MEDIA_DIR@/materials/textures FileSystem=@OGRE_MEDIA_DIR@/models diff --git a/CMakeModules/visp-config.bat.in b/CMakeModules/visp-config.bat.in index d75e3b1c091bdab18078e4f1cf2a27846def4e45..6c8167a2a1ea6010bba084da04fde55912679198 100755 --- a/CMakeModules/visp-config.bat.in +++ b/CMakeModules/visp-config.bat.in @@ -1,9 +1,9 @@ @rem ############################################################################# @rem # -@rem # $Id: visp-config.bat.in 4057 2013-01-05 13:10:29Z fspindle $ +@rem # $Id: visp-config.bat.in 4961 2014-11-14 13:06:26Z fspindle $ @rem # @rem # This file is part of the ViSP software. -@rem # Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +@rem # Copyright (C) 2005 - 2014 by INRIA. All rights reserved. @rem # @rem # This software is free software; you can redistribute it and/or @rem # modify it under the terms of the GNU General Public License @@ -55,11 +55,11 @@ set VERSION=${VISP_VERSION} set DEFS=${VISP_CONFIG_SCRIPT_DEFS} -set INCLUDE=${VISP_CONFIG_SCRIPT_INC} +set INCLUDE="${VISP_CONFIG_SCRIPT_INC}" set OPENMP_SUPPORT=${VISP_OPENMP_SUPPORT} -set LIBDIR=${VISP_CONFIG_SCRIPT_LIBDIR} +set LIBDIR="${VISP_CONFIG_SCRIPT_LIBDIR}" set LIBS_DEBUG=${VISP_CONFIG_SCRIPT_LIBS_DEBUG} @@ -106,7 +106,7 @@ for %%a in (%*) do ( if "%%a" == "--version" ( echo ViSP %VERSION% Visual Servoing Platform echo. - echo Copyright 2005 - 2013 Inria. All rights reserved. + echo Copyright 2005 - 2014 Inria. All rights reserved. goto END ) if "%%a" == "--dumpversion" ( @@ -118,7 +118,7 @@ for %%a in (%*) do ( :USAGE echo ViSP %VERSION% (Visual Servoing Platform) -echo Copyright (C) 2005 - 2013 Inria. All rights reserved. +echo Copyright (C) 2005 - 2014 Inria. All rights reserved. echo. echo Usage: %0 [--prefix] [--def] [--include] [--openmp] [--libpath] echo [--libs-debug] [--libs-optimized] [--version] [--dumpversion] [--help] diff --git a/CMakeModules/visp-config.in b/CMakeModules/visp-config.in index 7d39279afa5219820cd7cd03c81c2bc97af637e7..8f11a5a85aa1a134c37ab5647fadc10807931c44 100755 --- a/CMakeModules/visp-config.in +++ b/CMakeModules/visp-config.in @@ -2,10 +2,10 @@ ############################################################################# # -# $Id: visp-config.in 4203 2013-04-08 16:56:12Z fspindle $ +# $Id: visp-config.in 5043 2014-12-08 16:14:15Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -61,7 +61,7 @@ usage() cat <<EOF ViSP $VERSION (Visual Servoing Platform) -Copyright (C) 2005 - 2013 Inria. All rights reserved. +Copyright (C) 2005 - 2014 Inria. All rights reserved. Usage: $0 [--prefix] [--cflags] [--libs] [--version] [--dumpversion] [--help] @@ -80,7 +80,7 @@ EOF if ! test "$1"; then usage; - exit -1; + exit 0; fi; for arg in $@; do @@ -91,9 +91,9 @@ for arg in $@; do --version) echo "ViSP $VERSION (Visual Servoing Platform)" echo "" - echo "Copyright (C) 2005 - 2013 Inria. All rights reserved.";; + echo "Copyright (C) 2005 - 2014 Inria. All rights reserved.";; --dumpversion) echo $NO_NEWLINE_OPTION "$VERSION$NO_NEWLINE_CHARACTER";; - *) usage; exit -1 ;; + *) usage; exit 0 ;; esac; done; echo "" diff --git a/CMakeModules/visp-config.install.in b/CMakeModules/visp-config.install.in index e797f269b7774e5d661c2014d257fe618f45cfbf..6bd8a10344adaff748c8eb8d7f9016fa67d452ef 100755 --- a/CMakeModules/visp-config.install.in +++ b/CMakeModules/visp-config.install.in @@ -5,7 +5,7 @@ # $Id: visp-config.in 4057 2013-01-05 13:10:29Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -48,20 +48,14 @@ relpath=`(cd $relpath/..; pwd)` PREFIX=$relpath -CFLAGS="${VISP_CONFIG_CFLAGS_SCRIPT}" +CFLAGS_CMD=`pkg-config --cflags visp` +CFLAGS=$CFLAGS_CMD -LIBS="${VISP_CONFIG_LIBS_SCRIPT}" +LIBS_CMD=`pkg-config --libs visp` +LIBS=$LIBS_CMD -VERSION="${VISP_VERSION}" - -#CFLAGS_CMD=`pkg-config --cflags visp` -#CFLAGS=$CFLAGS_CMD - -#LIBS_CMD=`pkg-config --libs visp` -#LIBS=$LIBS_CMD - -#VERSION_CMD=`pkg-config --modversion visp` -#VERSION=$VERSION_CMD +VERSION_CMD=`pkg-config --modversion visp` +VERSION=$VERSION_CMD NO_NEWLINE_CHARACTER="${VISP_ECHO_NO_NEWLINE_CHARACTER}" NO_NEWLINE_OPTION="${VISP_ECHO_NO_NEWLINE_OPTION}" @@ -71,7 +65,7 @@ usage() cat <<EOF ViSP $VERSION (Visual Servoing Platform) -Copyright (C) 2005 - 2013 Inria. All rights reserved. +Copyright (C) 2005 - 2014 Inria. All rights reserved. Usage: $0 [--prefix] [--cflags] [--libs] [--version] [--dumpversion] [--help] @@ -90,7 +84,7 @@ EOF if ! test "$1"; then usage; - exit -1; + exit 0; fi; for arg in $@; do @@ -101,9 +95,9 @@ for arg in $@; do --version) echo "ViSP $VERSION (Visual Servoing Platform)" echo "" - echo "Copyright (C) 2005 - 2013 Inria. All rights reserved.";; + echo "Copyright (C) 2005 - 2014 Inria. All rights reserved.";; --dumpversion) echo $NO_NEWLINE_OPTION "$VERSION$NO_NEWLINE_CHARACTER";; - *) usage; exit -1 ;; + *) usage; exit 0 ;; esac; done; echo "" diff --git a/CMakeModules/visp.pc.in b/CMakeModules/visp.pc.in index 93ab39cf8b30ec9964d63dd61d6e338c4baa07c2..4594265e00382b6e78d5b5a3288dcb5e4f069ffc 100644 --- a/CMakeModules/visp.pc.in +++ b/CMakeModules/visp.pc.in @@ -7,7 +7,7 @@ includedir=@includedir@ Name: ViSP Description: Visual Servoing Platform -URL: http://www.irisa.fr/lagadic/visp +URL: http://team.inria.fr/lagadic/visp Version: ${VISP_VERSION} Libs: ${VISP_CONFIG_LIBS_PC} diff --git a/CMakeSourceFileList.cmake b/CMakeSourceFileList.cmake index b8e8a55ecdefaf3692c192cdc15132be1867bd4c..ec9a60af2e28a6e1930f9696e98a21a53180e922 100644 --- a/CMakeSourceFileList.cmake +++ b/CMakeSourceFileList.cmake @@ -3,7 +3,7 @@ # $Id: CMakeSourceFileList.cmake,v 1.29 2008-12-17 14:45:01 fspindle Exp $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -73,6 +73,14 @@ SET (SRC_COMPUTER_VISION computer-vision/pose-estimation/vpPoseVirtualVisualServoing.cpp ) +if(VISP_HAVE_ZBAR) + list(APPEND SRC_DETECTION detection/barcode/vpDetectorQRCode.cpp) +endif() + +if(VISP_HAVE_DMTX) + list(APPEND SRC_DETECTION detection/barcode/vpDetectorDataMatrixCode.cpp) +endif() + SET (SRC_EXCEPTION exceptions/vpException.cpp ) @@ -81,9 +89,6 @@ SET (SRC_DEVICE_FRAMEGRABBER device/framegrabber/disk/vpDiskGrabber.cpp ) -IF(VISP_HAVE_DC1394_1) - LIST(APPEND SRC_DEVICE_FRAMEGRABBER device/framegrabber/1394/vp1394Grabber.cpp) -ENDIF() IF(VISP_HAVE_DC1394_2) LIST(APPEND SRC_DEVICE_FRAMEGRABBER device/framegrabber/1394/vp1394TwoGrabber.cpp) ENDIF() @@ -117,12 +122,17 @@ SET (SRC_KEY_POINT key-point/vpBasicKeyPoint.cpp ) -IF(VISP_HAVE_OPENCV_NONFREE) - LIST(APPEND SRC_KEY_POINT key-point/vpKeyPointSurf.cpp) -ENDIF() -IF(VISP_HAVE_OPENCV) - LIST(APPEND SRC_KEY_POINT key-point/vpPlanarObjectDetector.cpp) - LIST(APPEND SRC_KEY_POINT key-point/vpFernClassifier.cpp) +if(VISP_HAVE_OPENCV_NONFREE AND OpenCV_VERSION VERSION_LESS 3.0.0) + list(APPEND SRC_KEY_POINT key-point/vpKeyPointSurf.cpp) +endif() +if(VISP_HAVE_OPENCV) + list(APPEND SRC_KEY_POINT key-point/vpPlanarObjectDetector.cpp) + list(APPEND SRC_KEY_POINT key-point/vpFernClassifier.cpp) + list (APPEND SRC_KEY_POINT key-point/vpKeyPoint.cpp) +endif() + +IF(VISP_HAVE_XML2) + LIST(APPEND SRC_KEY_POINT key-point/vpXmlConfigParserKeyPoint.cpp) ENDIF() IF(VISP_HAVE_LIBFREENECT_AND_DEPENDENCIES) @@ -180,6 +190,10 @@ IF(VISP_HAVE_LAPACK) LIST(APPEND SRC_MATH math/matrix/vpMatrix_cholesky.cpp) ENDIF() +IF(VISP_HAVE_XML2) + LIST(APPEND SRC_MATH math/transformation/vpXmlParserHomogeneousMatrix.cpp) +ENDIF() + SET (SRC_ROBOT robot/robot/vpRobot.cpp robot/robot/vpRobotTemplate.cpp @@ -216,9 +230,6 @@ IF(VISP_HAVE_BICLOPS) LIST(APPEND SRC_ROBOT robot/real-robot/biclops/vpRobotBiclopsController.cpp) LIST(APPEND SRC_ROBOT robot/real-robot/biclops/vpRobotBiclops.cpp) ENDIF() -IF(VISP_HAVE_CYCAB) - LIST(APPEND SRC_ROBOT robot/real-robot/cycab/vpRobotCycab.cpp) -ENDIF() IF(VISP_HAVE_PTU46) LIST(APPEND SRC_ROBOT robot/real-robot/ptu46/vpRobotPtu46.cpp) ENDIF() @@ -233,7 +244,6 @@ IF(VISP_HAVE_VIPER850) ENDIF() SET (SRC_SERVO - servo/vpAdaptativeGain.cpp servo/vpAdaptiveGain.cpp servo/vpServo.cpp servo/vpServoData.cpp @@ -243,23 +253,23 @@ SET (SRC_SERVO SET (SRC_SIMULATOR simulator/image-simulator/vpImageSimulator.cpp simulator/wireframe-simulator/vpWireFrameSimulator.cpp - simulator/wireframe-simulator/core/vpArit.c - simulator/wireframe-simulator/core/vpAritio.c - simulator/wireframe-simulator/core/vpBound.c - simulator/wireframe-simulator/core/vpBoundio.c - simulator/wireframe-simulator/core/vpClipping.c - simulator/wireframe-simulator/core/vpDisplay.c - simulator/wireframe-simulator/core/vpKeyword.c - simulator/wireframe-simulator/core/vpLex.c - simulator/wireframe-simulator/core/vpMyio.c - simulator/wireframe-simulator/core/vpParser.c - simulator/wireframe-simulator/core/vpProjection.c - simulator/wireframe-simulator/core/vpRfstack.c - simulator/wireframe-simulator/core/vpSkipio.c - simulator/wireframe-simulator/core/vpTmstack.c - simulator/wireframe-simulator/core/vpToken.c - simulator/wireframe-simulator/core/vpViewio.c - simulator/wireframe-simulator/core/vpVwstack.c + simulator/wireframe-simulator/core/vpArit.cpp + simulator/wireframe-simulator/core/vpAritio.cpp + simulator/wireframe-simulator/core/vpBound.cpp + simulator/wireframe-simulator/core/vpBoundio.cpp + simulator/wireframe-simulator/core/vpClipping.cpp + simulator/wireframe-simulator/core/vpCoreDisplay.cpp + simulator/wireframe-simulator/core/vpKeyword.cpp + simulator/wireframe-simulator/core/vpLex.cpp + simulator/wireframe-simulator/core/vpMyio.cpp + simulator/wireframe-simulator/core/vpParser.cpp + simulator/wireframe-simulator/core/vpProjection.cpp + simulator/wireframe-simulator/core/vpRfstack.cpp + simulator/wireframe-simulator/core/vpSkipio.cpp + simulator/wireframe-simulator/core/vpTmstack.cpp + simulator/wireframe-simulator/core/vpToken.cpp + simulator/wireframe-simulator/core/vpViewio.cpp + simulator/wireframe-simulator/core/vpVwstack.cpp ) IF(VISP_HAVE_X11 OR VISP_HAVE_GDI OR VISP_HAVE_OPENCV OR VISP_HAVE_D3D9 OR VISP_HAVE_GTK) list(APPEND SRC_SIMULATOR simulator/coin-simulator/vpProjectionDisplay.cpp) @@ -274,6 +284,7 @@ IF(VISP_HAVE_OGRE) ENDIF() SET (SRC_TOOLS + tools/convert/vpConvert.cpp tools/geometry/vpPlane.cpp tools/geometry/vpRect.cpp tools/geometry/vpTriangle.cpp @@ -325,10 +336,12 @@ SET (SRC_TRACKING tracking/moving-edges/vpMeNurbs.cpp tracking/mbt/vpMbTracker.cpp + tracking/mbt/vpMbtPolygon.cpp tracking/mbt/edge/vpMbEdgeTracker.cpp + tracking/mbt/edge/vpMbtDistanceCircle.cpp tracking/mbt/edge/vpMbtDistanceCylinder.cpp tracking/mbt/edge/vpMbtDistanceLine.cpp - tracking/mbt/edge/vpMbtPolygon.cpp + tracking/mbt/edge/vpMbtMeEllipse.cpp tracking/mbt/edge/vpMbtMeLine.cpp tracking/moments/vpMoment.cpp @@ -343,19 +356,41 @@ SET (SRC_TRACKING tracking/moments/vpMomentGravityCenter.cpp tracking/moments/vpMomentGravityCenterNormalized.cpp tracking/moments/vpMomentObject.cpp - ) -IF(VISP_HAVE_XML2) - LIST(APPEND SRC_TRACKING tracking/mbt/edge/vpMbtXmlParser.cpp) - LIST(APPEND SRC_TRACKING tracking/mbt/klt/vpMbtKltXmlParser.cpp) -ENDIF() + tracking/template-tracker/vpTemplateTracker.cpp + tracking/template-tracker/ssd/vpTemplateTrackerSSD.cpp + tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.cpp + tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.cpp + tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.cpp + tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.cpp + tracking/template-tracker/zncc/vpTemplateTrackerZNCC.cpp + tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.cpp + tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.cpp + tracking/template-tracker/tools/vpTemplateTrackerBSpline.cpp + tracking/template-tracker/tools/vpTemplateTrackerZone.cpp + tracking/template-tracker/tools/vpTemplateTrackerTriangle.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarp.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.cpp + tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.cpp + ) -IF(VISP_HAVE_OPENCV) - LIST(APPEND SRC_TRACKING tracking/klt/vpKltOpencv.cpp) - LIST(APPEND SRC_TRACKING tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp) - LIST(APPEND SRC_TRACKING tracking/mbt/klt/vpMbtKltPolygon.cpp) - LIST(APPEND SRC_TRACKING tracking/mbt/klt/vpMbKltTracker.cpp) -ENDIF() +if(VISP_HAVE_XML2) + list(APPEND SRC_TRACKING tracking/mbt/vpMbXmlParser.cpp) + list(APPEND SRC_TRACKING tracking/mbt/edge/vpMbtXmlParser.cpp) + list(APPEND SRC_TRACKING tracking/mbt/klt/vpMbtKltXmlParser.cpp) + list(APPEND SRC_TRACKING tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.cpp) +endif() + +if(VISP_HAVE_OPENCV) + list(APPEND SRC_TRACKING detection/face/vpDetectorFace.cpp) + list(APPEND SRC_TRACKING tracking/klt/vpKltOpencv.cpp) + list(APPEND SRC_TRACKING tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp) + list(APPEND SRC_TRACKING tracking/mbt/klt/vpMbtDistanceKltPoints.cpp) + list(APPEND SRC_TRACKING tracking/mbt/klt/vpMbKltTracker.cpp) +endif() SET (SRC_VIDEO video/vpVideoReader.cpp @@ -433,6 +468,7 @@ SET (SRC_ALL ${SRC_CAMERA} ${SRC_COMPUTER_VISION} ${SRC_EXCEPTION} + ${SRC_DETECTION} ${SRC_DEVICE_DISPLAY} ${SRC_DEVICE_FRAMEGRABBER} ${SRC_DEVICE_KINECT} diff --git a/CTestConfig.cmake b/CTestConfig.cmake index 1a01ddb6ae67d6283af3cdad7a70b46ad27aa9b1..e35b40d7588b85ea2f73f424274d8adf44b3c265 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -3,7 +3,7 @@ # $Id: CTestConfig.cmake,v 1.9 2008-12-11 13:19:44 fspindle Exp $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -59,6 +59,13 @@ ELSE(BUILDNAME) SET(BUILDNAME "${CMAKE_SYSTEM_NAME}") ENDIF(BUILDNAME) +# Add i386 or amd64 +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BUILDNAME "${BUILDNAME}-amd64") +else() + set(BUILDNAME "${BUILDNAME}-i386") +endif() + # Add the compiler name, e.g. "g++, msvc7..." if(MSVC70) set(BUILDNAME "${BUILDNAME}-msvc70") @@ -72,6 +79,8 @@ elseif(MSVC10) set(BUILDNAME "${BUILDNAME}-msvc10") elseif(MSVC11) set(BUILDNAME "${BUILDNAME}-msvc11") +elseif(MSVC12) + set(BUILDNAME "${BUILDNAME}-msvc12") elseif(MSVC) set(BUILDNAME "${BUILDNAME}-msvc") elseif(BORLAND) @@ -100,9 +109,9 @@ ENDIF(CMAKE_COMPILER_IS_GNUCC) # Add the type of library generation, e.g. "Dynamic or Static" IF(BUILD_SHARED_LIBS) - SET(BUILDNAME "${BUILDNAME}-Dynamic") + SET(BUILDNAME "${BUILDNAME}-Dyn") ELSE(BUILD_SHARED_LIBS) - SET(BUILDNAME "${BUILDNAME}-Static") + SET(BUILDNAME "${BUILDNAME}-Sta") ENDIF(BUILD_SHARED_LIBS) # Add the build type, e.g. "Debug, Release..." @@ -139,19 +148,15 @@ endif() #---- Framegrabers ---- # Firewire dc1394-2.x IF(VISP_HAVE_DC1394_2) - SET(BUILDNAME "${BUILDNAME}-dc1394.2") + SET(BUILDNAME "${BUILDNAME}-dc1394") ENDIF(VISP_HAVE_DC1394_2) -# Firewire dc1394-1.x -IF(VISP_HAVE_DC1394_1) - SET(BUILDNAME "${BUILDNAME}-dc1394.1") -ENDIF(VISP_HAVE_DC1394_1) # Video 4 linux 2 (V4L2) IF(VISP_HAVE_V4L2) SET(BUILDNAME "${BUILDNAME}-v4l2") ENDIF(VISP_HAVE_V4L2) # Directshow IF(VISP_HAVE_DIRECTSHOW) - SET(BUILDNAME "${BUILDNAME}-Directshow") + SET(BUILDNAME "${BUILDNAME}-dshow") ENDIF(VISP_HAVE_DIRECTSHOW) IF(VISP_HAVE_CMU1394) SET(BUILDNAME "${BUILDNAME}-CMU1394") @@ -160,10 +165,9 @@ IF(VISP_HAVE_LIBFREENECT) SET(BUILDNAME "${BUILDNAME}-freenect") ENDIF() IF(VISP_HAVE_LIBUSB_1) - SET(BUILDNAME "${BUILDNAME}-usb1") + SET(BUILDNAME "${BUILDNAME}-usb") ENDIF() - #---- Video-devices ---- # X11 IF(VISP_HAVE_X11) @@ -182,9 +186,17 @@ IF(VISP_HAVE_D3D9) SET(BUILDNAME "${BUILDNAME}-Direct3D") ENDIF(VISP_HAVE_D3D9) # OpenCV -IF(VISP_HAVE_OPENCV) - SET(BUILDNAME "${BUILDNAME}-OpenCV") -ENDIF(VISP_HAVE_OPENCV) +if(VISP_HAVE_OPENCV) + if(OpenCV_VERSION) + if(OPENCV_XFEATURES2D_FOUND OR OPENCV_XFEATURES2D_FOUND) + set(BUILDNAME "${BUILDNAME}-OpenCV_contrib${OpenCV_VERSION}") + else() + set(BUILDNAME "${BUILDNAME}-OpenCV${OpenCV_VERSION}") + endif() + else() + set(BUILDNAME "${BUILDNAME}-OpenCV") + endif() +endif(VISP_HAVE_OPENCV) #---- Mathematics ---- # Lapack (Linear Algebra PACKage) @@ -230,10 +242,10 @@ IF(VISP_HAVE_FFMPEG) SET(BUILDNAME "${BUILDNAME}-ffmpeg") ENDIF(VISP_HAVE_FFMPEG) IF(VISP_HAVE_LIBJPEG) - SET(BUILDNAME "${BUILDNAME}-libjpeg") + SET(BUILDNAME "${BUILDNAME}-jpeg") ENDIF(VISP_HAVE_LIBJPEG) IF(VISP_HAVE_LIBPNG) - SET(BUILDNAME "${BUILDNAME}-libpng") + SET(BUILDNAME "${BUILDNAME}-png") ENDIF(VISP_HAVE_LIBPNG) IF(VISP_HAVE_ZLIB) SET(BUILDNAME "${BUILDNAME}-zlib") @@ -241,16 +253,37 @@ ENDIF() #---- Misc ---- # XML -IF(VISP_HAVE_XML2) - SET(BUILDNAME "${BUILDNAME}-xml") -ENDIF(VISP_HAVE_XML2) +if(VISP_HAVE_XML2) + set(BUILDNAME "${BUILDNAME}-xml") +endif() # PThread -IF(VISP_HAVE_PTHREAD) - SET(BUILDNAME "${BUILDNAME}-pthread") -ENDIF(VISP_HAVE_PTHREAD) +if(VISP_HAVE_PTHREAD) + set(BUILDNAME "${BUILDNAME}-pthread") +endif() # OpenMP -IF(VISP_HAVE_OPENMP) - SET(BUILDNAME "${BUILDNAME}-OpenMP") -ENDIF() +if(VISP_HAVE_OPENMP) + set(BUILDNAME "${BUILDNAME}-OpenMP") +endif() +if(VISP_HAVE_DMTX) + set(BUILDNAME "${BUILDNAME}-dmtx") +endif() +if(VISP_HAVE_ZBAR) + set(BUILDNAME "${BUILDNAME}-zbar") +endif() + + +#---- Special compiler flags ---- +if(ACTIVATE_WARNING_STRICT_OVERFLOW) + SET(BUILDNAME "${BUILDNAME}-Wov") +endif() +if(ACTIVATE_WARNING_FLOAT_EQUAL) + SET(BUILDNAME "${BUILDNAME}-Weq") +endif() +if(USE_CPP11) + SET(BUILDNAME "${BUILDNAME}-c11") +endif() +if(MOMENTS_COMBINE_MATRICES) + SET(BUILDNAME "${BUILDNAME}-Moment") +endif() #MESSAGE("BUILDNAME=${BUILDNAME}") diff --git a/ChangeLog b/ChangeLog index 5a19e429a68ef8d49976c0bcbc858b4313985e92..9d8a0a7ba34de62a8320db1d6a58085f4580eb5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,10 +2,131 @@ Visual Servoing Platform Resume of the change log - Copyright Projet Lagadic / IRISA-INRIA Rennes, 2005-2013 - http://www.irisa.fr/lagadic + Copyright Projet Lagadic / IRISA-INRIA Rennes, 2005 - 2015 + http://team.inria.fr/lagadic +ViSP 2.10.0 (released February 13th, 2015) + - New features + . New object detection and localisation capabilities that allow to + initialize for instance the model-based tracker (see tutorial). + . New bar code detectors that uses libdmtx and libzbar (see tutorial): + + vpDetectorQRcode class allows to detect QR codes + + vpDetectorDataMatrixCode class allows to detect data matrices + . New vpDetectorFace class that allows face detection based on OpenCV + Haar cascade classifier capabilities (see tutorial) + . New vpConvert class for type conversions from OpenCV to ViSP. + . Model-based tracker: + + Introduction of 3D circles that project as ellipses. + + In *.cao files comments are allowed on lines starting with # character. + + New functions getError() and getRobustWeights() useful to compute + tracking quality indicators. + + Introduction of setLod() for "level of detail" that checks if the size + of the feature is large enough to be considered by the tracker. + + Based on the rank of the interaction matrix turn off automatically the + degrees of freedom that cannot be estimated. Especially useful when + considering 3D objets with only cylinders and circles. + + and many other improvements and fixes. + . Video reader and writer support mpeg4. ffmpeg is no more requested + in vpVideoReader and vpVideoWriter. If ffmpeg is not detected or installed + we use OpenCV. + . New vpXmlParserHomogeneousMatrix class to read/write homogeneous + transformations from/to xml files + . Auto detection of visp-images-data Debian or Ubuntu package that allows + testing without setting VISP_INPUT_IMAGE_PATH + . If OpenCV version >= 2.4.8 use only OpenCV C++ api. Classes that use + IplImages as input are deprecated. + . Compat with RaspberryPi architecture + . Compat with OpenCV 3.x + . Compat with Yarp 2.3.63 + . Compat with CMake 3.x + . Compat with MinGW w64 from http://mingw-w64.sourceforge.net/ + - Tutorials + . New tutorial to show how to install ViSP from Ubuntu official packages. + . New tutorial to show how to install ViSP on CentOS. + . New tutorial to show how to install ViSP on openSUSE. + . New tutorial to show how to install ViSP on Raspberry Pi. + . New tutorial to show how to install ViSP on Windows 8.1 with msvc. + . New tutorial to show how to install ViSP on Windows 8.1 with mingw. + . New keypoints matching tutorial. + . New bar code detection tutorial. + . New face detection tutorial. + . New object detection and localization tutorial + . Update KLT tracker tutorial to show how to initialize the tracker online. + . Update blob tracker tutorial to show how to use v4l2 live camera. + . Update model-based tracking tutorial for advanced usage. + - Bug fixed + . [17112] Error after second call to track() in vpMbEdgeTracker + . [17212] vp1394TwoGrabber: unable to select a camera from its guid + . [17457] Cannot apply a secondary task to a 7dof robot + . [17550] Build error with ffmpeg libraries + . [17620] vpXmlParserCamera::save() doesn't save the camera name and + image size + . [17644] vpDisplayX doesn't work on RaspberryPi + . [17659] ViSP+Ogre3D+boost: libboost_chrono cannot be opened + . [17705] Bad endianness support in vpDisplayX + . [17766] Compat with cmake 3.0.0: failed due to new CMP0026 policy + . [17972] Ogre examples are not working with Fedora 20 + . [17983] Unable to build ViSP when an older version was previously + installed + . [18258] Stack overflow when using vpDot under Windows + . [16895] undefined reference to `vpGaussRand::gaussianDraw()' + . [18406] Infinite loop in `vpPose::poseRansac()' + . [18446] Bad KLT points detection in vpMbKtlTracker and vpMbEdgeKltTracker. + . [18451] Infinite loop in vpSimulatorViper850::stopMotion() + . [18452] Empty filename check in vpImageIo, vpVideoReader, vpVideoWriter is erroneous + . [18459] Error when initializing KLT related trackers but no key point is deteted. + . [18471] Bad fov computation in vpCameraParameters. + . [18550] Linking error with Homebrew install of OpenCV on MacOS + +---------------------------------------------- +ViSP 2.9.0 (released February 18th, 2014) + - New features + . Introduction of a new template tracker using SSD (vpTemplateTrackerSSD + classes) or ZNCC (vpTemplateTrackerZNCC classes). These trackers are able + to estimate 2D transformations such as translation, SRT (scale, rotation + and translation), affine and homography. A video + illustrating this tracker capabilities is available on: + http://team.inria.fr/lagadic/visp/computer-vision.html + . In vpServo introduction of a new controller able to ensure continuous + sequencing + - Improvements + . Add missing const to member functions and arguments passed by reference. + . Introduction of a new option BUILD_WITH_STATIC_CRT that allows to set + /MT (default) or /MD msvc build option + . Compatibility with mingw, msvc11 (2012), msvc12 (2013) + and open solaris 11 + . Trace and debug can now be enabled/disabled using CMake + ACTIVATE_DEBUG_TRACE option + . Fix errors detected by Covery Scan static code analyser + . Fix warnings detected with Gnu g++ -Weffc++ -Wshadow compiler flags + . Improve a lot of classes + - New examples + . New example that shows how to use the template tracker + . A lot of new sample code provided in tutorial folder + - New tutorials + . Installation from source on Linux Fedora + . Image filtering + . Template tracking + . Homography estimation from points + . Keypoint matching + . How to boost your visual servo control law + . Debug and trace printings + - Bug fixed + . [16263] libopencv_nonfree for Surf not detected by CMake. + . [16307] vpVideoReader::setLastFrameIndex() doesn't work. + . [16371] No convergence in SVDcmp + . [16465] FTBFS on armhf, ppc, s390: Errors while running CTest + . [16696] Build with OpenCV fails with msvc (/MT conflicts with /MD) + . [16781] Unable to open .pgm saved with Matlab imwrite + . [16823] Problems with camera calibration code + . [16889] Link error with CMU 1394 on win x64 arch + . [16895] undefined reference to `vpGaussRand::gaussianDraw()' + . [16919] Unable to link with OpenCV 2.4.8 under Windows + . [16924] MinGW: unable to build ViSP as a shared library + . [16928] vpVideoReader is unable to find the number of the first image + from a generic sequence of images +---------------------------------------------- ViSP 2.8.0 (released July 24th, 2013) - New features . New camera calibration tool: example/camera_calibration.cpp diff --git a/INSTALL.txt b/INSTALL.txt index e5d7bb24e3176fb3816fd22711b53e58e1547441..f39d4f03891e6dafe5c70757faa11b6612c3067b 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,7 +1,7 @@ ViSP-2.7.0 Visual Servoing Platform - Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + Copyright (C) 2005 - 2014 by INRIA. All rights reserved. www: http://www.irisa.fr/lagadic diff --git a/README.txt b/README.txt index 39ca54d415303f365dd0a09c6cfaccf05e36afd6..db3a910ec463154ce4edcae894c997bb256d5213 100644 --- a/README.txt +++ b/README.txt @@ -1,7 +1,7 @@ - ViSP-2.7.0 + ViSP-2.10.0 Visual Servoing Platform - Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + Copyright (C) 2005 - 2014 by INRIA. All rights reserved. www: http://www.irisa.fr/lagadic diff --git a/ViSP-third-party.txt.cmake b/ViSP-third-party.txt.cmake index 7bcfbfb89840d86c3b3419c38fde112a8f68aa00..f8c19ead381a6f08267f2a62904a034e2ea25acc 100644 --- a/ViSP-third-party.txt.cmake +++ b/ViSP-third-party.txt.cmake @@ -25,15 +25,13 @@ Robots Pioneer : ${VISP_HAVE_PIONEER_FOUND} Viper S650 : ${VISP_HAVE_VIPER650_FOUND} Viper S850 : ${VISP_HAVE_VIPER850_FOUND} - Cycab : ${VISP_HAVE_CYCAB_FOUND} -Video devices (display) +Video devices (display) X11 : ${VISP_HAVE_X11_FOUND} GTK : ${VISP_HAVE_GTK_FOUND} OpenCV : ${VISP_HAVE_OPENCV_FOUND} GDI : ${VISP_HAVE_GDI_FOUND} Direct3D : ${VISP_HAVE_D3D9_FOUND} Framegrabbers - Firewire libdc1394-1.x : ${VISP_HAVE_DC1394_1_FOUND} Firewire libdc1394-2.x : ${VISP_HAVE_DC1394_2_FOUND} Video For Linux Two : ${VISP_HAVE_V4L2_FOUND} DirectShow : ${VISP_HAVE_DIRECTSHOW_FOUND} @@ -53,6 +51,8 @@ Misc: XML2 : ${VISP_HAVE_XML2_FOUND} pthread : ${VISP_HAVE_PTHREAD_FOUND} OpenMP : ${VISP_HAVE_OPENMP_FOUND} + zbar : ${VISP_HAVE_ZBAR_FOUND} + dmtx : ${VISP_HAVE_DMTX_FOUND} Documentation: Doxygen : ${VISP_HAVE_DOXYGEN_FOUND} Graphviz dot : ${VISP_HAVE_DOT_FOUND} diff --git a/data/ogre-simulator/media/materials/scripts/Examples.material b/data/ogre-simulator/media/materials/scripts/Examples.material new file mode 100644 index 0000000000000000000000000000000000000000..306da3ee015b3f7b9316bc41bbb456ca5d5ca4a6 --- /dev/null +++ b/data/ogre-simulator/media/materials/scripts/Examples.material @@ -0,0 +1,33 @@ + + +material Examples/Robot +{ + // Software blending technique + technique + { + pass + { + + texture_unit + { + texture r2skin.jpg + } + } + } +} + +material Examples/GrassFloor +{ + technique + { + pass + { + texture_unit + { + texture grass_1024.jpg + } + } + } +} + + diff --git a/data/ogre-simulator/media/materials/textures/grass_1024.jpg b/data/ogre-simulator/media/materials/textures/grass_1024.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e65691b3ade1d22b29624f885015865c6d3aa4b6 Binary files /dev/null and b/data/ogre-simulator/media/materials/textures/grass_1024.jpg differ diff --git a/data/ogre-simulator/media/materials/textures/r2skin.jpg b/data/ogre-simulator/media/materials/textures/r2skin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3e83a1246c0ee2f665e7b6df2ee92313e7ad2681 Binary files /dev/null and b/data/ogre-simulator/media/materials/textures/r2skin.jpg differ diff --git a/data/ogre-simulator/media/models/robot.mesh b/data/ogre-simulator/media/models/robot.mesh new file mode 100644 index 0000000000000000000000000000000000000000..a191498e89d75db3c3509f56abdbd03af7bc002e Binary files /dev/null and b/data/ogre-simulator/media/models/robot.mesh differ diff --git a/data/ogre-simulator/media/models/robot.skeleton b/data/ogre-simulator/media/models/robot.skeleton new file mode 100644 index 0000000000000000000000000000000000000000..0aa8bde538159fee69f9f86311d4191f0821bf9b Binary files /dev/null and b/data/ogre-simulator/media/models/robot.skeleton differ diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index 9793b560ad0c1f5ce9771ed8ec5a58dda1f1a5c6..3d98dfad389d052277f2603a5d5f7935b7cd2a36 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -49,7 +49,3 @@ SET (SRC_SUBDIRS # Build process propagation in the sub directories SUBDIRS(${SRC_SUBDIRS}) -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/demo/wireframe-simulator/CMakeLists.txt b/demo/wireframe-simulator/CMakeLists.txt index 2455aded1e09b30a756e37d992a96b233dd95237..ff71aa1addab651a1d4e75048f7293fab3b071c1 100644 --- a/demo/wireframe-simulator/CMakeLists.txt +++ b/demo/wireframe-simulator/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,47 +43,45 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set (SOURCE servoSimu4Points.cpp servoSimuCylinder.cpp servoSimuSphere.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_dependencies(visp_demos ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "demos") + endif() -IF(UNIX) - INSTALL(TARGETS ${binary} - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp/demo/wireframe-simulator - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - OWNER_WRITE) -ELSE() - INSTALL(TARGETS ${binary} - DESTINATION demo/wireframe-simulator - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ - OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - OWNER_WRITE) -ENDIF() - -ENDFOREACH(source) + if(UNIX) + install(TARGETS ${binary} + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/visp-${VISP_VERSION}/demo/wireframe-simulator + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + OWNER_WRITE) + else() + install(TARGETS ${binary} + DESTINATION demo/wireframe-simulator + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + OWNER_WRITE) + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(servoSimu4Points servoSimu4Points -d) -ADD_TEST(servoSimuCylinder servoSimuCylinder -d) -ADD_TEST(servoSimuSphere servoSimuSphere -d) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(servoSimu4Points servoSimu4Points -d) +add_test(servoSimuCylinder servoSimuCylinder -d) +add_test(servoSimuSphere servoSimuSphere -d) diff --git a/demo/wireframe-simulator/servoSimu4Points.cpp b/demo/wireframe-simulator/servoSimu4Points.cpp index 7436e7d401edcd29843d3b22b39fa7e37bbb6fe5..1e2fbbfeaa453e1464f2ed2b60ed430c8e810dc9 100644 --- a/demo/wireframe-simulator/servoSimu4Points.cpp +++ b/demo/wireframe-simulator/servoSimu4Points.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoSimu4Points.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoSimu4Points.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,6 +71,9 @@ #ifdef VISP_HAVE_DISPLAY +void usage(const char *name, std::string ipath, const char *badparam); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &display); + /*! Print the program options. @@ -132,17 +135,17 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, std::string &ipath, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'd': display = false; break; case 'h': usage(argv[0],ipath, NULL); return false; break; default: - usage(argv[0],ipath, optarg); + usage(argv[0],ipath, optarg_); return false; break; } } @@ -151,7 +154,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &display) // standalone param or error usage(argv[0], ipath, NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -162,36 +165,35 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - std::string opt_ipath; - std::string env_ipath; - std::string ipath ; - std::string filename; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_display) == false) { - exit (-1); - } - - vpImage<vpRGBa> Iint(480,640,255); - vpImage<vpRGBa> Iext1(480,640,255); - vpImage<vpRGBa> Iext2(480,640,255); + try { + bool opt_display = true; + std::string opt_ipath; + std::string env_ipath; + std::string ipath ; + std::string filename; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_display) == false) { + exit (-1); + } + + vpImage<vpRGBa> Iint(480,640,255); + vpImage<vpRGBa> Iext1(480,640,255); + vpImage<vpRGBa> Iext2(480,640,255); #if defined VISP_HAVE_X11 - vpDisplayX display[3]; + vpDisplayX display[3]; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display[3]; + vpDisplayOpenCV display[3]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[3]; + vpDisplayGDI display[3]; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display[3]; + vpDisplayD3D display[3]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[3]; + vpDisplayGTK display[3]; #endif - - if (opt_display) - { - try + + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(Iint, 100, 100,"The internal view") ; @@ -207,236 +209,145 @@ main(int argc, const char ** argv) vpDisplay::display(Iext2); vpDisplay::flush(Iext2); } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpServo task; - vpSimulatorCamera robot ; - float sampling_time = 0.040f; // Sampling period in second - robot.setSamplingTime(sampling_time); - - // Since the task gain lambda is very high, we need to increase default max velocities - robot.setMaxTranslationVelocity(10); - robot.setMaxRotationVelocity(vpMath::rad(180)); - - // Set initial position of the object in the camera frame - vpHomogeneousMatrix cMo(0,0.1,2.0,vpMath::rad(35),vpMath::rad(25),0); - // Set desired position of the object in the camera frame - vpHomogeneousMatrix cdMo(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); - // Set initial position of the object in the world frame - vpHomogeneousMatrix wMo(0.0,0.0,0.2,0,0,0); - // Position of the camera in the world frame - vpHomogeneousMatrix wMc, cMw; - wMc = wMo * cMo.inverse(); - cMw = wMc.inverse(); - - //The four point used as visual features - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1,0) ; - point[3].setWorldCoordinates(-0.1,0.1,0) ; - point[2].setWorldCoordinates(0.1,0.1,0) ; - point[1].setWorldCoordinates(0.1,-0.1,0) ; - - // Projection of the points - for (int i = 0 ; i < 4 ; i++) - point[i].track(cMo); - - //Set the current visual feature - vpFeaturePoint p[4]; - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i], point[i]); - - // Projection of the points - for (int i = 0 ; i < 4 ; i++) - point[i].track(cdMo); - - vpFeaturePoint pd[4]; - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(pd[i], point[i]); - - task.setServo(vpServo::EYEINHAND_L_cVe_eJe); - task.setInteractionMatrixType(vpServo::DESIRED); - - vpHomogeneousMatrix cMe; // Identity - vpVelocityTwistMatrix cVe(cMe); - task.set_cVe(cVe); - - vpMatrix eJe; - robot.get_eJe(eJe); - task.set_eJe(eJe); - - for (int i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - - task.setLambda(10); - - std::list<vpImageSimulator> list; - vpImageSimulator imsim; - - vpColVector X[4]; - for (int i = 0; i < 4; i++) X[i].resize(3); - X[0][0] = -0.2; - X[0][1] = -0.2; - X[0][2] = 0; - - X[1][0] = 0.2; - X[1][1] = -0.2; - X[1][2] = 0; - - X[2][0] = 0.2; - X[2][1] = 0.2; - X[2][2] = 0; - - X[3][0] = -0.2; - X[3][1] = 0.2; - X[3][2] = 0; - - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - if (! env_ipath.empty()) - ipath = env_ipath; - - if (!opt_ipath.empty()) - ipath = opt_ipath; - - filename = ipath + vpIoTools::path("/ViSP-images/mire/mire.pgm"); - - try - { - imsim.init(filename.c_str(), X); - } - catch(...) - { - vpTRACE("You need the ViSP data "); - task.kill(); - return 1; - } - - list.push_back(imsim); - - vpWireFrameSimulator sim; - - // Set the scene - sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD,list); - - // Initialize simulator frames - sim.set_fMo( wMo ); // Position of the object in the world reference frame - sim.setCameraPositionRelObj(cMo) ; // initial position of the camera - sim.setDesiredCameraPosition(cdMo); // desired position of the camera - - // Set the External camera position - vpHomogeneousMatrix camMf(vpHomogeneousMatrix(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0)); - sim.setExternalCameraPosition(camMf); - - //Computes the position of a camera which is fixed in the object frame - vpHomogeneousMatrix camoMf(0,0.0,1.5,0,vpMath::rad(140),0); - camoMf = camoMf*(sim.get_fMo().inverse()); - - //Set the parameters of the cameras (internal and external) - vpCameraParameters camera(1000,1000,320,240); - sim.setInternalCameraParameters(camera); - sim.setExternalCameraParameters(camera); - - int stop = 10; - - if (opt_display) - { - stop = 2500; - - //Get the internal and external views - sim.getInternalImage(Iint); - sim.getExternalImage(Iext1); - sim.getExternalImage(Iext2, camoMf); - - //Display the object frame (current and desired position) - vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); - - //Display the object frame the world reference frame and the camera frame - vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,camMf,camera,0.2,vpColor::none); - - //Display the world reference frame and the object frame - vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext2,camoMf*sim.get_fMo(),camera,0.05,vpColor::none); - - vpDisplay::flush(Iint); - vpDisplay::flush(Iext1); - vpDisplay::flush(Iext2); - - std::cout << "Click on a display" << std::endl; - while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext1,false) && !vpDisplay::getClick(Iext2,false)){}; - } + vpServo task; + vpSimulatorCamera robot ; + float sampling_time = 0.040f; // Sampling period in second + robot.setSamplingTime(sampling_time); + + // Since the task gain lambda is very high, we need to increase default max velocities + robot.setMaxTranslationVelocity(10); + robot.setMaxRotationVelocity(vpMath::rad(180)); + + // Set initial position of the object in the camera frame + vpHomogeneousMatrix cMo(0,0.1,2.0,vpMath::rad(35),vpMath::rad(25),0); + // Set desired position of the object in the camera frame + vpHomogeneousMatrix cdMo(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); + // Set initial position of the object in the world frame + vpHomogeneousMatrix wMo(0.0,0.0,0.2,0,0,0); + // Position of the camera in the world frame + vpHomogeneousMatrix wMc, cMw; + wMc = wMo * cMo.inverse(); + cMw = wMc.inverse(); + + //The four point used as visual features + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1,0) ; + point[3].setWorldCoordinates(-0.1,0.1,0) ; + point[2].setWorldCoordinates(0.1,0.1,0) ; + point[1].setWorldCoordinates(0.1,-0.1,0) ; + + // Projection of the points + for (int i = 0 ; i < 4 ; i++) + point[i].track(cMo); - robot.setPosition( wMc ); - //Print the task - task.print() ; + //Set the current visual feature + vpFeaturePoint p[4]; + for (int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i], point[i]); - int iter = 0; - vpColVector v ; + // Projection of the points + for (int i = 0 ; i < 4 ; i++) + point[i].track(cdMo); - while(iter++ < stop) - { - if (opt_display) - { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext1) ; - vpDisplay::display(Iext2) ; - } + vpFeaturePoint pd[4]; + for (int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(pd[i], point[i]); + + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED); - double t = vpTime::measureTimeMs(); + vpHomogeneousMatrix cMe; // Identity + vpVelocityTwistMatrix cVe(cMe); + task.set_cVe(cVe); - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); - robot.getPosition(wMc) ; - cMo = wMc.inverse() * wMo; for (int i = 0 ; i < 4 ; i++) - { - point[i].track(cMo) ; - vpFeatureBuilder::create(p[i],point[i]) ; - } + task.addFeature(p[i],pd[i]) ; + + task.setLambda(10); - v = task.computeControlLaw() ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - - //Compute the movement of the object around the world reference frame. - vpHomogeneousMatrix a(0, 0, 0.2, 0, 0, 0); - vpHomogeneousMatrix b(0, 0, 0, vpMath::rad(1.5*iter), 0, 0); - vpHomogeneousMatrix c(0, 0, 0, 0, vpMath::rad(2.5*iter), 0); + std::list<vpImageSimulator> list; + vpImageSimulator imsim; - // Move the object in the world frame - wMo = b*c*a; + vpColVector X[4]; + for (int i = 0; i < 4; i++) X[i].resize(3); + X[0][0] = -0.2; + X[0][1] = -0.2; + X[0][2] = 0; - sim.set_fMo( wMo ); //Move the object in the simulator - sim.setCameraPositionRelObj(cMo); + X[1][0] = 0.2; + X[1][1] = -0.2; + X[1][2] = 0; - //Compute the position of the external view which is fixed in the object frame - camoMf.buildFrom(0,0.0,1.5,0,vpMath::rad(150),0); + X[2][0] = 0.2; + X[2][1] = 0.2; + X[2][2] = 0; + + X[3][0] = -0.2; + X[3][1] = 0.2; + X[3][2] = 0; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + if (! env_ipath.empty()) + ipath = env_ipath; + + if (!opt_ipath.empty()) + ipath = opt_ipath; + + filename = vpIoTools::createFilePath(ipath, "ViSP-images/mire/mire.pgm"); + + imsim.init(filename.c_str(), X); + + list.push_back(imsim); + + vpWireFrameSimulator sim; + + // Set the scene + sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD,list); + + // Initialize simulator frames + sim.set_fMo( wMo ); // Position of the object in the world reference frame + sim.setCameraPositionRelObj(cMo) ; // initial position of the camera + sim.setDesiredCameraPosition(cdMo); // desired position of the camera + + // Set the External camera position + vpHomogeneousMatrix camMf(vpHomogeneousMatrix(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0)); + sim.setExternalCameraPosition(camMf); + + //Computes the position of a camera which is fixed in the object frame + vpHomogeneousMatrix camoMf(0,0.0,1.5,0,vpMath::rad(140),0); camoMf = camoMf*(sim.get_fMo().inverse()); + //Set the parameters of the cameras (internal and external) + vpCameraParameters camera(1000,1000,320,240); + sim.setInternalCameraParameters(camera); + sim.setExternalCameraParameters(camera); + + int stop = 10; + if (opt_display) { + stop = 2500; + //Get the internal and external views sim.getInternalImage(Iint); sim.getExternalImage(Iext1); - sim.getExternalImage(Iext2,camoMf); + sim.getExternalImage(Iext2, camoMf); //Display the object frame (current and desired position) vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); - //Display the camera frame, the object frame the world reference frame - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition(),camera,0.2,vpColor::none); + //Display the object frame the world reference frame and the camera frame + vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,camMf,camera,0.2,vpColor::none); //Display the world reference frame and the object frame vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); @@ -445,17 +356,97 @@ main(int argc, const char ** argv) vpDisplay::flush(Iint); vpDisplay::flush(Iext1); vpDisplay::flush(Iext2); + + std::cout << "Click on a display" << std::endl; + while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext1,false) && !vpDisplay::getClick(Iext2,false)){}; } - vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + robot.setPosition( wMc ); + //Print the task + task.print() ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + int iter = 0; + vpColVector v ; - task.print() ; - task.kill() ; + while(iter++ < stop) + { + if (opt_display) + { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext1) ; + vpDisplay::display(Iext2) ; + } + + double t = vpTime::measureTimeMs(); + + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + robot.getPosition(wMc) ; + cMo = wMc.inverse() * wMo; + for (int i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + vpFeatureBuilder::create(p[i],point[i]) ; + } + + v = task.computeControlLaw() ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + //Compute the movement of the object around the world reference frame. + vpHomogeneousMatrix a(0, 0, 0.2, 0, 0, 0); + vpHomogeneousMatrix b(0, 0, 0, vpMath::rad(1.5*iter), 0, 0); + vpHomogeneousMatrix c(0, 0, 0, 0, vpMath::rad(2.5*iter), 0); + + // Move the object in the world frame + wMo = b*c*a; + + sim.set_fMo( wMo ); //Move the object in the simulator + sim.setCameraPositionRelObj(cMo); + + //Compute the position of the external view which is fixed in the object frame + camoMf.buildFrom(0,0.0,1.5,0,vpMath::rad(150),0); + camoMf = camoMf*(sim.get_fMo().inverse()); + + if (opt_display) + { + //Get the internal and external views + sim.getInternalImage(Iint); + sim.getExternalImage(Iext1); + sim.getExternalImage(Iext2,camoMf); + + //Display the object frame (current and desired position) + vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); + + //Display the camera frame, the object frame the world reference frame + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition(),camera,0.2,vpColor::none); + + //Display the world reference frame and the object frame + vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext2,camoMf*sim.get_fMo(),camera,0.05,vpColor::none); + + vpDisplay::flush(Iint); + vpDisplay::flush(Iext1); + vpDisplay::flush(Iext2); + } + + vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + task.print() ; + task.kill() ; - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int diff --git a/demo/wireframe-simulator/servoSimuCylinder.cpp b/demo/wireframe-simulator/servoSimuCylinder.cpp index 0dc0fa0181e9274b54fe376fbb7fad193513f322..6e3fd3522514d3c2f1d7557ec01776e2ac65d61b 100644 --- a/demo/wireframe-simulator/servoSimuCylinder.cpp +++ b/demo/wireframe-simulator/servoSimuCylinder.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoSimuCylinder.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoSimuCylinder.cpp 4659 2014-02-09 14:11:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,6 +71,8 @@ #define GETOPTARGS "dh" #ifdef VISP_HAVE_DISPLAY +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &display); /*! @@ -126,16 +128,16 @@ OPTIONS: \n\ */ bool getOptions(int argc, const char **argv, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -144,7 +146,7 @@ bool getOptions(int argc, const char **argv, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -155,31 +157,30 @@ bool getOptions(int argc, const char **argv, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - - // Read the command line options - if (getOptions(argc, argv, opt_display) == false) { - exit (-1); - } - - vpImage<vpRGBa> Iint(480,640,255); - vpImage<vpRGBa> Iext(480,640,255); + try { + bool opt_display = true; + + // Read the command line options + if (getOptions(argc, argv, opt_display) == false) { + exit (-1); + } + + vpImage<vpRGBa> Iint(480,640,255); + vpImage<vpRGBa> Iext(480,640,255); #if defined VISP_HAVE_X11 - vpDisplayX display[2]; + vpDisplayX display[2]; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display[2]; + vpDisplayOpenCV display[2]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[2]; + vpDisplayGDI display[2]; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display[2]; + vpDisplayD3D display[2]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[2]; + vpDisplayGTK display[2]; #endif - - if (opt_display) - { - try + + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(Iint, 100, 100,"The internal view") ; @@ -191,197 +192,82 @@ main(int argc, const char ** argv) vpDisplay::display(Iext); vpDisplay::flush(Iext); } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpServo task; - vpRobotCamera robot ; - float sampling_time = 0.040f; // Sampling period in second - robot.setSamplingTime(sampling_time); - - // Set initial position of the object in the camera frame - vpHomogeneousMatrix cMo(0,0.1,0.3,vpMath::rad(35),vpMath::rad(25),vpMath::rad(75)); - // Set desired position of the object in the camera frame - vpHomogeneousMatrix cdMo(0.0,0.0,0.5,vpMath::rad(90),vpMath::rad(0),vpMath::rad(0)); - // Set initial position of the object in the world frame - vpHomogeneousMatrix wMo(0.0,0.0,0,0,0,0); - // Position of the camera in the world frame - vpHomogeneousMatrix wMc, cMw; - wMc = wMo * cMo.inverse(); - cMw = wMc.inverse(); - - // Create a cylinder - vpCylinder cylinder(0,0,1,0,0,0,0.1); - - // Projection of the cylinder - cylinder.track(cMo); - - //Set the current visual feature - vpFeatureLine l[2]; - vpFeatureBuilder::create(l[0], cylinder, vpCylinder::line1); - vpFeatureBuilder::create(l[1], cylinder, vpCylinder::line2); - - // Projection of the cylinder - cylinder.track(cdMo); - - vpFeatureLine ld[2]; - vpFeatureBuilder::create(ld[0], cylinder, vpCylinder::line1); - vpFeatureBuilder::create(ld[1], cylinder, vpCylinder::line2); - - task.setServo(vpServo::EYEINHAND_L_cVe_eJe); - task.setInteractionMatrixType(vpServo::DESIRED); - - vpHomogeneousMatrix cMe; - vpVelocityTwistMatrix cVe(cMe); - task.set_cVe(cVe); - - vpMatrix eJe; - robot.get_eJe(eJe); - task.set_eJe(eJe); - - for (int i = 0 ; i < 2 ; i++) - task.addFeature(l[i],ld[i]) ; - - task.setLambda(1); - - vpWireFrameSimulator sim; - - // Set the scene - sim.initScene(vpWireFrameSimulator::CYLINDER, vpWireFrameSimulator::D_STANDARD); - - // Initialize simulator frames - sim.set_fMo( wMo ); // Position of the object in the world reference frame - sim.setCameraPositionRelObj(cMo) ; // initial position of the camera - sim.setDesiredCameraPosition(cdMo); // desired position of the camera - - // Set the External camera position - vpHomogeneousMatrix camMf(vpHomogeneousMatrix(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0)); - sim.setExternalCameraPosition(camMf); - - // Set the parameters of the cameras (internal and external) - vpCameraParameters camera(1000,1000,320,240); - sim.setInternalCameraParameters(camera); - sim.setExternalCameraParameters(camera); - - int stop = 10; - - if (opt_display) - { - stop = 2500; + vpServo task; + vpRobotCamera robot ; + float sampling_time = 0.040f; // Sampling period in second + robot.setSamplingTime(sampling_time); + + // Set initial position of the object in the camera frame + vpHomogeneousMatrix cMo(0,0.1,0.3,vpMath::rad(35),vpMath::rad(25),vpMath::rad(75)); + // Set desired position of the object in the camera frame + vpHomogeneousMatrix cdMo(0.0,0.0,0.5,vpMath::rad(90),vpMath::rad(0),vpMath::rad(0)); + // Set initial position of the object in the world frame + vpHomogeneousMatrix wMo(0.0,0.0,0,0,0,0); + // Position of the camera in the world frame + vpHomogeneousMatrix wMc, cMw; + wMc = wMo * cMo.inverse(); + cMw = wMc.inverse(); + + // Create a cylinder + vpCylinder cylinder(0,0,1,0,0,0,0.1); + + // Projection of the cylinder + cylinder.track(cMo); + + //Set the current visual feature + vpFeatureLine l[2]; + vpFeatureBuilder::create(l[0], cylinder, vpCylinder::line1); + vpFeatureBuilder::create(l[1], cylinder, vpCylinder::line2); - //Get the internal and external views - sim.getInternalImage(Iint); - sim.getExternalImage(Iext); + // Projection of the cylinder + cylinder.track(cdMo); - //Display the object frame (current and desired position) - vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); + vpFeatureLine ld[2]; + vpFeatureBuilder::create(ld[0], cylinder, vpCylinder::line1); + vpFeatureBuilder::create(ld[1], cylinder, vpCylinder::line2); - //Display the object frame the world reference frame and the camera frame - vpDisplay::displayFrame(Iext,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext,camMf*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext,camMf,camera,0.2,vpColor::none); - - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); - - std::cout << "Click on a display" << std::endl; - while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext,false)){}; - } - - robot.setPosition( cMw ); - - //Print the task - task.print(); - - int iter = 0; - vpColVector v ; - - //Set the secondary task parameters - vpColVector e1(6) ; e1 = 0 ; - vpColVector e2(6) ; e2 = 0 ; - vpColVector proj_e1 ; - vpColVector proj_e2 ; - iter = 0; - double rapport = 0; - double vitesse = 0.3; - int tempo = 600; - - while(iter++ < stop) - { - if (opt_display) - { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - } + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED); - double t = vpTime::measureTimeMs(); + vpHomogeneousMatrix cMe; + vpVelocityTwistMatrix cVe(cMe); + task.set_cVe(cVe); - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); - robot.getPosition(cMw) ; - cMo = cMw * wMo; + for (int i = 0 ; i < 2 ; i++) + task.addFeature(l[i],ld[i]) ; - cylinder.track(cMo) ; - vpFeatureBuilder::create(l[0], cylinder, vpCylinder::line1); - vpFeatureBuilder::create(l[1], cylinder, vpCylinder::line2); + task.setLambda(1); - v = task.computeControlLaw() ; - - //Compute the velocity with the secondary task - if ( iter%tempo < 200 && iter%tempo >= 0) - { - e2 = 0; - e1[0] = -fabs(vitesse) ; - proj_e1 = task.secondaryTask(e1); - rapport = -vitesse/proj_e1[0]; - proj_e1 *= rapport ; - v += proj_e1 ; - } + vpWireFrameSimulator sim; - if ( iter%tempo < 300 && iter%tempo >= 200) - { - e1 = 0; - e2[1] = -fabs(vitesse) ; - proj_e2 = task.secondaryTask(e2); - rapport = -vitesse/proj_e2[1]; - proj_e2 *= rapport ; - v += proj_e2 ; - } + // Set the scene + sim.initScene(vpWireFrameSimulator::CYLINDER, vpWireFrameSimulator::D_STANDARD); - if ( iter%tempo < 500 && iter%tempo >= 300) - { - e2 = 0; - e1[0] = -fabs(vitesse) ; - proj_e1 = task.secondaryTask(e1); - rapport = vitesse/proj_e1[0]; - proj_e1 *= rapport ; - v += proj_e1 ; - } + // Initialize simulator frames + sim.set_fMo( wMo ); // Position of the object in the world reference frame + sim.setCameraPositionRelObj(cMo) ; // initial position of the camera + sim.setDesiredCameraPosition(cdMo); // desired position of the camera - if ( iter%tempo < 600 && iter%tempo >= 500) - { - e1 = 0; - e2[1] = -fabs(vitesse) ; - proj_e2 = task.secondaryTask(e2); - rapport = vitesse/proj_e2[1]; - proj_e2 *= rapport ; - v += proj_e2 ; - } + // Set the External camera position + vpHomogeneousMatrix camMf(vpHomogeneousMatrix(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0)); + sim.setExternalCameraPosition(camMf); - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Set the parameters of the cameras (internal and external) + vpCameraParameters camera(1000,1000,320,240); + sim.setInternalCameraParameters(camera); + sim.setExternalCameraParameters(camera); - // Update the simulator frames - sim.set_fMo( wMo ); // This line is not really requested since the object doesn't move - sim.setCameraPositionRelObj( cMo ); + int stop = 10; if (opt_display) { + stop = 2500; + //Get the internal and external views sim.getInternalImage(Iint); sim.getExternalImage(Iext); @@ -391,23 +277,137 @@ main(int argc, const char ** argv) vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); //Display the object frame the world reference frame and the camera frame - vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition(),camera,0.2,vpColor::none);; + vpDisplay::displayFrame(Iext,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext,camMf*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext,camMf,camera,0.2,vpColor::none); vpDisplay::flush(Iint); vpDisplay::flush(Iext); + + std::cout << "Click on a display" << std::endl; + while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext,false)){}; } - vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + robot.setPosition( cMw ); - std::cout << "|| s - s* || = " << (task.getError() ).sumSquare() <<std::endl ; - } + //Print the task + task.print(); + + int iter = 0; + vpColVector v ; + + //Set the secondary task parameters + vpColVector e1(6) ; e1 = 0 ; + vpColVector e2(6) ; e2 = 0 ; + vpColVector proj_e1 ; + vpColVector proj_e2 ; + iter = 0; + double rapport = 0; + double vitesse = 0.3; + int tempo = 600; - task.print() ; - task.kill() ; + while(iter++ < stop) + { + if (opt_display) + { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + } + + double t = vpTime::measureTimeMs(); + + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + robot.getPosition(cMw) ; + cMo = cMw * wMo; + + cylinder.track(cMo) ; + vpFeatureBuilder::create(l[0], cylinder, vpCylinder::line1); + vpFeatureBuilder::create(l[1], cylinder, vpCylinder::line2); + + v = task.computeControlLaw() ; + + //Compute the velocity with the secondary task + if ( iter%tempo < 200 && iter%tempo >= 0) + { + e2 = 0; + e1[0] = -fabs(vitesse) ; + proj_e1 = task.secondaryTask(e1); + rapport = -vitesse/proj_e1[0]; + proj_e1 *= rapport ; + v += proj_e1 ; + } + + if ( iter%tempo < 300 && iter%tempo >= 200) + { + e1 = 0; + e2[1] = -fabs(vitesse) ; + proj_e2 = task.secondaryTask(e2); + rapport = -vitesse/proj_e2[1]; + proj_e2 *= rapport ; + v += proj_e2 ; + } + + if ( iter%tempo < 500 && iter%tempo >= 300) + { + e2 = 0; + e1[0] = -fabs(vitesse) ; + proj_e1 = task.secondaryTask(e1); + rapport = vitesse/proj_e1[0]; + proj_e1 *= rapport ; + v += proj_e1 ; + } + + if ( iter%tempo < 600 && iter%tempo >= 500) + { + e1 = 0; + e2[1] = -fabs(vitesse) ; + proj_e2 = task.secondaryTask(e2); + rapport = vitesse/proj_e2[1]; + proj_e2 *= rapport ; + v += proj_e2 ; + } + + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + // Update the simulator frames + sim.set_fMo( wMo ); // This line is not really requested since the object doesn't move + sim.setCameraPositionRelObj( cMo ); + + if (opt_display) + { + //Get the internal and external views + sim.getInternalImage(Iint); + sim.getExternalImage(Iext); + + //Display the object frame (current and desired position) + vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); + + //Display the object frame the world reference frame and the camera frame + vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext,sim.getExternalCameraPosition(),camera,0.2,vpColor::none);; + + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + } + + vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + + std::cout << "|| s - s* || = " << (task.getError() ).sumSquare() <<std::endl ; + } + + task.print() ; + task.kill() ; - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int diff --git a/demo/wireframe-simulator/servoSimuSphere.cpp b/demo/wireframe-simulator/servoSimuSphere.cpp index fce1d042d6d97bfb67dd815ec0e152e52760372e..b26af02a42ad1852e1ff42641f178f1b1acf1b1d 100644 --- a/demo/wireframe-simulator/servoSimuSphere.cpp +++ b/demo/wireframe-simulator/servoSimuSphere.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoSimuSphere.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoSimuSphere.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,6 +74,11 @@ #ifdef VISP_HAVE_DISPLAY +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &display); +void computeVisualFeatures(const vpSphere &sphere, vpGenericFeature &s); +void computeInteractionMatrix(const vpGenericFeature &s,const vpSphere &sphere, vpMatrix &L); + /*! Print the program options. @@ -119,16 +124,16 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -137,7 +142,7 @@ bool getOptions(int argc, const char **argv, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -157,7 +162,7 @@ bool getOptions(int argc, const char **argv, bool &display) with h2 = (gx²+gy²)/(4*n20*gy²+4*n02*gx²-8n11gxgy) with n20,n02,n11 the second order moments of the sphere */ -void computeVisualFeatures(const vpSphere sphere, vpGenericFeature &s) +void computeVisualFeatures(const vpSphere &sphere, vpGenericFeature &s) { double gx = sphere.get_x(); double gy = sphere.get_y(); @@ -185,7 +190,7 @@ void computeVisualFeatures(const vpSphere sphere, vpGenericFeature &s) with I3 the 3x3 identity matrix with [s]x the skew matrix related to s */ -void computeInteractionMatrix(const vpGenericFeature s,const vpSphere sphere, vpMatrix &L) +void computeInteractionMatrix(const vpGenericFeature &s,const vpSphere &sphere, vpMatrix &L) { L = 0; L[0][0] = -1/sphere.getR(); @@ -208,32 +213,31 @@ void computeInteractionMatrix(const vpGenericFeature s,const vpSphere sphere, vp int main(int argc, const char ** argv) { - bool opt_display = true; - - // Read the command line options - if (getOptions(argc, argv, opt_display) == false) { - exit (-1); - } - - vpImage<vpRGBa> Iint(480,640,255); - vpImage<vpRGBa> Iext1(480,640,255); - vpImage<vpRGBa> Iext2(480,640,255); + try { + bool opt_display = true; + + // Read the command line options + if (getOptions(argc, argv, opt_display) == false) { + exit (-1); + } + + vpImage<vpRGBa> Iint(480,640,255); + vpImage<vpRGBa> Iext1(480,640,255); + vpImage<vpRGBa> Iext2(480,640,255); #if defined VISP_HAVE_X11 - vpDisplayX display[3]; + vpDisplayX display[3]; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display[3]; + vpDisplayOpenCV display[3]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[3]; + vpDisplayGDI display[3]; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display[3]; + vpDisplayD3D display[3]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[3]; + vpDisplayGTK display[3]; #endif - - if (opt_display) - { - try + + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(Iint, 100, 100,"The internal view") ; @@ -249,162 +253,92 @@ main(int argc, const char ** argv) vpDisplay::display(Iext2); vpDisplay::flush(Iext2); } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpServo task; - vpRobotCamera robot ; - float sampling_time = 0.040f; // Sampling period in second - robot.setSamplingTime(sampling_time); - - // Since the task gain lambda is very high, we need to increase default max velocities - robot.setMaxTranslationVelocity(10); - robot.setMaxRotationVelocity(vpMath::rad(180)); - - // Set initial position of the object in the camera frame - vpHomogeneousMatrix cMo(0,0.1,2.0,vpMath::rad(35),vpMath::rad(25),0); - // Set desired position of the object in the camera frame - vpHomogeneousMatrix cdMo(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); - // Set initial position of the object in the world frame - vpHomogeneousMatrix wMo(0.0,0.0,0,0,0,0); - // Position of the camera in the world frame - vpHomogeneousMatrix wMc, cMw; - wMc = wMo * cMo.inverse(); - cMw = wMc.inverse(); - - robot.setPosition( cMw ); - - //The sphere - vpSphere sphere(0,0,0,0.15); - - // Projection of the sphere - sphere.track(cMo); + vpServo task; + vpRobotCamera robot ; + float sampling_time = 0.040f; // Sampling period in second + robot.setSamplingTime(sampling_time); - //Set the current visual feature - vpGenericFeature s(3); - computeVisualFeatures(sphere, s); - - // Projection of the points - sphere.track(cdMo); + // Since the task gain lambda is very high, we need to increase default max velocities + robot.setMaxTranslationVelocity(10); + robot.setMaxRotationVelocity(vpMath::rad(180)); - vpGenericFeature sd(3); - computeVisualFeatures(sphere, sd); - - vpMatrix L(3,6); - computeInteractionMatrix(sd,sphere,L); - sd.setInteractionMatrix(L); + // Set initial position of the object in the camera frame + vpHomogeneousMatrix cMo(0,0.1,2.0,vpMath::rad(35),vpMath::rad(25),0); + // Set desired position of the object in the camera frame + vpHomogeneousMatrix cdMo(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); + // Set initial position of the object in the world frame + vpHomogeneousMatrix wMo(0.0,0.0,0,0,0,0); + // Position of the camera in the world frame + vpHomogeneousMatrix wMc, cMw; + wMc = wMo * cMo.inverse(); + cMw = wMc.inverse(); - task.setServo(vpServo::EYEINHAND_L_cVe_eJe); - task.setInteractionMatrixType(vpServo::DESIRED); - - vpHomogeneousMatrix cMe; - vpVelocityTwistMatrix cVe(cMe); - task.set_cVe(cVe); + robot.setPosition( cMw ); - vpMatrix eJe; - robot.get_eJe(eJe); - task.set_eJe(eJe); + //The sphere + vpSphere sphere(0,0,0,0.15); - task.addFeature(s,sd) ; - - task.setLambda(7); - - vpWireFrameSimulator sim; - - // Set the scene - sim.initScene(vpWireFrameSimulator::SPHERE, vpWireFrameSimulator::D_STANDARD); + // Projection of the sphere + sphere.track(cMo); - // Initialize simulator frames - sim.set_fMo( wMo ); // Position of the object in the world reference frame - sim.setCameraPositionRelObj(cMo) ; // initial position of the camera - sim.setDesiredCameraPosition(cdMo); // desired position of the camera - - // Set the External camera position - vpHomogeneousMatrix camMf(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0); - sim.setExternalCameraPosition(camMf); - - // Computes the position of a camera which is fixed in the object frame - vpHomogeneousMatrix camoMf(0,0.0,2.5,0,vpMath::rad(140),0); - camoMf = camoMf*(sim.get_fMo().inverse()); - - // Set the parameters of the cameras (internal and external) - vpCameraParameters camera(1000,1000,320,240); - sim.setInternalCameraParameters(camera); - sim.setExternalCameraParameters(camera); - - int stop = 10; - - if (opt_display) - { - stop = 1000; - //Get the internal and external views - sim.getInternalImage(Iint); - sim.getExternalImage(Iext1); - sim.getExternalImage(Iext2,camoMf); - - //Display the object frame (current and desired position) - vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); - - //Display the object frame the world reference frame and the camera frame - vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,camMf,camera,0.2,vpColor::none); - - //Display the world reference frame and the object frame - vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext2,camoMf*sim.get_fMo(),camera,0.05,vpColor::none); - - vpDisplay::flush(Iint); - vpDisplay::flush(Iext1); - vpDisplay::flush(Iext2); + //Set the current visual feature + vpGenericFeature s(3); + computeVisualFeatures(sphere, s); - std::cout << "Click on a display" << std::endl; - while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext1,false) && !vpDisplay::getClick(Iext2,false)){}; - } + // Projection of the points + sphere.track(cdMo); - //Print the task - task.print() ; + vpGenericFeature sd(3); + computeVisualFeatures(sphere, sd); - int iter = 0; - vpColVector v ; + vpMatrix L(3,6); + computeInteractionMatrix(sd,sphere,L); + sd.setInteractionMatrix(L); - while(iter++ < stop) - { - if (opt_display) - { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext1) ; - vpDisplay::display(Iext2) ; - } + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED); - double t = vpTime::measureTimeMs(); + vpHomogeneousMatrix cMe; + vpVelocityTwistMatrix cVe(cMe); + task.set_cVe(cVe); - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); - robot.getPosition(cMw) ; - cMo = cMw * wMo; + task.addFeature(s,sd) ; - sphere.track(cMo); + task.setLambda(7); - //Set the current visual feature - computeVisualFeatures(sphere, s); + vpWireFrameSimulator sim; + + // Set the scene + sim.initScene(vpWireFrameSimulator::SPHERE, vpWireFrameSimulator::D_STANDARD); + + // Initialize simulator frames + sim.set_fMo( wMo ); // Position of the object in the world reference frame + sim.setCameraPositionRelObj(cMo) ; // initial position of the camera + sim.setDesiredCameraPosition(cdMo); // desired position of the camera - v = task.computeControlLaw() ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - sim.setCameraPositionRelObj(cMo); + // Set the External camera position + vpHomogeneousMatrix camMf(0.0,0,3.5,vpMath::rad(0),vpMath::rad(30),0); + sim.setExternalCameraPosition(camMf); - //Compute the position of the external view which is fixed in the object frame - camoMf.buildFrom(0,0.0,2.5,0,vpMath::rad(150),0); + // Computes the position of a camera which is fixed in the object frame + vpHomogeneousMatrix camoMf(0,0.0,2.5,0,vpMath::rad(140),0); camoMf = camoMf*(sim.get_fMo().inverse()); + // Set the parameters of the cameras (internal and external) + vpCameraParameters camera(1000,1000,320,240); + sim.setInternalCameraParameters(camera); + sim.setExternalCameraParameters(camera); + + int stop = 10; + if (opt_display) { + stop = 1000; //Get the internal and external views sim.getInternalImage(Iint); sim.getExternalImage(Iext1); @@ -414,10 +348,10 @@ main(int argc, const char ** argv) vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); - //Display the camera frame, the object frame the world reference frame - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); - vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition(),camera,0.2,vpColor::none); + //Display the object frame the world reference frame and the camera frame + vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,camMf*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,camMf,camera,0.2,vpColor::none); //Display the world reference frame and the object frame vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); @@ -426,17 +360,85 @@ main(int argc, const char ** argv) vpDisplay::flush(Iint); vpDisplay::flush(Iext1); vpDisplay::flush(Iext2); + + std::cout << "Click on a display" << std::endl; + while (!vpDisplay::getClick(Iint,false) && !vpDisplay::getClick(Iext1,false) && !vpDisplay::getClick(Iext2,false)){}; } - vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + //Print the task + task.print() ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + int iter = 0; + vpColVector v ; + + while(iter++ < stop) + { + if (opt_display) + { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext1) ; + vpDisplay::display(Iext2) ; + } + + double t = vpTime::measureTimeMs(); - task.print() ; - task.kill() ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; - return 0; + robot.getPosition(cMw) ; + cMo = cMw * wMo; + + sphere.track(cMo); + + //Set the current visual feature + computeVisualFeatures(sphere, s); + + v = task.computeControlLaw() ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + sim.setCameraPositionRelObj(cMo); + + //Compute the position of the external view which is fixed in the object frame + camoMf.buildFrom(0,0.0,2.5,0,vpMath::rad(150),0); + camoMf = camoMf*(sim.get_fMo().inverse()); + + if (opt_display) + { + //Get the internal and external views + sim.getInternalImage(Iint); + sim.getExternalImage(Iext1); + sim.getExternalImage(Iext2,camoMf); + + //Display the object frame (current and desired position) + vpDisplay::displayFrame(Iint,cMo,camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iint,cdMo,camera,0.2,vpColor::none); + + //Display the camera frame, the object frame the world reference frame + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo()*cMo.inverse(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition()*sim.get_fMo(),camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext1,sim.getExternalCameraPosition(),camera,0.2,vpColor::none); + + //Display the world reference frame and the object frame + vpDisplay::displayFrame(Iext2,camoMf,camera,0.2,vpColor::none); + vpDisplay::displayFrame(Iext2,camoMf*sim.get_fMo(),camera,0.05,vpColor::none); + + vpDisplay::flush(Iint); + vpDisplay::flush(Iext1); + vpDisplay::flush(Iext2); + } + + vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + task.print() ; + task.kill() ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int diff --git a/distclean.sh b/distclean.sh deleted file mode 100755 index cf5bc40352f8c47cd1647d51903bbb50c48a8862..0000000000000000000000000000000000000000 --- a/distclean.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/bin/sh -############################################################################# -# -# $Id: distclean.sh,v 1.3 2006-05-29 09:58:22 fspindle Exp $ -# -# This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. -# -# This software is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# ("GPL") version 2 as published by the Free Software Foundation. -# See the file LICENSE.txt at the root directory of this source -# distribution for additional information about the GNU GPL. -# -# For using ViSP with software that can not be combined with the GNU -# GPL, please contact INRIA about acquiring a ViSP Professional -# Edition License. -# -# See http://www.irisa.fr/lagadic/visp/visp.html for more information. -# -# This software was developed at: -# INRIA Rennes - Bretagne Atlantique -# Campus Universitaire de Beaulieu -# 35042 Rennes Cedex -# France -# http://www.irisa.fr/lagadic -# -# If you have questions regarding the use of this file, please contact -# INRIA at visp@inria.fr -# -# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -# -# Description: -# Cleaning cmake produced files. -# -# Authors: -# Fabien Spindler -# -############################################################################# - - -RM="rm -f" - -$RM CMakeCache.txt CMakeOutput.log config.log warning.log cmake.check_cache -$RM install_manifest.txt DartConfiguration.tcl -$RM doc/config-doxygen warning.log cmake_uninstall.cmake -$RM include/visp/vpConfig.h -$RM VISPBuildSettings.cmake -$RM VISPConfig.cmake -$RM VISPUse.cmake -$RM bin/visp-config - -$RM -r Testing autom4te.cache - -for i in `find . -type d -name "CMakeFiles" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done -for i in `find . -type d -name "CMakeTmp" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done -for i in `find . -type d -name "*.dir" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done -for i in `find . test -type d -name "debug" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done -for i in `find lib -type d -name "debug" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done -for i in `find example -type d -name "debug" -print | sort` - do - $RM -r $i; - echo "$RM -r $i" - done - - - -for i in `find . -type f -name "*.rule" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "DartTestfile.txt" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*~" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.plg" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.vcproj*" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "vc60.idb*" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.dsp*" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.dsw" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "cmake_install.cmake" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.opt" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.sln" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.ncb" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.suo" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type d \( -name Makefile \) -prune -o -type f -name "Makefile" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.obj" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.o" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.bb" -print | sort` - do - $RM $i; - echo "$RM $i" - done -for i in `find . -type f -name "*.bbg" -print | sort` - do - $RM $i; - echo "$RM $i" - done diff --git a/doc/biblio/references.bib b/doc/biblio/references.bib index dbd4e3e01f990ea835d698e5e829daf9aabdd024..e651b967ef7701a4811910653523772d0ec5f4fb 100644 --- a/doc/biblio/references.bib +++ b/doc/biblio/references.bib @@ -1,3 +1,16 @@ +@Article{Fischler81, + author = {Fischler, N. and Bolles, R.C.}, + title = {Random Sample Consensus: A Paradigm for Model + Fitting with Application to Image Analysis and + Automated Cartography}, + journal = {Communication of the ACM}, + year = 1981, + volume = 24, + number = 6, + pages = {381--395}, + month = jun +} + @Article{Tsai89a, author = {Tsai, R. and Lenz, R.}, title = {A New Technique for Fully Autonomous and Efficient @@ -67,24 +80,29 @@ x-advisor = {Chaumette, F.}, positioning task of a cumbersome object has been realized using two cameras mounted on a manipulator robot.}, - pdf = - {http://www.irisa.fr/lagadic/pdf/1998_these_malis.pdf}, - urltransp = - {http://www.irisa.fr/lagadic/pdf/1998_these_malis_transp.pdf} + url = {http://www.irisa.fr/lagadic/pdf/1998_these_malis.pdf}, } -@article{Malis00b, - author = {Malis, E. and Chaumette, F. and Boudet, S.}, - title = {2 1/2 {D} Visual Servoing with Respect to Unknown - Objects Through a New Estimation Scheme of Camera - Displacement}, - journal = {Int. Journal of Computer Vision}, - volume = 37, - year = 2000, - number = 1, - month = jun, - pages = {79-97} +@inproceedings{Irani98a, + title = {Robust multi-sensor image alignment}, + author = {Irani, M. and Anandan, P.}, + booktitle = {IEEE Int. Conf. on Computer Vision, ICCV'98}, + pages = {959-966}, + address = {Bombay, India}, + year = 1998 } +@article{Malis00b, + Author = {Malis, E. and Chaumette, F.}, + url = {http://www.irisa.fr/lagadic/pdf/2000_ijcv_malis.pdf}, + Title = {2 1/2 D visual servoing with respect to unknown objects through a new estimation scheme of camera displacement}, + Journal = {Int. Journal of Computer Vision}, + Volume = { 37}, + Number = {1}, + Pages = {79--97}, + Publisher = {Kluwer}, + Month = {June}, + Year = {2000} +} @Article{Dementhon95, author = {Dementhon, D. and Davis, L.}, @@ -97,8 +115,16 @@ x-advisor = {Chaumette, F.}, keyword = {pose} } -@InProceedings{Marchand02c, +@book{Hartley01a, + author = { Hartley, R. and Zisserman,A.}, + title = { Multiple View Geometry in Computer Vision }, + publisher = {Cambridge University Press}, + year = 2001 +} + +@article{Marchand02c, author = {Marchand, E. and Chaumette, F.}, + url = {http://hal.inria.fr/inria-00352096}, title = {Virtual Visual Servoing: a framework for real-time augmented reality}, booktitle = {EUROGRAPHICS'02 Conf. Proceeding}, @@ -111,8 +137,19 @@ x-advisor = {Chaumette, F.}, month = sep } -@Article{Chaumette04a, +@article{Baker04a, + author = {Baker, S. and Matthews, I.}, + title = { Lucas-kanade 20 years on: A unifying framework}, + journal = {Int. Journal of Computer Vision}, + year = 2004, + volume = 56, + number = 3, + pages = {221-255} +} + +@article{Chaumette04a, author = {Chaumette, F.}, + url = {http://hal.inria.fr/inria-00352019}, title = {Image moments: a general and useful set of features for visual servoing}, journal = {IEEE Trans. on Robotics}, @@ -137,13 +174,12 @@ x-advisor = {Chaumette, F.}, that a correct behavior of the system is obtained if we consider either a simple symmetrical object or a planar object with complex and unknown shape.}, - pdf = - {http://www.irisa.fr/lagadic/pdf/2004_itro_chaumette.pdf}, keyword = {visual servoing, robotics, modeling} } @article{Marchand05b, Author = {Marchand, E. and Spindler, F. and Chaumette, F.}, + url = {http://hal.inria.fr/inria-00351899}, Title = {ViSP for visual servoing: a generic software platform with a wide class of robot control skills}, Journal = {IEEE Robotics and Automation Magazine}, Volume = { 12}, @@ -156,6 +192,7 @@ x-advisor = {Chaumette, F.}, @article{Tahri05z, Author = {Tahri, O. and Chaumette, F.}, + url = {http://hal.inria.fr/inria-00351908}, Title = {Point-based and region-based image moments for visual servoing of planar objects}, Journal = {IEEE Trans. on Robotics}, Volume = { 21}, @@ -169,6 +206,7 @@ x-advisor = {Chaumette, F.}, @Article{Comport06b, author = {Comport, A.I. and Marchand, E. and Pressigout, M. and F. Chaumette}, + url = {http://hal.inria.fr/inria-00161250}, title = {Real-time markerless tracking for augmented reality: the virtual visual servoing framework}, journal = {IEEE Trans. on Visualization and Computer Graphics}, @@ -176,10 +214,7 @@ x-advisor = {Chaumette, F.}, month = jul, number = 4, volume = 12, - doi = {http://dx.doi.org/10.1109/TVCG.2006.78}, pages = {615-628}, - pdf = - {http://www.irisa.fr/lagadic/pdf/2006_ieee_tvcg_comport.pdf}, abstract = { Tracking is a very important research subject in a real-time augmented reality context. The main requirements for trackers are high accuracy and @@ -212,32 +247,131 @@ x-advisor = {Chaumette, F.}, @article{Chaumette06a, Author = {Chaumette, F. and Hutchinson, S.}, + url = {http://hal.inria.fr/inria-00350283}, Title = {Visual servo control, Part I: Basic approaches}, Journal = {IEEE Robotics and Automation Magazine}, - Volume = { 13}, + Volume = {13}, Number = {4}, - Pages = {82--90}, + Pages = {82-90}, Month = {December}, Year = {2006} } @article{Chaumette07a, Author = {Chaumette, F. and Hutchinson, S.}, + url = {http://hal.inria.fr/inria-00350638}, Title = {Visual servo control, Part II: Advanced approaches}, Journal = {IEEE Robotics and Automation Magazine}, - Volume = { 14}, + Volume = {14}, Number = {1}, - Pages = {109--118}, + Pages = {109-118}, Month = {March}, Year = {2007} } -@InProceedings{Corke09a, +@article{Corke09a, Author = {Corke, P. and Spindler, F. and Chaumette, F.}, + url = {http://hal.inria.fr/inria-00436741}, Title = {Combining Cartesian and cylindrical coordinates in IBVS}, BookTitle = {IEEE Int. Conf. on Intelligent Robots and Systems, IROS'09}, Pages = {5962--5967}, Address = {St Louis, USA}, Month = {October}, Year = {2009} -} \ No newline at end of file +} + +@article{Mansard07e, + Author = {Mansard, N. and Chaumette, F.}, + url = {https://hal.inria.fr/inria-00350593}, + Title = {Task sequencing for high level sensor-based control}, + Journal = {IEEE Trans. on Robotics}, + Volume = { 23}, + Number = {1}, + Pages = {60--72}, + Month = {February}, + Year = {2007} +} + +@Article{Ozuysal10, + author = {Ozuysal, M. and Calonder, M. and Lepetit, V. and Fua, P.}, + title = {Fast keypoint recognition using random ferns}, + journal = {IEEE Trans. on Pattern Analysis and Machine Intelligence}, + year = 2010, + volume = 32, + number = 3, + pages = {448-461}, + month = {March} +} + +@techreport{Lepetit04c, + author = {Lepetit, V. and Fua, P.}, + title = {Towards Recognizing Feature Points using Classification Trees}, + institution = {EPFL}, + year = 2004, + type = {Technical Report}, + number = {IC/2004/74} +} + +@InProceedings{Collewet08c, + hal_id = {inria-00261398}, + url = {http://hal.inria.fr/inria-00261398}, + title = {Visual servoing set free from image processing}, + author = {Collewet, C. and Marchand, E. and Chaumette, F.}, + abstract = {This paper proposes a new way to achieve robotic tasks by visual servoing. Instead of using geometric features (points, straight lines, pose, homography, etc.) as it is usually done, we use directly the luminance of all pixels in the image. Since most of the classical control laws fail in this case, we turn the visual servoing problem into an optimization problem leading to a new control law. Experimental results validate the proposed approach and show its robustness regarding to approximated depths, non Lambertian objects and partial occlusions.}, + language = {English}, + affiliation = {LAGADIC - INRIA - IRISA}, + booktitle = {IEEE Int. Conf. on Robotics and Automation, ICRA'08}, + address = {Pasadena, United States}, + organization = {IEEE}, + audience = {international}, + year = {2008}, +} + +@InProceedings{Marchand96f, + Author = {Marchand, E. and Chaumette, F. and Rizzo, A.}, + url = {http://www.irisa.fr/lagadic/pdf/1996_iros_marchand.pdf}, + Title = {Using the task function approach to avoid robot joint limits and kinematic singularities in visual servoing}, + BookTitle = {IEEE/RSJ Int. Conf. on Intelligent Robots and Systems, IROS'96}, + Volume = {3}, + Pages = {1083--1090}, + Address = {Osaka, Japan}, + Month = {November}, + Year = {1996} +} + +@article{Chaumette01c, + Author = {Chaumette, F. and Marchand, E.}, + url = {http://hal.inria.fr/inria-00352118}, + Title = {A redundancy-based iterative approach for avoiding joint limits: Application to visual servoing}, + Journal = {IEEE Trans. on Robotics and Automation}, + Volume = {17}, + Number = {5}, + Pages = {719--730}, + Publisher = {IEEE}, + Month = {October}, + Year = {2001} +} + +@article{Gentle:2004, + author = {Gentle, James E.}, + edition = {2nd}, + publisher = {Springer}, + title = {Random Number Generation and Monte Carlo Methods}, + year = 2004 +} + +@article{Park:1988, + author = {Park, S. K. and Miller, K. W.}, + title = {Random Number Generators: Good Ones Are Hard to Find}, + journal = {Commun. ACM}, + issue_date = {Oct. 1988}, + volume = {31}, + number = {10}, + month = oct, + year = {1988}, + pages = {1192--1201}, + numpages = {10}, + publisher = {ACM}, + address = {New York, NY, USA}, +} + diff --git a/doc/config-doxygen.in b/doc/config-doxygen.in index fe2037b3b8fc74b028db147616a59444bb23d6bd..2a21e8e21501eddda0cdb43edfd0d552c6c34297 100644 --- a/doc/config-doxygen.in +++ b/doc/config-doxygen.in @@ -672,7 +672,8 @@ INPUT = "@VISP_SOURCE_DIR@/src" \ "@VISP_SOURCE_DIR@/tutorial" \ "@VISP_SOURCE_DIR@/demo" \ "@VISP_SOURCE_DIR@/test" \ - "@VISP_SOURCE_DIR@/doc" + "@VISP_SOURCE_DIR@/doc" \ + "@VISP_BINARY_DIR@/doc" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -1552,7 +1553,7 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ VISP_HAVE_LAPACK \ VISP_HAVE_OPENCV \ VISP_HAVE_OPENCV_NONFREE \ - VISP_HAVE_OPENCV_VERSION=0x111111 \ + VISP_HAVE_OPENCV_VERSION=0x020408 \ VISP_HAVE_OGRE \ VISP_HAVE_OGRE_PLUGINS_PATH \ VISP_HAVE_OGRE_RESOURCES_PATH \ @@ -1564,7 +1565,6 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ VISP_HAVE_SOQT \ VISP_HAVE_SOWIN \ VISP_HAVE_SOXT \ - VISP_HAVE_DC1394_1 \ VISP_HAVE_DC1394_2 \ VISP_HAVE_CMU1394 \ VISP_HAVE_V4L2 \ @@ -1575,7 +1575,6 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ VISP_HAVE_PTU46 \ VISP_HAVE_VIPER650 \ VISP_HAVE_VIPER850 \ - VISP_HAVE_CYCABTK \ VISP_HAVE_PIONEER \ VISP_HAVE_PARPORT \ VISP_HAVE_XML2 \ @@ -1586,13 +1585,16 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ VISP_HAVE_LIBFREENECT \ VISP_HAVE_LIBUSB_1 \ VISP_HAVE_YARP \ + VISP_HAVE_ZBAR \ + VISP_HAVE_DMTX \ VISP_HAVE_ACCESS_TO_NAS \ VISP_HAVE_CPP11_COMPATIBILITY \ VISP_BUILD_DEPRECATED_FUNCTIONS \ WIN32 \ APPLE \ UNIX \ - VP_DEBUG + VP_DEBUG \ + VP_TRACE # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/doc/image/img-ViSP-images.jpg b/doc/image/img-ViSP-images.jpg deleted file mode 100644 index 429e263aaf0a2bd80154c815695eed276218483a..0000000000000000000000000000000000000000 Binary files a/doc/image/img-ViSP-images.jpg and /dev/null differ diff --git a/doc/image/img-ccmake-centos-all.png b/doc/image/img-ccmake-centos-all.png new file mode 100644 index 0000000000000000000000000000000000000000..b9ecef6d9cdb970ae43a7a38b839aeeb18f6656a Binary files /dev/null and b/doc/image/img-ccmake-centos-all.png differ diff --git a/doc/image/img-ccmake-centos.png b/doc/image/img-ccmake-centos.png new file mode 100644 index 0000000000000000000000000000000000000000..49c361cb676fdd4e73aa91a0908e87c87dfe152a Binary files /dev/null and b/doc/image/img-ccmake-centos.png differ diff --git a/doc/image/img-ccmake-opensuse-all.png b/doc/image/img-ccmake-opensuse-all.png new file mode 100644 index 0000000000000000000000000000000000000000..6180b974162825094b5b91fc8df59079c6aaffb6 Binary files /dev/null and b/doc/image/img-ccmake-opensuse-all.png differ diff --git a/doc/image/img-ccmake-opensuse.png b/doc/image/img-ccmake-opensuse.png new file mode 100644 index 0000000000000000000000000000000000000000..0b780d17914e2fd2a2b58325bb55318466bfe8b3 Binary files /dev/null and b/doc/image/img-ccmake-opensuse.png differ diff --git a/doc/image/img-ccmake-raspberry-pi.png b/doc/image/img-ccmake-raspberry-pi.png new file mode 100644 index 0000000000000000000000000000000000000000..385477527b2d50f455b3e8ac8982d44bf89f21aa Binary files /dev/null and b/doc/image/img-ccmake-raspberry-pi.png differ diff --git a/doc/image/img-chessboard-01.png b/doc/image/img-chessboard-01.png index 721f13bc39532224768e958d8364fcf02897b600..4e92fd62c1a9c709e8da7d7e1c3c034a38db5612 100644 Binary files a/doc/image/img-chessboard-01.png and b/doc/image/img-chessboard-01.png differ diff --git a/doc/image/img-circle.png b/doc/image/img-circle.png new file mode 100644 index 0000000000000000000000000000000000000000..c2f75aa93afcd2a9866e705b646b9dc1d2a3a5c0 Binary files /dev/null and b/doc/image/img-circle.png differ diff --git a/doc/image/img-circles-grid-02.png b/doc/image/img-circles-grid-02.png index 684ef7567f32d2cb21f7b982911f2a6ce0c29275..44504e36409d294546396a8958e0bea55bd6f53e 100644 Binary files a/doc/image/img-circles-grid-02.png and b/doc/image/img-circles-grid-02.png differ diff --git a/doc/image/img-cmake-debug-trace.jpg b/doc/image/img-cmake-debug-trace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1ef2ed23df1d189dc71a4f3b6419df80d9fddbb8 Binary files /dev/null and b/doc/image/img-cmake-debug-trace.jpg differ diff --git a/doc/image/img-cmake-win-1.jpg b/doc/image/img-cmake-win-1.jpg deleted file mode 100755 index c9256fb7479612257e2a0179e47646cbd42736a1..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-1.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-2.jpg b/doc/image/img-cmake-win-2.jpg deleted file mode 100755 index 6ae3f0d4223b3aa670ed57832ccde99624713d11..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-2.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-4.jpg b/doc/image/img-cmake-win-4.jpg deleted file mode 100755 index df1852884d927c548a967d726155616a7c673fa1..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-4.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-6.jpg b/doc/image/img-cmake-win-6.jpg deleted file mode 100755 index b75a90a86851a54440fd60d5b0f89205ea731026..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-6.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-generate.jpg b/doc/image/img-cmake-win-generate.jpg deleted file mode 100755 index 07abe875637a8c40177588dde5ef68476d3cb210..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-generate.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-install-prefix.jpg b/doc/image/img-cmake-win-install-prefix.jpg deleted file mode 100755 index a70b426a13b9f7ec4519e5d4abda5d1480db87ff..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-install-prefix.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-msvc-version.jpg b/doc/image/img-cmake-win-msvc-version.jpg deleted file mode 100755 index 78d188bc573838720ace18a8aef822be4c2394a9..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-msvc-version.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-ogre-found-advanced.jpg b/doc/image/img-cmake-win-ogre-found-advanced.jpg deleted file mode 100755 index 21ee2233b3366723e07ff5ea2a58c65c247c00d8..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-ogre-found-advanced.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-ogre-found.jpg b/doc/image/img-cmake-win-ogre-found.jpg deleted file mode 100755 index 60e5d0d17049e9b8f346541023ea084b463a6270..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-ogre-found.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-opencv.jpg b/doc/image/img-cmake-win-opencv.jpg deleted file mode 100755 index 81e06edf56f313720966d5d79c70d4b1a8bea126..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-opencv.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win-solution.jpg b/doc/image/img-cmake-win-solution.jpg deleted file mode 100755 index 6571c15dca857b61b9a9646a75de498529a8001e..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmake-win-solution.jpg and /dev/null differ diff --git a/doc/image/img-cmake-win7-create-build-folder.jpg b/doc/image/img-cmake-win7-create-build-folder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c07e4bc19a57bab8f0c546ab6fe753339e4ff3c Binary files /dev/null and b/doc/image/img-cmake-win7-create-build-folder.jpg differ diff --git a/doc/image/img-cmake-win7-generate.jpg b/doc/image/img-cmake-win7-generate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4beeaed7dfdad0f607bc70ba3bc159e577fabf93 Binary files /dev/null and b/doc/image/img-cmake-win7-generate.jpg differ diff --git a/doc/image/img-cmake-win7-msvc-config-end.jpg b/doc/image/img-cmake-win7-msvc-config-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54dee35bd01f1240e7fa5139826e4ec62f5a0831 Binary files /dev/null and b/doc/image/img-cmake-win7-msvc-config-end.jpg differ diff --git a/doc/image/img-cmake-win7-msvc-config.jpg b/doc/image/img-cmake-win7-msvc-config.jpg new file mode 100644 index 0000000000000000000000000000000000000000..acf85cde33989017aeac34c749f833a1ff5756ec Binary files /dev/null and b/doc/image/img-cmake-win7-msvc-config.jpg differ diff --git a/doc/image/img-cmake-win7-msvc-launch.jpg b/doc/image/img-cmake-win7-msvc-launch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a382d3887d4490bdf0b28a6ea8e022b971d0eff Binary files /dev/null and b/doc/image/img-cmake-win7-msvc-launch.jpg differ diff --git a/doc/image/img-cmake-win7-msvc-version.jpg b/doc/image/img-cmake-win7-msvc-version.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a480e0f32105e703fb4e1a944cf50f46e6917a78 Binary files /dev/null and b/doc/image/img-cmake-win7-msvc-version.jpg differ diff --git a/doc/image/img-cmake-win7-opencv-advanced.jpg b/doc/image/img-cmake-win7-opencv-advanced.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c816a10967b0566fa901d1740b51fc3796ae8e2 Binary files /dev/null and b/doc/image/img-cmake-win7-opencv-advanced.jpg differ diff --git a/doc/image/img-cmake-win7-opencv.jpg b/doc/image/img-cmake-win7-opencv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb8f9bdc530061a8a303b05ed05758579c951c3a Binary files /dev/null and b/doc/image/img-cmake-win7-opencv.jpg differ diff --git a/doc/image/img-cmake-win7-solution.jpg b/doc/image/img-cmake-win7-solution.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2bb2ccda27510d5b0b683c1b185792615e316487 Binary files /dev/null and b/doc/image/img-cmake-win7-solution.jpg differ diff --git a/doc/image/img-cmake-win8.1-4.jpg b/doc/image/img-cmake-win8.1-4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4c18a1541165a060e534262de7bd1d85eb37a777 Binary files /dev/null and b/doc/image/img-cmake-win8.1-4.jpg differ diff --git a/doc/image/img-cmake-win8.1-6.jpg b/doc/image/img-cmake-win8.1-6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8dbd4e77822ab365b422c19b4a0ccb85a8278fec Binary files /dev/null and b/doc/image/img-cmake-win8.1-6.jpg differ diff --git a/doc/image/img-cmake-win8.1-create-build-folder.jpg b/doc/image/img-cmake-win8.1-create-build-folder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3de022432a14de1abf4978ae8fd5f000e735e43d Binary files /dev/null and b/doc/image/img-cmake-win8.1-create-build-folder.jpg differ diff --git a/doc/image/img-cmake-win8.1-gdi.jpg b/doc/image/img-cmake-win8.1-gdi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..03eb63ce8f3b8dfcd9fcb0d2335b4670e1720f15 Binary files /dev/null and b/doc/image/img-cmake-win8.1-gdi.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-configure-end.jpg b/doc/image/img-cmake-win8.1-mingw-configure-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..948f78412099bcd64bdbc246b56d6acfd84ec213 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-configure-end.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-configure.jpg b/doc/image/img-cmake-win8.1-mingw-configure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..925560ad9609d052c11e2b94403cff4078bf5921 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-configure.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-generate.jpg b/doc/image/img-cmake-win8.1-mingw-generate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6bf443c643314e69c091aa6d518c991e95832d7 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-generate.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-launch.jpg b/doc/image/img-cmake-win8.1-mingw-launch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..277ae0334b7c04c07cc24af99dcb8001e5c62414 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-launch.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-opencv-detected-advanced.jpg b/doc/image/img-cmake-win8.1-mingw-opencv-detected-advanced.jpg new file mode 100644 index 0000000000000000000000000000000000000000..32ef3c281400de4d18eb881d9f5174c4e55217a6 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-opencv-detected-advanced.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-opencv-detected.jpg b/doc/image/img-cmake-win8.1-mingw-opencv-detected.jpg new file mode 100644 index 0000000000000000000000000000000000000000..df0312fe0123136f8b608a8116e6d38cd5008704 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-opencv-detected.jpg differ diff --git a/doc/image/img-cmake-win8.1-mingw-version.jpg b/doc/image/img-cmake-win8.1-mingw-version.jpg new file mode 100644 index 0000000000000000000000000000000000000000..12079016c99964c7dd0165566689c4f3ae42c911 Binary files /dev/null and b/doc/image/img-cmake-win8.1-mingw-version.jpg differ diff --git a/doc/image/img-cmake-win8.1-msvc-generate.jpg b/doc/image/img-cmake-win8.1-msvc-generate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..be57b42c13ebd5f4f0d71d14cdfd81e47c5fb55b Binary files /dev/null and b/doc/image/img-cmake-win8.1-msvc-generate.jpg differ diff --git a/doc/image/img-cmake-win8.1-msvc-launch.jpg b/doc/image/img-cmake-win8.1-msvc-launch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59c8289a24553816a90e8dadfa13adcfe0a72fcf Binary files /dev/null and b/doc/image/img-cmake-win8.1-msvc-launch.jpg differ diff --git a/doc/image/img-cmake-win8.1-msvc-version.jpg b/doc/image/img-cmake-win8.1-msvc-version.jpg new file mode 100644 index 0000000000000000000000000000000000000000..adb01eaa0858a3e158839c862595d85653894fc6 Binary files /dev/null and b/doc/image/img-cmake-win8.1-msvc-version.jpg differ diff --git a/doc/image/img-cmake-win8.1-opencv-advanced.jpg b/doc/image/img-cmake-win8.1-opencv-advanced.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c9fdbcece2466408275bd02521198489fe16cf3c Binary files /dev/null and b/doc/image/img-cmake-win8.1-opencv-advanced.jpg differ diff --git a/doc/image/img-cmake-win8.1-opencv.jpg b/doc/image/img-cmake-win8.1-opencv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e49a416d7a0c25494d194fa9d9d2b230891be3bc Binary files /dev/null and b/doc/image/img-cmake-win8.1-opencv.jpg differ diff --git a/doc/image/img-cmd-displayGDI.jpg b/doc/image/img-cmd-displayGDI.jpg deleted file mode 100644 index fb86f30f9b930f0d25ee836ca2a96e57d4e9f6f3..0000000000000000000000000000000000000000 Binary files a/doc/image/img-cmd-displayGDI.jpg and /dev/null differ diff --git a/doc/image/img-cylinder.png b/doc/image/img-cylinder.png new file mode 100644 index 0000000000000000000000000000000000000000..3c19d10f7cb1a0426a08d8362fff012ffef85235 Binary files /dev/null and b/doc/image/img-cylinder.png differ diff --git a/doc/image/img-detection-datamatrix.png b/doc/image/img-detection-datamatrix.png new file mode 100644 index 0000000000000000000000000000000000000000..30fa84e6f58691932f57adeac6ab9604d64a77be Binary files /dev/null and b/doc/image/img-detection-datamatrix.png differ diff --git a/doc/image/img-detection-qrcode.png b/doc/image/img-detection-qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..b51c4314958b46d49113bac70c42f3249c99ab5c Binary files /dev/null and b/doc/image/img-detection-qrcode.png differ diff --git a/doc/image/img-detection-step.png b/doc/image/img-detection-step.png new file mode 100644 index 0000000000000000000000000000000000000000..f3cdd0bb355be68fc4c7a05ca044f9aac0204e7c Binary files /dev/null and b/doc/image/img-detection-step.png differ diff --git a/doc/image/img-ibvs-control-law-adaptive.png b/doc/image/img-ibvs-control-law-adaptive.png new file mode 100644 index 0000000000000000000000000000000000000000..4dc71cd2f3abeedc8f3e4f796677d731ba4d1fff Binary files /dev/null and b/doc/image/img-ibvs-control-law-adaptive.png differ diff --git a/doc/image/img-ibvs-control-law-continuous-adaptive.png b/doc/image/img-ibvs-control-law-continuous-adaptive.png new file mode 100644 index 0000000000000000000000000000000000000000..a6e51e639c3de096d4c8400ac6617c495006cf77 Binary files /dev/null and b/doc/image/img-ibvs-control-law-continuous-adaptive.png differ diff --git a/doc/image/img-ibvs-control-law-exponential.png b/doc/image/img-ibvs-control-law-exponential.png new file mode 100644 index 0000000000000000000000000000000000000000..6b430e7e65bb9dff6fe52903f285492d35189861 Binary files /dev/null and b/doc/image/img-ibvs-control-law-exponential.png differ diff --git a/doc/image/img-initClickTemplateTracker.png b/doc/image/img-initClickTemplateTracker.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1ee211e8c005c7055286c0e3fc3700258eb515 Binary files /dev/null and b/doc/image/img-initClickTemplateTracker.png differ diff --git a/doc/image/img-learning-step.png b/doc/image/img-learning-step.png new file mode 100644 index 0000000000000000000000000000000000000000..aa22f385b0b14fff63f80e77e59557e2e38a8ee0 Binary files /dev/null and b/doc/image/img-learning-step.png differ diff --git a/doc/image/img-lena-blured-default.png b/doc/image/img-lena-blured-default.png new file mode 100644 index 0000000000000000000000000000000000000000..1f83f88335aebb24e4f270d41e8e61c3adfc1ca6 Binary files /dev/null and b/doc/image/img-lena-blured-default.png differ diff --git a/doc/image/img-lena-blured-var2.png b/doc/image/img-lena-blured-var2.png new file mode 100644 index 0000000000000000000000000000000000000000..7f75fe53a0b56c9ccde27c8dd0495e3966c73d63 Binary files /dev/null and b/doc/image/img-lena-blured-var2.png differ diff --git a/doc/image/img-lena-canny.png b/doc/image/img-lena-canny.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce75eba9043bf13065e11a9c6122894c2846bcb Binary files /dev/null and b/doc/image/img-lena-canny.png differ diff --git a/doc/image/img-lena-dIxy.png b/doc/image/img-lena-dIxy.png new file mode 100644 index 0000000000000000000000000000000000000000..b24bf0bdc48346306847bbe4ebe62d5f7c0af1f6 Binary files /dev/null and b/doc/image/img-lena-dIxy.png differ diff --git a/doc/image/img-lena-gray.png b/doc/image/img-lena-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..696820dbe5758473bb422bac19bd390a3089fd8e Binary files /dev/null and b/doc/image/img-lena-gray.png differ diff --git a/doc/image/img-lena-pyr.png b/doc/image/img-lena-pyr.png new file mode 100644 index 0000000000000000000000000000000000000000..092de5137179bccf3caa7ae1185365e84b38a2a8 Binary files /dev/null and b/doc/image/img-lena-pyr.png differ diff --git a/doc/image/img-lena-sobel.png b/doc/image/img-lena-sobel.png new file mode 100644 index 0000000000000000000000000000000000000000..3672e0b875c2b16751c3289d9eb0746ea30bad45 Binary files /dev/null and b/doc/image/img-lena-sobel.png differ diff --git a/doc/image/img-mingw64-installer-finished.jpg b/doc/image/img-mingw64-installer-finished.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a38a755d666e350d7ab83df3e296c43223ed2438 Binary files /dev/null and b/doc/image/img-mingw64-installer-finished.jpg differ diff --git a/doc/image/img-mingw64-installer-process.jpg b/doc/image/img-mingw64-installer-process.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a8c4863a3e671fd5b08c5f47b2de094a8b1a5cc Binary files /dev/null and b/doc/image/img-mingw64-installer-process.jpg differ diff --git a/doc/image/img-mingw64-installer-started.jpg b/doc/image/img-mingw64-installer-started.jpg new file mode 100644 index 0000000000000000000000000000000000000000..931714c9f7ab7ae5ace4535692df87ddb5feb322 Binary files /dev/null and b/doc/image/img-mingw64-installer-started.jpg differ diff --git a/doc/image/img-msvc-1.jpg b/doc/image/img-msvc-1.jpg deleted file mode 100755 index 12fc66c9d788271a021725dd845fd27d2379f02f..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-1.jpg and /dev/null differ diff --git a/doc/image/img-msvc-2.jpg b/doc/image/img-msvc-2.jpg deleted file mode 100755 index 85cbadbd400861deee49e7a2f8fa7d9957f2b9a1..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-2.jpg and /dev/null differ diff --git a/doc/image/img-msvc-4.jpg b/doc/image/img-msvc-4.jpg deleted file mode 100755 index 6a86193757495332f3dcaa36a57609a493a59d17..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-4.jpg and /dev/null differ diff --git a/doc/image/img-msvc-build.jpg b/doc/image/img-msvc-build.jpg deleted file mode 100644 index c384f014c8f3185766c4cfdd5ea24d1404ede3b9..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-build.jpg and /dev/null differ diff --git a/doc/image/img-msvc-install-end.jpg b/doc/image/img-msvc-install-end.jpg deleted file mode 100644 index 193afe1f81ebe8c02defd0cb3e92bedaf0bbab1b..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-install-end.jpg and /dev/null differ diff --git a/doc/image/img-msvc-install.jpg b/doc/image/img-msvc-install.jpg deleted file mode 100644 index f6f6dd0ab6d8a36315d804e48dbe7d0f3cb0e0de..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-install.jpg and /dev/null differ diff --git a/doc/image/img-msvc-release.jpg b/doc/image/img-msvc-release.jpg deleted file mode 100644 index 30e857496f54b7c07cddaa351d7589730f3a8898..0000000000000000000000000000000000000000 Binary files a/doc/image/img-msvc-release.jpg and /dev/null differ diff --git a/doc/image/img-opencv-issue-dshow.jpg b/doc/image/img-opencv-issue-dshow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4de4fd57f4ce081bc60e9f450d23c14d981a4f9e Binary files /dev/null and b/doc/image/img-opencv-issue-dshow.jpg differ diff --git a/doc/image/img-opencv-issue-ipp.jpg b/doc/image/img-opencv-issue-ipp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..462c2f5f5175f868b0d6dc74aaf5e923a674ca9e Binary files /dev/null and b/doc/image/img-opencv-issue-ipp.jpg differ diff --git a/doc/image/img-opencv-issue-test-big-endian.jpg b/doc/image/img-opencv-issue-test-big-endian.jpg new file mode 100644 index 0000000000000000000000000000000000000000..20868a373734e80c28fc76bf6e6010f2d5e7a248 Binary files /dev/null and b/doc/image/img-opencv-issue-test-big-endian.jpg differ diff --git a/doc/image/img-opencv-issue-tiff.jpg b/doc/image/img-opencv-issue-tiff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..946b306af8008a826f12b31caa878f00cd7f3946 Binary files /dev/null and b/doc/image/img-opencv-issue-tiff.jpg differ diff --git a/doc/image/img-plane-hierarchical-diagram.jpg b/doc/image/img-plane-hierarchical-diagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c4aabea1542f6ca1e452e8fee946ea0b1c12c52e Binary files /dev/null and b/doc/image/img-plane-hierarchical-diagram.jpg differ diff --git a/doc/image/img-raspberry-pi.jpg b/doc/image/img-raspberry-pi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c3f2747c3d7d7c096ced1fd6792fa9bfcbd93a67 Binary files /dev/null and b/doc/image/img-raspberry-pi.jpg differ diff --git a/doc/image/img-teabox-click.jpg b/doc/image/img-teabox-click.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b971f78b3f5ef6a75d78dd098feeeb3dcf0231a Binary files /dev/null and b/doc/image/img-teabox-click.jpg differ diff --git a/doc/image/img-template-tracker.jpg b/doc/image/img-template-tracker.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0bf3caacb85f4010fccdadfc7e3ffd27b706b070 Binary files /dev/null and b/doc/image/img-template-tracker.jpg differ diff --git a/doc/image/img-win-ogre-dir.jpg b/doc/image/img-win-ogre-dir.jpg deleted file mode 100644 index 80432b37a2f1f4c4354260dcb22397e50e704d16..0000000000000000000000000000000000000000 Binary files a/doc/image/img-win-ogre-dir.jpg and /dev/null differ diff --git a/doc/image/img-win-ogre-var.jpg b/doc/image/img-win-ogre-var.jpg deleted file mode 100644 index 3005806fe484af88118eb2a91b7ca1e552f4b6a1..0000000000000000000000000000000000000000 Binary files a/doc/image/img-win-ogre-var.jpg and /dev/null differ diff --git a/doc/image/img-win7-ViSP-images.jpg b/doc/image/img-win7-ViSP-images.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7bc78d55786c2e33554b0d535c524bab5052b295 Binary files /dev/null and b/doc/image/img-win7-ViSP-images.jpg differ diff --git a/doc/image/img-win7-cmd-displayGDI.jpg b/doc/image/img-win7-cmd-displayGDI.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0be1bb84566d31b3816073c482d9bd0188a93103 Binary files /dev/null and b/doc/image/img-win7-cmd-displayGDI.jpg differ diff --git a/doc/image/img-win7-msvc-build-succeed.jpg b/doc/image/img-win7-msvc-build-succeed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d894f880f77070cb4f93a6e3e142336c10235028 Binary files /dev/null and b/doc/image/img-win7-msvc-build-succeed.jpg differ diff --git a/doc/image/img-win7-msvc-build.jpg b/doc/image/img-win7-msvc-build.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0382d6d5c2b7c1d43e06d3d939b4e8def3ba9d2e Binary files /dev/null and b/doc/image/img-win7-msvc-build.jpg differ diff --git a/doc/image/img-win7-msvc-install-end.jpg b/doc/image/img-win7-msvc-install-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f11a3a154ccf3d6f7a8532be64a0c24cc4d26826 Binary files /dev/null and b/doc/image/img-win7-msvc-install-end.jpg differ diff --git a/doc/image/img-win7-msvc-install-succeed.jpg b/doc/image/img-win7-msvc-install-succeed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9701b64b95254fe34fd8f9a7f6fb2ad8533675bf Binary files /dev/null and b/doc/image/img-win7-msvc-install-succeed.jpg differ diff --git a/doc/image/img-win7-msvc-install.jpg b/doc/image/img-win7-msvc-install.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ded148212261b92126383e0a1fc6966c42cf99f Binary files /dev/null and b/doc/image/img-win7-msvc-install.jpg differ diff --git a/doc/image/img-win7-msvc-open.jpg b/doc/image/img-win7-msvc-open.jpg new file mode 100644 index 0000000000000000000000000000000000000000..957cf0c093509a16359c207df70a252aa420260d Binary files /dev/null and b/doc/image/img-win7-msvc-open.jpg differ diff --git a/doc/image/img-win7-msvc-release.jpg b/doc/image/img-win7-msvc-release.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fec3a95c925d20e11dd35c3cd92435147843227d Binary files /dev/null and b/doc/image/img-win7-msvc-release.jpg differ diff --git a/doc/image/img-win8.1-cmd-displayGDI.jpg b/doc/image/img-win8.1-cmd-displayGDI.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a23766fa4b6c46dee56175fd2490b1355b1196e5 Binary files /dev/null and b/doc/image/img-win8.1-cmd-displayGDI.jpg differ diff --git a/doc/image/img-win8.1-cygwin-svn.jpg b/doc/image/img-win8.1-cygwin-svn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44ddfdac8b73f0b374a4bb34866ae9e43a66c2c1 Binary files /dev/null and b/doc/image/img-win8.1-cygwin-svn.jpg differ diff --git a/doc/image/img-win8.1-explorer-install-end.jpg b/doc/image/img-win8.1-explorer-install-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95458c4a806501193c0d069aa1ecd4680817a98e Binary files /dev/null and b/doc/image/img-win8.1-explorer-install-end.jpg differ diff --git a/doc/image/img-win8.1-mingw-opencv-cmake-configure-install.jpg b/doc/image/img-win8.1-mingw-opencv-cmake-configure-install.jpg new file mode 100644 index 0000000000000000000000000000000000000000..301047fb210c908e7b587f424f57f9b246b6e27c Binary files /dev/null and b/doc/image/img-win8.1-mingw-opencv-cmake-configure-install.jpg differ diff --git a/doc/image/img-win8.1-mingw-opencv-cmake-configure.jpg b/doc/image/img-win8.1-mingw-opencv-cmake-configure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3723af74860c5d005579a8e7b5fb17cd3ecf7243 Binary files /dev/null and b/doc/image/img-win8.1-mingw-opencv-cmake-configure.jpg differ diff --git a/doc/image/img-win8.1-msvc-build-end.jpg b/doc/image/img-win8.1-msvc-build-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1cf3ef0518ccab2b21c2fb8862a512d5319905b3 Binary files /dev/null and b/doc/image/img-win8.1-msvc-build-end.jpg differ diff --git a/doc/image/img-win8.1-msvc-build.jpg b/doc/image/img-win8.1-msvc-build.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e4ef2b2406a0db90493013bcae627f2771803104 Binary files /dev/null and b/doc/image/img-win8.1-msvc-build.jpg differ diff --git a/doc/image/img-win8.1-msvc-install-end.jpg b/doc/image/img-win8.1-msvc-install-end.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f26f6987c4fce9a1665da83d9fcb9b0a25340dc0 Binary files /dev/null and b/doc/image/img-win8.1-msvc-install-end.jpg differ diff --git a/doc/image/img-win8.1-msvc-install.jpg b/doc/image/img-win8.1-msvc-install.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ed2f5612d986a4e1bb79c104a93d5f2058294f7e Binary files /dev/null and b/doc/image/img-win8.1-msvc-install.jpg differ diff --git a/doc/image/img-win8.1-msvc-open.jpg b/doc/image/img-win8.1-msvc-open.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c7b8222906ed410032bee81b8c9001b2ed19916d Binary files /dev/null and b/doc/image/img-win8.1-msvc-open.jpg differ diff --git a/doc/image/img-win8.1-msvc-release.jpg b/doc/image/img-win8.1-msvc-release.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4a63729c9b34918883c28632822b0beb60fc083c Binary files /dev/null and b/doc/image/img-win8.1-msvc-release.jpg differ diff --git a/doc/image/img-win8.1-msvc-solution.jpg b/doc/image/img-win8.1-msvc-solution.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b09305b19abd894f206ab199cc81d26179ef868a Binary files /dev/null and b/doc/image/img-win8.1-msvc-solution.jpg differ diff --git a/doc/image/img-win8.1-visp-images.jpg b/doc/image/img-win8.1-visp-images.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ddccad86014ad9ce0ce13b03880f019bfc1d8ec6 Binary files /dev/null and b/doc/image/img-win8.1-visp-images.jpg differ diff --git a/doc/mainpage.doc.in b/doc/mainpage.doc.in index e012fb143cd69d7dce27b5c9c47280ac8cabd61b..83092b3ff8643d079870dcbd6785b692cd8ab43b 100644 --- a/doc/mainpage.doc.in +++ b/doc/mainpage.doc.in @@ -8,11 +8,11 @@ <p>ViSP \cite Marchand05b is a modular C++ library that allows fast development of visual servoing applications. ViSP is developed and maintained by the Inria -<a href="http://www.irisa.fr/lagadic" target="_parent">Lagadic</a> team located at <a -href="http://www.inria.fr/rennes" target="_parent">Inria Rennes</a>.</p> +<a href="http://team.inria.fr/lagadic" target="_parent">Lagadic</a> team located at <a +href="http://www.inria.fr/en/centre/rennes" target="_parent">Inria Rennes</a>.</p> <p>ViSP official site is <a -href="http://www.irisa.fr/lagadic/visp/visp.html" target="_parent">http://www.irisa.fr/lagadic/visp/visp.html</a></p> +href="http://team.inria.fr/lagadic/visp" target="_parent">http://team.inria.fr/lagadic/visp</a></p> <p>If you have any problems or find any bugs, please report them at <a href="http://gforge.inria.fr/tracker/?group_id=397" target="_parent">http://gforge.inria.fr/tracker/?group_id=397</a>. If you may need help, please use the available forums <a href="http://gforge.inria.fr/forum/?group_id=397" target="_parent">http://gforge.inria.fr/forum/?group_id=397</a> or mailing lists <a href="http://gforge.inria.fr/mail/?group_id=397" target="_parent">http://gforge.inria.fr/mail/?group_id=397</a>.<p> @@ -22,7 +22,7 @@ It is also possible to contact ViSP main developers using: <a href="mailto:visp@ \section download_sec Download <p>From <a -href="http://www.irisa.fr/lagadic/visp/download.html" target="_parent">http://www.irisa.fr/lagadic/visp/download.html</a> +href="http://team.inria.fr/lagadic/visp/download.html" target="_parent">http://team.inria.fr/lagadic/visp/download.html</a> you can either download the latest stable release or the current development distribution using Subversion.</p> @@ -35,31 +35,78 @@ href="http://www.cmake.org" target="_parent">http://www.cmake.org</a>. Furthermo depending on your operation system and the capabilities (framegrabber, display, simulation, ...) you need, prior to install ViSP you may install third party libraries <a -href="http://www.irisa.fr/lagadic/visp/libraries.html" target="_parent">http://www.irisa.fr/lagadic/visp/libraries.html</a>.</p> +href="http://team.inria.fr/lagadic/visp/libraries.html" target="_parent">http://team.inria.fr/lagadic/visp/libraries.html</a>.</p> -<p>ViSP full installation procedure using CMake is explained in the getting started documents available from <a -href="http://www.irisa.fr/lagadic/visp/publication.html" target="_parent">http://www.irisa.fr/lagadic/visp/publication.html</a>.</p> +<p>ViSP full installation procedure using CMake is detailed in the \ref tuto_install_sdk "Installation from prebuild SDK tutorials" and \ref tuto_install_src "Installation from source tutorials". \ref tuto_started "Getting started tutorials" and documents in pdf are also available from <a +href="http://team.inria.fr/lagadic/visp/publication.html" target="_parent">http://team.inria.fr/lagadic/visp/publication.html</a>.</p> -\section Tutorial_sec Tutorials +\section tutorial Tutorials + +<p>Here after you will find a list of tutorials that show the basic use of <a href="classes.html">ViSP classes</a> with a small first code. + +\subsection tuto_install_sdk Installation from prebuild SDK + +- \ref tutorial-install-ubuntu-package <br>In this first tutorial you will learn how to install ViSP prebuilt library from Ubuntu packages. + +\subsection tuto_install_src Installation from source -<p>Here after you will find a list of tutorials that shows the basic use of <a href="classes.html">ViSP classes</a> with a small first code. - \ref tutorial-install-ubuntu <br>In this first tutorial you will learn how to install ViSP from source on Linux Ubuntu. -- \ref tutorial-install-win <br>In this tutorial you will learn how to install ViSP from source on Windows. +- \ref tutorial-install-fedora <br>In this other tutorial you will learn how to install ViSP from source on Linux Fedora. +- \ref tutorial-install-centos <br>In this other tutorial you will learn how to install ViSP from source on Linux CentOS. +- \ref tutorial-install-opensuse <br>In this other tutorial you will learn how to install ViSP from source on Linux openSUSE. +- \ref tutorial-install-raspberry <br>In this tutorial you will learn how to install ViSP from source on Raspberry Pi. +- \ref tutorial-install-win7 <br>In this tutorial you will learn how to install ViSP from source on Windows 7 with Visual C++ 2012. +- \ref tutorial-install-win81-msvc <br>In this tutorial you will learn how to install ViSP from source on Windows 8.1 with Visual C++ 2013. +- \ref tutorial-install-win81-mingw64 <br>In this tutorial you will learn how to install ViSP from source on Windows 8.1 with Mingw-w64. - \ref tutorial-install-iOS <br>In this tutorial you will learn how to install ViSP from source on OSX for iOS project. + +\subsection tuto_started Getting started + - \ref tutorial-getting-started <br>This tutorial shows how to build a project that uses ViSP to read and display an image. - \ref tutorial-getting-started-iOS <br>This tutorial shows how to build a project that uses ViSP on iOS devices. + +\subsection tuto_image Image manipulation + - \ref tutorial-grabber <br>This tutorial shows how to acquire images from a camera. +- \ref tutorial-image-filtering <br>This tutorial shows how to filter an image with ViSP. +- \ref tutorial-simu-image <br>This tutorial shows how to project the image of a planar scene to a given camera position. + +\subsection tuto_calib Camera calibration + - \ref tutorial-calibration <br>This tutorial explains how to calibrate a camera. + +\subsection tuto_tracking Tracking + - \ref tutorial-tracking-blob <br>This tutorial introduces blob tracking and detection. - \ref tutorial-tracking-keypoint <br>This tutorial focuses on keypoint tracking using Kanade-Lucas-Tomasi feature tracker. - \ref tutorial-tracking-me <br>This tutorial focuses on line and ellipse tracking using moving-edges. - \ref tutorial-tracking-mb <br>This tutorial focuses on model-based trackers using either edges, keypoints or and hybrid scheme that uses edges and keypoints. -- \ref tutorial-simu-image <br>This tutorial shows how to project the image of a planar scene to a given camera position. -- \ref tutorial-pose-estimation <br>This tutorial focuses on pose estimation from planar or non planar points. +- \ref tutorial-tracking-tt <br>This tutorial focuses on template trackers based on image registration approaches. + +\subsection tuto_detection Detection + +- \ref tutorial-matching <br>This tutorial shows how to detect and match keypoints. New since ViSP 2.10.0. +- \ref tutorial-matching-deprecated <br>This tutorial shows how to detect and match SURF keypoints. It will be deprecated if you use an OpenCV version equal to 2.8.0 or a more recent version. +- \ref tutorial-detection-barcode <br>This tutorial focuses on bar code (QR code, Data Matrix code) detection. New since ViSP 2.10.0. +- \ref tutorial-detection-face <br>This tutorial focuses on face detection thanks to OpenCV Haar cascade classifier. New since ViSP 2.10.0. +- \ref tutorial-detection-object <br>This tutorial shows how to learn keypoints detected on a known object and how to use the matched correspondences to detect and estimate the pose of the object. New since ViSP 2.10.0. + +\subsection tuto_computer_vision Computer vision + +- \ref tutorial-pose-estimation <br>This tutorial focuses on pose estimation from planar or non planar points. New since ViSP 2.10.0. +- \ref tutorial-homography <br>Here we explain how to estimate an homography from couples of matched points. +- \ref tutorial-homography-deprecated <br>Here we explain how to estimate an homography from couples of matched points. It will be deprecated if you use an OpenCV version equal to 2.8.0 or a more recent version. + +\subsection tuto_vs Visual servoing + - \ref tutorial-ibvs <br>This tutorial explains how to simulate an IBVS. - \ref tutorial-simu-robot-pioneer <br>This tutorial focuses on visual servoing simulation on a unicycle robot. The study case is a Pioneer P3-DX mobile robot equipped with a camera. -- \ref tutorial-plotter <br>This tutorial explains how to plot curves in real-time during a visual servo. +- \ref tutorial-boost-vs <br>This tutorial explains how to speed up the time to convergence of a visual servo. +\subsection tuto_tools Other tools + +- \ref tutorial-plotter <br>This tutorial explains how to plot curves in real-time during a visual servo. +- \ref tutorial-trace <br>This tutorial explains how to introduce trace in the code that could be enabled for debugging or disabled. \section Examples_sec Using ViSP @@ -75,7 +122,7 @@ scheme, ...</p> <p>ViSP library is an open source C++ library which is developed at <a href="http://www.inria.fr/en/centre/rennes">Inria</a> by <a href="http://team.inria.fr/lagadic/welcome-eng.html">Lagadic team</a>. If you enjoy using ViSP, you may contribute to the project in different ways. This will motivate us to continue the efforts. -- You can help VISP to be more widely known, by displaying a <a href="http://www.irisa.fr/lagadic/visp/documentation/visp-flyers.pdf">ViSP flyer</a> at work, in your lab or school. +- You can help VISP to be more widely known, by displaying a <a href="http://team.inria.fr/lagadic/visp/documentation/visp-flyers.pdf">ViSP flyer</a> at work, in your lab or school. - You can submit a <a href="https://gforge.inria.fr/tracker/?atid=1867&group_id=397&func=browse">bug report using the tracker</a>. - You can submit <a href="https://gforge.inria.fr/tracker/?func=browse&group_id=397&atid=1660">patches or new functionalities here</a>. - You can write new tutorials, new documentations or simply improve the existing documentation. @@ -85,35 +132,6 @@ scheme, ...</p> */ - -/* - ****************************************** - * Tutorial list - ****************************************** - */ -/*! - \defgroup Tutorial ViSP tutorials - -Here after you will find a list of tutorials that shows the basic use of <a href="classes.html">ViSP classes</a> with a small first code. -- \ref tutorial-install-ubuntu <br>In this first tutorial you will learn how to install ViSP from source on Linux Ubuntu. -- \ref tutorial-install-win <br>In this tutorial you will learn how to install ViSP from source on Windows. -- \ref tutorial-install-iOS <br>In this tutorial you will learn how to install ViSP from source on OSX for iOS project. -- \ref tutorial-getting-started <br>This tutorial shows how to build a project that uses ViSP to read and display an image. -- \ref tutorial-getting-started-iOS <br>This tutorial shows how to build a project that uses ViSP on iOS devices. -- \ref tutorial-grabber <br>This tutorial shows how to acquire images from a camera. -- \ref tutorial-calibration <br>This tutorial explains how to calibrate a camera. -- \ref tutorial-tracking-blob <br>This tutorial focuses on blob tracking and detection. -- \ref tutorial-tracking-keypoint <br>This tutorial focuses on keypoint tracking using Kanade-Lucas-Tomasi feature tracker. -- \ref tutorial-tracking-me <br>This tutorial focuses on line and ellipse tracking using moving-edges. -- \ref tutorial-tracking-mb <br>This tutorial focuses on model-based trackers using either edges, keypoints or and hybrid scheme that uses edges and keypoints. -- \ref tutorial-simu-image <br>This tutorial shows how to project the image of a planar scene to a given camera position. -- \ref tutorial-pose-estimation <br>This tutorial focuses on pose estimation from planar or non planar points. -- \ref tutorial-ibvs <br>This tutorial explains how to simulate an IBVS. -- \ref tutorial-simu-robot-pioneer <br>This tutorial focuses on visual servoing simulation on a unicycle robot. The study case is a Pioneer P3-DX mobile robot equipped with a camera. -- \ref tutorial-plotter <br>This tutorial explains how to plot curves in real-time during a visual servo. - -*/ - /* ****************************************** * ViSP capabilities diff --git a/doc/tutorial-boost-vs.doc b/doc/tutorial-boost-vs.doc new file mode 100644 index 0000000000000000000000000000000000000000..f43b0a0a1bab4b601e3fb347b6cff1b1a4301ecf --- /dev/null +++ b/doc/tutorial-boost-vs.doc @@ -0,0 +1,98 @@ +/** + +\page tutorial-boost-vs Tutorial: How to boost your visual servo control law +\tableofcontents + +\section intro_boost_vs Introduction + +This tutorial gives some hints to boost your visual servo control law in order to speed up the time to convergence. + +To illustrate this tutorial let us consider the example tutorial-ibvs-4pts-plotter.cpp introduced in \ref tutorial-ibvs. This example consider an image based visual servoing using four points as visual features. + +In the general case, considering \f$ \dot {\bf q} \f$ as the input velocities to the robot controller, the control laws provided in vpServo class lead to the following control law \f$ \dot {\bf q} = \pm \lambda {{\bf \widehat J}_e}^+ {\bf e}\f$ where the sign is negative for an eye in hand servo and positive for an eye to hand servo, \f$\lambda\f$ is a constant gain, \f$ {\bf \widehat J}_e\f$ is the task Jacobian and \f$\bf e \f$ is the error to regulate to zero. As described in \cite Chaumette06a, this control law ensure an exponential decoupled decrease of the error \f${\dot {\bf e}} = \pm \lambda {\bf e}\f$. + +This behavior is illustrated with the next figure, where we see the exponential decrease of the eight visual features (x and y for each point) and the corresponding six velocities that are applied to the robot controller. As a consequence, velocities are high when the error is important, and very low when the error is small near the convergence. At the beginning, we can also notice velocity discontinuities with velocities varying from zero to high values in one iteration. + +\image html img-ibvs-control-law-exponential.png "Convergence in 191 iterations with a constant gain." + +This behavior can be reproduced running tutorial-ibvs-4pts-plotter.cpp example. Here after we recall the important lines of code used to compute the control law: + +\code + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); // Set the constant gain value + for (unsigned int i = 0 ; i < 4 ; i++) { + ... + task.addFeature(p[i], pd[i]); // Add visual features to the task + } + while(1) { + for (unsigned int i = 0 ; i < 4 ; i++) { + ... + vpFeatureBuilder::create(p[i], point[i]); // Update the visual features used in the task + } + vpColVector v = task.computeControlLaw(); // Compute the control law + } +\endcode + + +\section adaptive_gain Using an adaptive gain + +As implemented in tutorial-ibvs-4pts-plotter-gain-adaptive.cpp it is possible to adapt the gain \f$ \lambda \f$ in order to depend on the infinity norm of the task Jacobian. The usage of an adaptive gain rather than a constant gain allows to reduce the convergence time. In that case the gain becomes: + + \f[ \lambda (x) = a * exp (-b*x) + c \f] + + where \f$ a \f$, \f$ b \f$ and \f$ c \f$ are constant parameters and \f$ x \f$ is the infinity norm of the task Jacobian to consider. + + The parameters \f$a,b,c\f$ are not set directly. They are computed from three other parameters + \f$\lambda(0), \lambda(\infty), {\dot \lambda}(0)\f$ that are more intuitive to tune: + \f[ a = \lambda(0) - \lambda(\infty) \f] + \f[ b = {\dot \lambda}(0) / a \f] + \f[ c = \lambda(\infty) \f] + + Here \f$ \lambda(0)\f$ represents the gain when \f$x=0\f$, \f$ \lambda(\infty)\f$ represents the gain when \f$x=\infty\f$ + and \f$ {\dot \lambda}(0)\f$ represents the slope of \f$\lambda(x)\f$ when \f$x=0\f$. + +The impact of the adaptive gain is illustrated in the next figure. During the servo, velocities applied to the controller are higher, especially when the visual error \f${\bf e}\f$ is small. But as in the previous section, using an adaptive gain doesn't insure continuous velocities especially at the first iteration. + +\image html img-ibvs-control-law-adaptive.png "Convergence in 91 iterations with an adaptive gain." + +This behavior can be reproduced running tutorial-ibvs-4pts-plotter-gain-adaptive.cpp example. Compared to the previous code given in \ref intro and available in tutorial-ibvs-4pts-plotter.cpp, here after we give the new lines of code that were introduced to use an adaptive gain: + +\code + vpAdaptiveGain lambda(4, 0.4, 30); // lambda(0)=4, lambda(oo)=0.4 and lambda_dot(0)=30 + task.setLambda(lambda); +\endcode + + +\section continuous_adaptive_gain Continuous sequencing + +As implemented in tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp it is also possible to ensure continuous sequencing to avoid velocity discontinuities. This behavior is achieved by introducing an additional term to the general form of the control law. This additional term comes from the task sequencing approach described in \cite Mansard07e equation (17). It allows to compute continuous velocities by avoiding abrupt changes in the command. + + The form of the control law considered here is the following: + + \f[ + {\bf \dot q} = \pm \lambda {{\bf \widehat J}_e}^+ {\bf e} \mp \lambda {{\bf \widehat J}_{e(0)}}^+ {{\bf e}(0)} \exp(-\mu t) + \f] + + where : + - \f${\bf \dot q}\f$ is the resulting continuous velocity command to apply to the robot controller. + - the sign of the control law depends on the eye in hand or eye to hand configuration. + - \f$\bf J\f$ is the Jacobian of the task. + - \f$\bf e = (s-s^*)\f$ is the error to regulate. + - \f$t\f$ is the time. + - \f$\mu\f$ is a gain. We recommend to set this value to 4. + - \f${\bf \widehat J}_{e(0)}^+ {\bf e}(0)\f$ is the value of \f${\bf \widehat J}_e^+ {\bf e}\f$ when \f$t=0\f$. + + +The effect of continuous sequencing is illustrated in the next figure where during the first iterations velocities are starting from zero. + +\image html img-ibvs-control-law-continuous-adaptive.png "Convergence in 98 iterations with an adaptive gain and continuous sequencing." + +This behavior can be reproduced running tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp example. Compared to the previous code given in \ref adaptive_gain and available in tutorial-ibvs-4pts-plotter-gain-adaptive.cpp, here after we give the new line of code that were introduced to ensure continuous sequencing: + +\code + vpColVector v = task.computeControlLaw(iter*robot.getSamplingTime()); +\endcode + +*/ diff --git a/doc/tutorial-calibration.doc b/doc/tutorial-calibration.doc index 16c33c6161837dbfa6913069bf6937b2df4e4e3a..f4d4bca08bddba125ab06c830172dc1dbc4b1ffd 100644 --- a/doc/tutorial-calibration.doc +++ b/doc/tutorial-calibration.doc @@ -48,16 +48,16 @@ The source code of the calibration tool is available in \c camera_calibration.cp We will not describe in detail the source, but just mention that: - the image processing is performed using OpenCV; - the estimation of the parameters is done using a virtual visual servoing scheme; -- the calibration tool takes as input a configuration file that allows to precise the kind of pattern used in the images (chessboard or circle grid), and the location of the images used as input. If \c libjpeg and \c libpng 3rd party libraries are installed and detected during ViSP configuration, you may consider .pgm, .ppm, .jpg, .png images. Default configuration files are provided in \c example/calibration folder; +- the calibration tool takes as input a configuration file that allows to precise the kind of pattern used in the images (chessboard or circles grid), and the location of the images used as input. If \c libjpeg and \c libpng 3rd party libraries are installed and detected during ViSP configuration, you may consider .pgm, .ppm, .jpg, .png images. Default configuration files are provided in \c example/calibration folder; - the resulting parameters are saved in \c camera.xml file. \section calibration_chessboard Calibration from a chessboard -In this section we consider the OpenCV chessboard pattern that has a size of 9 by 6. Each square of the chessboard is 0.025 meters large. We took 5 images called \c chessboard-01.png, \c chessboard-02.png, \c chessboard-03.png, \c chessboard-04.png, \c chessboard-05.png. Hereafter we give an example of such an image. These images are located in \c /tmp folder. +In this section we consider the OpenCV chessboard pattern that has a size of 9 by 6. Each square of the chessboard is 0.025 meters large. We took 5 images called \c chessboard-01.png, \c chessboard-02.png, ..., \c chessboard-05.png. Hereafter we give an example of one of these images. \image html img-chessboard-01.png Snapshot example of the chessboard used to calibrate the camera. -Before starting the calibration we need to create a configuration file. We create \c /tmp/chessboard.cfg with the following content: +Before starting the calibration we need to create a configuration file. We create \c default-chessboard.cfg with the following content: \code # Number of inner corners per a item row and column. (square, circle) BoardSize_Width: 9 @@ -71,12 +71,17 @@ Square_Size: 0.025 Calibrate_Pattern: CHESSBOARD # The input image sequence to use for calibration -Input: /tmp/chessboard-%02d.png +Input: chessboard-%02d.png + +# Tempo in seconds between two images. If > 10 wait a click to continue +Tempo: 1 \endcode +\note The images and the configuration file used in this tutorial are available in ViSP source code and copied in the same folder than the \c camera_calibration binary. + To estimate the camera parameters, just enter in ViSP \c \<binary_dir\>/examples/calibration and run: \code -./camera_calibration /tmp/chessboard.cfg +./camera_calibration default-chessboard.cfg \endcode This command will produce the following output: @@ -85,41 +90,48 @@ grid width : 9 grid height: 6 square size: 0.025 pattern : CHESSBOARD -input seq : /tmp/chessboard-%02d.png -frame: 1 status: 1 -frame: 2 status: 1 -frame: 3 status: 1 -frame: 4 status: 1 -frame: 5 status: 1 - -Calibration without distortion in progress on 5 images... +input seq : chessboard-%02d.png +tempo : 1 +frame: 1, status: 1, image used as input data +frame: 2, status: 1, image used as input data +frame: 3, status: 1, image used as input data +frame: 4, status: 1, image used as input data +frame: 5, status: 1, image used as input data + +Calibration without distorsion in progress on 5 images... Camera parameters for perspective projection without distortion: - px = 547.3080992 py = 546.9825167 - u0 = 324.6299338 v0 = 229.9072033 + px = 278.5184659 py = 273.9720502 + u0 = 162.1161106 v0 = 113.1789595 -Global reprojection error: 2.632851934 +Global reprojection error: 0.2784261067 Camera parameters without distortion successfully saved in "camera.xml" -Calibration with distortion in progress on 5 images... +Calibration with distorsion in progress on 5 images... Camera parameters for perspective projection with distortion: - px = 582.7521622 py = 580.6586412 - u0 = 326.5736348 v0 = 214.9591392 - kud = -0.3372254753 - kdu = 0.4021511255 + px = 276.3370556 py = 271.9804892 + u0 = 162.3656808 v0 = 113.4484506 + kud = 0.02739893948 + kdu = -0.02719442967 -Global reprojection error: 0.6350883328 +Global reprojection error: 0.2602153289 Camera parameters without distortion successfully saved in "camera.xml" + +Estimated pose on input data 0: 0.1004079988 0.07228624926 0.2759094615 0.1622201484 -0.04594748279 -3.067523182 +Estimated pose on input data 1: 0.1126235389 0.09590025615 0.2967542475 0.5743609549 -0.1960511892 -2.915893698 +Estimated pose on input data 2: 0.09983133876 0.08044014071 0.2920209765 -0.02917708148 -0.6751719307 3.046437745 +Estimated pose on input data 3: 0.07481330068 0.0832284992 0.2825482261 -0.09487329058 -0.220597075 -2.747906623 +Estimated pose on input data 4: 0.08061439562 0.08765353523 0.2837166409 0.1009190234 -0.09325252997 -2.906079819 \endcode The resulting parameters are also saved in \c ./camera.xml file. -\section calibration_circle Calibration from a circle grid +\section calibration_circle Calibration from a circles grid -In this section we consider the ViSP symmetric circle grid pattern that has a size of 6 by 6. Each circle center of gravity is 0.034 meters distant from it's horizontal or vertical neighbor. We took 6 images called \c circles-02.pgm, \c circles-03.pgm, ..., \c circles-07.pgm. Hereafter we give an example of such an image. These images are located in \c /tmp folder. +In this section we consider the ViSP symmetric circles grid pattern that has a size of 6 by 6. Each circle center of gravity is 0.034 meters distant from it's horizontal or vertical neighbor. We took 5 images called \c circles-01.pgm, \c circles-02.pgm, ..., \c circles-05.pgm. Hereafter we give an example of such an image. \image html img-circles-grid-02.png Snapshot example of the symmetric circles grid used to calibrate the camera. -Before starting the calibration we need to create a configuration file. We create \c /tmp/circles-grid.cfg with the following content: +Before starting the calibration we need to create a configuration file. We create \c circles-grid.cfg with the following content: \code # Number of inner corners per a item row and column. (square, circle) BoardSize_Width: 6 @@ -133,12 +145,17 @@ Square_Size: 0.034 Calibrate_Pattern: CIRCLES_GRID # The input image sequence to use for calibration -Input: /tmp/circles-%02d.pgm +Input: circles-%02d.pgm + +# Tempo in seconds between two images. If > 10 wait a click to continue +Tempo: 1 \endcode +\note The images and the configuration file used in this tutorial are available in ViSP source code and copied in the same folder than the \c camera_calibration binary. + To estimate the camera parameters, just enter in ViSP \c \<binary_dir\>/examples/calibration and run: \code -./camera_calibration /tmp/circles-grid.cfg +./camera_calibration circles-grid.cfg \endcode This command will produce the following output: @@ -147,39 +164,41 @@ grid width : 6 grid height: 6 square size: 0.034 pattern : CIRCLES_GRID -input seq : /tmp/circles-%02d.pgm -frame: 2 status: 1 -frame: 3 status: 1 -frame: 4 status: 1 -frame: 5 status: 0 -frame: 6 status: 1 -frame: 7 status: 1 - -Calibration without distortion in progress on 6 images... +input seq : circles-%02d.png +tempo : 1 +frame: 1, status: 1, image used as input data +frame: 2, status: 1, image used as input data +frame: 3, status: 1, image used as input data +frame: 4, status: 1, image used as input data +frame: 5, status: 1, image used as input data + +Calibration without distorsion in progress on 5 images... Camera parameters for perspective projection without distortion: - px = 552.4774691 py = 544.8066862 - u0 = 308.732533 v0 = 245.8146982 + px = 276.7844987 py = 273.2284128 + u0 = 164.029061 v0 = 113.2926414 -Global reprojection error: 0.2888553796 +Global reprojection error: 0.3245572722 Camera parameters without distortion successfully saved in "camera.xml" -Calibration with distortion in progress on 6 images... +Calibration with distorsion in progress on 5 images... Camera parameters for perspective projection with distortion: - px = 550.9009948 py = 543.1425164 - u0 = 309.1726232 v0 = 245.7667949 - kud = 0.01091468479 - kdu = -0.01088523009 + px = 272.6576029 py = 268.9209423 + u0 = 163.3267494 v0 = 112.9548567 + kud = 0.03132515383 + kdu = -0.03098719022 -Global reprojection error: 0.2763660773 +Global reprojection error: 0.2985458516 Camera parameters without distortion successfully saved in "camera.xml" + +Estimated pose on input data 0: -0.08883802146 -0.07573082723 0.254649414 0.009277810667 -0.1162730223 -0.06217958144 +Estimated pose on input data 1: -0.03031929668 -0.07792577124 0.226777101 0.04390110018 -0.474640394 0.09584680839 +Estimated pose on input data 2: 0.02757364367 -0.08075508106 0.2416734821 0.2541005213 -0.469141926 0.5746851856 +Estimated pose on input data 3: -0.08528071 -0.0552184701 0.216359278 0.433944401 -0.01692119727 -0.01151973247 +Estimated pose on input data 4: -0.1104723502 -0.0854285443 0.2684946566 0.4130829919 0.1926077657 0.2736623762 \endcode The resulting parameters are also saved in \c ./camera.xml file. -Note here that the following line indicates that the 5th frame corresponding to the image \c circles-05.pgm was not used in the calibration process since the status of the image processing is false. -\code -frame: 5 status: 0 -\endcode \section calibration_undistort Distorsion removal @@ -188,40 +207,18 @@ Once the camera is calibrated, you can remove the distortion in the images. The \include tutorial-undistort.cpp In this example we first load the image \c chessboard.pgm +\snippet tutorial-undistort.cpp Load image -\code - vpImage<unsigned char> I; - vpImageIo::read(I, "chessboard.pgm"); -\endcode - -Then we read the camera parameters with distortion of a camera named "Camera" from \c ./camera.xml file. This is only possible if ViSP was build with \c libxml2 3rd party support. +Then we read the camera parameters with distortion of a camera named "Camera" from \c ./camera.xml file. This is only possible if ViSP was build with \c libxml2 3rd party support. -\code - vpCameraParameters cam; -#ifdef VISP_HAVE_XML2 - vpXmlParserCamera p; - vpCameraParameters::vpCameraParametersProjType projModel; - projModel = vpCameraParameters::perspectiveProjWithDistortion; - if (p.parse(cam, "camera.xml", "Camera", projModel, I.getWidth(), I.getHeight()) != vpXmlParserCamera::SEQUENCE_OK) { - std::cout << "Cannot found parameters for camera named \"Camera\"" << std::endl; - } -\endcode +\snippet tutorial-undistort.cpp Load camera parameters from xml If vpXmlParserCamera is not available (this may occur if ViSP was not build with \c libxml2), we initialize the camera parameters "by hand" using the following code: - -\code -#else - cam.initPersProjWithDistortion(582.7, 580.6, 326.6, 215.0, -0.3372, 0.4021); -#endif -\endcode +\snippet tutorial-undistort.cpp Set camera parameters Finally, we create a new image \c Iud where distortion is removed. This image is saved in \c chessboard-undistort.pgm. -\code - vpImage<unsigned char> Iud; - vpImageTools::undistort(I, cam, Iud); - vpImageIo::write(Iud, "chessboard-undistort.pgm"); -\endcode +\snippet tutorial-undistort.cpp Create image without distorsion The resulting \c chessboard-undistort.pgm image is the following. diff --git a/doc/tutorial-detection-barcode.doc b/doc/tutorial-detection-barcode.doc new file mode 100644 index 0000000000000000000000000000000000000000..2a3438353ea7b0b7367a821f03434f67fb209d1e --- /dev/null +++ b/doc/tutorial-detection-barcode.doc @@ -0,0 +1,99 @@ +/** + +\page tutorial-detection-barcode Tutorial: Bar code detection +\tableofcontents + +\section intro_barcode Introduction + +This tutorial shows how to detect one or more barcodes with ViSP. To this end we provide two classes that are wrapper over 3rd party libraries: +- vpDetectorQRCode that is a wrappers over zbar library <http://zbar.sourceforge.net>. It allows to detect QR codes +- vpDetectorDataMatrix this is a wrapper over dmtx library <http://www.libdmtx.org>. It allows to detect Data Matrix codes. + +These classes inherit from vpDetectorBase class, a generic class dedicated to detection. For each detected bar code, it allows to retrieve some characteristics such as the bar code message if it exists, and in the image the polygon that contains the bar code, the bounding box or the center of gravity of the bar code. + +In the next sections you will find examples that show how to detect codes in a single image and or in images acquired from a camera connected to your computer. + +\section barcode_detection_basic Bar code detection (single image) + +The following example also available in tutorial-barcode-detector.cpp allows to detect either a QR code or Data Matrix on a single image. The user can select with --code-type option which code to detect. + +\include tutorial-barcode-detector.cpp + +To detect a QR code just run: +\code +$ ./tutorial-barcode-detector --code-type 0 +\endcode +You will get the following result: + +\image html img-detection-qrcode.png + +To detect a Data Matrix code just run: +\code +$ ./tutorial-barcode-detector --code-type 1 +\endcode +You will get the following result: + +\image html img-detection-datamatrix.png + +Now we explain the main lines of the source. + +First we have to include the header of the two classes that allow to detect either the QR code or the Data Matrix code. +\snippet tutorial-barcode-detector.cpp Include + +Then in the main() function before going further we need to check if at least one of the third party (zbar or dmtx libraries) were used to build ViSP. We also check if ViSP is able to display images using either X11, or the Graphical Device Interface (GDI) under Windows, or OpenCV. + +\snippet tutorial-barcode-detector.cpp Macro defined + +After reading the input image \e bar-code.pgm and creation of a display device in order to visualize the image, we create a NULL pointer to a detector base class. + +\snippet tutorial-barcode-detector.cpp Create base detector + +Since the classes that allow to detect QR codes and Data Matrix codes inherit from vpDetectorBase, using the variable \e opt_barcode we are able to construct the requested detector. + +\snippet tutorial-barcode-detector.cpp Create detector + +We are now reader to detect the bar code in the image. + +\snippet tutorial-barcode-detector.cpp Detection + +If one or more bar codes are detected, the variable \e status is set to \e true. +In that case, we can retrieve the number of detected bar codes in order to create a for loop over the codes. + +\snippet tutorial-barcode-detector.cpp Parse detected codes + +For each code, we can then get the location of the 4 points that define the polygon that contains the code, but also the bounding box. + +\snippet tutorial-barcode-detector.cpp Get location + +And finally, if it exists, we are also able to get the message that is coded in the bar code. + +\snippet tutorial-barcode-detector.cpp Get message + + +\section barcode_detection_live Bar code detection (live camera) + +This other example also available in tutorial-barcode-detector-live.cpp shows how to couple the bar code detector to an image grabber in order to detect bar codes on each new image acquired by a camera connected to your computer. + +\include tutorial-barcode-detector-live.cpp + +The usage of this example is similar to the previous one: +- with option --code-type you select if you want to detect a QR code (use --code-type 0) or a Data Matrix (use --code-type 1). +- if more than one camera is connected to you computer, with option --device you can select which camera to use. The first camera that is found has number 0. + +To detect QR codes on images acquired by a second camera connected to your computer use: +\code +$ ./tutorial-barcode-detector-live --code-type 0 --device 1 +\endcode + +The source code of this example is very similar to the previous one except that here we use camera framegrabber devices (see \ref tutorial-grabber). Two different grabber may be used: +- If ViSP was build with Video For Linux (V4L2) support available for example on Fedora or Ubuntu distribution, VISP_HAVE_V4L2 macro is defined. In that case, images coming from an USB camera are acquired using vpV4l2Grabber class. +- If ViSP wasn't build with V4L2 support, but with OpenCV we use cv::VideoCapture class to grab the images. Notice that when images are acquired with OpenCV there is an additional conversion from cv::Mat to vpImage. + +\snippet tutorial-barcode-detector-live.cpp Construct grabber + +Then in the while loop, at each iteration we acquire a new image +\snippet tutorial-barcode-detector-live.cpp Acquisition + +This new image is then given as input to the bar code detector. + +*/ diff --git a/doc/tutorial-detection-face.doc b/doc/tutorial-detection-face.doc new file mode 100644 index 0000000000000000000000000000000000000000..f6b45192443fa6641d5a7fb6f68473dc6156a809 --- /dev/null +++ b/doc/tutorial-detection-face.doc @@ -0,0 +1,103 @@ +/** + +\page tutorial-detection-face Tutorial: Face detection +\tableofcontents + +\section intro_face Introduction + +This tutorial shows how to detect one or more faces with ViSP. Face detection is performed using OpenCV Haar cascade capabilities that are used in vpDetectorFace class. At least OpenCV 2.2.0 or a more recent version is requested. + +In the next sections you will find examples that show how to detect faces in a video, or in images acquired by a camera connected to your computer. + +\section face_detection_video Face detection in a video + +The following example also available in tutorial-face-detector.cpp allows to detect faces in an mpeg video located near the source code. The Haar cascade classifier file requested by OpenCV is also provided in the same folder as the source code. + +\include tutorial-face-detector.cpp + +To detect the faces just run: +\code +$ ./tutorial-face-detector +\endcode +You will get the following result: + +\htmlonly +<iframe width="420" height="315" src="https://www.youtube.com/embed/zizukjfNLvE" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Now we explain the main lines of the source. + +First we have to include the header of the class that allows to detect a face. +\snippet tutorial-face-detector.cpp Include + +Then in the main() function before going further we need to check if OpenCV 2.2.0 is available. + +\snippet tutorial-face-detector.cpp Macro defined + +We set then the default input data: +- the name of the Haar cascade classifier file "haarcascade_frontalface_alt.xml" +- the name of the input video "video.mpeg" + +\snippet tutorial-face-detector.cpp Default settings + +With command line options it is possible to use other inputs. To know how just run: + +\code +$ ./tutorial-face-detector --help +Usage: ./tutorial-face-detector [--haar <haarcascade xml filename>] [--video <input video file>] [--help] +\endcode + +Then we open the video stream, create a windows named "ViSP viewer" where images and the resulting face detection will be displayed. + +The creation of the face detector is performed using + +\snippet tutorial-face-detector.cpp Face detector construction + +We need also to set the location and name of the xml file that contains the Haar cascade classifier data used to recognized a face. + +\snippet tutorial-face-detector.cpp Face detector setting + +Then we enter in the while loop where for each new image, the try to detect one or more faces: + +\snippet tutorial-face-detector.cpp Face detection + +If a face is detected, vpDetectorFace::detect() returns true. It is then possible to retrieve the number of faces that are detected: + +\snippet tutorial-face-detector.cpp Get number faces + +For each face, we have access to its location using vpDetectorFace::getPolygon(), its bounding box using vpDetectorFace::getBBox() and its identifier message using vpDetectorFace::getMessage(). + +\snippet tutorial-face-detector.cpp Get face characteristics + +\note When more than one face is detected, faces are ordered from the largest to the smallest. That means that vpDetectorFace::getPolygon(0), vpDetectorFace::getBBox(0) and vpDetectorFace::getMessage(0) return always the characteristics of the largest face. + +\section face_detection_live Face detection from a camera + +This other example also available in tutorial-face-detector-live.cpp shows how to detect one or more faces in images acquired by a camera connected to your computer. + +\include tutorial-face-detector-live.cpp + +The usage of this example is similar to the previous one. Just run +\code +$ ./tutorial-face-detector-live +\endcode + +Additional command line options are available to specify the location of the Haar cascade file and also the camera identifier if more than one camera is connected to your computer: + +\code +$ ./tutorial-face-detector-live --help +Usage: ./tutorial-face-detector-live [--device <camera device>] [--haar <haarcascade xml filename>] [--help] +\endcode + +The source code of this example is very similar to the previous one except that here we use camera framegrabber devices (see \ref tutorial-grabber). Two different grabber may be used: +- If ViSP was build with Video For Linux (V4L2) support available for example on Fedora or Ubuntu distribution, VISP_HAVE_V4L2 macro is defined. In that case, images coming from an USB camera are acquired using vpV4l2Grabber class. +- If ViSP wasn't build with V4L2 support, but with OpenCV we use cv::VideoCapture class to grab the images. Notice that when images are acquired with OpenCV there is an additional conversion from cv::Mat to vpImage. + +\snippet tutorial-face-detector-live.cpp Construct grabber + +Then in the while loop, at each iteration we acquire a new image +\snippet tutorial-face-detector-live.cpp Acquisition + +This new image is then given as input to the face detector. + +*/ diff --git a/doc/tutorial-detection-object.doc b/doc/tutorial-detection-object.doc new file mode 100644 index 0000000000000000000000000000000000000000..553c9527431c4a726144448af5d7e0a90261ddf7 --- /dev/null +++ b/doc/tutorial-detection-object.doc @@ -0,0 +1,125 @@ +/** + +\page tutorial-detection-object Tutorial: Object detection and localization +\tableofcontents + +This tutorial will show you how to use keypoints to detect and estimate the pose of a known object using his cad model. The first step consists in detecting and learning keypoints located on the faces of an object, while the second step makes the matching between the detected keypoints in the query image with those previously learned. The pair of matches are then used to estimate the pose of the object with the knowledge of the correspondences between the 2D and 3D coordinates. + +The next section presents a basic example of the detection of a teabox with a detailed description of the different steps. + +\section object_detection Object detection using keypoints + +\subsection detection_object_preamble Preamble + +You are advised to read the following tutorials \ref tutorial-tracking-mb and \ref tutorial-matching if you are not aware of these concepts. + +\subsection detection_object_principle Principle of object detection using keypoints + +A quick overview of the principle is summed-up in the following diagrams. + +\image html img-learning-step.png Learning step. + +The first part of the process consists in learning the characteristics of the considered object by extracting the keypoints detected on the different faces. +We use here the model-based tracker initialized given a known initial pose to have access to the cad model of the object. The cad model is then used to select only keypoints on faces that are visible and to calculate the 3D coordinates of keypoints. + +\note The calculation of the 3D coordinates of a keypoint is based on a planar location hypothesis. We assume that the keypoint is located on a planar face and the Z-coordinate is retrieved according to the proportional relation between the plane equation expressed in the normalized camera frame (derived from the image coordinate) and the same plane equation expressed in the camera frame, thanks to the known pose of the object. + +In this example the learned data (the list of 3D coordinates and the corresponding descriptors) are saved in a file and will be used later in the detection part. + +\image html img-detection-step.png Detection step. + +In a query image where we want to detect the object, we find the matches between the keypoints detected in the current image with those previously learned. +The estimation of the pose of the object can then be computed with the 3D/2D information. + +The next section presents an example of the detection and the pose estimation of a teabox. + +\subsection detection_object_teabox_example Teabox detection and pose estimation + +The following example comes from tutorial-detection-object-mbt.cpp and shows the different steps to detect and get the pose of a teabox. + +\include tutorial-detection-object-mbt.cpp + +You may recognize with the following lines the code used in \ref tutorial-mb-edge-tracker.cpp to initialize the model-based tracker at a given pose and with the appropriate configuration. + +\snippet tutorial-detection-object-mbt.cpp MBT code + +The modifications made to the code start from now. + +First, we have to choose about which type of keypoints will be used. SIFT keypoints are a widely type of keypoints used in computer vision, but depending of your version of OpenCV and due to some patents, certain types of keypoints will not be available. +Here, we will use SIFT if available, otherwise a combination of FAST keypoint detector and ORB descriptor extractor. + +\snippet tutorial-detection-object-mbt.cpp Keypoint selection + +The following line declares an instance of the vpKeyPoint class : + +\snippet tutorial-detection-object-mbt.cpp Keypoint declaration + +If libxml2 is available, you can load the configuration (type of detector, extractor, matcher, ransac pose estimation parameters) directly with an xml configuration file : + +\snippet tutorial-detection-object-mbt.cpp Keypoint xml config + +Otherwise, the configuration must be made in the code. + +\snippet tutorial-detection-object-mbt.cpp Keypoint code config + +We then detect keypoints in the reference image with the object we want to learn : + +\snippet tutorial-detection-object-mbt.cpp Keypoints reference detection + +But we need to keep keypoints only on faces of the teabox. This is done by using the model-based tracker to first eliminate keypoints which do not belong to the teabox and secondly to have the plane equation for each faces (and so to be able to compute the 3D coordinate from the 2D information). + +\snippet tutorial-detection-object-mbt.cpp Keypoints selection on faces + +The next step is the building of the reference keypoints. The descriptors for each keypoints are also extracted and the reference data consist of the lists of keypoints / descriptors and the list of 3D points. + +\snippet tutorial-detection-object-mbt.cpp Keypoints build reference + +We save the learning data in a binary format (the other possibilitie is to save in an xml format but which takes more space) to be able to use it later. + +\snippet tutorial-detection-object-mbt.cpp Save learning data + +We then visualize the result of the learning process by displaying with a cross the location of the keypoints: + +\snippet tutorial-detection-object-mbt.cpp Display reference keypoints + +We declare now another instance of the vpKeyPoint class dedicated this time to the detection of the teabox. If libxml2 is available, the configuration is directly loaded from an xml file, otherwise this is done directly in the code. + +\snippet tutorial-detection-object-mbt.cpp Init keypoint detection + +The previously saved binary file corresponding to the teabox learning data is loaded: + +\snippet tutorial-detection-object-mbt.cpp Load teabox learning data + +We are now ready to detect the teabox in a query image. The call to the function vpKeyPoint::matchPoint() returns true if the matching was successful and permits to get the estimated homogeneous matrix corresponding to the pose of the object. The reprojection error is also computed. + +\snippet tutorial-detection-object-mbt.cpp Matching and pose estimation + +In order to display the result, we use the tracker initialized at the estimated pose and we display also the location of the world frame: + +\snippet tutorial-detection-object-mbt.cpp Tracker set pose +\snippet tutorial-detection-object-mbt.cpp Display + +The pose of the detected object can then be used to initialize a tracker automatically rather then using a human initialization; see \ref tutorial-tracking-mb and \ref tutorial-tracking-tt. + +\subsection detection_object_quick_explanation Quick explanation about some parameters used in the example + +The content of the configuration file named detection-config-SIFT.xml and provided with this example is described in the following lines : + +\include detection-config-SIFT.xml + +In this configuration file, SIFT keypoints are used. + +Let us explain now the configuration of the matcher: +- a brute force matching will explore all the possible solutions to match a considered keypoints detected in the current image to the closest (in descriptor distance term) one in the reference set, contrary to the other type of matching using the library FLANN (Fast Library for Approximate Nearest Neighbors) which contains some optimizations to reduce the complexity of the solution set, +- to eliminate some possible false matching, one technique consists of keeping only the keypoints whose are sufficienly discriminated using a ratio test. + +Now, for the Ransac pose estimation part : +- two methods are provided to estimate the pose in a robust way: one using OpenCV, the other method uses a virtual visual servoing approach using ViSP, +- basically, a Ransac method is composed of two steps repeated a certain number of iterations: first we pick randomly 4 points and estimate the pose, the second step is to keep all points which sufficienly "agree" (the reprojection error is below a threshold) with the pose determinated in the first step. These points are inliers and form the consensus set, the other are outliers. +If enough points are in the consensus set (here 20 % of all the points), the pose is refined and returned, otherwise another iteration is made (here 200 iterations maximum). + +Below you will also find the content of detection-config.xml configuration file, also provided in this example. It allows to use FAST detector and ORB extractor. + +\include detection-config.xml + +*/ diff --git a/doc/tutorial-getting-started-iOS.doc b/doc/tutorial-getting-started-iOS.doc index cafc84704fcaba86aa2b290f51119381ebfca60a..c36ea6c9bf3e080d0b48f1b8021baec83bddbe5b 100644 --- a/doc/tutorial-getting-started-iOS.doc +++ b/doc/tutorial-getting-started-iOS.doc @@ -1,6 +1,6 @@ /** - \page tutorial-getting-started-iOS Tutorial: Getting started for iOS + \page tutorial-getting-started-iOS Tutorial: How to create and build a CMake project that uses ViSP on iOS \tableofcontents diff --git a/doc/tutorial-getting-started.doc b/doc/tutorial-getting-started.doc index 4532c3efc7328217bfa363e43f96af3828a78002..2a51888a323f75cb049e307960e05654c5bf9153 100644 --- a/doc/tutorial-getting-started.doc +++ b/doc/tutorial-getting-started.doc @@ -1,12 +1,19 @@ /** - \page tutorial-getting-started Tutorial: Getting started + \page tutorial-getting-started Tutorial: How to create and build a CMake project that uses ViSP on Unix or Windows \tableofcontents \note We assume in this tutorial that you have successfully installed ViSP. Information on ViSP installation is provided in: - - \ref tutorial-install-ubuntu - - \ref tutorial-install-win - - or on <a href="http://www.irisa.fr/lagadic/visp/install.html">Install from source page</a> +- \ref tutorial-install-ubuntu +- \ref tutorial-install-fedora +- \ref tutorial-install-opensuse +- \ref tutorial-install-raspberry +- \ref tutorial-install-win7 +- \ref tutorial-install-win81-msvc +- \ref tutorial-install-win81-mingw64 +- \ref tutorial-install-iOS +- \ref tutorial-install-ubuntu-package +- or on <a href="http://www.irisa.fr/lagadic/visp/install.html">Install from source page</a> In this tutorial you will learn how to use ViSP either on Unix-like systems (including OSX, Fedora, Ubuntu, Debian, ...) or on Windows. @@ -21,77 +28,57 @@ Let's start to write our first program to see how to read an image and \include tutorial-viewer.cpp Here is the detailed explanation of the source, line by line : -\code -#include <visp/vpDisplayD3D.h> -#include <visp/vpDisplayGDI.h> -#include <visp/vpDisplayGTk.h> -#include <visp/vpDisplayX.h> -#include <visp/vpDisplayOpenCV.h> -\endcode - Include all the headers for image viewers. The two first one are for Windows systems. They require that Direct 3D or the \e Graphical \e Device \e Interface (\e GDI) coming with the installation of Visual Studio are available. The third one needs GTK that is cross-platform. The fourth is for unix-like systems and requires that \e libX11 is available. The last one is also cross-platform and requires that OpenCV is available. +\snippet tutorial-viewer.cpp Include display -\code -#include <visp/vpImageIo.h> -\endcode +Include all the headers for image viewers. The two first one are for Windows systems. They require that Direct 3D or the \e Graphical \e Device \e Interface (\e GDI) coming with the installation of Visual Studio are available. The third one needs GTK that is cross-platform. The fourth is for unix-like systems and requires that \e libX11 is available. The last one is also cross-platform and requires that OpenCV is available. + +\snippet tutorial-viewer.cpp Include io Include the header that allows to read/write PGM, PPM, PNG and JPEG images from the disk using vpImageIo class. -\code -vpImage<vpRGBa> I; -\endcode +\snippet tutorial-viewer.cpp vpImage construction Create an instance of a color image where each pixel is coded in RGBa. -\code -try { - vpImageIo::read(I, argv[1]); -} -catch(...) { - std::cout << "Cannot read image \"" << argv[1] << "\"" << std::endl; - return -1; -} -\endcode +\snippet tutorial-viewer.cpp vpImage reading The image \c I is initialized by reading an image file from the disk. If the image format is not supported we throw an exception. -\code -#if defined(VISP_HAVE_X11) -vpDisplayX d(I); -#elif defined(VISP_HAVE_OPENCV) -vpDisplayOpenCV d(I); -#elif defined(VISP_HAVE_GTK) -vpDisplayGTK d(I); -#elif defined(VISP_HAVE_GDI) -vpDisplayGDI d(I); -#elif defined(VISP_HAVE_D3D9) -vpDisplayD3d d(I); -#else -std::cout << "No image viewer is available..." << std::endl; -#endif -\endcode +\snippet tutorial-viewer.cpp vpDisplay construction Create an instance of an image display window for image \c I. The first viewer that is available is used. Here we create the link between the image \c I and the display \c d. Note that an image can only have one display. -\code - vpDisplay::setTitle(I, "My image"); -\endcode +\snippet tutorial-viewer.cpp vpDisplay set title The title of the display is then set to \c "My image". -\code - vpDisplay::display(I); - vpDisplay::flush(I); -\endcode - First we display the content of the image \c I, then we flush the display to render the image. +\snippet tutorial-viewer.cpp vpDisplay display +First we display the content of the image \c I, then we flush the display to render the image. -\code - vpDisplay::getClick(I); -\endcode - Here we handle mouse events. We are waiting for a blocking mouse click to end the program. +\snippet tutorial-viewer.cpp vpDisplay get click +Here we handle mouse events. We are waiting for a blocking mouse click to end the program. \section image_cmake Create a CMake file -Now you have to create your CMakeLists.txt file. It should look like this: +Now you have to create your \c CMakeLists.txt file. It should look like this: + +\code +project(tutorial-image) + +cmake_minimum_required(VERSION 2.8) + +find_package(VISP REQUIRED) + +include_directories(${VISP_INCLUDE_DIRS}) +add_executable(tutorial-viewer tutorial-viewer.cpp) +target_link_libraries(tutorial-viewer ${VISP_LIBRARIES}) +\endcode + +The \c find_package() CMake command searches for a \c VISPConfig.cmake file that will define the corresponding variables: +- \c VISP_INCLUDE_DIRS : ViSP and third-party headers location +- \c VISP_LIBRARIES : ViSP and third-party libraries name and location + +Note that the previous \c CMakeLists.txt file can also be: \code project(tutorial-image) -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 2.8) find_package(VISP REQUIRED) if(VISP_FOUND) @@ -101,6 +88,17 @@ endif(VISP_FOUND) add_executable(tutorial-viewer tutorial-viewer.cpp) \endcode +where \c VISP_USE_FILE variable is set to the full path to \c VISPUse.cmake file that contains all the CMake material that allow to build your project with ViSP. In other terms, the line +\code + include(${VISP_USE_FILE}) +\endcode +will include the following lines to your \c CMakeFile.txt +\code + include_directories(${VISP_INCLUDE_DIRS}) + link_libraries(${VISP_LIBRARIES}) +\endcode + + \section image_unix On Unix-like systems \subsection image_unix_config Configure your project @@ -111,6 +109,33 @@ Proceed as with any other project using CMake: cmake . \endcode +By default \c cmake searches \c VISPConfig.cmake file in folders like \c /usr/share or \c /usr/local/share. If ViSP was not installed in \c /usr or \c /usr/local it is possible that you get the following error: +\code +CMake Error at CMakeLists.txt:5 (find_package): + Could not find module FindVISP.cmake or a configuration file for package + VISP. + + Adjust CMAKE_MODULE_PATH to find FindVISP.cmake or set VISP_DIR to the + directory containing a CMake configuration file for VISP. The file will + have one of the following names: + + VISPConfig.cmake + visp-config.cmake +\endcode + +To help \c cmake to find \c VISPConfig.cmake file, just set \c VISP_DIR environment variable and call \c cmake again: +\code +export VISP_DIR=/home/ViSP-install-folder/lib/<multi-arch-folder>/cmake/visp +cmake . +\endcode +or run cmake with the additional VISP_DIR definition +\code +cmake -DVISP_DIR=/home/ViSP-install-folder/lib/<multi-arch-folder>/cmake/visp . +\endcode + +Depending on the platform "<multi-arch-folder>" can be empty (OSX) or for example equal to "x86_64-linux-gnu" on Ubuntu. + + \subsection image_unix_build Generate the executable Just run: @@ -180,5 +205,5 @@ We suppose from now, that you have created a folder (let say \c C:/ViSP/ViSP-sta \image html img-lena-win.jpg -You are now ready to see the next \ref tutorial-grabber. +You are now ready to see the \ref tutorial-grabber or \ref tutorial-image-filtering. */ diff --git a/doc/tutorial-grabber.doc b/doc/tutorial-grabber.doc index e612372b244e6ad98c1616688ca08dc57f8108aa..3c2cf4df648651f09e7165ea3c2c82eb4c92ff93 100644 --- a/doc/tutorial-grabber.doc +++ b/doc/tutorial-grabber.doc @@ -7,22 +7,17 @@ \section grabber-camera-firewire-unix Images from firewire cameras -The next example shows how to use a framegrabber to acquire color images from a firewire camera under Unix. The following example suppose that libX11 and libdc1394-2 3rd party are available. +The next example also available in tutorial-grabber-1394.cpp shows how to use a framegrabber to acquire color images from a firewire camera under Unix. The following example suppose that libX11 and libdc1394-2 3rd party are available. \include tutorial-grabber-1394.cpp Here after we explain the new lines that are introduced. First an instance of the frame grabber is created. During the creating a bus reset is send. If you don't want to reset the firewire bus, just turn reset to false. -\code - vp1394TwoGrabber g(reset); -\endcode +\snippet tutorial-grabber-1394.cpp vp1394TwoGrabber construction Once the grabber is created, we set the camera image size, color coding, and framerate. -\code - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_60); -\endcode +\snippet tutorial-grabber-1394.cpp vp1394TwoGrabber settings Note that here you can specify some other settings such as the firewire transmission speed. For a more complete list of settings see vp1394TwoGrabber class. \code @@ -30,56 +25,46 @@ Note that here you can specify some other settings such as the firewire transmis \endcode Then the grabber is initialized using: -\code - g.open(I); -\endcode -From now the color image \c I is also initialized with the size corresponding to the grabber settings. +\snippet tutorial-grabber-1394.cpp vp1394TwoGrabber open +From now the color image \c I is also initialized with the size corresponding to the grabber settings. Then we enter in a while loop where image acquisition is simply done by: - -\code - g.acquire(I); -\endcode +\snippet tutorial-grabber-1394.cpp vp1394TwoGrabber acquire We are waiting for a non blocking mouse event to break the while loop before ending the program. -\code - if (vpDisplay::getClick(I, false)) break; -\endcode +\snippet tutorial-grabber-1394.cpp vp1394TwoGrabber click to exit In the previous example we use vp1394TwoGrabber class that works for firewire cameras under Unix. If you are under Windows, you may use vp1394CMUGrabber class. A similar example is provided in tutorial-grabber-CMU1394.cpp. - \section grabber-camera-other Images from other cameras - If you want to grab images from an usb camera under Unix, you may use vpV4l2Grabber class. To this end libv4l should be installed. An example is provided in tutorial-grabber-v4l2.cpp. It is also possible to grab images using OpenCV. You may find examples in tutorial-grabber-opencv.cpp and tutorial-grabber-opencv-bis.cpp. \section grabber-video-stream Images from a video stream -With ViSP it also possible to get images from an input video stream. To this end we exploit ffmpeg 3rd party. The example below available in tutorial-grabber-video.cpp shows how o consider an mpeg video stream: +With ViSP it also possible to get images from an input video stream. Supported formats are *.avi, *.mp4, *.mov, *.ogv, *.flv and many others... To this end we exploit ffmpeg or OpenCV 3rd parties. + +If ViSP was build with ffmpeg 3rd party support (cmake -DUSE_FFMPEG=ON ...), we use ffmpeg capabilities to decode the video stream. If ffmpeg is not found or used (cmake -DUSE_FFMPEG=OFF ...) and if OpenCV is available (cmake -DUSE_OPENCV=ON ...) we use rather OpenCV capabilities. This new feature was introduced in ViSP 2.10.0 and is especially useful under Windows where installing ffmpeg is quite complex. + +The example below available in tutorial-video-reader.cpp shows how o consider an mpeg video stream. + +\warning We recall that this example works only if ViSP was build with ffmpeg or OpenCV support. -\include tutorial-grabber-video.cpp +\include tutorial-video-reader.cpp We explain now the new lines that were introduced. -\code -#include <visp/vpTime.h> -#include <visp/vpVideoReader.h> -\endcode +\snippet tutorial-video-reader.cpp Include Include the header of the vpTime class that allows to measure time, and of the vpVideoReader class that allows to read a video stream. -\code - vpVideoReader g; -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader construction Create an instance of a video reader. -\code - g.setFileName("./video.mpg"); -\endcode -Set the name of the video stream. Here \c video.mpg refers to a mpeg file located in the same folder than the executable. +\snippet tutorial-video-reader.cpp vpVideoReader setting +Set the name of the video stream. Here \c videoname corresponds to a video file name location. For example we provide the file \c video.mpg located in the same folder than the executable. The vpVideoReader class can also handle a sequence of images. For example, to read the following images: @@ -93,40 +78,29 @@ image0004.png ... \endcode -you may use the following +you may use the following: \code g.setFileName("./image%04d.png"); \endcode -where you specify that each image number is coded with 4 digits. Here, ffmpeg is no yet mandatory, but rather \c libpng that should be available to read PNG images. Supported image formats are PPM, PGM, PNG and JPEG. In the last case, \c libjpeg should be installed. +where you specify that each image number is coded with 4 digits. Here, ffmpeg is no yet mandatory, but rather \c libpng or OpenCV that should be available to read PNG images. Supported image formats are PPM, PGM, PNG and JPEG. Then as for any other grabber, you have to initialize the frame grabber using: -\code - g.open(I); -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader open Then we enter in the while loop until the last image was not reached: -\code - while (! g.end() ) { -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader while loop To get the next image in the stream, we just use: - -\code - g.acquire(I); -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader acquire To synchronize the video decoding with the video framerate, we measure the beginning time of each loop iteration: -\code - double t = vpTime::measureTimeMs(); -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader loop start time The synchronization is done by waiting from the beginning of the iteration the corresponding time expressed in milliseconds by using: -\code - vpTime::wait(t, 1000. / g.getFramerate()); -\endcode +\snippet tutorial-video-reader.cpp vpVideoReader loop rate You are now ready to see the next \ref tutorial-tracking-blob. */ diff --git a/doc/tutorial-homography-deprecated.doc b/doc/tutorial-homography-deprecated.doc new file mode 100644 index 0000000000000000000000000000000000000000..dbc058f451029f84990dbf58a7334f4900a7e540 --- /dev/null +++ b/doc/tutorial-homography-deprecated.doc @@ -0,0 +1,131 @@ +/** + +\page tutorial-homography-deprecated Tutorial: Homography estimation from points (deprecated) + +\tableofcontents + +\section intro_homo_deprecated Introduction + +\note The second part of this tutorial is deprecated if your OpenCV version is equal to 2.8.0 or more recent. If so you should rather follow \ref tutorial-homography. + +This tutorial shows how to estimate an homography from points. Two cases are considered in the next sections: +- points are matched without possible mismatches. In that case vpHomography::DLT() or vpHomography::HLM() are used to estimate the homography, +- points are matched with possible mismatches. In that case vpHomography::ransac() or vpHomography::robust() are used. + +\section homography_deprecated Homography estimation + +Let us consider the following source code also available in tutorial-homography-from-points.cpp. To resume, we do the following: +- define four 3D points \c oP in an object frame, +- define an homogeneous transformation \c aMo from frame \c a to to the object frame \c o, +- define a similar homogeneous transformation \c bMo from frame \c b to to the object frame \c o, +- compute the coordinates of the four 3D points in the image plane \c a and \c b. These are the matched coordinates \c (xa,ya) and \c (xb,yb) that are then used to estimate the homography using either vpHomography::DLT() or vpHomography::HLM(). + +\include tutorial-homography-from-points.cpp + +Now we give a line by line explanation of the code: + +First we have to include the header of the vpHomography class. +\snippet tutorial-homography-from-points.cpp Include + +In the main() function we first define the 3D coordinates of 4 points that are localized in the plane Z=0: +\snippet tutorial-homography-from-points.cpp Set 3D points + +Then we define the homogeneous transformations between frames \c a, \c b and object frame \c o: +\snippet tutorial-homography-from-points.cpp Simulation + +From these transformations we compute the coordinates of the points in the image plane \c a and \c b. For each point we have its coordinates \c (xa,ya) in frame \c a and \c (xb,yb) in frame \c b: +\snippet tutorial-homography-from-points.cpp Image plane coordinates + +We have now matched couples of coordinates of four points that are used to estimate an homography between image plane \c a and \c b. Two methods are available, either using the DLT (Direct Linear Transform) algorithm or the HLM algorithm. +\snippet tutorial-homography-from-points.cpp Compute homography + +\note Note that vpHomography::HLM() allows to consider points that are not coplanar. + +Once the homography is estimated, the vpHomography class allows to extract the 3D homogeneous transformation between frames \c a and \c b: +\snippet tutorial-homography-from-points.cpp Get transformation + +Just for fun we print the values to this transformation using: +\snippet tutorial-homography-from-points.cpp Print results + +This code lead to the following output: +\code +Estimated displacement: + atb: 0.2016519874 -0.1008259937 0.1008259937 + athetaub: -3 20 5 + n: 0.2588190451 -1.124100812e-14 0.9659258263 +\endcode +where we can see that the values for \c atb and \c athetaub are the one specified at the beginning of the source code during \c aMb initialization. + +After we show how to retrieve the coordinates in pixels of a point (here point [3]) in the corresponding images using camera parameters: +\snippet tutorial-homography-from-points.cpp Get pixel coordinates + +At the end, we show how to project a point with pixel coordinates from image \c b to image \c a using the homography: +\snippet tutorial-homography-from-points.cpp Project + +This last part of the code produce the following output: +\code +Ground truth: Point 3 in pixels in frame b: 377.9450564, 193.9928711 +Ground truth: Point 3 in pixels in frame a: 353.8501593, 486.1851856 +Estimation from homography: Point 3 in pixels in frame a: 353.8501593, 486.1851856 +\endcode + + +\section ransac_deprecated Ransac or robust homography estimation + +This section follows the \ref tutorial-matching-deprecated. It explains how to exploit couples of matched points obtained using SURF detector in order to estimate an homography that allows to reject mismatched couples of points. The homography is then used to track a postcard from its initial position in the reference image. + +Let us consider the following source code also available in tutorial-matching-surf-homography-deprecated.cpp. + +\include tutorial-matching-surf-homography-deprecated.cpp + +The command line allows to use either Ransac algorithm of a robust M-estimator approach: +\code +% ./tutorial-matching-surf-homography-deprecated 0 // to run Ransac +% ./tutorial-matching-surf-homography-deprecated 1 // to run the robust M-estimator +\endcode + +Here after is the resulting video. The left image represents the reference image. The right images correspond to the successive images of the input video. All the green lines extremities represent the points that are well matched and used in the homography estimation process. All the red lines represent couple of matched points that are rejected by the robust estimator. + +\htmlonly +<iframe width="560" height="315" src="http://www.youtube.com/embed/g-aEH5Chmsg" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Now, let us explain the new lines that were introduced to estimate the homography. + +First we detect the command line arguments to be able later to user either Ransac or the robust M-estimator: +\snippet tutorial-matching-surf-homography-deprecated.cpp Select method + +We also initialize the coordinates of the pixels in the reference image that correspond to the postcard corners. These coordinates were obtained after a user initial click. To simplify the code, we set directly the coordinates of the points: +\snippet tutorial-matching-surf-homography-deprecated.cpp Set coordinates + +Using these coordinates, we display red lines around the postcard: +\snippet tutorial-matching-surf-homography-deprecated.cpp Display + +We need also to define roughly the parameters of our camera: +\snippet tutorial-matching-surf-homography-deprecated.cpp Camera + +For each new image, once points are matched using: +\snippet tutorial-matching-surf-homography-deprecated.cpp Matching + +We allocate new containers useful for the homography estimation. The coordinates of the points in the reference image will be stored in \c (mPref_x, mPref_y), while the corresponding coordinates in the current image will be stored in \c (mPcur_x, mPcur_y). We also allocate \c inliers a vector of boolean that will indicate if a couple of point is an inlier or an outlier: +\snippet tutorial-matching-surf-homography-deprecated.cpp Allocation + +To estimate the homography we need first to convert the points from pixel to meters: +\snippet tutorial-matching-surf-homography-deprecated.cpp Pixel conversion + +We can now estimate the homography using either Ransac or the robust M-estimator approach: +\snippet tutorial-matching-surf-homography-deprecated.cpp Homography estimation + +For Ransac we consider that at least 50 percent of the points should be inliers (\c mPref_x.size()/2) to reach a consensus and that a couple of point is stated as an inlier if the reprojection error is lower than 2 pixels (\c 2.0/cam.get_px()). + +Then using the homography, we project the coordinates of the postcard corners in the current image: +\snippet tutorial-matching-surf-homography-deprecated.cpp Projection + +We use these coordinates to draw blue lines arround the postcard: +\snippet tutorial-matching-surf-homography-deprecated.cpp Display contour + +Since the homography estimation updates the status of the couples of matched points as inliers or outliers, between the matched points we are able to draw green lines when they are inliers, or red lines when they are outliers. +\snippet tutorial-matching-surf-homography-deprecated.cpp Display matches + + +*/ diff --git a/doc/tutorial-homography.doc b/doc/tutorial-homography.doc new file mode 100644 index 0000000000000000000000000000000000000000..cc5ca7862c408af0fb494ca8d158124be0336805 --- /dev/null +++ b/doc/tutorial-homography.doc @@ -0,0 +1,129 @@ +/** + +\page tutorial-homography Tutorial: Homography estimation from points + +\tableofcontents + +\section intro Introduction + +This tutorial shows how to estimate an homography from points. Two cases are considered in the next sections: +- points are matched without possible mismatches. In that case vpHomography::DLT() or vpHomography::HLM() are used to estimate the homography, +- points are matched with possible mismatches. In that case vpHomography::ransac() or vpHomography::robust() are used. + +\section homography Homography estimation + +Let us consider the following source code also available in tutorial-homography-from-points.cpp. To resume, we do the following: +- define four 3D points \c oP in an object frame, +- define an homogeneous transformation \c aMo from frame \c a to to the object frame \c o, +- define a similar homogeneous transformation \c bMo from frame \c b to to the object frame \c o, +- compute the coordinates of the four 3D points in the image plane \c a and \c b. These are the matched coordinates \c (xa,ya) and \c (xb,yb) that are then used to estimate the homography using either vpHomography::DLT() or vpHomography::HLM(). + +\include tutorial-homography-from-points.cpp + +Now we give a line by line explanation of the code: + +First we have to include the header of the vpHomography class. +\snippet tutorial-homography-from-points.cpp Include + +In the main() function we first define the 3D coordinates of 4 points that are localized in the plane Z=0: +\snippet tutorial-homography-from-points.cpp Set 3D points + +Then we define the homogeneous transformations between frames \c a, \c b and object frame \c o: +\snippet tutorial-homography-from-points.cpp Simulation + +From these transformations we compute the coordinates of the points in the image plane \c a and \c b. For each point we have its coordinates \c (xa,ya) in frame \c a and \c (xb,yb) in frame \c b: +\snippet tutorial-homography-from-points.cpp Image plane coordinates + +We have now matched couples of coordinates of four points that are used to estimate an homography between image plane \c a and \c b. Two methods are available, either using the DLT (Direct Linear Transform) algorithm or the HLM algorithm. +\snippet tutorial-homography-from-points.cpp Compute homography + +\note Note that vpHomography::HLM() allows to consider points that are not coplanar. + +Once the homography is estimated, the vpHomography class allows to extract the 3D homogeneous transformation between frames \c a and \c b: +\snippet tutorial-homography-from-points.cpp Get transformation + +Just for fun we print the values to this transformation using: +\snippet tutorial-homography-from-points.cpp Print results + +This code lead to the following output: +\code +Estimated displacement: + atb: 0.2016519874 -0.1008259937 0.1008259937 + athetaub: -3 20 5 + n: 0.2588190451 -1.124100812e-14 0.9659258263 +\endcode +where we can see that the values for \c atb and \c athetaub are the one specified at the beginning of the source code during \c aMb initialization. + +After we show how to retrieve the coordinates in pixels of a point (here point [3]) in the corresponding images using camera parameters: +\snippet tutorial-homography-from-points.cpp Get pixel coordinates + +At the end, we show how to project a point with pixel coordinates from image \c b to image \c a using the homography: +\snippet tutorial-homography-from-points.cpp Project + +This last part of the code produce the following output: +\code +Ground truth: Point 3 in pixels in frame b: 377.9450564, 193.9928711 +Ground truth: Point 3 in pixels in frame a: 353.8501593, 486.1851856 +Estimation from homography: Point 3 in pixels in frame a: 353.8501593, 486.1851856 +\endcode + + +\section ransac Ransac or robust homography estimation + +This section follows the \ref tutorial-matching. It explains how to exploit couples of matched points obtained using SURF detector in order to estimate an homography that allows to reject mismatched couples of points. The homography is then used to track a postcard from its initial position in the reference image. + +Let us consider the following source code also available in tutorial-matching-keypoint-homography.cpp. + +\include tutorial-matching-keypoint-homography.cpp + +The command line allows to use either Ransac algorithm of a robust M-estimator approach: +\code +% ./tutorial-matching-keypoint-homography 0 // to run Ransac +% ./tutorial-matching-keypoint-homography 1 // to run the robust M-estimator +\endcode + +Here after is the resulting video. The left image represents the reference image. The right images correspond to the successive images of the input video. All the green lines extremities represent the points that are well matched and used in the homography estimation process. All the red lines represent couple of matched points that are rejected by the robust estimator. + +\htmlonly +<iframe width="560" height="315" src="http://www.youtube.com/embed/g-aEH5Chmsg" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Now, let us explain the new lines that were introduced to estimate the homography. + +First we detect the command line arguments to be able later to user either Ransac or the robust M-estimator: +\snippet tutorial-matching-keypoint-homography.cpp Select method + +We also initialize the coordinates of the pixels in the reference image that correspond to the postcard corners. These coordinates were obtained after a user initial click. To simplify the code, we set directly the coordinates of the points: +\snippet tutorial-matching-keypoint-homography.cpp Set coordinates + +Using these coordinates, we display red lines around the postcard: +\snippet tutorial-matching-keypoint-homography.cpp Display + +We need also to define roughly the parameters of our camera: +\snippet tutorial-matching-keypoint-homography.cpp Camera + +For each new image, once points are matched using: +\snippet tutorial-matching-keypoint-homography.cpp Matching + +We allocate new containers useful for the homography estimation. The coordinates of the points in the reference image will be stored in \c (mPref_x, mPref_y), while the corresponding coordinates in the current image will be stored in \c (mPcur_x, mPcur_y). We also allocate \c inliers a vector of boolean that will indicate if a couple of point is an inlier or an outlier: +\snippet tutorial-matching-keypoint-homography.cpp Allocation + +To estimate the homography we need first to convert the points from pixel to meters: +\snippet tutorial-matching-keypoint-homography.cpp Pixel conversion + +We can now estimate the homography using either Ransac or the robust M-estimator approach: +\snippet tutorial-matching-keypoint-homography.cpp Homography estimation + +For Ransac we consider that at least 50 percent of the points should be inliers (\c mPref_x.size()/2) to reach a consensus and that a couple of point is stated as an inlier if the reprojection error is lower than 2 pixels (\c 2.0/cam.get_px()). + +Then using the homography, we project the coordinates of the postcard corners in the current image: +\snippet tutorial-matching-keypoint-homography.cpp Projection + +We use these coordinates to draw blue lines arround the postcard: +\snippet tutorial-matching-keypoint-homography.cpp Display contour + +Since the homography estimation updates the status of the couples of matched points as inliers or outliers, between the matched points we are able to draw green lines when they are inliers, or red lines when they are outliers. +\snippet tutorial-matching-keypoint-homography.cpp Display matches + + +*/ diff --git a/doc/tutorial-ibvs.doc b/doc/tutorial-ibvs.doc index 694fd20c71719515fdf50cb1e0dcc7264aa820b9..be371d3c41281fed77098cf318deb12fd0bec95a 100644 --- a/doc/tutorial-ibvs.doc +++ b/doc/tutorial-ibvs.doc @@ -104,7 +104,7 @@ It is now time to define four visual features as points in the image-plane. To t Each feature is obtained by computing the position of the 3D points in the corresponding camera frame, and then by applying the perspective projection. Once current and desired features are created, they are added to the visual servo task. \code - for (int i = 0 ; i < 4 ; i++) { + for (unsigned int i = 0 ; i < 4 ; i++) { point[i].track(cdMo); vpFeatureBuilder::create(pd[i], point[i]); point[i].track(cMo); @@ -141,7 +141,7 @@ Now we can enter in the visual servo loop. When a velocity is applied to our fre The current visual features are then updated by projecting the 3D points in the image-plane associated to the new camera location \c cMo. \code - for (int i = 0 ; i < 4 ; i++) { + for (unsigned int i = 0 ; i < 4 ; i++) { point[i].track(cMo); vpFeatureBuilder::create(p[i], point[i]); } @@ -232,7 +232,7 @@ We create an instance of the vpProjectionDisplay class. This class is only avail \code #if defined(VISP_HAVE_DISPLAY) vpProjectionDisplay externalview; - for (int i = 0 ; i < 4 ; i++) + for (unsigned int i = 0 ; i < 4 ; i++) externalview.insert(point[i]) ; #endif \endcode @@ -354,7 +354,7 @@ To retrieve the simulated image that depends on the simulated camera position we The current visual features are computed using a vpDot2 blob tracker. Once four trackers are instantiated, we are waiting for a mouse click in each blob to initialize the tracker. Then we compute the current visual features \f$(x,y) \f$ from the camera parameters and the cog position of each blob. \code - for (int i = 0 ; i < 4 ; i++) { + for (unsigned int i = 0 ; i < 4 ; i++) { ... dot[i].setGraphics(true); dot[i].initTracking(I); @@ -372,7 +372,7 @@ In the visual servo loop, at each iteration we get a new image of the target. We track each blob and update the values of the current visual features as previously. \code - for (int i = 0 ; i < 4 ; i++) { + for (unsigned int i = 0 ; i < 4 ; i++) { dot[i].track(I); vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); \endcode @@ -387,4 +387,5 @@ depends on \f$(x,y)\f$ but also on \f$Z\f$ the depth of the feature, we need to } \endcode +You are now ready to see the \ref tutorial-simu-robot-pioneer and \ref tutorial-boost-vs. */ diff --git a/doc/tutorial-image-filtering.doc b/doc/tutorial-image-filtering.doc new file mode 100644 index 0000000000000000000000000000000000000000..df5b59e57290d579430ee11c9dd26746b86e589a --- /dev/null +++ b/doc/tutorial-image-filtering.doc @@ -0,0 +1,118 @@ +/** + +\page tutorial-image-filtering Tutorial: Image filtering +\tableofcontents + +This tutorial supposes that you have followed the \ref tutorial-getting-started. + +\section intro_img_filtering Introduction + +In this tutorial you will learn how to use ViSP filtering functions implemented in vpImageFilter class. + +Let us consider the following source code that comes from tutorial-image-filter.cpp. + +\include tutorial-image-filter.cpp + +Once build, you should have \c tutorial-image-filter binary. It shows how to apply different filters on an input image. Here we will consider lena.pgm as input image. + +\image html img-lena-gray.png + +To see the resulting filtered images, just run: + +\code +./tutorial-image-filter lena.pgm +\endcode + +The following sections give a line by line explanation of the source code dedicated to image filtering capabilities. + +\section blur Gaussian blur + +Lena input image is read from disk and is stored in \c I which is a gray level image declared as + +\snippet tutorial-image-filter.cpp vpImage construction + +To apply a Gaussian blur to this image we first have to declare a resulting floating-point image \c F. Then the blurred image could be obtained using the default Gaussian filter: + +\snippet tutorial-image-filter.cpp Gaussian blur + +The resulting image is the following: + +\image html img-lena-blured-default.png + +It is also possible to specify the Gaussian filter kernel size and the Gaussian standard deviation (sigma) using: + +\code +vpImageFilter::gaussianBlur(I, F, 7, 2); // Kernel size: 7, sigma: 2 +\endcode + +We thus obtain the following image: + +\image html img-lena-blured-var2.png + +\section gradient Gradients computation + +To compute the gradients or the spatial derivative along X use: + +\snippet tutorial-image-filter.cpp Gradients x + +Gradients along Y could be obtained using: + +\snippet tutorial-image-filter.cpp Gradients y + +The resulting floating-point images \c dIx, \c dIy are the following: + +\image html img-lena-dIxy.png + +\section canny Canny edge detector + +Canny edge detector function is only available if ViSP was build with OpenCV 2.1 or higher. + +After the declaration of a new image container \c C, Canny edge detector is applied using: +\snippet tutorial-image-filter.cpp Canny + +Where: +- 5: is the low threshold +- 15: is the high threshold set in the program as three times the lower threshold (following Canny’s recommendation) +- 3: is the size of the Sobel kernel used internally. + +The resulting image \c C is the following: + +\image html img-lena-canny.png + +\section convolution Convolution + +To apply a convolution to an image, we first have to define a kernel. +For example, let us consider the 3x3 Sobel kernel defined in \c K. + + \f[ + {\bf K} = \begin{tabular}{|c|c|c|} + \hline + 1 & 0 & -1 \\ + \hline + 2 & 0 & -2 \\ + \hline + 1 & 0 & -1 \\ + \hline + \end{tabular} + \f] + +\snippet tutorial-image-filter.cpp Convolution kernel + +After the declaration of a new floating-point image \c Gx, the convolution is obtained using: +\snippet tutorial-image-filter.cpp Convolution + +The content of the filtered image \c Gx is the following. + +\image html img-lena-sobel.png + +\section pyramid Gaussian image pyramid + +To construct a pyramid of Gaussian filtered images as a vector of images implemented in \c pyr[] you may use: +\snippet tutorial-image-filter.cpp Gaussian pyramid + +The content of \c pyr[0], \c pyr[1], \c pyr[2] is the following: +\image html img-lena-pyr.png + +You are now ready to see the next \ref tutorial-tracking-blob. + +*/ diff --git a/doc/tutorial-install-centos.doc b/doc/tutorial-install-centos.doc new file mode 100644 index 0000000000000000000000000000000000000000..5f59d50c28db6ea729cb3101a20eae7c9408bb6b --- /dev/null +++ b/doc/tutorial-install-centos.doc @@ -0,0 +1,246 @@ +/** + +\page tutorial-install-centos Tutorial: Installation from source on Linux CentOS +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on CentOS. These steps have been tested for CentOS 7.0 (x86_64) distribution, but should work with any other distribution as well. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_centos_required Required packages + +- gcc 4.4.x or later. This can be installed with: +\code +sudo yum install gcc-c++ +\endcode +- CMake 2.6 or higher that could be installed with: +\code +sudo yum install cmake +\endcode + +ViSP is interfaced with several optional <a href="http://www.irisa.fr/lagadic/visp/libraries.html">third-party libraries</a>. The installation of the corresponding packages is described in \ref install_centos_3rdparty section. + +\section install_centos_get_source Getting ViSP source code + +There are different ways to get ViSP source code: + +- You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip or a tarball. Once downloaded, uncompress the file using either +\code +tar xvzf ViSP-2.10.0.tar.gz +\endcode +or +\code +unzip ViSP-2.10.0.zip +\endcode + +- You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. Once downloaded, uncompress the file using +\code +unzip ViSP-2.y.z-snapshot-2015.mm.dd.zip +\endcode +- Or you get the cutting-edge ViSP from Subversion repository. To this end you have first to install subversion +\code +sudo yum install subversion +\endcode +and then use the following command +\code +svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-trunk +\endcode + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.10.0 + +\section install_centos_config Configuring ViSP from source + +- Create first a directory denoted \<binary_dir\> where you want to build ViSP. This directory will contain generated Makefiles, object files, and output libraries and binaries. +\code +cd $HOME; mkdir ViSP-build-release +\endcode + +- Enter the \<binary_dir\> and to configure the build type: +\code +cmake [<optional parameters>] <source_dir> +\endcode +For example: +\code +cd $HOME/ViSP-build-release +cmake -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_SHARED_LIBS=ON ../ViSP-2.10.0 +\endcode +A more versatile way to configure the build is to use \c ccmake, the CMake GUI: +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd parties that will be used. +\image html img-ccmake-centos.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP. + +- There is an other way to see which are the 3rd parties that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter an example of a possible content of this file: +\code + ViSP third-party libraries + +Below you will find the list of third party libraries used to +build ViSP on your computer. + +Mathematics: + Gnu Scientific Library : no + Lapack/blas : no +Simulator: + Ogre simulator : no + \- Ogre3D : no + \- OIS : no + Coin simulator : + \- Coin3D : no + \- SoWin : no + \- SoXt : no + \- SoQt : no + \- Qt4 : no + \- Qt3 : no +Robots + Afma6 : no + Afma4 : no + Biclops : no + Ptu46 : no + Pioneer : no + Viper S650 : no + Viper S850 : no +Video devices (display) + X11 : no + GTK : no + OpenCV : no + GDI : no + Direct3D : no +Framegrabbers + Firewire libdc1394-1.x : no + Firewire libdc1394-2.x : no + Video For Linux Two : no + DirectShow : no + CMU 1394 Digital Camera SDK : no + OpenCV : no +Specific devices + Yarp : no + Kinect : no + \-libfreenect : no + \-libusb-1.0 : no + \-pthread : yes +Video and image Read/Write: + FFMPEG : no + libjpeg : no + libpng : no +Misc: + XML2 : no + pthread : yes + OpenMP : yes + zbar : no + dmtx : no +Documentation: + Doxygen : no + Graphviz dot : no +ViSP built with C++11 features: no +\endcode +In our case, only \c pthread an OpenMP 3rd parties are detected. + +\subsection install_centos_3rdparty Optional 3rd party packages + +As mentioned previously, ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. We recommend to install the following: + +- OpenCV +\code +sudo yum install opencv-devel +\endcode +- libX11 to be able to open a window to display images +\code +sudo yum install libX11-devel +\endcode +- lapack and gsl to benefit from optimized mathematical capabilities +\code +sudo yum install lapack-devel gsl-devel +\endcode +- libv4l to grab images from usb or analogic cameras +\code +sudo yum install libv4l-devel +\endcode +- libxml2 to be able to configure the model-based trackers from xml files +\code +sudo yum install libxml2-devel +\endcode +- libjpeg, libpng to support jpeg and png images +\code +sudo yum install libjpeg-devel libpng-devel +\endcode + +Once installed, if you want that ViSP exploit the new 3rd parties, you have to configure ViSP again. + +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows now that all the previous optional 3rd parties are detected. +\image html img-ccmake-centos-all.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP after installation of optional 3rd party libraries. + +\note Other <a href="http://www.irisa.fr/lagadic/visp/libraries.html">3rd party libraries</a> are interfaced with ViSP. This is for example the case of <a href="http://www.irisa.fr/lagadic/visp/librarieslist.html#Ogre">Ogre</a> that can be used to enhance the visibility computation of the faces that are tracked with the model-based tracker (see \ref tutorial-tracking-mb), or <a href="http://www.irisa.fr/lagadic/visp/librarieslist.html#Coin">Coin</a> that can be used to parse vrml files that describe the 3D model of the object to track using the same model-based tracker. It seems that these 3rd parties are not packaged on CentOS. If you want to use these 3rd parties, you have to install them from source code. + +\subsection install_centos_generate Generating Makefiles + +To generate the makefiles, just press [g] key in the ccmake gui. + +Now we can build ViSP. + +\subsection install_centos_issues Known issues + +- On CentOS 7.0 with cmake 2.8.11, during cmake configuration you may encounter the following issue: +\code +CMake Warning at src/CMakeLists.txt:80 (add_library): + Cannot generate a safe runtime search path for target visp because files in + some directories may conflict with libraries in implicit directories: + + runtime library [libpthread.so] in /usr/lib64 may be hidden by files in: + //lib64 + + Some of these libraries may not be found correctly. +\endcode +The problem was that libpthread.so exists in /usr/lib64 and in //lib64. In //lib64 it should be a symbolic link to /usr/lib64. +\code +$ ls -als //lib64 + 0 lrwxrwxrwx. 1 root root 9 Feb 4 12:16 //lib64 -> usr/lib64 +$ ls -als //lib64/libpthread* +140 -rwxr-xr-x. 1 root root 141616 Jan 27 15:13 //lib64/libpthread-2.17.so + 4 -rw-r--r--. 1 root root 222 Jan 27 14:42 //lib64/libpthread.so + 0 lrwxrwxrwx. 1 root root 18 Feb 4 12:34 //lib64/libpthread.so.0 -> libpthread-2.17.so +\endcode +The fix consists in removing //lib64/libpthread.so and creating a new symbolic link +\code +$ cd //lib64 +$ sudo rm libpthread.so +$ sudo ln -s libpthread-2.17.so libpthread.so +$ ls -als libpthread* +140 -rwxr-xr-x. 1 root root 141616 Jan 27 15:13 libpthread-2.17.so + 0 lrwxrwxrwx. 1 root root 18 Feb 4 16:09 libpthread.so -> libpthread-2.17.so + 0 lrwxrwxrwx. 1 root root 18 Feb 4 12:34 libpthread.so.0 -> libpthread-2.17.so +\endcode + +- On CentOS 7.0, vpVideoReader is not able to read mpeg videos. If ffmpeg is not installed (this is our case since ffmpeg is not packaged for CentOS 7.0), this class uses OpenCV to read and decode videos. Some examples or tutorials provided in ViSP hang during cv::Capture::open() call. The reason is that OpenCV 2.4.5 cv::Capture seams buggy. This is for example the case if you run: +\code +$ ./example/video/videoReader +\endcode +A work arround consists in installing a more recent OpenCV version from source. + +\section install_centos_build Building ViSP from source + +- To build ViSP proceed with: +\code +make -j4 +\endcode + +- To install ViSP proceed with: +\code +sudo make install +\endcode + +- To build ViSP documentation, you have first to install Doxygen package: +\code +sudo yum install doxygen graphviz +\endcode +Then you can proceed with: +\code +make visp_doc +\endcode + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-install-fedora.doc b/doc/tutorial-install-fedora.doc new file mode 100644 index 0000000000000000000000000000000000000000..a9d4d85ba452bd31d8aba0ee61879ca7a7a64b32 --- /dev/null +++ b/doc/tutorial-install-fedora.doc @@ -0,0 +1,226 @@ +/** + +\page tutorial-install-fedora Tutorial: Installation from source on Linux Fedora +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on Linux Fedora. These steps have been tested for Fedora 21 (64 bit) distribution, but should work with any other distribution as well. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_fedora_required Required packages + +- gcc 4.4.x or later. This can be installed with: +\code +su -c "yum install gcc-c++" +\endcode +- CMake 2.6 or higher that could be installed with: +\code +su -c "yum install cmake" +\endcode + + +ViSP is interfaced with several optional <a href="http://www.irisa.fr/lagadic/visp/libraries.html">third-party libraries</a>. The installation of the corresponding packages is described in \ref install_fedora_3rdparty section. + +\section install_fedora_get_source Getting ViSP source code + +There are different ways to get ViSP source code: + +- You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip or a tarball. Once downloaded, uncompress the file using either +\code +tar xvzf ViSP-2.10.0.tar.gz +\endcode +or +\code +unzip ViSP-2.10.0.zip +\endcode + +- You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. Once downloaded, uncompress the file using +\code +unzip ViSP-2.y.z-snapshot-2015.mm.dd.zip +\endcode +- Or you get the cutting-edge ViSP from Subversion repository. To this end you have first to install subversion +\code +su -c "yum install subversion" +\endcode +and then use the following command +\code +svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-trunk +\endcode + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.10.0 + +\section install_fedora_config Configuring ViSP from source + +- Create first a directory denoted \<binary_dir\> where you want to build ViSP. This directory will contain generated Makefiles, object files, and output libraries and binaries. +\code +cd $HOME; mkdir ViSP-build-release +\endcode + +- Enter the \<binary_dir\> and to configure the build type: +\code +cmake [<optional parameters>] <source_dir> +\endcode +For example: +\code +cd $HOME/ViSP-build-release +cmake -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_SHARED_LIBS=ON ../ViSP-2.10.0 +\endcode +A more versatile way to configure the build is to use \c ccmake, the CMake GUI: +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd parties that will be used. +\image html img-ccmake-ubuntu.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP. + +- There is an other way to see which are the 3rd parties that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter an example of a possible content of this file: +\code + ViSP third-party libraries + +Below you will find the list of third party libraries used to +build ViSP on your computer. + +Mathematics: + Gnu Scientific Library : no + Lapack/blas : no +Simulator: + Ogre simulator : no + \- Ogre3D : no + \- OIS : no + Coin simulator : + \- Coin3D : no + \- SoWin : no + \- SoXt : no + \- SoQt : no + \- Qt4 : no + \- Qt3 : no +Robots + Afma6 : no + Afma4 : no + Biclops : no + Ptu46 : no + Pioneer : no + Viper S650 : no + Viper S850 : no +Video devices (display) + X11 : no + GTK : no + OpenCV : no + GDI : no + Direct3D : no +Framegrabbers + Firewire libdc1394-1.x : no + Firewire libdc1394-2.x : no + Video For Linux Two : no + DirectShow : no + CMU 1394 Digital Camera SDK : no + OpenCV : no +Specific devices + Yarp : no + Kinect : no + \-libfreenect : no + \-libusb-1.0 : no + \-pthread : yes +Video and image Read/Write: + FFMPEG : no + libjpeg : no + libpng : no +Misc: + XML2 : no + pthread : yes + OpenMP : yes + zbar : no + dmtx : no +Documentation: + Doxygen : no + Graphviz dot : no +ViSP built with C++11 features: no +\endcode +In our case, only \c pthread an OpenMP 3rd parties are detected. + +\subsection install_fedora_3rdparty Optional 3rd party packages + +As mentioned previously, ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. We recommend to install the following: + +- OpenCV +\code +su -c "yum install opencv-devel" +\endcode +- libX11 to be able to open a window to display images +\code +su -c "yum install libX11-devel" +\endcode +- lapack and gsl to benefit from optimized mathematical capabilities +\code +su -c "yum install lapack-devel gsl-devel" +\endcode +- libdc1394 to grab images from firewire cameras +\code +su -c "yum install libdc1394-devel" +\endcode +- libv4l to grab images from usb or analogic cameras +\code +su -c "yum install libv4l-devel" +\endcode +- Coin, to be able to support vrml cad model used by the model-based trackers +\code +su -c "yum install Coin2-devel" +\endcode +- libxml2 to be able to configure the model-based trackers from xml files +\code +su -c "yum install libxml2-devel" +\endcode +- libjpeg, libpng to support jpeg and png images +\code +su -c "yum install libjpeg-devel libpng-devel" +\endcode +- ffmpeg, to be able to read or encode compressed video streams +\code +su -c "yum install ffmpeg-devel" +\endcode +- Ogre 3D if you want to do augmented reality or simulation +\code +su -c "yum install ogre-devel ogre-samples ois-devel" +\endcode +- Bar code detection +\code +su -c "yum install zbar-devel libdmtx-devel" +\endcode + +Once installed, if you want that ViSP exploit the new 3rd parties, you have to configure ViSP again. + +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows now that all the previous optional 3rd parties are detected. +\image html img-ccmake-ubuntu-all.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP after installation of optional 3rd party libraries. + +\subsection install_fedora_generate Generating Makefiles + +To generate the makefiles, just press [g] key in the ccmake gui. + +Now we can build ViSP. + +\section install_fedora_build Building ViSP from source + +- To build ViSP proceed with: +\code +make -j4 +\endcode + +- To install ViSP proceed with: +\code +sudo make install +\endcode + +- To build ViSP documentation, you have first to install Doxygen package: +\code +su -c "yum install doxygen graphviz" +\endcode +Then you can proceed with: +\code +make visp_doc +\endcode + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-install-iOS.doc b/doc/tutorial-install-iOS.doc index e327c6f3b849dbccec693ce98c4922adb14bd889..040da89d3bbe595beaadad465f20432fad1833e2 100644 --- a/doc/tutorial-install-iOS.doc +++ b/doc/tutorial-install-iOS.doc @@ -5,12 +5,7 @@ In this tutorial you will learn how to install ViSP from source on OSX in order to be able to compile and use it for iOS development. These steps have been tested for OSX 10.8.4 but should work with any other distribution as well. -Concerning ViSP installation, we provide also other tutorials -- \ref tutorial-install-win -- \ref tutorial-install-ubuntu - -ViSP can also be installed on other platforms including OSX, Fedora, CentOS, openSUZE (see <a href="http://www.irisa.fr/lagadic/visp/install.html">Install from source page</a>). - +\note Concerning ViSP installation, we provide also other \ref tutorial. \section install_iOS_required Required software @@ -25,23 +20,23 @@ There are different ways to get ViSP source code: - You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip or a tarball. Once downloaded, uncompress the file using either \code -tar xvzf ViSP-2.7.0.tar.gz +tar xvzf ViSP-2.10.0.tar.gz \endcode or \code -unzip ViSP-2.7.0.zip +unzip ViSP-2.10.0.zip \endcode - You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. Once downloaded, uncompress the file using \code -unzip ViSP-2.7.1-snapshot-2013.05.02.zip +unzip ViSP-2.y.z-snapshot-2015.mm.dd.zip \endcode - Or you get the cutting-edge ViSP from Subversion repository. To this end you have first to install subversion and then use the following command \code svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-trunk \endcode -We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.7.0 +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.10.0 \section install_iOS_config Configuring ViSP from source @@ -67,7 +62,7 @@ As mentioned previously, ViSP is interfaced with some 3rd party libraries. The < - liblapack, libxml2, libjpeg, libpng, ffmpeg should be already included in your OS X package -Once installed, if you want that ViSP exploit the new 3rd party, you have to configure ViSP again. +Once installed, if you want that ViSP exploit the new 3rd parties, you have to configure ViSP again. Now we can finish the configuration stage by generating the Xcode project. diff --git a/doc/tutorial-install-opensuse.doc b/doc/tutorial-install-opensuse.doc new file mode 100644 index 0000000000000000000000000000000000000000..e1f3340b88d9b41e2f582c664cb01131d00a2cc7 --- /dev/null +++ b/doc/tutorial-install-opensuse.doc @@ -0,0 +1,215 @@ +/** + +\page tutorial-install-opensuse Tutorial: Installation from source on Linux openSUSE +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on openSUSE. These steps have been tested for openSUSE 13.2 (x86_64) distribution, but should work with any other distribution as well. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_opensuse_required Required packages + +- gcc 4.4.x or later. This can be installed with: +\code +sudo zypper install gcc-c++ +\endcode +- CMake 2.6 or higher that could be installed with: +\code +sudo zypper install cmake +\endcode + +ViSP is interfaced with several optional <a href="http://www.irisa.fr/lagadic/visp/libraries.html">third-party libraries</a>. The installation of the corresponding packages is described in \ref install_opensuse_3rdparty section. + +\section install_opensuse_get_source Getting ViSP source code + +There are different ways to get ViSP source code: + +- You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip or a tarball. Once downloaded, uncompress the file using either +\code +tar xvzf ViSP-2.10.0.tar.gz +\endcode +or +\code +unzip ViSP-2.10.0.zip +\endcode + +- You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. Once downloaded, uncompress the file using +\code +unzip ViSP-2.y.z-snapshot-2015.mm.dd.zip +\endcode +- Or you get the cutting-edge ViSP from Subversion repository. To this end you have first to install subversion +\code +sudo zypper install subversion +\endcode +and then use the following command +\code +svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-trunk +\endcode + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.10.0 + +\section install_opensuse_config Configuring ViSP from source + +- Create first a directory denoted \<binary_dir\> where you want to build ViSP. This directory will contain generated Makefiles, object files, and output libraries and binaries. +\code +cd $HOME; mkdir ViSP-build-release +\endcode + +- Enter the \<binary_dir\> and to configure the build type: +\code +cmake [<optional parameters>] <source_dir> +\endcode +For example: +\code +cd $HOME/ViSP-build-release +cmake -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_SHARED_LIBS=ON ../ViSP-2.10.0 +\endcode +A more versatile way to configure the build is to use \c ccmake, the CMake GUI: +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd parties that will be used. +\image html img-ccmake-opensuse.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP. + +- There is an other way to see which are the 3rd parties that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter an example of a possible content of this file: +\code + ViSP third-party libraries + +Below you will find the list of third party libraries used to +build ViSP on your computer. + +Mathematics: + Gnu Scientific Library : no + Lapack/blas : no +Simulator: + Ogre simulator : no + \- Ogre3D : no + \- OIS : no + Coin simulator : + \- Coin3D : no + \- SoWin : no + \- SoXt : no + \- SoQt : no + \- Qt4 : no + \- Qt3 : no +Robots + Afma6 : no + Afma4 : no + Biclops : no + Ptu46 : no + Pioneer : no + Viper S650 : no + Viper S850 : no +Video devices (display) + X11 : no + GTK : no + OpenCV : no + GDI : no + Direct3D : no +Framegrabbers + Firewire libdc1394-1.x : no + Firewire libdc1394-2.x : no + Video For Linux Two : no + DirectShow : no + CMU 1394 Digital Camera SDK : no + OpenCV : no +Specific devices + Yarp : no + Kinect : no + \-libfreenect : no + \-libusb-1.0 : no + \-pthread : yes +Video and image Read/Write: + FFMPEG : no + libjpeg : no + libpng : no +Misc: + XML2 : no + pthread : yes + OpenMP : yes + zbar : no + dmtx : no +Documentation: + Doxygen : no + Graphviz dot : no +ViSP built with C++11 features: no +\endcode +In our case, only \c pthread an OpenMP 3rd parties are detected. + +\subsection install_opensuse_3rdparty Optional 3rd party packages + +As mentioned previously, ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. We recommend to install the following: + +- OpenCV +\code +sudo zypper install opencv-devel +\endcode +- libX11 to be able to open a window to display images +\code +sudo zypper install libX11-devel +\endcode +- lapack and gsl to benefit from optimized mathematical capabilities +\code +sudo zypper install lapack-devel gsl-devel +\endcode +- libv4l to grab images from usb or analogic cameras +\code +sudo zypper install libv4l-devel +\endcode +- libxml2 to be able to configure the model-based trackers from xml files +\code +sudo zypper install libxml2-devel +\endcode +- libjpeg and libpng to support jpeg and png images +\code +sudo zypper install libjpeg8-devel libpng-devel +\endcode + +Once installed, if you want that ViSP exploit the new 3rd parties, you have to configure ViSP again. + +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows now that all the previous optional 3rd parties are detected. +\image html img-ccmake-opensuse-all.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP after installation of optional 3rd party libraries. + +\note Other <a href="http://www.irisa.fr/lagadic/visp/libraries.html">3rd party libraries</a> are interfaced with ViSP. This is for example the case of <a href="http://www.irisa.fr/lagadic/visp/librarieslist.html#Ogre">Ogre</a> that can be used to enhance the visibility computation of the faces that are tracked with the model-based tracker (see \ref tutorial-tracking-mb), or <a href="http://www.irisa.fr/lagadic/visp/librarieslist.html#Coin">Coin</a> that can be used to parse vrml files that describe the 3D model of the object to track using the same model-based tracker. It seems that these 3rd parties are not packaged on openSUSE. If you want to use these 3rd parties, you have to install them from source code. + +\subsection install_opensuse_generate Generating Makefiles + +To generate the makefiles, just press [g] key in the ccmake gui. + +Now we can build ViSP. + +\subsection install_opensuse_issues Known issues + +- Note that with openSUSE 12.04 but also with 13.02, libjpeg-devel package lead to libjpeg62.so installation, that may conflict with libjpeg8.so that is also installed. That's why we recommend to not install libjpeg-devel, but rather install libjpeg8-devel. +\code +Linking CXX executable HelloWorld +/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: warning: libjpeg.so.8, needed by /usr/lib64/libopencv_highgui.so.2.4.9, may conflict with libjpeg.so.62 +\endcode + +\section install_opensuse_build Building ViSP from source + +- To build ViSP proceed with: +\code +make -j4 +\endcode + +- To install ViSP proceed with: +\code +sudo make install +\endcode + +- To build ViSP documentation, you have first to install Doxygen package: +\code +sudo zypper install doxygen graphviz +\endcode +Then you can proceed with: +\code +make visp_doc +\endcode + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-install-raspberry.doc b/doc/tutorial-install-raspberry.doc new file mode 100644 index 0000000000000000000000000000000000000000..212b44bcb9b7bebb6aaf861112b85c50a4ef9720 --- /dev/null +++ b/doc/tutorial-install-raspberry.doc @@ -0,0 +1,230 @@ +/** + +\page tutorial-install-raspberry Tutorial: Installation from source on Raspberry Pi +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on <a href="http://www.raspberrypi.org/help/what-is-a-raspberry-pi">Raspberry Pi 1 or Pi 2</a> equipped with an optional HD camera module. + +\image html img-raspberry-pi.jpg + +In a first section we give some useful instructions to start with a Raspberry PI. Then in the second section, we focus on the installation of ViSP from source. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_raspberry Raspberry Pi installation + +\subsection raspberry Setting up Raspberry Pi + +There are a lot of documentation and tutorial that explain different ways to setup a Raspberry Pi. A good reference is the official page <a href="http://www.raspberrypi.org/">http://www.raspberrypi.org</a> + +We suggest to start with NOOBS (New Out Of the Box Software). Bellow we resume the different steps that are fully described in <a href="http://www.raspberrypi.org/help/noobs-setup/">http://www.raspberrypi.org/help/noobs-setup</a>: + +- Format you SD card (at least 4Go are requested) on your computer. To this end you can use "GParted partition editor" under Ubuntu. +- Download Noobs "Offline and network install" zip file from <a href="http://www.raspberrypi.org/downloads/">http://www.raspberrypi.org/downloads</a>. +- Unzip all the files on the SD card. +- Insert your SD Card into your Raspberry Pi. +- Plug in your keyboard, mouse, monitor and Ethernet cables. +- Now plug in the USB power cable to your Pi. +- Your Raspberry Pi will boot, and a window will appear with a list of different operating systems that you can install. Select the "recommended Raspbian" checkbox and click on "Install" button. +- When the install process has completed, the Raspberry Pi configuration menu (raspi-config) will load. Here you are able to set the time and date for your region and enable a Raspberry Pi camera board, or even create users. You can exit this menu by using Tab on your keyboard to move to Finish. + +\subsection login Logging in + +The default login for Raspbian is username \c pi with the password \c raspberry. + +\subsection update Update your Raspbian distribution + +If your Raspberry Pi is connected to Ethernet you can update your Raspbian distribution: +\code +sudo apt-get update +sudo apt-get upgrade +\endcode + +\subsection camera Setting up a Raspberry Pi camera + +If you have a Raspberry Pi camera module see <a href="http://www.raspberrypi.org/help/camera-module-setup/">http://www.raspberrypi.org/help/camera-module-setup</a>. To resume, enable the camera using: +\code +sudo raspi-config +\endcode +Enter in menu 5/ to enable the camera. + +Then load the camera module: +\code +sudo modprobe bcm2835-v4l2 +\endcode + +This will add the following modules: + +\code +pi@raspberrypi ~ $ lsmod +Module Size Used by +bcm2835_v4l2 37611 0 +videobuf2_core 30853 1 bcm2835_v4l2 +v4l2_common 7792 1 bcm2835_v4l2 +videodev 121362 3 bcm2835_v4l2,v4l2_common,videobuf2_core +\endcode + +To check if the camera is recognized and connected, run: +\code +v4l2-ctl --list-formats +\endcode + +After each Raspberry Pi reboot you need to relaunch the previous modprobe command. To load bcm2835-v4l2 module during reboot, you can edit \c /etc/modules file +\code +sudo nano /etc/modules +\endcode + +and add a line with the name of the module: +\code +bcm2835-v4l2 +\endcode + + +\subsection startx Start graphical user interface + +To load the graphical user interface, type \c startx and press Enter on your keyboard. This will later allow to use ViSP vpDisplayX or vpDisplayOpenCV classes useful to display images in a X11 window. + +\section install_visp ViSP installation +\subsection prerequisities Prerequisities + +First you need to install the following packagages (g++, CMake, Subversion) that are requested to get and build ViSP: +\code +sudo apt-get install build-essential cmake-curses-gui subversion +\endcode + +Then you can install the following packages (X11, lapack, gsl, v4l, xml, png, jpeg, opencv), that correspond to the optional 3rd party libraries that are interfaced with ViSP: +\code +sudo apt-get install libx11-dev liblapack-dev libgsl0-dev libv4l-dev libxml2-dev libpng12-dev libjpeg-dev libopencv-dev +\endcode + +The complete list of 3rd party libraries is detailed following <a href="http://team.inria.fr/lagadic/visp/libraries.html">http://team.inria.fr/lagadic/visp/libraries.html</a> link. + +\subsection get_source Getting ViSP source code + +There are different ways to get ViSP source code on Raspberry Pi: + +- You can download the <a href="http://team.inria.fr/lagadic/visp/download.html#latest">latest stable release</a> tarball. Once downloaded, uncompress the file using: +\code +mkdir /home/pi/soft; cd /home/pi/soft +wget http://gforge.inria.fr/frs/download.php/latestfile/475/ViSP-2.10.0.tar.gz +tar xvzf ViSP-2.10.0.tar.gz +\endcode + +- You can also download the cutting-edge version of the source code from Subversion repository using: +\code +mkdir /home/pi/soft; cd /home/pi/soft +svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-code +\endcode + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c /home/pi/soft/ViSP-2.10.0 or \c /home/pi/soft/ViSP-trunk + +\subsection config Configuring ViSP from source + +- Create first a directory denoted \<binary_dir\> where you want to build ViSP. This directory will contain generated Makefiles, object files, and output libraries and binaries. +\code +cd /home/pi/soft; mkdir ViSP-build-release +\endcode + +- Enter the \<binary_dir\> and configure the build type: +\code +cmake [<optional parameters>] <source_dir> +\endcode +For example: +\code +cd /home/pi/soft/ViSP-build-release +cmake -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_SHARED_LIBS=ON ../ViSP-2.10.0 +\endcode +A more versatile way to configure the build is to use \c ccmake, the CMake GUI: +\code +ccmake ../ViSP-2.10.0 +\endcode +The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd parties that will be used. Then to generate the makefiles, just press [g] key in the ccmake gui. +\image html img-ccmake-raspberry-pi.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP. + +- There is an other way to see which are the 3rd parties that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter an example of a possible content of this file: +\code + ViSP third-party libraries + +Below you will find the list of third party libraries used to +build ViSP on your computer. + +Mathematics: + Gnu Scientific Library : yes + Lapack/blas : yes +Simulator: + Ogre simulator : no + \- Ogre3D : no + \- OIS : no + Coin simulator : + \- Coin3D : no + \- SoWin : no + \- SoXt : no + \- SoQt : no + \- Qt4 : no + \- Qt3 : no +Robots + Afma6 : no + Afma4 : no + Biclops : no + Ptu46 : no + Pioneer : no + Viper S650 : no + Viper S850 : no +Video devices (display) + X11 : yes + GTK : no + OpenCV : yes + GDI : no + Direct3D : no +Framegrabbers + Firewire libdc1394-2.x : no + Video For Linux Two : yes + DirectShow : no + CMU 1394 Digital Camera SDK : no + OpenCV : yes +Specific devices + Yarp : no + Kinect : no + \-libfreenect : no + \-libusb-1.0 : no + \-pthread : yes +Video and image Read/Write: + FFMPEG : no + libjpeg : yes + libpng : yes +Misc: + XML2 : yes + pthread : yes + OpenMP : yes + zbar : no + dmtx : no +Documentation: + Doxygen : no + Graphviz dot : no +ViSP built with C++11 features: no +\endcode + +\subsection build Building ViSP from source + +- To build ViSP proceed with: +\code +make +\endcode + +- To install ViSP proceed with: +\code +sudo make install +\endcode + +- To build ViSP documentation, you have first to install Doxygen package: +\code +sudo apt-get install doxygen graphviz texlive-latex-base +\endcode +Then you can proceed with: +\code +make visp_doc +\endcode + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project on Raspberry Pi or on any other system. Then if you have a Raspberry Pi camera module, you can also follow the \ref tutorial-tracking-blob especially subsection Tracking form v4l2 live cameras. + +*/ diff --git a/doc/tutorial-install-ubuntu-package.doc b/doc/tutorial-install-ubuntu-package.doc new file mode 100644 index 0000000000000000000000000000000000000000..0e7eb6e0db892d910ee2ed9661c337521751c62a --- /dev/null +++ b/doc/tutorial-install-ubuntu-package.doc @@ -0,0 +1,89 @@ +/** + +\page tutorial-install-ubuntu-package Tutorial: Installation from prebuilt packages on Linux Ubuntu +\tableofcontents + +In this tutorial you will learn how to install ViSP from prebuilt official packages on Linux Ubuntu. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +Since Ubuntu 14.04 LTS, ViSP library can be installed from <a href="http://packages.ubuntu.com/search?keywords=visp&searchon=names&suite=trusty§ion=all">existing packages</a> on amd64 or i386 architectures. In Ubuntu 13.10, the packages are only available for amd64 architecture. + +The table below shows which are the packages available given the distribution and architecture. + +<table> +<tr> + <th colspan="2">Ubuntu 13.10 + <th colspan="2">Ubuntu 14.04 LTS + <th colspan="2">Ubuntu 14.10 +</tr> +<tr> + <th>i386</th> + <th>amd64</th> + <th>i386</th> + <th>amd64</th> + <th>i386</th> + <th>amd64</th> +</tr> +<tr> + <td></td> + <td>libvisp-dev</td> + <td>libvisp-dev</td> + <td>libvisp-dev</td> + <td>libvisp-dev</td> + <td>libvisp-dev</td> +</tr> +<tr> + <td></td> + <td>libvisp2.8</td> + <td>libvisp2.8</td> + <td>libvisp2.8</td> + <td>libvisp2.9</td> + <td>libvisp2.9</td> +</tr> +<tr> + <td></td> + <td>libvisp2.8-dbg</td> + <td>libvisp2.8-dbg</td> + <td>libvisp2.8-dbg</td> + <td>libvisp2.9-dbg</td> + <td>libvisp2.9-dbg</td> +</tr> +<tr> + <td></td> + <td>libvisp-doc</td> + <td>libvisp-doc</td> + <td>libvisp-doc</td> + <td>libvisp-doc</td> + <td>libvisp-doc</td> +</tr> +<tr> + <td></td> + <td>visp-images</td> + <td>visp-images</td> + <td>visp-images</td> + <td>visp-images</td> + <td>visp-images</td> +</tr> +</table> + +To install ViSP library and headers just run: + +\code +sudo apt-get install libvisp-dev +\endcode + +To install ViSP images data set useful to run ViSP examples you can run: +\code +sudo apt-get install visp-images +\endcode + +To install ViSP html documentation you can run: +\code +sudo apt-get install visp-doc +\endcode + + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-install-ubuntu.doc b/doc/tutorial-install-ubuntu.doc index fa0a25e16a13a776eeacd5cc4a81e9d7370cd77f..94118777aecf69fbe8a748fc07ff1f07a0b5d06e 100644 --- a/doc/tutorial-install-ubuntu.doc +++ b/doc/tutorial-install-ubuntu.doc @@ -3,14 +3,9 @@ \page tutorial-install-ubuntu Tutorial: Installation from source on Linux Ubuntu \tableofcontents -In this tutorial you will learn how to install ViSP from source on Linux Ubuntu. These steps have been tested for Ubuntu 13.04 (64 bit) distribution, but should work with any other distribution as well. - -Concerning ViSP installation, we provide also other tutorials -- \ref tutorial-install-win -- \ref tutorial-install-iOS - -ViSP can also be installed on other platforms including OSX, Fedora, CentOS, openSUZE (see <a href="http://www.irisa.fr/lagadic/visp/install.html">Install from source page</a>). +In this tutorial you will learn how to install ViSP from source on Linux Ubuntu. These steps have been tested for Ubuntu 14.04 (64 bit) distribution, but should work with any other distribution as well. +\note Concerning ViSP installation, we provide also other \ref tutorial. \section install_ubuntu_required Required packages @@ -32,16 +27,16 @@ There are different ways to get ViSP source code: - You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip or a tarball. Once downloaded, uncompress the file using either \code -tar xvzf ViSP-2.7.0.tar.gz +tar xvzf ViSP-2.10.0.tar.gz \endcode or \code -unzip ViSP-2.7.0.zip +unzip ViSP-2.10.0.zip \endcode - You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. Once downloaded, uncompress the file using \code -unzip ViSP-2.7.1-snapshot-2013.05.02.zip +unzip ViSP-2.y.z-snapshot-2015.mm.dd.zip \endcode - Or you get the cutting-edge ViSP from Subversion repository. To this end you have first to install subversion \code @@ -52,7 +47,7 @@ and then use the following command svn checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-trunk \endcode -We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.7.0 +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c $HOME/ViSP-2.10.0 \section install_ubuntu_config Configuring ViSP from source @@ -64,20 +59,20 @@ cd $HOME; mkdir ViSP-build-release - Enter the \<binary_dir\> and to configure the build type: \code cmake [<optional parameters>] <source_dir> -For example: \endcode +For example: \code cd $HOME/ViSP-build-release -cmake -D CMAKE_BUILD_TYPE=RELEASE -D BUILD_SHARED_LIBS=ON ../ViSP-2.7.0 +cmake -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_SHARED_LIBS=ON ../ViSP-2.10.0 \endcode A more versatile way to configure the build is to use \c ccmake, the CMake GUI: \code -ccmake ../ViSP-2.7.0 +ccmake ../ViSP-2.10.0 \endcode -The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd party that will be used. -\image html img-ccmake-ubuntu.png Snapshot of the ccmake \c ../ViSP-2.7.0 command used to configure ViSP. +The following image shows that this command allows to configure (just by pressing [c] key) the build in a more advanced way where some options could be easily turned On/Off. It allows also to see which are the 3rd parties that will be used. +\image html img-ccmake-ubuntu.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP. -- There is an other way to see which are the 3rd party that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter the content of this file: +- There is an other way to see which are the 3rd parties that are found during the configuration stage and that will be used by ViSP during the build. To this end you can have a look to the text file named ViSP-third-party.txt and located in \<binary_dir\>. We provide hereafter an example of a possible content of this file: \code ViSP third-party libraries @@ -106,7 +101,6 @@ Robots Pioneer : no Viper S650 : no Viper S850 : no - Cycab : no Video devices (display) X11 : no GTK : no @@ -134,16 +128,18 @@ Misc: XML2 : no pthread : yes OpenMP : yes + zbar : no + dmtx : no Documentation: Doxygen : no Graphviz dot : no ViSP built with C++11 features: no \endcode -In our case, only \c pthread an OpenMP 3rd party are detected. +In our case, only \c pthread an OpenMP 3rd parties are detected. \subsection install_ubuntu_3rdparty Optional 3rd party packages -As mentioned previously, ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. We recommand to install the following: +As mentioned previously, ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. We recommend to install the following: - OpenCV \code @@ -153,9 +149,9 @@ sudo apt-get install libopencv-dev \code sudo apt-get install libx11-dev \endcode -- liblapack to benefit from optimized mathematical capabilities +- lapack and GSL to benefit from optimized mathematical capabilities \code -sudo apt-get install liblapack-dev +sudo apt-get install liblapack-dev libgsl0-dev \endcode - libdc1394 to grab images from firewire cameras \code @@ -167,7 +163,7 @@ sudo apt-get install libv4l-dev \endcode - Coin, to be able to support vrml cad model used by the model-based trackers \code -sudo apt-get install libCoin60-dev +sudo apt-get install libCoin80-dev \endcode - libxml2 to be able to configure the model-based trackers from xml files \code @@ -183,16 +179,20 @@ sudo apt-get install libswscale-dev libavutil-dev libavformat-dev libavcodec-dev \endcode - Ogre 3D if you want to do augmented reality or simulation \code -sudo apt-get install libogre-dev libois-dev +sudo apt-get install libogre-1.9-dev libois-dev +\endcode +- Bar code detection +\code +sudo apt-get install libzbar-dev libdmtx-dev \endcode Once installed, if you want that ViSP exploit the new 3rd party, you have to configure ViSP again. \code -ccmake ../ViSP-2.7.0 +ccmake ../ViSP-2.10.0 \endcode -The following image shows now that all the previous optional 3rd party are detected. -\image html img-ccmake-ubuntu-all.png Snapshot of the ccmake \c ../ViSP-2.7.0 command used to configure ViSP after installation of optional 3rd party libraries. +The following image shows now that all the previous optional 3rd parties are detected. +\image html img-ccmake-ubuntu-all.png Snapshot of the ccmake \c ../ViSP-2.10.0 command used to configure ViSP after installation of optional 3rd party libraries. \subsection install_ubuntu_generate Generating Makefiles @@ -204,7 +204,7 @@ Now we can build ViSP. - To build ViSP proceed with: \code -make +make -j4 \endcode - To install ViSP proceed with: @@ -218,7 +218,7 @@ sudo apt-get install doxygen graphviz texlive-latex-base \endcode Then you can proceed with: \code -make html-doc +make visp_doc \endcode You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. diff --git a/doc/tutorial-install-win.doc b/doc/tutorial-install-win.doc deleted file mode 100644 index 95e81bf98bf38bcc0b56367ff3e487c701b5fad2..0000000000000000000000000000000000000000 --- a/doc/tutorial-install-win.doc +++ /dev/null @@ -1,192 +0,0 @@ -/** - -\page tutorial-install-win Tutorial: Installation from source on Windows -\tableofcontents - -In this tutorial you will learn how to install ViSP from source on Windows. These steps have been tested on Windows 7 (64 bit), with CMake 2.8 and Visual Studio 2012 but should work with any other version as well. - -Concerning ViSP installation, we provide also other tutorials -- \ref tutorial-install-ubuntu -- \ref tutorial-install-iOS - -ViSP can also be installed on other platforms OSX, Fedora, CentOS, openSUZE (see <a href="http://www.irisa.fr/lagadic/visp/install.html">Install from source page</a>). - - -\section install_win_required Required packages - -- Visual Studio C++. Note that ViSP can also be build with Visual Studio Express that could be downloaded at : http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products -- CMake 2.8 or higher that could be download at : http://www.cmake.org/cmake/resources/software.html - - -\section install_win_get_source Getting ViSP source code - -There are different ways to get ViSP source code: - -- You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip. -- You can also download a more <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a>. -- Or you can get the cutting-edge ViSP from Subversion repository svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP. - -We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c C:\\ViSP\\ViSP-2.7.1 - -\section install_win_config Configuring ViSP from source - -The goal of the configuration step is now to use CMake to produce a Visual Studio C++ solution that will be located in \<binary_dir\>, for example \c C:\\ViSP\\ViSP-2.7.1-build. - -- Launch CMake (cmake-gui) and complete the \<source_dir\> and \<binary_dir\> locations as in the next image. - -\image html img-cmake-win-1.jpg - -- Click then on "Configure" button. - -\image html img-cmake-win-2.jpg - -- Click on "Yes" to create the \c C:\\ViSP\\ViSP-2.7.1-build folder. -- Select then your compiler, for example here Visual Studio 11 Win64, and click on "Finish" button. - -\image html img-cmake-win-msvc-version.jpg - -- This will start CMake configuration. As shown in the next image, GDI (Graphical Device Interface) and OpenMP 3rd party are automatically detected. - -\image html img-cmake-win-4.jpg - -- As given in the previous image, note also that the installation folder is set to \c C:/Program \c Files \c (x86)/ViSP. Make sure that you have administrator privileges to write in that folder. If not and if you want to install ViSP, you have to set an other folder, for example \c C:/ViSP/ViSP-install. - -\image html img-cmake-win-install-prefix.jpg - -- Click then on "Configure" button. All the red lines should disappear. - -\image html img-cmake-win-6.jpg - -- Note also that the default configuration lead to the creation of a static library (with \c .lib extension). It is recommended to rather create a shared library (with \c .dll extension). As shown in the next image you have to check the \c BUILD_SHARED_LIBS option to enable DLL creation. Click then on "Configure" button. - -\image html img-cmake-win-shared.jpg - -\subsection install_win_3rdparty Optional 3rd party packages - -ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. - -\subsubsection install_win_3rdparty_opencv OpenCV 3rd party - -- We recommend to install OpenCV. The simplest is to install OpenCV pre-build libraries by following <a href="http://docs.opencv.org/doc/tutorials/introduction/windows_install/windows_install.html#windows-install-prebuild">OpenCV tutorial: Installation Using the Pre-build Libraries</a>. In our case we install \c OpenCV-2.4.5.exe pre-build SDK in \c C:\\OpenCV. The installer copied all the material in \c C:\\OpenCV\\opencv. - -- Now in order that ViSP detects OpenCV you have to set \c OPENCV_DIR environment variable. Start up a command window (in your "Start" menu click on "Run" and type in \c cmd.exe) and enter: -\code -setx OPENCV_DIR C:\OpenCV\opencv\build -\endcode - -- Here the directory is where you have the build directory (extracted or built). Inside this folder you should have a file named \c OpenCVConfig.cmake. - -- Quit and restart CMake (cmake gui). It is important to quit CMake in order to take into account the new \c OPENCV_DIR environment variable. Click "Configure" button. As shown in the next image, a new red line indicating that OpenCV is found should appear. - -\image html img-cmake-win-opencv.jpg - -- As shown in the next image, if you enable the advance view, you can see that we will use OpenCV libraries located in \c C:/OpenCV/opencv/build/x64/vc11/lib folder. - -\image html img-cmake-win-opencv-advanced.jpg - -- Press "Configure" button again. - -\subsubsection install_win_3rdparty_ogre Ogre3D 3rd party - -- We recommend also to install Ogre3D. As given in <a href="http://www.irisa.fr/lagadic/visp/librarieslist.html#Ogre">ViSP third party pages</a>, follow the steps described on <a href="http://www.ogre3d.org/tikiwiki/Installing+the+Ogre+SDK">Ogre Wiki</a>. In our case we install <a href="http://www.microsoft.com/en-us/download/confirmation.aspx?id=6812">DirectX SDK (June 2010)</a> and the latest <a href="http://www.ogre3d.org/download/sdk">Ogre SDK</a>; OGRE 1.9 RC 1 SDK for Visual C++ .Net 2012 (64-bit) in \c C:/ViSP/OgreSDK. You should adapt the versions that match your environment. - -- After Ogre3D installation you should get something similar to the next snapshot where Ogre3D SDK is installed in \c C:/ViSP/OgreSDK/OgreSDK_vc11_x64_v1-9-0unstable. -\image html img-win-ogre-dir.jpg - -- To help CMake detection of Ogre3D, set the following environment variables (\c OGRE_HOME and \c BOOST_ROOT) by opening a Window Command Shell. In our case it becomes: -\image html img-win-ogre-var.jpg - -- Set also the \c PATH environment variable to append the location of Ogre3D libraries used during execution. In our case we add \c C:/ViSP/OgreSDK/OgreSDK_vc11_x64_v1-9-0unstable/bin/Debug and \c C:/ViSP/OgreSDK/OgreSDK_vc11_x64_v1-9-0unstable/bin/Release. - -- Now quit and restart CMake to take into account the environment variables changes. Select "Configure" button. Ogre3D should be detected. - -\image html img-cmake-win-ogre-found.jpg - -- If you enter in the advanced view, you should have something similar to: - -\image html img-cmake-win-ogre-found-advanced.jpg - -- Make sure that \c Boost_, \c OGRE_ and \c DIRECT3D_ prefixed vars are well set. - -- Press "Configure" button again. - - -\subsection install_win_gene Ending the configuration - -- To finish the configuration, click on "Generate" button. - -\image html img-cmake-win-generate.jpg - -- Once the generation is done, in \c C:/ViSP/ViSP-2.7.1-build folder you have the Visual Studio \c VISP.sln generated solution file. - -\image html img-cmake-win-solution.jpg - -\section install_win_build Building ViSP from source - -- To build ViSP just double click on \c C:/ViSP/ViSP-2.7.1-build/VISP.sln solution file. This action will open ViSP project in Visual Studio C++. As shown in the next image, by default, Visual Studio position the solution configuration to \c Debug. - -\image html img-msvc-1.jpg - -- Enter menu "BUILD/Build Solution" to build ViSP. - -\image html img-msvc-build.jpg - -- At the end of the build process you should have the following indicating that all the build succeeded. - -\image html img-msvc-2.jpg - -- Now to install ViSP, build "INSTALL" project. To this end, apply a left click on "INSTALL" to select the project, then a right click to enter in the "Build" menu. - -\image html img-msvc-install.jpg - -- At the end of the installation, you should have the following. - -\image html img-msvc-4.jpg - -- As shown in the previous image, all the headers but also the generated library are copied in \c C:/ViSP/ViSP-install folder. - -- This ends ViSP installation with \c Debug configuration. - -- We recommend now to do the same with \c Release settings. As shown in the next image, select the \c Release configuration. - -\image html img-msvc-release.jpg - -- Now, as previously, build and install ViSP again. - -- At the end, in \c C:/ViSP/ViSP-install/bin folder you have two versions of ViSP DLL library; the one suffixed by "d" with debug information, the other one optimized with release compiler options. - -\image html img-msvc-install-end.jpg - -\section install_win_env_var Setting up PATH variable - -If you built static libraries then you are done. Otherwise, if you follow this tutorial step by step you need to add the bin folders path to the systems path. This is because you will use the ViSP and OpenCV libraries in form of "Dynamic-link libraries" (also known as DLL). Inside these are stored all the algorithms and information the libraries contains. The operating system will load them only on demand, during runtime. However, to do this he needs to know where they are. The systems \c PATH variable contains a list of folders where DLLs can be found. Add the ViSP and OpenCV libraries path to this and the OS will know where to look if he ever needs the libraries. Otherwise, you will need to copy the used DLLs right beside the applications executable file (exe) for the OS to find it. - -\code -C:\ViSP\ViSP-install\bin; C:\OpenCV\opencv\build\x64\vc11\bin -\endcode - -\section install_win_annex Appendix -\subsection install_annex_data Installing testing data - -Some ViSP examples and tests require data set (images, models). These data set is provided as a compressed zip files. - -- Download ViSP-images-2.7.0.zip from http://www.irisa.fr/lagadic/visp/download.html#dataDownloadForExample and uncompress it for example in \c C:/ViSP. - -\image html img-ViSP-images.jpg - -\subsection install_annex_usage Using testing data -- ViSP examples and tests are able to detect automatically the location of the requested data if you position an environment variable called \c VISP_INPUT_IMAGE_PATH. In our case, this variable should be set to \c C:\\ViSP. -\code -setx VISP_INPUT_IMAGE_PATH C:\ViSP -\endcode - -- It is also possible to run the examples and tests without positioning \c VISP_INPUT_IMAGE_PATH by using command line option \c -i \<\c path \c to \c the \c data \c set\>. If you want to run \c \<binary dir\>/example/device/display/Debug/displayGDI.exe, open a command window, enter in the right folder, and run: -\code -displayGDI.exe -i C:\ViSP -\endcode - -\image html img-cmd-displayGDI.jpg - -You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. - -*/ diff --git a/doc/tutorial-install-win7.doc b/doc/tutorial-install-win7.doc new file mode 100644 index 0000000000000000000000000000000000000000..97748a39699d972adb56d926899920e1d7f88aef --- /dev/null +++ b/doc/tutorial-install-win7.doc @@ -0,0 +1,201 @@ +/** + +\page tutorial-install-win7 Tutorial: Installation from source on Windows 7 with Visual C++ 2012 +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on Windows with Visual C++. These steps have been tested on Windows 7 (64 bit), with CMake 3.1 and Visual Studio 2012 but should work with any other version as well. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_win7_required Required packages + +- Visual Studio C++. In this tutorial we use Visual Studio 2012. Note that ViSP can also be build with Visual Studio Express that could be downloaded from http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products +- CMake 2.8 or higher that could be download at : http://www.cmake.org/cmake/resources/software.html + + +\section install_win7_get_source Getting ViSP source code + +There are different ways to get ViSP source code. + +\subsection install_win7_get_source_release Getting the latest release + +You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip. + +\subsection install_win7_get_source_snapshot Getting the latest snapshot + +When significant changes or bug fixes were introduced in the current developpement version of the source code, we provide snapshots. + +If available, you can download a <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a> following the link. + +\subsection install_win7_get_source_svn Getting the source from Subversion + +You can also get the cutting-edge ViSP version from Subversion repository svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP. + +One way to do that under Windows is to install Subversion that comes with Cygwin installer that is available on https://cygwin.com/install.html + +- From the previous link, download Cygwin installer for 32-bits or 64-bits version. +- Double click on the setup binary to start installation. Keep the default settings and select a mirror. +- As shown in the next image, in the window that allows to select the packages, search for "subversion" and select "subversion" package in "Devel": +\image html img-win8.1-cygwin-svn.jpg +- Click on Next button twice to start installation, and then on Finish button to exit installer. +- Now you should be able to use subversion +\code +C:\Users\...> C:\cygwin64\bin\svn.exe --version +svn, version 1.7.14 +\endcode + +Once installed, to get ViSP source code run: +\code +C:\Users\...> cd C:\ViSP +C:\Users\...> C:\cygwin64\bin\svn.exe checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-code +\endcode + +\section install_win7_config Configuring ViSP from source + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0 + +The goal of the configuration step is now to use CMake to produce a Visual Studio C++ solution that will be located in \<binary_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0-build. + +- Launch CMake (cmake-gui) and complete the \<source_dir\> and \<binary_dir\> locations as in the next image. + +\image html img-cmake-win7-msvc-launch.jpg + +- Click then on "Configure" button. + +\image html img-cmake-win7-create-build-folder.jpg + +- Click on "Yes" to create the \c C:\\ViSP\\ViSP-2.10.0-build folder. +- Select then your compiler, for example here Visual Studio 11 Win64, and click on "Finish" button. + +\image html img-cmake-win7-msvc-version.jpg + +- This will start CMake configuration. As shown in the next image, GDI (Graphical Device Interface) and OpenMP 3rd parties are automatically detected. + +\image html img-cmake-win7-msvc-config.jpg + +- As given in the previous image, note also that the installation folder is set to \c C:\\ViSP\\ViSP-2.10.0-build\\install. +\warning If you want to change the installation forder to \c C:/Program \c Files \c (x86)/ViSP, make sure that you have administrator privileges to write in that folder. + +- Click then on "Configure" button. All the red lines should disappear. +\image html img-cmake-win7-msvc-config-end.jpg +\note The default configuration lead to the creation of a shared library (with \c .dll extension). This is the default configuration that is recommended. If you want to create rather a static library (with \c .lib extension) you have to uncheck the \c BUILD_SHARED_LIBS option to disable DLL creation. + +\subsection install_win7_3rdparty Optional 3rd party packages + +ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. + +\subsubsection install_win7_3rdparty_opencv OpenCV 3rd party + +- We recommend to install OpenCV. From http://opencv.org/downloads.html download the latest OpenCV for Windows version. There is an <a href="http://docs.opencv.org/doc/tutorials/introduction/windows_install/windows_install.html#windows-install-prebuild">OpenCV tutorial: Installation Using the Pre-build Libraries</a> that may help. In our case we install \c OpenCV-3.0.0-beta.exe pre-build SDK in \c C:\\OpenCV. The installer opencv-3.0.0-beta.exe copied all the material in \c C:\\OpenCV\\opencv. + +- Now in order that ViSP detects OpenCV you have to set \c OpenCV_DIR environment variable. Start up a command window (in your "Start" menu click on "Run" and type in \c cmd.exe) and enter: +\code +setx OpenCV_DIR C:\OpenCV\opencv-3.0.0-beta\build +\endcode + +- Here the directory is where you have the build directory (extracted or built). Inside this folder you should have a file named \c OpenCVConfig.cmake. + +- Quit and restart CMake (cmake gui). <b>It is important to quit CMake</b> in order to take into account the new \c OpenCV_DIR environment variable. Click "Configure" button. As shown in the next image, a new red line indicating that OpenCV is found should appear. + +\image html img-cmake-win7-opencv.jpg + +- As shown in the next image, if you enable the advanced view, you can see that we will use OpenCV libraries located in \c C:/OpenCV/opencv/build/x64/vc11/lib folder. + +\image html img-cmake-win-opencv-advanced.jpg + +- Press "Configure" button again. + +\subsection install_win7_gene Ending the configuration + +- To finish the configuration, click on "Generate" button. + +\image html img-cmake-win7-generate.jpg + +- Once the generation is done, in \c C:/ViSP/ViSP-2.10.0-build folder you have the Visual Studio \c VISP.sln generated solution file. + +\image html img-cmake-win7-solution.jpg + +\section install_win7_build Building ViSP from source + +- To build ViSP just double click on \c C:/ViSP/ViSP-2.10.0-build/VISP.sln solution file. This action will open ViSP project in Visual Studio C++. As shown in the next image, by default, Visual Studio position the solution configuration to \c Debug. + +\image html img-win7-msvc-open.jpg + +- Enter menu "BUILD/Build Solution" to build ViSP. + +\image html img-win7-msvc-build.jpg + +- At the end of the build process you should have the following indicating that all the build succeeded. + +\image html img-win7-msvc-build-succeed.jpg + +- Now to install ViSP, build "INSTALL" project. To this end, apply a left click on "INSTALL" to select the project, then a right click to enter in the "Build" menu. + +\image html img-win7-msvc-install.jpg + +- At the end of the installation, you should have the following. + +\image html img-win7-msvc-install-succeed.jpg + +- As shown in the previous image, all the headers but also the generated library are copied in \c C:/ViSP/ViSP-install folder. + +- This ends ViSP installation with \c Debug configuration. + +- We recommend now to do the same with \c Release settings. As shown in the next image, select the \c Release configuration. + +\image html img-win7-msvc-release.jpg + +- Now, as previously, build and install ViSP again. + +- At the end, in \c C:/ViSP/ViSP-install/bin folder you have two versions of ViSP DLL library; the one suffixed by "d" with debug information, the other one optimized with release compiler options. + +\image html img-win7-msvc-install-end.jpg + +\section install_win7_env_var Setting up PATH variable + +If you built static libraries then you are done. Otherwise, if you follow this tutorial step by step you need to add the bin folders path to the systems path. This is because you will use ViSP and OpenCV libraries in form of "Dynamic-link libraries" (also known as DLL). Inside these are stored all the algorithms and information the libraries contains. The operating system will load them only on demand, during runtime. However, to do this he needs to know where they are. The systems \c PATH variable contains a list of folders where DLLs can be found. Add ViSP and OpenCV libraries path to this and the OS will know where to look if he ever needs the libraries. Otherwise, you will need to copy the used DLLs right beside the applications executable file (exe) for the OS to find it. + +To modify the PATH var and add the path to ViSP library, open a cmd terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\ViSP\ViSP-2.10.0-build\install\x64\vc12\bin" +\endcode + +Then to add the path to OpenCV 3rd party libraries, close and re-open a cmd-terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\OpenCV\opencv\build\x64\vc12\bin" +\endcode + +Then close and re-open a cmd terminal to check if the PATH var was well positioned + +\code +C:\Users\...> echo %PATH% +\endcode + + +\section install_win7_annex Appendix +\subsection install_win7_annex_data Installing testing data + +Some ViSP examples and tests require data set (images, models). These data set is provided as a compressed zip files. + +- Download ViSP-images-2.10.0.zip from http://www.irisa.fr/lagadic/visp/download.html#dataDownloadForExample and uncompress it for example in \c C:/ViSP. + +\image html img-win7-ViSP-images.jpg + +\subsection install_win7_annex_usage Using testing data +- ViSP examples and tests are able to detect automatically the location of the requested data if you position an environment variable called \c VISP_INPUT_IMAGE_PATH. In our case, this variable should be set to \c C:\\ViSP. +\code +setx VISP_INPUT_IMAGE_PATH C:\ViSP +\endcode + +- It is also possible to run the examples and tests without positioning \c VISP_INPUT_IMAGE_PATH by using command line option \c -i \<\c path \c to \c the \c data \c set\>. If you want to run \c \<binary dir\>/example/device/display/Debug/displayGDI.exe, open a command window, enter in the right folder, and run: +\code +displayGDI.exe -i C:\ViSP +\endcode + +\image html img-win7-cmd-displayGDI.jpg + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-install-win8.1-mingw-w64.doc b/doc/tutorial-install-win8.1-mingw-w64.doc new file mode 100644 index 0000000000000000000000000000000000000000..4166013e25eb5db82edb5b08407899ddf23b4a0d --- /dev/null +++ b/doc/tutorial-install-win8.1-mingw-w64.doc @@ -0,0 +1,319 @@ +/** + +\page tutorial-install-win81-mingw64 Tutorial: Installation from source on Windows 8.1 with Mingw-w64 +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on Windows 8.1 with Mingw-w64. These steps have been tested on Windows 8.1 (64 bit), with CMake 3.1.2 and Mingw-w64 - GCC for Windows 64 & 32 bits. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_win81_mingw64_required Required packages + +\subsection install_win81_mingw64_required_mingw Mingw-w64 + +Mingw-w64 could be found following http://mingw-w64.sourceforge.net. From that page, click on download "Win-builds" that will bring you to the page http://win-builds.org/download.html Here simply download and run the package manager, in our case we downloaded win-builds-1.5.0.exe file. + +- Once downloaded, double click on the the exe file. It will open an installation window. +- Select x86_64 and Mingw installation folder C:\\mingw as in the following image: +\image html img-mingw64-installer-started.jpg +\note It you want to install Mingw in an other folder, you have to set MINGW_DIR environment variable to your installation location in order to allow CMake to detect 3rd party libraries that come with Mingw installer. Fo example, if you install Mingw in C:\\folder\\mingw-w64, setting MINGW_DIR is simply done running in a cmd terminal: +\code +C:\Users\...> setx MINGW_DIR "C:\mingw" +\endcode +- Click on "OK" button. It will open an other window: +\image html img-mingw64-installer-process.jpg +- Where you can click on "Process" button to start the installation +- A window shows the progression of the installation. When all the packages are installed (in our case 92/92 packages, see next image) you can close all the installation windows. +\image html img-mingw64-installer-finished.jpg +- To finish the installation, just add C:\\mingw\\bin folder to the PATH variable. To this end open a cmd terminal and do the following: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\mingw\bin" +\endcode +- Close and re-open a cmd terminal, then run again +\code +C:\Users\...> echo %PATH% +\endcode +to check that C:\\mingw\\bin was added. + + +\subsection install_win81_mingw64_required_cmake CMake + +CMake 2.8 or higher that could be download at : http://www.cmake.org In our case we install CMake 3.1.2. + +- Download the latest binary distribution installer for Windows from http://www.cmake.org/download. You will find it under "Windows (Win32 Installer)". +- Install CMake just by double click on the binary cmake-3.1.2-win32-x86.exe you downloaded. + + +\section install_win81_mingw64_get_source Getting ViSP source code + +There are different ways to get ViSP source code. + +\subsection install_win81_mingw64_get_source_release Getting the latest release + +You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip. + +\subsection install_win81_mingw64_get_source_snapshot Getting the latest snapshot + +When significant changes or bug fixes were introduced in the current developpement version of the source code, we provide snapshots. + +If available, you can download a <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a> following the link. + +\subsection install_win81_mingw64_get_source_svn Getting the source from Subversion + +You can also get the cutting-edge ViSP version from Subversion repository svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP. + +One way to do that under Windows is to install Subversion that comes with Cygwin installer that is available on https://cygwin.com/install.html + +- From the previous link, download Cygwin installer for 32-bits or 64-bits version. +- Double click on the setup binary to start installation. Keep the default settings and select a mirror. +- As shown in the next image, in the window that allows to select the packages, search for "subversion" and select "subversion" package in "Devel": +\image html img-win8.1-cygwin-svn.jpg +- Click on Next button twice to start installation, and then on Finish button to exit installer. +- Now you should be able to use subversion +\code +C:\Users\...> C:\cygwin64\bin\svn.exe --version +svn, version 1.7.14 +\endcode + +Once installed, to get ViSP source code run: +\code +C:\Users\...> cd C:\ViSP +C:\Users\...> C:\cygwin64\bin\svn.exe checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-code +\endcode + + +\section install_win81_mingw64_config Configuring ViSP from source + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0 + +The goal of the configuration step is now to use CMake to produce all the material requested to build ViSP with Mingw. This material will be located in \<binary_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0-build. + +- Launch CMake (cmake-gui) and complete the \<source_dir\> and \<binary_dir\> locations as in the next image. + +\image html img-cmake-win8.1-mingw-launch.jpg + +- Click then on "Configure" button. + +\image html img-cmake-win8.1-create-build-folder.jpg + +- Click on "Yes" to create the \c C:\\ViSP\\ViSP-2.10.0-build folder. +- Select then "MinGW Makefiles" and click on "Finish" button. + +\image html img-cmake-win8.1-mingw-version.jpg + +- This will start CMake configuration. As shown in the next image, ffmpeg, gdi, libjpeg, libpng, pthread and libxml2 3rd party are detected. + +\image html img-cmake-win8.1-mingw-configure.jpg + +- As given in the previous image, note also that the installation folder is set to \c C:\\ViSP\\ViSP-2.10.0-build\\install. +\warning If you want to change the installation forder to \c C:/Program \c Files \c (x86)/ViSP, make sure that you have administrator privileges to write in that folder. + +- Click then on "Configure" button. All the red lines should disappear. +\image html img-cmake-win8.1-mingw-configure-end.jpg +\note The default configuration lead to the creation of a shared library (with \c .dll extension). This is the default configuration that is recommended. If you want to create rather a static library (with \c .lib extension) you have to uncheck the \c BUILD_SHARED_LIBS option to disable DLL creation. + +\subsection install_win81_mingw64_3rdparty Optional 3rd party packages + +ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. Some of them are automatically detected since they are part of MinGW distribution. Other may be installed from source. + +\subsubsection install_win81_mingw64_3rdparty_opencv OpenCV 3rd party + +- We recommend to install OpenCV. Since OpenCV is not package for Mingw, you have to install OpenCV from source. From http://opencv.org/downloads.html download the latest OpenCV for Windows version. In our case we install \c OpenCV-2.4.10.exe pre-build SDK in \c C:\\OpenCV. The installer opencv-2.4.10.exe copied all the prebuilt binaries, <b>but also the source code</b> in \c C:\\OpenCV\\opencv. +\note You can also install OpenCV 3.0.0. The principle remains the same. + +- Now we have to use CMake to configure OpenCV from source. As \<source_dir\> we will set \c C:\\OpenCV\\opencv\\sources and as \<binary_dir\> we set \c C:\\OpenCV\\opencv\\sources\\build-mingw. +\image html img-win8.1-mingw-opencv-cmake-configure.jpg + +- Click then on "Configure" button. +- Click on "Yes" to create the \c C:\\OpenCV\\opencv\\sources\\build-mingw. +- Select then "MinGW Makefiles" and click on "Finish" button. +\note Here you should encounter a first issue \ref issue_win81_mingw_opencv_endianness. +- Modify "CMAKE_INSTALL_PREFIX" to \c C:\\OpenCV\\opencv\\build folder. This is the location of the pre-build libraries that come with the installer. Doing that, allows to install OpenCV libraries in a same parent folder. +\image html img-win8.1-mingw-opencv-cmake-configure-install.jpg +- Click on "Generate" button to generate the Makefiles for Mingw. +- Open a cmd terminal, enter in the \<binary_dir\> folder and start mingw32-make +\code +C:\Users\...> cd C:\OpenCV\opencv\sources\build-mingw +C:\OpenCV\opencv\sources\build-mingw> mingw32-make +\endcode +\note Here you may encounter an other issue \ref issue_win81_mingw_opencv_tiff and maybe the following if you try to build with OpenCV 3.0.0: \ref issue_win81_mingw_opencv_ipp and \ref issue_win81_mingw_opencv_dshow. + +- To install OpenCV run: +\code +C:\OpenCV\opencv\sources\build-mingw> mingw32-make install +\endcode + +- Now in order that ViSP detects OpenCV you have to set \c OpenCV_DIR environment variable. Start up cmd terminal and enter: +\code +setx OpenCV_DIR C:\OpenCV\opencv\build +\endcode + +- Here the directory is the one where you have installed OpenCV. Inside this folder you should have a file named \c OpenCVConfig.cmake. + +- Quit and restart CMake Gui on ViSP. <b>It is important to quit CMake</b> in order to take into account the new \c OpenCV_DIR environment variable. Click "Configure" button. As shown in the next image, a new red line indicating that OpenCV is found should appear. +\image html img-cmake-win8.1-mingw-opencv-detected.jpg +\note If OpenCV is not detected, you may encounter the following issue \ref issue_win81_mingw_opencv_not_detected. + +- If you enable the advanced view, you can see that we will use OpenCV libraries located in \c C:/OpenCV/opencv/build/x64/mingw/lib folder. + +\image html img-cmake-win8.1-mingw-opencv-detected-advanced.jpg + +- Press "Configure" button again. + +\subsection install_win81_mingw64_gene Ending the configuration + +- To finish the configuration, click on "Generate" button. + +\image html img-cmake-win8.1-mingw-generate.jpg + +- Once the generation is done, in \c C:/ViSP/ViSP-2.10.0-build folder you have the Makefile file that will be used by Mingw to build the entire project. + +\section install_win81_mingw64_build Building ViSP from source + +- To build ViSP, open a cmd terminal, change to C:\\ViSP\ViSP-2.10.0-build folder and run mingw32-make: +\code +C:\Users\...> cd C:\ViSP\ViSP-2.10.0-build +C:\ViSP\ViSP-2.10.0-build> mingw32-make +\endcode +\note To \ref issue_win81_mingw_warning_pragma just follow the link. + +- Now to install ViSP, in the same cmd terminal run: +\code +C:\ViSP\ViSP-2.10.0-build> mingw32-make install +\endcode + +- At the end, in \c C:/ViSP/ViSP-2.10.0-build/install/x64/mingw/bin folder you have ViSP DLL library in libvisp-2100.dll file. +\note When CMAKE\_BUILD\_TYPE is set to Debug, the library name is suffixed by "d". + +\section install_win81_mingw64_env_var Setting up PATH variable + +If you built static libraries then you are done. Otherwise, if you follow this tutorial step by step you need to add the bin folders path to the systems path. This is because you will use ViSP and OpenCV libraries in form of "Dynamic-link libraries" (also known as DLL). Inside these are stored all the algorithms and information the libraries contains. The operating system will load them only on demand, during runtime. However, to do this he needs to know where they are. The systems \c PATH variable contains a list of folders where DLLs can be found. Add ViSP and OpenCV libraries path to this and the OS will know where to look if he ever needs the libraries. Otherwise, you will need to copy the used DLLs right beside the applications executable file (exe) for the OS to find it. + +To modify the PATH var and add the path to ViSP library, open a cmd terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\ViSP\ViSP-2.10.0-build\install\x64\mingw\bin" +\endcode + +Then to add the path to OpenCV 3rd party library, close and re-open a cmd-terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\OpenCV\opencv\build\x64\mingw\bin" +\endcode + +Then close and re-open a cmd terminal to check if the PATH var was well positioned + +\code +C:\Users\...> echo %PATH% +\endcode + + +\section install_win81_mingw64_annex Appendix +\subsection install_win81_mingw64_annex_data Installing testing data + +Some ViSP examples and tests require data set (images, models). These data set is provided as a compressed zip files. + +- Download ViSP-images-2.10.0.zip from http://www.irisa.fr/lagadic/visp/download.html#dataDownloadForExample and uncompress it for example in \c C:/ViSP. + +\image html img-win8.1-visp-images.jpg + +\subsection install_win81_mingw64_annex_usage Using testing data +- ViSP examples and tests are able to detect automatically the location of the requested data if you position an environment variable called \c VISP_INPUT_IMAGE_PATH. In our case, this variable should be set to \c C:\\ViSP. +\code +setx VISP_INPUT_IMAGE_PATH C:\ViSP +\endcode + +- It is also possible to run the examples and tests without positioning \c VISP_INPUT_IMAGE_PATH by using command line option \c -i \<\c path \c to \c the \c data \c set\>. If you want to run \c \<binary dir\>/example/device/display/Debug/displayGDI.exe, open a command window, enter in the right folder, and run: +\code +displayGDI.exe -i C:\ViSP +\endcode + +\image html img-win8.1-cmd-displayGDI.jpg + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +\section issue_win81_mingw Known issues +\subsection issue_win81_mingw_opencv_endianness OpenCV endianness failure during CMake configuration + +\note This issue occurs with OpenCV 2.4.10 and 2.3.0-beta. + +If you encounter the following issue during CMake configuration +\image html img-opencv-issue-test-big-endian.jpg + +Edit C:\\OpenCV\\opencv\\sources\\CMakeLists.txt file, and line 464 replace: +\code +test_big_endian(WORDS_BIGENDIAN) +\endcode +by: +\code +set(WORDS_BIGENDIAN 0) +\endcode + +\subsection issue_win81_mingw_opencv_tiff OpenCV build error: cannot build with tiff support + +\note This issue occurs with OpenCV 2.4.10 and 2.3.0-beta. + +If you encounter a build issue during libtiff build as given in the next image: +\image html img-opencv-issue-tiff.jpg + +- Open CMake GUI on OpenCV, turn BUILD_TIFF=OFF and also WITH_TIFF=OFF +- Click on "Configure" button, and then on "Generate" one. +- Build again OpenCV using +\code +C:\OpenCV\opencv\sources\build-mingw> mingw32-make +\endcode + +\subsection issue_win81_mingw_opencv_not_detected OpenCV not detected with Mingw build + +\note This issue occurs with OpenCV 2.4.10 and 2.3.0-beta. + +- To fix this issue, edit C:\\OpenCV\\opencv\\sources\\cmake\\OpenCVConfig.cmake, and line 89 replace: +\code + if(CMAKE_OPENCV_GCC_TARGET_MACHINE MATCHES "64") +\endcode +by: +\code + if(OPENCV_GCC_TARGET_MACHINE MATCHES "64") +\endcode + +- Then open a new cmd terminal to build and install OpenCV again: +\code +C:\OpenCV\opencv\sources\build-mingw> mingw32-make install +\endcode + +\subsection issue_win81_mingw_opencv_ipp OpenCV link error: cannot find -lRunTmChk + +\note This issue occurs with OpenCV 2.3.0-beta and with OpenCV master branch. + +The following image shows the link issue that may appear when building OpenCV with mingw: +\image html img-opencv-issue-ipp.jpg + +A work arround is to configure OpenCV without ipp support turning WITH_IPP=OFF and then trying to build again. + +\subsection issue_win81_mingw_opencv_dshow OpenCV build error: cannot build with dshow support + +\note This issue occurs with OpenCV 2.3.0-beta and with OpenCV master branch. + +The following image shows the link issue that may appear when building OpenCV with mingw: +\image html img-opencv-issue-dshow.jpg + +A work arround is to configure OpenCV without Direct Show support turning WITH_DSHOW=OFF and then trying to build again. + +\subsection issue_win81_mingw_warning_pragma Fix warning "ignoring #pragma comment(lib,"uuid.lib")" during build + +During the build of ViSP the following warning may occur: +\code +C:/mingw/mingw/include/urlmon.h:301:0: warning: ignoring #pragma comment [-Wunknown-pragmas] +#pragma comment(lib,"uuid.lib") +^ +\endcode + +To remove this warning, edit C:/mingw/mingw/include/urlmon.h and comment line 301: +\code +//#pragma comment(lib,"uuid.lib") +\endcode + + + +*/ diff --git a/doc/tutorial-install-win8.1-msvc.doc b/doc/tutorial-install-win8.1-msvc.doc new file mode 100644 index 0000000000000000000000000000000000000000..f4bb0e23e9f4a798e50c4372a0399b8417d157b5 --- /dev/null +++ b/doc/tutorial-install-win8.1-msvc.doc @@ -0,0 +1,211 @@ +/** + +\page tutorial-install-win81-msvc Tutorial: Installation from source on Windows 8.1 with Visual C++ 2013 +\tableofcontents + +In this tutorial you will learn how to install ViSP from source on Windows 8.1 with Visual C++. These steps have been tested on Windows 8.1 (64 bit), with CMake 3.1.1 and Visual Studio Express 2013 but should work with any other version as well. + +\note Concerning ViSP installation, we provide also other \ref tutorial. + +\section install_win81_msvc_required Required packages + +- Visual Studio C++. Note that ViSP can also be build with Visual Studio Express that could be downloaded from http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products In this tutorial we use Visual Studio Express 2013. +\note If you want to use Visual Studio Express, you have to install <b>Express 2013 for Windows Desktop</b>. The \b Desktop version is important to work with CMake. +- CMake 2.8 or higher that could be download at : http://www.cmake.org/cmake/resources/software.html + + +\section install_win81_msvc_get_source Getting ViSP source code + +There are different ways to get ViSP source code. + +\subsection install_win81_msvc_get_source_release Getting the latest release + +You can download the <a href="http://www.irisa.fr/lagadic/visp/download.html#latest">latest stable release</a> as a zip. + +\subsection install_win81_msvc_get_source_snapshot Getting the latest snapshot + +When significant changes or bug fixes were introduced in the current developpement version of the source code, we provide snapshots. + +If available, you can download a <a href="http://www.irisa.fr/lagadic/visp/download.html#snapshot">recent snapshot</a> following the link. + +\subsection install_win81_msvc_get_source_svn Getting the source from Subversion + +You can also get the cutting-edge ViSP version from Subversion repository svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP. + +One way to do that under Windows is to install Subversion that comes with Cygwin installer that is available on https://cygwin.com/install.html + +- From the previous link, download Cygwin installer for 32-bits or 64-bits version. +- Double click on the setup binary to start installation. Keep the default settings and select a mirror. +- As shown in the next image, in the window that allows to select the packages, search for "subversion" and select "subversion" package in "Devel": +\image html img-win8.1-cygwin-svn.jpg +- Click on Next button twice to start installation, and then on Finish button to exit installer. +- Now you should be able to use subversion +\code +C:\Users\...> C:\cygwin64\bin\svn.exe --version +svn, version 1.7.14 +\endcode + +Once installed, to get ViSP source code run: +\code +C:\Users\...> cd C:\ViSP +C:\Users\...> C:\cygwin64\bin\svn.exe checkout svn://scm.gforge.inria.fr/svn/visp/trunk/ViSP ViSP-code +\endcode + +\section install_win81_msvc_config Configuring ViSP from source + +We suppose now that ViSP source is in a directory denoted \<source_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0 + +The goal of the configuration step is now to use CMake to produce a Visual Studio C++ solution that will be located in \<binary_dir\>, for example \c C:\\ViSP\\ViSP-2.10.0-build. + +- Launch CMake (cmake-gui) and complete the \<source_dir\> and \<binary_dir\> locations as in the next image. + +\image html img-cmake-win8.1-msvc-launch.jpg + +- Click then on "Configure" button. + +\image html img-cmake-win8.1-create-build-folder.jpg + +- Click on "Yes" to create the \c C:\\ViSP\\ViSP-2.10.0-build folder. +- Select then your compiler, for example here Visual Studio Express 2013 Win64, and click on "Finish" button. + +\image html img-cmake-win8.1-msvc-version.jpg + +- This will start CMake configuration. As shown in the next image, only OpenMP 3rd party is automatically detected. + +\image html img-cmake-win8.1-4.jpg + +- As given in the previous image, note also that the installation folder is set to \c C:\\ViSP\\ViSP-2.10.0-build\\install. +\warning If you want to change the installation forder to \c C:/Program \c Files \c (x86)/ViSP, make sure that you have administrator privileges to write in that folder. + +- Click then on "Configure" button. All the red lines should disappear. +\image html img-cmake-win8.1-6.jpg +\note The default configuration lead to the creation of a shared library (with \c .dll extension). This is the default configuration that is recommended. If you want to create rather a static library (with \c .lib extension) you have to uncheck the \c BUILD_SHARED_LIBS option to disable DLL creation. + +\subsection install_win81_msvc_3rdparty Optional 3rd party packages + +ViSP is interfaced with some 3rd party libraries. The <a href="http://www.irisa.fr/lagadic/visp/libraries.html">complete list is provided here</a>. + +\subsubsection install_win81_msvc_3rdparty_gdi Windows Software Development Kit + +- We recommend to install the "Windows Software Development Kit (SDK) for windows 8.1" to get the Graphical Device Interface (GDI) capabilities. The GDi is used in ViSP to display images in a window thanks to vpDisplayGDI class. This SDK can be downloaded from https://msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx + +- Once installed, return to CMake Gui and click on "Configure" button. As shown in the the next image, GDI should be detected. + +\image html img-cmake-win8.1-gdi.jpg + +- Click again on "Configure" button. + +\subsubsection install_win81_msvc_3rdparty_opencv OpenCV 3rd party + +- We recommend to install OpenCV. From http://opencv.org/downloads.html download the latest OpenCV for Windows version. There is an <a href="http://docs.opencv.org/doc/tutorials/introduction/windows_install/windows_install.html#windows-install-prebuild">OpenCV tutorial: Installation Using the Pre-build Libraries</a> that may help. In our case we install \c OpenCV-2.4.10.exe pre-build SDK in \c C:\\OpenCV. The installer opencv-2.4.10.exe copied all the material in \c C:\\OpenCV\\opencv. + +- Now in order that ViSP detects OpenCV you have to set \c OpenCV_DIR environment variable. Start up a command window (in your "Start" menu click on "Run" and type in \c cmd.exe) and enter: +\code +setx OpenCV_DIR C:\OpenCV\opencv\build +\endcode + +- Here the directory is where you have the build directory (extracted or built). Inside this folder you should have a file named \c OpenCVConfig.cmake. + +- Quit and restart CMake (cmake gui). <b>It is important to quit CMake</b> in order to take into account the new \c OpenCV_DIR environment variable. Click "Configure" button. As shown in the next image, a new red line indicating that OpenCV is found should appear. + +\image html img-cmake-win8.1-opencv.jpg + +- As shown in the next image, if you enable the advanced view, you can see that we will use OpenCV libraries located in \c C:/OpenCV/opencv/build/x64/vc12/lib folder. + +\image html img-cmake-win8.1-opencv-advanced.jpg + +- Press "Configure" button again. + +\subsection install_win81_msvc_gene Ending the configuration + +- To finish the configuration, click on "Generate" button. + +\image html img-cmake-win8.1-msvc-generate.jpg + +- Once the generation is done, in \c C:/ViSP/ViSP-2.10.0-build folder you have the Visual Studio \c VISP.sln generated solution file. + +\image html img-win8.1-msvc-solution.jpg + +\section install_win81_msvc_build Building ViSP from source + +- To build ViSP just double click on \c C:/ViSP/ViSP-2.10.0-build/VISP.sln solution file. This action will open ViSP project in Visual Studio C++. As shown in the next image, by default, Visual Studio position the solution configuration to \c Debug. + +\image html img-win8.1-msvc-open.jpg + +- Enter menu "BUILD/Build Solution" to build ViSP. + +\image html img-win8.1-msvc-build.jpg + +- At the end of the build process you should have the following indicating that all the build succeeded. + +\image html img-win8.1-msvc-build-end.jpg + +- Now to install ViSP, build "INSTALL" project. To this end, apply a left click on "INSTALL" to select the project, then a right click to enter in the "Build" menu. + +\image html img-win8.1-msvc-install.jpg + +- At the end of the installation, you should have the following. + +\image html img-win8.1-msvc-install-end.jpg + +- As shown in the previous image, all the headers but also the generated library are copied in \c C:/ViSP/ViSP-2.10.0-build/install folder. + +- This ends ViSP installation with \c Debug configuration. + +- We recommend now to do the same with \c Release settings. As shown in the next image, select the \c Release configuration. + +\image html img-win8.1-msvc-release.jpg + +- Now, as previously, build and install ViSP again. + +- At the end, in \c C:/ViSP/ViSP-2.10.0-build/install/x64/vc12/bin folder you have two versions of ViSP DLL library; the one suffixed by "d" with debug information, the other one optimized with release compiler options. + +\image html img-win8.1-explorer-install-end.jpg + +\section install_win81_msvc_env_var Setting up PATH variable + +If you built static libraries then you are done. Otherwise, if you follow this tutorial step by step you need to add the bin folders path to the systems path. This is because you will use ViSP and OpenCV libraries in form of "Dynamic-link libraries" (also known as DLL). Inside these are stored all the algorithms and information the libraries contains. The operating system will load them only on demand, during runtime. However, to do this he needs to know where they are. The systems \c PATH variable contains a list of folders where DLLs can be found. Add ViSP and OpenCV libraries path to this and the OS will know where to look if he ever needs the libraries. Otherwise, you will need to copy the used DLLs right beside the applications executable file (exe) for the OS to find it. + +To modify the PATH var and add the path to ViSP library, open a cmd terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\ViSP\ViSP-2.10.0-build\install\x64\vc12\bin" +\endcode + +Then to add the path to OpenCV 3rd party library, close and re-open a cmd-terminal and run: +\code +C:\Users\...> echo %PATH% +C:\Users\...> setx PATH "%PATH%;C:\OpenCV\opencv\build\x64\vc12\bin" +\endcode + +Then close and re-open a cmd terminal to check if the PATH var was well positioned + +\code +C:\Users\...> echo %PATH% +\endcode + +\section install_win81_msvc_annex Appendix +\subsection install_win81_msvc_annex_data Installing testing data + +Some ViSP examples and tests require data set (images, models). These data set is provided as a compressed zip files. + +- Download ViSP-images-2.10.0.zip from http://www.irisa.fr/lagadic/visp/download.html#dataDownloadForExample and uncompress it for example in \c C:/ViSP. + +\image html img-win8.1-visp-images.jpg + +\subsection install_win81_msvc_annex_usage Using testing data +- ViSP examples and tests are able to detect automatically the location of the requested data if you position an environment variable called \c VISP_INPUT_IMAGE_PATH. In our case, this variable should be set to \c C:\\ViSP. +\code +setx VISP_INPUT_IMAGE_PATH C:\ViSP +\endcode + +- It is also possible to run the examples and tests without positioning \c VISP_INPUT_IMAGE_PATH by using command line option \c -i \<\c path \c to \c the \c data \c set\>. If you want to run \c \<binary dir\>/example/device/display/Debug/displayGDI.exe, open a command window, enter in the right folder, and run: +\code +displayGDI.exe -i C:\ViSP +\endcode + +\image html img-win8.1-cmd-displayGDI.jpg + +You are now ready to see the next \ref tutorial-getting-started that will show you how to use ViSP as a 3rd party to build your own project. + +*/ diff --git a/doc/tutorial-matching-deprecated.doc b/doc/tutorial-matching-deprecated.doc new file mode 100644 index 0000000000000000000000000000000000000000..f347d308b75b07d7d2b9817f9d85ac1f5d5f77a8 --- /dev/null +++ b/doc/tutorial-matching-deprecated.doc @@ -0,0 +1,63 @@ +/** + +\page tutorial-matching-deprecated Tutorial: Keypoint matching (deprecated) +\tableofcontents + +\section intro_matching_deprecated Introduction + +\note This tutorial is deprecated if your OpenCV version is equal to 3.0.0 or more recent. If so you should rather follow \ref tutorial-matching. + +This tutorial focuses on SURF key points manipulation. You will learn how to detect SURF key points on a reference image considered here as the first image of an mpeg video. Then in the next images of the video, key points that match those detected in the reference image using SURF descriptor are displayed. + +\note We assume that you are familiar with video framegrabbing described in \ref tutorial-grabber and with the way to display an image in a window described in \ref tutorial-getting-started. + +\section surf_deprecated SURF key points detection and matching + +Let us consider the following source code also available in tutorial-matching-surf-deprecated.cpp. + +\include tutorial-matching-surf-deprecated.cpp + +Here after is the resulting video. The left image represents the reference image. The right images correspond to the successive images of the input video. All the green lines extremities represent the points that are matched. + +\htmlonly +<iframe width="560" height="315" src="http://www.youtube.com/embed/sMbed_oYJgQ" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Now, let us explain the lines dedicated to the SURF keypoint usage. + +First we have to include the header of the vpKeyPointSurf class that is a wrapper over OpenCV classes. +\snippet tutorial-matching-surf-deprecated.cpp Include + +Note that this class is only available if ViSP was build with OpenCV non free module. This is ensured by the check of VISP_HAVE_OPENCV_NONFREE macro. To grab the images from the mpeg video stream we need also that ViSP was build with ffmpeg 3rd party. That's why we check VISP_HAVE_FFMPEG macro definition: +\snippet tutorial-matching-surf-deprecated.cpp Define + +Then we open the mpeg video stream and grab the first image of the video that is stored in \c I container. A Surf keypoint class is instantiated and keypoints are detected on the first image which is considered as the reference image: +\snippet tutorial-matching-surf-deprecated.cpp Construction + +The next lines are used to create image \c Idisp to render the matching results; left image for the reference image, right image for the current image that is processed: +\snippet tutorial-matching-surf-deprecated.cpp Create image + +Then a display using OpenCV is created and image \c Idisp is rendered: +\snippet tutorial-matching-surf-deprecated.cpp Init display + +We enter then in the \c while() loop where a new image is acquired from the video stream and inserted in the right part of image \c Idisp dedicated to rendering of the matching results. +\snippet tutorial-matching-surf-deprecated.cpp Acquisition + +We start the rendering by displaying the rendered image and by drawing a white vertical line to separate the reference image from the current one: +\snippet tutorial-matching-surf-deprecated.cpp Display + +Keypoint matches between the reference image and the current image \c I are detected using: +\snippet tutorial-matching-surf-deprecated.cpp Matching + +Then we parse all the matches to retrieve the coordinates of the points in the reference image (in \c iPref variable) and in the current image (in \c iPcur variable): +\snippet tutorial-matching-surf-deprecated.cpp Get matches + +Next we draw green lines between the matched points: +\snippet tutorial-matching-surf-deprecated.cpp Display matches + +At the end of the iteration, we flush all the previous display to the render window: +\snippet tutorial-matching-surf-deprecated.cpp Display flush + +You can now follow \ref tutorial-homography-deprecated to see how to exploit couple of matched points in order to estimate an homography that allows to track the position of an object. + +*/ diff --git a/doc/tutorial-matching.doc b/doc/tutorial-matching.doc new file mode 100644 index 0000000000000000000000000000000000000000..d373c7c99cd9da98f25169c0ba05570d1b19aacd --- /dev/null +++ b/doc/tutorial-matching.doc @@ -0,0 +1,77 @@ +/** + +\page tutorial-matching Tutorial: Keypoints matching +\tableofcontents + +\section intro_matching Introduction + +\note This tutorial is adapted if your OpenCV version is equal or greater than 2.1.1 version. + +This tutorial focuses on keypoints detection and mathing. You will learn how to detect keypoints on a reference image considered here as the first image of an mpeg video. Then in the next images of the video, keypoints that match those detected in the reference image are displayed. + +\note We assume that you are familiar with video framegrabbing described in \ref tutorial-grabber and with the way to display an image in a window described in \ref tutorial-getting-started. + +\section orb_keypoints ORB keypoints detection and matching + +Let us consider the following source code also available in tutorial-matching-keypoint.cpp. + +\include tutorial-matching-keypoint.cpp + +Here after is the resulting video. The left image represents the reference image. The right images correspond to the successive images of the input video. All the green lines extremities represent the points that are matched. + +\htmlonly +<iframe width="560" height="315" src="http://www.youtube.com/embed/sMbed_oYJgQ" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Now, let us explain the lines dedicated to the ORB keypoint detection and matching usage. + +First we have to include the header of the vpKeyPoint class that is a wrapper over OpenCV classes. +\snippet tutorial-matching-keypoint.cpp Include + +Note that this class is only available if ViSP was build with OpenCV. This is ensured by the check of VISP_HAVE_OPENCV_VERSION macro. +\snippet tutorial-matching-keypoint.cpp Define + +Then we open the mpeg video stream and grab the first image of the video that is stored in \c I container. The vpKeyPoint class is instantiated and keypoints are detected on the first image which is considered as the reference image: +\snippet tutorial-matching-keypoint.cpp Construction + +The next lines are used to create image \c Idisp to render the matching results; left image for the reference image, right image for the current image that is processed: +\snippet tutorial-matching-keypoint.cpp Create image + +Then a display using OpenCV is created and image \c Idisp is rendered: +\snippet tutorial-matching-keypoint.cpp Init display + +We enter then in the \c while() loop where a new image is acquired from the video stream and inserted in the right part of image \c Idisp dedicated to rendering of the matching results. +\snippet tutorial-matching-keypoint.cpp Acquisition + +We start the rendering by displaying the rendered image and by drawing a white vertical line to separate the reference image from the current one: +\snippet tutorial-matching-keypoint.cpp Display + +Keypoint matches between the reference image and the current image \c I are detected using: +\snippet tutorial-matching-keypoint.cpp Matching + +Then we parse all the matches to retrieve the coordinates of the points in the reference image (in \c iPref variable) and in the current image (in \c iPcur variable): +\snippet tutorial-matching-keypoint.cpp Get matches + +Next we draw green lines between the matched points: +\snippet tutorial-matching-keypoint.cpp Display matches + +At the end of the iteration, we flush all the previous display to the render window: +\snippet tutorial-matching-keypoint.cpp Display flush + +\section other_keypoints Using others types of keypoints + +Using other types of detectors / descriptors (SIFT, SURF, etc.) or matchers (Brute Force, Flann based matcher) is also possible. This can be easily done by using the correct OpenCV identifier name. For example, if you want to use the couple of detector / descriptor SIFT with a matching using FLANN, you only have to change the following lines : +\snippet tutorial-matching-keypoint-SIFT.cpp Construction + +A complete example is given in tutorial-matching-keypoint-SIFT.cpp + +Available types of detectors, extractors or matchers depend on OpenCV version. Check the OpenCV documentation to know which ones are available. + +Due to some patents, SIFT and SURF keypoints are available in a separate module in OpenCV: in nonfree module in OpenCV version before 3.0.0 and in xfeatures2d in OpenCV version from 3.0.0 if OpenCV contrib modules are build. If you want to use them, be sure that you have the nonfree module or xfeatures2d module. + +Some usage restrictions could also exist according to the usage you plan (e.g. research or commercial use). + +You can now follow \ref tutorial-homography to see how to exploit couple of matched points in order to estimate an homography that allows to track the position of an object. + +*/ + diff --git a/doc/tutorial-simu-image.doc b/doc/tutorial-simu-image.doc index d8eaa227b5b9ccc989848efd66875aa82de22c06..6256cba109357403b14c48e500606a47a603f9d3 100644 --- a/doc/tutorial-simu-image.doc +++ b/doc/tutorial-simu-image.doc @@ -22,62 +22,32 @@ The result of this program is shown in the next image. The provide hereafter the explanation of the new lines that were introduced. -\code -#include <visp/vpImageSimulator.h> -\endcode +\snippet tutorial-image-simulator.cpp Include Include the header of the vpImageSimulator class that allows to project an image to a given camera position. Then in the main() function we create an instance of a gray level image that corresponds to the image of the planar target, and then we read the image from the disk. -\code - vpImage<unsigned char> target; - vpImageIo::read(target, "./target_square.pgm"); -\endcode +\snippet tutorial-image-simulator.cpp Read image Since the previous image corresponds to a 20cm by 20cm target, we initialize the 3D coordinates of each corner in the plane Z=0. Each -\code - vpColVector X[4]; - for (int i = 0; i < 4; i++) X[i].resize(3); - // Top left Top right Bottom right Bottom left - X[0][0] = -0.1; X[1][0] = 0.1; X[2][0] = 0.1; X[3][0] = -0.1; - X[0][1] = -0.1; X[1][1] = -0.1; X[2][1] = 0.1; X[3][1] = 0.1; - X[0][2] = 0; X[1][2] = 0; X[2][2] = 0; X[3][2] = 0; -\endcode +\snippet tutorial-image-simulator.cpp Set model Then we create an instance of the image \c I that will contain the rendered image from a given camera position. -\code - vpImage<unsigned char> I(480, 640); -\endcode +\snippet tutorial-image-simulator.cpp Image construction Since the projection depends on the camera, we set its intrinsic parameters. -\code - vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); -\endcode +\snippet tutorial-image-simulator.cpp Camera parameters We also set the render position of the camera as an homogeneous transformation between the camera frame and the target frame. -\code - vpHomogeneousMatrix cMo(0, 0, 0.35, 0, vpMath::rad(30), vpMath::rad(15)); -\endcode +\snippet tutorial-image-simulator.cpp Set cMo We create here an instance of the planar image projector, set the interpolation to bilinear and initialize the projector with the image of the target and the coordinates of its corners. -\code - vpImageSimulator sim; - sim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION); - sim.init(target, X); -\endcode +\snippet tutorial-image-simulator.cpp Create simulator Now to retrieve the rendered image we first clean the content of the image to render, set the camera position, and finally get the image using the camera parameters. -\code - sim.setCleanPreviousImage(true); - sim.setCameraPosition(cMo); - sim.getImage(I, cam); -\endcode +\snippet tutorial-image-simulator.cpp Render image Then, if \c libjpeg is available, the rendered image is saved in the same directory then the executable. -\code -#ifdef VISP_HAVE_JPEG - vpImageIo::write(I, "./rendered_image.jpg"); -#endif -\endcode +\snippet tutorial-image-simulator.cpp Write image Finally, as in \ref tutorial-getting-started we open a window to display the rendered image. diff --git a/doc/tutorial-simu-robot-pioneer.doc b/doc/tutorial-simu-robot-pioneer.doc index 2d08a051a11407ca47ead1dc15e51e920a42f615..2ffcdec814305ee5a2c8387c19f898ecd4824aa7 100644 --- a/doc/tutorial-simu-robot-pioneer.doc +++ b/doc/tutorial-simu-robot-pioneer.doc @@ -233,4 +233,5 @@ The following control law is used: \end{array}\right] = -0.2 \left( {\bf L_{s} {^c}V_e {^e}J_e}\right)^{+} ({\bf s} - {\bf s}^*) \f] +You are now ready to see the next \ref tutorial-boost-vs. */ diff --git a/doc/tutorial-trace.doc b/doc/tutorial-trace.doc new file mode 100644 index 0000000000000000000000000000000000000000..2945ae325ebdf281e576b725aca2ee909e5a3c47 --- /dev/null +++ b/doc/tutorial-trace.doc @@ -0,0 +1,132 @@ +/** + +\page tutorial-trace Tutorial: Debug and trace printings +\tableofcontents + + +\section intro_trace Introduction + +ViSP allows to introduce trace and debug printings that may help debugging. To this end ViSP provides C or C++ macros that allows to print messages to the standard output std::cout or to std::cerr. + +~~~ +|----------|-------|-------------------------------------|---------------------------------------| +| output | type | std::cout | std::cerr | +|----------|-------|-------------------------------------|---------------------------------------| +| C-like | trace | vpTRACE, vpTRACE(level) | vpERROR_TRACE, vpERROR_TRACE(level) | +| | trace | vpIN_FCT, vpOUT_FCT | | +| | debug | vpDEBUG_TRACE, vpDEBUG_TRACE(level) | vpDERROR_TRACE, vpDERROR_TRACE(level) | +|----------|-------|-------------------------------------|---------------------------------------| +| C++-like | trace | vpCTRACE | vpCERROR | +| | debug | vpCDEBUG(level) | | +|----------|-------|-------------------------------------|---------------------------------------| +~~~ + +\subsection trace_macro Macros for trace + +Macro for tracing vpTRACE(), vpTRACE(level), vpERROR_TRACE(), vpERROR_TRACE(level), +vpIN_FCT() and vpOUT_FCT() +work like printf with carrier return at the end of the string, while +vpCTRACE() and vpCERROR() work like the C++ output streams std::cout +and std::cerr. All these macro print messages only if VP_TRACE macro is defined. + +\subsection debug_macro Macros for debug + +Macros for debug vpDEBUG_TRACE(level) and vpDERROR_TRACE(level) +work like printf while vpCDEBUG(level) works like the C++ output stream std::cout. +These macro print messages only if VP_DEBUG macro is defined and +if the debug \e level is greater than the one defined in VP_DEBUG_MODE +macro. Moreover vpDEBUG_ENABLE(level) can be used to check if a given debug level is active; vpDEBUG_ENABLE(level) is +equal to 1 if the debug \e level is greater than the debug mode +VP_DEBUG_MODE, 0 else. + +\section debug_trace_usage Debug and trace usage in ViSP library + +In ViSP, before an exception is thrown, trace macro are widely used to inform the user that an error occur. This is redundant, since the same trace message in generally associated to the exception that is thrown. Since ViSP 2.9.0, during CMake configuration it is possible to disable debug and trace printings by turning \c ACTIVATE_DEBUG_TRACE cmake variable to \c OFF. + +- Using cmake command just run: +\code +%cmake -DACTIVATE_DEBUG_TRACE=OFF <path to ViSP source code> +\endcode + +- or using ccmake GUI as shown in the next snapshot: + +\image html img-cmake-debug-trace.jpg + +\note When \c ACTIVATE_DEBUG_TRACE is turned to \c ON (this is the default behavior in ViSP), we simply define VP_TRACE and VP_DEBUG macro using the compiler -D option. + +\section example Debug and trace usage in your own project + +If you develop a project that uses ViSP library as a 3rd party, there are different ways to benefit from debug and trace macro described previously. + +- If ViSP was build with debug and trace enabled using \c cmake \c ACTIVATE_DEBUG_TRACE=ON, debug and trace are also enabled in your development. +- If debug and trace were disabled in ViSP (\c ACTIVATE_DEBUG_TRACE=OFF), you can enable debug and trace in your own development either by defining \c VP_DEBUG and/or \c VP_TRACE macro in your code using +\code +#define VP_TRACE +#define VP_DEBUG + +#include <visp/vpDebug.h> +\endcode +either by modifying your \c CMakeLists.txt file by adding an option as in ViSP: +\code +option(ACTIVATE_DEBUG_TRACE "Enable debug and trace printings" ON) + +if(ACTIVATE_DEBUG_TRACE) + add_definitions("-DVP_DEBUG -DVP_TRACE") +endif() +\endcode + + +The following example also available in tutorial-trace.cpp shows how to use the previous macro. + +\includelineno tutorial-trace.cpp + + +\note In the previous example it is important to notice that the following lines have to be put prior to any other ViSP includes: +\code +#define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 +#include <visp/vpDebug.h> +\endcode +For example, if you modify the previous example just by including <visp/vpImage.h> on the top of the file, you will get the following warnings: +\code +Building CXX object tutorial/trace/CMakeFiles/tutorial-trace.dir/tutorial-trace.cpp.o +.../ViSP-code/tutorial/trace/tutorial-trace.cpp:5:1: warning: "VP_DEBUG_MODE" redefined +In file included from .../ViSP-build-debug/include/visp/vpImage.h:52, + from .../ViSP-code/tutorial/trace/tutorial-trace.cpp:2: +.../ViSP-build-debug/include/visp/vpDebug.h:67:1: warning: this is the location of the previous definition +\endcode + + +When ViSP library was built without debug and trace the previous example produces the output: +\code +%./tutorial-trace +Debug level 1 active: 0 +Debug level 2 active: 0 +Debug level 3 active: 0 +\endcode + +When ViSP is rather build with debug and trace the previous example produces the output: +\code +%./tutorial-trace +(L0) begin /tmp/tutorial-trace.cpp: main(#9) : main() +Debug level 1 active: 1 +Debug level 2 active: 1 +Debug level 3 active: 0 +(L0) /tmp/tutorial-trace.cpp: main(#17) : C-like trace +(L1) /tmp/tutorial-trace.cpp: main(#18) : C-like trace level 1 +(L0) !! /tmp/tutorial-trace.cpp: main(#20) : C-like error trace +(L1) !! /tmp/tutorial-trace.cpp: main(#21) : C-like error trace level 1 +(L0) /tmp/tutorial-trace.cpp: main(#24) : C-like debug trace +(L0) !! /tmp/tutorial-trace.cpp: main(#25) : C-like error trace +(L2) /tmp/tutorial-trace.cpp: main(#27) : C-like debug trace level 2 +(L2) !! /tmp/tutorial-trace.cpp: main(#28) : C-like error trace level 2 +(L0) /tmp/tutorial-trace.cpp: main(#31) : C++-like trace +(L0) !! /tmp/tutorial-trace.cpp: main(#32) : C++-like error trace +(L2) /tmp/tutorial-trace.cpp: main(#35) : C++-like debug trace level 2 +(L0) end /tmp/tutorial-trace.cpp: main(#37) : main() +\endcode + +In the previous printings: +- the number after "L" indicates the debug or trace level; example (L2) is for level 2. +- the number after "#" indicates the line of the code that produce the printing; example main(#37) means in function main() at line 37. +- the "!!" indicate that the printing is on std::cerr. Others are on std::cout. +*/ diff --git a/doc/tutorial-tracking-blob.doc b/doc/tutorial-tracking-blob.doc index 259b1857f7364c944e2f7a8f3ba858da6691acc7..ed63c25713ac761eb57f3024069e6de7f6b1d118 100644 --- a/doc/tutorial-tracking-blob.doc +++ b/doc/tutorial-tracking-blob.doc @@ -5,115 +5,90 @@ \section tracking_blob_tracking Blob tracking -With ViSP you can track a blob using either vpDot or vpDot2 classes. +With ViSP you can track a blob using either vpDot or vpDot2 classes. By blob we mean a region of the image that has the same gray level. The blob can be white on a black background, or black on a white background. -\include tutorial-blob-tracker.cpp +In this tutorial we focus on vpDot2 class that provides more functionalities than vpDot class. As presented in section \ref tracking_blob_auto, it allows especially to automize the detection of blobs that have the same characteristics than a reference blob. -The videos show the result of the tracking on two different objects: +The next videos show the result of ViSP blob tracker on two different objects: \htmlonly <iframe width="420" height="315" src="http://www.youtube.com/embed/2Jv7OYBuPgI?rel=0" frameborder="0" allowfullscreen></iframe> <iframe width="420" height="315" src="http://www.youtube.com/embed/Kr2DJotfsiA?rel=0" frameborder="0" allowfullscreen></iframe> \endhtmlonly +In the next subsections we explain how to achieve this kind of tracking, first using a firewire live camera, then using a v4l2 live camera that can be an usb camera, or a Raspberry Pi camera module. + +\subsection live-firewire From a firewire live camera + +The following code also available in tutorial-blob-tracker-live-firewire.cpp file provided in ViSP source code tree allows to grab images from a firewire camera and track a blob. The initialisation is done with a user mouse click on a pixel that belongs to the blob. + +To acquire images from a firewire camera we use vp1394TwoGrabber class on unix-like systems or vp1394CMUGrabber class under Windows. These classes are described in the \ref tutorial-grabber. + +\include tutorial-blob-tracker-live-firewire.cpp + +From now, we assume that you have successfully followed the \ref tutorial-getting-started and the \ref tutorial-grabber. Here after we explain the new lines that are introduced. -First an instance of the blob tracker is created. -\code - vpDot2 d; -\endcode +\snippet tutorial-blob-tracker-live-firewire.cpp Construction Then we are modifying some default settings to allow drawings in overlay the contours pixels and the position of the center of gravity with a thickness of 2 pixels. -\code - blob.setGraphics(true); - blob.setGraphicsThickness(2); -\endcode +\snippet tutorial-blob-tracker-live-firewire.cpp Setting Then we are waiting for a user initialization throw a mouse click event in the blob to track. -\code - blob.initTracking(I); -\endcode +\snippet tutorial-blob-tracker-live-firewire.cpp Init The tracker is now initialized. The tracking can be performed on new images: +\snippet tutorial-blob-tracker-live-firewire.cpp Track + +\subsection live-v4l2 From a v4l2 live camera + +The following code also available in tutorial-blob-tracker-live-v4l2.cpp file provided in ViSP source code tree allows to grab images from a camera compatible with video for linux two driver (v4l2) and track a blob. Webcams or more generally USB cameras, but also the Raspberry Pi Camera Module can be considered. + +To acquire images from a v4l2 camera we use vpV4l2Grabber class on unix-like systems. This class is described in the \ref tutorial-grabber. + +\include tutorial-blob-tracker-live-v4l2.cpp + +The code is the same than the one presented in the previous subsection, except that here we use the vpV4l2Grabber class to grab images from usb cameras. Here we have also modified the while loop in order to catch an exception when the tracker fail: \code - blob.track(I); + try { blob.track(I); } + catch(...) { } \endcode +If possible, it allows the tracker to overcome a previous tracking failure (due to blur, blob outside the image,...) on the next available images. + + \section tracking_blob_auto Blob auto detection and tracking -The following example shows how to detect blobs in the first image and then track all the detected blobs. This functionality is only available with vpDot2 class. +The following example also available in tutorial-blob-auto-tracker.cpp file provided in ViSP source code tree shows how to detect blobs in the first image and then track all the detected blobs. This functionality is only available with vpDot2 class. Here we consider an image that is provided in ViSP source tree. \include tutorial-blob-auto-tracker.cpp - Here is a screen shot of the resulting program : +Here is a screen shot of the resulting program : - \image html img-blob-auto-detection.png +\image html img-blob-auto-detection.png - And here is the detailed explanation of the source : +And here is the detailed explanation of the source : - First we create an instance of the tracker. -\code - vpDot2 blob; -\endcode - Then, two cases are handled. The first case, when \c learn is set to \c true, consists in learning the blob characteristics. The user has to click in a blob that serves as reference blob. The size, area, gray level min and max, and some precision parameters will than be used to search similar blobs in the whole image. +First we create an instance of the tracker. +\snippet tutorial-blob-auto-tracker.cpp Construction -\code - if (learn) { - // Learn the characteristics of the blob to auto detect - blob.setGraphics(true); - blob.setGraphicsThickness(1); - blob.initTracking(I); - blob.track(I); - std::cout << "Blob characteristics: " << std::endl; - std::cout << " width : " << blob.getWidth() << std::endl; - std::cout << " height: " << blob.getHeight() << std::endl; - std::cout << " area: " << blob.getArea() << std::endl; - std::cout << " gray level min: " << blob.getGrayLevelMin() << std::endl; - std::cout << " gray level max: " << blob.getGrayLevelMax() << std::endl; - std::cout << " grayLevelPrecision: " << blob.getGrayLevelPrecision() << std::endl; - std::cout << " sizePrecision: " << blob.getSizePrecision() << std::endl; - std::cout << " ellipsoidShapePrecision: " << blob.getEllipsoidShapePrecision() << std::endl; - } -\endcode - If you have an precise idea of the dimensions of the blob to search, the second case consists is settings the reference characteristics directly. -\code - else { - // Set blob characteristics for the auto detection - blob.setWidth(50); - blob.setHeight(50); - blob.setArea(1700); - blob.setGrayLevelMin(0); - blob.setGrayLevelMax(30); - blob.setGrayLevelPrecision(0.8); - blob.setSizePrecision(0.65); - blob.setEllipsoidShapePrecision(0.65); - } -\endcode +Then, two cases are handled. The first case, when \c learn is set to \c true, consists in learning the blob characteristics. The user has to click in a blob that serves as reference blob. The size, area, gray level min and max, and some precision parameters will than be used to search similar blobs in the whole image. + +\snippet tutorial-blob-auto-tracker.cpp Learn + +If you have an precise idea of the dimensions of the blob to search, the second case consists is settings the reference characteristics directly. +\snippet tutorial-blob-auto-tracker.cpp Setting Once the blob characteristics are known, to search similar blobs in the image is simply done by: -\code - std::list<vpDot2> blob_list; - blob.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), auto_detected_blob_list); -\endcode +\snippet tutorial-blob-auto-tracker.cpp Search + Here \c blob_list contains the list of the blobs that are detected in the image \c I. When learning is enabled, the blob that is tracked is not in the list of auto detected blobs. We add it to the end of the list: -\code - if (learn) { - // The blob that is tracked by initTracking() is not in the list of auto detected blobs - // We add it: - blob_list.push_back(blob); - } -\endcode +\snippet tutorial-blob-auto-tracker.cpp Add learned dot Finally, when a new image is available we do the tracking of all the blobs: -\code - for(std::list<vpDot2>::iterator it=blob_list.begin(); it != blob_list.end(); ++it) { - (*it).setGraphics(true); - (*it).setGraphicsThickness(3); - (*it).track(I); - } -\endcode +\snippet tutorial-blob-auto-tracker.cpp Display You are now ready to see the next \ref tutorial-tracking-keypoint. diff --git a/doc/tutorial-tracking-keypoint.doc b/doc/tutorial-tracking-keypoint.doc index 6ef346f3ba78640c60fabedb42e21ac4a292b418..25d041a0800629d24f540b9073344bbf8f67e1a0 100644 --- a/doc/tutorial-tracking-keypoint.doc +++ b/doc/tutorial-tracking-keypoint.doc @@ -15,94 +15,136 @@ The video shows the result of the tracking: <iframe width="420" height="315" src="http://www.youtube.com/embed/ZYOG4kJPtaM?rel=0" frameborder="0" allowfullscreen></iframe> \endhtmlonly -And here is the line by line explanation of the source : - +The previous example can be run without command line options. In that case, keypoints are automatically detected before tracking. \code -#include <visp/vpImageConvert.h> -#include <visp/vpKltOpencv.h> -#include <visp/vpDisplayOpenCV.h> -#include <visp/vpVideoReader.h> +$ ./tutorial-klt-tracker \endcode -We include here the headers that define the corresponding classes. vpImageConvert class will be used to convert ViSP images implemented in vpImage class into OpenCV IplImage structure used as an entry by the KLT tracker. Then we include the header of vpKltOpencv class which is the wrapper over OpenCV KLT tracker implementation. We need also to include a device to display the images. We retain vpDisplayOpenCV that works on Unix and Windows since OpenCV is mandatory by the tracker. Finally we include vpVideoReader header that will be used to read an mpeg input stream. - +It can also be run with [--init-by-click] option. In that case, the user can select a set of keypoints to track with a left mouse click. A right mouse click stops the keypoints selection and allows to start the tracking. \code -#if (VISP_HAVE_OPENCV_VERSION >= 0x010100) && defined(VISP_HAVE_FFMPEG) +$ ./tutorial-klt-tracker --init-by-click \endcode -We use the previous macro to ensure that OpenCV at least version 1.1.0 requested by the tracker and the image viewer, and ffmpeg requested to read the video stream are available. +Here is the line by line explanation of the source : -\code - vpVideoReader reader; - reader.setFileName("video-postcard.mpeg"); -\endcode +\snippet tutorial-klt-tracker.cpp Include + +We include here the headers that define the corresponding classes. vpImageConvert class will be used to convert ViSP images implemented in vpImage class into OpenCV IplImage or cv::Mat structures used as an entry by the KLT tracker. Then we include the header of vpKltOpencv class which is the wrapper over OpenCV KLT tracker implementation. + +\note +- If OpenCV version is less that 2.8.0, vpKltOpencv class takes as input an IplImage. +- If OpenCV version is 2.8.0 or ;ore recent, vpKltOpencv class takes as input a cv::Mat image. + +We need also to include a device to display the images. We retain vpDisplayOpenCV that works on Unix and Windows since OpenCV is mandatory by the tracker. Finally we include vpVideoReader header that will be used to read an mpeg input stream. + +At the beginning of the main() function, we use the following macro to ensure that OpenCV requested by the tracker is available. Note that OpenCV will also be used to render the images and read the input video stream. + +\snippet tutorial-klt-tracker.cpp Check 3rd party The program starts by the creation of a vpVideoReader instance able to extract all the images of the video file \c video-postcard.mpeg. Here, the video should be in the same folder than the binary. -\code - vpImage<unsigned char> I; - reader.acquire(I); -\endcode -Returns the first image of the video in the gray level ViSP image container \c I. +\snippet tutorial-klt-tracker.cpp Create reader -\code - IplImage * cvI = NULL; - vpImageConvert::convert(I, cvI); -\endcode +Then we extract the first image of the video in the gray level ViSP image container \c I. -Then we convert \c I into \c cvI, an image in OpenCV IplImage format that will be used by the tracker. +\snippet tutorial-klt-tracker.cpp Acquire -\code - vpDisplayOpenCV d(I, 0, 0, "Klt tracking"); - vpDisplay::display(I); - vpDisplay::flush(I); -\endcode -Create a window associated to \c I, at position (0,0) in the screen, with "Klt tracking" as title, and display image \c I. +This image \c I is then converted into \c cvI, an OpenCV image format that will be used by the tracker. + +\snippet tutorial-klt-tracker.cpp Convert to OpenCV image + +We also create a window associated to \c I, at position (0,0) in the screen, with "Klt tracking" as title, and display image \c I. + +\snippet tutorial-klt-tracker.cpp Init display -\code - vpKltOpencv tracker; - - tracker.setMaxFeatures(200); - tracker.setWindowSize(10); - tracker.setQuality(0.01); - tracker.setMinDistance(15); - tracker.setHarrisFreeParameter(0.04); - tracker.setBlockSize(9); - tracker.setUseHarris(1); - tracker.setPyramidLevels(3); - - tracker.initTracking(cvI); -\endcode +From now we have to create an instance of the tracker and set the parameters of the Harris keypoint detector. + +\snippet tutorial-klt-tracker.cpp Create tracker -Create an instance of the tracker, set parameters of the Harris keypoint detector and finally initialize the tracker on \c cvI image. +The tracker is then initialized on \c cvI image. -\code - while ( ! reader.end() ) - { - reader.acquire(I); - vpImageConvert::convert(I, cvI); - vpDisplay::display(I); - - tracker.track(cvI); - tracker.display(I, vpColor::red); - - vpDisplay::flush(I); - } -\endcode +\snippet tutorial-klt-tracker.cpp Init tracker + +With the next line the user can know how many keypoints were detected automatically or selected by the user during initialization. + +\snippet tutorial-klt-tracker.cpp How many features + +\note If no keypoints were found, the next call to vpKltTracker::track() will throw an exception. + +To detect more keypoints, you may decrease the quality parameter set with the following line: + +\snippet tutorial-klt-tracker.cpp Quality + +Until the end of the video, we get \c I the next image in ViSP format, display and convert it in OpenCV format. Then we track the Harris keypoints using KLT tracker before displaying the keypoints that are tracked with a red cross. + +\snippet tutorial-klt-tracker.cpp While loop -Until the end of the video is not reached, we get \c I the next image in ViSP format, display and convert it in IplImage format. Then we track the Harris keypoints using KLT tracker before displaying the keypoints that are tracked with a red cross. +We are waiting for a mouse click event on image \c I to end the program. + +\snippet tutorial-klt-tracker.cpp Wait click + +With the following line, we release the memory allocated for the OpenCV IplImage \c cvI before ending the program. This has to be done only if OpenCV version is less than 2.8.0. + +\snippet tutorial-klt-tracker.cpp Release IplImage + +\section tracking_keypoint_klt_init KLT tracker with re-initialisation + +Once initialized, the number of tracked features decreases over the time. Depending on a criteria, it may sense to detect and track new features online. A possible criteria is for example to compare the number of currently tracked features to the initial number of detected features. If less than a given percentage of features are tracked, you can start a new detection. + +To get the number of detected or tracked features just call: \code - vpDisplay::getClick(I); + tracker.getNbFeatures(); \endcode -We are waiting a mouse click event on image \c I to end the program. -With the following line, we release the memory allocated for the OpenCV IplImage \c cvI before ending the program. +Then the idea is to add the previously tracked features to the list of features that are detected. + +The example tutorial-klt-tracker-with-reinit.cpp shows how to do that. In that example we start a new detection on frame 25. Compared to the previous code available in tutorial-klt-tracker.cpp we add the following lines: \code - cvReleaseImage(&cvI); + if (reader.getFrameIndex() == 25) { + std::cout << "Re initialize the tracker" << std::endl; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + // Save of previous features + std::vector<cv::Point2f> prev_features = tracker.getFeatures(); + + // Start a new feature detection + tracker.initTracking(cvI); + std::vector<cv::Point2f> new_features = tracker.getFeatures(); + + // Add previous features if they are not to close to detected one + double distance, minDistance_ = tracker.getMinDistance(); + bool is_redundant; + for (size_t i=0; i < prev_features.size(); i++) { + // Test if a previous feature is not redundant with one of the newly detected + is_redundant = false; + for (size_t j=0; j < new_features.size(); j++){ + distance = sqrt(vpMath::sqr(new_features[j].x-prev_features[i].x) + + vpMath::sqr(new_features[j].y-prev_features[i].y)); + if(distance < minDistance_){ + is_redundant = true; + break; + } + } + if(is_redundant){ + continue; + } + //std::cout << "Add previous feature with index " << i << std::endl; + tracker.addFeature(prev_features[i]); + } +#else + ... +#endif + } + // Track the features + tracker.track(cvI); \endcode +In this code we do the following: +- save the features that are tracked until now +- initialize the tracker to detect new features +- parse all the saved features and compare them to the newly detected features. If a previous feature is close in terms of geometric distanceto a newly detected one, it is rejected (in our case less than 2 pixels). If not, it is added to the list of detected features. + You are now ready to see the next \ref tutorial-tracking-me. */ diff --git a/doc/tutorial-tracking-mb.doc b/doc/tutorial-tracking-mb.doc index 351cda1646b58b4eb307fc5d0c8750b6bc98f773..50d6c0df8e4df18ba0fb255f0aebb19429edd303 100644 --- a/doc/tutorial-tracking-mb.doc +++ b/doc/tutorial-tracking-mb.doc @@ -3,15 +3,19 @@ \page tutorial-tracking-mb Tutorial: Model-based tracking \tableofcontents -With ViSP it is possible to track an object using its cad model. Considered objects should be modeled by lines or cylinders. The model of the object could be defined in vrml format, or in cao format. +With ViSP it is possible to track an object using its cad model. Considered objects should be modeled by lines, circles or cylinders. The model of the object could be defined in vrml format (except for circles), or in cao format. + +Next section highlights the differents versions of the markerless model-based trackers that are implemented in ViSP. + +\section tracking_mb Markerless model-based trackers The model-based tracker can consider moving-edges behind the lines of the model (see section \ref tracking_mb_edges). It can also consider keypoints that are detected and tracked on each visible face of the model (see section \ref tracking_mb_klt). The tracker can also handle moving-edges and keypoints in an hybrid scheme (see section \ref tracking_mb_hybrid). While the \ref tracking_mb_edges is appropriate to track untextured object, the \ref tracking_mb_klt is more designed to exploit textured objects with edges that are not really visible. The \ref tracking_mb_hybrid is appropriate to track textured objects with visible edges. -In the following sections, we consider the tracking of a tea box modeled in cao format. To simplify the source code, the tracking is performed on a single image. The extension to a sequence of images or to images acquired from a camera is easy. +In the following sections, we consider the tracking of a tea box modeled in cao format. -\section tracking_mb_edges Model-based edges tracker +\subsection tracking_mb_edges Model-based edges tracker The following example that comes from tutorial-mb-edge-tracker.cpp allows to track the tea box using vpMbEdgeTracker class. @@ -25,35 +29,24 @@ The video below shows the result of the tea box model-based edges tracking. Hereafter is the description of the new lines introduced in this example. -\code -#include <visp/vpMbEdgeTracker.h> -\endcode +\snippet tutorial-mb-edge-tracker.cpp Include -Here we include the header of the vpMbEdgeTracker class that allows to track an object from its cad model by using the moving-edges. The tracker will use image \c I and the intrinsic camera parameters \c cam as input. +Here we include the header of the vpMbEdgeTracker class that allows to track an object from its cad model using moving-edges. The tracker will use image \c I and the intrinsic camera parameters \c cam as input. -\code - vpImage<unsigned char> I; - vpCameraParameters cam; -\endcode +\snippet tutorial-mb-edge-tracker.cpp Image As output, it will estimate \c cMo, the pose of the object in the camera frame. -\code - vpHomogeneousMatrix cMo; -\endcode +\snippet tutorial-mb-edge-tracker.cpp cMo Once the input image \c teabox.pgm is loaded in \c I, a window is created and initialized with image \c I. Then we create an instance of the tracker. -\code - vpMbEdgeTracker tracker; -\endcode +\snippet tutorial-mb-edge-tracker.cpp Constructor There are then two different ways to initialize the tracker. -- The first one, if libxml2 is available, is to read the settings from \c teabox.xml input file. -\code - tracker.loadConfigFile("teabox.xml"); -\endcode +- The first one, if libxml2 is available, is to read the settings from \c teabox.xml input file if the file exists. +\snippet tutorial-mb-edge-tracker.cpp Load xml The content of the xml file is the following: \code <?xml version="1.0"?> @@ -82,57 +75,46 @@ The content of the xml file is the following: <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> \endcode - The second one consists in initializing the parameters directly in the source code: -\code - vpMe me; - me.setMaskSize(5); - me.setMaskNumber(180); - me.setRange(8); - me.setThreshold(10000); - me.setMu1(0.5); - me.setMu2(0.5); - me.setSampleStep(4); - me.setNbTotalSample(250); - tracker.setMovingEdge(me); - cam.initPersProjWithoutDistortion(839, 839, 325, 243); - tracker.setCameraParameters(cam); -\endcode +\snippet tutorial-mb-edge-tracker.cpp Set parameters -The display in the image window of additional drawings in overlay such as the moving edges positions, is then enabled by: -\code - tracker.setDisplayFeatures(true); -\endcode -An important setting concerns the visibility test that is used to determine if a face is visible. Note that moving-edges are tracked only on visible faces. Two different visibility tests are implemented; with or without Ogre ray tracing. With the next line, we disable Ogre visibility test. +An important setting concerns the visibility test that is used to determine if a face is visible. Note that moving-edges are tracked only on visible faces. Two different visibility tests are implemented; with or without Ogre ray tracing. The default test is the one without Ogre. The function vpMbEdgeTracker::setOgreVisibilityTest() allow to select which test is to use. +Let us now highlight how the visibility test works. As illustrated in the following figure, the angle \f$ \alpha \f$ between the normal of the face and the line going from the camera to the center of gravity of the face is used to determine if the face is visible. If we consider two parameters; the angle to determine if a face is appearing \f$ \alpha_{appear} \f$, and the angle to determine if the face is disappearing \f$ \alpha_{disappear} \f$, a face will be considered as visible if \f$ \alpha \leq \alpha_{disappear} \f$. We consider also that a new face is appearing if \f$ \alpha \geq \alpha_{appear} \f$. These two parameters can be set either in the xml file: \code - tracker.setOgreVisibilityTest(false); +<conf> + ... + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + </face> \endcode +or in the code: +\snippet tutorial-mb-edge-tracker.cpp Set angles -As illustrated in the following figure, the angle \f$ \alpha \f$ between the normal of the face and the line going from the camera to the center of gravity of the face is then used to determine if the face is visible. +\note When these two angle parameters are not set, their default values set to 89 degrees are used. \image html img-tracker-mb-visibility.jpg Principle of the visibility test used to determine if a face is visible. -- When Ogre visibility test is disabled, the algorithm that computes the normal of the face is very simple. It makes the assumption that faces are convex and oriented counter clockwise. Faces are considered as visible if \f$ \alpha < 90\f$ degrees. When only moving-edges are used (nor keypoints) and when the object to track is simple like a single box, we suggest as here to disable Ogre visibility test. It will help the tracker to detect as soon as possible new faces when only one face is currently tracked. -- When Ogre visibility test is enabled, the algorithm used to determine the visibility of a face is the same than previously except that once visible faces are detected thanks to their normal, we add an other test to reject faces that are partially occluded by an other one. This additional test is performed using Ogre ray-tracing capability. Note also that two additional parameters need to be introduced; the angle to determine if a face is appearing, and the angle to determine if the face is disappearing. +- When Ogre visibility test is disabled (we recall that this is the default behavior), the algorithm that computes the normal of the face is very simple. It makes the assumption that faces are convex and oriented counter clockwise. Here the face is considered as appearing if \f$ \alpha < 70\f$ degrees, and disappearing if \f$ \alpha > 80\f$ degrees. When only moving-edges are used (nor keypoints) and when the object to track is simple like a single box, we suggest as here to disable Ogre visibility test. +\snippet tutorial-mb-edge-tracker.cpp Set ogre + +- When Ogre visibility test is enabled, the algorithm used to determine the visibility of a face is the same than previously except that once visible faces are detected thanks to their normal, we add an other test to reject faces that are partially occluded by an other one. This additional test is performed using Ogre ray-tracing capability. \code tracker.setOgreVisibilityTest(true); - tracker.setAngleAppear(70); - tracker.setAngleDisappear(80); -\endcode -It is also possible to introduce these two parameters in the xml file. It should be modified by adding the following lines: -\code -<conf> - ... - <face> - <angle_appear>70</angle_appear> - <angle_disappear>80</angle_disappear> - </face> \endcode -Here the face is considered as appearing if \f$ \alpha < 70\f$ degrees, and disappearing if \f$ \alpha > 80\f$ degrees. + Additionally to the visibility test described above, it is also possible to use clipping. Firstly, the algorithm removes the faces that are not visibles, according to the visibility test used, then it will also remove the faces or parts of the faces that are out of the clipping planes. As illustrated in the following figure, different clipping planes can be enabled. @@ -140,9 +122,7 @@ Additionally to the visibility test described above, it is also possible to use Let's consider two plane categories: the ones belonging to the field of view or FOV (Left, Right, Up and Down), and the Near and Far clipping planes. The FOV planes can be enabled by: -\code - tracker.setClipping(vpMbtPolygon::FOV_CLIPPING); -\endcode +\snippet tutorial-mb-edge-tracker.cpp Set clipping fov which is equivalent to: @@ -165,14 +145,11 @@ For the Near and Far clipping it is quite different. Indeed, thoses planes requi tracker.setClipping(vpMbtPolygon::NEAR_CLIPPING | vpMbtPolygon::FAR_CLIPPING); \endcode -Or specify the distances in meters, which will automatically activate the clipping for thoses planes: +or the user can specify the distances in meters, which will automatically activate the clipping for thoses planes: -\code - tracker.setNearClippingDistance(0.1); - tracker.setFarClippingDistance(100.0); -\endcode +\snippet tutorial-mb-edge-tracker.cpp Set clipping distance -It is also possible to enable them in the xml file. It should be modified by adding the following lines: +It is also possible to enable them in the xml file. This is done with the following lines: \code <conf> @@ -187,19 +164,31 @@ It is also possible to enable them in the xml file. It should be modified by add Here for simplicity, the user just has the possibility to either activate all the FOV clipping planes or none of them (fov_clipping requires a boolean). -Now we are ready to load the cad model of the object in cao format with: -\code - tracker.loadModel("teabox.cao"); -\endcode -The file \c teabox.cao describes first the vertexes of the box, then the edges that corresponds to the faces. A more complete description of this file is provided in \ref tracking_mb_cao (\ref tracking_mb_cao_face). The next figure gives the index of the vertices that are defined in \c teabox.cao. +\note When clipping parameters are not set in the xml file, nor in the code, clipping is not used. Usually clipping is not helpful when the oject to track is simple. + +Now we are ready to load the cad model of the object. ViSP supports cad model in cao format or in vrml format. The cao format is a particular format only supported by ViSP. It doesn't require an additional 3rd party rather then vrml format that require Coin 3rd party. We load the cad model in cao format with: +\snippet tutorial-mb-edge-tracker.cpp Load cao +The file \c teabox.cao describes first the vertices of the box, then the edges that corresponds to the faces. A more complete description of this file is provided in \ref tracking_mb_cao_face. The next figure gives the index of the vertices that are defined in \c teabox.cao. + +To load the cad model in vrml the user has to replace the previous line by the following: +\snippet tutorial-mb-edge-tracker.cpp Load wrl +As for the cao format, \c teabox.wrl describes first the vertices of the box, then the edges that corresponds to the faces. A more complete description of this file is provided in \ref tracking_mb_vrml_face. \image html img-teabox-cao.jpg Index of the vertices used to model the tea box in cao format. -Four vertices with their 3D coordinates are then used to initialize the tracker: -\code - tracker.initClick(I, "teabox.init"); -\endcode -The content of \c teabox.init file is provided hereafter: +Once the model of the object to track is loaded, with the next line the display in the image window of additional drawings in overlay such as the moving edges positions, is then enabled by: +\snippet tutorial-mb-edge-tracker.cpp Set display + +Now we have to initialize the tracker. With the next line we choose to use a user interaction. +\snippet tutorial-mb-edge-tracker.cpp Init + +The user has to click in the image on four vertices with their 3D coordinates defined in the "teabox.init" file. The following image shows where the user has to click. + +\image html img-teabox-click.jpg Image "teabox.ppm" used to help the user to initialize the tracker. + +Matched 2D and 3D coordinates are then used to compute an initial pose used to initialize the tracker. Note also that the third optional argument "true" is used here to enable the display of an image that may help the user for the initialization. The name of this image is the same as the "*.init" file except the extension that sould be ".ppm". In our case it will be "teabox.ppm". + +The content of \c teabox.init file that defines 3D coordinates of some points of the model used during user intialization is provided hereafter. Note that all the characters after character '#' are considered as comments. \includelineno teabox.init @@ -214,37 +203,19 @@ Here the user has to click on vertex 0, 3, 2 and 5 in the window that displays i Next, in the infinite while loop, after displaying the next image, we track the object on a new image \c I. -\code - tracker.track(I); -\endcode +\snippet tutorial-mb-edge-tracker.cpp Track The result of the tracking is a pose \c cMo that could be obtained by: -\code - tracker.getPose(cMo); -\endcode - +\snippet tutorial-mb-edge-tracker.cpp Get pose Next lines are used first to retrieve the camera parameters used by the tracker, then to display the visible part of the cad model using red lines with 2 as thickness, and finally to display the object frame at the estimated position \c cMo. Each axis of the frame are 0.025 meters long. Using vpColor::none indicates that x-axis is displayed in red, y-axis in green, while z-axis in blue. The thickness of the axis is 3. -\code - tracker.getCameraParameters(cam); - tracker.display(I, cMo, cam, vpColor::red, 2); - vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); -\endcode +\snippet tutorial-mb-edge-tracker.cpp Display The last lines are here to free the memory allocated by libxml2 or Coin 3rd party libraries: -\code -#ifdef VISP_HAVE_XML2 - vpXmlParser::cleanup(); -#endif -#ifdef VISP_HAVE_COIN - SoDB::finish(); -#endif -\endcode - - +\snippet tutorial-mb-edge-tracker.cpp Cleanup -\section tracking_mb_klt Model-based KLT tracker +\subsection tracking_mb_klt Model-based keypoint tracker The following example that comes from tutorial-mb-klt-tracker.cpp allows to track the tea box using vpMbKltTracker class. @@ -260,17 +231,12 @@ This example is very similar to the one presented in \ref tracking_mb_edges exce As previously, there are two different ways to initialize the tracker. - The first one, if libxml2 is available, consists in reading the settings from an xml file. -\code - tracker.loadConfigFile("teabox.xml"); -\endcode +\snippet tutorial-mb-klt-tracker.cpp Load xml + The \c teabox.xml file used here contains the following: \code <?xml version="1.0"?> <conf> - <face> - <angle_appear>70</angle_appear> - <angle_disappear>80</angle_disappear> - </face> <klt> <mask_border>5</mask_border> <max_features>300</max_features> @@ -287,36 +253,27 @@ The \c teabox.xml file used here contains the following: <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> \endcode - The second one consists in initializing the parameters directly in the source code: -\code - tracker.setAngleAppear(70); - tracker.setAngleDisappear(80); - tracker.setMaskBorder(5); - vpKltOpencv klt_settings; - klt_settings.setMaxFeatures(300); - klt_settings.setWindowSize(5); - klt_settings.setQuality(0.015); - klt_settings.setMinDistance(8); - klt_settings.setHarrisFreeParameter(0.01); - klt_settings.setBlockSize(3); - klt_settings.setPyramidLevels(3); - tracker.setKltOpencv(klt_settings); - cam.initPersProjWithoutDistortion(839, 839, 325, 243); - tracker.setCameraParameters(cam); -\endcode +\snippet tutorial-mb-klt-tracker.cpp Set parameters -Note also that in this example we model the tea box with triangles: +Note also that in this example we can model the tea box with triangles: \code tracker.loadModel("teabox-triangle.cao"); \endcode -The file \c teabox-triangle.cao describes first the vertexes of the box, then the triangular faces. A more complete description of this file is provided in \ref tracking_mb_cao (\ref tracking_mb_cao_triangle). +The file \c teabox-triangle.cao describes first the vertices of the box, then the triangular faces. A more complete description of this file is provided in \ref tracking_mb_cao_triangle). Note that this is the only tracker for which lines of the model are not necessary edges of the object. - -\section tracking_mb_hybrid Model-based hybrid tracker +\subsection tracking_mb_hybrid Model-based hybrid tracker The following example that comes from tutorial-mb-hybrid-tracker.cpp allows to track the tea box using vpMbEdgeKltTracker class. @@ -351,10 +308,6 @@ We provide just hereafter the content of the \c teabox.xml file: <step>4</step> <nb_sample>250</nb_sample> </sample> - <face> - <angle_appear>70</angle_appear> - <angle_disappear>80</angle_disappear> - </face> <klt> <mask_border>5</mask_border> <max_features>300</max_features> @@ -371,13 +324,22 @@ We provide just hereafter the content of the \c teabox.xml file: <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> \endcode +\section tracking_model How to model the objects to track -\section tracking_mb_cao CAD model in cao format +ViSP supports two different ways to describe CAD models, either in cao or in vrml format. +- cao format is specific to ViSP. It allows to describe the CAD model of an object using a text file with extension \c .cao. +- vrml format is supported only if Coin 3rd party is installed. This format allows to describe the CAD model of an object using a text file with extension \c .wrl. -cao format is specific to ViSP. It allows to describe the CAD model of an object using a text file with extension \c .cao. \subsection tracking_mb_cao_face teabox.cao example @@ -389,19 +351,20 @@ This file describes the model of the tea box corresponding to the next image: \image html img-teabox-cao.jpg Index of the vertices used to model the tea box in cao format. -We make the choice to describe the faces of the box from the 3D points that correspond to the vertices. We provide now a line by line description of the file: +We make the choice to describe the faces of the box from the 3D points that correspond to the vertices. We provide now a line by line description of the file. Notice that the characters after the '#' are considered as comments. - line 1: Header of the \c .cao file -- line 2: The model is defined by 8 3D points. Here the 8 points correspond to the 8 vertices of the tea box presented in the previous figure. Thus, next 8 lines define the 3D points coordinates. -- line 3: 3D point with coordinate (0,0,0) corresponding to vertex 0 of the tea box. This point is also the origin of the frame in which all the 3D points are defined. -- line 4: 3D point with coordinate (0,0,-0.08) corresponding to vertex 1. -- line 5 to 10: The other 3D points corresponding to vertices 2 to 7 respectively. -- line 11: Number of single lines of the model. Sometimes, it could be useful to track a single line that doesn't lead to a face. -- line 12: Deprecated parameter that should always be set to 0. -- line 13: The number of faces defined by a set of points. Here our tea box has 6 faces. Thus, next 6 lines describe each face from the 3D points defined previously line 3 to 10. -- line 14: First face defined by 4 points, respectively vertices 0,1,2,3. The orientation of the face is counter clockwise by going from vertex 0 to vertex 1, than 2 and 3. This fixes the orientation of the normal of the face going outside the object. -- line 15: Second face also defined by 4 points, respectively vertices 1,6,5,2 to have a counter clockwise orientation. -- line 16 to 19: The four other faces of the box. -- line 20: Number of cylinders describing the model. Since we model a simple box, the number of cylinders is 0. +- line 3: The model is defined by 8 3D points. Here the 8 points correspond to the 8 vertices of the tea box presented in the previous figure. Thus, next 8 lines define the 3D points coordinates. +- line 4: 3D point with coordinate (0,0,0) corresponding to vertex 0 of the tea box. This point is also the origin of the frame in which all the 3D points are defined. +- line 5: 3D point with coordinate (0,0,-0.08) corresponding to vertex 1. +- line 6 to 11: The other 3D points corresponding to vertices 2 to 7 respectively. +- line 13: Number of 3D lines defined from two 3D points. It is possible to introduce 3D lines and then use these lines to define faces from these 3D lines. This is particularly useful to define faces from non-closed polygons. For instance, it can be used to specify the tracking of only 3 edges of a rectangle. Notice also that a 3D line that doesn't belong to a face is always visible and consequently always tracked. +- line 15: Number of faces defined from 3D lines. In our teabox example we decide to define all the faces from 3D points, that is why this value is set to 0. +- line 17: The number of faces defined by a set of 3D points. Here our teabox has 6 faces. Thus, next 6 lines describe each face from the 3D points defined previously line 4 to 11. Notice here that all the faces defined from 3D points corresponds to closed polygons. +- line 18: First face defined by 4 3D points, respectively vertices 0,1,2,3. The orientation of the face is counter clockwise by going from vertex 0 to vertex 1, then 2 and 3. This fixes the orientation of the normal of the face going outside the object. +- line 19: Second face also defined by 4 points, respectively vertices 1,6,5,2 to have a counter clockwise orientation. +- line 20 to 23: The four other faces of the box. +- line 25: Number of 3D cylinders describing the model. Since we model a simple box, the number of cylinders is 0. +- line 27: Number of 3D circles describing the model. For the same reason, the number of circles is 0. \subsection tracking_mb_cao_triangle teabox-triangle.cao example @@ -413,8 +376,304 @@ This file describes the model of the tea box corresponding to the next image: \image html img-teabox-cao-triangle.jpg Index of the vertices used to model the tea box in cao format with triangles. -Until line 12, the content of this file is similar to the one described in -\ref tracking_mb_cao_face. Line 13 we specify that the model contains 12 faces. Each face is then described as a triangle. +Until line 15, the content of this file is similar to the one described in +\ref tracking_mb_cao_face. Line 17 we specify that the model contains 12 faces. Each face is then described as a triangle. + +\subsection tracking_mb_vrml_face teabox.wrl example + +The content of the teabox.wrl file used in the \ref tracking_mb_edges is given hereafter. This content is to make into relation with teabox.cao described in \ref tracking_mb_cao_face. + +\includelineno teabox.wrl + +This file describes the model of the tea box corresponding to the next image: + +\image html img-teabox-cao.jpg Index of the vertices used to model the tea box in vrml format. + +We provide now a line by line description of the file where the faces of the box are defined from the vertices: +- line 1 to 10: Header of the \c .wrl file. +- line 13 to 20: 3D coordinates of the 8 tea box vertices. +- line 34 to 29: Each line describe a face. In this example, a face is defined by 4 vertices. For example, the first face join vertices 0,1,2,3. The orientation of the face is counter clockwise by going from vertex 0 to vertex 1, then 2 and 3. This fixes the orientation of the normal of the face going outside the object. + +\section advanced_model_sec Advanced: How to manipulate the model + +The following code shows how to access to the CAD model +- to check if a face is visible, +- to get the name of the face (only with models in .cao format for the moment) +- to check if the level of detail is enable/disable (only with models in .cao format for the moment) +- to access to the coordinates of the 3D points used to model a face +- from the pose \e cMo estimated by the tracker to compute the coordinates of the 3D points in the image + +\code + vpMbHiddenFaces<vpMbtPolygon> &faces = tracker.getFaces(); + std::cout << "Number of faces: " << faces.size() << std::endl; + for (unsigned int i=0; i < faces.size(); i++) { + std::vector<vpMbtPolygon*> &poly = faces.getPolygon(); + std::cout << "face " << i << " with index: " << poly[i]->getIndex() + << (poly[i]->getName().empty() ? "" : (" with name: " + poly[i]->getName())) + << " is " << (poly[i]->isVisible() ? "visible" : "not visible") + << " and has " << poly[i]->getNbPoint() << " points" + << " and LOD is" << (poly[i]->useLod ? "enabled" : "disabled") << std::endl; + + for (unsigned j=0; j<poly[i]->getNbPoint(); j++) { + vpPoint P = poly[i]->getPoint(j); + P.project(cMo); + std::cout << " P obj " << j << ": " << P.get_oX() << " " << P.get_oY() << " " << P.get_oZ() << std::endl; + std::cout << " P cam" << j << ": " << P.get_X() << " " << P.get_Y() << " " << P.get_Z() << std::endl; + + vpImagePoint iP; + vpMeterPixelConversion::convertPoint(cam, P.get_x(), P.get_y(), iP); + std::cout << " iP " << j << ": " << iP.get_u() << " " << iP.get_v() << std::endl; + } + } +\endcode + + +\section advanced_lod_sec Advanced: Level of detail (LOD) + +The level of detail (LOD) consists in introducing additional constraints to the visibility check to determine if the features of a face have to be tracked or not. Two parameters are used +- the line length (in pixel) +- the area of the face (in pixel²), that could be closed or not (you can define an open face by adding all the segments without the last one which closes the face) + +The tracker allows to enable/disable the level of detail concept using vpMbTracker::setLod() function. +This example permits to set LOD settings to all elements : +\code +tracker.setLod(true); +tracker.setMinLineLengthThresh(40.0); +tracker.setMinPolygonAreaThresh(500.0); +\endcode + +This example permits to set LOD settings to specific elements denominated by his name. +\code +tracker.setLod(false); +tracker.setLod(true, "Left line"); +tracker.setLod(true, "Front face"); +tracker.setMinLineLengthThresh(35.0, "Left line"); +tracker.setMinPolygonAreaThresh(120.0, "Front face"); +\endcode + +Furthermore, to set a name to a face see \ref advanced_cao_nam_sec. + + +\section advanced_cao_sec Advanced: CAD model in cao format + +\subsection advanced_cao_lin_sec How to model faces from lines +The first thing to do is to declare the differents points. Then you define each segment of the face with the index of the start point and with the index of the end point. +Finally, you define the face with the index of the segments which constitute the face. + +\note The way you declare the face segments (clockwise or counter clockwise) will determine the direction of the normal of the face and so will influe on the visibility of the face. + +\code +V1 +# Left wing model +6 # Number of points +# 3D points +-4 -3.8 0.7 +-6 -8.8 0.2 +-12 -21.7 -1.2 +-9 -21.7 -1.2 + 0.8 -8.8 0.2 + 4.6 -3.8 0.7 +# 3D lines +6 # Number of lines +0 1 # line 0 +1 2 +2 3 +3 4 +4 5 +5 0 # line 5 +# Faces from 3D lines +1 # Number of faces defined by lines +6 0 1 2 3 4 5 # face 0: [number of lines] [index of the lines]... +# Faces from 3D points +0 +# 3D cylinders +0 +# 3D circles +0 +\endcode + + +\subsection advanced_cao_cyl_sec How to model cylinders +The first thing to do is to declare the two points defining the cylinder axis of revolution. Then you declare the cylinder with the index of the points that define the cylinder axis of revolution and with the cylinder radius. + +\note For the level of detail, in a case of a cylinder, this is taking into account by using the length of the axis of revolution to determine the visibility. + +\image html img-cylinder.png Example of a cylinder. + +\code +V1 +# Cylinder model +2 # Number of points +# 3D points +16.9 0 0.5 # point 0 +-20 0 0.5 # point 1 +# 3D lines +0 +# Faces from 3D lines +0 +# Faces from 3D points +0 +# 3D cylinders +1 # Number of cylinders +0 1 2.4 # cylinder 0: [1st point on revolution axis] [2nd point on revolution axis] [radius] +# 3D circles +0 +\endcode + + +\subsection advanced_cao_cir_sec How to model circles +The first thing to do is to declare three points: one point for the center of the circle and two points on the circle plane (i.e. not necessary located on the perimeter of the circle but on the plane of the circle). Then you declare your circle with the radius and with index of the three points. + +\note The way you declare the two points on the circle plane (clockwise or counter clockwise) will determine the direction of the normal of the circle and so will influe on the visibility of the circle. +For the level of detail, in a case of a circle, this is taking into account by using the area of the bounding box of the circle to determine the visibility. + +\image html img-circle.png Example of a circle. + +\code +V1 +# Circle model +3 # Number of points +# 3D points +-3.4 14.6 1.1 # point 0 +-3.4 15.4 1.1 +-3.4 14.6 1.8 # point 2 +# 3D lines +0 +# Faces from 3D lines +0 +# Faces from 3D points +0 +# 3D cylinders +0 +# 3D circles +1 # Number of circles +0.8 0 2 1 # circle 0: [radius] [circle center] [1st point on circle plane] [2nd point on circle plane] +\endcode + + +\subsection advanced_cao_hie_sec How to create an hierarchical model +It could be useful to define a complex model instead of using one big model file with all the declaration with different sub-models, each one representing a specific part of the complex model in a specific model file. +To create an hierarchical model, the first step is to define all the elementary parts and then regroup them. + +\image html img-plane-hierarchical-diagram.jpg Example of a possible hierarchical modelling of a plane. + +For example, if we want to have a model of a plane, we could represent as elementary parts the left and right wings, the tailplane (which is constituted of some other parts) and a cylinder for the plane fuselage. +The following lines represent the top model of the plane. + +\code +V1 +# header +# load the different parts of the plane +load("wings.cao") # load the left and right wings +load("tailplane.cao") +# 3D points +2 # Number of points +16.9 0 0.5 +-20 0 0.5 +# 3D lines +0 +# Faces from 3D lines +0 +# Faces from 3D points +0 +# 3D cylinders +1 # Number of cylinders +0 1 2.4 # define the plane fuselage as a cylinder +# 3D circles +0 +\endcode + +\note The path to include another model can be expressed as an absolute or a relative path (relative to the file which includes the model). + + +\subsection advanced_cao_nam_sec How to set a name to a face +To exploit the name of a face in the code, see \ref advanced_lod_sec. + +It could be useful to give a name for a face in a model in order to easily modify his LOD parameters for example, or just for debuging purpose. +This is done directly in the model file : +\code +V1 +# header +# load the different parts of the plane +load("wings.cao") +load("tailplane.cao") +# 3D points +5 # Number of points +16.9 0 0.5 +-20 0 0.5 +-3.4 14.6 1.1 +-3.4 15.4 1.1 +-3.4 14.6 1.8 +# 3D lines +0 +# Faces from 3D lines +0 +# Faces from 3D points +0 +# 3D cylinders +1 # Number of cylinders +0 1 2.4 name=plane_fuselage +# 3D circles +1 # Number of circles +0.8 2 4 3 name="right reactor" +\endcode + +\note If the name contains space characters, it must be surrounded by quotes. +You can give a name to all the elements excepts for points. + + +\subsection advanced_cao_lod_sec How to tune the level of detail + +As explained in section \ref advanced_lod_sec the parameters of the lod can be set in the source code. They can also be set directly in the configuration file or in the CAD model in cao format. + +The following lines show the content of the configuration file : + +\code +<?xml version="1.0"?> +<conf> + <lod> + <use_lod>1</use_lod> + <min_line_length_threshold>40</min_line_length_threshold> + <min_polygon_area_threshold>150</min_polygon_area_threshold> + </lod> +</conf> +\endcode + +In CAD model file, you can specify the LOD settings to the desired elements : + +\code +V1 +# header +# load the different parts of the plane +load("wings.cao") +load("tailplane.cao") +# 3D points +5 # number of points +16.9 0 0.5 +-20 0 0.5 +-3.4 14.6 1.1 +-3.4 15.4 1.1 +-3.4 14.6 1.8 +# 3D lines +0 +# Faces from 3D lines +0 +# Faces from 3D points +0 +# 3D cylinders +1 # Number of cylinders +0 1 2.4 name=plane_fuselage useLod=true minLineLengthThreshold=100.0 +# 3D circles +1 # Number of circles +0.8 2 4 3 name="right reactor" useLod=true minPolygonAreaThreshold=40.0 +\endcode + +\note The order you call the methods to load the configuration file and to load the CAD model in the code will modify the result of the LOD parameters. +Basically, the LOD settings expressed in configuration file will have effect on all the elements in the CAD model while the LOD settings expressed in CAD model will be specific to an element. +The natural order would be to load first the configuration file and after the CAD model. + + +You are now ready to see the next \ref tutorial-tracking-tt. */ diff --git a/doc/tutorial-tracking-me.doc b/doc/tutorial-tracking-me.doc index cde4ac392bed427570a7fcde758d525933a215a0..33c35d0394e262554a71db0752843cc68cbc0c68 100644 --- a/doc/tutorial-tracking-me.doc +++ b/doc/tutorial-tracking-me.doc @@ -72,7 +72,7 @@ We then open the connection with the grabber and acquire an image in \c I. To be able to display image \c I and the tracking results in overlay in a window, we create a display instance. \code -#if defined UNIX +#if defined(VISP_HAVE_X11) vpDisplayX d(I, 0, 0, "Camera view"); #else vpDisplayGDI d(I, 0, 0, "Camera view"); diff --git a/doc/tutorial-tracking-tt.doc b/doc/tutorial-tracking-tt.doc new file mode 100644 index 0000000000000000000000000000000000000000..4e75387f6986c4fc3bb82608c4727c9b6ba072f7 --- /dev/null +++ b/doc/tutorial-tracking-tt.doc @@ -0,0 +1,212 @@ +/** + +\page tutorial-tracking-tt Tutorial: Template tracking +\tableofcontents + +With ViSP it is possible to track a template using image registration algorithms. Contrary to the common approaches based on visual features, this method allows to be much more robust to scene variations. + +In the following sections, we consider the tracking of a pattern. To simplify the source code, the tracking is performed on a single image. The extension to a sequence of images or to images acquired from a camera is easy. To this end see \ref tutorial-grabber. + +\section tracking_tt Track the painting + +The following example that comes from tutorial-template-tracker.cpp allows to track a template using vpTemplateTrackerSSDInverseCompositional class. +Let us denote that "SSDInverseCompositional" refers to the similarity function used for the image registration. In ViSP, we have implemented, for now, two different similarity functions: the "Sum of Square Differences" (vpTemplateTrackerSSD classes \cite Baker04a) and the "Zero-mean Normalized Cross Correlation" (vpTemplateTrackerZNCC classes \cite Irani98a). Both methods can be used in different ways: Inverse Compositional, Forward Compositional, Forward Additional, or ESM. + +\include tutorial-template-tracker.cpp + +The video below shows the result of the template tracking. + +\htmlonly +<iframe width="420" height="315" src="http://www.youtube.com/embed/hniUcaUSVBM" frameborder="0" allowfullscreen></iframe> +\endhtmlonly + +Hereafter is the description of the new lines introduced in this example. + +\snippet tutorial-template-tracker.cpp Include + + +Here we include the header of the vpTemplateTrackerSSDInverseCompositional class that allows to track the template. Actually, the tracker estimates the displacement of the template in the current image according to its initial pose. The computed displacement can be represented by multiple transformations, also called warps (vpTemplateTrackerWarp classes). In this example, we include the header vpTemplateTrackerWarpHomography class to define the possible transformation of the template as an homography. + +\snippet tutorial-template-tracker.cpp Construction + +Once the tracker is created with the desired warp function, parameters can be tuned to be more consistent with the expected behavior. Depending on these parameters the perfomances of the tracker in terms of processing time and estimation could be affected. Since here we deal with 640 by 480 pixel wide images, the images are significantly subsampled in order to reduce the time of the image processing to be compatible with real-time. + +\code + tracker.setSampling(2, 2); // Will consider only one pixel from two along rows and columns + // to create the reference template + tracker.setLambda(0.001); // Gain used in the optimization loop + tracker.setIterationMax(200); // Maximum number of iterations for the optimization loop + tracker.setPyramidal(2, 1); // First and last level of the pyramid. Full resolution image is at level 0. +\endcode + +The last step of the initialization is to select the template that will be tracked during the sequence. + +\snippet tutorial-template-tracker.cpp Init + +The vpTemplateTracker classes proposed in ViSP offer you the possibility to defined your template as multiple planar triangles. When calling the previous line, you will have to specify the triangles that define the template. + +\image html img-initClickTemplateTracker.png Initialization of the template without Delaunay triangulation. + +Let us denote that those triangles don't have to be spatially tied up. However, if you want to track a simple image as in this example, you should initialize the template as on the figure above. Left clicks on point number zero, one and two create the green triangle. Left clicks on point three and four and then right click on point number five create the red triangle and ends the initialization. +If ViSP is build with OpenCV, we also provide an initialization with automatic triangulation using Delaunay. To use it, you just have to call vpTemplateTracker::initClick(I, true). Then by left clicking on points number zero, one, two, four and right clicking on point number five initializes the tracker as on the image above. + +Next, in the infinite while loop, after displaying the next image, we track the object on a new image I. + +\snippet tutorial-template-tracker.cpp Track + +If you need to get the parameters of the current transformation of the template, it can be done by calling: + +\snippet tutorial-template-tracker.cpp Homography + +For further information about the warping parameters, see the following \ref warp_tt section. + +Then according to the computed transformation obtained from the last call to track() function, next line is used to display the template using red lines. + +\snippet tutorial-template-tracker.cpp Display + +\subsection warp_tt Warping classes + +In the example presented above, we focused on the vpTemplateTrackerWarpHomography warping class which is the most generic transformation available in ViSP for the template trackers. However, if you know that the template you want to track is constrained, other warps might be more suitable. + +\b vpTemplateTrackerWarpTranslation + +\f$w({\bf x},{\bf p}) = {\bf x} + {\bf t}\f$ with the following estimated parameters \f$ {\bf p} = (t_x, t_y)\f$ + +This class is the most simple transformation available for the template trackers. It only considers translation on two-axis (x-axis and y-axis). + +\b vpTemplateTrackerWarpSRT + +\f$w({\bf x},{\bf p}) = (1+s){\bf Rx} + {\bf t}\f$ with \f${\bf p} = (s, \theta, t_x, t_y)\f$ + +The SRT warp considers a scale factor, a rotation on z-axis and a 2D translation as in vpTemplateTrackerWarpTranslation. + +\b vpTemplateTrackerWarpAffine + + +\f$ w({\bf x},{\bf p}) = {\bf Ax} + {\bf t}\f$ with \f${\bf A} = \left( \begin{array}{cc} + 1+a_0 & a_2 \\ + a_1 & 1+a_3 + \end{array} \right)\f$, \f${\bf t} = \left( \begin{array}{c} + t_x \\ + t_y + \end{array} \right)\f$ and the estimated parameters \f${\bf p} = (a_0 ... a_3, t_x, t_y)\f$ + +The template transformation can also be defined as an affine transformation. This warping function preserves points, straight lines, and planes. + +\b vpTemplateTrackerWarpHomography + +\f$w({\bf x},{\bf p}) = {\bf Hx}\f$ with \f$ {\bf H}=\left( \begin{array}{ccc} + 1 + p_0 & p_3 & p_6 \\ + p_1 & 1+p_4 & p_7 \\ + p_2 & p_5 & 1.0 + \end{array} \right) \f$ and the estimated parameters \f${\bf p} = (p_0 ... p_7)\f$ + +As remind, the vpTemplateTrackerWarpHomography estimates the eight parameters of the homography matrix \f$ {\bf H}\f$. + +\b vpTemplateTrackerWarpHomographySL3 + +\f$w({\bf x},{\bf p}) = {\bf Hx}\f$ with \f${\bf p} = (p_0 ... p_7)\f$ + +The vpTemplateTrackerWarpHomographySL3 warp works exactly the same as the vpTemplateTrackerWarpHomography warp. The only difference is that here, the parameters of the homography are estimated in the SL3 reference frame. + +\subsection tune_tt How to tune the tracker + +When you want to obtain a perfect pose estimation, it is often time-consuming. However, by tuning the tracker, you can find a good compromise between speed and efficiency. Basically, what will make the difference is the size of the reference template. The more pixels it contains, the more time-consuming it will be. Fortunately, the solutions to avoid this problem are multiple. First of all lets come back on the vpTemplateTracker::setSampling() function. + +\code +tracker.setSampling(4, 4); // Will consider only one pixel from four along rows and columns + // to create the reference template. +\endcode + +In the example above, we decided to consider only one pixel from 16 (4 by 4) to create the reference template. Obviously, by increasing those values it will consider much less pixels, which unfortunately decrease the efficiency, but the tracking phase will be much faster. + +The tracking phase relies on an iterative algorithm minimizing a cost function. What does it mean? It means this algorithm has, at some point, to stop! Once again, you have the possibility to reduce the number of iterations of the algorithm by taking the risk to fall in a local minimum. + +\code +tracker.setIterationMax(50); // Maximum number of iterations for the optimization loop. +\endcode + +If this is still not enough for you, let's remember that all of our trackers can be used in a pyramidal way. By reducing the number of levels considered by the algorithm, you will consider, once again, much less pixels and be faster. + +\code +tracker.setPyramidal(3, 2); // First and last level of the pyramid +\endcode +Note here that when vpTemplateTracker::setPyramidal() function is not used, the pyramidal approach to speed up the algorithm is not used. + +Let us denote that if you're using vpTemplateTrackerSSDInverseCompositional or vpTemplateTrackerZNCCInverseCompositional, you also have another interesting option to speed up your tracking phase. + +\code +tracker.setUseTemplateSelect(true); +\endcode + +This function will force the tracker to only consider, in the reference template, the pixels that have an high gradient value. This is another solution to limit the number of considered pixels. + +As well as vpTemplateTrackerSSDInverseCompositional::setUseTemplateSelect() or vpTemplateTrackerZNCCInverseCompositional::setUseTemplateSelect(), another function, only available in vpTemplateTrackerSSDInverseCompositional and vpTemplateTrackerZNCCInverseCompositional is: + +\code +tracker.setThresholdRMS(1e-6); +\endcode + +By increasing this root mean square threshold value, the algorithm will reduce its number of iterations which should also speed up the tracking phase. This function should be used wisely with the vpTemplateTracker::setIterationMax() function. + +\subsection points_tt How to get the points of the template + +The previous code provided in tutorial-template-tracker.cpp can be modified to get the coordinates of the corners of the triangles that define the zone to track. To this end, as shown in the next lines, before the while loop we first define a reference zone and the corresponding warped zone. Then in the loop, we update the warped zone using the parameters of the warping model that is estimated by the tracker. From the warped zone, we extract all the triangles, and then for each triangles, we get the corners coordinates. + +\code + // Instantiate and get the reference zone + vpTemplateTrackerZone zone_ref = tracker.getZoneRef(); + // Instantiate a warped zone + vpTemplateTrackerZone zone_warped; + + while(!g.end()){ + g.acquire(I); + vpDisplay::display(I); + tracker.track(I); + + tracker.display(I, vpColor::red); + + // Get the estimated parameters + vpColVector p = tracker.getp(); + + // Update the warped zone given the tracker estimated parameters + warp.warpZone(zone_ref, p, zone_warped); + + // Parse all the triangles that describe the zone + for (int i=0; i < zone_warped.getNbTriangle(); i++) { + vpTemplateTrackerTriangle triangle; + // Get a triangle + zone_warped.getTriangle(i, triangle); + std::vector<vpImagePoint> corners; + // Get the 3 triangle corners + triangle.getCorners( corners ); + + // From here, each corner triangle is available in + // corners[0], corners[1] and corners[2] + + // Display a green cross over each corner + for(unsigned int j=0; j<corners.size(); j++) + vpDisplay::displayCross(I, corners[j], 15, vpColor::green, 2); + } + vpDisplay::displayRectangle(I, zone_warped.getBoundingBox(), vpColor::orange); +\endcode + +With the last line, we also sho how to get and display an orange rectangle that corresponds to the bounding box of all the triangles that define the zone. + +The resulting drawings introduced previously are shown in the next image. Here we initialize the tracker with 2 triangles that are not connex. + +\image html img-template-tracker.jpg + + +\section tracking_tt_example More examples + +The templateTracker.cpp source code provided in the example/tracking folder allows to test all the template tracker classes that derive from vpTemplateTracker as well as all the warping classes that derive from vpTemplateTrackerWarp. + +Once build, in a terminal just run: + +\code +./templateTracker -h +\endcode + +to see which are the command lines options. +*/ diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index e75ea1a18d46361baa9ab8588c6db589b08271f5..adfe43d557507eb678d342a11ea73345823d3248 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -64,7 +64,6 @@ SET (SRC_SUBDIRS servo-afma4 servo-afma6 servo-biclops - servo-cycab servo-pioneer servo-ptu46 servo-viper650 @@ -72,13 +71,8 @@ SET (SRC_SUBDIRS tools tracking video - wireframe-simulator + wireframe-simulator ) # Build process propagation in the sub directories SUBDIRS(${SRC_SUBDIRS}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/calibration/CMakeLists.txt b/example/calibration/CMakeLists.txt index b634146a7b0ac195d9636c4b03ff2698682c959d..8ca78d04bfc9502e656300ed33ce9961a725ee35 100644 --- a/example/calibration/CMakeLists.txt +++ b/example/calibration/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4277 2013-06-25 13:09:01Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,63 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set (SOURCE camera_calibration.cpp calibrateTsai.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run these tests automaticaly using the crontab under Unix or the task # manager under windows, we have to add the -d option to turn off the display # of acquired images -ADD_TEST(calibrateTsai calibrateTsai) +add_test(calibrateTsai calibrateTsai) + +# copy the data +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/default-chessboard.cfg" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/default-circles.cfg" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard-01.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard-02.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard-03.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard-04.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard-05.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/circles-01.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/circles-02.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/circles-03.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/circles-04.png" ) +list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/circles-05.png" ) +# Since CMake 3.0.0 policy CMP0026 was introduced to disallow location property on target. +# If CMake 3.0.0 is used, we use $<TARGET_FILE_DIR:tgt> to get the target location +if (CMAKE_VERSION VERSION_GREATER 2.8.12) + foreach(data ${data2copy}) + add_custom_command( + TARGET camera_calibration + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${data}" "$<TARGET_FILE_DIR:camera_calibration" + ) + endforeach() +else() + get_target_property(target_location camera_calibration LOCATION) + get_filename_component(target_location "${target_location}" PATH) + foreach(data ${data2copy}) + add_custom_command( + TARGET camera_calibration + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" + ) + endforeach() +endif() diff --git a/example/calibration/calibrateTsai.cpp b/example/calibration/calibrateTsai.cpp index 9d053053dd61914221b47279185f4adae46b23e0..77b299d2bbc49fec65095099bdd4b24c87d71bf4 100644 --- a/example/calibration/calibrateTsai.cpp +++ b/example/calibration/calibrateTsai.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: calibrateTsai.cpp 4273 2013-06-25 12:33:27Z fspindle $ + * $Id: calibrateTsai.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,85 +58,86 @@ int main() { - // We want to calibrate the hand to eye extrinsic camera parameters from 6 couple of poses: cMo and wMe - const int N = 6; - // Input: six couple of poses used as input in the calibration proces - std::vector<vpHomogeneousMatrix> cMo(N) ; // eye (camera) to object transformation. The object frame is attached to the calibrartion grid - std::vector<vpHomogeneousMatrix> wMe(N) ; // world to hand (end-effector) transformation - // Output: Result of the calibration - vpHomogeneousMatrix eMc; // hand (end-effector) to eye (camera) transformation + try { + // We want to calibrate the hand to eye extrinsic camera parameters from 6 couple of poses: cMo and wMe + const unsigned int N = 6; + // Input: six couple of poses used as input in the calibration proces + std::vector<vpHomogeneousMatrix> cMo(N) ; // eye (camera) to object transformation. The object frame is attached to the calibrartion grid + std::vector<vpHomogeneousMatrix> wMe(N) ; // world to hand (end-effector) transformation + // Output: Result of the calibration + vpHomogeneousMatrix eMc; // hand (end-effector) to eye (camera) transformation - // Initialize an eMc transformation used to produce the simulated input transformations cMo and wMe - vpTranslationVector etc(0.1, 0.2, 0.3); - vpThetaUVector erc; - erc[0] = vpMath::rad(10); // 10 deg - erc[1] = vpMath::rad(-10); // -10 deg - erc[2] = vpMath::rad(25); // 25 deg + // Initialize an eMc transformation used to produce the simulated input transformations cMo and wMe + vpTranslationVector etc(0.1, 0.2, 0.3); + vpThetaUVector erc; + erc[0] = vpMath::rad(10); // 10 deg + erc[1] = vpMath::rad(-10); // -10 deg + erc[2] = vpMath::rad(25); // 25 deg - eMc.buildFrom(etc, erc); - std::cout << "Simulated hand to eye transformation: eMc " << std::endl ; - std::cout << eMc << std::endl ; - std::cout << "Theta U rotation: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1]) << " " << vpMath::deg(erc[2]) << std::endl; + eMc.buildFrom(etc, erc); + std::cout << "Simulated hand to eye transformation: eMc " << std::endl ; + std::cout << eMc << std::endl ; + std::cout << "Theta U rotation: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1]) << " " << vpMath::deg(erc[2]) << std::endl; - vpColVector v_c(6) ; // camera velocity used to produce 6 simulated poses - for (int i=0 ; i < N ; i++) - { - v_c = 0 ; - if (i==0) { - // Initialize first poses - cMo[0].buildFrom(0, 0, 0.5, 0, 0, 0); // z=0.5 m - wMe[0].buildFrom(0, 0, 0, 0, 0, 0); // Id - } - else if (i==1) - v_c[3] = M_PI/8 ; - else if (i==2) - v_c[4] = M_PI/8 ; - else if (i==3) - v_c[5] = M_PI/10 ; - else if (i==4) - v_c[0] = 0.5 ; - else if (i==5) - v_c[1] = 0.8 ; + vpColVector v_c(6) ; // camera velocity used to produce 6 simulated poses + for (unsigned int i=0 ; i < N ; i++) + { + v_c = 0 ; + if (i==0) { + // Initialize first poses + cMo[0].buildFrom(0, 0, 0.5, 0, 0, 0); // z=0.5 m + wMe[0].buildFrom(0, 0, 0, 0, 0, 0); // Id + } + else if (i==1) + v_c[3] = M_PI/8 ; + else if (i==2) + v_c[4] = M_PI/8 ; + else if (i==3) + v_c[5] = M_PI/10 ; + else if (i==4) + v_c[0] = 0.5 ; + else if (i==5) + v_c[1] = 0.8 ; - vpHomogeneousMatrix cMc; // camera displacement - cMc = vpExponentialMap::direct(v_c) ; // Compute the camera displacement due to the velocity applied to the camera - if (i > 0) { - // From the camera displacement cMc, compute the wMe and cMo matrices - cMo[i] = cMc.inverse() * cMo[i-1]; - wMe[i] = wMe[i-1] * eMc * cMc * eMc.inverse(); + vpHomogeneousMatrix cMc; // camera displacement + cMc = vpExponentialMap::direct(v_c) ; // Compute the camera displacement due to the velocity applied to the camera + if (i > 0) { + // From the camera displacement cMc, compute the wMe and cMo matrices + cMo[i] = cMc.inverse() * cMo[i-1]; + wMe[i] = wMe[i-1] * eMc * cMc * eMc.inverse(); + } } - } - if (0) { - for (int i=0 ; i < N ; i++) { - vpHomogeneousMatrix wMo; - wMo = wMe[i] * eMc * cMo[i]; - std::cout << std::endl << "wMo[" << i << "] " << std::endl ; - std::cout << wMo << std::endl ; - std::cout << "cMo[" << i << "] " << std::endl ; - std::cout << cMo[i] << std::endl ; - std::cout << "wMe[" << i << "] " << std::endl ; - std::cout << wMe[i] << std::endl ; + if (0) { + for (unsigned int i=0 ; i < N ; i++) { + vpHomogeneousMatrix wMo; + wMo = wMe[i] * eMc * cMo[i]; + std::cout << std::endl << "wMo[" << i << "] " << std::endl ; + std::cout << wMo << std::endl ; + std::cout << "cMo[" << i << "] " << std::endl ; + std::cout << cMo[i] << std::endl ; + std::cout << "wMe[" << i << "] " << std::endl ; + std::cout << wMe[i] << std::endl ; + } } - } - // Reset the eMc matrix to eye - eMc.eye(); + // Reset the eMc matrix to eye + eMc.eye(); - // Compute the eMc hand to eye transformation from six poses - // - cMo[6]: camera to object poses as six homogeneous transformations - // - wMe[6]: world to hand (end-effector) poses as six homogeneous transformations - vpCalibration::calibrationTsai(cMo, wMe, eMc) ; + // Compute the eMc hand to eye transformation from six poses + // - cMo[6]: camera to object poses as six homogeneous transformations + // - wMe[6]: world to hand (end-effector) poses as six homogeneous transformations + vpCalibration::calibrationTsai(cMo, wMe, eMc) ; - std::cout << std::endl << "Output: hand to eye calibration result: eMc estimated " << std::endl ; - std::cout << eMc << std::endl ; - eMc.extract(erc); - std::cout << "Theta U rotation: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1]) << " " << vpMath::deg(erc[2]) << std::endl; - return 0 ; + std::cout << std::endl << "Output: hand to eye calibration result: eMc estimated " << std::endl ; + std::cout << eMc << std::endl ; + eMc.extract(erc); + std::cout << "Theta U rotation: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1]) << " " << vpMath::deg(erc[2]) << std::endl; + return 0 ; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1 ; + } } -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/example/calibration/camera_calibration.cpp b/example/calibration/camera_calibration.cpp index 701a77e594585fca7db0aa1b3fa98228c37cf48c..015460716dc67dcfe70c70486641e5cec85b3217 100644 --- a/example/calibration/camera_calibration.cpp +++ b/example/calibration/camera_calibration.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: camera_calibration.cpp 4286 2013-06-26 20:28:16Z fspindle $ + * $Id: camera_calibration.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,10 +61,21 @@ #include <visp/vpVideoReader.h> #include <visp/vpXmlParserCamera.h> +#ifndef DOXYGEN_SHOULD_SKIP_THIS + class Settings { public: - Settings() : goodInput(false) {} + Settings() + : boardSize(), calibrationPattern(UNDEFINED), squareSize(0.), input(), tempo(0.), goodInput(false), patternToUse() + { + boardSize = cv::Size(0, 0); + calibrationPattern = UNDEFINED; + squareSize = 0.025f; + goodInput = false; + tempo = 1.f; + + } enum Pattern { UNDEFINED, CHESSBOARD, CIRCLES_GRID}; bool read(const std::string &filename) // Read the parameters @@ -77,12 +88,14 @@ public: vpIoTools::readConfigVar("Square_Size:", squareSize); vpIoTools::readConfigVar("Calibrate_Pattern:", patternToUse); vpIoTools::readConfigVar("Input:", input); + vpIoTools::readConfigVar("Tempo:", tempo); std::cout << "grid width : " << boardSize.width << std::endl; std::cout << "grid height: " << boardSize.height << std::endl; std::cout << "square size: " << squareSize << std::endl; std::cout << "pattern : " << patternToUse << std::endl; std::cout << "input seq : " << input << std::endl; + std::cout << "tempo : " << tempo << std::endl; interprate(); return true; } @@ -102,8 +115,8 @@ public: goodInput = false; calibrationPattern = UNDEFINED; - if (!patternToUse.compare("CHESSBOARD")) calibrationPattern = CHESSBOARD; - if (!patternToUse.compare("CIRCLES_GRID")) calibrationPattern = CIRCLES_GRID; + if (patternToUse.compare("CHESSBOARD") == 0) calibrationPattern = CHESSBOARD; + else if (patternToUse.compare("CIRCLES_GRID") == 0) calibrationPattern = CIRCLES_GRID; if (calibrationPattern == UNDEFINED) { std::cerr << " Inexistent camera calibration mode: " << patternToUse << std::endl; goodInput = false; @@ -115,172 +128,215 @@ public: Pattern calibrationPattern;// One of the Chessboard, circles, or asymmetric circle pattern float squareSize; // The size of a square in your defined unit (point, millimeter,etc). std::string input; // The input image sequence + float tempo; // Tempo in seconds between two images. If > 10 wait a click to continue bool goodInput; private: std::string patternToUse; }; +#endif int main(int argc, const char ** argv) { - std::string outputFileName = "camera.xml"; - - Settings s; - const std::string inputSettingsFile = argc > 1 ? argv[1] : "default.cfg"; - if (! s.read(inputSettingsFile) ) { - std::cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << std::endl; - std::cout << std::endl << "Usage: " << argv[0] << " <configuration file>.cfg" << std::endl; - return -1; - } + try { + std::string outputFileName = "camera.xml"; + + Settings s; + const std::string inputSettingsFile = argc > 1 ? argv[1] : "default.cfg"; + if (! s.read(inputSettingsFile) ) { + std::cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << std::endl; + std::cout << std::endl << "Usage: " << argv[0] << " <configuration file>.cfg" << std::endl; + return -1; + } - if (! s.goodInput) - { - std::cout << "Invalid input detected. Application stopping. " << std::endl; - return -1; - } + if (! s.goodInput) + { + std::cout << "Invalid input detected. Application stopping. " << std::endl; + return -1; + } - // Start the calibration code - vpImage<unsigned char> I; - vpVideoReader reader; - reader.setFileName(s.input); - reader.open(I); + // Start the calibration code + vpImage<unsigned char> I; + vpVideoReader reader; + reader.setFileName(s.input); + reader.open(I); #ifdef VISP_HAVE_X11 - vpDisplayX d(I); + vpDisplayX d(I); #elif defined VISP_HAVE_GDI - vpDisplayGDI d(I); + vpDisplayGDI d(I); #elif defined VISP_HAVE_GTK - vpDisplayGTK d(I); + vpDisplayGTK d(I); #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV d(I); + vpDisplayOpenCV d(I); #endif - std::vector<vpPoint> model; - std::vector<vpCalibration> calibrator; - - for (int i=0; i< s.boardSize.height; i++) { - for (int j=0; j< s.boardSize.width; j++) { - vpPoint P; - P.setWorldCoordinates(j*s.squareSize, i*s.squareSize, 0); - model.push_back(P); - } - } - - long frame_index; - while(! reader.end()) { - frame_index = reader.getFrameIndex(); - reader.acquire(I); - vpDisplay::display(I); - - cv::Mat cvI; - std::vector<cv::Point2f> pointBuf; - vpImageConvert::convert(I, cvI); + std::vector<vpPoint> model; + std::vector<vpCalibration> calibrator; - bool found; - switch( s.calibrationPattern ) // Find feature points on the input format - { - case Settings::CHESSBOARD: - found = findChessboardCorners( cvI, s.boardSize, pointBuf, - CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE); - break; - case Settings::CIRCLES_GRID: - found = findCirclesGrid( cvI, s.boardSize, pointBuf, cv::CALIB_CB_SYMMETRIC_GRID ); - break; - default: - break; + for (int i=0; i< s.boardSize.height; i++) { + for (int j=0; j< s.boardSize.width; j++) { + vpPoint P; + P.setWorldCoordinates(j*s.squareSize, i*s.squareSize, 0); + model.push_back(P); + } } - std::cout << "frame: " << frame_index << " status: " << found << std::endl; - - if ( found) // If done with success, - { - std::vector<vpImagePoint> data; - - if (s.calibrationPattern == Settings::CHESSBOARD) { - // improve the found corners' coordinate accuracy for chessboard - cornerSubPix( cvI, pointBuf, cv::Size(11,11), - cv::Size(-1,-1), cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1 )); + long frame_index; + while(! reader.end()) { + frame_index = reader.getFrameIndex(); + reader.acquire(I); + vpDisplay::display(I); + + cv::Mat cvI; + std::vector<cv::Point2f> pointBuf; + vpImageConvert::convert(I, cvI); + + bool found = false; + switch( s.calibrationPattern ) // Find feature points on the input format + { + case Settings::CHESSBOARD: + //std::cout << "Use chessboard " << std::endl; + found = findChessboardCorners( cvI, s.boardSize, pointBuf, +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE); +#else + CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE); +#endif + break; + case Settings::CIRCLES_GRID: + //std::cout << "Use circle grid " << std::endl; + found = findCirclesGrid( cvI, s.boardSize, pointBuf, cv::CALIB_CB_SYMMETRIC_GRID ); + break; + case Settings::UNDEFINED: + default: + std::cout << "Unkown calibration grid " << std::endl; + break; } - char title[20]; sprintf(title, "image %ld", frame_index); - vpDisplay::setTitle(I, title); - for (unsigned int i=0; i < pointBuf.size(); i++) { - vpImagePoint ip(pointBuf[i].y, pointBuf[i].x); - data.push_back(ip); - vpDisplay::displayCross(I, ip, 10, vpColor::red); + std::cout << "frame: " << frame_index << ", status: " << found; + if (!found) + std::cout << ", image rejected" << std::endl; + else + std::cout << ", image used as input data" << std::endl; + + if ( found) // If done with success, + { + std::vector<vpImagePoint> data; + + if (s.calibrationPattern == Settings::CHESSBOARD) { + // improve the found corners' coordinate accuracy for chessboard + cornerSubPix( cvI, pointBuf, cv::Size(11,11), + cv::Size(-1,-1), +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::TermCriteria( cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 30, 0.1 )); +#else + cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1 )); +#endif + } + char title[20]; sprintf(title, "image %ld", frame_index); + vpDisplay::setTitle(I, title); + for (unsigned int i=0; i < pointBuf.size(); i++) { + vpImagePoint ip(pointBuf[i].y, pointBuf[i].x); + data.push_back(ip); + vpDisplay::displayCross(I, ip, 10, vpColor::red); + } + + // Calibration on a single mono image + vpCalibration calib; + calib.setLambda(0.5); + calib.clearPoint(); + for (unsigned int i=0; i<model.size(); i++) { + calib.addPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ(), data[i]); + } + vpHomogeneousMatrix cMo; + vpCameraParameters cam; + + // Set (u0,v0) in the middle of the image + double px = cam.get_px(); + double py = cam.get_px(); + double u0 = I.getWidth()/2; + double v0 = I.getHeight()/2; + cam.initPersProjWithoutDistortion(px, py, u0, v0); + + if (calib.computeCalibration(vpCalibration::CALIB_VIRTUAL_VS, cMo, cam, false) == 0) { + //std::cout << "camera parameters: " << cam << std::endl; + calibrator.push_back(calib); + } } - // Calibration on a single mono image - vpCalibration calib; - calib.setLambda(0.5); - calib.clearPoint(); - for (unsigned int i=0; i<model.size(); i++) { - calib.addPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ(), data[i]); + if (found) + vpDisplay::displayText(I, 15, 15, "Image processing succeed", vpColor::green); + else + vpDisplay::displayText(I, 15, 15, "Image processing fails", vpColor::green); + + if (s.tempo > 10.f) { + vpDisplay::displayText(I, 35, 15, "A click to process the next image", vpColor::green); + vpDisplay::flush(I); + vpDisplay::getClick(I); } - vpHomogeneousMatrix cMo; - vpCameraParameters cam; - - // Set (u0,v0) in the middle of the image - double px = cam.get_px(); - double py = cam.get_px(); - double u0 = I.getWidth()/2; - double v0 = I.getHeight()/2; - cam.initPersProjWithoutDistortion(px, py, u0, v0); - - if (calib.computeCalibration(vpCalibration::CALIB_VIRTUAL_VS, cMo, cam, false) == 0) { - //std::cout << "camera parameters: " << cam << std::endl; - calibrator.push_back(calib); + else { + vpDisplay::flush(I); + vpTime::wait(s.tempo*1000); } } - vpDisplay::flush(I); - //vpDisplay::getClick(I); - } - - // Now we consider the multi image calibration - // Calibrate by a non linear method based on virtual visual servoing - if (calibrator.empty()) { - std::cerr << "Unable to calibrate. Image processing failed !" << std::endl; - return 0; - } + // Now we consider the multi image calibration + // Calibrate by a non linear method based on virtual visual servoing + if (calibrator.empty()) { + std::cerr << "Unable to calibrate. Image processing failed !" << std::endl; + return 0; + } - std::cout << "\nCalibration without distorsion in progress on " << calibrator.size() << " images..." << std::endl; - vpCameraParameters cam; - double error; - if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS, calibrator, cam, error, false) == 0) { - std::cout << cam << std::endl; - std::cout << "Global reprojection error: " << error << std::endl; + std::cout << "\nCalibration without distorsion in progress on " << calibrator.size() << " images..." << std::endl; + vpCameraParameters cam; + double error; + if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS, calibrator, cam, error, false) == 0) { + std::cout << cam << std::endl; + std::cout << "Global reprojection error: " << error << std::endl; #ifdef VISP_HAVE_XML2 - vpXmlParserCamera xml; + vpXmlParserCamera xml; - if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK) - std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl; - else { - std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl; - std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl; - } + if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK) + std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl; + else { + std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl; + std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl; + } #endif - } - else - std::cout << "Calibration without distortion failed." << std::endl; + } + else + std::cout << "Calibration without distortion failed." << std::endl; + + std::cout << "\nCalibration with distorsion in progress on " << calibrator.size() << " images..." << std::endl; + if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS_DIST, calibrator, cam, error, false) == 0) { + std::cout << cam << std::endl; + std::cout << "Global reprojection error: " << error << std::endl; - std::cout << "\nCalibration with distorsion in progress on " << calibrator.size() << " images..." << std::endl; - if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS_DIST, calibrator, cam, error, false) == 0) { - std::cout << cam << std::endl; - std::cout << "Global reprojection error: " << error << std::endl; #ifdef VISP_HAVE_XML2 - vpXmlParserCamera xml; + vpXmlParserCamera xml; - if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK) - std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl; - else { - std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl; - std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl; - } + if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK) + std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl; + else { + std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl; + std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl; + } #endif + std::cout << std::endl; + for (unsigned int i=0; i<calibrator.size(); i++) + std::cout << "Estimated pose on input data " << i << ": " << vpPoseVector(calibrator[i].cMo_dist).t() << std::endl; + + } + else + std::cout << "Calibration with distortion failed." << std::endl; + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - else - std::cout << "Calibration with distortion failed." << std::endl; } #else int main() diff --git a/example/calibration/chessboard-01.png b/example/calibration/chessboard-01.png new file mode 100644 index 0000000000000000000000000000000000000000..4e92fd62c1a9c709e8da7d7e1c3c034a38db5612 Binary files /dev/null and b/example/calibration/chessboard-01.png differ diff --git a/example/calibration/chessboard-02.png b/example/calibration/chessboard-02.png new file mode 100644 index 0000000000000000000000000000000000000000..ab56b8e5dfb10c5c23b00ef7c5fd346b6a2dd2af Binary files /dev/null and b/example/calibration/chessboard-02.png differ diff --git a/example/calibration/chessboard-03.png b/example/calibration/chessboard-03.png new file mode 100644 index 0000000000000000000000000000000000000000..089bc3290052fb1ebcaa56b25e72d40459005278 Binary files /dev/null and b/example/calibration/chessboard-03.png differ diff --git a/example/calibration/chessboard-04.png b/example/calibration/chessboard-04.png new file mode 100644 index 0000000000000000000000000000000000000000..048515907dfc902e693bd1fd8afe9b6887ba0846 Binary files /dev/null and b/example/calibration/chessboard-04.png differ diff --git a/example/calibration/chessboard-05.png b/example/calibration/chessboard-05.png new file mode 100644 index 0000000000000000000000000000000000000000..efaa34ab37de9c43ea872990bed6b3cb533b06ff Binary files /dev/null and b/example/calibration/chessboard-05.png differ diff --git a/example/calibration/circles-01.png b/example/calibration/circles-01.png new file mode 100644 index 0000000000000000000000000000000000000000..a0340a054a6b07f730037f2e36c1dcf2b042e7ab Binary files /dev/null and b/example/calibration/circles-01.png differ diff --git a/example/calibration/circles-02.png b/example/calibration/circles-02.png new file mode 100644 index 0000000000000000000000000000000000000000..44504e36409d294546396a8958e0bea55bd6f53e Binary files /dev/null and b/example/calibration/circles-02.png differ diff --git a/example/calibration/circles-03.png b/example/calibration/circles-03.png new file mode 100644 index 0000000000000000000000000000000000000000..3a01fadaa4bb8c63f644c12b1b5a6fdf2c832c3b Binary files /dev/null and b/example/calibration/circles-03.png differ diff --git a/example/calibration/circles-04.png b/example/calibration/circles-04.png new file mode 100644 index 0000000000000000000000000000000000000000..48d0993e0da9e9822635d6c30ecbf82be79accdb Binary files /dev/null and b/example/calibration/circles-04.png differ diff --git a/example/calibration/circles-05.png b/example/calibration/circles-05.png new file mode 100644 index 0000000000000000000000000000000000000000..05fb8dcc42c36fa7cddf263b4263ca18cd6b3d75 Binary files /dev/null and b/example/calibration/circles-05.png differ diff --git a/example/calibration/default-chessboard.cfg b/example/calibration/default-chessboard.cfg index 4b040be211ac448a51ba75a592e59a51dec12556..9de6af08c8f737186d01441736b351a73eb928c2 100644 --- a/example/calibration/default-chessboard.cfg +++ b/example/calibration/default-chessboard.cfg @@ -11,3 +11,6 @@ Calibrate_Pattern: CHESSBOARD # The input image sequence to use for calibration Input: chessboard-%02d.png + +# Tempo in seconds between two images. If > 10 wait a click to continue +Tempo: 1 diff --git a/example/calibration/default-circles.cfg b/example/calibration/default-circles.cfg index 2c540baec36078d3ca6474da7cc7cfd92223b09e..bb68c64872a9d90a5096f36f2066e4ff10c759a6 100644 --- a/example/calibration/default-circles.cfg +++ b/example/calibration/default-circles.cfg @@ -10,4 +10,7 @@ Square_Size: 0.034 Calibrate_Pattern: CIRCLES_GRID # The input image sequence to use for calibration -Input: circle-grid-%02d.pgm +Input: circles-%02d.png + +# Tempo in seconds between two images. If > 10 wait a click to continue +Tempo: 1 diff --git a/example/coin-simulator/CMakeLists.txt b/example/coin-simulator/CMakeLists.txt index 3e619cc75e673fbe84ac0abdc7d1ee9d7f585153..21a6189669b84f0dd0bd20d57eb83c474d74c838 100644 --- a/example/coin-simulator/CMakeLists.txt +++ b/example/coin-simulator/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,26 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set (SOURCE simulateFourPoints2DCartesianCamVelocity.cpp simulateFourPoints2DPolarCamVelocity.cpp simulateCircle2DCamVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary} ${OPTION_TO_DESACTIVE_DISPLAY}) -ENDFOREACH(source) + add_test(${binary} ${binary} ${OPTION_TO_DESACTIVE_DISPLAY}) -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/coin-simulator/simulateCircle2DCamVelocity.cpp b/example/coin-simulator/simulateCircle2DCamVelocity.cpp index 728c2e945b1aac5cdd5eca1025b91f1ead820f40..b5b3c9ee12956bb6327872c3b135ee8eaf18df93 100644 --- a/example/coin-simulator/simulateCircle2DCamVelocity.cpp +++ b/example/coin-simulator/simulateCircle2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: simulateCircle2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: simulateCircle2DCamVelocity.cpp 5263 2015-02-04 13:43:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,9 +63,6 @@ #include <visp/vpCameraParameters.h> #include <visp/vpTime.h> #include <visp/vpSimulator.h> - - - #include <visp/vpMath.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpFeatureEllipse.h> @@ -76,11 +73,9 @@ #include <visp/vpParseArgv.h> #include <visp/vpIoTools.h> - #define GETOPTARGS "cdi:h" #define SAVE 0 - /*! Print the program options. @@ -189,108 +184,81 @@ void *mainLoop (void *_simu) unsigned int pos = 2 ; while (pos!=0) { - - vpServo task ; vpRobotCamera robot ; float sampling_time = 0.040f; // Sampling period in second robot.setSamplingTime(sampling_time); + robot.setMaxTranslationVelocity(4.); - /* std::cout << std::endl ; - std::cout << "-----------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a circle " << std::endl ; - std::cout << "-----------------------" << std::endl ; - std::cout << std::endl ; -*/ - - vpTRACE("sets the initial camera location " ) ; - - + // Sets the initial camera location robot.setPosition(cMo) ; simu->setCameraPosition(cMo) ; - if (pos==1) cMod[2][3] = 0.32 ; - vpTRACE("sets the circle coordinates in the world frame " ) ; + // Sets the circle coordinates in the world frame vpCircle circle ; circle.setWorldCoordinates(0,0,1,0,0,0,0.1) ; - vpTRACE("sets the desired position of the visual feature ") ; + // Sets the desired position of the visual feature vpFeatureEllipse pd ; circle.track(cMod) ; vpFeatureBuilder::create(pd,circle) ; - vpTRACE("project : computes the circle coordinates in the camera frame and its 2D coordinates" ) ; - - vpTRACE("sets the current position of the visual feature ") ; + // Project : computes the circle coordinates in the camera frame and its 2D coordinates + // Sets the current position of the visual feature vpFeatureEllipse p ; circle.track(cMo) ; vpFeatureBuilder::create(p,circle) ; - vpTRACE("define the task") ; - vpTRACE("\t we want an eye-in-hand control law") ; - vpTRACE("\t robot is controlled in the camera frame") ; + // Define the task + // We want an eye-in-hand control law + // Robot is controlled in the camera frame task.setServo(vpServo::EYEINHAND_CAMERA) ; task.setInteractionMatrixType(vpServo::CURRENT) ; - vpTRACE("\t we want to see a circle on a circle..") ; + // We want to see a circle on a circle std::cout << std::endl ; task.addFeature(p,pd) ; - vpTRACE("\t set the gain") ; - + // Set the gain task.setLambda(1.0) ; - // if (pos==2) - // task.setLambda(0.0251) ; - // else - // task.setLambda(0.0251) ; - - vpTRACE("Display task information " ) ; + // Display task information task.print() ; vpTime::wait(1000); // Sleep 1s - std::cout << "\nEnter a character to continue... " <<std::endl ; - { int a ; std::cin >> a ; } - - unsigned int iter=0 ; - vpTRACE("\t loop") ; + // Visual servoing loop unsigned int itermax ; if (pos==2) itermax = 75 ; else itermax = 100 ; - char name[FILENAME_MAX] ; - while(iter++<itermax) + while(iter++ < itermax) { double t = vpTime::measureTimeMs(); - std::cout << "---------------------------------------------" - << iter <<std::endl ; - vpColVector v ; - if (iter==1) vpTRACE("\t\t get the robot position ") ; + if (iter==1) std::cout << "get the robot position" << std::endl; robot.getPosition(cMo) ; - if (iter==1) vpTRACE("\t\t new circle position ") ; + if (iter==1) std::cout << "new circle position" << std::endl; //retrieve x,y and Z of the vpCircle structure circle.track(cMo) ; vpFeatureBuilder::create(p,circle); - if (iter==1) vpTRACE("\t\t compute the control law ") ; - v = task.computeControlLaw() ; - // vpTRACE("computeControlLaw" ) ; - std::cout << "Task rank: " << task.getTaskRank() <<std::endl ; - if (iter==1) - vpTRACE("\t\t send the camera velocity to the controller ") ; + if (iter==1) std::cout << "compute the control law" << std::endl; + vpColVector v = task.computeControlLaw() ; + if (iter==1) { + std::cout << "Task rank: " << task.getTaskRank() <<std::endl ; + std::cout << "send the camera velocity to the controller" << std::endl; + } robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; simu->setCameraPosition(cMo) ; if(SAVE==1) { + char name[FILENAME_MAX] ; sprintf(name,"/tmp/image.%04d.external.png",it) ; std::cout << "Save " << name << std::endl ; simu->write(name) ; @@ -299,99 +267,101 @@ void *mainLoop (void *_simu) simu->write(name) ; it++ ; } - // vpTRACE("\t\t || s - s* || ") ; + // std::cout << "\t\t || s - s* || " // std::cout << ( task.getError() ).sumSquare() <<std::endl ; ; vpTime::wait(t, sampling_time * 1000); // Wait 40 ms } pos-- ; task.kill(); - } - simu->closeMainApplication() ; void *a=NULL ; return a ; - // return (void *); } int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string filename; - std::string username; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string filename; + std::string username; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_INPUT_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_INPUT_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - vpCameraParameters cam ; - vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; + vpCameraParameters cam ; + vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; - if (opt_display) { + if (opt_display) { - vpSimulator simu ; - simu.initInternalViewer(300, 300) ; - simu.initExternalViewer(300, 300) ; + vpSimulator simu ; + simu.initInternalViewer(300, 300) ; + simu.initExternalViewer(300, 300) ; - vpTime::wait(1000) ; - simu.setZoomFactor(1.0f) ; - simu.addAbsoluteFrame() ; + vpTime::wait(1000) ; + simu.setZoomFactor(1.0f) ; + simu.addAbsoluteFrame() ; - // Load the cad model - filename = ipath + vpIoTools::path("/ViSP-images/iv/circle.iv"); - simu.load(filename.c_str(),fMo) ; + // Load the cad model + filename = vpIoTools::createFilePath(ipath, "ViSP-images/iv/circle.iv"); + simu.load(filename.c_str(),fMo) ; - simu.setInternalCameraParameters(cam) ; + simu.setInternalCameraParameters(cam) ; - simu.initApplication(&mainLoop) ; - simu.mainLoop() ; + simu.initApplication(&mainLoop) ; + simu.mainLoop() ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/example/coin-simulator/simulateFourPoints2DCartesianCamVelocity.cpp b/example/coin-simulator/simulateFourPoints2DCartesianCamVelocity.cpp index 729a1844d0db166376c9754292231c1b36d9e3b0..b1be43c844df1d4d0c4311cac402ff762f158681 100644 --- a/example/coin-simulator/simulateFourPoints2DCartesianCamVelocity.cpp +++ b/example/coin-simulator/simulateFourPoints2DCartesianCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: simulateFourPoints2DCartesianCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: simulateFourPoints2DCartesianCamVelocity.cpp 5263 2015-02-04 13:43:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,8 +62,6 @@ #include <visp/vpCameraParameters.h> #include <visp/vpTime.h> #include <visp/vpSimulator.h> - - #include <visp/vpMath.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpFeaturePoint.h> @@ -163,231 +161,213 @@ void *mainLoop (void *_simu) vpSimulator *simu = (vpSimulator *)_simu ; simu->initMainApplication() ; - for ( ; ; ) { - int i ; - - vpServo task ; - vpRobotCamera robot ; + vpServo task ; + vpRobotCamera robot ; - float sampling_time = 0.040f; // Sampling period in second - robot.setSamplingTime(sampling_time); + float sampling_time = 0.040f; // Sampling period in second + robot.setSamplingTime(sampling_time); - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocities are computed" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + // Sets the initial camera location + vpPoseVector vcMo ; - vpTRACE("sets the initial camera location " ) ; - vpPoseVector vcMo ; - - vcMo[0] = 0.3 ; - vcMo[1] = 0.2 ; - vcMo[2] = 3 ; - vcMo[3] = 0 ; - vcMo[4] = vpMath::rad(0) ; - vcMo[5] = vpMath::rad(40) ; - - vpHomogeneousMatrix cMo(vcMo) ; - robot.setPosition(cMo) ; - simu->setCameraPosition(cMo) ; + vcMo[0] = 0.3 ; + vcMo[1] = 0.2 ; + vcMo[2] = 3 ; + vcMo[3] = 0 ; + vcMo[4] = vpMath::rad(0) ; + vcMo[5] = vpMath::rad(40) ; - simu->getCameraPosition(cMo) ; - robot.setPosition(cMo) ; - - vpCameraParameters cam ; - - vpTRACE("sets the point coordinates in the world frame " ) ; - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1,0) ; - point[1].setWorldCoordinates(0.1,-0.1,0) ; - point[2].setWorldCoordinates(0.1,0.1,0) ; - point[3].setWorldCoordinates(-0.1,0.1,0) ; - - vpTRACE("project : computes the point coordinates in the camera frame and its 2D coordinates" ) ; - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; + vpHomogeneousMatrix cMo(vcMo) ; + robot.setPosition(cMo) ; + simu->setCameraPosition(cMo) ; - vpTRACE("sets the desired position of the point ") ; - vpFeaturePoint p[4] ; - for (i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure + simu->getCameraPosition(cMo) ; + robot.setPosition(cMo) ; + robot.setMaxTranslationVelocity(4.); + vpCameraParameters cam ; - vpTRACE("sets the desired position of the point ") ; - vpFeaturePoint pd[4] ; - - pd[0].buildFrom(-0.1,-0.1,1) ; - pd[1].buildFrom(0.1,-0.1,1) ; - pd[2].buildFrom(0.1,0.1,1) ; - pd[3].buildFrom(-0.1,0.1,1) ; - - vpTRACE("define the task") ; - vpTRACE("\t we want an eye-in-hand control law") ; - vpTRACE("\t articular velocity are computed") ; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::CURRENT) ; - - - vpTRACE("Set the position of the camera in the end-effector frame ") ; - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - vpTRACE("Set the Jacobian (expressed in the end-effector frame)") ; - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + // Sets the point coordinates in the world frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1,0) ; + point[1].setWorldCoordinates(0.1,-0.1,0) ; + point[2].setWorldCoordinates(0.1,0.1,0) ; + point[3].setWorldCoordinates(-0.1,0.1,0) ; - vpTRACE("\t we want to see a point on a point..") ; - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; + // Project : computes the point coordinates in the camera frame and its 2D coordinates + for (int i = 0 ; i < 4 ; i++) + point[i].track(cMo) ; - vpTRACE("\t set the gain") ; - task.setLambda(1.0) ; + // Sets the desired position of the point + vpFeaturePoint p[4] ; + for (int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure + // Sets the desired position of the point + vpFeaturePoint pd[4] ; - vpTRACE("Display task information " ) ; - task.print() ; + pd[0].buildFrom(-0.1,-0.1,1) ; + pd[1].buildFrom(0.1,-0.1,1) ; + pd[2].buildFrom(0.1,0.1,1) ; + pd[3].buildFrom(-0.1,0.1,1) ; - vpTime::wait(1000); // Sleep 1s - std::cout << "\nEnter a character to continue or CTRL-C to quit... " - << std::endl ; - { char a ; std::cin >> a ; } + // Define the task + // We want an eye-in-hand control law + // Articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::CURRENT) ; + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; - char name[FILENAME_MAX]; - unsigned int iter=0 ; - vpTRACE("\t loop") ; - while(iter++ < 100) { - double t = vpTime::measureTimeMs(); + // Set the Jacobian (expressed in the end-effector frame) + vpMatrix eJe ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; - vpColVector v ; + // We want to see a point on a point + for (int i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + // Set the gain + task.setLambda(1.0) ; - robot.getPosition(cMo) ; - for (i = 0 ; i < 4 ; i++) - { - point[i].track(cMo) ; - vpFeatureBuilder::create(p[i],point[i]) ; - } + std::cout << "Display task information" << std::endl; + task.print() ; - v = task.computeControlLaw() ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + vpTime::wait(1000); // Sleep 1s to ensure that all the thread are initialized - //vpTime::wait(100) ; + unsigned int iter=0 ; + // visual servo loop + while(iter++ < 100) { + double t = vpTime::measureTimeMs(); + vpColVector v ; - simu->setCameraPosition(cMo) ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + robot.getPosition(cMo) ; + for (int i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + vpFeatureBuilder::create(p[i],point[i]) ; + } - if(SAVE==1) - { - sprintf(name,"/tmp/image.%04d.external.png",iter) ; - std::cout << name << std::endl ; - simu->write(name) ; - sprintf(name,"/tmp/image.%04d.internal.png",iter) ; - simu->write(name) ; - } + v = task.computeControlLaw() ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + simu->setCameraPosition(cMo) ; + if(SAVE==1) + { + char name[FILENAME_MAX]; + sprintf(name,"/tmp/image.%04d.external.png",iter) ; + std::cout << name << std::endl ; + simu->write(name) ; + sprintf(name,"/tmp/image.%04d.internal.png",iter) ; + simu->write(name) ; } - vpTRACE("Display task information " ) ; - task.print() ; - task.kill() ; - std::cout << "\nEnter a character to continue..." <<std::endl ; - { char a ; std::cin >> a ; } + + vpTime::wait(t, sampling_time * 1000); // Wait 40 ms } + std::cout << "\nDisplay task information" << std::endl; + task.print() ; + task.kill() ; simu->closeMainApplication() ; - void *a=NULL ; return a ; - // return (void *); } - -int -main(int argc, const char ** argv) +int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string filename; - std::string username; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string filename; + std::string username; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_display) == false) { + exit (-1); } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - vpCameraParameters cam ; - vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } + vpCameraParameters cam ; + vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; - if (opt_display) { - vpSimulator simu ; - simu.initInternalViewer(300, 300) ; - simu.initExternalViewer(300, 300) ; + if (opt_display) { + vpSimulator simu ; + simu.initInternalViewer(300, 300) ; + simu.initExternalViewer(300, 300) ; - vpTime::wait(1000) ; - simu.setZoomFactor(1.0f) ; + vpTime::wait(1000) ; + simu.setZoomFactor(1.0f) ; - // Load the cad model - filename = ipath + vpIoTools::path("/ViSP-images/iv/4points.iv"); - simu.load(filename.c_str()) ; + // Load the cad model + filename = vpIoTools::createFilePath(ipath, "ViSP-images/iv/4points.iv"); + simu.load(filename.c_str()) ; - simu.setInternalCameraParameters(cam) ; - simu.setExternalCameraParameters(cam) ; - simu.initApplication(&mainLoop) ; + simu.setInternalCameraParameters(cam) ; + simu.setExternalCameraParameters(cam) ; + simu.initApplication(&mainLoop) ; - simu.mainLoop() ; + simu.mainLoop() ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/example/coin-simulator/simulateFourPoints2DPolarCamVelocity.cpp b/example/coin-simulator/simulateFourPoints2DPolarCamVelocity.cpp index b891e707ed7511dd5a0184fcf22ede2153f0f4b5..9ab7e7696abde3420a3c747c0a66ac9158d3e069 100644 --- a/example/coin-simulator/simulateFourPoints2DPolarCamVelocity.cpp +++ b/example/coin-simulator/simulateFourPoints2DPolarCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: simulateFourPoints2DPolarCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: simulateFourPoints2DPolarCamVelocity.cpp 5263 2015-02-04 13:43:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,8 +60,6 @@ #include <visp/vpCameraParameters.h> #include <visp/vpTime.h> #include <visp/vpSimulator.h> - - #include <visp/vpMath.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpFeaturePointPolar.h> @@ -161,260 +159,236 @@ void *mainLoop (void *_simu) vpSimulator *simu = (vpSimulator *)_simu ; simu->initMainApplication() ; - for ( ; ; ) { - vpServo task ; - vpRobotCamera robot ; - - float sampling_time = 0.040f; // Sampling period in second - robot.setSamplingTime(sampling_time); - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - vpTRACE("sets the initial camera location " ) ; - vpPoseVector vcMo ; + vpServo task ; + vpRobotCamera robot ; - vcMo[0] = 0. ; - vcMo[1] = 0. ; - vcMo[2] = 3 ; - vcMo[3] = 0 ; - vcMo[4] = vpMath::rad(0) ; - vcMo[5] = vpMath::rad(90) ; + float sampling_time = 0.040f; // Sampling period in second + robot.setSamplingTime(sampling_time); + robot.setMaxTranslationVelocity(4.); - vpHomogeneousMatrix cMo(vcMo) ; - robot.setPosition(cMo) ; - simu->setCameraPosition(cMo) ; - - simu->getCameraPosition(cMo) ; - robot.setPosition(cMo) ; + // Sets the initial camera location + vpPoseVector vcMo ; - vpCameraParameters cam ; + vcMo[0] = 0. ; + vcMo[1] = 0. ; + vcMo[2] = 3 ; + vcMo[3] = 0 ; + vcMo[4] = vpMath::rad(0) ; + vcMo[5] = vpMath::rad(90) ; - vpTRACE("sets the point coordinates in the world frame " ) ; - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1,0) ; - point[1].setWorldCoordinates(0.1,-0.1,0) ; - point[2].setWorldCoordinates(0.1,0.1,0) ; - point[3].setWorldCoordinates(-0.1,0.1,0) ; - - vpTRACE("project : computes the point coordinates in the camera frame and its 2D coordinates" ) ; - for (int i = 0 ; i < 4 ; i++) { - point[i].changeFrame(cMo); // Compute point coordinates in the camera frame - point[i].project(); // Compute desired point doordinates in the camera frame - } + vpHomogeneousMatrix cMo(vcMo) ; + robot.setPosition(cMo) ; + simu->setCameraPosition(cMo) ; - vpTRACE("sets the desired position of the point ") ; - vpFeaturePointPolar p[4] ; - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure to build the polar coordinates + simu->getCameraPosition(cMo) ; + robot.setPosition(cMo) ; - std::cout << "s: \n"; - for (int i=0; i < 4; i ++) { - printf("[%d] rho %f theta %f Z %f\n", - i, p[i].get_rho(), p[i].get_theta(), p[i].get_Z()); - } + vpCameraParameters cam ; - vpTRACE("sets the desired position of the point ") ; - vcMo[0] = 0 ; - vcMo[1] = 0 ; - vcMo[2] = 1 ; - vcMo[3] = vpMath::rad(0); - vcMo[4] = vpMath::rad(0); - vcMo[5] = vpMath::rad(0); - - vpHomogeneousMatrix cMod(vcMo); - - vpFeaturePointPolar pd[4] ; - vpPoint pointd[4]; // Desired position of the points - pointd[0].setWorldCoordinates(-0.1,-0.1,0) ; - pointd[1].setWorldCoordinates(0.1,-0.1,0) ; - pointd[2].setWorldCoordinates(0.1,0.1,0) ; - pointd[3].setWorldCoordinates(-0.1,0.1,0) ; - for (int i=0; i < 4; i ++) { - pointd[i].changeFrame(cMod); // Compute desired point doordinates in the camera frame - pointd[i].project(); // Compute desired point doordinates in the camera frame - - vpFeatureBuilder::create(pd[i], pointd[i]) ; //retrieve x,y and Z of the vpPoint structure to build the polar coordinates - } - std::cout << "s*: \n"; - for (int i=0; i < 4; i ++) { - printf("[%d] rho %f theta %f Z %f\n", - i, pd[i].get_rho(), pd[i].get_theta(), pd[i].get_Z()); - } - - vpTRACE("define the task") ; - vpTRACE("\t we want an eye-in-hand control law") ; - vpTRACE("\t articular velocity are computed") ; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::CURRENT) ; - - vpTRACE("Set the position of the camera in the end-effector frame ") ; - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - vpTRACE("Set the Jacobian (expressed in the end-effector frame)") ; - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + // Sets the point coordinates in the world frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1,0) ; + point[1].setWorldCoordinates(0.1,-0.1,0) ; + point[2].setWorldCoordinates(0.1,0.1,0) ; + point[3].setWorldCoordinates(-0.1,0.1,0) ; + + // Project : computes the point coordinates in the camera frame and its 2D coordinates + for (int i = 0 ; i < 4 ; i++) { + point[i].changeFrame(cMo); // Compute point coordinates in the camera frame + point[i].project(); // Compute desired point doordinates in the camera frame + } - vpTRACE("\t we want to see a point on a point..") ; - for (int i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; + // Sets the desired position of the point + vpFeaturePointPolar p[4] ; + for (int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure to build the polar coordinates - vpTRACE("\t set the gain") ; - task.setLambda(1.0) ; + std::cout << "s: \n"; + for (int i=0; i < 4; i ++) { + printf("[%d] rho %f theta %f Z %f\n", + i, p[i].get_rho(), p[i].get_theta(), p[i].get_Z()); + } + // Sets the desired position of the point + vcMo[0] = 0 ; + vcMo[1] = 0 ; + vcMo[2] = 1 ; + vcMo[3] = vpMath::rad(0); + vcMo[4] = vpMath::rad(0); + vcMo[5] = vpMath::rad(0); + + vpHomogeneousMatrix cMod(vcMo); + + vpFeaturePointPolar pd[4] ; + vpPoint pointd[4]; // Desired position of the points + pointd[0].setWorldCoordinates(-0.1,-0.1,0) ; + pointd[1].setWorldCoordinates(0.1,-0.1,0) ; + pointd[2].setWorldCoordinates(0.1,0.1,0) ; + pointd[3].setWorldCoordinates(-0.1,0.1,0) ; + for (int i=0; i < 4; i ++) { + pointd[i].changeFrame(cMod); // Compute desired point doordinates in the camera frame + pointd[i].project(); // Compute desired point doordinates in the camera frame + + vpFeatureBuilder::create(pd[i], pointd[i]) ; //retrieve x,y and Z of the vpPoint structure to build the polar coordinates + } + std::cout << "s*: \n"; + for (int i=0; i < 4; i ++) { + printf("[%d] rho %f theta %f Z %f\n", + i, pd[i].get_rho(), pd[i].get_theta(), pd[i].get_Z()); + } - vpTRACE("Display task information " ) ; - task.print() ; + // Define the task + // We want an eye-in-hand control law + // Articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::CURRENT) ; - vpTime::wait(1000); // Sleep 1s - std::cout << "\nEnter a character to continue or CTRL-C to quit... " - << std::endl ; - { char a ; std::cin >> a ; } + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; + // Set the Jacobian (expressed in the end-effector frame) + vpMatrix eJe ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; - char name[FILENAME_MAX]; - unsigned int iter=0 ; - vpTRACE("\t loop") ; - while(iter++ < 300) { - double t = vpTime::measureTimeMs(); + // We want to see a point on a point + for (int i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; - vpColVector v ; + // Set the gain + task.setLambda(1.0) ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + // Display task information + task.print() ; - robot.getPosition(cMo) ; - for (int i = 0 ; i < 4 ; i++) - { - point[i].track(cMo) ; - vpFeatureBuilder::create(p[i],point[i]) ; - } + vpTime::wait(1000); // Sleep 1s - v = task.computeControlLaw() ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + unsigned int iter=0 ; + // Visual servo loop + while(iter++ < 200) { + double t = vpTime::measureTimeMs(); - //vpTime::wait(100) ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + robot.getPosition(cMo) ; + for (int i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + vpFeatureBuilder::create(p[i],point[i]) ; + } - simu->setCameraPosition(cMo) ; + vpColVector v = task.computeControlLaw() ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + simu->setCameraPosition(cMo) ; - if(SAVE==1) - { - sprintf(name,"/tmp/image.%04d.external.png",iter) ; - std::cout << name << std::endl ; - simu->write(name) ; - sprintf(name,"/tmp/image.%04d.internal.png",iter) ; - simu->write(name) ; - } + if(SAVE==1) + { + char name[FILENAME_MAX]; + sprintf(name,"/tmp/image.%04d.external.png",iter) ; + std::cout << name << std::endl ; + simu->write(name) ; + sprintf(name,"/tmp/image.%04d.internal.png",iter) ; + simu->write(name) ; + } - vpTime::wait(t, sampling_time * 1000); // Wait 40 ms + vpTime::wait(t, sampling_time * 1000); // Wait 40 ms - } - vpTRACE("Display task information " ) ; - task.print() ; - task.kill() ; - - std::cout << "cMo:\n" << cMo << std::endl; - vpPoseVector pose(cMo); - std::cout << "final pose:\n" << pose.t() << std::endl; - - std::cout << "\nEnter a character to continue..." <<std::endl ; - { char a ; std::cin >> a ; } } + // Display task information + task.print() ; + task.kill() ; - simu->closeMainApplication() ; + std::cout << "cMo:\n" << cMo << std::endl; + vpPoseVector pose(cMo); + std::cout << "final pose:\n" << pose.t() << std::endl; + simu->closeMainApplication() ; void *a=NULL ; return a ; - // return (void *); } - -int -main(int argc, const char ** argv) +int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string filename; - std::string username; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string filename; + std::string username; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - vpCameraParameters cam ; - vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; + vpCameraParameters cam ; + vpHomogeneousMatrix fMo ; fMo[2][3] = 0 ; - if (opt_display) { - vpSimulator simu ; - simu.initInternalViewer(300, 300) ; - simu.initExternalViewer(300, 300) ; + if (opt_display) { + vpSimulator simu ; + simu.initInternalViewer(300, 300) ; + simu.initExternalViewer(300, 300) ; - vpTime::wait(1000) ; - simu.setZoomFactor(1.0f) ; + vpTime::wait(1000) ; + simu.setZoomFactor(1.0f) ; - // Load the cad model - filename = ipath + vpIoTools::path("/ViSP-images/iv/4points.iv"); - simu.load(filename.c_str()) ; + // Load the cad model + filename = vpIoTools::createFilePath(ipath, "ViSP-images/iv/4points.iv"); + simu.load(filename.c_str()) ; - simu.setInternalCameraParameters(cam) ; - simu.setExternalCameraParameters(cam) ; - simu.initApplication(&mainLoop) ; + simu.setInternalCameraParameters(cam) ; + simu.setExternalCameraParameters(cam) ; + simu.initApplication(&mainLoop) ; - simu.mainLoop() ; + simu.mainLoop() ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/example/device/CMakeLists.txt b/example/device/CMakeLists.txt index 7a3d65cdb4f0ed2af6bce106305c3c8c1699e578..81f049dc0eb6f17392b0eeaaef45be6d546aff26 100644 --- a/example/device/CMakeLists.txt +++ b/example/device/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -52,8 +52,3 @@ SET (SRC_SUBDIRS # Build process propagation in the sub directories SUBDIRS(${SRC_SUBDIRS}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/device/display/CMakeLists.txt b/example/device/display/CMakeLists.txt index 6c30fe5d8eb764760b41c523be63b69a3a1969f9..befd33cf8e95e4f13bff854209c5667b9d702960 100644 --- a/example/device/display/CMakeLists.txt +++ b/example/device/display/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE displaySequence.cpp displayGDI.cpp displayD3D.cpp @@ -54,30 +54,29 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(displaySequence displaySequence ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayGDI displayGDI -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayD3D displayD3D -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayGTK displayGTK -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayX displayX -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayOpenCV displayOpenCV -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(displayXMulti displayXMulti -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(displaySequence displaySequence ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayGDI displayGDI -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayD3D displayD3D -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayGTK displayGTK -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayX displayX -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayOpenCV displayOpenCV -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(displayXMulti displayXMulti -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/device/display/displayD3D.cpp b/example/device/display/displayD3D.cpp index 375340aa0ba826bf3e31e4f84f27cf82e167ef43..ccf1ed29088230f98f0c5ded55f9093f3a316a86 100755 --- a/example/device/display/displayD3D.cpp +++ b/example/device/display/displayD3D.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayD3D.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: displayD3D.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -180,254 +180,256 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path - opt_opath = "C:\\temp"; - - - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path + opt_opath = "C:\\temp"; + + + // Get the user login name + vpIoTools::getUserName(username); + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // Create a grey level image + vpImage<unsigned char> I ; + vpImagePoint ip, ip1, ip2; - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I, filename) ; - // Create a grey level image - vpImage<unsigned char> I ; - vpImagePoint ip, ip1, ip2; + // Create a display using X11 + vpDisplayD3D display; - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { - vpImageIo::read(I, filename) ; - } - catch (...) { - return -1; - } + if (opt_display) { + // For this grey level image, open a X11 display at position 100,100 + // in the screen, and with title "X11 display" + display.init(I, 100, 100, "X11 display") ; - // For this grey level image, open a D3D display at position 100,100 - // in the screen, and with title "D3D display" - vpDisplayD3D display; + // Display the image + vpDisplay::display(I) ; - if (opt_display) { - // We open a window using D3D. - // Its size is automatically defined by the image (I) size - display.init(I, 100, 100, "D3D display") ; + // Display in overlay a red cross at position 10,10 in the + // image. The lines are 10 pixels long + ip.set_i( 100 ); + ip.set_j( 10 ); - // Display the image - vpDisplay::display(I) ; + vpDisplay::displayCross(I, ip, 20, vpColor::red) ; - // Display in overlay a red cross at position 10,10 in the - // image. The lines are 10 pixels long - ip.set_i( 100 ); - ip.set_j( 10 ); - - vpDisplay::displayCross(I, ip, 20, vpColor::red) ; + // Display in overlay horizontal red lines + for (unsigned i=0 ; i < I.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + } - // Display in overlay horizontal red lines - for (unsigned i=0 ; i < I.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - } + // Display a ligne in the diagonal + ip1.set_i( -10 ); + ip1.set_j( -10 ); + ip2.set_i( I.getHeight() + 10 ); + ip2.set_j( I.getWidth() + 10 ); - // Display a ligne in the diagonal - ip1.set_i( -10 ); - ip1.set_j( -10 ); - ip2.set_i( I.getHeight() + 10 ); - ip2.set_j( I.getWidth() + 10 ); - - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - // Display in overlay vertical green dot lines - for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + // Display in overlay vertical green dot lines + for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; + } + + // Display a rectangle + ip.set_i( I.getHeight() - 45 ); + ip.set_j( -10 ); + vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; + + // Display in overlay a blue arrow ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; - } - - // Display a rectangle - ip.set_i( I.getHeight() - 45 ); - ip.set_j( -10 ); - vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 80 ); - ip.set_j( 80 ); - vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 80 ); + ip.set_j( 80 ); + vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + } + + ip.set_i( -10 ); + ip.set_j( 300 ); + vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; + + // Display in overlay a yellow string + ip.set_i( 85 ); + ip.set_j( 100 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::yellow) ; + //Flush the display + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + // Close the display + vpDisplay::close(I); } - ip.set_i( -10 ); - ip.set_j( 300 ); - vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; - - // Display in overlay a yellow string - ip.set_i( 85 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::yellow) ; - //Flush the display - vpDisplay::flush(I); - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; - } - - // Close the display - vpDisplay::close(I); - } + vpImage<vpRGBa> Irgba ; - // Create a color image - vpImage<vpRGBa> Irgba ; - - // Load a grey image from the disk and convert it to a color image - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { + // Load a grey image from the disk and convert it to a color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); vpImageIo::read(Irgba, filename) ; - } - catch (...) { - return -1; - } - // For this color image, open a D3D display at position 100,100 - // in the screen, and with title "D3D color display" - vpDisplayD3D displayRGBa; - - if (opt_display) { - // We open a window using D3D. - // Its size is automatically defined by the image (Irgba) size - displayRGBa.init(Irgba, 100, 100, "D3D color display"); - - // Display the color image - vpDisplay::display(Irgba) ; - vpDisplay::flush(Irgba) ; - - // If click is allowed, wait for a blocking mouse click to display a cross - // at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click to display a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(Irgba, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - } - else { - ip.set_i( 10 ); - ip.set_j( 20 ); - // Display a red cross at position i, j (i correspond to the row - // and j to the column coordinates in the image) - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - - } - // Flush the display. Sometimes the display content is - // bufferized. Force to display the content that has been bufferized. - vpDisplay::flush(Irgba); - - // If click is allowed, wait for a blocking mouse click to exit. - if (opt_click_allowed) { - std::cout << "\nA click to exit the program..." << std::endl; - vpDisplay::getClick(Irgba) ; - std::cout << "Bye" << std::endl; + // Create a new display + vpDisplayD3D displayRGBa; + + if (opt_display) { + // For this color image, open a X11 display at position 100,100 + // in the screen, and with title "X11 color display" + displayRGBa.init(Irgba, 100, 100, "X11 color display"); + + // Display the color image + vpDisplay::display(Irgba) ; + vpDisplay::flush(Irgba) ; + + // If click is allowed, wait for a blocking mouse click to display a cross + // at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click to display a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(Irgba, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + } + else { + ip.set_i( 10 ); + ip.set_j( 20 ); + // Display a red cross at position i, j (i correspond to the row + // and j to the column coordinates in the image) + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + + } + // Flush the display. Sometimes the display content is + // bufferized. Force to display the content that has been bufferized. + vpDisplay::flush(Irgba); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(Irgba, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_color.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a blocking mouse click to exit. + if (opt_click_allowed) { + std::cout << "\nA click to exit the program..." << std::endl; + vpDisplay::getClick(Irgba) ; + std::cout << "Bye" << std::endl; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/display/displayGDI.cpp b/example/device/display/displayGDI.cpp index 3176cf0a1bf85e26fa318fdf2841fa9e30b11557..41724ea35b38e1c6a98c2af94280740679200ba8 100755 --- a/example/device/display/displayGDI.cpp +++ b/example/device/display/displayGDI.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayGDI.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: displayGDI.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if ( defined(WIN32) && defined(VISP_HAVE_GDI) ) +#if ( defined(_WIN32) && defined(VISP_HAVE_GDI) ) #include <visp/vpDisplayGDI.h> @@ -179,254 +179,255 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path - opt_opath = "C:\\temp"; - - - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path + opt_opath = "C:\\temp"; + + // Get the user login name + vpIoTools::getUserName(username); + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // Create a grey level image + vpImage<unsigned char> I ; + vpImagePoint ip, ip1, ip2; - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I, filename) ; - // Create a grey level image - vpImage<unsigned char> I ; - vpImagePoint ip, ip1, ip2; + // Create a display using X11 + vpDisplayGDI display; - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { - vpImageIo::read(I, filename) ; - } - catch (...) { - return -1; - } + if (opt_display) { + // For this grey level image, open a X11 display at position 100,100 + // in the screen, and with title "X11 display" + display.init(I, 100, 100, "X11 display") ; - // For this grey level image, open a GDI display at position 100,100 - // in the screen, and with title "GDI display" - vpDisplayGDI display; + // Display the image + vpDisplay::display(I) ; - if (opt_display) { - // We open a window using GDI. - // Its size is automatically defined by the image (I) size - display.init(I, 100, 100, "GDI display") ; + // Display in overlay a red cross at position 10,10 in the + // image. The lines are 10 pixels long + ip.set_i( 100 ); + ip.set_j( 10 ); - // Display the image - vpDisplay::display(I) ; + vpDisplay::displayCross(I, ip, 20, vpColor::red) ; - // Display in overlay a red cross at position 10,10 in the - // image. The lines are 10 pixels long - ip.set_i( 100 ); - ip.set_j( 10 ); - - vpDisplay::displayCross(I, ip, 20, vpColor::red) ; + // Display in overlay horizontal red lines + for (unsigned i=0 ; i < I.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + } - // Display in overlay horizontal red lines - for (unsigned i=0 ; i < I.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - } + // Display a ligne in the diagonal + ip1.set_i( -10 ); + ip1.set_j( -10 ); + ip2.set_i( I.getHeight() + 10 ); + ip2.set_j( I.getWidth() + 10 ); - // Display a ligne in the diagonal - ip1.set_i( -10 ); - ip1.set_j( -10 ); - ip2.set_i( I.getHeight() + 10 ); - ip2.set_j( I.getWidth() + 10 ); - - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - // Display in overlay vertical green dot lines - for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + // Display in overlay vertical green dot lines + for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; + } + + // Display a rectangle + ip.set_i( I.getHeight() - 45 ); + ip.set_j( -10 ); + vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; + + // Display in overlay a blue arrow ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; - } - - // Display a rectangle - ip.set_i( I.getHeight() - 45 ); - ip.set_j( -10 ); - vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 80 ); - ip.set_j( 80 ); - vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 80 ); + ip.set_j( 80 ); + vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + } + + ip.set_i( -10 ); + ip.set_j( 300 ); + vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; + + // Display in overlay a yellow string + ip.set_i( 85 ); + ip.set_j( 100 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::yellow) ; + //Flush the display + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + // Close the display + vpDisplay::close(I); } - ip.set_i( -10 ); - ip.set_j( 300 ); - vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; - - // Display in overlay a yellow string - ip.set_i( 85 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::yellow) ; - //Flush the display - vpDisplay::flush(I); - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; - } - - // Close the display - vpDisplay::close(I); - } + vpImage<vpRGBa> Irgba ; - // Create a color image - vpImage<vpRGBa> Irgba ; - - // Load a grey image from the disk and convert it to a color image - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { + // Load a grey image from the disk and convert it to a color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); vpImageIo::read(Irgba, filename) ; - } - catch (...) { - return -1; - } - // For this color image, open a GDI display at position 100,100 - // in the screen, and with title "GDI color display" - vpDisplayGDI displayRGBa; - - if (opt_display) { - // We open a window using GDI. - // Its size is automatically defined by the image (Irgba) size - displayRGBa.init(Irgba, 100, 100, "GDI color display"); - - // Display the color image - vpDisplay::display(Irgba) ; - vpDisplay::flush(Irgba) ; - - // If click is allowed, wait for a blocking mouse click to display a cross - // at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click to display a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(Irgba, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - } - else { - ip.set_i( 10 ); - ip.set_j( 20 ); - // Display a red cross at position i, j (i correspond to the row - // and j to the column coordinates in the image) - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - - } - // Flush the display. Sometimes the display content is - // bufferized. Force to display the content that has been bufferized. - vpDisplay::flush(Irgba); - - // If click is allowed, wait for a blocking mouse click to exit. - if (opt_click_allowed) { - std::cout << "\nA click to exit the program..." << std::endl; - vpDisplay::getClick(Irgba) ; - std::cout << "Bye" << std::endl; + // Create a new display + vpDisplayGDI displayRGBa; + + if (opt_display) { + // For this color image, open a X11 display at position 100,100 + // in the screen, and with title "X11 color display" + displayRGBa.init(Irgba, 100, 100, "X11 color display"); + + // Display the color image + vpDisplay::display(Irgba) ; + vpDisplay::flush(Irgba) ; + + // If click is allowed, wait for a blocking mouse click to display a cross + // at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click to display a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(Irgba, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + } + else { + ip.set_i( 10 ); + ip.set_j( 20 ); + // Display a red cross at position i, j (i correspond to the row + // and j to the column coordinates in the image) + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + + } + // Flush the display. Sometimes the display content is + // bufferized. Force to display the content that has been bufferized. + vpDisplay::flush(Irgba); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(Irgba, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_color.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a blocking mouse click to exit. + if (opt_click_allowed) { + std::cout << "\nA click to exit the program..." << std::endl; + vpDisplay::getClick(Irgba) ; + std::cout << "Bye" << std::endl; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/display/displayGTK.cpp b/example/device/display/displayGTK.cpp index 83a13af1a0379ec778644a95c906389a8d77351b..ef7dbe93dc7a955b22f4f01c7f57127d076717fa 100644 --- a/example/device/display/displayGTK.cpp +++ b/example/device/display/displayGTK.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayGTK.cpp 4294 2013-07-01 16:05:49Z fspindle $ + * $Id: displayGTK.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -183,250 +183,259 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // Create a grey level image + vpImage<unsigned char> I ; + vpImagePoint ip, ip1, ip2; - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I, filename) ; - // Create a grey level image - vpImage<unsigned char> I ; - vpImagePoint ip, ip1, ip2; + // Create a display using X11 + vpDisplayGTK display; - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.png"); - try { - vpImageIo::read(I, filename) ; - } - catch (...) { - return -1; - } + if (opt_display) { + // For this grey level image, open a X11 display at position 100,100 + // in the screen, and with title "X11 display" + display.init(I, 100, 100, "X11 display") ; - // Create a display using GTK - vpDisplayGTK display; + // Display the image + vpDisplay::display(I) ; - if (opt_display) { - // For this grey level image, open a GTK display at position 100,100 - // in the screen, and with title "GTK display" - display.init(I, 100, 100, "GTK display") ; + // Display in overlay a red cross at position 10,10 in the + // image. The lines are 10 pixels long + ip.set_i( 100 ); + ip.set_j( 10 ); - // Display the image - vpDisplay::display(I) ; + vpDisplay::displayCross(I, ip, 20, vpColor::red) ; - // Display in overlay a red cross at position 10,10 in the - // image. The lines are 10 pixels long - ip.set_i( 100 ); - ip.set_j( 10 ); - - vpDisplay::displayCross(I, ip, 20, vpColor::red) ; + // Display in overlay horizontal red lines + for (unsigned i=0 ; i < I.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + } - // Display in overlay horizontal red lines - for (unsigned i=0 ; i < I.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - } + // Display a ligne in the diagonal + ip1.set_i( -10 ); + ip1.set_j( -10 ); + ip2.set_i( I.getHeight() + 10 ); + ip2.set_j( I.getWidth() + 10 ); - // Display a ligne in the diagonal - ip1.set_i( -10 ); - ip1.set_j( -10 ); - ip2.set_i( I.getHeight() + 10 ); - ip2.set_j( I.getWidth() + 10 ); - - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - // Display in overlay vertical green dot lines - for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + // Display in overlay vertical green dot lines + for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; + } + + // Display a rectangle + ip.set_i( I.getHeight() - 45 ); + ip.set_j( -10 ); + vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; + + // Display in overlay a blue arrow ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; - } - - // Display a rectangle - ip.set_i( I.getHeight() - 45 ); - ip.set_j( -10 ); - vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 80 ); - ip.set_j( 80 ); - vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 80 ); + ip.set_j( 80 ); + vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + } + + ip.set_i( -10 ); + ip.set_j( 300 ); + vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; + + // Display in overlay a yellow string + ip.set_i( 85 ); + ip.set_j( 100 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::yellow) ; + //Flush the display + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + // Close the display + vpDisplay::close(I); } - ip.set_i( -10 ); - ip.set_j( 300 ); - vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; - - // Display in overlay a yellow string - ip.set_i( 85 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::yellow) ; - //Flush the display - vpDisplay::flush(I); - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; + vpImage<vpRGBa> Irgba ; + + // Load a grey image from the disk and convert it to a color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(Irgba, filename) ; + + // Create a new display + vpDisplayGTK displayRGBa; + + if (opt_display) { + // For this color image, open a X11 display at position 100,100 + // in the screen, and with title "X11 color display" + displayRGBa.init(Irgba, 100, 100, "X11 color display"); + + // Display the color image + vpDisplay::display(Irgba) ; + vpDisplay::flush(Irgba) ; + + // If click is allowed, wait for a blocking mouse click to display a cross + // at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click to display a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(Irgba, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + } + else { + ip.set_i( 10 ); + ip.set_j( 20 ); + // Display a red cross at position i, j (i correspond to the row + // and j to the column coordinates in the image) + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + + } + // Flush the display. Sometimes the display content is + // bufferized. Force to display the content that has been bufferized. + vpDisplay::flush(Irgba); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(Irgba, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_color.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a blocking mouse click to exit. + if (opt_click_allowed) { + std::cout << "\nA click to exit the program..." << std::endl; + vpDisplay::getClick(Irgba) ; + std::cout << "Bye" << std::endl; + } } - - // Close the display - vpDisplay::close(I); + return 0; } - - // Create a color image - vpImage<vpRGBa> Irgba ; - - // Load a grey image from the disk and convert it to a color image - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(Irgba, filename) ; - - // Create a new GTK display - vpDisplayGTK displayRGBa; - - if (opt_display) { - // For this color image, open a GTK display at position 100,100 - // in the screen, and with title "GTK color display" - displayRGBa.init(Irgba, 100, 100, "GTK color display"); - - // Display the color image - vpDisplay::display(Irgba) ; - vpDisplay::flush(Irgba) ; - - // If click is allowed, wait for a blocking mouse click to display a cross - // at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click to display a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(Irgba, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - } - else { - ip.set_i( 10 ); - ip.set_j( 20 ); - // Display a red cross at position i, j (i correspond to the row - // and j to the column coordinates in the image) - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - - } - // Flush the display. Sometimes the display content is - // bufferized. Force to display the content that has been bufferized. - vpDisplay::flush(Irgba); - - // If click is allowed, wait for a blocking mouse click to exit. - if (opt_click_allowed) { - std::cout << "\nA click to exit the program..." << std::endl; - vpDisplay::getClick(Irgba) ; - std::cout << "Bye" << std::endl; - } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else @@ -437,4 +446,3 @@ main() } #endif - diff --git a/example/device/display/displayOpenCV.cpp b/example/device/display/displayOpenCV.cpp index 4512285a9b096126d94ad49cba6bd383fad23aa4..97d4fe0ef84433e8a3e386ce7683b07bf1313d16 100644 --- a/example/device/display/displayOpenCV.cpp +++ b/example/device/display/displayOpenCV.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayOpenCV.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: displayOpenCV.cpp 5005 2014-11-24 08:25:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,8 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> #include <stdlib.h> -#ifdef VISP_HAVE_OPENCV + +#if defined(VISP_HAVE_OPENCV) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -72,6 +73,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:o:p:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, + bool &click_allowed, std::string user, bool &display); + /*! Print the program options. @@ -83,8 +88,7 @@ \param user : Username. */ -void usage(const char *name, const char *badparam, std::string ipath, - std::string opath, std::string user) +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user) { fprintf(stdout, "\n\ Read an image on the disk, display it using OpenCV, display some\n\ @@ -150,23 +154,22 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, bool &click_allowed, - std::string user, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, + bool &click_allowed, std::string user, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -174,7 +177,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -184,256 +187,259 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // std::cout << "env_ipath: " << env_ipath << std::endl; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // Create a grey level image + vpImage<unsigned char> I ; + vpImagePoint ip, ip1, ip2; - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I, filename) ; - // Create a grey level image - vpImage<unsigned char> I ; - vpImagePoint ip, ip1, ip2; + // Create a display using X11 + vpDisplayOpenCV display; - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { - vpImageIo::read(I, filename) ; - } - catch (...) { - return -1; - } + if (opt_display) { + // For this grey level image, open a X11 display at position 100,100 + // in the screen, and with title "X11 display" + display.init(I, 100, 100, "X11 display") ; - // Create a display using OpenCV - vpDisplayOpenCV display; + // Display the image + vpDisplay::display(I) ; - if (opt_display) { - // For this grey level image, open a X11 display at position 100,100 - // in the screen, and with title "X11 display" - display.init(I, 100, 100, "OpenCV display") ; + // Display in overlay a red cross at position 10,10 in the + // image. The lines are 10 pixels long + ip.set_i( 100 ); + ip.set_j( 10 ); - // Display the image - vpDisplay::display(I) ; + vpDisplay::displayCross(I, ip, 20, vpColor::red) ; - // Display in overlay a red cross at position 10,10 in the - // image. The lines are 10 pixels long - ip.set_i( 100 ); - ip.set_j( 10 ); - - vpDisplay::displayCross(I, ip, 20, vpColor::red) ; + // Display in overlay horizontal red lines + for (unsigned i=0 ; i < I.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + } - // Display in overlay horizontal red lines - for (unsigned i=0 ; i < I.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - } + // Display a ligne in the diagonal + ip1.set_i( -10 ); + ip1.set_j( -10 ); + ip2.set_i( I.getHeight() + 10 ); + ip2.set_j( I.getWidth() + 10 ); - // Display a ligne in the diagonal - ip1.set_i( -10 ); - ip1.set_j( -10 ); - ip2.set_i( I.getHeight() + 10 ); - ip2.set_j( I.getWidth() + 10 ); - - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - // Display in overlay vertical green dot lines - for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + // Display in overlay vertical green dot lines + for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; + } + + // Display a rectangle + ip.set_i( I.getHeight() - 45 ); + ip.set_j( -10 ); + vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; + + // Display in overlay a blue arrow ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; - } - - // Display a rectangle - ip.set_i( I.getHeight() - 45 ); - ip.set_j( -10 ); - vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 80 ); - ip.set_j( 80 ); - vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 80 ); + ip.set_j( 80 ); + vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + } + + ip.set_i( -10 ); + ip.set_j( 300 ); + vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; + + // Display in overlay a yellow string + ip.set_i( 85 ); + ip.set_j( 100 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::yellow) ; + //Flush the display + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + // Close the display + vpDisplay::close(I); } - ip.set_i( -10 ); - ip.set_j( 300 ); - vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; - - // Display in overlay a yellow string - ip.set_i( 85 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::yellow) ; - //Flush the display - vpDisplay::flush(I); - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; - } - - // Close the display - vpDisplay::close(I); - } - - // Create a color image - vpImage<vpRGBa> Irgba ; + vpImage<vpRGBa> Irgba ; - // Load a grey image from the disk and convert it to a color image - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { + // Load a grey image from the disk and convert it to a color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); vpImageIo::read(Irgba, filename) ; - } - catch (...) { - return -1; - } - // Create a new display - vpDisplayOpenCV displayRGBa; - - if (opt_display) { - // For this color image, open a display at position 100,100 - // in the screen, and with title "OpenCV color display" - displayRGBa.init(Irgba, 100, 100, "OpenCV color display"); - - // Display the color image - vpDisplay::display(Irgba) ; - vpDisplay::flush(Irgba) ; - - // If click is allowed, wait for a blocking mouse click to display a cross - // at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click to display a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(Irgba, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - } - else { - ip.set_i( 10 ); - ip.set_j( 20 ); - // Display a red cross at position i, j (i correspond to the row - // and j to the column coordinates in the image) - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - - } - // Flush the display. Sometimes the display content is - // bufferized. Force to display the content that has been bufferized. - vpDisplay::flush(Irgba); - - // If click is allowed, wait for a blocking mouse click to exit. - if (opt_click_allowed) { - std::cout << "\nA click to exit the program..." << std::endl; - vpDisplay::getClick(Irgba) ; - std::cout << "Bye" << std::endl; + // Create a new display + vpDisplayOpenCV displayRGBa; + + if (opt_display) { + // For this color image, open a X11 display at position 100,100 + // in the screen, and with title "X11 color display" + displayRGBa.init(Irgba, 100, 100, "X11 color display"); + + // Display the color image + vpDisplay::display(Irgba) ; + vpDisplay::flush(Irgba) ; + + // If click is allowed, wait for a blocking mouse click to display a cross + // at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click to display a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(Irgba, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + } + else { + ip.set_i( 10 ); + ip.set_j( 20 ); + // Display a red cross at position i, j (i correspond to the row + // and j to the column coordinates in the image) + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + + } + // Flush the display. Sometimes the display content is + // bufferized. Force to display the content that has been bufferized. + vpDisplay::flush(Irgba); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(Irgba, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_color.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a blocking mouse click to exit. + if (opt_click_allowed) { + std::cout << "\nA click to exit the program..." << std::endl; + vpDisplay::getClick(Irgba) ; + std::cout << "Bye" << std::endl; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/display/displaySequence.cpp b/example/device/display/displaySequence.cpp index 9a5057a41cbfa06835112181ec065a2b3c2758e2..a7dd3ee56d11b7eb97b227ee91305d9f901191a0 100644 --- a/example/device/display/displaySequence.cpp +++ b/example/device/display/displaySequence.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displaySequence.cpp 4294 2013-07-01 16:05:49Z fspindle $ + * $Id: displaySequence.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -84,6 +84,11 @@ // List of allowed command line options #define GETOPTARGS "di:p:hf:n:s:w" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, + unsigned &first, unsigned &nimages, unsigned &step, bool &display, bool &wait); + /*! Print the program options. @@ -98,7 +103,7 @@ */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, - unsigned first, unsigned nimages, unsigned step) + unsigned first, unsigned nimages, unsigned step) { fprintf(stdout, "\n\ Read an image sequence from the disk and display it.\n\ @@ -180,26 +185,25 @@ SYNOPSIS\n\ */ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, - unsigned &first, unsigned &nimages, unsigned &step, - bool &display, bool &wait) + unsigned &first, unsigned &nimages, unsigned &step, bool &display, bool &wait) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'w': wait = true; break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step); return false; break; } } @@ -208,7 +212,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -218,140 +222,138 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 0; - unsigned opt_nimages = 80; - unsigned opt_step = 1; - bool opt_display = true; - bool opt_wait = false; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step, opt_display, opt_wait) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 0; + unsigned opt_nimages = 80; + unsigned opt_step = 1; + bool opt_display = true; + bool opt_wait = false; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, + opt_step, opt_display, opt_wait) == false) { + exit (-1); + } - if ( ! opt_display ) - opt_wait = false; // turn off the waiting - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + if ( ! opt_display ) + opt_wait = false; // turn off the waiting + + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + exit(-1); + } - unsigned iter = opt_first; - std::ostringstream s; - char cfilename[FILENAME_MAX]; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - if (opt_ppath.empty()){ + unsigned iter = opt_first; + std::ostringstream s; + char cfilename[FILENAME_MAX]; + if (opt_ppath.empty()){ - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file - // ViSP-images/cube/image.0001.pgm - // !! vpDotExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images.tar.gz" - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file + // ViSP-images/cube/image.0001.pgm + // !! vpDotExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images.tar.gz" - // Build the name of the image file + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + // Build the name of the image file - sprintf(cfilename,opt_ppath.c_str(), iter) ; - filename = cfilename; - } - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option, " << std::endl - << " or your -p " << opt_ppath << " option " <<std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable" - << std::endl; - exit(-1); - } + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { + sprintf(cfilename,opt_ppath.c_str(), iter) ; + filename = cfilename; + } + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option, " << std::endl + << " or your -p " << opt_ppath << " option " <<std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable" + << std::endl; + exit(-1); + } #if defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #endif - if (opt_display) { - try { + if (opt_display) { + // We open a window using either X11 or GTK or GDI. // Its size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; @@ -364,17 +366,11 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - // double tms_1 = vpTime::measureTimeMs() ; - unsigned niter=0 ; - // this is the loop over the image sequence - while (iter < opt_first + opt_nimages*opt_step) { - try { + // double tms_1 = vpTime::measureTimeMs() ; + unsigned niter=0 ; + // this is the loop over the image sequence + while (iter < opt_first + opt_nimages*opt_step) { double tms = vpTime::measureTimeMs() ; // set the new image name @@ -382,7 +378,7 @@ main(int argc, const char ** argv) if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf(cfilename, opt_ppath.c_str(), iter) ; @@ -409,16 +405,18 @@ main(int argc, const char ** argv) vpTime::wait(tms, 40) ; } niter++ ; + + iter += opt_step ; } - catch(...) { - exit(-1) ; - } - iter += opt_step ; + // double tms_2 = vpTime::measureTimeMs() ; + // double tms_total = tms_2 - tms_1 ; + // std::cout << "Total Time : "<< tms_total<<std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - // double tms_2 = vpTime::measureTimeMs() ; - // double tms_total = tms_2 - tms_1 ; - // std::cout << "Total Time : "<< tms_total<<std::endl; - } #else int diff --git a/example/device/display/displayX.cpp b/example/device/display/displayX.cpp index 2188a5ddd3e126c0be6edbd75445fb711ea08a04..b0280540484e9482df3afa8504d89e2c5cf514c2 100644 --- a/example/device/display/displayX.cpp +++ b/example/device/display/displayX.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayX.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: displayX.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:o:p:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, bool &click_allowed, + std::string user, bool &display); + /*! Print the program options. @@ -84,8 +88,7 @@ \param user : Username. */ -void usage(const char *name, const char *badparam, std::string ipath, - std::string opath, std::string user) +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user) { fprintf(stdout, "\n\ Read an image on the disk, display it using X11, display some\n\ @@ -151,23 +154,22 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, bool &click_allowed, +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, bool &click_allowed, std::string user, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -175,7 +177,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -185,256 +187,259 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // std::cout << "env_ipath: " << env_ipath << std::endl; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); + // Get the user login name + vpIoTools::getUserName(username); - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // Create a grey level image + vpImage<unsigned char> I ; + vpImagePoint ip, ip1, ip2; - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I, filename) ; - // Create a grey level image - vpImage<unsigned char> I ; - vpImagePoint ip, ip1, ip2; + // Create a display using X11 + vpDisplayX display; - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { - vpImageIo::read(I, filename) ; - } - catch (...) { - return -1; - } + if (opt_display) { + // For this grey level image, open a X11 display at position 100,100 + // in the screen, and with title "X11 display" + display.init(I, 100, 100, "X11 display") ; - // Create a display using X11 - vpDisplayX display; + // Display the image + vpDisplay::display(I) ; - if (opt_display) { - // For this grey level image, open a X11 display at position 100,100 - // in the screen, and with title "X11 display" - display.init(I, 100, 100, "X11 display") ; + // Display in overlay a red cross at position 10,10 in the + // image. The lines are 10 pixels long + ip.set_i( 100 ); + ip.set_j( 10 ); - // Display the image - vpDisplay::display(I) ; + vpDisplay::displayCross(I, ip, 20, vpColor::red) ; - // Display in overlay a red cross at position 10,10 in the - // image. The lines are 10 pixels long - ip.set_i( 100 ); - ip.set_j( 10 ); - - vpDisplay::displayCross(I, ip, 20, vpColor::red) ; + // Display in overlay horizontal red lines + for (unsigned i=0 ; i < I.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + } - // Display in overlay horizontal red lines - for (unsigned i=0 ; i < I.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - } + // Display a ligne in the diagonal + ip1.set_i( -10 ); + ip1.set_j( -10 ); + ip2.set_i( I.getHeight() + 10 ); + ip2.set_j( I.getWidth() + 10 ); - // Display a ligne in the diagonal - ip1.set_i( -10 ); - ip1.set_j( -10 ); - ip2.set_i( I.getHeight() + 10 ); - ip2.set_j( I.getWidth() + 10 ); - - vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; + vpDisplay::displayLine(I, ip1, ip2, vpColor::red) ; - // Display in overlay vertical green dot lines - for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + // Display in overlay vertical green dot lines + for (unsigned i=0 ; i < I.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; + } + + // Display a rectangle + ip.set_i( I.getHeight() - 45 ); + ip.set_j( -10 ); + vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; + + // Display in overlay a blue arrow ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green) ; - } - - // Display a rectangle - ip.set_i( I.getHeight() - 45 ); - ip.set_j( -10 ); - vpDisplay::displayRectangle(I, ip, 60, 80, vpColor::orange) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 80 ); - ip.set_j( 80 ); - vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 80 ); + ip.set_j( 80 ); + vpDisplay::displayCircle(I, ip, 20+i, vpColor::yellow) ; + } + + ip.set_i( -10 ); + ip.set_j( 300 ); + vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; + + // Display in overlay a yellow string + ip.set_i( 85 ); + ip.set_j( 100 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::yellow) ; + //Flush the display + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + // Close the display + vpDisplay::close(I); } - ip.set_i( -10 ); - ip.set_j( 300 ); - vpDisplay::displayCircle(I, ip, 100,vpColor::yellow) ; - - // Display in overlay a yellow string - ip.set_i( 85 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::yellow) ; - //Flush the display - vpDisplay::flush(I); - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; - } - - // Close the display - vpDisplay::close(I); - } + vpImage<vpRGBa> Irgba ; - // Create a color image - vpImage<vpRGBa> Irgba ; - - // Load a grey image from the disk and convert it to a color image - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - try { + // Load a grey image from the disk and convert it to a color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); vpImageIo::read(Irgba, filename) ; - } - catch (...) { - return -1; - } - // Create a new display - vpDisplayX displayRGBa; - - if (opt_display) { - // For this color image, open a X11 display at position 100,100 - // in the screen, and with title "X11 color display" - displayRGBa.init(Irgba, 100, 100, "X11 color display"); - - // Display the color image - vpDisplay::display(Irgba) ; - vpDisplay::flush(Irgba) ; - - // If click is allowed, wait for a blocking mouse click to display a cross - // at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click to display a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(Irgba, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - } - else { - ip.set_i( 10 ); - ip.set_j( 20 ); - // Display a red cross at position i, j (i correspond to the row - // and j to the column coordinates in the image) - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); - - } - // Flush the display. Sometimes the display content is - // bufferized. Force to display the content that has been bufferized. - vpDisplay::flush(Irgba); - - // If click is allowed, wait for a blocking mouse click to exit. - if (opt_click_allowed) { - std::cout << "\nA click to exit the program..." << std::endl; - vpDisplay::getClick(Irgba) ; - std::cout << "Bye" << std::endl; + // Create a new display + vpDisplayX displayRGBa; + + if (opt_display) { + // For this color image, open a X11 display at position 100,100 + // in the screen, and with title "X11 color display" + displayRGBa.init(Irgba, 100, 100, "X11 color display"); + + // Display the color image + vpDisplay::display(Irgba) ; + vpDisplay::flush(Irgba) ; + + // If click is allowed, wait for a blocking mouse click to display a cross + // at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click to display a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(Irgba, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + } + else { + ip.set_i( 10 ); + ip.set_j( 20 ); + // Display a red cross at position i, j (i correspond to the row + // and j to the column coordinates in the image) + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(Irgba, ip, 15, vpColor::red); + + } + // Flush the display. Sometimes the display content is + // bufferized. Force to display the content that has been bufferized. + vpDisplay::flush(Irgba); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(Irgba, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_color.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a blocking mouse click to exit. + if (opt_click_allowed) { + std::cout << "\nA click to exit the program..." << std::endl; + vpDisplay::getClick(Irgba) ; + std::cout << "Bye" << std::endl; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/display/displayXMulti.cpp b/example/device/display/displayXMulti.cpp index b624901f48f0232c67df195bcc8766b7c21f98e3..4bff5507f3960a677643d72d331d3ea39ca41e5e 100644 --- a/example/device/display/displayXMulti.cpp +++ b/example/device/display/displayXMulti.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: displayXMulti.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: displayXMulti.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -76,6 +76,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, bool &click_allowed, + std::string user, bool &display); + /*! Print the program options. @@ -151,23 +155,22 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, bool &click_allowed, +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, bool &click_allowed, std::string user, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -175,7 +178,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -185,221 +188,225 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // std::cout << "env_ipath: " << env_ipath << std::endl; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, - opt_click_allowed, username, opt_display) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, + opt_click_allowed, username, opt_display) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string odirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string odirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(odirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(odirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << odirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(odirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(odirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << odirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - // Create two color images - vpImage<vpRGBa> I1, I2 ; - vpImagePoint ip, ip1, ip2; + // Create two color images + vpImage<vpRGBa> I1, I2 ; + vpImagePoint ip, ip1, ip2; - try { - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(I1, filename) ; - } - catch(...) - { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } - try { - // Load a color image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpImageIo::read(I2, filename) ; - } - catch(...) - { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } - - // For each image, open a X11 display - vpDisplayX display1; - vpDisplayX display2; - - if (opt_display) { - // Attach image 1 to display 1 - display1.init(I1, 0, 0,"X11 Display 1...") ; - // Attach image 2 to display 2 - display2.init(I2, 200, 200,"X11 Display 2...") ; - // Display the images - vpDisplay::display(I1) ; - vpDisplay::display(I2) ; - - // In the first display, display in overlay horizontal red lines - for (unsigned int i=0 ; i < I1.getHeight() ; i+=20) { - ip1.set_i( i ); - ip1.set_j( 0 ); - ip2.set_i( i ); - ip2.set_j( I1.getWidth() ); - vpDisplay::displayLine(I1, ip1, ip2, vpColor::red) ; - } - - // In the first display, display in overlay vertical green dot lines - for (unsigned int i=0 ; i < I1.getWidth() ; i+=20) { - ip1.set_i( 0 ); - ip1.set_j( i ); - ip2.set_i( I1.getWidth() ); - ip2.set_j( i ); - vpDisplay::displayDotLine(I1, ip1, ip2, vpColor::green) ; + try { + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I1, filename) ; } - - // In the first display, display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I1, ip1, ip2, vpColor::blue) ; - - // In the first display, display in overlay some circles. The - // position of the center is 200, 200 the radius is increased by 20 - // pixels for each circle - for (unsigned int i=0 ; i < 100 ; i+=20) { - ip.set_i( 200 ); - ip.set_j( 200 ); - vpDisplay::displayCircle(I1, ip, 20+i,vpColor::yellow) ; + catch(...) + { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); } - - // In the first display, display in overlay a yellow string - ip.set_i( 100 ); - ip.set_j( 100 ); - vpDisplay::displayCharString(I1, ip, - "ViSP is a marvelous software", - vpColor::blue) ; - - //Flush displays. The displays must be flushed to show the overlay. - //without this line, nothing will be displayed. - vpDisplay::flush(I1); - vpDisplay::flush(I2); - - // If click is allowed, wait for a blocking mouse click in the first - // display, to display a cross at the clicked pixel position - if (opt_click_allowed) { - std::cout << "\nA click in the first display to draw a cross..." << std::endl; - // Blocking wait for a click. Get the position of the selected pixel - // (i correspond to the row and j to the column coordinates in the image) - vpDisplay::getClick(I1, ip); - // Display a red cross on the click pixel position - std::cout << "Cross position: " << ip << std::endl; - vpDisplay::displayCross(I1, ip, 15,vpColor::red); - vpDisplay::flush(I1); + try { + // Load a color image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(I2, filename) ; } - else { - ip.set_i( 50 ); - ip.set_j( 50 ); - // Display a red cross at position ip in the first display - std::cout << "Cross position: " << ip<< std::endl; - vpDisplay::displayCross(I1, ip, 15, vpColor::red); - vpDisplay::flush(I1); + catch(...) + { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); } - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image 1 and the overlay - vpDisplay::getImage(I1, Ioverlay) ; - - // Write the color image on the disk - filename = odirname + vpIoTools::path("/Klimt_grey.overlay.ppm"); - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - if (opt_click_allowed) { - std::cout << "\nA click in the second display to close the windows and exit..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I2) ; + // For each image, open a X11 display + vpDisplayX display1; + vpDisplayX display2; + + if (opt_display) { + // Attach image 1 to display 1 + display1.init(I1, 0, 0,"X11 Display 1...") ; + // Attach image 2 to display 2 + display2.init(I2, 200, 200,"X11 Display 2...") ; + // Display the images + vpDisplay::display(I1) ; + vpDisplay::display(I2) ; + + // In the first display, display in overlay horizontal red lines + for (unsigned int i=0 ; i < I1.getHeight() ; i+=20) { + ip1.set_i( i ); + ip1.set_j( 0 ); + ip2.set_i( i ); + ip2.set_j( I1.getWidth() ); + vpDisplay::displayLine(I1, ip1, ip2, vpColor::red) ; + } + + // In the first display, display in overlay vertical green dot lines + for (unsigned int i=0 ; i < I1.getWidth() ; i+=20) { + ip1.set_i( 0 ); + ip1.set_j( i ); + ip2.set_i( I1.getWidth() ); + ip2.set_j( i ); + vpDisplay::displayDotLine(I1, ip1, ip2, vpColor::green) ; + } + + // In the first display, display in overlay a blue arrow + ip1.set_i( 0 ); + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I1, ip1, ip2, vpColor::blue) ; + + // In the first display, display in overlay some circles. The + // position of the center is 200, 200 the radius is increased by 20 + // pixels for each circle + for (unsigned int i=0 ; i < 100 ; i+=20) { + ip.set_i( 200 ); + ip.set_j( 200 ); + vpDisplay::displayCircle(I1, ip, 20+i,vpColor::yellow) ; + } + + // In the first display, display in overlay a yellow string + ip.set_i( 100 ); + ip.set_j( 100 ); + vpDisplay::displayText(I1, ip, + "ViSP is a marvelous software", + vpColor::blue) ; + + //Flush displays. The displays must be flushed to show the overlay. + //without this line, nothing will be displayed. + vpDisplay::flush(I1); + vpDisplay::flush(I2); + + // If click is allowed, wait for a blocking mouse click in the first + // display, to display a cross at the clicked pixel position + if (opt_click_allowed) { + std::cout << "\nA click in the first display to draw a cross..." << std::endl; + // Blocking wait for a click. Get the position of the selected pixel + // (i correspond to the row and j to the column coordinates in the image) + vpDisplay::getClick(I1, ip); + // Display a red cross on the click pixel position + std::cout << "Cross position: " << ip << std::endl; + vpDisplay::displayCross(I1, ip, 15,vpColor::red); + vpDisplay::flush(I1); + } + else { + ip.set_i( 50 ); + ip.set_j( 50 ); + // Display a red cross at position ip in the first display + std::cout << "Cross position: " << ip<< std::endl; + vpDisplay::displayCross(I1, ip, 15, vpColor::red); + vpDisplay::flush(I1); + } + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image 1 and the overlay + vpDisplay::getImage(I1, Ioverlay) ; + + // Write the color image on the disk + filename = vpIoTools::createFilePath(odirname, "Klimt_grey.overlay.ppm"); + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + if (opt_click_allowed) { + std::cout << "\nA click in the second display to close the windows and exit..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I2) ; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/framegrabber/CMakeLists.txt b/example/device/framegrabber/CMakeLists.txt index 53c18727144126f1aaa6e2f75fa61c11c84a5ad2..acba5bfd8c22116c359bffea95a37efe180d430f 100644 --- a/example/device/framegrabber/CMakeLists.txt +++ b/example/device/framegrabber/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE grab1394Two.cpp grab1394CMU.cpp grabDisk.cpp @@ -55,15 +55,19 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run these tests automaticaly using the crontab under Unix or the task diff --git a/example/device/framegrabber/grab1394CMU.cpp b/example/device/framegrabber/grab1394CMU.cpp index 2f538ef92fce2cd9edec8af8930ffcbad812834e..fdca9c3efd2bd6bddb663a776bced4cdb93fae17 100644 --- a/example/device/framegrabber/grab1394CMU.cpp +++ b/example/device/framegrabber/grab1394CMU.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grab1394CMU.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: grab1394CMU.cpp 4659 2014-02-09 14:11:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,6 +64,10 @@ // List of allowed command line options #define GETOPTARGS "dhn:o:" +void usage(const char *name, const char *badparam, unsigned &nframes, std::string &opath); +bool getOptions(int argc, const char **argv, bool &display, + unsigned int &nframes, bool &save, std::string &opath); + /*! Print the program options. @@ -123,21 +127,21 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, bool &display, unsigned int &nframes, bool &save, std::string &opath) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; case 'n': - nframes = (unsigned int)atoi(optarg); break; + nframes = (unsigned int)atoi(optarg_); break; case 'o': save = true; - opath = optarg; break; + opath = optarg_; break; case 'h': usage(argv[0], NULL, nframes, opath); return false; break; default: - usage(argv[0], optarg, nframes, opath); + usage(argv[0], optarg_, nframes, opath); return false; break; } } @@ -146,7 +150,7 @@ bool getOptions(int argc, const char **argv, bool &display, // standalone param or error usage(argv[0], NULL, nframes, opath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -255,10 +259,9 @@ main(int argc, const char ** argv) std::cout << "Mean loop time: " << ttotal / nframes << " ms" << std::endl; std::cout << "Mean frequency: " << 1000./(ttotal / nframes) << " fps" << std::endl; } - catch(...) - { - std::cout << "Failure: exit" << std::endl; - return(-1); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/example/device/framegrabber/grab1394Two.cpp b/example/device/framegrabber/grab1394Two.cpp index 6b1e28a181ae140e9611f86c349566203ff22d77..401e31d7c57d116f1656ef9396f96fab22cbc448 100644 --- a/example/device/framegrabber/grab1394Two.cpp +++ b/example/device/framegrabber/grab1394Two.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grab1394Two.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: grab1394Two.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,10 +78,29 @@ // List of allowed command line options #define GETOPTARGS "b:c:df:g:hH:L:mn:io:p:rsT:v:W:" - - #define DUAL_ACQ +void usage(const char *name, const char *badparam, unsigned int camera, + const unsigned int &nframes, const std::string &opath, + const unsigned int &roi_left, const unsigned int &roi_top, + const unsigned int &roi_width, const unsigned int &roi_height, + const unsigned int &ringbuffersize, const unsigned int &panControl); +void read_options(int argc, const char **argv, bool &multi, unsigned int &camera, + unsigned int &nframes, bool &verbose_info, + bool &verbose_settings, + bool &videomode_is_set, + vp1394TwoGrabber::vp1394TwoVideoModeType &videomode, + bool &framerate_is_set, + vp1394TwoGrabber::vp1394TwoFramerateType &framerate, + bool &colorcoding_is_set, + vp1394TwoGrabber::vp1394TwoColorCodingType &colorcoding, + bool &ringbuffersize_is_set, + unsigned int &ringbuffersize, + bool &display, bool &save, std::string &opath, + unsigned int &roi_left, unsigned int &roi_top, + unsigned int &roi_width, unsigned int &roi_height, + bool &reset, + unsigned int &panControl, bool & panControl_is_set); /*! @@ -277,41 +296,41 @@ void read_options(int argc, const char **argv, bool &multi, unsigned int &camera /* * Lecture des options. */ - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': - camera = (unsigned int)atoi(optarg); break; + camera = (unsigned int)atoi(optarg_); break; case 'd': display = false; break; case 'f': framerate_is_set = true; - framerate = (vp1394TwoGrabber::vp1394TwoFramerateType) atoi(optarg); + framerate = (vp1394TwoGrabber::vp1394TwoFramerateType) atoi(optarg_); break; case 'g': colorcoding_is_set = true; - colorcoding = (vp1394TwoGrabber::vp1394TwoColorCodingType) atoi(optarg); + colorcoding = (vp1394TwoGrabber::vp1394TwoColorCodingType) atoi(optarg_); break; case 'H': - roi_height = (unsigned int) atoi(optarg); break; + roi_height = (unsigned int) atoi(optarg_); break; case 'i': verbose_info = true; break; case 'L': - roi_left = (unsigned int) atoi(optarg); break; + roi_left = (unsigned int) atoi(optarg_); break; case 'm': multi = true; break; case 'n': - nframes = (unsigned int)atoi(optarg); break; + nframes = (unsigned int)atoi(optarg_); break; case 'o': save = true; - opath = optarg; break; + opath = optarg_; break; case 'b': ringbuffersize_is_set = true; - ringbuffersize = (unsigned int) atoi(optarg); break; + ringbuffersize = (unsigned int) atoi(optarg_); break; case 'p': - panControl = (unsigned int) atoi(optarg); + panControl = (unsigned int) atoi(optarg_); panControl_is_set = true; break; case 'r': @@ -319,13 +338,13 @@ void read_options(int argc, const char **argv, bool &multi, unsigned int &camera case 's': verbose_settings = true; break; case 'T': - roi_top = (unsigned int) atoi(optarg); break; + roi_top = (unsigned int) atoi(optarg_); break; case 'v': videomode_is_set = true; - videomode = (vp1394TwoGrabber::vp1394TwoVideoModeType) atoi(optarg); + videomode = (vp1394TwoGrabber::vp1394TwoVideoModeType) atoi(optarg_); break; case 'W': - roi_width = (unsigned int) atoi(optarg); break; + roi_width = (unsigned int) atoi(optarg_); break; case 'h': case '?': usage(argv[0], NULL, camera, nframes, opath, @@ -341,7 +360,7 @@ void read_options(int argc, const char **argv, bool &multi, unsigned int &camera usage(argv[0], NULL, camera, nframes, opath, roi_left, roi_top, roi_width, roi_height, ringbuffersize, panControl); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; exit(-1); } } @@ -488,10 +507,10 @@ main(int argc, const char ** argv) vp1394TwoGrabber::vp1394TwoVideoModeType supmode = *it_lmode; if (curmode == supmode) std::cout << " * " << vp1394TwoGrabber::videoMode2string(supmode) - << " (-v " << supmode << ")" << std::endl; + << " (-v " << (int)supmode << ")" << std::endl; else std::cout << " " << vp1394TwoGrabber::videoMode2string(supmode) - << " (-v " << supmode << ")" << std::endl; + << " (-v " << (int)supmode << ")" << std::endl; if (g.isVideoModeFormat7(supmode)){ // Format 7 video mode; no framerate setting, but color @@ -503,11 +522,11 @@ main(int argc, const char ** argv) if ( (curmode == supmode) && (supcoding == curcoding) ) std::cout << " * " << vp1394TwoGrabber::colorCoding2string(supcoding) - << " (-g " << supcoding << ")" << std::endl; + << " (-g " << (int)supcoding << ")" << std::endl; else std::cout << " " << vp1394TwoGrabber::colorCoding2string(supcoding) - << " (-g " << supcoding << ")" << std::endl; + << " (-g " << (int)supcoding << ")" << std::endl; } } else { @@ -519,11 +538,11 @@ main(int argc, const char ** argv) if ( (curmode == supmode) && (supfps == curfps) ) std::cout << " * " << vp1394TwoGrabber::framerate2string(supfps) - << " (-f " << supfps << ")" << std::endl; + << " (-f " << (int)supfps << ")" << std::endl; else std::cout << " " << vp1394TwoGrabber::framerate2string(supfps) - << " (-f " << supfps << ")" << std::endl; + << " (-f " << (int)supfps << ")" << std::endl; } } } @@ -695,12 +714,12 @@ main(int argc, const char ** argv) if (display) delete [] d; #endif + return 0; } - catch (...) { - vpCERROR << "Failure: exit" << std::endl; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - std::cout << " the end" << std::endl; } #else int diff --git a/example/device/framegrabber/grabDirectShow.cpp b/example/device/framegrabber/grabDirectShow.cpp index 225e357c31e44986ee8b4fd680c3819b6a6149d1..cea7264c493eeea35ab1ab6f1fd77111a25bb262 100644 --- a/example/device/framegrabber/grabDirectShow.cpp +++ b/example/device/framegrabber/grabDirectShow.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabDirectShow.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: grabDirectShow.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -167,35 +167,34 @@ bool getOptions(int argc, const char **argv, bool &display, int main(int argc, const char ** argv) { - bool opt_display = true; - unsigned nframes = 50; - bool save = false; + try { + bool opt_display = true; + unsigned nframes = 50; + bool save = false; - // Declare an image. It size is not defined yet. It will be defined when the - // image will acquired the first time. + // Declare an image. It size is not defined yet. It will be defined when the + // image will acquired the first time. #ifdef GRAB_COLOR - vpImage<vpRGBa> I; // This is a color image (in RGBa format) + vpImage<vpRGBa> I; // This is a color image (in RGBa format) #else - vpImage<unsigned char> I; // This is a B&W image + vpImage<unsigned char> I; // This is a B&W image #endif - // Set default output image name for saving + // Set default output image name for saving #ifdef GRAB_COLOR - // Color images will be saved in PGM P6 format - std::string opath = "C:/temp/I%04d.ppm"; + // Color images will be saved in PGM P6 format + std::string opath = "C:/temp/I%04d.ppm"; #else - // B&W images will be saved in PGM P5 format - std::string opath = "C:/temp/I%04d.pgm"; + // B&W images will be saved in PGM P5 format + std::string opath = "C:/temp/I%04d.pgm"; #endif - // Read the command line options - if (getOptions(argc, argv, opt_display, nframes, save, opath) == false) { - exit (-1); - } - vpDirectShowGrabber* grabber = NULL; - try { + // Read the command line options + if (getOptions(argc, argv, opt_display, nframes, save, opath) == false) { + exit (-1); + } // Create the grabber - grabber = new vpDirectShowGrabber(); + vpDirectShowGrabber* grabber = new vpDirectShowGrabber(); //test if a camera is connected if(grabber->getDeviceNumber() == 0) @@ -209,29 +208,22 @@ main(int argc, const char ** argv) // Acquire an image grabber->acquire(I); - } - catch(...) - { - if (grabber !=NULL) delete grabber; - vpCTRACE << "Cannot acquire an image... " << std::endl ; - exit(-1); - } - std::cout << "Image size: width : " << I.getWidth() << " height: " - << I.getHeight() << std::endl; - // Creates a display + std::cout << "Image size: width : " << I.getWidth() << " height: " + << I.getHeight() << std::endl; + + // Creates a display #if defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #endif - if (opt_display) { - display.init(I,100,100,"DirectShow Framegrabber"); - } + if (opt_display) { + display.init(I,100,100,"DirectShow Framegrabber"); + } - try { double tbegin=0, tend=0, tloop=0, ttotal=0; ttotal = 0; @@ -242,8 +234,8 @@ main(int argc, const char ** argv) grabber->acquire(I); if (opt_display) { - //Displays the grabbed rgba image - vpDisplay::display(I); + //Displays the grabbed rgba image + vpDisplay::display(I); vpDisplay::flush(I); } @@ -264,14 +256,12 @@ main(int argc, const char ** argv) std::cout << "Mean frequency: " << 1000./(ttotal / nframes) << " fps" << std::endl; // Release the framegrabber - if (grabber !=NULL) delete grabber; - + delete grabber; + return 0; } - catch(...) - { - vpCERROR << "Failure: exit" << std::endl; - if (grabber !=NULL) delete grabber; - return(-1); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else // (defined (VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) diff --git a/example/device/framegrabber/grabDirectShowMulti.cpp b/example/device/framegrabber/grabDirectShowMulti.cpp index dcebf89ff1764dc7422cc53c3d2d4fb91260a92b..312c80a655384d0e543d3af5bb7e5010ffa988c3 100644 --- a/example/device/framegrabber/grabDirectShowMulti.cpp +++ b/example/device/framegrabber/grabDirectShowMulti.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabDirectShowMulti.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: grabDirectShowMulti.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -455,12 +455,12 @@ main(int argc, const char ** argv) if (display) delete [] d; - } - catch (...) { - vpCERROR << "Failure: exit" << std::endl; - } - - std::cout << " the end" << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else // (defined (VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) diff --git a/example/device/framegrabber/grabDisk.cpp b/example/device/framegrabber/grabDisk.cpp index 1ef12c91e05e891047a6dce3367f0164a0df7019..3fe498109753d5f6a536d410f56c0ce26b5e8c32 100644 --- a/example/device/framegrabber/grabDisk.cpp +++ b/example/device/framegrabber/grabDisk.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabDisk.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: grabDisk.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,15 +44,16 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> #include <stdlib.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) #include <visp/vpDiskGrabber.h> -#include <visp/vpImage.h> #include <visp/vpDisplay.h> #include <visp/vpDisplayX.h> -#include <visp/vpDisplayGTK.h> -#include <visp/vpTime.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpImage.h> +#include <visp/vpIoTools.h> #include <visp/vpParseArgv.h> +#include <visp/vpTime.h> /*! \file grabDisk.cpp @@ -66,6 +67,12 @@ // List of allowed command line options #define GETOPTARGS "b:de:f:i:hn:s:z:" +void usage(const char *name, const char *badparam, std::string ipath, std::string basename, + std::string ext, int first, unsigned int nimages, int step, unsigned int nzero); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &basename, + std::string &ext, int &first, unsigned int &nimages, + int &step, unsigned int &nzero, bool &display); + /* Print the program options. @@ -82,8 +89,7 @@ */ void usage(const char *name, const char *badparam, std::string ipath, std::string basename, - std::string ext, int first, unsigned int nimages, int step, - unsigned int nzero) + std::string ext, int first, unsigned int nimages, int step, unsigned int nzero) { fprintf(stdout, "\n\ Read an image sequence from the disk. Display it using X11 or GTK.\n\ @@ -163,25 +169,24 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ba std::string &ext, int &first, unsigned int &nimages, int &step, unsigned int &nzero, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'b': basename = optarg; break; + case 'b': basename = optarg_; break; case 'd': display = false; break; - case 'e': ext = optarg; break; - case 'f': first = atoi(optarg); break; - case 'i': ipath = optarg; break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = atoi(optarg); break; - case 'z': nzero = (unsigned) atoi(optarg); break; + case 'e': ext = optarg_; break; + case 'f': first = atoi(optarg_); break; + case 'i': ipath = optarg_; break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = atoi(optarg_); break; + case 'z': nzero = (unsigned) atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, basename, ext, first, nimages, step, nzero); return false; break; default: - usage(argv[0], optarg, ipath, basename, ext, first, nimages, - step, nzero); + usage(argv[0], optarg_, ipath, basename, ext, first, nimages, step, nzero); return false; break; } } @@ -190,7 +195,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ba // standalone param or error usage(argv[0], NULL, ipath, basename, ext, first, nimages, step, nzero); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -207,112 +212,105 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ba images. Each image corresponds to a PGM file. Display these images using X11 or GTK. */ -int -main(int argc, const char ** argv) +int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_basename = "ViSP-images/cube/image."; - std::string opt_ext = "pgm"; - bool opt_display = true; - - int opt_first = 5; - unsigned int opt_nimages = 70; - int opt_step = 1; - unsigned int opt_nzero = 4; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_basename, opt_ext, opt_first, - opt_nimages, opt_step, opt_nzero, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_basename = "ViSP-images/cube/image."; + std::string opt_ext = "pgm"; + bool opt_display = true; + + int opt_first = 5; + unsigned int opt_nimages = 70; + int opt_step = 1; + unsigned int opt_nzero = 4; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_basename, opt_ext, opt_first, + opt_nimages, opt_step, opt_nzero, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_basename, opt_ext, opt_first, + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath, opt_basename, opt_ext, opt_first, opt_nimages, opt_step, opt_nzero); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - // Declare a framegrabber able to read a sequence of successive - // images from the disk - vpDiskGrabber g; + // Declare a framegrabber able to read a sequence of successive + // images from the disk + vpDiskGrabber g; - // Set the path to the directory containing the sequence - g.setDirectory(ipath.c_str()); - // Set the image base name. The directory and the base name constitute - // the constant part of the full filename - g.setBaseName(opt_basename.c_str()); - // Set the step between two images of the sequence - g.setStep(opt_step); - // Set the number of digits to build the image number - g.setNumberOfZero(opt_nzero); - // Set the first frame number of the sequence - g.setImageNumber(opt_first); - // Set the image extension - g.setExtension(opt_ext.c_str()); + // Set the path to the directory containing the sequence + g.setDirectory(ipath.c_str()); + // Set the image base name. The directory and the base name constitute + // the constant part of the full filename + g.setBaseName(opt_basename.c_str()); + // Set the step between two images of the sequence + g.setStep(opt_step); + // Set the number of digits to build the image number + g.setNumberOfZero(opt_nzero); + // Set the first frame number of the sequence + g.setImageNumber(opt_first); + // Set the image extension + g.setExtension(opt_ext.c_str()); - // Open the framegrabber by loading the first image of the sequence - try { + // Open the framegrabber by loading the first image of the sequence g.open(I) ; - } - catch (...) { - vpERROR_TRACE("Cannot open the first image of the sequence... ") ; - exit(-1); - } - std::cout << "Image size: width : " << I.getWidth() << " height: " - << I.getHeight() << std::endl; + std::cout << "Image size: width : " << I.getWidth() << " height: " + << I.getHeight() << std::endl; - // We open a window using either X11 or GTK. - // Its size is automatically defined by the image (I) size -#if defined VISP_HAVE_X11 - vpDisplayX display; -#elif defined VISP_HAVE_GTK - vpDisplayGTK display; + // We open a window using either X11 or GDI. + // Its size is automatically defined by the image (I) size +#if defined(VISP_HAVE_X11) + vpDisplayX display(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI display(I); +#else + std::cout << "No image viewer is available..." << std::endl; #endif - if (opt_display) { - try { + if (opt_display) { display.init(I,100,100,"Disk Framegrabber"); // display the image @@ -323,16 +321,10 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Cannot display the image ") ; - exit(-1); - } - } - unsigned cpt = 1; - // this is the loop over the image sequence - try { + unsigned cpt = 1; + // this is the loop over the image sequence + while(cpt ++ < opt_nimages) { double tms = vpTime::measureTimeMs(); @@ -347,11 +339,12 @@ main(int argc, const char ** argv) } // Synchronise the loop to 40 ms vpTime::wait(tms, 40) ; - } + return 0; } - catch(...) { - vpERROR_TRACE("Error during the framegrabbing..."); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/example/device/framegrabber/grabOpenCV-2.cpp b/example/device/framegrabber/grabOpenCV-2.cpp index c7d7e1e868000b893d664c1d02bd8197dbdfdea7..a7f9c00d9f2b8acc2d71f54cded72a2a2d6bc93c 100644 --- a/example/device/framegrabber/grabOpenCV-2.cpp +++ b/example/device/framegrabber/grabOpenCV-2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabOpenCV-2.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: grabOpenCV-2.cpp 5005 2014-11-24 08:25:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,7 +52,7 @@ #include <visp/vpConfig.h> -#if defined(VISP_HAVE_OPENCV) +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) #include <visp/vpDisplayOpenCV.h> #include <visp/vpImage.h> @@ -65,41 +65,57 @@ // 1 to dial with a second camera attached to the computer int main(int argc, char** argv) { - int device = 0; - if (argc > 1) - device = atoi(argv[1]); - - std::cout << "Use device: " << device << std::endl; - cv::VideoCapture cap(device); // open the default camera - cap.set(CV_CAP_PROP_FRAME_WIDTH, 640); - cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480); - if(!cap.isOpened()) // check if we succeeded - return -1; - cv::Mat frame; - cap >> frame; // get a new frame from camera - - IplImage iplimage = frame; - std::cout << "Image size: " << iplimage.width << " " - << iplimage.height << std::endl; - - //vpImage<vpRGBa> I; // for color images - vpImage<unsigned char> I; // for gray images - vpImageConvert::convert(&iplimage, I); - vpDisplayOpenCV d(I); - - for(;;) { + try { + int device = 0; + if (argc > 1) + device = atoi(argv[1]); + + std::cout << "Use device: " << device << std::endl; + cv::VideoCapture cap(device); // open the default camera +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); + cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); +#else + cap.set(CV_CAP_PROP_FRAME_WIDTH, 640); + cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480); +#endif + if(!cap.isOpened()) // check if we succeeded + return -1; + cv::Mat frame; cap >> frame; // get a new frame from camera - iplimage = frame; - - // Convert the image in ViSP format and display it - vpImageConvert::convert(&iplimage, I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) // a click to exit - break; + + std::cout << "Image size: " +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + << (int)cap.get(cv::CAP_PROP_FRAME_WIDTH) << " " + << (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT) << std::endl; +#else + << (int)cap.get(CV_CAP_PROP_FRAME_WIDTH) << " " + << (int)cap.get(CV_CAP_PROP_FRAME_HEIGHT) << std::endl; +#endif + + //vpImage<vpRGBa> I; // for color images + vpImage<unsigned char> I; // for gray images + vpImageConvert::convert(frame, I); + + vpDisplayOpenCV d(I); + + for(;;) { + cap >> frame; // get a new frame from camera + + // Convert the image in ViSP format and display it + vpImageConvert::convert(frame, I); + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // a click to exit + break; + } + // the camera will be deinitialized automatically in VideoCapture destructor + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - // the camera will be deinitialized automatically in VideoCapture destructor - return 0; } #else diff --git a/example/device/framegrabber/grabOpenCV.cpp b/example/device/framegrabber/grabOpenCV.cpp index 012d5d4cc7a5cb0a7df634a8fef1bf9fca218554..82b278c5faabc9a24baef9158ba95c1e146cca1c 100644 --- a/example/device/framegrabber/grabOpenCV.cpp +++ b/example/device/framegrabber/grabOpenCV.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabOpenCV.cpp 4088 2013-02-04 07:59:35Z fspindle $ + * $Id: grabOpenCV.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,7 @@ */ -#if defined (VISP_HAVE_OPENCV) +#if defined (VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) #include <visp/vpOpenCVGrabber.h> @@ -63,6 +63,10 @@ // List of allowed command line options #define GETOPTARGS "dhn:o:D:" +void usage(const char *name, const char *badparam, unsigned int &nframes, std::string &opath); +bool getOptions(int argc, const char **argv, bool &display, + unsigned int &nframes, bool &save, std::string &opath, int &deviceType); + /*! Print the program options. @@ -126,33 +130,33 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, bool &display, unsigned int &nframes, bool &save, std::string &opath, int &deviceType) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; case 'D': - if (strcmp( optarg ,"ANY") == 0 ) {deviceType = CV_CAP_ANY;} - else if ( strcmp( optarg ,"MIL") == 0) {deviceType = CV_CAP_MIL;} - else if ( strcmp( optarg ,"VFW") == 0) {deviceType = CV_CAP_VFW;} - else if ( strcmp( optarg ,"V4L") == 0) {deviceType = CV_CAP_V4L;} - else if ( strcmp( optarg ,"V4L2") == 0) {deviceType = CV_CAP_V4L2;} - else if ( strcmp( optarg ,"DC1394") == 0) {deviceType = CV_CAP_DC1394;} - else if ( strcmp( optarg ,"CMU1394") == 0) {deviceType = CV_CAP_CMU1394;} - else if ( strcmp( optarg ,"DSHOW") == 0) {deviceType = CV_CAP_DSHOW;} + if (strcmp( optarg_ ,"ANY") == 0 ) {deviceType = CV_CAP_ANY;} + else if ( strcmp( optarg_ ,"MIL") == 0) {deviceType = CV_CAP_MIL;} + else if ( strcmp( optarg_ ,"VFW") == 0) {deviceType = CV_CAP_VFW;} + else if ( strcmp( optarg_ ,"V4L") == 0) {deviceType = CV_CAP_V4L;} + else if ( strcmp( optarg_ ,"V4L2") == 0) {deviceType = CV_CAP_V4L2;} + else if ( strcmp( optarg_ ,"DC1394") == 0) {deviceType = CV_CAP_DC1394;} + else if ( strcmp( optarg_ ,"CMU1394") == 0) {deviceType = CV_CAP_CMU1394;} + else if ( strcmp( optarg_ ,"DSHOW") == 0) {deviceType = CV_CAP_DSHOW;} else {std::cout << "Unknown type of device" << std::endl; deviceType = 0;} break; case 'n': - nframes = (unsigned int)atoi(optarg); break; + nframes = (unsigned int)atoi(optarg_); break; case 'o': save = true; - opath = optarg; break; + opath = optarg_; break; case 'h': usage(argv[0], NULL, nframes, opath); return false; break; default: - usage(argv[0], optarg, nframes, opath); + usage(argv[0], optarg_, nframes, opath); return false; break; } } @@ -161,7 +165,7 @@ bool getOptions(int argc, const char **argv, bool &display, // standalone param or error usage(argv[0], NULL, nframes, opath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -179,71 +183,71 @@ bool getOptions(int argc, const char **argv, bool &display, int main(int argc, const char ** argv) { - bool opt_display = true; - unsigned nframes = 50; - bool save = false; - int deviceType = CV_CAP_ANY; + try { + bool opt_display = true; + unsigned nframes = 50; + bool save = false; + int deviceType = CV_CAP_ANY; - // Declare an image. It size is not defined yet. It will be defined when the - // image will acquired the first time. + // Declare an image. It size is not defined yet. It will be defined when the + // image will acquired the first time. #ifdef GRAB_COLOR - vpImage<vpRGBa> I; // This is a color image (in RGBa format) + vpImage<vpRGBa> I; // This is a color image (in RGBa format) #else - vpImage<unsigned char> I; // This is a B&W image + vpImage<unsigned char> I; // This is a B&W image #endif - // Set default output image name for saving + // Set default output image name for saving #ifdef GRAB_COLOR - // Color images will be saved in PGM P6 format -# if defined(UNIX) - std::string opath = "/tmp/I%04d.ppm"; -# elif defined(WIN32) - std::string opath = "C:/temp/I%04d.ppm"; + // Color images will be saved in PGM P6 format +# if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + std::string opath = "/tmp/I%04d.ppm"; +# elif defined(_WIN32) + std::string opath = "C:/temp/I%04d.ppm"; # endif #else - // B&W images will be saved in PGM P5 format -# if defined(UNIX) - std::string opath = "/tmp/I%04d.pgm"; -# elif defined(WIN32) - std::string opath = "C:/temp/I%04d.pgm"; + // B&W images will be saved in PGM P5 format +# if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + std::string opath = "/tmp/I%04d.pgm"; +# elif defined(_WIN32) + std::string opath = "C:/temp/I%04d.pgm"; # endif #endif - // Read the command line options - if (getOptions(argc, argv, opt_display, nframes, save, opath, deviceType) == false) { - exit (-1); - } - // Create the grabber - vpOpenCVGrabber grabber ; - try { - // Set the type of device to detect. Here for example we expect to find a firewire camera. - grabber.setDeviceType(deviceType); + // Read the command line options + if (getOptions(argc, argv, opt_display, nframes, save, opath, deviceType) == false) { + exit (-1); + } + // Create the grabber + vpOpenCVGrabber grabber ; + try { + // Set the type of device to detect. Here for example we expect to find a firewire camera. + grabber.setDeviceType(deviceType); - // Initialize the grabber - grabber.open(I); + // Initialize the grabber + grabber.open(I); - // Acquire an image - grabber.acquire(I); - } - catch(...) - { - vpCTRACE << "Cannot acquire an image... " - << "Check if a camera is connected to your computer." - << std::endl ; - return 0; - } + // Acquire an image + grabber.acquire(I); + } + catch(...) + { + vpCTRACE << "Cannot acquire an image... " + << "Check if a camera is connected to your computer." + << std::endl ; + return 0; + } - std::cout << "Image size: width : " << I.getWidth() << " height: " - << I.getHeight() << std::endl; + std::cout << "Image size: width : " << I.getWidth() << " height: " + << I.getHeight() << std::endl; - // Creates a display - vpDisplayOpenCV display; + // Creates a display + vpDisplayOpenCV display; - if (opt_display) { - display.init(I,100,100,"OpenCV framegrabber"); - } + if (opt_display) { + display.init(I,100,100,"OpenCV framegrabber"); + } - try { double tbegin=0, tend=0, tloop=0, ttotal=0; ttotal = 0; @@ -275,11 +279,11 @@ main(int argc, const char ** argv) std::cout << "Mean loop time: " << ttotal / nframes << " ms" << std::endl; std::cout << "Mean frequency: " << 1000./(ttotal / nframes) << " fps" << std::endl; + return 0; } - catch(...) - { - vpCERROR << "Failure: exit" << std::endl; - return(-1); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else // defined (VISP_HAVE_OPENCV) diff --git a/example/device/framegrabber/grabV4l2.cpp b/example/device/framegrabber/grabV4l2.cpp index 3ed234a4dcf7b14ce44aa02a9256d2eba2eac310..8670fb83b0a30489cd53a16902a0313391195a6e 100644 --- a/example/device/framegrabber/grabV4l2.cpp +++ b/example/device/framegrabber/grabV4l2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: grabV4l2.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: grabV4l2.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -184,29 +184,29 @@ bool getOptions(int argc, const char **argv, unsigned &fps, unsigned &input, vpV4l2Grabber::vpV4l2PixelFormatType &pixelformat, vpImage_type &image_type, bool &save, std::string &opath) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'd': display = false; break; - case 'f': fps = (unsigned) atoi(optarg); break; - case 'i': input = (unsigned) atoi(optarg); break; - case 'n': niter = atol(optarg); break; + case 'f': fps = (unsigned) atoi(optarg_); break; + case 'i': input = (unsigned) atoi(optarg_); break; + case 'n': niter = atol(optarg_); break; case 'o': save = true; - opath = optarg; break; - case 'p': pixelformat = (vpV4l2Grabber::vpV4l2PixelFormatType) atoi(optarg); break; - case 's': scale = (unsigned) atoi(optarg); break; - case 't': image_type = (vpImage_type) atoi(optarg); break; - case 'v': sprintf(device, "%s", optarg); break; + opath = optarg_; break; + case 'p': pixelformat = (vpV4l2Grabber::vpV4l2PixelFormatType) atoi(optarg_); break; + case 's': scale = (unsigned) atoi(optarg_); break; + case 't': image_type = (vpImage_type) atoi(optarg_); break; + case 'v': sprintf(device, "%s", optarg_); break; case 'x': verbose = true; break; case 'h': usage(argv[0], NULL, fps, input, scale, niter, device, pixelformat, image_type, opath); return false; break; default: - usage(argv[0], optarg, fps, input, scale, niter, + usage(argv[0], optarg_, fps, input, scale, niter, device, pixelformat, image_type, opath); return false; break; } } @@ -216,7 +216,7 @@ bool getOptions(int argc, const char **argv, unsigned &fps, unsigned &input, usage(argv[0], NULL, fps, input, scale, niter, device, pixelformat, image_type, opath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -235,39 +235,39 @@ bool getOptions(int argc, const char **argv, unsigned &fps, unsigned &input, int main(int argc, const char ** argv) { - unsigned int opt_fps = 25; - unsigned int opt_input = 0; - unsigned int opt_scale = 1; - vpV4l2Grabber::vpV4l2PixelFormatType opt_pixelformat = vpV4l2Grabber::V4L2_YUYV_FORMAT; - long opt_iter = 100; - bool opt_verbose = false; - bool opt_display = true; - char opt_device[20]; - bool opt_save = false; - sprintf(opt_device, "/dev/video0"); - // Default output path for image saving - std::string opt_opath = "/tmp/I%04d.ppm"; - - vpImage_type opt_image_type = color_image; - - // Read the command line options - if (getOptions(argc, argv, opt_fps, opt_input, opt_scale, opt_display, - opt_verbose, opt_iter, opt_device, - opt_pixelformat, opt_image_type, opt_save, opt_opath) == false) { - exit (-1); - } + try { + unsigned int opt_fps = 25; + unsigned int opt_input = 0; + unsigned int opt_scale = 1; + vpV4l2Grabber::vpV4l2PixelFormatType opt_pixelformat = vpV4l2Grabber::V4L2_YUYV_FORMAT; + long opt_iter = 100; + bool opt_verbose = false; + bool opt_display = true; + char opt_device[20]; + bool opt_save = false; + sprintf(opt_device, "/dev/video0"); + // Default output path for image saving + std::string opt_opath = "/tmp/I%04d.ppm"; + + vpImage_type opt_image_type = color_image; + + // Read the command line options + if (getOptions(argc, argv, opt_fps, opt_input, opt_scale, opt_display, + opt_verbose, opt_iter, opt_device, + opt_pixelformat, opt_image_type, opt_save, opt_opath) == false) { + exit (-1); + } - // Declare an image, this is a gray level image (unsigned char) and - // an other one that is a color image. There size is not defined - // yet. It will be defined when the image will acquired the first - // time. - vpImage<unsigned char> Ig ; // grey level image - vpImage<vpRGBa> Ic ; // color image + // Declare an image, this is a gray level image (unsigned char) and + // an other one that is a color image. There size is not defined + // yet. It will be defined when the image will acquired the first + // time. + vpImage<unsigned char> Ig ; // grey level image + vpImage<vpRGBa> Ic ; // color image - // Creates the grabber - vpV4l2Grabber g; + // Creates the grabber + vpV4l2Grabber g; - try{ // Initialize the grabber g.setVerboseMode(opt_verbose); g.setDevice(opt_device); @@ -294,28 +294,17 @@ main(int argc, const char ** argv) std::cout << "Color image size: width : " << Ic.getWidth() << " height: " << Ic.getHeight() << std::endl; } - } - catch (vpException e) { - std::cout << "Catched exception: " << e.getMessage() << std::endl; - return 0; - } - catch(...) - { - vpERROR_TRACE("Cannot acquire an image...") ; - return 0; - } - // We open a window using either X11 or GTK. - // Its size is automatically defined by the image (I) size + // We open a window using either X11 or GTK. + // Its size is automatically defined by the image (I) size #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display the image // The image class has a member that specify a pointer toward // the display that has been initialized in the display declaration @@ -331,18 +320,7 @@ main(int argc, const char ** argv) vpDisplay::display(Ic) ; vpDisplay::flush(Ic) ; } - } - catch (vpException e) { - std::cout << "Exception: " << e.getMessage() << std::endl; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - try { // Acquisition loop long cpt = 1; while(cpt ++ < opt_iter) @@ -387,12 +365,12 @@ main(int argc, const char ** argv) } g.close(); + return 0; } - catch (vpException e) { - std::cout << "Exception: " << e.getMessage() << std::endl; - g.close(); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - } #else int diff --git a/example/device/kinect/CMakeLists.txt b/example/device/kinect/CMakeLists.txt index 82b768410a3679bbe3d352fa305f7d728adaf637..4bc470815db588add51b9cc5a492fb79d9973653 100644 --- a/example/device/kinect/CMakeLists.txt +++ b/example/device/kinect/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,17 +43,21 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE kinectAcquisition.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/device/kinect/kinectAcquisition.cpp b/example/device/kinect/kinectAcquisition.cpp index 391bda7f93885d81e93fc9b4d7774587c4620832..e043eefdfbb3664c8b22513e4b129717cf533903 100644 --- a/example/device/kinect/kinectAcquisition.cpp +++ b/example/device/kinect/kinectAcquisition.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: kinectAcquisition.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: kinectAcquisition.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -35,7 +35,7 @@ * Kinect example. * * Authors: - * C�line Teuli�re + * Celine Teuliere * *****************************************************************************/ @@ -64,62 +64,63 @@ #include <visp/vpTime.h> int main() { - // Init Kinect + try { + // Init Kinect #ifdef VISP_HAVE_LIBFREENECT_OLD - // This is the way to initialize Freenect with an old version of libfreenect packages under ubuntu lucid 10.04 - Freenect::Freenect<vpKinect> freenect; - vpKinect & kinect = freenect.createDevice(0); + // This is the way to initialize Freenect with an old version of libfreenect packages under ubuntu lucid 10.04 + Freenect::Freenect<vpKinect> freenect; + vpKinect & kinect = freenect.createDevice(0); #else - Freenect::Freenect freenect; - vpKinect & kinect = freenect.createDevice<vpKinect>(0); + Freenect::Freenect freenect; + vpKinect & kinect = freenect.createDevice<vpKinect>(0); #endif - // Set tilt angle in degrees - if (0) { - float angle = -3; - kinect.setTiltDegrees(angle); - } + // Set tilt angle in degrees + if (0) { + float angle = -3; + kinect.setTiltDegrees(angle); + } - // Init display + // Init display #if 1 - kinect.start(vpKinect::DMAP_MEDIUM_RES); // Start acquisition thread with a depth map resolution of 480x640 - vpImage<unsigned char> Idmap(480,640);//for medium resolution - vpImage<float> dmap(480,640);//for medium resolution + kinect.start(vpKinect::DMAP_MEDIUM_RES); // Start acquisition thread with a depth map resolution of 480x640 + vpImage<unsigned char> Idmap(480,640);//for medium resolution + vpImage<float> dmap(480,640);//for medium resolution #else - kinect.start(vpKinect::DMAP_LOW_RES); // Start acquisition thread with a depth map resolution of 240x320 (default resolution) - vpImage<unsigned char> Idmap(240,320);//for low resolution - vpImage<float> dmap(240,320);//for low resolution + kinect.start(vpKinect::DMAP_LOW_RES); // Start acquisition thread with a depth map resolution of 240x320 (default resolution) + vpImage<unsigned char> Idmap(240,320);//for low resolution + vpImage<float> dmap(240,320);//for low resolution #endif - vpImage<vpRGBa> Irgb(480,640),Iwarped(480,640); + vpImage<vpRGBa> Irgb(480,640),Iwarped(480,640); #if defined VISP_HAVE_X11 - vpDisplayX display, displayRgb, displayRgbWarped; + vpDisplayX display, displayRgb, displayRgbWarped; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; - vpDisplayGTK displayRgb; - vpDisplayGTK displayRgbWarped; + vpDisplayGTK display; + vpDisplayGTK displayRgb; + vpDisplayGTK displayRgbWarped; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; - vpDisplayOpenCV displayRgb; - vpDisplayOpenCV displayRgbWarped; + vpDisplayOpenCV display; + vpDisplayOpenCV displayRgb; + vpDisplayOpenCV displayRgbWarped; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; - vpDisplayGDI displayRgb; - vpDisplayGDI displayRgbWarped; + vpDisplayGDI display; + vpDisplayGDI displayRgb; + vpDisplayGDI displayRgbWarped; #endif - display.init(Idmap, 100, 200,"Depth map"); - displayRgb.init(Irgb, 900, 200,"Color Image"); - displayRgbWarped.init(Iwarped,900,700,"Warped Color Image"); + display.init(Idmap, 100, 200,"Depth map"); + displayRgb.init(Irgb, 900, 200,"Color Image"); + displayRgbWarped.init(Iwarped,900,700,"Warped Color Image"); - // A click to stop acquisition - std::cout << "Click in one image to stop acquisition" << std::endl; + // A click to stop acquisition + std::cout << "Click in one image to stop acquisition" << std::endl; - while(!vpDisplay::getClick(Idmap,false) && !vpDisplay::getClick(Irgb,false)) - { - kinect.getDepthMap(dmap); - kinect.getDepthMap(dmap, Idmap); - kinect.getRGB(Irgb); + while(!vpDisplay::getClick(Idmap,false) && !vpDisplay::getClick(Irgb,false)) + { + kinect.getDepthMap(dmap); + kinect.getDepthMap(dmap, Idmap); + kinect.getRGB(Irgb); vpDisplay::display(Idmap); vpDisplay::flush(Idmap); @@ -128,12 +129,21 @@ int main() { //Warped RGB image: kinect.warpRGBFrame(Irgb,dmap, Iwarped); - vpDisplay::display(Iwarped); - vpDisplay::flush(Iwarped); - } - std::cout << "Stop acquisition" << std::endl; - kinect.stop(); // Stop acquisition thread - return 0; + vpDisplay::display(Iwarped); + vpDisplay::flush(Iwarped); + } + std::cout << "Stop acquisition" << std::endl; + kinect.stop(); // Stop acquisition thread + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } + catch(...) { + std::cout << "Catch an exception " << std::endl; + return 1; + } } #else diff --git a/example/device/laserscanner/CMakeLists.txt b/example/device/laserscanner/CMakeLists.txt index 192ea37e873cfe6143c86d50d24d2bf1c2002e32..5ca5f10e50e3774e01b3d4fe325b80757309aeee 100644 --- a/example/device/laserscanner/CMakeLists.txt +++ b/example/device/laserscanner/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE SickLDMRS-Acq.cpp SickLDMRS-Process.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - #ADD_TEST(${binary} ${binary}) - -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/device/laserscanner/SickLDMRS-Acq.cpp b/example/device/laserscanner/SickLDMRS-Acq.cpp index bf81bbbd7bd2718165cb499b3631bf6663397c46..940a7ac0ceac60a0e81d816354257ec4d64d78b0 100644 --- a/example/device/laserscanner/SickLDMRS-Acq.cpp +++ b/example/device/laserscanner/SickLDMRS-Acq.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: SickLDMRS-Acq.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: SickLDMRS-Acq.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,28 +55,34 @@ #include <visp/vpParseArgv.h> -#if ( defined(UNIX) && ( ! defined(WIN32) ) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) int main() { - vpSickLDMRS laser; - std::string ip = "131.254.12.119"; - - laser.setIpAddress(ip); - laser.setup(); - unsigned long int iter = 0; + try { + vpSickLDMRS laser; + std::string ip = "131.254.12.119"; - for ( ; ; ) { - double t1 = vpTime::measureTimeMs(); - vpLaserScan laserscan[4]; - if (laser.measure(laserscan) == false) - continue; - - iter ++; - std::cout << "iter: " << iter << " time: " - << vpTime::measureTimeMs() - t1 << " ms" << std::endl; + laser.setIpAddress(ip); + laser.setup(); + unsigned long int iter = 0; + + for ( ; ; ) { + double t1 = vpTime::measureTimeMs(); + vpLaserScan laserscan[4]; + if (laser.measure(laserscan) == false) + continue; + + iter ++; + std::cout << "iter: " << iter << " time: " + << vpTime::measureTimeMs() - t1 << " ms" << std::endl; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - return 0; } #else // #ifdef UNIX diff --git a/example/device/laserscanner/SickLDMRS-Process.cpp b/example/device/laserscanner/SickLDMRS-Process.cpp index c4f43b9b0647d2964113fdac5823f6b38058349e..9a2ad7bad095330df3c1cfabf76c85d07eced7a1 100644 --- a/example/device/laserscanner/SickLDMRS-Process.cpp +++ b/example/device/laserscanner/SickLDMRS-Process.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: SickLDMRS-Process.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: SickLDMRS-Process.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ #include <visp/vp1394TwoGrabber.h> #include <visp/vpIoTools.h> -#if ( defined(UNIX) && ( ! defined(WIN32) ) ) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)) +#if ( !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) ) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK)) static int save = 0; static int layerToDisplay = 0xF; // 0xF = 1111 => all the layers are selected @@ -85,6 +85,9 @@ pthread_mutex_t shm_mutex; #endif std::string output_path; +void *laser_display_and_save_loop(void *); +void *laser_acq_loop(void *); +void *camera_acq_and_display_loop(void *); void *laser_display_and_save_loop(void *) { @@ -307,60 +310,66 @@ void *camera_acq_and_display_loop(void *) int main(int argc, const char ** argv) { - output_path = "data"; - // Test if the output path directory exist. If no try to create it - if (vpIoTools::checkDirectory(output_path) == false) { - try { - // Create a directory with name "username" - vpIoTools::makeDirectory(output_path); + try { + output_path = "data"; + // Test if the output path directory exist. If no try to create it + if (vpIoTools::checkDirectory(output_path) == false) { + try { + // Create a directory with name "username" + vpIoTools::makeDirectory(output_path); + } + catch (...) { + std::cout << "Cannot create " << output_path << " directory" << std::endl; + return false; + } } - catch (...) { - std::cout << "Cannot create " << output_path << " directory" << std::endl; - return false; + + // Parse the command line to set the variables + vpParseArgv::vpArgvInfo argTable[] = + { + {"-layer", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &layerToDisplay, + "The layer to display:\n" + "\t\t. 0x1 for layer 1.\n" + "\t\t. 0x2 for layer 2.\n" + "\t\t. 0x4 for layer 3.\n" + "\t\t. 0x8 for layer 4.\n" + "\t\tTo display all the layers you should set 0xF value." + }, + {"-save", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &save, + "Turn to 1 in order to save data." + }, + {"-h", vpParseArgv::ARGV_HELP, (char*) NULL, (char *) NULL, + "Display one or more measured layers form a Sick LD-MRS laser scanner."}, + {(char*) NULL, vpParseArgv::ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL} + } ; + + // Read the command line options + if(vpParseArgv::parse(&argc, argv, argTable, + vpParseArgv::ARGV_NO_LEFTOVERS | + vpParseArgv::ARGV_NO_ABBREV | + vpParseArgv::ARGV_NO_DEFAULTS)) { + return (false); } - } - // Parse the command line to set the variables - vpParseArgv::vpArgvInfo argTable[] = - { - {"-layer", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &layerToDisplay, - "The layer to display:\n" - "\t\t. 0x1 for layer 1.\n" - "\t\t. 0x2 for layer 2.\n" - "\t\t. 0x4 for layer 3.\n" - "\t\t. 0x8 for layer 4.\n" - "\t\tTo display all the layers you should set 0xF value." - }, - {"-save", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &save, - "Turn to 1 in order to save data." - }, - {"-h", vpParseArgv::ARGV_HELP, (char*) NULL, (char *) NULL, - "Display one or more measured layers form a Sick LD-MRS laser scanner."}, - {(char*) NULL, vpParseArgv::ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL} - } ; - - // Read the command line options - if(vpParseArgv::parse(&argc, argv, argTable, - vpParseArgv::ARGV_NO_LEFTOVERS | - vpParseArgv::ARGV_NO_ABBREV | - vpParseArgv::ARGV_NO_DEFAULTS)) { - return (false); - } - - time_offset = vpTime::measureTimeSecond(); + time_offset = vpTime::measureTimeSecond(); #ifdef VISP_HAVE_PTHREAD - pthread_t thread_camera_acq; - pthread_t thread_laser_acq; - pthread_t thread_laser_display; - pthread_create(&thread_camera_acq, NULL, &camera_acq_and_display_loop, NULL); - pthread_create(&thread_laser_acq, NULL, &laser_acq_loop, NULL); - pthread_create(&thread_laser_display, NULL, &laser_display_and_save_loop, NULL); - pthread_join(thread_camera_acq, 0); - pthread_join(thread_laser_acq, 0); - pthread_join(thread_laser_display, 0); + pthread_t thread_camera_acq; + pthread_t thread_laser_acq; + pthread_t thread_laser_display; + pthread_create(&thread_camera_acq, NULL, &camera_acq_and_display_loop, NULL); + pthread_create(&thread_laser_acq, NULL, &laser_acq_loop, NULL); + pthread_create(&thread_laser_display, NULL, &laser_display_and_save_loop, NULL); + pthread_join(thread_camera_acq, 0); + pthread_join(thread_laser_acq, 0); + pthread_join(thread_laser_display, 0); #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else // #ifdef UNIX and display diff --git a/example/device/light/CMakeLists.txt b/example/device/light/CMakeLists.txt index 52bbd0dbc03098052a534ac643abc8565a5e52ee..2628f47a98f083211327caff63ce84f654825a40 100644 --- a/example/device/light/CMakeLists.txt +++ b/example/device/light/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,21 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE ringLight.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - #ADD_TEST(${binary} ${binary}) - -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/device/light/ringLight.cpp b/example/device/light/ringLight.cpp index f9307a2965d861ea740c490c43fb39a04644ca0f..324ea0388c0d7497292c3084f8f15272b18bef34 100644 --- a/example/device/light/ringLight.cpp +++ b/example/device/light/ringLight.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: ringLight.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: ringLight.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -166,15 +166,15 @@ bool getOptions(int argc, const char **argv, bool &on, int &nsec, double &nmsec) int main(int argc, const char **argv) { - bool on = false; - int nsec = 5; // Time while the ring light is turned on - double nmsec = 0; // Pulse duration - - // Read the command line options - if (getOptions(argc, argv, on, nsec, nmsec) == false) { - exit (-1); - } try { + bool on = false; + int nsec = 5; // Time while the ring light is turned on + double nmsec = 0; // Pulse duration + + // Read the command line options + if (getOptions(argc, argv, on, nsec, nmsec) == false) { + exit (-1); + } vpRingLight light; diff --git a/example/direct-visual-servoing/CMakeLists.txt b/example/direct-visual-servoing/CMakeLists.txt index 1df6f762f1ebc6031ea520bf91871282ce1068cc..288a5abb72853bf7a5272c85062dd6b52bfdc0d7 100644 --- a/example/direct-visual-servoing/CMakeLists.txt +++ b/example/direct-visual-servoing/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,23 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE photometricVisualServoing.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() -ADD_TEST(photometricVisualServoing photometricVisualServoing -c -n 20 ${OPTION_TO_DESACTIVE_DISPLAY}) - - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(photometricVisualServoing photometricVisualServoing -c -n 20 ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/direct-visual-servoing/photometricVisualServoing.cpp b/example/direct-visual-servoing/photometricVisualServoing.cpp index 2027f6498ad6290bd0d7f25d4fca253b1dd98943..c9af07b162d7fc08ba6b782c44a5c2ac9f57bc32 100755 --- a/example/direct-visual-servoing/photometricVisualServoing.cpp +++ b/example/direct-visual-servoing/photometricVisualServoing.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: photometricVisualServoing.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: photometricVisualServoing.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,10 +41,7 @@ /*! \example photometricVisualServoing.cpp - Implemented from C. Collewet, E. Marchand, F. Chaumette. Visual - servoing set free from image processing. In IEEE Int. Conf. on - Robotics and Automation, ICRA'08, Pages 81-86, Pasadena, Californie, - Mai 2008. + Implemented from \cite Collewet08c. */ @@ -79,6 +76,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:n:h" +void usage(const char *name, const char *badparam, std::string ipath, int niter); +bool getOptions(int argc, const char **argv, std::string &ipath, + bool &click_allowed, bool &display, int &niter); + /*! Print the program options. @@ -141,19 +142,19 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, int &niter) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'n': niter = atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'n': niter = atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, niter); return false; break; default: - usage(argv[0], optarg, ipath, niter); + usage(argv[0], optarg_, ipath, niter); return false; break; } } @@ -162,7 +163,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath, niter); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -174,321 +175,304 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - int opt_niter = 400; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, - opt_display, opt_niter) == false) { - return (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + int opt_niter = 400; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, + opt_display, opt_niter) == false) { + return (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_niter); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath, opt_niter); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - vpImage<unsigned char> Itexture ; - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(Itexture,filename) ; - - vpColVector X[4]; - for (int i = 0; i < 4; i++) X[i].resize(3); - // Top left corner - X[0][0] = -0.3; - X[0][1] = -0.215; - X[0][2] = 0; - - // Top right corner - X[1][0] = 0.3; - X[1][1] = -0.215; - X[1][2] = 0; - - // Bottom right corner - X[2][0] = 0.3; - X[2][1] = 0.215; - X[2][2] = 0; - - //Bottom left corner - X[3][0] = -0.3; - X[3][1] = 0.215; - X[3][2] = 0; - - vpImageSimulator sim; - - sim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION) ; - sim.init(Itexture, X); - - - - - vpCameraParameters cam(870, 870, 160, 120); - - // ---------------------------------------------------------- - // Create the framegraber (here a simulated image) - vpImage<unsigned char> I(240,320,0) ; - vpImage<unsigned char> Id ; - - //camera desired position - vpHomogeneousMatrix cdMo ; - cdMo[2][3] = 1 ; - - //set the robot at the desired position - sim.setCameraPosition(cdMo) ; - sim.getImage(I,cam); // and aquire the image Id - Id = I ; - - - // display the image -#if defined VISP_HAVE_X11 - vpDisplayX d; -#elif defined VISP_HAVE_GDI - vpDisplayGDI d; -#elif defined VISP_HAVE_GTK - vpDisplayGTK d; -#endif + vpImage<unsigned char> Itexture ; + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(Itexture,filename) ; -#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) - if (opt_display) { - d.init(I, 20, 10, "Photometric visual servoing : s") ; - vpDisplay::display(I); - vpDisplay::flush(I); - } - if (opt_display && opt_click_allowed) { - std::cout << "Click in the image to continue..." << std::endl; - vpDisplay::getClick(I) ; - } -#endif + vpColVector X[4]; + for (int i = 0; i < 4; i++) X[i].resize(3); + // Top left corner + X[0][0] = -0.3; + X[0][1] = -0.215; + X[0][2] = 0; + // Top right corner + X[1][0] = 0.3; + X[1][1] = -0.215; + X[1][2] = 0; - // ---------------------------------------------------------- - // position the robot at the initial position - // ---------------------------------------------------------- + // Bottom right corner + X[2][0] = 0.3; + X[2][1] = 0.215; + X[2][2] = 0; - //camera desired position - vpHomogeneousMatrix cMo ; - cMo.buildFrom(0,0,1.2,vpMath::rad(15),vpMath::rad(-5),vpMath::rad(20)); - - //set the robot at the desired position - sim.setCameraPosition(cMo) ; - I =0 ; - sim.getImage(I,cam); // and aquire the image Id - -#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) - if (opt_display) { - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } - if (opt_display && opt_click_allowed) { - std::cout << "Click in the image to continue..." << std::endl; - vpDisplay::getClick(I) ; - } -#endif + //Bottom left corner + X[3][0] = -0.3; + X[3][1] = 0.215; + X[3][2] = 0; + + vpImageSimulator sim; + + sim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION) ; + sim.init(Itexture, X); - vpImage<unsigned char> Idiff ; - Idiff = I ; + vpCameraParameters cam(870, 870, 160, 120); + // ---------------------------------------------------------- + // Create the framegraber (here a simulated image) + vpImage<unsigned char> I(240,320,0) ; + vpImage<unsigned char> Id ; - vpImageTools::imageDifference(I,Id,Idiff) ; + //camera desired position + vpHomogeneousMatrix cdMo ; + cdMo[2][3] = 1 ; + //set the robot at the desired position + sim.setCameraPosition(cdMo) ; + sim.getImage(I,cam); // and aquire the image Id + Id = I ; - // Affiche de l'image de difference + // display the image #if defined VISP_HAVE_X11 - vpDisplayX d1; + vpDisplayX d; #elif defined VISP_HAVE_GDI - vpDisplayGDI d1; + vpDisplayGDI d; #elif defined VISP_HAVE_GTK - vpDisplayGTK d1; + vpDisplayGTK d; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV d; #endif -#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) - if (opt_display) { - d1.init(Idiff, 40+(int)I.getWidth(), 10, "photometric visual servoing : s-s* ") ; - vpDisplay::display(Idiff) ; - vpDisplay::flush(Idiff) ; - } -#endif - // create the robot (here a simulated free flying camera) - vpRobotCamera robot ; - robot.setSamplingTime(0.04); - robot.setPosition(cMo) ; - - // ------------------------------------------------------ - // Visual feature, interaction matrix, error - // s, Ls, Lsd, Lt, Lp, etc - // ------------------------------------------------------ - - // current visual feature built from the image - // (actually, this is the image...) - vpFeatureLuminance sI ; - sI.init( I.getHeight(), I.getWidth(), Z) ; - sI.setCameraParameters(cam) ; - sI.buildFrom(I) ; - - - // desired visual feature built from the image - vpFeatureLuminance sId ; - sId.init(I.getHeight(), I.getWidth(), Z) ; - sId.setCameraParameters(cam) ; - sId.buildFrom(Id) ; +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_OPENCV) + if (opt_display) { + d.init(I, 20, 10, "Photometric visual servoing : s") ; + vpDisplay::display(I); + vpDisplay::flush(I); + } + if (opt_display && opt_click_allowed) { + std::cout << "Click in the image to continue..." << std::endl; + vpDisplay::getClick(I) ; + } +#endif - - // Matrice d'interaction, Hessien, erreur,... - vpMatrix Lsd; // matrice d'interaction a la position desiree - vpMatrix Hsd; // hessien a la position desiree - vpMatrix H ; // Hessien utilise pour le levenberg-Marquartd - vpColVector error ; // Erreur I-I* - - // Compute the interaction matrix - // link the variation of image intensity to camera motion - - // here it is computed at the desired position - sId.interaction(Lsd) ; + // ---------------------------------------------------------- + // position the robot at the initial position + // ---------------------------------------------------------- - - // Compute the Hessian H = L^TL - Hsd = Lsd.AtA() ; + //camera desired position + vpHomogeneousMatrix cMo ; + cMo.buildFrom(0,0,1.2,vpMath::rad(15),vpMath::rad(-5),vpMath::rad(20)); - // Compute the Hessian diagonal for the Levenberg-Marquartd - // optimization process - unsigned int n = 6 ; - vpMatrix diagHsd(n,n) ; - diagHsd.eye(n); - for(unsigned int i = 0 ; i < n ; i++) diagHsd[i][i] = Hsd[i][i]; + //set the robot at the desired position + sim.setCameraPosition(cMo) ; + I =0 ; + sim.getImage(I,cam); // and aquire the image Id +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) + if (opt_display) { + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } + if (opt_display && opt_click_allowed) { + std::cout << "Click in the image to continue..." << std::endl; + vpDisplay::getClick(I) ; + } +#endif + vpImage<unsigned char> Idiff ; + Idiff = I ; - // ------------------------------------------------------ - // Control law - double lambda ; //gain - vpColVector e ; - vpColVector v ; // camera velocity send to the robot + vpImageTools::imageDifference(I,Id,Idiff) ; + // Affiche de l'image de difference +#if defined VISP_HAVE_X11 + vpDisplayX d1; +#elif defined VISP_HAVE_GDI + vpDisplayGDI d1; +#elif defined VISP_HAVE_GTK + vpDisplayGTK d1; +#endif +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) + if (opt_display) { + d1.init(Idiff, 40+(int)I.getWidth(), 10, "photometric visual servoing : s-s* ") ; + vpDisplay::display(Idiff) ; + vpDisplay::flush(Idiff) ; + } +#endif + // create the robot (here a simulated free flying camera) + vpRobotCamera robot ; + robot.setSamplingTime(0.04); + robot.setPosition(cMo) ; + + // ------------------------------------------------------ + // Visual feature, interaction matrix, error + // s, Ls, Lsd, Lt, Lp, etc + // ------------------------------------------------------ + + // current visual feature built from the image + // (actually, this is the image...) + vpFeatureLuminance sI ; + sI.init( I.getHeight(), I.getWidth(), Z) ; + sI.setCameraParameters(cam) ; + sI.buildFrom(I) ; - // ---------------------------------------------------------- - // Minimisation + // desired visual feature built from the image + vpFeatureLuminance sId ; + sId.init(I.getHeight(), I.getWidth(), Z) ; + sId.setCameraParameters(cam) ; + sId.buildFrom(Id) ; - double mu ; // mu = 0 : Gauss Newton ; mu != 0 : LM - double lambdaGN; + // Matrice d'interaction, Hessien, erreur,... + vpMatrix Lsd; // matrice d'interaction a la position desiree + vpMatrix Hsd; // hessien a la position desiree + vpMatrix H ; // Hessien utilise pour le levenberg-Marquartd + vpColVector error ; // Erreur I-I* + // Compute the interaction matrix + // link the variation of image intensity to camera motion - mu = 0.01; - lambda = 30 ; - lambdaGN = 30; + // here it is computed at the desired position + sId.interaction(Lsd) ; + // Compute the Hessian H = L^TL + Hsd = Lsd.AtA() ; + // Compute the Hessian diagonal for the Levenberg-Marquartd + // optimization process + unsigned int n = 6 ; + vpMatrix diagHsd(n,n) ; + diagHsd.eye(n); + for(unsigned int i = 0 ; i < n ; i++) diagHsd[i][i] = Hsd[i][i]; + // ------------------------------------------------------ + // Control law + double lambda ; //gain + vpColVector e ; + vpColVector v ; // camera velocity send to the robot + // ---------------------------------------------------------- + // Minimisation + double mu ; // mu = 0 : Gauss Newton ; mu != 0 : LM + double lambdaGN; - // set a velocity control mode - robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL) ; + mu = 0.01; + lambda = 30 ; + lambdaGN = 30; - // ---------------------------------------------------------- - int iter = 1; - int iterGN = 90 ; // swicth to Gauss Newton after iterGN iterations - - double normeError = 0; - do - { + // set a velocity control mode + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL) ; - std::cout << "--------------------------------------------" << iter++ << std::endl ; + // ---------------------------------------------------------- + int iter = 1; + int iterGN = 90 ; // swicth to Gauss Newton after iterGN iterations + double normeError = 0; + do { + std::cout << "--------------------------------------------" << iter++ << std::endl ; - // Acquire the new image - sim.setCameraPosition(cMo) ; - sim.getImage(I,cam) ; + // Acquire the new image + sim.setCameraPosition(cMo) ; + sim.getImage(I,cam) ; #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) - if (opt_display) { - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } + if (opt_display) { + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } #endif - vpImageTools::imageDifference(I,Id,Idiff) ; + vpImageTools::imageDifference(I,Id,Idiff) ; #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_GTK) - if (opt_display) { - vpDisplay::display(Idiff) ; - vpDisplay::flush(Idiff) ; - } + if (opt_display) { + vpDisplay::display(Idiff) ; + vpDisplay::flush(Idiff) ; + } #endif - // Compute current visual feature - sI.buildFrom(I) ; + // Compute current visual feature + sI.buildFrom(I) ; - // compute current error - sI.error(sId,error) ; + // compute current error + sI.error(sId,error) ; - normeError = (error.sumSquare()); - std::cout << "|e| "<<normeError <<std::endl ; + normeError = (error.sumSquare()); + std::cout << "|e| "<<normeError <<std::endl ; - // double t = vpTime::measureTimeMs() ; + // double t = vpTime::measureTimeMs() ; - // ---------- Levenberg Marquardt method -------------- - { - if (iter > iterGN) + // ---------- Levenberg Marquardt method -------------- { - mu = 0.0001 ; - lambda = lambdaGN; + if (iter > iterGN) + { + mu = 0.0001 ; + lambda = lambdaGN; + } + + // Compute the levenberg Marquartd term + { + H = ((mu * diagHsd) + Hsd).inverseByLU(); + } + // compute the control law + e = H * Lsd.t() *error ; + + v = - lambda*e; } - // Compute the levenberg Marquartd term - { - H = ((mu * diagHsd) + Hsd).inverseByLU(); - } - // compute the control law - e = H * Lsd.t() *error ; + std::cout << "lambda = " << lambda << " mu = " << mu ; + std::cout << " |Tc| = " << sqrt(v.sumSquare()) << std::endl; - v = - lambda*e; - } + // send the robot velocity + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + robot.getPosition(cMo) ; - std::cout << "lambda = " << lambda << " mu = " << mu ; - std::cout << " |Tc| = " << sqrt(v.sumSquare()) << std::endl; + } + while(normeError > 10000 && iter < opt_niter); - // send the robot velocity + v = 0 ; robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - robot.getPosition(cMo) ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - while(normeError > 10000 && iter < opt_niter); - - v = 0 ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - } diff --git a/example/homography/CMakeLists.txt b/example/homography/CMakeLists.txt index 39967e825c775f5b9dd839fca83f2a2dd00a50d2..74aeeb3eaf5013bf334d409196bf5ec76203e76e 100644 --- a/example/homography/CMakeLists.txt +++ b/example/homography/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,28 +43,27 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE -homographyHartleyDLT2DObject.cpp -homographyHLM2DObject.cpp -homographyHLM3DObject.cpp -homographyRansac2DObject.cpp +set(SOURCE + homographyHartleyDLT2DObject.cpp + homographyHLM2DObject.cpp + homographyHLM3DObject.cpp + homographyRansac2DObject.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test ADD_TEST(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/homography/homographyHLM2DObject.cpp b/example/homography/homographyHLM2DObject.cpp index 34e62d4cd86dbe43ffee074928726c7e29c4028f..3966301a00e861d0e852f6019f2196b859b2f01c 100644 --- a/example/homography/homographyHLM2DObject.cpp +++ b/example/homography/homographyHLM2DObject.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: homographyHLM2DObject.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: homographyHLM2DObject.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,9 @@ #define L 0.1 #define nbpt 5 +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -112,15 +115,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -129,7 +132,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -140,124 +143,79 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - int i ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpPoint P[nbpt] ; // Point to be tracked - double xa[nbpt], ya[nbpt] ; - double xb[nbpt], yb[nbpt] ; + vpPoint P[nbpt] ; // Point to be tracked + std::vector<double> xa(nbpt), ya(nbpt) ; + std::vector<double> xb(nbpt), yb(nbpt) ; - vpPoint aP[nbpt] ; // Point to be tracked - vpPoint bP[nbpt] ; // Point to be tracked + vpPoint aP[nbpt] ; // Point to be tracked + vpPoint bP[nbpt] ; // Point to be tracked - P[0].setWorldCoordinates(-L,-L, 0 ) ; - P[1].setWorldCoordinates(2*L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-L,3*L, 0 ) ; - P[4].setWorldCoordinates(0,0, 0 ) ; - /* + P[0].setWorldCoordinates(-L,-L, 0 ) ; + P[1].setWorldCoordinates(2*L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-L,3*L, 0 ) ; + P[4].setWorldCoordinates(0,0, 0 ) ; + /* P[5].setWorldCoordinates(10,20, 0 ) ; P[6].setWorldCoordinates(-10,12, 0 ) ; */ - vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; - vpHomogeneousMatrix aMb(1,0,0.0,vpMath::rad(10),0,vpMath::rad(40)) ; - vpHomogeneousMatrix aMo =aMb*bMo ; - for(i=0 ; i < nbpt ; i++) - { - P[i].project(aMo) ; - aP[i] = P[i] ; - xa[i] = P[i].get_x() ; - ya[i] = P[i].get_y() ; - } + vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; + vpHomogeneousMatrix aMb(1,0,0.0,vpMath::rad(10),0,vpMath::rad(40)) ; + vpHomogeneousMatrix aMo =aMb*bMo ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(aMo) ; + aP[i] = P[i] ; + xa[i] = P[i].get_x() ; + ya[i] = P[i].get_y() ; + } - for(i=0 ; i < nbpt ; i++) - { - P[i].project(bMo) ; - bP[i] = P[i] ; - xb[i] = P[i].get_x() ; - yb[i] = P[i].get_y() ; - } - std::cout << "-------------------------------" <<std::endl ; - std::cout << "aMb "<<std::endl <<aMb << std::endl ; - std::cout << "-------------------------------" <<std::endl ; - vpHomography aHb ; - - vpHomography::HLM(nbpt,xb,yb,xa,ya,true, aHb) ; - - vpTRACE("aHb computed using the Malis paralax algorithm") ; - aHb /= aHb[2][2] ; - std::cout << std::endl << aHb<< std::endl ; - - vpRotationMatrix aRb ; - vpTranslationVector aTb ; - vpColVector n ; - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("extract R, T and n ") ; - aHb.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("Compare with built homography H = R + t/d ") ; - vpPlane bp(0,0,1,1) ; - vpHomography aHb_built(aMb,bp) ; - vpTRACE( "aHb built from the displacement ") ; - std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; - - aHb_built.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("test if ap = aHb bp") ; - - for(i=0 ; i < nbpt ; i++) - { - std::cout << "Point "<< i<< std::endl ; - vpPoint p ; - std::cout << "(" ; - std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; - std::cout <<") = (" ; - p = aHb*bP[i] ; - std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; - } + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(bMo) ; + bP[i] = P[i] ; + xb[i] = P[i].get_x() ; + yb[i] = P[i].get_y() ; + } + std::cout << "-------------------------------" <<std::endl ; + std::cout << "aMb "<<std::endl <<aMb << std::endl ; + std::cout << "-------------------------------" <<std::endl ; + vpHomography aHb ; + + vpHomography::HLM(xb, yb, xa, ya, true, aHb) ; - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("test displacement") ; + aHb /= aHb[2][2] ; + std::cout << "aHb computed using the Malis paralax algorithm: \n" << aHb<< std::endl ; - std::list<vpRotationMatrix> laRb ; - std::list<vpTranslationVector> laTb ; - std::list<vpColVector> lnb ; + vpRotationMatrix aRb ; + vpTranslationVector aTb ; + vpColVector n ; - vpHomography::computeDisplacement(aHb,bP[0].get_x(),bP[0].get_y(), - laRb, laTb, lnb) ; + std::cout << "-------------------------------" <<std::endl ; + std::cout << "extract R, T and n " << std::endl; + aHb.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; - std::list<vpRotationMatrix>::const_iterator it_laRb = laRb.begin(); - std::list<vpTranslationVector>::const_iterator it_laTb = laTb.begin(); - std::list<vpColVector>::const_iterator it_lnb = lnb.begin(); - int k =1 ; - while (it_lnb != lnb.end()) - { - std::cout << "Solution " << k++ << std::endl ; + std::cout << "-------------------------------" <<std::endl ; + std::cout << "Compare with built homography H = R + t/d " << std::endl ; + vpPlane bp(0,0,1,1) ; + vpHomography aHb_built(aMb,bp) ; + std::cout << "aHb built from the displacement " << std::endl ; + std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; - aRb = *it_laRb; - aTb = *it_laTb; - n = *it_lnb; + aHb_built.computeDisplacement(aRb, aTb, n) ; std::cout << "Rotation: aRb" <<std::endl ; std::cout << aRb << std::endl ; std::cout << "Translation: aTb" <<std::endl; @@ -265,9 +223,57 @@ main(int argc, const char ** argv) std::cout << "Normal to the plane: n" <<std::endl; std::cout << (n).t() <<std::endl ; - ++ it_laRb; - ++ it_laTb; - ++ it_lnb; - } + std::cout << "-------------------------------" << std::endl ; + std::cout << "test if ap = aHb bp" << std::endl ; + + for(unsigned int i=0 ; i < nbpt ; i++) + { + std::cout << "Point "<< i<< std::endl ; + vpPoint p ; + std::cout << "(" ; + std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; + std::cout <<") = (" ; + p = aHb*bP[i] ; + std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; + } + std::cout << "-------------------------------" <<std::endl ; + std::cout << "test displacement" << std::endl ; + + std::list<vpRotationMatrix> laRb ; + std::list<vpTranslationVector> laTb ; + std::list<vpColVector> lnb ; + + vpHomography::computeDisplacement(aHb,bP[0].get_x(),bP[0].get_y(), + laRb, laTb, lnb) ; + + std::list<vpRotationMatrix>::const_iterator it_laRb = laRb.begin(); + std::list<vpTranslationVector>::const_iterator it_laTb = laTb.begin(); + std::list<vpColVector>::const_iterator it_lnb = lnb.begin(); + + int k =1 ; + while (it_lnb != lnb.end()) + { + std::cout << "Solution " << k++ << std::endl ; + + aRb = *it_laRb; + aTb = *it_laTb; + n = *it_lnb; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + ++ it_laRb; + ++ it_laTb; + ++ it_lnb; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/homography/homographyHLM3DObject.cpp b/example/homography/homographyHLM3DObject.cpp index 03fd41bbdc0e72760ac1ee15e9945fbccd22f46e..ffe94ae719c325c7ef30f4cc42660dc5fea84dfc 100644 --- a/example/homography/homographyHLM3DObject.cpp +++ b/example/homography/homographyHLM3DObject.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: homographyHLM3DObject.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: homographyHLM3DObject.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -72,6 +72,9 @@ #define L 0.1 #define nbpt 11 +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -111,15 +114,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -128,7 +131,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -139,106 +142,105 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - int i ; - - vpPoint P[nbpt] ; // Point to be tracked - double xa[nbpt], ya[nbpt] ; - double xb[nbpt], yb[nbpt] ; - - vpPoint aP[nbpt] ; // Point to be tracked - vpPoint bP[nbpt] ; // Point to be tracked - - P[0].setWorldCoordinates(-L,-L, 0 ) ; - P[1].setWorldCoordinates(2*L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-L,3*L, 0 ) ; - P[4].setWorldCoordinates(0,0, L ) ; - P[5].setWorldCoordinates(L,-2*L, L ) ; - P[6].setWorldCoordinates(L,-4*L, 2*L ) ; - P[7].setWorldCoordinates(-2*L,-L, -L ) ; - P[8].setWorldCoordinates(-5*L,-5*L, L ) ; - P[9].setWorldCoordinates(-2*L,+3*L, 2*L ) ; - P[10].setWorldCoordinates(-2*L,-0.5*L, 2*L ) ; - /* - P[5].setWorldCoordinates(10,20, 0 ) ; - P[6].setWorldCoordinates(-10,12, 0 ) ; - */ - vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; - vpHomogeneousMatrix aMb(0.1,0.1,0.1,vpMath::rad(10),0,vpMath::rad(40)) ; - vpHomogeneousMatrix aMo =aMb*bMo ; - for(i=0 ; i < nbpt ; i++) - { - P[i].project(aMo) ; - aP[i] = P[i] ; - xa[i] = P[i].get_x() ; - ya[i] = P[i].get_y() ; - } + vpPoint P[nbpt] ; // Point to be tracked + std::vector<double> xa(nbpt), ya(nbpt) ; + std::vector<double> xb(nbpt), yb(nbpt) ; + + vpPoint aP[nbpt] ; // Point to be tracked + vpPoint bP[nbpt] ; // Point to be tracked + + P[0].setWorldCoordinates(-L,-L, 0 ) ; + P[1].setWorldCoordinates(2*L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-L,3*L, 0 ) ; + P[4].setWorldCoordinates(0,0, L ) ; + P[5].setWorldCoordinates(L,-2*L, L ) ; + P[6].setWorldCoordinates(L,-4*L, 2*L ) ; + P[7].setWorldCoordinates(-2*L,-L, -L ) ; + P[8].setWorldCoordinates(-5*L,-5*L, L ) ; + P[9].setWorldCoordinates(-2*L,+3*L, 2*L ) ; + P[10].setWorldCoordinates(-2*L,-0.5*L, 2*L ) ; + + vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; + vpHomogeneousMatrix aMb(0.1,0.1,0.1,vpMath::rad(10),0,vpMath::rad(40)) ; + vpHomogeneousMatrix aMo =aMb*bMo ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(aMo) ; + aP[i] = P[i] ; + xa[i] = P[i].get_x() ; + ya[i] = P[i].get_y() ; + } - for(i=0 ; i < nbpt ; i++) - { - P[i].project(bMo) ; - bP[i] = P[i] ; - xb[i] = P[i].get_x() ; - yb[i] = P[i].get_y() ; - } + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(bMo) ; + bP[i] = P[i] ; + xb[i] = P[i].get_x() ; + yb[i] = P[i].get_y() ; + } - vpRotationMatrix aRb ; - vpTranslationVector aTb ; - vpColVector n ; - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("Compare with built homography H = R + t/d n ") ; - vpPlane bp(0,0,1,1) ; - vpHomography aHb_built(aMb,bp) ; - vpTRACE( "aHb built from the displacement ") ; - std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; - - aHb_built.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - std::cout << "-------------------------------" <<std::endl ; - std::cout << "aMb "<<std::endl <<aMb << std::endl ; - std::cout << "-------------------------------" <<std::endl ; - vpHomography aHb ; - - vpHomography::HLM(nbpt,xb,yb,xa,ya,false, aHb) ; - - vpTRACE("aHb computed using the Malis paralax algorithm") ; - aHb /= aHb[2][2] ; - std::cout << std::endl << aHb<< std::endl ; - - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("extract R, T and n ") ; - aHb.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("test if ap = aHb bp") ; - - for(i=0 ; i < nbpt ; i++) - { - std::cout << "Point "<< i<< std::endl ; - vpPoint p ; - std::cout << "(" ; - std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; - std::cout <<") = (" ; - p = aHb*bP[i] ; - std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; - } + vpRotationMatrix aRb ; + vpTranslationVector aTb ; + vpColVector n ; + std::cout << "-------------------------------" << std::endl ; + std::cout << "Compare with built homography H = R + t/d n " << std::endl ; + vpPlane bp(0,0,1,1) ; + vpHomography aHb_built(aMb,bp) ; + std::cout << "aHb built from the displacement: \n" << aHb_built/aHb_built[2][2] << std::endl ; + + aHb_built.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + std::cout << "aMb "<<std::endl <<aMb << std::endl ; + std::cout << "-------------------------------" <<std::endl ; + vpHomography aHb ; + + vpHomography::HLM(xb, yb, xa, ya, false, aHb) ; + + std::cout << "aHb computed using the Malis paralax algorithm" << std::endl ; + aHb /= aHb[2][2] ; + std::cout << std::endl << aHb<< std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + std::cout << "extract R, T and n " << std::endl ; + aHb.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + std::cout << "test if ap = aHb bp" << std::endl ; + + for(unsigned int i=0 ; i < nbpt ; i++) + { + std::cout << "Point "<< i<< std::endl ; + vpPoint p ; + std::cout << "(" ; + std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; + std::cout <<") = (" ; + p = aHb*bP[i] ; + std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/homography/homographyHartleyDLT2DObject.cpp b/example/homography/homographyHartleyDLT2DObject.cpp index 9ddd22960ab048778a180e8f69d47cbe6761156c..86561fd795e9814d73f287a2ca1c123f889027d0 100644 --- a/example/homography/homographyHartleyDLT2DObject.cpp +++ b/example/homography/homographyHartleyDLT2DObject.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: homographyHartleyDLT2DObject.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: homographyHartleyDLT2DObject.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,9 @@ #define L 0.1 #define nbpt 5 +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -113,15 +116,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -130,7 +133,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -141,99 +144,102 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - int i ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpPoint P[nbpt] ; // Point to be tracked - double xa[nbpt], ya[nbpt] ; - double xb[nbpt], yb[nbpt] ; + vpPoint P[nbpt] ; // Point to be tracked + std::vector<double> xa(nbpt), ya(nbpt), xb(nbpt), yb(nbpt); - vpPoint aP[nbpt] ; // Point to be tracked - vpPoint bP[nbpt] ; // Point to be tracked + vpPoint aP[nbpt] ; // Point to be tracked + vpPoint bP[nbpt] ; // Point to be tracked - P[0].setWorldCoordinates(-L,-L, 0 ) ; - P[1].setWorldCoordinates(2*L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-L,3*L, 0 ) ; - P[4].setWorldCoordinates(0,0, 0 ) ; - /* + P[0].setWorldCoordinates(-L,-L, 0 ) ; + P[1].setWorldCoordinates(2*L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-L,3*L, 0 ) ; + P[4].setWorldCoordinates(0,0, 0 ) ; + /* P[5].setWorldCoordinates(10,20, 0 ) ; P[6].setWorldCoordinates(-10,12, 0 ) ; */ - vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; - vpHomogeneousMatrix aMb(1,0,0.0,vpMath::rad(10),0,vpMath::rad(40)) ; - vpHomogeneousMatrix aMo =aMb*bMo ; - for(i=0 ; i < nbpt ; i++) - { - P[i].project(aMo) ; - aP[i] = P[i] ; - xa[i] = P[i].get_x() ; - ya[i] = P[i].get_y() ; - } + vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; + vpHomogeneousMatrix aMb(1,0,0.0,vpMath::rad(10),0,vpMath::rad(40)) ; + vpHomogeneousMatrix aMo =aMb*bMo ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(aMo) ; + aP[i] = P[i] ; + xa[i] = P[i].get_x() ; + ya[i] = P[i].get_y() ; + } - for(i=0 ; i < nbpt ; i++) - { - P[i].project(bMo) ; - bP[i] = P[i] ; - xb[i] = P[i].get_x() ; - yb[i] = P[i].get_y() ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(bMo) ; + bP[i] = P[i] ; + xb[i] = P[i].get_x() ; + yb[i] = P[i].get_y() ; + } + std::cout << "-------------------------------" <<std::endl ; + std::cout << "aMb "<<std::endl <<aMb << std::endl ; + std::cout << "-------------------------------" <<std::endl ; + vpHomography aHb ; + + vpHomography::DLT(xb, yb, xa, ya, aHb, true) ; + + vpTRACE("aHb computed using the DLT algorithm") ; + aHb /= aHb[2][2] ; + std::cout << std::endl << aHb<< std::endl ; + + vpRotationMatrix aRb ; + vpTranslationVector aTb ; + vpColVector n ; + + std::cout << "-------------------------------" <<std::endl ; + vpTRACE("extract R, T and n ") ; + aHb.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + vpTRACE("Compare with built homoraphy H = R + t/d ") ; + vpPlane bp(0,0,1,1) ; + vpHomography aHb_built(aMb,bp) ; + vpTRACE( "aHb built from the displacement ") ; + std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; + + aHb_built.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation: aRb" <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + vpTRACE("test if ap = aHb bp") ; + + for(unsigned int i=0 ; i < nbpt ; i++) + { + std::cout << "Point "<< i<< std::endl ; + vpPoint p ; + std::cout << "(" ; + std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; + std::cout <<") = (" ; + p = aHb*bP[i] ; + std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - std::cout << "-------------------------------" <<std::endl ; - std::cout << "aMb "<<std::endl <<aMb << std::endl ; - std::cout << "-------------------------------" <<std::endl ; - vpHomography aHb ; - - vpHomography::HartleyDLT(nbpt,xb,yb,xa,ya,aHb) ; - - vpTRACE("aHb computed using the DLT algorithm") ; - aHb /= aHb[2][2] ; - std::cout << std::endl << aHb<< std::endl ; - - vpRotationMatrix aRb ; - vpTranslationVector aTb ; - vpColVector n ; - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("extract R, T and n ") ; - aHb.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("Compare with built homoraphy H = R + t/d ") ; - vpPlane bp(0,0,1,1) ; - vpHomography aHb_built(aMb,bp) ; - vpTRACE( "aHb built from the displacement ") ; - std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; - - aHb_built.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation: aRb" <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE("test if ap = aHb bp") ; - - for(i=0 ; i < nbpt ; i++) - { - std::cout << "Point "<< i<< std::endl ; - vpPoint p ; - std::cout << "(" ; - std::cout << aP[i].get_x()/aP[i].get_w()<<", "<< aP[i].get_y()/aP[i].get_w() ; - std::cout <<") = (" ; - p = aHb*bP[i] ; - std::cout << p.get_x() /p.get_w()<<", "<< p.get_y()/ p.get_w() <<")"<<std::endl ; - } } diff --git a/example/homography/homographyRansac2DObject.cpp b/example/homography/homographyRansac2DObject.cpp index 2fdb479c4958e927a115c116b0f7cf119ba93ecc..7e092a20e9b2805b0818b65435c2b8dbe18c4efc 100644 --- a/example/homography/homographyRansac2DObject.cpp +++ b/example/homography/homographyRansac2DObject.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: homographyRansac2DObject.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: homographyRansac2DObject.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,8 +74,8 @@ // List of allowed command line options #define GETOPTARGS "h" -#define L 0.1 -#define nbpt 11 +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); /*! @@ -115,15 +115,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -132,7 +132,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -143,76 +143,102 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - int i ; - - vpPoint P[nbpt] ; // Point to be tracked - double xa[nbpt], ya[nbpt] ; - double xb[nbpt], yb[nbpt] ; - - vpPoint aP[nbpt] ; // Point to be tracked - vpPoint bP[nbpt] ; // Point to be tracked - - P[0].setWorldCoordinates(-L,-L, 0 ) ; // inlier - P[1].setWorldCoordinates(2*L,-L, 0 ) ; // inlier - P[2].setWorldCoordinates(L,L, 0 ) ; // inlier - P[3].setWorldCoordinates(-L,3*L, 0 ) ; // inlier - P[4].setWorldCoordinates(0,0, L ) ; - P[5].setWorldCoordinates(L,-2*L, L ) ; - P[6].setWorldCoordinates(L,-4*L, 2*L ) ; - P[7].setWorldCoordinates(-2*L,-L, -3*L ) ; - P[8].setWorldCoordinates(-5*L,-5*L, 0 ) ; // inlier - P[9].setWorldCoordinates(-2*L,+3*L, 4*L ) ; - P[10].setWorldCoordinates(-2*L,-0.5*L, 0 ) ; - /* - P[5].setWorldCoordinates(10,20, 0 ) ; - P[6].setWorldCoordinates(-10,12, 0 ) ; - */ - vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; - vpHomogeneousMatrix aMb(0.1,0.1,0.1,vpMath::rad(10),0,vpMath::rad(40)) ; - vpHomogeneousMatrix aMo =aMb*bMo ; - for(i=0 ; i < nbpt ; i++) - { - P[i].project(aMo) ; - aP[i] = P[i] ; - xa[i] = P[i].get_x() ; - ya[i] = P[i].get_y() ; - } + double L=0.1; + unsigned int nbpt = 11; + + std::vector<vpPoint> P(nbpt); // Point to be tracked + std::vector<double> xa(nbpt), ya(nbpt), xb(nbpt), yb(nbpt); + + std::vector<vpPoint> aP(nbpt); // Point to be tracked + std::vector<vpPoint> bP(nbpt); // Point to be tracked + + P[0].setWorldCoordinates(-L,-L, 0 ) ; // inlier + P[1].setWorldCoordinates(2*L,-L, 0 ) ; // inlier + P[2].setWorldCoordinates(L,L, 0 ) ; // inlier + P[3].setWorldCoordinates(-L,3*L, 0 ) ; // inlier + P[4].setWorldCoordinates(0,0, L ) ; + P[5].setWorldCoordinates(L,-2*L, L ) ; + P[6].setWorldCoordinates(L,-4*L, 2*L ) ; + P[7].setWorldCoordinates(-2*L,-L, -3*L ) ; + P[8].setWorldCoordinates(-5*L,-5*L, 0 ) ; // inlier + P[9].setWorldCoordinates(-2*L,+3*L, 4*L ) ; + P[10].setWorldCoordinates(-2*L,-0.5*L, 0 ) ; // inlier + + std::vector<bool> inliers_ground_truth(nbpt, false); + inliers_ground_truth[0] = true; + inliers_ground_truth[1] = true; + inliers_ground_truth[2] = true; + inliers_ground_truth[3] = true; + inliers_ground_truth[8] = true; + inliers_ground_truth[10] = true; + + vpHomogeneousMatrix bMo(0,0,1, 0,0,0) ; + vpHomogeneousMatrix aMb(0.1,0.1,0.1,vpMath::rad(10),0,vpMath::rad(40)) ; + vpHomogeneousMatrix aMo =aMb*bMo ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(aMo) ; + aP[i] = P[i] ; + xa[i] = P[i].get_x() ; + ya[i] = P[i].get_y() ; + } - for(i=0 ; i < nbpt ; i++) - { - P[i].project(bMo) ; - bP[i] = P[i] ; - xb[i] = P[i].get_x() ; - yb[i] = P[i].get_y() ; + for(unsigned int i=0 ; i < nbpt ; i++) + { + P[i].project(bMo) ; + bP[i] = P[i] ; + xb[i] = P[i].get_x() ; + yb[i] = P[i].get_y() ; + } + std::cout << "-------------------------------" <<std::endl ; + + vpRotationMatrix aRb ; + vpTranslationVector aTb ; + vpColVector n ; + std::cout << "Compare with built homography H = R + t/d n " << std::endl; + vpPlane bp(0,0,1,1) ; + vpHomography aHb_built(aMb,bp) ; + std::cout << "aHb built from the displacement: \n" << aHb_built/aHb_built[2][2] << std::endl ; + + aHb_built.computeDisplacement(aRb, aTb, n) ; + std::cout << "Rotation aRb: " <<std::endl ; + std::cout << aRb << std::endl ; + std::cout << "Translation: aTb" <<std::endl; + std::cout << (aTb).t() <<std::endl ; + std::cout << "Normal to the plane: n" <<std::endl; + std::cout << (n).t() <<std::endl ; + + std::cout << "-------------------------------" <<std::endl ; + vpHomography aHb; + std::vector<bool> inliers; + double residual; + // Suppose px=1000. Set the threshold to 2 pixels => 2/1000 + // In the data we have 6 inliers. We request that at least 6 are retrieved + vpHomography::ransac(xb, yb, xa, ya, aHb, inliers, residual, 6, 2./1000) ; + + std::cout << "aHb estimated using ransac:\n" << aHb << std::endl ; + std::cout << "Inliers indexes (should be 0,1,2,3,8,10): "; + for (unsigned int i=0; i< inliers.size(); i++) + if (inliers[i]) std::cout << i << ","; + std::cout << std::endl; + + if (inliers == inliers_ground_truth) { + std::cout << "Ransac estimation succeed" << std::endl; + return 0; + } + else { + std::cout << "Ransac estimation fails" << std::endl; + return 1; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - std::cout << "-------------------------------" <<std::endl ; - - vpRotationMatrix aRb ; - vpTranslationVector aTb ; - vpColVector n ; - vpTRACE("Compare with built homography H = R + t/d n ") ; - vpPlane bp(0,0,1,1) ; - vpHomography aHb_built(aMb,bp) ; - vpTRACE( "aHb built from the displacement ") ; - std::cout << std::endl <<aHb_built/aHb_built[2][2] << std::endl ; - - aHb_built.computeDisplacement(aRb, aTb, n) ; - std::cout << "Rotation aRb: " <<std::endl ; - std::cout << aRb << std::endl ; - std::cout << "Translation: aTb" <<std::endl; - std::cout << (aTb).t() <<std::endl ; - std::cout << "Normal to the plane: n" <<std::endl; - std::cout << (n).t() <<std::endl ; - - std::cout << "-------------------------------" <<std::endl ; - vpTRACE(" ") ; - vpHomography aHb ; - vpHomography::ransac(nbpt,xb,yb,xa,ya, aHb) ; - - std::cout << aHb << std::endl ; } diff --git a/example/image/CMakeLists.txt b/example/image/CMakeLists.txt index 59e38c699c8cac94e4a90ca66a5ea148b34f99c4..b95ae4c55192ffb14b001af71a490f0573961ceb 100644 --- a/example/image/CMakeLists.txt +++ b/example/image/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,24 +43,28 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE imageDiskRW.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(imageDiskRW imageDiskRW) +add_test(imageDiskRW imageDiskRW) diff --git a/example/image/imageDiskRW.cpp b/example/image/imageDiskRW.cpp index f153c6b0e20734941214746cee76849fab0dfafc..87db3f1ff27867f51d1b1b727c985ef3679ebe8b 100644 --- a/example/image/imageDiskRW.cpp +++ b/example/image/imageDiskRW.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: imageDiskRW.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: imageDiskRW.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); + /*! Print the program options. @@ -127,20 +130,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -148,7 +150,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -160,183 +162,171 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " imageDiskRW.cpp" <<std::endl << std::endl ; - - std::cout << " reading and writting of PPM image" << std::endl ; - std::cout << " read an image that does not exist" << std::endl ; - std::cout << " write in a directory that does no exist" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#if defined(UNIX) && !defined(WIN32) - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " imageDiskRW.cpp" <<std::endl << std::endl ; + + std::cout << " reading and writting of PPM image" << std::endl ; + std::cout << " read an image that does not exist" << std::endl ; + std::cout << " write in a directory that does no exist" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); + // Get the user login name + vpIoTools::getUserName(username); - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string dirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string dirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(dirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(dirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << dirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(dirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(dirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << dirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - - - ///////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + // First we wanted to have gray level image (8bits) + // vpImage is a template class you can declare vpImage of ... everything... + vpImage<unsigned char> I ; + // Although I is a gray level image you can read and write + // color image. Obviously the color will be translated as a gray level - // First we wanted to have gray level image (8bits) - // vpImage is a template class you can declare vpImage of ... everything... - vpImage<unsigned char> I ; + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(I, filename); - // Although I is a gray level image you can read and write - // color image. Obviously the color will be translated as a gray level + filename = vpIoTools::createFilePath(dirname, "IoPPM.Klimt_char.ppm"); + vpImageIo::write(I, filename) ; - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpImageIo::read(I, filename); - - filename = dirname + vpIoTools::path("/IoPPM.Klimt_char.ppm"); - vpImageIo::write(I, filename) ; - - - // test io error - // if the image you want to read on the disk does not exist - // an exception is thrown - try - { + // test io error + // if the image you want to read on the disk does not exist + // an exception is thrown //Try to load a non existing image - filename = ipath + vpIoTools::path("/ViSP-images/image-that-does-not-exist.ppm"); + try { + filename = vpIoTools::createFilePath(ipath, "ViSP-images/image-that-does-not-exist.ppm"); + vpImageIo::read(I,filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } - vpImageIo::read(I,filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } + // same thing if you to write in a directory that does not exist + // or where you are not allowd to write. + try { + filename = vpIoTools::createFilePath(dirname, "directory-that-does-not-exist/Klimt.ppm"); + vpImageIo::write(I,filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } - // same thing if you to write in a directory that does not exist - // or where you are not allowd to write. - try - { - filename = dirname + vpIoTools::path("/directory-that-does-not-exist/Klimt.ppm"); - vpImageIo::write(I,filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } - std::cout << "----------------------------------------------------" << std::endl ; + std::cout << "----------------------------------------------------" << std::endl ; - // Let's consider that the image is now a color image (32 bits RGBa) - vpImage<vpRGBa> Irgba ; + // Let's consider that the image is now a color image (32 bits RGBa) + vpImage<vpRGBa> Irgba ; - // read write unsigned char ppm image. + // read write unsigned char ppm image. - // Load a color image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpImageIo::read(Irgba, filename); + // Load a color image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(Irgba, filename); - // Write the content of the color image on the disk - filename = dirname + vpIoTools::path("/IoPGM.Klimt_rgba.ppm"); - vpImageIo::write(Irgba, filename) ; + // Write the content of the color image on the disk + filename = vpIoTools::createFilePath(dirname, "IoPGM.Klimt_rgba.ppm"); + vpImageIo::write(Irgba, filename) ; - // test io error - try - { - filename = ipath + vpIoTools::path("/ViSP-images/image-that-does-not-exist.ppm"); - vpImageIo::read(Irgba,filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } + // test io error + try { + filename = vpIoTools::createFilePath(ipath, "ViSP-images/image-that-does-not-exist.ppm"); + vpImageIo::read(Irgba,filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } - // test io error - try - { - filename = dirname + vpIoTools::path("/directory-that-does-not-exist/Klimt.ppm"); - vpImageIo::write(Irgba,filename) ; + // test io error + try { + filename = vpIoTools::createFilePath(dirname, "directory-that-does-not-exist/Klimt.ppm"); + vpImageIo::write(Irgba,filename) ; + } + + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } + return 0; } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/example/key-point/CMakeLists.txt b/example/key-point/CMakeLists.txt index 45f66f3db89fb56cf16265722b498c54426c71dd..fc8b2d30a7e2d1782e86ea9c47e57f081315fb31 100644 --- a/example/key-point/CMakeLists.txt +++ b/example/key-point/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,28 +43,26 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE keyPointSurf.cpp planarObjectDetector.cpp fernClassifier.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test #ADD_TEST(planarObjectDetector planarObjectDetector -l -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/key-point/fernClassifier.cpp b/example/key-point/fernClassifier.cpp index 7d2225154f7998e29a9cbbbae161d5033988ba4e..cf3cb619c42f5f81f1263f0f57eaf894cbd730c6 100644 --- a/example/key-point/fernClassifier.cpp +++ b/example/key-point/fernClassifier.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: fernClassifier.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: fernClassifier.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,14 +53,13 @@ #include <visp/vpConfig.h> #include <visp/vpDebug.h> -#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && (VISP_HAVE_OPENCV_VERSION >= 0x020000)) +#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) #include <iostream> #include <stdlib.h> #include <visp/vpFernClassifier.h> #include <visp/vpParseArgv.h> #include <visp/vpConfig.h> -#include <visp/vpOpenCVGrabber.h> #include <visp/vpImage.h> #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> @@ -71,7 +70,11 @@ #include <visp/vpTime.h> #include <iomanip> -#define GETOPTARGS "hlcdb:i:sp" +#define GETOPTARGS "hlcdb:i:p" + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &isLearning, std::string& dataFile, bool& click_allowed, + bool& display, bool& displayPoints, std::string& ipath); /*! @@ -89,7 +92,7 @@ object needs first to be learned (-l option). This learning process will create\ a file used to detect the object.\n\ \n\ SYNOPSIS\n\ - %s [-l] [-h] [-b] [-c] [-d] [-p] [-i] [-s]\n", name); + %s [-l] [-h] [-b] [-c] [-d] [-p] [-i]\n", name); fprintf(stdout, "\n\ OPTIONS: \n\ @@ -113,9 +116,6 @@ OPTIONS: \n\ \n\ -d \n\ Turn off the display.\n\ -\n\ - -s \n\ - Turn off the use of the sequence and use a webcam.\n\ \n\ -p \n\ display points of interest.\n\ @@ -139,30 +139,28 @@ OPTIONS: \n\ \param click_allowed : Mouse click activation. \param display : Display activation. \param displayPoints : Display points of interests activation. - \param useSequence : sequence of image de activation. \param ipath : Input image path. \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - bool &isLearning, std::string& dataFile, bool& click_allowed, bool& display, bool& displayPoints, bool& useSequence, std::string& ipath) +bool getOptions(int argc, const char **argv, bool &isLearning, std::string& dataFile, bool& click_allowed, + bool& display, bool& displayPoints, std::string& ipath) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; case 'l': isLearning = true; break; case 'h': usage(argv[0], NULL); return false; break; - case 'b': dataFile = optarg; break; + case 'b': dataFile = optarg_; break; case 'p': displayPoints = true; break; - case 's': useSequence = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -171,7 +169,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -183,284 +181,262 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char** argv) { - bool isLearning = false; - std::string dataFile("./dataFern"); - bool opt_click_allowed = true; - bool opt_display = true; - std::string objectName("object"); - bool displayPoints = false; - bool useSequence = true; - std::string opt_ipath; - std::string ipath; - std::string env_ipath; - std::string dirname; - std::string filename; - - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL){ - env_ipath = ptenv; - } + try { + bool isLearning = false; + std::string dataFile("./dataFern"); + bool opt_click_allowed = true; + bool opt_display = true; + std::string objectName("object"); + bool displayPoints = false; + std::string opt_ipath; + std::string ipath; + std::string env_ipath; + std::string dirname; + std::string filename; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()){ + ipath = env_ipath; + } - // Set the default input path - if (! env_ipath.empty()){ - ipath = env_ipath; - } + // Read the command line options + if (getOptions(argc, argv, + isLearning, dataFile, opt_click_allowed, opt_display, displayPoints, opt_ipath) == false) { + exit (-1); + } - // Read the command line options - if (getOptions(argc, argv, - isLearning, dataFile, opt_click_allowed, opt_display, displayPoints, useSequence, opt_ipath) == false) { - exit (-1); - } + // Get the option values + if (!opt_ipath.empty()){ + ipath = opt_ipath; + } - // Get the option values - if (useSequence && !opt_ipath.empty()){ - ipath = opt_ipath; - } + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (useSequence && !opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); } - } - // Test if an input path is set - if (useSequence && opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - - // Declare two images, these are gray level images (unsigned char) - vpImage <unsigned char> I; - vpImage <unsigned char> Iref; - - - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); - - // Build the name of the image file - unsigned iter = 0; // Image number - std::ostringstream s; - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - if(useSequence){ - vpCTRACE << "Load: " << filename << std::endl; - vpImageIo::read(Iref, filename) ; - I = Iref; - } - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + // Declare two images, these are gray level images (unsigned char) + vpImage <unsigned char> I; + vpImage <unsigned char> Iref; - - // Declare a framegrabber - vpOpenCVGrabber g; - if(!useSequence){ + + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); + + // Build the name of the image file + unsigned iter = 0; // Image number + std::ostringstream s; + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated try{ - g.open(I); + std::cout << "Load: " << filename << std::endl; + vpImageIo::read(Iref, filename) ; + I = Iref; } - catch(...){ - std::cout << "problem to initialise the framegrabber" << std::endl; + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; exit(-1); } - g.acquire(I); - // initialise the reference image - g.acquire(Iref); - } #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #endif - + #if defined VISP_HAVE_X11 - vpDisplayX displayRef; + vpDisplayX displayRef; #elif defined VISP_HAVE_GTK - vpDisplayGTK displayRef; + vpDisplayGTK displayRef; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayRef; + vpDisplayGDI displayRef; #endif - // declare a planar object detector - vpFernClassifier fern; + // declare a planar object detector + vpFernClassifier fern; - if(isLearning){ - if(opt_display){ - displayRef.init(Iref, 100, 100, "Reference image") ; - vpDisplay::display(Iref); - vpDisplay::flush(Iref); - } - vpImagePoint corners[2]; - if (opt_display && opt_click_allowed){ - std::cout << "Click on the top left and the bottom right corners to define the reference plane" << std::endl; - for (int i=0 ; i < 2 ; i++){ - vpDisplay::getClick(Iref, corners[i]); - std::cout << corners[i] << std::endl; + if(isLearning){ + if(opt_display){ + displayRef.init(Iref, 100, 100, "Reference image") ; + vpDisplay::display(Iref); + vpDisplay::flush(Iref); + } + vpImagePoint corners[2]; + if (opt_display && opt_click_allowed){ + std::cout << "Click on the top left and the bottom right corners to define the reference plane" << std::endl; + for (int i=0 ; i < 2 ; i++){ + vpDisplay::getClick(Iref, corners[i]); + std::cout << corners[i] << std::endl; + } + } + else{ + corners[0].set_ij(1,1); + corners[1].set_ij(I.getHeight()-2,I.getWidth()-2); } - } - else{ - corners[0].set_ij(1,1); - corners[1].set_ij(I.getHeight()-2,I.getWidth()-2); - } - if (opt_display) { - //Display the rectangle which defines the part of the image where the reference points are computed. - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); - vpDisplay::flush(Iref); - } + if (opt_display) { + //Display the rectangle which defines the part of the image where the reference points are computed. + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); + vpDisplay::flush(Iref); + } - if (opt_click_allowed){ - std::cout << "Click on the image to continue" << std::endl; - vpDisplay::getClick(Iref); - } + if (opt_click_allowed){ + std::cout << "Click on the image to continue" << std::endl; + vpDisplay::getClick(Iref); + } - vpRect roi(corners[0], corners[1]); + vpRect roi(corners[0], corners[1]); - std::cout << "> train the classifier on the selected plane. (may take up to several minutes)." << std::endl; - if(opt_display) { - vpDisplay::display(Iref); - vpDisplay::flush(Iref); - } - - try{ - fern.buildReference(Iref, roi); - } - catch(vpException e){ - std::cout << e.getMessage() << std::endl; - } - catch(...){ - std::cout << "unknown error, line " << __LINE__ << std::endl; - } - try{ - fern.record(objectName, dataFile); - } - catch(vpException e){ - std::cout << e.getMessage() << std::endl; - } - catch(...){ - std::cout << "unknown error, line " << __LINE__ << std::endl; + std::cout << "> train the classifier on the selected plane. (may take up to several minutes)." << std::endl; + if(opt_display) { + vpDisplay::display(Iref); + vpDisplay::flush(Iref); + } + + try{ + fern.buildReference(Iref, roi); + } + catch(vpException e){ + std::cout << e.getMessage() << std::endl; + } + catch(...){ + std::cout << "unknown error, line " << __LINE__ << std::endl; + } + try{ + fern.record(objectName, dataFile); + } + catch(vpException e){ + std::cout << e.getMessage() << std::endl; + } + catch(...){ + std::cout << "unknown error, line " << __LINE__ << std::endl; + } + std::cout << __LINE__ << std::endl; } - std::cout << __LINE__ << std::endl; - } - else{ - if(!vpIoTools::checkFilename(dataFile)){ - vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); - exit(-1); + else{ + if(!vpIoTools::checkFilename(dataFile)){ + vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); + exit(-1); + } + try{ + // load a previously recorded file + fern.load(dataFile, objectName); + } + catch(...){ + vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); + exit(-1); + } } - try{ - // load a previously recorded file - fern.load(dataFile, objectName); + + + if(opt_display){ + display.init(I, 110 + (int)Iref.getWidth(), 100, "Current image") ; + vpDisplay::display(I); + vpDisplay::flush(I); } - catch(...){ - vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); - exit(-1); + + if (opt_display && opt_click_allowed){ + std::cout << "Click on the current image to continue" << std::endl; + vpDisplay::displayText(I, vpImagePoint(15,15), + "Click on the current image to continue", vpColor::red); + vpDisplay::flush(I); + vpDisplay::getClick(I); } - } - - - if(opt_display){ - display.init(I, 110 + (int)Iref.getWidth(), 100, "Current image") ; - vpDisplay::display(I); - vpDisplay::flush(I); - } - if (opt_display && opt_click_allowed){ - std::cout << "Click on the current image to continue" << std::endl; - vpDisplay::displayCharString (I, vpImagePoint(15,15), - (char*)"Click on the current image to continue", vpColor::red); - vpDisplay::flush(I); - vpDisplay::getClick(I); - } - - for ( ; ; ) { - // acquire a new image - if(useSequence){ + for ( ; ; ) { + // acquire a new image iter++; if(iter >= 80){ break; - } + } s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); // read the image vpImageIo::read(I, filename); - } - else{ - g.acquire(I); - } - - if(opt_display){ - vpDisplay::display(I); - if(isLearning) - vpDisplay::display(Iref); - } - - double t0 = vpTime::measureTimeMs (); - // detection of the reference image - unsigned int nbpts; - try{ - nbpts = fern.matchPoint(I); - } - catch(vpException e){ - std::cout << e.getMessage() << std::endl; - return -1; - } - catch(...){ - std::cout << "unknown error line " << __LINE__ << std::endl; - return -1; - } - std::cout << "matching " << nbpts << " points : " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; - - if(opt_display){ - fern.display(Iref, I, 7); - vpDisplay::flush(I); - if(isLearning) - vpDisplay::flush(Iref); - if(vpDisplay::getClick(I, false)){ - break; + + if(opt_display){ + vpDisplay::display(I); + if(isLearning) + vpDisplay::display(Iref); + } + + double t0 = vpTime::measureTimeMs (); + // detection of the reference image + unsigned int nbpts; + try{ + nbpts = fern.matchPoint(I); + } + catch(vpException e){ + std::cout << e.getMessage() << std::endl; + return -1; + } + catch(...){ + std::cout << "unknown error line " << __LINE__ << std::endl; + return -1; + } + std::cout << "matching " << nbpts << " points : " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; + + if(opt_display){ + fern.display(Iref, I, 7); + vpDisplay::flush(I); + if(isLearning) + vpDisplay::flush(Iref); + if(vpDisplay::getClick(I, false)){ + break; + } } } + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } #else diff --git a/example/key-point/keyPointSurf.cpp b/example/key-point/keyPointSurf.cpp index b40a5c7f3e045572ac584e157e580c953b4b5a03..4eaacdbc343e34169c1d0d4cda1b3b1e4ab1317d 100644 --- a/example/key-point/keyPointSurf.cpp +++ b/example/key-point/keyPointSurf.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: keyPointSurf.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: keyPointSurf.cpp 5202 2015-01-24 09:29:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,7 +59,7 @@ #include <stdio.h> #include <sstream> #include <iomanip> -#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION >= 0x010100)) +#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) #include <visp/vpKeyPointSurf.h> @@ -76,6 +76,9 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display); + /*! Print the program options. @@ -130,21 +133,20 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -153,7 +155,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -164,205 +166,209 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> Iref ; - vpImage<unsigned char> Icur ; - - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); - - // Build the name of the image file - unsigned iter = 0; // Image number - std::ostringstream s; - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - - vpImageIo::read(Iref, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> Iref ; + vpImage<unsigned char> Icur ; - // We open a window using either X11, GTK or GDI. -#if defined VISP_HAVE_X11 - vpDisplayX display[2]; -#elif defined VISP_HAVE_GTK - vpDisplayGTK display[2]; -#elif defined VISP_HAVE_GDI - vpDisplayGDI display[2]; -#endif + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); - if (opt_display) { + // Build the name of the image file + unsigned int iter = 0; // Image number + std::ostringstream s; + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated try{ - // Display size is automatically defined by the image (I) size - display[0].init(Iref, 100, 100,"Display reference image") ; - vpDisplay::display(Iref) ; - vpDisplay::flush(Iref) ; + vpCTRACE << "Load: " << filename << std::endl; + + vpImageIo::read(Iref, filename) ; } catch(...) { - vpERROR_TRACE("Error while displaying the image") ; + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; exit(-1); } - } - vpImagePoint corners[2]; - if (opt_display && opt_click_allowed) - { - std::cout << "Click on the top left and the bottom right corners to define the part of the image where the reference points will be computed" << std::endl; - for (unsigned int i=0 ; i < 2 ; i++) - { - vpDisplay::getClick(Iref, corners[i]); - std::cout << corners[i] << std::endl; - } - } - else - { - corners[0].set_ij(156,209); - corners[1].set_ij(272,378); - } + // We open a window using either X11, GTK or GDI. +#if defined VISP_HAVE_X11 + vpDisplayX display[2]; +#elif defined VISP_HAVE_GTK + vpDisplayGTK display[2]; +#elif defined VISP_HAVE_GDI + vpDisplayGDI display[2]; +#endif - if (opt_display) - { - //Display the rectangle which defines the part of the image where the reference points are computed. - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); - vpDisplay::flush(Iref); - } + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display[0].init(Iref, 100, 100,"Display reference image") ; + vpDisplay::display(Iref) ; + vpDisplay::flush(Iref) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } + } - if (opt_click_allowed) - { - std::cout << "Click on the image to continue" << std::endl; - vpDisplay::getClick(Iref); - } + vpImagePoint corners[2]; + if (opt_display && opt_click_allowed) + { + std::cout << "Click on the top left and the bottom right corners to define the part of the image where the reference points will be computed" << std::endl; + for (unsigned int i=0 ; i < 2 ; i++) + { + vpDisplay::getClick(Iref, corners[i]); + std::cout << corners[i] << std::endl; + } + } + else + { + corners[0].set_ij(156,209); + corners[1].set_ij(272,378); + } - vpKeyPointSurf surf; - // unsigned int nbrRef; - unsigned int height, width; - height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); - width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); + if (opt_display) + { + //Display the rectangle which defines the part of the image where the reference points are computed. + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); + vpDisplay::flush(Iref); + } - //Computes the reference points - /* nbrRef = */ surf.buildReference(Iref, corners[0], height, width); + if (opt_click_allowed) + { + std::cout << "Click on the image to continue" << std::endl; + vpDisplay::getClick(Iref); + } - unsigned int nbrPair = 0; + vpKeyPointSurf surf; + // unsigned int nbrRef; + unsigned int height, width; + height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); + width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); - vpImageIo::read(Icur, filename); + //Computes the reference points + /* nbrRef = */ surf.buildReference(Iref, corners[0], height, width); - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display[1].init(Icur, (int)(100+Iref.getWidth()), 100,"Display current image") ; - vpDisplay::display(Icur) ; - vpDisplay::flush(Icur) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } + unsigned int nbrPair = 0; - for (int iter = 1 ; iter < 30 ; iter++) - { - std::cout <<"----------------------------------------------------------"<<std::endl; - // set the new image name - s.str(""); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - // read the image vpImageIo::read(Icur, filename); + if (opt_display) { - // Display the image - vpDisplay::display(Iref) ; - vpDisplay::display(Icur) ; + try{ + // Display size is automatically defined by the image (I) size + display[1].init(Icur, (int)(100+Iref.getWidth()), 100,"Display current image") ; + vpDisplay::display(Icur) ; + vpDisplay::flush(Icur) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - nbrPair = surf.matchPoint(Icur); - std::cout << "Number of matched point : " << nbrPair <<std::endl; - - if (opt_display) + for (iter = 1 ; iter < 30 ; iter++) { - // Display the matched features - surf.display(Iref, Icur, 7); - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::red); - vpDisplay::flush(Iref) ; - vpDisplay::flush(Icur) ; + std::cout <<"----------------------------------------------------------"<<std::endl; + // set the new image name + s.str(""); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + // read the image + vpImageIo::read(Icur, filename); + if (opt_display) { + // Display the image + vpDisplay::display(Iref) ; + vpDisplay::display(Icur) ; + } + + nbrPair = surf.matchPoint(Icur); + std::cout << "Number of matched point : " << nbrPair <<std::endl; + + if (opt_display) + { + // Display the matched features + surf.display(Iref, Icur, 7); + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::red); + vpDisplay::flush(Iref) ; + vpDisplay::flush(Icur) ; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } @@ -373,7 +379,7 @@ main() #if ( ! (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) ) vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); #else - vpERROR_TRACE("You do not have OpenCV-1.1.0 or a more recent release that contains opencv_nonfree component..."); + vpERROR_TRACE("You do not have 1.1.0 <= OpenCV < 3.0.0 that contains opencv_nonfree component..."); #endif } diff --git a/example/key-point/planarObjectDetector.cpp b/example/key-point/planarObjectDetector.cpp index b6415a6b279af0976ed8c106618c65f032546058..fbdfea1c425832f87c486fdfcd4bafea9f4317f1 100644 --- a/example/key-point/planarObjectDetector.cpp +++ b/example/key-point/planarObjectDetector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: planarObjectDetector.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: planarObjectDetector.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,14 +53,13 @@ #include <visp/vpConfig.h> #include <visp/vpDebug.h> -#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && (VISP_HAVE_OPENCV_VERSION >= 0x020000)) +#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) #include <iostream> #include <stdlib.h> #include <visp/vpPlanarObjectDetector.h> #include <visp/vpParseArgv.h> #include <visp/vpConfig.h> -#include <visp/vpOpenCVGrabber.h> #include <visp/vpImage.h> #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> @@ -73,7 +72,11 @@ #include <visp/vpV4l2Grabber.h> #include <visp/vp1394TwoGrabber.h> -#define GETOPTARGS "hlcdb:i:sp" +#define GETOPTARGS "hlcdb:i:p" + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &isLearning, std::string& dataFile, bool& click_allowed, + bool& display, bool& displayPoints, std::string& ipath); /*! @@ -141,30 +144,28 @@ OPTIONS: \n\ \param click_allowed : Mouse click activation. \param display : Display activation. \param displayPoints : Display points of interests activation. - \param useSequence : sequence of image de activation. \param ipath : Input image path. \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - bool &isLearning, std::string& dataFile, bool& click_allowed, bool& display, bool& displayPoints, bool& useSequence, std::string& ipath) +bool getOptions(int argc, const char **argv, bool &isLearning, std::string& dataFile, bool& click_allowed, + bool& display, bool& displayPoints, std::string& ipath) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; case 'l': isLearning = true; break; case 'h': usage(argv[0], NULL); return false; break; - case 'b': dataFile = optarg; break; + case 'b': dataFile = optarg_; break; case 'p': displayPoints = true; break; - case 's': useSequence = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -173,7 +174,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -185,273 +186,247 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char** argv) { - bool isLearning = false; - std::string dataFile("./dataPlanar"); - bool opt_click_allowed = true; - bool opt_display = true; - std::string objectName("object"); - bool displayPoints = false; - bool useSequence = true; - std::string opt_ipath; - std::string ipath; - std::string env_ipath; - std::string dirname; - std::string filename; - - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL){ - env_ipath = ptenv; - } - - // Set the default input path - if (! env_ipath.empty()){ - ipath = env_ipath; - } - - // Read the command line options - if (getOptions(argc, argv, - isLearning, dataFile, opt_click_allowed, opt_display, displayPoints, useSequence, opt_ipath) == false) { - exit (-1); - } + try { + bool isLearning = false; + std::string dataFile("./dataPlanar"); + bool opt_click_allowed = true; + bool opt_display = true; + std::string objectName("object"); + bool displayPoints = false; + std::string opt_ipath; + std::string ipath; + std::string env_ipath; + std::string dirname; + std::string filename; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()){ + ipath = env_ipath; + } - // Get the option values - if (useSequence && !opt_ipath.empty()){ - ipath = opt_ipath; - } + // Read the command line options + if (getOptions(argc, argv, + isLearning, dataFile, opt_click_allowed, opt_display, displayPoints, opt_ipath) == false) { + exit (-1); + } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (useSequence && !opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()){ + ipath = opt_ipath; } - } - // Test if an input path is set - if (useSequence && opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - - // Declare two images, these are gray level images (unsigned char) - vpImage <unsigned char> I; - vpImage <unsigned char> Iref; - - - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); - - // Build the name of the image file - unsigned iter = 0; // Image number - std::ostringstream s; - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - if(useSequence){ - vpCTRACE << "Load: " << filename << std::endl; - vpImageIo::read(Iref, filename) ; - I = Iref; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); } - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } - - // Declare a framegrabber - vpOpenCVGrabber g; - if(!useSequence){ + // Declare two images, these are gray level images (unsigned char) + vpImage <unsigned char> I; + vpImage <unsigned char> Iref; + + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); + + // Build the name of the image file + unsigned iter = 0; // Image number + std::ostringstream s; + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated try{ - g.open(I); + std::cout << "Load: " << filename << std::endl; + vpImageIo::read(Iref, filename) ; + I = Iref; } - catch(...){ - std::cout << "problem to initialise the framegrabber" << std::endl; + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; exit(-1); } - - - g.acquire(I); - // initialise the reference image - g.acquire(Iref); - } #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #endif - + #if defined VISP_HAVE_X11 - vpDisplayX displayRef; + vpDisplayX displayRef; #elif defined VISP_HAVE_GTK - vpDisplayGTK displayRef; + vpDisplayGTK displayRef; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayRef; + vpDisplayGDI displayRef; #endif - // declare a planar object detector - vpPlanarObjectDetector planar; + // declare a planar object detector + vpPlanarObjectDetector planar; - vpImagePoint corners[2]; - if(isLearning){ - if(opt_display){ - displayRef.init(Iref, 100, 100, "Reference image") ; - vpDisplay::display(Iref); - vpDisplay::flush(Iref); - } - if (opt_display && opt_click_allowed){ - std::cout << "Click on the top left and the bottom right corners to define the reference plane" << std::endl; - for (int i=0 ; i < 2 ; i++){ - vpDisplay::getClick(Iref, corners[i]); - std::cout << corners[i] << std::endl; + vpImagePoint corners[2]; + if(isLearning){ + if(opt_display){ + displayRef.init(Iref, 100, 100, "Reference image") ; + vpDisplay::display(Iref); + vpDisplay::flush(Iref); + } + if (opt_display && opt_click_allowed){ + std::cout << "Click on the top left and the bottom right corners to define the reference plane" << std::endl; + for (int i=0 ; i < 2 ; i++){ + vpDisplay::getClick(Iref, corners[i]); + std::cout << corners[i] << std::endl; + } + } + else{ + corners[0].set_ij(50, I.getWidth()-100);// small ROI for the automated test + corners[1].set_ij(I.getHeight()-100, I.getWidth()-2); } - } - else{ - corners[0].set_ij(50, I.getWidth()-100);// small ROI for the automated test - corners[1].set_ij(I.getHeight()-100, I.getWidth()-2); - } - if (opt_display) { - //Display the rectangle which defines the part of the image where the reference points are computed. - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); - vpDisplay::flush(Iref); - } + if (opt_display) { + //Display the rectangle which defines the part of the image where the reference points are computed. + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); + vpDisplay::flush(Iref); + } - if (opt_click_allowed){ - std::cout << "Click on the image to continue" << std::endl; - vpDisplay::getClick(Iref); - } + if (opt_click_allowed){ + std::cout << "Click on the image to continue" << std::endl; + vpDisplay::getClick(Iref); + } - vpRect roi(corners[0], corners[1]); + vpRect roi(corners[0], corners[1]); - std::cout << "> train the classifier on the selected plane (may take up to several minutes)." << std::endl; - if(opt_display) { - vpDisplay::display(Iref); - vpDisplay::flush(Iref); - } - double t0 = vpTime::measureTimeMs (); - planar.buildReference(Iref, roi); - std::cout << "build reference in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; - t0 = vpTime::measureTimeMs (); - planar.recordDetector(objectName, dataFile); - std::cout << "record detector in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; - } - else{ - if(!vpIoTools::checkFilename(dataFile)){ - vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); - exit(-1); + std::cout << "> train the classifier on the selected plane (may take up to several minutes)." << std::endl; + if(opt_display) { + vpDisplay::display(Iref); + vpDisplay::flush(Iref); + } + double t0 = vpTime::measureTimeMs (); + planar.buildReference(Iref, roi); + std::cout << "build reference in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; + t0 = vpTime::measureTimeMs (); + planar.recordDetector(objectName, dataFile); + std::cout << "record detector in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; } - try{ - // load a previously recorded file - planar.load(dataFile, objectName); + else{ + if(!vpIoTools::checkFilename(dataFile)){ + vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); + exit(-1); + } + try{ + // load a previously recorded file + planar.load(dataFile, objectName); + } + catch(...){ + vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); + exit(-1); + } } - catch(...){ - vpERROR_TRACE("cannot load the database with the specified name. Has the object been learned with the -l option? "); - exit(-1); + + if(opt_display){ + display.init(I, 110 + (int)Iref.getWidth(), 100, "Current image") ; + vpDisplay::display(I); + vpDisplay::flush(I); } - } - if(opt_display){ - display.init(I, 110 + (int)Iref.getWidth(), 100, "Current image") ; - vpDisplay::display(I); - vpDisplay::flush(I); - } + if (opt_display && opt_click_allowed){ + std::cout << "Click on the reference image to continue" << std::endl; + vpDisplay::displayText(Iref, vpImagePoint(15,15), + "Click on the reference image to continue", vpColor::red); + vpDisplay::flush(Iref); + vpDisplay::getClick(Iref); + } - if (opt_display && opt_click_allowed){ - std::cout << "Click on the reference image to continue" << std::endl; - vpDisplay::displayCharString (Iref, vpImagePoint(15,15), - (char*)"Click on the reference image to continue", vpColor::red); - vpDisplay::flush(Iref); - vpDisplay::getClick(Iref); - } - - for ( ; ; ) { - // acquire a new image - if(useSequence){ + for ( ; ; ) { + // acquire a new image iter++; if(iter >= 80){ break; - } + } s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); // read the image vpImageIo::read(I, filename); - } - else{ - g.acquire(I); - } - - if(opt_display){ - vpDisplay::display(I); - } - - double t0 = vpTime::measureTimeMs (); - // detection of the reference planar surface - bool isDetected = planar.matchPoint(I); - std::cout << "matching in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; - - if(isDetected){ - vpHomography H; - planar.getHomography(H); - std::cout << " > computed homography:" << std::endl << H << std::endl; + if(opt_display){ - if(isLearning){ - vpDisplay::display(Iref); - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); - planar.display(Iref, I, displayPoints); - vpDisplay::flush(Iref); - }else{ - planar.display(I, displayPoints); + vpDisplay::display(I); + } + + double t0 = vpTime::measureTimeMs (); + // detection of the reference planar surface + bool isDetected = planar.matchPoint(I); + std::cout << "matching in " << vpTime::measureTimeMs () - t0 << " ms" << std::endl; + + if(isDetected){ + vpHomography H; + planar.getHomography(H); + std::cout << " > computed homography:" << std::endl << H << std::endl; + if(opt_display){ + if(isLearning){ + vpDisplay::display(Iref); + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green); + planar.display(Iref, I, displayPoints); + vpDisplay::flush(Iref); + }else{ + planar.display(I, displayPoints); + } } } - } - else{ - std::cout << " > reference is not detected in the image" << std::endl; - } - if(opt_display){ - vpDisplay::flush(I); - if(vpDisplay::getClick(I, false)){ - break; + else{ + std::cout << " > reference is not detected in the image" << std::endl; + } + if(opt_display){ + vpDisplay::flush(I); + if(vpDisplay::getClick(I, false)){ + break; + } } } + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } #else diff --git a/example/manual/CMakeLists.txt b/example/manual/CMakeLists.txt index 284927794cc1cd23f76d7f426f5e83197fa50410..0e35823538508e98a082cf360b2269238cd25455 100644 --- a/example/manual/CMakeLists.txt +++ b/example/manual/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,11 +43,10 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE geometric-features/manGeometricFeatures.cpp hello-world/CMake/HelloWorld.cpp image-manipulation/manDisplay.cpp - image-manipulation/manGrab1394-1.cpp image-manipulation/manGrab1394-2.cpp image-manipulation/manGrabDirectShow.cpp image-manipulation/manGrabDisk.cpp @@ -61,20 +60,16 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - # ADD_TEST(${binary} ${binary}) - -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/manual/geometric-features/manGeometricFeatures.cpp b/example/manual/geometric-features/manGeometricFeatures.cpp index 3a116f07921a73417fa2af663c7ad41beee6655d..c3b11bdca97d78219d0ab169f7baaea8fd7e0d75 100644 --- a/example/manual/geometric-features/manGeometricFeatures.cpp +++ b/example/manual/geometric-features/manGeometricFeatures.cpp @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: manGeometricFeatures.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: manGeometricFeatures.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -77,65 +77,70 @@ int main() { #ifdef VISP_HAVE_GTK - - std::cout << "ViSP geometric features display example" <<std::endl; - unsigned int height = 288; - unsigned int width = 384; - vpImage<unsigned char> I(height,width); - I = 255; // I is a white image - - // create a display window - vpDisplayGTK display; - // initialize a display attached to image I - display.init(I,100,100,"ViSP geometric features display"); - // camera parameters to digitalize the image plane - vpCameraParameters cam(600,600,width/2,height/2); // px,py,u0,v0 - - // pose of the camera with reference to the scene - vpTranslationVector t(0,0,1); - vpRxyzVector rxyz(-M_PI/4,0,0); - vpRotationMatrix R(rxyz); - vpHomogeneousMatrix cMo(t, R); - - // scene building, geometric features definition - vpPoint point; - point.setWorldCoordinates(0,0,0);// (X0=0,Y0=0,Z0=0) - vpLine line; - line.setWorldCoordinates(1,1,0,0,0,0,1,0); // planes:(X+Y=0)&(Z=0) - vpCylinder cylinder; - cylinder.setWorldCoordinates(1,-1,0,0,0,0,0.1); // alpha=1,beta=-1,gamma=0, - // X0=0,Y0=0,Z0=0,R=0.1 - vpCircle circle; - circle.setWorldCoordinates(0,0,1,0,0,0,0.1); // plane:(Z=0),X0=0,Y0=0,Z=0,R=0.1 - vpSphere sphere; - sphere.setWorldCoordinates(0,0,0,0.1); // X0=0,Y0=0,Z0=0,R=0.1 - - // change frame to be the camera frame and project features in the image plane - point.project(cMo); - line.project(cMo); - cylinder.project(cMo); - circle.project(cMo); - sphere.project(cMo); - - // display the scene - vpDisplay::display(I); // display I - // draw the projections of the 3D geometric features in the image plane. - point.display(I,cam,vpColor::black); // draw a black cross over I - line.display(I,cam,vpColor::blue); // draw a blue line over I - cylinder.display(I,cam,vpColor::red); // draw two red lines over I - circle.display(I,cam,vpColor::orange); // draw an orange ellipse over I - sphere.display(I,cam,vpColor::black); // draw a black ellipse over I - - vpDisplay::flush(I); // flush the display buffer - std::cout << "A click in the display to exit" << std::endl; - vpDisplay::getClick(I); // wait for a click in the display to exit - - // save the drawing - vpImage<vpRGBa> Ic; - vpDisplay::getImage(I,Ic); - std::cout << "ViSP creates \"./geometricFeatures.ppm\" B&W image "<< std::endl; - vpImageIo::write(Ic, "./geometricFeatures.ppm"); + try { + std::cout << "ViSP geometric features display example" <<std::endl; + unsigned int height = 288; + unsigned int width = 384; + vpImage<unsigned char> I(height,width); + I = 255; // I is a white image + + // create a display window + vpDisplayGTK display; + // initialize a display attached to image I + display.init(I,100,100,"ViSP geometric features display"); + // camera parameters to digitalize the image plane + vpCameraParameters cam(600,600,width/2,height/2); // px,py,u0,v0 + + // pose of the camera with reference to the scene + vpTranslationVector t(0,0,1); + vpRxyzVector rxyz(-M_PI/4,0,0); + vpRotationMatrix R(rxyz); + vpHomogeneousMatrix cMo(t, R); + + // scene building, geometric features definition + vpPoint point; + point.setWorldCoordinates(0,0,0);// (X0=0,Y0=0,Z0=0) + vpLine line; + line.setWorldCoordinates(1,1,0,0,0,0,1,0); // planes:(X+Y=0)&(Z=0) + vpCylinder cylinder; + cylinder.setWorldCoordinates(1,-1,0,0,0,0,0.1); // alpha=1,beta=-1,gamma=0, + // X0=0,Y0=0,Z0=0,R=0.1 + vpCircle circle; + circle.setWorldCoordinates(0,0,1,0,0,0,0.1); // plane:(Z=0),X0=0,Y0=0,Z=0,R=0.1 + vpSphere sphere; + sphere.setWorldCoordinates(0,0,0,0.1); // X0=0,Y0=0,Z0=0,R=0.1 + + // change frame to be the camera frame and project features in the image plane + point.project(cMo); + line.project(cMo); + cylinder.project(cMo); + circle.project(cMo); + sphere.project(cMo); + + // display the scene + vpDisplay::display(I); // display I + // draw the projections of the 3D geometric features in the image plane. + point.display(I,cam,vpColor::black); // draw a black cross over I + line.display(I,cam,vpColor::blue); // draw a blue line over I + cylinder.display(I,cam,vpColor::red); // draw two red lines over I + circle.display(I,cam,vpColor::orange); // draw an orange ellipse over I + sphere.display(I,cam,vpColor::black); // draw a black ellipse over I + + vpDisplay::flush(I); // flush the display buffer + std::cout << "A click in the display to exit" << std::endl; + vpDisplay::getClick(I); // wait for a click in the display to exit + + // save the drawing + vpImage<vpRGBa> Ic; + vpDisplay::getImage(I,Ic); + std::cout << "ViSP creates \"./geometricFeatures.ppm\" B&W image "<< std::endl; + vpImageIo::write(Ic, "./geometricFeatures.ppm"); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } #endif - return 0; } diff --git a/example/manual/hello-world/Autotools/have_visp.m4 b/example/manual/hello-world/Autotools/have_visp.m4 index 3d99b63dad90dbadc3965c16f0f2fc126974b0ae..951eb3df63aa02ba5d18054d46aaaa9b7333e129 100644 --- a/example/manual/hello-world/Autotools/have_visp.m4 +++ b/example/manual/hello-world/Autotools/have_visp.m4 @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: have_visp.m4 4057 2013-01-05 13:10:29Z fspindle $ +# $Id: have_visp.m4 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/example/manual/hello-world/CMake/CMakeLists.txt b/example/manual/hello-world/CMake/CMakeLists.txt index 5e3c301e65192ad67d1108cb212c9c0e6b5cba53..64a265027ec94468ce638bd246a8c64d9ee98b32 100644 --- a/example/manual/hello-world/CMake/CMakeLists.txt +++ b/example/manual/hello-world/CMake/CMakeLists.txt @@ -1,10 +1,10 @@ -PROJECT(HelloWorld) +project(HelloWorld) -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +cmake_minimum_required(VERSION 2.6) -FIND_PACKAGE(VISP REQUIRED) -IF(VISP_FOUND) - INCLUDE(${VISP_USE_FILE}) -ENDIF(VISP_FOUND) +find_package(VISP REQUIRED) +if(VISP_FOUND) + include(${VISP_USE_FILE}) +endif() -ADD_EXECUTABLE(HelloWorld HelloWorld.cpp) +add_executable(HelloWorld HelloWorld.cpp) diff --git a/example/manual/hello-world/CMake/HelloWorld.cpp b/example/manual/hello-world/CMake/HelloWorld.cpp index 552bcb564865e7f0d1d7c80581e13b923f554d44..86acb033258a930e56f928db5984976f8623a737 100644 --- a/example/manual/hello-world/CMake/HelloWorld.cpp +++ b/example/manual/hello-world/CMake/HelloWorld.cpp @@ -6,18 +6,25 @@ int main() { - vpThetaUVector tu; + try { + vpThetaUVector tu; - // Construct a rotation matrix from the theta U angles - vpRotationMatrix R(vpMath::rad(0.),vpMath::rad(180)+100*std::numeric_limits<double>::epsilon(),0.); + // Construct a rotation matrix from the theta U angles + vpRotationMatrix R(vpMath::rad(0.),vpMath::rad(180)+100*std::numeric_limits<double>::epsilon(),0.); - // Extract the theta U angles from a rotation matrix - tu.buildFrom(R); + // Extract the theta U angles from a rotation matrix + tu.buildFrom(R); - // Since the rotation vector is 3 values column vector, the - // transpose operation produce a row vector. - vpRowVector tu_t = tu.t(); - - // Print the transpose row vector - std::cout << tu_t << std::endl; + // Since the rotation vector is 3 values column vector, the + // transpose operation produce a row vector. + vpRowVector tu_t = tu.t(); + + // Print the transpose row vector + std::cout << tu_t << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/manual/hello-world/Makefile/Makefile b/example/manual/hello-world/Makefile/Makefile index 19233aecbd7761d899030c33e85de6b6dbc4e249..3bdacce426b4f4e6ca062d25e4d5e0c5115acd57 100644 --- a/example/manual/hello-world/Makefile/Makefile +++ b/example/manual/hello-world/Makefile/Makefile @@ -3,17 +3,17 @@ CXX = g++ # In a Makefile there are two ways to build a project with ViSP as third party: # 1. Using visp-config script provided either in <visp build tree>/bin/visp-config # or in <visp install tree>/bin/visp-config (typically in /usr/bin/visp-config) -VISP_BUILD_DIR = /local/soft/ViSP/ViSP-build -VISP_CFLAGS = `$(VISP_BUILD_DIR)/bin/visp-config --cflags` -VISP_LDFLAGS = `$(VISP_BUILD_DIR)/bin/visp-config --libs` +#VISP_BUILD_DIR = /local/soft/ViSP/ViSP-build +#VISP_CFLAGS = `$(VISP_BUILD_DIR)/bin/visp-config --cflags` +#VISP_LDFLAGS = `$(VISP_BUILD_DIR)/bin/visp-config --libs` -# 2. Using pkg-config on visp.pc file provided in <visp install tree>/lib/pkgconfig/visp.pc -# (typically in /usr/lib/pkgconfig/visp.pc) +# 2. Using pkg-config on visp.pc file provided in <visp install tree>/lib/x86_64-linux-gnu/pkgconfig/visp.pc +# (typically in /usr/lib/x86_64-linux-gnu/pkgconfig/visp.pc) # If visp.pc pkg-config file is not found, you may set PKG_CONFIG_PATH # environment variable with the path to access to visp.pc -# For example: export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:<visp install tree>/lib/pkgconfig -#VISP_CFLAGS = `pkg-config --cflags visp` -#VISP_LDFLAGS = `pkg-config --libs visp` +# For example: export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:<visp install tree>/lib/x86_64-linux-gnu/pkgconfig +VISP_CFLAGS = `pkg-config --cflags visp` +VISP_LDFLAGS = `pkg-config --libs visp` HelloWorld: HelloWorld.cpp diff --git a/example/manual/image-manipulation/manDisplay.cpp b/example/manual/image-manipulation/manDisplay.cpp index 25fba953b8fa416646849ff24b37f305c8e91195..9ee243951c78880ca34b77830c2007addca818d0 100644 --- a/example/manual/image-manipulation/manDisplay.cpp +++ b/example/manual/image-manipulation/manDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manDisplay.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manDisplay.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,96 +55,102 @@ int main() { - // Create a grey level image - vpImage<vpRGBa> I ; + try { + // Create a grey level image + vpImage<vpRGBa> I ; - // Create image points for pixel coordinates - vpImagePoint ip, ip1, ip2; + // Create image points for pixel coordinates + vpImagePoint ip, ip1, ip2; - // Load a grey image from the disk. Klimt.ppm image is part of the ViSP - // image data set available from http://www.irisa.fr/lagadic/visp/download.html - std::string filename = "./Klimt.ppm"; - vpImageIo::read(I, filename) ; + // Load a grey image from the disk. Klimt.ppm image is part of the ViSP + // image data set available from http://www.irisa.fr/lagadic/visp/download.html + std::string filename = "./Klimt.ppm"; + vpImageIo::read(I, filename) ; #ifdef VISP_HAVE_GTK - // Create a display using GTK - vpDisplayGTK display; - - // For this grey level image, open a GTK display at position 100,100 - // in the screen, and with title "GTK display" - display.init(I, 100, 100, "GTK display") ; - - // Display the image - vpDisplay::display(I) ; - - // Display in overlay a red cross at position 100,10 in the - // image. The lines are 20 pixels long - ip.set_i( 200 ); - ip.set_j( 200 ); - vpDisplay::displayCross(I, ip, 20, vpColor::red, 3) ; - - // Display in overlay a horizontal red line - ip1.set_i( 10 ); - ip1.set_j( 0 ); - ip2.set_i( 10 ); - ip2.set_j( I.getWidth() ); - vpDisplay::displayLine(I, ip1, ip2, vpColor::red, 3) ; - - // Display in overlay a vertical green dot line - ip1.set_i( 0 ); - ip1.set_j( 20 ); - ip2.set_i( I.getWidth() ); - ip2.set_j( 20 ); - vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green, 3) ; - - // Display in overlay a blue arrow - ip1.set_i( 0 ); - ip1.set_j( 0 ); - ip2.set_i( 100 ); - ip2.set_j( 100 ); - vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue, 8, 4, 3) ; - - // Display in overlay some circles. The position of the center is 200, 200 - // the radius is increased by 20 pixels for each circle - for (unsigned i=0 ; i < 5 ; i++) { + // Create a display using GTK + vpDisplayGTK display; + + // For this grey level image, open a GTK display at position 100,100 + // in the screen, and with title "GTK display" + display.init(I, 100, 100, "GTK display") ; + + // Display the image + vpDisplay::display(I) ; + + // Display in overlay a red cross at position 100,10 in the + // image. The lines are 20 pixels long ip.set_i( 200 ); ip.set_j( 200 ); - vpDisplay::displayCircle(I, ip, 20*i, vpColor::white, false, 3) ; - } - - // Display in overlay a rectangle. - // The position of the top left corner is 300, 200. - // The width is 200. The height is 100. - ip.set_i( 280 ); - ip.set_j( 150 ); - vpDisplay::displayRectangle(I, ip, 270, 30,vpColor::purple, false, 3) ; - - // Display in overlay a yellow string - ip.set_i( 300 ); - ip.set_j( 160 ); - vpDisplay::displayCharString(I, ip, - "ViSP is a marvelous software", - vpColor::black) ; - //Flush the display : without this line nothing will appear on the screen - vpDisplay::flush(I); - - // Create a color image - vpImage<vpRGBa> Ioverlay ; - // Updates the color image with the original loaded image and the overlay - vpDisplay::getImage(I, Ioverlay) ; - - // Write the color image on the disk - filename = "./Klimt.overlay.ppm"; - vpImageIo::write(Ioverlay, filename) ; - - // If click is allowed, wait for a mouse click to close the display - std::cout << "\nA click to close the windows..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; - - // Close the display - vpDisplay::close(I); + vpDisplay::displayCross(I, ip, 20, vpColor::red, 3) ; + + // Display in overlay a horizontal red line + ip1.set_i( 10 ); + ip1.set_j( 0 ); + ip2.set_i( 10 ); + ip2.set_j( I.getWidth() ); + vpDisplay::displayLine(I, ip1, ip2, vpColor::red, 3) ; + + // Display in overlay a vertical green dot line + ip1.set_i( 0 ); + ip1.set_j( 20 ); + ip2.set_i( I.getWidth() ); + ip2.set_j( 20 ); + vpDisplay::displayDotLine(I, ip1, ip2, vpColor::green, 3) ; + + // Display in overlay a blue arrow + ip1.set_i( 0 ); + ip1.set_j( 0 ); + ip2.set_i( 100 ); + ip2.set_j( 100 ); + vpDisplay::displayArrow(I, ip1, ip2, vpColor::blue, 8, 4, 3) ; + + // Display in overlay some circles. The position of the center is 200, 200 + // the radius is increased by 20 pixels for each circle + for (unsigned i=0 ; i < 5 ; i++) { + ip.set_i( 200 ); + ip.set_j( 200 ); + vpDisplay::displayCircle(I, ip, 20*i, vpColor::white, false, 3) ; + } + + // Display in overlay a rectangle. + // The position of the top left corner is 300, 200. + // The width is 200. The height is 100. + ip.set_i( 280 ); + ip.set_j( 150 ); + vpDisplay::displayRectangle(I, ip, 270, 30,vpColor::purple, false, 3) ; + + // Display in overlay a yellow string + ip.set_i( 300 ); + ip.set_j( 160 ); + vpDisplay::displayText(I, ip, + "ViSP is a marvelous software", + vpColor::black) ; + //Flush the display : without this line nothing will appear on the screen + vpDisplay::flush(I); + + // Create a color image + vpImage<vpRGBa> Ioverlay ; + // Updates the color image with the original loaded image and the overlay + vpDisplay::getImage(I, Ioverlay) ; + + // Write the color image on the disk + filename = "./Klimt.overlay.ppm"; + vpImageIo::write(Ioverlay, filename) ; + + // If click is allowed, wait for a mouse click to close the display + std::cout << "\nA click to close the windows..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + + // Close the display + vpDisplay::close(I); #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/manual/image-manipulation/manGrab1394-2.cpp b/example/manual/image-manipulation/manGrab1394-2.cpp index 7d4e4494de3b3a24bbee9e5faec91da6a156d7e2..81d2ce56b30abaa381b4194fd78dd5268c5e7120 100644 --- a/example/manual/image-manipulation/manGrab1394-2.cpp +++ b/example/manual/image-manipulation/manGrab1394-2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manGrab1394-2.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manGrab1394-2.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,32 +62,37 @@ int main() { #ifdef VISP_HAVE_DC1394_2 + try { + unsigned int ncameras; // Number of cameras on the bus + vp1394TwoGrabber g; + g.getNumCameras(ncameras); + vpImage<unsigned char> *I = new vpImage<unsigned char> [ncameras]; - unsigned int ncameras; // Number of cameras on the bus - vp1394TwoGrabber g; - g.getNumCameras(ncameras); - vpImage<unsigned char> *I = new vpImage<unsigned char> [ncameras]; - - // If the first camera supports vpVIDEO_MODE_640x480_YUV422 video mode - g.setCamera(0); - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_YUV422); - - // If all cameras support 30 fps acquisition - for (unsigned int camera=0; camera < ncameras; camera ++) { - g.setCamera(camera); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); - } - - for ( ; ; ) { + // If the first camera supports vpVIDEO_MODE_640x480_YUV422 video mode + g.setCamera(0); + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_YUV422); + + // If all cameras support 30 fps acquisition for (unsigned int camera=0; camera < ncameras; camera ++) { - // Acquire successively images from the different cameras g.setCamera(camera); - g.acquire(I[camera]); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); } + + for ( ; ; ) { + for (unsigned int camera=0; camera < ncameras; camera ++) { + // Acquire successively images from the different cameras + g.setCamera(camera); + g.acquire(I[camera]); + } + } + delete [] I; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - delete [] I; #endif - return 0; } diff --git a/example/manual/image-manipulation/manGrabDirectShow.cpp b/example/manual/image-manipulation/manGrabDirectShow.cpp index e524cd2e69072e3e5806bd10e2195c5703058f7d..43f925ca4750b7b5677de9c9092f4b11fef8d290 100644 --- a/example/manual/image-manipulation/manGrabDirectShow.cpp +++ b/example/manual/image-manipulation/manGrabDirectShow.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manGrabDirectShow.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manGrabDirectShow.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,24 +59,31 @@ int main() { - vpImage<unsigned char> I; // Grey level image - + try { + vpImage<unsigned char> I; // Grey level image + #ifdef VISP_HAVE_DIRECTSHOW - vpDirectShowGrabber g; // Create the grabber - if(g.getDeviceNumber() == 0) //test if a camera is connected - { - g.close(); - return -1; - } - - g.open(); // Initialize the grabber - - g.setImageSize(640,480); // If the camera supports 640x480 image size - g.setFramerate(30); // If the camera supports 30fps framerate - - for ( ; ; ) - g.acquire(I); // Acquire an image + vpDirectShowGrabber g; // Create the grabber + if(g.getDeviceNumber() == 0) //test if a camera is connected + { + g.close(); + return -1; + } + + g.open(); // Initialize the grabber + + g.setImageSize(640,480); // If the camera supports 640x480 image size + g.setFramerate(30); // If the camera supports 30fps framerate + + for ( ; ; ) + g.acquire(I); // Acquire an image #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } + } diff --git a/example/manual/image-manipulation/manGrabDisk.cpp b/example/manual/image-manipulation/manGrabDisk.cpp index c5ab19c38c02a20067706e82cedf57e0bfec9509..6bafc461e00ec2bb325a2a943551c80ed706f845 100644 --- a/example/manual/image-manipulation/manGrabDisk.cpp +++ b/example/manual/image-manipulation/manGrabDisk.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manGrabDisk.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manGrabDisk.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,36 +59,43 @@ int main() { - vpImage<unsigned char> I; // Grey level image + try { + vpImage<unsigned char> I; // Grey level image - // Declare a framegrabber able to read a sequence of successive - // images from the disk - vpDiskGrabber g; + // Declare a framegrabber able to read a sequence of successive + // images from the disk + vpDiskGrabber g; - // Set the path to the directory containing the sequence - g.setDirectory("/tmp"); - // Set the image base name. The directory and the base name constitute - // the constant part of the full filename - g.setBaseName("image"); - // Set the step between two images of the sequence - g.setStep(3); - // Set the number of digits to build the image number - g.setNumberOfZero(4); - // Set the first frame number of the sequence - g.setImageNumber(1); - // Set the file extension of the images of the sequence - g.setExtension("pgm"); + // Set the path to the directory containing the sequence + g.setDirectory("/tmp"); + // Set the image base name. The directory and the base name constitute + // the constant part of the full filename + g.setBaseName("image"); + // Set the step between two images of the sequence + g.setStep(3); + // Set the number of digits to build the image number + g.setNumberOfZero(4); + // Set the first frame number of the sequence + g.setImageNumber(1); + // Set the file extension of the images of the sequence + g.setExtension("pgm"); - // Open the framegrabber by loading the first image of the sequence - g.open(I) ; + // Open the framegrabber by loading the first image of the sequence + g.open(I) ; - // this is the loop over the image sequence - for(int cpt = 0; cpt < 100; cpt++) - { - // read the image and then increment the image counter so that the next - // call to acquire(I) will get the next image - g.acquire(I) ; - } + // this is the loop over the image sequence + for(int cpt = 0; cpt < 100; cpt++) + { + // read the image and then increment the image counter so that the next + // call to acquire(I) will get the next image + g.acquire(I) ; + } - return 0; + return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/manual/image-manipulation/manGrabOpenCV.cpp b/example/manual/image-manipulation/manGrabOpenCV.cpp index 1bb26dd32efb99b27dcfd620633bfe247b98ef19..c2928ac0410bc4da86f30787404c05b5cc530b11 100644 --- a/example/manual/image-manipulation/manGrabOpenCV.cpp +++ b/example/manual/image-manipulation/manGrabOpenCV.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manGrabOpenCV.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manGrabOpenCV.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/manual/image-manipulation/manGrabV4l2.cpp b/example/manual/image-manipulation/manGrabV4l2.cpp index de32fd5d16de8821e80618aa959d61f35f1f974d..21910d57bd5dcd9a8c2efb16eef7b3346dbaf02a 100644 --- a/example/manual/image-manipulation/manGrabV4l2.cpp +++ b/example/manual/image-manipulation/manGrabV4l2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manGrabV4l2.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manGrabV4l2.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,19 +57,24 @@ int main() { + try { + vpImage<unsigned char> I; // Grey level image - vpImage<unsigned char> I; // Grey level image - #ifdef VISP_HAVE_V4L2 - vpV4l2Grabber g; - g.setInput(2); // Input 2 on the board - g.setWidth(768); // Acquired images are 768 width - g.setHeight(576); // Acquired images are 576 height - g.setNBuffers(3); // 3 ring buffers to ensure real-time acquisition - g.open(I); // Open the grabber - for ( ; ; ) - g.acquire(I); // Acquire a 768x576 grey image + vpV4l2Grabber g; + g.setInput(2); // Input 2 on the board + g.setWidth(768); // Acquired images are 768 width + g.setHeight(576); // Acquired images are 576 height + g.setNBuffers(3); // 3 ring buffers to ensure real-time acquisition + g.open(I); // Open the grabber + for ( ; ; ) + g.acquire(I); // Acquire a 768x576 grey image #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/example/manual/moments/manServoMomentsSimple.cpp b/example/manual/moments/manServoMomentsSimple.cpp index 7e684fb25b113f89f4a1f1183c2e396ce02a85d1..6cebf4154a0ffd2af9c979a4462e8f4bb4cceaa8 100644 --- a/example/manual/moments/manServoMomentsSimple.cpp +++ b/example/manual/moments/manServoMomentsSimple.cpp @@ -3,7 +3,7 @@ * $Id: servoMomentPolygon.cpp 3323 2011-09-13 15:23:56Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,11 @@ #include <limits> #include <iostream> //some console output //this function converts the plane defined by the cMo to 1/Z=Ax+By+C plane form -void cMoToABC(vpHomogeneousMatrix& cMo, double& A,double& B, double& C){ + +void cMoToABC(vpHomogeneousMatrix& cMo, double& A,double& B, double& C); + +void cMoToABC(vpHomogeneousMatrix& cMo, double& A,double& B, double& C) +{ vpPlane pl; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); @@ -76,98 +80,102 @@ void cMoToABC(vpHomogeneousMatrix& cMo, double& A,double& B, double& C){ int main() { - double x[8] = { 1,3, 4,-1 ,-3,-2,-1,1}; - double y[8] = { 0,1, 4, 4, -2,-2, 1,0}; - double A,B,C,Ad,Bd,Cd; - - int nbpoints = 8; - std::vector<vpPoint> vec_p,vec_p_d; // vectors that contain the vertices of the contour polygon - - vpHomogeneousMatrix cMo(0.1,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); - - cMoToABC(cMo,A,B,C); - cMoToABC(cdMo,Ad,Bd,Cd); - // Define source and destination polygons - for (int i = 0 ; i < nbpoints ; i++){ - vpPoint p; - p.setWorldCoordinates(x[i],y[i],0.0); - p.track(cMo) ; - vec_p.push_back(p); - p.track(cdMo) ; - vec_p_d.push_back(p); + try { + double x[8] = { 1,3, 4,-1 ,-3,-2,-1,1}; + double y[8] = { 0,1, 4, 4, -2,-2, 1,0}; + double A,B,C,Ad,Bd,Cd; + + int nbpoints = 8; + std::vector<vpPoint> vec_p,vec_p_d; // vectors that contain the vertices of the contour polygon + + vpHomogeneousMatrix cMo(0.1,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)); + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); + + cMoToABC(cMo,A,B,C); + cMoToABC(cdMo,Ad,Bd,Cd); + // Define source and destination polygons + for (int i = 0 ; i < nbpoints ; i++){ + vpPoint p; + p.setWorldCoordinates(x[i],y[i],0.0); + p.track(cMo) ; + vec_p.push_back(p); + p.track(cdMo) ; + vec_p_d.push_back(p); + } + + vpMomentObject cur(6); // Create a source moment object with 6 as maximum order + cur.setType(vpMomentObject::DENSE_POLYGON); // The object is defined by a countour polygon + cur.fromVector(vec_p); // Init the dense object with the source polygon + + vpMomentObject dst(6); // Create a destination moment object with 6 as maximum order + dst.setType(vpMomentObject::DENSE_POLYGON); // The object is defined by a countour polygon + dst.fromVector(vec_p_d); // Init the dense object with the destination polygon + + //init classic moment primitives (for source) + vpMomentCommon mdb_cur(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst)); //Init classic features + vpFeatureMomentCommon fmdb_cur(mdb_cur); + + ////init classic moment primitives (for destination) + vpMomentCommon mdb_dst(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst)); //Init classic features + vpFeatureMomentCommon fmdb_dst(mdb_dst); + + //update+compute moment primitives from object (for destination) + mdb_dst.updateAll(dst); + //update+compute features (+interaction matrixes) from plane + fmdb_dst.updateAll(Ad,Bd,Cd); + + //define visual servoing task + vpServo task; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(1) ; + + task.addFeature(fmdb_cur.getFeatureGravityNormalized(),fmdb_dst.getFeatureGravityNormalized()); + task.addFeature(fmdb_cur.getFeatureAn(),fmdb_dst.getFeatureAn()); + //the object is NOT symmetric + //select C4 and C6 + task.addFeature(fmdb_cur.getFeatureCInvariant(),fmdb_dst.getFeatureCInvariant(), + vpFeatureMomentCInvariant::selectC4() | vpFeatureMomentCInvariant::selectC6()); + task.addFeature(fmdb_cur.getFeatureAlpha(),fmdb_dst.getFeatureAlpha()); + + vpBasicFeature *al = new vpFeatureMomentAlpha(mdb_dst,0,0,1.); + al->init(); + al->error(*al); + //param robot + vpRobotCamera robot ; + float sampling_time = 0.010f; // Sampling period in seconds + robot.setSamplingTime(sampling_time); + robot.setPosition(cMo); + + do{ + robot.getPosition(cMo); + vec_p.clear(); + + for (int i = 0 ; i < nbpoints ; i++){ + vpPoint p; + p.setWorldCoordinates(x[i],y[i],0.0); + p.track(cMo) ; + vec_p.push_back(p); + } + cMoToABC(cMo,A,B,C); + + cur.fromVector(vec_p); + //update+compute moment primitives from object (for source) + mdb_cur.updateAll(cur); + //update+compute features (+interaction matrixes) from plane + fmdb_cur.updateAll(A,B,C); + + vpColVector v = task.computeControlLaw(); + task.print(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + double t = vpTime::measureTimeMs(); + vpTime::wait(t, sampling_time * 1000); // Wait 10 ms + } while(( task.getError() ).sumSquare()>0.005); + std::cout << "final error=" << ( task.getError() ).sumSquare() << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - vpMomentObject cur(6); // Create a source moment object with 6 as maximum order - cur.setType(vpMomentObject::DENSE_POLYGON); // The object is defined by a countour polygon - cur.fromVector(vec_p); // Init the dense object with the source polygon - - vpMomentObject dst(6); // Create a destination moment object with 6 as maximum order - dst.setType(vpMomentObject::DENSE_POLYGON); // The object is defined by a countour polygon - dst.fromVector(vec_p_d); // Init the dense object with the destination polygon - - //init classic moment primitives (for source) - vpMomentCommon mdb_cur(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst)); //Init classic features - vpFeatureMomentCommon fmdb_cur(mdb_cur); - - ////init classic moment primitives (for destination) - vpMomentCommon mdb_dst(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst)); //Init classic features - vpFeatureMomentCommon fmdb_dst(mdb_dst); - - - //update+compute moment primitives from object (for destination) - mdb_dst.updateAll(dst); - //update+compute features (+interaction matrixes) from plane - fmdb_dst.updateAll(Ad,Bd,Cd); - - //define visual servoing task - vpServo task; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(1) ; - - task.addFeature(fmdb_cur.getFeatureGravityNormalized(),fmdb_dst.getFeatureGravityNormalized()); - task.addFeature(fmdb_cur.getFeatureAn(),fmdb_dst.getFeatureAn()); - //the object is NOT symmetric - //select C4 and C6 - task.addFeature(fmdb_cur.getFeatureCInvariant(),fmdb_dst.getFeatureCInvariant(), - vpFeatureMomentCInvariant::selectC4() | vpFeatureMomentCInvariant::selectC6()); - task.addFeature(fmdb_cur.getFeatureAlpha(),fmdb_dst.getFeatureAlpha()); - - vpBasicFeature *al = new vpFeatureMomentAlpha(mdb_dst,0,0,1.); - al->init(); - al->error(*al); - //param robot - vpRobotCamera robot ; - float sampling_time = 0.010f; // Sampling period in seconds - robot.setSamplingTime(sampling_time); - robot.setPosition(cMo); - - do{ - robot.getPosition(cMo); - vec_p.clear(); - - for (int i = 0 ; i < nbpoints ; i++){ - vpPoint p; - p.setWorldCoordinates(x[i],y[i],0.0); - p.track(cMo) ; - vec_p.push_back(p); - } - cMoToABC(cMo,A,B,C); - - cur.fromVector(vec_p); - //update+compute moment primitives from object (for source) - mdb_cur.updateAll(cur); - //update+compute features (+interaction matrixes) from plane - fmdb_cur.updateAll(A,B,C); - - vpColVector v = task.computeControlLaw(); - task.print(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - double t = vpTime::measureTimeMs(); - vpTime::wait(t, sampling_time * 1000); // Wait 10 ms - }while(( task.getError() ).sumSquare()>0.005); - std::cout << "final error=" << ( task.getError() ).sumSquare() << std::endl; - - return 0; } diff --git a/example/manual/ogre/HelloWorldOgre.cpp b/example/manual/ogre/HelloWorldOgre.cpp index 5b58452d5a92345ca474933127b69369a0f33c63..e9c91c8b7dd7433491f7e8320b51d69f6a6a3e74 100644 --- a/example/manual/ogre/HelloWorldOgre.cpp +++ b/example/manual/ogre/HelloWorldOgre.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: HelloWorldOgre.cpp 4111 2013-02-06 17:27:14Z fspindle $ + * $Id: HelloWorldOgre.cpp 5128 2015-01-06 11:46:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,6 @@ #include <visp/vpOpenCVGrabber.h> #include <visp/vpV4l2Grabber.h> #include <visp/vp1394TwoGrabber.h> -#include <visp/vpDirectShowGrabber.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpImage.h> #include <visp/vpCameraParameters.h> @@ -57,89 +56,109 @@ int main() { + try { #if defined(VISP_HAVE_OGRE) -#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_DIRECTSHOW) || defined(VISP_HAVE_OPENCV) +#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) || (VISP_HAVE_OPENCV_VERSION >= 0x020100) - // Now we try to find an available framegrabber + // Image to stock gathered data + // Here we acquire a color image. The consequence will be that + // the background texture used in Ogre renderer will be also in color. + vpImage<vpRGBa> I; + + // Now we try to find an available framegrabber #if defined(VISP_HAVE_V4L2) - // Video for linux 2 grabber - vpV4l2Grabber grabber; + // Video for linux 2 grabber + vpV4l2Grabber grabber; + grabber.open(I); + grabber.acquire(I); #elif defined(VISP_HAVE_DC1394_2) - // libdc1394-2 - vp1394TwoGrabber grabber; -#elif defined(VISP_HAVE_DIRECTSHOW) - // OpenCV to gather images - vpOpenCVGrabber grabber; + // libdc1394-2 + vp1394TwoGrabber grabber; + grabber.open(I); + grabber.acquire(I); #elif defined(VISP_HAVE_OPENCV) - // OpenCV to gather images - vpOpenCVGrabber grabber; + // OpenCV to gather images + cv::VideoCapture grabber(0); // open the default camera + if(!grabber.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + grabber >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #endif - // Image to stock gathered data - // Here we acquire a color image. The consequence will be that - // the background texture used in Ogre renderer will be also in color. - vpImage<vpRGBa> I; - // Open frame grabber - // Here we acquire an image from an available framegrabber to update - // the image size - grabber.open(I); + // Parameters of our camera + double px = 565; + double py = 565; + double u0 = I.getWidth() / 2; + double v0 = I.getHeight() / 2; + vpCameraParameters cam(px,py,u0,v0); + // The matrix with our pose + // Defines the pose of the object in the camera frame + vpHomogeneousMatrix cMo; - // Parameters of our camera - double px = 565; - double py = 565; - double u0 = grabber.getWidth() / 2; - double v0 = grabber.getHeight() / 2; - vpCameraParameters cam(px,py,u0,v0); - // The matrix with our pose - // Defines the pose of the object in the camera frame - vpHomogeneousMatrix cMo; + // Our object + // A simulator with the camera parameters defined above, + // a grey level background image and of the good size + vpAROgre ogre(cam, I.getWidth(), I.getHeight()); + // Initialisation + // Here we load the requested plugins specified in the "plugins.cfg" file + // and the resources specified in the "resources.cfg" file + // These two files can be found respectively in ViSP_HAVE_OGRE_PLUGINS_PATH + // and ViSP_HAVE_OGRE_RESOURCES_PATH folders + ogre.init(I); - // Our object - // A simulator with the camera parameters defined above, - // a grey level background image and of the good size - vpAROgre ogre(cam, (unsigned int)grabber.getWidth(), (unsigned int)grabber.getHeight()); - // Initialisation - // Here we load the requested plugins specified in the "plugins.cfg" file - // and the resources specified in the "resources.cfg" file - // These two files can be found respectively in ViSP_HAVE_OGRE_PLUGINS_PATH - // and ViSP_HAVE_OGRE_RESOURCES_PATH folders - ogre.init(I); + // Create a basic scene + // ----------------------------------- + // Loading things + // ----------------------------------- + // As you will see in section 5, our + // application knows locations where + // it can search for medias. + // Here we use a mesh included in + // the installation files : a robot. + // ----------------------------------- + // Here we load the "robot.mesh" model that is found thanks to the resources locations + // specified in the "resources.cfg" file + ogre.load("Robot", "robot.mesh"); + ogre.setPosition("Robot", vpTranslationVector(-0.3, 0.2, 0)); + ogre.setScale("Robot", 0.001f,0.001f,0.001f); + ogre.setRotation("Robot", vpRotationMatrix(vpRxyzVector(M_PI, 0, 0))); - // Create a basic scene - // ----------------------------------- - // Loading things - // ----------------------------------- - // As you will see in section 5, our - // application knows locations where - // it can search for medias. - // Here we use a mesh included in - // the installation files : a robot. - // ----------------------------------- - // Here we load the "robot.mesh" model that is found thanks to the resources locations - // specified in the "resources.cfg" file - ogre.load("Robot", "robot.mesh"); - ogre.setPosition("Robot", vpTranslationVector(0, 0.05, 0.5)); - ogre.setScale("Robot", 0.001f,0.001f,0.001f); - ogre.setRotation("Robot", vpRotationMatrix(vpRxyzVector(M_PI, -M_PI/4, 0))); + cMo[2][3] = 1.; // Z = 1 meter + std::cout << "cMo:\n" << cMo << std::endl; - // Rendering loop, ended with on escape - while(ogre.continueRendering()){ - // Image Acquisition - // Acquire a new image - grabber.acquire(I); - //Pose computation - // ... - // cMo updated - // Display the robot at the position specified by cMo with vpAROgre - ogre.display(I,cMo); - } - // Release video device - grabber.close(); + // Rendering loop, ended with on escape + while(ogre.continueRendering()){ + // Acquire a new image +#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) + grabber.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + grabber >> frame; + vpImageConvert::convert(frame, I); +#endif + //Pose computation + // ... + // cMo updated + // Display the robot at the position specified by cMo with vpAROgre + ogre.display(I,cMo); + } #else - std::cout << "You need an available framegrabber to run this example" << std::endl; + std::cout << "You need an available framegrabber to run this example" << std::endl; #endif #else - std::cout << "You Ogre3D to run this example" << std::endl; + std::cout << "You need Ogre3D to run this example" << std::endl; #endif + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } + catch(...) { + std::cout << "Catch an exception " << std::endl; + return 1; + } } diff --git a/example/manual/ogre/HelloWorldOgreAdvanced.cpp b/example/manual/ogre/HelloWorldOgreAdvanced.cpp index 3ed4bc6c6c7ac52bbbd1dd5cdaeeff08bc705dcc..1033351bcdcd9a653ecc6f0512f9f58076f1fcf4 100644 --- a/example/manual/ogre/HelloWorldOgreAdvanced.cpp +++ b/example/manual/ogre/HelloWorldOgreAdvanced.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: HelloWorldOgreAdvanced.cpp 4111 2013-02-06 17:27:14Z fspindle $ + * $Id: HelloWorldOgreAdvanced.cpp 5128 2015-01-06 11:46:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,6 @@ #include <visp/vpOpenCVGrabber.h> #include <visp/vpV4l2Grabber.h> #include <visp/vp1394TwoGrabber.h> -#include <visp/vpDirectShowGrabber.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpImage.h> #include <visp/vpCameraParameters.h> @@ -70,6 +69,7 @@ public: unsigned int width = 640, unsigned int height = 480) : vpAROgre(cam, width, height) { + mAnimationState = NULL; } protected: @@ -79,9 +79,9 @@ protected: Ogre::Entity* robot = mSceneMgr->createEntity("Robot", "robot.mesh"); // Attach robot to scene graph Ogre::SceneNode* RobotNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("Robot"); - RobotNode->setPosition(0.0, 0.05f, 0.5); + RobotNode->setPosition((Ogre::Real)-0.3, (Ogre::Real)0.2, (Ogre::Real)0); RobotNode->attachObject(robot); - RobotNode->scale(0.001f,0.001f,0.001f); + RobotNode->scale((Ogre::Real)0.001,(Ogre::Real)0.001,(Ogre::Real)0.001); RobotNode->pitch(Ogre::Degree(180)); RobotNode->yaw(Ogre::Degree(-90)); @@ -108,63 +108,89 @@ protected: int main() { + try { #if defined(VISP_HAVE_OGRE) -#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_DIRECTSHOW) || defined(VISP_HAVE_OPENCV) +#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) || (VISP_HAVE_OPENCV_VERSION >= 0x020100) - // Now we try to find an available framegrabber + // Image to store gathered data + // Here we acquire a grey level image. The consequence will be that + // the background texture used in Ogre renderer will be also in grey + // level. + vpImage<unsigned char> I; + + // Now we try to find an available framegrabber #if defined(VISP_HAVE_V4L2) - // Video for linux 2 grabber - vpV4l2Grabber grabber; + // Video for linux 2 grabber + vpV4l2Grabber grabber; + // Open frame grabber + // Here we acquire an image from an available framegrabber to update + // the image size + grabber.open(I); + grabber.acquire(I); #elif defined(VISP_HAVE_DC1394_2) - // libdc1394-2 - vp1394TwoGrabber grabber; -#elif defined(VISP_HAVE_DIRECTSHOW) - // OpenCV to gather images - vpOpenCVGrabber grabber; + // libdc1394-2 + vp1394TwoGrabber grabber; + // Open frame grabber + // Here we acquire an image from an available framegrabber to update + // the image size + grabber.open(I); + grabber.acquire(I); #elif defined(VISP_HAVE_OPENCV) - // OpenCV to gather images - vpOpenCVGrabber grabber; + // OpenCV to gather images + cv::VideoCapture grabber(0); // open the default camera + if(!grabber.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + grabber >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #endif - // Image to store gathered data - // Here we acquire a grey level image. The consequence will be that - // the background texture used in Ogre renderer will be also in grey - // level. - vpImage<unsigned char> I; - // Open frame grabber - // Here we acquire an image from an available framegrabber to update - // the image size - grabber.open(I); - // Parameters of our camera - double px = 565; - double py = 565; - double u0 = grabber.getWidth() / 2; - double v0 = grabber.getHeight() / 2; - vpCameraParameters cam(px,py,u0,v0); - // The matrix with our pose - vpHomogeneousMatrix cMo; - - // Our object - vpAROgreAdvanced ogre(cam, (unsigned int)grabber.getWidth(), (unsigned int)grabber.getHeight()); - // Initialisation - ogre.init(I); - - // Rendering loop - while(ogre.continueRendering()){ - // Image Acquisition - grabber.acquire(I); - // Pose computation - // ... - // cMo updated - // Display with vpAROgre - ogre.display(I, cMo); - } - // Release video device - grabber.close(); + // Parameters of our camera + double px = 565; + double py = 565; + double u0 = I.getWidth() / 2; + double v0 = I.getHeight() / 2; + vpCameraParameters cam(px,py,u0,v0); + // The matrix with our pose + vpHomogeneousMatrix cMo; + cMo[2][3] = 1.; // Z = 1 meter + + // Our object + vpAROgreAdvanced ogre(cam, I.getWidth(), I.getHeight()); + // Initialisation + ogre.init(I); + + // Rendering loop + while(ogre.continueRendering()){ + // Acquire a new image +#if defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_DC1394_2) + grabber.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + grabber >> frame; + vpImageConvert::convert(frame, I); +#endif + // Pose computation + // ... + // cMo updated + // Display with vpAROgre + ogre.display(I, cMo); + } #else - std::cout << "You need an available framegrabber to run this example" << std::endl; + std::cout << "You need an available framegrabber to run this example" << std::endl; #endif #else - std::cout << "You Ogre3D to run this example" << std::endl; + std::cout << "You need Ogre3D to run this example" << std::endl; #endif + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } + catch(...) { + std::cout << "Catch an exception " << std::endl; + return 1; + } } diff --git a/example/manual/simulation/manServo4PointsDisplay.cpp b/example/manual/simulation/manServo4PointsDisplay.cpp index fdd213ed3887682abc7ce5dd65616265df6a4d65..9028c79bd3c1ce942808514ff87f041b43eb3dec 100755 --- a/example/manual/simulation/manServo4PointsDisplay.cpp +++ b/example/manual/simulation/manServo4PointsDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manServo4PointsDisplay.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manServo4PointsDisplay.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,129 +73,135 @@ int main() { - ////////////////////////////////////////// - // sets the initial camera location - vpHomogeneousMatrix cMo(0.3,0.2,3, - vpMath::rad(0),vpMath::rad(0),vpMath::rad(40)) ; - - /////////////////////////////////// - // initialize the robot - vpRobotCamera robot ; - robot.setSamplingTime(0.04); // 40ms - robot.setPosition(cMo) ; - - //initialize the camera parameters - vpCameraParameters cam(800,800,240,180); - - //Image definition - unsigned int height = 360 ; - unsigned int width = 480 ; - vpImage<unsigned char> I(height,width); - - //Display initialization - vpDisplayGTK disp; - disp.init(I,100,100,"Simulation display"); - - //////////////////////////////////////// - // Desired visual features initialization - - // sets the points coordinates in the object frame (in meter) - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1,0) ; - point[1].setWorldCoordinates(0.1,-0.1,0) ; - point[2].setWorldCoordinates(0.1,0.1,0) ; - point[3].setWorldCoordinates(-0.1,0.1,0) ; - - // sets the desired camera location - vpHomogeneousMatrix cMo_d(0,0,1,0,0,0) ; - - // computes the 3D point coordinates in the camera frame and its 2D coordinates - for (int i = 0 ; i < 4 ; i++) - point[i].project(cMo_d) ; - - // creates the associated features - vpFeaturePoint pd[4] ; - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(pd[i],point[i]) ; - - - /////////////////////////////////////// - // Current visual features initialization - - // computes the 3D point coordinates in the camera frame and its 2D coordinates - for (int i = 0 ; i < 4 ; i++) - point[i].project(cMo) ; - - // creates the associated features - vpFeaturePoint p[4] ; - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i],point[i]) ; - - - ///////////////////////////////// - // Task defintion - vpServo task ; - // we want an eye-in-hand control law ; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; - - // Set the position of the camera in the end-effector frame - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - - // we want to see a point on a point - for (int i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - // Set the gain - task.setLambda(1.0) ; - // Print the current information about the task - task.print(); - - - //////////////////////////////////////////////// - // The control loop - int k = 0; - while(k++ < 200){ - double t = vpTime::measureTimeMs(); - - // Display the image background - vpDisplay::display(I); - - // Update the current features + try { + ////////////////////////////////////////// + // sets the initial camera location + vpHomogeneousMatrix cMo(0.3,0.2,3, + vpMath::rad(0),vpMath::rad(0),vpMath::rad(40)) ; + + /////////////////////////////////// + // initialize the robot + vpRobotCamera robot ; + robot.setSamplingTime(0.04); // 40ms + robot.setPosition(cMo) ; + + //initialize the camera parameters + vpCameraParameters cam(800,800,240,180); + + //Image definition + unsigned int height = 360 ; + unsigned int width = 480 ; + vpImage<unsigned char> I(height,width); + + //Display initialization + vpDisplayGTK disp; + disp.init(I,100,100,"Simulation display"); + + //////////////////////////////////////// + // Desired visual features initialization + + // sets the points coordinates in the object frame (in meter) + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1,0) ; + point[1].setWorldCoordinates(0.1,-0.1,0) ; + point[2].setWorldCoordinates(0.1,0.1,0) ; + point[3].setWorldCoordinates(-0.1,0.1,0) ; + + // sets the desired camera location + vpHomogeneousMatrix cMo_d(0,0,1,0,0,0) ; + + // computes the 3D point coordinates in the camera frame and its 2D coordinates + for (int i = 0 ; i < 4 ; i++) + point[i].project(cMo_d) ; + + // creates the associated features + vpFeaturePoint pd[4] ; + for (int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(pd[i],point[i]) ; + + + /////////////////////////////////////// + // Current visual features initialization + + // computes the 3D point coordinates in the camera frame and its 2D coordinates for (int i = 0 ; i < 4 ; i++) - { point[i].project(cMo) ; + + // creates the associated features + vpFeaturePoint p[4] ; + for (int i = 0 ; i < 4 ; i++) vpFeatureBuilder::create(p[i],point[i]) ; - } - - // Display the task features (current and desired) - vpServoDisplay::display(task,cam,I); - vpDisplay::flush(I); - - // Update the robot Jacobian + + + ///////////////////////////////// + // Task defintion + vpServo task ; + // we want an eye-in-hand control law ; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; + + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; + // Set the Jacobian (expressed in the end-effector frame) + vpMatrix eJe ; robot.get_eJe(eJe) ; task.set_eJe(eJe) ; - - // Compute the control law - vpColVector v = task.computeControlLaw() ; - - // Send the computed velocity to the robot and compute the new robot position - robot.setVelocity(vpRobot::ARTICULAR_FRAME, v) ; - robot.getPosition(cMo) ; - + + // we want to see a point on a point + for (int i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; + // Set the gain + task.setLambda(1.0) ; // Print the current information about the task task.print(); - - // Wait 40 ms - vpTime::wait(t,40); - } - task.kill(); - return 0; + + + //////////////////////////////////////////////// + // The control loop + int k = 0; + while(k++ < 200){ + double t = vpTime::measureTimeMs(); + + // Display the image background + vpDisplay::display(I); + + // Update the current features + for (int i = 0 ; i < 4 ; i++) + { + point[i].project(cMo) ; + vpFeatureBuilder::create(p[i],point[i]) ; + } + + // Display the task features (current and desired) + vpServoDisplay::display(task,cam,I); + vpDisplay::flush(I); + + // Update the robot Jacobian + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // Compute the control law + vpColVector v = task.computeControlLaw() ; + + // Send the computed velocity to the robot and compute the new robot position + robot.setVelocity(vpRobot::ARTICULAR_FRAME, v) ; + robot.getPosition(cMo) ; + + // Print the current information about the task + task.print(); + + // Wait 40 ms + vpTime::wait(t,40); + } + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/manual/simulation/manSimu4Dots.cpp b/example/manual/simulation/manSimu4Dots.cpp index c2df04d2ffc24301dc5430e5d39b341c1a109f80..e915971f142ed6b76cc8908b6e4d56000ce6e106 100755 --- a/example/manual/simulation/manSimu4Dots.cpp +++ b/example/manual/simulation/manSimu4Dots.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manSimu4Dots.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manSimu4Dots.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -255,28 +255,35 @@ void *mainLoop (void *_simu) int main() { - vpSimulator simu ; - - // Internal view initialization : view from the robot camera - simu.initInternalViewer(480, 360) ; - // External view initialization : view from an external camera - simu.initExternalViewer(300, 300) ; - - // Inernal camera paramters initialization - vpCameraParameters cam(800,800,240,180) ; - simu.setInternalCameraParameters(cam) ; - - vpTime::wait(500) ; - // Load the scene - std::cout << "Load : ./4Points.iv" << std::endl - << "This file should be in the working directory" << std::endl; - - simu.load("./4points.iv") ; - - // Run the main loop - simu.initApplication(&mainLoop) ; - // Run the simulator - simu.mainLoop() ; + try { + vpSimulator simu ; + + // Internal view initialization : view from the robot camera + simu.initInternalViewer(480, 360) ; + // External view initialization : view from an external camera + simu.initExternalViewer(300, 300) ; + + // Inernal camera paramters initialization + vpCameraParameters cam(800,800,240,180) ; + simu.setInternalCameraParameters(cam) ; + + vpTime::wait(500) ; + // Load the scene + std::cout << "Load : ./4Points.iv" << std::endl + << "This file should be in the working directory" << std::endl; + + simu.load("./4points.iv") ; + + // Run the main loop + simu.initApplication(&mainLoop) ; + // Run the simulator + simu.mainLoop() ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/manual/simulation/manSimu4Points.cpp b/example/manual/simulation/manSimu4Points.cpp index 9a31ea4566f973e1aa1e012a61237d869a4eb1c6..4335ef8e7bf5fa92e82dfa314c0f57314297f3f3 100755 --- a/example/manual/simulation/manSimu4Points.cpp +++ b/example/manual/simulation/manSimu4Points.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: manSimu4Points.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: manSimu4Points.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -207,27 +207,34 @@ void *mainLoop (void *_simu) int main() { - vpSimulator simu ; - - // Internal view initialization : view from the robot camera - simu.initInternalViewer(480, 360) ; - // External view initialization : view from an external camera - simu.initExternalViewer(300, 300) ; - - // Inernal camera paramters initialization - vpCameraParameters cam(800,800,240,180) ; - simu.setInternalCameraParameters(cam) ; - - vpTime::wait(1000) ; - // Load the scene - std::cout << "Load : ./4Points.iv" << std::endl - << "This file should be in the working directory" << std::endl; - simu.load("./4points.iv") ; - - // Run the main loop - simu.initApplication(&mainLoop) ; - // Run the simulator - simu.mainLoop() ; + try { + vpSimulator simu ; + + // Internal view initialization : view from the robot camera + simu.initInternalViewer(480, 360) ; + // External view initialization : view from an external camera + simu.initExternalViewer(300, 300) ; + + // Inernal camera paramters initialization + vpCameraParameters cam(800,800,240,180) ; + simu.setInternalCameraParameters(cam) ; + + vpTime::wait(1000) ; + // Load the scene + std::cout << "Load : ./4Points.iv" << std::endl + << "This file should be in the working directory" << std::endl; + simu.load("./4points.iv") ; + + // Run the main loop + simu.initApplication(&mainLoop) ; + // Run the simulator + simu.mainLoop() ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/math/BSpline.cpp b/example/math/BSpline.cpp index 10129045389e1ccb4302a61202ea8e3317f79183..00379d2f30c0f1dee068ca854669c13e5a8a48b7 100644 --- a/example/math/BSpline.cpp +++ b/example/math/BSpline.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: BSpline.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: BSpline.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -75,6 +75,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -122,9 +125,9 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -132,7 +135,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -141,7 +144,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -152,147 +155,145 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_click_allowed = true; - bool opt_display = true; - - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + try { + bool opt_click_allowed = true; + bool opt_display = true; + + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I(540,480); + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I(540,480); - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display; + vpDisplayD3D display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display image") ; vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + + vpBSpline bSpline; + std::list<double> knots; + knots.push_back(0); + knots.push_back(0); + knots.push_back(0); + knots.push_back(1); + knots.push_back(2); + knots.push_back(3); + knots.push_back(4); + knots.push_back(4); + knots.push_back(5); + knots.push_back(5); + knots.push_back(5); + + std::list<vpImagePoint> controlPoints; + vpImagePoint pt; + pt.set_ij(50,300); + controlPoints.push_back(pt); + pt.set_ij(100,130); + controlPoints.push_back(pt); + pt.set_ij(150,400); + controlPoints.push_back(pt); + pt.set_ij(200,370); + controlPoints.push_back(pt); + pt.set_ij(250,120); + controlPoints.push_back(pt); + pt.set_ij(300,250); + controlPoints.push_back(pt); + pt.set_ij(350,200); + controlPoints.push_back(pt); + pt.set_ij(400,300); + controlPoints.push_back(pt); + + bSpline.set_p(2); + bSpline.set_knots(knots); + bSpline.set_controlPoints(controlPoints); + + std::cout << "The parameters are :" <<std::endl; + std::cout << "p : " << bSpline.get_p() <<std::endl; + std::cout << "" <<std::endl; + std::cout << "The knot vector :" <<std::endl; + std::list<double> knots_cur; + bSpline.get_knots(knots_cur); + unsigned int i_display=0; + for(std::list<double>::const_iterator it=knots_cur.begin(); it!=knots_cur.end(); ++it, ++i_display){ + std::cout << i_display << " ---> " << *it << std::endl; + } + std::cout << "The control points are :" <<std::endl; + std::list<vpImagePoint> controlPoints_cur; + bSpline.get_controlPoints(controlPoints_cur); + i_display=0; + for(std::list<vpImagePoint>::const_iterator it=controlPoints_cur.begin(); it!=controlPoints_cur.end(); ++it, ++i_display){ + std::cout << i_display << " ---> " << *it << std::endl; } - } - vpBSpline bSpline; - std::list<double> knots; - knots.push_back(0); - knots.push_back(0); - knots.push_back(0); - knots.push_back(1); - knots.push_back(2); - knots.push_back(3); - knots.push_back(4); - knots.push_back(4); - knots.push_back(5); - knots.push_back(5); - knots.push_back(5); - - std::list<vpImagePoint> controlPoints; - vpImagePoint pt; - pt.set_ij(50,300); - controlPoints.push_back(pt); - pt.set_ij(100,130); - controlPoints.push_back(pt); - pt.set_ij(150,400); - controlPoints.push_back(pt); - pt.set_ij(200,370); - controlPoints.push_back(pt); - pt.set_ij(250,120); - controlPoints.push_back(pt); - pt.set_ij(300,250); - controlPoints.push_back(pt); - pt.set_ij(350,200); - controlPoints.push_back(pt); - pt.set_ij(400,300); - controlPoints.push_back(pt); - - bSpline.set_p(2); - bSpline.set_knots(knots); - bSpline.set_controlPoints(controlPoints); - - std::cout << "The parameters are :" <<std::endl; - std::cout << "p : " << bSpline.get_p() <<std::endl; - std::cout << "" <<std::endl; - std::cout << "The knot vector :" <<std::endl; - std::list<double> knots_cur; - bSpline.get_knots(knots_cur); - unsigned int i_display=0; - for(std::list<double>::const_iterator it=knots_cur.begin(); it!=knots_cur.end(); ++it, ++i_display){ - std::cout << i_display << " ---> " << *it << std::endl; - } - std::cout << "The control points are :" <<std::endl; - std::list<vpImagePoint> controlPoints_cur; - bSpline.get_controlPoints(controlPoints_cur); - i_display=0; - for(std::list<vpImagePoint>::const_iterator it=controlPoints_cur.begin(); it!=controlPoints_cur.end(); ++it, ++i_display){ - std::cout << i_display << " ---> " << *it << std::endl; - } + unsigned int i = bSpline.findSpan(5/2.0); + std::cout << "The knot interval number for the value u = 5/2 is : " << i <<std::endl; - unsigned int i = bSpline.findSpan(5/2.0); - std::cout << "The knot interval number for the value u = 5/2 is : " << i <<std::endl; - - vpBasisFunction *N = NULL; - N = bSpline.computeBasisFuns(5/2.0); - std::cout << "The nonvanishing basis functions N(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < bSpline.get_p()+1; j++) - std::cout << N[j].value << std::endl; - - vpBasisFunction **N2 = NULL; - N2 = bSpline.computeDersBasisFuns(5/2.0, 2); - std::cout << "The first derivatives of the basis functions N'(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < bSpline.get_p()+1; j++) - std::cout << N2[1][j].value << std::endl; - - std::cout << "The second derivatives of the basis functions N''(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < bSpline.get_p()+1; j++) - std::cout << N2[2][j].value << std::endl; - - if (opt_display && opt_click_allowed) - { - double u = 0.0; - vpImagePoint pt; - while (u <= 5) + vpBasisFunction *N = NULL; + N = bSpline.computeBasisFuns(5/2.0); + std::cout << "The nonvanishing basis functions N(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < bSpline.get_p()+1; j++) + std::cout << N[j].value << std::endl; + + vpBasisFunction **N2 = NULL; + N2 = bSpline.computeDersBasisFuns(5/2.0, 2); + std::cout << "The first derivatives of the basis functions N'(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < bSpline.get_p()+1; j++) + std::cout << N2[1][j].value << std::endl; + + std::cout << "The second derivatives of the basis functions N''(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < bSpline.get_p()+1; j++) + std::cout << N2[2][j].value << std::endl; + + if (opt_display && opt_click_allowed) { - pt = bSpline.computeCurvePoint(u); - vpDisplay::displayCross(I,pt,4,vpColor::red); - u+=0.01; + double u = 0.0; + while (u <= 5) + { + pt = bSpline.computeCurvePoint(u); + vpDisplay::displayCross(I,pt,4,vpColor::red); + u+=0.01; + } + for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!= controlPoints.end(); ++it){ + vpDisplay::displayCross(I, *it, 4, vpColor::green); + } + vpDisplay::flush(I) ; + vpDisplay::getClick(I); } - for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!= controlPoints.end(); ++it){ - vpDisplay::displayCross(I, *it, 4, vpColor::green); + + if (N != NULL) delete[] N; + if (N2 != NULL) + { + for (unsigned int j = 0; j <= 2; j++) + delete[] N2[j]; + delete[] N2; } - vpDisplay::flush(I) ; - vpDisplay::getClick(I); + + return 0; } - - if (N != NULL) delete[] N; - if (N2 != NULL) - { - for (unsigned int j = 0; j <= 2; j++) - delete[] N2[j]; - delete[] N2; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } #else diff --git a/example/math/CMakeLists.txt b/example/math/CMakeLists.txt index 5e08c60ba870975dc2782058c2e949a3e4faae9a..d64b3e348f1f7cd6335fcd09218462249dadffb4 100644 --- a/example/math/CMakeLists.txt +++ b/example/math/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,27 +43,27 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE exponentialMap.cpp BSpline.cpp Nurbs.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary} -c ${OPTION_TO_DESACTIVE_DISPLAY}) + add_test(${binary} ${binary} -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/math/Nurbs.cpp b/example/math/Nurbs.cpp index 1423f43448c495424898e284e8b90d18e501ffd8..1bd00c58da491c34e9d6c12a872e9e561992f3b3 100644 --- a/example/math/Nurbs.cpp +++ b/example/math/Nurbs.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: Nurbs.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: Nurbs.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,8 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); /*! @@ -121,9 +123,9 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -131,7 +133,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -140,7 +142,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -151,242 +153,238 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_click_allowed = true; - bool opt_display = true; - - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + try { + bool opt_click_allowed = true; + bool opt_display = true; + + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I(540,480); - vpImage<unsigned char> I2(540,480); - vpImage<unsigned char> I3(540,480); + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I(540,480); + vpImage<unsigned char> I2(540,480); + vpImage<unsigned char> I3(540,480); - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display[3]; + vpDisplayX display[3]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[3]; + vpDisplayGDI display[3]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[3]; + vpDisplayGTK display[3]; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display[3]; + vpDisplayOpenCV display[3]; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(I, 100, 100,"Points as control points") ; vpDisplay::display(I) ; vpDisplay::flush(I) ; + } + vpNurbs Nurbs; + std::list<double> knots; + knots.push_back(0); + knots.push_back(0); + knots.push_back(0); + knots.push_back(1); + knots.push_back(2); + knots.push_back(3); + knots.push_back(4); + knots.push_back(4); + knots.push_back(5); + knots.push_back(5); + knots.push_back(5); + + std::list<vpImagePoint> controlPoints; + std::list<double> weights; + vpImagePoint pt; + pt.set_ij(50,300); + controlPoints.push_back(pt); + weights.push_back(1); + pt.set_ij(100,130); + controlPoints.push_back(pt); + weights.push_back(5); + pt.set_ij(150,400); + controlPoints.push_back(pt); + weights.push_back(0.2); + pt.set_ij(200,370); + controlPoints.push_back(pt); + weights.push_back(10); + pt.set_ij(250,120); + controlPoints.push_back(pt); + weights.push_back(1); + pt.set_ij(300,250); + controlPoints.push_back(pt); + weights.push_back(2); + pt.set_ij(350,200); + controlPoints.push_back(pt); + weights.push_back(3); + pt.set_ij(400,300); + controlPoints.push_back(pt); + weights.push_back(1); + + Nurbs.set_p(2); + Nurbs.set_knots(knots); + Nurbs.set_controlPoints(controlPoints); + Nurbs.set_weights(weights); + + std::cout << "The parameters are :" <<std::endl; + std::cout << "p : " << Nurbs.get_p() <<std::endl; + std::cout << "" <<std::endl; + std::cout << "The knot vector :" <<std::endl; + std::list<double> knots_cur; + Nurbs.get_knots(knots_cur); + unsigned int i_display=0; + for(std::list<double>::const_iterator it=knots_cur.begin(); it!=knots_cur.end(); ++it, ++i_display){ + std::cout << i_display << " ---> " << *it << std::endl; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + std::cout << "The control points are :" <<std::endl; + std::list<vpImagePoint> controlPoints_cur; + Nurbs.get_controlPoints(controlPoints_cur); + i_display=0; + for(std::list<vpImagePoint>::const_iterator it=controlPoints_cur.begin(); it!=controlPoints_cur.end(); ++it, ++i_display){ + std::cout << i_display << " ---> " << *it << std::endl; + } + std::cout << "The associated weights are :" <<std::endl; + std::list<double> weights_cur; + Nurbs.get_weights(weights_cur); + i_display=0; + for(std::list<double>::const_iterator it=weights_cur.begin(); it!=weights_cur.end(); ++it, ++i_display){ + std::cout << i_display << " ---> " << *it << std::endl; } - } - vpNurbs Nurbs; - std::list<double> knots; - knots.push_back(0); - knots.push_back(0); - knots.push_back(0); - knots.push_back(1); - knots.push_back(2); - knots.push_back(3); - knots.push_back(4); - knots.push_back(4); - knots.push_back(5); - knots.push_back(5); - knots.push_back(5); - - std::list<vpImagePoint> controlPoints; - std::list<double> weights; - vpImagePoint pt; - pt.set_ij(50,300); - controlPoints.push_back(pt); - weights.push_back(1); - pt.set_ij(100,130); - controlPoints.push_back(pt); - weights.push_back(5); - pt.set_ij(150,400); - controlPoints.push_back(pt); - weights.push_back(0.2); - pt.set_ij(200,370); - controlPoints.push_back(pt); - weights.push_back(10); - pt.set_ij(250,120); - controlPoints.push_back(pt); - weights.push_back(1); - pt.set_ij(300,250); - controlPoints.push_back(pt); - weights.push_back(2); - pt.set_ij(350,200); - controlPoints.push_back(pt); - weights.push_back(3); - pt.set_ij(400,300); - controlPoints.push_back(pt); - weights.push_back(1); - - Nurbs.set_p(2); - Nurbs.set_knots(knots); - Nurbs.set_controlPoints(controlPoints); - Nurbs.set_weights(weights); - - std::cout << "The parameters are :" <<std::endl; - std::cout << "p : " << Nurbs.get_p() <<std::endl; - std::cout << "" <<std::endl; - std::cout << "The knot vector :" <<std::endl; - std::list<double> knots_cur; - Nurbs.get_knots(knots_cur); - unsigned int i_display=0; - for(std::list<double>::const_iterator it=knots_cur.begin(); it!=knots_cur.end(); ++it, ++i_display){ - std::cout << i_display << " ---> " << *it << std::endl; - } - std::cout << "The control points are :" <<std::endl; - std::list<vpImagePoint> controlPoints_cur; - Nurbs.get_controlPoints(controlPoints_cur); - i_display=0; - for(std::list<vpImagePoint>::const_iterator it=controlPoints_cur.begin(); it!=controlPoints_cur.end(); ++it, ++i_display){ - std::cout << i_display << " ---> " << *it << std::endl; - } - std::cout << "The associated weights are :" <<std::endl; - std::list<double> weights_cur; - Nurbs.get_weights(weights_cur); - i_display=0; - for(std::list<double>::const_iterator it=weights_cur.begin(); it!=weights_cur.end(); ++it, ++i_display){ - std::cout << i_display << " ---> " << *it << std::endl; - } + unsigned int i = Nurbs.findSpan(5/2.0); + std::cout << "The knot interval number for the value u = 5/2 is : " << i <<std::endl; - unsigned int i = Nurbs.findSpan(5/2.0); - std::cout << "The knot interval number for the value u = 5/2 is : " << i <<std::endl; - - vpBasisFunction *N = NULL; - N = Nurbs.computeBasisFuns(5/2.0); - std::cout << "The nonvanishing basis functions N(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) - std::cout << N[j].value << std::endl; - - vpBasisFunction **N2 = NULL; - N2 = Nurbs.computeDersBasisFuns(5/2.0, 2); - std::cout << "The first derivatives of the basis functions N'(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) - std::cout << N2[1][j].value << std::endl; - - std::cout << "The second derivatives of the basis functions N''(u=5/2) are :" << std::endl; - for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) - std::cout << N2[2][j].value << std::endl; - - if (opt_display && opt_click_allowed) - { - double u = 0.0; - vpImagePoint pt; - while (u <= 5) + vpBasisFunction *N = NULL; + N = Nurbs.computeBasisFuns(5/2.0); + std::cout << "The nonvanishing basis functions N(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) + std::cout << N[j].value << std::endl; + + vpBasisFunction **N2 = NULL; + N2 = Nurbs.computeDersBasisFuns(5/2.0, 2); + std::cout << "The first derivatives of the basis functions N'(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) + std::cout << N2[1][j].value << std::endl; + + std::cout << "The second derivatives of the basis functions N''(u=5/2) are :" << std::endl; + for (unsigned int j = 0; j < Nurbs.get_p()+1; j++) + std::cout << N2[2][j].value << std::endl; + + if (opt_display && opt_click_allowed) { - pt = Nurbs.computeCurvePoint(u); - vpDisplay::displayCross(I,pt,4,vpColor::red); - u+=0.01; + double u = 0.0; + while (u <= 5) + { + pt = Nurbs.computeCurvePoint(u); + vpDisplay::displayCross(I,pt,4,vpColor::red); + u+=0.01; + } + for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ + vpDisplay::displayCross(I, *it, 4, vpColor::green); + } + + vpDisplay::flush(I) ; + vpDisplay::getClick(I); } - for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ - vpDisplay::displayCross(I, *it, 4, vpColor::green); + + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display[1].init(I2, 100, 100,"Points interpolation") ; + vpDisplay::display(I2) ; + vpDisplay::flush(I2) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - vpDisplay::flush(I) ; - vpDisplay::getClick(I); - } - - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display[1].init(I2, 100, 100,"Points interpolation") ; - vpDisplay::display(I2) ; + Nurbs.globalCurveInterp(controlPoints); + + if (opt_display && opt_click_allowed) + { + double u = 0.0; + while (u <= 1) + { + pt = Nurbs.computeCurvePoint(u); + vpDisplay::displayCross(I2,pt,4,vpColor::red); + u+=0.01; + } + + for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ + vpDisplay::displayCross(I2, *it, 4, vpColor::green); + } vpDisplay::flush(I2) ; + vpDisplay::getClick(I2); } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + + + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display[2].init(I3, 100, 100,"Points approximation") ; + vpDisplay::display(I3) ; + vpDisplay::flush(I3) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - - Nurbs.globalCurveInterp(controlPoints); - - if (opt_display && opt_click_allowed) - { - double u = 0.0; - vpImagePoint pt; - while (u <= 1) + + Nurbs.globalCurveApprox(controlPoints,5); + + if (opt_display && opt_click_allowed) { - pt = Nurbs.computeCurvePoint(u); - vpDisplay::displayCross(I2,pt,4,vpColor::red); - u+=0.01; - } + double u = 0.0; + while (u <= 1) + { + pt = Nurbs.computeCurvePoint(u); + vpDisplay::displayCross(I3,pt,4,vpColor::red); + u+=0.01; + } + + for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ + vpDisplay::displayCross(I3, *it, 4, vpColor::green); + } - for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ - vpDisplay::displayCross(I2, *it, 4, vpColor::green); - } - vpDisplay::flush(I2) ; - vpDisplay::getClick(I2); - } - - - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display[2].init(I3, 100, 100,"Points approximation") ; - vpDisplay::display(I3) ; vpDisplay::flush(I3) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - - Nurbs.globalCurveApprox(controlPoints,5); - - if (opt_display && opt_click_allowed) - { - double u = 0.0; - vpImagePoint pt; - while (u <= 1) - { - pt = Nurbs.computeCurvePoint(u); - vpDisplay::displayCross(I3,pt,4,vpColor::red); - u+=0.01; + vpDisplay::getClick(I3); } - for(std::list<vpImagePoint>::const_iterator it=controlPoints.begin(); it!=controlPoints.end(); ++it){ - vpDisplay::displayCross(I3, *it, 4, vpColor::green); + if (N != NULL) delete[] N; + if (N2 != NULL) + { + for (int j = 0; j <= 2; j++) + delete[] N2[j]; + delete[] N2; } - vpDisplay::flush(I3) ; - vpDisplay::getClick(I3); + return 0; + return 0; } - - if (N != NULL) delete[] N; - if (N2 != NULL) - { - for (int j = 0; j <= 2; j++) - delete[] N2[j]; - delete[] N2; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } #else int main() { - std::cout << "This example requires a video device. " + std::cout << "This example requires a video device. " << std::endl << "You should install X11, GTK, OpenCV, GDI or Direct3D" << std::endl diff --git a/example/math/exponentialMap.cpp b/example/math/exponentialMap.cpp index 5fe8cac163889f9da45b44ecc08cbb225a218652..2e627bc83e2e0dec2273a1f43b28bc4230784583 100644 --- a/example/math/exponentialMap.cpp +++ b/example/math/exponentialMap.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: exponentialMap.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: exponentialMap.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,72 +58,77 @@ int main() { - - vpTranslationVector t; - t[0] = 0.1; // t_x in m/s - t[1] = 0.2f; // t_y in m/s - t[2] = 0.f; // t_z in m/s - - vpRxyzVector rxyz; - rxyz[0] = vpMath::rad(0.f); // r_x in rad/s - rxyz[1] = vpMath::rad(0.f); // r_y in rad/s - rxyz[2] = vpMath::rad(90.f); // r_z in rad/s - - // Build a ThetaU rotation vector from a Rxyz vector - vpThetaUVector tu; - tu.buildFrom(rxyz); - - vpColVector v(6); // Velocity vector [t, thetaU]^t - - v[0] = t[0]; // t_x - v[1] = t[1]; // t_y - v[2] = t[2]; // t_z - v[3] = tu[0]; // ThetaU_x - v[4] = tu[1]; // ThetaU_y - v[5] = tu[2]; // ThetaU_z - - std::cout << "Considered velocity : \n" << v << std::endl; - - vpHomogeneousMatrix M; - - // Compute the displacement from the velocity applied during 1 second - M = vpExponentialMap::direct(v); - - { - // Extract translation from homogenous matrix - vpTranslationVector dt; // translation displacement - M.extract(dt); - - // Extract rotation from homogenous matrix - vpRotationMatrix R; - M.extract(R); - vpRxyzVector drxyz(R); // rotational displacement - - std::cout << "Displacement if velocity is applied during 1 s : \n" - << dt << " " << drxyz << std::endl; + try { + vpTranslationVector t; + t[0] = 0.1; // t_x in m/s + t[1] = 0.2f; // t_y in m/s + t[2] = 0.f; // t_z in m/s + + vpRxyzVector rxyz; + rxyz[0] = vpMath::rad(0.f); // r_x in rad/s + rxyz[1] = vpMath::rad(0.f); // r_y in rad/s + rxyz[2] = vpMath::rad(90.f); // r_z in rad/s + + // Build a ThetaU rotation vector from a Rxyz vector + vpThetaUVector tu; + tu.buildFrom(rxyz); + + vpColVector v(6); // Velocity vector [t, thetaU]^t + + v[0] = t[0]; // t_x + v[1] = t[1]; // t_y + v[2] = t[2]; // t_z + v[3] = tu[0]; // ThetaU_x + v[4] = tu[1]; // ThetaU_y + v[5] = tu[2]; // ThetaU_z + + std::cout << "Considered velocity : \n" << v << std::endl; + + vpHomogeneousMatrix M; + + // Compute the displacement from the velocity applied during 1 second + M = vpExponentialMap::direct(v); + + { + // Extract translation from homogenous matrix + vpTranslationVector dt; // translation displacement + M.extract(dt); + + // Extract rotation from homogenous matrix + vpRotationMatrix R; + M.extract(R); + vpRxyzVector drxyz(R); // rotational displacement + + std::cout << "Displacement if velocity is applied during 1 s : \n" + << dt << " " << drxyz << std::endl; + } + + // Compute the displacement from the velocity applied during 2 seconds + M = vpExponentialMap::direct(v, 2.f); + + { + // Extract translation from homogenous matrix + vpTranslationVector dt; // translation displacement + M.extract(dt); + + // Extract rotation from homogenous matrix + vpRotationMatrix R; + M.extract(R); + vpRxyzVector drxyz(R); // rotational displacement + + std::cout << "Displacement if velocity is applied during 2 s : \n" + << dt << " " << drxyz << std::endl; + } + + // Compute the velocity from the displacement observed during 2 seconds + v = vpExponentialMap::inverse(M, 2.f); + + std::cout << "Velocity from displacement observed during 2 s: \n" + << v << std::endl; + return 0; } - - // Compute the displacement from the velocity applied during 2 seconds - M = vpExponentialMap::direct(v, 2.f); - - { - // Extract translation from homogenous matrix - vpTranslationVector dt; // translation displacement - M.extract(dt); - - // Extract rotation from homogenous matrix - vpRotationMatrix R; - M.extract(R); - vpRxyzVector drxyz(R); // rotational displacement - - std::cout << "Displacement if velocity is applied during 2 s : \n" - << dt << " " << drxyz << std::endl; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - // Compute the velocity from the displacement observed during 2 seconds - v = vpExponentialMap::inverse(M, 2.f); - - std::cout << "Velocity from displacement observed during 2 s: \n" - << v << std::endl; - } diff --git a/example/moments/image/CMakeLists.txt b/example/moments/image/CMakeLists.txt index 4cce0c6c85389adcdf348812254723cdb9212148..046dc9c442df6311d99092b39c7e5381d6e55ff1 100644 --- a/example/moments/image/CMakeLists.txt +++ b/example/moments/image/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,20 +38,26 @@ # ############################################################################# -# All source and headers in local directory are added to the project before -# the exacutable is formed. - -FILE(GLOB SOURCE "*.cpp") -FILE(GLOB HEADERS "*.h") - -LIST(APPEND FILES ${SOURCE}) -LIST(APPEND FILES ${HEADERS}) - +# SOURCE variable corresponds to the list of all the sources to build binaries. +# The generate binary comes by removing the .cpp extension to +# the source name. +# +# If you want to add/remove a source, modify here +set(SOURCE + servoMomentImage.cpp +) +# rule for binary build +foreach(source ${SOURCE}) + # Compute the name of the binary to create + get_filename_component(binary ${source} NAME_WE) -ADD_EXECUTABLE(servoMomentsImage ${FILES}) -TARGET_LINK_LIBRARIES(servoMomentsImage ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + # From source compile the binary and add link rules + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/moments/image/servoMomentImage.cpp b/example/moments/image/servoMomentImage.cpp index 099c887ffacaef2f8400709e9f7dd02b3806cfc1..e6577d66fdbae4a4497c57d989ce126c9c23157a 100644 --- a/example/moments/image/servoMomentImage.cpp +++ b/example/moments/image/servoMomentImage.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoMomentImage.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoMomentImage.cpp 4670 2014-02-17 08:59:05Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,22 @@ #include <visp/vpPoseVector.h> #include <visp/vpPlot.h> +#if !defined(_WIN32) && !defined(VISP_HAVE_PTHREAD) +// Robot simulator used in this example is not available +int main() +{ + std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; + std::cout << "You should install pthread third-party library." << std::endl; +} +// No display available +#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) +int main() +{ + std::cout << "Can't run this example since no display capability is available." << std::endl; + std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; +} +#else + //setup robot parameters void paramRobot(); @@ -87,38 +103,29 @@ void init(vpHomogeneousMatrix& cMo, vpHomogeneousMatrix& cdMo); void execute(unsigned int nbIter); //launch the simulation void setInteractionMatrixType(vpServo::vpServoIteractionMatrixType type); double error(); -void _planeToABC(vpPlane& pl, double& A,double& B, double& C); +void planeToABC(vpPlane& pl, double& A,double& B, double& C); void paramRobot(); -#if !defined(WIN32) && !defined(VISP_HAVE_PTHREAD) -// Robot simulator used in this example is not available -int main() -{ - std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; - std::cout << "You should install pthread third-party library." << std::endl; -} -// No display available -#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) -int main() -{ - std::cout << "Can't run this example since no display capability is available." << std::endl; - std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; -} -#else void init_visp_plot(vpPlot& ); int main() { - //intial pose - vpHomogeneousMatrix cMo(-0.1,-0.1,1.5,-vpMath::rad(20),-vpMath::rad(20),-vpMath::rad(30)); - //Desired pose - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,-0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); + try { + //intial pose + vpHomogeneousMatrix cMo(-0.1,-0.1,1.5,-vpMath::rad(20),-vpMath::rad(20),-vpMath::rad(30)); + //Desired pose + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,-0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); - //init the simulation - init(cMo,cdMo); + //init the simulation + init(cMo,cdMo); - execute(1500); - return 0; + execute(1500); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } @@ -243,11 +250,11 @@ void initFeatures(){ vpPlane pl; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); pl.setABCD(0,0,1.0,0); pl.changeFrame(cdMo); - _planeToABC(pl,Ad,Bd,Cd); + planeToABC(pl,Ad,Bd,Cd); //extracting initial position (actually we only care about Zdst) vpTranslationVector vec; @@ -255,8 +262,8 @@ void initFeatures(){ ///////////////////////////// initializing moments and features ///////////////////////////////// //don't need to be specific, vpMomentCommon automatically loads Xg,Yg,An,Ci,Cj,Alpha moments - moments = new vpMomentCommon(vpMomentCommon ::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst), vec[2]); - momentsDes = new vpMomentCommon(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst),vec[2]); + moments = new vpMomentCommon(vpMomentCommon ::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst), vec[2], true); + momentsDes = new vpMomentCommon(vpMomentCommon::getSurface(dst),vpMomentCommon::getMu3(dst),vpMomentCommon::getAlpha(dst),vec[2], true); //same thing with common features featureMoments = new vpFeatureMomentCommon(*moments); featureMomentsDes = new vpFeatureMomentCommon(*momentsDes); @@ -316,8 +323,8 @@ void execute(unsigned int nbIter){ vpPlane pl; double A,B,C; pl.setABCD(0,0,1.0,0); - pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + pl.changeFrame(cMo); + planeToABC(pl,A,B,C); //track points, draw points and add refresh our object refreshScene(obj); @@ -383,7 +390,7 @@ double error(){return _error;} -void _planeToABC(vpPlane& pl, double& A,double& B, double& C){ +void planeToABC(vpPlane& pl, double& A,double& B, double& C){ if(fabs(pl.getD())<std::numeric_limits<double>::epsilon()){ std::cout << "Invalid position:" << std::endl; std::cout << cMo << std::endl; diff --git a/example/moments/points/CMakeLists.txt b/example/moments/points/CMakeLists.txt index 4d1934669c6c45b3f3fbd4789d175073c742f0da..e80fdb28bb9342328f46e07ea97b14d545250b2c 100644 --- a/example/moments/points/CMakeLists.txt +++ b/example/moments/points/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,20 +38,26 @@ # ############################################################################# -# All source and headers in local directory are added to the project before -# the exacutable is formed. - -FILE(GLOB SOURCE "*.cpp") -FILE(GLOB HEADERS "*.h") - -LIST(APPEND FILES ${SOURCE}) -LIST(APPEND FILES ${HEADERS}) - +# SOURCE variable corresponds to the list of all the sources to build binaries. +# The generate binary comes by removing the .cpp extension to +# the source name. +# +# If you want to add/remove a source, modify here +set(SOURCE + servoMomentPoints.cpp +) +# rule for binary build +foreach(source ${SOURCE}) + # Compute the name of the binary to create + get_filename_component(binary ${source} NAME_WE) -ADD_EXECUTABLE(servoMomentsPoints ${FILES}) -TARGET_LINK_LIBRARIES(servoMomentsPoints ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + # From source compile the binary and add link rules + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/moments/points/servoMomentPoints.cpp b/example/moments/points/servoMomentPoints.cpp index 015a674f91ace43c4f7b576c9241435d8197be22..b5e2bedd65bf6baeb28fd1eff274cd3fe2f40e04 100644 --- a/example/moments/points/servoMomentPoints.cpp +++ b/example/moments/points/servoMomentPoints.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoMomentPoints.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoMomentPoints.cpp 4673 2014-02-17 09:06:49Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,6 +67,23 @@ #include <visp/vpSimulatorAfma6.h> #include <visp/vpPlane.h> + +#if !defined(_WIN32) && !defined(VISP_HAVE_PTHREAD) +// Robot simulator used in this example is not available +int main() +{ + std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; + std::cout << "You should install pthread third-party library." << std::endl; +} +// No display available +#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) +int main() +{ + std::cout << "Can't run this example since no display capability is available." << std::endl; + std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; +} +#else + //setup robot parameters void paramRobot(); @@ -81,34 +98,26 @@ void init(vpHomogeneousMatrix& cMo, vpHomogeneousMatrix& cdMo); void execute(unsigned int nbIter); //launch the simulation void setInteractionMatrixType(vpServo::vpServoIteractionMatrixType type); double error(); -void _planeToABC(vpPlane& pl, double& A,double& B, double& C); +void planeToABC(vpPlane& pl, double& A,double& B, double& C); void paramRobot(); +void removeJointLimits(vpSimulatorAfma6& robot); -#if !defined(WIN32) && !defined(VISP_HAVE_PTHREAD) -// Robot simulator used in this example is not available -int main() -{ - std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; - std::cout << "You should install pthread third-party library." << std::endl; -} -// No display available -#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) int main() { - std::cout << "Can't run this example since no display capability is available." << std::endl; - std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; -} -#else -int main(){ - //intial pose - vpHomogeneousMatrix cMo(0.05,0.1,1.5,vpMath::rad(30),vpMath::rad(20),-vpMath::rad(15)); - //Desired pose - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); - - //init and run the simulation - init(cMo,cdMo); - execute(1500); - return 0; + try { //intial pose + vpHomogeneousMatrix cMo(0.05,0.1,1.5,vpMath::rad(30),vpMath::rad(20),-vpMath::rad(15)); + //Desired pose + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); + + //init and run the simulation + init(cMo,cdMo); + execute(1500); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } //init the right display @@ -146,12 +155,10 @@ vpFeatureMomentCommon *featureMomentsDes; vpMomentObject src(6); vpMomentObject dst(6); -using namespace std; - void initScene(){ - vector<vpPoint> src_pts; - vector<vpPoint> dst_pts; + std::vector<vpPoint> src_pts; + std::vector<vpPoint> dst_pts; double x[8] = { 1,3, 4,-1 ,-3,-2,-1,1}; double y[8] = { 0,1, 4, 4, -2,-2, 1,0}; @@ -187,11 +194,11 @@ void initFeatures(){ vpPlane pl; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); pl.setABCD(0,0,1.0,0); pl.changeFrame(cdMo); - _planeToABC(pl,Ad,Bd,Cd); + planeToABC(pl,Ad,Bd,Cd); //extracting initial position (actually we only care about Zdst) vpTranslationVector vec; @@ -229,7 +236,7 @@ void refreshScene(vpMomentObject &obj){ double x[8] = { 1,3, 4,-1 ,-3,-2,-1,1}; double y[8] = { 0,1, 4, 4, -2,-2, 1,0}; int nbpoints = 8; - vector<vpPoint> cur_pts; + std::vector<vpPoint> cur_pts; for (int i = 0 ; i < nbpoints ; i++){ vpPoint p; @@ -280,7 +287,7 @@ void execute(unsigned int nbIter){ double A,B,C; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); //track points, draw points and add refresh our object refreshScene(obj); @@ -316,7 +323,8 @@ void execute(unsigned int nbIter){ delete featureMomentsDes; } -void removeJointLimits(vpSimulatorAfma6& robot){ +void removeJointLimits(vpSimulatorAfma6& robot_) +{ vpColVector limMin(6); vpColVector limMax(6); limMin[0] = vpMath::rad(-3600); @@ -333,12 +341,12 @@ void removeJointLimits(vpSimulatorAfma6& robot){ limMax[4] = vpMath::rad(3600); limMax[5] = vpMath::rad(3600); - robot.setJointLimit(limMin,limMax); - robot.setMaxRotationVelocity(99999); - robot.setMaxTranslationVelocity(999999); + robot_.setJointLimit(limMin,limMax); + robot_.setMaxRotationVelocity(99999); + robot_.setMaxTranslationVelocity(999999); } -void _planeToABC(vpPlane& pl, double& A,double& B, double& C){ +void planeToABC(vpPlane& pl, double& A,double& B, double& C){ if(fabs(pl.getD())<std::numeric_limits<double>::epsilon()){ std::cout << "Invalid position:" << std::endl; std::cout << cMo << std::endl; diff --git a/example/moments/polygon/CMakeLists.txt b/example/moments/polygon/CMakeLists.txt index 904ebb5556faead290a5114186a6f12c19f6eab9..6b54b33e840ef373af122162e32ff54fc520a187 100644 --- a/example/moments/polygon/CMakeLists.txt +++ b/example/moments/polygon/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,20 +38,26 @@ # ############################################################################# -# All source and headers in local directory are added to the project before -# the exacutable is formed. - -FILE(GLOB SOURCE "*.cpp") -FILE(GLOB HEADERS "*.h") - -LIST(APPEND FILES ${SOURCE}) -LIST(APPEND FILES ${HEADERS}) - +# SOURCE variable corresponds to the list of all the sources to build binaries. +# The generate binary comes by removing the .cpp extension to +# the source name. +# +# If you want to add/remove a source, modify here +set(SOURCE + servoMomentPolygon.cpp +) +# rule for binary build +foreach(source ${SOURCE}) + # Compute the name of the binary to create + get_filename_component(binary ${source} NAME_WE) -ADD_EXECUTABLE(servoMomentsPolygon ${FILES}) -TARGET_LINK_LIBRARIES(servoMomentsPolygon ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + # From source compile the binary and add link rules + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/moments/polygon/servoMomentPolygon.cpp b/example/moments/polygon/servoMomentPolygon.cpp index 1b6654d21e0a232bbdd7fc197a2944b681de9128..87cdedeb98b5da4ed706911fc92b39e4b84569ed 100644 --- a/example/moments/polygon/servoMomentPolygon.cpp +++ b/example/moments/polygon/servoMomentPolygon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoMomentPolygon.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoMomentPolygon.cpp 4670 2014-02-17 08:59:05Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,22 @@ #include <visp/vpSimulatorAfma6.h> #include <visp/vpPlane.h> +#if !defined(_WIN32) && !defined(VISP_HAVE_PTHREAD) +// Robot simulator used in this example is not available +int main() +{ + std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; + std::cout << "You should install pthread third-party library." << std::endl; +} +// No display available +#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) +int main() +{ + std::cout << "Can't run this example since no display capability is available." << std::endl; + std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; +} +#else + //setup robot parameters void paramRobot(); @@ -80,34 +96,26 @@ void init(vpHomogeneousMatrix& cMo, vpHomogeneousMatrix& cdMo); void execute(unsigned int nbIter); //launch the simulation void setInteractionMatrixType(vpServo::vpServoIteractionMatrixType type); double error(); -void _planeToABC(vpPlane& pl, double& A,double& B, double& C); +void planeToABC(vpPlane& pl, double& A,double& B, double& C); void paramRobot(); +void removeJointLimits(vpSimulatorAfma6& robot); -#if !defined(WIN32) && !defined(VISP_HAVE_PTHREAD) -// Robot simulator used in this example is not available -int main() -{ - std::cout << "Can't run this example since vpSimulatorAfma6 capability is not available." << std::endl; - std::cout << "You should install pthread third-party library." << std::endl; -} -// No display available -#elif !defined(VISP_HAVE_X11) && !defined(VISP_HAVE_OPENCV) && !defined(VISP_HAVE_GDI) && !defined(VISP_HAVE_D3D9) && !defined(VISP_HAVE_GTK) int main() { - std::cout << "Can't run this example since no display capability is available." << std::endl; - std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl; -} -#else -int main(){ - //intial pose - vpHomogeneousMatrix cMo(-0.1,-0.1,1.5,-vpMath::rad(20),-vpMath::rad(20),-vpMath::rad(30)); - //Desired pose - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); - - //init and run the simulation - init(cMo,cdMo); - execute(1500); - return 0; + try { //intial pose + vpHomogeneousMatrix cMo(-0.1,-0.1,1.5,-vpMath::rad(20),-vpMath::rad(20),-vpMath::rad(30)); + //Desired pose + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); + + //init and run the simulation + init(cMo,cdMo); + execute(1500); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } //init the right display @@ -214,11 +222,11 @@ void initFeatures(){ vpPlane pl; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); pl.setABCD(0,0,1.0,0); pl.changeFrame(cdMo); - _planeToABC(pl,Ad,Bd,Cd); + planeToABC(pl,Ad,Bd,Cd); //extracting initial position (actually we only care about Zdst) vpTranslationVector vec; @@ -275,7 +283,7 @@ void execute(unsigned int nbIter){ double A,B,C; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); //track points, draw points and add refresh our object refreshScene(obj); @@ -313,7 +321,8 @@ void execute(unsigned int nbIter){ void setInteractionMatrixType(vpServo::vpServoIteractionMatrixType type){interaction_type=type;} double error(){return _error;} -void removeJointLimits(vpSimulatorAfma6& robot){ +void removeJointLimits(vpSimulatorAfma6& robot_) +{ vpColVector limMin(6); vpColVector limMax(6); limMin[0] = vpMath::rad(-3600); @@ -330,10 +339,10 @@ void removeJointLimits(vpSimulatorAfma6& robot){ limMax[4] = vpMath::rad(3600); limMax[5] = vpMath::rad(3600); - robot.setJointLimit(limMin,limMax); + robot_.setJointLimit(limMin,limMax); } -void _planeToABC(vpPlane& pl, double& A,double& B, double& C){ +void planeToABC(vpPlane& pl, double& A,double& B, double& C){ if(fabs(pl.getD())<std::numeric_limits<double>::epsilon()){ std::cout << "Invalid position:" << std::endl; std::cout << cMo << std::endl; diff --git a/example/ogre-simulator/AROgre.cpp b/example/ogre-simulator/AROgre.cpp index a0f4668fbb7bf0915de949171b8d2413642b0899..71995b41986d94fa4c7f7bdcebc0d4efbb5d730c 100644 --- a/example/ogre-simulator/AROgre.cpp +++ b/example/ogre-simulator/AROgre.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: AROgre.cpp 4305 2013-07-05 13:23:47Z fspindle $ + * $Id: AROgre.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,10 +50,11 @@ #include <visp/vpConfig.h> #include <iostream> -//#if defined(VISP_HAVE_OGRE) && defined(VISP_HAVE_DISPLAY) -#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK)) +//#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK) || (defined(VISP_HAVE_X11) && ! defined(APPLE))) +#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK) || (defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)))) -#if defined(VISP_HAVE_X11) && ! defined(APPLE) +//#if defined(VISP_HAVE_X11) && ! defined(APPLE) +#if defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)) // produce an error on OSX: ‘typedef int Cursor’ // /usr/X11R6/include/X11/X.h:108: error: ‘Cursor’ has a previous // declaration as ‘typedef XID Cursor’. That's why it should not be @@ -190,6 +191,8 @@ public: if (resourcePath) mResourcePath = resourcePath; std::cout << "mResourcePath: " << mResourcePath<< std::endl; vecDevant = Ogre::Vector3(0,-1,0); + robot = NULL; + mAnimationState = NULL; } protected : @@ -312,7 +315,7 @@ protected : */ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, vpPose * mPose, vpDot2 *md, vpImagePoint *mcog, - vpHomogeneousMatrix *cmo, vpPoint *mP, + vpHomogeneousMatrix *cMo, vpPoint *mP, const bool &opt_click_allowed) { // --------------------------------------------------- @@ -320,7 +323,8 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, // ---------------------------------------------------- bool opt_display = true; -#if defined(VISP_HAVE_X11) && ! defined(APPLE) +//#if defined(VISP_HAVE_X11) && ! defined(APPLE) +#if defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)) // produce an error on OSX: ‘typedef int Cursor’ // /usr/X11R6/include/X11/X.h:108: error: ‘Cursor’ has a previous // declaration as ‘typedef XID Cursor’. That's why it should not be @@ -335,7 +339,6 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, #elif defined VISP_HAVE_D3D9 vpDisplayD3D display; #endif - for (unsigned int i=0 ; i < 4 ; i++) { if (opt_display) { @@ -493,16 +496,16 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, // minimisation method // Pose by Lagrange it provides an initialization of the pose - mPose->computePose(vpPose::LAGRANGE, *cmo) ; + mPose->computePose(vpPose::LAGRANGE, *cMo) ; // the pose is now refined using the virtual visual servoing approach // Warning: cMo needs to be initialized otherwise it may diverge - mPose->computePose(vpPose::VIRTUAL_VS, *cmo) ; + mPose->computePose(vpPose::VIRTUAL_VS, *cMo) ; // Display breifly just to have a glimpse a the ViSP pose // while(cpt<500){ if( opt_display ){ // Display the computed pose - mPose->display(I,*cmo,*mcam, 0.05, vpColor::red) ; + mPose->display(I,*cMo,*mcam, 0.05, vpColor::red) ; vpDisplay::flush(I) ; vpTime::wait(800); } @@ -512,189 +515,178 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, int main(int argc, const char **argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed) == false) { + exit (-1); } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - std::ostringstream s; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; + + exit(-1); + } - if (opt_ppath.empty()){ - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + std::ostringstream s; - // Build the name of the image file + if (opt_ppath.empty()){ + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - s.setf(std::ios::right, std::ios::adjustfield); - s << "image.%04d.pgm"; - filename = dirname + s.str(); - } - else { - filename = opt_ppath; - } + // Build the name of the image file - //We will read a sequence of images - vpVideoReader grabber; - grabber.setFirstFrameIndex(1); - grabber.setFileName(filename.c_str()); - // Grey level image associated to a display in the initial pose computation - vpImage<unsigned char> Idisplay; - // Grey level image to track points - vpImage<unsigned char> I; - // RGBa image to get background - vpImage<vpRGBa> IC; - // Matrix representing camera parameters - vpHomogeneousMatrix cmo; - - // Variables used for pose computation purposes - vpPose mPose; - vpDot2 md[4]; - vpImagePoint mcog[4]; - vpPoint mP[4]; - - // CameraParameters we got from calibration - // Keep u0 and v0 as center of the screen - vpCameraParameters mcam; - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - grabber.open(Idisplay); - grabber.acquire(Idisplay); - vpCameraParameters mcamTmp(592,570,grabber.getWidth()/2,grabber.getHeight()/2); - // Compute the initial pose of the camera - computeInitialPose(&mcamTmp, Idisplay, &mPose, md, mcog, &cmo, mP, - opt_click_allowed); - // Close the framegrabber - grabber.close(); + s.setf(std::ios::right, std::ios::adjustfield); + s << "image.%04d.pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { + filename = opt_ppath; + } - // Associate the grabber to the RGBa image - grabber.open(IC); - mcam.init(mcamTmp); - } - catch(...) - { - // an exception is thrown if an exception from readPGM has been caught - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + //We will read a sequence of images + vpVideoReader grabber; + grabber.setFirstFrameIndex(1); + grabber.setFileName(filename.c_str()); + // Grey level image associated to a display in the initial pose computation + vpImage<unsigned char> Idisplay; + // Grey level image to track points + vpImage<unsigned char> I; + // RGBa image to get background + vpImage<vpRGBa> IC; + // Matrix representing camera parameters + vpHomogeneousMatrix cMo; + + // Variables used for pose computation purposes + vpPose mPose; + vpDot2 md[4]; + vpImagePoint mcog[4]; + vpPoint mP[4]; + + // CameraParameters we got from calibration + // Keep u0 and v0 as center of the screen + vpCameraParameters mcam; + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; + grabber.open(Idisplay); + grabber.acquire(Idisplay); + vpCameraParameters mcamTmp(592,570,grabber.getWidth()/2,grabber.getHeight()/2); + // Compute the initial pose of the camera + computeInitialPose(&mcamTmp, Idisplay, &mPose, md, mcog, &cMo, mP, + opt_click_allowed); + // Close the framegrabber + grabber.close(); + + // Associate the grabber to the RGBa image + grabber.open(IC); + mcam.init(mcamTmp); + } + catch(...) + { + // an exception is thrown if an exception from readPGM has been caught + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // Create a vpRAOgre object with color background - vpAROgreExample ogre(mcam, (unsigned int)grabber.getWidth(), (unsigned int)grabber.getHeight()); - // Initialize it - ogre.init(IC); + // Create a vpRAOgre object with color background + vpAROgreExample ogre(mcam, (unsigned int)grabber.getWidth(), (unsigned int)grabber.getHeight()); + // Initialize it + ogre.init(IC); - try - { - double t0 = vpTime::measureTimeMs(); + double t0 = vpTime::measureTimeMs(); // Rendering loop - while(ogre.continueRendering() && !grabber.end()){ - // Acquire a frame + while(ogre.continueRendering() && !grabber.end()) { + // Acquire a frame grabber.acquire(IC); // Convert it to a grey level image for tracking purpose vpImageConvert::convert(IC,I); - - // Update pose calculation - try{ - // kill the point list - mPose.clearPoint() ; - - // track the dot - for (int i=0 ; i < 4 ; i++) + + // kill the point list + mPose.clearPoint() ; + + // track the dot + for (int i=0 ; i < 4 ; i++) + { + // track the point + md[i].track(I, mcog[i]) ; + md[i].setGrayLevelPrecision(0.90); + // pixel->meter conversion { - // track the point - md[i].track(I, mcog[i]) ; - md[i].setGrayLevelPrecision(0.90); - // pixel->meter conversion - { - double x=0, y=0; - vpPixelMeterConversion::convertPoint(mcam, mcog[i], x, y) ; - mP[i].set_x(x) ; - mP[i].set_y(y) ; - } - - // and added to the pose computation point list - mPose.addPoint(mP[i]) ; + double x=0, y=0; + vpPixelMeterConversion::convertPoint(mcam, mcog[i], x, y) ; + mP[i].set_x(x) ; + mP[i].set_y(y) ; } - // the pose structure has been updated - // the pose is now updated using the virtual visual servoing approach - // Dementhon or lagrange is no longuer necessary, pose at the - // previous iteration is sufficient - mPose.computePose(vpPose::VIRTUAL_VS, cmo); - } - catch(...){ - vpERROR_TRACE("Error in tracking loop") ; - return false; + // and added to the pose computation point list + mPose.addPoint(mP[i]) ; } + // the pose structure has been updated + + // the pose is now updated using the virtual visual servoing approach + // Dementhon or lagrange is no longuer necessary, pose at the + // previous iteration is sufficient + mPose.computePose(vpPose::VIRTUAL_VS, cMo); // Display with ogre - ogre.display(IC,cmo); + ogre.display(IC,cMo); // Wait so that the video does not go too fast double t1 = vpTime::measureTimeMs(); @@ -703,25 +695,22 @@ int main(int argc, const char **argv) } // Close the grabber grabber.close(); + + return 0; } - catch (Ogre::Exception& e) - { - std::cerr << "Exception:\n"; - std::cerr << e.getFullDescription().c_str() << "\n"; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; return 1; } - catch (...) - { - std::cerr << "Exception: " << "\n"; + catch(...) { + std::cout << "Catch an exception " << std::endl; return 1; } - - return EXIT_SUCCESS; } #else // VISP_HAVE_OGRE && VISP_HAVE_DISPLAY int main() { - std::cout << "You should install Ogre3D to run this example..." << std::endl; + std::cout << "You should install Ogre3D or a display (GTK or OpenCV...) to run this example..." << std::endl; } #endif diff --git a/example/ogre-simulator/AROgreBasic.cpp b/example/ogre-simulator/AROgreBasic.cpp index d006bcd31996023cdba890ec91fa71f0b846f2af..3236ecfb1216bdaf9a71d8b7125aeadc816de68b 100644 --- a/example/ogre-simulator/AROgreBasic.cpp +++ b/example/ogre-simulator/AROgreBasic.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: AROgreBasic.cpp 4305 2013-07-05 13:23:47Z fspindle $ + * $Id: AROgreBasic.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,10 +49,12 @@ #include <visp/vpConfig.h> #include <iostream> -//#if defined(VISP_HAVE_OGRE) && defined(VISP_HAVE_DISPLAY) -#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK)) -#if defined(VISP_HAVE_X11) && ! defined(APPLE) +//#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK) || (defined(VISP_HAVE_X11) && ! defined(APPLE))) +#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_D3D9) || defined(VISP_HAVE_GTK) || (defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)))) + +//#if defined(VISP_HAVE_X11) && ! defined(APPLE) +#if defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)) // produce an error on OSX: ‘typedef int Cursor’ // /usr/X11R6/include/X11/X.h:108: error: ‘Cursor’ has a previous // declaration as ‘typedef XID Cursor’. That's why it should not be @@ -179,7 +181,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, */ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, vpPose * mPose, vpDot2 *md, vpImagePoint *mcog, - vpHomogeneousMatrix *cmo, vpPoint *mP, + vpHomogeneousMatrix *cMo, vpPoint *mP, const bool &opt_click_allowed) { // --------------------------------------------------- @@ -187,7 +189,8 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, // ---------------------------------------------------- bool opt_display = true; -#if defined(VISP_HAVE_X11) && ! defined(APPLE) +//#if defined(VISP_HAVE_X11) && ! defined(APPLE) +#if defined(VISP_HAVE_X11) && ! (defined(__APPLE__) && defined(__MACH__)) // produce an error on OSX: ‘typedef int Cursor’ // /usr/X11R6/include/X11/X.h:108: error: ‘Cursor’ has a previous // declaration as ‘typedef XID Cursor’. That's why it should not be @@ -364,16 +367,16 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, // minimisation method // Pose by Lagrange it provides an initialization of the pose - mPose->computePose(vpPose::LAGRANGE, *cmo) ; + mPose->computePose(vpPose::LAGRANGE, *cMo) ; // the pose is now refined using the virtual visual servoing approach // Warning: cMo needs to be initialized otherwise it may diverge - mPose->computePose(vpPose::VIRTUAL_VS, *cmo) ; + mPose->computePose(vpPose::VIRTUAL_VS, *cMo) ; // Display breifly just to have a glimpse a the ViSP pose // while(cpt<500){ if( opt_display ){ // Display the computed pose - mPose->display(I,*cmo,*mcam, 0.05, vpColor::red) ; + mPose->display(I,*cMo,*mcam, 0.05, vpColor::red) ; vpDisplay::flush(I) ; vpTime::wait(1000); } @@ -382,222 +385,214 @@ void computeInitialPose(vpCameraParameters *mcam, vpImage<unsigned char> &I, int main(int argc, const char **argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; + + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - // vpImage<unsigned char> I ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + // vpImage<unsigned char> I ; - // unsigned iter = 0; - std::ostringstream s; - // char cfilename[FILENAME_MAX]; + // unsigned iter = 0; + std::ostringstream s; + // char cfilename[FILENAME_MAX]; - if (opt_ppath.empty()){ - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + if (opt_ppath.empty()){ + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - // Build the name of the image file + // Build the name of the image file - s.setf(std::ios::right, std::ios::adjustfield); - s << "image.%04d.pgm"; - filename = dirname + s.str(); - } - else { - filename = opt_ppath; - } - - //We will read a sequence of images - vpVideoReader grabber; - grabber.setFirstFrameIndex(1); - grabber.setFileName(filename.c_str()); - // Grey level image associated to a display in the initial pose computation - vpImage<unsigned char> Idisplay; - // Grey level image to track points - vpImage<unsigned char> I; - // RGBa image to get background - vpImage<vpRGBa> IC; - // Matrix representing camera parameters - vpHomogeneousMatrix cmo; - - // Variables used for pose computation purposes - vpPose mPose; - vpDot2 md[4]; - vpImagePoint mcog[4]; - vpPoint mP[4]; - - // CameraParameters we got from calibration - // Keep u0 and v0 as center of the screen - vpCameraParameters mcam; - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - grabber.open(Idisplay); - grabber.acquire(Idisplay); - vpCameraParameters mcamTmp(592,570,grabber.getWidth()/2,grabber.getHeight()/2); - // Compute the initial pose of the camera - computeInitialPose(&mcamTmp, Idisplay, &mPose, md, mcog, &cmo, mP, - opt_click_allowed); - // Close the framegrabber - grabber.close(); + s.setf(std::ios::right, std::ios::adjustfield); + s << "image.%04d.pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { + filename = opt_ppath; + } - // Associate the grabber to the RGBa image - grabber.open(IC); - mcam.init(mcamTmp); - } - catch(...) - { - // an exception is thrown if an exception from readPGM has been caught - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + //We will read a sequence of images + vpVideoReader grabber; + grabber.setFirstFrameIndex(1); + grabber.setFileName(filename.c_str()); + // Grey level image associated to a display in the initial pose computation + vpImage<unsigned char> Idisplay; + // Grey level image to track points + vpImage<unsigned char> I; + // RGBa image to get background + vpImage<vpRGBa> IC; + // Matrix representing camera parameters + vpHomogeneousMatrix cMo; + + // Variables used for pose computation purposes + vpPose mPose; + vpDot2 md[4]; + vpImagePoint mcog[4]; + vpPoint mP[4]; + + // CameraParameters we got from calibration + // Keep u0 and v0 as center of the screen + vpCameraParameters mcam; + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; + grabber.open(Idisplay); + grabber.acquire(Idisplay); + vpCameraParameters mcamTmp(592,570,grabber.getWidth()/2,grabber.getHeight()/2); + // Compute the initial pose of the camera + computeInitialPose(&mcamTmp, Idisplay, &mPose, md, mcog, &cMo, mP, + opt_click_allowed); + // Close the framegrabber + grabber.close(); + + // Associate the grabber to the RGBa image + grabber.open(IC); + mcam.init(mcamTmp); + } + catch(...) + { + // an exception is thrown if an exception from readPGM has been caught + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // Create a vpRAOgre object with color background - vpAROgre ogre(mcam, (unsigned int)grabber.getWidth(), (unsigned int)grabber.getHeight()); - // Initialize it - ogre.init(IC); - ogre.load("Robot", "robot.mesh"); - ogre.setScale("Robot", 0.001f,0.001f,0.001f); - ogre.setRotation("Robot", vpRotationMatrix(vpRxyzVector(M_PI/2, -M_PI/2, 0))); + // Create a vpRAOgre object with color background + vpAROgre ogre(mcam, grabber.getWidth(), grabber.getHeight()); + // Initialize it + ogre.init(IC); + ogre.load("Robot", "robot.mesh"); + ogre.setScale("Robot", 0.001f,0.001f,0.001f); + ogre.setRotation("Robot", vpRotationMatrix(vpRxyzVector(M_PI/2, -M_PI/2, 0))); + + // Add an optional point light source + Ogre::Light * light = ogre.getSceneManager()->createLight(); + light->setDiffuseColour(1, 1, 1); // scaled RGB values + light->setSpecularColour(1, 1, 1); // scaled RGB values + light->setPosition(-5, -5, 10); + light->setType(Ogre::Light::LT_POINT); - try - { // Rendering loop - while(ogre.continueRendering() && !grabber.end()){ + while(ogre.continueRendering() && !grabber.end()) { // Acquire a frame grabber.acquire(IC); // Convert it to a grey level image for tracking purpose vpImageConvert::convert(IC,I); - - // Update pose calculation - try{ - // kill the point list - mPose.clearPoint() ; - - // track the dot - for (int i=0 ; i < 4 ; i++) + + // kill the point list + mPose.clearPoint() ; + + // track the dot + for (int i=0 ; i < 4 ; i++) { + // track the point + md[i].track(I, mcog[i]) ; + md[i].setGrayLevelPrecision(0.90); + // pixel->meter conversion { - // track the point - md[i].track(I, mcog[i]) ; - md[i].setGrayLevelPrecision(0.90); - // pixel->meter conversion - { - double x=0, y=0; - vpPixelMeterConversion::convertPoint(mcam, mcog[i], x, y) ; - mP[i].set_x(x) ; - mP[i].set_y(y) ; - } - - // and added to the pose computation point list - mPose.addPoint(mP[i]) ; + double x=0, y=0; + vpPixelMeterConversion::convertPoint(mcam, mcog[i], x, y) ; + mP[i].set_x(x) ; + mP[i].set_y(y) ; } - // the pose structure has been updated - // the pose is now updated using the virtual visual servoing approach - // Dementhon or lagrange is no longuer necessary, pose at the - // previous iteration is sufficient - mPose.computePose(vpPose::VIRTUAL_VS, cmo); - } - catch(...){ - vpERROR_TRACE("Error in tracking loop") ; - return false; + // and added to the pose computation point list + mPose.addPoint(mP[i]) ; } + // the pose structure has been updated + + // the pose is now updated using the virtual visual servoing approach + // Dementhon or lagrange is no longuer necessary, pose at the + // previous iteration is sufficient + mPose.computePose(vpPose::VIRTUAL_VS, cMo); // Display with ogre - ogre.display(IC,cmo); + ogre.display(IC,cMo); // Wait so that the video does not go too fast vpTime::wait(15); } // Close the grabber grabber.close(); + return 0; } - catch (Ogre::Exception& e) - { - std::cerr << "Exception:\n"; - std::cerr << e.getFullDescription().c_str() << "\n"; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; return 1; } - catch (...) - { - std::cerr << "Exception: " << "\n"; + catch(...) { + std::cout << "Catch an exception " << std::endl; return 1; } - - return EXIT_SUCCESS; } #else // VISP_HAVE_OGRE && VISP_HAVE_DISPLAY int main() { - std::cout << "You should install Ogre3D to run this example..." << std::endl; + std::cout << "You should install Ogre3D or a display (GTK or OpenCV...) to run this example..." << std::endl; } #endif diff --git a/example/ogre-simulator/CMakeLists.txt b/example/ogre-simulator/CMakeLists.txt index 518c2653af406a588183b8c30f1e1e4cdf85e35c..9b19f1707213ded1d3e16b8a939ec71ee9cc7926 100644 --- a/example/ogre-simulator/CMakeLists.txt +++ b/example/ogre-simulator/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE AROgre.cpp AROgreBasic.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - #ADD_TEST(${binary} ${binary} ${OPTION_TO_DESACTIVE_DISPLAY}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/parse-argv/CMakeLists.txt b/example/parse-argv/CMakeLists.txt index 2dfe6617174721d6c11fd656aa5e97acdac73c7a..ba94d4e49697a6064e33e9e2e8b4cf24eb36147b 100644 --- a/example/parse-argv/CMakeLists.txt +++ b/example/parse-argv/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,25 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE parse-argv1.cpp parse-argv2.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/parse-argv/parse-argv1.cpp b/example/parse-argv/parse-argv1.cpp index 7953412abda64cfcf4d0a6f638db87620307bf33..2634975134135e27a624e9d1c1934d3c1bdb66a9 100644 --- a/example/parse-argv/parse-argv1.cpp +++ b/example/parse-argv/parse-argv1.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: parse-argv1.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: parse-argv1.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,6 +63,9 @@ // List of allowed command line options #define GETOPTARGS "d:f:i:h" +void usage(const char *name, const char *badparam, int i_val, float f_val, double d_val); +bool getOptions(int argc, const char **argv, int &i_val, float &f_val, double &d_val); + /*! Print the program options. @@ -118,18 +121,18 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, int &i_val, float &f_val, double &d_val) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'd': d_val = atof(optarg); break; - case 'f': f_val = (float) atof(optarg); break; - case 'i': i_val = atoi(optarg); break; + case 'd': d_val = atof(optarg_); break; + case 'f': f_val = (float) atof(optarg_); break; + case 'i': i_val = atoi(optarg_); break; case 'h': usage(argv[0], NULL, i_val, f_val, d_val); return false; break; default: - usage(argv[0], optarg, i_val, f_val, d_val); return false; break; + usage(argv[0], optarg_, i_val, f_val, d_val); return false; break; } } @@ -137,7 +140,7 @@ bool getOptions(int argc, const char **argv, int &i_val, float &f_val, double &d // standalone param or error usage(argv[0], NULL, i_val, f_val, d_val); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -147,32 +150,30 @@ bool getOptions(int argc, const char **argv, int &i_val, float &f_val, double &d int main(int argc, const char ** argv) { - using ::std::cout; - using ::std::endl; + try { + using ::std::cout; + using ::std::endl; - int i_val = 3; - float f_val = 3.14f; - double d_val = 3.1415; + int i_val = 3; + float f_val = 3.14f; + double d_val = 3.1415; - // Read the command line options - if (getOptions(argc, argv, i_val, f_val, d_val) == false) { - return (-1); - } + // Read the command line options + if (getOptions(argc, argv, i_val, f_val, d_val) == false) { + return (-1); + } - cout << "Your parameters: " << endl; - cout << " Integer value: " << i_val << endl; - cout << " Float value: " << f_val << endl; - cout << " Double value: " << d_val << endl << endl; - cout << "Call " << argv[0] - << " -h to see how to change these parameters." << endl; + cout << "Your parameters: " << endl; + cout << " Integer value: " << i_val << endl; + cout << " Float value: " << f_val << endl; + cout << " Double value: " << d_val << endl << endl; + cout << "Call " << argv[0] + << " -h to see how to change these parameters." << endl; - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } - - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/example/parse-argv/parse-argv2.cpp b/example/parse-argv/parse-argv2.cpp index 9d25532042dbfbfd6385e2b852d5128606876075..420174d8171a9badfdc50b6dcc7417c2174089ae 100644 --- a/example/parse-argv/parse-argv2.cpp +++ b/example/parse-argv/parse-argv2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: parse-argv2.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: parse-argv2.cpp 5311 2015-02-11 17:42:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,37 +63,48 @@ int main(int argc, const char ** argv) { - using ::std::cout; - using ::std::endl; - - int i_val = 3; - float f_val = 3.14f; - double d_val = 3.1415; - - vpParseArgv::vpArgvInfo argTable[] = - { - {"-integer", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &i_val, - "An integer value."}, - {"-float", vpParseArgv::ARGV_FLOAT, (char*) NULL, (char *) &f_val, - "A float value."}, - {"-double", vpParseArgv::ARGV_DOUBLE, (char*) NULL, (char *) &d_val, - "A double value."}, - {(char*) NULL, vpParseArgv::ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL} - } ; - - // Read the command line options - if(vpParseArgv::parse(&argc, argv, argTable, 0)) { - return (-1); + try { + using ::std::cout; + using ::std::endl; + + int i_val = 3; + float f_val = 3.14f; + double d_val = 3.1415; + int flag = 0; + + vpParseArgv::vpArgvInfo argTable[] = + { + {"-flag", vpParseArgv::ARGV_CONSTANT, 0, (char *) &flag, + "Flag enabled."}, + {"-integer", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &i_val, + "An integer value."}, + {"-float", vpParseArgv::ARGV_FLOAT, (char*) NULL, (char *) &f_val, + "A float value."}, + {"-double", vpParseArgv::ARGV_DOUBLE, (char*) NULL, (char *) &d_val, + "A double value."}, + {(char*) NULL, vpParseArgv::ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL} + } ; + + // Read the command line options + if(vpParseArgv::parse(&argc, argv, argTable, 0)) { + return (-1); + } + + cout << "Your parameters: " << endl; + cout << " Integer value: " << i_val << endl; + cout << " Float value: " << f_val << endl; + cout << " Double value: " << d_val << endl << endl; + cout << " Flag : " << flag << endl << endl; + + cout << "Call " << argv[0] + << " -h to see how to change these parameters." << endl; + + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - cout << "Your parameters: " << endl; - cout << " Integer value: " << i_val << endl; - cout << " Float value: " << f_val << endl; - cout << " Double value: " << d_val << endl << endl; - cout << "Call " << argv[0] - << " -h to see how to change these parameters." << endl; - - return 0; } diff --git a/example/pose-estimation/CMakeLists.txt b/example/pose-estimation/CMakeLists.txt index d8ac10488114022d1c10651229a8a97fb427e2cc..c6700eac3d161e43603389540e0ad59b735ec2aa 100644 --- a/example/pose-estimation/CMakeLists.txt +++ b/example/pose-estimation/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -48,15 +48,19 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH @@ -64,9 +68,4 @@ ENDFOREACH(source) # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(poseVirtualVS poseVirtualVS -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(poseVirtualVS poseVirtualVS -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/pose-estimation/poseVirtualVS.cpp b/example/pose-estimation/poseVirtualVS.cpp index 5226561e1bb76ae4705262a339b13c995ca0673b..3bf6d19e66828e4c61e6f82afe2a3965f4f46b48 100644 --- a/example/pose-estimation/poseVirtualVS.cpp +++ b/example/pose-estimation/poseVirtualVS.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: poseVirtualVS.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: poseVirtualVS.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,7 +69,7 @@ #include <stdio.h> #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -78,6 +78,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpPose.h> #include <visp/vpDot.h> @@ -89,6 +90,12 @@ // List of allowed command line options #define GETOPTARGS "cdi:p:hf:n:s:" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, + unsigned &first, unsigned &nimages, unsigned &step, + bool &click_allowed, bool &display); + /*! Print the program options. @@ -179,22 +186,22 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp unsigned &first, unsigned &nimages, unsigned &step, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step); return false; break; } } @@ -203,7 +210,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -216,177 +223,174 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 0; - unsigned opt_nimages = 80; - unsigned opt_step = 1; - bool opt_click_allowed = true; - bool opt_display = true; - - - int i ; - - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " poseVirtualVS.cpp" <<std::endl << std::endl ; - - std::cout << " Example of dots tracking in an image sequence and pose computation" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_nimages, opt_step, opt_click_allowed, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (opt_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 0; + unsigned opt_nimages = 80; + unsigned opt_step = 1; + bool opt_click_allowed = true; + bool opt_display = true; + + int i ; + + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " poseVirtualVS.cpp" <<std::endl << std::endl ; + + std::cout << " Example of dots tracking in an image sequence and pose computation" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_nimages, opt_step, opt_click_allowed, opt_display) == false) { + exit (-1); } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<< std::endl - << " use personal images" <<std::endl << std::endl; - exit(-1); - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (opt_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<< std::endl + << " use personal images" <<std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; - unsigned iter = opt_first ; - std::ostringstream s; - char cfilename[FILENAME_MAX]; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - if (opt_ppath.empty()){ + unsigned iter = opt_first ; + std::ostringstream s; + char cfilename[FILENAME_MAX]; - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file /ViSP-images/cube/image.0001.pgm - // !! poseExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images-x.y.z.tar.gz" + if (opt_ppath.empty()){ - // directory name - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file /ViSP-images/cube/image.0001.pgm + // !! poseExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images-x.y.z.tar.gz" + // directory name + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); - // Build the name of the image file + // Build the name of the image file - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { - sprintf(cfilename, opt_ppath.c_str(), iter) ; - filename = cfilename; - } + sprintf(cfilename, opt_ppath.c_str(), iter) ; + filename = cfilename; + } - // define the vpDot structure, here 4 dots will tracked - vpDot d[4] ; + // define the vpDot structure, here 4 dots will tracked + vpDot d[4] ; - for (i=0 ; i < 4 ; i++) - { - // by using setGraphics, we request to see the all the pixel of the dot - // in green on the screen. - // It uses the overlay image plane. - // The default of this setting is that it is time consumming + for (i=0 ; i < 4 ; i++) + { + // by using setGraphics, we request to see the all the pixel of the dot + // in green on the screen. + // It uses the overlay image plane. + // The default of this setting is that it is time consumming - if (opt_display) { - d[i].setGraphics(true) ; - } - else { - d[i].setGraphics(false) ; + if (opt_display) { + d[i].setGraphics(true) ; + } + else { + d[i].setGraphics(false) ; + } } - } - // Read the PGM image named "s" on the disk, and put the bitmap into the - // image structure I. - // I is initialized to the correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpImageIo::read(I,filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - if (opt_ppath.empty()) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option, " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable" - << std::endl; - } - else { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " or your -p " << opt_ppath << " option " <<std::endl - << std::endl; + // Read the PGM image named "s" on the disk, and put the bitmap into the + // image structure I. + // I is initialized to the correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpImageIo::read(I,filename) ; } - exit(-1); + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + if (opt_ppath.empty()) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option, " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable" + << std::endl; + } + else { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " or your -p " << opt_ppath << " option " <<std::endl + << std::endl; + } + exit(-1); - } + } - // We open a window using either the X11 or GTK or GDI window manager - // it will be located in 100,100 and titled "tracking using vpDot" - // its size is automatically defined by the image (I) size + // We open a window using either the X11 or GTK or GDI window manager + // it will be located in 100,100 and titled "tracking using vpDot" + // its size is automatically defined by the image (I) size #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I,100,100,"tracking using vpDot"); // display the image @@ -397,20 +401,12 @@ main(int argc, const char** argv) vpDisplay::display(I) ; //Flush the display vpDisplay::flush(I) ; - - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - return(-1) ; } - } - vpImagePoint cog[4]; // Center of gravity of the dot - try{ + vpImagePoint cog[4]; // Center of gravity of the dot if (opt_display && opt_click_allowed) { // dot coordinates (u,v) = (column,row) - std::cout << "Click the four white dots on the object corner clockwise" + std::cout << "Click the four white dots on the object corner clockwise" << std::endl ; for (i=0 ; i < 4 ; i++) { @@ -460,122 +456,115 @@ main(int argc, const char** argv) d[3].track(I, cog[3]); vpDisplay::flush(I) ; } - } - catch(...) - { - vpERROR_TRACE("Error in tracking initialization ") ; - return(-1) ; - } - if (opt_display) - { + if (opt_display) + { + + // display a red cross (size 10) in the image at the dot center + // of gravity location + // + // WARNING + // in the vpDisplay class member's when pixel coordinates + // are considered the first element is the row index and the second + // is the column index: + // vpDisplay::displayCross(Image, row index, column index, size, color) + // therefore u and v are inverted wrt to the vpDot specification + // Alternatively, to avoid this problem another set of member have + // been defined in the vpDisplay class. + // If the method name is postfixe with _uv the specification is : + // vpDisplay::displayCross_uv(Image, column index, row index, size, color) - // display a red cross (size 10) in the image at the dot center - // of gravity location + for (i=0 ; i < 4 ; i++) + vpDisplay::displayCross(I, cog[i], 10, vpColor::red) ; + + // flush the X11 buffer + vpDisplay::flush(I) ; + } + + // -------------------------------------------------------- + // Now wil compute the pose // - // WARNING - // in the vpDisplay class member's when pixel coordinates - // are considered the first element is the row index and the second - // is the column index: - // vpDisplay::displayCross(Image, row index, column index, size, color) - // therefore u and v are inverted wrt to the vpDot specification - // Alternatively, to avoid this problem another set of member have - // been defined in the vpDisplay class. - // If the method name is postfixe with _uv the specification is : - // vpDisplay::displayCross_uv(Image, column index, row index, size, color) + // The pose will be contained in an homogeneous matrix cMo + vpHomogeneousMatrix cMo ; + + + // We need a structure that content both the 3D coordinates of the point + // in the object frame and the 2D coordinates of the point expressed in meter + // the vpPoint class is ok for that + vpPoint P[4] ; + + // The vpPose class mainly contents a list of vpPoint (that is (X,Y,Z, x, y) ) + vpPose pose ; + // the list of point is cleared (if that's not done before) + pose.clearPoint() ; + + // we set the 3D points coordinates (in meter !) in the object/world frame + double L=0.04 ; + P[0].setWorldCoordinates(-L,-L, 0 ) ; // (X,Y,Z) + P[1].setWorldCoordinates(L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-L,L, 0 ) ; + + + // set the camera intrinsic parameters + // see more details about the model in vpCameraParameters + double px = 600 ; + double py = 600 ; + double u0 = 192 ; + double v0 = 144 ; + vpCameraParameters cam(px,py,u0,v0) ; + + // pixel-> meter conversion for (i=0 ; i < 4 ; i++) - vpDisplay::displayCross(I, cog[i], 10, vpColor::red) ; + { + // u[i]. v[i] are expressed in pixel + // conversion in meter is achieved using + // x = (u-u0)/px + // y = (v-v0)/py + // where px, py, u0, v0 are the intrinsic camera parameters + double x=0, y=0; + vpPixelMeterConversion::convertPoint(cam, cog[i], x,y) ; + P[i].set_x(x) ; + P[i].set_y(y) ; + } - // flush the X11 buffer - vpDisplay::flush(I) ; - } - // -------------------------------------------------------- - // Now wil compute the pose - // - - // The pose will be contained in an homogeneous matrix cMo - vpHomogeneousMatrix cMo ; - - - // We need a structure that content both the 3D coordinates of the point - // in the object frame and the 2D coordinates of the point expressed in meter - // the vpPoint class is ok for that - vpPoint P[4] ; - - // The vpPose class mainly contents a list of vpPoint (that is (X,Y,Z, x, y) ) - vpPose pose ; - // the list of point is cleared (if that's not done before) - pose.clearPoint() ; - - // we set the 3D points coordinates (in meter !) in the object/world frame - double L=0.04 ; - P[0].setWorldCoordinates(-L,-L, 0 ) ; // (X,Y,Z) - P[1].setWorldCoordinates(L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-L,L, 0 ) ; - - - // set the camera intrinsic parameters - // see more details about the model in vpCameraParameters - double px = 600 ; - double py = 600 ; - double u0 = 192 ; - double v0 = 144 ; - vpCameraParameters cam(px,py,u0,v0) ; - - // pixel-> meter conversion - for (i=0 ; i < 4 ; i++) - { - // u[i]. v[i] are expressed in pixel - // conversion in meter is achieved using - // x = (u-u0)/px - // y = (v-v0)/py - // where px, py, u0, v0 are the intrinsic camera parameters - double x=0, y=0; - vpPixelMeterConversion::convertPoint(cam, cog[i], x,y) ; - P[i].set_x(x) ; - P[i].set_y(y) ; - } + // The pose structure is build, we put in the point list the set of point + // here both 2D and 3D world coordinates are known + for (i=0 ; i < 4 ; i++) + { + pose.addPoint(P[i]) ; // and added to the pose computation point list + } + // compute the initial pose using Dementhon method followed by a non linear + // minimisation method + + // Pose by Lagrange it provides an initialization of the pose + pose.computePose(vpPose::LAGRANGE, cMo) ; + // the pose is now refined using the virtual visual servoing approach + // Warning: cMo needs to be initialized otherwise it may diverge + pose.computePose(vpPose::VIRTUAL_VS, cMo) ; + if( opt_display ){ + // display the compute pose + pose.display(I,cMo,cam, 0.05, vpColor::red) ; + vpDisplay::flush(I) ; + } - // The pose structure is build, we put in the point list the set of point - // here both 2D and 3D world coordinates are known - for (i=0 ; i < 4 ; i++) - { - pose.addPoint(P[i]) ; // and added to the pose computation point list - } + // Covariance Matrix Computation + // Uncomment if you want to compute the covariance matrix. + // pose.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. - // compute the initial pose using Dementhon method followed by a non linear - // minimisation method - - // Pose by Lagrange it provides an initialization of the pose - pose.computePose(vpPose::LAGRANGE, cMo) ; - // the pose is now refined using the virtual visual servoing approach - // Warning: cMo needs to be initialized otherwise it may diverge - pose.computePose(vpPose::VIRTUAL_VS, cMo) ; - if( opt_display ){ - // display the compute pose - pose.display(I,cMo,cam, 0.05, vpColor::red) ; - vpDisplay::flush(I) ; - } - - // Covariance Matrix Computation - // Uncomment if you want to compute the covariance matrix. - // pose.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. - - unsigned niter = 0; - // this is the loop over the image sequence - while (iter < opt_nimages) - { - try { + unsigned niter = 0; + // this is the loop over the image sequence + while (iter < opt_nimages) + { // set the new image name if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf( cfilename, opt_ppath.c_str(), iter) ; @@ -627,26 +616,28 @@ main(int argc, const char** argv) vpDisplay::flush(I) ; } - + // Covariance Matrix Display - // Uncomment if you want to print the covariance matrix. + // Uncomment if you want to print the covariance matrix. // Make sure pose.setCovarianceComputation(true) has been called (uncomment below). // std::cout << pose.getCovarianceMatrix() << std::endl << std::endl; - + niter++ ; + + iter += opt_step ; } - catch(...){ - vpERROR_TRACE("Error in tracking loop") ; - return(-1) ; - } - iter += opt_step ; + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11 or GTK functionalities to display images..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV functionalities to display images..."); } #endif diff --git a/example/robot-simulator/afma6/CMakeLists.txt b/example/robot-simulator/afma6/CMakeLists.txt index 01412494b384653871affc530fa8fdfbd47a635b..43b97b4200d480e5a257bc1ff947c20fcb3e0708 100644 --- a/example/robot-simulator/afma6/CMakeLists.txt +++ b/example/robot-simulator/afma6/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 2457 2010-01-07 10:41:18Z nmelchio $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE -servoSimuAfma6FourPoints2DCamVelocity.cpp +set(SOURCE + servoSimuAfma6FourPoints2DCamVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test -ADD_TEST(servoSimuAfma6FourPoints2DCamVelocity servoSimuAfma6FourPoints2DCamVelocity -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(servoSimuAfma6FourPoints2DCamVelocity servoSimuAfma6FourPoints2DCamVelocity -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/robot-simulator/afma6/servoSimuAfma6FourPoints2DCamVelocity.cpp b/example/robot-simulator/afma6/servoSimuAfma6FourPoints2DCamVelocity.cpp index 3845b4d3a5b7d16afb5f81790230055791c241cf..f4544d1829a211bffbbc884eb611d4b615ca1168 100644 --- a/example/robot-simulator/afma6/servoSimuAfma6FourPoints2DCamVelocity.cpp +++ b/example/robot-simulator/afma6/servoSimuAfma6FourPoints2DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuFourPoints2DPolarCamVelocityDisplay.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined(WIN32) || defined(VISP_HAVE_PTHREAD)) && (defined (VISP_HAVE_X11) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI)) +#if (defined(_WIN32) || defined(VISP_HAVE_PTHREAD)) && (defined (VISP_HAVE_X11) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI)) // We need to use threading capabilities. Thus on Unix-like // platforms, the libpthread third-party library need to be @@ -88,6 +88,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -137,9 +140,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -147,7 +150,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -156,7 +159,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -166,192 +169,192 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_click_allowed = true; - bool opt_display = true; + try { + bool opt_click_allowed = true; + bool opt_display = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // We open two displays, one for the internal camera view, the other one for - // the external view, using either X11, GTK or GDI. + // We open two displays, one for the internal camera view, the other one for + // the external view, using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX displayInt; + vpDisplayX displayInt; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt; + vpDisplayGDI displayInt; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV displayInt; + vpDisplayOpenCV displayInt; #endif - vpImage<unsigned char> Iint(480, 640, 255); + vpImage<unsigned char> Iint(480, 640, 255); - if (opt_display) { - // open a display for the visualization - displayInt.init(Iint,700,0, "Internal view") ; - } + if (opt_display) { + // open a display for the visualization + displayInt.init(Iint,700,0, "Internal view") ; + } - int i; - vpServo task; - - std::cout << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" - << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.05,-0.05,0.7, - vpMath::rad(10), vpMath::rad(10), vpMath::rad(-30)); - - // sets the point coordinates in the object frame - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.045,-0.045,0) ; - point[3].setWorldCoordinates(-0.045,0.045,0) ; - point[2].setWorldCoordinates(0.045,0.045,0) ; - point[1].setWorldCoordinates(0.045,-0.045,0) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; - - // sets the desired position of the point - vpFeaturePoint p[4] ; - for (i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the feature point s* - vpFeaturePoint pd[4] ; - - // Desired pose - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); - - // Projection of the points - for (int i = 0 ; i < 4 ; i++) - point[i].track(cdMo); - - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(pd[i], point[i]); - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::DESIRED) ; - - // we want to see a point on a point - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - - // set the gain - task.setLambda(0.8) ; - - // Declaration of the robot - vpSimulatorAfma6 robot(opt_display); - - // Initialise the robot and especially the camera - robot.init(vpAfma6::TOOL_CCMOP, vpCameraParameters::perspectiveProjWithoutDistortion); - robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); - - // Initialise the object for the display part*/ - robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - - // Initialise the position of the object relative to the pose of the robot's camera - robot.initialiseObjectRelativeToCamera(cMo); - - // Set the desired position (for the displaypart) - robot.setDesiredCameraPosition(cdMo); - - // Get the internal robot's camera parameters - vpCameraParameters cam; - robot.getCameraParameters(cam,Iint); - - if (opt_display) - { - //Get the internal view - vpDisplay::display(Iint); - robot.getInternalView(Iint); - vpDisplay::flush(Iint); - } + vpServo task; + + std::cout << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" + << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.05,-0.05,0.7, + vpMath::rad(10), vpMath::rad(10), vpMath::rad(-30)); + + // sets the point coordinates in the object frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.045,-0.045,0) ; + point[3].setWorldCoordinates(-0.045,0.045,0) ; + point[2].setWorldCoordinates(0.045,0.045,0) ; + point[1].setWorldCoordinates(0.045,-0.045,0) ; + + // computes the point coordinates in the camera frame and its 2D coordinates + for (unsigned int i = 0 ; i < 4 ; i++) + point[i].track(cMo) ; - // Display task information - task.print() ; + // sets the desired position of the point + vpFeaturePoint p[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure - unsigned int iter=0 ; - vpTRACE("\t loop") ; - while(iter++<500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + // sets the desired position of the feature point s* + vpFeaturePoint pd[4] ; - // Get the Time at the beginning of the loop - double t = vpTime::measureTimeMs(); + // Desired pose + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); - // Get the current pose of the camera - cMo = robot.get_cMo(); + // Projection of the points + for (unsigned int i = 0 ; i < 4 ; i++) + point[i].track(cdMo); - if (iter==1) { - std::cout <<"Initial robot position with respect to the object frame:\n"; - cMo.print(); - } + for (unsigned int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(pd[i], point[i]); - // new point position - for (i = 0 ; i < 4 ; i++) - { - point[i].track(cMo) ; - // retrieve x,y and Z of the vpPoint structure - try { - vpFeatureBuilder::create(p[i],point[i]) ; - } - catch(...) - { - break; - } - } + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::DESIRED) ; + + // we want to see a point on a point + for (unsigned int i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; + + // set the gain + task.setLambda(0.8) ; + + // Declaration of the robot + vpSimulatorAfma6 robot(opt_display); + + // Initialise the robot and especially the camera + robot.init(vpAfma6::TOOL_CCMOP, vpCameraParameters::perspectiveProjWithoutDistortion); + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + + // Initialise the object for the display part*/ + robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); + + // Initialise the position of the object relative to the pose of the robot's camera + robot.initialiseObjectRelativeToCamera(cMo); + + // Set the desired position (for the displaypart) + robot.setDesiredCameraPosition(cdMo); + + // Get the internal robot's camera parameters + vpCameraParameters cam; + robot.getCameraParameters(cam,Iint); if (opt_display) { - // Get the internal view and display it - vpDisplay::display(Iint) ; + //Get the internal view + vpDisplay::display(Iint); robot.getInternalView(Iint); vpDisplay::flush(Iint); } - if (opt_display && opt_click_allowed && iter == 1) + // Display task information + task.print() ; + + unsigned int iter=0 ; + vpTRACE("\t loop") ; + while(iter++<500) { - // suppressed for automate test - std::cout << "Click in the internal view window to continue..." << std::endl; - vpDisplay::getClick(Iint) ; - } + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // compute the control law - v = task.computeControlLaw() ; + // Get the Time at the beginning of the loop + double t = vpTime::measureTimeMs(); - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Get the current pose of the camera + cMo = robot.get_cMo(); - std::cout << "|| s - s* || " << ( task.getError() ).sumSquare() <<std::endl ; + if (iter==1) { + std::cout <<"Initial robot position with respect to the object frame:\n"; + cMo.print(); + } - // The main loop has a duration of 10 ms at minimum - vpTime::wait(t,10); - } + // new point position + for (unsigned int i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + // retrieve x,y and Z of the vpPoint structure + vpFeatureBuilder::create(p[i],point[i]) ; + } + + if (opt_display) + { + // Get the internal view and display it + vpDisplay::display(Iint) ; + robot.getInternalView(Iint); + vpDisplay::flush(Iint); + } + + if (opt_display && opt_click_allowed && iter == 1) + { + // suppressed for automate test + std::cout << "Click in the internal view window to continue..." << std::endl; + vpDisplay::getClick(Iint) ; + } + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || " << ( task.getError() ).sumSquare() <<std::endl ; + + // The main loop has a duration of 10 ms at minimum + vpTime::wait(t,10); + } - // Display task information - task.print() ; - task.kill(); + // Display task information + task.print() ; + task.kill(); - std::cout <<"Final robot position with respect to the object frame:\n"; - cMo.print(); + std::cout <<"Final robot position with respect to the object frame:\n"; + cMo.print(); - if (opt_display && opt_click_allowed) - { - // suppressed for automate test - std::cout << "Click in the internal view window to end..." << std::endl; - vpDisplay::getClick(Iint) ; + if (opt_display && opt_click_allowed) + { + // suppressed for automate test + std::cout << "Click in the internal view window to end..." << std::endl; + vpDisplay::getClick(Iint) ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } #else diff --git a/example/robot-simulator/camera/CMakeLists.txt b/example/robot-simulator/camera/CMakeLists.txt index 04b7afba7ad5ad8f8d23cff460408c86f5b1d068..b05b61b153e60990ed1dca97ea8647ad536960af 100644 --- a/example/robot-simulator/camera/CMakeLists.txt +++ b/example/robot-simulator/camera/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 2457 2010-01-07 10:41:18Z nmelchio $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,66 +43,65 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE -servoSimu3D_cdMc_CamVelocity.cpp -servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp -servoSimu3D_cMcd_CamVelocity.cpp -servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp -servoSimuCircle2DCamVelocity.cpp -servoSimuCircle2DCamVelocityDisplay.cpp -servoSimuCylinder2DCamVelocityDisplay.cpp -servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp -servoSimuFourPoints2DCamVelocity.cpp -servoSimuFourPoints2DCamVelocityDisplay.cpp -servoSimuFourPoints2DPolarCamVelocityDisplay.cpp -servoSimuLine2DCamVelocityDisplay.cpp -servoSimuPoint2DCamVelocity1.cpp -servoSimuPoint2DCamVelocity2.cpp -servoSimuPoint2DCamVelocity3.cpp -servoSimuPoint2DhalfCamVelocity1.cpp -servoSimuPoint2DhalfCamVelocity2.cpp -servoSimuPoint2DhalfCamVelocity3.cpp -servoSimuPoint3DCamVelocity.cpp -servoSimuSphere2DCamVelocity.cpp -servoSimuSphere2DCamVelocitySecondaryTask.cpp -servoSimuSquareLine2DCamVelocityDisplay.cpp -servoSimuThetaUCamVelocity.cpp +set(SOURCE + servoSimu3D_cdMc_CamVelocity.cpp + servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp + servoSimu3D_cMcd_CamVelocity.cpp + servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp + servoSimuCircle2DCamVelocity.cpp + servoSimuCircle2DCamVelocityDisplay.cpp + servoSimuCylinder2DCamVelocityDisplay.cpp + servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp + servoSimuFourPoints2DCamVelocity.cpp + servoSimuFourPoints2DCamVelocityDisplay.cpp + servoSimuFourPoints2DPolarCamVelocityDisplay.cpp + servoSimuLine2DCamVelocityDisplay.cpp + servoSimuPoint2DCamVelocity1.cpp + servoSimuPoint2DCamVelocity2.cpp + servoSimuPoint2DCamVelocity3.cpp + servoSimuPoint2DhalfCamVelocity1.cpp + servoSimuPoint2DhalfCamVelocity2.cpp + servoSimuPoint2DhalfCamVelocity3.cpp + servoSimuPoint3DCamVelocity.cpp + servoSimuSphere2DCamVelocity.cpp + servoSimuSphere2DCamVelocitySecondaryTask.cpp + servoSimuSquareLine2DCamVelocityDisplay.cpp + servoSimuThetaUCamVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test -ADD_TEST(servoSimu3D_cdMc_CamVelocity servoSimu3D_cdMc_CamVelocity ) -ADD_TEST(servoSimu3D_cdMc_CamVelocityWithoutVpServo servoSimu3D_cdMc_CamVelocityWithoutVpServo ) -ADD_TEST(servoSimu3D_cMcd_CamVelocity servoSimu3D_cMcd_CamVelocity ) -ADD_TEST(servoSimu3D_cMcd_CamVelocityWithoutVpServo servoSimu3D_cMcd_CamVelocityWithoutVpServo ) -ADD_TEST(servoSimuCircle2DCamVelocity servoSimuCircle2DCamVelocity ) -ADD_TEST(servoSimuCircle2DCamVelocityDisplay servoSimuCircle2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) -ADD_TEST(servoSimuCylinder2DCamVelocityDisplay servoSimuCylinder2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) -ADD_TEST(servoSimuFourPoints2DCamVelocity servoSimuFourPoints2DCamVelocity ) -ADD_TEST(servoSimuFourPoints2DCamVelocityDisplay servoSimuFourPoints2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) -ADD_TEST(servoSimuLine2DCamVelocityDisplay servoSimuLine2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) -ADD_TEST(servoSimuPoint2DCamVelocity1 servoSimuPoint2DCamVelocity1 ) -ADD_TEST(servoSimuPoint2DCamVelocity2 servoSimuPoint2DCamVelocity2 ) -ADD_TEST(servoSimuPoint2DCamVelocity3 servoSimuPoint2DCamVelocity3 ) -ADD_TEST(servoSimuPoint2DhalfCamVelocity1 servoSimuPoint2DhalfCamVelocity1 ) -ADD_TEST(servoSimuPoint2DhalfCamVelocity2 servoSimuPoint2DhalfCamVelocity2 ) -ADD_TEST(servoSimuPoint2DhalfCamVelocity3 servoSimuPoint2DhalfCamVelocity3 ) -ADD_TEST(servoSimuPoint3DCamVelocity servoSimuPoint3DCamVelocity ) -ADD_TEST(servoSimuSphere2DCamVelocity servoSimuSphere2DCamVelocity ) -ADD_TEST(servoSimuSphere2DCamVelocitySecondaryTask servoSimuSphere2DCamVelocitySecondaryTask ) -ADD_TEST(servoSimuThetaUCamVelocity servoSimuThetaUCamVelocity ) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(servoSimu3D_cdMc_CamVelocity servoSimu3D_cdMc_CamVelocity ) +add_test(servoSimu3D_cdMc_CamVelocityWithoutVpServo servoSimu3D_cdMc_CamVelocityWithoutVpServo ) +add_test(servoSimu3D_cMcd_CamVelocity servoSimu3D_cMcd_CamVelocity ) +add_test(servoSimu3D_cMcd_CamVelocityWithoutVpServo servoSimu3D_cMcd_CamVelocityWithoutVpServo ) +add_test(servoSimuCircle2DCamVelocity servoSimuCircle2DCamVelocity ) +add_test(servoSimuCircle2DCamVelocityDisplay servoSimuCircle2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) +add_test(servoSimuCylinder2DCamVelocityDisplay servoSimuCylinder2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) +add_test(servoSimuFourPoints2DCamVelocity servoSimuFourPoints2DCamVelocity ) +add_test(servoSimuFourPoints2DCamVelocityDisplay servoSimuFourPoints2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) +add_test(servoSimuLine2DCamVelocityDisplay servoSimuLine2DCamVelocityDisplay -c ${OPTION_TO_DESACTIVE_DISPLAY} ) +add_test(servoSimuPoint2DCamVelocity1 servoSimuPoint2DCamVelocity1 ) +add_test(servoSimuPoint2DCamVelocity2 servoSimuPoint2DCamVelocity2 ) +add_test(servoSimuPoint2DCamVelocity3 servoSimuPoint2DCamVelocity3 ) +add_test(servoSimuPoint2DhalfCamVelocity1 servoSimuPoint2DhalfCamVelocity1 ) +add_test(servoSimuPoint2DhalfCamVelocity2 servoSimuPoint2DhalfCamVelocity2 ) +add_test(servoSimuPoint2DhalfCamVelocity3 servoSimuPoint2DhalfCamVelocity3 ) +add_test(servoSimuPoint3DCamVelocity servoSimuPoint3DCamVelocity ) +add_test(servoSimuSphere2DCamVelocity servoSimuSphere2DCamVelocity ) +add_test(servoSimuSphere2DCamVelocitySecondaryTask servoSimuSphere2DCamVelocitySecondaryTask ) +add_test(servoSimuThetaUCamVelocity servoSimuThetaUCamVelocity ) diff --git a/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocity.cpp b/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocity.cpp index 5057531a700647831f479697901edbbae20c8f47..2ee501accc927738e1ca50c2ccecf5d3f9be6ba0 100644 --- a/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimu3D_cMcd_CamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -113,15 +116,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -130,7 +133,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -140,155 +143,162 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed camera velocities (m/s, rad/s) to achieve the task - // - the 6 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; -#ifdef WIN32 - logdirname ="C:/temp/" + username; + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed camera velocities (m/s, rad/s) to achieve the task + // - the 6 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; +#if defined(_WIN32) + logdirname ="C:/temp/" + username; #else - logdirname ="/tmp/" + username; + logdirname ="/tmp/" + username; #endif - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; - - // Open the log file name - std::ofstream flog(logfilename.c_str()); - - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : 3D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // Sets the initial camera location - vpPoseVector c_r_o(// Translation tx,ty,tz - 0.1, 0.2, 2, - // ThetaU rotation - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; - - // From the camera pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // Set the robot initial position - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - // Sets the desired camera location - vpPoseVector cd_r_o(// Translation tx,ty,tz - 0, 0, 1, - // ThetaU rotation - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - - // From the camera desired pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cdMo(cd_r_o) ; - - // Compute the transformation from the initial camera position to the desired one - vpHomogeneousMatrix cMcd ; - cMcd = cMo*cdMo.inverse() ; - - // Build the 3D translation feature: ctc* - vpFeatureTranslation t(vpFeatureTranslation::cMcd) ; - t.buildFrom(cMcd) ; - - // Build the 3D rotation feature: thetaU_cRc* - vpFeatureThetaU tu(vpFeatureThetaU::cRcd); // current feature - tu.buildFrom(cMcd) ; - - // Sets the desired rotation (always zero !) since s is the - // rotation that the camera has to achieve. Here s* = (0, 0)^T - vpFeatureTranslation td(vpFeatureTranslation::cMcd) ; - vpFeatureThetaU tud(vpFeatureThetaU::cRcd); // desired feature - - // Define the task - // - we want an eye-in-hand control law - // - the robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - // - we use here the interaction matrix computed with the current - // features - task.setInteractionMatrixType(vpServo::CURRENT); - - // Add the current and desired visual features - task.addFeature(t,td) ; // 3D translation - task.addFeature(tu,tud) ; // 3D rotation theta u - - // - set the constant gain to 1.0 - task.setLambda(1) ; - - // Display task information - task.print() ; + std::string logfilename; + logfilename = logdirname + "/log.dat"; + + // Open the log file name + std::ofstream flog(logfilename.c_str()); + + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : 3D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Sets the initial camera location + vpPoseVector c_r_o(// Translation tx,ty,tz + 0.1, 0.2, 2, + // ThetaU rotation + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; + + // From the camera pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // Set the robot initial position + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; // Compute the position of the object in the world frame - unsigned int iter=0 ; - // Start the visual servoing loop. We stop the servo after 200 iterations - while(iter++ < 200) { - std::cout << "------------------------------------" << iter <<std::endl ; - vpColVector v ; + // Sets the desired camera location + vpPoseVector cd_r_o(// Translation tx,ty,tz + 0, 0, 1, + // ThetaU rotation + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // From the camera desired pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cdMo(cd_r_o) ; - // new displacement to achieve + // Compute the transformation from the initial camera position to the desired one + vpHomogeneousMatrix cMcd ; cMcd = cMo*cdMo.inverse() ; - // Update the current visual features + // Build the 3D translation feature: ctc* + vpFeatureTranslation t(vpFeatureTranslation::cMcd) ; t.buildFrom(cMcd) ; + + // Build the 3D rotation feature: thetaU_cRc* + vpFeatureThetaU tu(vpFeatureThetaU::cRcd); // current feature tu.buildFrom(cMcd) ; - // Compute the control law - v = task.computeControlLaw() ; + // Sets the desired rotation (always zero !) since s is the + // rotation that the camera has to achieve. Here s* = (0, 0)^T + vpFeatureTranslation td(vpFeatureTranslation::cMcd) ; + vpFeatureThetaU tud(vpFeatureThetaU::cRcd); // desired feature + + // Define the task + // - we want an eye-in-hand control law + // - the robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + // - we use here the interaction matrix computed with the current + // features + task.setInteractionMatrixType(vpServo::CURRENT); + + // Add the current and desired visual features + task.addFeature(t,td) ; // 3D translation + task.addFeature(tu,tud) ; // 3D rotation theta u + + // - set the constant gain to 1.0 + task.setLambda(1) ; // Display task information - if (iter==1) task.print() ; + task.print() ; - // Send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + unsigned int iter=0 ; + // Start the visual servoing loop. We stop the servo after 200 iterations + while(iter++ < 200) { + std::cout << "------------------------------------" << iter <<std::endl ; + vpColVector v ; - // Retrieve the error - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - // Save log - flog << v.t() << " " << ( task.getError() ).t() << std::endl; - } - // Display task information - task.print() ; + // new displacement to achieve + cMcd = cMo*cdMo.inverse() ; + + // Update the current visual features + t.buildFrom(cMcd) ; + tu.buildFrom(cMcd) ; + + // Compute the control law + v = task.computeControlLaw() ; + + // Display task information + if (iter==1) task.print() ; + + // Send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - // Kill the task - task.kill(); + // Retrieve the error + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - // Close the log file - flog.close(); + // Save log + flog << v.t() << " " << ( task.getError() ).t() << std::endl; + } + // Display task information + task.print() ; + + // Kill the task + task.kill(); + + // Close the log file + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp b/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp index 69518b944516ade71cfa725c657f6d33cf46f3e6..8b504499de04928b9d94b5656b88b8bbdcbde91a 100644 --- a/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp +++ b/example/robot-simulator/camera/servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp @@ -3,7 +3,7 @@ * $Id: servoSimu3D_cMcd_CamVelocityWithoutVpServo.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -97,6 +97,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -137,15 +140,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -154,7 +157,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -164,134 +167,141 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed camera velocities (m/s, rad/s) to achieve the task - // - the 6 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; -#ifdef WIN32 - logdirname ="C:/temp/" + username; + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed camera velocities (m/s, rad/s) to achieve the task + // - the 6 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; +#if defined(_WIN32) + logdirname ="C:/temp/" + username; #else - logdirname ="/tmp/" + username; + logdirname ="/tmp/" + username; #endif - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); - } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; - - // Open the log file name - std::ofstream flog(logfilename.c_str()); - - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : 3D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // Sets the initial camera location - vpPoseVector c_r_o(// Translation tx,ty,tz - 0.1, 0.2, 2, - // ThetaU rotation - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; - - // From the camera pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // Set the robot initial position - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - // Sets the desired camera location - vpPoseVector cd_r_o(// Translation tx,ty,tz - 0, 0, 1, - // ThetaU rotation - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - - // From the camera desired pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cdMo(cd_r_o) ; - - vpHomogeneousMatrix cMcd; // Transformation between current and desired camera frame - vpRotationMatrix cRcd; // Rotation between current and desired camera frame - - // Set the constant gain of the servo - double lambda = 1; - - unsigned int iter=0 ; - // Start the visual servoing loop. We stop the servo after 200 iterations - while(iter++ < 200) { - std::cout << "------------------------------------" << iter <<std::endl ; - - // get the robot position + std::string logfilename; + logfilename = logdirname + "/log.dat"; + + // Open the log file name + std::ofstream flog(logfilename.c_str()); + + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : 3D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Sets the initial camera location + vpPoseVector c_r_o(// Translation tx,ty,tz + 0.1, 0.2, 2, + // ThetaU rotation + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; + + // From the camera pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // Set the robot initial position + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; - - // new displacement to achieve - cMcd = cMo*cdMo.inverse() ; - - // Extract the translation vector ctc* which is the current - // translational visual feature. - vpTranslationVector ctcd; - cMcd.extract(ctcd); - // Compute the current theta U visual feature - vpThetaUVector tu_cRcd(cMcd); - - // Create the identity matrix - vpMatrix I(3,3); - I.setIdentity(); - - // Compute the camera translational velocity - vpColVector v(3); - v = lambda * ( I - vpColVector::skew(tu_cRcd) ) * ctcd; - // Compute the camera rotational velocity - vpColVector w(3); - w = lambda * tu_cRcd; - - // Update the complete camera velocity vector - vpColVector velocity(6); - for (unsigned int i=0; i<3; i++) { - velocity[i] = v[i]; // Translational velocity - velocity[i+3] = w[i]; // Rotational velocity + wMo = wMc * cMo; // Compute the position of the object in the world frame + + // Sets the desired camera location + vpPoseVector cd_r_o(// Translation tx,ty,tz + 0, 0, 1, + // ThetaU rotation + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + + // From the camera desired pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cdMo(cd_r_o) ; + + vpHomogeneousMatrix cMcd; // Transformation between current and desired camera frame + vpRotationMatrix cRcd; // Rotation between current and desired camera frame + + // Set the constant gain of the servo + double lambda = 1; + + unsigned int iter=0 ; + // Start the visual servoing loop. We stop the servo after 200 iterations + while(iter++ < 200) { + std::cout << "------------------------------------" << iter <<std::endl ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new displacement to achieve + cMcd = cMo*cdMo.inverse() ; + + // Extract the translation vector ctc* which is the current + // translational visual feature. + vpTranslationVector ctcd; + cMcd.extract(ctcd); + // Compute the current theta U visual feature + vpThetaUVector tu_cRcd(cMcd); + + // Create the identity matrix + vpMatrix I(3,3); + I.setIdentity(); + + // Compute the camera translational velocity + vpColVector v(3); + v = lambda * ( I - vpColVector::skew(tu_cRcd) ) * ctcd; + // Compute the camera rotational velocity + vpColVector w(3); + w = lambda * tu_cRcd; + + // Update the complete camera velocity vector + vpColVector velocity(6); + for (unsigned int i=0; i<3; i++) { + velocity[i] = v[i]; // Translational velocity + velocity[i+3] = w[i]; // Rotational velocity + } + + // Send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, velocity) ; + + // Retrieve the error (s-s*) + std::cout << "|| s - s* || = " << ctcd.t() << " " << tu_cRcd.t() << std::endl; + + // Save log + flog << velocity.t() << " " << ctcd.t() << " " << tu_cRcd.t() << std::endl; } - // Send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, velocity) ; - - // Retrieve the error (s-s*) - std::cout << "|| s - s* || = " << ctcd.t() << " " << tu_cRcd.t() << std::endl; - - // Save log - flog << velocity.t() << " " << ctcd.t() << " " << tu_cRcd.t() << std::endl; + // Close the log file + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Close the log file - flog.close(); } diff --git a/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocity.cpp b/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocity.cpp index e5ad3cc133c6455bb8920e820a70be8506c4531a..0d2cd25d5b70c1e2e37f5fb70d468b16bb88dd54 100644 --- a/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimu3D_cdMc_CamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,6 +74,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -114,15 +117,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -131,7 +134,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -141,153 +144,160 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed camera velocities (m/s, rad/s) to achieve the task - // - the 6 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; -#ifdef WIN32 - logdirname ="C:/temp/" + username; + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed camera velocities (m/s, rad/s) to achieve the task + // - the 6 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; +#if defined(_WIN32) + logdirname ="C:/temp/" + username; #else - logdirname ="/tmp/" + username; + logdirname ="/tmp/" + username; #endif - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; - - // Open the log file name - std::ofstream flog(logfilename.c_str()); - - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : 3D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - // Sets the initial camera location - vpPoseVector c_r_o(// Translation tx,ty,tz - 0.1, 0.2, 2, - // ThetaU rotation - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; - - // From the camera pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // Set the robot initial position - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - // Sets the desired camera location - vpPoseVector cd_r_o(// Translation tx,ty,tz - 0, 0, 1, - // ThetaU rotation - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - // From the camera desired pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cdMo(cd_r_o) ; - - // Compute the homogeneous transformation from the desired camera position to the initial one - vpHomogeneousMatrix cdMc ; - cdMc = cdMo*cMo.inverse() ; - - // Build the current visual features s = (c*tc, thetaU_c*Rc)^T - vpFeatureTranslation t(vpFeatureTranslation::cdMc) ; - vpFeatureThetaU tu(vpFeatureThetaU::cdRc); // current feature - t.buildFrom(cdMc) ; - tu.buildFrom(cdMc) ; - - // Sets the desired rotation (always zero !) since s is the - // rotation that the camera has to achieve. Here s* = (0, 0)^T - vpFeatureTranslation td(vpFeatureTranslation::cdMc) ; - vpFeatureThetaU tud(vpFeatureThetaU::cdRc); // desired feature - - // Define the task - // - we want an eye-in-hand control law - // - the robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - // - we use here the interaction matrix computed with the - // current features - task.setInteractionMatrixType(vpServo::CURRENT); - - // Add the current and desired visual features - task.addFeature(t,td) ; // 3D translation - task.addFeature(tu,tud) ; // 3D rotation - - // - set the constant gain to 1.0 - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // Start the visual servoing loop. We stop the servo after 200 iterations - while(iter++ < 200) { - std::cout << "-----------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + std::string logfilename; + logfilename = logdirname + "/log.dat"; + + // Open the log file name + std::ofstream flog(logfilename.c_str()); - // new displacement to achieve + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : 3D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + + // Sets the initial camera location + vpPoseVector c_r_o(// Translation tx,ty,tz + 0.1, 0.2, 2, + // ThetaU rotation + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; + + // From the camera pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // Set the robot initial position + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; // Compute the position of the object in the world frame + + // Sets the desired camera location + vpPoseVector cd_r_o(// Translation tx,ty,tz + 0, 0, 1, + // ThetaU rotation + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + // From the camera desired pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cdMo(cd_r_o) ; + + // Compute the homogeneous transformation from the desired camera position to the initial one + vpHomogeneousMatrix cdMc ; cdMc = cdMo*cMo.inverse() ; - // Update the current visual features + // Build the current visual features s = (c*tc, thetaU_c*Rc)^T + vpFeatureTranslation t(vpFeatureTranslation::cdMc) ; + vpFeatureThetaU tu(vpFeatureThetaU::cdRc); // current feature t.buildFrom(cdMc) ; tu.buildFrom(cdMc) ; - // Compute the control law - v = task.computeControlLaw() ; + // Sets the desired rotation (always zero !) since s is the + // rotation that the camera has to achieve. Here s* = (0, 0)^T + vpFeatureTranslation td(vpFeatureTranslation::cdMc) ; + vpFeatureThetaU tud(vpFeatureThetaU::cdRc); // desired feature + + // Define the task + // - we want an eye-in-hand control law + // - the robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + // - we use here the interaction matrix computed with the + // current features + task.setInteractionMatrixType(vpServo::CURRENT); + + // Add the current and desired visual features + task.addFeature(t,td) ; // 3D translation + task.addFeature(tu,tud) ; // 3D rotation + + // - set the constant gain to 1.0 + task.setLambda(1) ; // Display task information - if (iter==1) task.print() ; + task.print() ; - // Send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + unsigned int iter=0 ; + // Start the visual servoing loop. We stop the servo after 200 iterations + while(iter++ < 200) { + std::cout << "-----------------------------------" << iter <<std::endl ; + vpColVector v ; - // Retrieve the error - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - // Save log - flog << v.t() << " " << ( task.getError() ).t() << std::endl; - } - // Display task information - task.print() ; + // new displacement to achieve + cdMc = cdMo*cMo.inverse() ; + + // Update the current visual features + t.buildFrom(cdMc) ; + tu.buildFrom(cdMc) ; + + // Compute the control law + v = task.computeControlLaw() ; + + // Display task information + if (iter==1) task.print() ; + + // Send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - // Kill the task - task.kill(); + // Retrieve the error + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - // Close the log file - flog.close(); + // Save log + flog << v.t() << " " << ( task.getError() ).t() << std::endl; + } + // Display task information + task.print() ; + + // Kill the task + task.kill(); + + // Close the log file + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp b/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp index f2ad91339a7e78f9695b6fb4dfd6c7be5975991d..0348c7564e61b823622a3b0826afab63f1404d13 100644 --- a/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp +++ b/example/robot-simulator/camera/servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp @@ -3,7 +3,7 @@ * $Id: servoSimu3D_cdMc_CamVelocityWithoutVpServo.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -91,6 +91,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -131,15 +134,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -148,7 +151,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -158,133 +161,140 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed camera velocities (m/s, rad/s) to achieve the task - // - the 6 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; -#ifdef WIN32 - logdirname ="C:/temp/" + username; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed camera velocities (m/s, rad/s) to achieve the task + // - the 6 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; +#if defined(_WIN32) + logdirname ="C:/temp/" + username; #else - logdirname ="/tmp/" + username; + logdirname ="/tmp/" + username; #endif - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); - } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; - - // Open the log file name - std::ofstream flog(logfilename.c_str()); - - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program without vpServo and vpFeature classes " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : 3D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - // Sets the initial camera location - vpPoseVector c_r_o(// Translation tx,ty,tz - 0.1, 0.2, 2, - // ThetaU rotation - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; - - // From the camera pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // Set the robot initial position - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - // Sets the desired camera location - vpPoseVector cd_r_o(// Translation tx,ty,tz - 0, 0, 1, - // ThetaU rotation - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - // From the camera desired pose build the corresponding homogeneous matrix - vpHomogeneousMatrix cdMo(cd_r_o) ; - - vpHomogeneousMatrix cdMc; // Transformation between desired and current camera frame - vpRotationMatrix cdRc; // Rotation between desired and current camera frame - vpRotationMatrix cRcd; // Rotation between current and desired camera frame - - // Set the constant gain of the servo - double lambda = 1; - - unsigned int iter=0 ; - // Start the visual servoing loop. We stop the servo after 200 iterations - while(iter++ < 200) { - std::cout << "-----------------------------------" << iter <<std::endl ; - - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; - - // new displacement to achieve - cdMc = cdMo*cMo.inverse() ; - - // Extract the translation vector c*tc which is the current - // translational visual feature. - vpTranslationVector cdtc; - cdMc.extract(cdtc); - // Extract the rotation matrix c*Rc - cdMc.extract(cdRc); - // Compute the inverse rotation cRc* (in fact the transpose of c*Rc) - cRcd = cdRc.inverse(); - // Compute the current theta U visual feature - vpThetaUVector tu_cdRc(cdMc); - // Compute the camera translational velocity - vpColVector v(3); - v = -lambda * cRcd * cdtc; - // Compute the camera rotational velocity - vpColVector w(3); - w = -lambda * tu_cdRc; - - // Update the complete camera velocity vector - vpColVector velocity(6); - for (unsigned int i=0; i<3; i++) { - velocity[i] = v[i]; // Translational velocity - velocity[i+3] = w[i]; // Rotational velocity + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } + std::string logfilename; + logfilename = logdirname + "/log.dat"; - // Send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, velocity) ; + // Open the log file name + std::ofstream flog(logfilename.c_str()); - // Retrieve the error (s-s*) - std::cout << "|| s - s* || = " << cdtc.t() << " " << tu_cdRc.t() << std::endl; + vpSimulatorCamera robot ; - // Save log - flog << velocity.t() << " " << cdtc.t() << " " << tu_cdRc.t() << std::endl; + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program without vpServo and vpFeature classes " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : 3D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + + // Sets the initial camera location + vpPoseVector c_r_o(// Translation tx,ty,tz + 0.1, 0.2, 2, + // ThetaU rotation + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) ) ; + + // From the camera pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // Set the robot initial position + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; // Compute the position of the object in the world frame + + // Sets the desired camera location + vpPoseVector cd_r_o(// Translation tx,ty,tz + 0, 0, 1, + // ThetaU rotation + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + // From the camera desired pose build the corresponding homogeneous matrix + vpHomogeneousMatrix cdMo(cd_r_o) ; + + vpHomogeneousMatrix cdMc; // Transformation between desired and current camera frame + vpRotationMatrix cdRc; // Rotation between desired and current camera frame + vpRotationMatrix cRcd; // Rotation between current and desired camera frame + + // Set the constant gain of the servo + double lambda = 1; + + unsigned int iter=0 ; + // Start the visual servoing loop. We stop the servo after 200 iterations + while(iter++ < 200) { + std::cout << "-----------------------------------" << iter <<std::endl ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new displacement to achieve + cdMc = cdMo*cMo.inverse() ; + + // Extract the translation vector c*tc which is the current + // translational visual feature. + vpTranslationVector cdtc; + cdMc.extract(cdtc); + // Extract the rotation matrix c*Rc + cdMc.extract(cdRc); + // Compute the inverse rotation cRc* (in fact the transpose of c*Rc) + cRcd = cdRc.inverse(); + // Compute the current theta U visual feature + vpThetaUVector tu_cdRc(cdMc); + // Compute the camera translational velocity + vpColVector v(3); + v = -lambda * cRcd * cdtc; + // Compute the camera rotational velocity + vpColVector w(3); + w = -lambda * tu_cdRc; + + // Update the complete camera velocity vector + vpColVector velocity(6); + for (unsigned int i=0; i<3; i++) { + velocity[i] = v[i]; // Translational velocity + velocity[i+3] = w[i]; // Rotational velocity + } + + // Send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, velocity) ; + + // Retrieve the error (s-s*) + std::cout << "|| s - s* || = " << cdtc.t() << " " << tu_cdRc.t() << std::endl; + + // Save log + flog << velocity.t() << " " << cdtc.t() << " " << tu_cdRc.t() << std::endl; + } + // Close the log file + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - // Close the log file - flog.close(); } diff --git a/example/robot-simulator/camera/servoSimuCircle2DCamVelocity.cpp b/example/robot-simulator/camera/servoSimuCircle2DCamVelocity.cpp index 9944fde20f5190e17f0d63d12674dacc0028b368..4b4b9c0bab9434b1ba1759df2b5aa36a26cd1ca7 100644 --- a/example/robot-simulator/camera/servoSimuCircle2DCamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimuCircle2DCamVelocity.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuCircle2DCamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -113,15 +116,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -130,7 +133,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -140,96 +143,103 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a circle " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - vpHomogeneousMatrix cMod ; - cMod[0][3] = 0 ; - cMod[1][3] = 0 ; - cMod[2][3] = 1 ; - - // sets the circle coordinates in the world frame - vpCircle circle ; - circle.setWorldCoordinates(0,0,1,0,0,0,0.1) ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // sets the desired position of the visual feature - vpFeatureEllipse pd ; - circle.track(cMod) ; - vpFeatureBuilder::create(pd,circle) ; + vpServo task ; + vpSimulatorCamera robot ; - // project : computes the circle coordinates in the camera frame and its 2D coordinates + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a circle " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; - // sets the current position of the visual feature - vpFeatureEllipse p ; - circle.track(cMo) ; - vpFeatureBuilder::create(p,circle) ; - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; - // - we want to see a circle on a circle - std::cout << std::endl ; - task.addFeature(p,pd) ; + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; // Compute the position of the object in the world frame - // - set the gain - task.setLambda(1) ; + vpHomogeneousMatrix cMod ; + cMod[0][3] = 0 ; + cMod[1][3] = 0 ; + cMod[2][3] = 1 ; - // Display task information - task.print() ; + // sets the circle coordinates in the world frame + vpCircle circle ; + circle.setWorldCoordinates(0,0,1,0,0,0,0.1) ; - unsigned int iter=0 ; - // loop - while(iter++ < 500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + // sets the desired position of the visual feature + vpFeatureEllipse pd ; + circle.track(cMod) ; + vpFeatureBuilder::create(pd,circle) ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // project : computes the circle coordinates in the camera frame and its 2D coordinates - // new circle position: retrieve x,y and Z of the vpCircle structure + // sets the current position of the visual feature + vpFeatureEllipse p ; circle.track(cMo) ; - vpFeatureBuilder::create(p,circle); - - // compute the control law - v = task.computeControlLaw() ; - std::cout << "task rank: " << task.getTaskRank() <<std::endl ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + vpFeatureBuilder::create(p,circle) ; + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + + // - we want to see a circle on a circle + std::cout << std::endl ; + task.addFeature(p,pd) ; + + // - set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++ < 500) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new circle position: retrieve x,y and Z of the vpCircle structure + circle.track(cMo) ; + vpFeatureBuilder::create(p,circle); + + // compute the control law + v = task.computeControlLaw() ; + std::cout << "task rank: " << task.getTaskRank() <<std::endl ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() << std::endl ; + } - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() << std::endl ; + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); } diff --git a/example/robot-simulator/camera/servoSimuCircle2DCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuCircle2DCamVelocityDisplay.cpp index 4c70e19549442b06dee372f37b7d599dafc7a9fa..e3ca194d33b605e120b22a575c65fff0026f3ea9 100644 --- a/example/robot-simulator/camera/servoSimuCircle2DCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuCircle2DCamVelocityDisplay.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuCircle2DCamVelocityDisplay.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -64,6 +64,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeatureLine.h> #include <visp/vpHomogeneousMatrix.h> @@ -78,6 +79,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -130,9 +134,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -140,7 +144,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -149,7 +153,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -160,141 +164,149 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click_allowed = true; + try { + bool opt_display = true; + bool opt_click_allowed = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - vpImage<unsigned char> I(512,512,0) ; + vpImage<unsigned char> I(512,512,0) ; - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display.init(I, 100, 100,"Camera view...") ; - // Display the image - // The image class has a member that specify a pointer toward - // the display that has been initialized in the display declaration - // therefore is is no longuer necessary to make a reference to the - // display variable. - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display.init(I, 100, 100,"Camera view...") ; + // Display the image + // The image class has a member that specify a pointer toward + // the display that has been initialized in the display declaration + // therefore is is no longuer necessary to make a reference to the + // display variable. + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - double px, py ; px = py = 600 ; - double u0, v0 ; u0 = v0 = 256 ; - - vpCameraParameters cam(px,py,u0,v0); - - vpServo task ; - vpSimulatorCamera robot ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(0,0,1, - vpMath::rad(0), vpMath::rad(80), vpMath::rad(30)) ; - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - vpHomogeneousMatrix cMod(-0.1,-0.1,0.7, - vpMath::rad(40), vpMath::rad(10), vpMath::rad(30)) ; - - // sets the circle coordinates in the world frame - vpCircle circle ; - circle.setWorldCoordinates(0,0,1, - 0,0,0, - 0.1) ; - - // sets the desired position of the visual feature - vpFeatureEllipse pd ; - circle.track(cMod) ; - vpFeatureBuilder::create(pd,circle) ; - - // project : computes the circle coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - vpFeatureEllipse p ; - circle.track(cMo) ; - vpFeatureBuilder::create(p,circle) ; - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - task.setInteractionMatrixType(vpServo::DESIRED) ; - // - we want to see a circle on a circle - task.addFeature(p,pd) ; - // - set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++ < 200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position + double px, py ; px = py = 600 ; + double u0, v0 ; u0 = v0 = 256 ; + + vpCameraParameters cam(px,py,u0,v0); + + vpServo task ; + vpSimulatorCamera robot ; + + // sets the initial camera location + vpHomogeneousMatrix cMo(0,0,1, + vpMath::rad(0), vpMath::rad(80), vpMath::rad(30)) ; + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; // Compute the position of the object in the world frame - // new circle position - // retrieve x,y and Z of the vpCircle structure - circle.track(cMo) ; - vpFeatureBuilder::create(p,circle); - circle.print() ; - p.print() ; + vpHomogeneousMatrix cMod(-0.1,-0.1,0.7, + vpMath::rad(40), vpMath::rad(10), vpMath::rad(30)) ; - if (opt_display) { - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; - } + // sets the circle coordinates in the world frame + vpCircle circle ; + circle.setWorldCoordinates(0,0,1, + 0,0,0, + 0.1) ; - // compute the control law - v = task.computeControlLaw() ; - std::cout << "task rank: " << task.getTaskRank() <<std::endl ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // sets the desired position of the visual feature + vpFeatureEllipse pd ; + circle.track(cMod) ; + vpFeatureBuilder::create(pd,circle) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // project : computes the circle coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature + vpFeatureEllipse p ; + circle.track(cMo) ; + vpFeatureBuilder::create(p,circle) ; + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + task.setInteractionMatrixType(vpServo::DESIRED) ; + // - we want to see a circle on a circle + task.addFeature(p,pd) ; + // - set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++ < 200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new circle position + // retrieve x,y and Z of the vpCircle structure + circle.track(cMo) ; + vpFeatureBuilder::create(p,circle); + circle.print() ; + p.print() ; + + if (opt_display) { + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + } + + // compute the control law + v = task.computeControlLaw() ; + std::cout << "task rank: " << task.getTaskRank() <<std::endl ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - // Display task information - task.print() ; - task.kill(); + // Display task information + task.print() ; + task.kill(); - if (opt_display && opt_click_allowed) { - std::cout << "Click in the camera view window to end..." << std::endl; - vpDisplay::getClick(I) ; + if (opt_display && opt_click_allowed) { + std::cout << "Click in the camera view window to end..." << std::endl; + vpDisplay::getClick(I) ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplay.cpp index 2b3f4dec9c590fd7e3ef4dbab5f8fa23dad20e57..091b86c0b96bc2d46fc3fcf953306230245db7d2 100644 --- a/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplay.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuCylinder2DCamVelocityDisplay.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,7 +53,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -63,6 +63,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeatureLine.h> #include <visp/vpHomogeneousMatrix.h> @@ -77,6 +78,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -126,9 +130,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -136,7 +140,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -145,7 +149,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -156,177 +160,186 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click_allowed = true; + try { + bool opt_display = true; + bool opt_click_allowed = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - vpImage<unsigned char> I(512,512,255) ; + vpImage<unsigned char> I(512,512,255) ; - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display.init(I, 100, 100,"Camera view...") ; - // Display the image - // The image class has a member that specify a pointer toward - // the display that has been initialized in the display declaration - // therefore is is no longuer necessary to make a reference to the - // display variable. - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display.init(I, 100, 100,"Camera view...") ; + // Display the image + // The image class has a member that specify a pointer toward + // the display that has been initialized in the display declaration + // therefore is is no longuer necessary to make a reference to the + // display variable. + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - double px, py ; px = py = 600 ; - double u0, v0 ; u0 = v0 = 256 ; + double px, py ; px = py = 600 ; + double u0, v0 ; u0 = v0 = 256 ; - vpCameraParameters cam(px,py,u0,v0); + vpCameraParameters cam(px,py,u0,v0); - vpServo task ; - vpSimulatorCamera robot ; + vpServo task ; + vpSimulatorCamera robot ; - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.2,0.1,2, - vpMath::rad(5), vpMath::rad(5), vpMath::rad(20)); + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.2,0.1,2, + vpMath::rad(5), vpMath::rad(5), vpMath::rad(20)); - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame - - // sets the final camera location (for simulation purpose) - vpHomogeneousMatrix cMod(0,0,1, - vpMath::rad(-60), vpMath::rad(0), vpMath::rad(0)); - - // sets the cylinder coordinates in the world frame - vpCylinder cylinder(0,1,0, // direction - 0,0,0, // point of the axis - 0.1) ; // radius - - // sets the desired position of the visual feature - cylinder.track(cMod) ; - cylinder.print() ; - - vpFeatureLine ld[2] ; - int i ; - for(i=0 ; i < 2 ; i++) - vpFeatureBuilder::create(ld[i],cylinder,i) ; - - // computes the cylinder coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - cylinder.track(cMo) ; - cylinder.print() ; - - vpFeatureLine l[2] ; - for(i=0 ; i < 2 ; i++) - { - vpFeatureBuilder::create(l[i],cylinder,i) ; - l[i].print() ; - } - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) ; - // it can also be interesting to test these possibilities - // task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE) ; - task.setInteractionMatrixType(vpServo::DESIRED,vpServo::PSEUDO_INVERSE) ; - //task.setInteractionMatrixType(vpServo::DESIRED, vpServo::TRANSPOSE) ; - // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::TRANSPOSE) ; - - // - we want to see 2 lines on 2 lines - task.addFeature(l[0],ld[0]) ; - task.addFeature(l[1],ld[1]) ; - - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; - - // Display task information - task.print() ; - - if (opt_display && opt_click_allowed) { - std::cout << "\n\nClick in the camera view window to start..." << std::endl; - vpDisplay::getClick(I) ; - } + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; // Compute the position of the object in the world frame - // - set the gain - task.setLambda(1) ; + // sets the final camera location (for simulation purpose) + vpHomogeneousMatrix cMod(0,0,1, + vpMath::rad(-60), vpMath::rad(0), vpMath::rad(0)); - // Display task information - task.print() ; + // sets the cylinder coordinates in the world frame + vpCylinder cylinder(0,1,0, // direction + 0,0,0, // point of the axis + 0.1) ; // radius - unsigned int iter=0 ; - // loop - do - { - std::cout << "---------------------------------------------" << iter++ <<std::endl ; - vpColVector v ; + // sets the desired position of the visual feature + cylinder.track(cMod) ; + cylinder.print() ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + vpFeatureLine ld[2] ; + int i ; + for(i=0 ; i < 2 ; i++) + vpFeatureBuilder::create(ld[i],cylinder,i) ; - // new line position - // retrieve x,y and Z of the vpLine structure + // computes the cylinder coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature cylinder.track(cMo) ; - // cylinder.print() ; + cylinder.print() ; + + vpFeatureLine l[2] ; for(i=0 ; i < 2 ; i++) { vpFeatureBuilder::create(l[i],cylinder,i) ; - // l[i].print() ; + l[i].print() ; } - if (opt_display) { - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) ; + // it can also be interesting to test these possibilities + // task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE) ; + task.setInteractionMatrixType(vpServo::DESIRED,vpServo::PSEUDO_INVERSE) ; + //task.setInteractionMatrixType(vpServo::DESIRED, vpServo::TRANSPOSE) ; + // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::TRANSPOSE) ; + + // - we want to see 2 lines on 2 lines + task.addFeature(l[0],ld[0]) ; + task.addFeature(l[1],ld[1]) ; + + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + + // Display task information + task.print() ; + + if (opt_display && opt_click_allowed) { + std::cout << "\n\nClick in the camera view window to start..." << std::endl; + vpDisplay::getClick(I) ; } - // compute the control law - v = task.computeControlLaw() ; + // - set the gain + task.setLambda(1) ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Display task information + task.print() ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; + unsigned int iter=0 ; + // loop + do + { + std::cout << "---------------------------------------------" << iter++ <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new line position + // retrieve x,y and Z of the vpLine structure + cylinder.track(cMo) ; + // cylinder.print() ; + for(i=0 ; i < 2 ; i++) + { + vpFeatureBuilder::create(l[i],cylinder,i) ; + // l[i].print() ; + } + + if (opt_display) { + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + } + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; + + // vpDisplay::getClick(I) ; + } + while(( task.getError() ).sumSquare() > 1e-9) ; - // vpDisplay::getClick(I) ; - } - while(( task.getError() ).sumSquare() > 1e-9) ; + if (opt_display && opt_click_allowed) { + std::cout << "\nClick in the camera view window to end..." << std::endl; + vpDisplay::getClick(I) ; + } - if (opt_display && opt_click_allowed) { - std::cout << "\nClick in the camera view window to end..." << std::endl; - vpDisplay::getClick(I) ; + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp b/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp index d98db77f22fc032edd51e5cf0eef850f24d7b79d..efc02a5fb1e80f4178b5c7a790e099d38a0a07be 100644 --- a/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp +++ b/example/robot-simulator/camera/servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuCylinder2DCamVelocityDisplaySecondaryTask.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,7 +56,7 @@ #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -66,6 +66,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeatureLine.h> #include <visp/vpHomogeneousMatrix.h> @@ -80,6 +81,8 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); /*! @@ -130,9 +133,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -140,7 +143,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -149,7 +152,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -160,286 +163,296 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click_allowed = true; + try { + bool opt_display = true; + bool opt_click_allowed = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - vpImage<unsigned char> Iint(512,512,0) ; - vpImage<unsigned char> Iext(512,512,0) ; + vpImage<unsigned char> Iint(512,512,0) ; + vpImage<unsigned char> Iext(512,512,0) ; - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX displayInt; - vpDisplayX displayExt; + vpDisplayX displayInt; + vpDisplayX displayExt; #elif defined VISP_HAVE_GTK - vpDisplayGTK displayInt; - vpDisplayGTK displayExt; + vpDisplayGTK displayInt; + vpDisplayGTK displayExt; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt; - vpDisplayGDI displayExt; + vpDisplayGDI displayInt; + vpDisplayGDI displayExt; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV displayInt; + vpDisplayOpenCV displayExt; #endif - if (opt_display) { - try{ - // Display size is automatically defined by the image (Iint) and (Iext) size - displayInt.init(Iint, 100, 100,"Internal view") ; - displayExt.init(Iext, (int)(130+Iint.getWidth()), 100, "External view") ; - // Display the image - // The image class has a member that specify a pointer toward - // the display that has been initialized in the display declaration - // therefore is is no longuer necessary to make a reference to the - // display variable. - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - vpDisplay::flush(Iint) ; - vpDisplay::flush(Iext) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + if (opt_display) { + try{ + // Display size is automatically defined by the image (Iint) and (Iext) size + displayInt.init(Iint, 100, 100,"Internal view") ; + displayExt.init(Iext, (int)(130+Iint.getWidth()), 100, "External view") ; + // Display the image + // The image class has a member that specify a pointer toward + // the display that has been initialized in the display declaration + // therefore is is no longuer necessary to make a reference to the + // display variable. + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + vpDisplay::flush(Iint) ; + vpDisplay::flush(Iext) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - - vpProjectionDisplay externalview ; - - //Set the camera parameters - double px, py ; px = py = 600 ; - double u0, v0 ; u0 = v0 = 256 ; - - vpCameraParameters cam(px,py,u0,v0); - - vpServo task ; - vpSimulatorCamera robot ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.2,0.1,2, - vpMath::rad(5), vpMath::rad(5), vpMath::rad(20)); - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; // Compute the position of the object in the world frame + vpProjectionDisplay externalview ; - // sets the final camera location (for simulation purpose) - vpHomogeneousMatrix cMod(0,0,1, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + //Set the camera parameters + double px, py ; px = py = 600 ; + double u0, v0 ; u0 = v0 = 256 ; - // sets the cylinder coordinates in the world frame - vpCylinder cylinder(0,1,0, // direction - 0,0,0, // point of the axis - 0.1) ; // radius + vpCameraParameters cam(px,py,u0,v0); - externalview.insert(cylinder) ; + vpServo task ; + vpSimulatorCamera robot ; - // sets the desired position of the visual feature - cylinder.track(cMod) ; - cylinder.print() ; + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.2,0.1,2, + vpMath::rad(5), vpMath::rad(5), vpMath::rad(20)); - //Build the desired line features thanks to the cylinder and especially its paramaters in the image frame - vpFeatureLine ld[2] ; - int i ; - for(i=0 ; i < 2 ; i++) - vpFeatureBuilder::create(ld[i],cylinder,i) ; - - // computes the cylinder coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - cylinder.track(cMo) ; - cylinder.print() ; - - //Build the current line features thanks to the cylinder and especially its paramaters in the image frame - vpFeatureLine l[2] ; - for(i=0 ; i < 2 ; i++) - { - vpFeatureBuilder::create(l[i],cylinder,i) ; - l[i].print() ; - } - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - task.setInteractionMatrixType(vpServo::DESIRED,vpServo::PSEUDO_INVERSE) ; - // it can also be interesting to test these possibilities - // task.setInteractionMatrixType(vpServo::CURRENT,vpServo::PSEUDO_INVERSE) ; - // task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE) ; - // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) ; - // task.setInteractionMatrixType(vpServo::DESIRED, vpServo::TRANSPOSE) ; - // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::TRANSPOSE) ; - - // we want to see 2 lines on 2 lines - task.addFeature(l[0],ld[0]) ; - task.addFeature(l[1],ld[1]) ; - - // Set the point of view of the external view - vpHomogeneousMatrix cextMo(0,0,6, - vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; - - // Display the initial scene - vpServoDisplay::display(task,cam,Iint) ; - externalview.display(Iext,cextMo, cMo, cam, vpColor::red); - vpDisplay::flush(Iint) ; - vpDisplay::flush(Iext) ; - - // Display task information - task.print() ; - - if (opt_display && opt_click_allowed) { - std::cout << "\n\nClick in the internal camera view window to start..." << std::endl; - vpDisplay::getClick(Iint) ; - } - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // The first loop is needed to reach the desired position - do - { - std::cout << "---------------------------------------------" << iter++ <<std::endl ; - vpColVector v ; - - // get the robot position + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; // Compute the position of the object in the world frame - // new line position - // retrieve x,y and Z of the vpLine structure - // Compute the parameters of the cylinder in the camera frame and in the image frame - cylinder.track(cMo) ; + // sets the final camera location (for simulation purpose) + vpHomogeneousMatrix cMod(0,0,1, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - //Build the current line features thanks to the cylinder and especially its paramaters in the image frame - for(i=0 ; i < 2 ; i++) - { - vpFeatureBuilder::create(l[i],cylinder,i) ; - } + // sets the cylinder coordinates in the world frame + vpCylinder cylinder(0,1,0, // direction + 0,0,0, // point of the axis + 0.1) ; // radius - // Display the current scene - if (opt_display) { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - vpServoDisplay::display(task,cam,Iint) ; - externalview.display(Iext,cextMo, cMo, cam, vpColor::red); - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); - } + externalview.insert(cylinder) ; - // compute the control law - v = task.computeControlLaw() ; + // sets the desired position of the visual feature + cylinder.track(cMod) ; + cylinder.print() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } - while(( task.getError() ).sumSquare() > 1e-9) ; - - - // Second loop is to compute the control law while taking into account the secondary task. - // In this example the secondary task is cut in four steps. - // The first one consists in impose a movement of the robot along the x axis of the object frame with a velocity of 0.5. - // The second one consists in impose a movement of the robot along the y axis of the object frame with a velocity of 0.5. - // The third one consists in impose a movement of the robot along the x axis of the object frame with a velocity of -0.5. - // The last one consists in impose a movement of the robot along the y axis of the object frame with a velocity of -0.5. - // Each steps is made during 200 iterations. - vpColVector e1(6) ; e1 = 0 ; - vpColVector e2(6) ; e2 = 0 ; - vpColVector proj_e1 ; - vpColVector proj_e2 ; - iter = 0; - double rapport = 0; - double vitesse = 0.5; - unsigned int tempo = 800; - - while(iter < tempo) - { - vpColVector v ; - - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + //Build the desired line features thanks to the cylinder and especially its paramaters in the image frame + vpFeatureLine ld[2] ; + int i ; + for(i=0 ; i < 2 ; i++) + vpFeatureBuilder::create(ld[i],cylinder,i) ; + // computes the cylinder coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature cylinder.track(cMo) ; + cylinder.print() ; + //Build the current line features thanks to the cylinder and especially its paramaters in the image frame + vpFeatureLine l[2] ; for(i=0 ; i < 2 ; i++) { vpFeatureBuilder::create(l[i],cylinder,i) ; + l[i].print() ; } - if (opt_display) - { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - vpServoDisplay::display(task,cam,Iint) ; - externalview.display(Iext,cextMo, cMo, cam, vpColor::red); - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + task.setInteractionMatrixType(vpServo::DESIRED,vpServo::PSEUDO_INVERSE) ; + // it can also be interesting to test these possibilities + // task.setInteractionMatrixType(vpServo::CURRENT,vpServo::PSEUDO_INVERSE) ; + // task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE) ; + // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) ; + // task.setInteractionMatrixType(vpServo::DESIRED, vpServo::TRANSPOSE) ; + // task.setInteractionMatrixType(vpServo::CURRENT, vpServo::TRANSPOSE) ; + + // we want to see 2 lines on 2 lines + task.addFeature(l[0],ld[0]) ; + task.addFeature(l[1],ld[1]) ; + + // Set the point of view of the external view + vpHomogeneousMatrix cextMo(0,0,6, + vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; + + // Display the initial scene + vpServoDisplay::display(task,cam,Iint) ; + externalview.display(Iext,cextMo, cMo, cam, vpColor::red); + vpDisplay::flush(Iint) ; + vpDisplay::flush(Iext) ; + + // Display task information + task.print() ; + + if (opt_display && opt_click_allowed) { + std::cout << "\n\nClick in the internal camera view window to start..." << std::endl; + vpDisplay::getClick(Iint) ; } - v = task.computeControlLaw() ; + // set the gain + task.setLambda(1) ; - if ( iter%tempo < 200 /*&& iter%tempo >= 0*/) - { - e2 = 0; - e1[0] = fabs(vitesse) ; - proj_e1 = task.secondaryTask(e1); - rapport = vitesse/proj_e1[0]; - proj_e1 *= rapport ; - v += proj_e1 ; - } + // Display task information + task.print() ; - if ( iter%tempo < 400 && iter%tempo >= 200) + unsigned int iter=0 ; + // The first loop is needed to reach the desired position + do { - e1 = 0; - e2[1] = fabs(vitesse) ; - proj_e2 = task.secondaryTask(e2); - rapport = vitesse/proj_e2[1]; - proj_e2 *= rapport ; - v += proj_e2 ; + std::cout << "---------------------------------------------" << iter++ <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new line position + // retrieve x,y and Z of the vpLine structure + // Compute the parameters of the cylinder in the camera frame and in the image frame + cylinder.track(cMo) ; + + //Build the current line features thanks to the cylinder and especially its paramaters in the image frame + for(i=0 ; i < 2 ; i++) + { + vpFeatureBuilder::create(l[i],cylinder,i) ; + } + + // Display the current scene + if (opt_display) { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + vpServoDisplay::display(task,cam,Iint) ; + externalview.display(Iext,cextMo, cMo, cam, vpColor::red); + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + } + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; } - - if ( iter%tempo < 600 && iter%tempo >= 400) + while(( task.getError() ).sumSquare() > 1e-9) ; + + + // Second loop is to compute the control law while taking into account the secondary task. + // In this example the secondary task is cut in four steps. + // The first one consists in impose a movement of the robot along the x axis of the object frame with a velocity of 0.5. + // The second one consists in impose a movement of the robot along the y axis of the object frame with a velocity of 0.5. + // The third one consists in impose a movement of the robot along the x axis of the object frame with a velocity of -0.5. + // The last one consists in impose a movement of the robot along the y axis of the object frame with a velocity of -0.5. + // Each steps is made during 200 iterations. + vpColVector e1(6) ; e1 = 0 ; + vpColVector e2(6) ; e2 = 0 ; + vpColVector proj_e1 ; + vpColVector proj_e2 ; + iter = 0; + double rapport = 0; + double vitesse = 0.5; + unsigned int tempo = 800; + + while(iter < tempo) { - e2 = 0; - e1[0] = -fabs(vitesse) ; - proj_e1 = task.secondaryTask(e1); - rapport = -vitesse/proj_e1[0]; - proj_e1 *= rapport ; - v += proj_e1 ; + vpColVector v ; + + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + cylinder.track(cMo) ; + + for(i=0 ; i < 2 ; i++) + { + vpFeatureBuilder::create(l[i],cylinder,i) ; + } + + if (opt_display) + { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + vpServoDisplay::display(task,cam,Iint) ; + externalview.display(Iext,cextMo, cMo, cam, vpColor::red); + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + } + + v = task.computeControlLaw() ; + + if ( iter%tempo < 200 /*&& iter%tempo >= 0*/) + { + e2 = 0; + e1[0] = fabs(vitesse) ; + proj_e1 = task.secondaryTask(e1); + rapport = vitesse/proj_e1[0]; + proj_e1 *= rapport ; + v += proj_e1 ; + } + + if ( iter%tempo < 400 && iter%tempo >= 200) + { + e1 = 0; + e2[1] = fabs(vitesse) ; + proj_e2 = task.secondaryTask(e2); + rapport = vitesse/proj_e2[1]; + proj_e2 *= rapport ; + v += proj_e2 ; + } + + if ( iter%tempo < 600 && iter%tempo >= 400) + { + e2 = 0; + e1[0] = -fabs(vitesse) ; + proj_e1 = task.secondaryTask(e1); + rapport = -vitesse/proj_e1[0]; + proj_e1 *= rapport ; + v += proj_e1 ; + } + + if ( iter%tempo < 800 && iter%tempo >= 600) + { + e1 = 0; + e2[1] = -fabs(vitesse) ; + proj_e2 = task.secondaryTask(e2); + rapport = -vitesse/proj_e2[1]; + proj_e2 *= rapport ; + v += proj_e2 ; + } + + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + + iter++; } - if ( iter%tempo < 800 && iter%tempo >= 600) - { - e1 = 0; - e2[1] = -fabs(vitesse) ; - proj_e2 = task.secondaryTask(e2); - rapport = -vitesse/proj_e2[1]; - proj_e2 *= rapport ; - v += proj_e2 ; + if (opt_display && opt_click_allowed) { + std::cout << "\nClick in the internal camera view window to end..." << std::endl; + vpDisplay::getClick(Iint) ; } - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - - iter++; + // Display task information + task.print() ; + task.kill(); + return 0; } - - if (opt_display && opt_click_allowed) { - std::cout << "\nClick in the internal camera view window to end..." << std::endl; - vpDisplay::getClick(Iint) ; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); } #else @@ -447,7 +460,7 @@ main(int argc, const char ** argv) int main() { - std::cout << "You do not have X11, GTK or GDI display functionalities..." << std::endl; + std::cout << "You do not have X11, GTK, GDI or OpenCV display functionalities..." << std::endl; } #endif diff --git a/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocity.cpp b/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocity.cpp index ae20ae2e83064721e5907578948f66f979db0b10..7d750d955c3d71be4984e85d645a2c6175058273 100644 --- a/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuFourPoints2DCamVelocity.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -113,15 +116,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -130,7 +133,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -140,127 +143,134 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - int i ; - vpServo task ; - vpSimulatorCamera robot ; - - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location with respect to the object - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the point coordinates in the object frame - vpPoint point[4] ; - point[0].setWorldCoordinates(-1,-1,0) ; - point[1].setWorldCoordinates(1,-1,0) ; - point[2].setWorldCoordinates(1,1,0) ; - point[3].setWorldCoordinates(-1,1,0) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; - - // sets the desired position of the point - vpFeaturePoint p[4] ; - for (i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the point - vpFeaturePoint pd[4] ; - - pd[0].buildFrom(-0.1,-0.1, 1) ; - pd[1].buildFrom( 0.1,-0.1, 1) ; - pd[2].buildFrom( 0.1, 0.1, 1) ; - pd[3].buildFrom(-0.1, 0.1, 1) ; - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::MEAN) ; - - // Set the position of the camera in the end-effector frame - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - - // we want to see a point on a point - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++<1500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + int i ; + vpServo task ; + vpSimulatorCamera robot ; - // Set the Jacobian (expressed in the end-effector frame) - // since q is modified eJe is modified - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - // get the robot position + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location with respect to the object + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; + + // sets the point coordinates in the object frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-1,-1,0) ; + point[1].setWorldCoordinates(1,-1,0) ; + point[2].setWorldCoordinates(1,1,0) ; + point[3].setWorldCoordinates(-1,1,0) ; - // update new point position and corresponding features + // computes the point coordinates in the camera frame and its 2D coordinates for (i = 0 ; i < 4 ; i++) - { point[i].track(cMo) ; - //retrieve x,y and Z of the vpPoint structure - vpFeatureBuilder::create(p[i],point[i]) ; - } - // since vpServo::MEAN interaction matrix is used, we need also to update the desired features at each iteration + + // sets the desired position of the point + vpFeaturePoint p[4] ; + for (i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i], point[i]) ; //retrieve x,y and Z of the vpPoint structure + + // sets the desired position of the point + vpFeaturePoint pd[4] ; + pd[0].buildFrom(-0.1,-0.1, 1) ; pd[1].buildFrom( 0.1,-0.1, 1) ; pd[2].buildFrom( 0.1, 0.1, 1) ; pd[3].buildFrom(-0.1, 0.1, 1) ; - // compute the control law ") ; - v = task.computeControlLaw() ; + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::MEAN) ; - // send the camera velocity to the controller ") ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() << std::endl; - } + // Set the Jacobian (expressed in the end-effector frame) + vpMatrix eJe ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // we want to see a point on a point + for (i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; - // Display task information - task.print() ; - task.kill(); + // set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++<1500) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // Set the Jacobian (expressed in the end-effector frame) + // since q is modified eJe is modified + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // update new point position and corresponding features + for (i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + //retrieve x,y and Z of the vpPoint structure + vpFeatureBuilder::create(p[i],point[i]) ; + } + // since vpServo::MEAN interaction matrix is used, we need also to update the desired features at each iteration + pd[0].buildFrom(-0.1,-0.1, 1) ; + pd[1].buildFrom( 0.1,-0.1, 1) ; + pd[2].buildFrom( 0.1, 0.1, 1) ; + pd[3].buildFrom(-0.1, 0.1, 1) ; + + // compute the control law ") ; + v = task.computeControlLaw() ; + + // send the camera velocity to the controller ") ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() << std::endl; + } + + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocityDisplay.cpp index 1c63a68f3f3be7fb181a9ba467599271981ec30b..e3f31a09c1cd3ed02931d4293409ba45cc4ba292 100644 --- a/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuFourPoints2DCamVelocityDisplay.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuFourPoints2DCamVelocityDisplay.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -67,6 +67,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeaturePoint.h> #include <visp/vpHomogeneousMatrix.h> @@ -81,6 +82,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -130,9 +134,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -140,7 +144,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -149,7 +153,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -159,183 +163,192 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { + try { + bool opt_click_allowed = true; + bool opt_display = true; - bool opt_click_allowed = true; - bool opt_display = true; - - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // We open two displays, one for the internal camera view, the other one for - // the external view, using either X11, GTK or GDI. + // We open two displays, one for the internal camera view, the other one for + // the external view, using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX displayInt; - vpDisplayX displayExt; + vpDisplayX displayInt; + vpDisplayX displayExt; #elif defined VISP_HAVE_GTK - vpDisplayGTK displayInt; - vpDisplayGTK displayExt; + vpDisplayGTK displayInt; + vpDisplayGTK displayExt; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt; - vpDisplayGDI displayExt; + vpDisplayGDI displayInt; + vpDisplayGDI displayExt; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV displayInt; + vpDisplayOpenCV displayExt; #endif - // open a display for the visualization + // open a display for the visualization - vpImage<unsigned char> Iint(300, 300, 0) ; - vpImage<unsigned char> Iext(300, 300, 0) ; + vpImage<unsigned char> Iint(300, 300, 0) ; + vpImage<unsigned char> Iext(300, 300, 0) ; - if (opt_display) { - displayInt.init(Iint,0,0, "Internal view") ; - displayExt.init(Iext,330,000, "External view") ; + if (opt_display) { + displayInt.init(Iint,0,0, "Internal view") ; + displayExt.init(Iext,330,000, "External view") ; - } - vpProjectionDisplay externalview ; - - double px, py ; px = py = 500 ; - double u0, v0 ; u0 = 150, v0 = 160 ; - - vpCameraParameters cam(px,py,u0,v0); - - int i ; - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" - << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.1,-0.1,1, - vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; - - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - vpHomogeneousMatrix cextMo(0,0,2, - 0,0,0) ;//vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; - - // sets the point coordinates in the object frame - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1,0) ; - point[1].setWorldCoordinates(0.1,-0.1,0) ; - point[2].setWorldCoordinates(0.1,0.1,0) ; - point[3].setWorldCoordinates(-0.1,0.1,0) ; - - for (i = 0 ; i < 4 ; i++) - externalview.insert(point[i]) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; - - // sets the desired position of the point - vpFeaturePoint p[4] ; - for (i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the feature point s* - vpFeaturePoint pd[4] ; - - pd[0].buildFrom(-0.1,-0.1, 1) ; - pd[1].buildFrom( 0.1,-0.1, 1) ; - pd[2].buildFrom( 0.1, 0.1, 1) ; - pd[3].buildFrom(-0.1, 0.1, 1) ; - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::MEAN) ; - - // Set the position of the camera in the end-effector frame ") ; - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - - // we want to see a point on a point - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - - // set the gain - task.setLambda(1) ; - - // Display task information " ) ; - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + } + vpProjectionDisplay externalview ; - // Set the Jacobian (expressed in the end-effector frame) - // since q is modified eJe is modified - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + double px, py ; px = py = 500 ; + double u0, v0 ; u0 = 150, v0 = 160 ; - // get the robot position + vpCameraParameters cam(px,py,u0,v0); + + int i ; + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" + << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.1,-0.1,1, + vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; + + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; + + vpHomogeneousMatrix cextMo(0,0,2, + 0,0,0) ;//vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; + + // sets the point coordinates in the object frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1,0) ; + point[1].setWorldCoordinates(0.1,-0.1,0) ; + point[2].setWorldCoordinates(0.1,0.1,0) ; + point[3].setWorldCoordinates(-0.1,0.1,0) ; - // update new point position and corresponding features for (i = 0 ; i < 4 ; i++) - { + externalview.insert(point[i]) ; + + // computes the point coordinates in the camera frame and its 2D coordinates + for (i = 0 ; i < 4 ; i++) point[i].track(cMo) ; - //retrieve x,y and Z of the vpPoint structure - vpFeatureBuilder::create(p[i],point[i]) ; - } - // since vpServo::MEAN interaction matrix is used, we need also to update the desired features at each iteration + + // sets the desired position of the point + vpFeaturePoint p[4] ; + for (i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure + + // sets the desired position of the feature point s* + vpFeaturePoint pd[4] ; + pd[0].buildFrom(-0.1,-0.1, 1) ; pd[1].buildFrom( 0.1,-0.1, 1) ; pd[2].buildFrom( 0.1, 0.1, 1) ; pd[3].buildFrom(-0.1, 0.1, 1) ; - if (opt_display) { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - vpServoDisplay::display(task,cam,Iint) ; - externalview.display(Iext,cextMo, cMo, cam, vpColor::green) ; - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); - } + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::MEAN) ; - // compute the control law - v = task.computeControlLaw() ; + // Set the position of the camera in the end-effector frame ") ; + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Set the Jacobian (expressed in the end-effector frame) + vpMatrix eJe ; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // we want to see a point on a point + for (i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; + + // set the gain + task.setLambda(1) ; - // Display task information - task.print() ; - task.kill(); + // Display task information " ) ; + task.print() ; - std::cout <<"Final robot position with respect to the object frame:\n"; - cMo.print(); + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // Set the Jacobian (expressed in the end-effector frame) + // since q is modified eJe is modified + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // update new point position and corresponding features + for (i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + //retrieve x,y and Z of the vpPoint structure + vpFeatureBuilder::create(p[i],point[i]) ; + } + // since vpServo::MEAN interaction matrix is used, we need also to update the desired features at each iteration + pd[0].buildFrom(-0.1,-0.1, 1) ; + pd[1].buildFrom( 0.1,-0.1, 1) ; + pd[2].buildFrom( 0.1, 0.1, 1) ; + pd[3].buildFrom(-0.1, 0.1, 1) ; + + if (opt_display) { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + vpServoDisplay::display(task,cam,Iint) ; + externalview.display(Iext,cextMo, cMo, cam, vpColor::green) ; + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + } + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - if (opt_display && opt_click_allowed) { - // suppressed for automate test - std::cout << "\n\nClick in the internal view window to end..." << std::endl; - vpDisplay::getClick(Iint) ; + // Display task information + task.print() ; + task.kill(); + + std::cout <<"Final robot position with respect to the object frame:\n"; + cMo.print(); + + if (opt_display && opt_click_allowed) { + // suppressed for automate test + std::cout << "\n\nClick in the internal view window to end..." << std::endl; + vpDisplay::getClick(Iint) ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } #else @@ -343,7 +356,7 @@ main(int argc, const char ** argv) int main() { - std::cout << "You do not have X11, GTK or GDI display functionalities..." << std::endl; + std::cout << "You do not have X11, GTK, GDI or OpenCV display functionalities..." << std::endl; } #endif diff --git a/example/robot-simulator/camera/servoSimuFourPoints2DPolarCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuFourPoints2DPolarCamVelocityDisplay.cpp index a9858be89f98df9c623170db3bfaba9711526661..8e9d1b68c223c6fcf49ed57ce274bbf84d2bbd74 100644 --- a/example/robot-simulator/camera/servoSimuFourPoints2DPolarCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuFourPoints2DPolarCamVelocityDisplay.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuFourPoints2DPolarCamVelocityDisplay.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -69,6 +69,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeaturePointPolar.h> #include <visp/vpHomogeneousMatrix.h> @@ -86,6 +87,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -135,9 +139,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -145,7 +149,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -154,7 +158,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -164,341 +168,350 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed camera velocities (m/s, rad/s) to achieve the task - // - the 6 mesured camera velocities (m/s, rad/s) - // - the 6 mesured joint positions (m, rad) - // - the 8 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; -#ifdef WIN32 - logdirname ="C:/temp/" + username; + try { + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed camera velocities (m/s, rad/s) to achieve the task + // - the 6 mesured camera velocities (m/s, rad/s) + // - the 6 mesured joint positions (m, rad) + // - the 8 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; +#if defined(_WIN32) + logdirname ="C:/temp/" + username; #else - logdirname ="/tmp/" + username; + logdirname ="/tmp/" + username; #endif - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; + std::string logfilename; + logfilename = logdirname + "/log.dat"; - // Open the log file name - std::ofstream flog(logfilename.c_str()); + // Open the log file name + std::ofstream flog(logfilename.c_str()); - bool opt_click_allowed = true; - bool opt_display = true; + bool opt_click_allowed = true; + bool opt_display = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // We open two displays, one for the internal camera view, the other one for - // the external view, using either X11, GTK or GDI. + // We open two displays, one for the internal camera view, the other one for + // the external view, using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX displayInt; - vpDisplayX displayExt; + vpDisplayX displayInt; + vpDisplayX displayExt; #elif defined VISP_HAVE_GTK - vpDisplayGTK displayInt; - vpDisplayGTK displayExt; + vpDisplayGTK displayInt; + vpDisplayGTK displayExt; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt; - vpDisplayGDI displayExt; + vpDisplayGDI displayInt; + vpDisplayGDI displayExt; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV displayInt; + vpDisplayOpenCV displayExt; #endif - // open a display for the visualization + // open a display for the visualization - vpImage<unsigned char> Iint(300, 300, 0) ; - vpImage<unsigned char> Iext(300, 300, 0) ; + vpImage<unsigned char> Iint(300, 300, 0) ; + vpImage<unsigned char> Iext(300, 300, 0) ; - if (opt_display) { - displayInt.init(Iint,0,0, "Internal view") ; - displayExt.init(Iext,330,000, "External view") ; + if (opt_display) { + displayInt.init(Iint,0,0, "Internal view") ; + displayExt.init(Iext,330,000, "External view") ; - } - vpProjectionDisplay externalview ; + } + vpProjectionDisplay externalview ; - double px, py ; px = py = 500 ; - double u0, v0 ; u0 = 150, v0 = 160 ; + double px, py ; px = py = 500 ; + double u0, v0 ; u0 = 150, v0 = 160 ; - vpCameraParameters cam(px,py,u0,v0); + vpCameraParameters cam(px,py,u0,v0); - int i ; - vpServo task ; - vpSimulatorCamera robot ; + int i ; + vpServo task ; + vpSimulatorCamera robot ; - std::cout << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" - << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << std::endl ; + std::cout << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" + << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << std::endl ; - // #define TRANS_Z_PURE - // #define TRANS_X_PURE - // #define ROT_Z_PURE - // #define ROT_X_PURE + // #define TRANS_Z_PURE + // #define TRANS_X_PURE + // #define ROT_Z_PURE + // #define ROT_X_PURE #define COMPLEX - //#define PROBLEM + //#define PROBLEM #if defined(TRANS_Z_PURE) - // sets the initial camera location - vpHomogeneousMatrix cMo(0, 0, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0, 0, 2, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0, 0, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0, 0, 2, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); #elif defined(TRANS_X_PURE) - // sets the initial camera location - vpHomogeneousMatrix cMo(0.3, 0.3, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0.5, 0.3, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0.3, 0.3, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0.5, 0.3, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); #elif defined(ROT_Z_PURE) - // sets the initial camera location - vpHomogeneousMatrix cMo(0, 0, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0, 0, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(180)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0, 0, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0, 0, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(180)); #elif defined(ROT_X_PURE) - // sets the initial camera location - vpHomogeneousMatrix cMo(0, 0, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0, 0, 3, - vpMath::rad(45), vpMath::rad(0), vpMath::rad(0)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0, 0, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0, 0, 3, + vpMath::rad(45), vpMath::rad(0), vpMath::rad(0)); #elif defined(COMPLEX) - // sets the initial camera location - vpHomogeneousMatrix cMo(0.2, 0.2, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0, 0, 2.5, - vpMath::rad(45), vpMath::rad(10), vpMath::rad(30)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0.2, 0.2, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0, 0, 2.5, + vpMath::rad(45), vpMath::rad(10), vpMath::rad(30)); #elif defined(PROBLEM) - // Bad behavior with an interaction matrix computed from the desired features - // sets the initial camera location - vpHomogeneousMatrix cMo(0.2, 0.2, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - // sets the desired camera location - vpHomogeneousMatrix cMod(0.4, 0.2, 3, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // Bad behavior with an interaction matrix computed from the desired features + // sets the initial camera location + vpHomogeneousMatrix cMo(0.2, 0.2, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the desired camera location + vpHomogeneousMatrix cMod(0.4, 0.2, 3, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); #endif - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - vpHomogeneousMatrix cextMo(0,0,6, - vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; - - - // sets the point coordinates in the object frame - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.25,-0.25,0) ; - point[1].setWorldCoordinates(0.25,-0.25,0) ; - point[2].setWorldCoordinates(0.25,0.25,0) ; - point[3].setWorldCoordinates(-0.25,0.25,0) ; - - - for (i = 0 ; i < 4 ; i++) - externalview.insert(point[i]) ; - - // sets the desired position of the feature point s*" - vpFeaturePointPolar pd[4] ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; - // computes the point coordinates in the desired camera frame and - // its 2D coordinates - for (i = 0 ; i < 4 ; i++) { - point[i].track(cMod); - // Computes the polar coordinates from the image point - // cartesian coordinates - vpFeatureBuilder::create(pd[i],point[i]); - } + vpHomogeneousMatrix cextMo(0,0,6, + vpMath::rad(40), vpMath::rad(10), vpMath::rad(60)) ; - // computes the point coordinates in the camera frame and its 2D - // coordinates - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; + // sets the point coordinates in the object frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.25,-0.25,0) ; + point[1].setWorldCoordinates(0.25,-0.25,0) ; + point[2].setWorldCoordinates(0.25,0.25,0) ; + point[3].setWorldCoordinates(-0.25,0.25,0) ; - // sets the desired position of the point - vpFeaturePointPolar p[4] ; - for (i = 0 ; i < 4 ; i++) { - // retrieve x,y and Z of the vpPoint structure to initialize the - // visual feature - vpFeatureBuilder::create(p[i], point[i]); - } - // Define the task; - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - // task.setInteractionMatrixType(vpServo::MEAN) ; - // task.setInteractionMatrixType(vpServo::DESIRED) ; - task.setInteractionMatrixType(vpServo::CURRENT) ; + for (i = 0 ; i < 4 ; i++) + externalview.insert(point[i]) ; + // sets the desired position of the feature point s*" + vpFeaturePointPolar pd[4] ; - // Set the position of the camera in the end-effector frame - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; + // computes the point coordinates in the desired camera frame and + // its 2D coordinates + for (i = 0 ; i < 4 ; i++) { + point[i].track(cMod); + // Computes the polar coordinates from the image point + // cartesian coordinates + vpFeatureBuilder::create(pd[i],point[i]); + } - // we want to see a point on a point - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - // set the gain - task.setLambda(1) ; + // computes the point coordinates in the camera frame and its 2D + // coordinates + for (i = 0 ; i < 4 ; i++) + point[i].track(cMo) ; + // sets the desired position of the point + vpFeaturePointPolar p[4] ; + for (i = 0 ; i < 4 ; i++) { + // retrieve x,y and Z of the vpPoint structure to initialize the + // visual feature + vpFeatureBuilder::create(p[i], point[i]); + } - std::cout << "\nDisplay task information: " << std::endl; - task.print() ; + // Define the task; + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + // task.setInteractionMatrixType(vpServo::MEAN) ; + // task.setInteractionMatrixType(vpServo::DESIRED) ; + task.setInteractionMatrixType(vpServo::CURRENT) ; - unsigned int iter=0 ; - // loop - while(iter++ < 200) { - std::cout << "---------------------------------------------" - << iter <<std::endl ; - vpColVector v ; + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; // Set the Jacobian (expressed in the end-effector frame) - // Since q is modified eJe is modified + vpMatrix eJe ; robot.get_eJe(eJe) ; task.set_eJe(eJe) ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; - - // Compute new point position - for (i = 0 ; i < 4 ; i++) { - point[i].track(cMo) ; - // retrieve x,y and Z of the vpPoint structure to compute the feature - vpFeatureBuilder::create(p[i],point[i]) ; - } - - if (opt_display) { - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; + // we want to see a point on a point + for (i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; + + // set the gain + task.setLambda(1) ; + + + std::cout << "\nDisplay task information: " << std::endl; + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++ < 200) { + std::cout << "---------------------------------------------" + << iter <<std::endl ; + vpColVector v ; + + + // Set the Jacobian (expressed in the end-effector frame) + // Since q is modified eJe is modified + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // Compute new point position + for (i = 0 ; i < 4 ; i++) { + point[i].track(cMo) ; + // retrieve x,y and Z of the vpPoint structure to compute the feature + vpFeatureBuilder::create(p[i],point[i]) ; + } + + if (opt_display) { + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + + vpServoDisplay::display(task,cam,Iint) ; + externalview.display(Iext,cextMo, cMo, cam, vpColor::green); + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + } + + // Compute the control law + v = task.computeControlLaw() ; + + if (iter==1) { + std::cout << "Display task information: " << std::endl; + task.print() ; + } + + task.print(vpServo::FEATURE_CURRENT); + task.print(vpServo::FEATURE_DESIRED); + + // Send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + // Save velocities applied to the robot in the log file + // v[0], v[1], v[2] correspond to camera translation velocities in m/s + // v[3], v[4], v[5] correspond to camera rotation velocities in rad/s + flog << v[0] << " " << v[1] << " " << v[2] << " " + << v[3] << " " << v[4] << " " << v[5] << " "; + + std::cout << "v: " << v.t() << std::endl; + + std::cout << "|| s - s* || = "<< ( task.getError() ).sumSquare() << std::endl; + + // Save feature error (s-s*) for the 4 feature points. For each feature + // point, we have 2 errors (along x and y axis). This error is expressed + // in meters in the camera frame + flog << ( task.getError() ).t() << " ";// s-s* for point 4 + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + + // Save current visual feature s = (rho,theta) + for (i = 0 ; i < 4 ; i++) { + flog << p[i].get_rho() << " " << p[i].get_theta() << " "; + } + // Save current position of the points + for (i = 0 ; i < 4 ; i++) { + flog << point[i].get_x() << " " << point[i].get_y() << " "; + } + flog << std::endl; + + if (iter == 1) { + vpImagePoint ip; + ip.set_i( 10 ); + ip.set_j( 10 ); + + std::cout << "\nClick in the internal camera view to continue..." << std::endl; + vpDisplay::displayText(Iint, ip, + "A click to continue...",vpColor::red); + vpDisplay::flush(Iint); + vpDisplay::getClick(Iint); + } - vpServoDisplay::display(task,cam,Iint) ; - externalview.display(Iext,cextMo, cMo, cam, vpColor::green); - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); } - // Compute the control law - v = task.computeControlLaw() ; - - if (iter==1) { - std::cout << "Display task information: " << std::endl; - task.print() ; - } - task.print(vpServo::FEATURE_CURRENT); - task.print(vpServo::FEATURE_DESIRED); + flog.close() ; // Close the log file - // Send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - // Save velocities applied to the robot in the log file - // v[0], v[1], v[2] correspond to camera translation velocities in m/s - // v[3], v[4], v[5] correspond to camera rotation velocities in rad/s - flog << v[0] << " " << v[1] << " " << v[2] << " " - << v[3] << " " << v[4] << " " << v[5] << " "; + // Display task information + task.print() ; - std::cout << "v: " << v.t() << std::endl; + // Kill the task + task.kill(); - std::cout << "|| s - s* || = "<< ( task.getError() ).sumSquare() << std::endl; + std::cout <<"Final robot position with respect to the object frame:\n"; + cMo.print(); - // Save feature error (s-s*) for the 4 feature points. For each feature - // point, we have 2 errors (along x and y axis). This error is expressed - // in meters in the camera frame - flog << ( task.getError() ).t() << " ";// s-s* for point 4 - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - - // Save current visual feature s = (rho,theta) - for (i = 0 ; i < 4 ; i++) { - flog << p[i].get_rho() << " " << p[i].get_theta() << " "; + if (opt_display && opt_click_allowed) { + // suppressed for automate test + std::cout << "\n\nClick in the internal view to end..." << std::endl; + vpDisplay::getClick(Iint) ; } - // Save current position of the points - for (i = 0 ; i < 4 ; i++) { - flog << point[i].get_x() << " " << point[i].get_y() << " "; - } - flog << std::endl; - - if (iter == 1) { - vpImagePoint ip; - ip.set_i( 10 ); - ip.set_j( 10 ); - - std::cout << "\nClick in the internal camera view to continue..." << std::endl; - vpDisplay::displayCharString(Iint, ip, - "A click to continue...",vpColor::red); - vpDisplay::flush(Iint); - vpDisplay::getClick(Iint); - } - + return 0; } - - - flog.close() ; // Close the log file - - // Display task information - task.print() ; - - // Kill the task - task.kill(); - - std::cout <<"Final robot position with respect to the object frame:\n"; - cMo.print(); - - if (opt_display && opt_click_allowed) { - // suppressed for automate test - std::cout << "\n\nClick in the internal view to end..." << std::endl; - vpDisplay::getClick(Iint) ; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + std::cout << "You do not have X11, GTK, GDI or OpenCV display functionalities..." << std::endl; } #endif diff --git a/example/robot-simulator/camera/servoSimuLine2DCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuLine2DCamVelocityDisplay.cpp index 888ca654497996e61590a7f8702826b77f46185b..54c535143d04392ff71c4d160b5414be7ccb60ee 100644 --- a/example/robot-simulator/camera/servoSimuLine2DCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuLine2DCamVelocityDisplay.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuLine2DCamVelocityDisplay.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,7 +53,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -62,6 +62,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeatureLine.h> #include <visp/vpHomogeneousMatrix.h> @@ -76,6 +77,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -126,9 +130,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -136,7 +140,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -145,7 +149,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -156,165 +160,174 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click_allowed = true; + try { + bool opt_display = true; + bool opt_click_allowed = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - vpImage<unsigned char> I(512,512,0) ; + vpImage<unsigned char> I(512,512,0) ; - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display.init(I, 100, 100,"Camera view...") ; - // Display the image - // The image class has a member that specify a pointer toward - // the display that has been initialized in the display declaration - // therefore is is no longuer necessary to make a reference to the - // display variable. - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display.init(I, 100, 100,"Camera view...") ; + // Display the image + // The image class has a member that specify a pointer toward + // the display that has been initialized in the display declaration + // therefore is is no longuer necessary to make a reference to the + // display variable. + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - double px, py ; px = py = 600 ; - double u0, v0 ; u0 = v0 = 256 ; - - vpCameraParameters cam(px,py,u0,v0); - - vpServo task ; - vpSimulatorCamera robot ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.2,0.1,1, - vpMath::rad(5), vpMath::rad(5), vpMath::rad(90)); - - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the final camera location (for simulation purpose) - vpHomogeneousMatrix cMod(0,0,1, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - - // sets the line coordinates (2 planes) in the world frame - vpColVector plane1(4) ; - vpColVector plane2(4) ; - plane1[0] = 0; // z = 0 - plane1[1] = 0; - plane1[2] = 1; - plane1[3] = 0; - plane2[0] = 0; // y =0 - plane2[1] = 1; - plane2[2] = 0; - plane2[3] = 0; - - vpLine line ; - line.setWorldCoordinates(plane1, plane2) ; - - // sets the desired position of the visual feature - line.track(cMod) ; - line.print() ; - - vpFeatureLine ld ; - vpFeatureBuilder::create(ld,line) ; - - // computes the line coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - line.track(cMo) ; - line.print() ; - - vpFeatureLine l ; - vpFeatureBuilder::create(l,line) ; - l.print() ; - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - - // we want to see a line on a line - - task.addFeature(l,ld) ; - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; - - // set the gain - task.setLambda(1) ; - // Display task information " ) ; - task.print() ; - - if (opt_display && opt_click_allowed) { - std::cout << "\n\nClick in the camera view window to start..." << std::endl; - vpDisplay::getClick(I) ; - } + double px, py ; px = py = 600 ; + double u0, v0 ; u0 = v0 = 256 ; - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + vpCameraParameters cam(px,py,u0,v0); - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + vpServo task ; + vpSimulatorCamera robot ; - // new line position + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.2,0.1,1, + vpMath::rad(5), vpMath::rad(5), vpMath::rad(90)); + + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; + + // sets the final camera location (for simulation purpose) + vpHomogeneousMatrix cMod(0,0,1, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + + // sets the line coordinates (2 planes) in the world frame + vpColVector plane1(4) ; + vpColVector plane2(4) ; + plane1[0] = 0; // z = 0 + plane1[1] = 0; + plane1[2] = 1; + plane1[3] = 0; + plane2[0] = 0; // y =0 + plane2[1] = 1; + plane2[2] = 0; + plane2[3] = 0; + + vpLine line ; + line.setWorldCoordinates(plane1, plane2) ; + + // sets the desired position of the visual feature + line.track(cMod) ; + line.print() ; + + vpFeatureLine ld ; + vpFeatureBuilder::create(ld,line) ; + + // computes the line coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature line.track(cMo) ; - // retrieve x,y and Z of the vpLine structure - vpFeatureBuilder::create(l,line); + line.print() ; - if (opt_display) { - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; + vpFeatureLine l ; + vpFeatureBuilder::create(l,line) ; + l.print() ; + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + + // we want to see a line on a line + + task.addFeature(l,ld) ; + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + + // set the gain + task.setLambda(1) ; + // Display task information " ) ; + task.print() ; + + if (opt_display && opt_click_allowed) { + std::cout << "\n\nClick in the camera view window to start..." << std::endl; + vpDisplay::getClick(I) ; } - // compute the control law - v = task.computeControlLaw() ; + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // new line position + line.track(cMo) ; + // retrieve x,y and Z of the vpLine structure + vpFeatureBuilder::create(l,line); - if (opt_display && opt_click_allowed) { - std::cout << "\nClick in the camera view window to end..." << std::endl; - vpDisplay::getClick(I) ; - } + if (opt_display) { + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + } + + // compute the control law + v = task.computeControlLaw() ; - // Display task information - task.print() ; - task.kill(); + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + if (opt_display && opt_click_allowed) { + std::cout << "\nClick in the camera view window to end..." << std::endl; + vpDisplay::getClick(I) ; + } + + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + std::cout << "You do not have X11, GTK, GDI or OpenCV display functionalities..." << std::endl; } #endif diff --git a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity1.cpp b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity1.cpp index d28e601b38e6e82f6db951942664db8b4962e32e..d1066f2187055d93225352422638de56c233066e 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity1.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity1.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuPoint2DCamVelocity1.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -103,15 +106,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -120,7 +123,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -130,83 +133,90 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpServo task ; - vpSimulatorCamera robot ; + vpServo task ; + vpSimulatorCamera robot ; - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; - // sets the point coordinates in the world frame - vpPoint point ; - point.setWorldCoordinates(0,0,0) ; + // sets the point coordinates in the world frame + vpPoint point ; + point.setWorldCoordinates(0,0,0) ; - // computes the point coordinates in the camera frame and its 2D coordinates - point.track(cMo) ; + // computes the point coordinates in the camera frame and its 2D coordinates + point.track(cMo) ; - // sets the current position of the visual feature - vpFeaturePoint p ; - vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure + // sets the current position of the visual feature + vpFeaturePoint p ; + vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure - // sets the desired position of the visual feature - vpFeaturePoint pd ; - pd.buildFrom(0,0,1) ; // buildFrom(x,y,Z) ; + // sets the desired position of the visual feature + vpFeaturePoint pd ; + pd.buildFrom(0,0,1) ; // buildFrom(x,y,Z) ; - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; - // we want to see a point on a point - std::cout << std::endl ; - task.addFeature(p,pd) ; + // we want to see a point on a point + std::cout << std::endl ; + task.addFeature(p,pd) ; - // set the gain - task.setLambda(1) ; + // set the gain + task.setLambda(1) ; - // Display task information - task.print() ; + // Display task information + task.print() ; - unsigned int iter=0 ; - // loop - while(iter++<100) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + unsigned int iter=0 ; + // loop + while(iter++<100) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - // new point position - point.track(cMo) ; - //retrieve x,y and Z of the vpPoint structure - vpFeatureBuilder::create(p,point); + // new point position + point.track(cMo) ; + //retrieve x,y and Z of the vpPoint structure + vpFeatureBuilder::create(p,point); - // compute the control law - v = task.computeControlLaw() ; + // compute the control law + v = task.computeControlLaw() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - // Display task information - task.print() ; - task.kill(); + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity2.cpp b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity2.cpp index b483fd947ffe4aa6488e766f38b9b41378287bd0..1524bb64b39de1555200bfa6d552b7038e6f741b 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity2.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity2.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuPoint2DCamVelocity2.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,6 +70,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -111,15 +114,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -128,7 +131,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -138,106 +141,113 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + + vpServo task ; + vpSimulatorCamera robot ; + + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a point " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; + + // sets the point coordinates in the world frame + vpPoint point ; + point.setWorldCoordinates(0,0,0) ; + + // computes the point coordinates in the camera frame and its 2D coordinates + point.track(cMo) ; + + // sets the current position of the visual feature + vpFeaturePoint p ; + vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure + + // sets the desired position of the visual feature + vpFeaturePoint pd ; + pd.buildFrom(0,0,1) ; + + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::MEAN) ; - vpServo task ; - vpSimulatorCamera robot ; - - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a point " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the point coordinates in the world frame - vpPoint point ; - point.setWorldCoordinates(0,0,0) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - point.track(cMo) ; - - // sets the current position of the visual feature - vpFeaturePoint p ; - vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the visual feature - vpFeaturePoint pd ; - pd.buildFrom(0,0,1) ; - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::MEAN) ; - - // Set the position of the camera in the end-effector frame - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - - // we want to see a point on a point - task.addFeature(p,pd) ; - - // set the gain - task.setLambda(1) ; - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++<100) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; // Set the Jacobian (expressed in the end-effector frame) - // since q is modified eJe is modified + vpMatrix eJe ; robot.get_eJe(eJe) ; task.set_eJe(eJe) ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // we want to see a point on a point + task.addFeature(p,pd) ; - // new point position - point.track(cMo) ; - vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure - pd.buildFrom(0,0,1) ; // Since vpServo::MEAN interaction matrix is used, we need to update the desired feature at each iteration + // set the gain + task.setLambda(1) ; + // Display task information + task.print() ; - // compute the control law - v = task.computeControlLaw() ; + unsigned int iter=0 ; + // loop + while(iter++<100) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // Set the Jacobian (expressed in the end-effector frame) + // since q is modified eJe is modified + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new point position + point.track(cMo) ; + vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure + pd.buildFrom(0,0,1) ; // Since vpServo::MEAN interaction matrix is used, we need to update the desired feature at each iteration + + // compute the control law + v = task.computeControlLaw() ; - // Display task information - task.print() ; - task.kill(); + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity3.cpp b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity3.cpp index 9808dfd8f634bfb6e2d226ddd45fb8edbfad453f..5806ace9e67078454b9176477f534cea95d8907c 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DCamVelocity3.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DCamVelocity3.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuPoint2DCamVelocity3.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -111,15 +114,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -128,7 +131,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -138,104 +141,111 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a point " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; + + // sets the point coordinates in the world frame + vpPoint point ; + point.setWorldCoordinates(0,0,0) ; + + // computes the point coordinates in the camera frame and its 2D coordinates + point.track(cMo) ; + + // sets the current position of the visual feature + vpFeaturePoint p ; + vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure + + // sets the desired position of the visual feature + vpFeaturePoint pd ; + pd.buildFrom(0,0,1) ; // buildFrom(x,y,Z) ; + + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a point " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the point coordinates in the world frame - vpPoint point ; - point.setWorldCoordinates(0,0,0) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - point.track(cMo) ; - - // sets the current position of the visual feature - vpFeaturePoint p ; - vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the visual feature - vpFeaturePoint pd ; - pd.buildFrom(0,0,1) ; // buildFrom(x,y,Z) ; - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - - // Set the position of the camera in the end-effector frame - vpHomogeneousMatrix cMe ; - vpVelocityTwistMatrix cVe(cMe) ; - task.set_cVe(cVe) ; - - // Set the Jacobian (expressed in the end-effector frame) - vpMatrix eJe ; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - - // we want to see a point on a point - task.addFeature(p,pd, vpFeaturePoint::selectX()) ; - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++ < 100) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + // Set the position of the camera in the end-effector frame + vpHomogeneousMatrix cMe ; + vpVelocityTwistMatrix cVe(cMe) ; + task.set_cVe(cVe) ; // Set the Jacobian (expressed in the end-effector frame) - // since q is modified eJe is modified + vpMatrix eJe ; robot.get_eJe(eJe) ; task.set_eJe(eJe) ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // we want to see a point on a point + task.addFeature(p,pd, vpFeaturePoint::selectX()) ; - // new point position - point.track(cMo) ; - vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure + // set the gain + task.setLambda(1) ; - // compute the control law - v = task.computeControlLaw() ; + // Display task information + task.print() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + unsigned int iter=0 ; + // loop + while(iter++ < 100) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // Set the Jacobian (expressed in the end-effector frame) + // since q is modified eJe is modified + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new point position + point.track(cMo) ; + vpFeatureBuilder::create(p,point) ; //retrieve x,y and Z of the vpPoint structure - // Display task information - task.print() ; - task.kill(); + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity1.cpp b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity1.cpp index 3bb573ea15a79a0b816b3057186fe1698fa6601b..2788691fe5c72e83d873bef8991f96abe34e2c0e 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity1.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity1.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuPoint2DhalfCamVelocity1.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -112,15 +115,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -129,7 +132,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -139,124 +142,131 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + + vpServo task ; + vpSimulatorCamera robot ; - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " task : 2 1/2 D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpPoseVector c_r_o(0.1,0.2,2, - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) - ) ; - - vpHomogeneousMatrix cMo(c_r_o) ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the desired camera location - vpPoseVector cd_r_o(0,0,1, - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - vpHomogeneousMatrix cdMo(cd_r_o) ; - - // sets the point coordinates in the world frame - vpPoint point ; - point.setWorldCoordinates(0,0,0) ; - // computes the point coordinates in the camera frame and its 2D coordinates - point.track(cMo) ; - - vpPoint pointd ; - pointd.setWorldCoordinates(0,0,0) ; - pointd.track(cdMo) ; - //------------------------------------------------------------------ - // 1st feature (x,y) - // want to it at (0,0) - vpFeaturePoint p ; - vpFeatureBuilder::create(p,point) ; - - vpFeaturePoint pd ; - vpFeatureBuilder::create(pd,pointd) ; - - //------------------------------------------------------------------ - // 2nd feature (Z) - // not necessary to project twice (reuse p) - vpFeaturePoint3D Z ; - vpFeatureBuilder::create(Z,point) ; //retrieve x,y and Z of the vpPoint structure - - // want to see it one meter away (here again use pd) - vpFeaturePoint3D Zd ; - vpFeatureBuilder::create(Zd,pointd) ; //retrieve x,y and Z of the vpPoint structure - - //------------------------------------------------------------------ - // 3rd feature ThetaU - // compute the rotation that the camera has to achieve - vpHomogeneousMatrix cdMc ; - cdMc = cdMo*cMo.inverse() ; - - vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; - tu.buildFrom(cdMc) ; - - // sets the desired rotation (always zero !) - // since s is the rotation that the camera has to achieve - - //------------------------------------------------------------------ - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - - task.addFeature(p,pd) ; - task.addFeature(Z,Zd,vpFeaturePoint3D::selectZ()) ; - task.addFeature(tu) ; - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " task : 2 1/2 D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpPoseVector c_r_o(0.1,0.2,2, + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) + ) ; + + vpHomogeneousMatrix cMo(c_r_o) ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; + + // sets the desired camera location + vpPoseVector cd_r_o(0,0,1, + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + vpHomogeneousMatrix cdMo(cd_r_o) ; - // update the feature + // sets the point coordinates in the world frame + vpPoint point ; + point.setWorldCoordinates(0,0,0) ; + // computes the point coordinates in the camera frame and its 2D coordinates point.track(cMo) ; + + vpPoint pointd ; + pointd.setWorldCoordinates(0,0,0) ; + pointd.track(cdMo) ; + //------------------------------------------------------------------ + // 1st feature (x,y) + // want to it at (0,0) + vpFeaturePoint p ; vpFeatureBuilder::create(p,point) ; - vpFeatureBuilder::create(Z,point) ; + vpFeaturePoint pd ; + vpFeatureBuilder::create(pd,pointd) ; + + //------------------------------------------------------------------ + // 2nd feature (Z) + // not necessary to project twice (reuse p) + vpFeaturePoint3D Z ; + vpFeatureBuilder::create(Z,point) ; //retrieve x,y and Z of the vpPoint structure + + // want to see it one meter away (here again use pd) + vpFeaturePoint3D Zd ; + vpFeatureBuilder::create(Zd,pointd) ; //retrieve x,y and Z of the vpPoint structure + + //------------------------------------------------------------------ + // 3rd feature ThetaU + // compute the rotation that the camera has to achieve + vpHomogeneousMatrix cdMc ; cdMc = cdMo*cMo.inverse() ; + + vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; tu.buildFrom(cdMc) ; - // compute the control law - v = task.computeControlLaw() ; - // send the camera velocity to the controller ") ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // sets the desired rotation (always zero !) + // since s is the rotation that the camera has to achieve - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + //------------------------------------------------------------------ + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; - // Display task information - task.print() ; - task.kill(); - std::cout << "Final camera location:\n " << cMo << std::endl ; + task.addFeature(p,pd) ; + task.addFeature(Z,Zd,vpFeaturePoint3D::selectZ()) ; + task.addFeature(tu) ; + + // set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // update the feature + point.track(cMo) ; + vpFeatureBuilder::create(p,point) ; + vpFeatureBuilder::create(Z,point) ; + + cdMc = cdMo*cMo.inverse() ; + tu.buildFrom(cdMc) ; + + // compute the control law + v = task.computeControlLaw() ; + // send the camera velocity to the controller ") ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + // Display task information + task.print() ; + task.kill(); + std::cout << "Final camera location:\n " << cMo << std::endl ; + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity2.cpp b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity2.cpp index e8308cc8eeae7f86c05f26199f32c33656d26156..5e98f980fe1973c343eea69b8f4465e464d73a2c 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity2.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity2.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuPoint2DhalfCamVelocity2.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -109,15 +112,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -126,7 +129,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -136,221 +139,228 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " simulation of a 2 1/2 D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // In this example we will simulate a visual servoing task. - // In simulation, we have to define the scene frane Ro and the - // camera frame Rc. - // The camera location is given by an homogenous matrix cMo that - // describes the position of the camera in the scene frame. - - vpServo task ; - - // sets the initial camera location - // we give the camera location as a size 6 vector (3 translations in meter - // and 3 rotation (theta U representation) - vpPoseVector c_r_o(0.1,0.2,2, - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) - ) ; - - // this pose vector is then transformed in a 4x4 homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // We define a robot - // The vpSimulatorCamera implements a simple moving that is juste defined - // by its location cMo - vpSimulatorCamera robot ; - - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // Now that the current camera position has been defined, - // let us defined the defined camera location. - // It is defined by cdMo - // sets the desired camera location - vpPoseVector cd_r_o(0,0,1, - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - vpHomogeneousMatrix cdMo(cd_r_o) ; - - - //---------------------------------------------------------------------- - // A 2 1/2 D visual servoing can be defined by - // - the position of a point x,y - // - the difference between this point depth and a desire depth - // modeled by log Z/Zd to be regulated to 0 - // - the rotation that the camera has to realized cdMc - - // Let us now defined the current value of these features - - - // since we simulate we have to define a 3D point that will - // forward-projected to define the current position x,y of the - // reference point - - //------------------------------------------------------------------ - // First feature (x,y) - //------------------------------------------------------------------ - // Let oP be this ... point, - // a vpPoint class has three main member - // .oP : 3D coordinates in scene frame - // .cP : 3D coordinates in camera frame - // .p : 2D - - //------------------------------------------------------------------ - // sets the point coordinates in the world frame - vpPoint point ; - // defined point coordinates in the scene frame : oP - point.setWorldCoordinates(0,0,0) ; - // computes the point coordinates in the camera frame and its - // 2D coordinates cP and then p - // computes the point coordinates in the camera frame and its 2D coordinates" ) ; - point.track(cMo) ; - - // We also defined (again by forward projection) the desired position - // of this point according to the desired camera position - vpPoint pointd ; - pointd.setWorldCoordinates(0,0,0) ; - pointd.track(cdMo) ; - - // Nevertheless, a vpPoint is not a feature, this is just a "tracker" - // from which the feature are built - // a feature is juste defined by a vector s, a way to compute the - // interaction matrix and the error, and if required a (or a vector of) - // 3D information - - // for a point (x,y) Visp implements the vpFeaturePoint class. - // we no defined a feature for x,y (and for (x*,y*)) - vpFeaturePoint p,pd ; - - // and we initialized the vector s=(x,y) of p from the tracker P - // Z coordinates in p is also initialized, it will be used to compute - // the interaction matrix - vpFeatureBuilder::create(p,point) ; - vpFeatureBuilder::create(pd,pointd) ; - - //------------------------------------------------------------------ - // Second feature log (Z/Zd) - // not necessary to project twice (reuse p) - - // This case in intersting since this visual feature has not - // been predefined in VisP - // In such case we have a generic feature class vpGenericFeature - // We will have to defined - // the vector s : .set_s(...) - // the interaction matrix Ls : .setInteractionMatrix(...) - - // log(Z/Zd) is then a size 1 vector logZ - vpGenericFeature logZ(1) ; - // initialized to s = log(Z/Zd) - // Let us note that here we use the point P and Pd, it's not necessary - // to forward project twice (it's already done) - logZ.set_s(log(point.get_Z()/pointd.get_Z())) ; - - // This visual has to be regulated to zero - - //------------------------------------------------------------------ - // 3rd feature ThetaU - // The thetaU feature is defined, tu represents the rotation that the camera - // has to realized. - // the complete displacement is then defined by: - //------------------------------------------------------------------ - vpHomogeneousMatrix cdMc ; - // compute the rotation that the camera has to achieve - cdMc = cdMo*cMo.inverse() ; - - // from this displacement, we extract the rotation cdRc represented by - // the angle theta and the rotation axis u - vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; - tu.buildFrom(cdMc) ; - // This visual has to be regulated to zero - - // sets the desired rotation (always zero !) - // since s is the rotation that the camera has to realize - - //------------------------------------------------------------------ - // Let us now the task itself - //------------------------------------------------------------------ - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - // we choose to control the robot in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - // Interaction matrix is computed with the current value of s - task.setInteractionMatrixType(vpServo::CURRENT) ; - - // we build the task by "stacking" the visual feature - // previously defined - task.addFeature(p,pd) ; - task.addFeature(logZ) ; - task.addFeature(tu) ; - // addFeature(X,Xd) means X should be regulated to Xd - // addFeature(X) means that X should be regulated to 0 - // some features such as vpFeatureThetaU MUST be regulated to zero - // (otherwise, it will results in an error at exectution level) - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - //------------------------------------------------------------------ - // An now the closed loop - - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // update the feature + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " simulation of a 2 1/2 D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // In this example we will simulate a visual servoing task. + // In simulation, we have to define the scene frane Ro and the + // camera frame Rc. + // The camera location is given by an homogenous matrix cMo that + // describes the position of the camera in the scene frame. + + vpServo task ; + + // sets the initial camera location + // we give the camera location as a size 6 vector (3 translations in meter + // and 3 rotation (theta U representation) + vpPoseVector c_r_o(0.1,0.2,2, + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) + ) ; + + // this pose vector is then transformed in a 4x4 homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // We define a robot + // The vpSimulatorCamera implements a simple moving that is juste defined + // by its location cMo + vpSimulatorCamera robot ; + + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; + + // Now that the current camera position has been defined, + // let us defined the defined camera location. + // It is defined by cdMo + // sets the desired camera location + vpPoseVector cd_r_o(0,0,1, + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + vpHomogeneousMatrix cdMo(cd_r_o) ; + + + //---------------------------------------------------------------------- + // A 2 1/2 D visual servoing can be defined by + // - the position of a point x,y + // - the difference between this point depth and a desire depth + // modeled by log Z/Zd to be regulated to 0 + // - the rotation that the camera has to realized cdMc + + // Let us now defined the current value of these features + + + // since we simulate we have to define a 3D point that will + // forward-projected to define the current position x,y of the + // reference point + + //------------------------------------------------------------------ + // First feature (x,y) + //------------------------------------------------------------------ + // Let oP be this ... point, + // a vpPoint class has three main member + // .oP : 3D coordinates in scene frame + // .cP : 3D coordinates in camera frame + // .p : 2D + + //------------------------------------------------------------------ + // sets the point coordinates in the world frame + vpPoint point ; + // defined point coordinates in the scene frame : oP + point.setWorldCoordinates(0,0,0) ; + // computes the point coordinates in the camera frame and its + // 2D coordinates cP and then p + // computes the point coordinates in the camera frame and its 2D coordinates" ) ; point.track(cMo) ; - vpFeatureBuilder::create(p,point) ; - cdMc = cdMo*cMo.inverse() ; - tu.buildFrom(cdMc) ; - - // there is no feature for logZ, we explicitely build - // the related interaction matrix") ; + // We also defined (again by forward projection) the desired position + // of this point according to the desired camera position + vpPoint pointd ; + pointd.setWorldCoordinates(0,0,0) ; + pointd.track(cdMo) ; + + // Nevertheless, a vpPoint is not a feature, this is just a "tracker" + // from which the feature are built + // a feature is juste defined by a vector s, a way to compute the + // interaction matrix and the error, and if required a (or a vector of) + // 3D information + + // for a point (x,y) Visp implements the vpFeaturePoint class. + // we no defined a feature for x,y (and for (x*,y*)) + vpFeaturePoint p,pd ; + + // and we initialized the vector s=(x,y) of p from the tracker P + // Z coordinates in p is also initialized, it will be used to compute + // the interaction matrix + vpFeatureBuilder::create(p,point) ; + vpFeatureBuilder::create(pd,pointd) ; + + //------------------------------------------------------------------ + // Second feature log (Z/Zd) + // not necessary to project twice (reuse p) + + // This case in intersting since this visual feature has not + // been predefined in VisP + // In such case we have a generic feature class vpGenericFeature + // We will have to defined + // the vector s : .set_s(...) + // the interaction matrix Ls : .setInteractionMatrix(...) + + // log(Z/Zd) is then a size 1 vector logZ + vpGenericFeature logZ(1) ; + // initialized to s = log(Z/Zd) + // Let us note that here we use the point P and Pd, it's not necessary + // to forward project twice (it's already done) logZ.set_s(log(point.get_Z()/pointd.get_Z())) ; - vpMatrix LlogZ(1,6) ; - LlogZ[0][0] = LlogZ[0][1] = LlogZ[0][5] = 0 ; - LlogZ[0][2] = -1/p.get_Z() ; - LlogZ[0][3] = -p.get_y() ; - LlogZ[0][4] = p.get_x() ; - logZ.setInteractionMatrix(LlogZ) ; + // This visual has to be regulated to zero - // compute the control law - v = task.computeControlLaw() ; + //------------------------------------------------------------------ + // 3rd feature ThetaU + // The thetaU feature is defined, tu represents the rotation that the camera + // has to realized. + // the complete displacement is then defined by: + //------------------------------------------------------------------ + vpHomogeneousMatrix cdMc ; + // compute the rotation that the camera has to achieve + cdMc = cdMo*cMo.inverse() ; - // send the camera velocity to the controller ") ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // from this displacement, we extract the rotation cdRc represented by + // the angle theta and the rotation axis u + vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; + tu.buildFrom(cdMc) ; + // This visual has to be regulated to zero + + // sets the desired rotation (always zero !) + // since s is the rotation that the camera has to realize + + //------------------------------------------------------------------ + // Let us now the task itself + //------------------------------------------------------------------ + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + // we choose to control the robot in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + // Interaction matrix is computed with the current value of s + task.setInteractionMatrixType(vpServo::CURRENT) ; + + // we build the task by "stacking" the visual feature + // previously defined + task.addFeature(p,pd) ; + task.addFeature(logZ) ; + task.addFeature(tu) ; + // addFeature(X,Xd) means X should be regulated to Xd + // addFeature(X) means that X should be regulated to 0 + // some features such as vpFeatureThetaU MUST be regulated to zero + // (otherwise, it will results in an error at exectution level) + + // set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + //------------------------------------------------------------------ + // An now the closed loop + + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // update the feature + point.track(cMo) ; + vpFeatureBuilder::create(p,point) ; + + cdMc = cdMo*cMo.inverse() ; + tu.buildFrom(cdMc) ; + + // there is no feature for logZ, we explicitely build + // the related interaction matrix") ; + logZ.set_s(log(point.get_Z()/pointd.get_Z())) ; + vpMatrix LlogZ(1,6) ; + LlogZ[0][0] = LlogZ[0][1] = LlogZ[0][5] = 0 ; + LlogZ[0][2] = -1/p.get_Z() ; + LlogZ[0][3] = -p.get_y() ; + LlogZ[0][4] = p.get_x() ; + + logZ.setInteractionMatrix(LlogZ) ; + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller ") ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // Display task information + task.print() ; + task.kill(); + // Final camera location + std::cout << cMo << std::endl ; + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); - // Final camera location - std::cout << cMo << std::endl ; } diff --git a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity3.cpp b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity3.cpp index 76161538b0d893151b1b9fca7b835e7de49a2e50..d42e4931d983255d3913c7f9bd989d3ba748f0a5 100644 --- a/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity3.cpp +++ b/example/robot-simulator/camera/servoSimuPoint2DhalfCamVelocity3.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuPoint2DhalfCamVelocity3.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -109,15 +112,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -126,7 +129,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -136,197 +139,204 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " simulation of a 2 1/2 D visual servoing " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // In this example we will simulate a visual servoing task. - // In simulation, we have to define the scene frane Ro and the - // camera frame Rc. - // The camera location is given by an homogenous matrix cMo that - // describes the position of the camera in the scene frame. - - vpServo task ; - - // sets the initial camera location - // we give the camera location as a size 6 vector (3 translations in meter - // and 3 rotation (theta U representation) - vpPoseVector c_r_o(0.1,0.2,2, - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) - ) ; - - // this pose vector is then transformed in a 4x4 homogeneous matrix - vpHomogeneousMatrix cMo(c_r_o) ; - - // We define a robot - // The vpSimulatorCamera implements a simple moving that is juste defined - // by its location cMo - vpSimulatorCamera robot ; - - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // Now that the current camera position has been defined, - // let us defined the defined camera location. - // It is defined by cdMo - // sets the desired camera location " ) ; - vpPoseVector cd_r_o(0,0,1, - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - vpHomogeneousMatrix cdMo(cd_r_o) ; - - //---------------------------------------------------------------------- - // A 2 1/2 D visual servoing can be defined by - // - the position of a point x,y - // - the difference between this point depth and a desire depth - // modeled by log Z/Zd to be regulated to 0 - // - the rotation that the camera has to realized cdMc - - // Let us now defined the current value of these features - - - // since we simulate we have to define a 3D point that will - // forward-projected to define the current position x,y of the - // reference point - - //------------------------------------------------------------------ - // First feature (x,y) - //------------------------------------------------------------------ - // Let oP be this ... point, - // a vpPoint class has three main member - // .oP : 3D coordinates in scene frame - // .cP : 3D coordinates in camera frame - // .p : 2D - - //------------------------------------------------------------------ - // sets the point coordinates in the world frame - vpPoint P ; - // defined point coordinates in the scene frame : oP - P.setWorldCoordinates(0,0,0) ; - // computes the P coordinates in the camera frame and its - // 2D coordinates cP and then p - // computes the point coordinates in the camera frame and its 2D coordinates - P.track(cMo) ; - - // We also defined (again by forward projection) the desired position - // of this point according to the desired camera position - vpPoint Pd ; - Pd.setWorldCoordinates(0,0,0) ; - Pd.track(cdMo) ; - - // Nevertheless, a vpPoint is not a feature, this is just a "tracker" - // from which the feature are built - // a feature is juste defined by a vector s, a way to compute the - // interaction matrix and the error, and if required a (or a vector of) - // 3D information - - // for a point (x,y) Visp implements the vpFeaturePoint class. - // we no defined a feature for x,y (and for (x*,y*)) - vpFeaturePoint p,pd ; - - // and we initialized the vector s=(x,y) of p from the tracker P - // Z coordinates in p is also initialized, it will be used to compute - // the interaction matrix - vpFeatureBuilder::create(p,P) ; - vpFeatureBuilder::create(pd,Pd) ; - - // This visual has to be regulated to zero - - //------------------------------------------------------------------ - // 2nd feature ThetaUz and 3rd feature t - // The thetaU feature is defined, tu represents the rotation that the camera - // has to realized. t the translation. - // the complete displacement is then defined by: - //------------------------------------------------------------------ - vpHomogeneousMatrix cdMc ; - // compute the rotation that the camera has to achieve - cdMc = cdMo*cMo.inverse() ; - - // from this displacement, we extract the rotation cdRc represented by - // the angle theta and the rotation axis u - vpFeatureThetaU tuz(vpFeatureThetaU::cdRc) ; - tuz.buildFrom(cdMc) ; - // And the translations - vpFeatureTranslation t(vpFeatureTranslation::cdMc) ; - t.buildFrom(cdMc) ; - - // This visual has to be regulated to zero - - // sets the desired rotation (always zero !) - // since s is the rotation that the camera has to achieve - - //------------------------------------------------------------------ - // Let us now the task itself - //------------------------------------------------------------------ - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - // we choose to control the robot in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - // Interaction matrix is computed with the current value of s - task.setInteractionMatrixType(vpServo::CURRENT) ; - - // we build the task by "stacking" the visual feature - // previously defined - task.addFeature(t) ; - task.addFeature(p,pd) ; - task.addFeature(tuz,vpFeatureThetaU::TUz) ; //selection of TUz - - // addFeature(X,Xd) means X should be regulated to Xd - // addFeature(X) means that X should be regulated to 0 - // some features such as vpFeatureThetaU MUST be regulated to zero - // (otherwise, it will results in an error at exectution level) - - // set the gain - task.setLambda(1) ; - - // Display task information " ) ; - task.print() ; - //------------------------------------------------------------------ - // An now the closed loop - - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // update the feature + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " simulation of a 2 1/2 D visual servoing " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // In this example we will simulate a visual servoing task. + // In simulation, we have to define the scene frane Ro and the + // camera frame Rc. + // The camera location is given by an homogenous matrix cMo that + // describes the position of the camera in the scene frame. + + vpServo task ; + + // sets the initial camera location + // we give the camera location as a size 6 vector (3 translations in meter + // and 3 rotation (theta U representation) + vpPoseVector c_r_o(0.1,0.2,2, + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) + ) ; + + // this pose vector is then transformed in a 4x4 homogeneous matrix + vpHomogeneousMatrix cMo(c_r_o) ; + + // We define a robot + // The vpSimulatorCamera implements a simple moving that is juste defined + // by its location cMo + vpSimulatorCamera robot ; + + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; + + // Now that the current camera position has been defined, + // let us defined the defined camera location. + // It is defined by cdMo + // sets the desired camera location " ) ; + vpPoseVector cd_r_o(0,0,1, + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + vpHomogeneousMatrix cdMo(cd_r_o) ; + + //---------------------------------------------------------------------- + // A 2 1/2 D visual servoing can be defined by + // - the position of a point x,y + // - the difference between this point depth and a desire depth + // modeled by log Z/Zd to be regulated to 0 + // - the rotation that the camera has to realized cdMc + + // Let us now defined the current value of these features + + + // since we simulate we have to define a 3D point that will + // forward-projected to define the current position x,y of the + // reference point + + //------------------------------------------------------------------ + // First feature (x,y) + //------------------------------------------------------------------ + // Let oP be this ... point, + // a vpPoint class has three main member + // .oP : 3D coordinates in scene frame + // .cP : 3D coordinates in camera frame + // .p : 2D + + //------------------------------------------------------------------ + // sets the point coordinates in the world frame + vpPoint P ; + // defined point coordinates in the scene frame : oP + P.setWorldCoordinates(0,0,0) ; + // computes the P coordinates in the camera frame and its + // 2D coordinates cP and then p + // computes the point coordinates in the camera frame and its 2D coordinates P.track(cMo) ; - vpFeatureBuilder::create(p,P) ; + // We also defined (again by forward projection) the desired position + // of this point according to the desired camera position + vpPoint Pd ; + Pd.setWorldCoordinates(0,0,0) ; + Pd.track(cdMo) ; + + // Nevertheless, a vpPoint is not a feature, this is just a "tracker" + // from which the feature are built + // a feature is juste defined by a vector s, a way to compute the + // interaction matrix and the error, and if required a (or a vector of) + // 3D information + + // for a point (x,y) Visp implements the vpFeaturePoint class. + // we no defined a feature for x,y (and for (x*,y*)) + vpFeaturePoint p,pd ; + + // and we initialized the vector s=(x,y) of p from the tracker P + // Z coordinates in p is also initialized, it will be used to compute + // the interaction matrix + vpFeatureBuilder::create(p,P) ; + vpFeatureBuilder::create(pd,Pd) ; + + // This visual has to be regulated to zero + + //------------------------------------------------------------------ + // 2nd feature ThetaUz and 3rd feature t + // The thetaU feature is defined, tu represents the rotation that the camera + // has to realized. t the translation. + // the complete displacement is then defined by: + //------------------------------------------------------------------ + vpHomogeneousMatrix cdMc ; + // compute the rotation that the camera has to achieve cdMc = cdMo*cMo.inverse() ; + + // from this displacement, we extract the rotation cdRc represented by + // the angle theta and the rotation axis u + vpFeatureThetaU tuz(vpFeatureThetaU::cdRc) ; tuz.buildFrom(cdMc) ; + // And the translations + vpFeatureTranslation t(vpFeatureTranslation::cdMc) ; t.buildFrom(cdMc) ; - // compute the control law: v = -lambda L^+(s-sd) - v = task.computeControlLaw() ; - - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // This visual has to be regulated to zero + + // sets the desired rotation (always zero !) + // since s is the rotation that the camera has to achieve + + //------------------------------------------------------------------ + // Let us now the task itself + //------------------------------------------------------------------ + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + // we choose to control the robot in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + // Interaction matrix is computed with the current value of s + task.setInteractionMatrixType(vpServo::CURRENT) ; + + // we build the task by "stacking" the visual feature + // previously defined + task.addFeature(t) ; + task.addFeature(p,pd) ; + task.addFeature(tuz,vpFeatureThetaU::TUz) ; //selection of TUz + + // addFeature(X,Xd) means X should be regulated to Xd + // addFeature(X) means that X should be regulated to 0 + // some features such as vpFeatureThetaU MUST be regulated to zero + // (otherwise, it will results in an error at exectution level) + + // set the gain + task.setLambda(1) ; + + // Display task information " ) ; + task.print() ; + //------------------------------------------------------------------ + // An now the closed loop + + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // update the feature + P.track(cMo) ; + vpFeatureBuilder::create(p,P) ; + + cdMc = cdMo*cMo.inverse() ; + tuz.buildFrom(cdMc) ; + t.buildFrom(cdMc) ; + + // compute the control law: v = -lambda L^+(s-sd) + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // Display task information + task.print() ; + task.kill(); + // Final camera location + std::cout << "Final camera location: \n" << cMo << std::endl ; + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); - // Final camera location - std::cout << "Final camera location: \n" << cMo << std::endl ; } diff --git a/example/robot-simulator/camera/servoSimuPoint3DCamVelocity.cpp b/example/robot-simulator/camera/servoSimuPoint3DCamVelocity.cpp index 9032832d8826f86a1f8b0e93704463105f6aa653..b2361f4737eb75ea85bbc8b653f92b2fe082d05a 100644 --- a/example/robot-simulator/camera/servoSimuPoint3DCamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimuPoint3DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuPoint3DCamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -107,15 +110,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -124,7 +127,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -134,92 +137,99 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a 3D point " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - // sets the point coordinates in the world frame - vpPoint point ; - point.setWorldCoordinates(0,0,0) ; - - // computes the point coordinates in the camera frame - point.track(cMo) ; - - std::cout << "Point coordinates in the camera frame: " << point.cP.t() ; - - vpFeaturePoint3D p ; - p.buildFrom(point) ; - - // sets the desired position of the point - vpFeaturePoint3D pd ; - pd.set_XYZ(0,0,1) ; - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - - // we want to see a point on a point - std::cout << std::endl ; - task.addFeature(p,pd) ; - - // set the gain") ; - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a 3D point " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; - // new point position + // sets the point coordinates in the world frame + vpPoint point ; + point.setWorldCoordinates(0,0,0) ; + + // computes the point coordinates in the camera frame point.track(cMo) ; + + std::cout << "Point coordinates in the camera frame: " << point.cP.t() ; + + vpFeaturePoint3D p ; p.buildFrom(point) ; - // std::cout << p.cP.t() ; - // std::cout << (p.get_s()).t() ; - // compute the control law - v = task.computeControlLaw() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // sets the desired position of the point + vpFeaturePoint3D pd ; + pd.set_XYZ(0,0,1) ; + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + + // we want to see a point on a point + std::cout << std::endl ; + task.addFeature(p,pd) ; + + // set the gain") ; + task.setLambda(1) ; + + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new point position + point.track(cMo) ; + p.buildFrom(point) ; + // std::cout << p.cP.t() ; + // std::cout << (p.get_s()).t() ; + + // compute the control law + v = task.computeControlLaw() ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); } diff --git a/example/robot-simulator/camera/servoSimuSphere2DCamVelocity.cpp b/example/robot-simulator/camera/servoSimuSphere2DCamVelocity.cpp index 0840404b144696abf355784fdd506e23432de420..9c39af785e7993dbfdfcaf7bd3862f513999a7c5 100644 --- a/example/robot-simulator/camera/servoSimuSphere2DCamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimuSphere2DCamVelocity.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuSphere2DCamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -107,15 +110,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -124,7 +127,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -134,94 +137,100 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a sphere " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - vpHomogeneousMatrix cMod ; - cMod[0][3] = 0 ; - cMod[1][3] = 0 ; - cMod[2][3] = 1 ; - - // sets the sphere coordinates in the world frame - vpSphere sphere ; - sphere.setWorldCoordinates(0,0,0,0.1) ; - - // sets the desired position of the visual feature - vpFeatureEllipse pd ; - sphere.track(cMod) ; - vpFeatureBuilder::create(pd,sphere) ; - - // computes the sphere coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - vpFeatureEllipse p ; - sphere.track(cMo) ; - vpFeatureBuilder::create(p,sphere) ; - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - - // we want to see a sphere on a sphere - task.addFeature(p,pd) ; - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - - unsigned int iter=0 ; - // loop - while(iter++ < 500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a sphere " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; + + vpHomogeneousMatrix cMod ; + cMod[0][3] = 0 ; + cMod[1][3] = 0 ; + cMod[2][3] = 1 ; + + // sets the sphere coordinates in the world frame + vpSphere sphere ; + sphere.setWorldCoordinates(0,0,0,0.1) ; - // new sphere position: retrieve x,y and Z of the vpSphere structure + // sets the desired position of the visual feature + vpFeatureEllipse pd ; + sphere.track(cMod) ; + vpFeatureBuilder::create(pd,sphere) ; + + // computes the sphere coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature + vpFeatureEllipse p ; sphere.track(cMo) ; - vpFeatureBuilder::create(p,sphere); + vpFeatureBuilder::create(p,sphere) ; - // compute the control law - v = task.computeControlLaw() ; + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; - std::cout << "Task rank: " << task.getTaskRank() << std::endl ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // we want to see a sphere on a sphere + task.addFeature(p,pd) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; - } + // set the gain + task.setLambda(1) ; - // Display task information - task.print() ; - task.kill(); -} + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++ < 500) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new sphere position: retrieve x,y and Z of the vpSphere structure + sphere.track(cMo) ; + vpFeatureBuilder::create(p,sphere); + + // compute the control law + v = task.computeControlLaw() ; + + std::cout << "Task rank: " << task.getTaskRank() << std::endl ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } + + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } +} diff --git a/example/robot-simulator/camera/servoSimuSphere2DCamVelocitySecondaryTask.cpp b/example/robot-simulator/camera/servoSimuSphere2DCamVelocitySecondaryTask.cpp index 76dd01973b1db1c33543a90257485322a12a00c6..340f2dcb5643dbf1af31d3f0ca21456e3cf1b9e2 100644 --- a/example/robot-simulator/camera/servoSimuSphere2DCamVelocitySecondaryTask.cpp +++ b/example/robot-simulator/camera/servoSimuSphere2DCamVelocitySecondaryTask.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuSphere2DCamVelocitySecondaryTask.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,6 +67,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -109,15 +112,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -126,7 +129,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -136,107 +139,114 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpServo task ; - vpSimulatorCamera robot ; - - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo a sphere with a secondary task" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - - // sets the initial camera location - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; - - vpHomogeneousMatrix cMod ; - cMod[0][3] = 0 ; - cMod[1][3] = 0 ; - cMod[2][3] = 1 ; - - // sets the sphere coordinates in the world frame - vpSphere sphere ; - sphere.setWorldCoordinates(0,0,0,0.1) ; - - // sets the desired position of the visual feature - vpFeatureEllipse pd ; - sphere.track(cMod) ; - vpFeatureBuilder::create(pd,sphere) ; - - // computes the sphere coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - vpFeatureEllipse p ; - sphere.track(cMo) ; - vpFeatureBuilder::create(p,sphere) ; - - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - - // we want to see a sphere on a sphere - std::cout << std::endl ; - task.addFeature(p,pd) ; - - // set the gain - task.setLambda(1) ; - - // Display task information - task.print() ; - // exit(1) ; - unsigned int iter=0 ; - // loop - while(iter++ < 500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; - - // get the robot position + vpServo task ; + vpSimulatorCamera robot ; + + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo a sphere with a secondary task" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + + // sets the initial camera location + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; - - // new sphere position: retrieve x,y and Z of the vpSphere structure - sphere.track(cMo) ; - vpFeatureBuilder::create(p,sphere); + wMo = wMc * cMo; - vpColVector de2dt(6) ; - de2dt[2] = 1 ; // should be zero in (I-WpW)de2dt - de2dt[5] = 0.01 ; // should be ok - de2dt[0] = 0.01 ; // should generate a motion on (I-WpW)de2dt[4] + vpHomogeneousMatrix cMod ; + cMod[0][3] = 0 ; + cMod[1][3] = 0 ; + cMod[2][3] = 1 ; - // compute the control law - v = task.computeControlLaw() ; + // sets the sphere coordinates in the world frame + vpSphere sphere ; + sphere.setWorldCoordinates(0,0,0,0.1) ; - std::cout << "de2dt :"<< de2dt.t() << std::endl; - vpColVector sec ; - sec = task.secondaryTask(de2dt) ; - std::cout << "(I-WpW)de2dt :"<< sec.t() << std::endl; + // sets the desired position of the visual feature + vpFeatureEllipse pd ; + sphere.track(cMod) ; + vpFeatureBuilder::create(pd,sphere) ; - if (iter>20) v += sec ; - - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // computes the sphere coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature + vpFeatureEllipse p ; + sphere.track(cMo) ; + vpFeatureBuilder::create(p,sphere) ; + + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + + // we want to see a sphere on a sphere + std::cout << std::endl ; + task.addFeature(p,pd) ; + + // set the gain + task.setLambda(1) ; + + // Display task information + task.print() ; + // exit(1) ; + unsigned int iter=0 ; + // loop + while(iter++ < 500) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; + + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; + + // new sphere position: retrieve x,y and Z of the vpSphere structure + sphere.track(cMo) ; + vpFeatureBuilder::create(p,sphere); + + vpColVector de2dt(6) ; + de2dt[2] = 1 ; // should be zero in (I-WpW)de2dt + de2dt[5] = 0.01 ; // should be ok + de2dt[0] = 0.01 ; // should generate a motion on (I-WpW)de2dt[4] + + // compute the control law + v = task.computeControlLaw() ; + + std::cout << "de2dt :"<< de2dt.t() << std::endl; + vpColVector sec ; + sec = task.secondaryTask(de2dt) ; + std::cout << "(I-WpW)de2dt :"<< sec.t() << std::endl; + + if (iter>20) v += sec ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + } - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - - // Display task information - task.print() ; - task.kill(); } diff --git a/example/robot-simulator/camera/servoSimuSquareLine2DCamVelocityDisplay.cpp b/example/robot-simulator/camera/servoSimuSquareLine2DCamVelocityDisplay.cpp index 487b66f86a54aa50527366c4434ff1a497f4e15e..5d9f60850d5830cf88ae6217ea27de0e23c19ac0 100644 --- a/example/robot-simulator/camera/servoSimuSquareLine2DCamVelocityDisplay.cpp +++ b/example/robot-simulator/camera/servoSimuSquareLine2DCamVelocityDisplay.cpp @@ -4,7 +4,7 @@ * $Id: servoSimuSquareLine2DCamVelocityDisplay.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,7 +53,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <stdlib.h> #include <stdio.h> @@ -62,6 +62,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpFeatureLine.h> #include <visp/vpHomogeneousMatrix.h> @@ -77,6 +78,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -127,9 +131,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -137,7 +141,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -146,7 +150,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -157,177 +161,186 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click_allowed = true; + try { + bool opt_display = true; + bool opt_click_allowed = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - vpImage<unsigned char> I(512,512,0) ; + vpImage<unsigned char> I(512,512,0) ; - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ - // Display size is automatically defined by the image (I) size - display.init(I, 100, 100,"Camera view...") ; - // Display the image - // The image class has a member that specify a pointer toward - // the display that has been initialized in the display declaration - // therefore is is no longuer necessary to make a reference to the - // display variable. - vpDisplay::display(I) ; - vpDisplay::flush(I) ; - } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + if (opt_display) { + try{ + // Display size is automatically defined by the image (I) size + display.init(I, 100, 100,"Camera view...") ; + // Display the image + // The image class has a member that specify a pointer toward + // the display that has been initialized in the display declaration + // therefore is is no longuer necessary to make a reference to the + // display variable. + vpDisplay::display(I) ; + vpDisplay::flush(I) ; + } + catch(...) + { + vpERROR_TRACE("Error while displaying the image") ; + exit(-1); + } } - } - // Set the camera parameters - double px, py ; px = py = 600 ; - double u0, v0 ; u0 = v0 = 256 ; + // Set the camera parameters + double px, py ; px = py = 600 ; + double u0, v0 ; u0 = v0 = 256 ; - vpCameraParameters cam(px,py,u0,v0); + vpCameraParameters cam(px,py,u0,v0); - vpServo task ; - vpSimulatorCamera robot ; + vpServo task ; + vpSimulatorCamera robot ; - // sets the initial camera location - vpHomogeneousMatrix cMo(0.2,0.2,1, - vpMath::rad(45), vpMath::rad(45), vpMath::rad(125)); + // sets the initial camera location + vpHomogeneousMatrix cMo(0.2,0.2,1, + vpMath::rad(45), vpMath::rad(45), vpMath::rad(125)); - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; - // sets the final camera location (for simulation purpose) - vpHomogeneousMatrix cMod(0,0,1, - vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + // sets the final camera location (for simulation purpose) + vpHomogeneousMatrix cMod(0,0,1, + vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - int nbline = 4; + int nbline = 4; - // sets the line coordinates (2 planes) in the world frame - vpLine line[4] ; - line[0].setWorldCoordinates(1,0,0,0.05,0,0,1,0); - line[1].setWorldCoordinates(0,1,0,0.05,0,0,1,0); - line[2].setWorldCoordinates(1,0,0,-0.05,0,0,1,0); - line[3].setWorldCoordinates(0,1,0,-0.05,0,0,1,0); + // sets the line coordinates (2 planes) in the world frame + vpLine line[4] ; + line[0].setWorldCoordinates(1,0,0,0.05,0,0,1,0); + line[1].setWorldCoordinates(0,1,0,0.05,0,0,1,0); + line[2].setWorldCoordinates(1,0,0,-0.05,0,0,1,0); + line[3].setWorldCoordinates(0,1,0,-0.05,0,0,1,0); - vpFeatureLine ld[4] ; - vpFeatureLine l[4] ; + vpFeatureLine ld[4] ; + vpFeatureLine l[4] ; - // sets the desired position of the visual feature - for(int i = 0; i < nbline; i++) - { - line[i].track(cMod) ; - line[i].print() ; + // sets the desired position of the visual feature + for(int i = 0; i < nbline; i++) + { + line[i].track(cMod) ; + line[i].print() ; - vpFeatureBuilder::create(ld[i],line[i]) ; - } + vpFeatureBuilder::create(ld[i],line[i]) ; + } - // computes the line coordinates in the camera frame and its 2D coordinates - // sets the current position of the visual feature - for(int i = 0; i < nbline; i++) - { - line[i].track(cMo) ; - line[i].print() ; + // computes the line coordinates in the camera frame and its 2D coordinates + // sets the current position of the visual feature + for(int i = 0; i < nbline; i++) + { + line[i].track(cMo) ; + line[i].print() ; - vpFeatureBuilder::create(l[i],line[i]) ; - l[i].print() ; - } + vpFeatureBuilder::create(l[i],line[i]) ; + l[i].print() ; + } - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE); - //It could be also interesting to test the following tasks - //task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); - //task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE); + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE); + //It could be also interesting to test the following tasks + //task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); + //task.setInteractionMatrixType(vpServo::MEAN, vpServo::PSEUDO_INVERSE); - // we want to see a four lines on four lines - for(int i = 0; i < nbline; i++) - task.addFeature(l[i],ld[i]) ; + // we want to see a four lines on four lines + for(int i = 0; i < nbline; i++) + task.addFeature(l[i],ld[i]) ; - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; - // set the gain - task.setLambda(1) ; + // set the gain + task.setLambda(1) ; - // Display task information - task.print() ; + // Display task information + task.print() ; - if (opt_display && opt_click_allowed) { - std::cout << "\n\nClick in the camera view window to start..." << std::endl; - vpDisplay::getClick(I) ; - } + if (opt_display && opt_click_allowed) { + std::cout << "\n\nClick in the camera view window to start..." << std::endl; + vpDisplay::getClick(I) ; + } - unsigned int iter=0 ; - // loop - while(iter++<200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + unsigned int iter=0 ; + // loop + while(iter++<200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - // new line position: retrieve x,y and Z of the vpLine structure - for(int i = 0; i < nbline; i++) - { - line[i].track(cMo) ; - vpFeatureBuilder::create(l[i],line[i]); - } + // new line position: retrieve x,y and Z of the vpLine structure + for(int i = 0; i < nbline; i++) + { + line[i].track(cMo) ; + vpFeatureBuilder::create(l[i],line[i]); + } - if (opt_display) { - vpDisplay::display(I) ; - vpServoDisplay::display(task,cam,I) ; - vpDisplay::flush(I) ; - } + if (opt_display) { + vpDisplay::display(I) ; + vpServoDisplay::display(task,cam,I) ; + vpDisplay::flush(I) ; + } - // compute the control law - v = task.computeControlLaw() ; + // compute the control law + v = task.computeControlLaw() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; - } + } - if (opt_display && opt_click_allowed) { - std::cout << "\nClick in the camera view window to end..." << std::endl; - vpDisplay::getClick(I) ; - } + if (opt_display && opt_click_allowed) { + std::cout << "\nClick in the camera view window to end..." << std::endl; + vpDisplay::getClick(I) ; + } - // Display task information - task.print() ; - task.kill(); + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + std::cout << "You do not have X11, GTK, GDI or OpenCV display functionalities..." << std::endl; } #endif diff --git a/example/robot-simulator/camera/servoSimuThetaUCamVelocity.cpp b/example/robot-simulator/camera/servoSimuThetaUCamVelocity.cpp index fe54722307ec10bfb6ef42f74ea67e51c5fadaed..b28ab523f3c18585aa8fb2b664cd70fb8a53fb7e 100644 --- a/example/robot-simulator/camera/servoSimuThetaUCamVelocity.cpp +++ b/example/robot-simulator/camera/servoSimuThetaUCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuThetaUCamVelocity.cpp 2457 2010-01-07 10:41:18Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,7 +66,8 @@ // List of allowed command line options #define GETOPTARGS "h" - +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); /*! Print the program options. @@ -108,15 +109,15 @@ Set the program options. */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -125,7 +126,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -135,88 +136,95 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpServo task ; - vpSimulatorCamera robot ; + vpServo task ; + vpSimulatorCamera robot ; - std::cout << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo using theta U visual feature " << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; + std::cout << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo using theta U visual feature " << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; - // sets the initial camera location - vpPoseVector c_r_o(0.1,0.2,2, - vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) - ) ; + // sets the initial camera location + vpPoseVector c_r_o(0.1,0.2,2, + vpMath::rad(20), vpMath::rad(10), vpMath::rad(50) + ) ; - vpHomogeneousMatrix cMo(c_r_o) ; - // Compute the position of the object in the world frame - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc) ; - wMo = wMc * cMo; + vpHomogeneousMatrix cMo(c_r_o) ; + // Compute the position of the object in the world frame + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc) ; + wMo = wMc * cMo; - // sets the desired camera location - vpPoseVector cd_r_o(0,0,1, - vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; - vpHomogeneousMatrix cdMo(cd_r_o) ; + // sets the desired camera location + vpPoseVector cd_r_o(0,0,1, + vpMath::rad(0),vpMath::rad(0),vpMath::rad(0)) ; + vpHomogeneousMatrix cdMo(cd_r_o) ; - // compute the rotation that the camera has to realize - vpHomogeneousMatrix cdMc ; - cdMc = cdMo*cMo.inverse() ; - vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; - tu.buildFrom(cdMc) ; + // compute the rotation that the camera has to realize + vpHomogeneousMatrix cdMc ; + cdMc = cdMo*cMo.inverse() ; + vpFeatureThetaU tu(vpFeatureThetaU::cdRc) ; + tu.buildFrom(cdMc) ; - // define the task - // - we want an eye-in-hand control law - // - robot is controlled in the camera frame - task.setServo(vpServo::EYEINHAND_CAMERA) ; - task.setInteractionMatrixType(vpServo::DESIRED) ; + // define the task + // - we want an eye-in-hand control law + // - robot is controlled in the camera frame + task.setServo(vpServo::EYEINHAND_CAMERA) ; + task.setInteractionMatrixType(vpServo::DESIRED) ; - task.addFeature(tu) ; + task.addFeature(tu) ; - // - set the gain - task.setLambda(1) ; + // - set the gain + task.setLambda(1) ; - // Display task information - task.print() ; + // Display task information + task.print() ; - unsigned int iter=0 ; - // loop - while(iter++ < 200) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + unsigned int iter=0 ; + // loop + while(iter++ < 200) + { + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // get the robot position - robot.getPosition(wMc) ; - // Compute the position of the camera wrt the object frame - cMo = wMc.inverse() * wMo; + // get the robot position + robot.getPosition(wMc) ; + // Compute the position of the camera wrt the object frame + cMo = wMc.inverse() * wMo; - // new rotation to achieve - cdMc = cdMo*cMo.inverse() ; - tu.buildFrom(cdMc) ; + // new rotation to achieve + cdMc = cdMo*cMo.inverse() ; + tu.buildFrom(cdMc) ; - // compute the control law - v = task.computeControlLaw() ; + // compute the control law + v = task.computeControlLaw() ; - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; - } + std::cout << "|| s - s* || = " << ( task.getError() ).sumSquare() <<std::endl ; ; + } - // Display task information - task.print() ; - task.kill(); + // Display task information + task.print() ; + task.kill(); + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; + } } diff --git a/example/robot-simulator/viper850/CMakeLists.txt b/example/robot-simulator/viper850/CMakeLists.txt index c3ea7c65612ec57502e5e7d33a87f3ccd952d35c..f15684b51ebcd8f20542df187c6e109135650a6b 100644 --- a/example/robot-simulator/viper850/CMakeLists.txt +++ b/example/robot-simulator/viper850/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 2457 2010-01-07 10:41:18Z nmelchio $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE -servoSimuViper850FourPoints2DCamVelocity.cpp +set(SOURCE + servoSimuViper850FourPoints2DCamVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test -ADD_TEST(servoSimuViper850FourPoints2DCamVelocity servoSimuViper850FourPoints2DCamVelocity -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(servoSimuViper850FourPoints2DCamVelocity servoSimuViper850FourPoints2DCamVelocity -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/robot-simulator/viper850/servoSimuViper850FourPoints2DCamVelocity.cpp b/example/robot-simulator/viper850/servoSimuViper850FourPoints2DCamVelocity.cpp index 089be393d0633cb5bc4c1fc8c3e61de7f7753df6..f00a4760a896ef005cdd0056c9f258c65a3cf007 100644 --- a/example/robot-simulator/viper850/servoSimuViper850FourPoints2DCamVelocity.cpp +++ b/example/robot-simulator/viper850/servoSimuViper850FourPoints2DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoSimuFourPoints2DPolarCamVelocityDisplay.cpp 2503 2010-02-16 18:55:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <visp/vpDebug.h> #include <visp/vpConfig.h> -#if (defined(WIN32) || defined(VISP_HAVE_PTHREAD)) && (defined (VISP_HAVE_X11) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI)) +#if (defined(_WIN32) || defined(VISP_HAVE_PTHREAD)) && (defined (VISP_HAVE_X11) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GDI)) // We need to use threading capabilities. Thus on Unix-like // platforms, the libpthread third-party library need to be @@ -88,6 +88,9 @@ // List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + /*! Print the program options. @@ -137,9 +140,9 @@ Set the program options. */ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; @@ -147,7 +150,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -156,7 +159,7 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -166,194 +169,194 @@ bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) int main(int argc, const char ** argv) { - bool opt_click_allowed = true; - bool opt_display = true; + try { + bool opt_click_allowed = true; + bool opt_display = true; - // Read the command line options - if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // We open two displays, one for the internal camera view, the other one for - // the external view, using either X11, GTK or GDI. + // We open two displays, one for the internal camera view, the other one for + // the external view, using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX displayInt; + vpDisplayX displayInt; #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt; + vpDisplayGDI displayInt; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV displayInt; + vpDisplayOpenCV displayInt; #endif - // open a display for the visualization + // open a display for the visualization - vpImage<unsigned char> Iint(480, 640, 255); + vpImage<unsigned char> Iint(480, 640, 255); - if (opt_display) { - displayInt.init(Iint,700,0, "Internal view") ; - } - - int i; - vpServo task; - - std::cout << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << " Test program for vpServo " <<std::endl ; - std::cout << " Eye-in-hand task control, articular velocity are computed" - << std::endl ; - std::cout << " Simulation " << std::endl ; - std::cout << " task : servo 4 points " << std::endl ; - std::cout << "----------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // sets the initial camera location - vpHomogeneousMatrix cMo(-0.05,-0.05,0.7, - vpMath::rad(10), vpMath::rad(10), vpMath::rad(-30)); - - - // sets the point coordinates in the object frame - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.045,-0.045,0) ; - point[3].setWorldCoordinates(-0.045,0.045,0) ; - point[2].setWorldCoordinates(0.045,0.045,0) ; - point[1].setWorldCoordinates(0.045,-0.045,0) ; - - // computes the point coordinates in the camera frame and its 2D coordinates - for (i = 0 ; i < 4 ; i++) - point[i].track(cMo) ; - - // sets the desired position of the point - vpFeaturePoint p[4] ; - for (i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure - - // sets the desired position of the feature point s* - vpFeaturePoint pd[4] ; - - //Desired pose - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); - - // Projection of the points - for (int i = 0 ; i < 4 ; i++) - point[i].track(cdMo); - - for (int i = 0 ; i < 4 ; i++) - vpFeatureBuilder::create(pd[i], point[i]); - - // define the task - // - we want an eye-in-hand control law - // - articular velocity are computed - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::DESIRED) ; - - // - we want to see a point on a point - for (i = 0 ; i < 4 ; i++) - task.addFeature(p[i],pd[i]) ; - - // set the gain - task.setLambda(0.8) ; - - // Declaration of the robot - vpSimulatorViper850 robot(opt_display); - - // Initialise the robot and especially the camera - robot.init(vpViper850::TOOL_PTGREY_FLEA2_CAMERA,vpCameraParameters::perspectiveProjWithoutDistortion); - robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); - - // Initialise the object for the display part - robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - - // Initialise the position of the object relative to the pose of the robot's camera - robot.initialiseObjectRelativeToCamera(cMo); - - // Set the desired position (for the display part) - robot.setDesiredCameraPosition(cdMo); - - // Get the internal robot's camera parameters - vpCameraParameters cam; - robot.getCameraParameters(cam,Iint); - - if (opt_display) - { - //Get the internal view - vpDisplay::display(Iint); - robot.getInternalView(Iint); - vpDisplay::flush(Iint); - } + if (opt_display) { + displayInt.init(Iint,700,0, "Internal view") ; + } - // Display task information - task.print() ; + vpServo task; - unsigned int iter=0 ; - // loop - while(iter++<500) - { - std::cout << "---------------------------------------------" << iter <<std::endl ; - vpColVector v ; + std::cout << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << " Test program for vpServo " <<std::endl ; + std::cout << " Eye-in-hand task control, articular velocity are computed" + << std::endl ; + std::cout << " Simulation " << std::endl ; + std::cout << " task : servo 4 points " << std::endl ; + std::cout << "----------------------------------------------" << std::endl ; + std::cout << std::endl ; - //Get the Time at the beginning of the loop - double t = vpTime::measureTimeMs(); + // sets the initial camera location + vpHomogeneousMatrix cMo(-0.05,-0.05,0.7, + vpMath::rad(10), vpMath::rad(10), vpMath::rad(-30)); - //Get the current pose of the camera - cMo = robot.get_cMo(); - if (iter==1) { - std::cout <<"Initial robot position with respect to the object frame:\n"; - cMo.print(); - } + // sets the point coordinates in the object frame + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.045,-0.045,0) ; + point[3].setWorldCoordinates(-0.045,0.045,0) ; + point[2].setWorldCoordinates(0.045,0.045,0) ; + point[1].setWorldCoordinates(0.045,-0.045,0) ; - // new point position - for (i = 0 ; i < 4 ; i++) - { + // computes the point coordinates in the camera frame and its 2D coordinates + for (unsigned int i = 0 ; i < 4 ; i++) point[i].track(cMo) ; - //retrieve x,y and Z of the vpPoint structure - try { - vpFeatureBuilder::create(p[i],point[i]) ; - } - catch(...) - { - break; - } - } + + // sets the desired position of the point + vpFeaturePoint p[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(p[i],point[i]) ; //retrieve x,y and Z of the vpPoint structure + + // sets the desired position of the feature point s* + vpFeaturePoint pd[4] ; + + //Desired pose + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,0.8,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); + + // Projection of the points + for (unsigned int i = 0 ; i < 4 ; i++) + point[i].track(cdMo); + + for (unsigned int i = 0 ; i < 4 ; i++) + vpFeatureBuilder::create(pd[i], point[i]); + + // define the task + // - we want an eye-in-hand control law + // - articular velocity are computed + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::DESIRED) ; + + // - we want to see a point on a point + for (unsigned int i = 0 ; i < 4 ; i++) + task.addFeature(p[i],pd[i]) ; + + // set the gain + task.setLambda(0.8) ; + + // Declaration of the robot + vpSimulatorViper850 robot(opt_display); + + // Initialise the robot and especially the camera + robot.init(vpViper850::TOOL_PTGREY_FLEA2_CAMERA,vpCameraParameters::perspectiveProjWithoutDistortion); + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + + // Initialise the object for the display part + robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); + + // Initialise the position of the object relative to the pose of the robot's camera + robot.initialiseObjectRelativeToCamera(cMo); + + // Set the desired position (for the display part) + robot.setDesiredCameraPosition(cdMo); + + // Get the internal robot's camera parameters + vpCameraParameters cam; + robot.getCameraParameters(cam,Iint); if (opt_display) { - // Get the internal view and display it - vpDisplay::display(Iint) ; + //Get the internal view + vpDisplay::display(Iint); robot.getInternalView(Iint); vpDisplay::flush(Iint); } - if (opt_display && opt_click_allowed && iter == 1) + // Display task information + task.print() ; + + unsigned int iter=0 ; + // loop + while(iter++<500) { - // suppressed for automate test - std::cout << "Click in the internal view window to continue..." << std::endl; - vpDisplay::getClick(Iint) ; - } + std::cout << "---------------------------------------------" << iter <<std::endl ; + vpColVector v ; - // compute the control law - v = task.computeControlLaw() ; + //Get the Time at the beginning of the loop + double t = vpTime::measureTimeMs(); - // send the camera velocity to the controller - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + //Get the current pose of the camera + cMo = robot.get_cMo(); - std::cout << "|| s - s* || " << ( task.getError() ).sumSquare() <<std::endl ; + if (iter==1) { + std::cout <<"Initial robot position with respect to the object frame:\n"; + cMo.print(); + } - // The main loop has a duration of 10 ms at minimum - vpTime::wait(t,10); - } + // new point position + for (unsigned int i = 0 ; i < 4 ; i++) + { + point[i].track(cMo) ; + //retrieve x,y and Z of the vpPoint structure + vpFeatureBuilder::create(p[i],point[i]) ; + } + + if (opt_display) + { + // Get the internal view and display it + vpDisplay::display(Iint) ; + robot.getInternalView(Iint); + vpDisplay::flush(Iint); + } - // Display task information - task.print() ; - task.kill(); + if (opt_display && opt_click_allowed && iter == 1) + { + // suppressed for automate test + std::cout << "Click in the internal view window to continue..." << std::endl; + vpDisplay::getClick(Iint) ; + } + + // compute the control law + v = task.computeControlLaw() ; + + // send the camera velocity to the controller + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; + + std::cout << "|| s - s* || " << ( task.getError() ).sumSquare() <<std::endl ; + + // The main loop has a duration of 10 ms at minimum + vpTime::wait(t,10); + } - std::cout <<"Final robot position with respect to the object frame:\n"; - cMo.print(); + // Display task information + task.print() ; + task.kill(); - if (opt_display && opt_click_allowed) - { - // suppressed for automate test - std::cout << "Click in the internal view window to end..." << std::endl; - vpDisplay::getClick(Iint) ; + std::cout <<"Final robot position with respect to the object frame:\n"; + cMo.print(); + + if (opt_display && opt_click_allowed) + { + // suppressed for automate test + std::cout << "Click in the internal view window to end..." << std::endl; + vpDisplay::getClick(Iint) ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } #else diff --git a/example/servo-afma4/CMakeLists.txt b/example/servo-afma4/CMakeLists.txt index 33f25e09642e0d8b4a61e765c74424d67786ab36..ef53ba46a1fa58c374b9b1882f0aaca2a88b3638 100644 --- a/example/servo-afma4/CMakeLists.txt +++ b/example/servo-afma4/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE moveAfma4.cpp servoAfma4Point2DArtVelocity.cpp servoAfma4Point2DCamVelocity.cpp @@ -51,12 +51,16 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-afma4/moveAfma4.cpp b/example/servo-afma4/moveAfma4.cpp index 3cb1cae3a3932dd06c893bd2b2efa74e1063c4bb..f889ed7a739770dee7b1de6e08ccc3fb424f9e7c 100644 --- a/example/servo-afma4/moveAfma4.cpp +++ b/example/servo-afma4/moveAfma4.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: moveAfma4.cpp 4086 2013-02-02 07:26:19Z fspindle $ + * $Id: moveAfma4.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -241,11 +241,12 @@ main(int argc, const char ** argv) sleep(5) ; std::cout << "The end" << std::endl; + return 0; } - catch (...) { - vpERROR_TRACE(" Test failed") ; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } - return 0; } #else int diff --git a/example/servo-afma4/servoAfma4Point2DArtVelocity.cpp b/example/servo-afma4/servoAfma4Point2DArtVelocity.cpp index 01f8f4281f74edf54748bb50cd83f4ab0b9ec7be..53763b0ca9a2fd6c9d1480b977038fb4d7594634 100644 --- a/example/servo-afma4/servoAfma4Point2DArtVelocity.cpp +++ b/example/servo-afma4/servoAfma4Point2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma4Point2DArtVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma4Point2DArtVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -101,40 +101,40 @@ int main() { - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed joint velocities (m/s, rad/s) to achieve the task - // - the 6 mesured joint velocities (m/s, rad/s) - // - the 6 mesured joint positions (m, rad) - // - the 2 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; - logdirname ="/tmp/" + username; - - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); + try { + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed joint velocities (m/s, rad/s) to achieve the task + // - the 6 mesured joint velocities (m/s, rad/s) + // - the 6 mesured joint positions (m, rad) + // - the 2 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; + logdirname ="/tmp/" + username; + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; + std::string logfilename; + logfilename = logdirname + "/log.dat"; - // Open the log file name - std::ofstream flog(logfilename.c_str()); + // Open the log file name + std::ofstream flog(logfilename.c_str()); - try { // vpRobotAfma4 robot ; vpRobotAfma4 robot ; @@ -273,7 +273,7 @@ main() // v[0], v[1], v[2] correspond to joint translation velocities in m/s // v[3], v[4], v[5] correspond to joint rotation velocities in rad/s flog << v[0] << " " << v[1] << " " << v[2] << " " - << v[3] << " " << v[4] << " " << v[5] << " "; + << v[3] << " " << v[4] << " " << v[5] << " "; // Get the measured joint velocities of the robot vpColVector qvel; @@ -284,7 +284,7 @@ main() // - qvel[3], qvel[4], qvel[5] correspond to measured joint rotation // velocities in rad/s flog << qvel[0] << " " << qvel[1] << " " << qvel[2] << " " - << qvel[3] << " " << qvel[4] << " " << qvel[5] << " "; + << qvel[3] << " " << qvel[4] << " " << qvel[5] << " "; // Get the measured joint positions of the robot vpColVector q; @@ -295,7 +295,7 @@ main() // - q[3], q[4], q[5] correspond to measured joint rotation // positions in rad flog << q[0] << " " << q[1] << " " << q[2] << " " - << q[3] << " " << q[4] << " " << q[5] << " "; + << q[3] << " " << q[4] << " " << q[5] << " "; // Save feature error (s-s*) for the feature point. For this feature // point, we have 2 errors (along x and y axis). This error is expressed @@ -313,11 +313,9 @@ main() task.kill(); return 0; } - catch (...) - { - flog.close() ; // Close the log file - vpERROR_TRACE(" Test failed") ; - return 0; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } diff --git a/example/servo-afma4/servoAfma4Point2DCamVelocity.cpp b/example/servo-afma4/servoAfma4Point2DCamVelocity.cpp index 5a3499ff348f37cf84dee96fb3134b85cc253cfd..5549e73e46dc1b87d84916017b100cebe2e9f3ba 100644 --- a/example/servo-afma4/servoAfma4Point2DCamVelocity.cpp +++ b/example/servo-afma4/servoAfma4Point2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma4Point2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma4Point2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -98,41 +98,40 @@ int main() { - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed cam velocities (m/s, rad/s) to achieve the task - // - the 6 mesured joint velocities (m/s, rad/s) - // - the 6 mesured joint positions (m, rad) - // - the 2 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; - logdirname ="/tmp/" + username; - - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); + try { + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed cam velocities (m/s, rad/s) to achieve the task + // - the 6 mesured joint velocities (m/s, rad/s) + // - the 6 mesured joint positions (m, rad) + // - the 2 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; + logdirname ="/tmp/" + username; + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); - } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; + std::string logfilename; + logfilename = logdirname + "/log.dat"; - // Open the log file name - std::ofstream flog(logfilename.c_str()); + // Open the log file name + std::ofstream flog(logfilename.c_str()); - try - { vpRobotAfma4 robot ; vpServo task ; @@ -170,7 +169,7 @@ main() std::cout << "Click on a dot..." << std::endl; dot.initTracking(I) ; - + // Get the cog of the dot vpImagePoint cog = dot.getCog(); @@ -241,7 +240,7 @@ main() // v[0], v[1], v[2] correspond to camera translation velocities in m/s // v[3], v[4], v[5] correspond to camera rotation velocities in rad/s flog << v[0] << " " << v[1] << " " << v[2] << " " - << v[3] << " " << v[4] << " " << v[5] << " "; + << v[3] << " " << v[4] << " " << v[5] << " "; // Get the measured joint velocities of the robot vpColVector qvel; @@ -252,7 +251,7 @@ main() // - qvel[3], qvel[4], qvel[5] correspond to measured joint rotation // velocities in rad/s flog << qvel[0] << " " << qvel[1] << " " << qvel[2] << " " - << qvel[3] << " " << qvel[4] << " " << qvel[5] << " "; + << qvel[3] << " " << qvel[4] << " " << qvel[5] << " "; // Get the measured joint positions of the robot vpColVector q; @@ -263,7 +262,7 @@ main() // - q[3], q[4], q[5] correspond to measured joint rotation // positions in rad flog << q[0] << " " << q[1] << " " << q[2] << " " - << q[3] << " " << q[4] << " " << q[5] << " "; + << q[3] << " " << q[4] << " " << q[5] << " "; // Save feature error (s-s*) for the feature point. For this feature // point, we have 2 errors (along x and y axis). This error is expressed @@ -285,11 +284,9 @@ main() return 0; } - catch (...) - { - flog.close() ; // Close the log file - vpERROR_TRACE(" Test failed") ; - return 0; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } } diff --git a/example/servo-afma4/servoAfma4Point2DCamVelocityKalman.cpp b/example/servo-afma4/servoAfma4Point2DCamVelocityKalman.cpp index 17b8610692dc698ee089f401ce51a5c6ef6de4dd..05a8ec830e8fd579759fe24f0a5d7d9282c48cf7 100644 --- a/example/servo-afma4/servoAfma4Point2DCamVelocityKalman.cpp +++ b/example/servo-afma4/servoAfma4Point2DCamVelocityKalman.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma4Point2DCamVelocityKalman.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma4Point2DCamVelocityKalman.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -194,53 +194,53 @@ bool getOptions(int argc, const char **argv, KalmanType &kalman, int main(int argc, const char ** argv) { - KalmanType opt_kalman = K_NONE; - vpAdaptiveGain lambda; // Gain de la commande - bool doAdaptativeGain = true; // Compute adaptative gain - lambda.initStandard(4, 0.2, 40); - int opt_cam_frequency = 60; // 60 Hz - - // Read the command line options - if (getOptions(argc, argv, opt_kalman, doAdaptativeGain, lambda) == false) { - return (-1); - } - - // Log file creation in /tmp/$USERNAME/log.dat - // This file contains by line: - // - the 6 computed cam velocities (m/s, rad/s) to achieve the task - // - the 6 mesured joint velocities (m/s, rad/s) - // - the 6 mesured joint positions (m, rad) - // - the 2 values of s - s* - std::string username; - // Get the user login name - vpIoTools::getUserName(username); - - // Create a log filename to save velocities... - std::string logdirname; - logdirname ="/tmp/" + username; - - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(logdirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(logdirname); + try { + KalmanType opt_kalman = K_NONE; + vpAdaptiveGain lambda; // Gain de la commande + bool doAdaptativeGain = true; // Compute adaptative gain + lambda.initStandard(4, 0.2, 40); + int opt_cam_frequency = 60; // 60 Hz + + // Read the command line options + if (getOptions(argc, argv, opt_kalman, doAdaptativeGain, lambda) == false) { + return (-1); } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << logdirname << std::endl; - exit(-1); + + // Log file creation in /tmp/$USERNAME/log.dat + // This file contains by line: + // - the 6 computed cam velocities (m/s, rad/s) to achieve the task + // - the 6 mesured joint velocities (m/s, rad/s) + // - the 6 mesured joint positions (m, rad) + // - the 2 values of s - s* + std::string username; + // Get the user login name + vpIoTools::getUserName(username); + + // Create a log filename to save velocities... + std::string logdirname; + logdirname ="/tmp/" + username; + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(logdirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(logdirname); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << logdirname << std::endl; + exit(-1); + } } - } - std::string logfilename; - logfilename = logdirname + "/log.dat"; + std::string logfilename; + logfilename = logdirname + "/log.dat"; - // Open the log file name - std::ofstream flog(logfilename.c_str()); + // Open the log file name + std::ofstream flog(logfilename.c_str()); + + vpServo task ; - vpServo task ; - - try { vpImage<unsigned char> I ; vp1394TwoGrabber g(false); g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); @@ -271,16 +271,16 @@ main(int argc, const char ** argv) std::cout << "Eye-in-hand task control, velocity computed in the camera frame" << std::endl ; std::cout << "Task : servo a point \n" << std::endl ; - // Kalman filtering + // Kalman filtering switch(opt_kalman) { - case K_NONE: + case K_NONE: std::cout << "Servo with no target motion compensation (see -K option)\n"; break; case K_VELOCITY: std::cout << "Servo with target motion compensation using a Kalman filter\n" << "with constant velocity modelization (see -K option)\n"; break; - case K_ACCELERATION: + case K_ACCELERATION: std::cout << "Servo with target motion compensation using a Kalman filter\n" << "with constant acceleration modelization (see -K option)\n"; break; @@ -309,7 +309,7 @@ main(int argc, const char ** argv) // Sets the current position of the visual feature vpFeaturePoint p ; - vpFeatureBuilder::create(p, cam, dot) ; + vpFeatureBuilder::create(p, cam, dot) ; // Sets the desired position of the visual feature vpFeaturePoint pd ; @@ -336,7 +336,7 @@ main(int argc, const char ** argv) //!Initialize filter vpLinearKalmanFilterInstantiation kalman; - + // Initialize the kalman filter unsigned int nsignal = 2; // The two values of dedt double rho = 0.3; @@ -350,9 +350,9 @@ main(int argc, const char ** argv) kalman.setStateModel(vpLinearKalmanFilterInstantiation::stateConstVelWithColoredNoise_MeasureVel); state_size = kalman.getStateSize(); sigma_state.resize(state_size*nsignal); - sigma_state = 0.00001; // Same state variance for all signals + sigma_state = 0.00001; // Same state variance for all signals sigma_measure = 0.05; // Same measure variance for all the signals - double dummy = 0; // non used parameter dt for the velocity state model + double dummy = 0; // non used parameter dt for the velocity state model kalman.initFilter(nsignal, sigma_state, sigma_measure, rho, dummy); break; @@ -381,15 +381,15 @@ main(int argc, const char ** argv) vpColVector v(6), v1(6), v2(6); // robot velocities //task error vpColVector err(2), err_0(2), err_1(2); - vpColVector dedt_filt(2), dedt_mes(2); + vpColVector dedt_filt(2), dedt_mes(2); - t_1 = vpTime::measureTimeMs(); // t_1: time at previous iter + t_1 = vpTime::measureTimeMs(); // t_1: time at previous iter Tv_0 = 0; // - // Warning: In all varaible names, + // Warning: In all varaible names, // _0 means the value for the current iteration (t=0) // _1 means the value for the previous iteration (t=-1) // _2 means the value for the previous previous iteration (t=-2) @@ -426,7 +426,7 @@ main(int argc, const char ** argv) //!-------------------- Update displacements and time ------------------ //---------------------------------------------------------------------- vm_0 = vm; - + // Update current loop time and previous one Tv_1 = Tv_0; Tv_0 = Tv; @@ -437,13 +437,13 @@ main(int argc, const char ** argv) err = task.error; //!terme correctif : de/dt = Delta s / Delta t - L*vc - if (iter==0){ + if (iter==0){ err_0 = 0; err_1 = 0; dedt_mes = 0; dedt_filt = 0; } - else{ + else{ err_1 = err_0; err_0 = err; @@ -457,13 +457,13 @@ main(int argc, const char ** argv) //---------------------------------------------------------------------- //----------------------- Kalman Filter Equations ---------------------- //---------------------------------------------------------------------- - // Kalman filtering + // Kalman filtering switch(opt_kalman) { - case K_NONE: + case K_NONE: dedt_filt = 0; break; case K_VELOCITY: - case K_ACCELERATION: + case K_ACCELERATION: kalman.filter(dedt_mes); for (unsigned int i=0; i < nsignal; i++) { dedt_filt[i] = kalman.Xest[i*state_size]; @@ -477,15 +477,15 @@ main(int argc, const char ** argv) // std::cout << "task J1p: " << J1p.t() << std::endl ; // std::cout << "dedt_filt: " << dedt_filt.t() << std::endl ; - v = v1 + v2; - + v = v1 + v2; + // Display the current and desired feature points in the image display vpServoDisplay::display(task, cam, I) ; // std::cout << "v2 : " << v2.t() << std::endl ; // std::cout << "v1 : " << v1.t() << std::endl ; - + // std::cout << "v : " << v.t(); // Apply the camera velocities to the robot @@ -498,15 +498,15 @@ main(int argc, const char ** argv) // v[0], v[1], v[2] correspond to camera translation velocities in m/s // v[3], v[4], v[5] correspond to camera rotation velocities in rad/s flog << v[0] << " " << v[1] << " " << v[2] << " " - << v[3] << " " << v[4] << " " << v[5] << " "; + << v[3] << " " << v[4] << " " << v[5] << " "; // Save feature error (s-s*) for the feature point. For this feature // point, we have 2 errors (along x and y axis). This error is expressed // in meters in the camera frame - flog << task.error[0] << " " << task.error[1] << " "; + flog << task.error[0] << " " << task.error[1] << " "; // Save feature error (s-s*) in pixels in the image. - flog << cog.get_u()-cam.get_u0() << " " + flog << cog.get_u()-cam.get_u0() << " " << cog.get_v()-cam.get_v0() << " "; // Save de/dt @@ -534,15 +534,11 @@ main(int argc, const char ** argv) return 0; } - catch (...) { - flog.close() ; // Close the log file - - // Kill the task - task.kill(); - - vpERROR_TRACE(" Test failed") ; - return 0; + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + return 1; } + } diff --git a/example/servo-afma6/CMakeLists.txt b/example/servo-afma6/CMakeLists.txt index e32c378dac93c692b1e6252f08e51c65aa850e10..1cc2addfb1310344d8a90955865ab5cd15316d61 100644 --- a/example/servo-afma6/CMakeLists.txt +++ b/example/servo-afma6/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE servoAfma6Ellipse2DCamVelocity.cpp servoAfma6FourPoints2DArtVelocity.cpp servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp @@ -61,14 +61,16 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test -# ADD_TEST(${binary} ${binary}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-afma6/servoAfma62DhalfCamVelocity.cpp b/example/servo-afma6/servoAfma62DhalfCamVelocity.cpp index d7a0f240511fa745ff0d33852354dad5de3cb687..9180438597b16ecd7d34242ec1925cf9e24c0a91 100644 --- a/example/servo-afma6/servoAfma62DhalfCamVelocity.cpp +++ b/example/servo-afma6/servoAfma62DhalfCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma62DhalfCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma62DhalfCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Cylinder2DCamVelocity.cpp b/example/servo-afma6/servoAfma6Cylinder2DCamVelocity.cpp index fcfecf99f45f19fe0c5bcc996c8acc5652ff405e..a45fed980c1ab8fdc49d410863e9ea99f800dda8 100644 --- a/example/servo-afma6/servoAfma6Cylinder2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6Cylinder2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Cylinder2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6Cylinder2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp b/example/servo-afma6/servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp index 77436b2641570ae2825661d27f76d0eaf750c5cb..44429118db3f36d25467e9a8aba1f08b5069a8dd 100644 --- a/example/servo-afma6/servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp +++ b/example/servo-afma6/servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp 4107 2013-02-06 10:04:49Z fspindle $ + * $Id: servoAfma6Cylinder2DCamVelocitySecondaryTask.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Ellipse2DCamVelocity.cpp b/example/servo-afma6/servoAfma6Ellipse2DCamVelocity.cpp index 8452d4b1006106731394c9c04368d84ae708cc11..1accd1a233d4ef0d1e2350fc00a8833c0f44a28e 100644 --- a/example/servo-afma6/servoAfma6Ellipse2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6Ellipse2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Ellipse2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6Ellipse2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6FourPoints2DArtVelocity.cpp b/example/servo-afma6/servoAfma6FourPoints2DArtVelocity.cpp index 42d959e3e78aa00f549117a0a9dea8c8cf766f41..5e1c2cdb494338874a19353b16afad8f28ccfdc5 100644 --- a/example/servo-afma6/servoAfma6FourPoints2DArtVelocity.cpp +++ b/example/servo-afma6/servoAfma6FourPoints2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6FourPoints2DArtVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6FourPoints2DArtVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp b/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp index 952c098142bdeedb05178a697f2c10fbaf4aeff8..debf5ddf0865d59851187654f1111f26c49f4e0c 100644 --- a/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp +++ b/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6FourPoints2DCamVelocityInteractionCurrent.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp b/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp index 0fa9f323bbfa77b3aa05d91cb7195706b4bd912d..d59457f51ea8aedf0517184de691f0edb64bdf61 100644 --- a/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp +++ b/example/servo-afma6/servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6FourPoints2DCamVelocityInteractionDesired.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Line2DCamVelocity.cpp b/example/servo-afma6/servoAfma6Line2DCamVelocity.cpp index 1ea51394ae8b9421c4ae1dc8f76db4be943e48eb..3acfdff3e20b7c272a28d19ccac1576ccfced2d4 100644 --- a/example/servo-afma6/servoAfma6Line2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6Line2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Line2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6Line2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Point2DArtVelocity.cpp b/example/servo-afma6/servoAfma6Point2DArtVelocity.cpp index 43f0aa423d94ef524270b2c7f12ea8893834b428..7f0e31d955b9da8e6d9046c44560c83a2977d649 100644 --- a/example/servo-afma6/servoAfma6Point2DArtVelocity.cpp +++ b/example/servo-afma6/servoAfma6Point2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Point2DArtVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6Point2DArtVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Point2DCamVelocity.cpp b/example/servo-afma6/servoAfma6Point2DCamVelocity.cpp index 695a3dd6edfe43724cf80f440b879f2fb78b5457..6ed0b84aef400d4c53a31a558a9fca55e2233850 100644 --- a/example/servo-afma6/servoAfma6Point2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6Point2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Point2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6Point2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6Points2DCamVelocityEyeToHand.cpp b/example/servo-afma6/servoAfma6Points2DCamVelocityEyeToHand.cpp index 1858d2f6d88193a9fade3446396e7ffc074684fd..10652314030266797561532885faaf3c3d4c9f30 100644 --- a/example/servo-afma6/servoAfma6Points2DCamVelocityEyeToHand.cpp +++ b/example/servo-afma6/servoAfma6Points2DCamVelocityEyeToHand.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6Points2DCamVelocityEyeToHand.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: servoAfma6Points2DCamVelocityEyeToHand.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -284,14 +284,10 @@ int main() vpDisplay::display(I) ; ip.set_i( 265 ); ip.set_j( 150 ); - vpDisplay::displayCharString(I, ip, - "Eye-To-Hand Visual Servoing", - vpColor::green) ; + vpDisplay::displayText(I, ip, "Eye-To-Hand Visual Servoing", vpColor::green) ; ip.set_i( 280 ); ip.set_j( 150 ); - vpDisplay::displayCharString(I, ip, - "IRISA-INRIA Rennes, Lagadic project", - vpColor::green) ; + vpDisplay::displayText(I, ip, "IRISA-INRIA Rennes, Lagadic project", vpColor::green) ; try { for (i=0 ; i < nbPoint ; i++) diff --git a/example/servo-afma6/servoAfma6Segment2DCamVelocity.cpp b/example/servo-afma6/servoAfma6Segment2DCamVelocity.cpp index 867f09f88060df1a43704c2151cbf7428269a2bd..c151bdebf20521cf70b50730edf0137bfd4905e6 100644 --- a/example/servo-afma6/servoAfma6Segment2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6Segment2DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoAfma6Point2DCamVelocity.cpp 3616 2012-03-09 14:31:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6SquareLines2DCamVelocity.cpp b/example/servo-afma6/servoAfma6SquareLines2DCamVelocity.cpp index 10fae34613eca720379c22f521ceafb7574afb47..663fa0feb2e373ab6f362b74263e969ee532a8e3 100644 --- a/example/servo-afma6/servoAfma6SquareLines2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6SquareLines2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6SquareLines2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6SquareLines2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-afma6/servoAfma6TwoLines2DCamVelocity.cpp b/example/servo-afma6/servoAfma6TwoLines2DCamVelocity.cpp index 7bb5006060d708aadc1897b0d8880667adc5a20e..45f8bd8c5ea8fbb286b89633a3768d8f668fac02 100644 --- a/example/servo-afma6/servoAfma6TwoLines2DCamVelocity.cpp +++ b/example/servo-afma6/servoAfma6TwoLines2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoAfma6TwoLines2DCamVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoAfma6TwoLines2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-biclops/CMakeLists.txt b/example/servo-biclops/CMakeLists.txt index 62e38ba63ff5951fd01b7decfde1bda50fcb1917..183f15df7e76abb24cd5d4640a893abdc6930975 100644 --- a/example/servo-biclops/CMakeLists.txt +++ b/example/servo-biclops/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,17 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE moveBiclops.cpp servoBiclopsPoint2DArtVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-biclops/moveBiclops.cpp b/example/servo-biclops/moveBiclops.cpp index a0bc6c6f1c8dac5a631195491fae3371ac7651ff..c02ed031d1eb2633b9455bc3d69d33f45a0c9f5b 100644 --- a/example/servo-biclops/moveBiclops.cpp +++ b/example/servo-biclops/moveBiclops.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: moveBiclops.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: moveBiclops.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-biclops/servoBiclopsPoint2DArtVelocity.cpp b/example/servo-biclops/servoBiclopsPoint2DArtVelocity.cpp index bc30fe207b8b792172aaa4419491789d2303db0a..8450bd3bfcbc623077b0a5738828d1a5facf7ca7 100644 --- a/example/servo-biclops/servoBiclopsPoint2DArtVelocity.cpp +++ b/example/servo-biclops/servoBiclopsPoint2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoBiclopsPoint2DArtVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoBiclopsPoint2DArtVelocity.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,14 +69,13 @@ #include <visp/vpDebug.h> // Debug trace #include <signal.h> #include <stdlib.h> -#if ( defined (VISP_HAVE_BICLOPS) && (defined (VISP_HAVE_DC1394_2) /*|| defined (VISP_HAVE_DC1394_1)*/ || defined(VISP_HAVE_DIRECTSHOW)) ) +#if ( defined (VISP_HAVE_BICLOPS) && (defined (VISP_HAVE_DC1394_2) || defined(VISP_HAVE_DIRECTSHOW)) ) #ifdef VISP_HAVE_PTHREAD # include <pthread.h> #endif #include <visp/vp1394TwoGrabber.h> -//#include <visp/vp1394Grabber.h> #include <visp/vpDirectShowGrabber.h> #include <visp/vpImage.h> #include <visp/vpDisplay.h> @@ -224,9 +223,9 @@ main(int argc, const char ** argv) std::string opt_debugdir; // Set the default output path -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX opt_debugdir = "/tmp"; -#elif WIN32 +#elif defined(_WIN32) opt_debugdir = "C:/temp"; #endif @@ -279,8 +278,6 @@ main(int argc, const char ** argv) #if defined VISP_HAVE_DC1394_2 vp1394TwoGrabber g; - // #elif defined VISP_HAVE_DC1394_1 - // vp1394Grabber g; #elif defined VISP_HAVE_DIRECTSHOW vpDirectShowGrabber g; #endif @@ -302,11 +299,10 @@ main(int argc, const char ** argv) vpDisplayX display(I, 100, 100,"Display X...") ; #elif defined VISP_HAVE_GTK vpDisplayGTK display(I, 100, 100,"Display GTK...") ; -#elif defined WIN32 +#elif defined(_WIN32) vpDisplayGDI display(I, 100, 100,"Display GDI...") ; #endif - try{ vpDisplay::display(I) ; vpDisplay::flush(I) ; diff --git a/example/servo-cycab/servoCycab.cpp b/example/servo-cycab/servoCycab.cpp deleted file mode 100644 index 4d621269288455d303a5facf30c762bea6849754..0000000000000000000000000000000000000000 --- a/example/servo-cycab/servoCycab.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** - * - * $Id: servoCycab.cpp 4056 2013-01-05 13:04:42Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Send a command to the car-like Cycab mobile robot. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ -/*! - \example servoCycab.cpp - - Send a command to the car-like Cycab mobile robot. - -*/ - - - -#include <visp/vpConfig.h> -#include <iostream> -#include <cmath> // std::fabs -#include <limits> // numeric_limits - -#include <signal.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> -using namespace std; - -#ifdef VISP_HAVE_CYCAB -#include <visp/vpRobotCycab.h> -#include <visp/vpMath.h> -#include <visp/vpTime.h> - - -vpRobotCycab *cycab = NULL; -bool bFinish = false; - -bool end = false; - -// The first CTRL-C stop properly the car by decreasing the velocity -// and the steering angle, the second CTRL-C ends the execution -void sighdl(int n) -{ - printf("Received signal %d\n",n); - bFinish=true; - end = true; -} - -#define MAXV 1.5 // velocity in m/S -#define MAXPHI 20.0 // front steering angle in deg -#define MAX_ACC_V 6 // m/s^2 -#define MAX_VEL_PHI 4 // rad/s - -int main() -{ - double v, phi; // Command to send - double vm, phim; // Measures - int kv,kphi; - - cycab = new vpRobotCycab(); - - kv = kphi = 1; - v = 0.0; - phi = 0.0; - signal(SIGINT,sighdl); - signal(SIGTERM,sighdl); - signal(SIGPIPE,sighdl); - - double t0 = vpTime::measureTimeMs(); - double t1 = vpTime::measureTimeMs(); - double tprev; - bool ctrc = false; - double timestamp; - - - while (!end) { - tprev = t1; - t1 = vpTime::measureTimeMs(); - // Measures the velocity and the front steering angle from odometry - cycab->getOdometry(vm, phim, timestamp); // measured values from odometry - - printf("State: t=%.1f s v=%f m/s and phi=%f deg\n\t", - (timestamp-t0)/1000, vm, vpMath::deg(phim)); - - // Compute the command to apply to the car - if (1) { - v+=kv*0.002;if (fabs(v)>=MAXV) kv = -1 * kv; - phi+=kphi*0.002;if (fabs(phi)>= vpMath::rad(MAXPHI)) kphi = -1 * kphi; - } - else { - v=0.1;phi=0; - } - - // Check is CTRL-C is requested - if (bFinish) { - // we stop the Cycab by decreasing the velocity and the steering - // angle to zero - std::cout << "Cycab stop requested" << std::endl; - // Velocity decrease to zero - double sign_v = 0; - //if (vm != 0.) - if (std::fabs(vm) > std::numeric_limits<double>::epsilon()) - sign_v = fabs(vm)/vm; - v = vm - MAX_ACC_V*(t1-tprev)/1000*sign_v; - if (fabs(v) < 0.1) v = 0; - - // Steering decrease to zero - double sign_phi = 0; - //if (phim != 0.) - if (std::fabs(phim) > std::numeric_limits<double>::epsilon()) - sign_phi = fabs(phim)/phim; - phi = phim - MAX_VEL_PHI*(t1-tprev)/1000*sign_phi; - if (fabs(phi) < vpMath::rad(5)) phi = 0; - - // printf("stop requested: vm %f v %f phim %f phi %f sign_phi %f\n", - // vm, v, phim, phi, sign_phi); - //v = 0; - //phi = 0; - } - - // Send the command - printf("Send : v %f m/s and phi %f deg\n", v, vpMath::deg(phi)); - cycab->setCommand(v, phi); - - vpTime::wait(10); - - if (end && (!ctrc)) { end = false; ctrc=true;} - } - std::cout << "The end" << std::endl; - return 0; -} - -#else // VISP_HAVE_CYCAB -int main() -{ - cout << "Sorry no acces to the cycab" << endl; -} -#endif //VISP_HAVE_CYCAB - diff --git a/example/servo-cycab/servoCycabBasic.cpp b/example/servo-cycab/servoCycabBasic.cpp deleted file mode 100644 index 2abfd9d80732bd350ac4006949eaa80f67bc7756..0000000000000000000000000000000000000000 --- a/example/servo-cycab/servoCycabBasic.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** - * - * $Id: servoCycabBasic.cpp 4056 2013-01-05 13:04:42Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Send a command to the car-like Cycab mobile robot. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ -/*! - \example servoCycabBasic.cpp - - Send a command to the car-like Cycab mobile robot. - -*/ - - - -#include "visp/vpConfig.h" -#include "visp/vpRobotCycab.h" -#include "visp/vpMath.h" -#include <iostream> -#ifdef VISP_HAVE_CYCAB - -int main() -{ - try { - double v = 0.1, phi = vpMath::rad(0); - double x, y; - double x_min=2000, x_max=0, y_min=2000, y_max=0; - - vpRobotCycab robot; - - std::cout << "Send the command v: " << v << " m/s and phi: " - << vpMath::deg(phi) << " deg" << std::endl; - while (true) { - - robot.setCommand(v, phi); - robot.getJoystickPosition(x, y); - //std::cout << "x: " << x << " y: " << y << std::endl; - if (x < x_min) x_min = x; - if (y < y_min) y_min = y; - if (x > x_max) x_max = x; - if (y > y_max) y_max = y; - std::cout << "x_min: " << x_min << " x_max: " << x_max - << " y_min: " << y_min << " y_max: " << y_max - << std::endl; - - usleep(10000); - } - } - catch(...) { - std::cerr << "An exception was catched" << std::endl; - - return 1; - } - - return 0; -} - -#else // VISP_HAVE_CYCAB -int main() -{ - std::cout << "Sorry, you don't have access to the Cycab car-like mobile robot." - << std::endl; -} -#endif // VISP_HAVE_CYCAB - diff --git a/example/servo-pioneer/CMakeLists.txt b/example/servo-pioneer/CMakeLists.txt index c7e4908182ba543cebf7b58d77d8e5c20bf13e9b..76781f855d47568e85e4a144aead1189cae1b968 100644 --- a/example/servo-pioneer/CMakeLists.txt +++ b/example/servo-pioneer/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE movePioneer.cpp servoPioneerPoint2DDepth.cpp servoPioneerPoint2DDepthWithoutVpServo.cpp @@ -52,12 +52,17 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-pioneer/movePioneer.cpp b/example/servo-pioneer/movePioneer.cpp index 8a80def971ccc4ae5a438b474fa72644dd4cbfdd..31bb006dc517f37348ccb2a5330eb8ab5fd89760 100644 --- a/example/servo-pioneer/movePioneer.cpp +++ b/example/servo-pioneer/movePioneer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: movePioneer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: movePioneer.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,43 +69,44 @@ int main() */ int main(int argc, char **argv) { - std::cout << "\nWARNING: this program does no sensing or avoiding of obstacles, \n" - "the robot WILL collide with any objects in the way! Make sure the \n" - "robot has approximately 3 meters of free space on all sides.\n" - << std::endl; + try { + std::cout << "\nWARNING: this program does no sensing or avoiding of obstacles, \n" + "the robot WILL collide with any objects in the way! Make sure the \n" + "robot has approximately 3 meters of free space on all sides.\n" + << std::endl; - vpRobotPioneer robot; + vpRobotPioneer robot; - ArArgumentParser parser(&argc, argv); - parser.loadDefaultArguments(); + ArArgumentParser parser(&argc, argv); + parser.loadDefaultArguments(); - // ArRobotConnector connects to the robot, get some initial data from it such as type and name, - // and then loads parameter files for this robot. - ArRobotConnector robotConnector(&parser, &robot); - if(!robotConnector.connectRobot()) - { - ArLog::log(ArLog::Terse, "Could not connect to the robot."); - if(parser.checkHelpAndWarnUnparsed()) + // ArRobotConnector connects to the robot, get some initial data from it such as type and name, + // and then loads parameter files for this robot. + ArRobotConnector robotConnector(&parser, &robot); + if(!robotConnector.connectRobot()) + { + ArLog::log(ArLog::Terse, "Could not connect to the robot."); + if(parser.checkHelpAndWarnUnparsed()) + { + Aria::logOptions(); + Aria::exit(1); + } + } + if (!Aria::parseArgs()) { Aria::logOptions(); - Aria::exit(1); + Aria::shutdown(); + return false; } - } - if (!Aria::parseArgs()) - { - Aria::logOptions(); - Aria::shutdown(); - return false; - } - - std::cout << "Robot connected" << std::endl; - robot.useSonar(false); // disable the sonar device usage - // Robot velocities - vpColVector v(2), v_mes(2); + std::cout << "Robot connected" << std::endl; + robot.useSonar(false); // disable the sonar device usage + + // Robot velocities + vpColVector v(2), v_mes(2); - for (int i=0; i < 100; i++) - { + for (int i=0; i < 100; i++) + { double t = vpTime::measureTimeMs(); v = 0; @@ -120,28 +121,33 @@ int main(int argc, char **argv) std::cout << "Battery=" << robot.getBatteryVoltage() << std::endl; vpTime::wait(t, 40); - } + } - ArLog::log(ArLog::Normal, "simpleMotionCommands: Stopping."); - robot.lock(); - robot.stop(); - robot.unlock(); - ArUtil::sleep(1000); + ArLog::log(ArLog::Normal, "simpleMotionCommands: Stopping."); + robot.lock(); + robot.stop(); + robot.unlock(); + ArUtil::sleep(1000); - robot.lock(); - ArLog::log(ArLog::Normal, "simpleMotionCommands: Pose=(%.2f,%.2f,%.2f), Trans. Vel=%.2f, Rot. Vel=%.2f, Battery=%.2fV", - robot.getX(), robot.getY(), robot.getTh(), robot.getVel(), robot.getRotVel(), robot.getBatteryVoltage()); - robot.unlock(); + robot.lock(); + ArLog::log(ArLog::Normal, "simpleMotionCommands: Pose=(%.2f,%.2f,%.2f), Trans. Vel=%.2f, Rot. Vel=%.2f, Battery=%.2fV", + robot.getX(), robot.getY(), robot.getTh(), robot.getVel(), robot.getRotVel(), robot.getBatteryVoltage()); + robot.unlock(); - std::cout << "Ending robot thread..." << std::endl; - robot.stopRunning(); + std::cout << "Ending robot thread..." << std::endl; + robot.stopRunning(); - // wait for the thread to stop - robot.waitForRunExit(); + // wait for the thread to stop + robot.waitForRunExit(); - // exit - ArLog::log(ArLog::Normal, "simpleMotionCommands: Exiting."); - return 0; + // exit + ArLog::log(ArLog::Normal, "simpleMotionCommands: Exiting."); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #endif diff --git a/example/servo-pioneer/servoPioneerPanSegment3D.cpp b/example/servo-pioneer/servoPioneerPanSegment3D.cpp index 4b508eccb81b3b1f094cf6a5d81d963596d9f0a8..8b3296e5d34dca6334eb51a4816e8920450e17d5 100644 --- a/example/servo-pioneer/servoPioneerPanSegment3D.cpp +++ b/example/servo-pioneer/servoPioneerPanSegment3D.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoPioneerPanSegment3D.cpp 4258 2013-05-15 15:54:05Z fspindle $ + * $Id: servoPioneerPanSegment3D.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -86,326 +86,331 @@ int main(int argc, char **argv) { #if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394) #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) - vpImage<unsigned char> I; // Create a gray level image container - double lambda = 0.1; - // Scale parameter used to estimate the depth Z of the blob from its surface - //double coef = 0.9/14.85; // At 0.9m, the blob has a surface of 14.85 (Logitec sphere) - double coef = 1.2/13.0; // At 1m, the blob has a surface of 11.3 (AVT Pike 032C) - double L = 0.21; // 3D horizontal segment length - double Z_d = 0.8; // Desired distance along Z between camera and segment - bool normalized = true; // segment normilized features are used - - // Warning: To have a non singular task of rank 3, Y_d should be different from 0 so that - // the optical axis doesn't intersect the horizontal segment - double Y_d = -.11; // Desired distance along Y between camera and segment. - vpColVector qm(2); // Measured head position - qm = 0; - double qm_pan = 0; // Measured pan position (tilt is not handled in that example) + try { + vpImage<unsigned char> I; // Create a gray level image container + double lambda = 0.1; + // Scale parameter used to estimate the depth Z of the blob from its surface + //double coef = 0.9/14.85; // At 0.9m, the blob has a surface of 14.85 (Logitec sphere) + double coef = 1.2/13.0; // At 1m, the blob has a surface of 11.3 (AVT Pike 032C) + double L = 0.21; // 3D horizontal segment length + double Z_d = 0.8; // Desired distance along Z between camera and segment + bool normalized = true; // segment normilized features are used + + // Warning: To have a non singular task of rank 3, Y_d should be different from 0 so that + // the optical axis doesn't intersect the horizontal segment + double Y_d = -.11; // Desired distance along Y between camera and segment. + vpColVector qm(2); // Measured head position + qm = 0; + double qm_pan = 0; // Measured pan position (tilt is not handled in that example) #ifdef USE_REAL_ROBOT - // Initialize the biclops head - - vpRobotBiclops biclops("/usr/share/BiclopsDefault.cfg"); - biclops.setDenavitHartenbergModel(vpBiclops::DH1); - - // Move to the initial position - vpColVector q(2); - - q=0; -// q[0] = vpMath::rad(63); -// q[1] = vpMath::rad(12); // introduce a tilt angle to compensate camera sphere tilt so that the camera is parallel to the plane - - biclops.setRobotState(vpRobot::STATE_POSITION_CONTROL) ; - biclops.setPosition( vpRobot::ARTICULAR_FRAME, q ); - //biclops.setPositioningVelocity(50); - biclops.getPosition(vpRobot::ARTICULAR_FRAME, qm); - qm_pan = qm[0]; - - // Now the head will be controlled in velocity - biclops.setRobotState(vpRobot::STATE_VELOCITY_CONTROL) ; - - // Initialize the pioneer robot - vpRobotPioneer pioneer; - ArArgumentParser parser(&argc, argv); - parser.loadDefaultArguments(); - - // ArRobotConnector connects to the robot, get some initial data from it such as type and name, - // and then loads parameter files for this robot. - ArRobotConnector robotConnector(&parser, &pioneer); - if(!robotConnector.connectRobot()) - { - ArLog::log(ArLog::Terse, "Could not connect to the pioneer robot."); - if(parser.checkHelpAndWarnUnparsed()) + // Initialize the biclops head + + vpRobotBiclops biclops("/usr/share/BiclopsDefault.cfg"); + biclops.setDenavitHartenbergModel(vpBiclops::DH1); + + // Move to the initial position + vpColVector q(2); + + q=0; + // q[0] = vpMath::rad(63); + // q[1] = vpMath::rad(12); // introduce a tilt angle to compensate camera sphere tilt so that the camera is parallel to the plane + + biclops.setRobotState(vpRobot::STATE_POSITION_CONTROL) ; + biclops.setPosition( vpRobot::ARTICULAR_FRAME, q ); + //biclops.setPositioningVelocity(50); + biclops.getPosition(vpRobot::ARTICULAR_FRAME, qm); + qm_pan = qm[0]; + + // Now the head will be controlled in velocity + biclops.setRobotState(vpRobot::STATE_VELOCITY_CONTROL) ; + + // Initialize the pioneer robot + vpRobotPioneer pioneer; + ArArgumentParser parser(&argc, argv); + parser.loadDefaultArguments(); + + // ArRobotConnector connects to the robot, get some initial data from it such as type and name, + // and then loads parameter files for this robot. + ArRobotConnector robotConnector(&parser, &pioneer); + if(!robotConnector.connectRobot()) + { + ArLog::log(ArLog::Terse, "Could not connect to the pioneer robot."); + if(parser.checkHelpAndWarnUnparsed()) + { + Aria::logOptions(); + Aria::exit(1); + } + } + if (!Aria::parseArgs()) { Aria::logOptions(); - Aria::exit(1); + Aria::shutdown(); + return false; } - } - if (!Aria::parseArgs()) - { - Aria::logOptions(); - Aria::shutdown(); - return false; - } - pioneer.useSonar(false); // disable the sonar device usage + pioneer.useSonar(false); // disable the sonar device usage - // Wait 3 sec to be sure that the low level Aria thread used to control - // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) - // between the velocity send to the robot and the velocity that is really applied - // to the wheels. - sleep(3); + // Wait 3 sec to be sure that the low level Aria thread used to control + // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) + // between the velocity send to the robot and the velocity that is really applied + // to the wheels. + sleep(3); - std::cout << "Pioneer robot connected" << std::endl; + std::cout << "Pioneer robot connected" << std::endl; #endif - vpPioneerPan robot_pan; // Generic robot that computes the velocities for the pioneer and the biclops head + vpPioneerPan robot_pan; // Generic robot that computes the velocities for the pioneer and the biclops head - // Camera parameters. In this experiment we don't need a precise calibration of the camera - vpCameraParameters cam; + // Camera parameters. In this experiment we don't need a precise calibration of the camera + vpCameraParameters cam; - // Create the camera framegrabber + // Create the camera framegrabber #if defined(VISP_HAVE_V4L2) - // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) - vpV4l2Grabber g; - g.setScale(1); - g.setInput(0); - g.setDevice("/dev/video1"); - g.open(I); - // Logitec sphere parameters - cam.initPersProjWithoutDistortion(558, 555, 312, 210); + // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) + vpV4l2Grabber g; + g.setScale(1); + g.setInput(0); + g.setDevice("/dev/video1"); + g.open(I); + // Logitec sphere parameters + cam.initPersProjWithoutDistortion(558, 555, 312, 210); #elif defined(VISP_HAVE_DC1394_2) - // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) - vp1394TwoGrabber g(false); - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) + vp1394TwoGrabber g(false); + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #elif defined(VISP_HAVE_CMU1394) - // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) - vp1394CMUGrabber g; - g.setVideoMode(0, 5); // 640x480 MONO8 - g.setFramerate(4); // 30 Hz - g.open(I); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) + vp1394CMUGrabber g; + g.setVideoMode(0, 5); // 640x480 MONO8 + g.setFramerate(4); // 30 Hz + g.open(I); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #endif - // Acquire an image from the grabber - g.acquire(I); + // Acquire an image from the grabber + g.acquire(I); - // Create an image viewer + // Create an image viewer #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 10, 10, "Current frame"); + vpDisplayX d(I, 10, 10, "Current frame"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 10, 10, "Current frame"); + vpDisplayGDI d(I, 10, 10, "Current frame"); #endif - vpDisplay::display(I); - vpDisplay::flush(I); - - // The 3D segment consists in two horizontal dots - vpDot2 dot[2]; - for (int i=0; i <2; i++) - { - dot[i].setGraphics(true); - dot[i].setComputeMoments(true); - dot[i].setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape - dot[i].setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation - dot[i].setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level - dot[i].initTracking(I); + vpDisplay::display(I); vpDisplay::flush(I); - } - vpServo task; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; - task.setLambda(lambda) ; - vpVelocityTwistMatrix cVe ; // keep to identity - cVe = robot_pan.get_cVe() ; - task.set_cVe(cVe) ; - - std::cout << "cVe: \n" << cVe << std::endl; - - vpMatrix eJe; - - // Update the robot jacobian that depends on the pan position - robot_pan.set_eJe(qm_pan); - // Get the robot jacobian - eJe = robot_pan.get_eJe() ; - task.set_eJe(eJe) ; - std::cout << "eJe: \n" << eJe << std::endl; - - // Define a 3D horizontal segment an its cordinates in the image plane - vpPoint P[2]; - P[0].setWorldCoordinates(-L/2, 0, 0); - P[1].setWorldCoordinates( L/2, 0, 0); - // Define the desired camera position - vpHomogeneousMatrix cMo(0, Y_d, Z_d, 0, 0, 0); // Here we are in front of the segment - for (int i=0; i <2; i++) - { - P[i].changeFrame(cMo); - P[i].project(); // Here the x,y parameters obtained by perspective projection are computed - } + // The 3D segment consists in two horizontal dots + vpDot2 dot[2]; + for (int i=0; i <2; i++) + { + dot[i].setGraphics(true); + dot[i].setComputeMoments(true); + dot[i].setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape + dot[i].setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation + dot[i].setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level + dot[i].initTracking(I); + vpDisplay::flush(I); + } - // Estimate the depth of the segment extremity points - double surface[2]; - double Z[2]; // Depth of the segment points - for (int i=0; i<2; i++) - { - // Surface of the blob estimated from the image moment m00 and converted in meters - surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py())); + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; + task.setLambda(lambda) ; + vpVelocityTwistMatrix cVe ; // keep to identity + cVe = robot_pan.get_cVe() ; + task.set_cVe(cVe) ; + + std::cout << "cVe: \n" << cVe << std::endl; + + vpMatrix eJe; + + // Update the robot jacobian that depends on the pan position + robot_pan.set_eJe(qm_pan); + // Get the robot jacobian + eJe = robot_pan.get_eJe() ; + task.set_eJe(eJe) ; + std::cout << "eJe: \n" << eJe << std::endl; + + // Define a 3D horizontal segment an its cordinates in the image plane + vpPoint P[2]; + P[0].setWorldCoordinates(-L/2, 0, 0); + P[1].setWorldCoordinates( L/2, 0, 0); + // Define the desired camera position + vpHomogeneousMatrix cMo(0, Y_d, Z_d, 0, 0, 0); // Here we are in front of the segment + for (int i=0; i <2; i++) + { + P[i].changeFrame(cMo); + P[i].project(); // Here the x,y parameters obtained by perspective projection are computed + } - // Initial depth of the blob - Z[i] = coef * surface[i] ; - } + // Estimate the depth of the segment extremity points + double surface[2]; + double Z[2]; // Depth of the segment points + for (int i=0; i<2; i++) + { + // Surface of the blob estimated from the image moment m00 and converted in meters + surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py())); + + // Initial depth of the blob + Z[i] = coef * surface[i] ; + } - // Use here a feature segment builder - vpFeatureSegment s_segment(normalized), s_segment_d(normalized); // From the segment feature we use only alpha - vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]); - s_segment.setZ1(Z[0]); - s_segment.setZ2(Z[1]); - // Set the desired feature - vpFeatureBuilder::create(s_segment_d, P[0], P[1]); - s_segment.setZ1( P[0].get_Z() ); // Desired depth - s_segment.setZ2( P[1].get_Z() ); - - task.addFeature(s_segment, s_segment_d, - vpFeatureSegment::selectXc() | - vpFeatureSegment::selectL() | - vpFeatureSegment::selectAlpha()); + // Use here a feature segment builder + vpFeatureSegment s_segment(normalized), s_segment_d(normalized); // From the segment feature we use only alpha + vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]); + s_segment.setZ1(Z[0]); + s_segment.setZ2(Z[1]); + // Set the desired feature + vpFeatureBuilder::create(s_segment_d, P[0], P[1]); + s_segment.setZ1( P[0].get_Z() ); // Desired depth + s_segment.setZ2( P[1].get_Z() ); + + task.addFeature(s_segment, s_segment_d, + vpFeatureSegment::selectXc() | + vpFeatureSegment::selectL() | + vpFeatureSegment::selectAlpha()); #ifdef USE_PLOTTER - //Create a window (500 by 500) at position (700, 10) with two graphics - vpPlot graph(2, 500, 500, 700, 10, "Curves..."); - - //The first graphic contains 3 curve and the second graphic contains 3 curves - graph.initGraph(0,3); - graph.initGraph(1,3); - graph.setTitle(0, "Velocities"); - graph.setTitle(1, "Error s-s*"); - graph.setLegend(0, 0, "vx"); - graph.setLegend(0, 1, "wz"); - graph.setLegend(0, 2, "w_pan"); - graph.setLegend(1, 0, "xm/l"); - graph.setLegend(1, 1, "1/l"); - graph.setLegend(1, 2, "alpha"); + //Create a window (500 by 500) at position (700, 10) with two graphics + vpPlot graph(2, 500, 500, 700, 10, "Curves..."); + + //The first graphic contains 3 curve and the second graphic contains 3 curves + graph.initGraph(0,3); + graph.initGraph(1,3); + graph.setTitle(0, "Velocities"); + graph.setTitle(1, "Error s-s*"); + graph.setLegend(0, 0, "vx"); + graph.setLegend(0, 1, "wz"); + graph.setLegend(0, 2, "w_pan"); + graph.setLegend(1, 0, "xm/l"); + graph.setLegend(1, 1, "1/l"); + graph.setLegend(1, 2, "alpha"); #endif - vpColVector v; // vz, wx - unsigned int iter = 0; - try - { - while(1) + vpColVector v; // vz, wx + unsigned int iter = 0; + try { + while(1) + { #ifdef USE_REAL_ROBOT - // Get the new pan position - biclops.getPosition(vpRobot::ARTICULAR_FRAME, qm); + // Get the new pan position + biclops.getPosition(vpRobot::ARTICULAR_FRAME, qm); #endif - qm_pan = qm[0]; - - // Acquire a new image - g.acquire(I); - // Set the image as background of the viewer - vpDisplay::display(I); - - // Display the desired position of the segment - for (int i=0; i<2; i++) - P[i].display(I, cam, vpColor::red, 3); - - // Does the blob tracking - for (int i=0; i<2; i++) - dot[i].track(I); - - for (int i=0; i<2; i++) - { - // Surface of the blob estimated from the image moment m00 and converted in meters - surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py())); - - // Initial depth of the blob - Z[i] = coef * surface[i] ; - } - - // Update the features - vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]); - // Update the depth of the point. Useful only if current interaction matrix is used - // when task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) is set - s_segment.setZ1(Z[0]); - s_segment.setZ2(Z[1]); - - robot_pan.get_cVe(cVe); - task.set_cVe(cVe); - - // Update the robot jacobian that depends on the pan position - robot_pan.set_eJe(qm_pan); - // Get the robot jacobian - eJe = robot_pan.get_eJe(); - // Update the jacobian that will be used to compute the control law - task.set_eJe(eJe); - - // Compute the control law. Velocities are computed in the mobile robot reference frame - v = task.computeControlLaw(); - -// std::cout << "-----" << std::endl; -// std::cout << "v: " << v.t() << std::endl; -// std::cout << "error: " << task.getError().t() << std::endl; -// std::cout << "L:\n " << task.getInteractionMatrix() << std::endl; -// std::cout << "eJe:\n " << task.get_eJe() << std::endl; -// std::cout << "cVe:\n " << task.get_cVe() << std::endl; -// std::cout << "L_cVe_eJe:\n" << task.getInteractionMatrix() * task.get_cVe() * task.get_eJe() << std::endl; -// task.print() ; - if (task.getTaskRank() != 3) - std::cout << "Warning: task is of rank " << task.getTaskRank() << std::endl; + qm_pan = qm[0]; + + // Acquire a new image + g.acquire(I); + // Set the image as background of the viewer + vpDisplay::display(I); + + // Display the desired position of the segment + for (int i=0; i<2; i++) + P[i].display(I, cam, vpColor::red, 3); + + // Does the blob tracking + for (int i=0; i<2; i++) + dot[i].track(I); + + for (int i=0; i<2; i++) + { + // Surface of the blob estimated from the image moment m00 and converted in meters + surface[i] = 1./sqrt(dot[i].m00/(cam.get_px()*cam.get_py())); + + // Initial depth of the blob + Z[i] = coef * surface[i] ; + } + + // Update the features + vpFeatureBuilder::create(s_segment, cam, dot[0], dot[1]); + // Update the depth of the point. Useful only if current interaction matrix is used + // when task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE) is set + s_segment.setZ1(Z[0]); + s_segment.setZ2(Z[1]); + + robot_pan.get_cVe(cVe); + task.set_cVe(cVe); + + // Update the robot jacobian that depends on the pan position + robot_pan.set_eJe(qm_pan); + // Get the robot jacobian + eJe = robot_pan.get_eJe(); + // Update the jacobian that will be used to compute the control law + task.set_eJe(eJe); + + // Compute the control law. Velocities are computed in the mobile robot reference frame + v = task.computeControlLaw(); + + // std::cout << "-----" << std::endl; + // std::cout << "v: " << v.t() << std::endl; + // std::cout << "error: " << task.getError().t() << std::endl; + // std::cout << "L:\n " << task.getInteractionMatrix() << std::endl; + // std::cout << "eJe:\n " << task.get_eJe() << std::endl; + // std::cout << "cVe:\n " << task.get_cVe() << std::endl; + // std::cout << "L_cVe_eJe:\n" << task.getInteractionMatrix() * task.get_cVe() * task.get_eJe() << std::endl; + // task.print() ; + if (task.getTaskRank() != 3) + std::cout << "Warning: task is of rank " << task.getTaskRank() << std::endl; #ifdef USE_PLOTTER - graph.plot(0, iter, v); // plot velocities applied to the robot - graph.plot(1, iter, task.getError()); // plot error vector + graph.plot(0, iter, v); // plot velocities applied to the robot + graph.plot(1, iter, task.getError()); // plot error vector #endif #ifdef USE_REAL_ROBOT - // Send the velocity to the robot - vpColVector v_pioneer(2); // vx, wz - v_pioneer[0] = v[0]; - v_pioneer[1] = v[1]; - vpColVector v_biclops(2); // qdot pan and tilt - v_biclops[0] = v[2]; - v_biclops[1] = 0; - - std::cout << "Send velocity to the pionner: " << v_pioneer[0] << " m/s " - << vpMath::deg(v_pioneer[1]) << " deg/s" << std::endl; - std::cout << "Send velocity to the biclops head: " << vpMath::deg(v_biclops[0]) << " deg/s" << std::endl; - - pioneer.setVelocity(vpRobot::REFERENCE_FRAME, v_pioneer); - biclops.setVelocity(vpRobot::ARTICULAR_FRAME, v_biclops) ; + // Send the velocity to the robot + vpColVector v_pioneer(2); // vx, wz + v_pioneer[0] = v[0]; + v_pioneer[1] = v[1]; + vpColVector v_biclops(2); // qdot pan and tilt + v_biclops[0] = v[2]; + v_biclops[1] = 0; + + std::cout << "Send velocity to the pionner: " << v_pioneer[0] << " m/s " + << vpMath::deg(v_pioneer[1]) << " deg/s" << std::endl; + std::cout << "Send velocity to the biclops head: " << vpMath::deg(v_biclops[0]) << " deg/s" << std::endl; + + pioneer.setVelocity(vpRobot::REFERENCE_FRAME, v_pioneer); + biclops.setVelocity(vpRobot::ARTICULAR_FRAME, v_biclops) ; #endif - // Draw a vertical line which corresponds to the desired x coordinate of the dot cog - vpDisplay::displayLine(I, 0, cam.get_u0(), 479, cam.get_u0(), vpColor::red); - vpDisplay::flush(I); + // Draw a vertical line which corresponds to the desired x coordinate of the dot cog + vpDisplay::displayLine(I, 0, cam.get_u0(), 479, cam.get_u0(), vpColor::red); + vpDisplay::flush(I); - // A click in the viewer to exit - if ( vpDisplay::getClick(I, false) ) - break; + // A click in the viewer to exit + if ( vpDisplay::getClick(I, false) ) + break; - iter ++; - //break; + iter ++; + //break; + } + } + catch(...) + { } - } - catch(...) - { - } #ifdef USE_REAL_ROBOT - std::cout << "Ending robot thread..." << std::endl; - pioneer.stopRunning(); + std::cout << "Ending robot thread..." << std::endl; + pioneer.stopRunning(); - // wait for the thread to stop - pioneer.waitForRunExit(); + // wait for the thread to stop + pioneer.waitForRunExit(); #endif - // Kill the servo task - task.print() ; - task.kill(); - + // Kill the servo task + task.print() ; + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } #endif #endif } diff --git a/example/servo-pioneer/servoPioneerPoint2DDepth.cpp b/example/servo-pioneer/servoPioneerPoint2DDepth.cpp index b2f57cfc8f0afae94391bc886c3d271a5d8c51b8..73e89af79f76d9bd1122eeec40fa652ff6336879 100644 --- a/example/servo-pioneer/servoPioneerPoint2DDepth.cpp +++ b/example/servo-pioneer/servoPioneerPoint2DDepth.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoPioneerPoint2DDepth.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoPioneerPoint2DDepth.cpp 5128 2015-01-06 11:46:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <visp/vpServo.h> #include <visp/vpVelocityTwistMatrix.h> -#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394) || defined(VISP_HAVE_OPENCV) +#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394) || (VISP_HAVE_OPENCV_VERSION >= 0x020100) #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) #if defined(VISP_HAVE_PIONEER) # define TEST_COULD_BE_ACHIEVED @@ -93,169 +93,168 @@ #ifdef TEST_COULD_BE_ACHIEVED int main(int argc, char **argv) { - vpImage<unsigned char> I; // Create a gray level image container - double depth = 1.; - double lambda = 0.6; - double coef = 1./6.77; // Scale parameter used to estimate the depth Z of the blob from its surface - - vpRobotPioneer robot; - ArArgumentParser parser(&argc, argv); - parser.loadDefaultArguments(); - - // ArRobotConnector connects to the robot, get some initial data from it such as type and name, - // and then loads parameter files for this robot. - ArRobotConnector robotConnector(&parser, &robot); - if(!robotConnector.connectRobot()) - { - ArLog::log(ArLog::Terse, "Could not connect to the robot."); - if(parser.checkHelpAndWarnUnparsed()) + try { + vpImage<unsigned char> I; // Create a gray level image container + double depth = 1.; + double lambda = 0.6; + double coef = 1./6.77; // Scale parameter used to estimate the depth Z of the blob from its surface + + vpRobotPioneer robot; + ArArgumentParser parser(&argc, argv); + parser.loadDefaultArguments(); + + // ArRobotConnector connects to the robot, get some initial data from it such as type and name, + // and then loads parameter files for this robot. + ArRobotConnector robotConnector(&parser, &robot); + if(!robotConnector.connectRobot()) + { + ArLog::log(ArLog::Terse, "Could not connect to the robot."); + if(parser.checkHelpAndWarnUnparsed()) + { + Aria::logOptions(); + Aria::exit(1); + } + } + if (!Aria::parseArgs()) { Aria::logOptions(); - Aria::exit(1); + Aria::shutdown(); + return false; } - } - if (!Aria::parseArgs()) - { - Aria::logOptions(); - Aria::shutdown(); - return false; - } - // Wait 3 sec to be sure that the low level Aria thread used to control - // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) - // between the velocity send to the robot and the velocity that is really applied - // to the wheels. - vpTime::sleepMs(3000); + // Wait 3 sec to be sure that the low level Aria thread used to control + // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) + // between the velocity send to the robot and the velocity that is really applied + // to the wheels. + vpTime::sleepMs(3000); - std::cout << "Robot connected" << std::endl; + std::cout << "Robot connected" << std::endl; - // Camera parameters. In this experiment we don't need a precise calibration of the camera - vpCameraParameters cam; + // Camera parameters. In this experiment we don't need a precise calibration of the camera + vpCameraParameters cam; - // Create the camera framegrabber + // Create the camera framegrabber #if defined(VISP_HAVE_OPENCV) - int device = 1; - std::cout << "Use device: " << device << std::endl; - cv::VideoCapture g(device); // open the default camera - g.set(CV_CAP_PROP_FRAME_WIDTH, 640); - g.set(CV_CAP_PROP_FRAME_HEIGHT, 480); - if(!g.isOpened()) // check if we succeeded - return -1; - cv::Mat frame; - g >> frame; // get a new frame from camera - vpImageConvert::convert(frame, I); - - // Logitec sphere parameters - cam.initPersProjWithoutDistortion(558, 555, 312, 210); + int device = 1; + std::cout << "Use device: " << device << std::endl; + cv::VideoCapture g(device); // open the default camera + g.set(CV_CAP_PROP_FRAME_WIDTH, 640); + g.set(CV_CAP_PROP_FRAME_HEIGHT, 480); + if(!g.isOpened()) // check if we succeeded + return -1; + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); + + // Logitec sphere parameters + cam.initPersProjWithoutDistortion(558, 555, 312, 210); #elif defined(VISP_HAVE_V4L2) - // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) - vpV4l2Grabber g; - g.setScale(1); - g.setInput(0); - g.setDevice("/dev/video1"); - g.open(I); - // Logitec sphere parameters - cam.initPersProjWithoutDistortion(558, 555, 312, 210); + // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) + vpV4l2Grabber g; + g.setScale(1); + g.setInput(0); + g.setDevice("/dev/video1"); + g.open(I); + // Logitec sphere parameters + cam.initPersProjWithoutDistortion(558, 555, 312, 210); #elif defined(VISP_HAVE_DC1394_2) - // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) - vp1394TwoGrabber g(false); - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) + vp1394TwoGrabber g(false); + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #elif defined(VISP_HAVE_CMU1394) - // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) - vp1394CMUGrabber g; - g.setVideoMode(0, 5); // 640x480 MONO8 - g.setFramerate(4); // 30 Hz - g.open(I); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) + vp1394CMUGrabber g; + g.setVideoMode(0, 5); // 640x480 MONO8 + g.setFramerate(4); // 30 Hz + g.open(I); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #endif - // Acquire an image from the grabber + // Acquire an image from the grabber #if defined(VISP_HAVE_OPENCV) - g >> frame; // get a new frame from camera - vpImageConvert::convert(frame, I); + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #else - g.acquire(I); + g.acquire(I); #endif - // Create an image viewer + // Create an image viewer #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 10, 10, "Current frame"); + vpDisplayX d(I, 10, 10, "Current frame"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 10, 10, "Current frame"); + vpDisplayGDI d(I, 10, 10, "Current frame"); #endif - vpDisplay::display(I); - vpDisplay::flush(I); - - // Create a blob tracker - vpDot2 dot; - dot.setGraphics(true); - dot.setComputeMoments(true); - dot.setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape - dot.setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation - dot.setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level - dot.initTracking(I); - vpDisplay::flush(I); - - vpServo task; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; - task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; - task.setLambda(lambda) ; - vpVelocityTwistMatrix cVe ; - cVe = robot.get_cVe() ; - task.set_cVe(cVe) ; - - std::cout << "cVe: \n" << cVe << std::endl; - - vpMatrix eJe; - robot.get_eJe(eJe) ; - task.set_eJe(eJe) ; - std::cout << "eJe: \n" << eJe << std::endl; - - // Current and desired visual feature associated to the x coordinate of the point - vpFeaturePoint s_x, s_xd; - - // Create the current x visual feature - vpFeatureBuilder::create(s_x, cam, dot); - - // Create the desired x* visual feature - s_xd.buildFrom(0, 0, depth); - - // Add the feature - task.addFeature(s_x, s_xd) ; - - // Create the current log(Z/Z*) visual feature - vpFeatureDepth s_Z, s_Zd; - // Surface of the blob estimated from the image moment m00 and converted in meters - double surface = 1./sqrt(dot.m00/(cam.get_px()*cam.get_py())); - double Z, Zd; - // Initial depth of the blob in from of the camera - Z = coef * surface ; - // Desired depth Z* of the blob. This depth is learned and equal to the initial depth - Zd = Z; - - std::cout << "Z " << Z << std::endl; - s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 - s_Zd.buildFrom(s_x.get_x(), s_x.get_y(), Zd , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 - - // Add the feature - task.addFeature(s_Z, s_Zd) ; - - vpColVector v; // vz, wx - - try - { + vpDisplay::display(I); + vpDisplay::flush(I); + + // Create a blob tracker + vpDot2 dot; + dot.setGraphics(true); + dot.setComputeMoments(true); + dot.setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape + dot.setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation + dot.setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level + dot.initTracking(I); + vpDisplay::flush(I); + + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe) ; + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE) ; + task.setLambda(lambda) ; + vpVelocityTwistMatrix cVe ; + cVe = robot.get_cVe() ; + task.set_cVe(cVe) ; + + std::cout << "cVe: \n" << cVe << std::endl; + + vpMatrix eJe; + robot.get_eJe(eJe) ; + task.set_eJe(eJe) ; + std::cout << "eJe: \n" << eJe << std::endl; + + // Current and desired visual feature associated to the x coordinate of the point + vpFeaturePoint s_x, s_xd; + + // Create the current x visual feature + vpFeatureBuilder::create(s_x, cam, dot); + + // Create the desired x* visual feature + s_xd.buildFrom(0, 0, depth); + + // Add the feature + task.addFeature(s_x, s_xd) ; + + // Create the current log(Z/Z*) visual feature + vpFeatureDepth s_Z, s_Zd; + // Surface of the blob estimated from the image moment m00 and converted in meters + double surface = 1./sqrt(dot.m00/(cam.get_px()*cam.get_py())); + double Z, Zd; + // Initial depth of the blob in from of the camera + Z = coef * surface ; + // Desired depth Z* of the blob. This depth is learned and equal to the initial depth + Zd = Z; + + std::cout << "Z " << Z << std::endl; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 + s_Zd.buildFrom(s_x.get_x(), s_x.get_y(), Zd , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 + + // Add the feature + task.addFeature(s_Z, s_Zd) ; + + vpColVector v; // vz, wx + while(1) { // Acquire a new image #if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) - g >> frame; // get a new frame from camera - vpImageConvert::convert(frame, I); + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #else - g.acquire(I); + g.acquire(I); #endif // Set the image as background of the viewer vpDisplay::display(I); @@ -293,20 +292,21 @@ int main(int argc, char **argv) if ( vpDisplay::getClick(I, false) ) break; } - } - catch(...) - { - } - std::cout << "Ending robot thread..." << std::endl; - robot.stopRunning(); + std::cout << "Ending robot thread..." << std::endl; + robot.stopRunning(); - // wait for the thread to stop - robot.waitForRunExit(); + // wait for the thread to stop + robot.waitForRunExit(); - // Kill the servo task - task.print() ; - task.kill(); + // Kill the servo task + task.print() ; + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int main() diff --git a/example/servo-pioneer/servoPioneerPoint2DDepthWithoutVpServo.cpp b/example/servo-pioneer/servoPioneerPoint2DDepthWithoutVpServo.cpp index 3e7f0437116767d0bf50322aa87e1469acdacdce..f43906e70599a17f9d4c0d9471b72bd4b6af9078 100644 --- a/example/servo-pioneer/servoPioneerPoint2DDepthWithoutVpServo.cpp +++ b/example/servo-pioneer/servoPioneerPoint2DDepthWithoutVpServo.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoPioneerPoint2DDepthWithoutVpServo.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoPioneerPoint2DDepthWithoutVpServo.cpp 5128 2015-01-06 11:46:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ #include <visp/vpV4l2Grabber.h> #include <visp/vpVelocityTwistMatrix.h> -#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394) || defined(VISP_HAVE_OPENCV) +#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394) || (VISP_HAVE_OPENCV_VERSION >= 0x020100) #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) #if defined(VISP_HAVE_PIONEER) # define TEST_COULD_BE_ACHIEVED @@ -91,150 +91,149 @@ #ifdef TEST_COULD_BE_ACHIEVED int main(int argc, char **argv) { - vpImage<unsigned char> I; // Create a gray level image container - double depth = 1.; - double lambda = 0.6; - double coef = 1./6.77; // Scale parameter used to estimate the depth Z of the blob from its surface - - vpRobotPioneer robot; - ArArgumentParser parser(&argc, argv); - parser.loadDefaultArguments(); - - // ArRobotConnector connects to the robot, get some initial data from it such as type and name, - // and then loads parameter files for this robot. - ArRobotConnector robotConnector(&parser, &robot); - if(!robotConnector.connectRobot()) - { - ArLog::log(ArLog::Terse, "Could not connect to the robot."); - if(parser.checkHelpAndWarnUnparsed()) + try { + vpImage<unsigned char> I; // Create a gray level image container + double depth = 1.; + double lambda = 0.6; + double coef = 1./6.77; // Scale parameter used to estimate the depth Z of the blob from its surface + + vpRobotPioneer robot; + ArArgumentParser parser(&argc, argv); + parser.loadDefaultArguments(); + + // ArRobotConnector connects to the robot, get some initial data from it such as type and name, + // and then loads parameter files for this robot. + ArRobotConnector robotConnector(&parser, &robot); + if(!robotConnector.connectRobot()) + { + ArLog::log(ArLog::Terse, "Could not connect to the robot."); + if(parser.checkHelpAndWarnUnparsed()) + { + Aria::logOptions(); + Aria::exit(1); + } + } + if (!Aria::parseArgs()) { Aria::logOptions(); - Aria::exit(1); + Aria::shutdown(); + return false; } - } - if (!Aria::parseArgs()) - { - Aria::logOptions(); - Aria::shutdown(); - return false; - } - // Wait 3 sec to be sure that the low level Aria thread used to control - // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) - // between the velocity send to the robot and the velocity that is really applied - // to the wheels. - vpTime::sleepMs(3000); + // Wait 3 sec to be sure that the low level Aria thread used to control + // the robot is started. Without this delay we experienced a delay (arround 2.2 sec) + // between the velocity send to the robot and the velocity that is really applied + // to the wheels. + vpTime::sleepMs(3000); - std::cout << "Robot connected" << std::endl; + std::cout << "Robot connected" << std::endl; - // Camera parameters. In this experiment we don't need a precise calibration of the camera - vpCameraParameters cam; + // Camera parameters. In this experiment we don't need a precise calibration of the camera + vpCameraParameters cam; - // Create the camera framegrabber + // Create the camera framegrabber #if defined(VISP_HAVE_OPENCV) - int device = 1; - std::cout << "Use device: " << device << std::endl; - cv::VideoCapture g(device); // open the default camera - g.set(CV_CAP_PROP_FRAME_WIDTH, 640); - g.set(CV_CAP_PROP_FRAME_HEIGHT, 480); - if(!g.isOpened()) // check if we succeeded - return -1; - cv::Mat frame; - g >> frame; // get a new frame from camera - vpImageConvert::convert(frame, I); - - // Logitec sphere parameters - cam.initPersProjWithoutDistortion(558, 555, 312, 210); + int device = 1; + std::cout << "Use device: " << device << std::endl; + cv::VideoCapture g(device); // open the default camera + g.set(CV_CAP_PROP_FRAME_WIDTH, 640); + g.set(CV_CAP_PROP_FRAME_HEIGHT, 480); + if(!g.isOpened()) // check if we succeeded + return -1; + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); + + // Logitec sphere parameters + cam.initPersProjWithoutDistortion(558, 555, 312, 210); #elif defined(VISP_HAVE_V4L2) - // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) - vpV4l2Grabber g; - g.setScale(1); - g.setInput(0); - g.setDevice("/dev/video1"); - g.open(I); - // Logitec sphere parameters - cam.initPersProjWithoutDistortion(558, 555, 312, 210); + // Create a grabber based on v4l2 third party lib (for usb cameras under Linux) + vpV4l2Grabber g; + g.setScale(1); + g.setInput(0); + g.setDevice("/dev/video1"); + g.open(I); + // Logitec sphere parameters + cam.initPersProjWithoutDistortion(558, 555, 312, 210); #elif defined(VISP_HAVE_DC1394_2) - // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) - vp1394TwoGrabber g(false); - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on libdc1394-2.x third party lib (for firewire cameras under Linux) + vp1394TwoGrabber g(false); + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_30); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #elif defined(VISP_HAVE_CMU1394) - // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) - vp1394CMUGrabber g; - g.setVideoMode(0, 5); // 640x480 MONO8 - g.setFramerate(4); // 30 Hz - g.open(I); - // AVT Pike 032C parameters - cam.initPersProjWithoutDistortion(800, 795, 320, 216); + // Create a grabber based on CMU 1394 third party lib (for firewire cameras under windows) + vp1394CMUGrabber g; + g.setVideoMode(0, 5); // 640x480 MONO8 + g.setFramerate(4); // 30 Hz + g.open(I); + // AVT Pike 032C parameters + cam.initPersProjWithoutDistortion(800, 795, 320, 216); #endif - // Acquire an image from the grabber + // Acquire an image from the grabber #if defined(VISP_HAVE_OPENCV) - g >> frame; // get a new frame from camera - vpImageConvert::convert(frame, I); + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #else - g.acquire(I); + g.acquire(I); #endif - // Create an image viewer + // Create an image viewer #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 10, 10, "Current frame"); + vpDisplayX d(I, 10, 10, "Current frame"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 10, 10, "Current frame"); + vpDisplayGDI d(I, 10, 10, "Current frame"); #endif - vpDisplay::display(I); - vpDisplay::flush(I); - - // Create a blob tracker - vpDot2 dot; - dot.setGraphics(true); - dot.setComputeMoments(true); - dot.setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape - dot.setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation - dot.setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level - dot.initTracking(I); - vpDisplay::flush(I); - - // Current and desired visual feature associated to the x coordinate of the point - vpFeaturePoint s_x, s_xd; - - // Create the current x visual feature - vpFeatureBuilder::create(s_x, cam, dot); - - // Create the desired x* visual feature - s_xd.buildFrom(0, 0, depth); - vpMatrix L_x = s_xd.interaction(vpFeaturePoint::selectX()); - - // Create the current log(Z/Z*) visual feature - vpFeatureDepth s_Z; - // Surface of the blob estimated from the image moment m00 and converted in meters - double surface = 1./sqrt(dot.m00/(cam.get_px()*cam.get_py())); - double Z, Zd; - // Initial depth of the blob in from of the camera - Z = coef * surface ; - // Desired depth Z* of the blob. This depth is learned and equal to the initial depth - Zd = Z; - s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 - vpMatrix L_Z = s_Z.interaction(); - - vpVelocityTwistMatrix cVe = robot.get_cVe(); - vpMatrix eJe; // pioneer jacobian - robot.get_eJe(eJe); - - vpMatrix L; // Interaction matrix - L.stackMatrices(L_x); // constant since build with the desired feature - L.stackMatrices(L_Z); // not constant since it corresponds to log(Z/Z*) that evolves at each iteration - - vpColVector v; // vz, wx - - vpFeatureDepth s_Zd; - s_Zd.buildFrom(0, 0, 1, 0); // The value of s* is 0 with Z=1 meter. - - try - { + vpDisplay::display(I); + vpDisplay::flush(I); + + // Create a blob tracker + vpDot2 dot; + dot.setGraphics(true); + dot.setComputeMoments(true); + dot.setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape + dot.setGrayLevelPrecision(0.9); // to set the blob gray level bounds for binarisation + dot.setEllipsoidBadPointsPercentage(0.5); // to be accept 50% of bad inner and outside points with bad gray level + dot.initTracking(I); + vpDisplay::flush(I); + + // Current and desired visual feature associated to the x coordinate of the point + vpFeaturePoint s_x, s_xd; + + // Create the current x visual feature + vpFeatureBuilder::create(s_x, cam, dot); + + // Create the desired x* visual feature + s_xd.buildFrom(0, 0, depth); + vpMatrix L_x = s_xd.interaction(vpFeaturePoint::selectX()); + + // Create the current log(Z/Z*) visual feature + vpFeatureDepth s_Z; + // Surface of the blob estimated from the image moment m00 and converted in meters + double surface = 1./sqrt(dot.m00/(cam.get_px()*cam.get_py())); + double Z, Zd; + // Initial depth of the blob in from of the camera + Z = coef * surface ; + // Desired depth Z* of the blob. This depth is learned and equal to the initial depth + Zd = Z; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z , 0); // log(Z/Z*) = 0 that's why the last parameter is 0 + vpMatrix L_Z = s_Z.interaction(); + + vpVelocityTwistMatrix cVe = robot.get_cVe(); + vpMatrix eJe; // pioneer jacobian + robot.get_eJe(eJe); + + vpMatrix L; // Interaction matrix + L.stackMatrices(L_x); // constant since build with the desired feature + L.stackMatrices(L_Z); // not constant since it corresponds to log(Z/Z*) that evolves at each iteration + + vpColVector v; // vz, wx + + vpFeatureDepth s_Zd; + s_Zd.buildFrom(0, 0, 1, 0); // The value of s* is 0 with Z=1 meter. + while(1) { // Acquire a new image @@ -285,16 +284,17 @@ int main(int argc, char **argv) if ( vpDisplay::getClick(I, false) ) break; } - } - catch(...) - { - } - std::cout << "Ending robot thread..." << std::endl; - robot.stopRunning(); + std::cout << "Ending robot thread..." << std::endl; + robot.stopRunning(); - // wait for the thread to stop - robot.waitForRunExit(); + // wait for the thread to stop + robot.waitForRunExit(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int main() diff --git a/example/servo-pioneer/sonarPioneerReader.cpp b/example/servo-pioneer/sonarPioneerReader.cpp index 9c8cf4f8287231cc11d13237bbd0115c1193b22a..e40043d99599cfd2c4adbe8a87aa36eb8a6fb343 100644 --- a/example/servo-pioneer/sonarPioneerReader.cpp +++ b/example/servo-pioneer/sonarPioneerReader.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: sonarPioneerReader.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: sonarPioneerReader.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -207,142 +207,148 @@ void sonarPrinter(void) */ int main(int argc, char **argv) { - ArArgumentParser parser(&argc, argv); - parser.loadDefaultArguments(); + try { + ArArgumentParser parser(&argc, argv); + parser.loadDefaultArguments(); - robot = new vpRobotPioneer; + robot = new vpRobotPioneer; - // ArRobotConnector connects to the robot, get some initial data from it such as type and name, - // and then loads parameter files for this robot. - ArRobotConnector robotConnector(&parser, robot); - if(!robotConnector.connectRobot()) - { - ArLog::log(ArLog::Terse, "Could not connect to the robot"); - if(parser.checkHelpAndWarnUnparsed()) + // ArRobotConnector connects to the robot, get some initial data from it such as type and name, + // and then loads parameter files for this robot. + ArRobotConnector robotConnector(&parser, robot); + if(!robotConnector.connectRobot()) + { + ArLog::log(ArLog::Terse, "Could not connect to the robot"); + if(parser.checkHelpAndWarnUnparsed()) + { + Aria::logOptions(); + Aria::exit(1); + } + } + if (!Aria::parseArgs()) { Aria::logOptions(); - Aria::exit(1); + Aria::shutdown(); + return false; } - } - if (!Aria::parseArgs()) - { - Aria::logOptions(); - Aria::shutdown(); - return false; - } - - std::cout << "Robot connected" << std::endl; + + std::cout << "Robot connected" << std::endl; #if defined(VISP_HAVE_X11) || defined (VISP_HAVE_GDI) - // Create a display to show sensor data - if (isInitialized == false) - { - I.resize(half_size*2, half_size*2); - I = 255; + // Create a display to show sensor data + if (isInitialized == false) + { + I.resize(half_size*2, half_size*2); + I = 255; #if defined(VISP_HAVE_X11) - d = new vpDisplayX; + d = new vpDisplayX; #elif defined (VISP_HAVE_GDI) - d = new vpDisplayGDI; + d = new vpDisplayGDI; #endif - d->init(I, -1, -1, "Sonar range data"); - isInitialized = true; - } + d->init(I, -1, -1, "Sonar range data"); + isInitialized = true; + } #endif - // Activates the sonar - ArGlobalFunctor sonarPrinterCB(&sonarPrinter); - robot->addRangeDevice(&sonar); - robot->addUserTask("Sonar printer", 50, &sonarPrinterCB); + // Activates the sonar + ArGlobalFunctor sonarPrinterCB(&sonarPrinter); + robot->addRangeDevice(&sonar); + robot->addUserTask("Sonar printer", 50, &sonarPrinterCB); - robot->useSonar(true); // activates the sonar device usage + robot->useSonar(true); // activates the sonar device usage - // Robot velocities - vpColVector v_mes(2); + // Robot velocities + vpColVector v_mes(2); - for (int i=0; i < 1000; i++) - { - double t = vpTime::measureTimeMs(); + for (int i=0; i < 1000; i++) + { + double t = vpTime::measureTimeMs(); - v_mes = robot->getVelocity(vpRobot::REFERENCE_FRAME); - std::cout << "Trans. vel= " << v_mes[0] << " m/s, Rot. vel=" << vpMath::deg(v_mes[1]) << " deg/s" << std::endl; - v_mes = robot->getVelocity(vpRobot::ARTICULAR_FRAME); - std::cout << "Left wheel vel= " << v_mes[0] << " m/s, Right wheel vel=" << v_mes[1] << " m/s" << std::endl; - std::cout << "Battery=" << robot->getBatteryVoltage() << std::endl; + v_mes = robot->getVelocity(vpRobot::REFERENCE_FRAME); + std::cout << "Trans. vel= " << v_mes[0] << " m/s, Rot. vel=" << vpMath::deg(v_mes[1]) << " deg/s" << std::endl; + v_mes = robot->getVelocity(vpRobot::ARTICULAR_FRAME); + std::cout << "Left wheel vel= " << v_mes[0] << " m/s, Right wheel vel=" << v_mes[1] << " m/s" << std::endl; + std::cout << "Battery=" << robot->getBatteryVoltage() << std::endl; #if defined(VISP_HAVE_X11) || defined (VISP_HAVE_GDI) - if (isInitialized) { - // A mouse click to exit - // Before exiting save the last sonar image - if (vpDisplay::getClick(I, false) == true) { - { - // Set the default output path - std::string opath; -#ifdef UNIX - opath = "/tmp"; -#elif WIN32 - opath = "C:\\temp"; + if (isInitialized) { + // A mouse click to exit + // Before exiting save the last sonar image + if (vpDisplay::getClick(I, false) == true) { + { + // Set the default output path + std::string opath; +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opath = "/tmp"; +#elif defined(_WIN32) + opath = "C:\\temp"; #endif - std::string username = vpIoTools::getUserName(); - - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; - - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); - } - catch (...) { - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - exit(-1); + std::string username = vpIoTools::getUserName(); + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + exit(-1); + } } + std::string filename = opath + "/sonar.png"; + vpImage<vpRGBa> C; + vpDisplay::getImage(I, C); + vpImageIo::write(C, filename); } - std::string filename = opath + "/sonar.png"; - vpImage<vpRGBa> C; - vpDisplay::getImage(I, C); - vpImageIo::write(C, filename); + break; } - break; } - } #endif - vpTime::wait(t, 40); - } + vpTime::wait(t, 40); + } - ArLog::log(ArLog::Normal, "simpleMotionCommands: Stopping."); - robot->lock(); - robot->stop(); - robot->unlock(); - ArUtil::sleep(1000); + ArLog::log(ArLog::Normal, "simpleMotionCommands: Stopping."); + robot->lock(); + robot->stop(); + robot->unlock(); + ArUtil::sleep(1000); - robot->lock(); - ArLog::log(ArLog::Normal, "simpleMotionCommands: Pose=(%.2f,%.2f,%.2f), Trans. Vel=%.2f, Rot. Vel=%.2f, Battery=%.2fV", - robot->getX(), robot->getY(), robot->getTh(), robot->getVel(), robot->getRotVel(), robot->getBatteryVoltage()); - robot->unlock(); + robot->lock(); + ArLog::log(ArLog::Normal, "simpleMotionCommands: Pose=(%.2f,%.2f,%.2f), Trans. Vel=%.2f, Rot. Vel=%.2f, Battery=%.2fV", + robot->getX(), robot->getY(), robot->getTh(), robot->getVel(), robot->getRotVel(), robot->getBatteryVoltage()); + robot->unlock(); - std::cout << "Ending robot thread..." << std::endl; - robot->stopRunning(); + std::cout << "Ending robot thread..." << std::endl; + robot->stopRunning(); #if defined(VISP_HAVE_X11) || defined (VISP_HAVE_GDI) - if (isInitialized) { - if (d != NULL) - delete d; - } + if (isInitialized) { + if (d != NULL) + delete d; + } #endif - // wait for the thread to stop - robot->waitForRunExit(); + // wait for the thread to stop + robot->waitForRunExit(); - delete robot; + delete robot; - // exit - ArLog::log(ArLog::Normal, "simpleMotionCommands: Exiting."); - return 0; + // exit + ArLog::log(ArLog::Normal, "simpleMotionCommands: Exiting."); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #endif diff --git a/example/servo-ptu46/CMakeLists.txt b/example/servo-ptu46/CMakeLists.txt index c8ec1bbecb26699d01311ef199a1eca6acf13c76..9135d34d5016974d5039908cc87e80ff33ebfe0a 100644 --- a/example/servo-ptu46/CMakeLists.txt +++ b/example/servo-ptu46/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,18 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE movePtu46.cpp servoPtu46Point2DArtVelocity.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-ptu46/movePtu46.cpp b/example/servo-ptu46/movePtu46.cpp index d4b9b30a55d22d983fe04278424bd79f9f96405a..920aed4fad5290c3693029223b91868bee712d3d 100644 --- a/example/servo-ptu46/movePtu46.cpp +++ b/example/servo-ptu46/movePtu46.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: movePtu46.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: movePtu46.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,7 +56,7 @@ */ #include <visp/vpConfig.h> #include <visp/vpDebug.h> -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> #endif diff --git a/example/servo-ptu46/servoPtu46Point2DArtVelocity.cpp b/example/servo-ptu46/servoPtu46Point2DArtVelocity.cpp index 2c930937e0b294c9798458d1716585a69ddf5871..3a2a365c398afaa7276372e9c0bbe1d197b6d624 100644 --- a/example/servo-ptu46/servoPtu46Point2DArtVelocity.cpp +++ b/example/servo-ptu46/servoPtu46Point2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoPtu46Point2DArtVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoPtu46Point2DArtVelocity.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,20 +63,20 @@ */ #include <visp/vpConfig.h> #include <visp/vpDebug.h> // Debug trace -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> #endif #include <signal.h> -#if (defined(VISP_HAVE_PTU46) & defined (VISP_HAVE_DC1394) ) +#if (defined(VISP_HAVE_PTU46) & defined (VISP_HAVE_DC1394_2) ) #ifdef VISP_HAVE_PTHREAD # include <pthread.h> #endif -#include <visp/vp1394Grabber.h> +#include <visp/vp1394TwoGrabber.h> #include <visp/vpImage.h> #include <visp/vpDisplay.h> #include <visp/vpDisplayX.h> @@ -139,7 +139,7 @@ main() vpImage<unsigned char> I ; - vp1394Grabber g; + vp1394TwoGrabber g; g.open(I) ; diff --git a/example/servo-viper650/CMakeLists.txt b/example/servo-viper650/CMakeLists.txt index 512283044282dc298d239a9b69e29568328b8244..eefd0ef6770c21a8433ccfe5b3048a439b5ccdd6 100644 --- a/example/servo-viper650/CMakeLists.txt +++ b/example/servo-viper650/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 3750 2012-06-01 09:39:38Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,19 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE servoViper650Point2DCamVelocity.cpp servoViper650FourPoints2DArtVelocityInteractionCurrent.cpp servoViper650FourPoints2DCamVelocityInteractionCurrent.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-viper650/servoViper650FourPoints2DArtVelocityInteractionCurrent.cpp b/example/servo-viper650/servoViper650FourPoints2DArtVelocityInteractionCurrent.cpp index 5ee16cc6719b6a0338590f795a7d73b1914b1b1d..2f0ad87b6d802c4418561496bb2bed96cd3be4c8 100644 --- a/example/servo-viper650/servoViper650FourPoints2DArtVelocityInteractionCurrent.cpp +++ b/example/servo-viper650/servoViper650FourPoints2DArtVelocityInteractionCurrent.cpp @@ -3,7 +3,7 @@ * $Id: servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp 3870 2012-09-05 17:03:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper650/servoViper650FourPoints2DCamVelocityInteractionCurrent.cpp b/example/servo-viper650/servoViper650FourPoints2DCamVelocityInteractionCurrent.cpp index ac5c06da153a6e3d0528f13a112f4553adcaa6e8..facd496ca10b3da72a0045532887a989e878eb05 100644 --- a/example/servo-viper650/servoViper650FourPoints2DCamVelocityInteractionCurrent.cpp +++ b/example/servo-viper650/servoViper650FourPoints2DCamVelocityInteractionCurrent.cpp @@ -3,7 +3,7 @@ * $Id: servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp 3870 2012-09-05 17:03:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper650/servoViper650Point2DCamVelocity.cpp b/example/servo-viper650/servoViper650Point2DCamVelocity.cpp index bb7f49f2c225d8eb99f9021cbd0576824c44c93e..60cab3e285f227e241e88b225c973a614b83e63e 100644 --- a/example/servo-viper650/servoViper650Point2DCamVelocity.cpp +++ b/example/servo-viper650/servoViper650Point2DCamVelocity.cpp @@ -3,7 +3,7 @@ * $Id: servoViper650Point2DCamVelocity.cpp 3616 2012-03-09 14:31:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,7 @@ #include <fstream> #include <sstream> -#if (defined (VISP_HAVE_VIPER650) && defined (VISP_HAVE_DC1394_2)) +#if (defined (VISP_HAVE_VIPER650) && defined (VISP_HAVE_DC1394_2) && defined (VISP_HAVE_X11)) #include <visp/vp1394TwoGrabber.h> #include <visp/vpImage.h> diff --git a/example/servo-viper850/CMakeLists.txt b/example/servo-viper850/CMakeLists.txt index 167d3acff3f7109b81cb480ecdfad385d34d58c1..e93f230b9c38a146dc6e869d1a6ee9286beaecd6 100644 --- a/example/servo-viper850/CMakeLists.txt +++ b/example/servo-viper850/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp servoViper850FourPoints2DArtVelocityInteractionDesired.cpp servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp @@ -55,12 +55,16 @@ SET (SOURCE servoViper850Point2DCamVelocityKalman.cpp ) -# rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() diff --git a/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp b/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp index 6a004a177089f52728cdd0d85bcb3fbd7266be68..0a894bf4e912f7c2b17b5752045e21bc49c41255 100644 --- a/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp +++ b/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp 4065 2013-01-11 13:32:47Z fspindle $ + * $Id: servoViper850FourPoints2DArtVelocityInteractionCurrent.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionDesired.cpp b/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionDesired.cpp index 526900e3cb45e73ae19ab048dae887ae1bd35251..0a9e9687b2ee76ef9ae2c793722d0f3b5842f8f6 100644 --- a/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionDesired.cpp +++ b/example/servo-viper850/servoViper850FourPoints2DArtVelocityInteractionDesired.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850FourPoints2DArtVelocityInteractionDesired.cpp 4065 2013-01-11 13:32:47Z fspindle $ + * $Id: servoViper850FourPoints2DArtVelocityInteractionDesired.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp b/example/servo-viper850/servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp index 6a6a343eca2ff6d0993d7a147cb80847727d66a5..a10613a180197ff50aa608de61178199202cad31 100644 --- a/example/servo-viper850/servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp +++ b/example/servo-viper850/servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp 4065 2013-01-11 13:32:47Z fspindle $ + * $Id: servoViper850FourPoints2DCamVelocityInteractionCurrent.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850FourPointsKinect.cpp b/example/servo-viper850/servoViper850FourPointsKinect.cpp index eefc36ea3b9d56b8dabdd37a222c60682b7fd7f6..4fc9b644b96dd70a5a89e5f77946e070bbad1a47 100644 --- a/example/servo-viper850/servoViper850FourPointsKinect.cpp +++ b/example/servo-viper850/servoViper850FourPointsKinect.cpp @@ -4,7 +4,7 @@ * $Id: servoViper850FourPoints2DCamVelocityKinect.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp b/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp index 5d0957daba8ef83224b50ffc7e990d27ab9a2ae0..ea887eb7629f1989d8b517ea5f544f168e85ad8d 100644 --- a/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp +++ b/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoViper850Point2DArtVelocity-jointAvoidance-basic.cpp 4698 2014-03-26 06:55:37Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,10 +47,8 @@ Joint limits avoidance by stopping the motion on axis near the joint limits. - Implemented from section III.B in F. Chaumette, E. Marchand. A - redundancy-based iterative approach for avoiding joint limits: Application - to visual servoing. IEEE Trans. on Robotics and Automation, 17(5):719-730, - October 2001. + Implemented from section III.B in \cite Chaumette01c. + */ #include <visp/vpConfig.h> @@ -64,7 +62,7 @@ #include <cmath> // std::fabs #include <limits> // numeric_limits -#if (defined (VISP_HAVE_VIPER850) && defined (VISP_HAVE_DC1394_2)) +#if (defined (VISP_HAVE_VIPER850) && defined (VISP_HAVE_DC1394_2) && defined(VISP_HAVE_DISPLAY)) #include <visp/vp1394TwoGrabber.h> #include <visp/vpImage.h> diff --git a/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp b/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp index 427ecc924220bae79f86a27a5262cf4d955591fc..da8769045b9c385bb67d370ea9a5a251cebb91a8 100644 --- a/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp +++ b/example/servo-viper850/servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: servoViper850Point2DArtVelocity-jointAvoidance-gpa.cpp 4698 2014-03-26 06:55:37Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,16 +47,7 @@ Joint limits avoidance using a gradient projection approach. - Implemented from : - - - E. Marchand, F. Chaumette, A. Rizzo. Using the task function approach to - avoid robot joint limits and kinematic singularities in visual servoing. In - IEEE/RSJ Int. Conf. on Intelligent Robots and Systems, IROS'96, Volume 3, - Pages 1083-1090, Osaka, Japan, November 1996. details. - - - and section II.B in F. Chaumette, E. Marchand. A redundancy-based iterative - approach for avoiding joint limits: Application to visual servoing. IEEE - Trans. on Robotics and Automation, 17(5):719-730, October 2001. + Implemented from \cite Marchand96f and section II.B in \cite Chaumette01c. */ #include <visp/vpConfig.h> @@ -68,7 +59,7 @@ #include <fstream> #include <sstream> -#if (defined (VISP_HAVE_VIPER850) && defined (VISP_HAVE_DC1394_2)) +#if (defined (VISP_HAVE_VIPER850) && defined (VISP_HAVE_DC1394_2) && defined(VISP_HAVE_DISPLAY)) #include <visp/vp1394TwoGrabber.h> #include <visp/vpImage.h> diff --git a/example/servo-viper850/servoViper850Point2DArtVelocity.cpp b/example/servo-viper850/servoViper850Point2DArtVelocity.cpp index f85015a4c6fdbc68131129dca00d9d5b9351897d..a790b56166c59a3a05b728468a83c729033e0c1c 100644 --- a/example/servo-viper850/servoViper850Point2DArtVelocity.cpp +++ b/example/servo-viper850/servoViper850Point2DArtVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850Point2DArtVelocity.cpp 4065 2013-01-11 13:32:47Z fspindle $ + * $Id: servoViper850Point2DArtVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850Point2DCamVelocity.cpp b/example/servo-viper850/servoViper850Point2DCamVelocity.cpp index abfce2017f456600dc1e832b924479f3975af4dc..7b72b9c7fe8419c8d3e43ea9eb9f363d213cf7ed 100644 --- a/example/servo-viper850/servoViper850Point2DCamVelocity.cpp +++ b/example/servo-viper850/servoViper850Point2DCamVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850Point2DCamVelocity.cpp 4065 2013-01-11 13:32:47Z fspindle $ + * $Id: servoViper850Point2DCamVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/servo-viper850/servoViper850Point2DCamVelocityKalman.cpp b/example/servo-viper850/servoViper850Point2DCamVelocityKalman.cpp index 85865860a5172c5d585f70aec17e3d58a55956a2..e2926ad7d59e10229f9f331fef8b4fba10d637c1 100644 --- a/example/servo-viper850/servoViper850Point2DCamVelocityKalman.cpp +++ b/example/servo-viper850/servoViper850Point2DCamVelocityKalman.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: servoViper850Point2DCamVelocityKalman.cpp 4300 2013-07-04 09:21:07Z fspindle $ + * $Id: servoViper850Point2DCamVelocityKalman.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/tools/CMakeLists.txt b/example/tools/CMakeLists.txt index 2e83d624148b248a65fb0f7abbbace034d88c249..cad994093012a68697898b1ad30f937d3fca0eaf 100644 --- a/example/tools/CMakeLists.txt +++ b/example/tools/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE keyboard.cpp parallelPort.cpp plot2d.cpp @@ -52,22 +52,18 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - #ADD_TEST(${binary} ${binary}) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() -ENDFOREACH(source) - -ADD_TEST(histogram histogram) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(histogram histogram) diff --git a/example/tools/histogram.cpp b/example/tools/histogram.cpp index 09bf7d007e71fcd2fd0c1957a39ed267f7d2aaf7..d6d4fe98c76d92ecdd7f5078abc902f0d7dcf0cd 100644 --- a/example/tools/histogram.cpp +++ b/example/tools/histogram.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: histogram.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: histogram.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,6 +63,9 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); + /*! \example histogram.cpp @@ -81,8 +84,7 @@ \param user : Username. */ -void usage(const char *name, const char *badparam, std::string ipath, - std::string opath, std::string user) +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user) { fprintf(stdout, "\n\ Read an image on the disk, display it using X11, display some\n\ @@ -132,21 +134,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, - std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -154,7 +154,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -164,236 +164,230 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // std::cout << "env_ipath: " << env_ipath << std::endl; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef WIN32 - opt_opath = "C:/temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if defined(_WIN32) + opt_opath = "C:/temp"; #else - opt_opath = "/tmp"; + opt_opath = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - std::string dirname = opath + vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + std::string dirname = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(dirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(dirname); + } + catch (...) { + usage(argv[0], NULL, ipath, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << dirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(dirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(dirname); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (opt_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << dirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (opt_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Create a grey level image + vpImage<unsigned char> I ; + + // Load a grey image from the disk + filename = ipath; + if (opt_ipath.empty()) + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + + std::cout << "Read: " << filename << std::endl; + vpImageIo::read(I, filename) ; + + unsigned char distance = 60; + vpHistogram h; + + // Computes the histogram from the image + h.calculate(I); + + // Save the histogram + filename = dirname + vpIoTools::path("/histogram.txt"); + std::cout << "Save the histogram in: " << filename << std::endl; + h.write(filename); + + // Smooth the histogram + h.smooth(); + // Save the histogram + filename = dirname + vpIoTools::path("/histogram_smoothed.txt"); + std::cout << "Save the smoothed histogram in: " << filename << std::endl; + h.write(filename); + + std::list<vpHistogramPeak> peaks; + unsigned int nbpeaks = 0; + + // get all the histogram peaks + nbpeaks = h.getPeaks(peaks); + + vpTRACE("List of peaks"); + vpTRACE("Nb peaks: %d", nbpeaks); + if (nbpeaks) { + for(std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) + { + vpHistogramPeak p = *it; + vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue()); + } } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - - // Create a grey level image - vpImage<unsigned char> I ; - - // Load a grey image from the disk - filename = ipath; - if (opt_ipath.empty()) - filename += vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - - std::cout << "Read: " << filename << std::endl; - vpImageIo::read(I, filename) ; - unsigned char distance = 60; - vpHistogram h; - - // Computes the histogram from the image - h.calculate(I); - - // Save the histogram - filename = dirname + vpIoTools::path("/histogram.txt"); - std::cout << "Save the histogram in: " << filename << std::endl; - h.write(filename); + // sort all the histogram peaks list to have the highest peak at the + // beginning of the list, the smallest at the end. + nbpeaks = h.sort(peaks); + + vpTRACE("Sorted list of peaks"); + vpTRACE("Nb peaks: %d", nbpeaks); + if (nbpeaks) { + for(std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) + { + vpHistogramPeak p = *it; + vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue()); + } + } - // Smooth the histogram - h.smooth(); - // Save the histogram - filename = dirname + vpIoTools::path("/histogram_smoothed.txt"); - std::cout << "Save the smoothed histogram in: " << filename << std::endl; - h.write(filename); + // Get the two highest histogram peaks. peak1 is the highest + vpHistogramPeak peak1, peak2; + nbpeaks = h.getPeaks(distance, peak1, peak2); + if (nbpeaks != 2) { + std::cout << "Not a bimodal histogram..." << std::endl; + } + else { + vpTRACE("Bimodal histogram: main peak1: %d-%d second peak2: %d-%d", + peak1.getLevel(), peak1.getValue(), + peak2.getLevel(), peak2.getValue()); + } - std::list<vpHistogramPeak> peaks; - unsigned int nbpeaks = 0; + // Get the valey between the two highest peaks + vpHistogramValey valey; + if (h.getValey(peak1, peak2, valey) == false) { + vpTRACE("No valey found..."); + } + else { + vpTRACE("Valey: %d-%d", valey.getLevel(), valey.getValue()); + } - // get all the histogram peaks - nbpeaks = h.getPeaks(peaks); + vpHistogramValey valeyl, valeyr; - vpTRACE("List of peaks"); - vpTRACE("Nb peaks: %d", nbpeaks); - if (nbpeaks) { - for(std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) { - vpHistogramPeak p = *it; - vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue()); + // Search the two valeys around peak1 + unsigned ret = h.getValey(distance, peak1, valeyl, valeyr); + if (ret == 0x00) { + vpTRACE("No left and right valey for peak %d-%d...", + peak1.getLevel(), peak1.getValue()); + } + else if (ret == 0x10) { + vpTRACE("No right valey for peak %d-%d...", + peak1.getLevel(), peak1.getValue()); + vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); + } + else if (ret == 0x01) { + vpTRACE("No left valey for peak %d-%d...", + peak1.getLevel(), peak1.getValue()); + vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + } + else if (ret == 0x11) { + vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); + vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + } } - } - - // sort all the histogram peaks list to have the highest peak at the - // beginning of the list, the smallest at the end. - nbpeaks = h.sort(peaks); - - vpTRACE("Sorted list of peaks"); - vpTRACE("Nb peaks: %d", nbpeaks); - if (nbpeaks) { - for(std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) { - vpHistogramPeak p = *it; - vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue()); + // Search the two valeys around peak2 + unsigned ret = h.getValey(distance, peak2, valeyl, valeyr); + if (ret == 0x00) { + vpTRACE("No left and right valey for peak %d-%d...", + peak2.getLevel(), peak2.getValue()); + } + else if (ret == 0x10) { + vpTRACE("No right valey for peak %d-%d...", + peak2.getLevel(), peak2.getValue()); + vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); + } + else if (ret == 0x01) { + vpTRACE("No left valey for peak %d-%d...", + peak2.getLevel(), peak2.getValue()); + vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + } + else if (ret == 0x11) { + vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); + vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + } } - } - // Get the two highest histogram peaks. peak1 is the highest - vpHistogramPeak peak1, peak2; - nbpeaks = h.getPeaks(distance, peak1, peak2); - if (nbpeaks != 2) { - std::cout << "Not a bimodal histogram..." << std::endl; - } - else { - vpTRACE("Bimodal histogram: main peak1: %d-%d second peak2: %d-%d", - peak1.getLevel(), peak1.getValue(), - peak2.getLevel(), peak2.getValue()); - } - - // Get the valey between the two highest peaks - vpHistogramValey valey; - if (h.getValey(peak1, peak2, valey) == false) { - vpTRACE("No valey found..."); - } - else { - vpTRACE("Valey: %d-%d", valey.getLevel(), valey.getValue()); - } - - - vpHistogramValey valeyl, valeyr; - - { - // Search the two valeys around peak1 - unsigned ret = h.getValey(distance, peak1, valeyl, valeyr); - if (ret == 0x00) { - vpTRACE("No left and right valey for peak %d-%d...", - peak1.getLevel(), peak1.getValue()); - } - else if (ret == 0x10) { - vpTRACE("No right valey for peak %d-%d...", - peak1.getLevel(), peak1.getValue()); - vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); - } - else if (ret == 0x01) { - vpTRACE("No left valey for peak %d-%d...", - peak1.getLevel(), peak1.getValue()); - vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + //////////////////////////////////////////////////////////// + // get the valey between the two highest peaks. Here we don't know + // which of peakl or peakr is the highest. + vpHistogramPeak peakl, peakr; + if (h.getPeaks(distance, peakl, peakr, valey) == false) { + std::cout << "Not a bimodal histogram..." << std::endl; } - else if (ret == 0x11) { - vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); - vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); + else { + vpTRACE("Bimodal histogram: valey %d-%d for peakl: %d-%d peakr: %d-%d", + valey.getLevel(), valey.getValue(), + peakl.getLevel(), peakl.getValue(), + peakr.getLevel(), peakr.getValue()); } + return 0; } - { - // Search the two valeys around peak2 - unsigned ret = h.getValey(distance, peak2, valeyl, valeyr); - if (ret == 0x00) { - vpTRACE("No left and right valey for peak %d-%d...", - peak2.getLevel(), peak2.getValue()); - } - else if (ret == 0x10) { - vpTRACE("No right valey for peak %d-%d...", - peak2.getLevel(), peak2.getValue()); - vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); - } - else if (ret == 0x01) { - vpTRACE("No left valey for peak %d-%d...", - peak2.getLevel(), peak2.getValue()); - vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); - } - else if (ret == 0x11) { - vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue()); - vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue()); - } - } - - //////////////////////////////////////////////////////////// - // get the valey between the two highest peaks. Here we don't know - // which of peakl or peakr is the highest. - vpHistogramPeak peakl, peakr; - if (h.getPeaks(distance, peakl, peakr, valey) == false) { - std::cout << "Not a bimodal histogram..." << std::endl; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - else { - vpTRACE("Bimodal histogram: valey %d-%d for peakl: %d-%d peakr: %d-%d", - valey.getLevel(), valey.getValue(), - peakl.getLevel(), peakl.getValue(), - peakr.getLevel(), peakr.getValue()); - } - } - - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/example/tools/keyboard.cpp b/example/tools/keyboard.cpp index d12a13b7de68208d0b973a518894bb184c2ee9a0..ef97dd0279ab98e3e75c7a3f52b3fa20cc2dd6be 100644 --- a/example/tools/keyboard.cpp +++ b/example/tools/keyboard.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: keyboard.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: keyboard.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,7 +48,7 @@ #include <visp/vpConfig.h> #include <visp/vpDebug.h> -#if ( defined(UNIX) && ( ! defined(WIN32) ) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) #include <stdio.h> #include <iostream> #include <signal.h> diff --git a/example/tools/parallelPort.cpp b/example/tools/parallelPort.cpp index f2573335b26d70ee1223521ed1985576df364db1..789f5222d8253b27840db8ab991b93f347d7d601 100644 --- a/example/tools/parallelPort.cpp +++ b/example/tools/parallelPort.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: parallelPort.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: parallelPort.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/example/tools/plot2d.cpp b/example/tools/plot2d.cpp index cbf86f5c2b9daffbbf9ef503da77b1ce5051c5af..5c487c30e5daf1a01f9ae3ddb7c2c383fdec50d4 100644 --- a/example/tools/plot2d.cpp +++ b/example/tools/plot2d.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: plot2d.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: plot2d.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,65 +55,72 @@ int main () { #if defined(VISP_HAVE_DISPLAY) - vpPlot plot(2, 700, 700, 100, 200, "Curves..."); - - // Change the default font - // plot.setFont("-misc-fixed-bold-r-semicondensed--0-0-75-75-c-0-iso8859-10"); - - //Initialize the number of curve for each graphic - plot.initGraph(0,1); - plot.initGraph(1,1); - - //Set the color of the curves - plot.setColor(0,0,vpColor::green); - plot.setColor(1,0,vpColor::red); - - //Set the titles of the graphic - char title[40]; - strncpy( title, "cos function", 40 ); - plot.setTitle(0,title); - strncpy( title, "sin function", 40 ); - plot.setTitle(1, title); - - //Set the legend of each curves - char legend[40]; - strncpy( legend, "cos x", 40 ); - plot.setLegend(0,0,legend); - strncpy( legend, "sin x", 40 ); - plot.setLegend(1,0, legend); - - //Set the x axis legend of each curves - char unit[40]; - strncpy( unit, "x", 40 ); - plot.setUnitX(0,unit); - strncpy( unit, "x", 40 ); - plot.setUnitX(1,unit); - - //Set the y axis legend of each curves - strncpy( unit, "y", 40 ); - plot.setUnitY(0,unit); - strncpy( unit, "y", 40 ); - plot.setUnitY(1,unit); - - //Plot the cosinus and sinus functions - double i = 0; - while(i <= 20*2*M_PI) - { - double co = cos(i); - double si = sin(i); - plot.plot(0,0,i,co); - plot.plot(1,0,i,si); - i+=0.1; + try { + vpPlot plot(2, 700, 700, 100, 200, "Curves..."); + + // Change the default font + // plot.setFont("-misc-fixed-bold-r-semicondensed--0-0-75-75-c-0-iso8859-10"); + + //Initialize the number of curve for each graphic + plot.initGraph(0,1); + plot.initGraph(1,1); + + //Set the color of the curves + plot.setColor(0,0,vpColor::green); + plot.setColor(1,0,vpColor::red); + + //Set the titles of the graphic + char title[40]; + strncpy( title, "cos function", 40 ); + plot.setTitle(0,title); + strncpy( title, "sin function", 40 ); + plot.setTitle(1, title); + + //Set the legend of each curves + char legend[40]; + strncpy( legend, "cos x", 40 ); + plot.setLegend(0,0,legend); + strncpy( legend, "sin x", 40 ); + plot.setLegend(1,0, legend); + + //Set the x axis legend of each curves + char unit[40]; + strncpy( unit, "x", 40 ); + plot.setUnitX(0,unit); + strncpy( unit, "x", 40 ); + plot.setUnitX(1,unit); + + //Set the y axis legend of each curves + strncpy( unit, "y", 40 ); + plot.setUnitY(0,unit); + strncpy( unit, "y", 40 ); + plot.setUnitY(1,unit); + + //Plot the cosinus and sinus functions + double i = 0; + while(i <= 20*2*M_PI) + { + double co = cos(i); + double si = sin(i); + plot.plot(0,0,i,co); + plot.plot(1,0,i,si); + i+=0.1; + } + + vpDisplay::getClick(plot.I); + + //Save the datas as text files + plot.saveData(0, "dataCos.txt"); + plot.saveData(0, "dataSin.txt"); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - vpDisplay::getClick(plot.I); - - //Save the datas as text files - plot.saveData(0, "dataCos.txt"); - plot.saveData(0, "dataSin.txt"); + #else std::cout << "Plot functionalities are not avalaible since no display is available." << std::endl; #endif - return 0; } diff --git a/example/tools/plot3d.cpp b/example/tools/plot3d.cpp index 94848d5ce0377765d75a8d439bad9e055ea077bb..39e7f72c8705969c047f9adb3977268d78a040c4 100755 --- a/example/tools/plot3d.cpp +++ b/example/tools/plot3d.cpp @@ -3,7 +3,7 @@ * $Id: plot.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,64 +53,74 @@ int main () { #if defined(VISP_HAVE_DISPLAY) - //Create a window with one graphic - vpPlot plot(1); + try { + //Create a window with one graphic + vpPlot plot(1); - // Change the default font - //plot.setFont("-misc-fixed-bold-r-semicondensed--0-0-75-75-c-0-iso8859-10"); + // Change the default font + //plot.setFont("-misc-fixed-bold-r-semicondensed--0-0-75-75-c-0-iso8859-10"); - //The graphic contains 2 curves - plot.initGraph(0,2); - - //Set the graphic parameters - plot.setTitle(0, "First graphic"); - plot.setUnitX(0, "time (s)"); - plot.setUnitY(0, "y"); - plot.setUnitZ(0, "z"); - plot.setLegend(0,0, "y^2+z^2=1 and y(0) = 1"); - plot.setLegend(0,1, "y^2+z^2=1 and y(0) = -1"); - plot.setColor(0,0,vpColor::red); - plot.setColor(0,1,vpColor::green); + //The graphic contains 2 curves + plot.initGraph(0,2); - double x = 0; - double y = 1; - double z = 0 ; - double dx = 0.08; - double dy = 0.04; - double zsign = 1.0; + //Set the graphic parameters + plot.setTitle(0, "First graphic"); + plot.setUnitX(0, "time (s)"); + plot.setUnitY(0, "y"); + plot.setUnitZ(0, "z"); + plot.setLegend(0,0, "y^2+z^2=1 and y(0) = 1"); + plot.setLegend(0,1, "y^2+z^2=1 and y(0) = -1"); + plot.setColor(0,0,vpColor::red); + plot.setColor(0,1,vpColor::green); - unsigned long iter = 0; + double x = 0; + double y = 1; + double z = 0 ; + double dx = 0.08; + double dy = 0.04; + double zsign = 1.0; - std::cout << "Hit CTRL-C to exit..."; - while(1) { - if (iter < 300) { - //y*y+z*z = 1 - if (fabs(y) < 1.0) - z = sqrt(1.0-y*y); - else z = 0; + unsigned long iter = 0; - //Add points to the graphic - plot.plot(0,0, x, y,z*zsign); - plot.plot(0,1, x, -y,-z*zsign); - - x += dx; - - if (fabs(y) >= 1.0 ) - dy = -dy; - y += dy; - if (fabs(y) >= 1.0 ) - zsign = -zsign; - } - else { - // Tip: to allows modifying the point of view with the mouse we - // plot always the last point - plot.plot(0,0, x, y,z*zsign); - plot.plot(0,1, x, -y,-z*zsign); + std::cout << "Hit CTRL-C to or right mouse button to exit..." << std::endl; + bool end = false; + while( !end ) { + if (iter < 300) { + //y*y+z*z = 1 + if (fabs(y) < 1.0) + z = sqrt(1.0-y*y); + else z = 0; + + //Add points to the graphic + if (plot.plot(0,0, x, y, z*zsign) == vpMouseButton::button3) + end = true; + if (plot.plot(0,1, x, -y, -z*zsign) == vpMouseButton::button3) + end = true; + + x += dx; + + if (fabs(y) >= 1.0 ) + dy = -dy; + y += dy; + if (fabs(y) >= 1.0 ) + zsign = -zsign; + } + else { + // Tip: to allows modifying the point of view with the mouse we + // plot always the last point + if (plot.plot(0,0, x, y,z*zsign) == vpMouseButton::button3) + end = true; + if (plot.plot(0,1, x, -y,-z*zsign) == vpMouseButton::button3) + end = true; + } + iter ++; } - iter ++; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; #else std::cout << "Plot functionalities are not avalaible since no display is available." << std::endl; #endif diff --git a/example/tracking/CMakeLists.txt b/example/tracking/CMakeLists.txt index 39d1b5411de446ac0567f6687dd98ef531f70baa..fad0953d92d1f9aa9d0d4b7d476d00409152657d 100644 --- a/example/tracking/CMakeLists.txt +++ b/example/tracking/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4156 2013-03-11 16:12:24Z ayol $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,10 +43,11 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE mbtEdgeTracking.cpp mbtEdgeKltTracking.cpp mbtKltTracking.cpp + templateTracker.cpp trackDot2WithAutoDetection.cpp trackMeCircle.cpp trackMeEllipse.cpp @@ -58,30 +59,108 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test -ADD_TEST(mbtEdgeTracking mbtEdgeTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(mbtEdgeKltTracking mbtEdgeKltTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(mbtKltTracking mbtKltTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackDot2WithAutoDetection trackDot2WithAutoDetection -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackMeCircle trackMeCircle -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackMeEllipse trackMeEllipse -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackMeLine trackMeLine -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackMeNurbs trackMeNurbs -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackDot trackDot -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(trackDot2 trackDot2 -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(mbtEdgeTracking mbtEdgeTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(mbtEdgeKltTracking mbtEdgeKltTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(mbtKltTracking mbtKltTracking -c ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(mbtEdgeTracking-cao mbtEdgeTracking -c -f ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(mbtEdgeKltTracking-cao mbtEdgeKltTracking -c -f ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(mbtKltTracking-cao mbtKltTracking -c -f ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(mbtEdgeTracking-cao-nocyl mbtEdgeTracking -c -f -C ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(mbtEdgeKltTracking-cao-nocyl mbtEdgeKltTracking -c -f -C ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(trackDot2WithAutoDetection trackDot2WithAutoDetection -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackMeCircle trackMeCircle -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackMeEllipse trackMeEllipse -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackMeLine trackMeLine -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackMeNurbs trackMeNurbs -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackDot trackDot -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(trackDot2 trackDot2 -c ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-SSDESM-Affine templateTracker -c -l 2 -t 0 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDESM-Homography templateTracker -c -l 2 -t 0 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDESM-HomographySL3 templateTracker -c -l 2 -t 0 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDESM-SRT templateTracker -c -l 2 -t 0 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDESM-Translation templateTracker -c -l 2 -t 0 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-SSDESM-Affine templateTracker -c -l 2 -t 0 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDESM-Homography templateTracker -c -l 2 -t 0 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDESM-HomographySL3 templateTracker -c -l 2 -t 0 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDESM-SRT templateTracker -c -l 2 -t 0 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDESM-Translation templateTracker -c -l 2 -t 0 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-SSDForwardAdditional-Affine templateTracker -c -l 2 -t 1 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardAdditional-Homography templateTracker -c -l 2 -t 1 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardAdditional-HomographySL3 templateTracker -c -l 2 -t 1 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardAdditional-SRT templateTracker -c -l 2 -t 1 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardAdditional-Translation templateTracker -c -l 2 -t 1 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-SSDForwardAdditional-Affine templateTracker -c -l 2 -t 1 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardAdditional-Homography templateTracker -c -l 2 -t 1 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardAdditional-HomographySL3 templateTracker -c -l 2 -t 1 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardAdditional-SRT templateTracker -c -l 2 -t 1 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardAdditional-Translation templateTracker -c -l 2 -t 1 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-SSDForwardCompositional-Affine templateTracker -c -l 2 -t 2 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardCompositional-Homography templateTracker -c -l 2 -t 2 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardCompositional-HomographySL3 templateTracker -c -l 2 -t 2 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardCompositional-SRT templateTracker -c -l 2 -t 2 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDForwardCompositional-Translation templateTracker -c -l 2 -t 2 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-SSDForwardCompositional-Affine templateTracker -c -l 2 -t 2 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardCompositional-Homography templateTracker -c -l 2 -t 2 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardCompositional-HomographySL3 templateTracker -c -l 2 -t 2 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardCompositional-SRT templateTracker -c -l 2 -t 2 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDForwardCompositional-Translation templateTracker -c -l 2 -t 2 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-SSDInverseCompositional-Affine templateTracker -c -l 2 -t 3 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDInverseCompositional-Homography templateTracker -c -l 2 -t 3 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDInverseCompositional-HomographySL3 templateTracker -c -l 2 -t 3 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDInverseCompositional-SRT templateTracker -c -l 2 -t 3 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-SSDInverseCompositional-Translation templateTracker -c -l 2 -t 3 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-SSDInverseCompositional-Affine templateTracker -c -l 2 -t 3 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDInverseCompositional-Homography templateTracker -c -l 2 -t 3 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDInverseCompositional-HomographySL3 templateTracker -c -l 2 -t 3 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDInverseCompositional-SRT templateTracker -c -l 2 -t 3 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-SSDInverseCompositional-Translation templateTracker -c -l 2 -t 3 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-ZNCCForwardAdditional-Affine templateTracker -c -l 2 -t 4 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCForwardAdditional-Homography templateTracker -c -l 2 -t 4 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCForwardAdditional-HomographySL3 templateTracker -c -l 2 -t 4 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCForwardAdditional-SRT templateTracker -c -l 2 -t 4 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCForwardAdditional-Translation templateTracker -c -l 2 -t 4 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-ZNCCForwardAdditional-Affine templateTracker -c -l 2 -t 4 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCForwardAdditional-Homography templateTracker -c -l 2 -t 4 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCForwardAdditional-HomographySL3 templateTracker -c -l 2 -t 4 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCForwardAdditional-SRT templateTracker -c -l 2 -t 4 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCForwardAdditional-Translation templateTracker -c -l 2 -t 4 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) + +#add_test(templateTracker-ZNCCInverseCompositional-Affine templateTracker -c -l 2 -t 5 -w 0 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCInverseCompositional-Homography templateTracker -c -l 2 -t 5 -w 1 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCInverseCompositional-HomographySL3 templateTracker -c -l 2 -t 5 -w 2 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCInverseCompositional-SRT templateTracker -c -l 2 -t 5 -w 3 ${OPTION_TO_DESACTIVE_DISPLAY}) +#add_test(templateTracker-ZNCCInverseCompositional-Translation templateTracker -c -l 2 -t 5 -w 4 ${OPTION_TO_DESACTIVE_DISPLAY}) + +add_test(templateTrackerPyramidal-ZNCCInverseCompositional-Affine templateTracker -c -l 2 -t 5 -w 0 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCInverseCompositional-Homography templateTracker -c -l 2 -t 5 -w 1 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCInverseCompositional-HomographySL3 templateTracker -c -l 2 -t 5 -w 2 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCInverseCompositional-SRT templateTracker -c -l 2 -t 5 -w 3 -p ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(templateTrackerPyramidal-ZNCCInverseCompositional-Translation templateTracker -c -l 2 -t 5 -w 4 -p ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/tracking/mbtEdgeKltTracking.cpp b/example/tracking/mbtEdgeKltTracking.cpp index ac53ba8685fffe782ce8561f36c7d7a98d8f1b9f..84f3853abd0a2f729bda50595704f6bfe77ac06a 100644 --- a/example/tracking/mbtEdgeKltTracking.cpp +++ b/example/tracking/mbtEdgeKltTracking.cpp @@ -3,7 +3,7 @@ * $Id: mbtTracking.cpp 3957 2012-11-07 15:22:30Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,11 +60,15 @@ #include <visp/vpVideoReader.h> #include <visp/vpParseArgv.h> -#if defined (VISP_HAVE_OPENCV) && defined (VISP_HAVE_DISPLAY) +#if defined (VISP_HAVE_OPENCV) && defined (VISP_HAVE_DISPLAY) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) #define GETOPTARGS "x:m:i:n:dchtfCo" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayFeatures, bool &click_allowed, bool &display, + bool& cao3DModel, bool& trackCylinder, bool &useOgre); void usage(const char *name, const char *badparam) { @@ -134,17 +138,19 @@ OPTIONS: \n\ } -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, std::string &initFile, bool &displayFeatures, bool &click_allowed, bool &display, bool& cao3DModel, bool& trackCylinder, bool &useOgre) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayFeatures, bool &click_allowed, bool &display, + bool& cao3DModel, bool& trackCylinder, bool &useOgre) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'x': configFile = optarg; break; - case 'm': modelFile = optarg; break; - case 'n': initFile = optarg; break; + case 'i': ipath = optarg_; break; + case 'x': configFile = optarg_; break; + case 'm': modelFile = optarg_; break; + case 'n': initFile = optarg_; break; case 't': displayFeatures = false; break; case 'f': cao3DModel = true; break; case 'c': click_allowed = false; break; @@ -154,7 +160,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -163,7 +169,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -173,294 +179,365 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_configFile; - std::string configFile; - std::string opt_modelFile; - std::string modelFile; - std::string opt_initFile; - std::string initFile; - bool displayFeatures = true; - bool opt_click_allowed = true; - bool opt_display = true; - bool cao3DModel = false; - bool trackCylinder = true; - bool useOgre = false; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayFeatures, opt_click_allowed, opt_display, cao3DModel, trackCylinder, useOgre)) { - return (-1); - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() ){ - usage(argv[0], NULL); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << std::endl; - - return (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_configFile; + std::string configFile; + std::string opt_modelFile; + std::string modelFile; + std::string opt_initFile; + std::string initFile; + bool displayFeatures = true; + bool opt_click_allowed = true; + bool opt_display = true; + bool cao3DModel = false; + bool trackCylinder = true; + bool useOgre = false; + bool quit = false; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayFeatures, opt_click_allowed, opt_display, cao3DModel, trackCylinder, useOgre)) { + return (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - else - ipath = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - - if (!opt_configFile.empty()) - configFile = opt_configFile; - else if (!opt_ipath.empty()) - configFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - else - configFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - - if (!opt_modelFile.empty()){ - modelFile = opt_modelFile; - }else{ - std::string modelFileCao; - std::string modelFileWrl; - if(trackCylinder){ - modelFileCao = "/ViSP-images/mbt/cube_and_cylinder.cao"; - modelFileWrl = "/ViSP-images/mbt/cube_and_cylinder.wrl"; - }else{ - modelFileCao = "/ViSP-images/mbt/cube.cao"; - modelFileWrl = "/ViSP-images/mbt/cube.wrl"; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() ){ + usage(argv[0], NULL); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << std::endl; + + return (-1); } - if(!opt_ipath.empty()){ - if(cao3DModel){ - modelFile = opt_ipath + vpIoTools::path(modelFileCao); + // Get the option values + if (!opt_ipath.empty()) + ipath = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + else + ipath = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + + if (!opt_configFile.empty()) + configFile = opt_configFile; + else if (!opt_ipath.empty()) + configFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube.xml"); + else + configFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube.xml"); + + if (!opt_modelFile.empty()){ + modelFile = opt_modelFile; + }else{ + std::string modelFileCao; + std::string modelFileWrl; + if(trackCylinder){ + modelFileCao = "ViSP-images/mbt/cube_and_cylinder.cao"; + modelFileWrl = "ViSP-images/mbt/cube_and_cylinder.wrl"; + }else{ + modelFileCao = "ViSP-images/mbt/cube.cao"; + modelFileWrl = "ViSP-images/mbt/cube.wrl"; } - else{ + + if(!opt_ipath.empty()){ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = opt_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = opt_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); #endif - } - } - else{ - if(cao3DModel){ - modelFile = env_ipath + vpIoTools::path(modelFileCao); + } } else{ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = env_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(env_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = env_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); #endif + } } } - } - - if (!opt_initFile.empty()) - initFile = opt_initFile; - else if (!opt_ipath.empty()) - initFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - else - initFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - - vpImage<unsigned char> I; - vpVideoReader reader; - - reader.setFileName(ipath.c_str()); - try{ - reader.open(I); - }catch(...){ - std::cout << "Cannot open sequence: " << ipath << std::endl; - return -1; - } - - reader.acquire(I); - // initialise a display + if (!opt_initFile.empty()) + initFile = opt_initFile; + else if (!opt_ipath.empty()) + initFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube"); + else + initFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube"); + + vpImage<unsigned char> I; + vpVideoReader reader; + + reader.setFileName(ipath); + try{ + reader.open(I); + }catch(...){ + std::cout << "Cannot open sequence: " << ipath << std::endl; + return -1; + } + + reader.acquire(I); + + // initialise a display #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display; + vpDisplayD3D display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #else - opt_display = false; + opt_display = false; #endif - if (opt_display) - { + if (opt_display) + { #if (defined VISP_HAVE_DISPLAY) - display.init(I, 100, 100, "Test tracking") ; + display.init(I, 100, 100, "Test tracking") ; #endif - vpDisplay::display(I) ; - vpDisplay::flush(I); - } + vpDisplay::display(I) ; + vpDisplay::flush(I); + } - vpMbEdgeKltTracker tracker; - vpHomogeneousMatrix cMo; - vpCameraParameters cam; + vpMbEdgeKltTracker tracker; + vpHomogeneousMatrix cMo; + vpCameraParameters cam; - // Initialise the tracker: camera parameters, moving edge and KLT settings + // Initialise the tracker: camera parameters, moving edge and KLT settings #if defined (VISP_HAVE_XML2) - // From the xml file - tracker.loadConfigFile(configFile.c_str()); + // From the xml file + tracker.loadConfigFile(configFile); #else - // By setting the parameters: - cam.initPersProjWithoutDistortion(547, 542, 338, 234); - - vpMe me; - me.setMaskSize(5); - me.setMaskNumber(180); - me.setRange(7); - me.setThreshold(5000); - me.setMu1(0.5); - me.setMu2(0.5); - me.setMinSampleStep(4); - me.setNbTotalSample(250); - - vpKltOpencv klt; - klt.setMaxFeatures(10000); - klt.setWindowSize(5); - klt.setQuality(0.01); - klt.setMinDistance(5); - klt.setHarrisFreeParameter(0.01); - klt.setBlockSize(3); - klt.setPyramidLevels(3); - - tracker.setCameraParameters(cam); - tracker.setMovingEdge(me); - tracker.setKltOpencv(klt); - tracker.setAngleAppear( vpMath::rad(65) ); - tracker.setAngleDisappear( vpMath::rad(75) ); - tracker.setMaskBorder(5); - - // Specify the clipping to - tracker.setNearClippingDistance(0.01); - tracker.setFarClippingDistance(0.90); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); -// tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(7); + me.setThreshold(5000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + + vpKltOpencv klt; + klt.setMaxFeatures(10000); + klt.setWindowSize(5); + klt.setQuality(0.01); + klt.setMinDistance(5); + klt.setHarrisFreeParameter(0.01); + klt.setBlockSize(3); + klt.setPyramidLevels(3); + + tracker.setCameraParameters(cam); + tracker.setMovingEdge(me); + tracker.setKltOpencv(klt); + tracker.setAngleAppear( vpMath::rad(65) ); + tracker.setAngleDisappear( vpMath::rad(75) ); + tracker.setMaskBorder(5); + + // Specify the clipping to + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING #endif - - // Display the moving edges, and the Klt points - tracker.setDisplayFeatures(displayFeatures); - - // Tells if the tracker has to use Ogre3D for visibility tests - tracker.setOgreVisibilityTest(useOgre); - - // Retrieve the camera parameters from the tracker - tracker.getCameraParameters(cam); - - // Loop to position the cube - if (opt_display && opt_click_allowed) - { - while(!vpDisplay::getClick(I,false)){ - vpDisplay::display(I); - vpDisplay::displayCharString(I, 15, 10, - "click after positioning the object", - vpColor::red); - vpDisplay::flush(I) ; + + // Display the moving edges, and the Klt points + tracker.setDisplayFeatures(displayFeatures); + + // Tells if the tracker has to use Ogre3D for visibility tests + tracker.setOgreVisibilityTest(useOgre); + + // Retrieve the camera parameters from the tracker + tracker.getCameraParameters(cam); + + // Loop to position the cube + if (opt_display && opt_click_allowed) + { + while(!vpDisplay::getClick(I,false)){ + vpDisplay::display(I); + vpDisplay::displayText(I, 15, 10, "click after positioning the object", vpColor::red); + vpDisplay::flush(I) ; + } } - } - // Load the 3D model (either a vrml file or a .cao file) - try{ - tracker.loadModel(modelFile.c_str()); - } - catch(...) - { - return 0; - } - // Initialise the tracker by clicking on the image - // This function looks for - // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation - // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) - if (opt_display && opt_click_allowed) - { - tracker.initClick(I, initFile.c_str(), true); + // Load the 3D model (either a vrml file or a .cao file) + tracker.loadModel(modelFile); + + // Initialise the tracker by clicking on the image + // This function looks for + // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation + // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) + if (opt_display && opt_click_allowed) + { + tracker.initClick(I, initFile, true); + tracker.getPose(cMo); + // display the 3D model at the given pose + tracker.display(I,cMo, cam, vpColor::red); + } + else + { + vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); + tracker.initFromPose(I, cMoi); + } + + //track the model + tracker.track(I); tracker.getPose(cMo); - // display the 3D model at the given pose - tracker.display(I,cMo, cam, vpColor::red); - } - else - { - vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); - tracker.initFromPose(I, cMoi); - } - //track the model - tracker.track(I); - tracker.getPose(cMo); - - if (opt_display) - vpDisplay::flush(I); - - // Uncomment if you want to compute the covariance matrix. - // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. - - while (!reader.end()) - { - try + if (opt_display) + vpDisplay::flush(I); + + // Uncomment if you want to compute the covariance matrix. + // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. + + while (!reader.end()) { - // acquire a new image + // acquire a new image reader.acquire(I); // display the image if (opt_display) vpDisplay::display(I); - // track the object - tracker.track(I); - tracker.getPose(cMo); - // display the 3D model - if (opt_display) - { - tracker.display(I, cMo, cam, vpColor::darkRed); - // display the frame - vpDisplay::displayFrame (I, cMo, cam, 0.05, vpColor::blue); + + // Test to reset the tracker + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 10) { + vpTRACE("Test reset tracker"); + if (opt_display) + vpDisplay::display(I); + tracker.resetTracker(); +#if defined (VISP_HAVE_XML2) + tracker.loadConfigFile(configFile); +#else + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(7); + me.setThreshold(5000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + + vpKltOpencv klt; + klt.setMaxFeatures(10000); + klt.setWindowSize(5); + klt.setQuality(0.01); + klt.setMinDistance(5); + klt.setHarrisFreeParameter(0.01); + klt.setBlockSize(3); + klt.setPyramidLevels(3); + + tracker.setCameraParameters(cam); + tracker.setMovingEdge(me); + tracker.setKltOpencv(klt); + tracker.setAngleAppear( vpMath::rad(65) ); + tracker.setAngleDisappear( vpMath::rad(75) ); + tracker.setMaskBorder(5); + + // Specify the clipping to + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING +#endif + tracker.loadModel(modelFile); + tracker.setCameraParameters(cam); + tracker.setOgreVisibilityTest(useOgre); + tracker.initFromPose(I, cMo); } - - // Uncomment if you want to print the covariance matrix. + + // Test to set an initial pose + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 50) { + cMo.buildFrom(0.04371844921, 0.08438820979, 0.5382029442, 2.200417277, 0.873535825, -0.3479076844); + vpTRACE("Test set pose"); + tracker.setPose(I, cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); +// if (opt_click_allowed) { +// vpDisplay::flush(I); +// vpDisplay::getClick(I); +// } + } + } + + // track the object: stop tracking from frame 40 to 50 + if (reader.getFrameIndex() - reader.getFirstFrameIndex() < 40 || reader.getFrameIndex() > reader.getFirstFrameIndex() + 50) { + tracker.track(I); + tracker.getPose(cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); + } + } + + if (opt_click_allowed) { + vpDisplay::displayText(I, 10, 10, "Click to quit", vpColor::red); + if (vpDisplay::getClick(I, false)) { + quit = true; + break; + } + } + + // Uncomment if you want to print the covariance matrix. // Make sure tracker.setCovarianceComputation(true) has been called (uncomment below). // std::cout << tracker.getCovarianceMatrix() << std::endl << std::endl; - + + vpDisplay::flush(I) ; } - catch(...) - { - std::cout << "error caught" << std::endl; - break; + if (opt_click_allowed && !quit) { + vpDisplay::getClick(I); } - vpDisplay::flush(I) ; - } - reader.close(); + reader.close(); #if defined (VISP_HAVE_XML2) - // Cleanup memory allocated by xml library used to parse the xml config file in vpMbEdgeKltTracker::loadConfigFile() - vpXmlParser::cleanup(); + // Cleanup memory allocated by xml library used to parse the xml config file in vpMbEdgeKltTracker::loadConfigFile() + vpXmlParser::cleanup(); #endif -#ifdef VISP_HAVE_COIN - // Cleanup memory allocated by Coin library used to load a vrml model in vpMbEdgeKltTracker::loadModel() - SoDB::finish(); +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + // Cleanup memory allocated by Coin library used to load a vrml model in vpMbEdgeKltTracker::loadModel() + // We clean only if Coin was used. + if(! cao3DModel) + SoDB::finish(); #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/tracking/mbtEdgeTracking.cpp b/example/tracking/mbtEdgeTracking.cpp index 9edb4b4bf42a9d1ea2b7e74703fc5969b58e3121..04ec00cd4b992d1b268affea8315fbea4106d55a 100644 --- a/example/tracking/mbtEdgeTracking.cpp +++ b/example/tracking/mbtEdgeTracking.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: mbtEdgeTracking.cpp 4330 2013-07-20 11:04:19Z ayol $ + * $Id: mbtEdgeTracking.cpp 5156 2015-01-13 07:07:08Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,10 @@ #define GETOPTARGS "x:m:i:n:dchtfCo" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayFeatures, bool &click_allowed, bool &display, + bool& cao3DModel, bool& trackCylinder, bool &useOgre); void usage(const char *name, const char *badparam) { @@ -135,27 +139,29 @@ OPTIONS: \n\ } -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, std::string &initFile, bool &displayMovingEdge, bool &click_allowed, bool &display, bool& cao3DModel, bool& trackCylinder, bool &useOgre) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayFeatures, bool &click_allowed, bool &display, + bool& cao3DModel, bool& trackCylinder, bool &useOgre) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'x': configFile = optarg; break; - case 'm': modelFile = optarg; break; - case 'n': initFile = optarg; break; - case 't': displayMovingEdge = false; break; + case 'i': ipath = optarg_; break; + case 'x': configFile = optarg_; break; + case 'm': modelFile = optarg_; break; + case 'n': initFile = optarg_; break; + case 't': displayFeatures = false; break; case 'f': cao3DModel = true; break; case 'c': click_allowed = false; break; case 'd': display = false; break; case 'C': trackCylinder = false; break; - case 'o' : useOgre = true; break; + case 'o': useOgre = true; break; case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -164,7 +170,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -174,281 +180,341 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_configFile; - std::string configFile; - std::string opt_modelFile; - std::string modelFile; - std::string opt_initFile; - std::string initFile; - bool displayMovingEdge = true; - bool opt_click_allowed = true; - bool opt_display = true; - bool cao3DModel = false; - bool trackCylinder = true; - bool useOgre = false; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayMovingEdge, opt_click_allowed, opt_display, cao3DModel, trackCylinder, useOgre)) { - return (-1); - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() ){ - usage(argv[0], NULL); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << std::endl; - - return (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_configFile; + std::string configFile; + std::string opt_modelFile; + std::string modelFile; + std::string opt_initFile; + std::string initFile; + bool displayFeatures = true; + bool opt_click_allowed = true; + bool opt_display = true; + bool cao3DModel = false; + bool trackCylinder = true; + bool useOgre = false; + bool quit = false; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayFeatures, opt_click_allowed, opt_display, cao3DModel, trackCylinder, useOgre)) { + return (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - else - ipath = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - - if (!opt_configFile.empty()) - configFile = opt_configFile; - else if (!opt_ipath.empty()) - configFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - else - configFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - - if (!opt_modelFile.empty()){ - modelFile = opt_modelFile; - }else{ - std::string modelFileCao; - std::string modelFileWrl; - if(trackCylinder){ - modelFileCao = "/ViSP-images/mbt/cube_and_cylinder.cao"; - modelFileWrl = "/ViSP-images/mbt/cube_and_cylinder.wrl"; - }else{ - modelFileCao = "/ViSP-images/mbt/cube.cao"; - modelFileWrl = "/ViSP-images/mbt/cube.wrl"; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() ){ + usage(argv[0], NULL); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << std::endl; + + return (-1); } - if(!opt_ipath.empty()){ - if(cao3DModel){ - modelFile = opt_ipath + vpIoTools::path(modelFileCao); + // Get the option values + if (!opt_ipath.empty()) + ipath = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + else + ipath = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + + if (!opt_configFile.empty()) + configFile = opt_configFile; + else if (!opt_ipath.empty()) + configFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube.xml"); + else + configFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube.xml"); + + if (!opt_modelFile.empty()){ + modelFile = opt_modelFile; + }else{ + std::string modelFileCao; + std::string modelFileWrl; + if(trackCylinder){ + modelFileCao = "ViSP-images/mbt/cube_and_cylinder.cao"; + modelFileWrl = "ViSP-images/mbt/cube_and_cylinder.wrl"; + }else{ + modelFileCao = "ViSP-images/mbt/cube.cao"; + modelFileWrl = "ViSP-images/mbt/cube.wrl"; } - else{ + + if(!opt_ipath.empty()){ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = opt_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = opt_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); #endif - } - } - else{ - if(cao3DModel){ - modelFile = env_ipath + vpIoTools::path(modelFileCao); + } } else{ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = env_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(env_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = env_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); #endif + } } } - } - - if (!opt_initFile.empty()) - initFile = opt_initFile; - else if (!opt_ipath.empty()) - initFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - else - initFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - - vpImage<unsigned char> I; - vpVideoReader reader; - - reader.setFileName(ipath.c_str()); - try{ - reader.open(I); - }catch(...){ - std::cout << "Cannot open sequence: " << ipath << std::endl; - return -1; - } - - reader.acquire(I); - // initialise a display + if (!opt_initFile.empty()) + initFile = opt_initFile; + else if (!opt_ipath.empty()) + initFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube"); + else + initFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube"); + + vpImage<unsigned char> I; + vpVideoReader reader; + + reader.setFileName(ipath); + try{ + reader.open(I); + }catch(...){ + std::cout << "Cannot open sequence: " << ipath << std::endl; + return -1; + } + + reader.acquire(I); + + // initialise a display #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display; + vpDisplayD3D display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #else - opt_display = false; + opt_display = false; #endif - if (opt_display) - { + if (opt_display) + { #if (defined VISP_HAVE_DISPLAY) - display.init(I, 100, 100, "Test tracking") ; + display.init(I, 100, 100, "Test tracking") ; #endif - vpDisplay::display(I) ; - vpDisplay::flush(I); - } + vpDisplay::display(I) ; + vpDisplay::flush(I); + } - vpMbEdgeTracker tracker; - vpHomogeneousMatrix cMo; + vpMbEdgeTracker tracker; + vpHomogeneousMatrix cMo; - // Initialise the tracker: camera parameters, moving edge and KLT settings - vpCameraParameters cam; + // Initialise the tracker: camera parameters, moving edge and KLT settings + vpCameraParameters cam; #if defined (VISP_HAVE_XML2) - // From the xml file - tracker.loadConfigFile(configFile.c_str()); + // From the xml file + tracker.loadConfigFile(configFile); #else - // By setting the parameters: - cam.initPersProjWithoutDistortion(547, 542, 338, 234); - - vpMe me; - me.setMaskSize(5); - me.setMaskNumber(180); - me.setRange(7); - me.setThreshold(5000); - me.setMu1(0.5); - me.setMu2(0.5); - me.setMinSampleStep(4); - me.setNbTotalSample(250); - - tracker.setCameraParameters(cam); - tracker.setMovingEdge(me); - - // Specify the clipping to use - tracker.setNearClippingDistance(0.01); - tracker.setFarClippingDistance(0.90); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); -// tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(7); + me.setThreshold(5000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + + tracker.setCameraParameters(cam); + tracker.setMovingEdge(me); + + // Specify the clipping to use + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING #endif - // Display the moving edges, see documentation for the significations of the colour - tracker.setDisplayMovingEdges(displayMovingEdge); - - // Tells if the tracker has to use Ogre3D for visibility tests - tracker.setOgreVisibilityTest(useOgre); - - // Retrieve the camera parameters from the tracker - tracker.getCameraParameters(cam); - - // Loop to position the cube - if (opt_display && opt_click_allowed) - { - while(!vpDisplay::getClick(I,false)){ - vpDisplay::display(I); - vpDisplay::displayCharString(I, 15, 10, - "click after positioning the object", - vpColor::red); - vpDisplay::flush(I) ; + // Display the moving edges, see documentation for the signification of the colours + tracker.setDisplayFeatures(displayFeatures); + + // Tells if the tracker has to use Ogre3D for visibility tests + tracker.setOgreVisibilityTest(useOgre); + + // Retrieve the camera parameters from the tracker + tracker.getCameraParameters(cam); + + // Loop to position the cube + if (opt_display && opt_click_allowed) + { + while(!vpDisplay::getClick(I,false)){ + vpDisplay::display(I); + vpDisplay::displayText(I, 15, 10, "click after positioning the object", vpColor::red); + vpDisplay::flush(I) ; + } } - } - // Load the 3D model (either a vrml file or a .cao file) - try{ - tracker.loadModel(modelFile.c_str()); - } - catch(...) - { - return 0; - } - // Initialise the tracker by clicking on the image - // This function looks for - // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation - // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) - if (opt_display && opt_click_allowed) - { - tracker.initClick(I, initFile.c_str(), true); + // Load the 3D model (either a vrml file or a .cao file) + tracker.loadModel(modelFile); + + // Initialise the tracker by clicking on the image + // This function looks for + // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation + // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) + if (opt_display && opt_click_allowed) + { + tracker.initClick(I, initFile, true); + tracker.getPose(cMo); + // display the 3D model at the given pose + tracker.display(I,cMo, cam, vpColor::red); + } + else + { + vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); + tracker.initFromPose(I, cMoi); + } + + //track the model + tracker.track(I); tracker.getPose(cMo); - // display the 3D model at the given pose - tracker.display(I,cMo, cam, vpColor::red); - } - else - { - vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); - tracker.initFromPose(I, cMoi); - } - //track the model - tracker.track(I); - tracker.getPose(cMo); - - if (opt_display) - vpDisplay::flush(I); + if (opt_display) + vpDisplay::flush(I); - // Uncomment if you want to compute the covariance matrix. - // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. - - while (!reader.end()) - { - try + // Uncomment if you want to compute the covariance matrix. + // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. + + while (!reader.end()) { - // acquire a new image + // acquire a new image reader.acquire(I); // display the image if (opt_display) vpDisplay::display(I); - // track the object - tracker.track(I); - tracker.getPose(cMo); - // display the 3D model - if (opt_display) - { - tracker.display(I, cMo, cam, vpColor::darkRed); - // display the frame - vpDisplay::displayFrame (I, cMo, cam, 0.05, vpColor::blue); + + // Test to reset the tracker + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 10) { + vpTRACE("Test reset tracker"); + if (opt_display) + vpDisplay::display(I); + tracker.resetTracker(); +#if defined (VISP_HAVE_XML2) + tracker.loadConfigFile(configFile); +#else + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(7); + me.setThreshold(5000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + + tracker.setCameraParameters(cam); + tracker.setMovingEdge(me); + + // Specify the clipping to use + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING +#endif + tracker.loadModel(modelFile); + tracker.setCameraParameters(cam); + tracker.setOgreVisibilityTest(useOgre); + tracker.initFromPose(I, cMo); + } + + // Test to set an initial pose + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 50) { + cMo.buildFrom(0.04371844921, 0.08438820979, 0.5382029442, 2.200417277, 0.873535825, -0.3479076844); + vpTRACE("Test set pose"); + tracker.setPose(I, cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); +// if (opt_click_allowed) { +// vpDisplay::flush(I); +// vpDisplay::getClick(I); +// } + } + } + + // track the object: stop tracking from frame 40 to 50 + if (reader.getFrameIndex() - reader.getFirstFrameIndex() < 40 || reader.getFrameIndex() > reader.getFirstFrameIndex() + 50) { + tracker.track(I); + tracker.getPose(cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); + } } - - // Uncomment if you want to print the covariance matrix. + + if (opt_click_allowed) { + vpDisplay::displayText(I, 10, 10, "Click to quit", vpColor::red); + if (vpDisplay::getClick(I, false)) { + quit = true; + break; + } + } + + // Uncomment if you want to print the covariance matrix. // Make sure tracker.setCovarianceComputation(true) has been called (uncomment below). // std::cout << tracker.getCovarianceMatrix() << std::endl << std::endl; - + + vpDisplay::flush(I) ; } - catch(...) - { - std::cout << "error caught" << std::endl; - break; + + if (opt_click_allowed && !quit) { + vpDisplay::getClick(I); } - vpDisplay::flush(I) ; - } - reader.close(); + reader.close(); #if defined (VISP_HAVE_XML2) - // Cleanup memory allocated by xml library used to parse the xml config file in vpMbEdgeTracker::loadConfigFile() - vpXmlParser::cleanup(); + // Cleanup memory allocated by xml library used to parse the xml config file in vpMbEdgeTracker::loadConfigFile() + vpXmlParser::cleanup(); #endif -#ifdef VISP_HAVE_COIN - // Cleanup memory allocated by Coin library used to load a vrml model in vpMbEdgeTracker::loadModel() - SoDB::finish(); +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + // Cleanup memory allocated by Coin library used to load a vrml model in vpMbEdgeTracker::loadModel() + // We clean only if Coin was used. + if(! cao3DModel) + SoDB::finish(); #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/tracking/mbtKltTracking.cpp b/example/tracking/mbtKltTracking.cpp index 18a8217bf534ccb298f745f1f5722caa9844f9ab..080b67d934c685e712651fe0dc2dbc322d53fa63 100644 --- a/example/tracking/mbtKltTracking.cpp +++ b/example/tracking/mbtKltTracking.cpp @@ -3,7 +3,7 @@ * $Id: mbtTracking.cpp 3957 2012-11-07 15:22:30Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,11 +60,15 @@ #include <visp/vpVideoReader.h> #include <visp/vpParseArgv.h> -#if defined (VISP_HAVE_OPENCV) && defined (VISP_HAVE_DISPLAY) +#if defined (VISP_HAVE_OPENCV) && defined (VISP_HAVE_DISPLAY) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) #define GETOPTARGS "x:m:i:n:dchtfo" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayKltPoints, bool &click_allowed, bool &display, + bool& cao3DModel, bool &useOgre); void usage(const char *name, const char *badparam) { @@ -129,26 +133,28 @@ OPTIONS: \n\ } -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, std::string &initFile, bool &displayKltPoints, bool &click_allowed, bool &display, bool& cao3DModel, bool &useOgre) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &configFile, std::string &modelFile, + std::string &initFile, bool &displayKltPoints, bool &click_allowed, bool &display, + bool& cao3DModel, bool &useOgre) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'x': configFile = optarg; break; - case 'm': modelFile = optarg; break; - case 'n': initFile = optarg; break; + case 'i': ipath = optarg_; break; + case 'x': configFile = optarg_; break; + case 'm': modelFile = optarg_; break; + case 'n': initFile = optarg_; break; case 't': displayKltPoints = false; break; case 'f': cao3DModel = true; break; case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'o' : useOgre = true; break; + case 'o': useOgre = true; break; case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -157,7 +163,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -167,275 +173,336 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &co int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_configFile; - std::string configFile; - std::string opt_modelFile; - std::string modelFile; - std::string opt_initFile; - std::string initFile; - bool displayKltPoints = true; - bool opt_click_allowed = true; - bool opt_display = true; - bool cao3DModel = false; - bool useOgre = false; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayKltPoints, opt_click_allowed, opt_display, cao3DModel, useOgre)) { - return (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_configFile; + std::string configFile; + std::string opt_modelFile; + std::string modelFile; + std::string opt_initFile; + std::string initFile; + bool displayKltPoints = true; + bool opt_click_allowed = true; + bool opt_display = true; + bool cao3DModel = false; + bool useOgre = false; + bool quit = false; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (!getOptions(argc, argv, opt_ipath, opt_configFile, opt_modelFile, opt_initFile, displayKltPoints, opt_click_allowed, opt_display, cao3DModel, useOgre)) { + return (-1); + } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() ){ - usage(argv[0], NULL); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << std::endl; - - return (-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() ){ + usage(argv[0], NULL); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << std::endl; + + return (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - else - ipath = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube/image%04d.pgm"); - - if (!opt_configFile.empty()) - configFile = opt_configFile; - else if (!opt_ipath.empty()) - configFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - else - configFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube.xml"); - - if (!opt_modelFile.empty()){ - modelFile = opt_modelFile; - }else{ - std::string modelFileCao = "/ViSP-images/mbt/cube.cao"; - std::string modelFileWrl = "/ViSP-images/mbt/cube.wrl"; - - if(!opt_ipath.empty()){ - if(cao3DModel){ - modelFile = opt_ipath + vpIoTools::path(modelFileCao); - } - else{ + // Get the option values + if (!opt_ipath.empty()) + ipath = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + else + ipath = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube/image%04d.pgm"); + + if (!opt_configFile.empty()) + configFile = opt_configFile; + else if (!opt_ipath.empty()) + configFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube.xml"); + else + configFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube.xml"); + + if (!opt_modelFile.empty()){ + modelFile = opt_modelFile; + }else{ + std::string modelFileCao = "ViSP-images/mbt/cube.cao"; + std::string modelFileWrl = "ViSP-images/mbt/cube.wrl"; + + if(!opt_ipath.empty()){ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = opt_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = opt_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(opt_ipath, modelFileCao); #endif - } - } - else{ - if(cao3DModel){ - modelFile = env_ipath + vpIoTools::path(modelFileCao); + } } else{ + if(cao3DModel){ + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); + } + else{ #ifdef VISP_HAVE_COIN - modelFile = env_ipath + vpIoTools::path(modelFileWrl); + modelFile = vpIoTools::createFilePath(env_ipath, modelFileWrl); #else - std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; - modelFile = env_ipath + vpIoTools::path(modelFileCao); + std::cerr << "Coin is not detected in ViSP. Use the .cao model instead." << std::endl; + modelFile = vpIoTools::createFilePath(env_ipath, modelFileCao); #endif + } } } - } - - if (!opt_initFile.empty()) - initFile = opt_initFile; - else if (!opt_ipath.empty()) - initFile = opt_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - else - initFile = env_ipath + vpIoTools::path("/ViSP-images/mbt/cube"); - - vpImage<unsigned char> I; - vpVideoReader reader; - - reader.setFileName(ipath.c_str()); - try{ - reader.open(I); - }catch(...){ - std::cout << "Cannot open sequence: " << ipath << std::endl; - return -1; - } - - reader.acquire(I); - // initialise a display + if (!opt_initFile.empty()) + initFile = opt_initFile; + else if (!opt_ipath.empty()) + initFile = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mbt/cube"); + else + initFile = vpIoTools::createFilePath(env_ipath, "ViSP-images/mbt/cube"); + + vpImage<unsigned char> I; + vpVideoReader reader; + + reader.setFileName(ipath); + try{ + reader.open(I); + }catch(...){ + std::cout << "Cannot open sequence: " << ipath << std::endl; + return -1; + } + + reader.acquire(I); + + // initialise a display #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display; + vpDisplayD3D display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #else - opt_display = false; + opt_display = false; #endif - if (opt_display) - { + if (opt_display) + { #if (defined VISP_HAVE_DISPLAY) - display.init(I, 100, 100, "Test tracking") ; + display.init(I, 100, 100, "Test tracking") ; #endif - vpDisplay::display(I) ; - vpDisplay::flush(I); - } + vpDisplay::display(I) ; + vpDisplay::flush(I); + } - vpMbKltTracker tracker; - vpHomogeneousMatrix cMo; - - // Load tracker config file (camera parameters and moving edge settings) - vpCameraParameters cam; + vpMbKltTracker tracker; + vpHomogeneousMatrix cMo; + + // Load tracker config file (camera parameters and moving edge settings) + vpCameraParameters cam; #if defined (VISP_HAVE_XML2) - // From the xml file - tracker.loadConfigFile(configFile.c_str()); + // From the xml file + tracker.loadConfigFile(configFile); #else - // By setting the parameters: - cam.initPersProjWithoutDistortion(547, 542, 338, 234); - - vpKltOpencv klt; - klt.setMaxFeatures(10000); - klt.setWindowSize(5); - klt.setQuality(0.01); - klt.setMinDistance(5); - klt.setHarrisFreeParameter(0.01); - klt.setBlockSize(3); - klt.setPyramidLevels(3); - - tracker.setCameraParameters(cam); - tracker.setKltOpencv(klt); - tracker.setAngleAppear( vpMath::rad(65) ); - tracker.setAngleDisappear( vpMath::rad(75) ); - tracker.setMaskBorder(5); - - // Specify the clipping to use - tracker.setNearClippingDistance(0.01); - tracker.setFarClippingDistance(0.90); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); -// tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpKltOpencv klt; + klt.setMaxFeatures(10000); + klt.setWindowSize(5); + klt.setQuality(0.01); + klt.setMinDistance(5); + klt.setHarrisFreeParameter(0.01); + klt.setBlockSize(3); + klt.setPyramidLevels(3); + + tracker.setCameraParameters(cam); + tracker.setKltOpencv(klt); + tracker.setAngleAppear( vpMath::rad(65) ); + tracker.setAngleDisappear( vpMath::rad(75) ); + tracker.setMaskBorder(5); + + // Specify the clipping to use + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING #endif - - // Display the klt points - tracker.setDisplayFeatures(displayKltPoints); - - // Tells if the tracker has to use Ogre3D for visibility tests - tracker.setOgreVisibilityTest(useOgre); - // Retrieve the camera parameters from the tracker - tracker.getCameraParameters(cam); - - // Loop to position the cube - if (opt_display && opt_click_allowed) - { - while(!vpDisplay::getClick(I,false)){ - vpDisplay::display(I); - vpDisplay::displayCharString(I, 15, 10, - "click after positioning the object", - vpColor::red); - vpDisplay::flush(I) ; + // Display the klt points + tracker.setDisplayFeatures(displayKltPoints); + + // Tells if the tracker has to use Ogre3D for visibility tests + tracker.setOgreVisibilityTest(useOgre); + + // Retrieve the camera parameters from the tracker + tracker.getCameraParameters(cam); + + // Loop to position the cube + if (opt_display && opt_click_allowed) + { + while(!vpDisplay::getClick(I,false)){ + vpDisplay::display(I); + vpDisplay::displayText(I, 15, 10, "click after positioning the object", vpColor::red); + vpDisplay::flush(I) ; + } } - } - // Load the 3D model (either a vrml file or a .cao file) - try{ - tracker.loadModel(modelFile.c_str()); - } - catch(...) - { - return 0; - } - // Initialise the tracker by clicking on the image - // This function looks for - // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation - // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) - if (opt_display && opt_click_allowed) - { - tracker.initClick(I, initFile.c_str(), true); + // Load the 3D model (either a vrml file or a .cao file) + tracker.loadModel(modelFile); + + // Initialise the tracker by clicking on the image + // This function looks for + // - a ./cube/cube.init file that defines the 3d coordinates (in meter, in the object basis) of the points used for the initialisation + // - a ./cube/cube.ppm file to display where the user have to click (optionnal, set by the third parameter) + if (opt_display && opt_click_allowed) + { + tracker.initClick(I, initFile, true); + tracker.getPose(cMo); + // display the 3D model at the given pose + tracker.display(I,cMo, cam, vpColor::red); + } + else + { + vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); + tracker.initFromPose(I, cMoi); + } + + //track the model + tracker.track(I); tracker.getPose(cMo); - // display the 3D model at the given pose - tracker.display(I,cMo, cam, vpColor::red); - } - else - { - vpHomogeneousMatrix cMoi(0.02044769891, 0.1101505452, 0.5078963719, 2.063603907, 1.110231561, -0.4392789872); - tracker.initFromPose(I, cMoi); - } - //track the model - tracker.track(I); - tracker.getPose(cMo); - - if (opt_display) - vpDisplay::flush(I); + if (opt_display) + vpDisplay::flush(I); - // Uncomment if you want to compute the covariance matrix. - // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. - - while (!reader.end()) - { - try + // Uncomment if you want to compute the covariance matrix. + // tracker.setCovarianceComputation(true); //Important if you want tracker.getCovarianceMatrix() to work. + + while (!reader.end()) { - // acquire a new image + // acquire a new image reader.acquire(I); // display the image if (opt_display) vpDisplay::display(I); - // track the object - tracker.track(I); - tracker.getPose(cMo); - // display the 3D model - if (opt_display) - { - tracker.display(I, cMo, cam, vpColor::darkRed); - // display the frame - vpDisplay::displayFrame (I, cMo, cam, 0.05, vpColor::blue); + + // Test to reset the tracker + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 10) { + vpTRACE("Test reset tracker"); + if (opt_display) + vpDisplay::display(I); + tracker.resetTracker(); +#if defined (VISP_HAVE_XML2) + tracker.loadConfigFile(configFile); +#else + // By setting the parameters: + cam.initPersProjWithoutDistortion(547, 542, 338, 234); + + vpKltOpencv klt; + klt.setMaxFeatures(10000); + klt.setWindowSize(5); + klt.setQuality(0.01); + klt.setMinDistance(5); + klt.setHarrisFreeParameter(0.01); + klt.setBlockSize(3); + klt.setPyramidLevels(3); + + tracker.setCameraParameters(cam); + tracker.setKltOpencv(klt); + tracker.setAngleAppear( vpMath::rad(65) ); + tracker.setAngleDisappear( vpMath::rad(75) ); + tracker.setMaskBorder(5); + + // Specify the clipping to use + tracker.setNearClippingDistance(0.01); + tracker.setFarClippingDistance(0.90); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + // tracker.setClipping(tracker.getClipping() | vpMbtPolygon::LEFT_CLIPPING | vpMbtPolygon::RIGHT_CLIPPING | vpMbtPolygon::UP_CLIPPING | vpMbtPolygon::DOWN_CLIPPING); // Equivalent to FOV_CLIPPING +#endif + tracker.loadModel(modelFile); + tracker.setCameraParameters(cam); + tracker.setOgreVisibilityTest(useOgre); + tracker.initFromPose(I, cMo); } - + + // Test to set an initial pose + if (reader.getFrameIndex() == reader.getFirstFrameIndex() + 50) { + cMo.buildFrom(0.04371844921, 0.08438820979, 0.5382029442, 2.200417277, 0.873535825, -0.3479076844); + vpTRACE("Test set pose"); + tracker.setPose(I, cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); +// if (opt_click_allowed) { +// vpDisplay::flush(I); +// vpDisplay::getClick(I); +// } + } + } + + // track the object: stop tracking from frame 40 to 50 + if (reader.getFrameIndex() - reader.getFirstFrameIndex() < 40 || reader.getFrameIndex() > reader.getFirstFrameIndex() + 50) { + tracker.track(I); + tracker.getPose(cMo); + if (opt_display) { + // display the 3D model + tracker.display(I, cMo, cam, vpColor::darkRed); + // display the frame + vpDisplay::displayFrame (I, cMo, cam, 0.05); + } + } + + if (opt_click_allowed) { + vpDisplay::displayText(I, 10, 10, "Click to quit", vpColor::red); + if (vpDisplay::getClick(I, false)) { + quit = true; + break; + } + } + // Uncomment if you want to print the covariance matrix. // Make sure tracker.setCovarianceComputation(true) has been called (uncomment below). // std::cout << tracker.getCovarianceMatrix() << std::endl << std::endl; - + + vpDisplay::flush(I); } - catch(...) - { - std::cout << "error caught" << std::endl; - break; + if (opt_click_allowed && !quit) { + vpDisplay::getClick(I); } - vpDisplay::flush(I) ; - } - reader.close(); + + reader.close(); #if defined (VISP_HAVE_XML2) - // Cleanup memory allocated by xml library used to parse the xml config file in vpMbKltTracker::loadConfigFile() - vpXmlParser::cleanup(); + // Cleanup memory allocated by xml library used to parse the xml config file in vpMbKltTracker::loadConfigFile() + vpXmlParser::cleanup(); #endif -#ifdef VISP_HAVE_COIN - // Cleanup memory allocated by Coin library used to load a vrml model in vpMbKltTracker::loadModel() - SoDB::finish(); +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + // Cleanup memory allocated by Coin library used to load a vrml model in vpMbKltTracker::loadModel() + // We clean only if Coin was used. + if(! cao3DModel) + SoDB::finish(); #endif - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/example/tracking/templateTracker.cpp b/example/tracking/templateTracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26f397608650f18ea0a5071ea289212856e443d8 --- /dev/null +++ b/example/tracking/templateTracker.cpp @@ -0,0 +1,428 @@ +/**************************************************************************** + * + * $Id: templateTracker.cpp 5002 2014-11-24 08:18:58Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Example of template tracking. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ + +/*! + \example templateTracker.cpp + + \brief Example of template tracking. +*/ + +#include <visp/vpConfig.h> +#include <visp/vpDebug.h> +#include <visp/vpDisplayD3D.h> +#include <visp/vpDisplayGTK.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpHomogeneousMatrix.h> +#include <visp/vpImageIo.h> +#include <visp/vpIoTools.h> +#include <visp/vpMath.h> +#include <visp/vpVideoReader.h> +#include <visp/vpParseArgv.h> + +#include <visp/vpTemplateTrackerSSD.h> +#include <visp/vpTemplateTrackerSSDForwardAdditional.h> +#include <visp/vpTemplateTrackerSSDForwardCompositional.h> +#include <visp/vpTemplateTrackerSSDInverseCompositional.h> +#include <visp/vpTemplateTrackerSSDESM.h> +#include <visp/vpTemplateTrackerZNCCForwardAdditional.h> +#include <visp/vpTemplateTrackerZNCCInverseCompositional.h> + +#include <visp/vpTemplateTrackerWarpAffine.h> +#include <visp/vpTemplateTrackerWarpHomography.h> +#include <visp/vpTemplateTrackerWarpHomographySL3.h> +#include <visp/vpTemplateTrackerWarpSRT.h> +#include <visp/vpTemplateTrackerWarpTranslation.h> + +#if defined (VISP_HAVE_DISPLAY) + + +#define GETOPTARGS "cdhi:l:pt:w:" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +typedef enum { + WARP_AFFINE, + WARP_HOMOGRAPHY, + WARP_HOMOGRAPHY_SL3, + WARP_SRT, + WARP_TRANSLATION +} WarpType; + +typedef enum { + TRACKER_SSD_ESM, + TRACKER_SSD_FORWARD_ADDITIONAL, + TRACKER_SSD_FORWARD_COMPOSITIONAL, + TRACKER_SSD_INVERSE_COMPOSITIONAL, // The most efficient + TRACKER_ZNCC_FORWARD_ADDITIONEL, + TRACKER_ZNCC_INVERSE_COMPOSITIONAL +} TrackerType; + +#endif + +void usage(const char *name, const char *badparam, const WarpType &warp_type, + TrackerType &tracker_type, const long &last_frame); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, + bool &pyramidal, WarpType &warp_type, TrackerType &tracker_type, long &last_frame); + +void usage(const char *name, const char *badparam, const WarpType &warp_type, + TrackerType &tracker_type, const long &last_frame) +{ + fprintf(stdout, "\n\ +Example of template tracking.\n\ +\n\ +SYNOPSIS\n\ + %s [-i <test image path>] [-c] [-d] [-p] \n\ + [-w <warp type>] [-t <tracker type>] \n\ + [-l <last frame number>] [-h]\n", name ); + + fprintf(stdout, "\n\ +OPTIONS: Default\n\ + -i <input image path> \n\ + Set image input path.\n\ + From this path read images \n\ + \"ViSP-images/cube/image%%04d.pgm\". These \n\ + images come from ViSP-images-x.y.z.tar.gz available \n\ + on the ViSP website.\n\ + Setting the VISP_INPUT_IMAGE_PATH environment\n\ + variable produces the same behaviour than using\n\ + this option.\n\ + \n\ + -l <last frame number> %ld\n\ + Last frame number to consider.\n\ + \n\ + -d \n\ + Turn off the display.\n\ + \n\ + -c\n\ + Disable the mouse click. Useful to automaze the \n\ + execution of this program without humain intervention.\n\ + \n\ + -w <warp type=[0,1,2,3,4]> %d\n\ + Set the model used to warp the template. \n\ + Authorized values are:\n\ + %d : Affine\n\ + %d : Homography\n\ + %d : Homography in SL3\n\ + %d : SRT (scale, rotation, translation)\n\ + %d : Translation\n\ + \n\ + -t <tracker type=[0,1,2,3,4,5]> %d\n\ + Set the tracker used to track the template. \n\ + Authorized values are:\n\ + %d : SSD ESM\n\ + %d : SSD forward additional\n\ + %d : SSD forward compositional\n\ + %d : SSD inverse compositional\n\ + %d : ZNCC forward additional\n\ + %d : ZNCC inverse compositional\n\ + \n\ + -p\n\ + Enable pyramidal tracking.\n\ + \n\ + -h \n\ + Print the help.\n\n", + last_frame, (int)warp_type, + (int)WARP_AFFINE, (int)WARP_HOMOGRAPHY, (int)WARP_HOMOGRAPHY_SL3, (int)WARP_SRT, (int)WARP_TRANSLATION, + (int)tracker_type, + (int)TRACKER_SSD_ESM, (int)TRACKER_SSD_FORWARD_ADDITIONAL, (int)TRACKER_SSD_FORWARD_COMPOSITIONAL, + (int)TRACKER_SSD_INVERSE_COMPOSITIONAL, (int)TRACKER_ZNCC_FORWARD_ADDITIONEL, + (int)TRACKER_ZNCC_INVERSE_COMPOSITIONAL); + if (badparam) + fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam); +} + + +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display, + bool &pyramidal, WarpType &warp_type, TrackerType &tracker_type, long &last_frame) +{ + const char *optarg_; + int c; + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { + + switch (c) { + case 'c': click_allowed = false; break; + case 'd': display = false; break; + case 'h': usage(argv[0], NULL, warp_type, tracker_type, last_frame); return false; break; + case 'i': ipath = optarg_; break; + case 'l': last_frame = (long)atoi(optarg_); break; + case 'p': pyramidal = true; break; + case 't': tracker_type = (TrackerType)atoi(optarg_); break; + case 'w': warp_type = (WarpType)atoi(optarg_); break; + + default: + usage(argv[0], optarg_, warp_type, tracker_type, last_frame); + return false; break; + } + } + + if (warp_type > WARP_TRANSLATION) { + usage(argv[0], NULL, warp_type, tracker_type, last_frame); + std::cerr << "ERROR: " << std::endl; + std::cerr << " Bad argument -w <warp type> with \"warp type\"=" << (int)warp_type << std::endl << std::endl; + return false; + } + if (tracker_type > TRACKER_ZNCC_INVERSE_COMPOSITIONAL) { + usage(argv[0], NULL, warp_type, tracker_type, last_frame); + std::cerr << "ERROR: " << std::endl; + std::cerr << " Bad argument -t <tracker type> with \"tracker type\"=" << (int)tracker_type << std::endl << std::endl; + return false; + } + if ((c == 1) || (c == -1)) { + // standalone param or error + usage(argv[0], NULL, warp_type, tracker_type, last_frame); + std::cerr << "ERROR: " << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; + return false; + } + + return true; +} + +int +main(int argc, const char ** argv) +{ + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + bool opt_click_allowed = true; + bool opt_display = true; + bool opt_pyramidal = false; + TrackerType opt_tracker_type = TRACKER_SSD_INVERSE_COMPOSITIONAL; + WarpType opt_warp_type = WARP_AFFINE; + long opt_last_frame = 30; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (!getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display, opt_pyramidal, + opt_warp_type, opt_tracker_type, opt_last_frame)) { + return (-1); + } + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() ){ + usage(argv[0], NULL, opt_warp_type, opt_tracker_type, opt_last_frame); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << std::endl; + + return (-1); + } + + // Get the option values + if (!opt_ipath.empty()) + ipath = vpIoTools::createFilePath(opt_ipath, "ViSP-images/mire-2/image.%04d.pgm"); + else + ipath = vpIoTools::createFilePath(env_ipath, "ViSP-images/mire-2/image.%04d.pgm"); + + vpImage<unsigned char> I; + vpVideoReader reader; + + reader.setFileName(ipath.c_str()); + reader.setFirstFrameIndex(1); + reader.setLastFrameIndex(opt_last_frame); + try{ + reader.open(I); + }catch(...){ + std::cout << "Cannot open sequence: " << ipath << std::endl; + return -1; + } + reader.acquire(I); + + vpDisplay *display = NULL; + if (opt_display) + { + // initialise a display +#if defined VISP_HAVE_X11 + display = new vpDisplayX; +#elif defined VISP_HAVE_GDI + display = new vpDisplayGDI; +#elif defined VISP_HAVE_OPENCV + display = new vpDisplayOpenCV; +#elif defined VISP_HAVE_D3D9 + display = new vpDisplayD3D; +#elif defined VISP_HAVE_GTK + display = new vpDisplayGTK; +#else + opt_display = false; +#endif +#if (defined VISP_HAVE_DISPLAY) + display->init(I, 100, 100, "Test tracking") ; +#endif + vpDisplay::display(I) ; + vpDisplay::flush(I); + } + + vpTemplateTrackerWarp *warp = NULL; + switch(opt_warp_type) { + case WARP_AFFINE: warp = new vpTemplateTrackerWarpAffine; break; + case WARP_HOMOGRAPHY: warp = new vpTemplateTrackerWarpHomography; break; + case WARP_HOMOGRAPHY_SL3: warp = new vpTemplateTrackerWarpHomographySL3; break; + case WARP_SRT: warp = new vpTemplateTrackerWarpSRT; break; + case WARP_TRANSLATION: warp = new vpTemplateTrackerWarpTranslation; break; + } + + vpTemplateTracker *tracker = NULL; + switch(opt_tracker_type) { + case TRACKER_SSD_ESM: tracker = new vpTemplateTrackerSSDESM(warp); break; + case TRACKER_SSD_FORWARD_ADDITIONAL: tracker = new vpTemplateTrackerSSDForwardAdditional(warp); break; + case TRACKER_SSD_FORWARD_COMPOSITIONAL: tracker = new vpTemplateTrackerSSDForwardCompositional(warp); break; + case TRACKER_SSD_INVERSE_COMPOSITIONAL: tracker = new vpTemplateTrackerSSDInverseCompositional(warp); break; + case TRACKER_ZNCC_FORWARD_ADDITIONEL: tracker = new vpTemplateTrackerZNCCForwardAdditional(warp); break; + case TRACKER_ZNCC_INVERSE_COMPOSITIONAL: tracker = new vpTemplateTrackerZNCCInverseCompositional(warp); break; + } + + tracker->setSampling(2,2); + tracker->setLambda(0.001); + tracker->setThresholdGradient(60.); + tracker->setIterationMax(800); + if (opt_pyramidal) { + tracker->setPyramidal(2, 1); + } + bool delaunay = false; + if (opt_display && opt_click_allowed) + tracker->initClick(I, delaunay); + else { + std::vector<vpImagePoint> v_ip; + vpImagePoint ip; + ip.set_ij(166, 54); v_ip.push_back(ip); + ip.set_ij(284, 55); v_ip.push_back(ip); + ip.set_ij(259, 284); v_ip.push_back(ip); // ends the first triangle + ip.set_ij(259, 284); v_ip.push_back(ip); // start the second triangle + ip.set_ij(149, 240); v_ip.push_back(ip); + ip.set_ij(167, 58); v_ip.push_back(ip); + + tracker->initFromPoints(I, v_ip, false); + } + + while (! reader.end()) + { + std::cout << "Process image number " << reader.getFrameIndex() << std::endl; + // Acquire a new image + reader.acquire(I); + // Display the image + vpDisplay::display(I); + // Track the template + tracker->track(I); + + // Simulate a re-init + if (reader.getFrameIndex() == 10){ + std::cout << "re-init simulation" << std::endl; + if (opt_click_allowed) + vpDisplay::getClick(I); + + tracker->resetTracker(); + + if (opt_display && opt_click_allowed) { + vpDisplay::displayText(I, 10, 10, "Re-init simulation", vpColor::red); + vpDisplay::flush(I); + tracker->initClick(I, delaunay); + } + else { + std::vector<vpImagePoint> v_ip; + vpImagePoint ip; + ip.set_ij(146, 60); v_ip.push_back(ip); + ip.set_ij(254, 74); v_ip.push_back(ip); + ip.set_ij(228, 288); v_ip.push_back(ip); // ends the first triangle + ip.set_ij(228, 288); v_ip.push_back(ip); // start the second triangle + ip.set_ij(126, 242); v_ip.push_back(ip); + ip.set_ij(146, 60); v_ip.push_back(ip); + + tracker->initFromPoints(I, v_ip, false); + } + } + + // Display the template +#if 1 + tracker->display(I, vpColor::red, 3); +#else + vpTemplateTrackerZone zoneWarped_, zoneRef_ = tracker->getZoneRef(); + vpTemplateTrackerWarp *warp_ = tracker->getWarp(); + vpColVector p_= tracker->getp(); + warp_->warpZone(zoneRef_, p_, zoneWarped_); + zoneWarped_.display(I, vpColor::red, 3); + zoneRef_.display(I, vpColor::green, 3); +#endif + + vpDisplay::flush(I) ; + } + if (opt_click_allowed) { + vpDisplay::displayText(I, 10,10, "A click to exit...", vpColor::red); + vpDisplay::flush(I) ; + vpDisplay::getClick(I) ; + } + reader.close(); + if (display) + delete display; + + delete warp; + delete tracker; + + return 0; + } + catch(vpException e) + { + std::cout << "Catch an exception: " << e << std::endl; + return -1; + } +} + +#else + +int main() +{ + std::cout << "No display is available." << std::endl; + return 0; +} + +#endif diff --git a/example/tracking/trackDot.cpp b/example/tracking/trackDot.cpp index 2c58fb70db825362be9fdb701f258cb6ee42c5c2..7d895e6f80f64e1d74edf266e0a3c13b009da1b5 100644 --- a/example/tracking/trackDot.cpp +++ b/example/tracking/trackDot.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackDot.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackDot.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,7 +61,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -69,6 +69,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDot.h> #include <visp/vpParseArgv.h> #include <visp/vpIoTools.h> @@ -76,6 +77,11 @@ // List of allowed command line options #define GETOPTARGS "cdf:i:n:p:s:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display); + /*! Print the program options. @@ -90,7 +96,7 @@ Print the program options. */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, - unsigned first, unsigned nimages, unsigned step) + unsigned first, unsigned nimages, unsigned step) { fprintf(stdout, "\n\ Test dot tracking.\n\ @@ -162,27 +168,26 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, - unsigned &first, unsigned &nimages, unsigned &step, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step); return false; break; } } @@ -191,7 +196,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -202,141 +207,141 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 1; - unsigned opt_nimages = 500; - unsigned opt_step = 1; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step, opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 1; + unsigned opt_nimages = 500; + unsigned opt_step = 1; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, + opt_step, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + exit(-1); + } - unsigned iter = opt_first; - std::ostringstream s; - char cfilename[FILENAME_MAX]; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - if (opt_ppath.empty()){ + unsigned iter = opt_first; + std::ostringstream s; + char cfilename[FILENAME_MAX]; + if (opt_ppath.empty()){ - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file - // ViSP-images/mire-2/image.0001.pgm - // !! vpDotExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images.tar.gz" - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file + // ViSP-images/mire-2/image.0001.pgm + // !! vpDotExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images.tar.gz" - // Build the name of the image file + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + // Build the name of the image file - sprintf(cfilename,opt_ppath.c_str(), iter) ; - filename = cfilename; - } + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; + sprintf(cfilename,opt_ppath.c_str(), iter) ; + filename = cfilename; + } - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; - // We open a window using either X11, GTK or GDI. + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } + + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -347,30 +352,23 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - // by using setGraphics, we request to see the all the pixel of the dot - vpDot d ; - if (opt_display) { // by using setGraphics, we request to see the all the pixel of the dot - // in green on the screen. - // It uses the overlay image plane. - // The default of this setting is that it is time consumming - d.setGraphics(true) ; - } - else { - d.setGraphics(false) ; - } - // we also request to compute the dot moment m00, m10, m01, m11, m20, m02 - d.setComputeMoments(true); - d.setConnexity(vpDot::CONNEXITY_8); + vpDot d ; + if (opt_display) { + // by using setGraphics, we request to see the all the pixel of the dot + // in green on the screen. + // It uses the overlay image plane. + // The default of this setting is that it is time consumming + d.setGraphics(true) ; + } + else { + d.setGraphics(false) ; + } + // we also request to compute the dot moment m00, m10, m01, m11, m20, m02 + d.setComputeMoments(true); + d.setConnexity(vpDot::CONNEXITY_8); - try { if (opt_display && opt_click_allowed) { // tracking is initalized // if no other parameters are given to the iniTracking(..) method @@ -384,24 +382,17 @@ main(int argc, const char ** argv) // image point from which the dot is searched vpImagePoint ip; ip.set_u( 160 ); - ip.set_v( 212 ); + ip.set_v( 212 ); d.initTracking(I, ip) ; } - } - catch(...) - { - vpERROR_TRACE("Cannot initialise the tracking... ") ; - exit(-1); - } - try { vpImagePoint cog; while (iter < opt_first + opt_nimages*opt_step) { // set the new image name if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf(cfilename, opt_ppath.c_str(), iter) ; @@ -458,24 +449,25 @@ main(int argc, const char ** argv) } iter ++; } - } - catch (...) { - std::cerr << "Error during the tracking..." << std::endl; - std::cerr << "The progam was stopped." << std::endl; - exit(-1); - } - if (opt_display && opt_click_allowed) { - std::cout << "\nA click to exit..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; + + if (opt_display && opt_click_allowed) { + std::cout << "\nA click to exit..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackDot2.cpp b/example/tracking/trackDot2.cpp index 3836da96c424b6acbb5e14b1528070584b71cd80..a659837d7f63d68a8a7d345e90fe2dddfaf88f3d 100644 --- a/example/tracking/trackDot2.cpp +++ b/example/tracking/trackDot2.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackDot2.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackDot2.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,7 +53,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -61,6 +61,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDot2.h> #include <visp/vpParseArgv.h> #include <visp/vpIoTools.h> @@ -70,6 +71,10 @@ //int gsl_warnings_off; +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display); /*! \example trackDot2.cpp Example of dot tracking on an image sequence using vpDot2. @@ -90,7 +95,7 @@ Print the program options. */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, - unsigned first, unsigned nimages, unsigned step) + unsigned first, unsigned nimages, unsigned step) { fprintf(stdout, "\n\ Test dot tracking using vpDot2 class.\n\ @@ -162,27 +167,26 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, - unsigned &first, unsigned &nimages, unsigned &step, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step); return false; break; } } @@ -191,7 +195,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -201,142 +205,141 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 1; - unsigned opt_nimages = 500; - unsigned opt_step = 1; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step, opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 1; + unsigned opt_nimages = 500; + unsigned opt_step = 1; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, + opt_step, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + exit(-1); + } - unsigned iter = opt_first; - std::ostringstream s; - char cfilename[FILENAME_MAX]; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - if (opt_ppath.empty()){ + unsigned iter = opt_first; + std::ostringstream s; + char cfilename[FILENAME_MAX]; + if (opt_ppath.empty()){ - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file - // ViSP-images/mire-2/image.0001.pgm - // !! vpDotExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images.tar.gz" - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file + // ViSP-images/mire-2/image.0001.pgm + // !! vpDotExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images.tar.gz" - // Build the name of the image file + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + // Build the name of the image file - sprintf(cfilename,opt_ppath.c_str(), iter) ; - filename = cfilename; - } + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; + sprintf(cfilename,opt_ppath.c_str(), iter) ; + filename = cfilename; + } - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; + + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -347,52 +350,40 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error + // define the vpDot structure. - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - // define the vpDot structure. + // vpDot and vpDot2 correspond to two different algorithms designed to track + // a dot. vpDot is based on recurse connex componants (all the pixels of the + // dot are parsed), while vpDot2 is based on freeman chain code (only the + // contour of the dot is parsed) - // vpDot and vpDot2 correspond to two different algorithms designed to track - // a dot. vpDot is based on recurse connex componants (all the pixels of the - // dot are parsed), while vpDot2 is based on freeman chain code (only the - // contour of the dot is parsed) + vpDot2 d ; + vpImagePoint cog; - vpDot2 d ; - vpImagePoint cog; + if (opt_display) { + // by using setGraphics, we request to see the all the pixel of the dot + // in green on the screen. + // It uses the overlay image plane. + // The default of this setting is that it is time consumming - if (opt_display) { - // by using setGraphics, we request to see the all the pixel of the dot - // in green on the screen. - // It uses the overlay image plane. - // The default of this setting is that it is time consumming + d.setGraphics(true) ; + } + else { - d.setGraphics(true) ; - } - else { + d.setGraphics(false) ; + } + // We want to track an ellipsoid shape. If you want to track a non ellipsoid + // object, use d.setEllipsoidShape(0); + // we also request to compute the dot moment m00, m10, m01, m11, m20, m02 + d.setComputeMoments(true); + d.setGrayLevelPrecision(0.90); + + // tracking is initalized if no other parameters are given to the + // iniTracking(..) method a right mouse click on the dot is expected + // dot location can also be specified explicitely in the + // initTracking method : d.initTracking(I,ip) where ip is the image + // point from which the dot is searched - d.setGraphics(false) ; - } - // We want to track an ellipsoid shape. If you want to track a non ellipsoid - // object, use d.setEllipsoidShape(0); - // we also request to compute the dot moment m00, m10, m01, m11, m20, m02 - d.setComputeMoments(true); - d.setGrayLevelPrecision(0.90); - - // tracking is initalized if no other parameters are given to the - // iniTracking(..) method a right mouse click on the dot is expected - // dot location can also be specified explicitely in the - // initTracking method : d.initTracking(I,ip) where ip is the image - // point from which the dot is searched - - try{ if (opt_display && opt_click_allowed) { std::cout << "Click on a dot to track it."<< std::endl; d.initTracking(I) ; @@ -411,7 +402,7 @@ main(int argc, const char ** argv) << d.m10 / d.m00 << " " << d.m01 / d.m00 << std::endl; std::cout << "Size:" << std::endl; std::cout << " w: " << d.getWidth() << " h: " << d.getHeight() << std::endl; - std::cout << "Surface: " << d.getSurface() << std::endl; + std::cout << "Area: " << d.getArea() << std::endl; std::cout << "Moments:" << std::endl; std::cout << " m00: " << d.m00 << std::endl; std::cout << " m10: " << d.m10 << std::endl; @@ -429,20 +420,13 @@ main(int argc, const char ** argv) std::cout << " size precision: " << d.getSizePrecision() << std::endl; std::cout << " gray level precision: " << d.getGrayLevelPrecision() << std::endl; } - } - catch(...) - { - vpERROR_TRACE("Cannot initialise the tracking... ") ; - exit(-1); - } - try { while (iter < opt_first + opt_nimages*opt_step) { // set the new image name if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf(cfilename, opt_ppath.c_str(), iter) ; @@ -475,7 +459,7 @@ main(int argc, const char ** argv) << d.m10 / d.m00 << " " << d.m01 / d.m00 << std::endl; std::cout << "Size:" << std::endl; std::cout << " w: " << d.getWidth() << " h: " << d.getHeight() << std::endl; - std::cout << "Surface: " << d.getSurface() << std::endl; + std::cout << "Area: " << d.getArea() << std::endl; std::cout << "Moments:" << std::endl; std::cout << " m00: " << d.m00 << std::endl; std::cout << " m10: " << d.m10 << std::endl; @@ -513,23 +497,24 @@ main(int argc, const char ** argv) } iter ++; } + + if (opt_display && opt_click_allowed) { + std::cout << "\nA click to exit..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + return 0; } - catch (...) { - std::cerr << "Error during the tracking..." << std::endl; - std::cerr << "The progam was stopped." << std::endl; - exit(-1); - } - if (opt_display && opt_click_allowed) { - std::cout << "\nA click to exit..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackDot2WithAutoDetection.cpp b/example/tracking/trackDot2WithAutoDetection.cpp index bd497b2155255addcb922d4c0cb330b63bc12eb6..b05db3d0575d318310c3e5a12de228ba750c2a20 100644 --- a/example/tracking/trackDot2WithAutoDetection.cpp +++ b/example/tracking/trackDot2WithAutoDetection.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackDot2WithAutoDetection.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackDot2WithAutoDetection.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -66,7 +66,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> -#include <visp/vpList.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDot2.h> #include <visp/vpParseArgv.h> #include <visp/vpIoTools.h> @@ -74,6 +74,14 @@ // List of allowed command line options #define GETOPTARGS "cdi:p:f:n:s:S:G:E:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step, double sizePrecision, + double grayLevelPrecision, double ellipsoidShapePrecision ); +bool getOptions(int argc, const char **argv, std::string &ipath, + std::string &ppath,unsigned &first, unsigned &nimages, + unsigned &step, double &sizePrecision, double &grayLevelPrecision, + double &ellipsoidShapePrecision, bool &click_allowed, bool &display); + /*! Print the program options. @@ -93,7 +101,7 @@ */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, unsigned first, unsigned nimages, unsigned step, double sizePrecision, - double grayLevelPrecision, double ellipsoidShapePrecision ) + double grayLevelPrecision, double ellipsoidShapePrecision) { fprintf(stdout, "\n\ Test auto detection of dots using vpDot2.\n\ @@ -196,27 +204,27 @@ bool getOptions(int argc, const char **argv, std::string &ipath, unsigned &step, double &sizePrecision, double &grayLevelPrecision, double &ellipsoidShapePrecision, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; - case 'S': sizePrecision = atof(optarg);break; - case 'G': grayLevelPrecision = atof(optarg);break; - case 'E': ellipsoidShapePrecision = atof(optarg);break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; + case 'S': sizePrecision = atof(optarg_);break; + case 'G': grayLevelPrecision = atof(optarg_);break; + case 'E': ellipsoidShapePrecision = atof(optarg_);break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step, sizePrecision,grayLevelPrecision,ellipsoidShapePrecision); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step, + usage(argv[0], optarg_, ipath, ppath, first, nimages, step, sizePrecision,grayLevelPrecision,ellipsoidShapePrecision); return false; break; } @@ -227,7 +235,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, usage(argv[0], NULL, ipath, ppath, first, nimages, step, sizePrecision,grayLevelPrecision,ellipsoidShapePrecision); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -238,146 +246,145 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 1; - unsigned opt_nimages = 10; - unsigned opt_step = 1; - double opt_sizePrecision = 0.65; - double opt_grayLevelPrecision = 0.85; - double opt_ellipsoidShapePrecision = 0.8; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step,opt_sizePrecision,opt_grayLevelPrecision, - opt_ellipsoidShapePrecision, opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 1; + unsigned opt_nimages = 10; + unsigned opt_step = 1; + double opt_sizePrecision = 0.65; + double opt_grayLevelPrecision = 0.85; + double opt_ellipsoidShapePrecision = 0.8; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, + opt_step,opt_sizePrecision,opt_grayLevelPrecision, + opt_ellipsoidShapePrecision, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step,opt_sizePrecision,opt_grayLevelPrecision, opt_ellipsoidShapePrecision); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl; - exit(-1); - } + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; - std::ostringstream s; - char cfilename[FILENAME_MAX]; - unsigned iter = opt_first; // Image number + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; + std::ostringstream s; + char cfilename[FILENAME_MAX]; + unsigned iter = opt_first; // Image number - if (opt_ppath.empty()){ + if (opt_ppath.empty()){ - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file - // ViSP-images/cube/image.0001.pgm - // !! vpDotExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images.tar.gz" + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file + // ViSP-images/cube/image.0001.pgm + // !! vpDotExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images.tar.gz" - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - // Build the name of the image file + // Build the name of the image file - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { - sprintf(cfilename,opt_ppath.c_str(), iter) ; - filename = cfilename; - } - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or your -p " << opt_ppath << " option " <<std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + sprintf(cfilename,opt_ppath.c_str(), iter) ; + filename = cfilename; + } + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or your -p " << opt_ppath << " option " <<std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either GTK, X11 or GDI. + + // We open a window using either GTK, X11 or GDI. #if defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -388,19 +395,12 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - // Dot declaration - vpDot2 d ; + // Dot declaration + vpDot2 d ; - d.setGraphics(true); - if (opt_click_allowed & opt_display) { - try{ + d.setGraphics(true); + if (opt_click_allowed & opt_display) { d.setGrayLevelPrecision(opt_grayLevelPrecision); std::cout << "Please click on a dot to initialize detection" @@ -418,103 +418,102 @@ main(int argc, const char ** argv) printf("Dot characteristics: \n"); printf(" width : %lf\n", d.getWidth()); printf(" height: %lf\n", d.getHeight()); - printf(" surface: %lf\n", d.getSurface()); + printf(" area: %lf\n", d.getArea()); printf(" gray level min: %d\n", d.getGrayLevelMin()); printf(" gray level max: %d\n", d.getGrayLevelMax()); printf(" grayLevelPrecision: %lf\n", d.getGrayLevelPrecision()); printf(" sizePrecision: %lf\n", d.getSizePrecision()); printf(" ellipsoidShapePrecision: %lf\n", d.getEllipsoidShapePrecision()); } - catch(...) - { - std::cerr << "Cannot initialize the tracking..."<< std::endl; - exit(-1); - } - } - else{ - // Set dot characteristics for the auto detection - d.setGraphics(true); - d.setWidth(15.0); - d.setHeight(12.0); - d.setSurface(124); - d.setGrayLevelMin(164); - d.setGrayLevelMax(255); - d.setGrayLevelPrecision(opt_grayLevelPrecision); - d.setSizePrecision(opt_sizePrecision); - d.setEllipsoidShapePrecision(opt_ellipsoidShapePrecision); - } - - while (iter < opt_first + opt_nimages*opt_step) - { - - // set the new image name - - if (opt_ppath.empty()){ - - s.str(""); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { - sprintf(cfilename, opt_ppath.c_str(), iter) ; - filename = cfilename; + else{ + // Set dot characteristics for the auto detection + d.setGraphics(true); + d.setWidth(15.0); + d.setHeight(12.0); + d.setArea(124); + d.setGrayLevelMin(164); + d.setGrayLevelMax(255); + d.setGrayLevelPrecision(opt_grayLevelPrecision); + d.setSizePrecision(opt_sizePrecision); + d.setEllipsoidShapePrecision(opt_ellipsoidShapePrecision); } - // read the image - vpImageIo::read(I, filename); - if (opt_display) { - // Display the image - vpDisplay::display(I) ; - } + while (iter < opt_first + opt_nimages*opt_step) + { + // set the new image name - std::cout << "Search dots in image" << filename << std::endl; - std::list<vpDot2> list_d; - d.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), list_d) ; + if (opt_ppath.empty()){ - if( list_d.empty() ) { - std::cout << "Dot auto detection did not work." << std::endl; - return(-1) ; - } - else { - std::cout << std::endl << list_d.size() << " dots are detected" << std::endl; + s.str(""); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { + sprintf(cfilename, opt_ppath.c_str(), iter) ; + filename = cfilename; + } + // read the image + vpImageIo::read(I, filename); if (opt_display) { - int i=0; - // Parse all founded dots for display - for (std::list<vpDot2>::const_iterator it = list_d.begin(); it != list_d.end(); ++ it) - { - vpImagePoint cog = (*it).getCog(); + // Display the image + vpDisplay::display(I) ; + } - std::cout << "Dot " << i++ << " : " << cog.get_u() - << " " << cog.get_v() << std::endl; + std::cout << "Search dots in image" << filename << std::endl; + std::list<vpDot2> list_d; + d.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), list_d) ; - vpDisplay::displayCross(I, cog, 16, vpColor::blue, 3) ; + if( list_d.empty() ) { + std::cout << "Dot auto detection did not work." << std::endl; + return(-1) ; + } + else { + std::cout << std::endl << list_d.size() << " dots are detected" << std::endl; + + if (opt_display) { + int i=0; + // Parse all founded dots for display + for (std::list<vpDot2>::const_iterator it = list_d.begin(); it != list_d.end(); ++ it) + { + vpImagePoint cog = (*it).getCog(); + + std::cout << "Dot " << i++ << " : " << cog.get_u() + << " " << cog.get_v() << std::endl; + + vpDisplay::displayCross(I, cog, 16, vpColor::blue, 3) ; + } + vpDisplay::flush(I); } - vpDisplay::flush(I); } - } - // If click is allowed, wait for a mouse click to launch the next iteration + // If click is allowed, wait for a mouse click to launch the next iteration + if (opt_display && opt_click_allowed) { + std::cout << "\nA click to continue..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(I) ; + } + + iter += opt_step ; + } if (opt_display && opt_click_allowed) { - std::cout << "\nA click to continue..." << std::endl; + std::cout << "\nA click to exit..." << std::endl; // Wait for a blocking mouse click vpDisplay::getClick(I) ; } - iter += opt_step ; + return 0; } - if (opt_display && opt_click_allowed) { - std::cout << "\nA click to exit..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - vpTRACE("End..."); } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackKltOpencv.cpp b/example/tracking/trackKltOpencv.cpp index 0400c59706a815d0a0bee4c09410613702764a2b..5b2dd8004abbb034617e96bcdba4ac0ea0a961eb 100644 --- a/example/tracking/trackKltOpencv.cpp +++ b/example/tracking/trackKltOpencv.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackKltOpencv.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackKltOpencv.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,9 +53,9 @@ #include <iomanip> #include <vector> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined (VISP_HAVE_OPENCV)) -#if (defined (VISP_HAVE_OPENCV)) +#if defined (VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) #include <visp/vpKltOpencv.h> #include <visp/vpImage.h> @@ -63,12 +63,17 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpParseArgv.h> #include <visp/vpIoTools.h> // List of allowed command line options #define GETOPTARGS "cdf:i:n:p:s:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display); /*! \example trackKltOpencv.cpp @@ -91,7 +96,7 @@ Print the program options. */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, - unsigned first, unsigned nimages, unsigned step) + unsigned first, unsigned nimages, unsigned step) { fprintf(stdout, "\n\ Example of KLT tracking using OpenCV library.\n\ @@ -163,27 +168,26 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, - unsigned &first, unsigned &nimages, unsigned &step, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, + unsigned &nimages, unsigned &step, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step); return false; break; } } @@ -192,7 +196,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -202,149 +206,147 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned opt_first = 1; - unsigned opt_nimages = 500; - unsigned opt_step = 1; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step, opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string dirname; + std::string filename; + unsigned opt_first = 1; + unsigned opt_nimages = 500; + unsigned opt_step = 1; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, + opt_step, opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ - usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; - - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ + usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> vpI ; // This is a ViSP image used for display only - IplImage * cvI; // This is an OpenCV IPL image used by the tracker + exit(-1); + } - unsigned iter = opt_first; - std::ostringstream s; - char cfilename[FILENAME_MAX]; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> vpI ; // This is a ViSP image used for display only +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + IplImage * cvI = NULL; // This is an OpenCV IPL image used by the tracker +#else + cv::Mat cvI; +#endif - if (opt_ppath.empty()){ + unsigned iter = opt_first; + std::ostringstream s; + char cfilename[FILENAME_MAX]; + if (opt_ppath.empty()){ - // Warning : - // the image sequence is not provided with the ViSP package - // therefore the program will return you an error : - // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file - // ViSP-images/mire-2/image.0001.pgm - // !! vpDotExample.cpp: main(#95) :Error while reading the image - // terminate called after throwing an instance of 'vpImageException' - // - // The sequence is available on the visp www site - // http://www.irisa.fr/lagadic/visp/visp.html - // in the download section. It is named "ViSP-images.tar.gz" - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/mire-2/"); + // Warning : + // the image sequence is not provided with the ViSP package + // therefore the program will return you an error : + // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file + // ViSP-images/mire-2/image.0001.pgm + // !! vpDotExample.cpp: main(#95) :Error while reading the image + // terminate called after throwing an instance of 'vpImageException' + // + // The sequence is available on the visp www site + // http://www.irisa.fr/lagadic/visp/visp.html + // in the download section. It is named "ViSP-images.tar.gz" - // Build the name of the image file + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2"); - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - } - else { + // Build the name of the image file - sprintf(cfilename,opt_ppath.c_str(), iter) ; - filename = cfilename; - } + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + } + else { + sprintf(cfilename, opt_ppath.c_str(), iter) ; + filename = cfilename; + } + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + std::cout << "Load: " << filename << std::endl; - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - - // Load a ViSP image used for the display - vpImageIo::read(vpI, filename) ; - // Load an OpenCV IPL image used by the tracker - if((cvI = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE))== NULL) { - printf("Cannot read image: %s\n", filename.c_str()); - return (0); + // Load a ViSP image used for the display + vpImageIo::read(vpI, filename) ; + vpImageConvert::convert(vpI, cvI); + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); } - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(vpI, 100, 100,"Display...") ; // Display the image @@ -355,70 +357,50 @@ main(int argc, const char ** argv) vpDisplay::display(vpI) ; vpDisplay::flush(vpI) ; } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + // KLT tracker + vpKltOpencv tracker; + + // Event manager + //tracker.setOnNewFeature(&newFeature); + //tracker.setOnFeatureLost(&lostFeature); + //tracker.setIsFeatureValid(&isValid); + + // Tracker parameters + tracker.setTrackerId(1); + //tracker.setOnMeasureFeature(&modifyFeature); + tracker.setMaxFeatures(200); + tracker.setWindowSize(10); + tracker.setQuality(0.01); + tracker.setMinDistance(15); + tracker.setHarrisFreeParameter(0.04); + tracker.setBlockSize(9); + tracker.setUseHarris(1); + tracker.setPyramidLevels(3); + + // Point detection using Harris. In input we have an OpenCV IPL image + tracker.initTracking(cvI); + + if (opt_display) { + // Plot the Harris points on ViSP image + tracker.display(vpI, vpColor::red); } - } - - // KLT tracker - vpKltOpencv tracker; - - // Event manager - //tracker.setOnNewFeature(&newFeature); - //tracker.setOnFeatureLost(&lostFeature); - //tracker.setIsFeatureValid(&isValid); - - // Tracker parameters - tracker.setTrackerId(1); - //tracker.setOnMeasureFeature(&modifyFeature); - tracker.setMaxFeatures(200); - tracker.setWindowSize(10); - tracker.setQuality(0.01); - tracker.setMinDistance(15); - tracker.setHarrisFreeParameter(0.04); - tracker.setBlockSize(9); - tracker.setUseHarris(1); - tracker.setPyramidLevels(3); - - // Point detection using Harris. In input we have an OpenCV IPL image - tracker.initTracking(cvI); - - if (opt_display) { - // Plot the Harris points on ViSP image - tracker.display(vpI, vpColor::red); - } - // tracking is now initialized. We can start the tracker. - - try { + // tracking is now initialized. We can start the tracker. while (iter < opt_first + opt_nimages*opt_step) { // set the new image name if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf(cfilename, opt_ppath.c_str(), iter) ; filename = cfilename; } // read the image - std::cout << "read : " << filename << std::endl; - // Load a ViSP image used for the display vpImageIo::read(vpI, filename) ; - // Load an OpenCV IPL image used by the tracker - if((cvI = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE)) - == NULL) { - printf("Cannot read image: %s\n", filename.c_str()); - return (0); - } + vpImageConvert::convert(vpI, cvI); // track the dot and returns its coordinates in the image // results are given in float since many many are usually considered @@ -436,7 +418,7 @@ main(int argc, const char ** argv) // Tracking of the detected points tracker.track(cvI); std::cout << "Tracking performed in " << - vpTime::measureTimeMs() - time << " ms): " << std::endl; + vpTime::measureTimeMs() - time << " ms" << std::endl; if (opt_display) { // Display the tracked points @@ -446,16 +428,16 @@ main(int argc, const char ** argv) } iter += opt_step; } + if (opt_display && opt_click_allowed) { + std::cout << "\nA click to exit..." << std::endl; + // Wait for a blocking mouse click + vpDisplay::getClick(vpI) ; + } + return 0; } - catch (...) { - std::cerr << "Error during the tracking..." << std::endl; - std::cerr << "The progam was stopped." << std::endl; - exit(-1); - } - if (opt_display && opt_click_allowed) { - std::cout << "\nA click to exit..." << std::endl; - // Wait for a blocking mouse click - vpDisplay::getClick(vpI) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else @@ -469,7 +451,7 @@ main() int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackMeCircle.cpp b/example/tracking/trackMeCircle.cpp index 09d182e92e6d08679f9a9c899451db888bc48a17..43bc4e97b3949f7d81a814a6b87d0055cee751bd 100644 --- a/example/tracking/trackMeCircle.cpp +++ b/example/tracking/trackMeCircle.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackMeCircle.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackMeCircle.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,13 +59,14 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpColor.h> #include <visp/vpMeEllipse.h> @@ -75,6 +76,9 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display); + /*! Print the program options. @@ -129,21 +133,20 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -152,7 +155,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -163,108 +166,107 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/circle/"); + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/circle"); - // Build the name of the image file - filename = dirname + "circle.pgm"; + // Build the name of the image file + filename = vpIoTools::createFilePath(dirname, "circle.pgm"); - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -275,67 +277,65 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - - vpMeEllipse E1 ; - - vpMe me ; - me.setRange(20) ; - me.setSampleStep(2) ; - me.setPointsToTrack(60) ; - me.setThreshold(15000) ; + vpMeEllipse E1 ; + + vpMe me ; + me.setRange(20) ; + me.setSampleStep(2) ; + me.setPointsToTrack(60) ; + me.setThreshold(15000) ; + + E1.setCircle(true) ; + E1.setMe(&me) ; + E1.setDisplay(vpMeSite::RANGE_RESULT) ; + // If click is allowed, wait for a mouse click to select the points + // on the ellipse + if (opt_display && opt_click_allowed) { + E1.initTracking(I) ; + } + else { + // Create a list of points to automate the test + unsigned int n=5 ; + vpImagePoint *ip = new vpImagePoint [n]; + ip[0].set_i( 39 ); ip[0].set_j( 136 ); + ip[1].set_i( 42 ); ip[1].set_j( 83 ); + ip[2].set_i( 86 ); ip[2].set_j( 55 ); + ip[3].set_i( 132 ); ip[3].set_j( 72 ); + ip[4].set_i( 145 ); ip[4].set_j( 134 ); + E1.initTracking(I, n, ip) ; + delete [] ip ; + } - E1.setCircle(true) ; - E1.setMe(&me) ; - E1.setDisplay(vpMeSite::RANGE_RESULT) ; - // If click is allowed, wait for a mouse click to select the points - // on the ellipse - if (opt_display && opt_click_allowed) { - E1.initTracking(I) ; - } - else { - // Create a list of points to automate the test - unsigned int n=5 ; - vpImagePoint *ip = new vpImagePoint [n]; - ip[0].set_i( 39 ); ip[0].set_j( 136 ); - ip[1].set_i( 42 ); ip[1].set_j( 83 ); - ip[2].set_i( 86 ); ip[2].set_j( 55 ); - ip[3].set_i( 132 ); ip[3].set_j( 72 ); - ip[4].set_i( 145 ); ip[4].set_j( 134 ); - E1.initTracking(I, n, ip) ; - delete [] ip ; - } + if (opt_display) { + E1.display(I, vpColor::green) ; + vpDisplay::flush(I); + } - if (opt_display) { - E1.display(I, vpColor::green) ; - vpDisplay::flush(I); - } + vpTRACE("sample step %f ",E1.getMe()->getSampleStep()) ; + std::cout << "Tracking on image: " << filename << std::endl; + E1.track(I) ; + if (opt_display) { + vpDisplay::flush(I); + } - vpTRACE("sample step %f ",E1.getMe()->getSampleStep()) ; - std::cout << "Tracking on image: " << filename << std::endl; - E1.track(I) ; - if (opt_display) { - vpDisplay::flush(I); + if (opt_display && opt_click_allowed) { + std::cout << "A click to exit..." << std::endl; + vpDisplay::getClick(I) ; + } + std::cout <<"------------------------------------------------------------"<<std::endl; + return 0; } - - if (opt_display && opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - std::cout <<"------------------------------------------------------------"<<std::endl; - } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackMeEllipse.cpp b/example/tracking/trackMeEllipse.cpp index 317fb87ed9ba0a1aee4dd3a65d721cbd72752ae8..e35238549ccbe27bdfdc59922b677bedf34f777e 100644 --- a/example/tracking/trackMeEllipse.cpp +++ b/example/tracking/trackMeEllipse.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackMeEllipse.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackMeEllipse.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -68,6 +68,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpColor.h> #include <visp/vpMeEllipse.h> @@ -77,6 +78,9 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display); + /*! Print the program options. @@ -133,18 +137,18 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -153,7 +157,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_all // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -164,110 +168,109 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_all int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display) == false) { + exit (-1); } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image is - // read on the disk - vpImage<unsigned char> I ; - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/ellipse-1/"); + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image is + // read on the disk + vpImage<unsigned char> I ; - // Build the name of the image file - unsigned iter = 1; // Image number - std::ostringstream s; - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/ellipse-1"); - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; + // Build the name of the image file + unsigned int iter = 1; // Image number + std::ostringstream s; + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is thrown if an exception from readPGM has been caught - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is thrown if an exception from readPGM has been caught + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -278,88 +281,81 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpMeEllipse E1 ; - - vpMe me ; - me.setRange(20) ; - me.setSampleStep(2) ; - me.setPointsToTrack(60) ; - me.setThreshold(15000) ; - - E1.setMe(&me) ; - E1.setDisplay(vpMeSite::RANGE_RESULT) ; - if (opt_click_allowed) - E1.initTracking(I) ; - else { - // Create a list of points to automate the test - unsigned int n=5 ; - vpImagePoint *ip = new vpImagePoint [n]; - ip[0].set_i( 33 ); ip[0].set_j( 276 ); - ip[1].set_i( 83 ); ip[1].set_j( 126 ); - ip[2].set_i( 201 ); ip[2].set_j( 36 ); - ip[3].set_i( 243 ); ip[3].set_j( 164 ); - ip[4].set_i( 195 ); ip[4].set_j( 329 ); - E1.initTracking(I, n, ip) ; - delete [] ip ; - } - if (opt_display) { - E1.display(I, vpColor::green) ; - } + vpMeEllipse E1 ; + + vpMe me ; + me.setRange(20) ; + me.setSampleStep(2) ; + me.setPointsToTrack(60) ; + me.setThreshold(15000) ; + + E1.setMe(&me) ; + E1.setDisplay(vpMeSite::RANGE_RESULT) ; + if (opt_click_allowed) + E1.initTracking(I) ; + else { + // Create a list of points to automate the test + unsigned int n=5 ; + vpImagePoint *ip = new vpImagePoint [n]; + ip[0].set_i( 33 ); ip[0].set_j( 276 ); + ip[1].set_i( 83 ); ip[1].set_j( 126 ); + ip[2].set_i( 201 ); ip[2].set_j( 36 ); + ip[3].set_i( 243 ); ip[3].set_j( 164 ); + ip[4].set_i( 195 ); ip[4].set_j( 329 ); + E1.initTracking(I, n, ip) ; + delete [] ip ; + } + if (opt_display) { + E1.display(I, vpColor::green) ; + } - vpERROR_TRACE("sample step %f ",E1.getMe()->getSampleStep()) ; - E1.track(I) ; - if (opt_display && opt_click_allowed) { - std::cout << "A click to continue..." << std::endl; - vpDisplay::getClick(I) ; - } - std::cout <<"------------------------------------------------------------"<<std::endl; + vpERROR_TRACE("sample step %f ",E1.getMe()->getSampleStep()) ; + E1.track(I) ; + if (opt_display && opt_click_allowed) { + std::cout << "A click to continue..." << std::endl; + vpDisplay::getClick(I) ; + } + std::cout <<"------------------------------------------------------------"<<std::endl; - for (int iter = 1 ; iter < 51 ; iter++) // initially : iter < 1500 - { - // set the new image name - s.str(""); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - std::cout << "Tracking on image: " << filename << std::endl; - // read the image - vpImageIo::read(I, filename); - if (opt_display) { - // Display the image - vpDisplay::display(I) ; - } - try + for (iter = 1 ; iter < 51 ; iter++) // initially : iter < 1500 { + // set the new image name + s.str(""); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + std::cout << "Tracking on image: " << filename << std::endl; + // read the image + vpImageIo::read(I, filename); + if (opt_display) { + // Display the image + vpDisplay::display(I) ; + } + E1.track(I) ; - } - catch(...) - { - vpERROR_TRACE("Error in tracking vpMeLine ") ; - exit(1) ; - } - if (opt_display) { - E1.display(I,vpColor::green) ; - vpDisplay::flush(I) ; + if (opt_display) { + E1.display(I,vpColor::green) ; + vpDisplay::flush(I) ; + } + } + if (opt_display && opt_click_allowed) { + std::cout << "A click to exit..." << std::endl; + vpDisplay::getClick(I) ; } + return 0; } - if (opt_display && opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackMeLine.cpp b/example/tracking/trackMeLine.cpp index a74df86862a65129563eaedf5d7c6614e8cd1553..c91695c428555b830f96517e0abca336b726fd8a 100644 --- a/example/tracking/trackMeLine.cpp +++ b/example/tracking/trackMeLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackMeLine.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: trackMeLine.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,8 +59,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) - +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -68,6 +67,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpColor.h> #include <visp/vpMeLine.h> @@ -81,6 +81,9 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display); + /*! Print the program options. @@ -135,21 +138,20 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, std::string &ipath, - bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -158,7 +160,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -169,110 +171,110 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; - - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/line/"); - - // Build the name of the image file - unsigned iter = 1; // Image number - std::ostringstream s; - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; - - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; + + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/line"); + + // Build the name of the image file + unsigned int iter = 1; // Image number + std::ostringstream s; + s.setf(std::ios::right, std::ios::adjustfield); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; + + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -283,91 +285,81 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - - - vpMeLine L1 ; - - vpMe me ; - me.setRange(15) ; - me.setPointsToTrack(160) ; - me.setThreshold(15000) ; - - - L1.setMe(&me) ; - L1.setDisplay(vpMeSite::RANGE_RESULT) ; - - if (opt_display && opt_click_allowed) - L1.initTracking(I) ; - else { - vpImagePoint ip1, ip2; - ip1.set_i( 96 ); - ip1.set_j( 191 ); - ip2.set_i( 122 ); - ip2.set_j( 211 ); - L1.initTracking(I, ip1, ip2) ; - } - - if (opt_display) - L1.display(I, vpColor::green) ; - L1.track(I) ; - if (opt_display && opt_click_allowed) { - std::cout << "A click to continue..." << std::endl; - vpDisplay::getClick(I) ; - } - std::cout <<"----------------------------------------------------------"<<std::endl; + vpMeLine L1 ; + + vpMe me ; + me.setRange(15) ; + me.setPointsToTrack(160) ; + me.setThreshold(15000) ; + + L1.setMe(&me) ; + L1.setDisplay(vpMeSite::RANGE_RESULT) ; + + if (opt_display && opt_click_allowed) + L1.initTracking(I) ; + else { + vpImagePoint ip1, ip2; + ip1.set_i( 96 ); + ip1.set_j( 191 ); + ip2.set_i( 122 ); + ip2.set_j( 211 ); + L1.initTracking(I, ip1, ip2) ; + } - vpFeatureLine l ; + if (opt_display) + L1.display(I, vpColor::green) ; - vpCameraParameters cam ; - vpImage<vpRGBa> Ic ; - for (int iter = 1 ; iter < 30 ; iter++) - { - std::cout <<"----------------------------------------------------------"<<std::endl; - // set the new image name - s.str(""); - s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); - // read the image - vpImageIo::read(I, filename); - if (opt_display) { - // Display the image - vpDisplay::display(I) ; + L1.track(I) ; + if (opt_display && opt_click_allowed) { + std::cout << "A click to continue..." << std::endl; + vpDisplay::getClick(I) ; } + std::cout <<"----------------------------------------------------------"<<std::endl; + + vpFeatureLine l ; - try + vpCameraParameters cam ; + vpImage<vpRGBa> Ic ; + for (iter = 1 ; iter < 30 ; iter++) { + std::cout <<"----------------------------------------------------------"<<std::endl; + // set the new image name + s.str(""); + s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; + filename = vpIoTools::createFilePath(dirname, s.str()); + // read the image + vpImageIo::read(I, filename); + if (opt_display) { + // Display the image + vpDisplay::display(I) ; + } + std::cout << "Tracking on image: " << filename << std::endl; L1.track(I) ; - } - catch(...) - { - vpERROR_TRACE("Error in tracking vpMeLine ") ; - exit(1) ; - } - - vpTRACE("L1 : %f %f", L1.getRho(), vpMath::deg(L1.getTheta())) ; - vpFeatureBuilder::create(l,cam,L1) ; - vpTRACE("L1 : %f %f", l.getRho(), vpMath::deg(l.getTheta())) ; - if (opt_display) { - L1.display(I,vpColor::green) ; - vpDisplay::flush(I) ; - if (opt_click_allowed) { - std::cout << "A click to continue..." << std::endl; - vpDisplay::getClick(I) ; + vpTRACE("L1 : %f %f", L1.getRho(), vpMath::deg(L1.getTheta())) ; + vpFeatureBuilder::create(l,cam,L1) ; + vpTRACE("L1 : %f %f", l.getRho(), vpMath::deg(l.getTheta())) ; + + if (opt_display) { + L1.display(I,vpColor::green) ; + vpDisplay::flush(I) ; + if (opt_click_allowed) { + std::cout << "A click to continue..." << std::endl; + vpDisplay::getClick(I) ; + } } } + if (opt_display && opt_click_allowed) { + std::cout << "A click to exit..." << std::endl; + vpDisplay::getClick(I) ; + } + return 0; } - if (opt_display && opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } @@ -375,7 +367,7 @@ main(int argc, const char ** argv) int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/tracking/trackMeNurbs.cpp b/example/tracking/trackMeNurbs.cpp index fa99856219ab8f688cdf592315997da6c016b4a9..eda500c4efaede87efaf59dd5f925edf2d7b43c2 100644 --- a/example/tracking/trackMeNurbs.cpp +++ b/example/tracking/trackMeNurbs.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: trackMeNurbs.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: trackMeNurbs.cpp 5108 2015-01-05 07:48:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,7 +60,7 @@ #include <sstream> #include <iomanip> -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) #include <visp/vpImage.h> #include <visp/vpImageIo.h> @@ -68,6 +68,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpColor.h> #include <visp/vpMeNurbs.h> @@ -78,6 +79,9 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display); + /*! Print the program options. @@ -134,18 +138,18 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -154,7 +158,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_all // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -165,83 +169,83 @@ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_all int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_click_allowed, opt_display) == false) { + exit (-1); } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image is - // read on the disk - vpImage<unsigned char> I ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image is + // read on the disk + vpImage<unsigned char> I ; - // Set the path location of the image sequence - filename = ipath + vpIoTools::path("/ViSP-images/ellipse-1/image.%04d.pgm"); + // Set the path location of the image sequence + filename = vpIoTools::createFilePath(ipath, "ViSP-images/ellipse-1/image.%04d.pgm"); - // Build the name of the image file - vpVideoReader reader; - //Initialize the reader and get the first frame. - reader.setFileName(filename.c_str()); - reader.setFirstFrameIndex(1); - reader.open(I); + // Build the name of the image file + vpVideoReader reader; + //Initialize the reader and get the first frame. + reader.setFileName(filename.c_str()); + reader.setFirstFrameIndex(1); + reader.open(I); - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -252,92 +256,83 @@ main(int argc, const char ** argv) vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpMeNurbs nurbs ; + vpMeNurbs nurbs ; - vpMe me ; - me.setRange(30) ; - me.setSampleStep(5) ; - me.setPointsToTrack(60) ; - me.setThreshold(15000) ; + vpMe me ; + me.setRange(30) ; + me.setSampleStep(5) ; + me.setPointsToTrack(60) ; + me.setThreshold(15000) ; + nurbs.setMe(&me); + nurbs.setDisplay(vpMeSite::RANGE_RESULT) ; + nurbs.setNbControlPoints(14); - nurbs.setMe(&me); - nurbs.setDisplay(vpMeSite::RANGE_RESULT) ; - nurbs.setNbControlPoints(14); - - if (opt_click_allowed) - { - std::cout << "Click on points along the edge with the left button." << std::endl; - std::cout << "Then click on the right button to continue." << std::endl; - nurbs.initTracking(I); - } - else - { - // Create a list of points to automate the test - std::list<vpImagePoint> list; - list.push_back(vpImagePoint(178,357)); - list.push_back(vpImagePoint(212,287)); - list.push_back(vpImagePoint(236,210)); - list.push_back(vpImagePoint(240, 118)); - list.push_back(vpImagePoint(210, 40)); - - nurbs.initTracking(I, list) ; - } - if (opt_display) { - nurbs.display(I, vpColor::green) ; - } - - - nurbs.track(I) ; - if (opt_display && opt_click_allowed) { - std::cout << "A click to continue..." << std::endl; - vpDisplay::getClick(I) ; - } - std::cout <<"------------------------------------------------------------"<<std::endl; - - - for (int iter = 1 ; iter < 40 ; iter++) - { - //read the image - reader.getFrame(I,iter); + if (opt_click_allowed) + { + std::cout << "Click on points along the edge with the left button." << std::endl; + std::cout << "Then click on the right button to continue." << std::endl; + nurbs.initTracking(I); + } + else + { + // Create a list of points to automate the test + std::list<vpImagePoint> list; + list.push_back(vpImagePoint(178,357)); + list.push_back(vpImagePoint(212,287)); + list.push_back(vpImagePoint(236,210)); + list.push_back(vpImagePoint(240, 118)); + list.push_back(vpImagePoint(210, 40)); + + nurbs.initTracking(I, list) ; + } if (opt_display) { - // Display the image - vpDisplay::display(I) ; + nurbs.display(I, vpColor::green) ; } - try + + nurbs.track(I) ; + if (opt_display && opt_click_allowed) { + std::cout << "A click to continue..." << std::endl; + vpDisplay::getClick(I) ; + } + std::cout <<"------------------------------------------------------------"<<std::endl; + + for (int iter = 1 ; iter < 40 ; iter++) { + //read the image + reader.getFrame(I,iter); + if (opt_display) { + // Display the image + vpDisplay::display(I) ; + } + //Track the nurbs nurbs.track(I) ; - } - catch(...) - { - vpERROR_TRACE("Error in tracking vpMeLine ") ; - exit(1) ; - } - if (opt_display) { - nurbs.display(I,vpColor::green) ; - vpDisplay::flush(I) ; - vpTime::wait(100); + + if (opt_display) { + nurbs.display(I,vpColor::green) ; + vpDisplay::flush(I) ; + vpTime::wait(100); + } + } + if (opt_display && opt_click_allowed) { + std::cout << "A click to exit..." << std::endl; + vpDisplay::getClick(I) ; } + return 0; } - if (opt_display && opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; - vpDisplay::getClick(I) ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else int main() { - vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); + vpERROR_TRACE("You do not have X11, GTK, GDI or OpenCV display functionalities..."); } #endif diff --git a/example/video/CMakeLists.txt b/example/video/CMakeLists.txt index 9204ded644f8760844e079b5e3fd1d52eaca238a..1b2a73e5ddb4fad24c6e3cc3962c69530e2f26d2 100644 --- a/example/video/CMakeLists.txt +++ b/example/video/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 2158 2009-05-07 07:24:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -49,25 +49,26 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() -ENDFOREACH(source) # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(videoReader videoReader -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(imageSequenceReader imageSequenceReader -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(videoReader videoReader -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(imageSequenceReader imageSequenceReader -c ${OPTION_TO_DESACTIVE_DISPLAY}) -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/video/imageSequenceReader.cpp b/example/video/imageSequenceReader.cpp index e349b31af846d3c41847c5d721c922c0bf9016ba..5880542ef014499385ec713a41f4a1dbceab362b 100644 --- a/example/video/imageSequenceReader.cpp +++ b/example/video/imageSequenceReader.cpp @@ -3,7 +3,7 @@ * $Id: imageDiskRW.cpp 2158 2009-05-07 07:24:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,6 +65,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:p:f:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath); +bool getOptions(int argc, const char **argv, + std::string &ipath, std::string &ppath, int &first, bool &click_allowed, bool &display); + /*! Print the program options. @@ -137,20 +141,20 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, int &first, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; - case 'f': first = atoi(optarg); break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; + case 'f': first = atoi(optarg_); break; case 'h': usage(argv[0], NULL, ipath, ppath); return false; break; default: - usage(argv[0], optarg, ipath, ppath); return false; break; + usage(argv[0], optarg_, ipath, ppath); return false; break; } } @@ -158,7 +162,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, ppath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -170,171 +174,164 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string filename; + int opt_first = 1; + bool opt_click_allowed = true; + bool opt_display = true; + + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " videoImageSequenceReader.cpp" <<std::endl << std::endl ; + + std::cout << " reading an image sequence" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string filename; - int opt_first = 1; - bool opt_click_allowed = true; - bool opt_display = true; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " videoImageSequenceReader.cpp" <<std::endl << std::endl ; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ + usage(argv[0], NULL, ipath, opt_ppath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " video path where test images are located." << std::endl << std::endl; + exit(-1); + } - std::cout << " reading an image sequence" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; + ///////////////////////////////////////////////////////////////////// - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; + // vpImage is a template class you can declare vpImage of ... everything... + vpImage<vpRGBa> I ; - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + //Create the video Reader + vpVideoReader reader; - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + if (opt_ppath.empty()) + { + filename = vpIoTools::createFilePath(ipath, "ViSP-images/mire-2/image.%04d.pgm"); + } + else + { + filename.assign(opt_ppath); } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ - usage(argv[0], NULL, ipath, opt_ppath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " video path where test images are located." << std::endl << std::endl; - exit(-1); - } - - - ///////////////////////////////////////////////////////////////////// - - // vpImage is a template class you can declare vpImage of ... everything... - vpImage<vpRGBa> I ; - - //Create the video Reader - vpVideoReader reader; + //Initialize the reader and get the first frame. + reader.setFileName(filename); + reader.setFirstFrameIndex(opt_first); + reader.open(I); - if (opt_ppath.empty()) - { - filename = ipath + vpIoTools::path("/ViSP-images/mire-2/image.%04d.pgm"); - } - else - { - filename.assign(opt_ppath); - } - - //Initialize the reader and get the first frame. - reader.setFileName(filename.c_str()); - reader.setFirstFrameIndex(opt_first); - reader.open(I); - - // We open a window using either X11, GTK, GDI or OpenCV. + // We open a window using either X11, GTK, GDI or OpenCV. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #endif - if (opt_display) - { - try - { + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display video frame") ; vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) + + if (opt_display && opt_click_allowed) { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + std::cout << "Click on the image to read and display the second frame" << std::endl; + vpDisplay::getClick(I); } - } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click on the image to read and display the second frame" << std::endl; - vpDisplay::getClick(I); - } - - reader.getFrame(I,opt_first+1); - - if (opt_display) - { - vpDisplay::display(I) ; - vpDisplay::flush(I); - } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click on the image to read and display the last frame" << std::endl; - vpDisplay::getClick(I); - } - - reader.getFrame(I,reader.getLastFrameIndex()); - - if (opt_display) - { - vpDisplay::display(I) ; - vpDisplay::flush(I); - } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click to see the video" << std::endl; - vpDisplay::getClick(I); - } - - int lastFrame = reader.getLastFrameIndex(); - for (int i = opt_first; i <= lastFrame; i++) - { - reader.getFrame(I,i); + reader.getFrame(I,opt_first+1); + + if (opt_display) + { + vpDisplay::display(I) ; + vpDisplay::flush(I); + } + + if (opt_display && opt_click_allowed) + { + std::cout << "Click on the image to read and display the last frame" << std::endl; + vpDisplay::getClick(I); + } + + reader.getFrame(I,reader.getLastFrameIndex()); + if (opt_display) { vpDisplay::display(I) ; vpDisplay::flush(I); } + + if (opt_display && opt_click_allowed) + { + std::cout << "Click to see the video" << std::endl; + vpDisplay::getClick(I); + } + + int lastFrame = reader.getLastFrameIndex(); + + for (int i = opt_first; i <= lastFrame; i++) + { + reader.getFrame(I,i); + if (opt_display) + { + vpDisplay::display(I) ; + vpDisplay::flush(I); + } + } + + if (opt_display && opt_click_allowed) + { + std::cout << "Click to exit the test" << std::endl; + vpDisplay::getClick(I); + } + + return 0; } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click to exit the test" << std::endl; - vpDisplay::getClick(I); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } #else int main() diff --git a/example/video/videoReader.cpp b/example/video/videoReader.cpp index c8e93806ba15e19595ba6310cba63134345a2092..4d6b365faaf817f5e7f4061ffed5c993aff7c016 100644 --- a/example/video/videoReader.cpp +++ b/example/video/videoReader.cpp @@ -3,7 +3,7 @@ * $Id: imageDiskRW.cpp 2158 2009-05-07 07:24:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:p:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, + bool &click_allowed, bool &display); + /*! Print the program options. @@ -130,22 +134,22 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &ppath, bool &click_allowed, bool &display) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, + bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, ppath); return false; break; default: - usage(argv[0], optarg, ipath, ppath); return false; break; + usage(argv[0], optarg_, ipath, ppath); return false; break; } } @@ -153,7 +157,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, ppath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -165,161 +169,144 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " videoReader.cpp" <<std::endl << std::endl ; + + std::cout << " reading a video file" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed, + opt_display) == false) { + exit (-1); + } - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " videoReader.cpp" <<std::endl << std::endl ; + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ + usage(argv[0], NULL, ipath, opt_ppath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " video path where test images are located." << std::endl << std::endl; + exit(-1); + } - std::cout << " reading a video file" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; + ///////////////////////////////////////////////////////////////////// - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; + // vpImage is a template class you can declare vpImage of ... everything... + vpImage<vpRGBa> I ; - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_click_allowed, - opt_display) == false) { - exit (-1); - } + //Create the video Reader + vpVideoReader reader; - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + if (opt_ppath.empty()) + { + filename = vpIoTools::createFilePath(ipath, "ViSP-images/video/cube.mpeg"); + } + else + { + filename.assign(opt_ppath); } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()){ - usage(argv[0], NULL, ipath, opt_ppath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " video path where test images are located." << std::endl << std::endl; - exit(-1); - } - - - ///////////////////////////////////////////////////////////////////// - - - // vpImage is a template class you can declare vpImage of ... everything... - vpImage<vpRGBa> I ; - - //Create the video Reader - vpVideoReader reader; - if (opt_ppath.empty()) - { - filename = ipath + vpIoTools::path("/ViSP-images/video/cube.mpeg"); - } - else - { - filename.assign(opt_ppath); - } - - //Initialize the reader and get the first frame. - reader.setFileName(filename.c_str()); - try - { + //Initialize the reader and get the first frame. + std::cout << "Process video in " << filename << std::endl; + reader.setFileName(filename); reader.open(I); - } - catch(...) - { - return 0; - } - // We open a window using either X11, GTK, GDI or OpenCV. + // We open a window using either X11, GTK, GDI or OpenCV. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display; + vpDisplayOpenCV display; #endif - if (opt_display) - { - try - { + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display video frame") ; vpDisplay::display(I) ; vpDisplay::flush(I) ; } - catch(...) + + // if (opt_display && opt_click_allowed) + // { + // std::cout << "Click on the image to read and display the last key frame" << std::endl; + // vpDisplay::getClick(I); + // } + // + // reader.getFrame(I,reader.getLastFrameIndex()); + // + // if (opt_display) + // { + // vpDisplay::display(I) ; + // vpDisplay::flush(I); + // } + + if (opt_display && opt_click_allowed) { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); + std::cout << "Click to see the video" << std::endl; + vpDisplay::getClick(I); } - } - - // if (opt_display && opt_click_allowed) - // { - // std::cout << "Click on the image to read and display the last key frame" << std::endl; - // vpDisplay::getClick(I); - // } - // - // reader.getFrame(I,reader.getLastFrameIndex()); - // - // if (opt_display) - // { - // vpDisplay::display(I) ; - // vpDisplay::flush(I); - // } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click to see the video" << std::endl; - vpDisplay::getClick(I); - } - - int lastFrame = reader.getLastFrameIndex(); - //To go to the beginning of the video - reader.getFrame(I,0); - - for (int i = 0; i <= lastFrame; i++) - { - reader.acquire(I); - if (opt_display) + + while (! reader.end() ) { + std::cout << "Read frame: " << reader.getFrameIndex() << std::endl; + reader.acquire(I); + if (opt_display) + { + vpDisplay::display(I) ; + vpDisplay::flush(I); + } + } + + if (opt_display && opt_click_allowed) { - vpDisplay::display(I) ; - vpDisplay::flush(I); + std::cout << "Click to exit this example" << std::endl; + vpDisplay::getClick(I); } } - - if (opt_display && opt_click_allowed) - { - std::cout << "Click to exit the test" << std::endl; - vpDisplay::getClick(I); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } return 0; } diff --git a/example/wireframe-simulator/CMakeLists.txt b/example/wireframe-simulator/CMakeLists.txt index 523661c993e8caf82e77b063f559d4df7afbbd7c..8678ea0d02fba9bc1745d97b6630376eed893f4e 100644 --- a/example/wireframe-simulator/CMakeLists.txt +++ b/example/wireframe-simulator/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,29 +43,28 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE wireframeSimulator.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_examples ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "examples") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(wireframeSimulator wireframeSimulator -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(wireframeSimulator wireframeSimulator -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/example/wireframe-simulator/wireframeSimulator.cpp b/example/wireframe-simulator/wireframeSimulator.cpp index db922cf0cbd32b0437c4f42db60c142136c5ff51..46600a04ae99f17c62d12c0e445236045af6fa3b 100644 --- a/example/wireframe-simulator/wireframeSimulator.cpp +++ b/example/wireframe-simulator/wireframeSimulator.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: wireframeSimulator.cpp 4182 2013-03-27 13:20:58Z fspindle $ + * $Id: wireframeSimulator.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,6 +65,9 @@ #ifdef VISP_HAVE_DISPLAY +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &display, bool &click); + /*! Print the program options. @@ -113,9 +116,9 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, bool &display, bool &click) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click = false; break; @@ -123,7 +126,7 @@ bool getOptions(int argc, const char **argv, bool &display, bool &click) case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -132,7 +135,7 @@ bool getOptions(int argc, const char **argv, bool &display, bool &click) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -143,41 +146,39 @@ bool getOptions(int argc, const char **argv, bool &display, bool &click) int main(int argc, const char ** argv) { - bool opt_display = true; - bool opt_click = true; - - // Read the command line options - if (getOptions(argc, argv, opt_display, opt_click) == false) { - exit (-1); - } - - /* + try { + bool opt_display = true; + bool opt_click = true; + + // Read the command line options + if (getOptions(argc, argv, opt_display, opt_click) == false) { + exit (-1); + } + + /* Three vpImage are created : one for the main camera and the others for two external cameras */ - vpImage<vpRGBa> Iint(480,640,255); - vpImage<vpRGBa> Iext1(480,640,255); - vpImage<vpRGBa> Iext2(480,640,255); + vpImage<vpRGBa> Iint(480,640,255); + vpImage<vpRGBa> Iext1(480,640,255); + vpImage<vpRGBa> Iext2(480,640,255); - /* + /* Create a display for each different cameras. */ #if defined VISP_HAVE_X11 - vpDisplayX display[3]; + vpDisplayX display[3]; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV display[3]; + vpDisplayOpenCV display[3]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[3]; + vpDisplayGDI display[3]; #elif defined VISP_HAVE_D3D9 - vpDisplayD3D display[3]; + vpDisplayD3D display[3]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[3]; + vpDisplayGTK display[3]; #endif - - if (opt_display) - { - try - { + + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(Iint, 100, 100,"The internal view") ; display[1].init(Iext1, 100, 100,"The first external view") ; @@ -192,146 +193,145 @@ main(int argc, const char ** argv) vpDisplay::display(Iext2); vpDisplay::flush(Iext2); } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - //The homogeneous matrix which gives the current position of the main camera relative to the object - vpHomogeneousMatrix cMo(0,0.05,1.3,vpMath::rad(15),vpMath::rad(25),0); - - //The homogeneous matrix which gives the desired position of the main camera relative to the object - vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); + //The homogeneous matrix which gives the current position of the main camera relative to the object + vpHomogeneousMatrix cMo(0,0.05,1.3,vpMath::rad(15),vpMath::rad(25),0); + + //The homogeneous matrix which gives the desired position of the main camera relative to the object + vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),vpMath::rad(0))); - //Declaration of the simulator - vpWireFrameSimulator sim; - /* + //Declaration of the simulator + vpWireFrameSimulator sim; + /* Set the scene. It enables to choose the shape of the object and the shape of the desired object which is - displayed in the main camera view. It exists several objects in ViSP. See the html documentation of the + displayed in the main camera view. It exists several objects in ViSP. See the html documentation of the simulator class to have the complete list. Note : if you don't want to have a desired object displayed in the main camera view you can use the initObject Method. - Here the object is a plate with 4 points and it is the same object which is used to display the object at the desired position. + Here the object is a plate with 4 points and it is the same object which is used to display the object at the desired position. */ - sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - - /* + sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); + + /* The object at the current position will be displayed in blue The object at the desired position will be displayed in red The camera will be display in green */ - sim.setCurrentViewColor(vpColor::blue); - sim.setDesiredViewColor(vpColor::red); - sim.setCameraColor(vpColor::green); - /* + sim.setCurrentViewColor(vpColor::blue); + sim.setDesiredViewColor(vpColor::red); + sim.setCameraColor(vpColor::green); + /* Set the current and the desired position of the camera relative to the object. */ - sim.setCameraPositionRelObj(cMo) ; - sim.setDesiredCameraPosition(cdMo); - /* + sim.setCameraPositionRelObj(cMo) ; + sim.setDesiredCameraPosition(cdMo); + /* Set the main external camera's position relative to the world reference frame. More information about the different frames are given in the html documentation. */ - vpHomogeneousMatrix camMw(vpHomogeneousMatrix(0.0,0,4.5,vpMath::rad(0),vpMath::rad(-30),0)); - sim.setExternalCameraPosition(camMw); - - /* + vpHomogeneousMatrix camMw(vpHomogeneousMatrix(0.0,0,4.5,vpMath::rad(0),vpMath::rad(-30),0)); + sim.setExternalCameraPosition(camMw); + + /* Set the parameters of the cameras (internal and external) */ - vpCameraParameters camera(1000,1000,320,240); - sim.setInternalCameraParameters(camera); - sim.setExternalCameraParameters(camera); - - vpHomogeneousMatrix camoMw(vpHomogeneousMatrix(-0.3,0.2,2.5,vpMath::rad(0),vpMath::rad(10),0)); - - if (opt_display) - { - //Get the view of the internal camera - sim.getInternalImage(Iint); - //Get the view of the main external camera - sim.getExternalImage(Iext1); - //Get the view of an external camera that you can positionned thanks - //to a vpHomogeneousMatrix which describes the position of the camera - //relative to the world reference frame. - sim.getExternalImage(Iext2,camoMw); - //Display the views. - - vpDisplay::flush(Iint); - vpDisplay::flush(Iext1); - vpDisplay::flush(Iext2); - } - - std::cout << std::endl; - std::cout << "Here are presented the effect of the basic functions of the simulator" << std::endl; - std::cout << std::endl; - - if (opt_display) - { - if (opt_click) { - std::cout << "Click on the internal view window to continue. the object will move. The external cameras are fixed. The main camera moves too because the homogeneous matrix cMo didn't change." << std::endl; - vpDisplay::getClick(Iint); + vpCameraParameters camera(1000,1000,320,240); + sim.setInternalCameraParameters(camera); + sim.setExternalCameraParameters(camera); + + vpHomogeneousMatrix camoMw(vpHomogeneousMatrix(-0.3,0.2,2.5,vpMath::rad(0),vpMath::rad(10),0)); + + if (opt_display) + { + //Get the view of the internal camera + sim.getInternalImage(Iint); + //Get the view of the main external camera + sim.getExternalImage(Iext1); + //Get the view of an external camera that you can positionned thanks + //to a vpHomogeneousMatrix which describes the position of the camera + //relative to the world reference frame. + sim.getExternalImage(Iext2,camoMw); + //Display the views. + + vpDisplay::flush(Iint); + vpDisplay::flush(Iext1); + vpDisplay::flush(Iext2); } - vpDisplay::display(Iint) ; - vpDisplay::display(Iext1) ; - vpDisplay::display(Iext2) ; - } - /* + + std::cout << std::endl; + std::cout << "Here are presented the effect of the basic functions of the simulator" << std::endl; + std::cout << std::endl; + + if (opt_display) + { + if (opt_click) { + std::cout << "Click on the internal view window to continue. the object will move. The external cameras are fixed. The main camera moves too because the homogeneous matrix cMo didn't change." << std::endl; + vpDisplay::getClick(Iint); + } + vpDisplay::display(Iint) ; + vpDisplay::display(Iext1) ; + vpDisplay::display(Iext2) ; + } + /* To move the object you have to define a vpHomogeneousMatrix which gives the position of the object relative to the world refrenece frame. */ - vpHomogeneousMatrix mov(0.05,0.05,0.2,vpMath::rad(10),0,0); - sim.set_fMo(mov); - - - if (opt_display) - { - //Get the view of the internal camera - sim.getInternalImage(Iint); - //Get the view of the main external camera - sim.getExternalImage(Iext1); - //Get the view of an external camera that you can positionned thanks - //to a vpHomogeneousMatrix which describes the position of the camera - //relative to the world reference frame. - sim.getExternalImage(Iext2,camoMw); - //Display the views. - - vpDisplay::flush(Iint); - vpDisplay::flush(Iext1); - vpDisplay::flush(Iext2); - } - - std::cout << std::endl; - if (opt_display) - { - if (opt_click) { - std::cout << "Click on the internal view window to continue" << std::endl; - vpDisplay::getClick(Iint); + vpHomogeneousMatrix mov(0.05,0.05,0.2,vpMath::rad(10),0,0); + sim.set_fMo(mov); + + + if (opt_display) + { + //Get the view of the internal camera + sim.getInternalImage(Iint); + //Get the view of the main external camera + sim.getExternalImage(Iext1); + //Get the view of an external camera that you can positionned thanks + //to a vpHomogeneousMatrix which describes the position of the camera + //relative to the world reference frame. + sim.getExternalImage(Iext2,camoMw); + //Display the views. + + vpDisplay::flush(Iint); + vpDisplay::flush(Iext1); + vpDisplay::flush(Iext2); } - } - std::cout << std::endl; - std::cout << "Now you can move the main external camera. Click inside the corresponding window with one of the three buttons of your mouse and move the pointer." << std::endl; - std::cout << std::endl; - std::cout << "Click on the internal view window when you are finished" << std::endl; - - /* + + std::cout << std::endl; + if (opt_display) + { + if (opt_click) { + std::cout << "Click on the internal view window to continue" << std::endl; + vpDisplay::getClick(Iint); + } + } + std::cout << std::endl; + std::cout << "Now you can move the main external camera. Click inside the corresponding window with one of the three buttons of your mouse and move the pointer." << std::endl; + std::cout << std::endl; + std::cout << "Click on the internal view window when you are finished" << std::endl; + + /* To move the main external camera you need a loop containing the getExternalImage method. This functionnality is only available for the main external camera. */ - if (opt_display && opt_click) - { - while (!vpDisplay::getClick(Iint, false)) + if (opt_display && opt_click) { - vpDisplay::display(Iext1) ; - sim.getExternalImage(Iext1); - vpDisplay::flush(Iext1); + while (!vpDisplay::getClick(Iint, false)) + { + vpDisplay::display(Iext1) ; + sim.getExternalImage(Iext1); + vpDisplay::flush(Iext1); + } } - } - std::cout << std::endl; - std::cout << "You have seen the main capabilities of the simulator. Other specific functionalities are available. Please refers to the html documentation to access the list of all functions" << std::endl; - return 0; + std::cout << std::endl; + std::cout << "You have seen the main capabilities of the simulator. Other specific functionalities are available. Please refers to the html documentation to access the list of all functions" << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else int diff --git a/include/vpConfig.h.cmake b/include/vpConfig.h.cmake index e9fe8aaa682dc207401ab3b04321f7fcd0ae7e95..192b8467b2181d3842aab7e7cb507858400d3bd7 100644 --- a/include/vpConfig.h.cmake +++ b/include/vpConfig.h.cmake @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpConfig.h.cmake 4308 2013-07-08 08:47:09Z fspindle $ + * $Id: vpConfig.h.cmake 5324 2015-02-13 16:11:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,7 +42,7 @@ #define vpConfig_h #if defined _MSC_VER && _MSC_VER >= 1200 - #pragma warning( disable: 4100 4127 4251 4514 4668 4710 4820 ) + #pragma warning( disable: 4100 4127 4251 4275 4514 4668 4710 4820 ) #if _MSC_VER >= 1400 // 1400 = MSVC 8 2005 #pragma warning( disable: 4548 ) #endif @@ -53,6 +53,7 @@ // 4100 : undocumented ("unreferenced formal parameter") // 4127 : conditional expression is constant // 4251 : 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2', ie. disable warnings related to inline functions + // 4275 : non – DLL-interface classkey 'identifier' used as base for DLL-interface classkey 'identifier' // 4514 : 'function' : unreferenced inline function has been removed // 4548 : expression before comma has no effect // 4668 : 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' @@ -61,6 +62,29 @@ // 4986 : undocumented #endif +#if defined _MSC_VER && (_MSC_VER == 1500) +// Visual Studio 9 2008 specific stuff +// Fix running 64-bit OpenMP Debug Builds compiled with Visual Studio 2008 SP1 +// See discussion on https://gforge.inria.fr/forum/message.php?msg_id=149273&group_id=397 +// and the proposed fix: http://www.johanseland.com/2010/08/running-64-bit-openmp-debug-builds.html +# define _BIND_TO_CURRENT_OPENMP_VERSION 1 +#endif + +#if defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +// Work arround to fix build issues that may occur with Mingw: +// error: 'DBL_EPSILON' was not declared in this scope +// error: 'FLT_EPSILON' was not declared in this scope + +# include <float.h> + +# ifndef DBL_EPSILON +# define DBL_EPSILON __DBL_EPSILON__ +# endif +# ifndef FLT_EPSILON +# define FLT_EPSILON __FLT_EPSILON__ +# endif +#endif + // ViSP major version. #define VISP_VERSION_MAJOR ${VISP_VERSION_MAJOR} @@ -104,9 +128,12 @@ // Defined if OpenCV available. #cmakedefine VISP_HAVE_OPENCV -// Defined if OpenCV_nonfree available. +// Defined if OpenCV nonfree module available. Only with OpenCV < 3.0.0 #cmakedefine VISP_HAVE_OPENCV_NONFREE +// Defined if OpenCV xfeatures2d module available. Only since OpenCV >= 3.0.0 +#cmakedefine VISP_HAVE_OPENCV_XFEATURES2D + // OpenCV version in hexadecimal (for example 2.1.0 gives 0x020100). #ifdef VISP_HAVE_OPENCV # define VISP_HAVE_OPENCV_VERSION ${VISP_HAVE_OPENCV_VERSION} @@ -203,9 +230,6 @@ // Defined if ffmpeg library available. #cmakedefine VISP_HAVE_FFMPEG -// Defined if raw1394 and dc1394-1.x libraries available. -#cmakedefine VISP_HAVE_DC1394_1 - // Defined if raw1394 and dc1394-2.x libraries available. #cmakedefine VISP_HAVE_DC1394_2 @@ -247,19 +271,6 @@ // Defined if Irisa's Viper S850 robot available. #cmakedefine VISP_HAVE_VIPER850 -// Defined if Irisa's Cycab car-like mobile robot is found. -// If found, either VISP_HAVE_CYCABTK_OLD nor VISP_HAVE_CYCABTK -// is defined. -#cmakedefine VISP_HAVE_CYCAB - -// Defined if the old cycabtk library is found. CycabTk is used to -// communicate with Irisa's Cycab car-like robot (obsolete). -#cmakedefine VISP_HAVE_CYCABTK_OLD - -// Defined if the last cycabtk library is found. CycabTk is used to -// communicate with Irisa's Cycab car-like robot (to use). -#cmakedefine VISP_HAVE_CYCABTK - // Defined if the Aria library and (pthread, rt, dl libraries under Unix) is found. // These libraries are used to control Pioneer mobile robots. #cmakedefine VISP_HAVE_PIONEER @@ -267,6 +278,12 @@ // Defined if linux/parport.h is available for parallel port usage. #cmakedefine VISP_HAVE_PARPORT +// Defined if libzbar is available for bar code detection +#cmakedefine VISP_HAVE_ZBAR + +// Defined if libdmtx is available for bar code detection +#cmakedefine VISP_HAVE_DMTX + // Defined if Inria's NAS server hosting /udd/ is available // Used for the moment in vpAfma6 class to check if config files are // available in /udd/fspindle/robot/Afma6/current/include/ @@ -298,7 +315,7 @@ // // On Linux, set the visibility accordingly. If C++ symbol visibility // is handled by the compiler, see: http://gcc.gnu.org/wiki/Visibility -# if defined _WIN32 || defined __CYGWIN__ +# if defined(_WIN32) || defined(__CYGWIN__) // On Microsoft Windows, use dllimport and dllexport to tag symbols. # define VISP_DLLIMPORT __declspec(dllimport) # define VISP_DLLEXPORT __declspec(dllexport) @@ -315,7 +332,7 @@ # define VISP_DLLEXPORT # define VISP_DLLLOCAL # endif // __GNUC__ >= 4 -# endif // defined _WIN32 || defined __CYGWIN__ +# endif // defined(_WIN32) || defined(__CYGWIN__) // Under Windows, for shared libraries (DLL) we need to define export on // compilation or import on use (like a third party project). @@ -339,7 +356,7 @@ // Add the material to produce a warning when deprecated functions are used #ifndef vp_deprecated -# if defined (UNIX) +# if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # define vp_deprecated __attribute__((deprecated)) # else # define vp_deprecated __declspec(deprecated) diff --git a/macros/have_visp.m4 b/macros/have_visp.m4 index bdec273b28724e9c5dd46b105c712a4f8aea4b25..951eb3df63aa02ba5d18054d46aaaa9b7333e129 100644 --- a/macros/have_visp.m4 +++ b/macros/have_visp.m4 @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: have_visp.m4 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: have_visp.m4 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c735bbc5e655e6de9b05d8e75f1fe63da8bf3cfe..b1cd872d8c65a1d4ff9f99251bd24eb313eccf76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4059 2013-01-07 17:04:01Z fspindle $ +# $Id: CMakeLists.txt 4971 2014-11-16 13:01:59Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -42,51 +42,72 @@ # Include project files #---------------------------------------------------------------------- # include all the ViSP project .cpp files -INCLUDE(${VISP_SOURCE_DIR}/CMakeSourceFileList.cmake) +include(${VISP_SOURCE_DIR}/CMakeSourceFileList.cmake) # include all the ViSP project .h files -INCLUDE(${VISP_SOURCE_DIR}/CMakeHeaderFileList.cmake) - +include(${VISP_SOURCE_DIR}/CMakeHeaderFileList.cmake) #---------------------------------------------------------------------- # Create rule to copy all the headers from src to include/visp #---------------------------------------------------------------------- # For each header, we create a rule -SET(HEADER_IN_INCLUDE_DIR "") -FOREACH(header ${HEADER_ALL}) - GET_FILENAME_COMPONENT(headerName ${header} NAME) - ADD_CUSTOM_COMMAND( +set(HEADER_IN_INCLUDE_DIR "") +foreach(header_ ${HEADER_ALL}) + get_filename_component(headerName ${header_} NAME) + add_custom_command( OUTPUT ${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/visp/${headerName} - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${header} ${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/visp/${headerName} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${header} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${header_} ${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/visp/${headerName} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${header_} ) - SET(HEADER_IN_INCLUDE_DIR ${HEADER_IN_INCLUDE_DIR} ${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/visp/${headerName} + set(HEADER_IN_INCLUDE_DIR ${HEADER_IN_INCLUDE_DIR} ${VISP_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/visp/${headerName} ) -ENDFOREACH(header) +endforeach() #---------------------------------------------------------------------- # Create a top level rule to copy all the headers from src to include/visp #---------------------------------------------------------------------- -ADD_CUSTOM_TARGET(header ALL +add_custom_target(header ALL DEPENDS ${HEADER_IN_INCLUDE_DIR} ) - #---------------------------------------------------------------------- # build rule for the library #---------------------------------------------------------------------- -ADD_LIBRARY (${VISP_INTERN_LIBRARY} + +if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif() + +add_library(${VISP_INTERN_LIBRARY} ${SRC_ALL} ${HEADER_ALL} ${VISP_INCLUDE_DIR}/vpConfig.h) # create the headers in include/visp before compiling the lib -ADD_DEPENDENCIES(${VISP_INTERN_LIBRARY} header) +add_dependencies(${VISP_INTERN_LIBRARY} header) + +# extra target +add_dependencies(visp_library ${VISP_INTERN_LIBRARY}) # Append the library version information to the library target # properties. -SET_TARGET_PROPERTIES(${VISP_INTERN_LIBRARY} PROPERTIES +set_target_properties(${VISP_INTERN_LIBRARY} PROPERTIES VERSION ${VISP_VERSION} SOVERSION ${VISP_VERSION_MAJOR}.${VISP_VERSION_MINOR} + PUBLIC_HEADER "${HEADER_ALL};${VISP_INCLUDE_DIR}/vpConfig.h" + OUTPUT_NAME "${VISP_INTERN_LIBRARY}${VISP_DLLVERSION}" + DEBUG_POSTFIX "${VISP_DEBUG_POSTFIX}" + RUNTIME_OUTPUT_DIRECTORY "${BINARY_OUTPUT_PATH}" + LIBRARY_OUTPUT_DIRECTORY "${LIBRARY_OUTPUT_PATH}" + ARCHIVE_OUTPUT_DIRECTORY "${LIBRARY_OUTPUT_PATH}" ) +set_property(TARGET ${VISP_INTERN_LIBRARY} APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${VISP_EXTERN_INCLUDE_DIRS} +) +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${VISP_INTERN_LIBRARY} PROPERTIES FOLDER "library") + set_target_properties(header PROPERTIES FOLDER "library") +endif() + + # Link with external libs especially under windows for winmm.lib # needed to create visp.dll. Why ? Because for binaries, winmm.lib @@ -94,35 +115,19 @@ SET_TARGET_PROPERTIES(${VISP_INTERN_LIBRARY} PROPERTIES #IF(WIN32 AND BUILD_SHARED_LIBS AND HAVE_LIBWINMM) # TARGET_LINK_LIBRARIES(${VISP_INTERN_LIBRARY} "winmm") #ENDIF(WIN32 AND BUILD_SHARED_LIBS AND HAVE_LIBWINMM) -TARGET_LINK_LIBRARIES(${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) +target_link_libraries(${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) #---------------------------------------------------------------------- # customize install target #---------------------------------------------------------------------- -# install rule for the library -# if RPM package is to build, we need EXECUTE rights (see http://www.itk.org/Bug/view.php?id=12253) -IF(BUILD_PACKAGE_RPM) - #message("build rpm") - INSTALL(TARGETS ${VISP_INTERN_LIBRARY} - # Destination is either lib or lib64 on Unix 64 bits architectures - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - ) -else() - #message("don't build rpm") - INSTALL(TARGETS ${VISP_INTERN_LIBRARY} - # Destination is either lib or lib64 on Unix 64 bits architectures - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE - ) -endif() -# install rule for all the headers -INSTALL(FILES ${HEADER_ALL} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/visp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE - COMPONENT headers - ) +install( + TARGETS ${VISP_INTERN_LIBRARY} + EXPORT VISPTargets + # install rule for all the headers + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/visp + # Destination is either lib or lib64 on Unix 64 bits architectures + RUNTIME DESTINATION ${VISP_BIN_INSTALL_PATH} COMPONENT libraries + LIBRARY DESTINATION ${VISP_LIB_INSTALL_PATH} COMPONENT libraries + ARCHIVE DESTINATION ${VISP_LIB_INSTALL_PATH} COMPONENT libraries + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE +) diff --git a/src/camera/calibration/vpCalibration.cpp b/src/camera/calibration/vpCalibration.cpp index cb69acc3616c8ae98f599d07233412afd76ba158..48403fc06cba6e93b37f3dc1bf48c0fc4c5d5e16 100644 --- a/src/camera/calibration/vpCalibration.cpp +++ b/src/camera/calibration/vpCalibration.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCalibration.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpCalibration.cpp 5233 2015-01-30 13:50:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,8 @@ int vpCalibration::init() { npt = 0 ; + residual = residual_dist = 1000.; + LoX.clear() ; LoY.clear() ; LoZ.clear() ; @@ -70,10 +72,24 @@ int vpCalibration::init() return 0 ; } +/*! + Default constructor. + */ vpCalibration::vpCalibration() + : cMo(), cMo_dist(), cam(), cam_dist(), rMe(), eMc(), eMc_dist(), + npt(0), LoX(), LoY(), LoZ(), Lip(), residual(1000.), residual_dist(1000.) { init() ; } +/*! + Copy constructor. + */ +vpCalibration::vpCalibration(const vpCalibration &c) + : cMo(), cMo_dist(), cam(), cam_dist(), rMe(), eMc(), eMc_dist(), + npt(0), LoX(), LoY(), LoZ(), Lip(), residual(1000.), residual_dist(1000.) +{ + (*this) = c; +} /*! @@ -89,8 +105,8 @@ vpCalibration::~vpCalibration() \param twinCalibration : object to be copied */ -void vpCalibration::operator=(vpCalibration& twinCalibration ) - { +vpCalibration& vpCalibration::operator=(const vpCalibration& twinCalibration ) +{ npt = twinCalibration.npt ; LoX = twinCalibration.LoX ; LoY = twinCalibration.LoY ; @@ -109,6 +125,8 @@ void vpCalibration::operator=(vpCalibration& twinCalibration ) eMc = twinCalibration.eMc; eMc_dist = twinCalibration.eMc_dist; + + return (*this); } @@ -147,11 +165,10 @@ int vpCalibration::addPoint(double X, double Y, double Z, vpImagePoint &ip) /*! Compute the pose cMo - \param cam : camera intrinsic parameters used for computation - \param cMo : computed pose + \param camera : camera intrinsic parameters used for computation. + \param cMo_est : computed pose */ -void - vpCalibration::computePose(const vpCameraParameters &cam, vpHomogeneousMatrix &cMo) +void vpCalibration::computePose(const vpCameraParameters &camera, vpHomogeneousMatrix &cMo_est) { // The vpPose class mainly contents a list of vpPoint (that is (X,Y,Z, x, y) ) vpPose pose ; @@ -168,7 +185,7 @@ void vpPoint P; P.setWorldCoordinates(*it_LoX, *it_LoY, *it_LoZ); double x=0,y=0 ; - vpPixelMeterConversion::convertPoint(cam, *it_Lip, x,y) ; + vpPixelMeterConversion::convertPoint(camera, *it_Lip, x,y) ; P.set_x(x) ; P.set_y(y) ; @@ -195,50 +212,47 @@ void //we keep the better initialization if (residual_lagrange < residual_dementhon) - cMo = cMo_lagrange; + cMo_est = cMo_lagrange; else - cMo = cMo_dementhon; + cMo_est = cMo_dementhon; // the pose is now refined using the virtual visual servoing approach // Warning: cMo needs to be initialized otherwise it may diverge - pose.computePose(vpPose::VIRTUAL_VS, cMo) ; - + pose.computePose(vpPose::VIRTUAL_VS, cMo_est) ; } /*! Compute and return the standard deviation expressed in pixel for pose matrix and camera intrinsic parameters for model without distortion. - \param cMo : the matrix that defines the pose to be tested. - \param cam : camera intrinsic parameters to be tested. + \param cMo_est : the matrix that defines the pose to be tested. + \param camera : camera intrinsic parameters to be tested. \return the standard deviation by point of the error in pixel . */ -double - vpCalibration::computeStdDeviation(vpHomogeneousMatrix& cMo, - vpCameraParameters& cam) +double vpCalibration::computeStdDeviation(const vpHomogeneousMatrix& cMo_est, + const vpCameraParameters& camera) { - double residual = 0 ; + double residual_ = 0 ; std::list<double>::const_iterator it_LoX = LoX.begin(); std::list<double>::const_iterator it_LoY = LoY.begin(); std::list<double>::const_iterator it_LoZ = LoZ.begin(); std::list<vpImagePoint>::const_iterator it_Lip = Lip.begin(); - double u0 = cam.get_u0() ; - double v0 = cam.get_v0() ; - double px = cam.get_px() ; - double py = cam.get_py() ; + double u0 = camera.get_u0() ; + double v0 = camera.get_v0() ; + double px = camera.get_px() ; + double py = camera.get_py() ; vpImagePoint ip; for (unsigned int i =0 ; i < npt ; i++) { - double oX = *it_LoX; double oY = *it_LoY; double oZ = *it_LoZ; - double cX = oX*cMo[0][0]+oY*cMo[0][1]+oZ*cMo[0][2] + cMo[0][3]; - double cY = oX*cMo[1][0]+oY*cMo[1][1]+oZ*cMo[1][2] + cMo[1][3]; - double cZ = oX*cMo[2][0]+oY*cMo[2][1]+oZ*cMo[2][2] + cMo[2][3]; + double cX = oX*cMo_est[0][0]+oY*cMo_est[0][1]+oZ*cMo_est[0][2] + cMo_est[0][3]; + double cY = oX*cMo_est[1][0]+oY*cMo_est[1][1]+oZ*cMo_est[1][2] + cMo_est[1][3]; + double cZ = oX*cMo_est[2][0]+oY*cMo_est[2][1]+oZ*cMo_est[2][2] + cMo_est[2][3]; double x = cX/cZ ; double y = cY/cZ ; @@ -250,40 +264,39 @@ double double xp = u0 + x*px; double yp = v0 + y*py; - residual += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; + residual_ += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; ++it_LoX; ++it_LoY; ++it_LoZ; ++it_Lip; } - this->residual = residual ; - return sqrt(residual/npt) ; + this->residual = residual_ ; + return sqrt(residual_/npt) ; } /*! Compute and return the standard deviation expressed in pixel for pose matrix and camera intrinsic parameters with pixel to meter model. - \param cMo : the matrix that defines the pose to be tested. - \param cam : camera intrinsic parameters to be tested. + \param cMo_est : the matrix that defines the pose to be tested. + \param camera : camera intrinsic parameters to be tested. \return the standard deviation by point of the error in pixel . */ -double - vpCalibration::computeStdDeviation_dist(vpHomogeneousMatrix& cMo, - vpCameraParameters& cam) +double vpCalibration::computeStdDeviation_dist(const vpHomogeneousMatrix& cMo_est, + const vpCameraParameters& camera) { - double residual = 0 ; + double residual_ = 0 ; std::list<double>::const_iterator it_LoX = LoX.begin(); std::list<double>::const_iterator it_LoY = LoY.begin(); std::list<double>::const_iterator it_LoZ = LoZ.begin(); std::list<vpImagePoint>::const_iterator it_Lip = Lip.begin(); - double u0 = cam.get_u0() ; - double v0 = cam.get_v0() ; - double px = cam.get_px() ; - double py = cam.get_py() ; - double kud = cam.get_kud() ; - double kdu = cam.get_kdu() ; + double u0 = camera.get_u0() ; + double v0 = camera.get_v0() ; + double px = camera.get_px() ; + double py = camera.get_py() ; + double kud = camera.get_kud() ; + double kdu = camera.get_kdu() ; double inv_px = 1/px; double inv_py = 1/px; @@ -291,14 +304,13 @@ double for (unsigned int i =0 ; i < npt ; i++) { - double oX = *it_LoX; double oY = *it_LoY; double oZ = *it_LoZ; - double cX = oX*cMo[0][0]+oY*cMo[0][1]+oZ*cMo[0][2] + cMo[0][3]; - double cY = oX*cMo[1][0]+oY*cMo[1][1]+oZ*cMo[1][2] + cMo[1][3]; - double cZ = oX*cMo[2][0]+oY*cMo[2][1]+oZ*cMo[2][2] + cMo[2][3]; + double cX = oX*cMo_est[0][0]+oY*cMo_est[0][1]+oZ*cMo_est[0][2] + cMo_est[0][3]; + double cY = oX*cMo_est[1][0]+oY*cMo_est[1][1]+oZ*cMo_est[1][2] + cMo_est[1][3]; + double cZ = oX*cMo_est[2][0]+oY*cMo_est[2][1]+oZ*cMo_est[2][2] + cMo_est[2][3]; double x = cX/cZ ; double y = cY/cZ ; @@ -312,23 +324,23 @@ double double xp = u0 + x*px*r2ud; double yp = v0 + y*py*r2ud; - residual += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; + residual_ += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; double r2du = (vpMath::sqr((u-u0)*inv_px)+vpMath::sqr((v-v0)*inv_py)) ; xp = u0 + x*px - kdu*(u-u0)*r2du; yp = v0 + y*py - kdu*(v-v0)*r2du; - residual += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; + residual_ += (vpMath::sqr(xp-u) + vpMath::sqr(yp-v)) ; ++it_LoX; ++it_LoY; ++it_LoZ; ++it_Lip; } - residual /=2; + residual_ /=2; - this->residual_dist = residual; - return sqrt(residual/npt) ; + this->residual_dist = residual_; + return sqrt(residual_/npt) ; } /*! @@ -349,26 +361,26 @@ void Compute the calibration according to the desired method using one pose. \param method : Method that will be used to estimate the parameters. - \param cMo : the homogeneous matrix that defines the pose. - \param cam : intrinsic camera parameters. + \param cMo_est : estimated homogeneous matrix that defines the pose. + \param cam_est : estimated intrinsic camera parameters. \param verbose : set at true if information about the residual at each loop of the algorithm is hoped. \return 0 if the calibration computation succeed. */ int vpCalibration::computeCalibration(vpCalibrationMethodType method, - vpHomogeneousMatrix &cMo, - vpCameraParameters &cam, + vpHomogeneousMatrix &cMo_est, + vpCameraParameters &cam_est, bool verbose) { try{ - computePose(cam,cMo); + computePose(cam_est,cMo_est); switch (method) { case CALIB_LAGRANGE : case CALIB_LAGRANGE_VIRTUAL_VS : { - calibLagrange(cam, cMo); + calibLagrange(cam_est, cMo_est); } break; case CALIB_VIRTUAL_VS: @@ -386,23 +398,23 @@ int vpCalibration::computeCalibration(vpCalibrationMethodType method, case CALIB_LAGRANGE_VIRTUAL_VS_DIST: { if (verbose){std::cout << "start calibration without distortion"<< std::endl;} - calibVVS(cam, cMo, verbose); + calibVVS(cam_est, cMo_est, verbose); } break ; case CALIB_LAGRANGE: default: break; } - this->cMo = cMo; - this->cMo_dist = cMo; + this->cMo = cMo_est; + this->cMo_dist = cMo_est; //Print camera parameters if(verbose){ // std::cout << "Camera parameters without distortion :" << std::endl; - cam.printParameters(); + cam_est.printParameters(); } - this->cam = cam; + this->cam = cam_est; switch (method) { @@ -410,7 +422,7 @@ int vpCalibration::computeCalibration(vpCalibrationMethodType method, case CALIB_LAGRANGE_VIRTUAL_VS_DIST: { if (verbose){std::cout << "start calibration with distortion"<< std::endl;} - calibVVSWithDistortion(cam, cMo, verbose); + calibVVSWithDistortion(cam_est, cMo_est, verbose); } break ; case CALIB_LAGRANGE: @@ -424,12 +436,12 @@ int vpCalibration::computeCalibration(vpCalibrationMethodType method, // std::cout << "Camera parameters without distortion :" << std::endl; this->cam.printParameters(); // std::cout << "Camera parameters with distortion :" << std::endl; - cam.printParameters(); + cam_est.printParameters(); } - this->cam_dist = cam ; + this->cam_dist = cam_est ; - this->cMo_dist = cMo; + this->cMo_dist = cMo_est; return 0 ; } catch(...){ @@ -442,7 +454,7 @@ int vpCalibration::computeCalibration(vpCalibrationMethodType method, \param method : Method used to estimate the camera parameters. \param table_cal : Vector of vpCalibration. - \param cam : Estimated intrinsic camera parameters. + \param cam_est : Estimated intrinsic camera parameters. \param globalReprojectionError : Global reprojection error or global residual. \param verbose : Set at true if information about the residual at each loop of the algorithm is hoped. @@ -451,7 +463,7 @@ int vpCalibration::computeCalibration(vpCalibrationMethodType method, */ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, std::vector<vpCalibration> &table_cal, - vpCameraParameters& cam, + vpCameraParameters& cam_est, double &globalReprojectionError, bool verbose) { @@ -459,47 +471,50 @@ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, unsigned int nbPose = (unsigned int) table_cal.size(); for(unsigned int i=0;i<nbPose;i++){ if(table_cal[i].get_npt()>3) - table_cal[i].computePose(cam,table_cal[i].cMo); + table_cal[i].computePose(cam_est,table_cal[i].cMo); } - switch (method) { - case CALIB_LAGRANGE : + switch (method) { + case CALIB_LAGRANGE : { if(nbPose > 1){ std::cout << "this calibration method is not available in" << std::endl - << "vpCalibration::computeCalibrationMulti()" << std::endl; + << "vpCalibration::computeCalibrationMulti()" << std::endl; return -1 ; } else { - table_cal[0].calibLagrange(cam,table_cal[0].cMo); - table_cal[0].cam = cam ; - table_cal[0].cam_dist = cam ; + table_cal[0].calibLagrange(cam_est,table_cal[0].cMo); + table_cal[0].cam = cam_est ; + table_cal[0].cam_dist = cam_est ; table_cal[0].cMo_dist = table_cal[0].cMo ; } break; + } case CALIB_LAGRANGE_VIRTUAL_VS : - case CALIB_LAGRANGE_VIRTUAL_VS_DIST : + case CALIB_LAGRANGE_VIRTUAL_VS_DIST : { if(nbPose > 1){ std::cout << "this calibration method is not available in" << std::endl - << "vpCalibration::computeCalibrationMulti()" << std::endl - << "with several images." << std::endl; + << "vpCalibration::computeCalibrationMulti()" << std::endl + << "with several images." << std::endl; return -1 ; } else { - table_cal[0].calibLagrange(cam,table_cal[0].cMo); - table_cal[0].cam = cam ; - table_cal[0].cam_dist = cam ; + table_cal[0].calibLagrange(cam_est,table_cal[0].cMo); + table_cal[0].cam = cam_est ; + table_cal[0].cam_dist = cam_est ; table_cal[0].cMo_dist = table_cal[0].cMo ; } + calibVVSMulti(table_cal, cam_est, globalReprojectionError, verbose); + break ; + } case CALIB_VIRTUAL_VS: - case CALIB_VIRTUAL_VS_DIST: - { - calibVVSMulti(table_cal, cam, globalReprojectionError, verbose); - } + case CALIB_VIRTUAL_VS_DIST: { + calibVVSMulti(table_cal, cam_est, globalReprojectionError, verbose); break ; } + } //Print camera parameters if(verbose){ // std::cout << "Camera parameters without distortion :" << std::endl; - cam.printParameters(); + cam_est.printParameters(); } switch (method) @@ -515,7 +530,7 @@ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, if(verbose) std::cout << "Compute camera parameters with distortion"<<std::endl; - calibVVSWithDistortionMulti(table_cal, cam, globalReprojectionError, verbose); + calibVVSWithDistortionMulti(table_cal, cam_est, globalReprojectionError, verbose); } break ; } @@ -524,7 +539,7 @@ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, // std::cout << "Camera parameters without distortion :" << std::endl; table_cal[0].cam.printParameters(); // std::cout << "Camera parameters with distortion:" << std::endl; - cam.printParameters(); + cam_est.printParameters(); std::cout<<std::endl; } return 0 ; @@ -635,13 +650,17 @@ int vpCalibration::readData(const char* filename) vpImagePoint ip; std::ifstream f; f.open(filename); - if (f != NULL){ - int n ; + if (! f.fail()){ + unsigned int n ; f >> n ; std::cout << "There are "<< n <<" point on the calibration grid " << std::endl ; clearPoint() ; - for (int i=0 ; i < n ; i++) + + if (n > 100000) + throw(vpException(vpException::badValue, "Bad number of point in the calibration grid")); + + for (unsigned int i=0 ; i < n ; i++) { double x, y, z, u, v ; f >> x >> y >> z >> u >> v ; @@ -680,7 +699,7 @@ int vpCalibration::readGrid(const char* filename, unsigned int &n, try{ std::ifstream f; f.open(filename); - if (f != NULL){ + if (! f.fail()){ f >> n ; if(verbose) @@ -693,6 +712,9 @@ int vpCalibration::readGrid(const char* filename, unsigned int &n, oY.clear(); oZ.clear(); + if (n > 100000) + throw(vpException(vpException::badValue, "Bad number of point in the calibration grid")); + for (unsigned int i=0 ; i < n ; i++) { f >> no_pt >> x >> y >> z ; @@ -720,14 +742,22 @@ int vpCalibration::readGrid(const char* filename, unsigned int &n, \param I : Image where to display data. \param color : Color of the data. \param thickness : Thickness of the displayed data. + \param subsampling_factor : Subsampling factor. Default value is 1. + Admissible values are multiple of 2. Divide by this parameter the + coordinates of the data points resulting from image processing. */ int vpCalibration::displayData(vpImage<unsigned char> &I, vpColor color, - unsigned int thickness) + unsigned int thickness, int subsampling_factor) { for (std::list<vpImagePoint>::const_iterator it = Lip.begin(); it != Lip.end(); ++ it) { - vpDisplay::displayCross(I, *it, 12, color, thickness) ; + vpImagePoint ip = *it; + if (subsampling_factor > 1.) { + ip.set_u( ip.get_u() / subsampling_factor); + ip.set_v( ip.get_v() / subsampling_factor); + } + vpDisplay::displayCross(I, ip, 12, color, thickness) ; } return 0 ; } @@ -738,14 +768,17 @@ int vpCalibration::displayData(vpImage<unsigned char> &I, vpColor color, \param I : Image where to display grid data. \param color : Color of the data. \param thickness : Thickness of the displayed data. + \param subsampling_factor : Subsampling factor. Default value is 1. + Admissible values are multiple of 2. Divide by this parameter the + values of the camera parameters. */ int vpCalibration::displayGrid(vpImage<unsigned char> &I, vpColor color, - unsigned int thickness) + unsigned int thickness, int subsampling_factor) { - double u0_dist = cam_dist.get_u0() ; - double v0_dist = cam_dist.get_v0() ; - double px_dist = cam_dist.get_px() ; - double py_dist = cam_dist.get_py() ; + double u0_dist = cam_dist.get_u0() / subsampling_factor; + double v0_dist = cam_dist.get_v0() / subsampling_factor; + double px_dist = cam_dist.get_px() / subsampling_factor; + double py_dist = cam_dist.get_py() / subsampling_factor; double kud_dist = cam_dist.get_kud() ; // double kdu_dist = cam_dist.get_kdu() ; @@ -760,7 +793,6 @@ int vpCalibration::displayGrid(vpImage<unsigned char> &I, vpColor color, for (unsigned int i =0 ; i < npt ; i++) { - double oX = *it_LoX; double oY = *it_LoY; double oZ = *it_LoZ; @@ -814,7 +846,7 @@ int vpCalibration::displayGrid(vpImage<unsigned char> &I, vpColor color, \param method : Method used to estimate the camera parameters. \param nbPose : number of images used to compute multi-images calibration \param table_cal : array of vpCalibration. - \param cam : intrinsic camera parameters. + \param cam_est : estimated intrinsic camera parameters. \param verbose : set at true if information about the residual at each loop of the algorithm is hoped. @@ -823,53 +855,56 @@ int vpCalibration::displayGrid(vpImage<unsigned char> &I, vpColor color, int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, unsigned int nbPose, vpCalibration table_cal[], - vpCameraParameters& cam, + vpCameraParameters& cam_est, bool verbose) { try{ for(unsigned int i=0;i<nbPose;i++){ if(table_cal[i].get_npt()>3) - table_cal[i].computePose(cam,table_cal[i].cMo); + table_cal[i].computePose(cam_est,table_cal[i].cMo); } switch (method) { - case CALIB_LAGRANGE : + case CALIB_LAGRANGE : { if(nbPose > 1){ std::cout << "this calibration method is not available in" << std::endl - << "vpCalibration::computeCalibrationMulti()" << std::endl; + << "vpCalibration::computeCalibrationMulti()" << std::endl; return -1 ; } else { - table_cal[0].calibLagrange(cam,table_cal[0].cMo); - table_cal[0].cam = cam ; - table_cal[0].cam_dist = cam ; + table_cal[0].calibLagrange(cam_est,table_cal[0].cMo); + table_cal[0].cam = cam_est ; + table_cal[0].cam_dist = cam_est ; table_cal[0].cMo_dist = table_cal[0].cMo ; } break; + } case CALIB_LAGRANGE_VIRTUAL_VS : - case CALIB_LAGRANGE_VIRTUAL_VS_DIST : + case CALIB_LAGRANGE_VIRTUAL_VS_DIST : { if(nbPose > 1){ std::cout << "this calibration method is not available in" << std::endl - << "vpCalibration::computeCalibrationMulti()" << std::endl - << "with several images." << std::endl; + << "vpCalibration::computeCalibrationMulti()" << std::endl + << "with several images." << std::endl; return -1 ; } else { - table_cal[0].calibLagrange(cam,table_cal[0].cMo); - table_cal[0].cam = cam ; - table_cal[0].cam_dist = cam ; + table_cal[0].calibLagrange(cam_est,table_cal[0].cMo); + table_cal[0].cam = cam_est ; + table_cal[0].cam_dist = cam_est ; table_cal[0].cMo_dist = table_cal[0].cMo ; } + calibVVSMulti(nbPose, table_cal, cam_est, verbose); + break ; + } case CALIB_VIRTUAL_VS: - case CALIB_VIRTUAL_VS_DIST: - { - calibVVSMulti(nbPose, table_cal, cam, verbose); - } + case CALIB_VIRTUAL_VS_DIST: { + calibVVSMulti(nbPose, table_cal, cam_est, verbose); break ; } + } //Print camera parameters if(verbose){ // std::cout << "Camera parameters without distortion :" << std::endl; - cam.printParameters(); + cam_est.printParameters(); } switch (method) @@ -885,7 +920,7 @@ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, if(verbose) std::cout << "Compute camera parameters with distortion"<<std::endl; - calibVVSWithDistortionMulti(nbPose, table_cal, cam, verbose); + calibVVSWithDistortionMulti(nbPose, table_cal, cam_est, verbose); } break ; } @@ -894,7 +929,7 @@ int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, // std::cout << "Camera parameters without distortion :" << std::endl; table_cal[0].cam.printParameters(); // std::cout << "Camera parameters with distortion:" << std::endl; - cam.printParameters(); + cam_est.printParameters(); std::cout<<std::endl; } return 0 ; @@ -981,7 +1016,7 @@ int vpCalibration::readGrid(const char* filename,unsigned int &n, try{ std::ifstream f; f.open(filename); - if (f != NULL){ + if (! f.fail()){ f >> n ; if(verbose) @@ -992,6 +1027,10 @@ int vpCalibration::readGrid(const char* filename,unsigned int &n, oX.front(); oY.front(); oZ.front(); + + if (n > 100000) + throw(vpException(vpException::badValue, "Bad number of point in the calibration grid")); + for (unsigned int i=0 ; i < n ; i++) { f >> no_pt >> x >> y >> z ; diff --git a/src/camera/calibration/vpCalibration.h b/src/camera/calibration/vpCalibration.h index a75150ccf3168b0bcb83d77fe00dd1a964e254c9..7be48a69aa05681718b4b8e93be73e7a476e7b4a 100644 --- a/src/camera/calibration/vpCalibration.h +++ b/src/camera/calibration/vpCalibration.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCalibration.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpCalibration.h 4921 2014-10-09 08:19:29Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -105,36 +105,30 @@ public: vpHomogeneousMatrix eMc_dist; public: - int init() ; - - //! Suppress all the point in the array of point - int clearPoint() ; - // Add a new point in this array - int addPoint(double X, double Y, double Z, vpImagePoint &ip) ; - - //! Constructor + // Constructor vpCalibration() ; + vpCalibration(const vpCalibration& c) ; - //! Destructor + // Destructor virtual ~vpCalibration() ; - //! = operator - void operator=(vpCalibration& twinCalibration ); - //!get the residual in pixels - double getResidual(void) const {return residual;} - //!get the residual for perspective projection with distortion (in pixels) - double getResidual_dist(void) const {return residual_dist;} - //!get the number of points - unsigned int get_npt() const {return npt;} + // Add a new point in this array + int addPoint(double X, double Y, double Z, vpImagePoint &ip) ; + + // = operator + vpCalibration& operator=(const vpCalibration& twinCalibration); static void calibrationTsai(std::vector<vpHomogeneousMatrix> &cMo, std::vector<vpHomogeneousMatrix> &rMe, vpHomogeneousMatrix &eMc); + //! Suppress all the point in the array of point + int clearPoint() ; + void computeStdDeviation(double &deviation, double &deviation_dist); int computeCalibration(vpCalibrationMethodType method, - vpHomogeneousMatrix &cMo, - vpCameraParameters &cam, + vpHomogeneousMatrix &cMo_est, + vpCameraParameters &cam_est, bool verbose = false) ; static int computeCalibrationMulti(vpCalibrationMethodType method, std::vector<vpCalibration> &table_cal, @@ -145,17 +139,27 @@ public: static int computeCalibrationTsai(std::vector<vpCalibration> &table_cal, vpHomogeneousMatrix &eMc, vpHomogeneousMatrix &eMc_dist); - double computeStdDeviation(vpHomogeneousMatrix &cMo, - vpCameraParameters &cam); - double computeStdDeviation_dist(vpHomogeneousMatrix &cMo, - vpCameraParameters &cam); + double computeStdDeviation(const vpHomogeneousMatrix &cMo_est, + const vpCameraParameters &camera); + double computeStdDeviation_dist(const vpHomogeneousMatrix &cMo, + const vpCameraParameters &cam); int displayData(vpImage<unsigned char> &I, vpColor color=vpColor::red, - unsigned int thickness=1) ; + unsigned int thickness=1, int subsampling_factor=1) ; int displayGrid(vpImage<unsigned char> &I, vpColor color=vpColor::yellow, - unsigned int thickness=1) ; + unsigned int thickness=1, int subsampling_factor=1) ; - //!set the gain for the virtual visual servoing algorithm + //! Set the gain for the virtual visual servoing algorithm. static double getLambda(){return gain;} + + //!get the residual in pixels + double getResidual(void) const {return residual;} + //!get the residual for perspective projection with distortion (in pixels) + double getResidual_dist(void) const {return residual_dist;} + //!get the number of points + unsigned int get_npt() const {return npt;} + + int init() ; + int readData(const char *filename) ; static int readGrid(const char *filename,unsigned int &n, std::list<double> &oX, std::list<double> &oY, std::list<double> &oZ, diff --git a/src/camera/calibration/vpCalibrationException.h b/src/camera/calibration/vpCalibrationException.h index 4e83a6080701f4e5d42c806e27620f935fcfa190..17724202e736513e67d2a54bad563545e24ed56d 100644 --- a/src/camera/calibration/vpCalibrationException.h +++ b/src/camera/calibration/vpCalibrationException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCalibrationException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCalibrationException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,12 +70,12 @@ */ class VISP_EXPORT vpCalibrationException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpCalibration member */ - enum errorCodeEnum + enum errorCodeEnum { //! error returns by a constructor constructionError, @@ -90,25 +90,19 @@ public: forbiddenOperatorError, } ; -public: - vpCalibrationException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpCalibrationException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpCalibrationException (const int code) - : vpException(code){ ; } - // vpMatrixException() : vpException() { ;} + public: + vpCalibrationException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpCalibrationException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpCalibrationException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpCalibrationException_ERROR_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/camera/calibration/vpCalibrationTools.cpp b/src/camera/calibration/vpCalibrationTools.cpp index 54a7c0e21d53fb2218df65342ba94a48b308516b..37230606e1e6fe817a4d450286a49face4589343 100644 --- a/src/camera/calibration/vpCalibrationTools.cpp +++ b/src/camera/calibration/vpCalibrationTools.cpp @@ -10,8 +10,7 @@ #undef MIN void -vpCalibration::calibLagrange( - vpCameraParameters &cam, vpHomogeneousMatrix &cMo) +vpCalibration::calibLagrange(vpCameraParameters &cam_est, vpHomogeneousMatrix &cMo_est) { vpMatrix A(2*npt,3) ; @@ -141,7 +140,7 @@ vpCalibration::calibLagrange( resul[3] = sqrt(sol[6]*sol[6]+sol[7]*sol[7]+sol[8]*sol[8] /* py */ -resul[1]*resul[1]); - cam.initPersProjWithoutDistortion(resul[2],resul[3],resul[0],resul[1]); + cam_est.initPersProjWithoutDistortion(resul[2],resul[3],resul[0],resul[1]); resul[4] = (sol[9]-sol[11]*resul[0])/resul[2]; /* X0 */ resul[5] = (sol[10]-sol[11]*resul[1])/resul[3]; /* Y0 */ @@ -159,12 +158,12 @@ vpCalibration::calibLagrange( for (unsigned int i=0 ; i < 3 ; i++) { for (unsigned int j=0 ; j < 3 ; j++) - cMo[i][j] = rd[i][j]; + cMo_est[i][j] = rd[i][j]; } - for (unsigned int i=0 ; i < 3 ; i++) cMo[i][3] = resul[i+4] ; + for (unsigned int i=0 ; i < 3 ; i++) cMo_est[i][3] = resul[i+4] ; - this->cMo = cMo ; - this->cMo_dist = cMo; + this->cMo = cMo_est ; + this->cMo_dist = cMo_est; double deviation,deviation_dist; this->computeStdDeviation(deviation,deviation_dist); @@ -173,11 +172,11 @@ vpCalibration::calibLagrange( void -vpCalibration::calibVVS( - vpCameraParameters &cam, - vpHomogeneousMatrix &cMo, - bool verbose) +vpCalibration::calibVVS(vpCameraParameters &cam_est, + vpHomogeneousMatrix &cMo_est, + bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int n_points = npt ; @@ -199,7 +198,6 @@ vpCalibration::calibVVS( for (unsigned int i =0 ; i < n_points ; i++) { - oX[i] = *it_LoX; oY[i] = *it_LoY; oZ[i] = *it_LoZ; @@ -209,7 +207,6 @@ vpCalibration::calibVVS( u[i] = ip.get_u() ; v[i] = ip.get_v() ; - ++it_LoX; ++it_LoY; ++it_LoZ; @@ -223,22 +220,21 @@ vpCalibration::calibVVS( double r =1e12-1; while (vpMath::equal(residu_1,r,threshold) == false && iter < nbIterMax) { - iter++ ; residu_1 = r ; - double px = cam.get_px(); - double py = cam.get_py(); - double u0 = cam.get_u0(); - double v0 = cam.get_v0(); + double px = cam_est.get_px(); + double py = cam_est.get_py(); + double u0 = cam_est.get_u0(); + double v0 = cam_est.get_v0(); r = 0 ; for (unsigned int i=0 ; i < n_points; i++) { - cX[i] = oX[i]*cMo[0][0]+oY[i]*cMo[0][1]+oZ[i]*cMo[0][2] + cMo[0][3]; - cY[i] = oX[i]*cMo[1][0]+oY[i]*cMo[1][1]+oZ[i]*cMo[1][2] + cMo[1][3]; - cZ[i] = oX[i]*cMo[2][0]+oY[i]*cMo[2][1]+oZ[i]*cMo[2][2] + cMo[2][3]; + cX[i] = oX[i]*cMo_est[0][0]+oY[i]*cMo_est[0][1]+oZ[i]*cMo_est[0][2] + cMo_est[0][3]; + cY[i] = oX[i]*cMo_est[1][0]+oY[i]*cMo_est[1][1]+oZ[i]*cMo_est[1][2] + cMo_est[1][3]; + cZ[i] = oX[i]*cMo_est[2][0]+oY[i]*cMo_est[2][1]+oZ[i]*cMo_est[2][2] + cMo_est[2][3]; Pd[2*i] = u[i] ; Pd[2*i+1] = v[i] ; @@ -249,7 +245,6 @@ vpCalibration::calibVVS( r += ((vpMath::sqr(P[2*i]-Pd[2*i]) + vpMath::sqr(P[2*i+1]-Pd[2*i+1]))) ; } - vpColVector error ; error = P-Pd ; //r = r/n_points ; @@ -257,7 +252,6 @@ vpCalibration::calibVVS( vpMatrix L(n_points*2,10) ; for (unsigned int i=0 ; i < n_points; i++) { - double x = cX[i] ; double y = cY[i] ; double z = cZ[i] ; @@ -267,7 +261,6 @@ vpCalibration::calibVVS( double Y = y*inv_z ; //--------------- - { L[2*i][0] = px * (-inv_z) ; L[2*i][1] = 0 ; @@ -296,8 +289,6 @@ vpCalibration::calibVVS( L[2*i+1][8]= 0; L[2*i+1][9]= Y ; } - - } // end interaction vpMatrix Lp ; Lp = L.pseudoInverse(1e-10) ; @@ -312,10 +303,10 @@ vpCalibration::calibVVS( for (unsigned int i=0 ; i <6 ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithoutDistortion(px+Tc[8],py+Tc[9], + cam_est.initPersProjWithoutDistortion(px+Tc[8],py+Tc[9], u0+Tc[6],v0+Tc[7]) ; - cMo = vpExponentialMap::direct(Tc_v).inverse()*cMo ; + cMo_est = vpExponentialMap::direct(Tc_v).inverse()*cMo_est ; if (verbose) std::cout << " std dev " << sqrt(r/n_points) << std::endl; @@ -326,21 +317,23 @@ vpCalibration::calibVVS( throw(vpCalibrationException(vpCalibrationException::convergencyError, "Maximum number of iterations reached")) ; } - this->cMo = cMo; - this->cMo_dist = cMo; + this->cMo = cMo_est; + this->cMo_dist = cMo_est; this->residual = r; this->residual_dist = r; if (verbose) std::cout << " std dev " << sqrt(r/n_points) << std::endl; - + // Restore ostream format + std::cout.flags(original_flags); } void vpCalibration::calibVVSMulti(std::vector<vpCalibration> &table_cal, - vpCameraParameters &cam, + vpCameraParameters &cam_est, double &globalReprojectionError, bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int nbPoint[256]; //number of points by image unsigned int nbPointTotal = 0; //total number of points @@ -353,6 +346,12 @@ vpCalibration::calibVVSMulti(std::vector<vpCalibration> &table_cal, nbPointTotal += nbPoint[i]; } + if (nbPointTotal < 4) { + //vpERROR_TRACE("Not enough point to calibrate"); + throw(vpCalibrationException(vpCalibrationException::notInitializedError, + "Not enough point to calibrate")) ; + } + vpColVector oX(nbPointTotal), cX(nbPointTotal) ; vpColVector oY(nbPointTotal), cY(nbPointTotal) ; vpColVector oZ(nbPointTotal), cZ(nbPointTotal) ; @@ -400,10 +399,10 @@ vpCalibration::calibVVSMulti(std::vector<vpCalibration> &table_cal, iter++ ; residu_1 = r ; - double px = cam.get_px(); - double py = cam.get_py(); - double u0 = cam.get_u0(); - double v0 = cam.get_v0(); + double px = cam_est.get_px(); + double py = cam_est.get_py(); + double u0 = cam_est.get_u0(); + double v0 = cam_est.get_v0(); r = 0 ; curPoint = 0 ; //current point indice @@ -504,7 +503,7 @@ vpCalibration::calibVVSMulti(std::vector<vpCalibration> &table_cal, for (unsigned int i = 0 ; i < nbPose6 ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithoutDistortion(px+Tc[nbPose6+2], + cam_est.initPersProjWithoutDistortion(px+Tc[nbPose6+2], py+Tc[nbPose6+3], u0+Tc[nbPose6], v0+Tc[nbPose6+1]) ; @@ -534,20 +533,22 @@ vpCalibration::calibVVSMulti(std::vector<vpCalibration> &table_cal, for (unsigned int p = 0 ; p < nbPose ; p++) { table_cal[p].cMo_dist = table_cal[p].cMo ; - table_cal[p].cam = cam; - table_cal[p].cam_dist = cam; + table_cal[p].cam = cam_est; + table_cal[p].cam_dist = cam_est; double deviation,deviation_dist; table_cal[p].computeStdDeviation(deviation,deviation_dist); } globalReprojectionError = sqrt(r/nbPointTotal); + // Restore ostream format + std::cout.flags(original_flags); } void -vpCalibration::calibVVSWithDistortion( - vpCameraParameters& cam, - vpHomogeneousMatrix& cMo, - bool verbose) +vpCalibration::calibVVSWithDistortion(vpCameraParameters& cam_est, + vpHomogeneousMatrix& cMo_est, + bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int n_points =npt ; @@ -590,23 +591,21 @@ vpCalibration::calibVVSWithDistortion( double r =1e12-1; while (vpMath::equal(residu_1,r,threshold) == false && iter < nbIterMax) { - iter++ ; residu_1 = r ; - r = 0 ; - double u0 = cam.get_u0() ; - double v0 = cam.get_v0() ; + double u0 = cam_est.get_u0() ; + double v0 = cam_est.get_v0() ; - double px = cam.get_px() ; - double py = cam.get_py() ; + double px = cam_est.get_px() ; + double py = cam_est.get_py() ; double inv_px = 1/px ; double inv_py = 1/py ; - double kud = cam.get_kud() ; - double kdu = cam.get_kdu() ; + double kud = cam_est.get_kud() ; + double kdu = cam_est.get_kdu() ; double k2ud = 2*kud; double k2du = 2*kdu; @@ -619,9 +618,9 @@ vpCalibration::calibVVSWithDistortion( unsigned int i42 = 4*i+2; unsigned int i43 = 4*i+3; - cX[i] = oX[i]*cMo[0][0]+oY[i]*cMo[0][1]+oZ[i]*cMo[0][2] + cMo[0][3]; - cY[i] = oX[i]*cMo[1][0]+oY[i]*cMo[1][1]+oZ[i]*cMo[1][2] + cMo[1][3]; - cZ[i] = oX[i]*cMo[2][0]+oY[i]*cMo[2][1]+oZ[i]*cMo[2][2] + cMo[2][3]; + cX[i] = oX[i]*cMo_est[0][0]+oY[i]*cMo_est[0][1]+oZ[i]*cMo_est[0][2] + cMo_est[0][3]; + cY[i] = oX[i]*cMo_est[1][0]+oY[i]*cMo_est[1][1]+oZ[i]*cMo_est[1][2] + cMo_est[1][3]; + cZ[i] = oX[i]*cMo_est[2][0]+oY[i]*cMo_est[2][1]+oZ[i]*cMo_est[2][2] + cMo_est[2][3]; double x = cX[i] ; double y = cY[i] ; @@ -670,7 +669,6 @@ vpCalibration::calibVVSWithDistortion( P[i42] = u0 + px*X*kr2ud ; P[i43] = v0 + py*Y*kr2ud ; - r += (vpMath::sqr(P[i4]-Pd[i4]) + vpMath::sqr(P[i41]-Pd[i41]) + vpMath::sqr(P[i42]-Pd[i42]) + @@ -762,12 +760,12 @@ vpCalibration::calibVVSWithDistortion( for (unsigned int i=0 ; i <6 ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithDistortion(px + Tc[8], py + Tc[9], + cam_est.initPersProjWithDistortion(px + Tc[8], py + Tc[9], u0 + Tc[6], v0 + Tc[7], kud + Tc[11], kdu + Tc[10]); - cMo = vpExponentialMap::direct(Tc_v).inverse()*cMo ; + cMo_est = vpExponentialMap::direct(Tc_v).inverse()*cMo_est ; if (verbose) std::cout << " std dev " << sqrt(r/n_points) << std::endl; @@ -779,19 +777,23 @@ vpCalibration::calibVVSWithDistortion( "Maximum number of iterations reached")) ; } this->residual_dist = r; - this->cMo_dist = cMo ; - this->cam_dist = cam ; + this->cMo_dist = cMo_est ; + this->cam_dist = cam_est ; if (verbose) std::cout << " std dev " << sqrt(r/n_points) << std::endl; + + // Restore ostream format + std::cout.flags(original_flags); } void vpCalibration::calibVVSWithDistortionMulti(std::vector<vpCalibration> &table_cal, - vpCameraParameters &cam, double &globalReprojectionError, + vpCameraParameters &cam_est, double &globalReprojectionError, bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int nbPoint[1024]; //number of points by image unsigned int nbPointTotal = 0; //total number of points @@ -803,6 +805,13 @@ vpCalibration::calibVVSWithDistortionMulti(std::vector<vpCalibration> &table_cal nbPointTotal += nbPoint[i]; } + if (nbPointTotal < 4) + { + //vpERROR_TRACE("Not enough point to calibrate"); + throw(vpCalibrationException(vpCalibrationException::notInitializedError, + "Not enough point to calibrate")) ; + } + vpColVector oX(nbPointTotal), cX(nbPointTotal) ; vpColVector oY(nbPointTotal), cY(nbPointTotal) ; vpColVector oZ(nbPointTotal), cZ(nbPointTotal) ; @@ -869,16 +878,16 @@ vpCalibration::calibVVSWithDistortionMulti(std::vector<vpCalibration> &table_cal vpMatrix L(nbPointTotal*4,nbPose6+6) ; curPoint = 0 ; //current point indice - double px = cam.get_px() ; - double py = cam.get_py() ; - double u0 = cam.get_u0() ; - double v0 = cam.get_v0() ; + double px = cam_est.get_px() ; + double py = cam_est.get_py() ; + double u0 = cam_est.get_u0() ; + double v0 = cam_est.get_v0() ; double inv_px = 1/px ; double inv_py = 1/py ; - double kud = cam.get_kud() ; - double kdu = cam.get_kdu() ; + double kud = cam_est.get_kud() ; + double kdu = cam_est.get_kdu() ; double k2ud = 2*kud; double k2du = 2*kdu; @@ -1031,7 +1040,7 @@ vpCalibration::calibVVSWithDistortionMulti(std::vector<vpCalibration> &table_cal for (unsigned int i = 0 ; i < 6*nbPose ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithDistortion( px+Tc[nbPose6+2], py+Tc[nbPose6+3], + cam_est.initPersProjWithDistortion( px+Tc[nbPose6+2], py+Tc[nbPose6+3], u0+Tc[nbPose6], v0+Tc[nbPose6+1], kud + Tc[nbPose6+5], kdu + Tc[nbPose6+4]); @@ -1062,12 +1071,15 @@ vpCalibration::calibVVSWithDistortionMulti(std::vector<vpCalibration> &table_cal int totalPoints = 0; for (unsigned int p = 0 ; p < nbPose ; p++) { - table_cal[p].cam_dist = cam ; - perViewError = table_cal[p].computeStdDeviation_dist(table_cal[p].cMo_dist, cam); + table_cal[p].cam_dist = cam_est ; + perViewError = table_cal[p].computeStdDeviation_dist(table_cal[p].cMo_dist, cam_est); totalError += perViewError*perViewError * table_cal[p].npt; totalPoints += (int)table_cal[p].npt; } globalReprojectionError = sqrt(r/(nbPointTotal)); + + // Restore ostream format + std::cout.flags(original_flags); } #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS @@ -1488,9 +1500,10 @@ void vpCalibration::calibrationTsai(std::vector<vpHomogeneousMatrix>& cMo, void vpCalibration::calibVVSMulti(unsigned int nbPose, vpCalibration table_cal[], - vpCameraParameters &cam, + vpCameraParameters &cam_est, bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int nbPoint[256]; //number of points by image unsigned int nbPointTotal = 0; //total number of points @@ -1503,6 +1516,13 @@ vpCalibration::calibVVSMulti(unsigned int nbPose, nbPointTotal += nbPoint[i]; } + if (nbPointTotal < 4) + { + //vpERROR_TRACE("Not enough point to calibrate"); + throw(vpCalibrationException(vpCalibrationException::notInitializedError, + "Not enough point to calibrate")) ; + } + vpColVector oX(nbPointTotal), cX(nbPointTotal) ; vpColVector oY(nbPointTotal), cY(nbPointTotal) ; vpColVector oZ(nbPointTotal), cZ(nbPointTotal) ; @@ -1550,10 +1570,10 @@ vpCalibration::calibVVSMulti(unsigned int nbPose, iter++ ; residu_1 = r ; - double px = cam.get_px(); - double py = cam.get_py(); - double u0 = cam.get_u0(); - double v0 = cam.get_v0(); + double px = cam_est.get_px(); + double py = cam_est.get_py(); + double u0 = cam_est.get_u0(); + double v0 = cam_est.get_v0(); r = 0 ; curPoint = 0 ; //current point indice @@ -1654,7 +1674,7 @@ vpCalibration::calibVVSMulti(unsigned int nbPose, for (unsigned int i = 0 ; i < nbPose6 ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithoutDistortion(px+Tc[nbPose6+2], + cam_est.initPersProjWithoutDistortion(px+Tc[nbPose6+2], py+Tc[nbPose6+3], u0+Tc[nbPose6], v0+Tc[nbPose6+1]) ; @@ -1683,13 +1703,16 @@ vpCalibration::calibVVSMulti(unsigned int nbPose, for (unsigned int p = 0 ; p < nbPose ; p++) { table_cal[p].cMo_dist = table_cal[p].cMo ; - table_cal[p].cam = cam; - table_cal[p].cam_dist = cam; + table_cal[p].cam = cam_est; + table_cal[p].cam_dist = cam_est; double deviation,deviation_dist; table_cal[p].computeStdDeviation(deviation,deviation_dist); } if (verbose) std::cout << " Global std dev " << sqrt(r/nbPointTotal) << std::endl; + + // Restore ostream format + std::cout.flags(original_flags); } @@ -1697,9 +1720,10 @@ void vpCalibration::calibVVSWithDistortionMulti( unsigned int nbPose, vpCalibration table_cal[], - vpCameraParameters &cam, + vpCameraParameters &cam_est, bool verbose) { + std::ios::fmtflags original_flags( std::cout.flags() ); std::cout.precision(10); unsigned int nbPoint[1024]; //number of points by image unsigned int nbPointTotal = 0; //total number of points @@ -1711,6 +1735,13 @@ vpCalibration::calibVVSWithDistortionMulti( nbPointTotal += nbPoint[i]; } + if (nbPointTotal < 4) + { + //vpERROR_TRACE("Not enough point to calibrate"); + throw(vpCalibrationException(vpCalibrationException::notInitializedError, + "Not enough point to calibrate")) ; + } + vpColVector oX(nbPointTotal), cX(nbPointTotal) ; vpColVector oY(nbPointTotal), cY(nbPointTotal) ; vpColVector oZ(nbPointTotal), cZ(nbPointTotal) ; @@ -1777,16 +1808,16 @@ vpCalibration::calibVVSWithDistortionMulti( vpMatrix L(nbPointTotal*4,nbPose6+6) ; curPoint = 0 ; //current point indice - double px = cam.get_px() ; - double py = cam.get_py() ; - double u0 = cam.get_u0() ; - double v0 = cam.get_v0() ; + double px = cam_est.get_px() ; + double py = cam_est.get_py() ; + double u0 = cam_est.get_u0() ; + double v0 = cam_est.get_v0() ; double inv_px = 1/px ; double inv_py = 1/py ; - double kud = cam.get_kud() ; - double kdu = cam.get_kdu() ; + double kud = cam_est.get_kud() ; + double kdu = cam_est.get_kdu() ; double k2ud = 2*kud; double k2du = 2*kdu; @@ -1939,7 +1970,7 @@ vpCalibration::calibVVSWithDistortionMulti( for (unsigned int i = 0 ; i < 6*nbPose ; i++) Tc_v[i] = Tc[i] ; - cam.initPersProjWithDistortion( px+Tc[nbPose6+2], py+Tc[nbPose6+3], + cam_est.initPersProjWithDistortion( px+Tc[nbPose6+2], py+Tc[nbPose6+3], u0+Tc[nbPose6], v0+Tc[nbPose6+1], kud + Tc[nbPose6+5], kdu + Tc[nbPose6+4]); @@ -1967,10 +1998,12 @@ vpCalibration::calibVVSWithDistortionMulti( for (unsigned int p = 0 ; p < nbPose ; p++) { - table_cal[p].cam_dist = cam ; - table_cal[p].computeStdDeviation_dist(table_cal[p].cMo_dist, - cam); + table_cal[p].cam_dist = cam_est ; + table_cal[p].computeStdDeviation_dist(table_cal[p].cMo_dist, cam_est); } if (verbose) std::cout <<" Global std dev " << sqrt(r/(nbPointTotal)) << std::endl; + + // Restore ostream format + std::cout.flags(original_flags); } diff --git a/src/camera/vpCameraParameters.cpp b/src/camera/vpCameraParameters.cpp index ef7d8c79664276e703c55ad0b6af8d311a464ce3..8900c032656ca6b0c5e3aacc76f4a7f1f88010da 100644 --- a/src/camera/vpCameraParameters.cpp +++ b/src/camera/vpCameraParameters.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCameraParameters.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpCameraParameters.cpp 5215 2015-01-27 19:03:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,6 +54,9 @@ #include <visp/vpRotationMatrix.h> #include <cmath> #include <limits> +#include <iostream> +#include <sstream> +#include <iomanip> const double vpCameraParameters::DEFAULT_PX_PARAMETER = 600.0; const double vpCameraParameters::DEFAULT_PY_PARAMETER = 600.0; @@ -72,12 +75,15 @@ const vpCameraParameters::vpCameraParametersProjType \sa init() */ vpCameraParameters::vpCameraParameters() + : + px(DEFAULT_PX_PARAMETER), py(DEFAULT_PY_PARAMETER), + u0(DEFAULT_U0_PARAMETER), v0(DEFAULT_V0_PARAMETER), + kud(DEFAULT_KUD_PARAMETER), kdu(DEFAULT_KDU_PARAMETER), + width(0), height(0), + isFov(false), m_hFovAngle(0), m_vFovAngle(0), fovNormals(), + inv_px(1./DEFAULT_PX_PARAMETER), inv_py(1./DEFAULT_PY_PARAMETER), + projModel(DEFAULT_PROJ_TYPE) { - isFov = false; - fovAngleX = 0; - fovAngleY = 0; - width = 0; - height = 0; init() ; } @@ -85,6 +91,14 @@ vpCameraParameters::vpCameraParameters() Copy constructor */ vpCameraParameters::vpCameraParameters(const vpCameraParameters &c) + : + px(DEFAULT_PX_PARAMETER), py(DEFAULT_PY_PARAMETER), + u0(DEFAULT_U0_PARAMETER), v0(DEFAULT_V0_PARAMETER), + kud(DEFAULT_KUD_PARAMETER), kdu(DEFAULT_KDU_PARAMETER), + width(0), height(0), + isFov(false), m_hFovAngle(0), m_vFovAngle(0), fovNormals(), + inv_px(1./DEFAULT_PX_PARAMETER), inv_py(1./DEFAULT_PY_PARAMETER), + projModel(DEFAULT_PROJ_TYPE) { init(c) ; } @@ -92,40 +106,46 @@ vpCameraParameters::vpCameraParameters(const vpCameraParameters &c) /*! Constructor for perspective projection without distortion model - \param px,py : pixel size - \param u0,v0 : principal points + \param cam_px,cam_py : pixel size + \param cam_u0,cam_v0 : principal points */ -vpCameraParameters::vpCameraParameters(const double px, const double py, - const double u0, const double v0) +vpCameraParameters::vpCameraParameters(const double cam_px, const double cam_py, + const double cam_u0, const double cam_v0) + : + px(DEFAULT_PX_PARAMETER), py(DEFAULT_PY_PARAMETER), + u0(DEFAULT_U0_PARAMETER), v0(DEFAULT_V0_PARAMETER), + kud(DEFAULT_KUD_PARAMETER), kdu(DEFAULT_KDU_PARAMETER), + width(0), height(0), + isFov(false), m_hFovAngle(0), m_vFovAngle(0), fovNormals(), + inv_px(1./DEFAULT_PX_PARAMETER), inv_py(1./DEFAULT_PY_PARAMETER), + projModel(DEFAULT_PROJ_TYPE) { - isFov = false; - fovAngleX = 0; - fovAngleY = 0; - width = 0; - height = 0; - initPersProjWithoutDistortion(px,py,u0,v0) ; + initPersProjWithoutDistortion(cam_px,cam_py,cam_u0,cam_v0) ; } /*! Constructor for perspective projection with distortion model - \param px,py : pixel size - \param u0,v0 : principal points - \param kud : undistorted to distorted radial distortion - \param kdu : distorted to undistorted radial distortion + \param cam_px,cam_py : pixel size + \param cam_u0,cam_v0 : principal points + \param cam_kud : undistorted to distorted radial distortion + \param cam_kdu : distorted to undistorted radial distortion */ -vpCameraParameters::vpCameraParameters(const double px, const double py, - const double u0, const double v0, - const double kud, const double kdu) +vpCameraParameters::vpCameraParameters(const double cam_px, const double cam_py, + const double cam_u0, const double cam_v0, + const double cam_kud, const double cam_kdu) + : + px(DEFAULT_PX_PARAMETER), py(DEFAULT_PY_PARAMETER), + u0(DEFAULT_U0_PARAMETER), v0(DEFAULT_V0_PARAMETER), + kud(DEFAULT_KUD_PARAMETER), kdu(DEFAULT_KDU_PARAMETER), + width(0), height(0), + isFov(false), m_hFovAngle(0), m_vFovAngle(0), fovNormals(), + inv_px(1./DEFAULT_PX_PARAMETER), inv_py(1./DEFAULT_PY_PARAMETER), + projModel(DEFAULT_PROJ_TYPE) { - isFov = false; - fovAngleX = 0; - fovAngleY = 0; - width = 0; - height = 0; - initPersProjWithDistortion(px,py,u0,v0,kud,kdu) ; + initPersProjWithDistortion(cam_px,cam_py,cam_u0,cam_v0,cam_kud,cam_kdu) ; } /*! @@ -133,16 +153,7 @@ vpCameraParameters::vpCameraParameters(const double px, const double py, */ void vpCameraParameters::init() -{ - this->projModel = DEFAULT_PROJ_TYPE ; - - this->px = DEFAULT_PX_PARAMETER ; - this->py = DEFAULT_PY_PARAMETER ; - this->u0 = DEFAULT_U0_PARAMETER ; - this->v0 = DEFAULT_V0_PARAMETER ; - this->kud = DEFAULT_KUD_PARAMETER ; - this->kdu = DEFAULT_KDU_PARAMETER ; - +{ if (fabs(this->px)<1e-6) { vpERROR_TRACE("Camera parameter px = 0") ; @@ -162,19 +173,49 @@ vpCameraParameters::init() /*! Initialization with specific parameters using perpective projection without distortion model. - \param px,py : pixel size - \param u0,v0 : principal point + \param cam_px,cam_py : the ratio between the focal length and the size of a pixel. + \param cam_u0,cam_v0 : principal point coordinates in pixels. + + The following sample code shows how to use this function: + \code +#include <visp/vpCameraParameters.h> +#include <visp/vpImage.h> + +int main() +{ + vpImage<unsigned char> I(480, 640); + double u0 = I.getWidth() / 2.; + double v0 = I.getHeight() / 2.; + double px = 600; + double py = 600; + vpCameraParameters cam; + cam.initPersProjWithoutDistortion(px, py, u0, v0); + cam.computeFov(I.getWidth(), I.getHeight()); + std::cout << cam << std::endl; + std::cout << "Field of view (horizontal: " << vpMath::deg(cam.getHorizontalFovAngle()) + << " and vertical: " << vpMath::deg(cam.getVerticalFovAngle()) << " degrees)" << std::endl; +} + \endcode + It produces the following output: + \code +Camera parameters for perspective projection without distortion: + px = 600 py = 600 + u0 = 320 v0 = 240 + +Field of view (horizontal: 56.145 and vertical: 43.6028 degrees) + \endcode + */ void -vpCameraParameters::initPersProjWithoutDistortion(const double px, - const double py, const double u0, const double v0) +vpCameraParameters::initPersProjWithoutDistortion(const double cam_px, const double cam_py, + const double cam_u0, const double cam_v0) { this->projModel = vpCameraParameters::perspectiveProjWithoutDistortion ; - this->px = px ; - this->py = py ; - this->u0 = u0 ; - this->v0 = v0 ; + this->px = cam_px ; + this->py = cam_py ; + this->u0 = cam_u0 ; + this->v0 = cam_v0 ; this->kud = 0 ; this->kdu = 0 ; @@ -197,24 +238,56 @@ vpCameraParameters::initPersProjWithoutDistortion(const double px, /*! Initialization with specific parameters using perpective projection with distortion model. - \param px,py : pixel size - \param u0,v0 : principal points - \param kud : undistorted to distorted radial distortion - \param kdu : distorted to undistorted radial distortion + \param cam_px,cam_py : the ratio between the focal length and the size of a pixel. + \param cam_u0,cam_v0 : principal points coordinates in pixels. + \param cam_kud : undistorted to distorted radial distortion. + \param cam_kdu : distorted to undistorted radial distortion. + + The following sample code shows how to use this function: + \code +#include <visp/vpCameraParameters.h> +#include <visp/vpImage.h> + +int main() +{ + vpImage<unsigned char> I(480, 640); + double u0 = I.getWidth() / 2.; + double v0 = I.getHeight() / 2.; + double px = 600; + double py = 600; + double kud = -0.19; + double kdu = 0.20; + vpCameraParameters cam; + cam.initPersProjWithDistortion(px, py, u0, v0, kud, kdu); + cam.computeFov(I.getWidth(), I.getHeight()); + std::cout << cam << std::endl; + std::cout << "Field of view (horizontal: " << vpMath::deg(cam.getHorizontalFovAngle()) + << " and vertical: " << vpMath::deg(cam.getVerticalFovAngle()) << " degrees)" << std::endl; +} + \endcode + It produces the following output: + \code +Camera parameters for perspective projection with distortion: + px = 600 py = 600 + u0 = 320 v0 = 240 + kud = -0.19 + kdu = 0.2 + +Field of view (horizontal: 56.14497387 and vertical: 43.60281897 degrees) \endcode */ void -vpCameraParameters::initPersProjWithDistortion(const double px, const double py, - const double u0, const double v0, - const double kud, const double kdu) +vpCameraParameters::initPersProjWithDistortion(const double cam_px, const double cam_py, + const double cam_u0, const double cam_v0, + const double cam_kud, const double cam_kdu) { this->projModel = vpCameraParameters::perspectiveProjWithDistortion ; - this->px = px ; - this->py = py ; - this->u0 = u0 ; - this->v0 = v0 ; - this->kud = kud ; - this->kdu = kdu ; + this->px = cam_px ; + this->py = cam_py ; + this->u0 = cam_u0 ; + this->v0 = cam_v0 ; + this->kud = cam_kud ; + this->kdu = cam_kdu ; if (fabs(px)<1e-6) { @@ -277,6 +350,54 @@ vpCameraParameters::initFromCalibrationMatrix(const vpMatrix& _K) initPersProjWithoutDistortion (_K[0][0], _K[1][1], _K[0][2], _K[1][2]); } +/*! + Initialize the camera model without distorsion from the image dimension and the camera field of view. + \param w : Image width. + \param h : Image height. + \param hfov : Camera horizontal field of view angle expressed in radians. + \param vfov : Camera vertical field of view angle expressed in radians. + + The following sample code shows how to use this function: + \code +#include <visp/vpCameraParameters.h> +#include <visp/vpImage.h> + +int main() +{ + vpImage<unsigned char> I(480, 640); + vpCameraParameters cam; + double hfov = vpMath::rad(56); + double vfov = vpMath::rad(43); + cam.initFromFov(I.getWidth(), I.getHeight(), hfov, vfov); + + std::cout << cam << std::endl; + std::cout << "Field of view (horizontal: " << vpMath::deg(cam.getHorizontalFovAngle()) + << " and vertical: " << vpMath::deg(cam.getVerticalFovAngle()) << " degrees)" << std::endl; +} + \endcode + It produces the following output: + \code +Camera parameters for perspective projection without distortion: + px = 601.832 py = 609.275 + u0 = 320 v0 = 240 + +Field of view (horizontal: 56 and vertical: 43 degrees) + \endcode + */ +void +vpCameraParameters::initFromFov(const unsigned int &w, const unsigned int &h, const double &hfov, const double &vfov) +{ + projModel = vpCameraParameters::perspectiveProjWithoutDistortion; + u0 = (double)w/2.; + v0 = (double)h/2.; + px = u0 / tan(hfov/2); + py = v0 / tan(vfov/2); + kud = 0; + kdu = 0; + inv_px = 1./px; + inv_py = 1./py; + computeFov(w, h); +} /*! copy operator @@ -296,11 +417,11 @@ vpCameraParameters& inv_py = cam.inv_py; isFov = cam.isFov; - fovAngleX = cam.fovAngleX; - fovAngleY = cam.fovAngleY; - fovNormals = cam.fovNormals; + m_hFovAngle = cam.m_hFovAngle; + m_vFovAngle = cam.m_vFovAngle; width = cam.width; height = cam.height; + fovNormals = cam.fovNormals; return *this ; } @@ -319,8 +440,8 @@ vpCameraParameters::computeFov(const unsigned int &w, const unsigned int &h) isFov = true; - fovAngleX = atan((double)w / ( 2.0 * px )); - fovAngleY = atan((double)h / ( 2.0 * py )); + double half_hFovAngle = atan((double)w / ( 2.0 * px )); + double half_vFovAngle = atan((double)h / ( 2.0 * py )); width = w; height = h; @@ -329,8 +450,8 @@ vpCameraParameters::computeFov(const unsigned int &w, const unsigned int &h) n = 0; n[0] = 1.0; - vpRotationMatrix Rleft(0,-fovAngleX,0); - vpRotationMatrix Rright(0,fovAngleX,0); + vpRotationMatrix Rleft (0,-half_hFovAngle,0); + vpRotationMatrix Rright(0, half_hFovAngle,0); vpColVector nLeft, nRight; @@ -343,8 +464,8 @@ vpCameraParameters::computeFov(const unsigned int &w, const unsigned int &h) n = 0; n[1] = 1.0; - vpRotationMatrix Rup(fovAngleY,0,0); - vpRotationMatrix Rdown(-fovAngleY,0,0); + vpRotationMatrix Rup ( half_vFovAngle,0,0); + vpRotationMatrix Rdown(-half_vFovAngle,0,0); vpColVector nUp, nDown; @@ -353,6 +474,9 @@ vpCameraParameters::computeFov(const unsigned int &w, const unsigned int &h) nDown = Rdown * n; fovNormals[3] = nDown.normalize(); + + m_hFovAngle = 2 * half_hFovAngle; + m_vFovAngle = 2 * half_vFovAngle; } } @@ -447,6 +571,7 @@ vpCameraParameters::get_K_inverse() const void vpCameraParameters::printParameters() { + std::ios::fmtflags original_flags( std::cout.flags() ); switch(projModel){ case vpCameraParameters::perspectiveProjWithoutDistortion : std::cout.precision(10); @@ -465,6 +590,8 @@ vpCameraParameters::printParameters() std::cout << " kdu = " << kdu << std::endl ; break; } + // Restore ostream format + std::cout.flags(original_flags); } /*! @@ -473,37 +600,33 @@ vpCameraParameters::printParameters() \param os : Output stream. \param cam : Camera parameters. */ -std::ostream & operator << (std::ostream & os, - const vpCameraParameters &cam) +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpCameraParameters &cam) { switch(cam.get_projModel()){ - case vpCameraParameters::perspectiveProjWithoutDistortion : - os << "Camera parameters for perspective projection without distortion:" - << std::endl ; - os << " px = " << cam.get_px() <<"\t py = "<< cam.get_py() - << std::endl ; - os << " u0 = " << cam.get_u0() <<"\t v0 = "<< cam.get_v0() - << std::endl ; - break; - case vpCameraParameters::perspectiveProjWithDistortion : - os.precision(10); - os << "Camera parameters for perspective projection with distortion:" - << std::endl ; - os << " px = " << cam.get_px() <<"\t py = "<< cam.get_py() - << std::endl ; - os << " u0 = " << cam.get_u0() <<"\t v0 = "<< cam.get_v0() - << std::endl ; - os << " kud = " << cam.get_kud() << std::endl ; - os << " kdu = " << cam.get_kdu() << std::endl ; - break; - } + case vpCameraParameters::perspectiveProjWithoutDistortion : + os << "Camera parameters for perspective projection without distortion:" + << std::endl ; + os << " px = " << cam.get_px() <<"\t py = "<< cam.get_py() + << std::endl ; + os << " u0 = " << cam.get_u0() <<"\t v0 = "<< cam.get_v0() + << std::endl ; + break; + case vpCameraParameters::perspectiveProjWithDistortion : + std::ios_base::fmtflags original_flags = os.flags(); + os.precision(10); + os << "Camera parameters for perspective projection with distortion:" + << std::endl ; + os << " px = " << cam.get_px() <<"\t py = "<< cam.get_py() + << std::endl ; + os << " u0 = " << cam.get_u0() <<"\t v0 = "<< cam.get_v0() + << std::endl ; + os << " kud = " << cam.get_kud() << std::endl ; + os << " kdu = " << cam.get_kdu() << std::endl ; + + os.flags(original_flags); // restore os to standard state + break; + } return os; } -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ - diff --git a/src/camera/vpCameraParameters.h b/src/camera/vpCameraParameters.h index 3bd73d4528a57421f57720fce5723f34fd8ecfde..28906872e6df97fe6a77affb1d223578d6f30935 100644 --- a/src/camera/vpCameraParameters.h +++ b/src/camera/vpCameraParameters.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCameraParameters.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpCameraParameters.h 5207 2015-01-26 09:05:28Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -90,10 +90,9 @@ \end{array}\right. \f] - The conversion from pixel coordinates \f$(u,v)\f$ in the normalized - space \f$(x,y)\f$ is implemented in vpPixelMeterConversion, whereas - the conversion from normalized coordinates into pixel is implemented - in vpMeterPixelConversion. + The initialization of such a model can be done using: + - initPersProjWithoutDistortion() that allows to set \f$u_0,v_0,p_x,p_y\f$ parameters; + - initFromFov() that computes the parameters from an image size and a camera field of view. <b>2. Camera parameters for a perspective projection with distortion model</b> @@ -152,13 +151,19 @@ y = f_y(u,v) = \frac{v-v_0}{p_y}\left(1+k_{du}\left( {\left(\frac{u-u_0}{p_{x}}\right)}^2 + {\left(\frac{v-v_0}{p_{y}}\right)}^2 \right)\right) \end{array}\right. \f] - The conversion from pixel coordinates \f$(u,v)\f$ in the normalized - space \f$(x,y)\f$ is implemented in vpPixelMeterConversion. + + The initialization of such a model can be done using: + - initPersProjWithDistortion() that allows to set \f$u_0,v_0,p_x,p_y,k_{ud},k_{du}\f$ parameters; + + \note The conversion from pixel coordinates \f$(u,v)\f$ in the normalized + space \f$(x,y)\f$ is implemented in vpPixelMeterConversion, whereas + the conversion from normalized coordinates into pixel is implemented + in vpMeterPixelConversion. The selection of one of these modelisations is done during vpCameraParameters initialisation. - Here an example of camera initialisation, for a model without distortion: + Here an example of camera initialisation, for a model without distortion. A complete example is given in initPersProjWithoutDistortion(). \code double px = 600; double py = 600; @@ -173,7 +178,7 @@ std::cout << cam << std::endl; \endcode - Here an example of camera initialisation, for a model with distortion: + Here an example of camera initialisation, for a model with distortion. A complete example is given in initPersProjWithDistortion(). \code double px = 600; double py = 600; @@ -198,6 +203,9 @@ \endcode An XML parser for camera parameters is also provided in vpXmlParserCamera. + + Note also that the \ref tutorial-calibration shows how to calibrate a camera + to obtain the parameters corresponding to both models implemented in this class. */ class VISP_EXPORT vpCameraParameters { @@ -225,11 +233,10 @@ public : void init() ; void init(const vpCameraParameters &c) ; void initFromCalibrationMatrix(const vpMatrix& _K); - - void initPersProjWithoutDistortion(const double px, const double py, - const double u0, const double v0) ; - void initPersProjWithDistortion(const double px, const double py, - const double u0, const double v0, const double kud,const double kdu) ; + void initFromFov(const unsigned int &w, const unsigned int &h, const double &hfov, const double &vfov); + void initPersProjWithoutDistortion(const double px, const double py, const double u0, const double v0); + void initPersProjWithDistortion(const double px, const double py, const double u0, const double v0, + const double kud,const double kdu) ; /*! Specify if the fov has been computed. @@ -243,27 +250,27 @@ public : void computeFov(const unsigned int &w, const unsigned int &h); /*! - Get the horizontal angle of the field of view. - - \sa computeFov() - - \return AngleX computed with px and width. + Get the horizontal angle in radian of the field of view. + + \return FOV horizontal angle computed with px and width. + + \sa computeFov(), getVerticalFovAngle() */ - inline double getFovAngleX() const { - if(!isFov) vpTRACE("Warning: The FOV is not computed, getFovAngleX() won't be significant."); - return fovAngleX; + inline double getHorizontalFovAngle() const { + if(!isFov) vpTRACE("Warning: The FOV is not computed, getHorizontalFovAngle() won't be significant."); + return m_hFovAngle; } - + /*! - Get the vertical angle of the field of view. - - \sa computeFov() - - \return AngleY computed with py and height. + Get the vertical angle in radian of the field of view. + + \return FOV vertical angle computed with py and height. + + \sa computeFov(), getHorizontalFovAngle() */ - inline double getFovAngleY() const { - if(!isFov) vpTRACE("Warning: The FOV is not computed, getFovAngleY() won't be significant."); - return fovAngleY; + inline double getVerticalFovAngle() const { + if(!isFov) vpTRACE("Warning: The FOV is not computed, getVerticalFovAngle() won't be significant."); + return m_vFovAngle; } /*! @@ -299,8 +306,36 @@ public : vpMatrix get_K_inverse() const; void printParameters() ; - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpCameraParameters &cam); + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpCameraParameters &cam); + +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + /*! + \deprecated This function is deprecated. Use rather getHorizontalFovAngle(). + Get the horizontal angle of the field of view. + + \sa computeFov() + + \return AngleX computed with px and width. + */ + vp_deprecated inline double getFovAngleX() const { + if(!isFov) vpTRACE("Warning: The FOV is not computed, getFovAngleX() won't be significant."); + return m_hFovAngle; + } + + /*! + \deprecated This function is deprecated. Use rather getVerticalFovAngle(). + Get the vertical angle in radian of the field of view. + + \sa computeFov() + + \return FOV vertical angle computed with py and height. + */ + vp_deprecated inline double getFovAngleY() const { + if(!isFov) vpTRACE("Warning: The FOV is not computed, getFovAngleY() won't be significant."); + return m_hFovAngle; + } + +#endif private: static const double DEFAULT_U0_PARAMETER; @@ -320,14 +355,13 @@ private: unsigned int width ; //!< Width of the image used for the fov computation unsigned int height ; //!< Height of the image used for the fov computation bool isFov ; //!< Boolean to specify if the fov has been computed - double fovAngleX ; //!< AngleX/2.0 of the fov - double fovAngleY ; //!< AngleY/2.0 of the fov + double m_hFovAngle ; //!< Field of view horizontal angle + double m_vFovAngle ; //!< Field of view vertical angle std::vector<vpColVector> fovNormals ; //!< Normals of the planes describing the fov double inv_px, inv_py; vpCameraParametersProjType projModel ; //!< used projection model - } ; #endif diff --git a/src/camera/vpMeterPixelConversion.cpp b/src/camera/vpMeterPixelConversion.cpp index 726e2f16fd6d271f98e7a112ac893d2a43947ec2..a7ab8768af542b0d3c637936110cf9ff79e44f66 100644 --- a/src/camera/vpMeterPixelConversion.cpp +++ b/src/camera/vpMeterPixelConversion.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeterPixelConversion.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMeterPixelConversion.cpp 4740 2014-05-26 07:15:44Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,7 +51,7 @@ #include <visp/vpMath.h> #include <visp/vpDebug.h> -//! line coordinates conversion (rho,theta) +//! Line coordinates conversion (rho,theta). void vpMeterPixelConversion::convertLine(const vpCameraParameters &cam, const double &rho_m, const double &theta_m, @@ -71,14 +71,46 @@ vpMeterPixelConversion::convertLine(const vpCameraParameters &cam, theta_p = atan2(cam.px*si, cam.py*co) ; rho_p = (cam.px*cam.py*rho_m + cam.u0*cam.py*co + cam.v0*cam.px*si) ; rho_p /= d ; +} +/*! + Converts an ellipse with parameters expressed in meter in the image + plane in parameters expressed in the image in pixels. -} + The ellipse is here represented by its parameters \f$x_c,y_c,\mu_{20}, \mu_{11}, \mu_{02}\f$. + \param cam [in]: Intrinsic camera parameters. + \param circle [in]: 3D circle with parameters circle.p[] expressed in meters in the image plane. + \param center [out]: Center of the corresponding ellipse in the image with coordinates + expressed in pixels. + \param mu20_p,mu11_p,mu02_p [out]: Centered moments expressed in pixels. -/* - * Local variables: - * c-basic-offset: 2 - * End: + The following code shows how to use this function: + \code + vpCircle circle; + double mu20_p, mu11_p, mu02_p; + circle.changeFrame(cMo); + circle.projection(); + vpMeterPixelConversion::convertEllipse(cam, circle, center_p, mu20_p, mu11_p, mu02_p); + vpDisplay::displayEllipse(I, center_p, mu20_p, mu11_p, mu02_p); + \endcode */ +void +vpMeterPixelConversion::convertEllipse(const vpCameraParameters &cam, + const vpCircle &circle, vpImagePoint ¢er, + double &mu20_p, double &mu11_p, double &mu02_p) +{ + // Get the parameters of the ellipse in the image plane + double xc_m = circle.p[0]; + double yc_m = circle.p[1]; + double mu20_m = circle.p[2]; + double mu11_m = circle.p[3]; + double mu02_m = circle.p[4]; + + // Convert from meter to pixels + vpMeterPixelConversion::convertPoint(cam, xc_m, yc_m, center); + mu20_p = mu20_m*vpMath::sqr(cam.get_px()); + mu11_p = mu11_m*cam.get_px()*cam.get_py(); + mu02_p = mu02_m*vpMath::sqr(cam.get_py()); +} diff --git a/src/camera/vpMeterPixelConversion.h b/src/camera/vpMeterPixelConversion.h index 13a00c41f9a011de9bbebc703d2a06170100b55a..405f7c0e27c648103f9da9c2d20f5bc638b9dbe3 100644 --- a/src/camera/vpMeterPixelConversion.h +++ b/src/camera/vpMeterPixelConversion.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeterPixelConversion.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMeterPixelConversion.h 4726 2014-04-23 11:58:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,9 +52,10 @@ */ #include <visp/vpCameraParameters.h> -#include<visp/vpException.h> -#include<visp/vpMath.h> -#include<visp/vpImagePoint.h> +#include <visp/vpCircle.h> +#include <visp/vpException.h> +#include <visp/vpImagePoint.h> +#include <visp/vpMath.h> /*! \class vpMeterPixelConversion @@ -70,6 +71,13 @@ class VISP_EXPORT vpMeterPixelConversion { public: + static void convertEllipse(const vpCameraParameters &cam, + const vpCircle &circle, vpImagePoint ¢er, + double &mu20_p, double &mu11_p, double &mu02_p); + + static void convertLine(const vpCameraParameters &cam, + const double &rho_m, const double &theta_m, + double &rho_p, double &theta_p) ; /*! @@ -93,10 +101,9 @@ public: */ inline static void - convertPoint( const vpCameraParameters &cam, + convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v) - { switch(cam.projModel){ case vpCameraParameters::perspectiveProjWithoutDistortion : @@ -132,10 +139,9 @@ public: */ inline static void - convertPoint( const vpCameraParameters &cam, - const double &x, const double &y, - vpImagePoint &iP) - + convertPoint(const vpCameraParameters &cam, + const double &x, const double &y, + vpImagePoint &iP) { switch(cam.projModel){ case vpCameraParameters::perspectiveProjWithoutDistortion : @@ -147,6 +153,7 @@ public: } } +#ifndef DOXYGEN_SHOULD_SKIP_THIS /*! \brief Point coordinates conversion without distortion from @@ -158,9 +165,8 @@ public: inline static void convertPointWithoutDistortion(const vpCameraParameters &cam, - const double &x, const double &y, - double &u, double &v) - + const double &x, const double &y, + double &u, double &v) { u = x * cam.px + cam.u0 ; v = y * cam.py + cam.v0 ; @@ -178,14 +184,13 @@ public: inline static void convertPointWithoutDistortion(const vpCameraParameters &cam, - const double &x, const double &y, - vpImagePoint &iP) - + const double &x, const double &y, + vpImagePoint &iP) { iP.set_u( x * cam.px + cam.u0 ); iP.set_v( y * cam.py + cam.v0 ); } - + /*! \brief Point coordinates conversion with distortion from @@ -202,9 +207,8 @@ public: */ inline static void convertPointWithDistortion(const vpCameraParameters &cam, - const double &x, const double &y, - double &u, double &v) - + const double &x, const double &y, + double &u, double &v) { double r2 = 1.+cam.kud*(x*x+y*y); u = cam.u0 + cam.px*x*r2; @@ -228,26 +232,16 @@ public: */ inline static void convertPointWithDistortion(const vpCameraParameters &cam, - const double &x, const double &y, - vpImagePoint &iP) - + const double &x, const double &y, + vpImagePoint &iP) { double r2 = 1.+cam.kud*(x*x+y*y); iP.set_u( cam.u0 + cam.px*x*r2 ); iP.set_v( cam.v0 + cam.py*y*r2 ); } - - //! line coordinates conversion (rho,theta) - static void convertLine(const vpCameraParameters &cam, - const double &rho_m, const double &theta_m, - double &rho_p, double &theta_p) ; +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS } ; #endif -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/camera/vpPixelMeterConversion.cpp b/src/camera/vpPixelMeterConversion.cpp index acf73b82544e9cb40c8975a5cbdbb81ed0449574..d05735bcab5a01370f5461bfbdb3c72d85953e0b 100644 --- a/src/camera/vpPixelMeterConversion.cpp +++ b/src/camera/vpPixelMeterConversion.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPixelMeterConversion.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPixelMeterConversion.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -75,7 +75,7 @@ vpPixelMeterConversion::convertLine(const vpCameraParameters &cam, void vpPixelMeterConversion::convertMoment(const vpCameraParameters &cam, unsigned int order, - const vpMatrix &moment_pixel, + const vpMatrix &moment_pixel, vpMatrix &moment_meter) { @@ -83,13 +83,13 @@ vpPixelMeterConversion::convertMoment(const vpCameraParameters &cam, double yc = -cam.v0 ; double xc = -cam.u0 ; - for (unsigned int k=0; k<order; k++) // itération correspondant à l'ordre du moment + for (unsigned int k=0; k<order; k++) // iteration correspondant e l'ordre du moment { - for (unsigned int p=0 ; p<order; p++) // itération en X - for (unsigned int q=0; q<order; q++) // itération en Y - if (p+q==k) // on est bien dans la matrice triangulaire supérieure + for (unsigned int p=0 ; p<order; p++) // iteration en X + for (unsigned int q=0; q<order; q++) // iteration en Y + if (p+q==k) // on est bien dans la matrice triangulaire superieure { - m[p][q] = 0; // initialisation à 0 + m[p][q] = 0; // initialisation e 0 for(unsigned int r=0; r<=p; r++) // somme externe for(unsigned int t=0; t<=q; t++) // somme interne { @@ -104,7 +104,7 @@ vpPixelMeterConversion::convertMoment(const vpCameraParameters &cam, } - for (unsigned int k=0; k<order; k++) // itération correspondant à l'ordre du moment + for (unsigned int k=0; k<order; k++) // iteration correspondant e l'ordre du moment for (unsigned int p=0 ; p<order; p++) for (unsigned int q=0; q<order; q++) if (p+q==k) @@ -112,7 +112,7 @@ vpPixelMeterConversion::convertMoment(const vpCameraParameters &cam, m[p][q] *= pow(cam.inv_px,(int)(1+p)) * pow(cam.inv_py,(int)(1+q)); } - for (unsigned int k=0; k<order; k++) // itération correspondant à l'ordre du moment + for (unsigned int k=0; k<order; k++) // iteration correspondant e l'ordre du moment for (unsigned int p=0 ; p<order; p++) for (unsigned int q=0; q<order; q++) if (p+q==k) diff --git a/src/camera/vpPixelMeterConversion.h b/src/camera/vpPixelMeterConversion.h index 698f53f0af0bc9fb4b1c769e8a1f164aff0972d8..f93f24f719aa87ca243966a111c77fdccbf1a5fc 100644 --- a/src/camera/vpPixelMeterConversion.h +++ b/src/camera/vpPixelMeterConversion.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPixelMeterConversion.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPixelMeterConversion.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/camera/vpXmlParserCamera.cpp b/src/camera/vpXmlParserCamera.cpp index a2e0fc0491a7b04f75892f223a8866e9d1cb320c..d47716faa7153b970e4ddc367bde438c7116d36d 100644 --- a/src/camera/vpXmlParserCamera.cpp +++ b/src/camera/vpXmlParserCamera.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpXmlParserCamera.cpp 4270 2013-06-25 12:15:16Z fspindle $ + * $Id: vpXmlParserCamera.cpp 5264 2015-02-04 13:49:55Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,20 +80,22 @@ /*! Default constructor */ -vpXmlParserCamera::vpXmlParserCamera(): vpXmlParser(){ - camera_name = ""; - image_width = 0; - image_height = 0; - subsampling_width = 0; - subsampling_height = 0; - full_width = 0; - full_height = 0; +vpXmlParserCamera::vpXmlParserCamera() + : vpXmlParser(), + camera(), camera_name(), image_width(0), image_height(0), + subsampling_width(0), subsampling_height(0), full_width(0), full_height(0) +{ } /*! Copy constructor \param twinParser : parser object to copy */ -vpXmlParserCamera::vpXmlParserCamera(vpXmlParserCamera& twinParser): vpXmlParser(twinParser){ +vpXmlParserCamera::vpXmlParserCamera(vpXmlParserCamera& twinParser) + : vpXmlParser(twinParser), + camera(), camera_name(), image_width(0), image_height(0), + subsampling_width(0), subsampling_height(0), full_width(0), full_height(0) + +{ this->camera = twinParser.camera; this->camera_name = twinParser.camera_name; this->image_width = twinParser.image_width; @@ -126,27 +128,27 @@ vpXmlParserCamera::operator =(const vpXmlParserCamera& twinParser) { Parse an xml file to load camera parameters. \param cam : camera parameters to fill. \param filename : name of the xml file to parse - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. \param projModel : camera projection model needed. - \param image_width : image width on which camera calibration was performed. + \param im_width : image width on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : image height on which camera calibration was performed. + \param im_height : image height on which camera calibration was performed. Set as 0 if not ambiguous. \return error code. */ int -vpXmlParserCamera::parse(vpCameraParameters &cam, const char * filename, - const std::string& camera_name, +vpXmlParserCamera::parse(vpCameraParameters &cam, const std::string &filename, + const std::string& cam_name, const vpCameraParameters::vpCameraParametersProjType &projModel, - const unsigned int image_width, - const unsigned int image_height) + const unsigned int im_width, + const unsigned int im_height) { xmlDocPtr doc; xmlNodePtr node; - doc = xmlParseFile(filename); + doc = xmlParseFile(filename.c_str()); if (doc == NULL) { return SEQUENCE_ERROR; @@ -159,7 +161,7 @@ vpXmlParserCamera::parse(vpCameraParameters &cam, const char * filename, return SEQUENCE_ERROR; } - int ret = this ->read (doc, node, camera_name, projModel, image_width, image_height); + int ret = this ->read (doc, node, cam_name, projModel, im_width, im_height); cam = camera ; @@ -172,26 +174,26 @@ vpXmlParserCamera::parse(vpCameraParameters &cam, const char * filename, Save camera parameters in an xml file. \param cam : camera parameters to save. \param filename : name of the xml file to fill. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration was performed. + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. \return error code. */ int -vpXmlParserCamera::save(const vpCameraParameters &cam, const char * filename, - const std::string& camera_name, - const unsigned int image_width, - const unsigned int image_height) +vpXmlParserCamera::save(const vpCameraParameters &cam, const std::string &filename, + const std::string& cam_name, + const unsigned int im_width, + const unsigned int im_height) { xmlDocPtr doc; xmlNodePtr node; xmlNodePtr nodeCamera = NULL; - doc = xmlReadFile(filename,NULL,XML_PARSE_NOWARNING + XML_PARSE_NOERROR + doc = xmlReadFile(filename.c_str(), NULL, XML_PARSE_NOWARNING + XML_PARSE_NOERROR + XML_PARSE_NOBLANKS); if (doc == NULL){ doc = xmlNewDoc ((xmlChar*)"1.0"); @@ -215,8 +217,8 @@ vpXmlParserCamera::save(const vpCameraParameters &cam, const char * filename, this->camera = cam; - int nbCamera = count(doc, node, camera_name,cam.get_projModel(), - image_width, image_height); + int nbCamera = count(doc, node, cam_name,cam.get_projModel(), + im_width, im_height); if( nbCamera > 0){ // vpCERROR << nbCamera // << " set(s) of camera parameters is(are) already "<< std::endl @@ -227,15 +229,15 @@ vpXmlParserCamera::save(const vpCameraParameters &cam, const char * filename, return SEQUENCE_ERROR; } - nodeCamera = find_camera(doc, node, camera_name, image_width, image_height); + nodeCamera = find_camera(doc, node, cam_name, im_width, im_height); if(nodeCamera == NULL){ - write(node, camera_name, image_width, image_height); + write(node, cam_name, im_width, im_height); } else{ write_camera(nodeCamera); } - xmlSaveFormatFile(filename,doc,1); + xmlSaveFormatFile(filename.c_str(), doc, 1); xmlFreeDoc(doc); return SEQUENCE_OK; @@ -248,33 +250,33 @@ vpXmlParserCamera::save(const vpCameraParameters &cam, const char * filename, \param doc : XML file. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : subsampling of the image width sent by the camera. + \param subsampl_width : subsampling of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : subsampling of the image height sent by the camera. + \param subsampl_height : subsampling of the image height sent by the camera. Set as 0 if not ambiguous. \return error code. */ int vpXmlParserCamera::read (xmlDocPtr doc, xmlNodePtr node, - const std::string& camera_name, + const std::string& cam_name, const vpCameraParameters::vpCameraParametersProjType &projModel, - const unsigned int image_width, - const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) + const unsigned int im_width, + const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { // char * val_char; vpXmlCodeType prop; vpXmlCodeSequenceType back = SEQUENCE_OK; - int nbCamera = 0; + unsigned int nbCamera = 0; for (node = node->xmlChildrenNode; node != NULL; node = node->next) { @@ -299,8 +301,8 @@ vpXmlParserCamera::read (xmlDocPtr doc, xmlNodePtr node, } */ if (prop == CODE_XML_CAMERA){ - if (SEQUENCE_OK == this->read_camera (doc, node, camera_name, projModel, - image_width, image_height, subsampling_width, subsampling_height)) + if (SEQUENCE_OK == this->read_camera (doc, node, cam_name, projModel, + im_width, im_height, subsampl_width, subsampl_height)) nbCamera++; } else back = SEQUENCE_ERROR; @@ -326,27 +328,27 @@ vpXmlParserCamera::read (xmlDocPtr doc, xmlNodePtr node, \param doc : XML file. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : subsampling of the image width sent by the camera. + \param subsampl_width : subsampling of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : subsampling of the image height sent by the camera. + \param subsampl_height : subsampling of the image height sent by the camera. Set as 0 if not ambiguous. \return number of available camera parameters corresponding with inputs. */ int vpXmlParserCamera::count (xmlDocPtr doc, xmlNodePtr node, - const std::string& camera_name, + const std::string& cam_name, const vpCameraParameters::vpCameraParametersProjType &projModel, - const unsigned int image_width, - const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) + const unsigned int im_width, + const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { // char * val_char; vpXmlCodeType prop; @@ -374,9 +376,9 @@ vpXmlParserCamera::count (xmlDocPtr doc, xmlNodePtr node, } */ if (prop== CODE_XML_CAMERA) { - if (SEQUENCE_OK == this->read_camera (doc, node, camera_name, projModel, - image_width, image_height, - subsampling_width, subsampling_height)) + if (SEQUENCE_OK == this->read_camera (doc, node, cam_name, projModel, + im_width, im_height, + subsampl_width, subsampl_height)) nbCamera++; } } @@ -389,26 +391,26 @@ vpXmlParserCamera::count (xmlDocPtr doc, xmlNodePtr node, \param doc : XML file. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : subsampling of the image width sent by the camera. + \param subsampl_width : subsampling of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : subsampling of the image height sent by the camera. + \param subsampl_height : subsampling of the image height sent by the camera. Set as 0 if not ambiguous. \return number of available camera parameters corresponding with inputs. */ xmlNodePtr vpXmlParserCamera::find_camera (xmlDocPtr doc, xmlNodePtr node, - const std::string& camera_name, - const unsigned int image_width, - const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) + const std::string& cam_name, + const unsigned int im_width, + const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { // char * val_char; vpXmlCodeType prop; @@ -435,9 +437,9 @@ vpXmlParserCamera::find_camera (xmlDocPtr doc, xmlNodePtr node, } */ if(prop == CODE_XML_CAMERA){ - if (SEQUENCE_OK == this->read_camera_header(doc, node, camera_name, - image_width, image_height, - subsampling_width, subsampling_height)) + if (SEQUENCE_OK == this->read_camera_header(doc, node, cam_name, + im_width, im_height, + subsampl_width, subsampl_height)) return node; } } @@ -449,15 +451,15 @@ vpXmlParserCamera::find_camera (xmlDocPtr doc, xmlNodePtr node, \param doc : XML file. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : scale of the image width sent by the camera. + \param subsampl_width : scale of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : scale of the image height sent by the camera. + \param subsampl_height : scale of the image height sent by the camera. Set as 0 if not ambiguous. \return error code. @@ -465,12 +467,12 @@ vpXmlParserCamera::find_camera (xmlDocPtr doc, xmlNodePtr node, */ int vpXmlParserCamera::read_camera (xmlDocPtr doc, xmlNodePtr node, - const std::string& camera_name, + const std::string& cam_name, const vpCameraParameters::vpCameraParametersProjType &projModel, - const unsigned int image_width, - const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) + const unsigned int im_width, + const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { vpXmlCodeType prop; /* read value in the XML file. */ @@ -499,12 +501,13 @@ vpXmlParserCamera::read_camera (xmlDocPtr doc, xmlNodePtr node, switch (prop) { - case CODE_XML_CAMERA_NAME:{ + case CODE_XML_CAMERA_NAME: { char * val_char = xmlReadCharChild(doc, node); camera_name_tmp = val_char; + std::cout << "Found camera with name: \"" << camera_name_tmp << "\"" << std::endl; xmlFree(val_char); - }break; - + break; + } case CODE_XML_WIDTH: image_width_tmp = xmlReadUnsignedIntChild(doc, node); break; @@ -552,13 +555,21 @@ vpXmlParserCamera::read_camera (xmlDocPtr doc, xmlNodePtr node, } } - if( !((projModelFound == true) && (camera_name == camera_name_tmp) && - (abs((int)image_width - (int)image_width_tmp) < allowedPixelDiffOnImageSize || image_width == 0) && - (abs((int)image_height - (int)image_height_tmp) < allowedPixelDiffOnImageSize || image_height == 0) && - ( subsampling_width == 0 || - abs((int)subsampling_width - (int)subsampling_width_tmp) < (allowedPixelDiffOnImageSize * (int)(subsampling_width_tmp / subsampling_width)))&& - ( subsampling_height == 0 || - abs((int)subsampling_height - (int)subsampling_height_tmp) < (allowedPixelDiffOnImageSize * (int)(subsampling_width_tmp / subsampling_width))))){ + // Create a specific test for subsampling_width and subsampling_height to ensure that division by zero is not possible in the next test + bool test_subsampling_width = true; + bool test_subsampling_height = true; + + if (subsampling_width) { + test_subsampling_width = (abs((int)subsampl_width - (int)subsampling_width_tmp) < (allowedPixelDiffOnImageSize * (int)(subsampling_width_tmp / subsampling_width))); + } + if (subsampling_height) { + test_subsampling_height = (abs((int)subsampl_height - (int)subsampling_height_tmp) < (allowedPixelDiffOnImageSize * (int)(subsampling_height_tmp / subsampling_height))); + } + if( !((projModelFound == true) && (cam_name == camera_name_tmp) && + (abs((int)im_width - (int)image_width_tmp) < allowedPixelDiffOnImageSize || im_width == 0) && + (abs((int)im_height - (int)image_height_tmp) < allowedPixelDiffOnImageSize || im_height == 0) && + (test_subsampling_width)&& + (test_subsampling_height))){ back = SEQUENCE_ERROR; } else{ @@ -578,15 +589,15 @@ vpXmlParserCamera::read_camera (xmlDocPtr doc, xmlNodePtr node, \param doc : XML file. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : scale of the image width sent by the camera. + \param subsampl_width : scale of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : scale of the image height sent by the camera. + \param subsampl_height : scale of the image height sent by the camera. Set as 0 if not ambiguous. \return error code. @@ -595,11 +606,11 @@ vpXmlParserCamera::read_camera (xmlDocPtr doc, xmlNodePtr node, int vpXmlParserCamera:: read_camera_header (xmlDocPtr doc, xmlNodePtr node, - const std::string& camera_name, - const unsigned int image_width, - const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) + const std::string& cam_name, + const unsigned int im_width, + const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { vpXmlCodeType prop; /* read value in the XML file. */ @@ -672,13 +683,13 @@ read_camera_header (xmlDocPtr doc, xmlNodePtr node, break; } } - if( !((camera_name == camera_name_tmp) && - (image_width == image_width_tmp || image_width == 0) && - (image_height == image_height_tmp || image_height == 0) && - (subsampling_width == subsampling_width_tmp || - subsampling_width == 0)&& - (subsampling_height == subsampling_height_tmp || - subsampling_height == 0))){ + if( !((cam_name == camera_name_tmp) && + (im_width == image_width_tmp || im_width == 0) && + (im_height == image_height_tmp || im_height == 0) && + (subsampl_width == subsampling_width_tmp || + subsampl_width == 0)&& + (subsampl_height == subsampling_height_tmp || + subsampl_height == 0))){ back = SEQUENCE_ERROR; } return back; @@ -780,14 +791,18 @@ vpXmlParserCamera::read_camera_model (xmlDocPtr doc, xmlNodePtr node, } } + if(model_type == NULL) { + vpERROR_TRACE("projection model type doesn't match with any known model !"); + return SEQUENCE_ERROR; + } + if( !strcmp(model_type,LABEL_XML_MODEL_WITHOUT_DISTORTION)){ if (nb != 5 || validation != 0x1F) { vpCERROR <<"ERROR in 'model' field:\n"; vpCERROR << "it must contain 5 parameters\n"; - if(model_type != NULL){ - xmlFree(model_type); - } + xmlFree(model_type); + return SEQUENCE_ERROR; } cam_tmp.initPersProjWithoutDistortion(px,py,u0,v0) ; @@ -797,23 +812,20 @@ vpXmlParserCamera::read_camera_model (xmlDocPtr doc, xmlNodePtr node, { vpCERROR <<"ERROR in 'model' field:\n"; vpCERROR << "it must contain 7 parameters\n"; - if(model_type != NULL){ - xmlFree(model_type); - } + xmlFree(model_type); + return SEQUENCE_ERROR; } cam_tmp.initPersProjWithDistortion(px,py,u0,v0,kud,kdu); } else{ vpERROR_TRACE("projection model type doesn't match with any known model !"); - if(model_type != NULL){ - xmlFree(model_type); - } - return SEQUENCE_ERROR; - } - if(model_type != NULL){ xmlFree(model_type); + + return SEQUENCE_ERROR; } + xmlFree(model_type); + return back; } @@ -821,24 +833,24 @@ vpXmlParserCamera::read_camera_model (xmlDocPtr doc, xmlNodePtr node, Write camera parameters in an XML Tree. \param node : XML tree, pointing on a marker equipement. - \param camera_name : name of the camera : useful if the xml file has multiple + \param cam_name : name of the camera : useful if the xml file has multiple camera parameters. Set as "" if the camera name is not ambiguous. - \param image_width : width of image on which camera calibration was performed. + \param im_width : width of image on which camera calibration was performed. Set as 0 if not ambiguous. - \param image_height : height of the image on which camera calibration + \param im_height : height of the image on which camera calibration was performed. Set as 0 if not ambiguous. - \param subsampling_width : subsampling of the image width sent by the camera. + \param subsampl_width : subsampling of the image width sent by the camera. Set as 0 if not ambiguous. - \param subsampling_height : subsampling of the image height sent by the camera. + \param subsampl_height : subsampling of the image height sent by the camera. Set as 0 if not ambiguous. \return error code. */ int vpXmlParserCamera:: -write (xmlNodePtr node, const std::string& camera_name, - const unsigned int image_width, const unsigned int image_height, - const unsigned int subsampling_width, - const unsigned int subsampling_height) +write (xmlNodePtr node, const std::string& cam_name, + const unsigned int im_width, const unsigned int im_height, + const unsigned int subsampl_width, + const unsigned int subsampl_height) { int back = SEQUENCE_OK; @@ -851,46 +863,46 @@ write (xmlNodePtr node, const std::string& camera_name, { //<name> - if(!camera_name.empty()){ + if(!cam_name.empty()){ node_tmp = xmlNewComment((xmlChar*)"Name of the camera"); xmlAddChild(node_camera,node_tmp); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_CAMERA_NAME, - (xmlChar*)camera_name.c_str()); + (xmlChar*)cam_name.c_str()); } - if(image_width != 0 || image_height != 0){ + if(im_width != 0 || im_height != 0){ char str[11]; //<image_width> node_tmp = xmlNewComment((xmlChar*)"Size of the image on which camera calibration was performed"); xmlAddChild(node_camera,node_tmp); - sprintf(str,"%u",image_width); + sprintf(str,"%u",im_width); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_WIDTH,(xmlChar*)str); //<image_height> - sprintf(str,"%u",image_height); + sprintf(str,"%u",im_height); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_HEIGHT,(xmlChar*)str); if(subsampling_width != 0 || subsampling_height != 0){ node_tmp = xmlNewComment((xmlChar*)"Subsampling used to obtain the current size of the image."); xmlAddChild(node_camera,node_tmp); //<subsampling_width> - sprintf(str,"%u",subsampling_width); + sprintf(str,"%u",subsampl_width); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_SUBSAMPLING_WIDTH, (xmlChar*)str); //<subsampling_height> - sprintf(str,"%u",subsampling_height); + sprintf(str,"%u",subsampl_height); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_SUBSAMPLING_HEIGHT, (xmlChar*)str); node_tmp = xmlNewComment((xmlChar*)"The full size is the sensor size actually used to grab the image. full_width = subsampling_width * image_width"); xmlAddChild(node_camera,node_tmp); //<full_width> - sprintf(str,"%u",image_width*subsampling_width); + sprintf(str,"%u",im_width*subsampl_width); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_FULL_WIDTH, (xmlChar*)str); //<full_height> - sprintf(str,"%u",image_height*subsampling_height); + sprintf(str,"%u",im_height*subsampl_height); xmlNewTextChild(node_camera,NULL,(xmlChar*)LABEL_XML_FULL_HEIGHT, (xmlChar*)str); } diff --git a/src/camera/vpXmlParserCamera.h b/src/camera/vpXmlParserCamera.h index de24acd6e7765f95104b98de74602eda012641c6..26a9d993f94d5f444d0be31589e6595e09e248fe 100644 --- a/src/camera/vpXmlParserCamera.h +++ b/src/camera/vpXmlParserCamera.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpXmlParserCamera.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpXmlParserCamera.h 5006 2014-11-24 15:57:45Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -241,11 +241,11 @@ public: vpXmlParserCamera& operator =(const vpXmlParserCamera& twinparser); ~vpXmlParserCamera(){} - int parse(vpCameraParameters &cam, const char * filename, + int parse(vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, const vpCameraParameters::vpCameraParametersProjType &projModel, const unsigned int image_width = 0, const unsigned int image_height = 0); - int save(const vpCameraParameters &cam, const char * filename, + int save(const vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, const unsigned int image_width = 0, const unsigned int image_height = 0); @@ -257,16 +257,16 @@ public: unsigned int getSubsampling_height(){return this->subsampling_height;} vpCameraParameters getCameraParameters(){return this->camera;} - void setCameraName(const std::string& camera_name){ - this->camera_name = camera_name; + void setCameraName(const std::string& name){ + this->camera_name = name; } void setWidth(const unsigned int width){ this->image_width = width ; } void setHeight(const unsigned int height){ this->image_height = height ; } - void setSubsampling_width(const unsigned int subsampling_width){ - this->subsampling_width = subsampling_width ; + void setSubsampling_width(const unsigned int subsampling){ + this->subsampling_width = subsampling ; } - void setSubsampling_height(const unsigned int subsampling_height){ - this->subsampling_height = subsampling_height ; + void setSubsampling_height(const unsigned int subsampling){ + this->subsampling_height = subsampling ; } private: diff --git a/src/computer-vision/homography-estimation/vpHomography.cpp b/src/computer-vision/homography-estimation/vpHomography.cpp index a16e81eb3999e4f3041655fa9898d7c43349dc74..cb4ee3033b8b06211789560a52d2d9b94a61bdd5 100644 --- a/src/computer-vision/homography-estimation/vpHomography.cpp +++ b/src/computer-vision/homography-estimation/vpHomography.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomography.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHomography.cpp 4661 2014-02-10 19:34:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,55 +47,24 @@ the particular case of homography */ -#include <visp/vpHomography.h> +#include <stdio.h> + #include <visp/vpDebug.h> +#include <visp/vpHomography.h> #include <visp/vpMatrix.h> +#include <visp/vpRobust.h> // Exception #include <visp/vpException.h> #include <visp/vpMatrixException.h> -// Debug trace -#include <visp/vpDebug.h> - -#include <stdio.h> - - - -/*! - \brief initialiaze a 4x4 matrix as identity -*/ - -void -vpHomography::init() -{ - unsigned int i,j ; - - try { - vpMatrix::resize(3,3) ; - } - catch(vpException me) - { - vpERROR_TRACE("Error caught") ; - throw ; - } - - - for (i=0 ; i < 3 ; i++) - for (j=0 ; j < 3; j++) - if (i==j) - (*this)[i][j] = 1.0 ; - else - (*this)[i][j] = 0.0; - -} - /*! \brief initialize an homography as Identity */ -vpHomography::vpHomography() : vpMatrix() +vpHomography::vpHomography() : data(NULL), aMb(), bP() { - init() ; + data = new double [9]; + setIdentity(); } @@ -103,98 +72,87 @@ vpHomography::vpHomography() : vpMatrix() \brief initialize an homography from another homography */ -vpHomography::vpHomography(const vpHomography &aHb) : vpMatrix() +vpHomography::vpHomography(const vpHomography &H) : data(NULL), aMb(), bP() { - init() ; - *this = aHb ; + data = new double [9]; + + *this = H; } /*! \brief initialize an homography from another homography */ - -vpHomography::vpHomography(const vpHomogeneousMatrix &aMb, - const vpPlane &_bP) : vpMatrix() +vpHomography::vpHomography(const vpHomogeneousMatrix &M, const vpPlane &p) : data(NULL), aMb(), bP() { - - - init() ; - - buildFrom(aMb,_bP) ; - - + data = new double [9]; + buildFrom(M, p) ; } vpHomography::vpHomography(const vpThetaUVector &tu, - const vpTranslationVector &atb, - const vpPlane &_bP) : vpMatrix() + const vpTranslationVector &atb, + const vpPlane &p) : data(NULL), aMb(), bP() { - init() ; - buildFrom(tu,atb,_bP) ; + data = new double [9]; + buildFrom(tu, atb, p) ; } vpHomography::vpHomography(const vpRotationMatrix &aRb, - const vpTranslationVector &atb, - const vpPlane &_bP) : vpMatrix() + const vpTranslationVector &atb, + const vpPlane &p) : data(NULL), aMb(), bP() { - init() ; - buildFrom(aRb,atb,_bP) ; - } + data = new double [9]; + buildFrom(aRb, atb, p) ; +} -vpHomography::vpHomography(const vpPoseVector &arb, - const vpPlane &_bP) : vpMatrix() +vpHomography::vpHomography(const vpPoseVector &arb, const vpPlane &p) : data(NULL), aMb(), bP() { - - init() ; - buildFrom(arb,_bP) ; + data = new double [9]; + buildFrom(arb, p) ; } - +vpHomography::~vpHomography() +{ + if (data) + delete [] data; + data = NULL; +} void -vpHomography::buildFrom(const vpHomogeneousMatrix &aMb, - const vpPlane &_bP) +vpHomography::buildFrom(const vpHomogeneousMatrix &M, + const vpPlane &p) { - - - insert(aMb) ; - insert(_bP) ; + insert(M) ; + insert(p) ; build() ; - - } void vpHomography::buildFrom(const vpThetaUVector &tu, - const vpTranslationVector &atb, - const vpPlane &_bP) + const vpTranslationVector &atb, + const vpPlane &p) { - insert(tu) ; insert(atb) ; - insert(_bP) ; + insert(p) ; build() ; } void vpHomography::buildFrom(const vpRotationMatrix &aRb, - const vpTranslationVector &atb, - const vpPlane &_bP) + const vpTranslationVector &atb, + const vpPlane &p) { - init() ; insert(aRb) ; insert(atb) ; - insert(_bP) ; + insert(p) ; build() ; } void -vpHomography::buildFrom(const vpPoseVector &arb, - const vpPlane &_bP) +vpHomography::buildFrom(const vpPoseVector &arb, const vpPlane &p) { - aMb.buildFrom(arb[0],arb[1],arb[2],arb[3],arb[4],arb[5]) ; - insert(_bP) ; + insert(p) ; build() ; } @@ -204,31 +162,28 @@ vpHomography::buildFrom(const vpPoseVector &arb, /*! - \brief insert the rotational component and - recompute the homography + \brief insert the rotational component. + To recompute the homography call build(). */ void vpHomography::insert(const vpRotationMatrix &aRb) { aMb.insert(aRb) ; - //build() ; } + /*! - \brief insert the rotational component and - recompute the homography + \brief insert the rotational component. + To recompute the homography call build(). */ void -vpHomography::insert(const vpHomogeneousMatrix &_aMb) +vpHomography::insert(const vpHomogeneousMatrix &M) { - - aMb = _aMb ; - //build() ; + this->aMb = M ; } - /*! \brief insert the rotational component, insert a - theta u vector (transformation into a rotation matrix) and - recompute the homography + theta u vector (transformation into a rotation matrix). + To recompute the homography call build(). */ void @@ -236,56 +191,53 @@ vpHomography::insert(const vpThetaUVector &tu) { vpRotationMatrix aRb(tu) ; aMb.insert(aRb) ; - //build() ; } - /*! - \brief insert the translational component in a homography and - recompute the homography + \brief insert the translational component in a homography. + To recompute the homography call build(). */ void vpHomography::insert(const vpTranslationVector &atb) { aMb.insert(atb) ; - //build() ; } /*! - \brief insert the reference plane and - recompute the homography + \brief insert the reference plane. + To recompute the homography call build(). */ void -vpHomography::insert(const vpPlane &_bP) +vpHomography::insert(const vpPlane &p) { - - bP= _bP ; - //build() ; + this->bP = p; } - /*! - \brief invert the homography + \brief Invert the homography - - \return [H]^-1 + \return \f$\bf H^{-1}\f$ */ vpHomography vpHomography::inverse() const { - vpHomography bHa ; + vpMatrix M = (*this); + vpMatrix Minv; + M.pseudoInverse(Minv, 1e-16); + vpHomography H; - vpMatrix::pseudoInverse(bHa,1e-16) ; + for(unsigned int i=0; i<3; i++) + for(unsigned int j=0; j<3; j++) + H[i][j] = Minv[i][j]; - return bHa; + return H; } /*! - \brief invert the homography - + \brief Invert the homography. - \param bHa : [H]^-1 + \param bHa : \f$\bf H^{-1}\f$ with H = *this. */ void vpHomography::inverse(vpHomography &bHa) const @@ -297,14 +249,13 @@ vpHomography::inverse(vpHomography &bHa) const void vpHomography::save(std::ofstream &f) const { - if (f != NULL) + if (! f.fail()) { f << *this ; } else { - vpERROR_TRACE("\t\t file not open " ); - throw(vpException(vpException::ioError, "\t\t file not open")) ; + throw(vpException(vpException::ioError, "Cannot write the homography to the output stream")) ; } } @@ -328,7 +279,7 @@ vpHomography vpHomography::operator*(const vpHomography &H) const for(unsigned int j = 0; j < 3; j++) { double s = 0.; for(unsigned int k = 0; k < 3; k ++) { - s += (*this)[i][k] * H[k][j]; + s += (*this)[i][k] * H[k][j]; } Hp[i][j] = s; } @@ -336,6 +287,27 @@ vpHomography vpHomography::operator*(const vpHomography &H) const return Hp; } +/*! + Operation a = aHb * b. + + \param b : 3 dimension vector. +*/ +vpColVector +vpHomography::operator*(const vpColVector &b) const +{ + if (b.size() != 3) + throw(vpException(vpException::dimensionError, "Cannot multiply an homography by a vector of dimension %d", b.size())); + + vpColVector a(3); + for(unsigned int i=0; i<3; i++) { + a[i] = 0.; + for(unsigned int j=0; j<3; j++) + a[i] += (*this)[i][j] * b[j]; + } + + return a; +} + /*! Multiply an homography by a scalar. @@ -377,14 +349,67 @@ vpHomography vpHomography::operator*(const double &v) const vpHomography vpHomography::operator/(const double &v) const { vpHomography H; - double one_over_v = 1. / v; - + if (std::fabs(v) <= std::numeric_limits<double>::epsilon()) + throw vpMatrixException(vpMatrixException::divideByZeroError, "Divide by zero in method /=(double v)"); + + double vinv = 1/v ; + for (unsigned int i=0; i < 9; i ++) { - H.data[i] = this->data[i] * one_over_v; + H.data[i] = this->data[i] * vinv; } return H; } + +//! Divide all the element of the homography matrix by v : Hij = Hij / v +vpHomography & vpHomography::operator/=(double v) +{ + //if (x == 0) + if (std::fabs(v) <= std::numeric_limits<double>::epsilon()) + throw vpMatrixException(vpMatrixException::divideByZeroError, "Divide by zero in method /=(double v)"); + + double vinv = 1/v ; + + for (unsigned int i=0;i<9;i++) + data[i] *= vinv; + + return *this; +} + +/*! + Copy operator. + Allow operation such as aHb = H + + \param H : Homography matrix to be copied. +*/ +vpHomography & +vpHomography::operator=(const vpHomography &H) +{ + for (unsigned int i=0; i< 3; i++) + for (unsigned int j=0; j< 3; j++) + (*this)[i][j] = H[i][j]; + + return *this; +} +/*! + Copy operator. + Allow operation such as aHb = H + + \param H : Matrix to be copied. +*/ +vpHomography & +vpHomography::operator=(const vpMatrix &H) +{ + if (H.getRows() !=3 || H.getCols() != 3) + throw(vpException(vpException::dimensionError, "The matrix is not an homography")); + + for (unsigned int i=0; i< 3; i++) + for (unsigned int j=0; j< 3; j++) + (*this)[i][j] = H[i][j]; + + return *this; +} + /*! Read an homography in a file, verify if it is really an homogeneous matrix. @@ -394,29 +419,29 @@ vpHomography vpHomography::operator/(const double &v) const void vpHomography::load(std::ifstream &f) { - if (f != NULL) + if (! f.fail()) { for (unsigned int i=0 ; i < 3 ; i++) for (unsigned int j=0 ; j < 3 ; j++) { - f>> (*this)[i][j] ; + f >> (*this)[i][j] ; } } else { - vpERROR_TRACE("\t\t file not open " ); - throw(vpException(vpException::ioError, "\t\t file not open")) ; + throw(vpException(vpException::ioError, "Cannot read the homography from the input stream")) ; } } - -//! Print the matrix as a vector [T thetaU] +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS +//! Print the homography as a matrix. void vpHomography::print() { std::cout <<*this << std::endl ; } +#endif // #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! \brief Compute aHb such that @@ -428,23 +453,26 @@ vpHomography::print() void vpHomography::build() { - unsigned int i,j ; - vpColVector n(3) ; vpColVector atb(3) ; - for (i=0 ; i < 3 ; i++) + vpMatrix aRb(3,3); + for (unsigned int i=0 ; i < 3 ; i++) { - atb[i] = aMb[i][3] ; - for (j=0 ; j < 3 ; j++) (*this)[i][j] = aMb[i][j]; + atb[i] = aMb[i][3]; + for (unsigned int j=0 ; j < 3 ; j++) + aRb[i][j] = aMb[i][j]; } bP.getNormal(n) ; double d = bP.getD() ; - *this -= atb*n.t()/d ; // the d used in the equation is such as nX=d is the + vpMatrix aHb = aRb - atb*n.t()/d ; // the d used in the equation is such as nX=d is the // plane equation. So if the plane is described by // Ax+By+Cz+D=0, d=-D + for (unsigned int i=0 ; i < 3 ; i++) + for (unsigned int j=0 ; j < 3 ; j++) + (*this)[i][j] = aHb[i][j]; } /*! @@ -457,30 +485,284 @@ vpHomography::build() */ void vpHomography::build(vpHomography &aHb, - const vpHomogeneousMatrix &aMb, - const vpPlane &bP) + const vpHomogeneousMatrix &aMb, + const vpPlane &bP) { - unsigned int i,j ; - vpColVector n(3) ; vpColVector atb(3) ; - for (i=0 ; i < 3 ; i++) + vpMatrix aRb(3,3); + for (unsigned int i=0 ; i < 3 ; i++) { atb[i] = aMb[i][3] ; - for (j=0 ; j < 3 ; j++) aHb[i][j] = aMb[i][j]; + for (unsigned int j=0 ; j < 3 ; j++) + aRb[i][j] = aMb[i][j]; } bP.getNormal(n) ; double d = bP.getD() ; - aHb -= atb*n.t()/d ; // the d used in the equation is such as nX=d is the + vpMatrix aHb_ = aRb - atb*n.t()/d ; // the d used in the equation is such as nX=d is the // plane equation. So if the plane is described by // Ax+By+Cz+D=0, d=-D + for (unsigned int i=0 ; i < 3 ; i++) + for (unsigned int j=0 ; j < 3 ; j++) + aHb[i][j] = aHb_[i][j]; } -/* - * Local variables: - * c-basic-offset: 2 - * End: +/*! + Set the homography as identity transformation. +*/ +void +vpHomography::setIdentity() +{ + for (unsigned int i=0 ; i < 3 ; i++) + for (unsigned int j=0 ; j < 3; j++) + if (i==j) + (*this)[i][j] = 1.0 ; + else + (*this)[i][j] = 0.0; +} + +/*! + Given \c iPa a point with coordinates \f$(u_a,v_a)\f$ expressed in pixel in image a, and the homography \c bHa that + links image a and b, computes the coordinates of the point \f$(u_b,v_b)\f$ in the image b using the camera parameters + matrix \f$\bf K\f$. + + Compute \f$^b{\bf p} = {\bf K} \; {^b}{\bf H}_a \; {\bf K}^{-1} {^a}{\bf p}\f$ with \f$^a{\bf p}=(u_a,v_a,1)\f$ + and \f$^b{\bf p}=(u_b,v_b,1)\f$ + + \return The coordinates in pixel of the point with coordinates \f$(u_b,v_b)\f$. + */ +vpImagePoint vpHomography::project(const vpCameraParameters &cam, const vpHomography &bHa, const vpImagePoint &iPa) +{ + double xa = iPa.get_u(); + double ya = iPa.get_v(); + vpMatrix H = cam.get_K() * bHa * cam.get_K_inverse(); + double z = xa * H[2][0] + ya * H[2][1] + H[2][2]; + double xb = (xa * H[0][0] + ya * H[0][1] + H[0][2]) / z; + double yb = (xa * H[1][0] + ya * H[1][1] + H[1][2]) / z; + + vpImagePoint iPb(yb, xb); + + return iPb; +} +/*! + Given \c Pa a point with normalized coordinates \f$(x_a,y_a,1)\f$ in the image plane a, and the homography \c bHa that + links image a and b, computes the normalized coordinates of the point \f$(x_b,y_b,1)\f$ in the image plane b. + + Compute \f$^b{\bf p} = {^b}{\bf H}_a \; {^a}{\bf p}\f$ with \f$^a{\bf p}=(x_a,y_a,1)\f$ + and \f$^b{\bf p}=(x_b,y_b,1)\f$ + + \return The coordinates in meter of the point with coordinates \f$(x_b,y_b)\f$. + */ +vpPoint vpHomography::project(const vpHomography &bHa, const vpPoint &Pa) +{ + double xa = Pa.get_x(); + double ya = Pa.get_y(); + double z = xa * bHa[2][0] + ya * bHa[2][1] + bHa[2][2]; + double xb = (xa * bHa[0][0] + ya * bHa[0][1] + bHa[0][2]) / z; + double yb = (xa * bHa[1][0] + ya * bHa[1][1] + bHa[1][2]) / z; + + vpPoint Pb; + Pb.set_x(xb); + Pb.set_y(yb); + + return Pb; +} + +/*! + From couples of matched points \f$^a{\bf p}=(x_a,y_a,1)\f$ in image a + and \f$^b{\bf p}=(x_b,y_b,1)\f$ in image b with homogeneous coordinates, computes the + homography matrix by resolving \f$^a{\bf p} = ^a{\bf H}_b\; ^b{\bf p}\f$ + using a robust estimation scheme. + + This method is to compare to DLT() except that here a robust estimator is used to reject + couples of points that are considered as outliers. + + At least 4 couples of points are needed. + + \param xb, yb : Coordinates vector of matched points in image b. These coordinates are expressed in meters. + \param xa, ya : Coordinates vector of matched points in image a. These coordinates are expressed in meters. + \param aHb : Estimated homography that relies the transformation from image a to image b. + \param inliers : Vector that indicates if a matched point is an inlier (true) or an outlier (false). + \param residual : Global residual computed as + \f$r = \sqrt{1/n \sum_{inliers} {\| {^a{\bf p} - {\hat{^a{\bf H}_b}} {^b{\bf p}}} \|}^{2}}\f$ with \f$n\f$ the + number of inliers. + \param weights_threshold : Threshold applied on the weights updated during the robust estimation and + used to consider if a point is an outlier or an inlier. Values should be in [0:1]. + A couple of matched points that have a weight lower than this threshold is considered as an outlier. + A value equal to zero indicates that all the points are inliers. + \param niter : Number of iterations of the estimation process. + \param normalization : When set to true, the coordinates of the points are normalized. The normalization + carried out is the one preconized by Hartley. + + \sa DLT(), ransac() */ +void +vpHomography::robust(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + vpHomography &aHb, + std::vector<bool> &inliers, + double &residual, + double weights_threshold, + unsigned int niter, + bool normalization) +{ + unsigned int n = (unsigned int) xb.size(); + if (yb.size() != n || xa.size() != n || ya.size() != n) + throw(vpException(vpException::dimensionError, + "Bad dimension for robust homography estimation")); + + // 4 point are required + if(n<4) + throw(vpException(vpException::fatalError, "There must be at least 4 matched points")); + + try{ + std::vector<double> xan, yan, xbn, ybn; + + double xg1=0., yg1=0., coef1=0., xg2=0., yg2=0., coef2=0.; + + vpHomography aHbn; + + if (normalization) { + vpHomography::HartleyNormalization(xb, yb, xbn, ybn, xg1, yg1, coef1); + vpHomography::HartleyNormalization(xa, ya, xan, yan, xg2, yg2, coef2); + } + else { + xbn = xb; + ybn = yb; + xan = xa; + yan = ya; + } + + unsigned int nbLinesA = 2; + vpMatrix A(nbLinesA*n,8); + vpColVector X(8); + vpColVector Y(nbLinesA*n); + vpMatrix W(nbLinesA*n, nbLinesA*n) ; // Weight matrix + + vpColVector w(nbLinesA*n) ; + + // All the weights are set to 1 at the beginning to use a classical least square scheme + w = 1; + // Update the square matrix associated to the weights + for (unsigned int i=0; i < nbLinesA*n; i ++) { + W[i][i] = w[i]; + } + + // build matrix A + for(unsigned int i=0; i<n;i++) { + A[nbLinesA*i][0]=xbn[i]; + A[nbLinesA*i][1]=ybn[i]; + A[nbLinesA*i][2]=1; + A[nbLinesA*i][3]=0 ; + A[nbLinesA*i][4]=0 ; + A[nbLinesA*i][5]=0; + A[nbLinesA*i][6]=-xbn[i]*xan[i] ; + A[nbLinesA*i][7]=-ybn[i]*xan[i]; + + A[nbLinesA*i+1][0]=0 ; + A[nbLinesA*i+1][1]=0; + A[nbLinesA*i+1][2]=0; + A[nbLinesA*i+1][3]=xbn[i]; + A[nbLinesA*i+1][4]=ybn[i]; + A[nbLinesA*i+1][5]=1; + A[nbLinesA*i+1][6]=-xbn[i]*yan[i]; + A[nbLinesA*i+1][7]=-ybn[i]*yan[i]; + + Y[nbLinesA*i] = xan[i]; + Y[nbLinesA*i+1] = yan[i]; + } + + vpMatrix WA; + vpMatrix WAp; + unsigned int iter = 0; + vpRobust r(nbLinesA*n) ; // M-Estimator + + while (iter < niter) { + WA = W * A; + + X = WA.pseudoInverse(1e-26)*W*Y; + vpColVector residu; + residu = Y - A * X; + + // Compute the weights using the Tukey biweight M-Estimator + r.setIteration(iter) ; + r.MEstimator(vpRobust::TUKEY, residu, w) ; + + // Update the weights matrix + for (unsigned int i=0; i < n*nbLinesA; i ++) { + W[i][i] = w[i]; + } + // Build the homography + for(unsigned int i=0;i<8;i++) + aHbn.data[i]= X[i]; + aHbn[2][2] = 1; + { + vpMatrix aHbnorm = aHbn; + aHbnorm /= aHbnorm[2][2] ; + } + + iter ++; + } + inliers.resize(n); + unsigned int nbinliers = 0; + for(unsigned int i=0; i< n; i++) { + if (w[i*2] < weights_threshold && w[i*2+1] < weights_threshold) + inliers[i] = false; + else { + inliers[i] = true; + nbinliers ++; + } + } + + if (normalization) { + // H after denormalization + vpHomography::HartleyDenormalization(aHbn, aHb, xg1, yg1, coef1, xg2, yg2, coef2); + } + else { + aHb = aHbn; + } + + residual = 0 ; + vpColVector a(3), b(3), c(3); + for (unsigned int i=0 ; i < n ; i++) { + if (inliers[i]) { + a[0] = xa[i] ; a[1] = ya[i] ; a[2] = 1 ; + b[0] = xb[i] ; b[1] = yb[i] ; b[2] = 1 ; + + c = aHb*b ; c /= c[2] ; + residual += (a-c).sumSquare(); + } + } + + residual = sqrt(residual/nbinliers); + } + catch(...) + { + throw(vpException(vpException::fatalError, "Cannot estimate an homography")) ; + } +} + +/*! +\brief std::cout a matrix +*/ +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpHomography &H) +{ + std::ios_base::fmtflags original_flags = s.flags(); + + s.precision(10) ; + for (unsigned int i=0;i<H.getRows();i++) { + for (unsigned int j=0;j<H.getCols();j++){ + s << H[i][j] << " "; + } + // We don't add a \n char on the end of the last matrix line + if (i < H.getRows()-1) + s << std::endl; + } + + s.flags(original_flags); // restore s to standard state + + return s; +} diff --git a/src/computer-vision/homography-estimation/vpHomography.h b/src/computer-vision/homography-estimation/vpHomography.h index f4d031bdd9ebdd6e0e26cf1c13212638e0ad5c0d..c7c885377c077e3f505ec4f59830db4640b8f4fb 100644 --- a/src/computer-vision/homography-estimation/vpHomography.h +++ b/src/computer-vision/homography-estimation/vpHomography.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomography.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpHomography.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,14 +51,17 @@ tools for homography computation. #ifndef vpHomography_hh #define vpHomography_hh +#include <list> +#include <vector> + +#include <visp/vpCameraParameters.h> +#include <visp/vpImagePoint.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpPlane.h> #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS # include <visp/vpList.h> #endif -#include <list> - /*! \class vpHomography @@ -84,7 +87,9 @@ tools for homography computation. { ^b{\bf n}^T} \f] - The example below shows how to manipulate this class to first + The \ref tutorial-homography explains how to use this class. + + The example below shows also how to manipulate this class to first compute a ground truth homography from camera poses, project pixel coordinates points using an homography and lastly estimate an homography from a subset of 4 matched points in frame a and frame b @@ -123,7 +128,7 @@ int main() // Compute the coordinates in pixels of the 4 object points in the // camera frame a vpPoint Pa[4]; - double xa[4], ya[4]; // Coordinates in pixels of the points in frame a + std::vector<double> xa(4), ya(4); // Coordinates in pixels of the points in frame a for(int i=0 ; i < 4 ; i++) { Pa[i] = Po[i]; Pa[i].project(aMo); // Project the points from object frame to @@ -136,7 +141,7 @@ int main() // Compute the coordinates in pixels of the 4 object points in the // camera frame b vpPoint Pb[4]; - double xb[4], yb[4]; // Coordinates in pixels of the points in frame b + std::vector<double> xb(4), yb(4); // Coordinates in pixels of the points in frame b for(int i=0 ; i < 4 ; i++) { Pb[i] = Po[i]; Pb[i].project(bMo); // Project the points from object frame to @@ -162,7 +167,7 @@ int main() } // Estimate the homography from 4 points coordinates expressed in pixels - vpHomography::HartleyDLT(4, xb, yb, xa, ya, aHb); + vpHomography::DLT(xb, yb, xa, ya, aHb, true); aHb /= aHb[2][2]; // Apply a scale factor to have aHb[2][2] = 1 std::cout << "Estimated homography aHb: \n" << aHb<< std::endl; @@ -170,211 +175,264 @@ int main() \endcode */ -class VISP_EXPORT vpHomography : public vpMatrix +class VISP_EXPORT vpHomography { - -private: - static const double sing_threshold; // = 0.0001; - static const double threshold_rotation; - static const double threshold_displacement; - vpHomogeneousMatrix aMb ; - // bool isplanar; - //! reference plane coordinates expressed in Rb - vpPlane bP ; - void init() ; - -private: - //! insert a rotation matrix - void insert(const vpHomogeneousMatrix &aRb) ; - //! insert a rotation matrix - void insert(const vpRotationMatrix &aRb) ; - //! insert a theta u vector (transformation into a rotation matrix) - void insert(const vpThetaUVector &tu) ; - //! insert a translation vector - void insert(const vpTranslationVector &atb) ; - //! insert a translation vector - void insert(const vpPlane &bP) ; - - static void initRansac(unsigned int n, - double *xb, double *yb, - double *xa, double *ya, - vpColVector &x) ; - -public: - static void HartleyNormalization(unsigned int n, - double *x, double *y, - double *xn, double *yn, - double &xg, double &yg, - double &coef); - static void HartleyDenormalization(vpHomography &aHbn, - vpHomography &aHb, - double xg1, double yg1, double coef1, - double xg2, double yg2, double coef2 ) ; - - - vpHomography() ; - - //! copy constructor - vpHomography(const vpHomography &aMb) ; - //! Construction from Translation and rotation and a plane - vpHomography(const vpHomogeneousMatrix &aMb, - const vpPlane &bP) ; - //! Construction from Translation and rotation and a plane - vpHomography(const vpRotationMatrix &aRb, - const vpTranslationVector &atb, - const vpPlane &bP) ; - //! Construction from Translation and rotation and a plane - vpHomography(const vpThetaUVector &tu, - const vpTranslationVector &atb, - const vpPlane &bP) ; - //! Construction from Translation and rotation and a plane - vpHomography(const vpPoseVector &arb, - const vpPlane &bP) ; - virtual ~vpHomography() { } - - //! Construction from Translation and rotation and a plane - void buildFrom(const vpRotationMatrix &aRb, - const vpTranslationVector &atb, + public: + //! Data array + double *data; + + private: + static const double sing_threshold; // = 0.0001; + static const double threshold_rotation; + static const double threshold_displacement; + vpHomogeneousMatrix aMb ; + // bool isplanar; + //! reference plane coordinates expressed in Rb + vpPlane bP ; + + private: + //! build the homography from aMb and Rb + void build() ; + + //! insert a rotation matrix + void insert(const vpHomogeneousMatrix &aRb) ; + //! insert a rotation matrix + void insert(const vpRotationMatrix &aRb) ; + //! insert a theta u vector (transformation into a rotation matrix) + void insert(const vpThetaUVector &tu) ; + //! insert a translation vector + void insert(const vpTranslationVector &atb) ; + //! insert a translation vector + void insert(const vpPlane &bP) ; + + static void initRansac(unsigned int n, + double *xb, double *yb, + double *xa, double *ya, + vpColVector &x) ; + + public: + vpHomography() ; + vpHomography(const vpHomography &H) ; + //! Construction from Translation and rotation and a plane + vpHomography(const vpHomogeneousMatrix &aMb, const vpPlane &bP) ; - //! Construction from Translation and rotation and a plane - void buildFrom(const vpThetaUVector &tu, + //! Construction from Translation and rotation and a plane + vpHomography(const vpRotationMatrix &aRb, const vpTranslationVector &atb, const vpPlane &bP) ; - //! Construction from Translation and rotation and a plane - void buildFrom(const vpPoseVector &arb, + //! Construction from Translation and rotation and a plane + vpHomography(const vpThetaUVector &tu, + const vpTranslationVector &atb, const vpPlane &bP) ; - //! Construction from homogeneous matrix and a plane - void buildFrom(const vpHomogeneousMatrix &aMb, + //! Construction from Translation and rotation and a plane + vpHomography(const vpPoseVector &arb, const vpPlane &bP) ; - - //! build the homography from aMb and Rb - void build() ; - - - void computeDisplacement(vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) ; - - void computeDisplacement(const vpColVector& nd, - vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) ; - - //! Load an homography from a file - void load(std::ifstream &f) ; - //! Print the matrix - void print() ; - //! Save an homography in a file - void save(std::ofstream &f) const ; - - //! invert the homography - vpHomography inverse() const ; - //! invert the homography - void inverse(vpHomography &Hi) const; - - // Multiplication by an homography - vpHomography operator*(const vpHomography &H) const; - - // Multiplication by a scalar - vpHomography operator*(const double &v) const; - - // Division by a scalar - vpHomography operator/(const double &v) const; - - //! build the homography from aMb and Rb - static void build(vpHomography &aHb, - const vpHomogeneousMatrix &aMb, - const vpPlane &bP) ; - - static void DLT(unsigned int n, - double *xb, double *yb , - double *xa, double *ya, - vpHomography &aHb) ; - - static void HartleyDLT(unsigned int n, - double *xb, double *yb , - double *xa, double *ya, - vpHomography &aHb) ; - static void HLM(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - bool isplan, - vpHomography &aHb) ; - - static void computeDisplacement(const vpHomography &aHb, - const vpColVector& nd, - vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) ; - - static void computeDisplacement (const vpHomography &aHb, - vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) ; - - static void computeDisplacement(const vpMatrix &H, - const double x, - const double y, - std::list<vpRotationMatrix> & vR, - std::list<vpTranslationVector> & vT, - std::list<vpColVector> & vN) ; - static double computeDisplacement(unsigned int nbpoint, - vpPoint *c1P, - vpPoint *c2P, - vpPlane &oN, - vpHomogeneousMatrix &c2Mc1, - vpHomogeneousMatrix &c1Mo, - int userobust - ) ; - static double computeDisplacement(unsigned int nbpoint, - vpPoint *c1P, - vpPoint *c2P, - vpPlane *oN, - vpHomogeneousMatrix &c2Mc1, - vpHomogeneousMatrix &c1Mo, - int userobust - ) ; - static double computeResidual(vpColVector &x, vpColVector &M, vpColVector &d); - // VVS - static double computeRotation(unsigned int nbpoint, - vpPoint *c1P, - vpPoint *c2P, - vpHomogeneousMatrix &c2Mc1, - int userobust - ) ; - static void computeTransformation(vpColVector &x,unsigned int *ind, vpColVector &M) ; - - static bool degenerateConfiguration(vpColVector &x,unsigned int *ind) ; - static bool degenerateConfiguration(vpColVector &x,unsigned int *ind, double threshold_area); - - static bool ransac(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &aHb, - int consensus = 1000, - double threshold = 1e-6 - ) ; - - static bool ransac(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &aHb, - vpColVector& inliers, - double& residual, - int consensus = 1000, - double epsilon = 1e-6, - double areaThreshold = 0.0); + virtual ~vpHomography(); + + //! Construction from Translation and rotation and a plane + void buildFrom(const vpRotationMatrix &aRb, + const vpTranslationVector &atb, + const vpPlane &bP) ; + //! Construction from Translation and rotation and a plane + void buildFrom(const vpThetaUVector &tu, + const vpTranslationVector &atb, + const vpPlane &bP) ; + //! Construction from Translation and rotation and a plane + void buildFrom(const vpPoseVector &arb, + const vpPlane &bP) ; + //! Construction from homogeneous matrix and a plane + void buildFrom(const vpHomogeneousMatrix &aMb, + const vpPlane &bP) ; + + void computeDisplacement(vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) ; + + void computeDisplacement(const vpColVector& nd, + vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) ; + + //! Return the number of rows of the homography matrix. + inline unsigned int getRows() const { return 3;} + //! Return the number of columns of the homography matrix. + inline unsigned int getCols() const { return 3; } + + //! invert the homography + vpHomography inverse() const ; + //! invert the homography + void inverse(vpHomography &Hi) const; + + //! Load an homography from a file + void load(std::ifstream &f) ; + + //! Write elements Hij (usage : H[i][j] = x ) + inline double *operator[](unsigned int i) { return &data[i*3]; } + //! Read elements Hij (usage : x = H[i][j] ) + inline double *operator[](unsigned int i) const {return &data[i*3];} + + // Multiplication by an homography + vpHomography operator*(const vpHomography &H) const; + + // Multiplication by a scalar + vpHomography operator*(const double &v) const; + vpColVector operator*(const vpColVector &b) const; + + // Division by a scalar + vpHomography operator/(const double &v) const; + vpHomography & operator/=(double v); + vpHomography & operator=(const vpHomography &H); + vpHomography & operator=(const vpMatrix &H); + + //! Save an homography in a file + void save(std::ofstream &f) const ; + + void setIdentity(); + + friend VISP_EXPORT std::ostream &operator << (std::ostream &s,const vpHomography &H); + + static void DLT(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya , + vpHomography &aHb, + bool normalization=true); + + static void HLM(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + bool isplanar, + vpHomography &aHb) ; + + static bool ransac(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + vpHomography &aHb, + std::vector<bool> &inliers, + double &residual, + unsigned int nbInliersConsensus, + double threshold, + bool normalization=true); + + static vpImagePoint project(const vpCameraParameters &cam, const vpHomography &bHa, const vpImagePoint &iPa); + static vpPoint project(const vpHomography &bHa, const vpPoint &Pa); + + static void robust(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + vpHomography &aHb, + std::vector<bool> &inlier, + double &residual, + double weights_threshold=0.4, + unsigned int niter=4, + bool normalization=true); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + //! build the homography from aMb and Rb + static void build(vpHomography &aHb, + const vpHomogeneousMatrix &aMb, + const vpPlane &bP) ; + + static void computeDisplacement(const vpHomography &aHb, + const vpColVector& nd, + vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) ; + + static void computeDisplacement (const vpHomography &aHb, + vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) ; + + static void computeDisplacement(const vpMatrix &H, + const double x, + const double y, + std::list<vpRotationMatrix> & vR, + std::list<vpTranslationVector> & vT, + std::list<vpColVector> & vN) ; + static double computeDisplacement(unsigned int nbpoint, + vpPoint *c1P, + vpPoint *c2P, + vpPlane &oN, + vpHomogeneousMatrix &c2Mc1, + vpHomogeneousMatrix &c1Mo, + int userobust + ) ; + static double computeDisplacement(unsigned int nbpoint, + vpPoint *c1P, + vpPoint *c2P, + vpPlane *oN, + vpHomogeneousMatrix &c2Mc1, + vpHomogeneousMatrix &c1Mo, + int userobust + ) ; + static double computeResidual(vpColVector &x, vpColVector &M, vpColVector &d); + // VVS + static double computeRotation(unsigned int nbpoint, + vpPoint *c1P, + vpPoint *c2P, + vpHomogeneousMatrix &c2Mc1, + int userobust) ; + static void computeTransformation(vpColVector &x,unsigned int *ind, vpColVector &M) ; + static bool degenerateConfiguration(vpColVector &x,unsigned int *ind) ; + static bool degenerateConfiguration(vpColVector &x,unsigned int *ind, double threshold_area); + static bool degenerateConfiguration(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya); + static void HartleyNormalization(unsigned int n, + const double *x, const double *y, + double *xn, double *yn, + double &xg, double &yg, + double &coef); + static void HartleyNormalization(const std::vector<double> &x, const std::vector<double> &y, + std::vector<double> &xn, std::vector<double> &yn, + double &xg, double &yg, double &coef); + static void HartleyDenormalization(vpHomography &aHbn, + vpHomography &aHb, + double xg1, double yg1, double coef1, + double xg2, double yg2, double coef2 ) ; + +#endif // DOXYGEN_SHOULD_SKIP_THIS #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - /*! + /*! @name Deprecated functions */ - vp_deprecated static void computeDisplacement(const vpMatrix H, - const double x, - const double y, - vpList<vpRotationMatrix> & vR, - vpList<vpTranslationVector> & vT, - vpList<vpColVector> & vN) ; + vp_deprecated static void computeDisplacement(const vpMatrix H, + const double x, + const double y, + vpList<vpRotationMatrix> & vR, + vpList<vpTranslationVector> & vT, + vpList<vpColVector> & vN); + + static void DLT(unsigned int n, + double *xb, double *yb , + double *xa, double *ya, + vpHomography &aHb) ; + vp_deprecated static void HartleyDLT(unsigned int n, + double *xb, double *yb , + double *xa, double *ya, + vpHomography &aHb) ; + + static void HLM(unsigned int n, + double *xb, double *yb, + double *xa, double *ya , + bool isplan, + vpHomography &aHb) ; + + //! Print the matrix + vp_deprecated void print() ; + + vp_deprecated static bool ransac(unsigned int n, + double *xb, double *yb, + double *xa, double *ya , + vpHomography &aHb, + int consensus = 1000, + double threshold = 1e-6) ; + + vp_deprecated static bool ransac(unsigned int n, + double *xb, double *yb, + double *xa, double *ya , + vpHomography &aHb, + vpColVector& inliers, + double residual = 0.1, // Not used + int consensus = 1000, + double threshold = 1e-6, + double areaThreshold = 0.0); #endif // VISP_BUILD_DEPRECATED_FUNCTIONS } ; diff --git a/src/computer-vision/homography-estimation/vpHomographyDLT.cpp b/src/computer-vision/homography-estimation/vpHomographyDLT.cpp index 1c48c142203b7e7c4a86630f005516d18deaf4ed..10f4ff5a15219f1c4bded1e9f77037be1255aaac 100644 --- a/src/computer-vision/homography-estimation/vpHomographyDLT.cpp +++ b/src/computer-vision/homography-estimation/vpHomographyDLT.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomographyDLT.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpHomographyDLT.cpp 5052 2014-12-11 14:18:49Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,17 +48,74 @@ */ #include <visp/vpHomography.h> +#include <visp/vpMatrix.h> #include <visp/vpMatrixException.h> #include <cmath> // std::fabs #include <limits> // numeric_limits +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +void +vpHomography::HartleyNormalization(const std::vector<double> &x, const std::vector<double> &y, + std::vector<double> &xn, std::vector<double> &yn, + double &xg, double &yg, double &coef) +{ + if (x.size() != y.size()) + throw(vpException(vpException::dimensionError, + "Hartley normalization require that x and y vector have the same dimension")); + + unsigned int n = (unsigned int) x.size(); + if (xn.size() != n) + xn.resize(n); + if (yn.size() != n) + yn.resize(n); + + xg = 0 ; + yg = 0 ; + + for (unsigned int i =0 ; i < n ; i++) + { + xg += x[i] ; + yg += y[i] ; + } + xg /= n ; + yg /= n ; + + // Changement d'origine : le centre de gravite doit correspondre + // a l'origine des coordonnees + double distance=0; + for(unsigned int i=0; i<n;i++) + { + double xni=x[i]-xg; + double yni=y[i]-yg; + xn[i] = xni ; + yn[i] = yni ; + distance+=sqrt(vpMath::sqr(xni)+vpMath::sqr(yni)); + }//for translation sur tous les points + + //Changement d'echelle + distance/=n; + //calcul du coef de changement d'echelle + //if(distance ==0) + if(std::fabs(distance) <= std::numeric_limits<double>::epsilon()) + coef=1; + else + coef=sqrt(2.0)/distance; + + for(unsigned int i=0; i<n;i++) + { + xn[i] *= coef; + yn[i] *= coef; + } +} + void vpHomography::HartleyNormalization(unsigned int n, - double *x, double *y, - double *xn, double *yn, - double &xg, double &yg, - double &coef) + const double *x, const double *y, + double *xn, double *yn, + double &xg, double &yg, + double &coef) { unsigned int i; xg = 0 ; @@ -72,8 +129,8 @@ vpHomography::HartleyNormalization(unsigned int n, xg /= n ; yg /= n ; - //Changement d'origine : le centre de gravité doit correspondre - // à l'origine des coordonnées + //Changement d'origine : le centre de gravite doit correspondre + // a l'origine des coordonnees double distance=0; for(i=0; i<n;i++) { @@ -84,9 +141,9 @@ vpHomography::HartleyNormalization(unsigned int n, distance+=sqrt(vpMath::sqr(xni)+vpMath::sqr(yni)); }//for translation sur tous les points - //Changement d'échelle + //Changement d'echelle distance/=n; - //calcul du coef de changement d'échelle + //calcul du coef de changement d'echelle //if(distance ==0) if(std::fabs(distance) <= std::numeric_limits<double>::epsilon()) coef=1; @@ -98,7 +155,6 @@ vpHomography::HartleyNormalization(unsigned int n, xn[i] *= coef; yn[i] *= coef; } - } //--------------------------------------------------------------------------------------- @@ -110,8 +166,8 @@ vpHomography::HartleyDenormalization(vpHomography &aHbn, double xg2, double yg2, double coef2 ) { - //calcul des transformations à appliquer sur M_norm pour obtenir M - //en fonction des deux normalisations effectuées au début sur + //calcul des transformations a appliquer sur M_norm pour obtenir M + //en fonction des deux normalisations effectuees au debut sur //les points: aHb = T2^ aHbn T1 vpMatrix T1(3,3); vpMatrix T2(3,3); @@ -129,19 +185,29 @@ vpHomography::HartleyDenormalization(vpHomography &aHbn, T2[0][2]=-coef2*xg2 ; T2[1][2]=-coef2*yg2 ; - T2T=T2.pseudoInverse(1e-16) ; - vpMatrix maHb=T2T*(vpMatrix)aHbn*T1; + vpMatrix aHbn_(3,3); + for(unsigned int i=0; i<3; i++) + for(unsigned int j=0; j<3; j++) + aHbn_[i][j] = aHbn[i][j]; - for (unsigned int i=0 ; i < 3 ; i++) - for (unsigned int j=0 ; j < 3 ; j++) aHb[i][j] = maHb[i][j] ; + vpMatrix maHb=T2T*aHbn_*T1; + for (unsigned int i=0 ; i < 3 ; i++) + for (unsigned int j=0 ; j < 3 ; j++) + aHb[i][j] = maHb[i][j] ; } +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS + +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! + \deprecated You should rather use + vpHomography::DLT(const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, vpHomography &, bool) + \brief Computes the homography matrix using the DLT (Direct Linear Transform) algorithm on normalized data. @@ -157,12 +223,12 @@ vpHomography::HartleyDenormalization(vpHomography &aHbn, */ void vpHomography::HartleyDLT(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &aHb) + double *xb, double *yb, + double *xa, double *ya , + vpHomography &aHb) { try{ - //initialise les données initiales + //initialise les donnees initiales // code_retour =InitialData(n, p1,p2); // normalize points @@ -173,9 +239,9 @@ vpHomography::HartleyDLT(unsigned int n, double xg1, yg1, coef1 ; vpHomography::HartleyNormalization(n, - xb,yb, - xbn,ybn, - xg1, yg1,coef1); + xb,yb, + xbn,ybn, + xg1, yg1,coef1); double *xan; double *yan; @@ -184,15 +250,15 @@ vpHomography::HartleyDLT(unsigned int n, double xg2, yg2, coef2 ; vpHomography::HartleyNormalization(n, - xa,ya, - xan,yan, - xg2, yg2,coef2); + xa,ya, + xan,yan, + xg2, yg2,coef2); vpHomography aHbn ; //compute the homography using the DLT from normalized data vpHomography::DLT(n,xbn,ybn,xan,yan,aHbn); - //H dénormalisée + //H denormalisee vpHomography::HartleyDenormalization(aHbn,aHb,xg1,yg1,coef1,xg2,yg2, coef2); delete [] xbn; @@ -207,9 +273,14 @@ vpHomography::HartleyDLT(unsigned int n, throw ; } } +#endif //#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! + \deprecated You should rather use + vpHomography::DLT(const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, vpHomography &, bool) + \brief Computes the homography matrix wrt. the data using the DLT (Direct Linear Transform) algorithm. @@ -225,7 +296,6 @@ vpHomography::HartleyDLT(unsigned int n, At least 4 correspondant points couples are needed. - For each point, in homogeneous coordinates we have: \f[ \mathbf{p}_{a}= \mathbf{H}\mathbf{p}_{b} @@ -244,7 +314,6 @@ vpHomography::HartleyDLT(unsigned int n, \mathbf{p}_{a} \times \mathbf{H}\mathbf{p}_{b} =\left( \begin{array}{c}y_{a}\mathbf{h}_3^T\mathbf{p}_{b}-w_{a}\mathbf{h}_2^T\mathbf{p}_{b} \\w_{a}\mathbf{h}_1^T\mathbf{p}_{b} -x_{a}\mathbf{h}_3^T \mathbf{p}_{b} \\x_{a}\mathbf{h}_2^T \mathbf{p}_{b}- y_{a}\mathbf{h}_1^T\mathbf{p}_{b}\end{array}\right) \f] - \f[ \underbrace{\left( \begin{array}{ccc}\mathbf{0}^T & -w_{a} \mathbf{p}_{b}^T & y_{a} \mathbf{p}_{b}^T \\ w_{a} @@ -267,9 +336,9 @@ vpHomography::HartleyDLT(unsigned int n, that should be 8 is deficient. */ void vpHomography::DLT(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &aHb) + double *xb, double *yb, + double *xa, double *ya , + vpHomography &aHb) { // 4 point are required @@ -284,19 +353,17 @@ void vpHomography::DLT(unsigned int n, vpColVector h(9); vpColVector D(9); vpMatrix V(9,9); - unsigned int i, j; // We need here to compute the SVD on a (n*2)*9 matrix (where n is // the number of points). if n == 4, the matrix has more columns // than rows. This kind of matrix is not supported by GSL for // SVD. The solution is to add an extra line with zeros - if (n == 4) + if (n == 4) A.resize(2*n+1,9); // build matrix A - for(i=0; i<n;i++) + for(unsigned int i=0; i<n;i++) { - A[2*i][0]=0; A[2*i][1]=0; A[2*i][2]=0; @@ -321,24 +388,24 @@ void vpHomography::DLT(unsigned int n, // Add an extra line with zero. if (n == 4) { - for (int i=0; i < 9; i ++) { - A[2*n][i] = 0; + for (unsigned int i=0; i < 9; i ++) { + A[2*n][i] = 0; } - } + } // solve Ah = 0 // SVD Decomposition A = UDV^T (destructive wrt A) A.svd(D,V); // on en profite pour effectuer un controle sur le rang de la matrice : - // pas plus de 2 valeurs singulières quasi=0 + // pas plus de 2 valeurs singulieres quasi=0 int rank=0; - for(i = 0; i<9;i++) if(D[i]>1e-7) rank++; + for(unsigned int i = 0; i<9;i++) if(D[i]>1e-7) rank++; if(rank <7) { vpTRACE(" Rank is : %d, should be 8", rank); throw(vpMatrixException(vpMatrixException::rankDeficient, - "\n\t\t Matrix rank is deficient")) ; + "\n\t\t Matrix rank is deficient")) ; } //h = is the column of V associated with the smallest singular value of A @@ -346,17 +413,17 @@ void vpHomography::DLT(unsigned int n, // singular value... we seek for the smallest double smallestSv = 1e30 ; unsigned int indexSmallestSv = 0 ; - for (i=0 ; i < 9 ; i++) + for (unsigned int i=0 ; i < 9 ; i++) if ((D[i] < smallestSv) ){ smallestSv = D[i] ;indexSmallestSv = i ; } - h=V.column(indexSmallestSv+1); + h=V.getCol(indexSmallestSv); // build the homography - for(i =0;i<3;i++) + for(unsigned int i =0;i<3;i++) { - for(j=0;j<3;j++) - aHb[i][j]=h[3*i+j]; + for(unsigned int j=0;j<3;j++) + aHb[i][j]=h[3*i+j]; } } @@ -373,3 +440,192 @@ void vpHomography::DLT(unsigned int n, } } +#endif // #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + +/*! + From couples of matched points \f$^a{\bf p}=(x_a,y_a,1)\f$ in image a + and \f$^b{\bf p}=(x_b,y_b,1)\f$ in image b with homogeneous coordinates, computes the + homography matrix by resolving \f$^a{\bf p} = ^a{\bf H}_b\; ^b{\bf p}\f$ + using the DLT (Direct Linear Transform) algorithm. + + At least 4 couples of points are needed. + + To do so, we use the DLT algorithm on the data, + ie we resolve the linear system by SDV : \f$\bf{Ah} =0\f$ where + \f$\bf{h}\f$ is the vector with the terms of \f$^a{\bf H}_b\f$ and + \f$\mathbf{A}\f$ depends on the points coordinates. + + For each point, in homogeneous coordinates we have: + \f[ + ^a{\bf p} = ^a{\bf H}_b\; ^b{\bf p} + \f] + which is equivalent to: + \f[ + ^a{\bf p} \times {^a{\bf H}_b \; ^b{\bf p}} =0 + \f] + If we note \f$\mathbf{h}_j^T\f$ the \f$j^{\textrm{th}}\f$ line of \f$^a{\bf H}_b\f$, we can write: + \f[ + ^a{\bf H}_b \; ^b{\bf p} = \left( \begin{array}{c}\mathbf{h}_1^T \;^b{\bf p} \\\mathbf{h}_2^T \; ^b{\bf p} \\\mathbf{h}_3^T \;^b{\bf p} \end{array}\right) + \f] + + Setting \f$^a{\bf p}=(x_{a},y_{a},w_{a})\f$, the cross product can be rewritten by: + \f[ + ^a{\bf p} \times ^a{\bf H}_b \; ^b{\bf p} =\left( \begin{array}{c}y_{a}\mathbf{h}_3^T \; ^b{\bf p}-w_{a}\mathbf{h}_2^T \; ^b{\bf p} \\w_{a}\mathbf{h}_1^T \; ^b{\bf p} -x_{a}\mathbf{h}_3^T \; ^b{\bf p} \\x_{a}\mathbf{h}_2^T \; ^b{\bf p}- y_{a}\mathbf{h}_1^T \; ^b{\bf p}\end{array}\right) + \f] + + \f[ + \underbrace{\left( \begin{array}{ccc}\mathbf{0}^T & -w_{a} \; ^b{\bf p}^T + & y_{a} \; ^b{\bf p}^T \\ w_{a} + \; ^b{\bf p}^T&\mathbf{0}^T & -x_{a} \; ^b{\bf p}^T \\ + -y_{a} \; ^b{\bf p}^T & x_{a} \; ^b{\bf p}^T & + \mathbf{0}^T\end{array}\right)}_{\mathbf{A}_i (3\times 9)} + \underbrace{\left( \begin{array}{c}\mathbf{h}_{1}^{T} \\ + \mathbf{h}_{2}^{T}\\\mathbf{h}_{3}^{T}\end{array}\right)}_{\mathbf{h} (9\times 1)}=0 + \f] + + leading to an homogeneous system to be solved: \f$\mathbf{A}\mathbf{h}=0\f$ with + \f$\mathbf{A}=\left(\mathbf{A}_1^T, ..., \mathbf{A}_i^T, ..., \mathbf{A}_n^T \right)^T\f$. + + It can be solved using an SVD decomposition: + \f[\bf A = UDV^T \f] + <b>h</b> is the column of <b>V</b> associated with the smalest singular value of <b>A + </b> + + \param xb, yb : Coordinates vector of matched points in image b. These coordinates are expressed in meters. + \param xa, ya : Coordinates vector of matched points in image a. These coordinates are expressed in meters. + \param aHb : Estimated homography that relies the transformation from image a to image b. + \param normalization : When set to true, the coordinates of the points are normalized. The normalization + carried out is the one preconized by Hartley. + + \exception vpMatrixException::rankDeficient : When the rank of the matrix + that should be 8 is deficient. +*/ +void vpHomography::DLT(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya , + vpHomography &aHb, + bool normalization) +{ + unsigned int n = (unsigned int) xb.size(); + if (yb.size() != n || xa.size() != n || ya.size() != n) + throw(vpException(vpException::dimensionError, + "Bad dimension for DLT homography estimation")); + + // 4 point are required + if(n<4) + throw(vpException(vpException::fatalError, "There must be at least 4 matched points")); + + try{ + std::vector<double> xan, yan, xbn, ybn; + + double xg1=0., yg1=0., coef1=0., xg2=0., yg2=0., coef2=0.; + + vpHomography aHbn; + + if (normalization) { + vpHomography::HartleyNormalization(xb, yb, xbn, ybn, xg1, yg1, coef1); + vpHomography::HartleyNormalization(xa, ya, xan, yan, xg2, yg2, coef2); + } + else { + xbn = xb; + ybn = yb; + xan = xa; + yan = ya; + } + + vpMatrix A(2*n,9); + vpColVector h(9); + vpColVector D(9); + vpMatrix V(9,9); + + // We need here to compute the SVD on a (n*2)*9 matrix (where n is + // the number of points). if n == 4, the matrix has more columns + // than rows. This kind of matrix is not supported by GSL for + // SVD. The solution is to add an extra line with zeros + if (n == 4) + A.resize(2*n+1,9); + + // build matrix A + for(unsigned int i=0; i<n;i++) + { + A[2*i][0]=0; + A[2*i][1]=0; + A[2*i][2]=0; + A[2*i][3]=-xbn[i] ; + A[2*i][4]=-ybn[i] ; + A[2*i][5]=-1; + A[2*i][6]=xbn[i]*yan[i] ; + A[2*i][7]=ybn[i]*yan[i]; + A[2*i][8]=yan[i]; + + + A[2*i+1][0]=xbn[i] ; + A[2*i+1][1]=ybn[i] ; + A[2*i+1][2]=1; + A[2*i+1][3]=0; + A[2*i+1][4]=0; + A[2*i+1][5]=0; + A[2*i+1][6]=-xbn[i]*xan[i]; + A[2*i+1][7]=-ybn[i]*xan[i]; + A[2*i+1][8]=-xan[i] ; + } + + // Add an extra line with zero. + if (n == 4) { + for (unsigned int i=0; i < 9; i ++) { + A[2*n][i] = 0; + } + } + + // solve Ah = 0 + // SVD Decomposition A = UDV^T (destructive wrt A) + A.svd(D,V); + + // on en profite pour effectuer un controle sur le rang de la matrice : + // pas plus de 2 valeurs singulieres quasi=0 + int rank=0; + for(unsigned int i = 0; i<9;i++) if(D[i]>1e-7) rank++; + if(rank <7) + { + vpTRACE(" Rank is : %d, should be 8", rank); + throw(vpMatrixException(vpMatrixException::rankDeficient, + "\n\t\t Matrix rank is deficient")) ; + } + //h = is the column of V associated with the smallest singular value of A + + // since we are not sure that the svd implemented sort the + // singular value... we seek for the smallest + double smallestSv = 1e30 ; + unsigned int indexSmallestSv = 0 ; + for (unsigned int i=0 ; i < 9 ; i++) + if ((D[i] < smallestSv) ){ smallestSv = D[i] ;indexSmallestSv = i ; } + + h=V.getCol(indexSmallestSv); + + // build the homography + for(unsigned int i =0;i<3;i++) + { + for(unsigned int j=0;j<3;j++) + aHbn[i][j]=h[3*i+j]; + } + + if (normalization) { + // H after denormalization + vpHomography::HartleyDenormalization(aHbn, aHb, xg1, yg1, coef1, xg2, yg2, coef2); + } + else { + aHb = aHbn; + } + + } + catch(vpMatrixException me) + { + vpTRACE("Matrix Exception ") ; + throw(me) ; + } + catch(vpException me) + { + vpERROR_TRACE("caught another error") ; + std::cout <<std::endl << me << std::endl ; + throw(me) ; + } +} diff --git a/src/computer-vision/homography-estimation/vpHomographyExtract.cpp b/src/computer-vision/homography-estimation/vpHomographyExtract.cpp index 63590430f7358b228d59084f927eb8d0a065af2e..8937d63a6906cc2095723955f0efcb9cf0a22a13 100644 --- a/src/computer-vision/homography-estimation/vpHomographyExtract.cpp +++ b/src/computer-vision/homography-estimation/vpHomographyExtract.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomographyExtract.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHomographyExtract.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,16 +50,70 @@ /* ------------------------------------------------------------------------ */ const double vpHomography::sing_threshold = 0.0001; +/*! + Compute the camera displacement between two images from the homography \f$ + {^a}{\bf H}_b \f$ which is here an implicit parameter (*this). + + \param aRb : Rotation matrix as an output \f$ {^a}{\bf R}_b \f$. + + \param atb : Translation vector as an output \f$ ^a{\bf t}_b \f$. + + \param n : Normal vector to the plane as an output. + +*/ +void vpHomography::computeDisplacement(vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) +{ + + + vpColVector nd(3) ; + nd[0]=0;nd[1]=0;nd[2]=1; + + computeDisplacement(*this,aRb,atb,n); + +} + /*! Compute the camera displacement between two images from the homography \f$ - {^a}{\bf H}_b \f$. + {^a}{\bf H}_b \f$ which is here an implicit parameter (*this). Camera displacement between \f$ {^a}{\bf p} \f$ and \f$ {^a}{\bf p} \f$ is represented as a rotation matrix \f$ {^a}{\bf R}_b \f$ and a translation vector \f$ ^a{\bf t}_b \f$ from which an homogenous matrix can be build (vpHomogeneousMatrix). + \param nd : Input normal vector to the plane used to compar with the normal + vector \e n extracted from the homography. + + \param aRb : Rotation matrix as an output \f$ {^a}{\bf R}_b \f$. + + \param atb : Translation vector as an output \f$ ^a{\bf t}_b \f$. + + \param n : Normal vector to the plane as an output. + +*/ +void vpHomography::computeDisplacement(const vpColVector& nd, + vpRotationMatrix &aRb, + vpTranslationVector &atb, + vpColVector &n) +{ + computeDisplacement(*this,nd, aRb,atb,n); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +/*! + + Compute the camera displacement between two images from the homography \f$ + {^a}{\bf H}_b \f$. + + Camera displacement between \f$ {^a}{\bf p} \f$ and \f$ {^b}{\bf p} \f$ is + represented as a rotation matrix \f$ {^a}{\bf R}_b \f$ and a translation + vector \f$ ^a{\bf t}_b \f$ from which an homogenous matrix can be build + (vpHomogeneousMatrix). + \param aHb : Input homography \f$ {^a}{\bf H}_b \f$. \param nd : Input normal vector to the plane used to compar with the normal @@ -79,7 +133,7 @@ void vpTranslationVector &atb, vpColVector &n) { - /**** Déclarations des variables ****/ + /**** Declarations des variables ****/ vpMatrix aRbint(3,3) ; vpColVector svTemp(3), sv(3); @@ -341,35 +395,6 @@ void } -/*! - - Compute the camera displacement between two images from the homography \f$ - {^a}{\bf H}_b \f$ which is here an implicit parameter (*this). - - Camera displacement between \f$ {^a}{\bf p} \f$ and \f$ {^a}{\bf p} \f$ is - represented as a rotation matrix \f$ {^a}{\bf R}_b \f$ and a translation - vector \f$ ^a{\bf t}_b \f$ from which an homogenous matrix can be build - (vpHomogeneousMatrix). - - \param nd : Input normal vector to the plane used to compar with the normal - vector \e n extracted from the homography. - - \param aRb : Rotation matrix as an output \f$ {^a}{\bf R}_b \f$. - - \param atb : Translation vector as an output \f$ ^a{\bf t}_b \f$. - - \param n : Normal vector to the plane as an output. - - \sa computeDisplacement(const vpHomography &, const vpColVector &, vpRotationMatrix &, vpTranslationVector &, vpColVector &) -*/ -void - vpHomography::computeDisplacement(const vpColVector& nd, - vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) -{ - computeDisplacement(*this,nd, aRb,atb,n); -} /*! Compute the camera displacement between two images from the homography \f$ @@ -394,7 +419,7 @@ void vpTranslationVector &atb, vpColVector &n) { - /**** Déclarations des variables ****/ + /**** Declarations des variables ****/ vpMatrix aRbint(3,3) ; vpColVector svTemp(3), sv(3); @@ -655,32 +680,6 @@ void } - -/*! - Compute the camera displacement between two images from the homography \f$ - {^a}{\bf H}_b \f$ which is here an implicit parameter (*this). - - \param aRb : Rotation matrix as an output \f$ {^a}{\bf R}_b \f$. - - \param atb : Translation vector as an output \f$ ^a{\bf t}_b \f$. - - \param n : Normal vector to the plane as an output. - - \sa computeDisplacement(const vpHomography &, vpRotationMatrix &, vpTranslationVector &, vpColVector &) -*/ -void vpHomography::computeDisplacement(vpRotationMatrix &aRb, - vpTranslationVector &atb, - vpColVector &n) -{ - - - vpColVector nd(3) ; - nd[0]=0;nd[1]=0;nd[2]=1; - - computeDisplacement(*this,aRb,atb,n); - -} - void vpHomography::computeDisplacement(const vpMatrix &H, const double x, const double y, @@ -697,7 +696,7 @@ void vpHomography::computeDisplacement(const vpMatrix &H, vT.clear(); vN.clear(); - /**** Déclarations des variables ****/ + /**** Declarations des variables ****/ int cas1 =1, cas2=2, cas3=3, cas4=4; int cas =0; bool norm1ok=false, norm2ok = false,norm3ok=false,norm4ok =false; @@ -1333,6 +1332,7 @@ void vpHomography::computeDisplacement(const vpMatrix &H, printf("fin : Homographie_EstimationDeplacementCamera\n"); #endif } +#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! @@ -1355,7 +1355,7 @@ void vpHomography::computeDisplacement(const vpMatrix H, vT.kill(); vN.kill(); - /**** Déclarations des variables ****/ + /**** Declarations des variables ****/ int cas1 =1, cas2=2, cas3=3, cas4=4; int cas =0; bool norm1ok=false, norm2ok = false,norm3ok=false,norm4ok =false; diff --git a/src/computer-vision/homography-estimation/vpHomographyMalis.cpp b/src/computer-vision/homography-estimation/vpHomographyMalis.cpp index 7a75eca10245ae812a44f91f273b09c973e15c09..0423c113959c3359a0a4c266c7aa4ec48ab155f2 100644 --- a/src/computer-vision/homography-estimation/vpHomographyMalis.cpp +++ b/src/computer-vision/homography-estimation/vpHomographyMalis.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomographyMalis.cpp 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpHomographyMalis.cpp 5264 2015-02-04 13:49:55Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -91,16 +91,36 @@ const double eps = 1e-6 ; **************************************************************************** */ +void changeFrame(unsigned int *pts_ref, + unsigned int nb_pts, + vpMatrix &pd, vpMatrix &p, + vpMatrix &pnd, vpMatrix &pn, + vpMatrix &M, vpMatrix &Mdp); +void HLM2D(unsigned int nb_pts, + vpMatrix &points_des, + vpMatrix &points_cour, + vpMatrix &H); +void HLM3D(unsigned int nb_pts, + vpMatrix &pd, + vpMatrix &p, + vpMatrix &H); +void HLM(unsigned int q_cible, + unsigned int nbpt, + double *xm, double *ym, + double *xmi, double *ymi, + vpMatrix &H); + +void HLM(unsigned int q_cible, + const std::vector<double> &xm, const std::vector<double> &ym, + const std::vector<double> &xmi, const std::vector<double> &ymi, + vpMatrix &H); void changeFrame(unsigned int *pts_ref, - unsigned int nb_pts, - vpMatrix &pd, vpMatrix &p, - vpMatrix &pnd, vpMatrix &pn, - vpMatrix &M, vpMatrix &Mdp) + unsigned int nb_pts, + vpMatrix &pd, vpMatrix &p, + vpMatrix &pnd, vpMatrix &pn, + vpMatrix &M, vpMatrix &Mdp) { - - - unsigned int i,j, k ; unsigned int cont_pts; /* */ double lamb_des[3]; /* */ @@ -240,16 +260,16 @@ HLM2D(unsigned int nb_pts, M[3*j+2][8] = 0 ; } - /** calcul de la pseudo-inverse V de M et des valeurs singulières **/ + /** calcul de la pseudo-inverse V de M et des valeurs singulieres **/ M.svd(sv,V); /***** La meilleure solution est le vecteur de V associe a la valeur singuliere la plus petite en valeur absolu. Pour cela on parcourt la matrice des valeurs singulieres - et on repère la plus petite valeur singulière, on en profite + et on repere la plus petite valeur singuliere, on en profite pour effectuer un controle sur le rang de la matrice : pas plus - de 2 valeurs singulières quasi=0 + de 2 valeurs singulieres quasi=0 *****/ vals_inf = fabs(sv[0]) ; vect = 0 ; @@ -268,7 +288,7 @@ HLM2D(unsigned int nb_pts, } - /** cas d'erreur : plus de 2 valeurs singulières =0 **/ + /** cas d'erreur : plus de 2 valeurs singulieres =0 **/ if (contZeros > 2) { //vpERROR_TRACE("matrix is rank deficient"); throw (vpMatrixException(vpMatrixException::matrixError, @@ -320,10 +340,10 @@ HLM3D(unsigned int nb_pts, unsigned int cont_pts; /* Pour compter le nombre de points dans l'image */ //unsigned int nl; /*** Nombre de lignes ***/ unsigned int nc ; /*** Nombre de colonnes ***/ - unsigned int pts_ref[4]; /*** définit lesquels des points de - l'image sont les points de référence***/ + unsigned int pts_ref[4]; /*** definit lesquels des points de + l'image sont les points de reference***/ /*** ***/ - int perm; /*** Compte le nombre de permutations, quand le nombre + unsigned int perm; /*** Compte le nombre de permutations, quand le nombre de permutations =0 arret de l'ordonnancement **/ int cont_zeros; /*** pour compter les valeurs quasi= a zero ***/ unsigned int cont; @@ -345,10 +365,10 @@ HLM3D(unsigned int nb_pts, vpMatrix H_int(3,3) ; - vpMatrix pn((nb_pts-3),3) ; //points courant nouveau repère + vpMatrix pn((nb_pts-3),3) ; //points courant nouveau repere - vpMatrix pnd((nb_pts-3),3) ; //points dérivés nouveau repère + vpMatrix pnd((nb_pts-3),3) ; //points derives nouveau repere /* preparation du changement de repere */ /**** @@ -491,7 +511,7 @@ HLM3D(unsigned int nb_pts, pour obtenir une solution il faut au moins 5 equations independantes donc il faut au moins la mise en correspondence de 3+5 points *****/ - vpColVector sv(nc) ; //Vecteur contenant les valeurs singulières + vpColVector sv(nc) ; //Vecteur contenant les valeurs singulieres CtC.svd(sv,V) ; @@ -512,18 +532,18 @@ HLM3D(unsigned int nb_pts, perm = 0; for (i=1; i < nc ;i++) { if (svSorted[i-1] > svSorted[i]) { - v_temp = svSorted[i-1] ; - svSorted[i-1] = svSorted[i] ; - svSorted[i] = v_temp ; - perm = perm + 1; + v_temp = svSorted[i-1] ; + svSorted[i-1] = svSorted[i] ; + svSorted[i] = v_temp ; + perm = perm + 1; } } } /***** - Parcours de la matrice ordonnée des valeurs singulières - On note "cont_zeros" le nbre de valeurs quasi= à 0. - On note "vect" le rang de la plus petite valeur singlière + Parcours de la matrice ordonnee des valeurs singulieres + On note "cont_zeros" le nbre de valeurs quasi= a 0. + On note "vect" le rang de la plus petite valeur singliere en valeur absolu *****/ @@ -605,29 +625,30 @@ HLM3D(unsigned int nb_pts, } } +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /************************************************************************** * NOM : * Homographie_CrvMafCalculHomographie * * DESCRIPTION : - * Calcul de l'homographie, en fonction de la cible désirée et de la cible - * en cours. C'est une estimation linéaire. - * Cette procédure n'effectue pas elle-même le calcul de l'homographie : - * elle se contente d'appeler la bonne sous-procédure. - * Cette procédure est appellée par "crv_maf_calcul_tomographie". + * Calcul de l'homographie, en fonction de la cible desiree et de la cible + * en cours. C'est une estimation lineaire. + * Cette procedure n'effectue pas elle-meme le calcul de l'homographie : + * elle se contente d'appeler la bonne sous-procedure. + * Cette procedure est appellee par "crv_maf_calcul_tomographie". * **************************************************************************** * ENTREES : * STR_CIBLE_ASSER *cible_asser Pointeur sur structure contenant les - * commandes du robot, les données de la + * commandes du robot, les donnees de la * carte... * Voir "cvda/edixaa/param/robot.h" - * STR_VITESSE_ROBOT *data_common Pointeur sur la structure décrivant la + * STR_VITESSE_ROBOT *data_common Pointeur sur la structure decrivant la * cible d'asservissement. * Voir "cvda/edixia/param/param.h" * STR_MACH_DIV *machine_div Pointeur sur structure contenant divers - * paramètres de configuration du robot. + * parametres de configuration du robot. * Voir "cvda/edixia/param/param.h" * * SORTIES : @@ -636,7 +657,7 @@ HLM3D(unsigned int nb_pts, * **************************************************************************** - * AUTEUR : BOSSARD Nicolas. INSA Rennes 5ème année. + * AUTEUR : BOSSARD Nicolas. INSA Rennes 5eme annee. * * DATE DE CREATION : 01/12/98 * @@ -690,9 +711,57 @@ HLM(unsigned int q_cible, } /* fin procedure calcul_homogaphie */ +#endif // #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + +void +HLM(unsigned int q_cible, + const std::vector<double> &xm, const std::vector<double> &ym, + const std::vector<double> &xmi, const std::vector<double> &ymi, + vpMatrix &H) +{ + unsigned int nbpt = (unsigned int)xm.size(); + + /**** + on regarde si il y a au moins un point mais pour l'homographie + il faut au moins quatre points + ****/ + vpMatrix pd(nbpt,3) ; + vpMatrix p(nbpt,3) ; + + for (unsigned int i=0;i<nbpt;i++) { + /**** + on assigne les points fournies par la structure robot + pour la commande globale + ****/ + pd[i][0] = xmi[i]; + pd[i][1] = ymi[i]; + pd[i][2] = 1.0 ; + p[i][0] = xm[i]; + p[i][1] = ym[i]; + p[i][2] = 1.0 ; + } + switch (q_cible) { + case (1): + case (2): + /* La cible est planaire de type points */ -#endif + HLM2D(nbpt,pd,p,H); + + break; + case (3) : /* cible non planaire : chateau */ + /* cible non planaire de type points */ + HLM3D(nbpt,pd,p,H); + break; + } /* fin switch */ + + + +} /* fin procedure calcul_homogaphie */ + +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS + +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! \brief Computes the homography matrix from planar \cite TheseMalis or non planar points @@ -712,20 +781,15 @@ HLM(unsigned int q_cible, */ void vpHomography::HLM(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - bool isplanar, - vpHomography &aHb) + double *xb, double *yb, + double *xa, double *ya , + bool isplanar, + vpHomography &aHb) { -#ifndef DOXYGEN_SHOULD_SKIP_THIS - unsigned int i,j; unsigned int q_cible; vpMatrix H; // matrice d'homographie en metre - aHb.setIdentity(); - - if (isplanar) q_cible =1; else @@ -736,7 +800,56 @@ void vpHomography::HLM(unsigned int n, for(i=0;i<3;i++) for(j=0;j<3;j++) aHb[i][j] = H[i][j]; +} + +#endif //#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + +/*! + From couples of matched points \f$^a{\bf p}=(x_a,y_a,1)\f$ in image a + and \f$^b{\bf p}=(x_b,y_b,1)\f$ in image b with homogeneous coordinates, computes the + homography matrix by resolving \f$^a{\bf p} = ^a{\bf H}_b\; ^b{\bf p}\f$ + using Ezio Malis linear method (HLM) \cite Malis00b. + + This method can consider points that are planar or non planar. The algorithm for planar + scene implemented in this file is described in Ezio + Malis PhD thesis \cite TheseMalis. + + \param xb, yb : Coordinates vector of matched points in image b. These coordinates are expressed in meters. + \param xa, ya : Coordinates vector of matched points in image a. These coordinates are expressed in meters. + \param isplanar : If true the points are assumed to be in a plane, + otherwise there are assumed to be non planar. + \param aHb : Estimated homography that relies the transformation from image a to image b. + + If the boolean isplanar is true the points are assumed to be in a plane + otherwise there are assumed to be non planar. + + \sa DLT() when the scene is planar. +*/ +void vpHomography::HLM(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + bool isplanar, + vpHomography &aHb) +{ + unsigned int n = (unsigned int) xb.size(); + if (yb.size() != n || xa.size() != n || ya.size() != n) + throw(vpException(vpException::dimensionError, + "Bad dimension for HLM shomography estimation")); + + // 4 point are required + if(n<4) + throw(vpException(vpException::fatalError, "There must be at least 4 matched points")); + + // The reference plane is the plane build from the 3 first points. + unsigned int q_cible; + vpMatrix H; // matrice d'homographie en metre + + if (isplanar) + q_cible =1; + else + q_cible =3; + + ::HLM(q_cible, xa, ya, xb, yb, H) ; -#endif + aHb = H; } diff --git a/src/computer-vision/homography-estimation/vpHomographyRansac.cpp b/src/computer-vision/homography-estimation/vpHomographyRansac.cpp index d60d8a77db23a306e5c9e0c8002f4e418c76326f..777c0eeb144c63f01d34b991221619e66132d9a7 100644 --- a/src/computer-vision/homography-estimation/vpHomographyRansac.cpp +++ b/src/computer-vision/homography-estimation/vpHomographyRansac.cpp @@ -12,7 +12,7 @@ * Version control * =============== * - * $Id: vpHomographyRansac.cpp 3496 2011-11-22 15:14:32Z fspindle $ + * $Id: vpHomographyRansac.cpp 4649 2014-02-07 14:57:11Z fspindle $ * optimized by Tran to improve speed. * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ @@ -20,6 +20,10 @@ #include <visp/vpColVector.h> #include <visp/vpRansac.h> +#include <visp/vpImage.h> +#include <visp/vpDisplay.h> +#include <visp/vpMeterPixelConversion.h> + #define vpEps 1e-6 /*! @@ -27,6 +31,10 @@ \brief function used to estimate an homography using the Ransac algorithm */ +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +bool iscolinear(double *x1, double *x2, double *x3); +bool isColinear(vpColVector &p1, vpColVector &p2, vpColVector &p3); bool iscolinear(double *x1, double *x2, double *x3) @@ -42,6 +50,11 @@ iscolinear(double *x1, double *x2, double *x3) // scale. return ((vpColVector::cross(p2-p1, p3-p1).sumSquare()) < vpEps); } +bool +isColinear(vpColVector &p1, vpColVector &p2, vpColVector &p3) +{ + return ((vpColVector::cross(p2-p1, p3-p1).sumSquare()) < vpEps); +} bool @@ -149,14 +162,49 @@ vpHomography::degenerateConfiguration(vpColVector &x, unsigned int *ind) iscolinear(pb[0],pb[2],pb[3]) || iscolinear(pb[1],pb[2],pb[3])); } +bool +vpHomography::degenerateConfiguration(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya) +{ + unsigned int n = (unsigned int)xb.size(); + if (n < 4) + throw(vpException(vpException::fatalError, "There must be at least 4 matched points")); + + std::vector<vpColVector> pa(n), pb(n); + for (unsigned i=0; i<n;i++) { + pa[i].resize(3); + pa[i][0] = xa[i]; + pa[i][1] = ya[i]; + pa[i][2] = 1; + pb[i].resize(3); + pb[i][0] = xb[i]; + pb[i][1] = yb[i]; + pb[i][2] = 1; + } + + for (unsigned int i = 0; i < n-2; i++) { + for (unsigned int j = i+1; j < n-1; j++) { + for (unsigned int k = j+1; k < n ; k++) + { + if (isColinear(pa[i], pa[j], pa[k])) { + return true; + } + if (isColinear(pb[i], pb[j], pb[k])){ + return true; + } + } + } + } + return false; +} // Fit model to this random selection of data points. void vpHomography::computeTransformation(vpColVector &x, unsigned int *ind, vpColVector &M) { unsigned int i ; unsigned int n = x.getRows()/4 ; - double xa[4], xb[4]; - double ya[4], yb[4]; + std::vector<double> xa(4), xb(4); + std::vector<double> ya(4), yb(4); unsigned int n2 = n * 2; unsigned int ind2; for(i=0 ; i < 4 ; i++) @@ -171,8 +219,7 @@ vpHomography::computeTransformation(vpColVector &x, unsigned int *ind, vpColVect vpHomography aHb ; try { - vpHomography::HLM(4,xb, yb, xa, ya, true, aHb); - //vpHomography::HLM(8, xb, yb, xa, ya, false, aHb); //modified 13/09 + vpHomography::HLM(xb, yb, xa, ya, true, aHb); } catch(...) { @@ -240,6 +287,7 @@ vpHomography::computeResidual(vpColVector &x, vpColVector &M, vpColVector &d) return 0 ; } +#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS void @@ -261,14 +309,20 @@ vpHomography::initRansac(unsigned int n, } } +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + +/*! + \deprecated This method is deprecated. You should rather use + vpHomography::ransac(const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, vpHomography &, std::vector<bool> &, double &, unsigned int, double, bool) + */ bool vpHomography::ransac(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &aHb, - int consensus, - double threshold - ) + double *xb, double *yb, + double *xa, double *ya , + vpHomography &aHb, + int consensus, + double threshold + ) { vpColVector x ; vpHomography::initRansac(n, xb, yb, xa, ya, x) ; @@ -291,6 +345,8 @@ vpHomography::ransac(unsigned int n, } /*! + \deprecated This method is deprecated. You should rather use + vpHomography::ransac(const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, const std::vector<double> &, vpHomography &, std::vector<bool> &, double &, unsigned int, double, bool) Computes homography matrix \f$ b^H_a \f$ such as \f$X_b = b^H_a X_a \f$ with \f$ X_a = (xa, ya)^t \f$ and \f$ X_b = (xb, yb)^t \f$. @@ -321,14 +377,14 @@ vpHomography::ransac(unsigned int n, */ bool vpHomography::ransac(unsigned int n, - double *xb, double *yb, - double *xa, double *ya , - vpHomography &bHa, - vpColVector &inliers, - double& /* residual */, - int consensus, - double threshold, - double areaThreshold) + double *xb, double *yb, + double *xa, double *ya , + vpHomography &bHa, + vpColVector &inliers, + double /* residual */, + int consensus, + double threshold, + double areaThreshold) { vpColVector x ; vpHomography::initRansac(n, xb, yb, xa, ya, x); @@ -348,9 +404,215 @@ bool vpHomography::ransac(unsigned int n, bHa /= bHa[2][2]; return ransacable; } +#endif //#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +/*! + + From couples of matched points \f$^a{\bf p}=(x_a,y_a,1)\f$ in image a + and \f$^b{\bf p}=(x_b,y_b,1)\f$ in image b with homogeneous coordinates, computes the + homography matrix by resolving \f$^a{\bf p} = ^a{\bf H}_b\; ^b{\bf p}\f$ + using Ransac algorithm. + + \param xb, yb : Coordinates vector of matched points in image b. These coordinates are expressed in meters. + \param xa, ya : Coordinates vector of matched points in image a. These coordinates are expressed in meters. + \param aHb : Estimated homography that relies the transformation from image a to image b. + \param inliers : Vector that indicates if a matched point is an inlier (true) or an outlier (false). + \param residual : Global residual computed as + \f$r = \sqrt{1/n \sum_{inliers} {\| {^a{\bf p} - {\hat{^a{\bf H}_b}} {^b{\bf p}}} \|}^{2}}\f$ with \f$n\f$ the + number of inliers. + + \param nbInliersConsensus : Minimal number of points requested to fit the estimated homography. + + \param threshold : Threshold for outlier removing. A point is considered as an outlier if the reprojection error + \f$\| {^a{\bf p} - {\hat{^a{\bf H}_b}} {^b{\bf p}}} \|\f$ is greater than this threshold. + + \param normalization : When set to true, the coordinates of the points are normalized. The normalization + carried out is the one preconized by Hartley. + + \return true if the homography could be computed, false otherwise. + +*/ +bool vpHomography::ransac(const std::vector<double> &xb, const std::vector<double> &yb, + const std::vector<double> &xa, const std::vector<double> &ya, + vpHomography &aHb, + std::vector<bool> &inliers, + double &residual, + unsigned int nbInliersConsensus, + double threshold, + bool normalization) +{ + unsigned int n = (unsigned int)xb.size(); + if (yb.size() != n || xa.size() != n || ya.size() != n) + throw(vpException(vpException::dimensionError, + "Bad dimension for robust homography estimation")); + + // 4 point are required + if(n<4) + throw(vpException(vpException::fatalError, "There must be at least 4 matched points")); + + vpUniRand random((const long)time(NULL)) ; + + std::vector<unsigned int> best_consensus; + std::vector<unsigned int> cur_consensus; + std::vector<unsigned int> cur_outliers; + std::vector<unsigned int> cur_randoms; + + std::vector<unsigned int> rand_ind; + + unsigned int nbMinRandom = 4 ; + unsigned int ransacMaxTrials = 1000; + unsigned int maxDegenerateIter = 1000; + + unsigned int nbTrials = 0; + unsigned int nbDegenerateIter = 0; + unsigned int nbInliers = 0; + + bool foundSolution = false; + + std::vector<double> xa_rand(nbMinRandom); + std::vector<double> ya_rand(nbMinRandom); + std::vector<double> xb_rand(nbMinRandom); + std::vector<double> yb_rand(nbMinRandom); + + if (inliers.size() != n) + inliers.resize(n); + + while (nbTrials < ransacMaxTrials && nbInliers < nbInliersConsensus) + { + cur_outliers.clear(); + cur_randoms.clear(); + + bool degenerate = true; + while(degenerate == true){ + std::vector<bool> usedPt(n, false); + + rand_ind.clear(); + for(unsigned int i = 0; i < nbMinRandom; i++) + { + // Generate random indicies in the range 0..n + unsigned int r = (unsigned int)ceil(random()*n) -1; + while(usedPt[r]) { + r = (unsigned int)ceil(random()*n) -1; + } + usedPt[r] = true; + rand_ind.push_back(r); + + xa_rand[i] = xa[r]; + ya_rand[i] = ya[r]; + xb_rand[i] = xb[r]; + yb_rand[i] = yb[r]; + } + + try{ + if (! vpHomography::degenerateConfiguration(xb_rand, yb_rand, xa_rand, ya_rand)) { + vpHomography::DLT(xb_rand, yb_rand, xa_rand, ya_rand, aHb, normalization); + degenerate = false; + } + } + catch(...){ + degenerate = true; + } + + nbDegenerateIter ++; + + if (nbDegenerateIter > maxDegenerateIter){ + vpERROR_TRACE("Unable to select a nondegenerate data set"); + throw(vpException(vpException::fatalError, "Unable to select a nondegenerate data set")); + } + } + + aHb /= aHb[2][2] ; + + // Computing Residual + double r = 0; + vpColVector a(3), b(3), c(3) ; + for (unsigned int i=0 ; i < nbMinRandom ; i++) { + a[0] = xa_rand[i] ; a[1] = ya_rand[i] ; a[2] = 1 ; + b[0] = xb_rand[i] ; b[1] = yb_rand[i] ; b[2] = 1 ; + + c = aHb*b; c /= c[2] ; + r += (a-c).sumSquare() ; + //cout << "point " <<i << " " << (a-c).sumSquare() <<endl ;; + } + + // Finding inliers & ouliers + r = sqrt(r/nbMinRandom); + //std::cout << "Candidate residual: " << r << std::endl; + if (r < threshold) + { + unsigned int nbInliersCur = 0; + for (unsigned int i = 0; i < n ; i++) + { + a[0] = xa[i] ; a[1] = ya[i] ; a[2] = 1 ; + b[0] = xb[i] ; b[1] = yb[i] ; b[2] = 1 ; + + c = aHb*b ; c /= c[2] ; + double error = sqrt((a-c).sumSquare()) ; + if(error <= threshold){ + nbInliersCur++; + cur_consensus.push_back(i); + inliers[i] = true; + } + else{ + cur_outliers.push_back(i); + inliers[i] = false; + } + } + //std::cout << "nb inliers that matches: " << nbInliersCur << std::endl; + if(nbInliersCur > nbInliers) + { + foundSolution = true; + best_consensus = cur_consensus; + nbInliers = nbInliersCur; + } + + cur_consensus.clear(); + } + + nbTrials++; + if(nbTrials >= ransacMaxTrials){ + vpERROR_TRACE("Ransac reached the maximum number of trials"); + foundSolution = true; + } + } + + if(foundSolution){ + if(nbInliers >= nbInliersConsensus) + { + std::vector<double> xa_best(best_consensus.size()); + std::vector<double> ya_best(best_consensus.size()); + std::vector<double> xb_best(best_consensus.size()); + std::vector<double> yb_best(best_consensus.size()); + + for(unsigned i = 0 ; i < best_consensus.size(); i++) + { + xa_best[i] = xa[best_consensus[i]]; + ya_best[i] = ya[best_consensus[i]]; + xb_best[i] = xb[best_consensus[i]]; + yb_best[i] = yb[best_consensus[i]]; + } + + vpHomography::DLT(xb_best, yb_best, xa_best, ya_best, aHb, normalization) ; + aHb /= aHb[2][2]; + + residual = 0 ; + vpColVector a(3), b(3), c(3); + for (unsigned int i=0 ; i < best_consensus.size() ; i++) { + a[0] = xa_best[i] ; a[1] = ya_best[i] ; a[2] = 1 ; + b[0] = xb_best[i] ; b[1] = yb_best[i] ; b[2] = 1 ; + + c = aHb*b ; c /= c[2] ; + residual += (a-c).sumSquare() ; + } + + residual = sqrt(residual/best_consensus.size()); + return true; + } + else { + return false; + } + } + else { + return false; + } +} diff --git a/src/computer-vision/homography-estimation/vpHomographyVVS.cpp b/src/computer-vision/homography-estimation/vpHomographyVVS.cpp index bbb25b6c7410d3de049a0ebef22a89202954ace4..9d8688d7176c785a33b46158b1c7c24c419de844 100644 --- a/src/computer-vision/homography-estimation/vpHomographyVVS.cpp +++ b/src/computer-vision/homography-estimation/vpHomographyVVS.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomographyVVS.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHomographyVVS.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,6 +55,8 @@ const double vpHomography::threshold_rotation = 1e-7; const double vpHomography::threshold_displacement = 1e-18; +#ifndef DOXYGEN_SHOULD_SKIP_THIS + static void updatePoseRotation(vpColVector& dx,vpHomogeneousMatrix& mati) { @@ -126,14 +128,16 @@ vpHomography::computeRotation(unsigned int nbpoint, unsigned int n=0 ; for (unsigned int i=0 ; i < nbpoint ; i++) { // if ((c2P[i].get_x() !=-1) && (c1P[i].get_x() !=-1)) - if ( (std::fabs(c2P[i].get_x() + 1) > std::fabs(vpMath::maximum(c2P[i].get_x(), 1.))) - && - (std::fabs(c1P[i].get_x() + 1) > std::fabs(vpMath::maximum(c1P[i].get_x(), 1.))) ) - { - n++ ; - } + if ( (std::fabs(c2P[i].get_x() + 1) > std::fabs(vpMath::maximum(c2P[i].get_x(), 1.))) + && + (std::fabs(c1P[i].get_x() + 1) > std::fabs(vpMath::maximum(c1P[i].get_x(), 1.))) ) + { + n++ ; + } } - if ((only_1==1) || (only_2==1)) ; else n *=2 ; + //if ((only_1==1) || (only_2==1)) ; else n *=2 ; + if ( (! only_1) && (! only_2) ) + n *=2 ; vpRobust robust(n); vpColVector res(n) ; @@ -159,9 +163,9 @@ vpHomography::computeRotation(unsigned int nbpoint, int k =0 ; for (unsigned int i=0 ; i < nbpoint ; i++) { //if ((c2P[i].get_x() !=-1) && (c1P[i].get_x() !=-1)) - if ( (std::fabs(c2P[i].get_x() + 1) > std::fabs(vpMath::maximum(c2P[i].get_x(), 1.))) - && - (std::fabs(c1P[i].get_x() + 1) > std::fabs(vpMath::maximum(c1P[i].get_x(), 1.))) ) + if ( (std::fabs(c2P[i].get_x() + 1) > std::fabs(vpMath::maximum(c2P[i].get_x(), 1.))) + && + (std::fabs(c1P[i].get_x() + 1) > std::fabs(vpMath::maximum(c1P[i].get_x(), 1.))) ) { p2[0] = c2P[i].get_x() ; p2[1] = c2P[i].get_y() ; @@ -240,35 +244,35 @@ vpHomography::computeRotation(unsigned int nbpoint, { robust.setIteration(0); - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int l=0 ; l < n ; l++) { - res[k] = vpMath::sqr(e[2*k]) + vpMath::sqr(e[2*k+1]) ; + res[l] = vpMath::sqr(e[2*l]) + vpMath::sqr(e[2*l+1]) ; } robust.MEstimator(vpRobust::TUKEY, res, w); // compute the pseudo inverse of the interaction matrix - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int l=0 ; l < n ; l++) { - W[2*k][2*k] = w[k] ; - W[2*k+1][2*k+1] = w[k] ; + W[2*l][2*l] = w[l] ; + W[2*l+1][2*l+1] = w[l] ; } } else { - for (unsigned int k=0 ; k < 2*n ; k++) W[k][k] = 1 ; + for (unsigned int l=0 ; l < 2*n ; l++) W[l][l] = 1 ; } // CreateDiagonalMatrix(w, W) ; (L).pseudoInverse(Lp, 1e-6) ; // Compute the camera velocity - vpColVector c2Rc1, v(6) ; + vpColVector c2rc1, v(6) ; - c2Rc1 = -2*Lp*W*e ; - for (unsigned int i=0 ; i < 3 ; i++) v[i+3] = c2Rc1[i] ; + c2rc1 = -2*Lp*W*e ; + for (unsigned int i=0 ; i < 3 ; i++) v[i+3] = c2rc1[i] ; // only for simulation - updatePoseRotation(c2Rc1, c2Mc1) ; + updatePoseRotation(c2rc1, c2Mc1) ; r =e.sumSquare() ; if ((W*e).sumSquare() < 1e-10) break ; @@ -307,13 +311,9 @@ vpHomography::computeDisplacement(unsigned int nbpoint, int userobust ) { - - vpColVector e(2) ; double r_1 = -1 ; - - vpColVector p2(3) ; vpColVector p1(3) ; vpColVector Hp2(3) ; @@ -324,13 +324,17 @@ vpHomography::computeDisplacement(unsigned int nbpoint, vpMatrix H1(2,6) ; vpColVector e1(2) ; - int only_1 = 1 ; - int only_2 = 0 ; + int only_1; + int only_2; int iter = 0 ; - unsigned int i ; unsigned int n=0 ; n = nbpoint ; - if ((only_1==1) || (only_2==1)) ; else n *=2 ; + only_1 = 1 ; + only_2 = 0 ; + + //if ((only_1==1) || (only_2==1)) ; else n *=2 ; + if ( (! only_1) && (! only_2) ) + n *=2 ; vpRobust robust(n); vpColVector res(n) ; @@ -347,11 +351,9 @@ vpHomography::computeDisplacement(unsigned int nbpoint, iter =0 ; while (vpMath::equal(r_1,r,threshold_displacement) == false ) { - r_1 =r ; // compute current position - //Change frame (current) vpHomogeneousMatrix c1Mc2, c2Mo ; vpRotationMatrix c1Rc2, c2Rc1 ; @@ -367,10 +369,9 @@ vpHomography::computeDisplacement(unsigned int nbpoint, getPlaneInfo(oN, c1Mo, N1, d1) ; getPlaneInfo(oN, c2Mo, N2, d2) ; - vpMatrix L(2,3), Lp ; int k =0 ; - for (i=0 ; i < nbpoint ; i++) + for (unsigned int i=0 ; i < nbpoint ; i++) { p2[0] = c2P[i].get_x() ; p2[1] = c2P[i].get_y() ; @@ -405,18 +406,16 @@ vpHomography::computeDisplacement(unsigned int nbpoint, vpMatrix c1CFc2(6,6) ; { vpMatrix sTR = c1Tc2.skew()*(vpMatrix)c1Rc2 ; - for (unsigned int k=0 ; k < 3 ; k++) + for (unsigned int k_=0 ; k_ < 3 ; k_++) for (unsigned int l=0 ; l<3 ; l++) { - c1CFc2[k][l] = c1Rc2[k][l] ; - c1CFc2[k+3][l+3] = c1Rc2[k][l] ; - c1CFc2[k][l+3] = sTR[k][l] ; + c1CFc2[k_][l] = c1Rc2[k_][l] ; + c1CFc2[k_+3][l+3] = c1Rc2[k_][l] ; + c1CFc2[k_][l+3] = sTR[k_][l] ; } } H2 = H2*c1CFc2 ; - - // Set up the error vector e2[0] = Hp2[0] - c1P[i].get_x() ; e2[1] = Hp2[1] - c1P[i].get_y() ; @@ -435,7 +434,6 @@ vpHomography::computeDisplacement(unsigned int nbpoint, e1[0] = Hp1[0] - c2P[i].get_x() ; e1[1] = Hp1[1] - c2P[i].get_y() ; - if (only_2==1) { if (k == 0) { L = H2 ; e = e2 ; } @@ -474,23 +472,22 @@ vpHomography::computeDisplacement(unsigned int nbpoint, if (userobust) { robust.setIteration(0); - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int l=0 ; l < n ; l++) { - res[k] = vpMath::sqr(e[2*k]) + vpMath::sqr(e[2*k+1]) ; + res[l] = vpMath::sqr(e[2*l]) + vpMath::sqr(e[2*l+1]) ; } robust.MEstimator(vpRobust::TUKEY, res, w); - // compute the pseudo inverse of the interaction matrix - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int l=0 ; l < n ; l++) { - W[2*k][2*k] = w[k] ; - W[2*k+1][2*k+1] = w[k] ; + W[2*l][2*l] = w[l] ; + W[2*l+1][2*l+1] = w[l] ; } } else { - for (unsigned int k=0 ; k < 2*n ; k++) W[k][k] = 1 ; + for (unsigned int l=0 ; l < 2*n ; l++) W[l][l] = 1 ; } (W*L).pseudoInverse(Lp, 1e-16) ; // Compute the camera velocity @@ -504,8 +501,6 @@ vpHomography::computeDisplacement(unsigned int nbpoint, // UpdatePose2(c2Tcc1, c2Mc1) ; r =(W*e).sumSquare() ; - - if (r < 1e-15) {break ; } if (iter>1000){break ; } if (r>r_1) { break ; } @@ -532,8 +527,6 @@ vpHomography::computeDisplacement(unsigned int nbpoint, vpColVector e(2) ; double r_1 = -1 ; - - vpColVector p2(3) ; vpColVector p1(3) ; vpColVector Hp2(3) ; @@ -545,13 +538,17 @@ vpHomography::computeDisplacement(unsigned int nbpoint, vpColVector e1(2) ; - int only_1 = 1 ; - int only_2 = 0 ; + int only_1; + int only_2; int iter = 0 ; unsigned int i ; unsigned int n=0 ; + only_1 = 1 ; + only_2 = 0 ; n = nbpoint ; - if ((only_1==1) || (only_2==1)) ; else n *=2 ; + // if ((only_1==1) || (only_2==1)) ; else n *=2 ; + if ( (! only_1) && (! only_2) ) + n *=2 ; vpRobust robust(n); vpColVector res(n) ; @@ -626,18 +623,16 @@ vpHomography::computeDisplacement(unsigned int nbpoint, vpMatrix c1CFc2(6,6) ; { vpMatrix sTR = c1Tc2.skew()*(vpMatrix)c1Rc2 ; - for (unsigned int k=0 ; k < 3 ; k++) + for (unsigned int k_=0 ; k_ < 3 ; k_++) for (unsigned int l=0 ; l<3 ; l++) { - c1CFc2[k][l] = c1Rc2[k][l] ; - c1CFc2[k+3][l+3] = c1Rc2[k][l] ; - c1CFc2[k][l+3] = sTR[k][l] ; + c1CFc2[k_][l] = c1Rc2[k_][l] ; + c1CFc2[k_+3][l+3] = c1Rc2[k_][l] ; + c1CFc2[k_][l+3] = sTR[k_][l] ; } } H2 = H2*c1CFc2 ; - - // Set up the error vector e2[0] = Hp2[0] - c1P[i].get_x() ; e2[1] = Hp2[1] - c1P[i].get_y() ; @@ -695,23 +690,23 @@ vpHomography::computeDisplacement(unsigned int nbpoint, if (userobust) { robust.setIteration(0); - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int k_=0 ; k_ < n ; k_++) { - res[k] = vpMath::sqr(e[2*k]) + vpMath::sqr(e[2*k+1]) ; + res[k_] = vpMath::sqr(e[2*k_]) + vpMath::sqr(e[2*k_+1]) ; } robust.MEstimator(vpRobust::TUKEY, res, w); // compute the pseudo inverse of the interaction matrix - for (unsigned int k=0 ; k < n ; k++) + for (unsigned int k_=0 ; k_ < n ; k_++) { - W[2*k][2*k] = w[k] ; - W[2*k+1][2*k+1] = w[k] ; + W[2*k_][2*k_] = w[k_] ; + W[2*k_+1][2*k_+1] = w[k_] ; } } else { - for (unsigned int k=0 ; k < 2*n ; k++) W[k][k] = 1 ; + for (unsigned int k_=0 ; k_ < 2*n ; k_++) W[k_][k_] = 1 ; } (W*L).pseudoInverse(Lp, 1e-16) ; // Compute the camera velocity @@ -737,3 +732,4 @@ vpHomography::computeDisplacement(unsigned int nbpoint, } +#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/computer-vision/pose-estimation/vpLevenbergMarquartd.cpp b/src/computer-vision/pose-estimation/vpLevenbergMarquartd.cpp index 2f4c7aaf18babba5f2f26ae18e0ba8ff9e18c9f7..47c24794d8c0f1144c941bbef0dabdf0e547f626 100644 --- a/src/computer-vision/pose-estimation/vpLevenbergMarquartd.cpp +++ b/src/computer-vision/pose-estimation/vpLevenbergMarquartd.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLevenbergMarquartd.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLevenbergMarquartd.cpp 5107 2015-01-05 07:47:13Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,12 +40,13 @@ * *****************************************************************************/ -#include <visp/vpLevenbergMarquartd.h> -#include <visp/vpMath.h> - #include <cmath> // std::fabs #include <limits> // numeric_limits #include <iostream> +#include <algorithm> // std::min + +#include <visp/vpLevenbergMarquartd.h> +#include <visp/vpMath.h> #define SIGN(x) ((x) < 0 ? -1 : 1) #define SWAP(a,b,c) {(c) = (a); (a) = (b); (b) = (c);} @@ -233,7 +234,7 @@ double enorm (const double *x, int n) * */ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, - double *delta, double *par, double *x, double *sdiag, double *wa1, double *wa2) + double *delta, double *par, double *x, double *sdiag, double *wa1, double *wa2) { const double tol1 = 0.1, tol001 = 0.001; /* tolerance a 0.1 et a 0.001 */ @@ -276,8 +277,8 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, if (jm1 >= 0) { - for (unsigned int j = 0; j <= (unsigned int)jm1; j++) - wa1[j] -= *MIJ(r, i, j, ldr) * temp; + for (unsigned int j = 0; j <= (unsigned int)jm1; j++) + wa1[j] -= *MIJ(r, i, j, ldr) * temp; } } } @@ -315,22 +316,22 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, { for (int i = 0; i < n; i++) { - l = ipvt[i]; - wa1[i] = diag[l] * (wa2[l] / dxnorm); + l = ipvt[i]; + wa1[i] = diag[l] * (wa2[l] / dxnorm); } for (int i = 0; i < n; i++) { - long im1; - sum = 0.0; - im1 = (i - 1L); - - if (im1 >= 0) - { - for (unsigned int j = 0; j <= (unsigned int)im1; j++) - sum += (*MIJ(r, i, j, ldr) * wa1[j]); - } - wa1[i] = (wa1[i] - sum) / *MIJ(r, i, i, ldr); + long im1; + sum = 0.0; + im1 = (i - 1L); + + if (im1 >= 0) + { + for (unsigned int j = 0; j <= (unsigned int)im1; j++) + sum += (*MIJ(r, i, j, ldr) * wa1[j]); + } + wa1[i] = (wa1[i] - sum) / *MIJ(r, i, i, ldr); } temp = enorm(wa1, n); @@ -346,7 +347,7 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, sum = 0.0; for (int j = 0; j <= i; j++) - sum += *MIJ(r, i, j, ldr) * qtb[j]; + sum += *MIJ(r, i, j, ldr) * qtb[j]; l = ipvt[i]; wa1[i] = sum / diag[l]; @@ -384,17 +385,17 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, */ //if (*par == 0.0) if (std::fabs(*par) <= std::numeric_limits<double>::epsilon() ) - *par = vpMath::maximum(dwarf, (tol001 * paru)); + *par = vpMath::maximum(dwarf, (tol001 * paru)); temp = sqrt(*par); for (int i = 0; i < n; i++) - wa1[i] = temp * diag[i]; + wa1[i] = temp * diag[i]; qrsolv(n, r, ldr, ipvt, wa1, qtb, x, sdiag, wa2); for (int i = 0; i < n; i++) - wa2[i] = diag[i] * x[i]; + wa2[i] = diag[i] * x[i]; dxnorm = enorm(wa2, n); temp = fp; @@ -409,16 +410,15 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, //if ((std::fabs(fp) <= tol1 * (*delta)) || ((parl == 0.0) && (fp <= temp) // && (temp < 0.0)) || (iter == 10)) if ((std::fabs(fp) <= tol1 * (*delta)) || ((std::fabs(parl) <= std::numeric_limits<double>::epsilon()) && (fp <= temp) - && (temp < 0.0)) || (iter == 10)) + && (temp < 0.0)) || (iter == 10)) { - /* - * terminaison. - */ + // terminaison. - if (iter == 0) - *par = 0.0; + // Remove the two next lines since this is a dead code + /* if (iter == 0) + *par = 0.0; */ - return (0); + return (0); } /* @@ -427,20 +427,20 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, for (int i = 0; i < n; i++) { - l = ipvt[i]; - wa1[i] = diag[l] * (wa2[l] / dxnorm); + l = ipvt[i]; + wa1[i] = diag[l] * (wa2[l] / dxnorm); } for (unsigned int i = 0; i < (unsigned int)n; i++) { - wa1[i] = wa1[i] / sdiag[i]; - temp = wa1[i]; - jp1 = i + 1; - if ( (unsigned int) n >= jp1) - { - for (unsigned int j = jp1; j < (unsigned int)n; j++) - wa1[j] -= (*MIJ(r, i, j, ldr) * temp); - } + wa1[i] = wa1[i] / sdiag[i]; + temp = wa1[i]; + jp1 = i + 1; + if ( (unsigned int) n >= jp1) + { + for (unsigned int j = jp1; j < (unsigned int)n; j++) + wa1[j] -= (*MIJ(r, i, j, ldr) * temp); + } } temp = enorm(wa1, n); @@ -451,10 +451,10 @@ int lmpar(int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, * de parl ou paru. */ if (fp > 0.0) - parl = vpMath::maximum(parl, *par); + parl = vpMath::maximum(parl, *par); if (fp < 0.0) - paru = vpMath::minimum(paru, *par); + paru = vpMath::minimum(paru, *par); /* * calcul d'une estimee ameliree de "par". @@ -575,7 +575,7 @@ int qrfac(int m, int n, double *a, int lda, int *pivot, int *ipvt, /* * epsmch est la precision machine. */ - epsmch = DBL_EPSILON; + epsmch = std::numeric_limits<double>::epsilon(); /* * calcul des normes initiales des lignes et initialisation @@ -857,7 +857,7 @@ int qrsolv (int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, /* * resolution du systeme triangulaire pour z. Si le systeme est - * singulier, on obtient une solution au moindres carrés. + * singulier, on obtient une solution au moindres carres. */ nsing = n; @@ -917,7 +917,7 @@ int qrsolv (int n, double *r, int ldr, int *ipvt, double *diag, double *qtb, * xtol Erreur relative desiree dans la solution approximee. La * terminaison survient quand l'erreur relative entre deux * iterations consecutives est au moins egal a xtol. - * gtol Mesure de l'orthogonalité entre le vecteur des fonctions et les + * gtol Mesure de l'orthogonalite entre le vecteur des fonctions et les * colonnes du jacobien. La terminaison survient quand le cosinus * de l'angle entre fvec et n'importe quelle colonne du jacobien * est au moins egal a gtol, en valeur absolue. @@ -1025,12 +1025,12 @@ int lmder (void (*ptr_fcn)(int m, int n, double *xc, double *fvecc, int count = 0; int i, j, l; double actred, delta, dirder, epsmch, fnorm, fnorm1, gnorm; - double ratio = DBL_EPSILON; + double ratio = std::numeric_limits<double>::epsilon(); double par, pnorm, prered; double sum, temp, temp1, temp2, xnorm = 0.0; /* epsmch est la precision machine. */ - epsmch = DBL_EPSILON; + epsmch = std::numeric_limits<double>::epsilon(); *info = 0; iflag = 0; diff --git a/src/computer-vision/pose-estimation/vpLevenbergMarquartd.h b/src/computer-vision/pose-estimation/vpLevenbergMarquartd.h index 36ea86d6658f10c6ed6d6c50487b8bc147b9119e..a746f59a44f380174571c9fa239a35248a301ed1 100644 --- a/src/computer-vision/pose-estimation/vpLevenbergMarquartd.h +++ b/src/computer-vision/pose-estimation/vpLevenbergMarquartd.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLevenbergMarquartd.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLevenbergMarquartd.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/computer-vision/pose-estimation/vpPose.cpp b/src/computer-vision/pose-estimation/vpPose.cpp index 292e2b0b2989c39e30c7b85d6b62c10610ef87f3..ab652586684e2bf3604d67a8d37af2aef77f38f7 100644 --- a/src/computer-vision/pose-estimation/vpPose.cpp +++ b/src/computer-vision/pose-estimation/vpPose.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpPose.cpp 4056 2013-01-05 13:04:42Z fspindle $ +* $Id: vpPose.cpp 5212 2015-01-26 17:45:45Z strinh $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,11 +71,10 @@ vpPose::init() #endif npt = 0 ; listP.clear() ; + c3d.clear(); lambda = 0.25 ; - c3d = NULL ; - vvsIterMax = 200 ; distanceToPlaneForCoplanarityTest = 0.001 ; @@ -86,13 +85,19 @@ vpPose::init() ransacThreshold = 0.0001; ransacNbInlierConsensus = 4; + residual = 0; #if (DEBUG_LEVEL1) std::cout << "end vpPose::Init() " << std::endl ; #endif } +/*! Defaukt constructor. */ vpPose::vpPose() + : npt(0), listP(), residual(0), lambda(0.25), vvsIterMax(200), c3d(), + computeCovariance(false), covarianceMatrix(), + ransacNbInlierConsensus(4), ransacMaxTrials(1000), ransacInliers(), ransacThreshold(0.0001), + distanceToPlaneForCoplanarityTest(0.001) { #if (DEBUG_LEVEL1) std::cout << "begin vpPose::vpPose() " << std::endl ; @@ -107,7 +112,7 @@ vpPose::vpPose() } /*! -\brief destructor delete the array of point (freed the memory) + Destructor that deletes the array of point (freed the memory). */ vpPose::~vpPose() { @@ -122,7 +127,7 @@ vpPose::~vpPose() #endif } /*! -\brief delete the array of point + Delete the array of point */ void vpPose::clearPoint() @@ -338,8 +343,7 @@ the pose matrix 'cMo'. double vpPose::computeResidual(const vpHomogeneousMatrix &cMo) const { - - double residual = 0 ; + double residual_ = 0 ; vpPoint P ; for(std::list<vpPoint>::const_iterator it=listP.begin(); it != listP.end(); ++it) { @@ -349,9 +353,9 @@ vpPose::computeResidual(const vpHomogeneousMatrix &cMo) const P.track(cMo) ; - residual += vpMath::sqr(x-P.get_x()) + vpMath::sqr(y-P.get_y()) ; + residual_ += vpMath::sqr(x-P.get_x()) + vpMath::sqr(y-P.get_y()) ; } - return residual ; + return residual_ ; } @@ -378,8 +382,8 @@ LAGRANGE_VIRTUAL_VS Virtual visual servoing initialized using Lagrange approach */ -void -vpPose::computePose(vpPoseMethodType methode, vpHomogeneousMatrix& cMo) +bool +vpPose::computePose(vpPoseMethodType methode, vpHomogeneousMatrix& cMo, bool (*func)(vpHomogeneousMatrix *)) { #if (DEBUG_LEVEL1) std::cout << "begin vpPose::ComputePose()" << std::endl ; @@ -505,7 +509,7 @@ vpPose::computePose(vpPoseMethodType methode, vpHomogeneousMatrix& cMo) "Not enough points ")) ; } try { - poseRansac(cMo); + return poseRansac(cMo, func); } catch(...) { @@ -559,6 +563,9 @@ vpPose::computePose(vpPoseMethodType methode, vpHomogeneousMatrix& cMo) #if (DEBUG_LEVEL1) std::cout << "end vpPose::ComputePose()" << std::endl ; #endif + + //If here, there was no exception thrown so return true + return true; } @@ -672,8 +679,8 @@ vpPose::poseFromRectangle(vpPoint &p1,vpPoint &p2, vpHomogeneousMatrix & cMo) { - double rectx[4] ; - double recty[4] ; + std::vector<double> rectx(4) ; + std::vector<double> recty(4) ; rectx[0]= 0 ; recty[0]=0 ; rectx[1]=1 ; @@ -682,8 +689,8 @@ vpPose::poseFromRectangle(vpPoint &p1,vpPoint &p2, recty[2]=1 ; rectx[3]=0 ; recty[3]=1 ; - double irectx[4] ; - double irecty[4] ; + std::vector<double> irectx(4) ; + std::vector<double> irecty(4) ; irectx[0]=(p1.get_x()) ; irecty[0]=(p1.get_y()) ; irectx[1]=(p2.get_x()) ; @@ -697,8 +704,8 @@ vpPose::poseFromRectangle(vpPoint &p1,vpPoint &p2, vpMatrix H(3,3); vpHomography hom; - // vpHomography::HartleyDLT(4,rectx,recty,irectx,irecty,hom); - vpHomography::HLM(4,rectx,recty,irectx,irecty,1,hom); + // vpHomography::HartleyDLT(rectx,recty,irectx,irecty,hom); + vpHomography::HLM(rectx,recty,irectx,irecty,1,hom); for (unsigned int i=0 ; i < 3 ; i++) for(unsigned int j=0 ; j < 3 ; j++) H[i][j] = hom[i][j] ; @@ -711,8 +718,8 @@ vpPose::poseFromRectangle(vpPoint &p1,vpPoint &p2, vpMatrix Kinv =K.pseudoInverse(); vpMatrix KinvH =Kinv*H; - kh1=KinvH.column(1); - kh2=KinvH.column(2); + kh1=KinvH.getCol(0); + kh2=KinvH.getCol(1); double s= sqrt(kh1.sumSquare())/sqrt(kh2.sumSquare()); diff --git a/src/computer-vision/pose-estimation/vpPose.h b/src/computer-vision/pose-estimation/vpPose.h index 282718cf670e40295de3b7e046cd9bdf78a66ddf..191aea118bebdca34efd1b0d5cac85ad9ba82ab4 100644 --- a/src/computer-vision/pose-estimation/vpPose.h +++ b/src/computer-vision/pose-estimation/vpPose.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPose.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPose.h 5211 2015-01-26 17:44:35Z strinh $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -102,7 +102,7 @@ protected : private: int vvsIterMax ; //! define the maximum number of iteration in VVS //! variable used in the Dementhon approach - vpPoint *c3d ; + std::vector<vpPoint> c3d ; //! Flag used to specify if the covariance matrix has to be computed or not. bool computeCovariance; //! Covariance matrix @@ -120,7 +120,7 @@ protected: int calculArbreDementhon(vpMatrix &b, vpColVector &U, vpHomogeneousMatrix &cMo) ; public: - //! constructor + // constructor vpPose() ; //! destructor virtual ~vpPose() ; @@ -130,7 +130,7 @@ public: void clearPoint() ; //! compute the pose for a given method - void computePose(vpPoseMethodType methode, vpHomogeneousMatrix &cMo) ; + bool computePose(vpPoseMethodType methode, vpHomogeneousMatrix &cMo, bool (*func)(vpHomogeneousMatrix *)=NULL) ; //! compute the residual (i.e., the quality of the result) //! compute the residual (in meter for pose M) double computeResidual(const vpHomogeneousMatrix &cMo) const ; @@ -156,7 +156,7 @@ public: //! Levenberg Marquartd non linear minimization approach) void poseLowe(vpHomogeneousMatrix & cMo) ; //! compute the pose using the Ransac approach - void poseRansac(vpHomogeneousMatrix & cMo) ; + bool poseRansac(vpHomogeneousMatrix & cMo, bool (*func)(vpHomogeneousMatrix *)=NULL) ; //! compute the pose using a robust virtual visual servoing approach void poseVirtualVSrobust(vpHomogeneousMatrix & cMo) ; //! compute the pose using virtual visual servoing approach @@ -167,10 +167,17 @@ public: void setVvsIterMax(int nb) { vvsIterMax = nb ; } void setRansacNbInliersToReachConsensus(const unsigned int &nbC){ ransacNbInlierConsensus = nbC; } - void setRansacThreshold(const double &t){ ransacThreshold = t; } + void setRansacThreshold(const double &t) { + //Test whether or not t is > 0 + if(t > 0) { + ransacThreshold = t; + } else { + throw vpException(vpException::badValue, "The Ransac threshold must be positive as we deal with distance."); + } + } void setRansacMaxTrials(const int &rM){ ransacMaxTrials = rM; } - unsigned int getRansacNbInliers(){ return (unsigned int) ransacInliers.size(); } - std::vector<vpPoint> getRansacInliers(){ return ransacInliers; } + unsigned int getRansacNbInliers() const { return (unsigned int) ransacInliers.size(); } + std::vector<vpPoint> getRansacInliers() const{ return ransacInliers; } /*! Set if the covaraince matrix has to be computed in the Virtual Visual Servoing approach. diff --git a/src/computer-vision/pose-estimation/vpPoseDementhon.cpp b/src/computer-vision/pose-estimation/vpPoseDementhon.cpp index 219062520e0f7acf19aeb1ca67ae115b6a1cd5bb..58edca4e248f214b39fe89fcdff393821fbf07a9 100644 --- a/src/computer-vision/pose-estimation/vpPoseDementhon.cpp +++ b/src/computer-vision/pose-estimation/vpPoseDementhon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpPoseDementhon.cpp 4276 2013-06-25 12:36:48Z fspindle $ +* $Id: vpPoseDementhon.cpp 5198 2015-01-23 17:32:04Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,34 +70,20 @@ vpPose::poseDementhonNonPlan(vpHomogeneousMatrix &cMo) double seuil=1.0; double f=1.; - // CPoint c3d[npt] ; - - if (c3d !=NULL) delete [] c3d ; - c3d = new vpPoint[npt] ; - vpPoint p0 = listP.front() ; - vpPoint P ; - int i=0; + c3d.clear(); + vpPoint P; for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) { - P = *it ; - c3d[i] = P ; - c3d[i].set_oX(P.get_oX()-p0.get_oX()) ; - c3d[i].set_oY(P.get_oY()-p0.get_oY()) ; - c3d[i].set_oZ(P.get_oZ()-p0.get_oZ()) ; - ++i; + P = (*it); + P.set_oX(P.get_oX()-p0.get_oX()) ; + P.set_oY(P.get_oY()-p0.get_oY()) ; + P.set_oZ(P.get_oZ()-p0.get_oZ()) ; + c3d.push_back(P) ; } - vpMatrix a ; - try{ - a.resize(npt,3) ; - } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; - } + vpMatrix a(npt,3) ; for (unsigned int i=0 ; i < npt ; i++) { @@ -136,31 +122,9 @@ vpPose::poseDementhonNonPlan(vpHomogeneousMatrix &cMo) int cpt = 0 ; vpColVector I, J, k ; - try{ - I.resize(3) ; - } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; - } - try{ - J.resize(3) ; - } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; - } - - try { - k.resize(3) ; - } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; - } + I.resize(3) ; + J.resize(3) ; + k.resize(3) ; while(cpt < 20) { @@ -183,9 +147,9 @@ vpPose::poseDementhonNonPlan(vpHomogeneousMatrix &cMo) if (normI+normJ < 1e-10) { - vpERROR_TRACE(" normI+normJ = 0, division par zero " ) ; + //vpERROR_TRACE(" normI+normJ = 0, division par zero " ) ; throw(vpException(vpException::divideByZeroError, - "division by zero ")) ; + "Division by zero in Dementhon pose computation: normI+normJ = 0")) ; } k = vpColVector::cross(I,J) ; @@ -199,9 +163,9 @@ vpPose::poseDementhonNonPlan(vpHomogeneousMatrix &cMo) } if (npt==0) { - vpERROR_TRACE( " npt = 0, division par zero "); + //vpERROR_TRACE( " npt = 0, division par zero "); throw(vpException(vpException::divideByZeroError, - "division by zero ")) ; + "Division by zero in Dementhon pose computation: no points")) ; } seuil/=npt; } @@ -227,8 +191,6 @@ vpPose::poseDementhonNonPlan(vpHomogeneousMatrix &cMo) cMo[0][3] -= (p0.get_oX()*cMo[0][0]+p0.get_oY()*cMo[0][1]+p0.get_oZ()*cMo[0][2]); cMo[1][3] -= (p0.get_oX()*cMo[1][0]+p0.get_oY()*cMo[1][1]+p0.get_oZ()*cMo[1][2]); cMo[2][3] -= (p0.get_oX()*cMo[2][0]+p0.get_oY()*cMo[2][1]+p0.get_oZ()*cMo[2][2]); - - delete [] c3d ; c3d = NULL ; } @@ -375,7 +337,6 @@ vpPose::calculArbreDementhon(vpMatrix &b, vpColVector &U, vpColVector I(3) ; vpColVector J(3) ; - vpHomogeneousMatrix cMo_old ; smin_old = 2*smin ; cpt = 0; @@ -519,21 +480,17 @@ vpPose::poseDementhonPlan(vpHomogeneousMatrix &cMo) unsigned int i,j,k ; - if (c3d !=NULL) delete []c3d ; - c3d = new vpPoint[npt] ; - vpPoint p0 = listP.front() ; vpPoint P ; - i=0 ; + c3d.clear(); for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) { P = *it; - c3d[i] = P ; - c3d[i].set_oX(P.get_oX()-p0.get_oX()) ; - c3d[i].set_oY(P.get_oY()-p0.get_oY()) ; - c3d[i].set_oZ(P.get_oZ()-p0.get_oZ()) ; - i++ ; + P.set_oX(P.get_oX()-p0.get_oX()) ; + P.set_oY(P.get_oY()-p0.get_oY()) ; + P.set_oZ(P.get_oZ()-p0.get_oZ()) ; + c3d.push_back(P); } vpMatrix a ; @@ -626,7 +583,7 @@ vpPose::poseDementhonPlan(vpHomogeneousMatrix &cMo) //calcul de U vpColVector U(3) ; - U = ata.column(imin+1) ; + U = ata.getCol(imin) ; #if (DEBUG_LEVEL2) { @@ -714,7 +671,6 @@ vpPose::poseDementhonPlan(vpHomogeneousMatrix &cMo) cMo[1][3] -= p0.get_oX()*cMo[1][0]+p0.get_oY()*cMo[1][1]+p0.get_oZ()*cMo[1][2]; cMo[2][3] -= p0.get_oX()*cMo[2][0]+p0.get_oY()*cMo[2][1]+p0.get_oZ()*cMo[2][2]; - delete [] c3d ; c3d = NULL ; #if (DEBUG_LEVEL1) std::cout << "end CCalculPose::PoseDementhonPlan()" << std::endl ; #endif @@ -735,12 +691,10 @@ vpPose::poseDementhonPlan(vpHomogeneousMatrix &cMo) */ double vpPose::computeResidualDementhon(const vpHomogeneousMatrix &cMo) { - unsigned int i ; - double residual = 0 ; - + double residual_ = 0 ; - residual =0 ; - for (i =0 ; i < npt ; i++) + residual_ =0 ; + for (unsigned int i =0 ; i < npt ; i++) { double X = c3d[i].get_oX()*cMo[0][0]+c3d[i].get_oY()*cMo[0][1]+c3d[i].get_oZ()*cMo[0][2] + cMo[0][3]; @@ -750,11 +704,9 @@ double vpPose::computeResidualDementhon(const vpHomogeneousMatrix &cMo) double x = X/Z ; double y = Y/Z ; - - - residual += vpMath::sqr(x-c3d[i].get_x()) + vpMath::sqr(y-c3d[i].get_y()) ; + residual_ += vpMath::sqr(x-c3d[i].get_x()) + vpMath::sqr(y-c3d[i].get_y()) ; } - return residual ; + return residual_ ; } @@ -762,9 +714,3 @@ double vpPose::computeResidualDementhon(const vpHomogeneousMatrix &cMo) #undef DEBUG_LEVEL2 #undef DEBUG_LEVEL3 - -/* -* Local variables: -* c-basic-offset: 2 -* End: -*/ diff --git a/src/computer-vision/pose-estimation/vpPoseException.h b/src/computer-vision/pose-estimation/vpPoseException.h index 09132b6bfd8e4ada276b68711963a3d86f634ba5..23c4c54f38a0141e8a2ed2ca2b0414a463607396 100644 --- a/src/computer-vision/pose-estimation/vpPoseException.h +++ b/src/computer-vision/pose-estimation/vpPoseException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPoseException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,12 +67,12 @@ */ class VISP_EXPORT vpPoseException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpPose member */ - enum errorCodeEnum + enum errorCodeEnum { poseError, //! something is not initialized @@ -84,25 +84,20 @@ public: notEnoughPointError } ; -public: - vpPoseException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpPoseException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpPoseException (const int code) - : vpException(code){ ; } - // vpPoseException() : vpException() { ;} + public: + vpPoseException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpPoseException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpPoseException (const int id) + : vpException(id){ ; } + // vpPoseException() : vpException() { ;} }; - - - - -#endif /* #ifndef __vpPoseException_ERROR_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/computer-vision/pose-estimation/vpPoseFeatures.cpp b/src/computer-vision/pose-estimation/vpPoseFeatures.cpp index 7f1a1060e65d59f0cae142b939a087fca1fa8bfe..132c074351f7fbfa02920e43c20954d2abc5c45b 100644 --- a/src/computer-vision/pose-estimation/vpPoseFeatures.cpp +++ b/src/computer-vision/pose-estimation/vpPoseFeatures.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseFeatures.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPoseFeatures.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,13 +44,11 @@ Default constructor. */ vpPoseFeatures::vpPoseFeatures() + : maxSize(0), totalSize(0), vvsIterMax(200), lambda(1.0), verbose(false), computeCovariance(false), + covarianceMatrix(), featurePoint_Point_list(), featurePoint3D_Point_list(), featureVanishingPoint_Point_list(), + featureVanishingPoint_DuoLine_list(), featureEllipse_Sphere_list(), featureEllipse_Circle_list(), + featureLine_Line_list(), featureLine_DuoLineInt_List(), featureSegment_DuoPoints_list() { - lambda = 1.0; - vvsIterMax = 200; - totalSize = 0; - maxSize = 0; - - verbose = false; } /*! diff --git a/src/computer-vision/pose-estimation/vpPoseFeatures.h b/src/computer-vision/pose-estimation/vpPoseFeatures.h index 409cec78742321502c61fc473731d1d49928b0dd..787ed4ba1a8e2ebc27bb28e3b7ab65e56036ff1b 100644 --- a/src/computer-vision/pose-estimation/vpPoseFeatures.h +++ b/src/computer-vision/pose-estimation/vpPoseFeatures.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseFeatures.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPoseFeatures.h 4660 2014-02-09 14:13:27Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -323,7 +323,7 @@ public: \brief Template class that allows to estimate a pose from all kind of specific features if the compiler support C++ 11. */ template< typename featureType, typename RetType, typename ...Args > -class VISP_EXPORT vpPoseSpecificFeatureTemplate : public vpPoseSpecificFeature +class vpPoseSpecificFeatureTemplate : public vpPoseSpecificFeature { private: featureType desiredFeature; @@ -372,7 +372,7 @@ public: \brief Template class that allows to estimate a pose from all kind of specific features if the compiler support C++ 11. */ template< typename ObjectType, typename featureType, typename RetType, typename ...Args > -class VISP_EXPORT vpPoseSpecificFeatureTemplateObject : public vpPoseSpecificFeature +class vpPoseSpecificFeatureTemplateObject : public vpPoseSpecificFeature { private: featureType desiredFeature; @@ -440,6 +440,7 @@ private: struct vpDuo{ FeatureType *desiredFeature; FirstParamType firstParam; + vpDuo() : desiredFeature(NULL), firstParam() {} }; template<typename FeatureType, typename FirstParamType, typename SecondParamType> @@ -447,6 +448,8 @@ private: FeatureType *desiredFeature; FirstParamType firstParam; SecondParamType secondParam; + + vpTrio() : desiredFeature(NULL), firstParam(), secondParam() {} }; #endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/computer-vision/pose-estimation/vpPoseLagrange.cpp b/src/computer-vision/pose-estimation/vpPoseLagrange.cpp index 6f2543144a9b2ae1215f2f907ba61b52b1670e38..8491fae30c50e8447c382882dcc8b976ef32b3bf 100644 --- a/src/computer-vision/pose-estimation/vpPoseLagrange.cpp +++ b/src/computer-vision/pose-estimation/vpPoseLagrange.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpPoseLagrange.cpp 4056 2013-01-05 13:04:42Z fspindle $ +* $Id: vpPoseLagrange.cpp 5198 2015-01-23 17:32:04Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -385,20 +385,20 @@ vpPose::poseLagrangePlan(vpHomogeneousMatrix &cMo, const int coplanar_plane_type for (i=0;i<3;i++) {s += (X1[i]*X2[i]);} for (i=0;i<3;i++) {X2[i] -= (s*X1[i]);} /* X1^T X2 = 0 */ - s = 0.0; - for (i=0;i<3;i++) {s += (X2[i]*X2[i]);} + //s = 0.0; + //for (i=0;i<3;i++) {s += (X2[i]*X2[i]);} + s = X2[0]*X2[0] + X2[1]*X2[1] + X2[2]*X2[2]; // To avoid a Coverity copy/past error if (s<1e-10) { - std::cout << "Points that produce an error: " << std::endl; - for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) - { - std::cout << "P: " << (*it).get_x() << " " << (*it).get_y() << " " - << (*it).get_oX() << " " << (*it).get_oY() << " " << (*it).get_oZ() << std::endl; - } - vpERROR_TRACE( "division par zero ") ; +// std::cout << "Points that produce an error: " << std::endl; +// for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) +// { +// std::cout << "P: " << (*it).get_x() << " " << (*it).get_y() << " " +// << (*it).get_oX() << " " << (*it).get_oY() << " " << (*it).get_oZ() << std::endl; +// } throw(vpException(vpException::divideByZeroError, - "division by zero ")) ; + "Division by zero in Lagrange pose computation (planar plane case)")) ; } s = 1.0/sqrt(s); @@ -453,13 +453,11 @@ vpPose::poseLagrangePlan(vpHomogeneousMatrix &cMo, const int coplanar_plane_type } } } - catch(...) + catch(vpException &e) { - vpERROR_TRACE(" ") ; - throw ; + throw e; } - #if (DEBUG_LEVEL1) std::cout << "end vpCalculPose::PoseLagrange(...) " << std::endl ; #endif @@ -559,21 +557,21 @@ vpPose::poseLagrangeNonPlan(vpHomogeneousMatrix &cMo) for (i=0;i<3;i++) {s += (X1[i]*X2[i]);} for (i=0;i<3;i++) {X2[i] -= (s*X1[i]);} /* X1^T X2 = 0 */ - s = 0.0; - for (i=0;i<3;i++) {s += (X2[i]*X2[i]);} + //s = 0.0; + //for (i=0;i<3;i++) {s += (X2[i]*X2[i]);} + s = X2[0]*X2[0] + X2[1]*X2[1] + X2[2]*X2[2]; // To avoid a Coverity copy/past error if (s<1e-10) { - std::cout << "Points that produce an error: " << std::endl; - for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) - { - std::cout << "P: " << (*it).get_x() << " " << (*it).get_y() << " " - << (*it).get_oX() << " " << (*it).get_oY() << " " << (*it).get_oZ() << std::endl; - } - vpERROR_TRACE(" division par zero " ) ; +// std::cout << "Points that produce an error: " << std::endl; +// for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) +// { +// std::cout << "P: " << (*it).get_x() << " " << (*it).get_y() << " " +// << (*it).get_oX() << " " << (*it).get_oY() << " " << (*it).get_oZ() << std::endl; +// } + //vpERROR_TRACE(" division par zero " ) ; throw(vpException(vpException::divideByZeroError, - "division by zero ")) ; - + "Division by zero in Lagrange pose computation (non planar plane case)")) ; } s = 1.0/sqrt(s); @@ -585,7 +583,6 @@ vpPose::poseLagrangeNonPlan(vpHomogeneousMatrix &cMo) calculTranslation (a, b, nl, 3, 6, X1, X2) ; - for (i=0 ; i<3 ; i++) { cMo[i][0] = X1[i]; @@ -595,10 +592,9 @@ vpPose::poseLagrangeNonPlan(vpHomogeneousMatrix &cMo) } } - catch(...) + catch(vpException &e) { - vpERROR_TRACE(" ") ; - throw ; + throw e; } #if (DEBUG_LEVEL1) @@ -609,11 +605,3 @@ vpPose::poseLagrangeNonPlan(vpHomogeneousMatrix &cMo) #undef DEBUG_LEVEL1 #undef DEBUG_LEVEL2 - - - -/* -* Local variables: -* c-basic-offset: 2 -* End: -*/ diff --git a/src/computer-vision/pose-estimation/vpPoseLowe.cpp b/src/computer-vision/pose-estimation/vpPoseLowe.cpp index 1f5c4f9a6bb2d3be5bb6437c142901bc677e3568..0e08327bdde039dacea9cd844ff5d7eb42ddd4b1 100644 --- a/src/computer-vision/pose-estimation/vpPoseLowe.cpp +++ b/src/computer-vision/pose-estimation/vpPoseLowe.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpPoseLowe.cpp 4276 2013-06-25 12:36:48Z fspindle $ +* $Id: vpPoseLowe.cpp 5107 2015-01-05 07:47:13Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,18 +40,16 @@ * *****************************************************************************/ - - - +#include <math.h> +#include <float.h> +#include <string.h> +#include <limits> // numeric_limits // besoin de la librairie mathematique, en particulier des // fonctions de minimisation de Levenberg Marquartd #include <visp/vpLevenbergMarquartd.h> - #include <visp/vpPose.h> -#include <math.h> -#include <float.h> -#include <string.h> + #define NBR_PAR 6 #define X3_SIZE 3 #define MINIMUM 0.000001 @@ -94,6 +92,8 @@ static double XO[NBPTMAX],YO[NBPTMAX],ZO[NBPTMAX]; #define MINI 0.001 #define MINIMUM 0.000001 +void eval_function(int npt,double *xc,double *f); +void fcn (int m, int n, double *xc, double *fvecc, double *jac, int ldfjac, int iflag); void eval_function(int npt,double *xc,double *f) { @@ -290,7 +290,6 @@ vpPose::poseLowe(vpHomogeneousMatrix & cMo) #if (DEBUG_LEVEL1) std::cout << "begin CCalcuvpPose::PoseLowe(...) " << std::endl; #endif - unsigned int i; int n, m; /* nombre d'elements dans la matrice jac */ int lwa; /* taille du vecteur wa */ int ldfjac; /* taille maximum d'une ligne de jac */ @@ -305,7 +304,7 @@ vpPose::poseLowe(vpHomogeneousMatrix & cMo) m = (int)(2 * npt); /* nombres d'equations */ lwa = 2 * NBPTMAX + 50; /* taille du vecteur de travail */ ldfjac = 2 * NBPTMAX; /* nombre d'elements max sur une ligne */ - tol = DBL_EPSILON; /* critere d'arret */ + tol = std::numeric_limits<double>::epsilon(); /* critere d'arret */ // c = cam ; // for (i=0;i<3;i++) @@ -321,19 +320,18 @@ vpPose::poseLowe(vpHomogeneousMatrix & cMo) } vpPoint P ; - i=0; + unsigned int i_=0; for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) { P = *it; - XI[i] = P.get_x();//*cam.px + cam.xc ; - YI[i] = P.get_y() ;//;*cam.py + cam.yc ; - XO[i] = P.get_oX(); - YO[i] = P.get_oY(); - ZO[i] = P.get_oZ(); - ++i; + XI[i_] = P.get_x();//*cam.px + cam.xc ; + YI[i_] = P.get_y() ;//;*cam.py + cam.yc ; + XO[i_] = P.get_oX(); + YO[i_] = P.get_oY(); + ZO[i_] = P.get_oZ(); + ++i_; } - tst_lmder = lmder1 (&fcn, m, n, sol, f, &jac[0][0], ldfjac, tol, &info, - ipvt, lwa, wa); + tst_lmder = lmder1 (&fcn, m, n, sol, f, &jac[0][0], ldfjac, tol, &info, ipvt, lwa, wa); if (tst_lmder == -1) { std::cout << " in CCalculPose::PoseLowe(...) : " ; @@ -341,10 +339,10 @@ vpPose::poseLowe(vpHomogeneousMatrix & cMo) // return FATAL_ERROR ; } - for (i = 0; i < 3; i++) + for (unsigned int i = 0; i < 3; i++) u[i] = sol[i + 3]; - for (i=0;i<3;i++) + for (unsigned int i=0;i<3;i++) { cMo[i][3] = sol[i]; u[i] = sol[i+3]; diff --git a/src/computer-vision/pose-estimation/vpPoseRansac.cpp b/src/computer-vision/pose-estimation/vpPoseRansac.cpp index e6b6dec1fc694515f0f1e568651dc4e743b53ccb..9e74a48f57870e6e29495fb6e1fc0e465124f9e2 100644 --- a/src/computer-vision/pose-estimation/vpPoseRansac.cpp +++ b/src/computer-vision/pose-estimation/vpPoseRansac.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseRansac.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPoseRansac.cpp 5211 2015-01-26 17:44:35Z strinh $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,6 +37,7 @@ * Authors: * Eric Marchand * Aurelien Yol + * Souriya Trinh * *****************************************************************************/ @@ -46,6 +47,13 @@ \brief function used to estimate a pose using the Ransac algorithm */ +#include <iostream> +#include <cmath> // std::fabs +#include <limits> // numeric_limits +#include <stdlib.h> +#include <algorithm> // std::count +#include <float.h> //DBL_MAX + #include <visp/vpPose.h> #include <visp/vpColVector.h> #include <visp/vpRansac.h> @@ -53,11 +61,6 @@ #include <visp/vpList.h> #include <visp/vpPoseException.h> -#include <iostream> -#include <cmath> // std::fabs -#include <limits> // numeric_limits -#include <stdlib.h> - #define eps 1e-6 @@ -65,10 +68,13 @@ Compute the pose using the Ransac approach. \param cMo : Computed pose + \param func : Pointer to a function that takes in parameter a vpHomogeneousMatrix + and returns true if the pose check is OK or false otherwise + \return True if we found at least 4 points with a reprojection error below ransacThreshold. */ -void vpPose::poseRansac(vpHomogeneousMatrix & cMo) +bool vpPose::poseRansac(vpHomogeneousMatrix & cMo, bool (*func)(vpHomogeneousMatrix *)) { - srand(0); + srand(0); //Fix seed here so we will have the same pseudo-random series at each run. std::vector<unsigned int> best_consensus; std::vector<unsigned int> cur_consensus; std::vector<unsigned int> cur_outliers; @@ -77,28 +83,52 @@ void vpPose::poseRansac(vpHomogeneousMatrix & cMo) int nbTrials = 0; unsigned int nbMinRandom = 4 ; unsigned int nbInliers = 0; + double r, r_lagrange, r_dementhon; vpHomogeneousMatrix cMo_lagrange, cMo_dementhon; - double r, r_lagrange, r_dementhon; + + if (size < 4) { + //vpERROR_TRACE("Not enough point to compute the pose"); + throw(vpPoseException(vpPoseException::notInitializedError, + "Not enough point to compute the pose")) ; + } bool foundSolution = false; while (nbTrials < ransacMaxTrials && nbInliers < (unsigned)ransacNbInlierConsensus) - { + { + //Hold the list of the index of the inliers (points in the consensus set) + cur_consensus.clear(); + + //Use a temporary variable because if not, the cMo passed in parameters will be modified when + // we compute the pose for the minimal sample sets but if the pose is not correct when we pass + // a function pointer we do not want to modify the cMo passed in parameters + vpHomogeneousMatrix cMo_tmp; cur_outliers.clear(); cur_randoms.clear(); + //Vector of used points, initialized at false for all points std::vector<bool> usedPt(size, false); - vpPose poseMin ; - for(unsigned int i = 0; i < nbMinRandom; i++) + vpPose poseMin; + for(unsigned int i = 0; i < nbMinRandom;) { - unsigned int r = (unsigned int)rand()%size; - while(usedPt[r] ) r = (unsigned int)rand()%size; - usedPt[r] = true; + if((size_t) std::count(usedPt.begin(), usedPt.end(), true) == usedPt.size()) { + //All points was picked once, break otherwise we stay in an infinite loop + break; + } + + //Pick a point randomly + unsigned int r_ = (unsigned int) rand() % size; + while(usedPt[r_]) { + //If already picked, pick another point randomly + r_ = (unsigned int) rand() % size; + } + //Mark this point as already picked + usedPt[r_] = true; std::list<vpPoint>::const_iterator iter = listP.begin(); - std::advance(iter, r); + std::advance(iter, r_); vpPoint pt = *iter; bool degenerate = false; @@ -110,91 +140,146 @@ void vpPose::poseRansac(vpHomogeneousMatrix & cMo) break; } } - if(!degenerate){ - poseMin.addPoint(pt) ; - cur_randoms.push_back(r); + if(!degenerate) { + poseMin.addPoint(pt); + cur_randoms.push_back(r_); + //Increment the number of points picked + i++; } - else - i--; } - poseMin.computePose(vpPose::LAGRANGE, cMo_lagrange) ; - r_lagrange = poseMin.computeResidual(cMo_lagrange) ; - poseMin.computePose(vpPose::DEMENTHON, cMo_dementhon) ; - r_dementhon = poseMin.computeResidual(cMo_dementhon) ; + if(poseMin.npt < nbMinRandom) { + nbTrials++; + continue; + } + + //Flags set if pose computation is OK + bool is_valid_lagrange = false; + bool is_valid_dementhon = false; + + //Set maximum value for residuals + r_lagrange = DBL_MAX; + r_dementhon = DBL_MAX; - if (r_lagrange < r_dementhon) { - r = r_lagrange; - cMo = cMo_lagrange; + try { + poseMin.computePose(vpPose::LAGRANGE, cMo_lagrange); + r_lagrange = poseMin.computeResidual(cMo_lagrange); + is_valid_lagrange = true; + } catch(/*vpException &e*/...) { +// std::cerr << e.what() << std::endl; } - else { - r = r_dementhon; - cMo = cMo_dementhon; + + try { + poseMin.computePose(vpPose::DEMENTHON, cMo_dementhon); + r_dementhon = poseMin.computeResidual(cMo_dementhon); + is_valid_dementhon = true; + } catch(/*vpException &e*/...) { +// std::cerr << e.what() << std::endl; } - r = sqrt(r)/(double)nbMinRandom; - if (r < ransacThreshold) - { - unsigned int nbInliersCur = 0; - unsigned int iter = 0; - for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) - { - vpPoint pt = *it; - vpPoint p(pt) ; - p.track(cMo) ; - - double d = vpMath::sqr(p.get_x() - pt.get_x()) + vpMath::sqr(p.get_y() - pt.get_y()) ; - double error = sqrt(d) ; - if(error < ransacThreshold){ - // the point is considered as inlier if the error is below the threshold - // But, we need also to check if it is not a degenerate point - bool degenerate = false; - - for(unsigned int it_inlier_index = 0; it_inlier_index< cur_consensus.size(); it_inlier_index++){ - std::list<vpPoint>::const_iterator it_point = listP.begin(); - std::advance(it_point, cur_consensus[it_inlier_index]); - vpPoint pt = *it_point; - - vpPoint ptdeg = *it; - if( ((fabs(pt.get_x() - ptdeg.get_x()) < 1e-6) && (fabs(pt.get_y() - ptdeg.get_y()) < 1e-6)) || - ((fabs(pt.get_oX() - ptdeg.get_oX()) < 1e-6) && (fabs(pt.get_oY() - ptdeg.get_oY()) < 1e-6) && (fabs(pt.get_oZ() - ptdeg.get_oZ()) < 1e-6))){ - degenerate = true; - break; + //If at least one pose computation is OK, + //we can continue, otherwise pick another random set + if(is_valid_lagrange || is_valid_dementhon) { + if (r_lagrange < r_dementhon) { + r = r_lagrange; +// cMo = cMo_lagrange; + cMo_tmp = cMo_lagrange; + } + else { + r = r_dementhon; +// cMo = cMo_dementhon; + cMo_tmp = cMo_dementhon; + } + r = sqrt(r) / (double) nbMinRandom; + + //Filter the pose using some criterion (orientation angles, translations, etc.) + bool isPoseValid = true; + if(func != NULL) { + isPoseValid = func(&cMo_tmp); + if(isPoseValid) { + cMo = cMo_tmp; + } + } else { + //No post filtering on pose, so copy cMo_temp to cMo + cMo = cMo_tmp; + } + + if (isPoseValid && r < ransacThreshold) + { + unsigned int nbInliersCur = 0; + unsigned int iter = 0; + for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) + { + vpPoint pt = *it; + vpPoint p(pt) ; + p.track(cMo) ; + + double d = vpMath::sqr(p.get_x() - pt.get_x()) + vpMath::sqr(p.get_y() - pt.get_y()) ; + double error = sqrt(d) ; + if(error < ransacThreshold) { + // the point is considered as inlier if the error is below the threshold + // But, we need also to check if it is not a degenerate point + bool degenerate = false; + + for(unsigned int it_inlier_index = 0; it_inlier_index< cur_consensus.size(); it_inlier_index++){ + std::list<vpPoint>::const_iterator it_point = listP.begin(); + std::advance(it_point, cur_consensus[it_inlier_index]); + pt = *it_point; + + vpPoint ptdeg = *it; + if( ((fabs(pt.get_x() - ptdeg.get_x()) < 1e-6) && (fabs(pt.get_y() - ptdeg.get_y()) < 1e-6)) || + ((fabs(pt.get_oX() - ptdeg.get_oX()) < 1e-6) && (fabs(pt.get_oY() - ptdeg.get_oY()) < 1e-6) && (fabs(pt.get_oZ() - ptdeg.get_oZ()) < 1e-6))){ + degenerate = true; + break; + } } - } - if(!degenerate){ - nbInliersCur++; - cur_consensus.push_back(iter); + if(!degenerate) { + nbInliersCur++; + cur_consensus.push_back(iter); + } + else { + cur_outliers.push_back(iter); + } } else { cur_outliers.push_back(iter); } - } - else - cur_outliers.push_back(iter); + + iter++; + } + + if(nbInliersCur > nbInliers) + { + foundSolution = true; + best_consensus = cur_consensus; + nbInliers = nbInliersCur; + } + + nbTrials++; - iter++; + if(nbTrials >= ransacMaxTrials) { + vpERROR_TRACE("Ransac reached the maximum number of trials"); + foundSolution = true; + } } + else { + nbTrials++; - if(nbInliersCur > nbInliers) - { - foundSolution = true; - best_consensus = cur_consensus; - nbInliers = nbInliersCur; + if(nbTrials >= ransacMaxTrials) { + vpERROR_TRACE("Ransac reached the maximum number of trials"); + } } - + } else { nbTrials++; - cur_consensus.clear(); - - if(nbTrials >= ransacMaxTrials){ + + if(nbTrials >= ransacMaxTrials) { vpERROR_TRACE("Ransac reached the maximum number of trials"); - foundSolution = true; } } } - if(foundSolution){ + if(foundSolution) { //std::cout << "Nombre d'inliers " << nbInliers << std::endl ; //Display the random picked points @@ -213,8 +298,12 @@ void vpPose::poseRansac(vpHomogeneousMatrix & cMo) std::cout << std::endl; */ - if(nbInliers >= (unsigned)ransacNbInlierConsensus) - { + //Even if the cardinality of the best consensus set is inferior to ransacNbInlierConsensus, + //we want to refine the solution with data in best_consensus and return this pose. + //This is an approach used for example in p118 in Multiple View Geometry in Computer Vision, Hartley, R.~I. and Zisserman, A. + if(nbInliers >= nbMinRandom) //if(nbInliers >= (unsigned)ransacNbInlierConsensus) + { + //Refine the solution using all the points in the consensus set and with VVS pose estimation vpPose pose ; for(unsigned i = 0 ; i < best_consensus.size(); i++) { @@ -225,22 +314,51 @@ void vpPose::poseRansac(vpHomogeneousMatrix & cMo) pose.addPoint(pt) ; ransacInliers.push_back(pt); } - - pose.computePose(vpPose::LAGRANGE, cMo_lagrange) ; - r_lagrange = pose.computeResidual(cMo_lagrange) ; - pose.computePose(vpPose::DEMENTHON, cMo_dementhon) ; - r_dementhon = pose.computeResidual(cMo_dementhon) ; - if (r_lagrange < r_dementhon) { - cMo = cMo_lagrange; + //Flags set if pose computation is OK + bool is_valid_lagrange = false; + bool is_valid_dementhon = false; + + //Set maximum value for residuals + r_lagrange = DBL_MAX; + r_dementhon = DBL_MAX; + + try { + pose.computePose(vpPose::LAGRANGE, cMo_lagrange); + r_lagrange = pose.computeResidual(cMo_lagrange); + is_valid_lagrange = true; + } catch(/*vpException &e*/...) { +// std::cerr << e.what() << std::endl; } - else { - cMo = cMo_dementhon; + + try { + pose.computePose(vpPose::DEMENTHON, cMo_dementhon); + r_dementhon = pose.computeResidual(cMo_dementhon); + is_valid_dementhon = true; + } catch(/*vpException &e*/...) { +// std::cerr << e.what() << std::endl; } - pose.computePose(vpPose::VIRTUAL_VS, cMo) ; + if(is_valid_lagrange || is_valid_dementhon) { + if (r_lagrange < r_dementhon) { + cMo = cMo_lagrange; + } + else { + cMo = cMo_dementhon; + } + + pose.setCovarianceComputation(computeCovariance); + pose.computePose(vpPose::VIRTUAL_VS, cMo); + if(computeCovariance) { + covarianceMatrix = pose.covarianceMatrix; + } + } + } else { + return false; } } + + return foundSolution; } /*! diff --git a/src/computer-vision/pose-estimation/vpPoseVirtualVisualServoing.cpp b/src/computer-vision/pose-estimation/vpPoseVirtualVisualServoing.cpp index 6070859ed1936d43b8ac22b23d304518795d4bc6..fa9477c03bad25b50fa8822c69a7d7b3de3fe332 100755 --- a/src/computer-vision/pose-estimation/vpPoseVirtualVisualServoing.cpp +++ b/src/computer-vision/pose-estimation/vpPoseVirtualVisualServoing.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseVirtualVisualServoing.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPoseVirtualVisualServoing.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,7 +96,7 @@ vpPose::poseVirtualVS(vpHomogeneousMatrix & cMo) residu_1 = r ; // Compute the interaction matrix and the error - unsigned int k =0 ; + k =0 ; for (std::list<vpPoint>::const_iterator it = lP.begin(); it != lP.end(); ++it) { P = *it; @@ -186,14 +186,14 @@ vpPose::poseVirtualVSrobust(vpHomogeneousMatrix & cMo) std::list<vpPoint> lP ; // create sd - unsigned int k =0 ; + unsigned int k_ =0 ; for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it) { P = *it; - sd[2*k] = P.get_x() ; - sd[2*k+1] = P.get_y() ; + sd[2*k_] = P.get_x() ; + sd[2*k_+1] = P.get_y() ; lP.push_back(P) ; - k ++; + k_ ++; } int iter = 0 ; res.resize(s.getRows()/2) ; @@ -206,8 +206,7 @@ vpPose::poseVirtualVSrobust(vpHomogeneousMatrix & cMo) residu_1 = r ; // Compute the interaction matrix and the error - vpPoint P ; - k =0 ; + k_ =0 ; for (std::list<vpPoint>::const_iterator it = lP.begin(); it != lP.end(); ++it) { P = *it; @@ -216,24 +215,24 @@ vpPose::poseVirtualVSrobust(vpHomogeneousMatrix & cMo) // perspective projection P.track(cMo) ; - double x = s[2*k] = P.get_x(); // point projected from cMo - double y = s[2*k+1] = P.get_y(); + double x = s[2*k_] = P.get_x(); // point projected from cMo + double y = s[2*k_+1] = P.get_y(); double Z = P.get_Z() ; - L[2*k][0] = -1/Z ; - L[2*k][1] = 0 ; - L[2*k][2] = x/Z ; - L[2*k][3] = x*y ; - L[2*k][4] = -(1+x*x) ; - L[2*k][5] = y ; - - L[2*k+1][0] = 0 ; - L[2*k+1][1] = -1/Z ; - L[2*k+1][2] = y/Z ; - L[2*k+1][3] = 1+y*y ; - L[2*k+1][4] = -x*y ; - L[2*k+1][5] = -x ; - - k ++; + L[2*k_][0] = -1/Z ; + L[2*k_][1] = 0 ; + L[2*k_][2] = x/Z ; + L[2*k_][3] = x*y ; + L[2*k_][4] = -(1+x*x) ; + L[2*k_][5] = y ; + + L[2*k_+1][0] = 0 ; + L[2*k_+1][1] = -1/Z ; + L[2*k_+1][2] = y/Z ; + L[2*k_+1][3] = 1+y*y ; + L[2*k_+1][4] = -x*y ; + L[2*k_+1][5] = -x ; + + k_ ++; } error = s - sd ; diff --git a/src/data-structure/vpList.h b/src/data-structure/vpList.h index 44baab2e12de6b3c83e107b4d97e83a9e7bb9cfc..485acb0dd444d9aaded3ec1fcac33175bbd588a3 100644 --- a/src/data-structure/vpList.h +++ b/src/data-structure/vpList.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpList.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpList.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,10 +64,11 @@ template <class type> class vpListElement { - public: - vpListElement<type> *prev; //<! pointer to the previous element in the list - vpListElement<type> *next; //<! pointer to the next element in the list - type val; //<! value of the element + public: + vpListElement() : prev(NULL), next(NULL), val() {}; + vpListElement<type> *prev; //<! pointer to the previous element in the list + vpListElement<type> *next; //<! pointer to the next element in the list + type val; //<! value of the element } ; #endif /* DOXYGEN_SHOULD_SKIP_THIS */ @@ -173,7 +174,7 @@ class vpList inline unsigned int nbElement(void); // returns the number of items currently in the list inline unsigned int nbElements(void); // returns the number of items currently in the list - void operator=(const vpList<type>& l); + vpList<type>& operator=(const vpList<type>& l); inline void operator+=(vpList<type>& l); inline void operator+=(const type& l); @@ -222,7 +223,7 @@ void vpList<type>::init() \sa init() */ template<class type> -vpList<type>::vpList() +vpList<type>::vpList() : nb(0), first(NULL), last(NULL), cur(NULL) { init() ; } @@ -236,13 +237,10 @@ vpList<type>::vpList() template<class type> vpList<type>::~vpList() { - kill() ; - if (first != NULL) delete first ; - if (last != NULL) delete last ; - - + /*if (first != NULL) */ delete first ; + /*if (last != NULL) */ delete last ; } /*! @@ -741,7 +739,7 @@ void vpList<type>::suppress(void) */ template<class type> -void vpList<type>::operator=(const vpList<type>& l) +vpList<type>& vpList<type>::operator=(const vpList<type>& l) { type x ; vpListElement<type> *e ; @@ -758,6 +756,8 @@ void vpList<type>::operator=(const vpList<type>& l) nb = l.nb ; cur = first->next ; + + return *this; } /*! @@ -806,6 +806,7 @@ void vpList<type>::operator += (const type& l) */ template<class type> vpList<type>::vpList(const vpList<type>& l) + : nb(0), first(NULL), last(NULL), cur(NULL) { init() ; *this = l; diff --git a/src/detection/barcode/vpDetectorDataMatrixCode.cpp b/src/detection/barcode/vpDetectorDataMatrixCode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b746697c7d67c460088c79219a92aa09be2b1f0f --- /dev/null +++ b/src/detection/barcode/vpDetectorDataMatrixCode.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** + * + * $Id$ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Base class for bar code detection. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#include <assert.h> + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_DMTX + +#include <dmtx.h> + +#include <visp/vpDetectorDataMatrixCode.h> + +/*! + Default constructor that does nothing. + */ +vpDetectorDataMatrixCode::vpDetectorDataMatrixCode() +{ +} + +/*! + Detect datamatrix bar codes in the image. Return true if a bar code is detected, false otherwise. + + \param I : Input image. + */ +bool vpDetectorDataMatrixCode::detect(const vpImage<unsigned char> &I) +{ + bool detected = false; + m_message.clear(); + m_polygon.clear(); + m_nb_objects = 0; + DmtxRegion *reg; + DmtxDecode *dec; + DmtxImage *img; + DmtxMessage *msg; + + img = dmtxImageCreate(I.bitmap, (int) I.getWidth(), (int) I.getHeight(), DmtxPack8bppK); + assert(img != NULL); + + dec = dmtxDecodeCreate(img, 1); + assert(dec != NULL); + + bool end = false; + do { + reg = dmtxRegionFindNext(dec, 0); + + if(reg != NULL) { + msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined); + if(msg != NULL) { + + std::vector<vpImagePoint> polygon; + + DmtxVector2 p00, p10, p11, p01; + + p00.X = p00.Y = p10.Y = p01.X = 0.0; + p10.X = p01.Y = p11.X = p11.Y = 1.0; + dmtxMatrix3VMultiplyBy(&p00, reg->fit2raw); + dmtxMatrix3VMultiplyBy(&p10, reg->fit2raw); + dmtxMatrix3VMultiplyBy(&p11, reg->fit2raw); + dmtxMatrix3VMultiplyBy(&p01, reg->fit2raw); + + polygon.push_back(vpImagePoint(I.getHeight()-p00.Y, p00.X)); + polygon.push_back(vpImagePoint(I.getHeight()-p10.Y, p10.X)); + polygon.push_back(vpImagePoint(I.getHeight()-p11.Y, p11.X)); + polygon.push_back(vpImagePoint(I.getHeight()-p01.Y, p01.X)); + + m_polygon.push_back(polygon); + detected = true; + m_message.push_back( (const char *)msg->output); + + m_nb_objects ++; + } + else { + end = true; + } + dmtxMessageDestroy(&msg); + } + else { + end = true; + } + dmtxRegionDestroy(®); + + } while (!end); + + dmtxDecodeDestroy(&dec); + dmtxImageDestroy(&img); + return detected; +} + +#endif diff --git a/src/detection/barcode/vpDetectorDataMatrixCode.h b/src/detection/barcode/vpDetectorDataMatrixCode.h new file mode 100644 index 0000000000000000000000000000000000000000..92a90e0e3f0027ba14277e5485bf1877b96bb866 --- /dev/null +++ b/src/detection/barcode/vpDetectorDataMatrixCode.h @@ -0,0 +1,122 @@ +/**************************************************************************** + * + * $Id$ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Base class for bar code detection. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#ifndef __vpDetectorDataMatrixCode_h__ +#define __vpDetectorDataMatrixCode_h__ + +#include <vector> +#include <utility> +#include <string> + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_DMTX + +#include <visp/vpDetectorBase.h> +#include <visp/vpImage.h> + +/*! + \class vpDetectorDataMatrixCode + + Base class for bar code detector. This class is a wrapper over libdmtx + available from http://www.libdmtx.org/ + + The detect() function allows to detect multiple QR codes in an image. Once detected, + for each QR code it is possible to retrieve the location of the corners using getPolygon(), + the encoded message using getMessage(), the bounding box using getBBox() and the center + of gravity using getCog(). + + The following sample code shows how to use this class to detect QR codes in an image. + \code +#include <visp/vpDetectorDataMatrixCode.h> +#include <visp/vpImageIo.h> + +int main() +{ +#ifdef VISP_HAVE_DMTX + vpImage<unsigned char> I; + vpImageIo::read(I, "bar-code.pgm"); + + vpDetectorDataMatrixCode detector; + + bool status = detector.detect(I); + if (status) { + for(size_t i=0; i < detector.getNbObjects(); i++) { + std::cout << "Bar code " << i << ":" << std::endl; + std::vector<vpImagePoint> p = detector.getPolygon(i); + for(size_t j=0; j < p.size(); j++) + std::cout << " Point " << j << ": " << p[j] << std::endl; + std::cout << " Message: \"" << detector.getMessage(i) << "\"" << std::endl; + } + } +#endif +} + \endcode + + The previous example may produce results like: + \code +Bar code 0: + Point 0: 273.21, 78.9799 + Point 1: 390.016, 85.1014 + Point 2: 388.024, 199.185 + Point 3: 269.23, 192.96 + Message: "datamatrix 1" +Bar code 1: + Point 0: 262.23, 396.404 + Point 1: 381.041, 402.631 + Point 2: 378.92, 524.188 + Point 3: 257.916, 519.962 + Message: "datamatrix 2" + \endcode + + Other examples are also provided in tutorial-barcode-detector.cpp and + tutorial-barcode-detector-live.cpp + + */ +class VISP_EXPORT vpDetectorDataMatrixCode : public vpDetectorBase +{ +public: + vpDetectorDataMatrixCode(); + virtual ~vpDetectorDataMatrixCode() {}; + bool detect(const vpImage<unsigned char> &I); +}; + +#endif +#endif diff --git a/src/detection/barcode/vpDetectorQRCode.cpp b/src/detection/barcode/vpDetectorQRCode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..659ec301c7d5bffef218d020c053d0ecd2ea1711 --- /dev/null +++ b/src/detection/barcode/vpDetectorQRCode.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** + * + * $Id$ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Base class for bar code detection. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_ZBAR + +#include <visp/vpDetectorQRCode.h> + + +/*! + Default constructor. + */ +vpDetectorQRCode::vpDetectorQRCode() : m_scanner() +{ + // configure the reader + m_scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); +} + +/*! + Detect QR codes in the image. Return true if a code is detected, false otherwise. + + \param I : Input image. + */ +bool vpDetectorQRCode::detect(const vpImage<unsigned char> &I) +{ + bool detected = false; + m_message.clear(); + m_polygon.clear(); + m_nb_objects = 0; + + m_scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1); + unsigned int width = I.getWidth(); + unsigned int height = I.getHeight(); + + // wrap image data + zbar::Image img(width, height, "Y800", I.bitmap, (unsigned long)(width * height)); + + // scan the image for barcodes + m_nb_objects = (size_t) m_scanner.scan(img); + + // extract results + for(zbar::Image::SymbolIterator symbol = img.symbol_begin(); + symbol != img.symbol_end(); + ++symbol) { + m_message.push_back( symbol->get_data() ); + detected = true; + + std::vector<vpImagePoint> polygon; + for(unsigned int i=0; i < (unsigned int)symbol->get_location_size(); i++){ + polygon.push_back(vpImagePoint(symbol->get_location_y(i), symbol->get_location_x(i))); + } + m_polygon.push_back(polygon); + } + + // clean up + img.set_data(NULL, 0); + + return detected; +} + +#endif diff --git a/src/detection/barcode/vpDetectorQRCode.h b/src/detection/barcode/vpDetectorQRCode.h new file mode 100644 index 0000000000000000000000000000000000000000..3839ea0f9c0f5426caae7735dd206e41caaac00c --- /dev/null +++ b/src/detection/barcode/vpDetectorQRCode.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * + * $Id$ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Base class for bar code detection. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#ifndef __vpDetectorQRCode_h__ +#define __vpDetectorQRCode_h__ + +#include <vector> +#include <utility> +#include <string> + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_ZBAR + +#include <zbar.h> + +#include <visp/vpDetectorBase.h> +#include <visp/vpImage.h> + +/*! + \class vpDetectorQRCode + + Base class for bar code detector. This class is a wrapper over libzbar + available from http://zbar.sourceforge.net/ + + The detect() function allows to detect multiple QR codes in an image. Once detected, + for each QR code it is possible to retrieve the location of the corners using getPolygon(), + the encoded message using getMessage(), the bounding box using getBBox() and the center + of gravity using getCog(). + + The following sample code shows how to use this class to detect QR codes in an image. + \code +#include <visp/vpDetectorQRCode.h> +#include <visp/vpImageIo.h> + +int main() +{ +#ifdef VISP_HAVE_ZBAR + vpImage<unsigned char> I; + vpImageIo::read(I, "bar-code.pgm"); + + vpDetectorQRCode detector; + + bool status = detector.detect(I); + if (status) { + for(size_t i=0; i < detector.getNbObjects(); i++) { + std::cout << "Bar code " << i << ":" << std::endl; + std::vector<vpImagePoint> p = detector.getPolygon(i); + for(size_t j=0; j < p.size(); j++) + std::cout << " Point " << j << ": " << p[j] << std::endl; + std::cout << " Message: \"" << detector.getMessage(i) << "\"" << std::endl; + } + } +#endif +} + \endcode + + The previous example may produce results like: + \code +Bar code 0: + Point 0: 48, 212 + Point 1: 57, 84 + Point 2: 188, 92 + Point 3: 183, 220 + Message: "qrcode 2" +Bar code 1: + Point 0: 26, 550 + Point 1: 35, 409 + Point 2: 174, 414 + Point 3: 167, 555 + Message: "qrcode 1" + \endcode + + Other examples are also provided in tutorial-barcode-detector.cpp and + tutorial-barcode-detector-live.cpp + */ +class VISP_EXPORT vpDetectorQRCode : public vpDetectorBase +{ +protected: + zbar::ImageScanner m_scanner; //!< QR code detector. + +public: + vpDetectorQRCode(); + virtual ~vpDetectorQRCode() {}; + bool detect(const vpImage<unsigned char> &I); +}; + +#endif +#endif diff --git a/src/detection/face/vpDetectorFace.cpp b/src/detection/face/vpDetectorFace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f1e1722803ab9ab02cb16db079f9daae1a7e1de --- /dev/null +++ b/src/detection/face/vpDetectorFace.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Detect faces. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpConfig.h> + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020200) + +#include <algorithm> + +#include <visp/vpDetectorFace.h> +#include <visp/vpImageConvert.h> + +bool vpSortLargestFace(cv::Rect rect1, cv::Rect rect2) +{ + return (rect1.area() > rect2.area()); +} + +/*! + Default constructor. + */ +vpDetectorFace::vpDetectorFace() : + m_faces(), m_face_cascade(), m_frame_gray() +{ +} + +/*! + Set the name of the OpenCV cascade classifier file used for face detection. + \param filename : Full path to access to the file. Such a file can be found in OpenCV. Within the last versions + it was name "haarcascade_frontalface_alt.xml". + */ +void vpDetectorFace::setCascadeClassifierFile(const std::string &filename) +{ + if( ! m_face_cascade.load( filename ) ) { + throw vpException(vpException::ioError, "Cannot read haar file: %s", filename.c_str()); + } +} + +/*! + Allows to detect a face in the image. When more than one face is detected, faces are sorted from largest to smallest. + + \param I : Input image to process. + \return true if one or more faces are found, false otherwise. + + The number of detected faces is returned using getNbObjects(). + If a face is found the functions getBBox(), getCog() return some information about the location of the face. + + The largest face is always available using getBBox(0) or getCog(0). + */ +bool vpDetectorFace::detect(const vpImage<unsigned char> &I) +{ + vpImageConvert::convert(I, m_frame_gray); + + bool detected = false; + m_message.clear(); + m_polygon.clear(); + m_nb_objects = 0; + + m_faces.clear(); +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + m_face_cascade.detectMultiScale( m_frame_gray, m_faces, 1.1, 2, 0, cv::Size(30, 30) ); +#else + m_face_cascade.detectMultiScale( m_frame_gray, m_faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, cv::Size(30, 30) ); +#endif + + m_nb_objects = m_faces.size(); + + std::sort(m_faces.begin(), m_faces.end(), vpSortLargestFace); + + if (m_faces.size()) + for( size_t i = 0; i < m_faces.size(); i++ ) { + std::ostringstream message; + message << "Face " << i; + m_message.push_back( message.str() ); + + detected = true; + + std::vector<vpImagePoint> polygon; + double x = m_faces[i].tl().x; + double y = m_faces[i].tl().y; + double w = m_faces[i].size().width; + double h = m_faces[i].size().height; + + polygon.push_back(vpImagePoint(y , x )); + polygon.push_back(vpImagePoint(y+h, x )); + polygon.push_back(vpImagePoint(y+h, x+w)); + polygon.push_back(vpImagePoint(y , x+w)); + + m_polygon.push_back(polygon); + } + + return detected; +} + + +#endif diff --git a/src/detection/face/vpDetectorFace.h b/src/detection/face/vpDetectorFace.h new file mode 100644 index 0000000000000000000000000000000000000000..d27ba882191976e8503d6806f62b5b338138f457 --- /dev/null +++ b/src/detection/face/vpDetectorFace.h @@ -0,0 +1,100 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Detect faces. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#ifndef __vpDetectorFace_h__ +#define __vpDetectorFace_h__ + +#include <visp/vpConfig.h> + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020200) + +#include <opencv2/objdetect/objdetect.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> + +#include <visp/vpDetectorBase.h> + +/*! + \class vpDetectorFace + + The vpDetectorFace class is a wrapper over OpenCV Haar cascade face detection capabilities. + To use this class ViSP should be build against OpenCV 2.2.0 or a more recent version. + + The following sample code shows how to use this class to detect the largest face in the image. + The cascade classifier file "haarcascade_frontalface_alt.xml" can be found in ViSP source code or in OpenCV. + \code +#include <visp/vpDetectorFace.h> + +int main() +{ + vpImage<unsigned char> I; + vpDetectorFace face_detector; + face_detector.setCascadeClassifierFile("haarcascade_frontalface_alt.xml"); + + while(1) { + // acquire a new image in I + bool face_found = face_detector.track(I); + if (face_found) { + vpRect face_bbox = face_detector.getBoundingBox(0); // largest face has index 0 + } + } +} + \endcode + + A more complete example that works with images acquired from a camera is provided in tutorial-face-detector-live.cpp. + */ +class VISP_EXPORT vpDetectorFace : public vpDetectorBase +{ +protected: + std::vector<cv::Rect> m_faces; //!< Bounding box of each detected face. + cv::CascadeClassifier m_face_cascade; //!< Haar cascade classifier file name. + cv::Mat m_frame_gray; //!< OpenCV image used as input for the face detection. + +public: + vpDetectorFace(); + /*! + Default destructor. + */ + virtual ~vpDetectorFace() {}; + void setCascadeClassifierFile(const std::string &filename); + + bool detect(const vpImage<unsigned char> &I); +}; + +#endif +#endif diff --git a/src/detection/vpDetectorBase.h b/src/detection/vpDetectorBase.h new file mode 100644 index 0000000000000000000000000000000000000000..e0191f4d5bac711c86a4efbdc96e2e736db5228e --- /dev/null +++ b/src/detection/vpDetectorBase.h @@ -0,0 +1,162 @@ +/**************************************************************************** + * + * $Id$ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Base class for object detection. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#ifndef __vpDetectorBase_h__ +#define __vpDetectorBase_h__ + +#include <vector> +#include <utility> +#include <string> +#include <assert.h> + +#include <visp/vpImage.h> +#include <visp/vpImagePoint.h> +#include <visp/vpRect.h> + +/*! + \class vpDetectorBase + + Base class for object detection. + + This class is a generic class that can be used to detect: + - bar codes like QRcodes of Data matrices. The example given in tutorial-barcode-detector.cpp shows + how to detect one or more bar codes in an image. In tutorial-barcode-detector-live.cpp you will find + an other example that shows how to use this class to detect bar codes in images acquired by a camera. + - faces. An example is provided in tutorial-face-detector-live.cpp. + */ +class VISP_EXPORT vpDetectorBase +{ +protected: + std::vector< std::vector<vpImagePoint> > m_polygon; //!< For each object, defines the polygon that contains the object. + std::vector< std::string > m_message; //!< Message attached to each object. + size_t m_nb_objects; //!< Number of detected objects. + +public: + /*! + Default constructor. + */ + vpDetectorBase() : m_polygon(), m_message(), m_nb_objects(0) {} + /*! + Default destructor. + */ + virtual ~vpDetectorBase() {}; + + /*! + Detect objects in an image. + \param I : Image where to detect objects. + \return true if one or multiple objects are detected, false otherwise. + */ + virtual bool detect(const vpImage<unsigned char> &I) = 0; + /*! + Returns object container box as a vector of points. + */ + std::vector< std::vector<vpImagePoint> > & getPolygon() + { + return m_polygon; + } + + /*! + Returns ith object container box as a vector of points. + */ + std::vector<vpImagePoint> & getPolygon(size_t i) + { + if (i < m_polygon.size()) + return m_polygon[i]; + else + throw(vpException(vpException::badValue, "Bad index to retrieve object %d. Only %d objects are detected.", i, m_polygon.size())); + } + /*! + Returns the contained message of the ith object if there is one. + */ + std::string & getMessage(size_t i) + { + if (i < m_polygon.size()) + return m_message[i]; + else + throw(vpException(vpException::badValue, "Bad index to retrieve object %d . Only %d objects are detected.", i, m_polygon.size())); + } + /*! + Returns the contained message of the ith object if there is one. + */ + std::vector< std::string > & getMessage() + { + return m_message; + } + /*! + Return the number of objects that are detected. + */ + size_t getNbObjects() const {return m_nb_objects; } + /*! + Return the center of gravity location of the ith object. + */ + vpImagePoint getCog(size_t i) const + { + vpImagePoint cog(0,0); + for(size_t j=0; j < m_polygon[i].size(); j++) { + cog += m_polygon[i][j]; + } + cog /= (double)m_polygon[i].size(); + return cog; + } + /*! + Return the bounding box of the ith object. + */ + vpRect getBBox(size_t i) const + { + assert(m_polygon[i].size() > 2); + + double left, right; + double top, bottom; + left = right = m_polygon[i][0].get_u(); + top = bottom = m_polygon[i][0].get_v(); + for(size_t j=0; j < m_polygon[i].size(); j++) { + double u = m_polygon[i][j].get_u(); + double v = m_polygon[i][j].get_v(); + if (u < left) left = u; + if (u > right) right = u; + if (v < top) top = v; + if (v > bottom) bottom = v; + } + vpRect roi(vpImagePoint(top, left), vpImagePoint(bottom, right)); + return roi; + } +}; + +#endif diff --git a/src/device/display/vpDisplay.cpp b/src/device/display/vpDisplay.cpp index 50f501fed02de0975f52278010d3bda8854b8494..6a99f611c960ade518eb007953e6174579c8ddcb 100644 --- a/src/device/display/vpDisplay.cpp +++ b/src/device/display/vpDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplay.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplay.cpp 5008 2014-11-25 17:59:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,6 +40,8 @@ * *****************************************************************************/ +#include <limits> + #include <visp/vpDisplay.h> #include <visp/vpDisplayException.h> #include <visp/vpImageConvert.h> @@ -58,25 +60,28 @@ Default constructor. */ vpDisplay::vpDisplay() + : displayHasBeenInitialized(false), windowXPosition(0), windowYPosition(0), width(0), height(0), title_() {} + +/*! + Copy constructor. +*/ +vpDisplay::vpDisplay(const vpDisplay &d) + : displayHasBeenInitialized(false), windowXPosition(0), windowYPosition(0), width(0), height(0), title_() { - displayHasBeenInitialized = false; - windowXPosition = 0; - windowYPosition = 0; + displayHasBeenInitialized = d.displayHasBeenInitialized; + windowXPosition = d.windowXPosition; + windowYPosition = d.windowYPosition; - title = new char[1024]; - strcpy (title, "" ); + width = d.width; + height = d.height; } + /*! Destructor that desallocates memory. */ vpDisplay::~vpDisplay() { displayHasBeenInitialized = false ; - - if (title != NULL) { - delete [] title; - title = NULL; - } } /*! @@ -112,12 +117,14 @@ vpDisplay::setFont ( const vpImage<unsigned char> &I, } /*! Set the windows title. + \note This functionality is not implemented when vpDisplayOpenCV is used. + \param I : Image associated to the display window. \param windowtitle : Window title. */ void vpDisplay::setTitle ( const vpImage<unsigned char> &I, - const char *windowtitle ) + const char *windowtitle ) { try @@ -722,6 +729,7 @@ vpDisplay::displayArrow ( const vpImage<vpRGBa> &I, /*! Display a string at the image point \e ip location. + Use rather displayText() that does the same. To select the font used to display the string, use setFont(). @@ -730,7 +738,7 @@ vpDisplay::displayArrow ( const vpImage<vpRGBa> &I, \param string : String to display in overlay. \param color : String color. - \sa setFont() + \sa setFont(), displayText() */ void @@ -755,7 +763,8 @@ vpDisplay::displayCharString ( const vpImage<unsigned char> &I, /*! Display a string at the image point \e ip location. - + Use rather displayText() that does the same. + To select the font used to display the string, use setFont(). \param I : Image associated to the display. @@ -763,7 +772,7 @@ vpDisplay::displayCharString ( const vpImage<unsigned char> &I, \param string : String to display in overlay. \param color : String color. - \sa setFont() + \sa setFont(), displayText() */ void @@ -788,7 +797,8 @@ vpDisplay::displayCharString ( const vpImage<vpRGBa> &I, /*! Display a string at the image point (i,j) location. - + Use rather displayText() that does the same. + To select the font used to display the string, use setFont(). \param I : Image associated to the display. @@ -796,7 +806,7 @@ vpDisplay::displayCharString ( const vpImage<vpRGBa> &I, \param string : String to display in overlay. \param color : String color. - \sa setFont() + \sa setFont(), displayText() */ void @@ -825,7 +835,8 @@ vpDisplay::displayCharString ( const vpImage<unsigned char> &I, /*! Display a string at the image point (i,j) location. - + Use rather displayText() that does the same. + To select the font used to display the string, use setFont(). \param I : Image associated to the display. @@ -833,13 +844,13 @@ vpDisplay::displayCharString ( const vpImage<unsigned char> &I, \param string : String to display in overlay. \param color : String color. - \sa setFont() + \sa setFont(), displayText() */ void vpDisplay::displayCharString ( const vpImage<vpRGBa> &I, int i, int j, const char *string, - const vpColor &color) + const vpColor &color) { try { @@ -858,6 +869,145 @@ vpDisplay::displayCharString ( const vpImage<vpRGBa> &I, } } +/*! + + Display a string at the image point \e ip location. + + To select the font used to display the string, use setFont(). + + \param I : Image associated to the display. + \param ip : Upper left image point location of the string in the display. + \param s : String to display in overlay. + \param color : String color. + + \sa setFont() + +*/ +void +vpDisplay::displayText ( const vpImage<unsigned char> &I, + const vpImagePoint &ip, const std::string &s, + const vpColor &color ) +{ + try + { + if ( I.display != NULL ) + { + ( I.display )->displayCharString ( ip, s.c_str(), color ) ; + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + +/*! + + Display a string at the image point \e ip location. + + To select the font used to display the string, use setFont(). + + \param I : Image associated to the display. + \param ip : Upper left image point location of the string in the display. + \param s : String to display in overlay. + \param color : String color. + + \sa setFont() + +*/ +void +vpDisplay::displayText ( const vpImage<vpRGBa> &I, + const vpImagePoint &ip, const std::string &s, + const vpColor &color ) +{ + try + { + if ( I.display != NULL ) + { + ( I.display )->displayCharString ( ip, s.c_str(), color ) ; + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + +/*! + + Display a string at the image point (i,j) location. + + To select the font used to display the string, use setFont(). + + \param I : Image associated to the display. + \param i,j : Upper left image point location of the string in the display. + \param s : String to display in overlay. + \param color : String color. + + \sa setFont() + +*/ +void +vpDisplay::displayText ( const vpImage<unsigned char> &I, + int i, int j, const std::string &s, + const vpColor &color) +{ + try + { + if ( I.display != NULL ) + { + vpImagePoint ip; + ip.set_i( i ); + ip.set_j( j ); + + ( I.display )->displayCharString ( ip, s.c_str(), color ) ; + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + +/*! + + Display a string at the image point (i,j) location. + + To select the font used to display the string, use setFont(). + + \param I : Image associated to the display. + \param i,j : Upper left image point location of the string in the display. + \param s : String to display in overlay. + \param color : String color. + + \sa setFont() + +*/ +void +vpDisplay::displayText ( const vpImage<vpRGBa> &I, + int i, int j, const std::string &s, + const vpColor &color) +{ + try + { + if ( I.display != NULL ) + { + vpImagePoint ip; + ip.set_i( i ); + ip.set_j( j ); + ( I.display )->displayCharString ( ip, s.c_str(), color ) ; + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + /*! Display a circle. \param I : The image associated to the display. @@ -1356,16 +1506,24 @@ void vpDisplay::displayLine ( const vpImage<vpRGBa> &I, \param I : The image associated to the display. \param ip : Point location. \param color : Point color. + \param thickness : Thickness of the point */ void vpDisplay::displayPoint ( const vpImage<unsigned char> &I, const vpImagePoint &ip, - const vpColor &color ) + const vpColor &color, + unsigned int thickness ) { try { if ( I.display != NULL ) { - ( I.display )->displayPoint ( ip, color ) ; + if (thickness == 1) + ( I.display )->displayPoint ( ip, color ) ; + else { + vpRect rect(0, 0, thickness, thickness); + rect.moveCenter(ip); + ( I.display )->displayRectangle ( rect, color, true ) ; + } } } catch ( ... ) @@ -1379,16 +1537,24 @@ void vpDisplay::displayPoint ( const vpImage<unsigned char> &I, \param I : The image associated to the display. \param ip : Point location. \param color : Point color. + \param thickness : Thickness of the point */ void vpDisplay::displayPoint ( const vpImage<vpRGBa> &I, const vpImagePoint &ip, - const vpColor &color ) + const vpColor &color, + unsigned int thickness ) { try { if ( I.display != NULL ) { - ( I.display )->displayPoint ( ip, color ) ; + if (thickness == 1) + ( I.display )->displayPoint ( ip, color ) ; + else { + vpRect rect(0, 0, thickness, thickness); + rect.moveCenter(ip); + ( I.display )->displayRectangle ( rect, color, true ) ; + } } } catch ( ... ) @@ -1403,10 +1569,12 @@ void vpDisplay::displayPoint ( const vpImage<vpRGBa> &I, \param I : The image associated to the display. \param i,j : Point location. \param color : Point color. + \param thickness : Thickness of the point */ void vpDisplay::displayPoint ( const vpImage<unsigned char> &I, int i, int j, - const vpColor &color ) + const vpColor &color, + unsigned int thickness ) { try { @@ -1415,7 +1583,13 @@ void vpDisplay::displayPoint ( const vpImage<unsigned char> &I, vpImagePoint ip; ip.set_i( i ); ip.set_j( j ); - ( I.display )->displayPoint ( ip, color ) ; + if (thickness == 1) + ( I.display )->displayPoint ( ip, color ) ; + else { + vpRect rect(0, 0, thickness, thickness); + rect.moveCenter(ip); + ( I.display )->displayRectangle ( rect, color, true ) ; + } } } catch ( ... ) @@ -1431,10 +1605,13 @@ void vpDisplay::displayPoint ( const vpImage<unsigned char> &I, \param I : The image associated to the display. \param i,j : Point location. \param color : Point color. + \param thickness : Thickness of the point + */ void vpDisplay::displayPoint ( const vpImage<vpRGBa> &I, int i, int j, - const vpColor &color ) + const vpColor &color, + unsigned int thickness ) { try { @@ -1443,7 +1620,71 @@ void vpDisplay::displayPoint ( const vpImage<vpRGBa> &I, vpImagePoint ip; ip.set_i( i ); ip.set_j( j ); - ( I.display )->displayPoint ( ip, color ) ; + if (thickness == 1) + ( I.display )->displayPoint ( ip, color ) ; + else { + vpRect rect(0, 0, thickness, thickness); + rect.moveCenter(ip); + ( I.display )->displayRectangle ( rect, color, true ) ; + } + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + +/*! + Display a polygon defined by a vector of image points. + \param I : The image associated to the display. + \param vip : Vector of image point that define the vertexes of the polygon. + \param color : Line color. + \param thickness : Line thickness. + +*/ +void +vpDisplay::displayPolygon(const vpImage<unsigned char> &I, + const std::vector<vpImagePoint> &vip, + const vpColor &color, + unsigned int thickness) +{ + try + { + if ( I.display != NULL ) + { + for (unsigned int i=0; i< vip.size(); i++) + ( I.display )->displayLine ( vip[i], vip[(i+1)%vip.size()], color, thickness ); + } + } + catch ( ... ) + { + vpERROR_TRACE ( "Error caught" ) ; + throw ; + } +} + +/*! + Display a polygon defined by a vector of image points. + \param I : The image associated to the display. + \param vip : Vector of image point that define the vertexes of the polygon. + \param color : Line color. + \param thickness : Line thickness. + +*/ +void +vpDisplay::displayPolygon(const vpImage<vpRGBa> &I, + const std::vector<vpImagePoint> &vip, + const vpColor &color, + unsigned int thickness) +{ + try + { + if ( I.display != NULL ) + { + for (unsigned int i=0; i< vip.size(); i++) + ( I.display )->displayLine ( vip[i], vip[(i+1)%vip.size()], color, thickness ); } } catch ( ... ) @@ -2046,6 +2287,8 @@ void vpDisplay::close ( vpImage<unsigned char> &I ) /*! Set the windows title. + \note This functionality is not implemented when vpDisplayOpenCV is used. + \param I : Image associated to the display window. \param windowtitle : Window title. */ @@ -2480,7 +2723,7 @@ bool vpDisplay::getClick ( const vpImage<unsigned char> &I, bool vpDisplay::getClick ( const vpImage<unsigned char> &I, vpImagePoint &ip, vpMouseButton::vpMouseButtonType& button, - bool blocking) + bool blocking) { try { @@ -2497,6 +2740,56 @@ bool vpDisplay::getClick ( const vpImage<unsigned char> &I, return false ; } +/*! + Wait for a mouse button click and get the position of the clicked + image point. The button used to click is also set. + + \param I [in] : The displayed image. + + \param button [out] : The button used to click. + + \param blocking [in] : + - When set to true, this method waits until a mouse button is + pressed and then returns always true. + - When set to false, returns true only if a mouse button is + pressed, otherwise returns false. + + \return true if a mouse button is pressed, false otherwise. + +*/ +bool vpDisplay::getClick ( const vpImage<unsigned char> &I, + vpMouseButton::vpMouseButtonType& button, + bool blocking) +{ + vpImagePoint ip; + return vpDisplay::getClick(I, ip, button, blocking); +} + +/*! + Wait for a mouse button click and get the position of the clicked + image point. The button used to click is also set. + + \param I [in] : The displayed image. + + \param button [out] : The button used to click. + + \param blocking [in] : + - When set to true, this method waits until a mouse button is + pressed and then returns always true. + - When set to false, returns true only if a mouse button is + pressed, otherwise returns false. + + \return true if a mouse button is pressed, false otherwise. + +*/ +bool vpDisplay::getClick ( const vpImage<vpRGBa> &I, + vpMouseButton::vpMouseButtonType& button, + bool blocking) +{ + vpImagePoint ip; + return vpDisplay::getClick(I, ip, button, blocking); +} + /*! Wait for a mouse button click release and get the position of the clicked image point. The button used to click is also set. @@ -2538,6 +2831,57 @@ vpDisplay::getClickUp ( const vpImage<unsigned char> &I, } return false ; } + +/*! + Wait for a mouse button click release and get the position of the clicked + image point. The button used to click is also set. + + \param I [in] : The displayed image. + + \param button [out] : The clicked button. + + \param blocking [in] : + - When set to true, this method waits until a mouse button is + released and then returns always true. + - When set to false, returns true only if a mouse button is + released, otherwise returns false. + + \return true if a mouse button is released, false otherwise. + +*/ +bool vpDisplay::getClickUp ( const vpImage<unsigned char> &I, + vpMouseButton::vpMouseButtonType& button, + bool blocking) +{ + vpImagePoint ip; + return vpDisplay::getClickUp(I, ip, button, blocking); +} + +/*! + Wait for a mouse button click release and get the position of the clicked + image point. The button used to click is also set. + + \param I [in] : The displayed image. + + \param button [out] : The clicked button. + + \param blocking [in] : + - When set to true, this method waits until a mouse button is + released and then returns always true. + - When set to false, returns true only if a mouse button is + released, otherwise returns false. + + \return true if a mouse button is released, false otherwise. + +*/ +bool vpDisplay::getClickUp ( const vpImage<vpRGBa> &I, + vpMouseButton::vpMouseButtonType& button, + bool blocking) +{ + vpImagePoint ip; + return vpDisplay::getClickUp(I, ip, button, blocking); +} + /*! Get a keyboard event. @@ -3215,3 +3559,402 @@ vpDisplay::getPointerPosition (const vpImage<vpRGBa> &I, vpImagePoint &ip) } return false; } + +/*! + Display an ellipse from its parameters expressed in pixels. + \param I : Image to consider. + \param center : Center \f$(u_c, v_c)\f$ of the ellipse. + \param coef1, coef2, coef3 : Depending on the parameter \e use_centered_moments these parameters + are: + - the centered moments expressed in pixels: \f$\mu_{20}, \mu_{11}, \mu_{02}\f$; + - the major and minor axis lenght in pixels and the excentricity of the ellipse in radians: \f$a, b, e\f$. + \param use_centered_moments : When false, the parameters coef1, coef2, coef3 + are the parameters \f$a, b, e\f$. When true, the parameters coef1, coef2, coef3 are rather the centered moments + \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ expressed in pixels. In that case, we compute the parameters \e a, \e b and \e e + from the centered moments. + \param color : Drawings color. + \param thickness : Drawings thickness. + + All the points \f$(u_\theta,v_\theta)\f$ on the ellipse are drawn thanks to its parametric representation: + + \f[ \left(\begin{array}{c} + u_\theta \\ + v_\theta + \end{array} \right) = \left(\begin{array}{c} + u_c \\ + v_c + \end{array} \right) + \left(\begin{array}{cc} + \cos(e) & -\sin(e) \\ + \sin(e) & \cos(e) + \end{array} \right) \left(\begin{array}{c} + a \cos(\theta) \\ + b \sin(\theta) + \end{array} \right) \f] + + with \f$0 \leq \theta \leq 2\pi\f$. + + The following example shows how to use for example this function to display the result of a tracking. + \code + vpMeEllipse ellipse; + ... + vpDisplay::display(I); + ellipse.track(I) ; + + vpDisplay::displayEllipse(I, ellipse.getCenter(), ellipse.get_mu20(), ellipse.get_mu11(), ellipse.get_mu02(), + true, vpColor::orange, 1); + vpDisplay::flush(I); + \endcode +*/ +void vpDisplay::displayEllipse(const vpImage<unsigned char> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + bool use_centered_moments, + const vpColor &color, + unsigned int thickness) +{ + vpDisplay::displayEllipse(I, center, coef1, coef2, coef3, 0., vpMath::rad(360), use_centered_moments, color, thickness); +} + +/*! + Display an ellipse from its parameters expressed in pixels. + \param I : Image to consider. + \param center : Center \f$(u_c, v_c)\f$ of the ellipse. + \param coef1, coef2, coef3 : Depending on the parameter \e use_centered_moments these parameters + are: + - the centered moments expressed in pixels: \f$\mu_{20}, \mu_{11}, \mu_{02}\f$; + - the major and minor axis lenght in pixels and the excentricity of the ellipse in radians: \f$a, b, e\f$. + \param theta1, theta2 : Angles \f$(\theta_1, \theta_2)\f$ in radians used to select a portion of the ellipse. + If theta1=0 and theta2=vpMath::rad(360) all the ellipse is displayed. + \param use_centered_moments : When false, the parameters coef1, coef2, coef3 + are the parameters \f$a, b, e\f$. When true, the parameters coef1, coef2, coef3 are rather the centered moments + \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ expressed in pixels. In that case, we compute the parameters \e a, \e b and \e e + from the centered moments. + \param color : Drawings color. + \param thickness : Drawings thickness. + + All the points \f$(u_\theta,v_\theta)\f$ on the ellipse are drawn thanks to its parametric representation: + + \f[ \left(\begin{array}{c} + u_\theta \\ + v_\theta + \end{array} \right) = \left(\begin{array}{c} + u_c \\ + v_c + \end{array} \right) + \left(\begin{array}{cc} + \cos(e) & -\sin(e) \\ + \sin(e) & \cos(e) + \end{array} \right) \left(\begin{array}{c} + a \cos(\theta) \\ + b \sin(\theta) + \end{array} \right) \f] + + with \f$\theta_1 \leq \theta \leq \theta_2\f$. + + The following example shows how to use for example this function to display the result of a tracking. + \code + vpMeEllipse ellipse; + ... + vpDisplay::display(I); + ellipse.track(I) ; + + vpDisplay::displayEllipse(I, ellipse.getCenter(), ellipse.get_mu20(), + ellipse.get_mu11(), ellipse.get_mu02(), + ellipse.getSmallestAngle(), ellipse.getHighestAngle(), + true, vpColor::orange, 1); + vpDisplay::flush(I); + \endcode +*/ +void vpDisplay::displayEllipse(const vpImage<vpRGBa> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + const double &theta1, const double &theta2, bool use_centered_moments, + const vpColor &color, + unsigned int thickness) +{ + try + { + if ( I.display != NULL ) + { + double j1, i1; + vpImagePoint iP11; + double j2, i2; + vpImagePoint iP22; + j1 = j2 = i1 = i2 = 0 ; + double a=0., b=0., e=0.; + + double mu20_p = coef1; + double mu11_p = coef2; + double mu02_p = coef3; + + if (use_centered_moments) { + if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) { + + double val_p = sqrt(vpMath::sqr(mu20_p-mu02_p) + 4*vpMath::sqr(mu11_p)); + a = sqrt((mu20_p + mu02_p + val_p)/2); + b = sqrt((mu20_p + mu02_p - val_p)/2); + + e = (mu02_p - mu20_p + val_p)/(2*mu11_p); + e = atan(e); + } + else { + a = sqrt(mu20_p); + b = sqrt(mu02_p); + e = 0.; + } + } + else { + a = coef1; + b = coef2; + e = coef3; + } + + // Approximation of the circumference of an ellipse: + // [Ramanujan, S., "Modular Equations and Approximations to ," + // Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372] + double t = (a-b)/(a+b); + double circumference = M_PI*(a+b)*(1 + 3*vpMath::sqr(t)/(10 + sqrt(4 - 3*vpMath::sqr(t)))); + + int nbpoints = (int)(floor(circumference/5)); + if (nbpoints < 10) + nbpoints = 10; + double incr = 2*M_PI / nbpoints ; // angle increment + + double smallalpha = theta1; + double highalpha = theta2; + double ce = cos(e); + double se = sin(e); + + double k = smallalpha ; + j1 = a *cos(k) ; // equation of an ellipse + i1 = b *sin(k) ; // equation of an ellipse + + // (i1,j1) are the coordinates on the origin centered ellipse ; + // a rotation by "e" and a translation by (xci,jc) are done + // to get the coordinates of the point on the shifted ellipse + iP11.set_j ( center.get_j() + ce *j1 - se *i1 ); + iP11.set_i ( center.get_i() + se *j1 + ce *i1 ); + + while (k+incr<highalpha+incr) + { + j2 = a *cos(k+incr) ; // equation of an ellipse + i2 = b *sin(k+incr) ; // equation of an ellipse + + // to get the coordinates of the point on the shifted ellipse + iP22.set_j ( center.get_j() + ce *j2 - se *i2 ); + iP22.set_i ( center.get_i() + se *j2 + ce *i2 ); + + ( I.display )->displayLine(iP11, iP22, color, thickness) ; + + i1 = i2; + j1 = j2; + iP11 = iP22; + + k += incr ; + } + } + } + catch ( vpException &e ) + { + throw(e) ; + } +} + +/*! + Display an ellipse from its parameters expressed in pixels. + \param I : Image to consider. + \param center : Center \f$(u_c, v_c)\f$ of the ellipse. + \param coef1, coef2, coef3 : Depending on the parameter \e use_centered_moments these parameters + are: + - the centered moments expressed in pixels: \f$\mu_{20}, \mu_{11}, \mu_{02}\f$; + - the major and minor axis lenght in pixels and the excentricity of the ellipse in radians: \f$a, b, e\f$. + \param use_centered_moments : When false, the parameters coef1, coef2, coef3 + are the parameters \f$a, b, e\f$. When true, the parameters coef1, coef2, coef3 are rather the centered moments + \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ expressed in pixels. In that case, we compute the parameters \e a, \e b and \e e + from the centered moments. + \param color : Drawings color. + \param thickness : Drawings thickness. + + All the points \f$(u_\theta,v_\theta)\f$ on the ellipse are drawn thanks to its parametric representation: + + \f[ \left(\begin{array}{c} + u_\theta \\ + v_\theta + \end{array} \right) = \left(\begin{array}{c} + u_c \\ + v_c + \end{array} \right) + \left(\begin{array}{cc} + \cos(e) & -\sin(e) \\ + \sin(e) & \cos(e) + \end{array} \right) \left(\begin{array}{c} + a \cos(\theta) \\ + b \sin(\theta) + \end{array} \right) \f] + + with \f$0 \leq \theta \leq 2\pi\f$. + + The following example shows how to use for example this function to display the result of a tracking. + \code + vpMeEllipse ellipse; + ... + vpDisplay::display(I); + ellipse.track(I) ; + + vpDisplay::displayEllipse(I, ellipse.getCenter(), ellipse.get_mu20(), ellipse.get_mu11(), ellipse.get_mu02(), + true, vpColor::orange, 1); + vpDisplay::flush(I); + \endcode +*/ +void vpDisplay::displayEllipse(const vpImage<vpRGBa> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + bool use_centered_moments, + const vpColor &color, + unsigned int thickness) +{ + vpDisplay::displayEllipse(I, center, coef1, coef2, coef3, 0., vpMath::rad(360), use_centered_moments, color, thickness); +} + +/*! + Display an ellipse from its parameters expressed in pixels. + \param I : Image to consider. + \param center : Center \f$(u_c, v_c)\f$ of the ellipse. + \param coef1, coef2, coef3 : Depending on the parameter \e use_centered_moments these parameters + are: + - the centered moments expressed in pixels: \f$\mu_{20}, \mu_{11}, \mu_{02}\f$; + - the major and minor axis lenght in pixels and the excentricity of the ellipse in radians: \f$a, b, e\f$. + \param theta1, theta2 : Angles \f$(\theta_1, \theta_2)\f$ in radians used to select a portion of the ellipse. + If theta1=0 and theta2=vpMath::rad(360) all the ellipse is displayed. + \param use_centered_moments : When false, the parameters coef1, coef2, coef3 + are the parameters \f$a, b, e\f$. When true, the parameters coef1, coef2, coef3 are rather the centered moments + \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ expressed in pixels. In that case, we compute the parameters \e a, \e b and \e e + from the centered moments. + \param color : Drawings color. + \param thickness : Drawings thickness. + + All the points \f$(u_\theta,v_\theta)\f$ on the ellipse are drawn thanks to its parametric representation: + + \f[ \left(\begin{array}{c} + u_\theta \\ + v_\theta + \end{array} \right) = \left(\begin{array}{c} + u_c \\ + v_c + \end{array} \right) + \left(\begin{array}{cc} + \cos(e) & -\sin(e) \\ + \sin(e) & \cos(e) + \end{array} \right) \left(\begin{array}{c} + a \cos(\theta) \\ + b \sin(\theta) + \end{array} \right) \f] + + with \f$\theta_1 \leq \theta \leq \theta_2\f$. + + The following example shows how to use for example this function to display the result of a tracking. + \code + vpMeEllipse ellipse; + ... + vpDisplay::display(I); + ellipse.track(I) ; + + vpDisplay::displayEllipse(I, ellipse.getCenter(), ellipse.get_mu20(), + ellipse.get_mu11(), ellipse.get_mu02(), + ellipse.getSmallestAngle(), ellipse.getHighestAngle(), + true, vpColor::orange, 1); + vpDisplay::flush(I); + \endcode +*/ +void vpDisplay::displayEllipse(const vpImage<unsigned char> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + const double &theta1, const double &theta2, bool use_centered_moments, + const vpColor &color, + unsigned int thickness) +{ + try + { + if ( I.display != NULL ) + { + double j1, i1; + vpImagePoint iP11; + double j2, i2; + vpImagePoint iP22; + j1 = j2 = i1 = i2 = 0 ; + double a=0., b=0., e=0.; + + double mu20_p = coef1; + double mu11_p = coef2; + double mu02_p = coef3; + + if (use_centered_moments) { + if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) { + + double val_p = sqrt(vpMath::sqr(mu20_p-mu02_p) + 4*vpMath::sqr(mu11_p)); + a = sqrt((mu20_p + mu02_p + val_p)/2); + b = sqrt((mu20_p + mu02_p - val_p)/2); + + e = (mu02_p - mu20_p + val_p)/(2*mu11_p); + e = atan(e); + } + else { + a = sqrt(mu20_p); + b = sqrt(mu02_p); + e = 0.; + } + } + else { + a = coef1; + b = coef2; + e = coef3; + } + + // Approximation of the circumference of an ellipse: + // [Ramanujan, S., "Modular Equations and Approximations to ," + // Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372] + double t = (a-b)/(a+b); + double circumference = M_PI*(a+b)*(1 + 3*vpMath::sqr(t)/(10 + sqrt(4 - 3*vpMath::sqr(t)))); + + int nbpoints = (int)(floor(circumference/5)); + if (nbpoints < 10) + nbpoints = 10; + double incr = 2*M_PI / nbpoints ; // angle increment + + double smallalpha = theta1; + double highalpha = theta2; + double ce = cos(e); + double se = sin(e); + + double k = smallalpha ; + j1 = a *cos(k) ; // equation of an ellipse + i1 = b *sin(k) ; // equation of an ellipse + + // (i1,j1) are the coordinates on the origin centered ellipse ; + // a rotation by "e" and a translation by (xci,jc) are done + // to get the coordinates of the point on the shifted ellipse + iP11.set_j ( center.get_j() + ce *j1 - se *i1 ); + iP11.set_i ( center.get_i() + se *j1 + ce *i1 ); + + while (k+incr<highalpha+incr) + { + j2 = a *cos(k+incr) ; // equation of an ellipse + i2 = b *sin(k+incr) ; // equation of an ellipse + + // to get the coordinates of the point on the shifted ellipse + iP22.set_j ( center.get_j() + ce *j2 - se *i2 ); + iP22.set_i ( center.get_i() + se *j2 + ce *i2 ); + + ( I.display )->displayLine(iP11, iP22, color, thickness) ; + + i1 = i2; + j1 = j2; + iP11 = iP22; + + k += incr ; + } + } + } + catch ( vpException &e ) + { + throw(e) ; + } +} + diff --git a/src/device/display/vpDisplay.h b/src/device/display/vpDisplay.h index e350bbc60143c59b2068abc6ed37078ce9085262..078b74daa5bdae42d6112b128223637f9a75b2d8 100644 --- a/src/device/display/vpDisplay.h +++ b/src/device/display/vpDisplay.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplay.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplay.h 5089 2014-12-19 07:58:34Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,10 +44,10 @@ #ifndef vpDisplay_h #define vpDisplay_h -// image -#include <visp/vpImage.h> +#include <string> +#include <sstream> -//color +#include <visp/vpImage.h> #include <visp/vpColor.h> #include <visp/vpMouseButton.h> #include <visp/vpRGBa.h> @@ -89,11 +89,10 @@ int main() vpImage<unsigned char> I; // Grey level image // Read an image in PGM P5 format -#ifdef UNIX - //vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(I, "/tmp/Klimt.pgm"); -#elif WIN32 +#ifdef _WIN32 vpImageIo::read(I, "C:/temp/ViSP-images/Klimt/Klimt.pgm"); +#else + vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); #endif vpDisplay *d; @@ -181,13 +180,9 @@ class VISP_EXPORT vpDisplay int windowXPosition ; //! display position int windowYPosition ; - //! display title - char *title ; - char *font; unsigned int width ; unsigned int height ; - - vpDisplay() ; + std::string title_; /*! Display an arrow from image point \e ip1 to image point \e ip2. @@ -252,10 +247,10 @@ class VISP_EXPORT vpDisplay \param color : Line color. \param thickness : Line thickness. */ - virtual void displayLine(const vpImagePoint &ip1, - const vpImagePoint &ip2, - const vpColor &color, - unsigned int thickness=1) =0; + virtual void displayLine(const vpImagePoint &ip1, + const vpImagePoint &ip2, + const vpColor &color, + unsigned int thickness=1) =0; /*! Display a point at the image point \e ip location. @@ -316,6 +311,8 @@ class VISP_EXPORT vpDisplay unsigned int thickness=1)=0 ; public: + vpDisplay() ; + vpDisplay(const vpDisplay& d) ; virtual ~vpDisplay(); /*! @@ -356,7 +353,16 @@ class VISP_EXPORT vpDisplay virtual void displayImageROI(const vpImage<unsigned char> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height) =0 ; virtual void displayImageROI(const vpImage<vpRGBa> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height) =0 ; - + /*! + Return the position (along the horizontal axis) on the screen of the display window. + \sa getWindowYPosition() + */ + int getWindowXPosition() const {return windowXPosition;}; + /*! + Return the position (along the vertical axis) on the screen of the display window. + \sa getWindowXPosition() + */ + int getWindowYPosition() const {return windowYPosition;}; /*! Flushes the display. It's necessary to use this function to see the results of any drawing. @@ -613,11 +619,11 @@ class VISP_EXPORT vpDisplay double size, const vpColor &color, unsigned int thickness) ; static void displayCharString(const vpImage<unsigned char> &I, - const vpImagePoint &ip, const char *string, - const vpColor &color) ; + const vpImagePoint &ip, const char *string, + const vpColor &color) ; static void displayCharString(const vpImage<unsigned char> &I, - int i, int j, const char *string, - const vpColor &color) ; + int i, int j, const char *string, + const vpColor &color) ; static void displayCircle(const vpImage<unsigned char> &I, const vpImagePoint ¢er, unsigned int radius, const vpColor &color, @@ -645,10 +651,22 @@ class VISP_EXPORT vpDisplay int i1, int j1, int i2, int j2, const vpColor &color, unsigned int thickness=1) ; + static void displayEllipse(const vpImage<unsigned char> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + bool use_centered_moments, + const vpColor &color, + unsigned int thickness=1); + static void displayEllipse(const vpImage<unsigned char> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + const double &theta1, const double &theta2, bool use_centered_moments, + const vpColor &color, + unsigned int thickness=1); static void displayFrame(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, - double size, const vpColor &color, + double size, const vpColor &color=vpColor::none, unsigned int thickness=1) ; static void displayLine(const vpImage<unsigned char> &I, const vpImagePoint &ip1, @@ -660,11 +678,17 @@ class VISP_EXPORT vpDisplay const vpColor &color, unsigned int thickness=1) ; static void displayPoint(const vpImage<unsigned char> &I, - const vpImagePoint &ip, - const vpColor &color) ; + const vpImagePoint &ip, + const vpColor &color, + unsigned int thickness=1) ; static void displayPoint(const vpImage<unsigned char> &I, - int i, int j, - const vpColor &color) ; + int i, int j, + const vpColor &color, + unsigned int thickness=1) ; + static void displayPolygon(const vpImage<unsigned char> &I, + const std::vector<vpImagePoint> &vip, + const vpColor &color, + unsigned int thickness=1) ; static void displayRectangle(const vpImage<unsigned char> &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, @@ -696,7 +720,12 @@ class VISP_EXPORT vpDisplay const vpColor &color, unsigned int thickness=1); static void displayROI(const vpImage<unsigned char> &I,const vpRect &roi) ; - + static void displayText(const vpImage<unsigned char> &I, + const vpImagePoint &ip, const std::string &s, + const vpColor &color) ; + static void displayText(const vpImage<unsigned char> &I, + int i, int j, const std::string &s, + const vpColor &color) ; static void flush(const vpImage<unsigned char> &I) ; static void flushROI(const vpImage<unsigned char> &I,const vpRect &roi) ; @@ -704,13 +733,19 @@ class VISP_EXPORT vpDisplay static bool getClick(const vpImage<unsigned char> &I, vpImagePoint &ip, bool blocking=true) ; static bool getClick(const vpImage<unsigned char> &I, - vpImagePoint &ip, - vpMouseButton::vpMouseButtonType &button, - bool blocking=true) ; + vpImagePoint &ip, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; + static bool getClick(const vpImage<unsigned char> &I, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; static bool getClickUp(const vpImage<unsigned char> &I, - vpImagePoint &ip, - vpMouseButton::vpMouseButtonType &button, - bool blocking=true) ; + vpImagePoint &ip, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; + static bool getClickUp(const vpImage<unsigned char> &I, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; static void getImage(const vpImage<unsigned char> &Is, vpImage<vpRGBa> &Id) ; static bool getKeyboardEvent(const vpImage<unsigned char> &I, @@ -750,11 +785,11 @@ class VISP_EXPORT vpDisplay double size, const vpColor &color, unsigned int thickness) ; static void displayCharString(const vpImage<vpRGBa> &I, - const vpImagePoint &ip, const char *string, - const vpColor &color) ; + const vpImagePoint &ip, const char *string, + const vpColor &color) ; static void displayCharString(const vpImage<vpRGBa> &I, - int i, int j, const char *string, - const vpColor &color) ; + int i, int j, const char *string, + const vpColor &color) ; static void displayCircle(const vpImage<vpRGBa> &I, const vpImagePoint ¢er, unsigned int radius, const vpColor &color, @@ -785,8 +820,20 @@ class VISP_EXPORT vpDisplay static void displayFrame(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, - double size, const vpColor &color, + double size, const vpColor &color=vpColor::none, unsigned int thickness=1) ; + static void displayEllipse(const vpImage<vpRGBa> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + bool use_centered_moments, + const vpColor &color, + unsigned int thickness=1); + static void displayEllipse(const vpImage<vpRGBa> &I, + const vpImagePoint ¢er, + const double &coef1, const double &coef2, const double &coef3, + const double &angle1, const double &angle2, bool use_centered_moments, + const vpColor &color, + unsigned int thickness=1); static void displayLine(const vpImage<vpRGBa> &I, const vpImagePoint &ip1, const vpImagePoint &ip2, @@ -797,11 +844,17 @@ class VISP_EXPORT vpDisplay const vpColor &color, unsigned int thickness=1) ; static void displayPoint(const vpImage<vpRGBa> &I, - const vpImagePoint &ip, - const vpColor &color) ; + const vpImagePoint &ip, + const vpColor &color, + unsigned int thickness=1) ; static void displayPoint(const vpImage<vpRGBa> &I, - int i, int j, - const vpColor &color) ; + int i, int j, + const vpColor &color, + unsigned int thickness=1) ; + static void displayPolygon(const vpImage<vpRGBa> &I, + const std::vector<vpImagePoint> &vip, + const vpColor &color, + unsigned int thickness=1) ; static void displayRectangle(const vpImage<vpRGBa> &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, @@ -834,6 +887,12 @@ class VISP_EXPORT vpDisplay const vpColor &color, unsigned int thickness=1); static void displayROI(const vpImage<vpRGBa> &I, const vpRect &roi) ; + static void displayText(const vpImage<vpRGBa> &I, + const vpImagePoint &ip, const std::string &s, + const vpColor &color) ; + static void displayText(const vpImage<vpRGBa> &I, + int i, int j, const std::string &s, + const vpColor &color) ; static void flush(const vpImage<vpRGBa> &I) ; static void flushROI(const vpImage<vpRGBa> &I, const vpRect &roi) ; @@ -841,13 +900,19 @@ class VISP_EXPORT vpDisplay static bool getClick(const vpImage<vpRGBa> &I, vpImagePoint &ip, bool blocking=true) ; static bool getClick(const vpImage<vpRGBa> &I, - vpImagePoint &ip, - vpMouseButton::vpMouseButtonType &button, - bool blocking=true) ; + vpImagePoint &ip, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; + static bool getClick(const vpImage<vpRGBa> &I, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; + static bool getClickUp(const vpImage<vpRGBa> &I, + vpImagePoint &ip, + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; static bool getClickUp(const vpImage<vpRGBa> &I, - vpImagePoint &ip, - vpMouseButton::vpMouseButtonType &button, - bool blocking=true) ; + vpMouseButton::vpMouseButtonType &button, + bool blocking=true) ; static void getImage(const vpImage<vpRGBa> &Is, vpImage<vpRGBa> &Id) ; static bool getKeyboardEvent(const vpImage<vpRGBa> &I, diff --git a/src/device/display/vpDisplayException.h b/src/device/display/vpDisplayException.h index e3eb704fc9edc238ef0b33b294ab94b6238d95a3..7c6263149ddeb9a951c60434adc1dcc8e0682e8d 100644 --- a/src/device/display/vpDisplayException.h +++ b/src/device/display/vpDisplayException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDisplayException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,12 +73,12 @@ */ class VISP_EXPORT vpDisplayException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpDisplay member */ - enum errorDisplayCodeEnum + enum errorDisplayCodeEnum { notInitializedError, cannotOpenWindowError, @@ -89,33 +89,26 @@ public: depthNotSupportedError } ; -public: - vpDisplayException(const int code, const char * msg) - : vpException(code, msg) + public: + vpDisplayException(const int id, const char* format, ...) { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); } - - vpDisplayException (const int code, const std::string & msg) - : vpException(code, msg) + + vpDisplayException (const int id, const std::string & msg) + : vpException(id, msg) { } - - vpDisplayException (const int code) - : vpException(code) - { - } - -}; - - - + vpDisplayException (const int id) + : vpException(id) + { + } -#endif /* #ifndef __vpDisplayException_H */ - +}; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/device/display/vpDisplayGTK.cpp b/src/device/display/vpDisplayGTK.cpp index 239549e135c4c11986a0955334151f3f58a3a32f..43d56c2c4b02893569ae01d8e07abf27e9e5b479 100644 --- a/src/device/display/vpDisplayGTK.cpp +++ b/src/device/display/vpDisplayGTK.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayGTK.cpp 4174 2013-03-22 10:28:41Z fspindle $ + * $Id: vpDisplayGTK.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,10 +80,13 @@ vpDisplayGTK::vpDisplayGTK(vpImage<unsigned char> &I, int x, int y, - const char *title) : vpDisplay() + const char *title) + : widget(NULL), background(NULL), gc(NULL), + blue(), red(), yellow(), green(), cyan(), orange(), white(), black(), gdkcolor(), + lightBlue(), darkBlue(), lightRed(), darkRed(),lightGreen(), darkGreen(), + purple(), lightGray(), gray(), darkGray(), + colormap(NULL), font(NULL), vectgtk(NULL), col(NULL), ncol(0), nrow(0) { - col = NULL; - widget = NULL ; init(I, x, y, title) ; } @@ -99,10 +102,13 @@ vpDisplayGTK::vpDisplayGTK(vpImage<unsigned char> &I, vpDisplayGTK::vpDisplayGTK(vpImage<vpRGBa> &I, int x, int y, - const char *title) : vpDisplay() + const char *title) + : widget(NULL), background(NULL), gc(NULL), + blue(), red(), yellow(), green(), cyan(), orange(), white(), black(), gdkcolor(), + lightBlue(), darkBlue(), lightRed(), darkRed(),lightGreen(), darkGreen(), + purple(), lightGray(), gray(), darkGray(), + colormap(NULL), font(NULL), vectgtk(NULL), col(NULL), ncol(0), nrow(0) { - col = NULL; - widget = NULL ; init(I, x, y, title) ; } @@ -130,16 +136,20 @@ int main() } \endcode */ -vpDisplayGTK::vpDisplayGTK(int x, int y, const char *title) : vpDisplay() +vpDisplayGTK::vpDisplayGTK(int x, int y, const char *title) + : widget(NULL), background(NULL), gc(NULL), + blue(), red(), yellow(), green(), cyan(), orange(), white(), black(), gdkcolor(), + lightBlue(), darkBlue(), lightRed(), darkRed(),lightGreen(), darkGreen(), + purple(), lightGray(), gray(), darkGray(), + colormap(NULL), font(NULL), vectgtk(NULL), col(NULL), ncol(0), nrow(0) { windowXPosition = x ; windowYPosition = y ; - col = NULL; - widget = NULL ; - - if (title != NULL) - strcpy(this->title, title) ; + if(title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); } /*! @@ -161,10 +171,14 @@ int main() } \endcode */ -vpDisplayGTK::vpDisplayGTK() : vpDisplay() +vpDisplayGTK::vpDisplayGTK() + : vpDisplay(), widget(NULL), background(NULL), gc(NULL), + blue(), red(), yellow(), green(), cyan(), orange(), white(), black(), gdkcolor(), + lightBlue(), darkBlue(), lightRed(), darkRed(),lightGreen(), darkGreen(), + purple(), lightGray(), gray(), darkGray(), + colormap(NULL), font(NULL), vectgtk(NULL), col(NULL), ncol(0), nrow(0) + { - col = NULL; - widget = NULL ; } /*! @@ -240,13 +254,13 @@ vpDisplayGTK::init(vpImage<vpRGBa> &I, /*! Initialize the display size, position and title. - \param width, height : Width and height of the window. + \param w, h : Width and height of the window. \param x, y : The window is set at position x,y (column index, row index). \param title : Window title. */ void -vpDisplayGTK::init(unsigned int width, unsigned int height, +vpDisplayGTK::init(unsigned int w, unsigned int h, int x, int y, const char *title) { @@ -255,8 +269,8 @@ vpDisplayGTK::init(unsigned int width, unsigned int height, char **argv ; gtk_init(argc,&argv); - this->width = width; - this->height = height; + this->width = w; + this->height = h; /* Create the window*/ widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -361,15 +375,20 @@ vpDisplayGTK::init(unsigned int width, unsigned int height, gdk_colormap_alloc_color(colormap,&darkGray,FALSE,TRUE); col[vpColor::id_darkGray] = &darkGray ; - /* Chargement des polices */ - Police1 = gdk_font_load("-*-times-medium-r-normal-*-16-*-*-*-*-*-*-*"); - Police2 = gdk_font_load("-*-courier-bold-r-normal-*-*-140-*-*-*-*-*-*"); + // Try to load a default font + font = gdk_font_load("-*-times-medium-r-normal-*-16-*-*-*-*-*-*-*"); + if (font == NULL) + font = gdk_font_load("-*-courier-bold-r-normal-*-*-140-*-*-*-*-*-*"); + if (font == NULL) + font = gdk_font_load("-*-courier 10 pitch-medium-r-normal-*-16-*-*-*-*-*-*-*"); - if (title != NULL) - strcpy(this->title, title) ; + if(title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); displayHasBeenInitialized = true ; - setTitle(this->title) ; + gdk_window_set_title(widget->window, title_.c_str()); } @@ -380,7 +399,7 @@ vpDisplayGTK::init(unsigned int width, unsigned int height, Set the font used to display a text in overlay. The display is performed using displayCharString(). - \param font : The expected font name. + \param fontname : The expected font name. \note Under UNIX, to know all the available fonts, use the "xlsfonts" binary in a terminal. You can also use the "xfontsel" binary. @@ -388,9 +407,9 @@ vpDisplayGTK::init(unsigned int width, unsigned int height, \sa displayCharString() */ void -vpDisplayGTK::setFont(const char * /* font */) +vpDisplayGTK::setFont(const char *fontname) { - vpERROR_TRACE("Not yet implemented" ) ; + font = gdk_font_load((const gchar*)fontname); } /*! @@ -402,8 +421,11 @@ vpDisplayGTK::setTitle(const char *title) { if (displayHasBeenInitialized) { - if (title != NULL) - gdk_window_set_title(widget->window,(char *)title); + if(title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); + gdk_window_set_title(widget->window, title_.c_str()); } else { @@ -486,24 +508,24 @@ void vpDisplayGTK::displayImage(const vpImage<unsigned char> &I) \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w : Width of the region of interest - \param height : Height of the region of interest + \param h : Height of the region of interest \sa init(), closeDisplay() */ -void vpDisplayGTK::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayGTK::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, const unsigned int w, const unsigned int h ) { if (displayHasBeenInitialized) { vpImage<unsigned char> Itemp; - vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),height,width,Itemp); + vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(), h, w,Itemp); /* Copie de l'image dans le pixmap fond */ gdk_draw_gray_image(background, - gc, (gint)iP.get_u(), (gint)iP.get_v(), (gint)width, (gint)height, + gc, (gint)iP.get_u(), (gint)iP.get_v(), (gint)w, (gint)h, GDK_RGB_DITHER_NONE, I.bitmap, - (gint)width); + (gint)w); /* Le pixmap background devient le fond de la zone de dessin */ gdk_window_set_back_pixmap(widget->window, background, FALSE); @@ -575,24 +597,24 @@ void vpDisplayGTK::displayImage(const vpImage<vpRGBa> &I) \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w : Width of the region of interest - \param height : Height of the region of interest + \param h : Height of the region of interest \sa init(), closeDisplay() */ -void vpDisplayGTK::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayGTK::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, const unsigned int w, const unsigned int h ) { if (displayHasBeenInitialized) { vpImage<vpRGBa> Itemp; - vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),height,width,Itemp); + vpImageTools::createSubImage(I,(unsigned int)iP.get_i(), (unsigned int)iP.get_j(), h, w, Itemp); /* Copie de l'image dans le pixmap fond */ gdk_draw_rgb_32_image(background, - gc, (gint)iP.get_u(), (gint)iP.get_v(), (gint)width, (gint)height, + gc, (gint)iP.get_u(), (gint)iP.get_v(), (gint)w, (gint)h, GDK_RGB_DITHER_NONE, (unsigned char *)Itemp.bitmap, - (gint)(4*width)); + (gint)(4*w)); /* Permet de fermer la fen�tre si besoin (cas des s�quences d'images) */ //while (g_main_iteration(FALSE)); @@ -700,10 +722,10 @@ void vpDisplayGTK::clearDisplay(const vpColor & /* color */) \param thickness : Thickness of the lines used to display the arrow. */ void vpDisplayGTK::displayArrow ( const vpImagePoint &ip1, - const vpImagePoint &ip2, - const vpColor &color, - unsigned int w, unsigned int h, - unsigned int thickness) + const vpImagePoint &ip2, + const vpColor &color, + unsigned int w, unsigned int h, + unsigned int thickness) { if (displayHasBeenInitialized) { @@ -722,21 +744,24 @@ void vpDisplayGTK::displayArrow ( const vpImagePoint &ip1, a /= lg ; b /= lg ; - vpImagePoint ip3; + vpImagePoint ip3; ip3.set_i(ip2.get_i() - w*a); ip3.set_j(ip2.get_j() - w*b); - vpImagePoint ip4; - ip4.set_i( ip3.get_i() - b*h ); - ip4.set_j( ip3.get_j() + a*h ); + vpImagePoint ip4; + ip4.set_i( ip3.get_i() - b*h ); + ip4.set_j( ip3.get_j() + a*h ); - displayLine ( ip2, ip4, color, thickness ) ; + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - ip4.set_i( ip3.get_i() + b*h ); - ip4.set_j( ip3.get_j() - a*h ); + ip4.set_i( ip3.get_i() + b*h ); + ip4.set_j( ip3.get_j() - a*h ); + + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip1, ip2, color, thickness ) ; + displayLine ( ip1, ip2, color, thickness ) ; } } catch (...) @@ -780,12 +805,13 @@ void vpDisplayGTK::displayCharString ( const vpImagePoint &ip, gdk_colormap_alloc_color(colormap,&gdkcolor,FALSE,TRUE); gdk_gc_set_foreground(gc, &gdkcolor); } - - gdk_draw_string(background, Police2, gc, - vpMath::round( ip.get_u() ), - vpMath::round( ip.get_v() ), - (const gchar *)text); - + if (font != NULL) + gdk_draw_string(background, font, gc, + vpMath::round( ip.get_u() ), + vpMath::round( ip.get_v() ), + (const gchar *)text); + else + std::cout << "Cannot draw string: no font is selected" << std::endl; } else { @@ -1017,7 +1043,7 @@ void vpDisplayGTK::displayPoint ( const vpImagePoint &ip, width and \e height the rectangle size. \param topLeft : Top-left corner of the rectangle. - \param width,height : Rectangle size. + \param w,h : Rectangle size. \param color : Rectangle color. \param fill : When set to true fill the rectangle. @@ -1027,7 +1053,7 @@ void vpDisplayGTK::displayPoint ( const vpImagePoint &ip, */ void vpDisplayGTK::displayRectangle ( const vpImagePoint &topLeft, - unsigned int width, unsigned int height, + unsigned int w, unsigned int h, const vpColor &color, bool fill, unsigned int thickness ) { @@ -1052,12 +1078,12 @@ vpDisplayGTK::displayRectangle ( const vpImagePoint &topLeft, gdk_draw_rectangle(background, gc, FALSE, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - (gint)width-1, (gint)height-1); + (gint)w-1, (gint)h-1); else gdk_draw_rectangle(background, gc, TRUE, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - (gint)width, (gint)height); + (gint)w, (gint)h); if (thickness > 1) gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, @@ -1107,19 +1133,19 @@ vpDisplayGTK::displayRectangle ( const vpImagePoint &topLeft, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_BEVEL) ; - int width = vpMath::round( bottomRight.get_u() - topLeft.get_u() ); - int height = vpMath::round( bottomRight.get_v() - topLeft.get_v() ); + int w = vpMath::round( bottomRight.get_u() - topLeft.get_u() ); + int h = vpMath::round( bottomRight.get_v() - topLeft.get_v() ); if (fill == false) gdk_draw_rectangle(background, gc, FALSE, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - width-1,height-1); + w-1,h-1); else gdk_draw_rectangle(background, gc, TRUE, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - width, height); + w, h); if (thickness > 1) gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, @@ -1505,11 +1531,11 @@ unsigned int vpDisplayGTK::getScreenDepth() \warning Not implemented */ -void vpDisplayGTK::getScreenSize(unsigned int &width, unsigned int &height) +void vpDisplayGTK::getScreenSize(unsigned int &w, unsigned int &h) { vpTRACE("Not implemented") ; - width = 0; - height = 0; + w = 0; + h = 0; } /*! diff --git a/src/device/display/vpDisplayGTK.h b/src/device/display/vpDisplayGTK.h index 64d34aec7afb2ec6909e51b3eb7a83ec1901b5e6..dc58ec2aca00089ead46a266a648c11b8e2e41fe 100644 --- a/src/device/display/vpDisplayGTK.h +++ b/src/device/display/vpDisplayGTK.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayGTK.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplayGTK.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,10 +44,10 @@ #define vpDisplayGTK_h #include <visp/vpConfig.h> -#include <visp/vpDisplay.h> #if ( defined(VISP_HAVE_GTK) ) #include <visp/vpImage.h> +#include <visp/vpDisplay.h> #include <gtk/gtk.h> #include <gdk/gdkrgb.h> @@ -83,10 +83,10 @@ int main() vpImage<unsigned char> I; // Grey level image // Read an image in PGM P5 format -#ifdef UNIX - vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); -#elif WIN32 +#ifdef _WIN32 vpImageIo::read(I, "C:/temp/ViSP-images/Klimt/Klimt.pgm"); +#else + vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); #endif vpDisplayGTK d; @@ -154,7 +154,7 @@ private: purple, lightGray, gray, darkGray; GdkColormap *colormap; - GdkFont *Police1,*Police2; + GdkFont *font; guchar *vectgtk; GdkColor **col ; int ncol, nrow ; @@ -208,7 +208,7 @@ public: protected: - void setFont( const char *font ); + void setFont( const char *fontname ); void setTitle(const char *title) ; void setWindowPosition(int winx, int winy); diff --git a/src/device/display/vpDisplayOpenCV.cpp b/src/device/display/vpDisplayOpenCV.cpp index 1eafa603452fc82e65bc238650bd0c40d2f063cc..a11addc44a48907e18a785a5f18bb961cbe9cd49 100644 --- a/src/device/display/vpDisplayOpenCV.cpp +++ b/src/device/display/vpDisplayOpenCV.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayOpenCV.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpDisplayOpenCV.cpp 5204 2015-01-24 13:18:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,7 @@ #include <visp/vpConfig.h> -#if ( defined(VISP_HAVE_OPENCV) ) +#if defined(VISP_HAVE_OPENCV) #include <stdio.h> #include <stdlib.h> @@ -67,6 +67,19 @@ #include <visp/vpDebug.h> #include <visp/vpDisplayException.h> +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + +# include <opencv2/imgproc/imgproc.hpp> +# include <opencv2/core/core_c.h> // for CV_FILLED versus cv::FILLED + +# ifndef CV_RGB +# define CV_RGB( r, g, b ) cv::Scalar( (b), (g), (r), 0 ) +# endif +#endif + +std::vector<std::string> vpDisplayOpenCV::m_listTitles = std::vector<std::string>(); +unsigned int vpDisplayOpenCV::m_nbWindows = 0; + /*! Constructor. Initialize a display to visualize a gray level image @@ -80,11 +93,21 @@ vpDisplayOpenCV::vpDisplayOpenCV(vpImage<unsigned char> &I, int x, int y, - const char *title) : vpDisplay() + const char *title) + : + #if (VISP_HAVE_OPENCV_VERSION < 0x020408) + background(NULL), col(NULL), cvcolor(), font(NULL), + #else + background(), col(NULL), cvcolor(), font(cv::FONT_HERSHEY_PLAIN), fontScale(0.8f), + #endif + fontHeight(10), ncol(0), nrow(0), x_move(0), y_move(0) , move(false), + x_lbuttondown(0), y_lbuttondown(0), lbuttondown(false), + x_mbuttondown(0), y_mbuttondown(0), mbuttondown(false), + x_rbuttondown(0), y_rbuttondown(0), rbuttondown(false), + x_lbuttonup(0), y_lbuttonup(0), lbuttonup(false), + x_mbuttonup(0), y_mbuttonup(0), mbuttonup(false), + x_rbuttonup(0), y_rbuttonup(0), rbuttonup(false) { - col = NULL; - background = NULL; - font = NULL; init(I, x, y, title) ; } @@ -100,11 +123,21 @@ vpDisplayOpenCV::vpDisplayOpenCV(vpImage<unsigned char> &I, vpDisplayOpenCV::vpDisplayOpenCV(vpImage<vpRGBa> &I, int x, int y, - const char *title) : vpDisplay() + const char *title) + : + #if (VISP_HAVE_OPENCV_VERSION < 0x020408) + background(NULL), col(NULL), cvcolor(), font(NULL), + #else + background(), col(NULL), cvcolor(), font(cv::FONT_HERSHEY_PLAIN), fontScale(0.8f), + #endif + fontHeight(10), ncol(0), nrow(0), x_move(0), y_move(0) , move(false), + x_lbuttondown(0), y_lbuttondown(0), lbuttondown(false), + x_mbuttondown(0), y_mbuttondown(0), mbuttondown(false), + x_rbuttondown(0), y_rbuttondown(0), rbuttondown(false), + x_lbuttonup(0), y_lbuttonup(0), lbuttonup(false), + x_mbuttonup(0), y_mbuttonup(0), mbuttonup(false), + x_rbuttonup(0), y_rbuttonup(0), rbuttonup(false) { - col = NULL; - background = NULL; - font = NULL; init(I, x, y, title) ; } @@ -130,16 +163,49 @@ int main() } \endcode */ -vpDisplayOpenCV::vpDisplayOpenCV ( int x, int y, const char *title ) : vpDisplay() +vpDisplayOpenCV::vpDisplayOpenCV ( int x, int y, const char *title ) + : + #if (VISP_HAVE_OPENCV_VERSION < 0x020408) + background(NULL), col(NULL), cvcolor(), font(NULL), + #else + background(), col(NULL), cvcolor(), font(cv::FONT_HERSHEY_PLAIN), fontScale(0.8f), + #endif + fontHeight(10), ncol(0), nrow(0), x_move(0), y_move(0) , move(false), + x_lbuttondown(0), y_lbuttondown(0), lbuttondown(false), + x_mbuttondown(0), y_mbuttondown(0), mbuttondown(false), + x_rbuttondown(0), y_rbuttondown(0), rbuttondown(false), + x_lbuttonup(0), y_lbuttonup(0), lbuttonup(false), + x_mbuttonup(0), y_mbuttonup(0), mbuttonup(false), + x_rbuttonup(0), y_rbuttonup(0), rbuttonup(false) { - col = NULL; - background = NULL; - font = NULL; windowXPosition = x; windowYPosition = y; - if (title != NULL) - strcpy (this->title, title); + if(title != NULL){ + title_ = std::string(title); + } + else{ + std::ostringstream s; + s << m_nbWindows++; + title_ = std::string("Window ") + s.str(); + } + + bool isInList; + do{ + isInList = false; + for(size_t i = 0 ; i < m_listTitles.size() ; i++){ + if(m_listTitles[i] == title_){ + std::ostringstream s; + s << m_nbWindows++; + title_ = std::string("Window ") + s.str(); + isInList = true; + break; + } + } + } + while(isInList); + + m_listTitles.push_back(title_); } /*! @@ -161,11 +227,21 @@ int main() } \endcode */ -vpDisplayOpenCV::vpDisplayOpenCV() : vpDisplay() +vpDisplayOpenCV::vpDisplayOpenCV() + : + #if (VISP_HAVE_OPENCV_VERSION < 0x020408) + background(NULL), col(NULL), cvcolor(), font(NULL), + #else + background(), col(NULL), cvcolor(), font(cv::FONT_HERSHEY_PLAIN), fontScale(0.8f), + #endif + fontHeight(10), ncol(0), nrow(0), x_move(0), y_move(0) , move(false), + x_lbuttondown(0), y_lbuttondown(0), lbuttondown(false), + x_mbuttondown(0), y_mbuttondown(0), mbuttondown(false), + x_rbuttondown(0), y_rbuttondown(0), rbuttondown(false), + x_lbuttonup(0), y_lbuttonup(0), lbuttonup(false), + x_mbuttonup(0), y_mbuttonup(0), mbuttonup(false), + x_rbuttonup(0), y_rbuttonup(0), rbuttonup(false) { - col = NULL; - background = NULL; - font = NULL; } /*! @@ -174,7 +250,9 @@ vpDisplayOpenCV::vpDisplayOpenCV() : vpDisplay() vpDisplayOpenCV::~vpDisplayOpenCV() { closeDisplay() ; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) cvReleaseImage(&background); +#endif } /*! @@ -233,7 +311,7 @@ vpDisplayOpenCV::init(vpImage<vpRGBa> &I, /*! Initialize the display size, position and title. - \param width, height : Width and height of the window. + \param w, h : Width and height of the window. \param x, y : The window is set at position x,y (column index, row index). \param title : Window title. @@ -241,29 +319,67 @@ vpDisplayOpenCV::init(vpImage<vpRGBa> &I, with an available display device suach as Gtk, Cocoa, Carbon, Qt. */ void -vpDisplayOpenCV::init(unsigned int width, unsigned int height, +vpDisplayOpenCV::init(unsigned int w, unsigned int h, int x, int y, const char *title) { - this->width = width; - this->height = height; + this->width = w; + this->height = h; if (x != -1) this->windowXPosition = x; if (y != -1) this->windowYPosition = y; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) int flags = CV_WINDOW_AUTOSIZE; +#else + int flags = cv::WINDOW_AUTOSIZE; +#endif + + if(title_.empty()){ + if(title != NULL){ + title_ = std::string(title); + } + else{ + + std::ostringstream s; + s << m_nbWindows++; + title_ = std::string("Window ") + s.str(); + } - if (title != NULL) - strcpy(this->title, title) ; + bool isInList; + do{ + isInList = false; + for(size_t i = 0 ; i < m_listTitles.size() ; i++){ + if(m_listTitles[i] == title_){ + std::ostringstream s; + s << m_nbWindows++; + title_ = std::string("Window ") + s.str(); + isInList = true; + break; + } + } + } + while(isInList); + + m_listTitles.push_back(title_); + } /* Create the window*/ - if (cvNamedWindow( this->title, flags ) < 0) { +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + if (cvNamedWindow( this->title_.c_str(), flags ) < 0) { vpERROR_TRACE("OpenCV was not built with a display device"); throw(vpDisplayException(vpDisplayException::notInitializedError, "OpenCV was not built with a display device")) ; } - cvMoveWindow( this->title, this->windowXPosition, this->windowYPosition ); +#else + cv::namedWindow( this->title_, flags ); +#endif +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvMoveWindow( this->title_.c_str(), this->windowXPosition, this->windowYPosition ); +#else + cv::moveWindow( this->title_.c_str(), this->windowXPosition, this->windowYPosition ); +#endif move = false; lbuttondown = false; mbuttondown = false; @@ -271,13 +387,13 @@ vpDisplayOpenCV::init(unsigned int width, unsigned int height, lbuttonup = false; mbuttonup = false; rbuttonup = false; - cvSetMouseCallback( this->title, on_mouse, this ); - /* Create background pixmap */ -// background = cvCreateImage(cvSize((int)width,(int)height),IPL_DEPTH_8U,3); -// -// cvShowImage( this->title,background); - +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvSetMouseCallback( this->title_.c_str(), on_mouse, this ); col = new CvScalar[vpColor::id_unknown] ; +#else + cv::setMouseCallback( this->title_, on_mouse, this ); + col = new cv::Scalar[vpColor::id_unknown] ; +#endif /* Create color */ vpColor pcolor; // Predefined colors @@ -318,12 +434,19 @@ vpDisplayOpenCV::init(unsigned int width, unsigned int height, pcolor = vpColor::darkGray; col[vpColor::id_darkGray] = CV_RGB(pcolor.R, pcolor.G, pcolor.B) ; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) font = new CvFont; cvInitFont( font, CV_FONT_HERSHEY_PLAIN, 0.70f,0.70f); - CvSize fontSize; int baseline; cvGetTextSize( "A", font, &fontSize, &baseline ); +#else + int thickness = 1; + cv::Size fontSize; + int baseline; + fontSize = cv::getTextSize( "A", font, fontScale, thickness, &baseline ); +#endif + fontHeight = fontSize.height + baseline; displayHasBeenInitialized = true ; } @@ -360,11 +483,11 @@ vpDisplayOpenCV::setFont(const char * /* font */) void vpDisplayOpenCV::setTitle(const char * /* title */) { - static bool warn_displayed = false; - if (! warn_displayed) { - vpTRACE("Not implemented"); - warn_displayed = true; - } +// static bool warn_displayed = false; +// if (! warn_displayed) { +// vpTRACE("Not implemented"); +// warn_displayed = true; +// } } @@ -381,7 +504,11 @@ void vpDisplayOpenCV::setWindowPosition(int winx, int winy) if (displayHasBeenInitialized) { this->windowXPosition = winx; this->windowYPosition = winy; - cvMoveWindow( this->title, winx, winy ); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvMoveWindow( this->title_.c_str(), winx, winy ); +#else + cv::moveWindow( this->title_.c_str(), winx, winy ); +#endif } else { @@ -412,10 +539,6 @@ void vpDisplayOpenCV::displayImage(const vpImage<unsigned char> &I) width = I.getWidth(); height = I.getHeight(); /* Le pixmap background devient le fond de la zone de dessin */ - - /* Affichage */ - //gdk_window_clear(window); - //gdk_flush(); } else { @@ -436,24 +559,24 @@ void vpDisplayOpenCV::displayImage(const vpImage<unsigned char> &I) \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w, h : Width and height of the region of interest - \param height : Height of the region of interest - \sa init(), closeDisplay() */ -void vpDisplayOpenCV::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayOpenCV::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, + const unsigned int w, const unsigned int h ) { if (displayHasBeenInitialized) { vpImage<unsigned char> Itemp; - vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),height,width,Itemp); + vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),h,w,Itemp); vpImage<vpRGBa> Ic; - vpImageConvert::convert(Itemp,Ic); - - CvSize size = cvSize((int)this->width, (int)this->height); + vpImageConvert::convert(Itemp,Ic); + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) int depth = 8; int channels = 3; + CvSize size = cvSize((int)this->width, (int)this->height); if (background != NULL){ if(background->nChannels != channels || background->depth != depth || background->height != (int) I.getHeight() || background->width != (int) I.getWidth()){ @@ -462,27 +585,25 @@ void vpDisplayOpenCV::displayImageROI ( const vpImage<unsigned char> &I,const vp } } else background = cvCreateImage( size, depth, channels ); - IplImage* Ip = NULL; vpImageConvert::convert(Ic, Ip); - unsigned char * input = (unsigned char*)Ip->imageData; unsigned char * output = (unsigned char*)background->imageData; - + unsigned int iwidth = Ic.getWidth(); output = output + (int)(iP.get_i()*3*this->width+ iP.get_j()*3); - + unsigned int i = 0; - while (i < height) + while (i < h) { unsigned int j = 0; - while (j < width) + while (j < w) { - *(output+3*j) = *(input+j*3); - *(output+3*j+1) = *(input+j*3+1); - *(output+3*j+2) = *(input+j*3+2); - j++; + *(output+3*j) = *(input+j*3); + *(output+3*j+1) = *(input+j*3+1); + *(output+3*j+2) = *(input+j*3+2); + j++; } input = input + 3*iwidth; output = output + 3*this->width; @@ -490,6 +611,41 @@ void vpDisplayOpenCV::displayImageROI ( const vpImage<unsigned char> &I,const vp } cvReleaseImage(&Ip); +#else + int depth = CV_8U; + int channels = 3; + cv::Size size((int)this->width, (int)this->height); + if(background.channels() != channels || background.depth() != depth + || background.rows != (int) I.getHeight() || background.cols != (int) I.getWidth()){ + background = cv::Mat( size, CV_MAKETYPE(depth, channels) ); + } + + cv::Mat Ip; + vpImageConvert::convert(Ic, Ip); + + unsigned char * input = (unsigned char*)Ip.data; + unsigned char * output = (unsigned char*)background.data; + + unsigned int iwidth = Ic.getWidth(); + + output = output + (int)(iP.get_i()*3*this->width+ iP.get_j()*3); + + unsigned int i = 0; + while (i < h) + { + unsigned int j = 0; + while (j < w) + { + *(output+3*j) = *(input+j*3); + *(output+3*j+1) = *(input+j*3+1); + *(output+3*j+2) = *(input+j*3+2); + j++; + } + input = input + 3*iwidth; + output = output + 3*this->width; + i++; + } +#endif } else { @@ -522,7 +678,6 @@ void vpDisplayOpenCV::displayImage(const vpImage<vpRGBa> &I) /* Copie de l'image dans le pixmap fond */ width = I.getWidth(); height = I.getHeight(); - } else { @@ -543,19 +698,19 @@ void vpDisplayOpenCV::displayImage(const vpImage<vpRGBa> &I) \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w, h : Width and height of the region of interest - \param height : Height of the region of interest - \sa init(), closeDisplay() */ -void vpDisplayOpenCV::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayOpenCV::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, + const unsigned int w, const unsigned int h ) { if (displayHasBeenInitialized) { vpImage<vpRGBa> Ic; - vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),height,width,Ic); + vpImageTools::createSubImage(I,(unsigned int)iP.get_i(),(unsigned int)iP.get_j(),h,w,Ic); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) CvSize size = cvSize((int)this->width, (int)this->height); int depth = 8; int channels = 3; @@ -579,15 +734,15 @@ void vpDisplayOpenCV::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePo output = output + (int)(iP.get_i()*3*this->width+ iP.get_j()*3); unsigned int i = 0; - while (i < height) + while (i < h) { unsigned int j = 0; - while (j < width) + while (j < w) { - *(output+3*j) = *(input+j*3); - *(output+3*j+1) = *(input+j*3+1); - *(output+3*j+2) = *(input+j*3+2); - j++; + *(output+3*j) = *(input+j*3); + *(output+3*j+1) = *(input+j*3+1); + *(output+3*j+2) = *(input+j*3+2); + j++; } input = input + 3*iwidth; output = output + 3*this->width; @@ -595,6 +750,40 @@ void vpDisplayOpenCV::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePo } cvReleaseImage(&Ip); +#else + int depth = CV_8U; + int channels = 3; + cv::Size size((int)this->width, (int)this->height); + if(background.channels() != channels || background.depth() != depth + || background.rows != (int) I.getHeight() || background.cols != (int) I.getWidth()){ + background = cv::Mat( size, CV_MAKETYPE(depth, channels) ); + } + cv::Mat Ip; + vpImageConvert::convert(Ic, Ip); + + unsigned char * input = (unsigned char*)Ip.data; + unsigned char * output = (unsigned char*)background.data; + + unsigned int iwidth = Ic.getWidth(); + + output = output + (int)(iP.get_i()*3*this->width+ iP.get_j()*3); + + unsigned int i = 0; + while (i < h) + { + unsigned int j = 0; + while (j < w) + { + *(output+3*j) = *(input+j*3); + *(output+3*j+1) = *(input+j*3+1); + *(output+3*j+2) = *(input+j*3+2); + j++; + } + input = input + 3*iwidth; + output = output + 3*this->width; + i++; + } +#endif } else { @@ -628,14 +817,28 @@ void vpDisplayOpenCV::closeDisplay() { delete [] col ; col = NULL ; } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) if (font != NULL) { delete font ; font = NULL ; } - +#endif if (displayHasBeenInitialized) { - cvDestroyWindow( this->title ); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvDestroyWindow( this->title_.c_str() ); +#else + cv::destroyWindow( this->title_ ); +#endif + + for(size_t i = 0 ; i < m_listTitles.size() ; i++){ + if(title_ == m_listTitles[i]){ + m_listTitles.erase(m_listTitles.begin()+(long int)i); + break; + } + } + + title_.clear(); displayHasBeenInitialized= false; } @@ -651,8 +854,13 @@ void vpDisplayOpenCV::flushDisplay() { if (displayHasBeenInitialized) { - cvShowImage(this->title, background ); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvShowImage(this->title_.c_str(), background ); cvWaitKey(5); +#else + cv::imshow(this->title_, background ); + cv::waitKey(5); +#endif } else { @@ -671,8 +879,13 @@ void vpDisplayOpenCV::flushDisplayROI(const vpImagePoint &/*iP*/, const unsigned { if (displayHasBeenInitialized) { - cvShowImage(this->title, background ); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvShowImage(this->title_.c_str(), background ); cvWaitKey(5); +#else + cv::imshow(this->title_.c_str(), background ); + cv::waitKey(5); +#endif } else { @@ -703,10 +916,10 @@ void vpDisplayOpenCV::clearDisplay(const vpColor & /* color */) \param thickness : Thickness of the lines used to display the arrow. */ void vpDisplayOpenCV::displayArrow ( const vpImagePoint &ip1, - const vpImagePoint &ip2, - const vpColor &color, - unsigned int w, unsigned int h, - unsigned int thickness) + const vpImagePoint &ip2, + const vpColor &color, + unsigned int w, unsigned int h, + unsigned int thickness) { if (displayHasBeenInitialized) { @@ -717,30 +930,33 @@ void vpDisplayOpenCV::displayArrow ( const vpImagePoint &ip1, //if ((a==0)&&(b==0)) if ((std::fabs(a) <= std::numeric_limits<double>::epsilon()) - &&(std::fabs(b)<= std::numeric_limits<double>::epsilon())) + &&(std::fabs(b)<= std::numeric_limits<double>::epsilon())) { // DisplayCrossLarge(i1,j1,3,col) ; } else { - a /= lg ; + a /= lg ; b /= lg ; - vpImagePoint ip3; + vpImagePoint ip3; ip3.set_i(ip2.get_i() - w*a); ip3.set_j(ip2.get_j() - w*b); - vpImagePoint ip4; - ip4.set_i( ip3.get_i() - b*h ); - ip4.set_j( ip3.get_j() + a*h ); + vpImagePoint ip4; + ip4.set_i( ip3.get_i() - b*h ); + ip4.set_j( ip3.get_j() + a*h ); - displayLine ( ip2, ip4, color, thickness ) ; + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - ip4.set_i( ip3.get_i() + b*h ); - ip4.set_j( ip3.get_j() - a*h ); + ip4.set_i( ip3.get_i() + b*h ); + ip4.set_j( ip3.get_j() - a*h ); + + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip1, ip2, color, thickness ) ; + displayLine ( ip1, ip2, color, thickness ) ; } } catch (...) @@ -775,17 +991,31 @@ void vpDisplayOpenCV::displayCharString( const vpImagePoint &ip, if (displayHasBeenInitialized) { if (color.id < vpColor::id_unknown) { - cvPutText( background, text, - cvPoint( vpMath::round( ip.get_u() ), - vpMath::round( ip.get_v()+fontHeight ) ), - font, col[color.id] ); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvPutText( background, text, + cvPoint( vpMath::round( ip.get_u() ), + vpMath::round( ip.get_v()+fontHeight ) ), + font, col[color.id] ); +#else + cv::putText( background, text, + cv::Point( vpMath::round( ip.get_u() ), + vpMath::round( ip.get_v()+fontHeight ) ), + font, fontScale, col[color.id] ); +#endif } else { cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvPutText( background, text, - cvPoint( vpMath::round( ip.get_u() ), - vpMath::round( ip.get_v()+fontHeight ) ), - font, cvcolor ); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvPutText( background, text, + cvPoint( vpMath::round( ip.get_u() ), + vpMath::round( ip.get_v()+fontHeight ) ), + font, cvcolor ); +#else + cv::putText( background, text, + cv::Point( vpMath::round( ip.get_u() ), + vpMath::round( ip.get_v()+fontHeight ) ), + font, fontScale, cvcolor ); +#endif } } else @@ -814,32 +1044,65 @@ void vpDisplayOpenCV::displayCircle(const vpImagePoint ¢er, { if (fill == false) { if (color.id < vpColor::id_unknown) { - cvCircle( background, - cvPoint( vpMath::round( center.get_u() ), - vpMath::round( center.get_v() ) ), - (int)radius, col[color.id], (int)thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvCircle( background, + cvPoint( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, col[color.id], (int)thickness); +#else + cv::circle( background, + cv::Point( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, col[color.id], (int)thickness); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvCircle( background, - cvPoint( vpMath::round( center.get_u() ), - vpMath::round( center.get_v() ) ), - (int)radius, cvcolor, (int)thickness); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvCircle( background, + cvPoint( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, cvcolor, (int)thickness); +#else + cv::circle( background, + cv::Point( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, cvcolor, (int)thickness); +#endif } } else { +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + int filled = cv::FILLED; +#else + int filled = CV_FILLED; +#endif if (color.id < vpColor::id_unknown) { - cvCircle( background, - cvPoint( vpMath::round( center.get_u() ), - vpMath::round( center.get_v() ) ), - (int)radius, col[color.id], CV_FILLED); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvCircle( background, + cvPoint( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, col[color.id], filled); +#else + cv::circle( background, + cv::Point( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, col[color.id], filled); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvCircle( background, - cvPoint( vpMath::round( center.get_u() ), - vpMath::round( center.get_v() ) ), - (int)radius, cvcolor, CV_FILLED); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvCircle( background, + cvPoint( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, cvcolor, filled); +#else + cv::circle( background, + cv::Point( vpMath::round( center.get_u() ), + vpMath::round( center.get_v() ) ), + (int)radius, cvcolor, filled); +#endif } } } @@ -907,30 +1170,48 @@ vpDisplayOpenCV::displayCross(const vpImagePoint &ip, */ void vpDisplayOpenCV::displayDotLine(const vpImagePoint &ip1, - const vpImagePoint &ip2, - const vpColor &color, - unsigned int thickness) + const vpImagePoint &ip2, + const vpColor &color, + unsigned int thickness) { if (displayHasBeenInitialized) { - vpTRACE("Dot lines are not yet implemented"); + //vpTRACE("Dot lines are not yet implemented"); if (color.id < vpColor::id_unknown) { - cvLine( background, - cvPoint( vpMath::round( ip1.get_u() ), - vpMath::round( ip1.get_v() ) ), - cvPoint( vpMath::round( ip2.get_u() ), - vpMath::round( ip2.get_v() ) ), - col[color.id], (int) thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvLine( background, + cvPoint( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cvPoint( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + col[color.id], (int) thickness); +#else + cv::line( background, + cv::Point( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cv::Point( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + col[color.id], (int) thickness); +#endif } else { cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvLine( background, - cvPoint( vpMath::round( ip1.get_u() ), - vpMath::round( ip1.get_v() ) ), - cvPoint( vpMath::round( ip2.get_u() ), - vpMath::round( ip2.get_v() ) ), - cvcolor, (int) thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvLine( background, + cvPoint( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cvPoint( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + cvcolor, (int) thickness); +#else + cv::line( background, + cv::Point( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cv::Point( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + cvcolor, (int) thickness); +#endif } } else @@ -957,21 +1238,39 @@ vpDisplayOpenCV::displayLine(const vpImagePoint &ip1, if (displayHasBeenInitialized) { if (color.id < vpColor::id_unknown) { - cvLine( background, - cvPoint( vpMath::round( ip1.get_u() ), - vpMath::round( ip1.get_v() ) ), - cvPoint( vpMath::round( ip2.get_u() ), - vpMath::round( ip2.get_v() ) ), - col[color.id], (int) thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvLine( background, + cvPoint( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cvPoint( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + col[color.id], (int) thickness); +#else + cv::line( background, + cv::Point( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cv::Point( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + col[color.id], (int) thickness); +#endif } else { cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvLine( background, - cvPoint( vpMath::round( ip1.get_u() ), - vpMath::round( ip1.get_v() ) ), - cvPoint( vpMath::round( ip2.get_u() ), - vpMath::round( ip2.get_v() ) ), - cvcolor, (int) thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvLine( background, + cvPoint( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cvPoint( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + cvcolor, (int) thickness); +#else + cv::line( background, + cv::Point( vpMath::round( ip1.get_u() ), + vpMath::round( ip1.get_v() ) ), + cv::Point( vpMath::round( ip2.get_u() ), + vpMath::round( ip2.get_v() ) ), + cvcolor, (int) thickness); +#endif } } else @@ -1035,7 +1334,7 @@ void vpDisplayOpenCV::displayPoint(const vpImagePoint &ip, width and \e height the rectangle size. \param topLeft : Top-left corner of the rectangle. - \param width,height : Rectangle size. + \param w,h : Rectangle size in terms of width and height. \param color : Rectangle color. \param fill : When set to true fill the rectangle. @@ -1045,7 +1344,7 @@ void vpDisplayOpenCV::displayPoint(const vpImagePoint &ip, */ void vpDisplayOpenCV::displayRectangle(const vpImagePoint &topLeft, - unsigned int width, unsigned int height, + unsigned int w, unsigned int h, const vpColor &color, bool fill, unsigned int thickness) { @@ -1053,41 +1352,81 @@ vpDisplayOpenCV::displayRectangle(const vpImagePoint &topLeft, { if (fill == false) { if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( topLeft.get_u()+width ), - vpMath::round( topLeft.get_v()+height ) ), - col[color.id], (int)thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + col[color.id], (int)thickness); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + col[color.id], (int)thickness); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( topLeft.get_u()+width ), - vpMath::round( topLeft.get_v()+height ) ), - cvcolor, (int)thickness); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + cvcolor, (int)thickness); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + cvcolor, (int)thickness); +#endif } } else { +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + int filled = cv::FILLED; +#else + int filled = CV_FILLED; +#endif if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( topLeft.get_u()+width ), - vpMath::round( topLeft.get_v()+height ) ), - col[color.id], CV_FILLED); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + col[color.id], filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + col[color.id], filled); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( topLeft.get_u()+width ), - vpMath::round( topLeft.get_v()+height ) ), - cvcolor, CV_FILLED); - + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + cvcolor, filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( topLeft.get_u()+w ), + vpMath::round( topLeft.get_v()+h ) ), + cvcolor, filled); +#endif } } } @@ -1120,41 +1459,81 @@ vpDisplayOpenCV::displayRectangle ( const vpImagePoint &topLeft, { if (fill == false) { if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( bottomRight.get_u() ), - vpMath::round( bottomRight.get_v() ) ), - col[color.id], (int)thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + col[color.id], (int)thickness); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + col[color.id], (int)thickness); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( bottomRight.get_u() ), - vpMath::round( bottomRight.get_v() ) ), - cvcolor, (int)thickness); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + cvcolor, (int)thickness); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + cvcolor, (int)thickness); +#endif } } else { +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + int filled = cv::FILLED; +#else + int filled = CV_FILLED; +#endif if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( bottomRight.get_u() ), - vpMath::round( bottomRight.get_v() ) ), - col[color.id], CV_FILLED); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + col[color.id], filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + col[color.id], filled); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( topLeft.get_u() ), - vpMath::round( topLeft.get_v() ) ), - cvPoint( vpMath::round( bottomRight.get_u() ), - vpMath::round( bottomRight.get_v() ) ), - cvcolor, CV_FILLED); - + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cvPoint( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + cvcolor, filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( topLeft.get_u() ), + vpMath::round( topLeft.get_v() ) ), + cv::Point( vpMath::round( bottomRight.get_u() ), + vpMath::round( bottomRight.get_v() ) ), + cvcolor, filled); +#endif } } } @@ -1187,41 +1566,83 @@ vpDisplayOpenCV::displayRectangle(const vpRect &rectangle, { if (fill == false) { if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( rectangle.getLeft() ), - vpMath::round( rectangle.getBottom() ) ), - cvPoint( vpMath::round( rectangle.getRight() ), - vpMath::round( rectangle.getTop() ) ), - col[color.id], (int)thickness); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cvPoint( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + col[color.id], (int)thickness); +#else + cv::rectangle( background, + cv::Point( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cv::Point( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + col[color.id], (int)thickness); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( rectangle.getLeft() ), - vpMath::round( rectangle.getBottom() ) ), - cvPoint( vpMath::round( rectangle.getRight() ), - vpMath::round( rectangle.getTop() ) ), - cvcolor, (int)thickness); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cvPoint( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + cvcolor, (int)thickness); + +#else + cv::rectangle( background, + cv::Point( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cv::Point( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + cvcolor, (int)thickness); +#endif } } else { +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + int filled = cv::FILLED; +#else + int filled = CV_FILLED; +#endif if (color.id < vpColor::id_unknown) { - cvRectangle( background, - cvPoint( vpMath::round( rectangle.getLeft() ), - vpMath::round( rectangle.getBottom() ) ), - cvPoint( vpMath::round( rectangle.getRight() ), - vpMath::round( rectangle.getTop() ) ), - col[color.id], CV_FILLED); +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cvPoint( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + col[color.id], filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cv::Point( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + col[color.id], filled); +#endif } else { - cvcolor = CV_RGB(color.R, color.G, color.B) ; - cvRectangle( background, - cvPoint( vpMath::round( rectangle.getLeft() ), - vpMath::round( rectangle.getBottom() ) ), - cvPoint( vpMath::round( rectangle.getRight() ), - vpMath::round( rectangle.getTop() ) ), - cvcolor, CV_FILLED); + cvcolor = CV_RGB(color.R, color.G, color.B) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + cvRectangle( background, + cvPoint( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cvPoint( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + cvcolor, filled); +#else + cv::rectangle( background, + cv::Point( vpMath::round( rectangle.getLeft() ), + vpMath::round( rectangle.getBottom() ) ), + cv::Point( vpMath::round( rectangle.getRight() ), + vpMath::round( rectangle.getTop() ) ), + cvcolor, filled); +#endif } } } @@ -1274,7 +1695,12 @@ vpDisplayOpenCV::getClick(bool blocking) ret = true ; rbuttondown = false; } - if (blocking) cvWaitKey(10); + if (blocking) +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvWaitKey(10); +#else + cv::waitKey(10); +#endif } while ( ret == false && blocking == true); } else { @@ -1342,7 +1768,12 @@ vpDisplayOpenCV::getClick(vpImagePoint &ip, bool blocking) ip.set_v( v ); rbuttondown = false; } - if (blocking) cvWaitKey(10); + if (blocking) +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvWaitKey(10); +#else + cv::waitKey(10); +#endif } while ( ret == false && blocking == true); } else { @@ -1416,7 +1847,12 @@ vpDisplayOpenCV::getClick(vpImagePoint &ip, button = vpMouseButton::button3; rbuttondown = false; } - if (blocking) cvWaitKey(10); + if (blocking) +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvWaitKey(10); +#else + cv::waitKey(10); +#endif } while ( ret == false && blocking == true); } else { @@ -1492,7 +1928,12 @@ vpDisplayOpenCV::getClickUp(vpImagePoint &ip, button = vpMouseButton::button3; rbuttonup = false; } - if (blocking) cvWaitKey(10); + if (blocking) +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvWaitKey(10); +#else + cv::waitKey(10); +#endif } while ( ret == false && blocking == true); } else { @@ -1518,58 +1959,86 @@ void vpDisplayOpenCV::on_mouse( int event, int x, int y, int /*flags*/, void* di vpDisplayOpenCV* disp = (vpDisplayOpenCV*)display; switch ( event ) { - case CV_EVENT_MOUSEMOVE: - { - disp->move = true; - disp->x_move = x; - disp->y_move = y; - break; - } - case CV_EVENT_LBUTTONDOWN: - { - disp->lbuttondown = true; - disp->x_lbuttondown = x; - disp->y_lbuttondown = y; - break; - } - case CV_EVENT_MBUTTONDOWN: - { - disp->mbuttondown = true; - disp->x_mbuttondown = x; - disp->y_mbuttondown = y; - break; - } - case CV_EVENT_RBUTTONDOWN: - { - disp->rbuttondown = true; - disp->x_rbuttondown = x; - disp->y_rbuttondown = y; - break; - } - case CV_EVENT_LBUTTONUP: - { - disp->lbuttonup = true; - disp->x_lbuttonup = x; - disp->y_lbuttonup = y; - break; - } - case CV_EVENT_MBUTTONUP: - { - disp->mbuttonup = true; - disp->x_mbuttonup = x; - disp->y_mbuttonup = y; - break; - } - case CV_EVENT_RBUTTONUP: - { - disp->rbuttonup = true; - disp->x_rbuttonup = x; - disp->y_rbuttonup = y; - break; - } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_MOUSEMOVE: +#else + case cv::EVENT_MOUSEMOVE: +#endif + { + disp->move = true; + disp->x_move = x; + disp->y_move = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_LBUTTONDOWN: +#else + case cv::EVENT_LBUTTONDOWN: +#endif + { + disp->lbuttondown = true; + disp->x_lbuttondown = x; + disp->y_lbuttondown = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_MBUTTONDOWN: +#else + case cv::EVENT_MBUTTONDOWN: +#endif + { + disp->mbuttondown = true; + disp->x_mbuttondown = x; + disp->y_mbuttondown = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_RBUTTONDOWN: +#else + case cv::EVENT_RBUTTONDOWN: +#endif + { + disp->rbuttondown = true; + disp->x_rbuttondown = x; + disp->y_rbuttondown = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_LBUTTONUP: +#else + case cv::EVENT_LBUTTONUP: +#endif + { + disp->lbuttonup = true; + disp->x_lbuttonup = x; + disp->y_lbuttonup = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_MBUTTONUP: +#else + case cv::EVENT_MBUTTONUP: +#endif + { + disp->mbuttonup = true; + disp->x_mbuttonup = x; + disp->y_mbuttonup = y; + break; + } +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + case CV_EVENT_RBUTTONUP: +#else + case cv::EVENT_RBUTTONUP: +#endif + { + disp->rbuttonup = true; + disp->x_rbuttonup = x; + disp->y_rbuttonup = y; + break; + } - default : - break; + default : + break; } } @@ -1601,7 +2070,12 @@ vpDisplayOpenCV::getKeyboardEvent(bool blocking) else delay = 10; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) key_pressed = cvWaitKey(delay); +#else + key_pressed = cv::waitKey(delay); +#endif + if (key_pressed == -1) return false; return true; @@ -1644,7 +2118,11 @@ vpDisplayOpenCV::getKeyboardEvent(char *string, bool blocking) else delay = 10; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) key_pressed = cvWaitKey(delay); +#else + key_pressed = cv::waitKey(delay); +#endif if (key_pressed == -1) return false; else { diff --git a/src/device/display/vpDisplayOpenCV.h b/src/device/display/vpDisplayOpenCV.h index d27158e8d2eb07318dca53957b636488a887fb12..4848d619f38c54211ff61b70cb48bca7f540e9a0 100644 --- a/src/device/display/vpDisplayOpenCV.h +++ b/src/device/display/vpDisplayOpenCV.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayOpenCV.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplayOpenCV.h 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,9 +44,9 @@ #define vpDisplayOpenCV_h #include <visp/vpConfig.h> -#include <visp/vpDisplay.h> -#if ( defined(VISP_HAVE_OPENCV) ) +#if defined(VISP_HAVE_OPENCV) +#include <visp/vpDisplay.h> #include <visp/vpImage.h> #include <visp/vpImageConvert.h> @@ -144,11 +144,21 @@ int main() class VISP_EXPORT vpDisplayOpenCV: public vpDisplay { private: +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) //! true if OpenCV display is ready to use IplImage* background; CvScalar *col ; CvScalar cvcolor; CvFont *font; +#else + cv::Mat background; + cv::Scalar *col ; + cv::Scalar cvcolor; + int font; + float fontScale; +#endif + static std::vector<std::string> m_listTitles; + static unsigned int m_nbWindows ; int fontHeight; int ncol, nrow ; int x_move; @@ -272,9 +282,3 @@ protected: #endif #endif - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/device/display/vpDisplayX.cpp b/src/device/display/vpDisplayX.cpp index 0fdebd9b36c83d5c019fc9f255ae8088111eacf8..f2a9d999b85d6823f0a16d69e564209aa9fdf8de 100644 --- a/src/device/display/vpDisplayX.cpp +++ b/src/device/display/vpDisplayX.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayX.cpp 4174 2013-03-22 10:28:41Z fspindle $ + * $Id: vpDisplayX.cpp 5214 2015-01-27 18:33:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -79,9 +79,12 @@ vpDisplayX::vpDisplayX ( vpImage<unsigned char> &I, int x, int y, - const char *title ) : vpDisplay() + const char *title ) + : display(NULL), window(), Ximage(NULL), lut(), context(), + screen(0), planes(0), event(), pixmap(), x_color(NULL), + screen_depth(8), xcolor(), values(), size(0), ximage_data_init(false), + RMask(0), GMask(0), BMask(0), RShift(0), GShift(0), BShift(0) { - x_color = NULL; init ( I, x, y, title ) ; } @@ -98,9 +101,12 @@ vpDisplayX::vpDisplayX ( vpImage<unsigned char> &I, vpDisplayX::vpDisplayX ( vpImage<vpRGBa> &I, int x, int y, - const char *title ) : vpDisplay() + const char *title ) + : display(NULL), window(), Ximage(NULL), lut(), context(), + screen(0), planes(0), event(), pixmap(), x_color(NULL), + screen_depth(8), xcolor(), values(), size(0), ximage_data_init(false), + RMask(0), GMask(0), BMask(0), RShift(0), GShift(0), BShift(0) { - x_color = NULL; init ( I, x, y, title ) ; } @@ -126,17 +132,19 @@ int main() } \endcode */ -vpDisplayX::vpDisplayX ( int x, int y, const char *title ) : vpDisplay() +vpDisplayX::vpDisplayX ( int x, int y, const char *title ) + : display(NULL), window(), Ximage(NULL), lut(), context(), + screen(0), planes(0), event(), pixmap(), x_color(NULL), + screen_depth(8), xcolor(), values(), size(0), ximage_data_init(false), + RMask(0), GMask(0), BMask(0), RShift(0), GShift(0), BShift(0) { windowXPosition = x ; windowYPosition = y ; - this->x_color = NULL; - if (title != NULL) - strcpy (this->title, title); - - ximage_data_init = false; + title_ = std::string(title); + else + title_ = std::string(" "); } /*! @@ -158,10 +166,12 @@ int main() } \endcode */ -vpDisplayX::vpDisplayX() : vpDisplay() +vpDisplayX::vpDisplayX() + : display(NULL), window(), Ximage(NULL), lut(), context(), + screen(0), planes(0), event(), pixmap(), x_color(NULL), + screen_depth(8), xcolor(), values(), size(0), ximage_data_init(false), + RMask(0), GMask(0), BMask(0), RShift(0), GShift(0), BShift(0) { - x_color = NULL; - ximage_data_init = false; } /*! @@ -195,10 +205,12 @@ vpDisplayX::init ( vpImage<unsigned char> &I, int x, int y, const char *title ) if (y != -1) windowYPosition = y ; - if ( title != NULL ) - strcpy ( this->title, title ) ; + if (title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); - // Positionnement de la fenetre dans l'�cran. + // Positionnement de la fenetre dans l'ecran. if ( ( windowXPosition < 0 ) || ( windowYPosition < 0 ) ) { hints.flags = 0; @@ -225,11 +237,10 @@ vpDisplayX::init ( vpImage<unsigned char> &I, int x, int y, const char *title ) lut = DefaultColormap ( display, screen ); screen_depth = (unsigned int)DefaultDepth ( display, screen ); - if ( ( window = - XCreateSimpleWindow ( display, RootWindow ( display, screen ), - windowXPosition, windowYPosition, width, height, 1, - BlackPixel ( display, screen ), - WhitePixel ( display, screen ) ) ) == 0 ) + if ( ( window = XCreateSimpleWindow ( display, RootWindow ( display, screen ), + windowXPosition, windowYPosition, width, height, 1, + BlackPixel ( display, screen ), + WhitePixel ( display, screen ) ) ) == 0 ) { vpERROR_TRACE ( "Can't create window." ); throw ( vpDisplayException ( vpDisplayException::cannotOpenWindowError, @@ -276,6 +287,14 @@ vpDisplayX::init ( vpImage<unsigned char> &I, int x, int y, const char *title ) XSetWindowColormap ( display, window, lut ) ; XInstallColormap ( display, lut ) ; + Visual *visual = DefaultVisual (display, screen); + RMask = visual->red_mask; + GMask = visual->green_mask; + BMask = visual->blue_mask; + + RShift = 15 - getMsb(RMask); /* these are right-shifts */ + GShift = 15 - getMsb(GMask); + BShift = 15 - getMsb(BMask); } // @@ -568,7 +587,7 @@ vpDisplayX::init ( vpImage<unsigned char> &I, int x, int y, const char *title ) } } - XSetStandardProperties ( display, window, this->title, this->title, None, 0, 0, &hints ); + XSetStandardProperties ( display, window, this->title_.c_str(), this->title_.c_str(), None, 0, 0, &hints ); XMapWindow ( display, window ) ; // Selection des evenements. XSelectInput ( display, window, @@ -598,21 +617,24 @@ vpDisplayX::init ( vpImage<unsigned char> &I, int x, int y, const char *title ) // Pixmap creation. pixmap = XCreatePixmap ( display, window, width, height, screen_depth ); - do - XNextEvent ( display, &event ); - while ( event.xany.type != Expose ); + // Hangs when forward X11 is used to send the display to an other computer +// do +// XNextEvent ( display, &event ); +// while ( event.xany.type != Expose ); { Ximage = XCreateImage ( display, DefaultVisual ( display, screen ), screen_depth, ZPixmap, 0, NULL, I.getWidth() , I.getHeight(), XBitmapPad ( display ), 0 ); - Ximage->data = ( char * ) malloc ( I.getWidth() * I.getHeight() * (unsigned int)Ximage->bits_per_pixel / 8 ); + Ximage->data = ( char * ) malloc ( I.getHeight() * (unsigned int)Ximage->bytes_per_line ); ximage_data_init = true; } displayHasBeenInitialized = true ; - setTitle ( this->title ) ; + + XStoreName ( display, window, title_.c_str() ); + XSync ( display, 1 ); I.display = this ; @@ -642,10 +664,12 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) x_color= new unsigned long [vpColor::id_unknown]; } - if ( title != NULL ) - strcpy ( this->title, title ) ; + if (title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); - // Positionnement de la fenetre dans l'�cran. + // Positionnement de la fenetre dans l'ecran. if ( ( windowXPosition < 0 ) || ( windowYPosition < 0 ) ) { hints.flags = 0; @@ -722,9 +746,17 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) colortable[i] = xcolor.pixel; } + Visual *visual = DefaultVisual (display, screen); + RMask = visual->red_mask; + GMask = visual->green_mask; + BMask = visual->blue_mask; + + RShift = 15 - getMsb(RMask); /* these are right-shifts */ + GShift = 15 - getMsb(GMask); + BShift = 15 - getMsb(BMask); + XSetWindowColormap ( display, window, lut ) ; XInstallColormap ( display, lut ) ; - } @@ -1019,7 +1051,7 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) } } - XSetStandardProperties ( display, window, this->title, this->title, None, 0, 0, &hints ); + XSetStandardProperties ( display, window, this->title_.c_str(), this->title_.c_str(), None, 0, 0, &hints ); XMapWindow ( display, window ) ; // Selection des evenements. XSelectInput ( display, window, @@ -1048,10 +1080,10 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) // Pixmap creation. pixmap = XCreatePixmap ( display, window, width, height, screen_depth ); - do - XNextEvent ( display, &event ); - while ( event.xany.type != Expose ); - + // Hangs when forward X11 is used to send the display to an other computer +// do +// XNextEvent ( display, &event ); +// while ( event.xany.type != Expose ); { Ximage = XCreateImage ( display, DefaultVisual ( display, screen ), @@ -1059,15 +1091,15 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) I.getWidth() , I.getHeight(), XBitmapPad ( display ), 0 ); - Ximage->data = ( char * ) malloc ( I.getWidth() * I.getHeight() - * (unsigned int)Ximage->bits_per_pixel / 8 ); + Ximage->data = ( char * ) malloc ( I.getHeight() * (unsigned int)Ximage->bytes_per_line ); ximage_data_init = true; } displayHasBeenInitialized = true ; XSync ( display, true ); - setTitle ( this->title ) ; + + XStoreName ( display, window, title_.c_str() ); I.display = this ; } @@ -1076,17 +1108,15 @@ vpDisplayX::init ( vpImage<vpRGBa> &I, int x, int y, const char *title ) /*! Initialize the display size, position and title. - \param width, height : Width and height of the window. + \param w, h : Width and height of the window. \param x, y : The window is set at position x,y (column index, row index). \param title : Window title. */ -void vpDisplayX::init ( unsigned int width, unsigned int height, - int x, int y, const char *title ) +void vpDisplayX::init ( unsigned int w, unsigned int h, int x, int y, const char *title ) { - /* setup X11 ------------------------------------------------------------- */ - this->width = width; - this->height = height; + this->width = w; + this->height = h; XSizeHints hints; @@ -1094,7 +1124,7 @@ void vpDisplayX::init ( unsigned int width, unsigned int height, windowXPosition = x ; if (y != -1) windowYPosition = y ; - // Positionnement de la fenetre dans l'�cran. + // Positionnement de la fenetre dans l'ecran. if ( ( windowXPosition < 0 ) || ( windowYPosition < 0 ) ) { hints.flags = 0; @@ -1106,8 +1136,10 @@ void vpDisplayX::init ( unsigned int width, unsigned int height, hints.y = windowYPosition; } - if ( title != NULL ) - strcpy ( this->title, title ) ; + if (title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); if ( ( display = XOpenDisplay ( NULL ) ) == NULL ) { @@ -1174,6 +1206,14 @@ void vpDisplayX::init ( unsigned int width, unsigned int height, XSetWindowColormap ( display, window, lut ) ; XInstallColormap ( display, lut ) ; + Visual *visual = DefaultVisual (display, screen); + RMask = visual->red_mask; + GMask = visual->green_mask; + BMask = visual->blue_mask; + + RShift = 15 - getMsb(RMask); /* these are right-shifts */ + GShift = 15 - getMsb(GMask); + BShift = 15 - getMsb(BMask); } vpColor pcolor; // predefined colors @@ -1489,7 +1529,7 @@ void vpDisplayX::init ( unsigned int width, unsigned int height, } } - XSetStandardProperties ( display, window, this->title, this->title, None, 0, 0, &hints ); + XSetStandardProperties ( display, window, this->title_.c_str(), this->title_.c_str(), None, 0, 0, &hints ); XMapWindow ( display, window ) ; // Selection des evenements. XSelectInput ( display, window, @@ -1518,24 +1558,24 @@ void vpDisplayX::init ( unsigned int width, unsigned int height, // Pixmap creation. pixmap = XCreatePixmap ( display, window, width, height, screen_depth ); - do - XNextEvent ( display, &event ); - while ( event.xany.type != Expose ); + // Hangs when forward X11 is used to send the display to an other computer +// do +// XNextEvent ( display, &event ); +// while ( event.xany.type != Expose ); { Ximage = XCreateImage ( display, DefaultVisual ( display, screen ), screen_depth, ZPixmap, 0, NULL, width, height, XBitmapPad ( display ), 0 ); - Ximage->data = ( char * ) malloc ( width * height - * (unsigned int)Ximage->bits_per_pixel / 8 ); + Ximage->data = ( char * ) malloc ( height * (unsigned int)Ximage->bytes_per_line ); ximage_data_init = true; } displayHasBeenInitialized = true ; XSync ( display, true ); - setTitle ( this->title ) ; + XStoreName ( display, window, title_.c_str() ); } /*! @@ -1588,7 +1628,11 @@ vpDisplayX::setTitle ( const char *title ) { if ( displayHasBeenInitialized ) { - XStoreName ( display, window, title ); + if(title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); + XStoreName ( display, window, title_.c_str() ); } else { @@ -1647,11 +1691,11 @@ void vpDisplayX::displayImage ( const vpImage<unsigned char> &I ) // ROUGE, VERT, BLEU, JAUNE { unsigned int i = 0; - unsigned int size = width * height; + unsigned int size_ = width * height; unsigned char nivGris; unsigned char nivGrisMax = 255 - vpColor::id_unknown; - while ( i < size ) + while ( i < size_ ) { nivGris = src_8[i] ; if ( nivGris > nivGrisMax ) @@ -1671,14 +1715,15 @@ void vpDisplayX::displayImage ( const vpImage<unsigned char> &I ) } case 16: { - unsigned short *dst_16 = NULL; - dst_16 = ( unsigned short* ) Ximage->data; - - for ( unsigned int i = 0; i < height ; i++ ) - { + unsigned short *dst_16 = ( unsigned short* ) Ximage->data; + unsigned char *dst_8 = NULL; + unsigned int bytes_per_line = (unsigned int)Ximage->bytes_per_line; + for ( unsigned int i = 0; i < height ; i++ ) { + dst_8 = (unsigned char*) Ximage->data + i * bytes_per_line; + dst_16 = (unsigned short *) dst_8; for ( unsigned int j=0 ; j < width; j++ ) { - * ( dst_16+ ( i*width+j ) ) = ( unsigned short ) colortable[I[i][j]] ; + * ( dst_16 + j ) = ( unsigned short ) colortable[I[i][j]] ; } } @@ -1694,10 +1739,10 @@ void vpDisplayX::displayImage ( const vpImage<unsigned char> &I ) default: { unsigned char *dst_32 = NULL; - unsigned int size = width * height ; + unsigned int size_ = width * height ; dst_32 = ( unsigned char* ) Ximage->data; unsigned char *bitmap = I.bitmap ; - unsigned char *n = I.bitmap + size; + unsigned char *n = I.bitmap + size_; //for (unsigned int i = 0; i < size; i++) // suppression de l'iterateur i while ( bitmap < n ) { @@ -1737,56 +1782,80 @@ void vpDisplayX::displayImage ( const vpImage<unsigned char> &I ) */ void vpDisplayX::displayImage ( const vpImage<vpRGBa> &I ) { - if ( displayHasBeenInitialized ) { - switch ( screen_depth ) { - case 24: - case 32: - { - /* + case 16: { + unsigned short *dst_16 = NULL; + unsigned char *dst_8 = NULL; + vpRGBa* bitmap = I.bitmap; + unsigned int r, g, b; + unsigned int bytes_per_line = (unsigned int)Ximage->bytes_per_line; + + for ( unsigned int i = 0; i < height ; i++ ) { + dst_8 = (unsigned char*) Ximage->data + i * bytes_per_line; + dst_16 = (unsigned short *) dst_8; + for ( unsigned int j=0 ; j < width; j++ ) + { + r = bitmap->R; + g = bitmap->G; + b = bitmap->B; + * ( dst_16 + j ) = (((r << 8) >> RShift) & RMask) | + (((g << 8) >> GShift) & GMask) | + (((b << 8) >> BShift) & BMask); + bitmap++; + } + } + + XPutImage ( display, pixmap, context, Ximage, 0, 0, 0, 0, width, height ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + + break; + } + case 24: + case 32: + { + /* * 32-bit source, 24/32-bit destination */ - - unsigned char *dst_32 = NULL; - dst_32 = ( unsigned char* ) Ximage->data; - vpRGBa* bitmap = I.bitmap; - unsigned int sizeI = I.getWidth() * I.getHeight(); -#ifdef BIGENDIAN - // little indian/big indian - for ( unsigned int i = 0; i < sizeI ; i++ ) - { + unsigned char *dst_32 = NULL; + dst_32 = ( unsigned char* ) Ximage->data; + vpRGBa* bitmap = I.bitmap; + unsigned int sizeI = I.getWidth() * I.getHeight(); + if (XImageByteOrder(display) == 1) { + // big endian + for ( unsigned int i = 0; i < sizeI ; i++ ) { *(dst_32++) = bitmap->A; *(dst_32++) = bitmap->R; *(dst_32++) = bitmap->G; *(dst_32++) = bitmap->B; - bitmap++; + bitmap++; } -#else - for ( unsigned int i = 0; i < sizeI; i++ ) - { + } + else { + // little endian + for ( unsigned int i = 0; i < sizeI; i++ ) { *(dst_32++) = bitmap->B; *(dst_32++) = bitmap->G; *(dst_32++) = bitmap->R; *(dst_32++) = bitmap->A; - bitmap++; + bitmap++; } -#endif - // Affichage de l'image dans la Pixmap. - XPutImage ( display, pixmap, context, Ximage, 0, 0, 0, 0, width, height ); - XSetWindowBackgroundPixmap ( display, window, pixmap ); -// XClearWindow ( display, window ); -// XSync ( display,1 ); - break; - } - default: - vpERROR_TRACE ( "Unsupported depth (%d bpp) for color display", - screen_depth ) ; - throw ( vpDisplayException ( vpDisplayException::depthNotSupportedError, - "Unsupported depth for color display" ) ) ; + // Affichage de l'image dans la Pixmap. + XPutImage ( display, pixmap, context, Ximage, 0, 0, 0, 0, width, height ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + // XClearWindow ( display, window ); + // XSync ( display,1 ); + break; + + } + default: + vpERROR_TRACE ( "Unsupported depth (%d bpp) for color display", + screen_depth ) ; + throw ( vpDisplayException ( vpDisplayException::depthNotSupportedError, + "Unsupported depth for color display" ) ) ; } } else @@ -1852,136 +1921,146 @@ void vpDisplayX::displayImage ( const unsigned char *I ) \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w, h : Width and height of the region of interest - \param height : Height of the region of interest - \sa init(), closeDisplay() */ -void vpDisplayX::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayX::displayImageROI ( const vpImage<unsigned char> &I,const vpImagePoint &iP, + const unsigned int w, const unsigned int h ) { if ( displayHasBeenInitialized ) { switch ( screen_depth ) { - case 8: + case 8: + { + unsigned char *src_8 = NULL; + unsigned char *dst_8 = NULL; + src_8 = ( unsigned char * ) I.bitmap; + dst_8 = ( unsigned char * ) Ximage->data; + // Correction de l'image de facon a liberer les niveaux de gris + // ROUGE, VERT, BLEU, JAUNE { - unsigned char *src_8 = NULL; - unsigned char *dst_8 = NULL; - src_8 = ( unsigned char * ) I.bitmap; - dst_8 = ( unsigned char * ) Ximage->data; - // Correction de l'image de facon a liberer les niveaux de gris - // ROUGE, VERT, BLEU, JAUNE + //int size = width * height; + unsigned char nivGris; + unsigned char nivGrisMax = 255 - vpColor::id_unknown; + + //unsigned int iwidth = I.getWidth(); + unsigned int iwidth = I.getWidth(); + + src_8 = src_8 + (int)(iP.get_i()*iwidth+ iP.get_j()); + dst_8 = dst_8 + (int)(iP.get_i()*this->width+ iP.get_j()); + + unsigned int i = 0; + while (i < h) { - //int size = width * height; - unsigned char nivGris; - unsigned char nivGrisMax = 255 - vpColor::id_unknown; - - //unsigned int iwidth = I.getWidth(); - unsigned int iwidth = I.getWidth(); - - src_8 = src_8 + (int)(iP.get_i()*iwidth+ iP.get_j()); - dst_8 = dst_8 + (int)(iP.get_i()*this->height+ iP.get_j()); - - unsigned int i = 0; - while (i < height) - { - unsigned int j = 0; - while (j < width) - { - nivGris = *(src_8+j); - if ( nivGris > nivGrisMax ) - *(dst_8+j) = 255; - else - *(dst_8+j) = nivGris; - j++; - } - src_8 = src_8 + iwidth; - dst_8 = dst_8 + this->height; - i++; - } + unsigned int j = 0; + while (j < w) + { + nivGris = *(src_8+j); + if ( nivGris > nivGrisMax ) + *(dst_8+j) = 255; + else + *(dst_8+j) = nivGris; + j++; + } + src_8 = src_8 + iwidth; + dst_8 = dst_8 + this->width; + i++; } - - // Affichage de l'image dans la Pixmap. - XPutImage ( display, pixmap, context, Ximage, iP.get_u(), iP.get_v(), iP.get_u(), iP.get_v(), width, height ); - XSetWindowBackgroundPixmap ( display, window, pixmap ); -// XClearWindow ( display, window ); -// XSync ( display,1 ); - break; } - case 16: - { - unsigned short *dst_16 = NULL; - dst_16 = ( unsigned short* ) Ximage->data; - unsigned char *src_8 = NULL; - src_8 = ( unsigned char * ) I.bitmap; - - unsigned int iwidth = I.getWidth(); - - src_8 = src_8 + (int)(iP.get_i()*iwidth+ iP.get_j()); - dst_16 = dst_16 + (int)(iP.get_i()*this->height+ iP.get_j()); - - unsigned int i = 0; - while (i < height) - { - unsigned int j = 0; - while (j < width) - { - *(dst_16+j) = ( unsigned short ) colortable[*(src_8+j)]; - j++; - } - src_8 = src_8 + iwidth; - dst_16 = dst_16 + this->height; - i++; - } - // Affichage de l'image dans la Pixmap. - XPutImage ( display, pixmap, context, Ximage, iP.get_u(), iP.get_v(), iP.get_u(), iP.get_v(), width, height ); - XSetWindowBackgroundPixmap ( display, window, pixmap ); -// XClearWindow ( display, window ); -// XSync ( display,1 ); - break; + // Affichage de l'image dans la Pixmap. + XPutImage ( display, pixmap, context, Ximage, (int)iP.get_u(), (int)iP.get_v(), (int)iP.get_u(), (int)iP.get_v(), w, h ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + // XClearWindow ( display, window ); + // XSync ( display,1 ); + break; + } + case 16: + { + unsigned short *dst_16 = NULL; + unsigned char *dst_8 = NULL; + unsigned int bytes_per_line = (unsigned int)Ximage->bytes_per_line; + for ( unsigned int i = (unsigned int)iP.get_i(); i < (unsigned int)(iP.get_i()+h) ; i++ ) { + dst_8 = (unsigned char *) Ximage->data + i * bytes_per_line; + dst_16 = (unsigned short *) dst_8; + for ( unsigned int j=(unsigned int)iP.get_j() ; j < (unsigned int)(iP.get_j()+w); j++ ) + { + * ( dst_16 + j ) = ( unsigned short ) colortable[I[i][j]] ; + } } - case 24: - default: - { - unsigned char *dst_32 = NULL; - //unsigned int size = width * height ; - dst_32 = ( unsigned char* ) Ximage->data; - unsigned char *src_8 = I.bitmap ; - //unsigned char *n = I.bitmap + size; - - unsigned int iwidth = I.getWidth(); - - src_8 = src_8 + (int)(iP.get_i()*iwidth+ iP.get_j()); - dst_32 = dst_32 + (int)(iP.get_i()*4*this->width+ iP.get_j()*4); - - unsigned int i = 0; - while (i < height) - { - unsigned int j = 0; - while (j < width) - { - unsigned char val = *(src_8+j); - *(dst_32+4*j) = val; - *(dst_32+4*j+1) = val; - *(dst_32+4*j+2) = val; - *(dst_32+4*j+3) = val; - j++; - } - src_8 = src_8 + iwidth; - dst_32 = dst_32 + 4*this->width; - i++; - } +// unsigned char *src_8 = (unsigned char *) I.bitmap; +// unsigned char *dst_8 = (unsigned char *) Ximage->data; +// unsigned short *dst_16 = NULL; + +// unsigned int iwidth = I.getWidth(); + +// src_8 += (int)(iP.get_i()*iwidth + iP.get_j()); +// dst_8 += (int)(iP.get_i()*Ximage->bytes_per_line + iP.get_j()*Ximage->bits_per_pixel/8); +// dst_16 = (unsigned short *) dst_8; + +// unsigned int i = 0; +// while (i < h) { +// unsigned int j = 0; + +// while (j < w) { +// *(dst_16 + j) = ( unsigned short ) colortable[*(src_8+j)]; +// j++; +// } +// src_8 = src_8 + iwidth; +// dst_16 = dst_16 + Ximage->bytes_per_line; +// i++; +// } + + // Affichage de l'image dans la Pixmap. + XPutImage ( display, pixmap, context, Ximage, (int)iP.get_u(), (int)iP.get_v(), (int)iP.get_u(), (int)iP.get_v(), w, h ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + // XClearWindow ( display, window ); + // XSync ( display,1 ); + break; + } - // Affichage de l'image dans la Pixmap. - XPutImage ( display, pixmap, context, Ximage, iP.get_u(), iP.get_v(), iP.get_u(), iP.get_v(), width, height ); - XSetWindowBackgroundPixmap ( display, window, pixmap ); -// XClearWindow ( display, window ); -// XSync ( display,1 ); - break; + case 24: + default: + { + unsigned char *dst_32 = NULL; + //unsigned int size = width * height ; + dst_32 = ( unsigned char* ) Ximage->data; + unsigned char *src_8 = I.bitmap ; + //unsigned char *n = I.bitmap + size; + + unsigned int iwidth = I.getWidth(); + + src_8 = src_8 + (int)(iP.get_i()*iwidth+ iP.get_j()); + dst_32 = dst_32 + (int)(iP.get_i()*4*this->width+ iP.get_j()*4); + + unsigned int i = 0; + while (i < h) + { + unsigned int j = 0; + while (j < w) + { + unsigned char val = *(src_8+j); + *(dst_32+4*j) = val; + *(dst_32+4*j+1) = val; + *(dst_32+4*j+2) = val; + *(dst_32+4*j+3) = val; + j++; + } + src_8 = src_8 + iwidth; + dst_32 = dst_32 + 4*this->width; + i++; } + + // Affichage de l'image dans la Pixmap. + XPutImage ( display, pixmap, context, Ximage, (int)iP.get_u(), (int)iP.get_v(), (int)iP.get_u(), (int)iP.get_v(), w, h ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + // XClearWindow ( display, window ); + // XSync ( display,1 ); + break; + } } } else @@ -2004,74 +2083,109 @@ void vpDisplayX::displayImageROI ( const vpImage<unsigned char> &I,const vpImage \param iP : Top left corner of the region of interest - \param width : Width of the region of interest + \param w, h : Width and height of the region of interest - \param height : Height of the region of interest - \sa init(), closeDisplay() */ -void vpDisplayX::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, const unsigned int width, const unsigned int height ) +void vpDisplayX::displayImageROI ( const vpImage<vpRGBa> &I,const vpImagePoint &iP, + const unsigned int w, const unsigned int h ) { - if ( displayHasBeenInitialized ) { - switch ( screen_depth ) { - case 24: - case 32: - { - /* + case 16: { + unsigned short *dst_16 = NULL; + unsigned char *dst_8 = NULL; + unsigned int r, g, b; + unsigned int bytes_per_line = (unsigned int)Ximage->bytes_per_line; + for ( unsigned int i = (unsigned int)iP.get_i(); i < (unsigned int)(iP.get_i()+h) ; i++ ) { + dst_8 = (unsigned char *) Ximage->data + i * bytes_per_line; + dst_16 = (unsigned short *) dst_8; + for ( unsigned int j=(unsigned int)iP.get_j() ; j < (unsigned int)(iP.get_j()+w); j++ ) + { + r = I[i][j].R; + g = I[i][j].G; + b = I[i][j].B; + * ( dst_16 + j ) = (((r << 8) >> RShift) & RMask) | + (((g << 8) >> GShift) & GMask) | + (((b << 8) >> BShift) & BMask); + } + } + + XPutImage ( display, pixmap, context, Ximage, 0, 0, 0, 0, width, height ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + + break; + } + case 24: + case 32: + { + /* * 32-bit source, 24/32-bit destination */ - unsigned char *dst_32 = NULL; - dst_32 = ( unsigned char* ) Ximage->data; - vpRGBa* src_32 = I.bitmap; - //unsigned int sizeI = I.getWidth() * I.getHeight(); - - unsigned int iwidth = I.getWidth(); - - src_32 = src_32 + (int)(iP.get_i()*iwidth+ iP.get_j()); - dst_32 = dst_32 + (int)(iP.get_i()*4*this->width+ iP.get_j()*4); - - unsigned int i = 0; - while (i < height) - { - unsigned int j = 0; - while (j < width) - { -#ifdef BIGENDIAN - *(dst_32+4*j) = (src_32+j)->A; - *(dst_32+4*j+1) = (src_32+j)->R; - *(dst_32+4*j+2) = (src_32+j)->G; - *(dst_32+4*j+3) = (src_32+j)->B; -#else - *(dst_32+4*j) = (src_32+j)->B; - *(dst_32+4*j+1) = (src_32+j)->G; - *(dst_32+4*j+2) = (src_32+j)->R; - *(dst_32+4*j+3) = (src_32+j)->A; -#endif - j++; - } - src_32 = src_32 + iwidth; - dst_32 = dst_32 + 4*this->width; - i++; - } - - // Affichage de l'image dans la Pixmap. - XPutImage ( display, pixmap, context, Ximage, iP.get_u(), iP.get_v(), iP.get_u(), iP.get_v(), width, height ); - XSetWindowBackgroundPixmap ( display, window, pixmap ); -// XClearWindow ( display, window ); -// XSync ( display,1 ); - break; + unsigned char *dst_32 = NULL; + dst_32 = ( unsigned char* ) Ximage->data; + vpRGBa* src_32 = I.bitmap; + //unsigned int sizeI = I.getWidth() * I.getHeight(); + + unsigned int iwidth = I.getWidth(); + + src_32 = src_32 + (int)(iP.get_i()*iwidth+ iP.get_j()); + dst_32 = dst_32 + (int)(iP.get_i()*4*this->width+ iP.get_j()*4); + + unsigned int i = 0; + + if (XImageByteOrder(display) == 1) { + // big endian + while (i < h) { + unsigned int j = 0; + while (j < w) { + *(dst_32+4*j) = (src_32+j)->A; + *(dst_32+4*j+1) = (src_32+j)->R; + *(dst_32+4*j+2) = (src_32+j)->G; + *(dst_32+4*j+3) = (src_32+j)->B; + + j++; + } + src_32 = src_32 + iwidth; + dst_32 = dst_32 + 4*this->width; + i++; + } } - default: - vpERROR_TRACE ( "Unsupported depth (%d bpp) for color display", - screen_depth ) ; - throw ( vpDisplayException ( vpDisplayException::depthNotSupportedError, - "Unsupported depth for color display" ) ) ; + else { + // little endian + while (i < h) { + unsigned int j = 0; + while (j < w) { + *(dst_32+4*j) = (src_32+j)->B; + *(dst_32+4*j+1) = (src_32+j)->G; + *(dst_32+4*j+2) = (src_32+j)->R; + *(dst_32+4*j+3) = (src_32+j)->A; + + j++; + } + src_32 = src_32 + iwidth; + dst_32 = dst_32 + 4*this->width; + i++; + } + } + + // Affichage de l'image dans la Pixmap. + XPutImage ( display, pixmap, context, Ximage, (int)iP.get_u(), (int)iP.get_v(), (int)iP.get_u(), (int)iP.get_v(), w, h ); + XSetWindowBackgroundPixmap ( display, window, pixmap ); + // XClearWindow ( display, window ); + // XSync ( display,1 ); + break; + + } + default: + vpERROR_TRACE ( "Unsupported depth (%d bpp) for color display", + screen_depth ) ; + throw ( vpDisplayException ( vpDisplayException::depthNotSupportedError, + "Unsupported depth for color display" ) ) ; } } else @@ -2141,15 +2255,14 @@ void vpDisplayX::flushDisplay() It's necessary to use this function to see the results of any drawing. \param iP : Top left corner of the region of interest - \param width : Width of the region of interest - \param height : Height of the region of interest + \param w,h : Width and height of the region of interest */ -void vpDisplayX::flushDisplayROI(const vpImagePoint &iP, const unsigned int width, const unsigned int height) +void vpDisplayX::flushDisplayROI(const vpImagePoint &iP, const unsigned int w, const unsigned int h) { if ( displayHasBeenInitialized ) { //XClearWindow ( display, window ); - XClearArea ( display, window,iP.get_u(),iP.get_v(),width,height,0 ); + XClearArea ( display, window,(int)iP.get_u(),(int)iP.get_v(),w,h,0 ); XFlush ( display ); } else @@ -2203,10 +2316,10 @@ void vpDisplayX::clearDisplay ( const vpColor &color ) \param thickness : Thickness of the lines used to display the arrow. */ void vpDisplayX::displayArrow ( const vpImagePoint &ip1, - const vpImagePoint &ip2, + const vpImagePoint &ip2, const vpColor &color, unsigned int w, unsigned int h, - unsigned int thickness) + unsigned int thickness) { if ( displayHasBeenInitialized ) { @@ -2226,21 +2339,24 @@ void vpDisplayX::displayArrow ( const vpImagePoint &ip1, a /= lg ; b /= lg ; - vpImagePoint ip3; + vpImagePoint ip3; ip3.set_i(ip2.get_i() - w*a); ip3.set_j(ip2.get_j() - w*b); - vpImagePoint ip4; - ip4.set_i( ip3.get_i() - b*h ); - ip4.set_j( ip3.get_j() + a*h ); + vpImagePoint ip4; + ip4.set_i( ip3.get_i() - b*h ); + ip4.set_j( ip3.get_j() + a*h ); - displayLine ( ip2, ip4, color, thickness ) ; + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - ip4.set_i( ip3.get_i() + b*h ); - ip4.set_j( ip3.get_j() - a*h ); + ip4.set_i( ip3.get_i() + b*h ); + ip4.set_j( ip3.get_j() - a*h ); + + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) + displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip2, ip4, color, thickness ) ; - displayLine ( ip1, ip2, color, thickness ) ; + displayLine ( ip1, ip2, color, thickness ) ; } } catch ( ... ) @@ -2354,14 +2470,14 @@ void vpDisplayX::displayCircle ( const vpImagePoint ¢er, /*! Display a cross at the image point \e ip location. \param ip : Cross location. - \param size : Size (width and height) of the cross. + \param cross_size : Size (width and height) of the cross. \param color : Cross color. \param thickness : Thickness of the lines used to display the cross. */ void vpDisplayX::displayCross ( const vpImagePoint &ip, - unsigned int size, - const vpColor &color, - unsigned int thickness) + unsigned int cross_size, + const vpColor &color, + unsigned int thickness) { if ( displayHasBeenInitialized ) { @@ -2371,16 +2487,16 @@ void vpDisplayX::displayCross ( const vpImagePoint &ip, double j = ip.get_j(); vpImagePoint ip1, ip2; - ip1.set_i( i-size/2 ); + ip1.set_i( i-cross_size/2 ); ip1.set_j( j ); - ip2.set_i( i+size/2 ); + ip2.set_i( i+cross_size/2 ); ip2.set_j( j ); displayLine ( ip1, ip2, color, thickness ) ; ip1.set_i( i ); - ip1.set_j( j-size/2 ); + ip1.set_j( j-cross_size/2 ); ip2.set_i( i ); - ip2.set_j( j+size/2 ); + ip2.set_j( j+cross_size/2 ); displayLine ( ip1, ip2, color, thickness ) ; } @@ -2524,7 +2640,7 @@ void vpDisplayX::displayPoint ( const vpImagePoint &ip, width and \e height the rectangle size. \param topLeft : Top-left corner of the rectangle. - \param width,height : Rectangle size. + \param w,h : Rectangle size in terms of width and height. \param color : Rectangle color. \param fill : When set to true fill the rectangle. @@ -2534,9 +2650,9 @@ void vpDisplayX::displayPoint ( const vpImagePoint &ip, */ void vpDisplayX::displayRectangle ( const vpImagePoint &topLeft, - unsigned int width, unsigned int height, + unsigned int w, unsigned int h, const vpColor &color, bool fill, - unsigned int thickness ) + unsigned int thickness ) { if ( displayHasBeenInitialized ) { @@ -2558,14 +2674,14 @@ vpDisplayX::displayRectangle ( const vpImagePoint &topLeft, XDrawRectangle ( display, pixmap, context, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - width-1, height-1 ); + w-1, h-1 ); } else { XFillRectangle ( display, pixmap, context, vpMath::round( topLeft.get_u() ), vpMath::round( topLeft.get_v() ), - width, height ); + w, h ); } } else @@ -2592,7 +2708,7 @@ void vpDisplayX::displayRectangle ( const vpImagePoint &topLeft, const vpImagePoint &bottomRight, const vpColor &color, bool fill, - unsigned int thickness ) + unsigned int thickness ) { if ( displayHasBeenInitialized ) { @@ -2611,22 +2727,22 @@ vpDisplayX::displayRectangle ( const vpImagePoint &topLeft, XSetLineAttributes ( display, context, thickness, LineSolid, CapButt, JoinBevel ); - unsigned int width = (unsigned int)vpMath::round( std::fabs(bottomRight.get_u() - topLeft.get_u()) ); - unsigned int height = (unsigned int)vpMath::round( std::fabs(bottomRight.get_v() - topLeft.get_v()) ); + unsigned int w = (unsigned int)vpMath::round( std::fabs(bottomRight.get_u() - topLeft.get_u()) ); + unsigned int h = (unsigned int)vpMath::round( std::fabs(bottomRight.get_v() - topLeft.get_v()) ); if ( fill == false ) { XDrawRectangle ( display, pixmap, context, vpMath::round( topLeft.get_u() < bottomRight.get_u() ? topLeft.get_u() : bottomRight.get_u() ), vpMath::round( topLeft.get_v() < bottomRight.get_v() ? topLeft.get_v() : bottomRight.get_v() ), - width > 0 ? width-1 : 1, height > 0 ? height : 1 ); + w > 0 ? w-1 : 1, h > 0 ? h : 1 ); } else { XFillRectangle ( display, pixmap, context, vpMath::round( topLeft.get_u() < bottomRight.get_u() ? topLeft.get_u() : bottomRight.get_u() ), vpMath::round( topLeft.get_v() < bottomRight.get_v() ? topLeft.get_v() : bottomRight.get_v() ), - width, height ); + w, h ); } } else @@ -2736,7 +2852,7 @@ vpDisplayX::getClick(bool blocking) } if(ret){ - /* Recuperation de la coordonnee du pixel cliqu�. */ + /* Recuperation de la coordonnee du pixel clique. */ if ( XQueryPointer ( display, window, &rootwin, &childwin, @@ -2927,7 +3043,7 @@ vpDisplayX::getClickUp ( vpImagePoint &ip, } if(ret){ - /* Recuperation de la coordonnee du pixel cliqu�. */ + /* Recuperation de la coordonnee du pixel clique. */ if ( XQueryPointer ( display, window, &rootwin, &childwin, @@ -2960,14 +3076,9 @@ vpDisplayX::getClickUp ( vpImagePoint &ip, */ void vpDisplayX::getImage ( vpImage<vpRGBa> &I ) { - if ( displayHasBeenInitialized ) { - - XImage *xi ; - //xi= XGetImage ( display,window, 0,0, getWidth(), getHeight(), - // AllPlanes, ZPixmap ) ; XCopyArea (display,window, pixmap, context, 0,0, getWidth(), getHeight(), 0, 0); @@ -2988,28 +3099,41 @@ void vpDisplayX::getImage ( vpImage<vpRGBa> &I ) unsigned char *src_32 = NULL; src_32 = ( unsigned char* ) xi->data; -#ifdef BIGENDIAN - // little indian/big indian - for ( unsigned int i = 0; i < I.getWidth() * I.getHeight() ; i++ ) - { - I.bitmap[i].A = src_32[i*4] ; - I.bitmap[i].R = src_32[i*4 + 1] ; - I.bitmap[i].G = src_32[i*4 + 2] ; - I.bitmap[i].B = src_32[i*4 + 3] ; + if (screen_depth == 16) { + for ( unsigned int i = 0; i < I.getHeight() ; i++ ) { + size_t i_ = i*I.getWidth(); + for ( unsigned int j = 0; j < I.getWidth() ; j++ ) { + size_t ij_ = i_+j; + unsigned long pixel = XGetPixel(xi, (int)j, (int)i); + I.bitmap[ij_].R = (((pixel & RMask) << RShift) >> 8); + I.bitmap[ij_].G = (((pixel & GMask) << GShift) >> 8); + I.bitmap[ij_].B = (((pixel & BMask) << BShift) >> 8); + I.bitmap[ij_].A = 0; + } + } + } -#else - for ( unsigned int i = 0; i < I.getWidth() * I.getHeight() ; i++ ) - { - I.bitmap[i].B = src_32[i*4] ; - I.bitmap[i].G = src_32[i*4 + 1] ; - I.bitmap[i].R = src_32[i*4 + 2] ; - I.bitmap[i].A = src_32[i*4 + 3] ; + else { + if (XImageByteOrder(display) == 1) { + // big endian + for ( unsigned int i = 0; i < I.getWidth() * I.getHeight() ; i++ ) { + I.bitmap[i].A = src_32[i*4] ; + I.bitmap[i].R = src_32[i*4 + 1] ; + I.bitmap[i].G = src_32[i*4 + 2] ; + I.bitmap[i].B = src_32[i*4 + 3] ; + } + } + else { + // little endian + for ( unsigned int i = 0; i < I.getWidth() * I.getHeight() ; i++ ) { + I.bitmap[i].B = src_32[i*4] ; + I.bitmap[i].G = src_32[i*4 + 1] ; + I.bitmap[i].R = src_32[i*4 + 2] ; + I.bitmap[i].A = src_32[i*4 + 3] ; + } + } } -#endif - - XDestroyImage ( xi ) ; - } else { @@ -3024,46 +3148,46 @@ void vpDisplayX::getImage ( vpImage<vpRGBa> &I ) */ unsigned int vpDisplayX::getScreenDepth() { - Display *_display; - int screen; + Display *display_; + int screen_; unsigned int depth; - if ( ( _display = XOpenDisplay ( NULL ) ) == NULL ) + if ( ( display_ = XOpenDisplay ( NULL ) ) == NULL ) { vpERROR_TRACE ( "Can't connect display on server %s.", XDisplayName ( NULL ) ); throw ( vpDisplayException ( vpDisplayException::connexionError, "Can't connect display on server." ) ) ; } - screen = DefaultScreen ( _display ); - depth = (unsigned int)DefaultDepth ( _display, screen ); + screen_ = DefaultScreen ( display_ ); + depth = (unsigned int)DefaultDepth ( display_, screen_ ); - XCloseDisplay ( _display ); + XCloseDisplay ( display_ ); return ( depth ); } /*! Gets the window size. - \param width, height : Size of the display. + \param w, h : Size of the display in therms of width and height. */ -void vpDisplayX::getScreenSize ( unsigned int &width, unsigned int &height ) +void vpDisplayX::getScreenSize ( unsigned int &w, unsigned int &h ) { - Display *_display; - int screen; + Display *display_; + int screen_; - if ( ( _display = XOpenDisplay ( NULL ) ) == NULL ) + if ( ( display_ = XOpenDisplay ( NULL ) ) == NULL ) { vpERROR_TRACE ( "Can't connect display on server %s.", XDisplayName ( NULL ) ); throw ( vpDisplayException ( vpDisplayException::connexionError, "Can't connect display on server." ) ) ; } - screen = DefaultScreen ( _display ); - width = (unsigned int)DisplayWidth ( _display, screen ); - height = (unsigned int)DisplayHeight ( _display, screen ); + screen_ = DefaultScreen ( display_ ); + w = (unsigned int)DisplayWidth ( display_, screen_ ); + h = (unsigned int)DisplayHeight ( display_, screen_ ); - XCloseDisplay ( _display ); + XCloseDisplay ( display_ ); } /*! @@ -3257,10 +3381,20 @@ vpDisplayX::getPointerPosition ( vpImagePoint &ip) return ret ; } +/*! + Get the position of the most significant bit. +*/ +int vpDisplayX::getMsb(unsigned int u32val) +{ + int i; + + for (i = 31; i >= 0; --i) { + if (u32val & 0x80000000L) + break; + u32val <<= 1; + } + return i; +} + #endif -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/device/display/vpDisplayX.h b/src/device/display/vpDisplayX.h index d7b9c94029f79ca6edc0729cf918ed0d464b16e7..7296f27b23e7e8c3ef61854de46adc92c187ece3 100644 --- a/src/device/display/vpDisplayX.h +++ b/src/device/display/vpDisplayX.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayX.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplayX.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -152,7 +152,6 @@ int main() class VISP_EXPORT vpDisplayX: public vpDisplay { private: - int num_Xdisplay ; Display *display ; Window window ; XImage *Ximage ; @@ -169,28 +168,29 @@ private: XGCValues values; int size ; bool ximage_data_init; - + unsigned int RMask, GMask, BMask; + int RShift, GShift, BShift; + public: vpDisplayX() ; vpDisplayX(int winx, int winy, const char *title=NULL) ; vpDisplayX(vpImage<unsigned char> &I, int winx=-1, int winy=-1, - const char *title=NULL) ; + const char *title=NULL) ; vpDisplayX(vpImage<vpRGBa> &I, int winx=-1, int winy=-1, - const char *title=NULL) ; - + const char *title=NULL) ; virtual ~vpDisplayX() ; void init(vpImage<unsigned char> &I, - int winx=-1, int winy=-1, - const char *title=NULL) ; + int winx=-1, int winy=-1, + const char *title=NULL) ; void init(vpImage<vpRGBa> &I, - int winx=-1, int winy=-1, - const char *title=NULL) ; + int winx=-1, int winy=-1, + const char *title=NULL) ; void init(unsigned int width, unsigned int height, - int winx=-1, int winy=-1 , - const char *title=NULL) ; + int winx=-1, int winy=-1 , + const char *title=NULL) ; unsigned int getScreenDepth(); void getScreenSize(unsigned int &width, unsigned int &height); @@ -263,11 +263,15 @@ protected: bool getKeyboardEvent(bool blocking=true); bool getKeyboardEvent(char *string, bool blocking=true); + + int getMsb(unsigned int u32val); bool getPointerMotionEvent (vpImagePoint &ip); bool getPointerPosition (vpImagePoint &ip); inline unsigned int getWidth() const { return width ; } inline unsigned int getHeight() const { return height ; } + + } ; diff --git a/src/device/display/vpMouseButton.h b/src/device/display/vpMouseButton.h index 138cbd74d6e798c72eee1a44a9501c8818c484d2..b15e69fb6f23a3cf581c28f38fc9aff45aeb1917 100755 --- a/src/device/display/vpMouseButton.h +++ b/src/device/display/vpMouseButton.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMouseButton.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMouseButton.h 5232 2015-01-30 11:53:30Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,7 +48,7 @@ /*! \class vpMouseButton \ingroup ImageGUI - \brief Class that defines mouse button identiers. + \brief Class that defines mouse button identifiers. */ class VISP_EXPORT vpMouseButton { @@ -56,7 +56,8 @@ public: typedef enum { button1 = 1, /*!< Mouse left button. */ button2 = 2, /*!< Mouse middle button, or roll. */ - button3 = 3 /*!< Mouse right button. */ + button3 = 3, /*!< Mouse right button. */ + none = 0 /*!< No button. */ } vpMouseButtonType ; } ; diff --git a/src/device/display/windows/vpD3DRenderer.cpp b/src/device/display/windows/vpD3DRenderer.cpp index 6af10a3a9f1fafe04c5617fe8d2f7bc9198e3bb3..997c99c1ea84162855c4ff434e09408db6d8d336 100755 --- a/src/device/display/windows/vpD3DRenderer.cpp +++ b/src/device/display/windows/vpD3DRenderer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpD3DRenderer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpD3DRenderer.cpp 4733 2014-05-19 20:44:05Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ #ifndef DOXYGEN_SHOULD_SKIP_THIS #include <visp/vpConfig.h> -#if ( defined(WIN32) & defined(VISP_HAVE_D3D9) ) +#if ( defined(_WIN32) & defined(VISP_HAVE_D3D9) ) #include <visp/vpD3DRenderer.h> #include <visp/vpColor.h> @@ -1111,17 +1111,21 @@ void vpD3DRenderer::drawArrow(const vpImagePoint &ip1, vpImagePoint ip4 ; - ip4.set_i( ip3.get_i() + b*_h ); - ip4.set_j( ip3.get_j() - a*_h ); + ip4.set_i( ip3.get_i() + b*_h ); + ip4.set_j( ip3.get_j() - a*_h ); - MoveToEx(hDCMem, vpMath::round(ip2.get_j()), vpMath::round(ip2.get_i()), NULL); - LineTo(hDCMem, vpMath::round(ip4.get_j()), vpMath::round(ip4.get_i())); + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) { + MoveToEx(hDCMem, vpMath::round(ip2.get_j()), vpMath::round(ip2.get_i()), NULL); + LineTo(hDCMem, vpMath::round(ip4.get_j()), vpMath::round(ip4.get_i())); + } - ip4.set_i( ip3.get_i() - b*h ); - ip4.set_j( ip3.get_j() + a*h ); + ip4.set_i( ip3.get_i() - b*h ); + ip4.set_j( ip3.get_j() + a*h ); - MoveToEx(hDCMem, vpMath::round(ip2.get_j()), vpMath::round(ip2.get_i()), NULL); - LineTo(hDCMem, vpMath::round(ip4.get_j()), vpMath::round(ip4.get_i())); + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) { + MoveToEx(hDCMem, vpMath::round(ip2.get_j()), vpMath::round(ip2.get_i()), NULL); + LineTo(hDCMem, vpMath::round(ip4.get_j()), vpMath::round(ip4.get_i())); + } MoveToEx(hDCMem, vpMath::round(ip1.get_j()), vpMath::round(ip1.get_i()), NULL); LineTo(hDCMem, vpMath::round(ip2.get_j()), vpMath::round(ip2.get_i())); diff --git a/src/device/display/windows/vpD3DRenderer.h b/src/device/display/windows/vpD3DRenderer.h index 7edb7a905c641415a0b5c77dce0a03506daf462a..5231be19dc0d62c7f0ad2aefd2e9291ca76f7eca 100755 --- a/src/device/display/windows/vpD3DRenderer.h +++ b/src/device/display/windows/vpD3DRenderer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpD3DRenderer.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpD3DRenderer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/display/windows/vpDisplayD3D.cpp b/src/device/display/windows/vpDisplayD3D.cpp index a3a11d39efe0b677d5c221c1369421e9ae028277..36d4f0c2ee85af3e1aa01486d7c6cac3772834e1 100755 --- a/src/device/display/windows/vpDisplayD3D.cpp +++ b/src/device/display/windows/vpDisplayD3D.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayD3D.cpp 4174 2013-03-22 10:28:41Z fspindle $ + * $Id: vpDisplayD3D.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,7 +45,7 @@ */ #include <visp/vpConfig.h> -#if ( defined(WIN32) & defined(VISP_HAVE_D3D9) ) +#if ( defined(_WIN32) & defined(VISP_HAVE_D3D9) ) #include <visp/vpDisplayD3D.h> #include <visp/vpD3DRenderer.h> @@ -60,17 +60,19 @@ vpDisplayD3D::vpDisplayD3D(): vpDisplayWin32(new vpD3DRenderer()){} \brief Constructor : Initialize a display. \param winx, winy The window is set at position x,y (column index, row index). - \param _title Window's title. + \param title Window's title. */ -vpDisplayD3D::vpDisplayD3D(int winx, int winy, const char *_title) +vpDisplayD3D::vpDisplayD3D(int winx, int winy, const char *title) : vpDisplayWin32(new vpD3DRenderer()) { windowXPosition = winx; windowYPosition = winy; - if (_title != NULL) - strcpy(this->title, _title); + if (title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); } /*! @@ -80,15 +82,15 @@ vpDisplayD3D::vpDisplayD3D(int winx, int winy, const char *_title) \param I : Image to be displayed (note that image has to be initialized). \param winx, winy : The window is set at position x,y (column index, row index). -\param _title : Window's title. +\param title : Window's title. */ vpDisplayD3D::vpDisplayD3D(vpImage<vpRGBa> &I, int winx, int winy, - const char *_title) + const char *title) : vpDisplayWin32(new vpD3DRenderer()) { - init(I,winx,winy,_title); + init(I,winx,winy,title); } /*! @@ -98,15 +100,15 @@ vpDisplayD3D::vpDisplayD3D(vpImage<vpRGBa> &I, \param I Image to be displayed (note that image has to be initialized). \param winx, winy The window is set at position x,y (column index, row index). -\param _title Window's title. +\param title Window's title. */ vpDisplayD3D::vpDisplayD3D(vpImage<unsigned char> &I, int winx, int winy, - const char *_title) + const char *title) : vpDisplayWin32(new vpD3DRenderer()) { - init(I,winx,winy,_title); + init(I,winx,winy,title); } /*! diff --git a/src/device/display/windows/vpDisplayD3D.h b/src/device/display/windows/vpDisplayD3D.h index b11f7ef5151e599e3dcba24b3519192dc8b9db3a..dd71b666f943b10f7be4293d8eecc378ec7ea5fa 100755 --- a/src/device/display/windows/vpDisplayD3D.h +++ b/src/device/display/windows/vpDisplayD3D.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayD3D.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplayD3D.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -110,9 +110,9 @@ class VISP_EXPORT vpDisplayD3D : public vpDisplayWin32 { public: vpDisplayD3D(); - vpDisplayD3D(int winx, int winy, const char *_title=NULL); - vpDisplayD3D(vpImage<vpRGBa> &I,int winx=-1, int winy=-1, const char *_title=NULL); - vpDisplayD3D(vpImage<unsigned char> &I, int winx=-1, int winy=-1, const char *_title=NULL); + vpDisplayD3D(int winx, int winy, const char *title=NULL); + vpDisplayD3D(vpImage<vpRGBa> &I,int winx=-1, int winy=-1, const char *title=NULL); + vpDisplayD3D(vpImage<unsigned char> &I, int winx=-1, int winy=-1, const char *title=NULL); virtual ~vpDisplayD3D(); diff --git a/src/device/display/windows/vpDisplayGDI.cpp b/src/device/display/windows/vpDisplayGDI.cpp index 01303c07543f90f7217a5685bbdc1a15812768b9..697211052bcba79ddf32e5ab63496f644479eb47 100755 --- a/src/device/display/windows/vpDisplayGDI.cpp +++ b/src/device/display/windows/vpDisplayGDI.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayGDI.cpp 4174 2013-03-22 10:28:41Z fspindle $ + * $Id: vpDisplayGDI.cpp 4642 2014-02-05 12:42:30Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,17 +61,19 @@ vpDisplayGDI::vpDisplayGDI(): vpDisplayWin32(new vpGDIRenderer()){} \brief Constructor : Initialize a display. \param winx, winy The window is set at position x,y (column index, row index). - \param _title Window's title. + \param title Window's title. */ -vpDisplayGDI::vpDisplayGDI(int winx, int winy, const char *_title) +vpDisplayGDI::vpDisplayGDI(int winx, int winy, const char *title) : vpDisplayWin32(new vpGDIRenderer()) { windowXPosition = winx; windowYPosition = winy; - if (_title != NULL) - strcpy(this->title, _title); + if (title != NULL) + title_ = std::string(title); + else + title_ = std::string(" "); } /*! @@ -81,15 +83,15 @@ vpDisplayGDI::vpDisplayGDI(int winx, int winy, const char *_title) \param I : image to be displayed (note that image has to be initialized). \param winx, winy The window is set at position x,y (column index, row index). - \param _title Window's title. + \param title Window's title. */ vpDisplayGDI::vpDisplayGDI(vpImage<vpRGBa> &I, int winx, int winy, - const char *_title) + const char *title) : vpDisplayWin32(new vpGDIRenderer()) { - init(I,winx,winy,_title); + init(I,winx,winy,title); } /*! @@ -99,15 +101,15 @@ vpDisplayGDI::vpDisplayGDI(vpImage<vpRGBa> &I, \param I Image to be displayed (note that image has to be initialized). \param winx, winy The window is set at position x,y (column index, row index). - \param _title Window's title. + \param title Window's title. */ vpDisplayGDI::vpDisplayGDI(vpImage<unsigned char> &I, int winx, int winy, - const char *_title) + const char *title) : vpDisplayWin32(new vpGDIRenderer()) { - init(I,winx,winy,_title); + init(I,winx,winy,title); } /*! diff --git a/src/device/display/windows/vpDisplayGDI.h b/src/device/display/windows/vpDisplayGDI.h index 7b0f3f41beed6ab2677735e34f16cda388068fcf..ffe1b310cbe12bd7679b52528d9472388b105bf5 100755 --- a/src/device/display/windows/vpDisplayGDI.h +++ b/src/device/display/windows/vpDisplayGDI.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayGDI.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpDisplayGDI.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,11 +71,10 @@ int main() vpImage<unsigned char> I; // Grey level image // Read an image in PGM P5 format -#ifdef UNIX - //vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(I, "/tmp/Klimt.pgm"); -#elif WIN32 +#ifdef _WIN32 vpImageIo::read(I, "C:/temp/ViSP-images/Klimt/Klimt.pgm"); +#else + vpImageIo::read(I, "/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); #endif vpDisplayGDI d; @@ -134,9 +133,9 @@ class VISP_EXPORT vpDisplayGDI : public vpDisplayWin32 { public: vpDisplayGDI(); - vpDisplayGDI(int winx, int winy, const char *_title=NULL); - vpDisplayGDI(vpImage<vpRGBa> &I,int winx=-1, int winy=-1, const char *_title=NULL); - vpDisplayGDI(vpImage<unsigned char> &I, int winx=-1, int winy=-1, const char *_title=NULL); + vpDisplayGDI(int winx, int winy, const char *title=NULL); + vpDisplayGDI(vpImage<vpRGBa> &I,int winx=-1, int winy=-1, const char *title=NULL); + vpDisplayGDI(vpImage<unsigned char> &I, int winx=-1, int winy=-1, const char *title=NULL); virtual ~vpDisplayGDI(); }; diff --git a/src/device/display/windows/vpDisplayWin32.cpp b/src/device/display/windows/vpDisplayWin32.cpp index bb88afde14c2185ea3404480de4e8f9da1abc72a..fdcbd93ba69608a536d98c08373e753d5c7974af 100755 --- a/src/device/display/windows/vpDisplayWin32.cpp +++ b/src/device/display/windows/vpDisplayWin32.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayWin32.cpp 4174 2013-03-22 10:28:41Z fspindle $ + * $Id: vpDisplayWin32.cpp 4661 2014-02-10 19:34:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ const int vpDisplayWin32::MAX_INIT_DELAY = 5000; void vpCreateWindow(threadParam * param) { //char* title = param->title; - (param->vpDisp)->window.initWindow(param->title, param->x, param->y, + (param->vpDisp)->window.initWindow(param->title.c_str(), param->x, param->y, param->w, param->h); delete param; } @@ -150,7 +150,9 @@ void vpDisplayWin32::init(unsigned int width, unsigned int height, const char *title) { if (title != NULL) - strcpy(this->title, title) ; + title_ = std::string(title); + else + title_ = std::string(" "); if (x != -1) windowXPosition = x; @@ -164,7 +166,7 @@ void vpDisplayWin32::init(unsigned int width, unsigned int height, param->w = width; param->h = height; param->vpDisp = this; - param->title = this->title; + param->title = this->title_; //creates the window in a separate thread hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)vpCreateWindow, @@ -706,7 +708,7 @@ void vpDisplayWin32::flushDisplayROI(const vpImagePoint &iP, const unsigned int //sends a message to the window # if 1 // new version FS - WPARAM wp = (hr1.left_top << sizeof(unsigned short)) + hr1.right_bottom; + WPARAM wp = (WPARAM)(hr1.left_top << sizeof(unsigned short)) + hr1.right_bottom; LPARAM lp = (hr2.left_top << sizeof(unsigned short)) + hr2.right_bottom; # else // produce warnings with MinGW WPARAM wp=*((WPARAM*)(&hr1)); diff --git a/src/device/display/windows/vpDisplayWin32.h b/src/device/display/windows/vpDisplayWin32.h index b8d9bf0b656aa0010b7e6b994bd21a61ca6e4e2e..3e6e68004b7a90bf58d7c4c88d171d54b5283462 100755 --- a/src/device/display/windows/vpDisplayWin32.h +++ b/src/device/display/windows/vpDisplayWin32.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplayWin32.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDisplayWin32.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,6 +46,8 @@ #ifndef vpDisplayWin32_hh #define vpDisplayWin32_hh +#include <string> + #include <visp/vpImage.h> #include <visp/vpDisplay.h> #include <windows.h> @@ -76,7 +78,7 @@ struct threadParam unsigned int h; //! Title of the window. - char * title; + std::string title; }; #endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/src/device/display/windows/vpGDIRenderer.cpp b/src/device/display/windows/vpGDIRenderer.cpp index 468ef7f10607940d6012f42a7d53814224af8716..e67156db07dba5da0b09ab7e812f12e2da542c8b 100755 --- a/src/device/display/windows/vpGDIRenderer.cpp +++ b/src/device/display/windows/vpGDIRenderer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpGDIRenderer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpGDIRenderer.cpp 4733 2014-05-19 20:44:05Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1092,27 +1092,30 @@ void vpGDIRenderer::drawArrow(const vpImagePoint &ip1, //double t = 0 ; //while (t<=_l) - { - ip4.set_i( ip3.get_i() - b*h ); - ip4.set_j( ip3.get_j() + a*h ); - - MoveToEx(hDCMem, vpMath::round(ip2.get_u()), vpMath::round(ip2.get_v()), NULL); - LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v())); - - // t+=0.1 ; - } - - //t = 0 ; - //while (t>= -_l) - { - ip4.set_i( ip3.get_i() + b*h ); - ip4.set_j( ip3.get_j() - a*h ); - - MoveToEx(hDCMem, vpMath::round(ip2.get_u()), vpMath::round(ip2.get_v()), NULL); - LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v())); - - // t-=0.1 ; - } + { + ip4.set_i( ip3.get_i() - b*h ); + ip4.set_j( ip3.get_j() + a*h ); + + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) { + MoveToEx(hDCMem, vpMath::round(ip2.get_u()), vpMath::round(ip2.get_v()), NULL); + LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v())); + } + // t+=0.1 ; + } + + //t = 0 ; + //while (t>= -_l) + { + ip4.set_i( ip3.get_i() + b*h ); + ip4.set_j( ip3.get_j() - a*h ); + + if (lg > 2*vpImagePoint::distance(ip2, ip4) ) { + MoveToEx(hDCMem, vpMath::round(ip2.get_u()), vpMath::round(ip2.get_v()), NULL); + LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v())); + } + + // t-=0.1 ; + } MoveToEx(hDCMem, vpMath::round(ip1.get_u()), vpMath::round(ip1.get_v()), NULL); LineTo(hDCMem, vpMath::round(ip2.get_u()), vpMath::round(ip2.get_v())); diff --git a/src/device/display/windows/vpGDIRenderer.h b/src/device/display/windows/vpGDIRenderer.h index 9db8183391ea25cfee45f856f473e2260ea30d47..ca9631dd583caa108305a1318c4cf0c8e11e661d 100755 --- a/src/device/display/windows/vpGDIRenderer.h +++ b/src/device/display/windows/vpGDIRenderer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpGDIRenderer.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpGDIRenderer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/display/windows/vpWin32API.cpp b/src/device/display/windows/vpWin32API.cpp index a1342300a894b2f14ceec7214da9f918c063e683..6580fedffbfb38b9a4e2b0de73cbd3b10d23b461 100644 --- a/src/device/display/windows/vpWin32API.cpp +++ b/src/device/display/windows/vpWin32API.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWin32API.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpWin32API.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -108,11 +108,12 @@ void vpSelectObject(HWND hWnd, HDC hDC, HDC hDCMem, HGDIOBJ h){ BOOL vpReleaseSemaphore(HANDLE hSemaphore,LONG IReleaseCount,LPLONG lpPreviousCount){ BOOL ret = ReleaseSemaphore(hSemaphore,IReleaseCount,lpPreviousCount); +#ifndef __MINGW32__ if(ret==0){ vpProcessErrors("ReleaseSemaphore"); } +#endif return ret; - } void vpEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection){ diff --git a/src/device/display/windows/vpWin32API.h b/src/device/display/windows/vpWin32API.h index e5fcb86b0ed534649bcaf1cdb6762acdbedebd7f..9dfd7575ecd8901b3955e85b3aa60e8fd1bcb796 100644 --- a/src/device/display/windows/vpWin32API.h +++ b/src/device/display/windows/vpWin32API.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWin32API.h 4058 2013-01-06 14:59:58Z fspindle $ + * $Id: vpWin32API.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/display/windows/vpWin32Renderer.h b/src/device/display/windows/vpWin32Renderer.h index 2dee8936eb6fbd35e5cd0555ec4c45913d15decb..5602b09f100039e6463c5b3cd39ddebee6abc6a4 100755 --- a/src/device/display/windows/vpWin32Renderer.h +++ b/src/device/display/windows/vpWin32Renderer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWin32Renderer.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpWin32Renderer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/display/windows/vpWin32Window.cpp b/src/device/display/windows/vpWin32Window.cpp index f6ea76b8537a1e03f1c58a6324c84e217d6a958d..700e088df1ed81177a0222117131cdbfc6504210 100755 --- a/src/device/display/windows/vpWin32Window.cpp +++ b/src/device/display/windows/vpWin32Window.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWin32Window.cpp 4304 2013-07-04 14:29:24Z fspindle $ + * $Id: vpWin32Window.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,11 @@ //declares the window as thread local //allows multiple displays +#ifdef __MINGW32__ +vpWin32Window * window; +#else _declspec(thread) vpWin32Window * window; +#endif bool vpWin32Window::registered = false; /*! diff --git a/src/device/display/windows/vpWin32Window.h b/src/device/display/windows/vpWin32Window.h index 7a609e3f4bf624aee48b42251c5431231352100d..a3ea37c46070700e7ef94f25690deda5aaee91f6 100755 --- a/src/device/display/windows/vpWin32Window.h +++ b/src/device/display/windows/vpWin32Window.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWin32Window.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpWin32Window.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/1394/vp1394CMUGrabber.cpp b/src/device/framegrabber/1394/vp1394CMUGrabber.cpp index 2529bccfb413c465930dcb3115b4cdd48f0b7cd0..0e7dac0a86d55748fea32982ffea58dca336f504 100644 --- a/src/device/framegrabber/1394/vp1394CMUGrabber.cpp +++ b/src/device/framegrabber/1394/vp1394CMUGrabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vp1394CMUGrabber.cpp 4204 2013-04-10 16:51:46Z fspindle $ + * $Id: vp1394CMUGrabber.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/1394/vp1394CMUGrabber.h b/src/device/framegrabber/1394/vp1394CMUGrabber.h index cb45e1d272e4ac931999a3b3048c6fee41d96642..c34a837f81ee0c7347832b8e9f3ed2588c8e4b28 100644 --- a/src/device/framegrabber/1394/vp1394CMUGrabber.h +++ b/src/device/framegrabber/1394/vp1394CMUGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vp1394CMUGrabber.h 4216 2013-04-17 09:06:18Z fspindle $ + * $Id: vp1394CMUGrabber.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/1394/vp1394Grabber.cpp b/src/device/framegrabber/1394/vp1394Grabber.cpp deleted file mode 100644 index 7312e954440edc11ac06ca01591c7cf590f9fcdd..0000000000000000000000000000000000000000 --- a/src/device/framegrabber/1394/vp1394Grabber.cpp +++ /dev/null @@ -1,2394 +0,0 @@ -/**************************************************************************** - * - * $Id: vp1394Grabber.cpp 4056 2013-01-05 13:04:42Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Firewire cameras video capture. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ - - -/*! - \file vp1394Grabber.cpp - \brief member functions for firewire cameras - \ingroup libdevice -*/ -#include <iostream> - -#include <visp/vpConfig.h> - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -#if defined(VISP_HAVE_DC1394_1) - - - -#include <visp/vp1394Grabber.h> -#include <visp/vpFrameGrabberException.h> -#include <visp/vpImageIo.h> -#include <visp/vpImageConvert.h> - -const int vp1394Grabber::DROP_FRAMES = 1; /*!< With libdc1394-1.0.0 or more rencent versions, DROP_FRAMES has to be set to 1 in order to suppress the latency. */ -const int vp1394Grabber::NUM_BUFFERS = 8; /*!< Number of buffers */ -const int vp1394Grabber::MAX_PORTS = 4; /*!< Maximal number of ports */ -const int vp1394Grabber::MAX_CAMERAS = 8; /*!< Maximal number of cameras */ - -const char * vp1394Grabber::strFormats[NUM_FORMATS]= { - "Format_0", - "Format_1", - "Format_2", - "Reserved", // 3 reserved formats - "Reserved", - "Reserved", - "Format_6", - "Format_7" -}; - -const char * vp1394Grabber::strModesInFormat0[NUM_FORMAT0_MODES]= { - "Format_0, Mode_0: 160x120 YUV (4:4:4)", - "Format_0, Mode_1: 320x240 YUV (4:2:2)", - "Format_0, Mode_2: 640x480 YUV (4:1:1)", - "Format_0, Mode_3: 640x480 YUV (4:2:2)", - "Format_0, Mode_4: 640x480 RGB 24bpp", - "Format_0, Mode_5: 640x480 Mono 8bpp", - "Format_0, Mode_6: 640x480 Mono 16bpp" -}; - -const char * vp1394Grabber::strModesInFormat1[NUM_FORMAT1_MODES]= { - "Format_1, Mode_0: 800x600 YUV (4:2:2)", - "Format_1, Mode_1: 800x600 RGB 24bpp", - "Format_1, Mode_2: 800x600 Mono 8bpp", - "Format_1, Mode_3: 1024x768 YUV (4:2:2)", - "Format_1, Mode_4: 1024x768 RGB 24bpp", - "Format_1, Mode_5: 1024x768 Mono 8bpp", - "Format_1, Mode_6: 800x600 Mono 16bpp", - "Format_1, Mode_7: 1024x768 Mono 16bpp" -}; - -const char * vp1394Grabber::strModesInFormat2[NUM_FORMAT2_MODES]= { - "Format_2, Mode_0: 1280x960 YUV (4:2:2)", - "Format_2, Mode_1: 1280x960 RGB 24bpp", - "Format_2, Mode_2: 1280x960 Mono 8bpp", - "Format_2, Mode_3: 1600x1200 YUV (4:2:2)", - "Format_2, Mode_4: 1600x1200 RGB 24bpp", - "Format_2, Mode_5: 1600x1200 Mono 8bpp", - "Format_2, Mode_6: 1280x960 Mono 16bpp", - "Format_2, Mode_7: 1600x1200 Mono 16bpp" -}; - -const char * vp1394Grabber::strModesInFormat6[NUM_FORMAT6_MODES]= { - "Format6, Mode_0: EXIT (not supported)" -}; - -const char * vp1394Grabber::strModesInFormat7[NUM_MODE_FORMAT7]= { - "Format_7, Mode_0", - "Format_7, Mode_1", - "Format_7, Mode_2", - "Format_7, Mode_3", - "Format_7, Mode_4", - "Format_7, Mode_5", - "Format_7, Mode_6", - "Format_7, Mode_7" -}; - -const char * vp1394Grabber::strColorsInFormat7[NUM_COLOR_FORMAT7] = { - "Format_7 Color, Mono 8bpp", - "Format_7 Color, YUV 4:1:1", - "Format_7 Color, YUV 4:2:2", - "Format_7 Color, YUV 4:4:4", - "Format_7 Color, RGB 24bpp", - "Format_7 Color, Mono 16bpp", - "Format_7 Color, RGB 48bpp" -}; - -const char * vp1394Grabber::strFramerates[NUM_FRAMERATES] = { - "1.875 Hz", - "3.75 Hz", - "7.5 Hz", - "15 Hz", - "30 Hz", - "60 Hz" -}; - -/*! - \deprecated This class is deprecated. You shoud use - vp1394TwoGrabber class instead. - - Default constructor. Using this constructor, you have explicitly to call - open(). - - By default: - - the camera is the first found on the bus, - - Current camera settings can be changed using setCamera(), - setFormat(), setMode() or setFramerate() after a call to open(). The - list of supported formats, modes and framerates is available using - respectively getFormatSupported(), getModeSupported(), - getFramerateSupported(). - - \code - vpImage<unsigned char> I; - vp1394Grabber g; - g.open(I); - g.setFormat(FORMAT_VGA_NONCOMPRESSED); // Format_0 - g.setMode(MODE_640x480_MONO); // Mode 5 - g.setFramerate(FRAMERATE_15); // 15 fps - \endcode - - \sa open(), setCamera(), setFormat(), setMode(), setFramerate() - -*/ -vp1394Grabber::vp1394Grabber( ) -{ - sprintf(device_name, "/dev/video1394/0"); - - // allocations - handles = new raw1394handle_t [vp1394Grabber::MAX_CAMERAS]; - cameras = new dc1394_cameracapture [vp1394Grabber::MAX_CAMERAS]; - cam_count = new int [vp1394Grabber::MAX_CAMERAS]; - pformat = new int [vp1394Grabber::MAX_CAMERAS]; - pmode = new int [vp1394Grabber::MAX_CAMERAS]; - pframerate = new int [vp1394Grabber::MAX_CAMERAS]; - _width = new int [vp1394Grabber::MAX_CAMERAS]; - _height = new int [vp1394Grabber::MAX_CAMERAS]; - image_format = new vp1394ImageFormatType [vp1394Grabber::MAX_CAMERAS]; - - // Camera settings initialisation - for (int i=0; i < vp1394Grabber::MAX_CAMERAS; i ++) { - pformat[i] = 0; - pmode[i] = 0; - pframerate[i] = 0; - } - verbose = false; - - iso_transmission_started = false; - handle_created = false; - camera_found = false; - dma_started = false; - num_cameras = 0; - camera = 0; // the first camera on the bus - - // Image settings - for (int i=0; i < vp1394Grabber::MAX_CAMERAS; i ++) { - _width[i] = 0; - _height[i] = 0; - image_format[i] = RGB; - } - - init = false ; -} - -/*! - - \deprecated This class is deprecated. You shoud use - vp1394TwoGrabber class instead. - - Constructor which initialize the grabber and call the open() method. - - \param I : Image data structure (8 bits image) - - By default: - - the camera is the first found on the bus, - - Current camera settings can be changed using setCamera(), - setFormat(), setMode() or setFramerate() after a call to open(). The - list of supported formats, modes and framerates is available using - respectively getFormatSupported(), getModeSupported(), - getFramerateSupported(). - - \code - vpImage<unsigned char> I; - vp1394Grabber g(I); - g.setFormat(FORMAT_VGA_NONCOMPRESSED); // Format_0 - g.setMode(MODE_640x480_MONO); // Mode 5 - g.setFramerate(FRAMERATE_15); // 15 fps - \endcode - - \sa setCamera(), setFormat(), setMode(), setFramerate() -*/ -vp1394Grabber::vp1394Grabber(vpImage<unsigned char> &I) -{ - sprintf(device_name, "/dev/video1394/0"); - - // allocations - handles = new raw1394handle_t [vp1394Grabber::MAX_CAMERAS]; - cameras = new dc1394_cameracapture [vp1394Grabber::MAX_CAMERAS]; - cam_count = new int [vp1394Grabber::MAX_CAMERAS]; - pformat = new int [vp1394Grabber::MAX_CAMERAS]; - pmode = new int [vp1394Grabber::MAX_CAMERAS]; - pframerate = new int [vp1394Grabber::MAX_CAMERAS]; - _width = new int [vp1394Grabber::MAX_CAMERAS]; - _height = new int [vp1394Grabber::MAX_CAMERAS]; - image_format = new vp1394ImageFormatType [vp1394Grabber::MAX_CAMERAS]; - - // Camera settings - for (int i=0; i < vp1394Grabber::MAX_CAMERAS; i ++) { - pformat[i] = 0; - pmode[i] = 0; - pframerate[i] = 0; - } - verbose = false; - - iso_transmission_started = false; - handle_created = false; - camera_found = false; - num_cameras = 0; - camera = 0; // the first camera on the bus - - // Image settings - for (int i=0; i < vp1394Grabber::MAX_CAMERAS; i ++) { - _width[i] = 0; - _height[i] = 0; - image_format[i] = RGB; - } - - init = false ; - - open ( I ) ; -} - -/*! - - Destructor. - - Stops the iso transmission and close the device. - - \sa close() - -*/ -vp1394Grabber::~vp1394Grabber() -{ - close(); -} - -/*! - - If multiples cameras are connected on the bus, select the camero to dial - with. - - \param camera : A camera. The value must be comprised between 0 (the first - camera) and the number of cameras found on the bus and returned by - getNumCameras(). If two cameras are connected on the bus, setting \e camera - to one allows to communicate with the second one. - - \exception vpFrameGrabberException::settingError : If the required camera is - not reachable. - - \warning Has to be called after open() to be sure that a camera is detected. - - \code - vpImage<unsigned char> I; - vp1394Grabber g; - g.open(I); - unsigned int num_cameras; // Number of cameras on the bus - g.getNumCameras(num_cameras); - g.setCamera(num_cameras-1); // To dial with the last camera on the bus - g.acquire(I); // I contains the frame captured by the last camera on the bus - \endcode - - \sa setFormat(), setMode(), setFramerate(), open(), getNumCameras() - -*/ -void -vp1394Grabber::setCamera(unsigned int camera) -{ - if (camera >= num_cameras) { - close(); - vpERROR_TRACE("The required camera is not present"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "The required camera is not present") ); - } - - this->camera = camera; - -} -/*! - - Get the active camera on the bus. - - \param camera : A camera id. The value is comprised between 0 (the first - camera) and the number of cameras found on the bus and returned by - getNumCameras(). - - \sa setCamera(), getNumCameras() - -*/ -void -vp1394Grabber::getCamera(unsigned int &camera) -{ - camera = this->camera; - -} -/*! - - Set the capture format for a given camera on the bus. To set the format for a - specific camera, call setCamera() before. - - \param format : The camera image format. The current camera format is given by - getFormat(). The camera supported formats are given by - getFormatSupported(). The allowed values given in - libdc1394/dc1394_control.h are: - - FORMAT_VGA_NONCOMPRESSED for the Format_0 - - FORMAT_SVGA_NONCOMPRESSED_1 for the Format_1 - - FORMAT_SVGA_NONCOMPRESSED_2 for the Format_2 - - FORMAT_STILL_IMAGE for the Format_6 - - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - - \warning The requested format is sent to the camera only after a call to - close(). Depending on the format and the camera mode, image size can differ. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa setMode(), setFramerate(), open(), setCamera(), getNumCameras() - -*/ -void -vp1394Grabber::setFormat(int format) -{ - if (iso_transmission_started == true) { - - stopIsoTransmission(); - - for (unsigned int i=0; i < num_cameras; i++) - dc1394_dma_unlisten( handles[i], &cameras[i] ); - iso_transmission_started = false; - } - - this->pformat[camera] = format; - - setup(); - startIsoTransmission(); -} - -/*! - - Query the actual capture format of a given camera. The camera supported - formats are given by getFormatSupported(). - - \warning Before requerying the actual format a handle must - be created by calling open(), and a camera must be connected. - - \param format : The camera capture format (see file - libdc1394/dc1394_control.h), either : - - - FORMAT_VGA_NONCOMPRESSED for the Format_0 - - FORMAT_SVGA_NONCOMPRESSED_1 for the Format_1 - - FORMAT_SVGA_NONCOMPRESSED_2 for the Format_2 - - FORMAT_STILL_IMAGE for the Format_6 - - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - - \exception vpFrameGrabberException::settingError : If the required camera is - not present or if an error occurs. - - \sa setFormat(), getFormatSupported(), open(), setCamera(), getNumCameras() - -*/ -void -vp1394Grabber::getFormat(int & format) -{ - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - if(dc1394_get_camera_misc_info(handles[camera], - cameras[camera].node, - &miscinfo) !=DC1394_SUCCESS) { - vpERROR_TRACE("Unable to get misc info"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get misc info") ); - } - - format = miscinfo.format; -} - -/*! - - Query the available camera image formats. - - \warning Before requerying supported formats a handle must be created by - calling open(), and a camera must be connected. - - \param formats : The list of supported camera image formats. - - \return The number of supported camera image formats, 0 if an error occurs. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present or if an error occurs. - - \sa getModeSupported(), getFramerateSupported(), open(), - getFormat(), setCamera() - -*/ -int -vp1394Grabber::getFormatSupported(vpList<int> & formats) -{ - int nb = 0; // Number of supported formats - - // Refresh the list of supported formats - formats.kill(); - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - quadlet_t value; - if (dc1394_query_supported_formats(handles[camera], - cameras[camera].node, - &value) != DC1394_SUCCESS) { - vpERROR_TRACE("Could not query supported formats"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Could not query supported formats") ); - } - - if (value & (0x1<<31)) { - formats.addRight(FORMAT_VGA_NONCOMPRESSED); - nb ++; - } - if (value & (0x1<<30)) { - formats.addRight(FORMAT_SVGA_NONCOMPRESSED_1); - nb ++; - } - if (value & (0x1<<29)) { - formats.addRight(FORMAT_SVGA_NONCOMPRESSED_2); - nb ++; - } - if (value & (0x1<<25)) { - formats.addRight(FORMAT_STILL_IMAGE); - nb ++; - } - if (value & (0x1<<24)) { - formats.addRight(FORMAT_SCALABLE_IMAGE_SIZE); - nb ++; - } - - return nb; -} - -/*! - - Set the camera capture mode for a given camera on the bus. - - \param mode : The camera capture mode. The current camera mode is - given by getMode(). The camera supported modes are given by - getSupportedModes(). The allowed values are given in - libdc1394/dc1394_control.h file: - - - Allowed mode for Format 0: MODE_160x120_YUV444 = 64, MODE_320x240_YUV422, MODE_640x480_YUV411, MODE_640x480_YUV422, MODE_640x480_RGB, MODE_640x480_MONO, MODE_640x480_MONO16 - - - Allowed modes for Format 1: MODE_800x600_YUV422 = 96, MODE_800x600_RGB, MODE_800x600_MONO, MODE_1024x768_YUV422, MODE_1024x768_RGB, MODE_1024x768_MONO, MODE_800x600_MONO16, MODE_1024x768_MONO16 - - - Allowed modes for Format 2: MODE_1280x960_YUV422 = 128, MODE_1280x960_RGB, MODE_1280x960_MONO, MODE_1600x1200_YUV422, MODE_1600x1200_RGB, MODE_1600x1200_MONO, MODE_1280x960_MONO16, MODE_1600x1200_MONO16 - - - Allowed modes for Format 6: MODE_EXIF= 256 - - - Allowed modes for Format 7: MODE_FORMAT7_0= 288, MODE_FORMAT7_1, MODE_FORMAT7_2, MODE_FORMAT7_3, MODE_FORMAT7_4, MODE_FORMAT7_5, MODE_FORMAT7_6, MODE_FORMAT7_7 - - - All these modes are not supported by your camera. The camera supported modes - are given by getModeSupported(). - - \warning The requested format is sent to the camera only after a call to - close(). Depending on the format and the camera mode, image size can differ. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa setFormat(), setFramerate(), open(), getNumCameras(), setCamera() - -*/ -void -vp1394Grabber::setMode(int mode) -{ - if (iso_transmission_started == true) { - - stopIsoTransmission(); - - for (unsigned int i=0; i < num_cameras; i++) - dc1394_dma_unlisten( handles[i], &cameras[i] ); - iso_transmission_started = false; - } - - this->pmode[camera] = mode; - - setup(); - startIsoTransmission(); -} - -/*! - - Query the actual capture mode of a given camera. Given a format, all - the camera supported modes are given by getModeSupported(). - - \warning Before requerying the actual mode a handle must - be created by calling open(), and a camera must be connected. - - \param mode : The camera capture mode. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \sa setMode(), getModeSupported(), open(), setCamera() - -*/ -void -vp1394Grabber::getMode(int & mode) -{ - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - if(dc1394_get_camera_misc_info(handles[camera], - cameras[camera].node, - &miscinfo) !=DC1394_SUCCESS) { - vpERROR_TRACE("Unable to get misc info"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get misc info") ); - } - - mode = miscinfo.mode; -} - -/*! - - Query the available camera modes for the given format. - - \warning Before requerying supported modes for a given format a handle must - be created by calling open(), and a camera must be connected. - - \param format : Camera image format. Values for format are parts of the list - (see file libdc1394/dc1394_control.h): - - - FORMAT_VGA_NONCOMPRESSED = 384 for the Format_0 - - FORMAT_SVGA_NONCOMPRESSED_1 for the Format_1 - - FORMAT_SVGA_NONCOMPRESSED_2 for the Format_2 - - FORMAT_STILL_IMAGE = 390 for the Format_6 - - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - - \param modes : The list of supported camera modes. - - \return The number of supported camera modes, 0 if an error occurs. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \sa getMode(), getFramerateSupported(), open(), setCamera() -*/ -int -vp1394Grabber::getModeSupported(int format, vpList<int> & modes) -{ - int nb = 0; // Number of supported modes - - // Refresh the list of supported modes - modes.kill(); - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - quadlet_t value; - if (dc1394_query_supported_modes(handles[camera], - cameras[camera].node, - format, &value) != DC1394_SUCCESS) { - vpERROR_TRACE("Could not query supported modes for format %d\n", format); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Could not query supported mode") ); - } - - switch (format) { - case FORMAT_VGA_NONCOMPRESSED: - for (int m = MODE_FORMAT0_MIN; m <= MODE_FORMAT0_MAX; m ++) { - if (value & (0x1<<(31-(m-MODE_FORMAT0_MIN)))) { - modes.addRight(m); - nb ++; - } - } - break; - - case FORMAT_SVGA_NONCOMPRESSED_1: - for (int m = MODE_FORMAT1_MIN; m <= MODE_FORMAT1_MAX; m ++) { - if (value & (0x1<<(31-(m-MODE_FORMAT1_MIN)))) { - modes.addRight(m); - nb ++; - } - } - break; - - case FORMAT_SVGA_NONCOMPRESSED_2: - for (int m = MODE_FORMAT2_MIN; m <= MODE_FORMAT2_MAX; m ++) { - if (value & (0x1<<(31-(m-MODE_FORMAT2_MIN)))) { - modes.addRight(m); - nb ++; - } - } - break; - - case FORMAT_STILL_IMAGE: - for (int m = MODE_FORMAT6_MIN; m <= MODE_FORMAT6_MAX; m ++) { - if (value & (0x1<<(31-(m-MODE_FORMAT6_MIN)))) { - modes.addRight(m); - nb ++; - } - } - break; - - case FORMAT_SCALABLE_IMAGE_SIZE: - for (int m = MODE_FORMAT7_MIN; m <= MODE_FORMAT7_MAX; m ++) { - if (value & (0x1<<(31-(m-MODE_FORMAT7_MIN)))) { - modes.addRight(m); - nb ++; - } - } - break; - } - - return nb; -} - -/*! - - Set the capture framerate for a given camera on the bus. - - \param framerate : The camera framerate. The current framerate of the camera - is given by getFramerate(). The camera supported framerates are given by - getFramerateSupported(). The allowed values given in - libdc1394/dc1394_control.h are: - - - FRAMERATE_1_875 = 32 for 1.875 fps, - - FRAMERATE_3_75 for 3.75 fps, - - FRAMERATE_7_5 for 7.5 fps, - - FRAMERATE_15 for 15 fps, - - FRAMERATE_30 for 30 fps, - - FRAMERATE_60 for 60 fps, - - FRAMERATE_120 for 120 fps, - - FRAMERATE_240 for 240 fps. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa setFormat(), setMode(), open(), getNumCameras(), setCamera() - -*/ -void -vp1394Grabber::setFramerate(int framerate) -{ - if (iso_transmission_started == true) { - - stopIsoTransmission(); - - for (unsigned int i=0; i < num_cameras; i++) - dc1394_dma_unlisten( handles[i], &cameras[i] ); - iso_transmission_started = false; - } - - this->pframerate[camera] = framerate; - - setup(); - startIsoTransmission(); -} - -/*! - - Query the actual capture framerate of a given camera. The camera supported - framerates are given by getFramerateSupported(). - - \warning Before requerying the actual framerate a handle must - be created by calling open(), and a camera must be connected. - - \param framerate : The camera capture framerate. - - \return true if the framerate was obtained, false if an error occurs. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \sa setFramerate(), getFramerateSupported(), open(), getNumCameras(), - setCamera() - -*/ -void -vp1394Grabber::getFramerate(int & framerate) -{ - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - if(dc1394_get_camera_misc_info(handles[camera], - cameras[camera].node, - &miscinfo) !=DC1394_SUCCESS) { - vpERROR_TRACE("Unable to get misc info"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get misc info") ); - } - - framerate = miscinfo.framerate; -} - -/*! - - Query the available framerates for the given camera image format and mode - (see file libdc1394/dc1394_control.h). The framerate is only available if - format is either FORMAT_VGA_NONCOMPRESSED (format 0), - FORMAT_SVGA_NONCOMPRESSED_1 (format 1) or FORMAT_SVGA_NONCOMPRESSED_2 (format - 2). If format is equal to FORMAT_STILL_IMAGE (format 6) or - FORMAT_SCALABLE_IMAGE_SIZE (format 7), the framerate is not available. In - this case, we return 0. - - \warning Before requerying supported framerates for a given format and mode a - handle must be created by calling open(), and a camera must be connected. - - \param format : Camera image format. - \param mode : Camera mode. - \param framerates ; The list of supported camera framerates. - - \return The number of supported camera image framerates, 0 if an error - occurs. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \sa getFormatSupported(), getModeSupported(), open(), getFramerate(), - setCamera() -*/ -int -vp1394Grabber::getFramerateSupported(int format, int mode, - vpList<int> & framerates) -{ - int nb = 0; // Number of supported framerates - - // Refresh the list of supported framerates - framerates.kill(); - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - switch(format) { - case FORMAT_VGA_NONCOMPRESSED: - case FORMAT_SVGA_NONCOMPRESSED_1: - case FORMAT_SVGA_NONCOMPRESSED_2: - { - - quadlet_t value; - if (dc1394_query_supported_framerates(handles[camera], - cameras[camera].node, - format, mode, - &value) != DC1394_SUCCESS) { - vpERROR_TRACE("Could not query supported frametates for format %d\n" - "and mode %d\n", format, mode); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Could not query supported framerates") ); - } - - for (int f = FRAMERATE_MIN; f <= FRAMERATE_MAX; f ++) { - if (value & (0x1<<(31-(f-FRAMERATE_MIN)))) { - framerates.addRight(f); - nb ++; - } - } - } - break; - default: - // Framerate not available for: - // - FORMAT_STILL_IMAGE for the Format_6 - // - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - return 0; - } - - return nb; -} - -/*! - - Converts the string containing the description of the format into the format - dentifier. - - \param format : The string describing the format given by Format(int) - - \return The camera capture format identifier, either : - - FORMAT_VGA_NONCOMPRESSED for the Format_0 - - FORMAT_SVGA_NONCOMPRESSED_1 for the Format_1 - - FORMAT_SVGA_NONCOMPRESSED_2 for the Format_2 - - FORMAT_STILL_IMAGE for the Format_6 - - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - . - This method returns 0 if the string does not match to a format string. - - \sa convertFormat(int), convertMode(std::string), convertFramerate(std::string&) - -*/ -int vp1394Grabber::convertFormat(std::string format) -{ - for (int i = FORMAT_MIN; i <= FORMAT_MAX; i ++) { - // if (format.compare(0, format(i).length(), format(i)) == 0) { - if (format.compare(0, convertFormat(i).length(), convertFormat(i)) == 0) { - return i; - } - }; - - return 0; -} -/*! - - Converts the string containing the description of the mode into the mode - dentifier. - - \param mode : The string describing the mode given by Mode(int) - - \return The camera capture mode identifier. - - This method returns 0 if the string does not match to a mode string. - - \sa convertMode(int), convertFormat(std::string), convertFramerate(std::string) - -*/ -int vp1394Grabber::convertMode(std::string mode) -{ - for (int i = MODE_FORMAT0_MIN; i <= MODE_FORMAT0_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - for (int i = MODE_FORMAT1_MIN; i <= MODE_FORMAT1_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - for (int i = MODE_FORMAT2_MIN; i <= MODE_FORMAT2_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - for (int i = MODE_FORMAT6_MIN; i <= MODE_FORMAT6_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - for (int i = MODE_FORMAT7_MIN; i <= MODE_FORMAT7_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - for (int i = COLOR_FORMAT7_MIN; i <= COLOR_FORMAT7_MAX; i ++) { - if (mode.compare(convertMode(i)) == 0) - return i; - }; - - return 0; -} -/*! - - Converts the string containing the description of the framerate into the - framerate dentifier. - - \param framerate : The string describing the framerate given by - Framerate(int). - - \return The camera capture framerate identifier. - - This method returns 0 if the string does not match to a framerate string. - - \sa convertFramerate(int), convertFormat(std::string &), convertMode(std::string) - -*/ -int vp1394Grabber::convertFramerate(std::string framerate) -{ - for (int i = FRAMERATE_MIN; i <= FRAMERATE_MAX; i ++) { - if (framerate.compare(convertFramerate(i)) == 0) - return i; - }; - - return 0; -} - - -/*! - - Converts the format identifier into a string containing the description of - the format. - - \param format : The camera capture format, either : - - FORMAT_VGA_NONCOMPRESSED for the Format_0 - - FORMAT_SVGA_NONCOMPRESSED_1 for the Format_1 - - FORMAT_SVGA_NONCOMPRESSED_2 for the Format_2 - - FORMAT_STILL_IMAGE for the Format_6 - - FORMAT_SCALABLE_IMAGE_SIZE for the Format_7 - - \return A string describing the format, an empty string if the format is not - supported. - - \sa convertMode(), convertFramerate() - -*/ -std::string vp1394Grabber::convertFormat(int format) -{ - std::string _format; - if ((format >= FORMAT_MIN) && (format <= FORMAT_MAX)) { - switch (format) { - case FORMAT_VGA_NONCOMPRESSED: _format = strFormats[0]; break; - case FORMAT_SVGA_NONCOMPRESSED_1: _format = strFormats[1]; break; - case FORMAT_SVGA_NONCOMPRESSED_2: _format = strFormats[2]; break; - // 3 reserved formats - case FORMAT_SVGA_NONCOMPRESSED_2 + 1: _format = strFormats[3]; break; - case FORMAT_SVGA_NONCOMPRESSED_2 + 2: _format = strFormats[4]; break; - case FORMAT_SVGA_NONCOMPRESSED_2 + 3: _format = strFormats[5]; break; - - case FORMAT_STILL_IMAGE: _format = strFormats[6]; break; - case FORMAT_SCALABLE_IMAGE_SIZE: _format = strFormats[7]; break; - default: - break; - } - } - else { - std::cout << "The format " << format - << " is not supported by the camera" << std::endl; - _format = "Not Valid"; - } - - return _format; -} -/*! - - Converts the mode identifier into a string containing the description of the - mode. - - \param mode : The camera capture mode. - - \return A string describing the mode, an empty string if the mode is not - supported. - - \sa convertFormat(), convertFramerate() - -*/ -std::string vp1394Grabber::convertMode(int mode) -{ - std::string _mode; - - if ((mode >= MODE_FORMAT0_MIN) && (mode <= MODE_FORMAT0_MAX)) { - switch (mode) { - case MODE_160x120_YUV444: _mode = strModesInFormat0[0]; break; - case MODE_320x240_YUV422: _mode = strModesInFormat0[1]; break; - case MODE_640x480_YUV411: _mode = strModesInFormat0[2]; break; - case MODE_640x480_YUV422: _mode = strModesInFormat0[3]; break; - case MODE_640x480_RGB: _mode = strModesInFormat0[4]; break; - case MODE_640x480_MONO: _mode = strModesInFormat0[5]; break; - case MODE_640x480_MONO16: _mode = strModesInFormat0[6]; break; - default: break; - } - } - else if ((mode >= MODE_FORMAT1_MIN) && (mode <= MODE_FORMAT1_MAX)) { - switch (mode) { - case MODE_800x600_YUV422: _mode = strModesInFormat1[0]; break; - case MODE_800x600_RGB: _mode = strModesInFormat1[1]; break; - case MODE_800x600_MONO: _mode = strModesInFormat1[2]; break; - case MODE_1024x768_YUV422: _mode = strModesInFormat1[3]; break; - case MODE_1024x768_RGB: _mode = strModesInFormat1[4]; break; - case MODE_1024x768_MONO: _mode = strModesInFormat1[5]; break; - case MODE_800x600_MONO16: _mode = strModesInFormat1[6]; break; - case MODE_1024x768_MONO16: _mode = strModesInFormat1[7]; break; - default: break; - } - } - else if ((mode >= MODE_FORMAT2_MIN) && (mode <= MODE_FORMAT2_MAX)) { - switch (mode) { - case MODE_1280x960_YUV422: _mode = strModesInFormat2[0]; break; - case MODE_1280x960_RGB: _mode = strModesInFormat2[1]; break; - case MODE_1280x960_MONO: _mode = strModesInFormat2[2]; break; - case MODE_1600x1200_YUV422: _mode = strModesInFormat2[3]; break; - case MODE_1600x1200_RGB: _mode = strModesInFormat2[4]; break; - case MODE_1600x1200_MONO: _mode = strModesInFormat2[5]; break; - case MODE_1280x960_MONO16: _mode = strModesInFormat2[6]; break; - case MODE_1600x1200_MONO16: _mode = strModesInFormat2[7]; break; - default: break; - } - } - else if ((mode >= MODE_FORMAT6_MIN) && (mode <= MODE_FORMAT6_MAX)) { - switch (mode) { - case MODE_EXIF: _mode = strModesInFormat6[0]; break; - default: break; - } - } - else if ((mode >= MODE_FORMAT7_MIN) && (mode <= MODE_FORMAT7_MAX)) { - switch (mode) { - case MODE_FORMAT7_0: _mode = strModesInFormat7[0]; break; - case MODE_FORMAT7_1: _mode = strModesInFormat7[1]; break; - case MODE_FORMAT7_2: _mode = strModesInFormat7[2]; break; - case MODE_FORMAT7_3: _mode = strModesInFormat7[3]; break; - case MODE_FORMAT7_4: _mode = strModesInFormat7[4]; break; - case MODE_FORMAT7_5: _mode = strModesInFormat7[5]; break; - case MODE_FORMAT7_6: _mode = strModesInFormat7[6]; break; - case MODE_FORMAT7_7: _mode = strModesInFormat7[7]; break; - default: break; - } - } - else if ((mode >= COLOR_FORMAT7_MIN) && (mode <= COLOR_FORMAT7_MAX)) { - switch (mode) { - case COLOR_FORMAT7_MONO8: _mode = strColorsInFormat7[0]; break; - case COLOR_FORMAT7_YUV411: _mode = strColorsInFormat7[1]; break; - case COLOR_FORMAT7_YUV422: _mode = strColorsInFormat7[2]; break; - case COLOR_FORMAT7_YUV444: _mode = strColorsInFormat7[3]; break; - case COLOR_FORMAT7_RGB8: _mode = strColorsInFormat7[4]; break; - case COLOR_FORMAT7_MONO16: _mode = strColorsInFormat7[5]; break; - case COLOR_FORMAT7_RGB16: _mode = strColorsInFormat7[6]; break; - default: break; - } - } - else { - std::cout << "The mode " << mode << " is not supported by the camera" << std::endl; - _mode = "Not valid"; - } - - return _mode; -} - -/*! - - Converts the framerate identifier into a string - containing the description of the framerate. - - \param framerate : The camera capture framerate. - - \return A string describing the framerate, an empty string if the framerate - is not supported. - - \sa convertFormat(), convertMode() - -*/ -std::string vp1394Grabber::convertFramerate(int framerate) -{ - std::string _framerate; - - if ((framerate >= FRAMERATE_MIN) && (framerate <= FRAMERATE_MAX)) { - switch (framerate) { - case FRAMERATE_1_875: _framerate = strFramerates[0]; break; - case FRAMERATE_3_75: _framerate = strFramerates[1]; break; - case FRAMERATE_7_5: _framerate = strFramerates[2]; break; - case FRAMERATE_15: _framerate = strFramerates[3]; break; - case FRAMERATE_30: _framerate = strFramerates[4]; break; - case FRAMERATE_60: _framerate = strFramerates[5]; break; - default: break; - } - } - else { - std::cout << "The framerate " << framerate - << " is not supported by the camera" << std::endl; - _framerate = "Not valid"; - } - - return _framerate; - -} - -/*! - - Set the shutter for a given camera. - - \param shutter : The shutter value to apply to the camera. - - - \exception vpFrameGrabberException::settingError : If the required camera is - not present or if an error occurs. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa getShutter(), open(), getNumCameras(), setCamera() -*/ -void -vp1394Grabber::setShutter(unsigned int shutter) -{ - unsigned int min_shutter = 0; - unsigned int max_shutter = 0; - - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - if(dc1394_get_min_value(handles[camera], - cameras[camera].node, - FEATURE_SHUTTER, &min_shutter) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get min shutter value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get min shutter value") ); - } - - if(dc1394_get_max_value(handles[camera], - cameras[camera].node, - FEATURE_SHUTTER, &max_shutter) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get max shutter value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get max shutter value") ); - } - - if (shutter < min_shutter || shutter > max_shutter) { - vpCERROR << "The requested shutter " << shutter - << " must be comprised between " << min_shutter - << " and " << max_shutter << std::endl; - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Cannot set shutter: bad value") ); - } - - if ( dc1394_set_shutter(handles[camera], - cameras[camera].node, - shutter) != DC1394_SUCCESS) { - vpERROR_TRACE("Unable to set shutter"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Cannot set shutter") ); - } -} - -/*! - - Query the actual shutter value of a given camera and the bounded shutter - values. - - \warning Before requerying the shutter a handle must - be created by calling open(), and a camera must be connected. - - \param min_shutter : Minimal autorized shutter value. - - \param shutter : The current camera shutter value. This value is comprised - between \e min_shutter and \e max_shutter. - - \param max_shutter : Maximal autorized shutter value. - - - \exception vpFrameGrabberException::settingError : If the required camera is - not present or if an error occurs. - - \sa setShutter(), open(), getNumCameras(), setCamera() - -*/ -void -vp1394Grabber::getShutter(unsigned int &min_shutter, - unsigned int &shutter, - unsigned int &max_shutter) -{ - shutter = 0; - min_shutter = 0; - max_shutter = 0; - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - if(dc1394_get_shutter(handles[camera], - cameras[camera].node, - &shutter) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get shutter value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get shutter value") ); - - } - - if(dc1394_get_min_value(handles[camera], - cameras[camera].node, - FEATURE_SHUTTER, &min_shutter) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get min shutter value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get min shutter value") ); - } - - if(dc1394_get_max_value(handles[camera], - cameras[camera].node, - FEATURE_SHUTTER, &max_shutter) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get max shutter value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get max shutter value") ); - } -} - -/*! - - Set the gain for a given camera. - - \warning Before setting the gain a handle must - be created by calling open(), and a camera must be connected. - - \param gain The gain value to apply to the camera. - - \exception vpFrameGrabberException::settingError : If the required camera is not present or if an error occurs. - - \sa getGain(), getNumCameras(), setCamera() -*/ -void -vp1394Grabber::setGain(unsigned int gain) -{ - unsigned int min_gain = 0; - unsigned int max_gain = 0; - - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - if(dc1394_get_min_value(handles[camera], - cameras[camera].node, - FEATURE_GAIN, &min_gain) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get min gain value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get min gain value") ); - } - - if(dc1394_get_max_value(handles[camera], - cameras[camera].node, - FEATURE_GAIN, &max_gain) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get max gain value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get max gain value") ); - } - - if (gain < min_gain || gain > max_gain) { - vpCERROR << "The requested gain " << gain - << " must be comprised between " << min_gain - << " and " << max_gain << std::endl; - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Cannot set shutter: bad value") ); - } - - if ( dc1394_set_gain(handles[camera], - cameras[camera].node, - gain) != DC1394_SUCCESS) { - vpERROR_TRACE("Unable to set gain"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Cannot set gain") ); - } -} - -/*! - - Query the actual gain value of a given camera and the bounded gain - values. - - \warning Before requerying the gain a handle must - be created by calling open(), and a camera must be connected. - - \param min_gain : Minimal autorized gain value. - - \param gain : The current camera gain value. This value is comprised - between \e min_gain and \e max_gain. - - \param max_gain : Maximal autorized gain value. - - \exception vpFrameGrabberException::settingError :If the required camera is - not present or if an error occurs. - - \sa setGain(), open(), getNumCameras(), setCamera() - -*/ -void -vp1394Grabber::getGain(unsigned int &min_gain, - unsigned int &gain, - unsigned int &max_gain) -{ - gain = 0; - min_gain = 0; - max_gain = 0; - - if (handle_created == false) { - close(); - vpERROR_TRACE("To set the shutter the handle must be created"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter the handle must be created") ); - } - if (camera_found == false) { - close(); - vpERROR_TRACE("To set the shutter a camera must be connected"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "To set the shutter a camera must be connected") ); - } - - if(dc1394_get_gain(handles[camera], - cameras[camera].node, - &gain) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get gain value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get gain value") ); - - } - - if(dc1394_get_min_value(handles[camera], - cameras[camera].node, - FEATURE_GAIN, &min_gain) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get min gain value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get min gain value") ); - } - - if(dc1394_get_max_value(handles[camera], - cameras[camera].node, - FEATURE_GAIN, &max_gain) !=DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get max gain value"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "Unable to get max gain value") ); - - } -} - -/*! - - Get the image width. It depends on the camera format setFormat() and mode - setMode(). The width of the images is only available after a call to open(). - - \param width : The image width, zero if the required camera is not available. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa getHeight(), setCamera() - -*/ -void vp1394Grabber::getWidth(unsigned int &width) -{ - if (camera >= num_cameras) { - width = 0; - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "The required camera is not present") ); - } - - width = this->_width[camera]; - this->width = width; -} - -/*! - - Get the image height. It depends on the camera format setFormat() and mode - setMode(). The height of the images is only available after a call to - open(). - - \param height : The image height. - - \exception vpFrameGrabberException::settingError : If the required camera is - not present. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa getWidth(), getImageFormat(), close(), getNumCameras(), setCamera() - -*/ -void vp1394Grabber::getHeight(unsigned int &height) -{ - if (camera >= num_cameras) { - height = 0; - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "The required camera is not present") ); - } - - height = this->_height[camera]; - this->height = height; -} - -/*! - - Query the number of cameras on the bus. - - \param cameras : The number of cameras found on the bus. - - -*/ -void -vp1394Grabber::getNumCameras(unsigned int &cameras) -{ - if (camera_found == false) { - vpCTRACE << "No camera found..."<< std::endl; - cameras = 0; - } - - cameras = num_cameras; -} - - -/*! - Initialize grey level image acquisition - - \param I : Image data structure (8 bits image) - -*/ -void -vp1394Grabber::open(vpImage<unsigned char> &I) -{ - - open(); - - // Get the actual camera format, mode and framerate - getFormat(pformat[camera]); - getMode(pmode[camera]); - getFramerate(pframerate[camera]); - - setup(); - startIsoTransmission(); - - unsigned int w, h; - getWidth( w ) ; - getHeight( h ) ; - - vpDEBUG_TRACE(10, "%d %d", h, w ) ; - - I.resize(h, w) ; - - init = true ; - -} - -/*! - Initialize color image (in RGBa format) acquisition - - \param I : Image data structure (RGBa format) - -*/ -void -vp1394Grabber::open(vpImage<vpRGBa> &I) -{ - - open(); - - // Get the actual camera format, mode and framerate - getFormat(pformat[camera]); - getMode(pmode[camera]); - getFramerate(pframerate[camera]); - - setup(); - startIsoTransmission(); - - unsigned int w, h; - getWidth( w ) ; - getHeight( h ) ; - - vpDEBUG_TRACE(10, "%d %d", h, w ) ; - - I.resize(h, w) ; - - init = true ; - -} - -/*! - Acquire a grey level image from a given camera. - - \param I : Image data structure (8 bits image) - - \exception vpFrameGrabberException::initializationError : If the device is - not openned. - - \sa getField(), setCamera() -*/ -void -vp1394Grabber::acquire(vpImage<unsigned char> &I) -{ - - if (init==false) - { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "Initialization not done") ); - } - - int *bitmap = NULL ; - bitmap = dmaCapture(); - - unsigned int w, h; - getWidth( w ) ; - getHeight( h ) ; - - if ((I.getWidth() != w)||(I.getHeight() != h)) - I.resize(h, w) ; - - int size = I.getHeight()*I.getWidth(); - switch (image_format[camera]) - { - case MONO: - memcpy(I.bitmap, (unsigned char *) bitmap, size*sizeof(unsigned char)); - break; - case YUV411: { - vpImageConvert::YUV411ToGrey( (unsigned char *) bitmap, I.bitmap, size); - break; - } - case YUV422: { - vpImageConvert::YUV422ToGrey( (unsigned char *) bitmap, I.bitmap, size); - break; - } - case RGB: { - vpImageConvert::RGBToGrey((unsigned char *) bitmap, I.bitmap, size); - break; - } - case RGBa: { - vpImageConvert::RGBaToGrey((unsigned char *) bitmap, I.bitmap, size); - break; - } - default: - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Format conversion not implemented. Acquisition failed.") ); - break; - }; - - dmaDoneWithBuffer(); - -} - -/*! - Acquire a color image from a given camera. - - \param I : Image data structure (RGBa image) - - \exception vpFrameGrabberException::initializationError : If the device is - not openned. - - \sa getField(), setCamera() -*/ -void -vp1394Grabber::acquire(vpImage<vpRGBa> &I) -{ - - if (init==false) - { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "Initialization not done") ); - } - - int *bitmap = NULL ; - bitmap = dmaCapture(); - - unsigned int w, h; - getWidth( w ) ; - getHeight( h ) ; - - if ((I.getWidth() != w)||(I.getHeight() != h)) - I.resize(h, w) ; - - int size = I.getHeight()*I.getWidth(); - switch (image_format[camera]) - { - case MONO: - vpImageConvert::GreyToRGBa((unsigned char *) bitmap, - (unsigned char *) I.bitmap, size); - break; - case YUV411: { - vpImageConvert::YUV411ToRGBa( (unsigned char *) bitmap, - (unsigned char *) I.bitmap, size); - break; - } - case YUV422: { - vpImageConvert::YUV422ToRGBa( (unsigned char *) bitmap, - (unsigned char *) I.bitmap, size); - break; - } - case RGB: { - vpImageConvert::RGBToRGBa((unsigned char *) bitmap, - (unsigned char *) I.bitmap, size); - break; - } - case RGBa: { - memcpy((unsigned char *) I.bitmap, (unsigned char *) bitmap, size); - break; - } - default: - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Format conversion not implemented. Acquisition failed.") ); - break; - }; - - dmaDoneWithBuffer(); - -} - - -/*! - - Open ohci and asign handle to it and get the camera nodes and - describe them as we find them. - - \exception vpFrameGrabberException::initializationError : If a raw1394 handle - can't be aquired. - - \sa close() -*/ -void -vp1394Grabber::open() -{ - //int num_nodes; - int num_ports = vp1394Grabber::MAX_PORTS; - struct raw1394_portinfo ports[vp1394Grabber::MAX_PORTS]; - raw1394handle_t raw_handle; - - if (handles == NULL) - handles = new raw1394handle_t [vp1394Grabber::MAX_CAMERAS]; - if (cameras == NULL) - cameras = new dc1394_cameracapture [vp1394Grabber::MAX_CAMERAS]; - if (cam_count == NULL) - cam_count = new int [vp1394Grabber::MAX_CAMERAS]; - if (pformat == NULL) - pformat = new int [vp1394Grabber::MAX_CAMERAS]; - if (pmode == NULL) - pmode = new int [vp1394Grabber::MAX_CAMERAS]; - if (pframerate == NULL) - pframerate = new int [vp1394Grabber::MAX_CAMERAS]; - if (_width == NULL) - _width = new int [vp1394Grabber::MAX_CAMERAS]; - if (_height == NULL) - _height = new int [vp1394Grabber::MAX_CAMERAS]; - if (image_format == NULL) - image_format = new vp1394ImageFormatType [vp1394Grabber::MAX_CAMERAS]; - - raw_handle = raw1394_new_handle(); - - if (raw_handle==NULL) { - close(); - vpERROR_TRACE("Unable to aquire a raw1394 handle\n\n" - "Please check \n" - " - if the kernel modules `ieee1394',`raw1394' and `ohci1394' are loaded \n" - " - if you have read/write access to /dev/raw1394\n\n"); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "Unable to aquire a raw1394 handle") ); - } - - num_ports = raw1394_get_port_info(raw_handle, ports, num_ports); - raw1394_destroy_handle(raw_handle); - if (verbose) - printf("number of ports detected: %d\n", num_ports); - - if (num_ports < 1) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "no ports found") ); - } - - //num_nodes = raw1394_get_nodecount(raw_handle); - num_cameras = 0; - - /* get dc1394 handle to each port */ - for (int p = 0; p < num_ports; p++) { - - /* get the camera nodes and describe them as we find them */ - raw_handle = raw1394_new_handle(); - raw1394_set_port( raw_handle, p ); - - camera_nodes = NULL; - camera_nodes = dc1394_get_camera_nodes(raw_handle, &cam_count[p], 0); - raw1394_destroy_handle(raw_handle); - if (verbose) - fprintf(stdout, "%d camera(s) on port %d\n", cam_count[p], p); - - for (int i = 0; i < cam_count[p]; i++, num_cameras ++) { - handles[num_cameras] = dc1394_create_handle(p); - if (handles[num_cameras]==NULL) { - close(); - vpERROR_TRACE("Unable to aquire a raw1394 handle\n"); - vpERROR_TRACE("did you load the drivers?\n"); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "Unable to aquire a raw1394 handle.") ); - } - cameras[num_cameras].node = camera_nodes[i]; - - } - if (cam_count[p]) - dc1394_free_camera_nodes(camera_nodes); - } - - if (num_cameras < 1) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::initializationError, - "no cameras found") ); - } - vpDEBUG_TRACE(10, "%d cameras detected\n", num_cameras); - - camera_found = true; - - handle_created = true; - - // Get the actual camera format, mode and framerate - getFormat(pformat[camera]); - getMode(pmode[camera]); - getFramerate(pframerate[camera]); -} - -/*! - - Get the feature and set the dma capture. - - Set the camera to the specified format setFormat(), mode setMode() and - framerate setFramerate(). Considering the camera format and mode, updates - the captured image size. - - \exception vpFrameGrabberException::otherError : If unable to get feature set - or setup the camera. - - \sa setFormat(), setMode(), setFramerate(), getWidth(), getHeight() - -*/ -void -vp1394Grabber::setup() -{ - unsigned int channel; - unsigned int speed; - - if ( handle_created == true && camera_found == true) { - - for (unsigned int i = 0; i < num_cameras; i++) { - if (verbose) { - dc1394_feature_set features; - - if(dc1394_get_camera_feature_set(handles[i], - cameras[i].node, - &features) != DC1394_SUCCESS) { - close(); - - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to get feature set") ); - } - else { - dc1394_print_feature_set(&features); - } - } - - // After setting the camera format and mode we update the image size - getImageCharacteristics(pformat[i], - pmode[i], - _width[i], - _height[i], - image_format[i]); - - if (dc1394_get_iso_channel_and_speed(handles[i], - cameras[i].node, - &channel, - &speed) != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to get the iso channel number") ); - } - - if (pformat[i] == FORMAT_SCALABLE_IMAGE_SIZE) { - if( dc1394_dma_setup_format7_capture(handles[i], - cameras[i].node, - channel, - pmode[i], - speed, - USE_MAX_AVAIL, /*max packet size*/ - 0, 0, /* left, top */ - _width[i], - _height[i], - vp1394Grabber::NUM_BUFFERS, - vp1394Grabber::DROP_FRAMES, - device_name, - &cameras[i]) != DC1394_SUCCESS) { - close(); - - vpERROR_TRACE("Unable to setup camera in format 7 mode 0-\n" - "check line %d of %s to" - "make sure that the video mode,framerate and format are " - "supported by your camera.\n", - __LINE__,__FILE__); - - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to setup camera in format 7 ") ); - - } - if (verbose) { - unsigned int qpp; // packet bytes - if (dc1394_query_format7_byte_per_packet(handles[i], - cameras[i].node, - pmode[i], - &qpp) != DC1394_SUCCESS) { - close(); - - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to query format7 byte_per_packet ") ); - } - //std::cout << "Format 7: byte per packet : " << qpp << std::endl; - } - - } else { - - if (dc1394_dma_setup_capture(handles[i], - cameras[i].node, - channel, - pformat[i], - pmode[i], - speed, - pframerate[i], - vp1394Grabber::NUM_BUFFERS, - vp1394Grabber::DROP_FRAMES, - device_name, - &cameras[i]) != DC1394_SUCCESS) { - vpERROR_TRACE("Unable to setup camera- check line %d of %s to" - "make sure that the video mode,framerate and format are " - "supported by your camera.\n", - __LINE__,__FILE__); - close(); - - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to setup camera ") ); - } - } - } - } -} - -/*! - - Gets the image size and coding format, depending on the camera image format - and the camera mode. - - \param _format : The camera capture format. - \param _mode : The camera capture mode. - \param width : Width of the image for the given camera capture format - and mode. - - \param height : Height of the image for the given camera capture format and - mode. - - \param image_format : Coding image format for the given camera capture - format and mode. - - \exception vpFrameGrabberException::otherError : If camera mode (see - setMode()) and the image format (see setFormat()) are incompatible. - - \sa setFormat(), setMode(), getWidth(), getHeight(), setCamera() -*/ -void -vp1394Grabber::getImageCharacteristics(int _format, int _mode, - int &width, int &height, - vp1394ImageFormatType &_image_format) -{ - switch(_format) - { - case FORMAT_VGA_NONCOMPRESSED: - switch(_mode) - { - case MODE_160x120_YUV444: - width = 160; height = 120; - _image_format = YUV444; - break; - case MODE_320x240_YUV422: - width = 320; height = 240; - _image_format = YUV422; - break; - case MODE_640x480_YUV411: - width = 640; height = 480; - _image_format = YUV411; - break; - case MODE_640x480_YUV422: - width = 640; height = 480; - _image_format = YUV422; - break; - case MODE_640x480_RGB: - width = 640; height = 480; - _image_format = RGB; - break; - case MODE_640x480_MONO: - width = 640; height = 480; - _image_format = MONO; - break; - case MODE_640x480_MONO16: - width = 640; height = 480; - _image_format = MONO; - break; - default: - close(); - vpERROR_TRACE("Error: camera image format and camera mode are uncompatible...\n"); - vpERROR_TRACE("format: %d and mode: %d\n", _format, _mode); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Wrong mode for format 0") ); - break; - } - break; - case FORMAT_SVGA_NONCOMPRESSED_1: - switch(_mode) - { - case MODE_800x600_YUV422: - width = 800; height = 600; - _image_format = YUV422; - break; - case MODE_800x600_RGB: - width = 800; height = 600; - _image_format = RGB; - break; - case MODE_800x600_MONO: - width = 800; height = 600; - _image_format = MONO; - break; - case MODE_800x600_MONO16: - width = 800; height = 600; - _image_format = MONO16; - break; - case MODE_1024x768_YUV422: - width = 1024; height = 768; - _image_format = YUV422; - break; - case MODE_1024x768_RGB: - width = 1024; height = 768; - _image_format = RGB; - break; - case MODE_1024x768_MONO: - width = 1024; height = 768; - _image_format = MONO; - break; - case MODE_1024x768_MONO16: - width = 1024; height = 768; - _image_format = MONO16; - break; - default: - close(); - vpERROR_TRACE("Error: camera image format and camera mode are uncompatible...\n"); - vpERROR_TRACE("format: %d and mode: %d\n", _format, _mode); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Wrong mode for format 1") ); - break; - } - break; - case FORMAT_SVGA_NONCOMPRESSED_2: - switch(_mode) - { - case MODE_1280x960_YUV422: - width = 1280; height = 960; - _image_format = YUV422; - break; - case MODE_1280x960_RGB: - width = 1280; height = 960; - _image_format = RGB; - break; - case MODE_1280x960_MONO: - width = 1280; height = 960; - _image_format = MONO; - break; - case MODE_1280x960_MONO16: - width = 1280; height = 960; - _image_format = MONO16; - break; - case MODE_1600x1200_YUV422: - width = 1600; height = 1200; - _image_format = YUV422; - break; - case MODE_1600x1200_RGB: - width = 1600; height = 1200; - _image_format = RGB; - break; - case MODE_1600x1200_MONO: - width = 1600; height = 1200; - _image_format = MONO; - break; - case MODE_1600x1200_MONO16: - width = 1600; height = 1200; - _image_format = MONO16; - break; - default: - close(); - vpERROR_TRACE("Error: camera image format and camera mode are uncompatible...\n"); - vpERROR_TRACE("format: %d and mode: %d\n", _format, _mode); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Wrong mode for format 2") ); - break; - } - break; - case FORMAT_SCALABLE_IMAGE_SIZE: -#if 1 - switch(_mode) - { - case MODE_FORMAT7_0: - width = 656; height = 492; - _image_format = YUV422; - break; - - case MODE_FORMAT7_1: - width = 328; height = 492; - _image_format = MONO; - break; - case MODE_FORMAT7_2: - width = 656; height = 244; - _image_format = MONO; - break; - case MODE_FORMAT7_3: - case MODE_FORMAT7_4: - case MODE_FORMAT7_5: - case MODE_FORMAT7_6: - case MODE_FORMAT7_7: - width = 656; height = 244; - _image_format = MONO; - break; - } - - -#else - // Did not work. Even if subsampling is activated in MODE_FORMAT7_1, image - // max size is the size of the image without considering the subsampling - switch(_mode) - { - case MODE_FORMAT7_0: - // _width = 656; _height = 492; - _image_format = YUV422; - break; - - case MODE_FORMAT7_1: - // _width = 328; _height = 492; - _image_format = MONO; - break; - case MODE_FORMAT7_2: - // _width = 656; _height = 244; - _image_format = MONO; - break; - } - - // In format 7 we query set the image size to the maximal image size - if (dc1394_query_format7_max_image_size(handles[camera], - cameras[camera].node, - _mode, - &width, - &_eight) != DC1394_SUCCESS) { - close(); - vpERROR_TRACE("Unable to get maximal image size for format 7\n"); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to get maximal image size for format 7 ") ); - } - std::cout << "max width=" << width << " height: " << height << std::endl; -#endif - - break; - default: - close(); - vpERROR_TRACE("Error: camera image format and camera mode are uncompatible...\n"); - vpERROR_TRACE("format: %d and mode: %d\n", _format, _mode); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Wrong format") ); - break; - } -} - - -/*! - - Captures a frame from the given camera using DMA (direct memory acces). Two - policies are available, either this fonction waits for a frame (waiting - mode), either it returns if no frame is available (polling mode). - - After you have finished with the frame, you must return the buffer to the - pool by calling dmaDoneWithBuffer(). - - \param waiting : Capture mode; true if you want to wait for an available - image (waiting mode), false to activate the polling mode. - - \return NULL if no frame is available, the address of the image buffer - otherwise. - - \exception vpFrameGrabberException::otherError : If no frame is available. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa dmaDoneWithBuffer(), getNumCameras(), setCamera() -*/ - -int* -vp1394Grabber::dmaCapture(bool waiting) -{ - - if (camera >= num_cameras) { - std::cout << "The required camera is not present..." - << std::endl; - return NULL; - } - - if ( handle_created == true && camera_found == true) { - if (waiting) { - if (num_cameras == 1) { - // Only one camera on the bus - if (dc1394_dma_single_capture(&cameras[camera]) != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "No frame is available...") ); - return NULL; - } - } - else { - // More than one camera on the bus. - if (dc1394_dma_multi_capture(cameras, num_cameras) != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "No frame is available...") ); - return NULL; - } - // std::cout << "-"; - } - } - else { - if (num_cameras == 1) { - // Only one camera on the bus - if (dc1394_dma_single_capture_poll(&cameras[camera]) != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "No frame is available...") ); - return NULL; - } - } - else { - // More than one camera on the bus. - if (dc1394_dma_multi_capture_poll(cameras, num_cameras) - != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "No frame is available...") ); - return NULL; - } - - } - } - } - - return cameras[camera].capture_buffer; -} - -/*! - - Return the buffer to the pool for the given camera. This allows the driver to - use the buffer previously handed to the user. - - \exception vpFrameGrabberException::settingError :If the required camera is - not present. - - \exception vpFrameGrabberException::otherError : If can't stop the dma - access. - - \warning Has to be called after open() to be sure that a camera is detected. - - \sa dmaCapture(), setCamera() - -*/ -void -vp1394Grabber::dmaDoneWithBuffer() -{ - - if (camera >= num_cameras) { - close(); - vpERROR_TRACE("The required camera is not present"); - throw (vpFrameGrabberException(vpFrameGrabberException::settingError, - "The required camera is not present") ); - } - - if ( handle_created == true && camera_found == true) { - if (dc1394_dma_done_with_buffer(&cameras[camera]) != DC1394_SUCCESS) { - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Can't done the dma") ); - } - } -} - - - -/*! - - Close the link between the camera and the acquisition program - -*/ -void -vp1394Grabber::close() -{ - if (iso_transmission_started == true) { - - stopIsoTransmission(); - - for (unsigned int i=0; i < num_cameras; i++) - dc1394_dma_unlisten( handles[i], &cameras[i] ); - iso_transmission_started = false; - } - if ((camera_found == true) && (dma_started == true)) { - for (unsigned int i=0; i < num_cameras; i++) - dc1394_dma_release_camera( handles[i], &cameras[i]); - camera_found = false; - dma_started = false; - } - if (handle_created == true) { - for (unsigned int i=0; i < num_cameras; i++) - dc1394_destroy_handle(handles[i]); - handle_created = false; - } - - if (handles != NULL) { delete [] handles; handles = NULL; } - if (cameras != NULL) { delete [] cameras; cameras = NULL; } - if (cam_count != NULL) { delete [] cam_count; cam_count = NULL; } - if (pformat != NULL) { delete [] pformat; pformat = NULL; } - if (pmode != NULL) { delete [] pmode; pmode = NULL; } - if (pframerate != NULL) { delete [] pframerate; pframerate = NULL; } - if (_width != NULL) { delete [] _width; _width = NULL; } - if (_height != NULL) { delete [] _height; _height = NULL; } - if (image_format != NULL) { delete [] image_format; image_format = NULL; } - -} -/*! - Start the transmission of the images for all the cameras on the bus. - - \exception vpFrameGrabberException::otherError : If Unable to start camera - iso transmission. - - \sa stopIsoTransmission() - -*/ -void vp1394Grabber::startIsoTransmission() -{ - if ( handle_created == true && camera_found == true) { - - for (unsigned int i = 0; i < num_cameras; i ++) { - if (dc1394_start_iso_transmission(handles[i], - cameras[i].node) !=DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Unable to start camera iso transmission") ); - } - } - iso_transmission_started = true; - } -} - -/*! - - Stop the Iso transmission for all the cameras on the bus. - - \return true on success, false otherwise. - - \sa StartIsoTransmission() - -*/ -void vp1394Grabber::stopIsoTransmission() -{ - - if (iso_transmission_started == true) { - if (handle_created == true && camera_found == true) { - for (unsigned int i = 0; i < num_cameras; i ++) { - if (dc1394_stop_iso_transmission(handles[i], - cameras[i].node) != DC1394_SUCCESS) { - close(); - throw (vpFrameGrabberException(vpFrameGrabberException::otherError, - "Can't stop the camera") ); - } - - } - iso_transmission_started = false; - } - } -} - - -#endif -#endif diff --git a/src/device/framegrabber/1394/vp1394Grabber.h b/src/device/framegrabber/1394/vp1394Grabber.h deleted file mode 100644 index fad5e9369699bd41d43055f46a317dd4fe0fa715..0000000000000000000000000000000000000000 --- a/src/device/framegrabber/1394/vp1394Grabber.h +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** - * - * $Id: vp1394Grabber.h 4323 2013-07-18 09:24:01Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Firewire cameras video capture. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ - -/*! - \file vp1394Grabber.h - \brief class for firewire cameras video capture. - - \warning This class needs at least libdc1394-1.0.0 and - libraw1394-1.1.0. These libraries are available from - http://sourceforge.net/projects/libdc1394 and - http://sourceforge.net/projects/libraw1394 . - - vp1394Grabber was tested with a Marlin F033C camera. This grabber is - not working with a PointGrey DragonFly 2 camera. Since libdc1394-1.x - is deprecated, you should better use vp1394TwoGrabber based on - libdc1394-2.x. - -*/ - -#ifndef vp1394Grabber_h -#define vp1394Grabber_h - -#include <visp/vpConfig.h> - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - -#if defined(VISP_HAVE_DC1394_1) - -#include <string> - -#include <libraw1394/raw1394.h> -#include <libdc1394/dc1394_control.h> - -#include <visp/vpImage.h> -#include <visp/vpFrameGrabber.h> -#include <visp/vpRGBa.h> -#include <visp/vpList.h> - - - -/*! - \class vp1394Grabber - - \ingroup Framegrabber CameraDriver - - \brief Class for firewire ieee1394 video devices using libdc1394-1.x api - - \deprecated This class is deprecated. You should use - vp1394TwoGrabber class instead. - - Needs libraw1394-1.2.0 and libdc1394-1.1.0 or more recent versions - available on http://sourceforge.net. - - This class was tested with Marlin F033C and F131B cameras. This grabber is - not working with a PointGrey DragonFly 2 camera. Since libdc1394-1.x - is deprecated, you should better use vp1394TwoGrabber based on - libdc1394-2.x. - - The code below shows how to use this class. - \code -#include <visp/vpConfig.h> -#include <visp/vpImage.h> -#include <visp/vpImageIo.h> -#include <visp/vp1394Grabber.h> - -int main() -{ -#if defined(VISP_HAVE_DC1394_1) - vpImage<unsigned char> I; // Create a gray level image container - vp1394Grabber g; // Create a grabber based on libdc1394-1.x third party lib - g.setFormat(FORMAT_VGA_NONCOMPRESSED); // Format_0 - g.setMode(MODE_640x480_MONO); // Mode 5 - g.setFramerate(FRAMERATE_15); // 15 fps - - g.open(I); // Open the framegrabber - g.acquire(I); // Acquire an image - vpImageIo::write(I, "image.pgm"); // Write image on the disk -#endif -} - \endcode - - \author Fabien Spindler (Fabien.Spindler@irisa.fr), Irisa / Inria Rennes - -*/ -class VISP_EXPORT vp1394Grabber : public vpFrameGrabber -{ -public: - /*! \enum vp1394ImageFormatType - Supported image coding format. - */ - typedef enum { - YUV444, /*!< YUV 4:4:4 coding format (24 bits/pixel) */ - YUV422, /*!< YUV 4:2:2 coding format (16 bits/pixel) */ - YUV411, /*!< YUV 4:1:1 coding format (12 bits/pixel) */ - RGB, /*!< RGB coding format (24 bits/pixel) */ - RGBa, /*!< RGBa coding format (32 bits/pixel) */ - MONO, /*!< MONO8 coding format (8 bits/pixel) */ - MONO16 /*!< MONO16 coding format (16 bits/pixel) */ - } vp1394ImageFormatType; - - static const int DROP_FRAMES; // Number of frames to drop - static const int NUM_BUFFERS; // Number of buffers - static const int MAX_PORTS; // Port maximal number - static const int MAX_CAMERAS; // Maximal number of cameras on the bus - - static const char * strFormats[NUM_FORMATS]; - static const char * strModesInFormat0[NUM_FORMAT0_MODES]; - static const char * strModesInFormat1[NUM_FORMAT1_MODES]; - static const char * strModesInFormat2[NUM_FORMAT2_MODES]; - static const char * strModesInFormat6[NUM_FORMAT6_MODES]; - static const char * strModesInFormat7[NUM_MODE_FORMAT7]; - static const char * strColorsInFormat7[NUM_COLOR_FORMAT7]; - static const char * strFramerates[NUM_FRAMERATES]; - - -public: - vp_deprecated vp1394Grabber(); - vp_deprecated vp1394Grabber(vpImage<unsigned char> &I); - virtual ~vp1394Grabber(); - - void setCamera(unsigned int camera); - void getCamera(unsigned int &camera); - - void setFormat(int format); - void getFormat(int & format); - int getFormatSupported(vpList<int> & formats); - - void setMode(int mode); - void getMode(int & mode); - int getModeSupported(int format, vpList<int> & modes); - - void setFramerate(int framerate); - void getFramerate(int & framerate); - int getFramerateSupported(int format, int mode, vpList<int> & framerates); - - int convertFormat (std::string format); - int convertMode (std::string mode); - int convertFramerate(std::string framerate); - - std::string convertFormat (int format); - std::string convertMode (int mode); - std::string convertFramerate(int framerate); - - void setShutter(unsigned int shutter); - void getShutter(unsigned int &min_shutter, - unsigned int &shutter, - unsigned int &max_shutter); - - void setGain(unsigned int gain); - void getGain(unsigned int &min_gain, - unsigned int &gain, - unsigned int &max_gain); - - void open(vpImage<unsigned char> &I); - void acquire(vpImage<unsigned char> &I); - void open(vpImage<vpRGBa> &I); - void acquire(vpImage<vpRGBa> &I); - void close(); - - void getWidth (unsigned int &width); - void getHeight(unsigned int &height); - void getNumCameras(unsigned int &cameras); - - -private: - - void open(); - void setup(); - - void getImageCharacteristics(int _format, int _mode, - int &width, int &height, - vp1394ImageFormatType &imageformat); - int* dmaCapture(bool waiting = true); - void dmaDoneWithBuffer(); - - void startIsoTransmission(); - void stopIsoTransmission(); - -private: - bool iso_transmission_started; - bool handle_created; - bool camera_found; - bool camera_nodes_allocated; - bool dma_started; - unsigned int num_cameras; - /* declarations for libdc1394 */ - raw1394handle_t *handles; // MAX_CAMERAS - dc1394_cameracapture *cameras; // MAX_CAMERAS - nodeid_t *camera_nodes; - dc1394_miscinfo miscinfo; - int *cam_count; // MAX_CAMERAS - - /* declarations for video1394 */ - char device_name[FILENAME_MAX]; - - unsigned int camera; - // Camera settings - int *pformat; // MAX_CAMERAS - int *pmode; // MAX_CAMERAS - int *pframerate; // MAX_CAMERAS - bool verbose; - - // Image settings - int *_width; // MAX_CAMERAS - int *_height; // MAX_CAMERAS - vp1394ImageFormatType *image_format; // MAX_CAMERAS -} ; - -#endif -#endif -#endif diff --git a/src/device/framegrabber/1394/vp1394TwoGrabber.cpp b/src/device/framegrabber/1394/vp1394TwoGrabber.cpp index a5a6d45dcdb77c0e72032f72111864feafa06e96..77848539eaff469d19906f06390c5ae61bae3ac4 100644 --- a/src/device/framegrabber/1394/vp1394TwoGrabber.cpp +++ b/src/device/framegrabber/1394/vp1394TwoGrabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vp1394TwoGrabber.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vp1394TwoGrabber.cpp 4728 2014-04-23 12:26:44Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -168,28 +168,20 @@ int main() */ vp1394TwoGrabber::vp1394TwoGrabber(bool reset) + : camera(NULL), cameras(NULL), num_cameras(0), camera_id(0), verbose(false), camIsOpen(NULL), + num_buffers(4), // ring buffer size + isDataModified(NULL), initialShutterMode(NULL), dataCam(NULL) + #ifdef VISP_HAVE_DC1394_2_CAMERA_ENUMERATE // new API > libdc1394-2.0.0-rc7 + , d(NULL), + list(NULL) + #endif { // protected members width = height = 0; // private members - num_cameras = 0; - cameras = NULL; - camera_id = 0; - verbose = false;//true; - camIsOpen = NULL; init = false; - cameras = NULL; -#ifdef VISP_HAVE_DC1394_2_CAMERA_ENUMERATE // new API > libdc1394-2.0.0-rc7 - d = NULL; - list = NULL; -#endif - num_buffers = 4; // ring buffer size - isDataModified = NULL; - initialShutterMode = NULL; - dataCam = NULL; - reset = false; initialize(reset); @@ -220,7 +212,7 @@ vp1394TwoGrabber::~vp1394TwoGrabber() If multiples cameras are connected on the bus, select the camero to dial with. - \param camera_id : A camera identifier or GUID. By identifier, we + \param cam_id : A camera identifier or GUID. By identifier, we mean a value comprised between 0 (the first camera found on the bus) and the number of cameras found on the bus and returned by getNumCameras() minus 1. If two cameras are connected on the bus, @@ -332,37 +324,38 @@ int main() } \endcode - \sa setFormat(), setVideoMode(), setFramerate(), getNumCameras() */ void -vp1394TwoGrabber::setCamera(uint64_t camera_id) +vp1394TwoGrabber::setCamera(uint64_t cam_id) { // Suppose that if camera_id is a camera GUID, this value is greater // than the number of cameras connected to the bus - if (camera_id >= num_cameras) { + if (cam_id >= num_cameras) { // Check if camera_id is a camera guid bool is_guid = false; // check if the camera_id is a guid for (unsigned int i=0; i< num_cameras; i++) { - if (cameras[i]->guid == camera_id) { - this->camera_id = i; - is_guid = true; - break; + if (cameras[i]->guid == cam_id) { + this->camera_id = i; + is_guid = true; + break; } } if (is_guid == false) { + std::cout << "Error: The camera with guid 0x" << std::hex << cam_id << " is not present" << std::endl; + std::cout << num_cameras << " camera(s) connected" << std::endl; + for (unsigned int i=0; i< num_cameras; i++) { + std::cout << " - camera " << i << " with guid 0x" << std::hex << cameras[i]->guid << std::endl; + } close(); - vpERROR_TRACE("The camera with GUID 0x%x or id %u is not present", - camera_id, camera_id); - vpERROR_TRACE("Only %u camera on the bus.", num_cameras); throw (vpFrameGrabberException(vpFrameGrabberException::settingError, "The required camera is not present") ); } } else { - this->camera_id = camera_id; + this->camera_id = (unsigned int) cam_id; // The input cam_id is not a uint64_t guid, but the index of the camera } // create a pointer to the working camera @@ -374,7 +367,7 @@ vp1394TwoGrabber::setCamera(uint64_t camera_id) Get the active camera identifier on the bus. - \param camera_id : The active camera identifier. The value is + \param cam_id : The active camera identifier. The value is comprised between 0 (the first camera) and the number of cameras found on the bus returned by getNumCameras() minus 1. @@ -385,10 +378,10 @@ vp1394TwoGrabber::setCamera(uint64_t camera_id) */ void -vp1394TwoGrabber::getCamera(uint64_t &camera_id) +vp1394TwoGrabber::getCamera(uint64_t &cam_id) { if (num_cameras) { - camera_id = this->camera_id; + cam_id = this->camera_id; } else { close(); @@ -434,7 +427,7 @@ vp1394TwoGrabber::getCamera() */ void -vp1394TwoGrabber::getNumCameras(unsigned int &ncameras) +vp1394TwoGrabber::getNumCameras(unsigned int &ncameras) const { if (! num_cameras) { vpCTRACE << "No camera found..."<< std::endl; @@ -452,7 +445,7 @@ vp1394TwoGrabber::getNumCameras(unsigned int &ncameras) */ unsigned int -vp1394TwoGrabber::getNumCameras() +vp1394TwoGrabber::getNumCameras() const { unsigned int ncameras = 0; if (! num_cameras) { @@ -1345,10 +1338,10 @@ vp1394TwoGrabber::isColorCodingSupported(vp1394TwoVideoModeType mode, \param top : Position of the upper left roi corner. - \param width : Roi width. If width is set to 0, uses the maximum + \param w : Roi width. If width is set to 0, uses the maximum allowed image width. - \param height : Roi height. If width is set to 0, uses the maximum + \param h : Roi height. If width is set to 0, uses the maximum allowed image height. @@ -1362,7 +1355,7 @@ vp1394TwoGrabber::isColorCodingSupported(vp1394TwoVideoModeType mode, */ void vp1394TwoGrabber::setFormat7ROI(unsigned int left, unsigned int top, - unsigned int width, unsigned int height) + unsigned int w, unsigned int h) { open(); if (! num_cameras) { @@ -1397,8 +1390,8 @@ vp1394TwoGrabber::setFormat7ROI(unsigned int left, unsigned int top, } #if 0 vpTRACE("left: %d top: %d width: %d height: %d", left, top, - width == 0 ? DC1394_USE_MAX_AVAIL: width, - height == 0 ? DC1394_USE_MAX_AVAIL : height); + width == 0 ? DC1394_USE_MAX_AVAIL: w, + height == 0 ? DC1394_USE_MAX_AVAIL : h); vpTRACE("max_width: %d max_height: %d", max_width, max_height); #endif @@ -1416,27 +1409,26 @@ vp1394TwoGrabber::setFormat7ROI(unsigned int left, unsigned int top, int32_t roi_width; int32_t roi_height; - if (width != 0) { + if (w != 0) { // Check if roi width is acceptable (ie roi is contained in the image) - if (width > (max_width - left)) - width = (max_width - left); - roi_width = (int32_t)width; + if (w > (max_width - left)) + w = (max_width - left); + roi_width = (int32_t)w; } else { roi_width = DC1394_USE_MAX_AVAIL; } - if (height != 0) { + if (h != 0) { // Check if roi height is acceptable (ie roi is contained in the image) - if (height > (max_height - top)) - height = (max_height - top); - roi_height = (int32_t)height; + if (h > (max_height - top)) + h = (max_height - top); + roi_height = (int32_t)h; } else { roi_height = DC1394_USE_MAX_AVAIL; } - if (dc1394_format7_set_roi(camera, _videomode, (dc1394color_coding_t) DC1394_QUERY_FROM_CAMERA, // color_coding DC1394_USE_MAX_AVAIL/*DC1394_QUERY_FROM_CAMERA*/, // bytes_per_packet @@ -1530,7 +1522,7 @@ vp1394TwoGrabber::initialize(bool reset) dc1394_reset_bus(cameras[0]); } - if (list != NULL) + // if (list != NULL) dc1394_camera_free_list (list); list = NULL; @@ -1771,7 +1763,7 @@ vp1394TwoGrabber::setRingBufferSize(unsigned int size) \sa setRingBufferSize() */ unsigned int -vp1394TwoGrabber::getRingBufferSize() +vp1394TwoGrabber::getRingBufferSize() const { return num_buffers; } @@ -1781,6 +1773,9 @@ vp1394TwoGrabber::getRingBufferSize() and max exposure time, but only for AVT cameras. In that case use setAutoShutter(unsigned int, unsigned int). + \param enable : Flag to enable or disable the auto shutter. If false, set the + shutter as manual. + \exception vpFrameGrabberException::initializationError : If no camera found on the bus. @@ -1813,7 +1808,7 @@ int main() \sa setAutoShutter(unsigned int, unsigned int), getAutoShutter() */ void -vp1394TwoGrabber::setAutoShutter() +vp1394TwoGrabber::setAutoShutter(bool enable) { if (! num_cameras) { close(); @@ -1822,6 +1817,14 @@ vp1394TwoGrabber::setAutoShutter() "No camera found") ); } + dc1394feature_mode_t mode; + if (enable) { + mode = DC1394_FEATURE_MODE_AUTO; + } + else { + mode = DC1394_FEATURE_MODE_MANUAL; + } + if (dc1394_feature_set_power(camera, DC1394_FEATURE_SHUTTER, DC1394_ON) != DC1394_SUCCESS) { // vpERROR_TRACE("Cannot set shutter on. \n"); @@ -1832,7 +1835,7 @@ vp1394TwoGrabber::setAutoShutter() if (dc1394_feature_set_mode(camera, DC1394_FEATURE_SHUTTER, - DC1394_FEATURE_MODE_AUTO) + mode) != DC1394_SUCCESS) { // vpERROR_TRACE("Cannot set auto shutter. \n"); close(); @@ -1930,6 +1933,9 @@ vp1394TwoGrabber::getAutoShutter(unsigned int &minvalue, unsigned int &maxvalue) and max gain, but only for AVT cameras. In that case use setAutoGain(unsigned int, unsigned int). + \param enable : Flag to enable or disable the auto gain. If false, set the + gain as manual. + \exception vpFrameGrabberException::initializationError : If no camera found on the bus. @@ -1948,7 +1954,7 @@ int main() vp1394TwoGrabber g(false); // Don't reset the bus g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_FORMAT7_0 ); g.setColorCoding(vp1394TwoGrabber::vpCOLOR_CODING_MONO8); - g.setAutoGain(); // Enable auto gain + g.setAutoGain(true); // Enable auto gain g.setIsoTransmissionSpeed(vp1394TwoGrabber::vpISO_SPEED_800); // 1394b while(1) g.acquire(I); @@ -1962,7 +1968,7 @@ int main() \sa setAutoGain(unsigned int, unsigned int), getAutoGain() */ void -vp1394TwoGrabber::setAutoGain() +vp1394TwoGrabber::setAutoGain(bool enable) { if (! num_cameras) { close(); @@ -1971,7 +1977,15 @@ vp1394TwoGrabber::setAutoGain() "No camera found") ); } - if (dc1394_feature_set_power(camera, DC1394_FEATURE_SHUTTER, DC1394_ON) + dc1394feature_mode_t mode; + if (enable) { + mode = DC1394_FEATURE_MODE_AUTO; + } + else { + mode = DC1394_FEATURE_MODE_MANUAL; + } + + if (dc1394_feature_set_power(camera, DC1394_FEATURE_GAIN, DC1394_ON) != DC1394_SUCCESS) { // vpERROR_TRACE("Cannot set shutter on. \n"); close(); @@ -1981,7 +1995,7 @@ vp1394TwoGrabber::setAutoGain() if (dc1394_feature_set_mode(camera, DC1394_FEATURE_GAIN, - DC1394_FEATURE_MODE_AUTO) + mode) != DC1394_SUCCESS) { // vpERROR_TRACE("Cannot set auto gain. \n"); close(); @@ -2924,7 +2938,7 @@ vp1394TwoGrabber::acquire(vpImage<vpRGBa> &I, Get the image width. It depends on the camera video mode setVideoMode(). The image size is only available after a call to open() or acquire(). - \param width : The image width, zero if the required camera is not available. + \param w : The image width, zero if the required camera is not available. \exception vpFrameGrabberException::initializationError : If no camera found on the bus. @@ -2935,7 +2949,7 @@ vp1394TwoGrabber::acquire(vpImage<vpRGBa> &I, \sa getHeight(), open(), acquire() */ -void vp1394TwoGrabber::getWidth(unsigned int &width) +void vp1394TwoGrabber::getWidth(unsigned int &w) { if (! num_cameras) { close(); @@ -2944,7 +2958,7 @@ void vp1394TwoGrabber::getWidth(unsigned int &width) "No camera found") ); } - width = this->width; + w = this->width; } /*! @@ -2981,7 +2995,7 @@ unsigned int vp1394TwoGrabber::getWidth() setVideoMode(). The image size is only available after a call to open() or acquire(). - \param height : The image height. + \param h : The image height. \exception vpFrameGrabberException::initializationError : If no camera found on the bus. @@ -2992,7 +3006,7 @@ unsigned int vp1394TwoGrabber::getWidth() \sa getWidth() */ -void vp1394TwoGrabber::getHeight(unsigned int &height) +void vp1394TwoGrabber::getHeight(unsigned int &h) { if (! num_cameras) { close(); @@ -3001,7 +3015,7 @@ void vp1394TwoGrabber::getHeight(unsigned int &height) "No camera found") ); } - height = this->height; + h = this->height; } /*! @@ -3096,8 +3110,8 @@ std::string vp1394TwoGrabber::videoMode2string(vp1394TwoVideoModeType videomode) _str = strVideoMode[_videomode - DC1394_VIDEO_MODE_MIN]; } else { - vpCERROR << "The video mode " << videomode - << " is not supported by the camera" << std::endl; + vpCERROR << "The video mode " << (int)videomode + << " is not supported by the camera" << std::endl; } return _str; @@ -3125,8 +3139,8 @@ std::string vp1394TwoGrabber::framerate2string(vp1394TwoFramerateType fps) _str = strFramerate[_fps - DC1394_FRAMERATE_MIN]; } else { - vpCERROR << "The framerate " << fps - << " is not supported by the camera" << std::endl; + vpCERROR << "The framerate " << (int)fps + << " is not supported by the camera" << std::endl; } return _str; @@ -3155,8 +3169,8 @@ std::string vp1394TwoGrabber::colorCoding2string(vp1394TwoColorCodingType colorc } else { - vpCERROR << "The color coding " << colorcoding - << " is not supported by the camera" << std::endl; + vpCERROR << "The color coding " << (int)colorcoding + << " is not supported by the camera" << std::endl; } return _str; @@ -3319,7 +3333,7 @@ void vp1394TwoGrabber::resetBus() dc1394_camera_free (camera); dc1394_free (d); d = NULL; - if (cameras != NULL) + //if (cameras != NULL) delete [] cameras; cameras = NULL ; #elif defined VISP_HAVE_DC1394_2_FIND_CAMERAS // old API <= libdc1394-2.0.0-rc7 @@ -3764,7 +3778,7 @@ vp1394TwoGrabber::getFramerateSupported(vp1394TwoVideoModeType mode, */ uint32_t vp1394TwoGrabber::getColorCodingSupported(vp1394TwoVideoModeType mode, - vpList<vp1394TwoColorCodingType> & codings) + vpList<vp1394TwoColorCodingType> & codings) { if (! num_cameras) { close(); diff --git a/src/device/framegrabber/1394/vp1394TwoGrabber.h b/src/device/framegrabber/1394/vp1394TwoGrabber.h index 123a1e724cdee39c87299f588a2f9f24f7f6c129..4422023f0ed21837e20be61c3e3f7a74cf3412f7 100644 --- a/src/device/framegrabber/1394/vp1394TwoGrabber.h +++ b/src/device/framegrabber/1394/vp1394TwoGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vp1394TwoGrabber.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vp1394TwoGrabber.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -382,10 +382,10 @@ class VISP_EXPORT vp1394TwoGrabber : public vpFrameGrabber void getGuid(uint64_t &guid); void getHeight(unsigned int &height); unsigned int getHeight(); - void getNumCameras(unsigned int &ncameras); - unsigned int getNumCameras(); + void getNumCameras(unsigned int &ncameras) const; + unsigned int getNumCameras() const; unsigned int getParameterValue(vp1394TwoParametersType param); - unsigned int getRingBufferSize(); + unsigned int getRingBufferSize() const; void getVideoMode(vp1394TwoVideoModeType & videomode); uint32_t getVideoModeSupported(std::list<vp1394TwoVideoModeType> & videomodes); void getWidth(unsigned int &width); @@ -406,9 +406,9 @@ class VISP_EXPORT vp1394TwoGrabber : public vpFrameGrabber void resetBus(); - void setAutoGain(); + void setAutoGain(bool enable = true); void setAutoGain(unsigned int minvalue, unsigned int maxvalue); - void setAutoShutter(); + void setAutoShutter(bool enable = true); void setAutoShutter(unsigned int minvalue, unsigned int maxvalue); void setCamera(uint64_t camera); void setColorCoding(vp1394TwoColorCodingType coding); diff --git a/src/device/framegrabber/OpenCV/vpOpenCVGrabber.cpp b/src/device/framegrabber/OpenCV/vpOpenCVGrabber.cpp index f4e0c0ca21d07598fd9b8414b5ab7ebd025d18dd..55a576592ee913bf13fefe53162cf7d43d9fa53a 100644 --- a/src/device/framegrabber/OpenCV/vpOpenCVGrabber.cpp +++ b/src/device/framegrabber/OpenCV/vpOpenCVGrabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpOpenCVGrabber.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpOpenCVGrabber.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,7 +46,7 @@ #include <visp/vpOpenCVGrabber.h> -#if defined(VISP_HAVE_OPENCV) +#if ( defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408)) #include <visp/vpImageConvert.h> #include <visp/vpFrameGrabberException.h> @@ -58,17 +58,13 @@ Basic Constructor. */ vpOpenCVGrabber::vpOpenCVGrabber() + : capture(NULL), DeviceType(0), flip(false) { // public memebers init = false; // protected members width = height = 0; - - // private members - capture = NULL; - DeviceType = 0; - flip = false; } @@ -245,14 +241,14 @@ void vpOpenCVGrabber::setFramerate(const double framerate) \warning This function must be called after open() method. - \param width : The requested value of the captured image width. + \param w : The requested value of the captured image width. \exception vpFrameGrabberException::initializationError If no camera was found. */ -void vpOpenCVGrabber::setWidth(const unsigned int width) +void vpOpenCVGrabber::setWidth(const unsigned int w) { - if ( cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width)) + if ( cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, w)) { close(); vpERROR_TRACE("Impossible to set the size of the grabber"); @@ -260,7 +256,7 @@ void vpOpenCVGrabber::setWidth(const unsigned int width) "Impossible to set the size of the grabber") ); } - this->width = width; + this->width = w; } /*! @@ -268,14 +264,14 @@ void vpOpenCVGrabber::setWidth(const unsigned int width) \warning This function must be called after open() method. - \param height : The requested value of the captured image height. + \param h : The requested value of the captured image height. \exception vpFrameGrabberException::initializationError If no camera was found. */ -void vpOpenCVGrabber::setHeight(const unsigned int height) +void vpOpenCVGrabber::setHeight(const unsigned int h) { - if ( cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height)) + if ( cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, h)) { close(); vpERROR_TRACE("Impossible to set the size of the grabber"); @@ -283,7 +279,7 @@ void vpOpenCVGrabber::setHeight(const unsigned int height) "Impossible to set the size of the grabber") ); } - this->height = height; + this->height = h; } /*! diff --git a/src/device/framegrabber/OpenCV/vpOpenCVGrabber.h b/src/device/framegrabber/OpenCV/vpOpenCVGrabber.h index 61b10335c59f80ed71d6e6b40974588f76dc2fdb..1587a92b3ec912e82aca25793bc3da850ab1fa5c 100644 --- a/src/device/framegrabber/OpenCV/vpOpenCVGrabber.h +++ b/src/device/framegrabber/OpenCV/vpOpenCVGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpOpenCVGrabber.h 4216 2013-04-17 09:06:18Z fspindle $ + * $Id: vpOpenCVGrabber.h 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,7 @@ #include <visp/vpConfig.h> -#if defined(VISP_HAVE_OPENCV) +#if ( defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408)) #if VISP_HAVE_OPENCV_VERSION >= 0x020101 # include <opencv2/highgui/highgui.hpp> diff --git a/src/device/framegrabber/directshow/vpDirectShowDevice.cpp b/src/device/framegrabber/directshow/vpDirectShowDevice.cpp index 94536e6986ea92ecf8434fff67654fce0b44e47d..21de5903cde648bff141176b4004c59f767e0a27 100644 --- a/src/device/framegrabber/directshow/vpDirectShowDevice.cpp +++ b/src/device/framegrabber/directshow/vpDirectShowDevice.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowDevice.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowDevice.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -120,5 +120,10 @@ bool vpDirectShowDevice::operator==(vpDirectShowDevice& dev) && devPath==dev.devPath; } +VISP_EXPORT std::ostream& operator<<(std::ostream& os, vpDirectShowDevice& dev) +{ + return os<<dev.name<<std::endl<<dev.desc<<std::endl<<dev.devPath; +} + #endif #endif diff --git a/src/device/framegrabber/directshow/vpDirectShowDevice.h b/src/device/framegrabber/directshow/vpDirectShowDevice.h index d3bc59cc66cc5b35906ad8722637644c0cfb3017..6f35fe237253e532f0191130c3ed10fabad53e02 100644 --- a/src/device/framegrabber/directshow/vpDirectShowDevice.h +++ b/src/device/framegrabber/directshow/vpDirectShowDevice.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowDevice.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowDevice.h 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -79,10 +79,7 @@ public: bool operator==(vpDirectShowDevice& dev); - friend VISP_EXPORT std::ostream& operator<<(std::ostream& os, vpDirectShowDevice& dev) - { - return os<<dev.name<<std::endl<<dev.desc<<std::endl<<dev.devPath; - } + friend VISP_EXPORT std::ostream& operator<<(std::ostream& os, vpDirectShowDevice& dev); }; #endif #endif diff --git a/src/device/framegrabber/directshow/vpDirectShowGrabber.cpp b/src/device/framegrabber/directshow/vpDirectShowGrabber.cpp index f49bb4f2d128782a1a873b9009914d0091ad5bee..22355688b6c790f3b03f04d9d95e27468c2aa7d8 100644 --- a/src/device/framegrabber/directshow/vpDirectShowGrabber.cpp +++ b/src/device/framegrabber/directshow/vpDirectShowGrabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowGrabber.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowGrabber.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/directshow/vpDirectShowGrabber.h b/src/device/framegrabber/directshow/vpDirectShowGrabber.h index ddc20276818330e537fe061c83195dfec59bb6f8..d360e26bde1913692d35b5314c074f7ed69a0d2f 100644 --- a/src/device/framegrabber/directshow/vpDirectShowGrabber.h +++ b/src/device/framegrabber/directshow/vpDirectShowGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowGrabber.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowGrabber.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.cpp b/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.cpp index 807888968a11ea20b039f0b8d6c931c8e98403a1..c2c489336217588b511f9bd7465781a69496884b 100755 --- a/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.cpp +++ b/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowGrabberImpl.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowGrabberImpl.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.h b/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.h index 7133ec925a9bab483c8b846c3666f9faf45c4cc7..6f07bde788f09f9107b66f211e9188bc1680856d 100755 --- a/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.h +++ b/src/device/framegrabber/directshow/vpDirectShowGrabberImpl.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowGrabberImpl.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowGrabberImpl.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/directshow/vpDirectShowSampleGrabberI.h b/src/device/framegrabber/directshow/vpDirectShowSampleGrabberI.h index da66cf353f9399c350d4f787310cb85f672bcf39..f233f3c1144abe119d4e4bda422fd369725c104e 100644 --- a/src/device/framegrabber/directshow/vpDirectShowSampleGrabberI.h +++ b/src/device/framegrabber/directshow/vpDirectShowSampleGrabberI.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDirectShowSampleGrabberI.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDirectShowSampleGrabberI.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/disk/vpDiskGrabber.cpp b/src/device/framegrabber/disk/vpDiskGrabber.cpp index e31e6e4d192c546e7bc3601b9c650df4efb2528d..d1c78cd73e45da48baf5b3bed851e0ef0333a93f 100644 --- a/src/device/framegrabber/disk/vpDiskGrabber.cpp +++ b/src/device/framegrabber/disk/vpDiskGrabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDiskGrabber.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDiskGrabber.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,22 +47,30 @@ Elementary constructor. */ vpDiskGrabber::vpDiskGrabber() + : image_number(0), image_step(1), number_of_zero(0), useGenericName(false) { setDirectory("/tmp"); setBaseName("I"); - setImageNumber(0); - setStep(1); - setNumberOfZero(0); setExtension("pgm"); init = false; - useGenericName = false; } -vpDiskGrabber::vpDiskGrabber(const char *genericName) +vpDiskGrabber::vpDiskGrabber(const char *generic_name) + : image_number(0), image_step(1), number_of_zero(0), useGenericName(false) { - strcpy(this->genericName, genericName); + setDirectory("/tmp"); + setBaseName("I"); + setExtension("pgm"); + + init = false; + if (strlen( generic_name ) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the generic name")); + } + + strcpy(this->genericName, generic_name); useGenericName = true; } @@ -82,16 +90,13 @@ vpDiskGrabber::vpDiskGrabber(const char *dir, const char *basename, long number, int step, unsigned int noz, const char *ext) + : image_number(number), image_step(step), number_of_zero(noz), useGenericName(false) { setDirectory(dir); setBaseName(basename); - setImageNumber(number); - setStep(step); - setNumberOfZero(noz); setExtension(ext); init = false; - useGenericName = false; } void @@ -244,19 +249,19 @@ vpDiskGrabber::acquire(vpImage<float> &I) Acquire an image: read a pgm image from the disk. After this call, the image number is incremented considering the step. - \param I the read image - \param image_number The index of the desired image. + \param I : The image read from a file. + \param img_number : The number of the desired image. */ void -vpDiskGrabber::acquire(vpImage<unsigned char> &I, long image_number) +vpDiskGrabber::acquire(vpImage<unsigned char> &I, long img_number) { char name[FILENAME_MAX] ; if(useGenericName) - sprintf(name,genericName,image_number) ; + sprintf(name,genericName,img_number) ; else - sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,image_number,extension) ; + sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,img_number,extension) ; vpDEBUG_TRACE(2, "load: %s\n", name); @@ -270,19 +275,19 @@ vpDiskGrabber::acquire(vpImage<unsigned char> &I, long image_number) Acquire an image: read a ppm image from the disk. After this call, the image number is incremented considering the step. - \param I the read image - \param image_number The index of the desired image. + \param I : The image read from a file. + \param img_number : The number of the desired image. */ void -vpDiskGrabber::acquire(vpImage<vpRGBa> &I, long image_number) +vpDiskGrabber::acquire(vpImage<vpRGBa> &I, long img_number) { char name[FILENAME_MAX] ; if(useGenericName) - sprintf(name,genericName,image_number) ; + sprintf(name,genericName,img_number) ; else - sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,image_number,extension) ; + sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,img_number,extension) ; vpDEBUG_TRACE(2, "load: %s\n", name); @@ -298,19 +303,19 @@ vpDiskGrabber::acquire(vpImage<vpRGBa> &I, long image_number) Acquire an image: read a pfm image from the disk. After this call, the image number is incremented considering the step. - \param I the read image - \param image_number The index of the desired image. + \param I : The image read from a file. + \param img_number : The number of the desired image. */ void -vpDiskGrabber::acquire(vpImage<float> &I, long image_number) +vpDiskGrabber::acquire(vpImage<float> &I, long img_number) { char name[FILENAME_MAX] ; if(useGenericName) - sprintf(name,genericName,image_number) ; + sprintf(name,genericName,img_number) ; else - sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,image_number,extension) ; + sprintf(name,"%s/%s%0*ld.%s",directory,base_name,number_of_zero,img_number,extension) ; vpDEBUG_TRACE(2, "load: %s\n", name); @@ -399,8 +404,13 @@ vpDiskGrabber::setNumberOfZero(unsigned int noz) } void -vpDiskGrabber::setGenericName(const char *genericName) +vpDiskGrabber::setGenericName(const char *generic_name) { - strcpy(this->genericName, genericName) ; + if (strlen( generic_name ) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the generic name")); + } + + strcpy(this->genericName, generic_name) ; useGenericName = true; } diff --git a/src/device/framegrabber/disk/vpDiskGrabber.h b/src/device/framegrabber/disk/vpDiskGrabber.h index eb3b8cfbafc4e344e425accb97278fc1a46f0e78..a85b70ca186eb5a51f894cf20ef80ea7eccc29a4 100644 --- a/src/device/framegrabber/disk/vpDiskGrabber.h +++ b/src/device/framegrabber/disk/vpDiskGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDiskGrabber.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDiskGrabber.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/framegrabber/generic-framegrabber/vpFrameGrabber.h b/src/device/framegrabber/generic-framegrabber/vpFrameGrabber.h index b9bfb74c1e5e6e76384f81270412b809f2eaad23..975adbd234566f5b2de27aee5e4b7b54e647be0b 100644 --- a/src/device/framegrabber/generic-framegrabber/vpFrameGrabber.h +++ b/src/device/framegrabber/generic-framegrabber/vpFrameGrabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFrameGrabber.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpFrameGrabber.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -121,6 +121,7 @@ public: inline unsigned int getWidth() const { return width ; } public: + vpFrameGrabber() : init(false), height(0), width(0) {}; virtual ~vpFrameGrabber() { ; } virtual void open(vpImage<unsigned char> &I) =0 ; diff --git a/src/device/framegrabber/generic-framegrabber/vpFrameGrabberException.h b/src/device/framegrabber/generic-framegrabber/vpFrameGrabberException.h index 772263d2c511887436c887db1b5e6bc7a400a293..1439f7ab2d8eefa2cf1de811219e86936dc1dc8d 100644 --- a/src/device/framegrabber/generic-framegrabber/vpFrameGrabberException.h +++ b/src/device/framegrabber/generic-framegrabber/vpFrameGrabberException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFrameGrabberException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFrameGrabberException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,37 +71,32 @@ */ class VISP_EXPORT vpFrameGrabberException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpFrameGrabber member */ - enum errorFrameGrabberCodeEnum + enum errorFrameGrabberCodeEnum { settingError, initializationError, otherError } ; -public: - vpFrameGrabberException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpFrameGrabberException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpFrameGrabberException (const int code) - : vpException(code){ ; } + public: + vpFrameGrabberException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpFrameGrabberException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpFrameGrabberException (const int id) + : vpException(id){ ; } }; - - - - #endif /* #ifndef __vpFrameGrabberException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/device/framegrabber/v4l2/vpV4l2Grabber.cpp b/src/device/framegrabber/v4l2/vpV4l2Grabber.cpp index 365f7c2418074371faf23a3acdc3e2cfd15fa2e0..57ec4c776914ee9492586265a4d6fccfc7b72146 100644 --- a/src/device/framegrabber/v4l2/vpV4l2Grabber.cpp +++ b/src/device/framegrabber/v4l2/vpV4l2Grabber.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpV4l2Grabber.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpV4l2Grabber.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -115,24 +115,14 @@ const unsigned int vpV4l2Grabber::FRAME_SIZE = 288; */ vpV4l2Grabber::vpV4l2Grabber() + : fd(-1), device(), cap(), streamparm(), inp(NULL), std(NULL), fmt(NULL), ctl(NULL), + fps(0), fmt_v4l2(), fmt_me(), reqbufs(), buf_v4l2(NULL), buf_me(NULL), queue(0), + waiton_cpt(0), index_buffer(0), m_verbose(false), m_nbuffers(3), field(0), streaming(false), + m_input(vpV4l2Grabber::DEFAULT_INPUT), + m_framerate(vpV4l2Grabber::framerate_25fps), + m_frameformat(vpV4l2Grabber::V4L2_FRAME_FORMAT), + m_pixelformat(vpV4l2Grabber::V4L2_YUYV_FORMAT) { - fd = -1; - streaming = false; - verbose = false; - field = 0; - width = 0; - height = 0; - queue = 0; - waiton_cpt= 0; - index_buffer = 0; - - inp = NULL; - std = NULL; - fmt = NULL; - ctl = NULL; - buf_v4l2 = NULL; - buf_me = NULL; - setDevice("/dev/video0"); setNBuffers(3); setFramerate(vpV4l2Grabber::framerate_25fps); @@ -184,24 +174,14 @@ vpV4l2Grabber::vpV4l2Grabber() */ vpV4l2Grabber::vpV4l2Grabber(bool verbose) + : fd(-1), device(), cap(), streamparm(), inp(NULL), std(NULL), fmt(NULL), ctl(NULL), + fps(0), fmt_v4l2(), fmt_me(), reqbufs(), buf_v4l2(NULL), buf_me(NULL), queue(0), + waiton_cpt(0), index_buffer(0), m_verbose(verbose), m_nbuffers(3), field(0), streaming(false), + m_input(vpV4l2Grabber::DEFAULT_INPUT), + m_framerate(vpV4l2Grabber::framerate_25fps), + m_frameformat(vpV4l2Grabber::V4L2_FRAME_FORMAT), + m_pixelformat(vpV4l2Grabber::V4L2_YUYV_FORMAT) { - fd = -1; - streaming = false; - this->verbose = verbose; - field = 0; - width = 0; - height = 0; - queue = 0; - waiton_cpt= 0; - index_buffer = 0; - - inp = NULL; - std = NULL; - fmt = NULL; - ctl = NULL; - buf_v4l2 = NULL; - buf_me = NULL; - setDevice("/dev/video0"); setNBuffers(3); setFramerate(vpV4l2Grabber::framerate_25fps); @@ -244,24 +224,14 @@ vpV4l2Grabber::vpV4l2Grabber(bool verbose) \endcode */ vpV4l2Grabber::vpV4l2Grabber(unsigned input, unsigned scale) + : fd(-1), device(), cap(), streamparm(), inp(NULL), std(NULL), fmt(NULL), ctl(NULL), + fps(0), fmt_v4l2(), fmt_me(), reqbufs(), buf_v4l2(NULL), buf_me(NULL), queue(0), + waiton_cpt(0), index_buffer(0), m_verbose(false), m_nbuffers(3), field(0), streaming(false), + m_input(vpV4l2Grabber::DEFAULT_INPUT), + m_framerate(vpV4l2Grabber::framerate_25fps), + m_frameformat(vpV4l2Grabber::V4L2_FRAME_FORMAT), + m_pixelformat(vpV4l2Grabber::V4L2_YUYV_FORMAT) { - fd = -1; - streaming = false; - verbose = false; - field = 0; - width = 0; - height = 0; - queue = 0; - waiton_cpt= 0; - index_buffer = 0; - - inp = NULL; - std = NULL; - fmt = NULL; - ctl = NULL; - buf_v4l2 = NULL; - buf_me = NULL; - setDevice("/dev/video0"); setNBuffers(3); setFramerate(vpV4l2Grabber::framerate_25fps); @@ -302,26 +272,15 @@ vpV4l2Grabber::vpV4l2Grabber(unsigned input, unsigned scale) \endcode */ -vpV4l2Grabber::vpV4l2Grabber(vpImage<unsigned char> &I, - unsigned input, unsigned scale ) +vpV4l2Grabber::vpV4l2Grabber(vpImage<unsigned char> &I, unsigned input, unsigned scale ) + : fd(-1), device(), cap(), streamparm(), inp(NULL), std(NULL), fmt(NULL), ctl(NULL), + fps(0), fmt_v4l2(), fmt_me(), reqbufs(), buf_v4l2(NULL), buf_me(NULL), queue(0), + waiton_cpt(0), index_buffer(0), m_verbose(false), m_nbuffers(3), field(0), streaming(false), + m_input(vpV4l2Grabber::DEFAULT_INPUT), + m_framerate(vpV4l2Grabber::framerate_25fps), + m_frameformat(vpV4l2Grabber::V4L2_FRAME_FORMAT), + m_pixelformat(vpV4l2Grabber::V4L2_YUYV_FORMAT) { - fd = -1; - streaming = false; - verbose = false; - field = 0; - width = 0; - height = 0; - queue = 0; - waiton_cpt= 0; - index_buffer = 0; - - inp = NULL; - std = NULL; - fmt = NULL; - ctl = NULL; - buf_v4l2 = NULL; - buf_me = NULL; - setDevice("/dev/video0"); setNBuffers(3); setFramerate(vpV4l2Grabber::framerate_25fps); @@ -339,8 +298,8 @@ vpV4l2Grabber::vpV4l2Grabber(vpImage<unsigned char> &I, Setup the Video For Linux Two (V4L2) driver in streaming mode. \param I : Image data structure (Color RGB32 bits image) - \param _input : Video input port. - \param _scale : Decimation factor. + \param input : Video input port. + \param scale : Decimation factor. Default settings are: @@ -365,30 +324,20 @@ vpV4l2Grabber::vpV4l2Grabber(vpImage<unsigned char> &I, \endcode */ -vpV4l2Grabber::vpV4l2Grabber(vpImage<vpRGBa> &I, unsigned _input, unsigned _scale ) +vpV4l2Grabber::vpV4l2Grabber(vpImage<vpRGBa> &I, unsigned input, unsigned scale ) + : fd(-1), device(), cap(), streamparm(), inp(NULL), std(NULL), fmt(NULL), ctl(NULL), + fps(0), fmt_v4l2(), fmt_me(), reqbufs(), buf_v4l2(NULL), buf_me(NULL), queue(0), + waiton_cpt(0), index_buffer(0), m_verbose(false), m_nbuffers(3), field(0), streaming(false), + m_input(vpV4l2Grabber::DEFAULT_INPUT), + m_framerate(vpV4l2Grabber::framerate_25fps), + m_frameformat(vpV4l2Grabber::V4L2_FRAME_FORMAT), + m_pixelformat(vpV4l2Grabber::V4L2_YUYV_FORMAT) { - fd = -1; - streaming = false; - verbose = false; - field = 0; - width = 0; - height = 0; - queue = 0; - waiton_cpt= 0; - index_buffer = 0; - - inp = NULL; - std = NULL; - fmt = NULL; - ctl = NULL; - buf_v4l2 = NULL; - buf_me = NULL; - setDevice("/dev/video0"); setNBuffers(3); setFramerate(vpV4l2Grabber::framerate_25fps); - setInput(_input); - setScale(_scale); + setInput(input); + setScale(scale); init = false; @@ -411,7 +360,7 @@ vpV4l2Grabber::~vpV4l2Grabber() void vpV4l2Grabber::setInput(unsigned input) { - this->input = input; + this->m_input = input; } /*! @@ -457,9 +406,9 @@ vpV4l2Grabber::open(vpImage<unsigned char> &I) { open(); - if( v4l2_ioctl (fd, VIDIOC_S_INPUT, &input) == -1 ) + if( v4l2_ioctl (fd, VIDIOC_S_INPUT, &m_input) == -1 ) { - std::cout << "Warning: cannot set input channel to " << input << std::endl; + std::cout << "Warning: cannot set input channel to " << m_input << std::endl; } vpV4l2PixelFormatType req_pixelformat = getPixelFormat(); @@ -470,7 +419,7 @@ vpV4l2Grabber::open(vpImage<unsigned char> &I) startStreaming(); } catch(...) { - if (verbose) { + if (m_verbose) { std::cout << "Requested pixel format [" << req_pixelformat << "] not compatible with camera" << std::endl; std::cout << "Try to found a compatible pixel format..." << std::endl; @@ -485,15 +434,15 @@ vpV4l2Grabber::open(vpImage<unsigned char> &I) setPixelFormat((vpV4l2PixelFormatType)format); setFormat(); startStreaming(); - if (verbose) - std::cout << "This format [" << pixelformat + if (m_verbose) + std::cout << "This format [" << m_pixelformat << "] is compatible with camera" << std::endl; break; } catch (...) { - if (verbose) - std::cout << "This format [" << pixelformat + if (m_verbose) + std::cout << "This format [" << m_pixelformat << "] is not compatible with camera" << std::endl; if (format == (int)V4L2_MAX_FORMAT) { std::cout << "No pixel format compatible with the camera was found" << std::endl; @@ -525,9 +474,9 @@ vpV4l2Grabber::open(vpImage<vpRGBa> &I) { open(); - if( v4l2_ioctl (fd, VIDIOC_S_INPUT, &input) == -1 ) + if( v4l2_ioctl (fd, VIDIOC_S_INPUT, &m_input) == -1 ) { - std::cout << "Warning: cannot set input channel to " << input << std::endl; + std::cout << "Warning: cannot set input channel to " << m_input << std::endl; } vpV4l2PixelFormatType req_pixelformat = getPixelFormat(); @@ -538,8 +487,8 @@ vpV4l2Grabber::open(vpImage<vpRGBa> &I) startStreaming(); } catch(...) { - if (verbose) { - std::cout << "Requested pixel format [" << pixelformat + if (m_verbose) { + std::cout << "Requested pixel format [" << m_pixelformat << "] not compatible with camera" << std::endl; std::cout << "Try to found a compatible pixel format..." << std::endl; } @@ -553,15 +502,15 @@ vpV4l2Grabber::open(vpImage<vpRGBa> &I) setPixelFormat((vpV4l2PixelFormatType)format); setFormat(); startStreaming(); - if (verbose) - std::cout << "This format [" << pixelformat + if (m_verbose) + std::cout << "This format [" << m_pixelformat << "] is compatible with camera" << std::endl; break; } catch (...) { - if (verbose) - std::cout << "This format [" << pixelformat + if (m_verbose) + std::cout << "This format [" << m_pixelformat << "] is not compatible with camera" << std::endl; } @@ -628,7 +577,7 @@ vpV4l2Grabber::acquire(vpImage<unsigned char> &I, struct timeval ×tamp) if ((I.getWidth() != width)||(I.getHeight() != height)) I.resize(height, width) ; - switch(pixelformat) { + switch(m_pixelformat) { case V4L2_GREY_FORMAT: memcpy(I.bitmap, bitmap, height * width*sizeof(unsigned char)); break; @@ -711,7 +660,7 @@ vpV4l2Grabber::acquire(vpImage<vpRGBa> &I, struct timeval ×tamp) // The framegrabber acquire aRGB format. We just shift the data from 1 byte all the data and initialize the last byte - switch(pixelformat) { + switch(m_pixelformat) { case V4L2_GREY_FORMAT: vpImageConvert::GreyToRGBa((unsigned char *) bitmap, (unsigned char *) I.bitmap, width*height); break; @@ -784,7 +733,7 @@ vpV4l2Grabber::getField() void vpV4l2Grabber::setFramerate(vpV4l2Grabber::vpV4l2FramerateType framerate) { - this->framerate = framerate; + this->m_framerate = framerate; if (framerate == vpV4l2Grabber::framerate_25fps) setFrameFormat(V4L2_IMAGE_FORMAT); @@ -805,7 +754,7 @@ vpV4l2Grabber::setFramerate(vpV4l2Grabber::vpV4l2FramerateType framerate) vpV4l2Grabber::vpV4l2FramerateType vpV4l2Grabber::getFramerate() { - return framerate; + return m_framerate; } @@ -894,7 +843,7 @@ vpV4l2Grabber::open() throw (vpFrameGrabberException(vpFrameGrabberException::otherError, "Is not a V4L2 device") ); } - if (verbose) { + if (m_verbose) { fprintf(stdout, "v4l2 info:\n" " device: %s\n" " %s %d.%d.%d / %s @ %s\n", @@ -919,11 +868,21 @@ vpV4l2Grabber::open() if (cap.capabilities & V4L2_CAP_STREAMING) fprintf(stdout, " Support streaming capture.\n"); else - fprintf(stdout, " Does not support streaming capture.\n"); + fprintf(stdout, " Does not support streaming capture\n"); if(cap.capabilities & V4L2_CAP_ASYNCIO) - fprintf(stdout, " Support asynchronous I/O methods.\n"); + fprintf(stdout, " Support asynchronous I/O methods\n"); else - fprintf(stdout, " Does not support asynchronous I/O methods.\n"); + fprintf(stdout, " Does not support asynchronous I/O methods\n"); + if(cap.capabilities & V4L2_CAP_TIMEPERFRAME) + fprintf(stdout, " Support time per frame field\n"); + else + fprintf(stdout, " Does not support time per frame field\n"); + // Get framerate + streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(fd, VIDIOC_G_PARM, &streamparm) != -1) { + fprintf(stdout, " Current acquisition framerate: %d fps\n", + streamparm.parm.output.timeperframe.denominator); + } } getCapabilities(); @@ -982,30 +941,30 @@ vpV4l2Grabber::getCapabilities() void vpV4l2Grabber::setFormat() { - fmt_me.width = _width; - fmt_me.height = _height; - //fmt_me.bytesperline = _width; // bad (normally _width * depth / 8), but works + fmt_me.width = width; + fmt_me.height = height; + //fmt_me.bytesperline = width; // bad (normally width * depth / 8), but works // because initialized later by an ioctl call to VIDIOC_S_FMT - switch(pixelformat) { + switch(m_pixelformat) { case V4L2_GREY_FORMAT : fmt_me.pixelformat = V4L2_PIX_FMT_GREY; - if (verbose) + if (m_verbose) fprintf(stdout,"v4l2: new capture params (V4L2_PIX_FMT_GREY)\n"); break; case V4L2_RGB24_FORMAT: fmt_me.pixelformat = V4L2_PIX_FMT_RGB24; - if (verbose) + if (m_verbose) fprintf(stdout,"v4l2: new capture params (V4L2_PIX_FMT_RGB24)\n"); break; case V4L2_RGB32_FORMAT: fmt_me.pixelformat = V4L2_PIX_FMT_RGB32; - if (verbose) + if (m_verbose) fprintf(stdout,"v4l2: new capture params (V4L2_PIX_FMT_RGB32)\n"); break; case V4L2_BGR24_FORMAT: fmt_me.pixelformat = V4L2_PIX_FMT_BGR24; - if (verbose) + if (m_verbose) fprintf(stdout,"v4l2: new capture params (V4L2_PIX_FMT_BGR24)\n"); break; case V4L2_YUYV_FORMAT: fmt_me.pixelformat = V4L2_PIX_FMT_YUYV; - if (verbose) + if (m_verbose) fprintf(stdout,"v4l2: new capture params (V4L2_PIX_FMT_YUYV)\n"); break; @@ -1033,14 +992,14 @@ vpV4l2Grabber::setFormat() //printf("1 - w: %d h: %d\n", fmt_v4l2.fmt.pix.width, fmt_v4l2.fmt.pix.height); - switch (frameformat) { + switch (m_frameformat) { case V4L2_FRAME_FORMAT: fmt_v4l2.fmt.pix.field = V4L2_FIELD_ALTERNATE; - if (verbose) { + if (m_verbose) { fprintf(stdout,"v4l2: new capture params (V4L2_FIELD_ALTERNATE)\n"); } break; case V4L2_IMAGE_FORMAT: fmt_v4l2.fmt.pix.field = V4L2_FIELD_INTERLACED; - if (verbose) { + if (m_verbose) { fprintf(stdout,"v4l2: new capture params (V4L2_FIELD_INTERLACED)\n"); } break; @@ -1052,9 +1011,7 @@ vpV4l2Grabber::setFormat() } //height and width of the captured image or frame - width = _width; - height = _height; - if( frameformat == V4L2_FRAME_FORMAT && height > FRAME_SIZE ) + if( m_frameformat == V4L2_FRAME_FORMAT && height > FRAME_SIZE ) { height = FRAME_SIZE; } @@ -1082,7 +1039,7 @@ vpV4l2Grabber::setFormat() fmt_me.height = fmt_v4l2.fmt.pix.height; fmt_me.bytesperline = fmt_v4l2.fmt.pix.bytesperline; - if (verbose) { + if (m_verbose) { fprintf(stdout,"v4l2: new capture params (%dx%d, %c%c%c%c, %d byte, %d bytes per line)\n", fmt_me.width, fmt_me.height, fmt_v4l2.fmt.pix.pixelformat & 0xff, @@ -1112,7 +1069,7 @@ vpV4l2Grabber::startStreaming() /* setup buffers */ memset (&(reqbufs), 0, sizeof (reqbufs)); - reqbufs.count = nbuffers; + reqbufs.count = m_nbuffers; reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbufs.memory = V4L2_MEMORY_MMAP; @@ -1144,7 +1101,7 @@ vpV4l2Grabber::startStreaming() memcpy(&buf_me[i].fmt, &fmt_me, sizeof(ng_video_fmt)); buf_me[i].size = buf_me[i].fmt.bytesperline * buf_me[i].fmt.height; - // if (verbose) + // if (m_verbose) // std::cout << "1: buf_v4l2[" << i << "].length: " << buf_v4l2[i].length // << " buf_v4l2[" << i << "].offset: " << buf_v4l2[i].m.offset // << std::endl; @@ -1163,7 +1120,7 @@ vpV4l2Grabber::startStreaming() buf_me[i].refcount = 0; -// if (verbose) +// if (m_verbose) // { // std::cout << "2: buf_v4l2[" << i << "].length: " << buf_v4l2[i].length // << " buf_v4l2[" << i << "].offset: " << buf_v4l2[i].m.offset @@ -1171,7 +1128,7 @@ vpV4l2Grabber::startStreaming() // std::cout << "2: buf_me[" << i << "].size: " << buf_me[i].size << std::endl; // } - if (verbose) + if (m_verbose) printBufInfo(buf_v4l2[i]); } @@ -1211,7 +1168,7 @@ vpV4l2Grabber::stopStreaming() } /* free buffers */ for (i = 0; i < reqbufs.count; i++) { - if (verbose) + if (m_verbose) printBufInfo(buf_v4l2[i]); //vpTRACE("v4l2_munmap()"); @@ -1306,7 +1263,7 @@ vpV4l2Grabber::waiton(__u32 &index, struct timeval ×tamp) timestamp = buf_v4l2[index].timestamp; - // if(verbose) + // if(m_verbose) // { // vpERROR_TRACE("field: %d\n", buf_v4l2[index].field); diff --git a/src/device/framegrabber/v4l2/vpV4l2Grabber.h b/src/device/framegrabber/v4l2/vpV4l2Grabber.h index da741f4d570e7228feb6efb0177a668819c66483..08d4ad225087b9a6f88f959c5c8b908c6bd6d709 100644 --- a/src/device/framegrabber/v4l2/vpV4l2Grabber.h +++ b/src/device/framegrabber/v4l2/vpV4l2Grabber.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpV4l2Grabber.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpV4l2Grabber.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -200,9 +200,9 @@ public: vpV4l2Grabber(bool verbose); vpV4l2Grabber(unsigned input, unsigned scale = vpV4l2Grabber::DEFAULT_SCALE); vpV4l2Grabber(vpImage<unsigned char> &I, - unsigned input, unsigned scale = vpV4l2Grabber::DEFAULT_SCALE); + unsigned input, unsigned scale = vpV4l2Grabber::DEFAULT_SCALE); vpV4l2Grabber(vpImage<vpRGBa> &I, - unsigned input, unsigned scale = vpV4l2Grabber::DEFAULT_SCALE); + unsigned input, unsigned scale = vpV4l2Grabber::DEFAULT_SCALE); virtual ~vpV4l2Grabber() ; public: @@ -224,14 +224,14 @@ public: */ inline vpV4l2PixelFormatType getPixelFormat() { - return (this->pixelformat); + return (this->m_pixelformat); } /*! Activates the verbose mode to print additional information on stdout. \param verbose : If true activates the verbose mode. */ void setVerboseMode(bool verbose) { - this->verbose = verbose; + this->m_verbose = verbose; }; void setFramerate(vpV4l2FramerateType framerate); @@ -241,17 +241,17 @@ public: Set image width to acquire. */ - inline void setWidth(unsigned width) + inline void setWidth(unsigned w) { - this->_width = width; + this->width = w; } /*! Set image height to acquire. */ - inline void setHeight(unsigned height) + inline void setHeight(unsigned h) { - this->_height = height; + this->height = h; } void setScale(unsigned scale = vpV4l2Grabber::DEFAULT_SCALE) ; @@ -269,7 +269,7 @@ public: */ inline void setNBuffers(unsigned nbuffers) { - this->nbuffers = nbuffers; + this->m_nbuffers = nbuffers; } /*! @@ -278,9 +278,9 @@ public: \param devname : Device name (like /dev/video0). */ - inline void setDevice(const char *devname) + inline void setDevice(const std::string &devname) { - sprintf(device, "%s", devname); + sprintf(device, "%s", devname.c_str()); } /*! @@ -292,9 +292,9 @@ public: */ inline void setPixelFormat(vpV4l2PixelFormatType pixelformat) { - this->pixelformat = pixelformat; - if (this->pixelformat >= V4L2_MAX_FORMAT) - this->pixelformat = V4L2_RGB24_FORMAT; + this->m_pixelformat = pixelformat; + if (this->m_pixelformat >= V4L2_MAX_FORMAT) + this->m_pixelformat = V4L2_RGB24_FORMAT; } void close(); @@ -311,7 +311,7 @@ private: */ inline void setFrameFormat(vpV4l2FrameFormatType frameformat) { - this->frameformat = frameformat; + this->m_frameformat = frameformat; } void open(); void getCapabilities(); @@ -344,17 +344,15 @@ private: unsigned int waiton_cpt; __u32 index_buffer; //!< index of the buffer in use - bool verbose; - unsigned nbuffers; + bool m_verbose; + unsigned m_nbuffers; unsigned int field; bool streaming; - unsigned input; - unsigned _width; - unsigned _height; - vpV4l2FramerateType framerate; - vpV4l2FrameFormatType frameformat; - vpV4l2PixelFormatType pixelformat; + unsigned m_input; + vpV4l2FramerateType m_framerate; + vpV4l2FrameFormatType m_frameformat; + vpV4l2PixelFormatType m_pixelformat; } ; #endif diff --git a/src/device/kinect/vpKinect.cpp b/src/device/kinect/vpKinect.cpp index cfab762a013468ae6f9027d86bca29d5b16f430c..1f032d0e570cf2578f299f504ce9586bd490c8f5 100644 --- a/src/device/kinect/vpKinect.cpp +++ b/src/device/kinect/vpKinect.cpp @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpKinect.cpp 4329 2013-07-20 07:06:49Z fspindle $ + * $Id: vpKinect.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,7 +56,10 @@ */ vpKinect::vpKinect(freenect_context *ctx, int index) : Freenect::FreenectDevice(ctx, index), + m_rgb_mutex(), m_depth_mutex(), RGBcam(), IRcam(), + rgbMir(), irMrgb(), DMres(DMAP_LOW_RES), hd(240), wd(320), + dmap(), IRGB(), m_new_rgb_frame(false), m_new_depth_map(false), m_new_depth_image(false), @@ -79,7 +82,7 @@ vpKinect::~vpKinect() void vpKinect::start(vpKinect::vpDMResolution res) { - DMres = res; + DMres = res; height = 480; width = 640; //!Calibration parameters are the parameters found for our Kinect device. Note that they can differ from one device to another. @@ -240,12 +243,12 @@ bool vpKinect::getDepthMap(vpImage<float>& map,vpImage<unsigned char>& Imap) /*! Get RGB image */ -bool vpKinect::getRGB(vpImage<vpRGBa>& IRGB) +bool vpKinect::getRGB(vpImage<vpRGBa>& I_RGB) { vpMutex::vpScopedLock lock(m_rgb_mutex); if (!m_new_rgb_frame) return false; - IRGB = this->IRGB; + I_RGB = this->IRGB; m_new_rgb_frame = false; return true; } diff --git a/src/device/kinect/vpKinect.h b/src/device/kinect/vpKinect.h index 5eaab28c569a87d92326204f2c35492987fc1e7c..d4b2d99b8968d20f0959d17eb9ca588642834d8f 100644 --- a/src/device/kinect/vpKinect.h +++ b/src/device/kinect/vpKinect.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpKinect.h 4131 2013-02-11 20:45:31Z fspindle $ + * $Id: vpKinect.h 4574 2014-01-09 08:48:51Z fspindle $ * * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/device/laserscanner/sick/vpSickLDMRS.cpp b/src/device/laserscanner/sick/vpSickLDMRS.cpp index 86de3d627359f0a67add8fb37aa61befd2d4107d..78fdf6b098baea67b6f16de989df667301d45445 100644 --- a/src/device/laserscanner/sick/vpSickLDMRS.cpp +++ b/src/device/laserscanner/sick/vpSickLDMRS.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSickLDMRS.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSickLDMRS.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,22 +39,24 @@ * *****************************************************************************/ -#if ( defined(UNIX) && !defined(WIN32) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) #include <visp/vpSickLDMRS.h> #include <visp/vpMath.h> #include <visp/vpDebug.h> #include <visp/vpTime.h> #include <sys/socket.h> +#include <sys/select.h> #include <netinet/in.h> #include <fcntl.h> #include <errno.h> #include <netdb.h> #include <string.h> -#include <strings.h> +//#include <strings.h> #include <math.h> #include <assert.h> #include <stdlib.h> +#include <limits.h> @@ -72,19 +74,18 @@ body messages. */ vpSickLDMRS::vpSickLDMRS() + : socket_fd(-1), body(NULL), vAngle(), time_offset(0), + isFirstMeasure(true), maxlen_body(104000) { ip = "131.254.12.119"; port = 12002; - body = new unsigned char [104000]; - isFirstMeasure = true; - time_offset = 0; + body = new unsigned char [maxlen_body]; vAngle.resize(4); // Vertical angle of the 4 layers vAngle[0] = vpMath::rad(-1.2); vAngle[1] = vpMath::rad(-0.4); vAngle[2] = vpMath::rad( 0.4); vAngle[3] = vpMath::rad( 1.2); - } /*! @@ -99,16 +100,16 @@ vpSickLDMRS::~vpSickLDMRS() /*! Initialize the connexion with the Sick LD-MRS laser scanner. - \param ip : Ethernet address of the laser. - \param port : Ethernet port of the laser. + \param ip_address : Ethernet address of the laser. + \param com_port : Ethernet port of the laser. \return true if the device was initialized, false otherwise. */ -bool vpSickLDMRS::setup(std::string ip, int port) +bool vpSickLDMRS::setup(std::string ip_address, int com_port) { - setIpAddress( ip ); - setPort( port ); + setIpAddress( ip_address ); + setPort( com_port ); return ( this->setup() ); } @@ -130,7 +131,8 @@ bool vpSickLDMRS::setup() fprintf(stderr, "Failed to create socket\n"); return false; } - bzero(&serv_addr, sizeof(serv_addr)); + //bzero(&serv_addr, sizeof(serv_addr)); + memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; // Internet/IP serv_addr.sin_addr.s_addr = inet_addr(ip.c_str()); // IP address serv_addr.sin_port = htons(port); // server port @@ -266,6 +268,9 @@ bool vpSickLDMRS::measure(vpLaserScan laserscan[4]) double rDist; // radial distance in meters vpScanPoint scanPoint; + if (numPoints > USHRT_MAX-2) + throw(vpException (vpException::ioError, "Out of range number of point")); + for (int i=0; i < numPoints; i++) { ushortptr = (unsigned short *) (body+44+i*10); unsigned char layer = ((unsigned char) body[44+i*10])&0x0F; diff --git a/src/device/laserscanner/sick/vpSickLDMRS.h b/src/device/laserscanner/sick/vpSickLDMRS.h index b6fd478153308f42df68ed6852ff3571775fccad..062517c5536ec4fddd8403ba6dd7a5b26978de39 100644 --- a/src/device/laserscanner/sick/vpSickLDMRS.h +++ b/src/device/laserscanner/sick/vpSickLDMRS.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSickLDMRS.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSickLDMRS.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,17 +43,18 @@ #include <visp/vpConfig.h> -#if ( defined(UNIX) && !defined(WIN32) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) #include <arpa/inet.h> #include <iostream> #include <vector> - +#include <string.h> #include <visp/vpScanPoint.h> #include <visp/vpLaserScan.h> #include <visp/vpLaserScanner.h> #include <visp/vpColVector.h> +#include <visp/vpException.h> /*! @@ -80,7 +81,7 @@ int main() { -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX std::string ip = "131.254.12.119"; vpSickLDMRS laser; @@ -98,7 +99,7 @@ int main() vpScanPoint p; for (unsigned int i=0; i < pointsInLayer.size(); i++) { - std::cout << pointsInLayer[i] << std::endl; + std::cout << pointsInLayer[i] << std::endl; } } } @@ -117,26 +118,42 @@ class VISP_EXPORT vpSickLDMRS : public vpLaserScanner }; vpSickLDMRS(); /*! Copy constructor. */ - vpSickLDMRS(const vpSickLDMRS &sick) : vpLaserScanner(sick) { + vpSickLDMRS(const vpSickLDMRS &sick) + : vpLaserScanner(sick), socket_fd(-1), body(NULL), vAngle(), time_offset(0), + isFirstMeasure(true), maxlen_body(104000) + { + *this = sick; + }; + virtual ~vpSickLDMRS(); + /*! Copy constructor. */ + vpSickLDMRS &operator=(const vpSickLDMRS &sick) + { socket_fd = sick.socket_fd; + vAngle = sick.vAngle; + time_offset = sick.time_offset; + isFirstMeasure = sick.isFirstMeasure; + maxlen_body = sick.maxlen_body; + if (body) delete [] body; body = new unsigned char [104000]; + memcpy(body, sick.body, maxlen_body); + return (*this); }; - virtual ~vpSickLDMRS(); + bool setup(std::string ip, int port); bool setup(); bool measure(vpLaserScan laserscan[4]); protected: -#ifdef WIN32 +#if defined(_WIN32) SOCKET socket_fd; #else int socket_fd; #endif - private: unsigned char *body; vpColVector vAngle; // constant vertical angle for each layer double time_offset; bool isFirstMeasure; + size_t maxlen_body; }; #endif diff --git a/src/device/laserscanner/vpLaserScan.h b/src/device/laserscanner/vpLaserScan.h index 6ca3763fa613a085d826a07d5dd516b42eaf0993..b2e57ae59ad5a367fcf385f793eb76364095c609 100644 --- a/src/device/laserscanner/vpLaserScan.h +++ b/src/device/laserscanner/vpLaserScan.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpLaserScan.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLaserScan.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -67,17 +67,16 @@ class VISP_EXPORT vpLaserScan { public: /*! Default constructor that initialize all the internal variable to zero. */ - vpLaserScan() { - startTimestamp = 0; - endTimestamp = 0; - measurementId = 0; - numSteps = 0; - startAngle = 0; - stopAngle = 0; - numPoints = 0; + vpLaserScan() + : listScanPoints(), startTimestamp(0), endTimestamp(0), measurementId(0), + numSteps(0), startAngle(0), stopAngle(0), numPoints(0) + { } /*! Copy constructor. */ - vpLaserScan(const vpLaserScan &scan) { + vpLaserScan(const vpLaserScan &scan) + : listScanPoints(), startTimestamp(0), endTimestamp(0), measurementId(0), + numSteps(0), startAngle(0), stopAngle(0), numPoints(0) + { startTimestamp = scan.startTimestamp; endTimestamp = scan.endTimestamp; measurementId = scan.measurementId; @@ -103,32 +102,32 @@ class VISP_EXPORT vpLaserScan } /*! Specifies the id of former measurements and increases with every measurement. */ - inline void setMeasurementId(const unsigned short &measurementId) { - this->measurementId = measurementId; + inline void setMeasurementId(const unsigned short &id) { + this->measurementId = id; } /*! Start time of measurement. */ - inline void setStartTimestamp(const double &startTimestamp) { - this->startTimestamp = startTimestamp; + inline void setStartTimestamp(const double &start_timestamp) { + this->startTimestamp = start_timestamp; } /*! End time of measurement. */ - inline void setEndTimestamp(const double &endTimestamp) { - this->endTimestamp = endTimestamp; + inline void setEndTimestamp(const double &end_timestamp) { + this->endTimestamp = end_timestamp; } /*! Angular steps per scanner rotation. */ - inline void setNumSteps(const unsigned short &numSteps) { - this->numSteps = numSteps; + inline void setNumSteps(const unsigned short &num_steps) { + this->numSteps = num_steps; } /*! Start angle of the measurement in angular steps. */ - inline void setStartAngle(const short &startAngle) { - this->startAngle = startAngle; + inline void setStartAngle(const short &start_angle) { + this->startAngle = start_angle; } /*! Stop angle of the measurement in angular steps. */ - inline void setStopAngle(const short &stopAngle) { - this->stopAngle = stopAngle; + inline void setStopAngle(const short &stop_angle) { + this->stopAngle = stop_angle; } /*! Number of measured points of the measurement. */ - inline void setNumPoints(const unsigned short &numPoints) { - this->numPoints = numPoints; + inline void setNumPoints(const unsigned short &num_points) { + this->numPoints = num_points; } /*! Return the measurement start time. */ inline double getStartTimestamp() { diff --git a/src/device/laserscanner/vpLaserScanner.h b/src/device/laserscanner/vpLaserScanner.h index 5c986f4e525a36c58851a4b4e1460f50d11e01f9..bc30f0d71a49ea30085c50b5534559b757e6f195 100644 --- a/src/device/laserscanner/vpLaserScanner.h +++ b/src/device/laserscanner/vpLaserScanner.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpLaserScanner.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLaserScanner.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -61,12 +61,11 @@ class VISP_EXPORT vpLaserScanner { public: /*! Default constructor that initialize all the internal variable to zero. */ - vpLaserScanner() { - ip = "null"; - port = 0; - }; + vpLaserScanner() : ip("null"), port(0) {}; /*! Copy constructor. */ - vpLaserScanner(const vpLaserScanner &scanner) { + vpLaserScanner(const vpLaserScanner &scanner) + : ip("null"), port(0) + { ip = scanner.ip; port = scanner.port; }; @@ -74,15 +73,15 @@ class VISP_EXPORT vpLaserScanner virtual ~vpLaserScanner() {}; /*! Set the Ethernet address of the laser. */ - void setIpAddress(std::string ip) { - this->ip = ip; + void setIpAddress(std::string ip_address) { + this->ip = ip_address; }; - + /*! Set the communication port. */ - void setPort(int port) { - this->port = port; + void setPort(int com_port) { + this->port = com_port; }; - + protected: std::string ip; int port; diff --git a/src/device/laserscanner/vpScanPoint.h b/src/device/laserscanner/vpScanPoint.h index 74e135b74d0ff796e60e20267d544958eedc83d1..cd8533e3437a3176c68fdbc77a2f608e1aa2450f 100644 --- a/src/device/laserscanner/vpScanPoint.h +++ b/src/device/laserscanner/vpScanPoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpScanPoint.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpScanPoint.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,6 +44,7 @@ #include <visp/vpMath.h> #include <ostream> +#include <sstream> #include <cmath> // std::fabs #include <limits> // numeric_limits #include <math.h> @@ -75,40 +76,38 @@ class /* VISP_EXPORT */ vpScanPoint // Note that here VISP_EXPORT should not be { public: /*! Default constructor. */ - inline vpScanPoint() { - this->rDist = 0; - this->hAngle = 0; - this->vAngle = 0; - } + inline vpScanPoint() : rDist(0), hAngle(0), vAngle(0) {} /*! Copy constructor. */ - inline vpScanPoint(const vpScanPoint &scanpoint) { + inline vpScanPoint(const vpScanPoint &scanpoint) : rDist(0), hAngle(0), vAngle(0) { this->rDist = scanpoint.rDist; this->hAngle = scanpoint.hAngle; this->vAngle = scanpoint.vAngle; } /*! Set the polar point coordinates. - \param rDist : Radial distance in meter. - \param hAngle : Horizontal angle in radian. - \param vAngle : Vertical angle in radian. + \param r_dist : Radial distance in meter. + \param h_angle : Horizontal angle in radian. + \param v_angle : Vertical angle in radian. */ - inline vpScanPoint(double rDist, double hAngle, double vAngle) { - this->rDist = rDist; - this->hAngle = hAngle; - this->vAngle = vAngle; + inline vpScanPoint(double r_dist, double h_angle, double v_angle) + : rDist(r_dist), hAngle(h_angle), vAngle(v_angle) + { + this->rDist = r_dist; + this->hAngle = h_angle; + this->vAngle = v_angle; } /*! Destructor that does nothing. */ inline virtual ~vpScanPoint() {}; /*! Set the polar point coordinates. - \param rDist : Radial distance in meter. - \param hAngle : Horizontal angle in radian. - \param vAngle : Vertical angle in radian. + \param r_dist : Radial distance in meter. + \param h_angle : Horizontal angle in radian. + \param v_angle : Vertical angle in radian. */ - inline void setPolar(double rDist, double hAngle, double vAngle) { - this->rDist = rDist; - this->hAngle = hAngle; - this->vAngle = vAngle; + inline void setPolar(double r_dist, double h_angle, double v_angle) { + this->rDist = r_dist; + this->hAngle = h_angle; + this->vAngle = v_angle; } /*! Return the radial distance in meter. @@ -250,13 +249,18 @@ std::cout << p << std::endl; */ inline std::ostream &operator << (std::ostream &s, const vpScanPoint &p) { - s.precision(10); - s << p.getRadialDist() << " " - << p.getHAngle() << " " - << p.getVAngle() << " " - << p.getX() << " " - << p.getY() << " " << p.getZ(); - return s; - } + std::ios_base::fmtflags original_flags = s.flags(); + + s.precision(10); + s << p.getRadialDist() << " " + << p.getHAngle() << " " + << p.getVAngle() << " " + << p.getX() << " " + << p.getY() << " " << p.getZ(); + + s.setf(original_flags); // restore s to standard state + + return s; +} #endif diff --git a/src/device/light/vpRingLight.cpp b/src/device/light/vpRingLight.cpp index d16a526aa816738340669e4cf21a94d0aa3fa5b4..74a5924c7beea95d1d6d0aecaef7a73294d53e16 100644 --- a/src/device/light/vpRingLight.cpp +++ b/src/device/light/vpRingLight.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRingLight.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRingLight.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ Turn the ring light off. */ -vpRingLight::vpRingLight() +vpRingLight::vpRingLight() : parport() { off(); } diff --git a/src/device/light/vpRingLight.h b/src/device/light/vpRingLight.h index 6d349c37fd4c34cfbd6d9d7dee805a29b8dd191d..b1784f401abdee3c68e60bd93037a0d701a62eb5 100644 --- a/src/device/light/vpRingLight.h +++ b/src/device/light/vpRingLight.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRingLight.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRingLight.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/exceptions/vpException.cpp b/src/exceptions/vpException.cpp index c0af7b8b25c9a7e23f3fc258fdb1f99aad005bf0..c5eda2153f762d3744fd651c5d8fc1fb61bf6201 100644 --- a/src/exceptions/vpException.cpp +++ b/src/exceptions/vpException.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpException.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpException.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,44 +44,37 @@ \brief error that can be emited by the vp class and its derivates */ +#include <stdio.h> #include "visp/vpException.h" /* ------------------------------------------------------------------------- */ /* --- CONSTRUCTORS -------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ - - -vpException::vpException (int _code) - : - code (_code), - message () - +vpException::vpException (int id) + : code (id), message () { - return ; } - -vpException::vpException (int _code, - const std::string & _msg) - : - code (_code), - message (_msg) - +vpException::vpException (int id, const std::string & msg) + : code (id), message (msg) { - return ; } - -vpException::vpException (int _code, - const char * _msg) - : - code (_code), - message (_msg) +vpException::vpException (int id, const char* format, ...) + : code (id), message () { - return ; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); } +vpException::vpException (const int id, const char* format, va_list args) + : code (id), message () +{ + setMessage(format, args); +} /* ------------------------------------------------------------------------ */ /* --- DESTRUCTORS -------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ @@ -92,11 +85,19 @@ vpException::vpException (int _code, // { // } + +void vpException::setMessage(const char* format, va_list args) +{ + char buffer[1024]; + vsnprintf (buffer, 1024, format, args); + std::string msg(buffer); + message = msg; +} + /* ------------------------------------------------------------------------ */ /* --- ACCESSORS ---------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ - const char *vpException::getMessage (void) { return (this->message) .c_str(); @@ -133,9 +134,8 @@ const char* vpException::what () const throw() /* --- OP << --------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ -std::ostream & -operator << (std::ostream & os, - const vpException & error) +VISP_EXPORT std::ostream & +operator << (std::ostream & os, const vpException & error) { os << "Error [" << error.code << "]:\t" << error.message << std::endl; diff --git a/src/exceptions/vpException.h b/src/exceptions/vpException.h index e64558dbf2256782ed03fed1c3a4639d48fb93ae..d15124f7d04a67af0ae3c4125e953252b6c25b5d 100644 --- a/src/exceptions/vpException.h +++ b/src/exceptions/vpException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpException.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,6 +57,7 @@ /* Classes standards. */ #include <iostream> /* Classe std::ostream. */ #include <string> /* Classe string. */ +#include <stdarg.h> /* --------------------------------------------------------------------- */ /* --- CLASS ----------------------------------------------------------- */ @@ -74,23 +75,23 @@ */ class VISP_EXPORT vpException : public std::exception { + protected: -private: + //! Contains the error code, see the errorCodeEnum table for details. + int code; - //! Contains the error code, see the errorCodeEnum table for details. - int code; + //! Contains an error message (can be empty) + std::string message; - //! Contains an error message (can be empty) - std::string message; + //! Set the message container + void setMessage(const char* format, va_list args); -private: + //! forbid the empty constructor (protected) + vpException(): code(notInitialized), message("") { }; - //! forbid the empty constructor (private) - vpException(); + public: -public: - - enum generalExceptionEnum + enum generalExceptionEnum { memoryAllocationError, memoryFreeError, @@ -105,42 +106,31 @@ public: notInitialized /*!< Used to indicate that a parameter is not initialized. */ } ; - vpException (const int code, const char * msg); - vpException (const int code, const std::string & msg); - vpException (const int code); + vpException (const int code, const char* format, va_list args); + vpException (const int code, const char* format, ...); + vpException (const int code, const std::string & msg); + vpException (const int code); - /*! - Basic destructor. Do nothing but implemented to fit the inheritance from - std::exception - */ - virtual ~vpException() throw() {} + /*! + Basic destructor. Do nothing but implemented to fit the inheritance from + std::exception + */ + virtual ~vpException() throw() {} - //! send the object code - int getCode (void); + //! send the object code + int getCode (void); - //! send a reference (constant) related the error message (can be empty) - const std::string &getStringMessage (void); - //! send a pointer on the array of \e char related to the error string. - //!Cannot be \e NULL. - const char *getMessage (void); + //! send a reference (constant) related the error message (can be empty) + const std::string &getStringMessage (void); + //! send a pointer on the array of \e char related to the error string. + //!Cannot be \e NULL. + const char *getMessage (void); - //! print the error structure - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpException & art); + //! print the error structure + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpException & art); - const char* what () const throw(); + const char* what () const throw(); }; - - - - #endif /* #ifndef __vpException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/image/vpColor.cpp b/src/image/vpColor.cpp index 42e762aad5ecb4e9c56bbf058fbb5cec7cc40692..27598c61ec1b98b9f51e34937e261eb3d30d2604 100755 --- a/src/image/vpColor.cpp +++ b/src/image/vpColor.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpColor.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpColor.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -121,3 +121,25 @@ vpColor const vpColor::allColors[vpColor::nbColors] = { /*!< Predefined none color with R=G=B=0 and identifier vpColor::id_unknown. */ vpColor const vpColor::none = vpColor(0, 0, 0, id_unknown); +/*! + Compare two colors. + + Return true if the R,G,B components are the same. + + \param c1,c2 : Color to compare. +*/ +VISP_EXPORT bool operator==( const vpColor &c1, const vpColor &c2 ) { + return ( ( c1.R == c2.R ) && ( c1.G == c2.G ) && ( c1.B == c2.B) ); +} + +/*! + + Compare two colors. + + Return true if the R,G,B components are different. + + \param c1,c2 : Color to compare. +*/ +VISP_EXPORT bool operator!=( const vpColor &c1, const vpColor &c2 ) { + return ( ( c1.R != c2.R ) || ( c1.G != c2.G ) || ( c1.B == c2.B) ); +} diff --git a/src/image/vpColor.h b/src/image/vpColor.h index 5a6b7359f6391a095ae322ccfbf57f89e290ff38..adeaea2043a6e1ca5e538991b3dd13125cc42bbf 100644 --- a/src/image/vpColor.h +++ b/src/image/vpColor.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpColor.h 4243 2013-05-07 07:58:55Z fspindle $ + * $Id: vpColor.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -194,33 +194,37 @@ class VISP_EXPORT vpColor : public vpRGBa /*! Construct a color from its RGB values. - \param R : Red component. - \param G : Green component. - \param B : Blue component. + \param r : Red component. + \param g : Green component. + \param b : Blue component. - \param id : The color identifier to indicate if this color is or + \param cid : The color identifier to indicate if this color is or not a predefined one. */ - inline vpColor(unsigned char R, unsigned char G, unsigned char B, - vpColor::vpColorIdentifier id=vpColor::id_unknown) - : vpRGBa(R, G, B), id(id) + inline vpColor(unsigned char r, unsigned char g, unsigned char b, + vpColor::vpColorIdentifier cid=vpColor::id_unknown) + : vpRGBa(r, g, b), id(cid) {}; + /*! Default destructor. */ + inline virtual ~vpColor() {}; + friend VISP_EXPORT bool operator==( const vpColor &c1, const vpColor &c2 ); + friend VISP_EXPORT bool operator!=( const vpColor &c1, const vpColor &c2 ); /*! Set a color from its RGB values. - \param R : Red component. - \param G : Green component. - \param B : Blue component. + \param r : Red component. + \param g : Green component. + \param b : Blue component. The color identifier is set to vpColor::id_unknown to indicate that this color is not a predefined one. */ - inline void setColor(unsigned char R, unsigned char G, unsigned char B) { - this->R = R; - this->G = G; - this->B = B; + inline void setColor(unsigned char r, unsigned char g, unsigned char b) { + this->R = r; + this->G = g; + this->B = b; this->A = 0; id = id_unknown; }; @@ -231,37 +235,10 @@ class VISP_EXPORT vpColor : public vpRGBa \param i : color indice */ - static inline vpColor getColor(const unsigned int &i) {return vpColor::allColors[i % vpColor::nbColors];}; + static inline vpColor getColor(const unsigned int &i) { + return vpColor::allColors[i % vpColor::nbColors]; + }; }; -/*! - Compare two colors. - - Return true if the R,G,B components are the same. - - \param c1,c2 : Color to compare. -*/ -VISP_EXPORT inline bool operator==( const vpColor &c1, const vpColor &c2 ) { - return ( ( c1.R == c2.R ) && ( c1.G == c2.G ) && ( c1.B == c2.B) ); -} - -/*! - - Compare two colors. - - Return true if the R,G,B components are different. - - \param c1,c2 : Color to compare. -*/ -VISP_EXPORT inline bool operator!=( const vpColor &c1, const vpColor &c2 ) { - return ( ( c1.R != c2.R ) || ( c1.G != c2.G ) || ( c1.B == c2.B) ); -} - #endif - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/image/vpImage.h b/src/image/vpImage.h index 08dd1a2301c56052d5fc0bf7c6ff82cde4ffb65f..bc17911aca4d53e19ae3f749a7f28fa456305150 100644 --- a/src/image/vpImage.h +++ b/src/image/vpImage.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpImage.h 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpImage.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -134,8 +134,10 @@ public: void init(unsigned int height, unsigned int width) ; //! set the size of the image void init(unsigned int height, unsigned int width, Type value) ; - //! set the size of the image - void resize(const unsigned int height, const unsigned int width) ; + //! set the size of the image without initializing it. + void resize(const unsigned int h, const unsigned int w) ; + //! set the size of the image and initialize it. + void resize(const unsigned int h, const unsigned int w, const Type val) ; //! destructor void destroy() ; @@ -168,7 +170,7 @@ public: */ inline unsigned int getRows() const { return height ; } - /*! + /*! Get the number of columns in the image. \return The image number of column, or image width. @@ -177,7 +179,7 @@ public: */ inline unsigned int getCols() const { return width ; } - /*! + /*! Get the image size. \return The image size = width * height. @@ -217,9 +219,11 @@ public: //! operator[] allows operation like I[i] = x. inline Type *operator[]( const unsigned int i) { return row[i];} + inline Type *operator[]( const int i) { return row[i];} //! operator[] allows operation like x = I[i] inline const Type *operator[](unsigned int i) const { return row[i];} + inline const Type *operator[](int i) const { return row[i];} /*! Get the value of an image point. @@ -244,15 +248,15 @@ public: */ inline void operator()(const unsigned int i, const unsigned int j, - const Type &v) + const Type &v) { bitmap[i*width+j] = v ; } /*! Get the value of an image point. - \param ip : An image point with sub-pixel coordinates. Sub-pixel - coordinates are roughly transformed to insigned int coordinates by cast. + \param ip : An image point with sub-pixel coordinates. Sub-pixel + coordinates are roughly transformed to insigned int coordinates by cast. \return Value of the image point \e ip. @@ -269,14 +273,14 @@ public: /*! Set the value of an image point. - \param ip : An image point with sub-pixel coordinates. Sub-pixel - coordinates are roughly transformed to insigned int coordinates by cast. + \param ip : An image point with sub-pixel coordinates. Sub-pixel + coordinates are roughly transformed to insigned int coordinates by cast. \param v : Value to set for the image point. */ inline void operator()(const vpImagePoint &ip, - const Type &v) + const Type &v) { unsigned int i = (unsigned int) ip.get_i(); unsigned int j = (unsigned int) ip.get_j(); @@ -287,9 +291,9 @@ public: vpImage<Type> operator-(const vpImage<Type> &B); //! Copy operator - void operator=(const vpImage<Type> &I) ; + vpImage<Type>& operator=(const vpImage<Type> &I) ; - void operator=(const Type &v); + vpImage<Type>& operator=(const Type &v); bool operator==(const vpImage<Type> &I); bool operator!=(const vpImage<Type> &I); @@ -331,21 +335,23 @@ private: /*! \brief Image initialisation - Allocate memory for an [height x width] image + Allocate memory for an [h x w] image. - Set all the element of the bitmap to value + \param w : Image width. + \param h : Image height. + \param value : Set all the element of the bitmap to \e value. \exception vpException::memoryAllocationError - \sa vpImage::init(height, width) + \sa vpImage::init(h, w) */ template<class Type> void -vpImage<Type>::init(unsigned int height, unsigned int width, Type value) +vpImage<Type>::init(unsigned int h, unsigned int w, Type value) { try { - init(height,width) ; + init(h,w) ; } catch(vpException me) { @@ -361,7 +367,10 @@ vpImage<Type>::init(unsigned int height, unsigned int width, Type value) /*! \brief Image initialization - Allocate memory for an [height x width] image + Allocate memory for an [h x w] image. + + \param w : Image width. + \param h : Image height. Element of the bitmap are not initialized @@ -374,10 +383,10 @@ vpImage<Type>::init(unsigned int height, unsigned int width, Type value) */ template<class Type> void -vpImage<Type>::init(unsigned int height, unsigned int width) +vpImage<Type>::init(unsigned int h, unsigned int w) { - if (height != this->height) { + if (h != this->height) { if (row != NULL) { vpDEBUG_TRACE(10,"Destruction row[]"); delete [] row; @@ -385,7 +394,7 @@ vpImage<Type>::init(unsigned int height, unsigned int width) } } - if ((height != this->height) || (width != this->width)) + if ((h != this->height) || (w != this->width)) { if (bitmap != NULL) { vpDEBUG_TRACE(10,"Destruction bitmap[]") ; @@ -394,14 +403,11 @@ vpImage<Type>::init(unsigned int height, unsigned int width) } } - - - this->width = width ; - this->height = height ; + this->width = w ; + this->height = h; npixels=width*height; - if (bitmap == NULL) bitmap = new Type[npixels] ; // vpERROR_TRACE("Allocate bitmap %p",bitmap) ; @@ -409,7 +415,7 @@ vpImage<Type>::init(unsigned int height, unsigned int width) { vpERROR_TRACE("cannot allocate bitmap ") ; throw(vpException(vpException::memoryAllocationError, - "cannot allocate bitmap ")) ; + "cannot allocate bitmap ")) ; } if (row == NULL) row = new Type*[height] ; @@ -418,7 +424,7 @@ vpImage<Type>::init(unsigned int height, unsigned int width) { vpERROR_TRACE("cannot allocate row ") ; throw(vpException(vpException::memoryAllocationError, - "cannot allocate row ")) ; + "cannot allocate row ")) ; } unsigned int i ; @@ -429,9 +435,12 @@ vpImage<Type>::init(unsigned int height, unsigned int width) /*! \brief Constructor - Allocate memory for an [height x width] image + Allocate memory for an [h x w] image. + + \param w : Image width. + \param h : Image height. - Element of the bitmap are set to zero + Element of the bitmap are set to zero. If the image has been already initialized, memory allocation is done only if the new image size is different, else we re-use the same @@ -442,20 +451,15 @@ vpImage<Type>::init(unsigned int height, unsigned int width) \sa vpImage::init(height, width) */ template<class Type> -vpImage<Type>::vpImage(unsigned int height, unsigned int width) +vpImage<Type>::vpImage(unsigned int h, unsigned int w) + : bitmap(NULL), display(NULL), npixels(0), width(0), height(0), row(NULL) { - bitmap = NULL ; - row = NULL ; - - display = NULL ; - this->height = this->width = 0 ; try { - init(height,width,0) ; + init(h,w,0) ; } - catch(vpException me) + catch(...) { - vpERROR_TRACE(" ") ; throw ; } } @@ -463,9 +467,11 @@ vpImage<Type>::vpImage(unsigned int height, unsigned int width) /*! \brief Constructor - Allocate memory for an [height x width] image + Allocate memory for an [height x width] image. - set all the element of the bitmap to value + \param w : Image width. + \param h : Image height. + \param value : Set all the element of the bitmap to value. If the image has been already initialized, memory allocation is done only if the new image size is different, else we re-use the same @@ -476,16 +482,12 @@ vpImage<Type>::vpImage(unsigned int height, unsigned int width) \sa vpImage::init(height, width, value) */ template<class Type> -vpImage<Type>::vpImage (unsigned int height, unsigned int width, Type value) +vpImage<Type>::vpImage (unsigned int h, unsigned int w, Type value) + : bitmap(NULL), display(NULL), npixels(0), width(0), height(0), row(NULL) { - bitmap = NULL ; - row = NULL ; - - display = NULL ; - this->height = this->width = 0 ; try { - init(height,width,value) ; + init(h,w,value) ; } catch(vpException me) { @@ -505,20 +507,53 @@ vpImage<Type>::vpImage (unsigned int height, unsigned int width, Type value) */ template<class Type> vpImage<Type>::vpImage() + : bitmap(NULL), display(NULL), npixels(0), width(0), height(0), row(NULL) { - bitmap = NULL ; - row = NULL ; +} - display = NULL ; +/*! + \brief resize the image : Image initialization + + Allocate memory for an [height x width] image. + + \warning The image is not initialized. + + \param w : Image width. + \param h : Image height. + + Element of the bitmap are not initialized + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \exception vpException::memoryAllocationError - this->height = this->width = 0 ; - this->npixels = 0; + \sa init(unsigned int, unsigned int) +*/ +template<class Type> +void +vpImage<Type>::resize(unsigned int h, unsigned int w) +{ + try + { + init(h, w) ; + } + catch(vpException me) + { + vpERROR_TRACE(" ") ; + throw ; + } } /*! \brief resize the image : Image initialization - Allocate memory for an [height x width] image + Allocate memory for an [height x width] image and initialize the image. + + \param w : Image width. + \param h : Image height. + \param val : Pixels value. Element of the bitmap are not initialized @@ -532,11 +567,11 @@ vpImage<Type>::vpImage() */ template<class Type> void -vpImage<Type>::resize(unsigned int height, unsigned int width) +vpImage<Type>::resize(unsigned int h, unsigned int w, const Type val) { try { - init(height, width) ; + init(h, w, val) ; } catch(vpException me) { @@ -545,6 +580,7 @@ vpImage<Type>::resize(unsigned int height, unsigned int width) } } + /*! \brief Destructor : Memory de-allocation @@ -596,23 +632,13 @@ vpImage<Type>::~vpImage() */ template<class Type> vpImage<Type>::vpImage(const vpImage<Type>& I) + : bitmap(NULL), display(NULL), npixels(0), width(0), height(0), row(NULL) { - bitmap = NULL ; - row = NULL ; - /* we first have to set the initial values of the image because resize function calls init function that test the actual size of the image */ - this->width = 0; - this->height = 0; - this->npixels = 0; try { - //if (I.bitmap!=NULL) -// if(I.getHeight() != 0 || I.getWidth() != 0) - { - resize(I.getHeight(),I.getWidth()); - unsigned int i; - memcpy(bitmap, I.bitmap, I.npixels*sizeof(Type)) ; - for (i =0 ; i < this->height ; i++) row[i] = bitmap + i*this->width ; - } + resize(I.getHeight(),I.getWidth()); + memcpy(bitmap, I.bitmap, I.npixels*sizeof(Type)) ; + for (unsigned int i =0 ; i < this->height ; i++) row[i] = bitmap + i*this->width ; } catch(vpException me) { @@ -673,14 +699,14 @@ void vpImage<Type>::getMinMaxValue(Type &min, Type &max) const \brief Copy operator */ template<class Type> -void vpImage<Type>::operator=(const vpImage<Type> &I) +vpImage<Type> & vpImage<Type>::operator=(const vpImage<Type> &I) { /* we first have to set the initial values of the image because resize function calls init function that test the actual size of the image */ if(bitmap != NULL){ delete[] bitmap; bitmap = NULL ; } - + if(row != NULL){ delete[] row; row = NULL ; @@ -692,28 +718,28 @@ void vpImage<Type>::operator=(const vpImage<Type> &I) { if(I.npixels != 0) { - if (bitmap == NULL){ + if (bitmap == NULL){ bitmap = new Type[npixels] ; } if (bitmap == NULL){ vpERROR_TRACE("cannot allocate bitmap ") ; throw(vpException(vpException::memoryAllocationError, - "cannot allocate bitmap ")) ; + "cannot allocate bitmap ")) ; } - if (row == NULL){ + if (row == NULL){ row = new Type*[height] ; } if (row == NULL){ vpERROR_TRACE("cannot allocate row ") ; throw(vpException(vpException::memoryAllocationError, - "cannot allocate row ")) ; - } + "cannot allocate row ")) ; + } memcpy(bitmap, I.bitmap, I.npixels*sizeof(Type)) ; - for (unsigned int i=0; i<this->height; i++){ + for (unsigned int i=0; i<this->height; i++){ row[i] = bitmap + i*this->width; } } @@ -723,6 +749,7 @@ void vpImage<Type>::operator=(const vpImage<Type> &I) vpERROR_TRACE(" ") ; throw ; } + return (* this); } @@ -733,10 +760,12 @@ void vpImage<Type>::operator=(const vpImage<Type> &I) \warning = must be defined for \f$ <\f$ Type \f$ > \f$ */ template<class Type> -void vpImage<Type>::operator=(const Type &v) +vpImage<Type>& vpImage<Type>::operator=(const Type &v) { for (unsigned int i=0 ; i < npixels ; i++) bitmap[i] = v ; + + return *this; } /*! @@ -747,15 +776,15 @@ void vpImage<Type>::operator=(const Type &v) template<class Type> bool vpImage<Type>::operator==(const vpImage<Type> &I) { - if (this->width != I.getWidth()) - return false; - if (this->height != I.getHeight()) - return false; - - for (unsigned int i=0 ; i < npixels ; i++) + if (this->width != I.getWidth()) + return false; + if (this->height != I.getHeight()) + return false; + + for (unsigned int i=0 ; i < npixels ; i++) { if (bitmap[i] != I.bitmap[i]) - return false; + return false; } return true ; } @@ -824,53 +853,53 @@ vpImage<Type> vpImage<Type>::operator-(const vpImage<Type> &B) */ template<class Type> void vpImage<Type>::insert(const vpImage<Type> &src, - const vpImagePoint topLeft) + const vpImagePoint topLeft) { Type* srcBitmap; Type* destBitmap; - + int itl = (int)topLeft.get_i(); int jtl = (int)topLeft.get_j(); - + int dest_ibegin = 0; int dest_jbegin = 0; int src_ibegin = 0; int src_jbegin = 0; - int dest_w = this->getWidth(); - int dest_h = this->getHeight(); - int src_w = src.getWidth(); - int src_h = src.getHeight(); - int wsize = src.getWidth(); - int hsize = src.getHeight(); - + int dest_w = (int)this->getWidth(); + int dest_h = (int)this->getHeight(); + int src_w = (int)src.getWidth(); + int src_h = (int)src.getHeight(); + int wsize = (int)src.getWidth(); + int hsize = (int)src.getHeight(); + if (itl >= dest_h || jtl >= dest_w) return; - + if (itl < 0) - src_ibegin = -itl; + src_ibegin = -itl; else dest_ibegin = itl; - + if (jtl < 0) src_jbegin = -jtl; else dest_jbegin = jtl; - + if (src_w - src_jbegin > dest_w - dest_jbegin) wsize = dest_w - dest_jbegin; else wsize = src_w - src_jbegin; - + if (src_h - src_ibegin > dest_h - dest_ibegin) hsize = dest_h - dest_ibegin; else hsize = src_h - src_ibegin; - + for (int i = 0; i < hsize; i++) { srcBitmap = src.bitmap + ((src_ibegin+i)*src_w+src_jbegin); destBitmap = this->bitmap + ((dest_ibegin+i)*dest_w+dest_jbegin); - + memcpy(destBitmap,srcBitmap,wsize*sizeof(Type)); } } @@ -979,7 +1008,7 @@ vpImage<Type>::quarterSizeImage(vpImage<Type> &res) I.doubleSizeImage(I2); vpImageIo::write(I2, "myDoubleSizeImage.pgm"); \endcode - + See halfSizeImage(vpImage<Type> &) for an example of pyramid construction. */ @@ -1007,22 +1036,22 @@ vpImage<Type>::doubleSizeImage(vpImage<Type> &res) //interpolate pixels B and I for(int i = 0; i < h; i += 2) for(int j = 1; j < w - 1; j += 2) - res[i][j] = (Type)(0.5 * ((*this)[i>>1][j>>1] - + (*this)[i>>1][(j>>1) + 1])); + res[i][j] = (Type)(0.5 * ((*this)[i>>1][j>>1] + + (*this)[i>>1][(j>>1) + 1])); //interpolate pixels E and G for(int i = 1; i < h - 1; i += 2) for(int j = 0; j < w; j += 2) - res[i][j] = (Type)(0.5 * ((*this)[i>>1][j>>1] - + (*this)[(i>>1)+1][j>>1])); + res[i][j] = (Type)(0.5 * ((*this)[i>>1][j>>1] + + (*this)[(i>>1)+1][j>>1])); //interpolate pixel F for(int i = 1; i < h - 1; i += 2) for(int j = 1; j < w - 1; j += 2) - res[i][j] = (Type)(0.25 * ((*this)[i>>1][j>>1] - + (*this)[i>>1][(j>>1)+1] - + (*this)[(i>>1)+1][j>>1] - + (*this)[(i>>1)+1][(j>>1)+1])); + res[i][j] = (Type)(0.25 * ((*this)[i>>1][j>>1] + + (*this)[i>>1][(j>>1)+1] + + (*this)[(i>>1)+1][j>>1] + + (*this)[(i>>1)+1][(j>>1)+1])); } /*! @@ -1070,7 +1099,7 @@ inline unsigned char vpImage<unsigned char>::getValue(double i, double j) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (i > height - 1) @@ -1083,7 +1112,7 @@ inline unsigned char vpImage<unsigned char>::getValue(double i, double j) const if(rratio < 0) rratio=-rratio; double cratio = j - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1125,7 +1154,7 @@ inline double vpImage<double>::getValue(double i, double j) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (i > height - 1) @@ -1138,7 +1167,7 @@ inline double vpImage<double>::getValue(double i, double j) const if(rratio < 0) rratio=-rratio; double cratio = j - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1162,7 +1191,7 @@ inline vpRGBa vpImage<vpRGBa>::getValue(double i, double j) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (i > height - 1) @@ -1175,7 +1204,7 @@ inline vpRGBa vpImage<vpRGBa>::getValue(double i, double j) const if(rratio < 0) rratio=-rratio; double cratio = j - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1218,7 +1247,7 @@ inline unsigned char vpImage<unsigned char>::getValue(vpImagePoint &ip) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (ip.get_i() > height - 1) @@ -1231,7 +1260,7 @@ inline unsigned char vpImage<unsigned char>::getValue(vpImagePoint &ip) const if(rratio < 0) rratio=-rratio; double cratio = ip.get_j() - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1256,7 +1285,7 @@ inline double vpImage<double>::getValue(vpImagePoint &ip) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (ip.get_i() > height - 1) @@ -1269,7 +1298,7 @@ inline double vpImage<double>::getValue(vpImagePoint &ip) const if(rratio < 0) rratio=-rratio; double cratio = ip.get_j() - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1293,7 +1322,7 @@ inline vpRGBa vpImage<vpRGBa>::getValue(vpImagePoint &ip) const if (iround >= height || jround >= width) { vpERROR_TRACE("Pixel outside the image") ; throw(vpException(vpImageException::notInTheImage, - "Pixel outside the image")); + "Pixel outside the image")); } if (ip.get_i() > height - 1) @@ -1306,7 +1335,7 @@ inline vpRGBa vpImage<vpRGBa>::getValue(vpImagePoint &ip) const if(rratio < 0) rratio=-rratio; double cratio = ip.get_j() - (double) jround; - if(cratio < 0) + if(cratio < 0) cratio=-cratio; rfrac = 1.0f - rratio; @@ -1570,10 +1599,3 @@ vpImage<Type>::doubleSizeImage(vpImage<Type>* res) #endif // VISP_BUILD_DEPRECATED_FUNCTIONS #endif - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/image/vpImageConvert.cpp b/src/image/vpImageConvert.cpp index f4f7b05325339ddb75904273066660eb034fd0b5..2e6f3093dc2b88f1c20e47c3b940dac11f61d410 100644 --- a/src/image/vpImageConvert.cpp +++ b/src/image/vpImageConvert.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageConvert.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpImageConvert.cpp 5204 2015-01-24 13:18:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,8 +65,7 @@ Convert a vpImage\<vpRGBa\> to a vpImage\<unsigned char\> \param dest : destination image */ void -vpImageConvert::convert(const vpImage<unsigned char> &src, - vpImage<vpRGBa> & dest) +vpImageConvert::convert(const vpImage<unsigned char> &src, vpImage<vpRGBa> & dest) { dest.resize(src.getHeight(), src.getWidth()) ; @@ -80,8 +79,7 @@ Convert a vpImage\<unsigned char\> to a vpImage\<vpRGBa\> \param dest : destination image */ void -vpImageConvert::convert(const vpImage<vpRGBa> &src, - vpImage<unsigned char> & dest) +vpImageConvert::convert(const vpImage<vpRGBa> &src, vpImage<unsigned char> & dest) { dest.resize(src.getHeight(), src.getWidth()) ; @@ -96,8 +94,7 @@ Convert a vpImage\<float\> to a vpImage\<unsigend char\> by renormalizing betwee \param dest : destination image */ void -vpImageConvert::convert(const vpImage<float> &src, - vpImage<unsigned char> &dest) +vpImageConvert::convert(const vpImage<float> &src, vpImage<unsigned char> &dest) { dest.resize(src.getHeight(), src.getWidth()) ; unsigned int max_xy = src.getWidth()*src.getHeight(); @@ -112,7 +109,7 @@ vpImageConvert::convert(const vpImage<float> &src, else if(val > 255) dest.bitmap[i] = 255; else - dest.bitmap[i] = (int)val; + dest.bitmap[i] = (unsigned char)val; } } @@ -122,8 +119,7 @@ Convert a vpImage\<unsigned char\> to a vpImage\<float\> by basic casting. \param dest : destination image */ void -vpImageConvert::convert(const vpImage<unsigned char> &src, - vpImage<float> &dest) +vpImageConvert::convert(const vpImage<unsigned char> &src, vpImage<float> &dest) { dest.resize(src.getHeight(), src.getWidth()) ; for (unsigned int i = 0; i < src.getHeight()*src.getWidth(); i++) @@ -136,8 +132,7 @@ Convert a vpImage\<double\> to a vpImage\<unsigend char\> by renormalizing betwe \param dest : destination image */ void -vpImageConvert::convert(const vpImage<double> &src, - vpImage<unsigned char> &dest) +vpImageConvert::convert(const vpImage<double> &src, vpImage<unsigned char> &dest) { dest.resize(src.getHeight(), src.getWidth()) ; unsigned int max_xy = src.getWidth()*src.getHeight(); @@ -152,7 +147,7 @@ vpImageConvert::convert(const vpImage<double> &src, else if(val > 255) dest.bitmap[i] = 255; else - dest.bitmap[i] = (int)val; + dest.bitmap[i] = (unsigned char)val; } } @@ -162,8 +157,7 @@ Convert a vpImage\<unsigned char\> to a vpImage\<double\> by basic casting. \param dest : destination image */ void -vpImageConvert::convert(const vpImage<unsigned char> &src, - vpImage<double> &dest) +vpImageConvert::convert(const vpImage<unsigned char> &src, vpImage<double> &dest) { dest.resize(src.getHeight(), src.getWidth()) ; for (unsigned int i = 0; i < src.getHeight()*src.getWidth(); i++) @@ -171,8 +165,12 @@ vpImageConvert::convert(const vpImage<unsigned char> &src, } #ifdef VISP_HAVE_OPENCV +// Deprecated: will be removed with OpenCV transcient from C to C++ api /*! - Convert a IplImage to a vpImage\<vpRGBa\> + \deprecated Rather then using OpenCV IplImage you should use cv::Mat images. + IplImage structure will be removed with OpenCV transcient from C to C++ api. + + Convert an IplImage to a vpImage\<vpRGBa\>. An IplImage is an OpenCV (Intel's Open source Computer Vision Library) image structure. See http://opencvlibrary.sourceforge.net/ for general @@ -194,7 +192,7 @@ vpImageConvert::convert(const vpImage<unsigned char> &src, int main() { -#ifdef VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) vpImage<vpRGBa> Ic; // A color image IplImage* Ip; @@ -275,7 +273,10 @@ vpImageConvert::convert(const IplImage* src, vpImage<vpRGBa> & dest, bool flip) } /*! - Convert a IplImage to a vpImage\<unsigned char\> + \deprecated Rather then using OpenCV IplImage you should use cv::Mat images. + IplImage structure will be removed with OpenCV transcient from C to C++ api. + + Convert an IplImage to a vpImage\<unsigned char\>. An IplImage is an OpenCV (Intel's Open source Computer Vision Library) image structure. See http://opencvlibrary.sourceforge.net/ for general @@ -297,7 +298,7 @@ vpImageConvert::convert(const IplImage* src, vpImage<vpRGBa> & dest, bool flip) int main() { -#ifdef VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) vpImage<unsigned char> Ig; // A grayscale image IplImage* Ip; @@ -315,8 +316,7 @@ int main() \endcode */ void -vpImageConvert::convert(const IplImage* src, - vpImage<unsigned char> &dest, bool flip) +vpImageConvert::convert(const IplImage* src, vpImage<unsigned char> &dest, bool flip) { int nChannel = src->nChannels; int depth = src->depth; @@ -357,26 +357,29 @@ vpImageConvert::convert(const IplImage* src, } else { - if(nChannel == 1 && depth == 8){ + if(nChannel == 1 && depth == 8){ + dest.resize((unsigned int)height, (unsigned int)width) ; unsigned char* beginOutput = (unsigned char*)dest.bitmap; - dest.resize((unsigned int)height, (unsigned int)width) ; - for (int i =0 ; i < height ; i++){ - memcpy(beginOutput + lineStep * ( 4 * width * ( height - 1 - i ) ) , src->imageData + i*widthStep, - (size_t)width); - } - } - if(nChannel == 3 && depth == 8){ - dest.resize((unsigned int)height, (unsigned int)width) ; - //for (int i = 0 ; i < height ; i++){ - BGRToGrey((unsigned char*)src->imageData /*+ i*widthStep*/, - dest.bitmap /*+ i*width*/, (unsigned int)width, (unsigned int)height/*1*/, true); - //} + for (int i =0 ; i < height ; i++){ + memcpy(beginOutput + lineStep * ( 4 * width * ( height - 1 - i ) ) , src->imageData + i*widthStep, + (size_t)width); } + } + if(nChannel == 3 && depth == 8){ + dest.resize((unsigned int)height, (unsigned int)width) ; + //for (int i = 0 ; i < height ; i++){ + BGRToGrey((unsigned char*)src->imageData /*+ i*widthStep*/, + dest.bitmap /*+ i*width*/, (unsigned int)width, (unsigned int)height/*1*/, true); + //} + } } } /*! - Convert a vpImage\<vpRGBa\> to a IplImage + \deprecated Rather then using OpenCV IplImage you should use cv::Mat images. + IplImage structure will be removed with OpenCV transcient from C to C++ api. + + Convert a vpImage\<vpRGBa\> to a IplImage. An IplImage is an OpenCV (Intel's Open source Computer Vision Library) image structure. See http://opencvlibrary.sourceforge.net/ for general @@ -397,7 +400,7 @@ vpImageConvert::convert(const IplImage* src, int main() { -#ifdef VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) vpImage<vpRGBa> Ic; // A color image IplImage* Ip = NULL; @@ -461,7 +464,10 @@ vpImageConvert::convert(const vpImage<vpRGBa> & src, IplImage *&dest) } /*! - Convert a vpImage\<unsigned char\> to a IplImage + \deprecated Rather then using OpenCV IplImage you should use cv::Mat images. + IplImage structure will be removed with OpenCV transcient from C to C++ api. + + Convert a vpImage\<unsigned char\> to a IplImage. An IplImage is an OpenCV (Intel's Open source Computer Vision Library) image structure. See http://opencvlibrary.sourceforge.net/ for general @@ -482,7 +488,7 @@ vpImageConvert::convert(const vpImage<vpRGBa> & src, IplImage *&dest) int main() { -#ifdef VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) vpImage<unsigned char> Ig; // A greyscale image IplImage* Ip = NULL; @@ -502,8 +508,7 @@ int main() \endcode */ void -vpImageConvert::convert(const vpImage<unsigned char> & src, - IplImage* &dest) +vpImageConvert::convert(const vpImage<unsigned char> & src, IplImage* &dest) { unsigned int height = src.getHeight(); unsigned int width = src.getWidth(); @@ -535,7 +540,7 @@ vpImageConvert::convert(const vpImage<unsigned char> & src, #if VISP_HAVE_OPENCV_VERSION >= 0x020100 /*! - Convert a cv::Mat to a vpImage\<vpRGBa\> + Convert a cv::Mat to a vpImage\<vpRGBa\>. A cv::Mat is an OpenCV image class. See http://opencv.willowgarage.com for the general OpenCV documentation, or @@ -577,8 +582,7 @@ int main() \endcode */ void -vpImageConvert::convert(const cv::Mat& src, - vpImage<vpRGBa>& dest, const bool flip) +vpImageConvert::convert(const cv::Mat& src, vpImage<vpRGBa>& dest, const bool flip) { if(src.type() == CV_8UC4){ dest.resize((unsigned int)src.rows, (unsigned int)src.cols); @@ -589,7 +593,7 @@ vpImageConvert::convert(const cv::Mat& src, rgbaVal.R = tmp[2]; rgbaVal.G = tmp[1]; rgbaVal.B = tmp[0]; - rgbaVal.A = tmp[3]; + rgbaVal.A = tmp[3]; if(flip) dest[dest.getRows()-i-1][j] = rgbaVal; else @@ -629,7 +633,7 @@ vpImageConvert::convert(const cv::Mat& src, } /*! - Convert a cv::Mat to a vpImage\<unsigned char\> + Convert a cv::Mat to a vpImage\<unsigned char\>. A cv::Mat is an OpenCV image class. See http://opencv.willowgarage.com for the general OpenCV documentation, or @@ -671,8 +675,7 @@ int main() \endcode */ void -vpImageConvert::convert(const cv::Mat& src, - vpImage<unsigned char>& dest, const bool flip) +vpImageConvert::convert(const cv::Mat& src, vpImage<unsigned char>& dest, const bool flip) { if(src.type() == CV_8UC1){ dest.resize((unsigned int)src.rows, (unsigned int)src.cols); @@ -715,7 +718,7 @@ vpImageConvert::convert(const cv::Mat& src, /*! - Convert a vpImage\<unsigned char\> to a cv::Mat + Convert a vpImage\<unsigned char\> to a cv::Mat. A cv::Mat is an OpenCV image class. See http://opencv.willowgarage.com for the general OpenCV documentation, or @@ -754,8 +757,7 @@ int main() \endcode */ void -vpImageConvert::convert(const vpImage<vpRGBa> & src, - cv::Mat& dest) +vpImageConvert::convert(const vpImage<vpRGBa> & src, cv::Mat& dest) { cv::Mat vpToMat((int)src.getRows(), (int)src.getCols(), CV_8UC4, (void*)src.bitmap); @@ -768,7 +770,7 @@ vpImageConvert::convert(const vpImage<vpRGBa> & src, } /*! - Convert a vpImage\<unsigned char\> to a cv::Mat + Convert a vpImage\<unsigned char\> to a cv::Mat. A cv::Mat is an OpenCV image class. See http://opencv.willowgarage.com for the general OpenCV documentation, or @@ -799,18 +801,17 @@ int main() vpImageIo::read(Ig, "image.pgm"); // Convert the vpImage<unsigned char> in to greyscale cv::Mat vpImageConvert::convert(Ig, Ip); - // Treatments on cv::MatIp + // Treatments on cv::Mat Ip //... // Save the cv::Mat on the disk - cv::imwrite("image.pgm", Ip); + cv::imwrite("image-cv.pgm", Ip); #endif } \endcode */ void -vpImageConvert::convert(const vpImage<unsigned char> & src, - cv::Mat& dest, const bool copyData) +vpImageConvert::convert(const vpImage<unsigned char> & src, cv::Mat& dest, const bool copyData) { if(copyData){ cv::Mat tmpMap((int)src.getRows(), (int)src.getCols(), CV_8UC1, (void*)src.bitmap); @@ -857,7 +858,7 @@ int main() \endcode */ void vpImageConvert::convert(const vpImage<unsigned char> & src, - yarp::sig::ImageOf< yarp::sig::PixelMono > *dest, const bool copyData) + yarp::sig::ImageOf< yarp::sig::PixelMono > *dest, const bool copyData) { if(copyData) { @@ -905,7 +906,7 @@ int main() \endcode */ void vpImageConvert::convert(const yarp::sig::ImageOf< yarp::sig::PixelMono > *src, - vpImage<unsigned char> & dest,const bool copyData ) + vpImage<unsigned char> & dest,const bool copyData) { dest.resize(src->height(),src->width()); if(copyData) @@ -948,7 +949,7 @@ int main() \endcode */ void vpImageConvert::convert(const vpImage<vpRGBa> & src, - yarp::sig::ImageOf< yarp::sig::PixelRgba > *dest, const bool copyData) + yarp::sig::ImageOf< yarp::sig::PixelRgba > *dest, const bool copyData) { if(copyData){ dest->resize(src.getWidth(),src.getHeight()); @@ -996,7 +997,7 @@ int main() \endcode */ void vpImageConvert::convert(const yarp::sig::ImageOf< yarp::sig::PixelRgba > *src, - vpImage<vpRGBa> & dest,const bool copyData) + vpImage<vpRGBa> & dest,const bool copyData) { dest.resize(src->height(),src->width()); if(copyData) @@ -1037,8 +1038,7 @@ int main() } \endcode */ -void vpImageConvert::convert(const vpImage<vpRGBa> & src, - yarp::sig::ImageOf< yarp::sig::PixelRgb > *dest) +void vpImageConvert::convert(const vpImage<vpRGBa> & src, yarp::sig::ImageOf< yarp::sig::PixelRgb > *dest) { dest->resize(src.getWidth(),src.getHeight()); for(unsigned int i = 0 ; i < src.getRows() ; i++){ @@ -1086,8 +1086,7 @@ int main() } \endcode */ -void vpImageConvert::convert(const yarp::sig::ImageOf< yarp::sig::PixelRgb > *src, - vpImage<vpRGBa> & dest) +void vpImageConvert::convert(const yarp::sig::ImageOf< yarp::sig::PixelRgb > *src, vpImage<vpRGBa> & dest) { dest.resize(src->height(),src->width()); for(int i = 0 ; i < src->height() ; i++){ @@ -1213,7 +1212,7 @@ void vpImageConvert::convertToJPEGBuffer(unsigned char *src, long unsigned int s \sa YUV422ToRGBa() */ void vpImageConvert::YUYVToRGBa(unsigned char* yuyv, unsigned char* rgba, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height) { unsigned char *s; unsigned char *d; @@ -1269,7 +1268,7 @@ void vpImageConvert::YUYVToRGBa(unsigned char* yuyv, unsigned char* rgba, \sa YUV422ToRGB() */ void vpImageConvert::YUYVToRGB(unsigned char* yuyv, unsigned char* rgb, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height) { unsigned char *s; unsigned char *d; @@ -1321,8 +1320,7 @@ void vpImageConvert::YUYVToRGB(unsigned char* yuyv, unsigned char* rgb, \sa YUV422ToGrey() */ -void vpImageConvert::YUYVToGrey(unsigned char* yuyv, unsigned char* grey, - unsigned int size) +void vpImageConvert::YUYVToGrey(unsigned char* yuyv, unsigned char* grey, unsigned int size) { unsigned int i=0,j=0; @@ -1341,9 +1339,7 @@ Convert YUV411 into RGB32 yuv411 : u y1 y2 v y3 y4 */ -void vpImageConvert::YUV411ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int size) +void vpImageConvert::YUV411ToRGBa(unsigned char* yuv, unsigned char* rgba, unsigned int size) { #if 1 // std::cout << "call optimized ConvertYUV411ToRGBa()" << std::endl; @@ -1470,9 +1466,7 @@ void vpImageConvert::YUV411ToRGBa(unsigned char* yuv, \sa YUYVToRGBa() */ -void vpImageConvert::YUV422ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int size) +void vpImageConvert::YUV422ToRGBa(unsigned char* yuv, unsigned char* rgba, unsigned int size) { #if 1 @@ -1552,12 +1546,8 @@ Convert YUV411 into Grey yuv411 : u y1 y2 v y3 y4 */ -void vpImageConvert::YUV411ToGrey(unsigned char* yuv, - unsigned char* grey, - unsigned int size) +void vpImageConvert::YUV411ToGrey(unsigned char* yuv, unsigned char* grey, unsigned int size) { - - unsigned int i=0,j=0; while( j < size*3/2) { @@ -1581,9 +1571,7 @@ void vpImageConvert::YUV411ToGrey(unsigned char* yuv, \sa YUYVToRGB() */ -void vpImageConvert::YUV422ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int size) +void vpImageConvert::YUV422ToRGB(unsigned char* yuv, unsigned char* rgb, unsigned int size) { #if 1 // std::cout << "call optimized convertYUV422ToRGB()" << std::endl; @@ -1661,9 +1649,7 @@ void vpImageConvert::YUV422ToRGB(unsigned char* yuv, \sa YUYVToGrey() */ -void vpImageConvert::YUV422ToGrey(unsigned char* yuv, - unsigned char* grey, - unsigned int size) +void vpImageConvert::YUV422ToGrey(unsigned char* yuv, unsigned char* grey, unsigned int size) { unsigned int i=0,j=0; @@ -1681,9 +1667,7 @@ Convert YUV411 into RGB yuv411 : u y1 y2 v y3 y4 */ -void vpImageConvert::YUV411ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int size) +void vpImageConvert::YUV411ToRGB(unsigned char* yuv, unsigned char* rgb, unsigned int size) { #if 1 // std::cout << "call optimized ConvertYUV411ToRGB()" << std::endl; @@ -1806,9 +1790,8 @@ void vpImageConvert::YUV411ToRGB(unsigned char* yuv, yuv420 : Y(NxM), U(N/2xM/2), V(N/2xM/2) */ -void vpImageConvert::YUV420ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int width, unsigned int height) +void vpImageConvert::YUV420ToRGBa(unsigned char* yuv, unsigned char* rgba, + unsigned int width, unsigned int height) { // std::cout << "call optimized ConvertYUV420ToRGBa()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -1908,8 +1891,8 @@ void vpImageConvert::YUV420ToRGBa(unsigned char* yuv, */ void vpImageConvert::YUV420ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int width, unsigned int height) + unsigned char* rgb, + unsigned int width, unsigned int height) { // std::cout << "call optimized ConvertYUV420ToRGB()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -2005,9 +1988,7 @@ void vpImageConvert::YUV420ToRGB(unsigned char* yuv, yuv420 : Y(NxM), U(N/2xM/2), V(N/2xM/2) */ -void vpImageConvert::YUV420ToGrey(unsigned char* yuv, - unsigned char* grey, - unsigned int size) +void vpImageConvert::YUV420ToGrey(unsigned char* yuv, unsigned char* grey, unsigned int size) { for(unsigned int i=0 ; i < size ; i++) { @@ -2021,9 +2002,7 @@ void vpImageConvert::YUV420ToGrey(unsigned char* yuv, yuv444 : u y v */ -void vpImageConvert::YUV444ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int size) +void vpImageConvert::YUV444ToRGBa(unsigned char* yuv, unsigned char* rgba, unsigned int size) { register int U, V, R, G, B, V2, U5, UV; register int Y; @@ -2062,9 +2041,7 @@ void vpImageConvert::YUV444ToRGBa(unsigned char* yuv, yuv444 : u y v */ -void vpImageConvert::YUV444ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int size) +void vpImageConvert::YUV444ToRGB(unsigned char* yuv, unsigned char* rgb, unsigned int size) { register int U, V, R, G, B, V2, U5, UV; register int Y; @@ -2103,9 +2080,7 @@ void vpImageConvert::YUV444ToRGB(unsigned char* yuv, yuv444 : u y v */ -void vpImageConvert::YUV444ToGrey(unsigned char* yuv, - unsigned char* grey, - unsigned int size) +void vpImageConvert::YUV444ToGrey(unsigned char* yuv, unsigned char* grey, unsigned int size) { yuv++; for(unsigned int i=0 ; i < size ; i++) @@ -2121,9 +2096,8 @@ void vpImageConvert::YUV444ToGrey(unsigned char* yuv, yuv420 : Y(NxM), V(N/2xM/2), U(N/2xM/2) */ -void vpImageConvert::YV12ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int width, unsigned int height) +void vpImageConvert::YV12ToRGBa(unsigned char* yuv, unsigned char* rgba, + unsigned int width, unsigned int height) { // std::cout << "call optimized ConvertYV12ToRGBa()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -2222,9 +2196,8 @@ void vpImageConvert::YV12ToRGBa(unsigned char* yuv, yuv420 : Y(NxM), V(N/2xM/2), U(N/2xM/2) */ -void vpImageConvert::YV12ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int height, unsigned int width) +void vpImageConvert::YV12ToRGB(unsigned char* yuv, unsigned char* rgb, + unsigned int height, unsigned int width) { // std::cout << "call optimized ConvertYV12ToRGB()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -2320,9 +2293,8 @@ void vpImageConvert::YV12ToRGB(unsigned char* yuv, yuv420 : Y(NxM), V(N/4xM/4), U(N/4xM/4) */ -void vpImageConvert::YVU9ToRGBa(unsigned char* yuv, - unsigned char* rgba, - unsigned int width, unsigned int height) +void vpImageConvert::YVU9ToRGBa(unsigned char* yuv, unsigned char* rgba, + unsigned int width, unsigned int height) { // std::cout << "call optimized ConvertYVU9ToRGBa()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -2614,9 +2586,8 @@ void vpImageConvert::YVU9ToRGBa(unsigned char* yuv, yuv420 : Y(NxM), V(N/4xM/4), U(N/4xM/4) */ -void vpImageConvert::YVU9ToRGB(unsigned char* yuv, - unsigned char* rgb, - unsigned int height, unsigned int width) +void vpImageConvert::YVU9ToRGB(unsigned char* yuv, unsigned char* rgb, + unsigned int height, unsigned int width) { // std::cout << "call optimized ConvertYVU9ToRGB()" << std::endl; register int U, V, R, G, B, V2, U5, UV; @@ -2892,8 +2863,7 @@ void vpImageConvert::YVU9ToRGB(unsigned char* yuv, Convert RGB into RGBa */ -void vpImageConvert::RGBToRGBa(unsigned char* rgb, unsigned char* rgba, - unsigned int size) +void vpImageConvert::RGBToRGBa(unsigned char* rgb, unsigned char* rgba, unsigned int size) { unsigned char *pt_input = rgb; unsigned char *pt_end = rgb + 3*size; @@ -2912,8 +2882,7 @@ void vpImageConvert::RGBToRGBa(unsigned char* rgb, unsigned char* rgba, Convert RGB into RGBa */ -void vpImageConvert::RGBaToRGB(unsigned char* rgba, unsigned char* rgb, - unsigned int size) +void vpImageConvert::RGBaToRGB(unsigned char* rgba, unsigned char* rgb, unsigned int size) { unsigned char *pt_input = rgba; unsigned char *pt_end = rgba + 4*size; @@ -2932,8 +2901,7 @@ void vpImageConvert::RGBaToRGB(unsigned char* rgba, unsigned char* rgb, http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html */ -void vpImageConvert::RGBToGrey(unsigned char* rgb, unsigned char* grey, - unsigned int size) +void vpImageConvert::RGBToGrey(unsigned char* rgb, unsigned char* grey, unsigned int size) { unsigned char *pt_input = rgb; unsigned char* pt_end = rgb + size*3; @@ -2953,8 +2921,7 @@ void vpImageConvert::RGBToGrey(unsigned char* rgb, unsigned char* grey, http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html */ -void vpImageConvert::RGBaToGrey(unsigned char* rgba, unsigned char* grey, - unsigned int size) +void vpImageConvert::RGBaToGrey(unsigned char* rgba, unsigned char* grey, unsigned int size) { unsigned char *pt_input = rgba; unsigned char* pt_end = rgba + size*4; @@ -2974,8 +2941,7 @@ void vpImageConvert::RGBaToGrey(unsigned char* rgba, unsigned char* grey, */ void -vpImageConvert::GreyToRGBa(unsigned char* grey, - unsigned char* rgba, unsigned int size) +vpImageConvert::GreyToRGBa(unsigned char* grey, unsigned char* rgba, unsigned int size) { unsigned char *pt_input = grey; unsigned char *pt_end = grey + size; @@ -2998,8 +2964,7 @@ vpImageConvert::GreyToRGBa(unsigned char* grey, */ void -vpImageConvert::GreyToRGB(unsigned char* grey, - unsigned char* rgb, unsigned int size) +vpImageConvert::GreyToRGB(unsigned char* grey, unsigned char* rgb, unsigned int size) { unsigned char *pt_input = grey; unsigned char* pt_end = grey + size; @@ -3024,7 +2989,7 @@ vpImageConvert::GreyToRGB(unsigned char* grey, */ void vpImageConvert::BGRToRGBa(unsigned char * bgr, unsigned char * rgba, - unsigned int width, unsigned int height, bool flip) + unsigned int width, unsigned int height, bool flip) { //if we have to flip the image, we start from the end last scanline so the //step is negative @@ -3062,7 +3027,7 @@ vpImageConvert::BGRToRGBa(unsigned char * bgr, unsigned char * rgba, */ void vpImageConvert::BGRToGrey(unsigned char * bgr, unsigned char * grey, - unsigned int width, unsigned int height, bool flip) + unsigned int width, unsigned int height, bool flip) { //if we have to flip the image, we start from the end last scanline so the //step is negative @@ -3097,7 +3062,7 @@ vpImageConvert::BGRToGrey(unsigned char * bgr, unsigned char * grey, */ void vpImageConvert::RGBToRGBa(unsigned char * rgb, unsigned char * rgba, - unsigned int width, unsigned int height, bool flip) + unsigned int width, unsigned int height, bool flip) { //if we have to flip the image, we start from the end last scanline so the //step is negative @@ -3132,7 +3097,7 @@ vpImageConvert::RGBToRGBa(unsigned char * rgb, unsigned char * rgba, */ void vpImageConvert::RGBToGrey(unsigned char * rgb, unsigned char * grey, - unsigned int width, unsigned int height, bool flip) + unsigned int width, unsigned int height, bool flip) { //if we have to flip the image, we start from the end last scanline so the //step is negative @@ -3206,8 +3171,7 @@ void vpImageConvert::computeYCbCrLUT() */ -void vpImageConvert::YCbCrToRGB(unsigned char *ycbcr, unsigned char *rgb, - unsigned int size) +void vpImageConvert::YCbCrToRGB(unsigned char *ycbcr, unsigned char *rgb, unsigned int size) { unsigned char *cbv; unsigned char *crv; @@ -3265,8 +3229,7 @@ void vpImageConvert::YCbCrToRGB(unsigned char *ycbcr, unsigned char *rgb, */ -void vpImageConvert::YCbCrToRGBa(unsigned char *ycbcr, unsigned char *rgba, - unsigned int size) +void vpImageConvert::YCbCrToRGBa(unsigned char *ycbcr, unsigned char *rgba, unsigned int size) { unsigned char *cbv; unsigned char *crv; @@ -3321,9 +3284,7 @@ void vpImageConvert::YCbCrToRGBa(unsigned char *ycbcr, unsigned char *rgba, - In grey format, each pixel is coded using 8 bytes. */ -void vpImageConvert::YCbCrToGrey(unsigned char* yuv, - unsigned char* grey, - unsigned int size) +void vpImageConvert::YCbCrToGrey(unsigned char* yuv, unsigned char* grey, unsigned int size) { unsigned int i=0,j=0; @@ -3353,8 +3314,7 @@ void vpImageConvert::YCbCrToGrey(unsigned char* yuv, Byte 2: Blue */ -void vpImageConvert::YCrCbToRGB(unsigned char *ycrcb, unsigned char *rgb, - unsigned int size) +void vpImageConvert::YCrCbToRGB(unsigned char *ycrcb, unsigned char *rgb, unsigned int size) { unsigned char *cbv; unsigned char *crv; @@ -3410,8 +3370,7 @@ void vpImageConvert::YCrCbToRGB(unsigned char *ycrcb, unsigned char *rgb, */ -void vpImageConvert::YCrCbToRGBa(unsigned char *ycrcb, unsigned char *rgba, - unsigned int size) +void vpImageConvert::YCrCbToRGBa(unsigned char *ycrcb, unsigned char *rgba, unsigned int size) { unsigned char *cbv; unsigned char *crv; @@ -3485,10 +3444,10 @@ int main() \endcode */ void vpImageConvert::split(const vpImage<vpRGBa> &src, - vpImage<unsigned char>* pR, - vpImage<unsigned char>* pG, - vpImage<unsigned char>* pB, - vpImage<unsigned char>* pa) + vpImage<unsigned char>* pR, + vpImage<unsigned char>* pG, + vpImage<unsigned char>* pB, + vpImage<unsigned char>* pa) { register size_t n = src.getNumberOfPixel(); unsigned int height = src.getHeight(); @@ -3549,8 +3508,7 @@ void vpImageConvert::split(const vpImage<vpRGBa> &src, \param size : The image size or the number of pixels. */ -void vpImageConvert::MONO16ToGrey(unsigned char *grey16, unsigned char *grey, - unsigned int size) +void vpImageConvert::MONO16ToGrey(unsigned char *grey16, unsigned char *grey, unsigned int size) { register int i = (((int)size)<<1)-1; register int j = (int)size-1; @@ -3572,8 +3530,7 @@ void vpImageConvert::MONO16ToGrey(unsigned char *grey16, unsigned char *grey, \param size : The image size or the number of pixels. */ -void vpImageConvert::MONO16ToRGBa(unsigned char *grey16, unsigned char *rgba, - unsigned int size) +void vpImageConvert::MONO16ToRGBa(unsigned char *grey16, unsigned char *rgba, unsigned int size) { register int i = (((int)size)<<1)-1; register int j = (int)(size*4-1); diff --git a/src/image/vpImageConvert.h b/src/image/vpImageConvert.h index c43d62d3eb2a779e69d30f5d3ab2edf321a1d058..a9f548e7a7c119b4acec1134c482df58c5ab131c 100644 --- a/src/image/vpImageConvert.h +++ b/src/image/vpImageConvert.h @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpImageConvert.h 4216 2013-04-17 09:06:18Z fspindle $ +* $Id: vpImageConvert.h 5204 2015-01-24 13:18:18Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,7 +57,14 @@ #include <visp/vpRGBa.h> #ifdef VISP_HAVE_OPENCV -# if (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1 +# if (VISP_HAVE_OPENCV_VERSION >= 0x030000) // Require opencv >= 3.0.0 +# include <opencv2/core/core.hpp> +# include <opencv2/highgui/highgui.hpp> +# include <opencv2/imgproc/imgproc_c.h> +# elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) // Require opencv >= 2.4.8 +# include <opencv2/core/core.hpp> +# include <opencv2/highgui/highgui.hpp> +# elif (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1 # include <opencv2/core/core.hpp> # include <opencv2/legacy/legacy.hpp> # include <opencv2/highgui/highgui.hpp> @@ -71,7 +78,7 @@ # include <yarp/sig/Image.h> #endif -#ifdef WIN32 +#if defined(_WIN32) # include <windows.h> #endif @@ -117,6 +124,7 @@ public: vpImage<double> &dest); #ifdef VISP_HAVE_OPENCV + // Deprecated: will be removed with OpenCV transcient from C to C++ api static void convert(const IplImage* src, vpImage<vpRGBa> & dest, bool flip = false) ; static void convert(const IplImage* src, @@ -125,7 +133,7 @@ public: IplImage *&dest) ; static void convert(const vpImage<unsigned char> & src, IplImage* &dest) ; -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 +# if VISP_HAVE_OPENCV_VERSION >= 0x020100 static void convert(const cv::Mat& src, vpImage<vpRGBa>& dest, const bool flip = false); static void convert(const cv::Mat& src, @@ -134,7 +142,7 @@ public: cv::Mat& dest) ; static void convert(const vpImage<unsigned char> & src, cv::Mat& dest, const bool copyData = true) ; -#endif +# endif #endif #ifdef VISP_HAVE_YARP @@ -272,10 +280,10 @@ public: unsigned char* rgb, unsigned int size); static void BGRToRGBa(unsigned char * bgr, unsigned char * rgba, - unsigned int width, unsigned int height, bool flip); + unsigned int width, unsigned int height, bool flip=false); static void BGRToGrey(unsigned char * bgr, unsigned char * grey, - unsigned int width, unsigned int height, bool flip); + unsigned int width, unsigned int height, bool flip=false); static void YCbCrToRGB(unsigned char *ycbcr, unsigned char *rgb, unsigned int size); diff --git a/src/image/vpImageException.h b/src/image/vpImageException.h index 7519545cabd6670c9629bcb3407ae509cd88cf48..79c7fe9b60cea82c38393b460591b3a9c543e86b 100644 --- a/src/image/vpImageException.h +++ b/src/image/vpImageException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImageException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,12 +73,12 @@ */ class VISP_EXPORT vpImageException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpImage member */ - enum errorImageCodeEnum + enum errorImageCodeEnum { ioError, noFileNameError, @@ -87,25 +87,19 @@ public: notInTheImage } ; -public: - vpImageException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpImageException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpImageException (const int code) - : vpException(code){ ; } - + public: + vpImageException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpImageException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpImageException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpImageException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/image/vpImageFilter.cpp b/src/image/vpImageFilter.cpp index e659452ea9e2fa9ae89a8a204e5b3a44585fab7c..1739fe8266fbdbcbe38d0cf8e410842e4cdba07b 100644 --- a/src/image/vpImageFilter.cpp +++ b/src/image/vpImageFilter.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageFilter.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImageFilter.cpp 5200 2015-01-24 08:34:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,10 +41,12 @@ #include <visp/vpImageFilter.h> #include <visp/vpImageConvert.h> -#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020101) -#include <opencv2/imgproc/imgproc_c.h> -#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) -#include <cv.h> +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020408) +# include <opencv2/imgproc/imgproc.hpp> +#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020101) +# include <opencv2/imgproc/imgproc_c.h> +#elif defined(VISP_HAVE_OPENCV) +# include <cv.h> #endif /*! @@ -145,7 +147,7 @@ vpImageFilter::filter(const vpImage<double> &I, int main() { -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 // Cany uses OpenCV v>=2.1.0 +#if VISP_HAVE_OPENCV_VERSION >= 0x020100 // Canny uses OpenCV >=2.1.0 // Constants for the Canny operator. const unsigned int gaussianFilterSize = 5; const double thresholdCanny = 15; @@ -174,11 +176,12 @@ int main() */ void vpImageFilter:: canny(const vpImage<unsigned char>& Isrc, - vpImage<unsigned char>& Ires, - const unsigned int gaussianFilterSize, - const double thresholdCanny, - const unsigned int apertureSobel) + vpImage<unsigned char>& Ires, + const unsigned int gaussianFilterSize, + const double thresholdCanny, + const unsigned int apertureSobel) { +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) IplImage* img_ipl = NULL; vpImageConvert::convert(Isrc, img_ipl); IplImage* edges_ipl; @@ -190,5 +193,499 @@ vpImageFilter:: canny(const vpImage<unsigned char>& Isrc, vpImageConvert::convert(edges_ipl, Ires); cvReleaseImage(&img_ipl); cvReleaseImage(&edges_ipl); +#else + cv::Mat img_cvmat, edges_cvmat; + vpImageConvert::convert(Isrc, img_cvmat); + cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize), 0, 0); + cv::Canny(img_cvmat, edges_cvmat, thresholdCanny, thresholdCanny, (int)apertureSobel); + vpImageConvert::convert(edges_cvmat, Ires); +#endif +} +#endif + +/*! + Apply a separable filter. + */ +void vpImageFilter::filter(const vpImage<unsigned char> &I, vpImage<double>& GI, const double *filter,unsigned int size) +{ + vpImage<double> GIx ; + filterX(I, GIx,filter,size); + filterY(GIx, GI,filter,size); + GIx.destroy(); +} + +/*! + Apply a separable filter. + */ +void vpImageFilter::filter(const vpImage<double> &I, vpImage<double>& GI, const double *filter,unsigned int size) +{ + vpImage<double> GIx ; + filterX(I, GIx,filter,size); + filterY(GIx, GI,filter,size); + GIx.destroy(); +} + +void vpImageFilter::filterX(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *filter,unsigned int size) +{ + dIx.resize(I.getHeight(),I.getWidth()) ; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < (size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::filterXLeftBorder(I,i,j,filter,size); + //dIx[i][j]=0; + } + for (unsigned int j=(size-1)/2 ; j < I.getWidth()-(size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::filterX(I,i,j,filter,size); + } + for (unsigned int j=I.getWidth()-(size-1)/2 ; j < I.getWidth() ; j++) + { + dIx[i][j]=vpImageFilter::filterXRightBorder(I,i,j,filter,size); + //dIx[i][j]=0; + } + } +} +void vpImageFilter::filterX(const vpImage<double> &I, vpImage<double>& dIx, const double *filter,unsigned int size) +{ + dIx.resize(I.getHeight(),I.getWidth()) ; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < (size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::filterXLeftBorder(I,i,j,filter,size); + //dIx[i][j]=0; + } + for (unsigned int j=(size-1)/2 ; j < I.getWidth()-(size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::filterX(I,i,j,filter,size); + } + for (unsigned int j=I.getWidth()-(size-1)/2 ; j < I.getWidth() ; j++) + { + dIx[i][j]=vpImageFilter::filterXRightBorder(I,i,j,filter,size); + //dIx[i][j]=0; + } + } +} +void vpImageFilter::filterY(const vpImage<unsigned char> &I, vpImage<double>& dIy, const double *filter,unsigned int size) +{ + dIy.resize(I.getHeight(),I.getWidth()) ; + for (unsigned int i=0 ; i < (size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterYTopBorder(I,i,j,filter,size); + } + } + for (unsigned int i=(size-1)/2 ; i < I.getHeight()-(size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterY(I,i,j,filter,size); + } + } + for (unsigned int i=I.getHeight()-(size-1)/2 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterYBottomBorder(I,i,j,filter,size); + } + } +} +void vpImageFilter::filterY(const vpImage<double> &I, vpImage<double>& dIy, const double *filter,unsigned int size) +{ + dIy.resize(I.getHeight(),I.getWidth()) ; + for (unsigned int i=0 ; i < (size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterYTopBorder(I,i,j,filter,size); + } + } + for (unsigned int i=(size-1)/2 ; i < I.getHeight()-(size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterY(I,i,j,filter,size); + } + } + for (unsigned int i=I.getHeight()-(size-1)/2 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::filterYBottomBorder(I,i,j,filter,size); + } + } +} + +/*! + Apply a Gaussian blur to an image. + \param I : Input image. + \param GI : Filtered image. + \param size : Filter size. This value should be odd. + \param sigma : Gaussian standard deviation. If it is equal to zero or negative, it is computed from filter size as sigma = (size-1)/6. + \param normalize : Flag indicating whether to normalize the filter coefficients or not. + + */ +void vpImageFilter::gaussianBlur(const vpImage<unsigned char> &I, vpImage<double>& GI, unsigned int size, double sigma, bool normalize) +{ + double *fg=new double[(size+1)/2] ; + vpImageFilter::getGaussianKernel(fg, size, sigma, normalize) ; + vpImage<double> GIx ; + vpImageFilter::filterX(I, GIx,fg,size); + vpImageFilter::filterY(GIx, GI,fg,size); + GIx.destroy(); + delete[] fg; +} + +/*! + Return the coefficients of a Gaussian filter. + + \param filter : Pointer to the filter kernel that should refer to a (size+1)/2 array. + The first value refers to the central coefficient, the next one to the right coefficients. Left coefficients could be deduced by symmetry. + \param size : Filter size. This value should be odd. + \param sigma : Gaussian standard deviation. If it is equal to zero or negative, it is computed from filter size as sigma = (size-1)/6. + \param normalize : Flag indicating whether to normalize the filter coefficients or not. +*/ +void vpImageFilter::getGaussianKernel(double *filter, unsigned int size, double sigma, bool normalize) +{ + if (size%2 != 1) + throw (vpImageException(vpImageException::incorrectInitializationError, + "Bad Gaussian filter size")); + + if (sigma<= 0) + sigma = (size-1)/6.0; + + int middle = (int)(size-1)/2; + double sigma2 = vpMath::sqr(sigma); + for( int i=0; i<= middle; i++) + { + filter[i] = (1./(sigma*sqrt(2.*M_PI)))*exp(-(i*i)/(2.*sigma2)); + } + if (normalize) { + //renormalization + double sum=0; + for(int i=1; i<=middle; i++) + { + sum += 2*filter[i] ; + } + sum += filter[0]; + + for(int i=0; i<=middle; i++) + { + filter[i] = filter[i]/sum; + } + } +} + +/*! + Return the coefficients of a Gaussian derivative filter that may be used to compute spatial image derivatives after applying a Gaussian blur. + + \param filter : Pointer to the filter kernel that should refer to a (size+1)/2 array. + The first value refers to the central coefficient, the next one to the right coefficients. Left coefficients could be deduced by symmetry. + \param size : Filter size. This value should be odd. + \param sigma : Gaussian standard deviation. If it is equal to zero or negative, it is computed from filter size as sigma = (size-1)/6. + \param normalize : Flag indicating whether to normalize the filter coefficients or not. +*/ +void vpImageFilter::getGaussianDerivativeKernel(double *filter, unsigned int size, double sigma, bool normalize) +{ + if (size%2 != 1) + throw (vpImageException(vpImageException::incorrectInitializationError, + "Bad Gaussian filter size")); + + if (sigma<= 0) + sigma = (size-1)/6.0; + + int middle = (int)(size-1)/2; + double sigma2 = vpMath::sqr(sigma); + filter[0] = 0.; + for(int i=1; i<= middle; i++) + { + filter[i] = -(1./(sigma*sqrt(2.*M_PI)))*(exp(-((i+1)*(i+1))/(2.*sigma2))-exp(-((i-1)*(i-1))/(2.*sigma2)))/2.; + } + + if (normalize) { + double sum=0; + for(int i=1; i<=middle; i++) + { + sum += 2.*(1./(sigma*sqrt(2.*M_PI)))*exp(-(i*i)/(2.*sigma2)); + } + sum += (1./(sigma*sqrt(2.*M_PI))) ; + + for(int i=1; i<=middle; i++) + { + filter[i] = filter[i]/sum; + } + } +} + + +void vpImageFilter::getGradX(const vpImage<unsigned char> &I, vpImage<double>& dIx) +{ + dIx.resize(I.getHeight(),I.getWidth()) ; + //dIx=0; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < 3 ; j++) + { + dIx[i][j]=0; + } + for (unsigned int j=3 ; j < I.getWidth()-3 ; j++) + { + dIx[i][j]=vpImageFilter::derivativeFilterX(I,i,j); + } + for (unsigned int j=I.getWidth()-3 ; j < I.getWidth() ; j++) + { + dIx[i][j]=0; + } + } } + +void vpImageFilter::getGradY(const vpImage<unsigned char> &I, vpImage<double>& dIy) +{ + dIy.resize(I.getHeight(),I.getWidth()) ; + //dIy=0; + for (unsigned int i=0 ; i < 3 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } + for (unsigned int i=3 ; i < I.getHeight()-3 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::derivativeFilterY(I,i,j); + } + } + for (unsigned int i=I.getHeight()-3 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } +} + +void vpImageFilter::getGradX(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *filter,unsigned int size) +{ + dIx.resize(I.getHeight(),I.getWidth()) ; + //#pragma omp parallel for + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < (size-1)/2 ; j++) + { + dIx[i][j]=0; + } + for (unsigned int j=(size-1)/2 ; j < I.getWidth()-(size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::derivativeFilterX(I,i,j,filter,size); + } + for (unsigned int j=I.getWidth()-(size-1)/2 ; j < I.getWidth() ; j++) + { + dIx[i][j]=0; + } + } +} +void vpImageFilter::getGradX(const vpImage<double> &I, vpImage<double>& dIx, const double *filter,unsigned int size) +{ + dIx.resize(I.getHeight(),I.getWidth()) ; + //dIx=0; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < (size-1)/2 ; j++) + { + dIx[i][j]=0; + } + for (unsigned int j=(size-1)/2 ; j < I.getWidth()-(size-1)/2 ; j++) + { + dIx[i][j]=vpImageFilter::derivativeFilterX(I,i,j,filter,size); + } + for (unsigned int j=I.getWidth()-(size-1)/2 ; j < I.getWidth() ; j++) + { + dIx[i][j]=0; + } + } +} + +void vpImageFilter::getGradY(const vpImage<unsigned char> &I, vpImage<double>& dIy, const double *filter,unsigned int size) +{ + dIy.resize(I.getHeight(),I.getWidth()) ; + //#pragma omp parallel for + for (unsigned int i=0 ; i < (size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } + //#pragma omp parallel for + for (unsigned int i=(size-1)/2 ; i < I.getHeight()-(size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::derivativeFilterY(I,i,j,filter,size); + } + } + //#pragma omp parallel for + for (unsigned int i=I.getHeight()-(size-1)/2 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } +} + +void vpImageFilter::getGradY(const vpImage<double> &I, vpImage<double>& dIy, const double *filter,unsigned int size) +{ + dIy.resize(I.getHeight(),I.getWidth()) ; + //dIy=0; + for (unsigned int i=0 ; i < (size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } + for (unsigned int i=(size-1)/2 ; i < I.getHeight()-(size-1)/2 ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=vpImageFilter::derivativeFilterY(I,i,j,filter,size); + } + } + for (unsigned int i=I.getHeight()-(size-1)/2 ; i < I.getHeight() ; i++) + { + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + dIy[i][j]=0; + } + } +} + +/*! + Compute the gradient along X after applying a gaussian filter along Y. + \param I : Input image + \param dIx : Gradient along X. + \param gaussianKernel : Gaussian kernel which values should be computed using vpImageFilter::getGaussianKernel(). + \param gaussianDerivativeKernel : Gaussian derivative kernel which values should be computed using vpImageFilter::getGaussianDerivativeKernel(). + \param size : Size of the Gaussian and Gaussian derivative kernels. + */ +void vpImageFilter::getGradXGauss2D(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *gaussianKernel, const double *gaussianDerivativeKernel, unsigned int size) +{ + vpImage<double> GIy; + vpImageFilter::filterY(I, GIy, gaussianKernel, size); + vpImageFilter::getGradX(GIy, dIx, gaussianDerivativeKernel, size); +} + +/*! + Compute the gradient along Y after applying a gaussian filter along X. + \param I : Input image + \param dIy : Gradient along Y. + \param gaussianKernel : Gaussian kernel which values should be computed using vpImageFilter::getGaussianKernel(). + \param gaussianDerivativeKernel : Gaussian derivative kernel which values should be computed using vpImageFilter::getGaussianDerivativeKernel(). + \param size : Size of the Gaussian and Gaussian derivative kernels. + */ +void vpImageFilter::getGradYGauss2D(const vpImage<unsigned char> &I, vpImage<double>& dIy, const double *gaussianKernel, const double *gaussianDerivativeKernel,unsigned int size) +{ + vpImage<double> GIx; + vpImageFilter::filterX(I, GIx, gaussianKernel, size); + vpImageFilter::getGradY(GIx, dIy, gaussianDerivativeKernel, size); +} + +//operation pour pyramide gaussienne +void vpImageFilter::getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI) +{ + vpImage<unsigned char> GIx; +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::Mat imgsrc, imgdest; + vpImageConvert::convert(I, imgsrc); + cv::pyrDown( imgsrc, imgdest, cv::Size((int)I.getWidth()/2,(int)I.getHeight()/2)); + vpImageConvert::convert(imgdest, GI); +#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat imgsrc, imgdest; + vpImageConvert::convert(I, imgsrc); + cv::pyrDown( imgsrc, imgdest, cvSize((int)I.getWidth()/2,(int)I.getHeight()/2)); + vpImageConvert::convert(imgdest, GI); +#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) + IplImage* imgsrc = NULL;//cvCreateImage(cvGetSize(imgign), IPL_DEPTH_8U, 1); + IplImage* imgdest = NULL;//cvCreateImage(cvGetSize(imgign), IPL_DEPTH_8U, 1); + imgsrc = cvCreateImage(cvSize((int)I.getWidth(),(int)I.getHeight()), IPL_DEPTH_8U, 1); + imgdest = cvCreateImage(cvSize((int)I.getWidth()/2,(int)I.getHeight()/2), IPL_DEPTH_8U, 1); + vpImageConvert::convert(I,imgsrc); + cvPyrDown( imgsrc, imgdest); + vpImageConvert::convert(imgdest,GI); + + cvReleaseImage(&imgsrc); + cvReleaseImage(&imgdest); + //vpImage<unsigned char> sGI;sGI=GI; + +#else + vpImageFilter::getGaussXPyramidal(I,GIx); + vpImageFilter::getGaussYPyramidal(GIx,GI); #endif +} + +void vpImageFilter::getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI) +{ +#if 0 + GI.resize(I.getHeight(),(int)((I.getWidth()+1.)/2.)) ; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + GI[i][0]=I[i][0]; + for (unsigned int j=1 ; j < ((I.getWidth()+1.)/2.)-1 ; j++) + { + GI[i][j]=vpImageFilter::filterGaussXPyramidal(I,i,2*j); + } + GI[i][(int)((I.getWidth()+1.)/2.)-1]=I[i][2*((int)((I.getWidth()+1.)/2.)-1)]; + } +#else + unsigned int w = I.getWidth()/2; + + GI.resize(I.getHeight(), w) ; + for (unsigned int i=0 ; i < I.getHeight() ; i++) + { + GI[i][0]=I[i][0]; + for (unsigned int j=1 ; j < w-1 ; j++) + { + GI[i][j]=vpImageFilter::filterGaussXPyramidal(I,i,2*j); + } + GI[i][w-1]=I[i][2*w-1]; + } + +#endif +} +void vpImageFilter::getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI) +{ + +#ifdef ORIG + GI.resize((int)((I.getHeight()+1.)/2.),I.getWidth()) ; + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + GI[0][j]=I[0][j]; + for (unsigned int i=1 ; i < ((I.getHeight()+1.)/2.)-1 ; i++) + { + GI[i][j]=vpImageFilter::filterGaussYPyramidal(I,2*i,j); + } + GI[(int)((I.getHeight()+1.)/2.)-1][j]=I[2*((int)((I.getHeight()+1.)/2.)-1)][j]; + } + +#else + unsigned int h = I.getHeight()/2; + + GI.resize(h, I.getWidth()) ; + for (unsigned int j=0 ; j < I.getWidth() ; j++) + { + GI[0][j]=I[0][j]; + for (unsigned int i=1 ; i < h-1 ; i++) + { + GI[i][j]=vpImageFilter::filterGaussYPyramidal(I,2*i,j); + } + GI[h-1][j]=I[2*h-1][j]; + } +#endif +} + + diff --git a/src/image/vpImageFilter.h b/src/image/vpImageFilter.h index db72daa6007525487b5fb1dddadb9cc745d57857..0a43b22759be1b80ac71cb90515f785582d293b8 100644 --- a/src/image/vpImageFilter.h +++ b/src/image/vpImageFilter.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpImageFilter.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImageFilter.h 4991 2014-11-21 16:03:09Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -72,16 +72,6 @@ class VISP_EXPORT vpImageFilter { public: - static void filter(const vpImage<double> &I, - vpImage<double>& Iu, - vpImage<double>& Iv, - const vpMatrix& M) ; - - - static void filter(const vpImage<unsigned char> &I, - vpImage<double>& If, - const vpMatrix& M) ; - #if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) static void canny(const vpImage<unsigned char>& I, vpImage<unsigned char>& Ic, @@ -91,161 +81,368 @@ public: #endif /*! - Apply a 5x5 Gaussian filter to an image pixel. + Apply a 1x3 derivative filter to an image pixel. - \param fr : Image to filter - \param r : coordinates (row) of the pixel + \param I : Image to filter + \param r: coordinates (row) of the pixel \param c : coordinates (column) of the pixel */ template<class T> - static double - gaussianFilter(vpImage<T> & fr, - const unsigned int r, const unsigned int c) + static double derivativeFilterX(const vpImage<T> &I, + const unsigned int r, const unsigned int c) { - //filter Gaussien - return ( - 15.0 * fr[r][c] - + 12.0 * ( fr[r-1][c] + fr[r][c-1] + fr[r+1][c] + fr[r][c+1] ) - + 9.0 * ( fr[r-1][c-1] + fr[r+1][c-1] + fr[r-1][c+1] + fr[r+1][c+1]) - + 5.0 * ( fr[r-2][c] + fr[r][c-2] + fr[r+2][c] + fr[r][c+2] ) - + 4.0 * ( fr[r-2][c+1] + fr[r-2][c-1] + fr[r-1][c-2] + fr[r+1][c-2] + - fr[r+2][c-1] + fr[r+2][c+1] + fr[r-1][c+2] + fr[r+1][c+2] ) - + 2.0 * ( fr[r-2][c-2] + fr[r+2][c-2] + fr[r-2][c+2] + fr[r+2][c+2] ) - ) - /159.0; + return (2047.0 *(I[r][c+1] - I[r][c-1]) + +913.0 *(I[r][c+2] - I[r][c-2]) + +112.0 *(I[r][c+3] - I[r][c-3]))/8418.0; } - - /*! - Apply a 1x3 Derivative Filter to an image pixel. + Apply a 3x1 derivative filter to an image pixel. - \param fr : Image to filter - \param r: coordinates (row) of the pixel + \param I : Image to filter + \param r : coordinates (row) of the pixel \param c : coordinates (column) of the pixel */ template<class T> - static double - derivativeFilterX(vpImage<T> & fr, - const unsigned int r, const unsigned int c) + static double derivativeFilterY(const vpImage<T> &I, + const unsigned int r, const unsigned int c) { - return (2047.0 *(fr[r][c+1] - fr[r][c-1]) - +913.0 *(fr[r][c+2] - fr[r][c-2]) - +112.0 *(fr[r][c+3] - fr[r][c-3]))/8418.0; + return (2047.0 *(I[r+1][c] - I[r-1][c]) + +913.0 *(I[r+2][c] - I[r-2][c]) + +112.0 *(I[r+3][c] - I[r-3][c]))/8418.0; } /*! - Apply a 3x1 Derivative Filter to an image pixel. + Apply a 1 x size Derivative Filter in X to an image pixel. - \param fr : Image to filter + \param I : Image to filter \param r : coordinates (row) of the pixel \param c : coordinates (column) of the pixel + \param filter : coefficients of the filter to be initialized using vpImageFilter::getGaussianDerivativeKernel(). + \param size : size of the filter + + \sa vpImageFilter::getGaussianDerivativeKernel() */ + template<class T> - static double - derivativeFilterY(vpImage<T> & fr, - const unsigned int r, const unsigned int c) + static double derivativeFilterX(const vpImage<T> &I, + const unsigned int r, const unsigned int c, + const double *filter, const unsigned int size) { - return (2047.0 *(fr[r+1][c] - fr[r-1][c]) - +913.0 *(fr[r+2][c] - fr[r-2][c]) - +112.0 *(fr[r+3][c] - fr[r-3][c]))/8418.0; + unsigned int i; + double result; + + result = 0; + + for(i=1; i<=(size-1)/2; i++) + { + result += filter[i]*(I[r][c+i] - I[r][c-i]) ; + } + return result; } + + /*! - build a Gaussian Derivative filter + Apply a size x 1 Derivative Filter in Y to an image pixel. - \param filter : array (of size t/2) that contains the filter - \param t : size of the filter + \param I : Image to filter + \param r : coordinates (row) of the pixel + \param c : coordinates (column) of the pixel + \param filter : coefficients of the filter to be initialized using vpImageFilter::getGaussianDerivativeKernel(). + \param size : size of the filter - \warning filter has to be deallocated + \sa vpImageFilter::getGaussianDerivativeKernel() */ - static void - coefficientGaussianDerivative(double *filter, const unsigned int t) + template<class T> + static double derivativeFilterY(const vpImage<T> &I, + const unsigned int r, const unsigned int c, + const double *filter, const unsigned int size) { unsigned int i; - // double sigma; - if (filter == NULL) - filter = new double[t/2] ; + double result; - double s2 = vpMath::sqr((t-1)/6.0); + result = 0; - for(i=1; i<=(t-1)/2; i++) + for(i=1; i<=(size-1)/2; i++) { - filter[i] = (i/(s2*sqrt(2*M_PI)))*exp((i*i)/(-2*s2)); + result += filter[i]*(I[r+i][c] - I[r-i][c]) ; + } + return result; + } + + static void filter(const vpImage<double> &I, + vpImage<double>& Iu, + vpImage<double>& Iv, + const vpMatrix& M) ; + + + static void filter(const vpImage<unsigned char> &I, + vpImage<double>& If, + const vpMatrix& M) ; + + + static void filter(const vpImage<unsigned char> &I, vpImage<double>& GI, const double *filter,unsigned int size); + static void filter(const vpImage<double> &I, vpImage<double>& GI, const double *filter,unsigned int size); + + static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j) + { + return (unsigned char)((1.*I[i][j-2]+4.*I[i][j-1]+6.*I[i][j]+4.*I[i][j+1]+1.*I[i][j+2])/16.); + } + static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j) + { + return (unsigned char)((1.*I[i-2][j]+4.*I[i-1][j]+6.*I[i][j]+4.*I[i+1][j]+1.*I[i+2][j])/16.); + } + + static void filterX(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *filter,unsigned int size); + static void filterX(const vpImage<double> &I, vpImage<double>& dIx, const double *filter,unsigned int size); + static inline double filterX(const vpImage<unsigned char> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; } + return result+filter[0]*I[r][c]; + } + + + static inline double filterXLeftBorder(const vpImage<unsigned char> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; + + result = 0; + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(c>i) + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; + else + result += filter[i]*(I[r][c+i] + I[r][i-c]) ; + } + return result+filter[0]*I[r][c]; } + static inline double filterXRightBorder(const vpImage<unsigned char> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; - /*! - Apply a 1 x size Derivative Filter in X to an image pixel. + result = 0; - \param I : Image to filter - \param r : coordinates (row) of the pixel - \param c : coordinates (column) of the pixel - \param filter : coefficients of the filter to be initialized using vpImageFilter::coefficientGaussianDerivative(). - \param size : size of the filter + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(c+i<I.getWidth()) + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; + else + result += filter[i]*(I[r][2*I.getWidth()-c-i-1] + I[r][c-i]) ; + } + return result+filter[0]*I[r][c]; + } - \sa vpImageFilter::coefficientGaussianDerivative() - */ + static inline double filterX(const vpImage<double> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; - template<class T> - static double - derivativeFilterX(vpImage<T> &I, - const unsigned int r, const unsigned int c, - double *filter, const unsigned int size) + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; + } + return result+filter[0]*I[r][c]; + } + + static inline double filterXLeftBorder(const vpImage<double> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) { - unsigned int i; - double result; + double result; - result = 0; + result = 0; - for(i=1; i<=(size-1)/2; i++) - { - result += filter[i]*(I[r][c+i] - I[r][c-i]) ; - } - return result; + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(c>i) + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; + else + result += filter[i]*(I[r][c+i] + I[r][i-c]) ; + } + return result+filter[0]*I[r][c]; } + static inline double filterXRightBorder(const vpImage<double> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; + result = 0; + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(c+i<I.getWidth()) + result += filter[i]*(I[r][c+i] + I[r][c-i]) ; + else + result += filter[i]*(I[r][2*I.getWidth()-c-i-1] + I[r][c-i]) ; + } + return result+filter[0]*I[r][c]; + } + + static void filterY(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *filter,unsigned int size); + static void filterY(const vpImage<double> &I, vpImage<double>& dIx, const double *filter,unsigned int size); + static inline double filterY(const vpImage<unsigned char> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + } + return result+filter[0]*I[r][c]; + } + + double static inline filterYTopBorder(const vpImage<unsigned char> &I, unsigned int r, unsigned int c, const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(r>i) + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + else + result += filter[i]*(I[r+i][c] + I[i-r][c]) ; + } + return result+filter[0]*I[r][c]; + } + + double static inline filterYBottomBorder(const vpImage<unsigned char> &I, unsigned int r, unsigned int c, const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(r+i<I.getHeight()) + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + else + result += filter[i]*(I[2*I.getHeight()-r-i-1][c] + I[r-i][c]) ; + } + return result+filter[0]*I[r][c]; + } + + static inline double filterYTopBorder(const vpImage<double> &I, unsigned int r, unsigned int c, const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(r>i) + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + else + result += filter[i]*(I[r+i][c] + I[i-r][c]) ; + } + return result+filter[0]*I[r][c]; + } + + static inline double filterYBottomBorder(const vpImage<double> &I, unsigned int r, unsigned int c, const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + if(r+i<I.getHeight()) + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + else + result += filter[i]*(I[2*I.getHeight()-r-i-1][c] + I[r-i][c]) ; + } + return result+filter[0]*I[r][c]; + } + + static inline double filterY(const vpImage<double> &I, + unsigned int r, unsigned int c, + const double *filter,unsigned int size) + { + double result; + + result = 0; + + for(unsigned int i=1; i<=(size-1)/2; i++) + { + result += filter[i]*(I[r+i][c] + I[r-i][c]) ; + } + return result+filter[0]*I[r][c]; + } + + static void gaussianBlur(const vpImage<unsigned char> &I, vpImage<double>& GI, unsigned int size=7, double sigma=0., bool normalize=true); /*! - Apply a size x 1 Derivative Filter in Y to an image pixel. + Apply a 5x5 Gaussian filter to an image pixel. - \param I : Image to filter + \param fr : Image to filter \param r : coordinates (row) of the pixel \param c : coordinates (column) of the pixel - \param filter : coefficients of the filter to be initialized using vpImageFilter::coefficientGaussianDerivative(). - \param size : size of the filter - - \sa vpImageFilter::coefficientGaussianDerivative() */ template<class T> - static double - derivativeFilterY(vpImage<T> &I, - const unsigned int r, const unsigned int c, - double *filter, const unsigned int size) + static double gaussianFilter(const vpImage<T> & fr, + const unsigned int r, const unsigned int c) { - unsigned int i; - double result; - - result = 0; - - for(i=1; i<=(size-1)/2; i++) - { - result += filter[i]*(I[r+i][c] - I[r-i][c]) ; - } - return result; + //filter Gaussien + return ( + 15.0 * fr[r][c] + + 12.0 * ( fr[r-1][c] + fr[r][c-1] + fr[r+1][c] + fr[r][c+1] ) + + 9.0 * ( fr[r-1][c-1] + fr[r+1][c-1] + fr[r-1][c+1] + fr[r+1][c+1]) + + 5.0 * ( fr[r-2][c] + fr[r][c-2] + fr[r+2][c] + fr[r][c+2] ) + + 4.0 * ( fr[r-2][c+1] + fr[r-2][c-1] + fr[r-1][c-2] + fr[r+1][c-2] + + fr[r+2][c-1] + fr[r+2][c+1] + fr[r-1][c+2] + fr[r+1][c+2] ) + + 2.0 * ( fr[r-2][c-2] + fr[r+2][c-2] + fr[r-2][c+2] + fr[r+2][c+2] ) + ) + /159.0; } + //operation pour pyramide gaussienne + static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI); + static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI); + static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char>& GI); + + static void getGaussianKernel(double *filter, unsigned int size, double sigma=0., bool normalize=true); + static void getGaussianDerivativeKernel(double *filter, unsigned int size, double sigma=0., bool normalize=true); + + //fonction renvoyant le gradient en X de l'image I pour traitement pyramidal => dimension /2 + static void getGradX(const vpImage<unsigned char> &I, vpImage<double>& dIx); + static void getGradX(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *filter, unsigned int size); + static void getGradX(const vpImage<double> &I, vpImage<double>& dIx, const double *filter, unsigned int size); + static void getGradXGauss2D(const vpImage<unsigned char> &I, vpImage<double>& dIx, const double *gaussianKernel, + const double *gaussianDerivativeKernel, unsigned int size); + + //fonction renvoyant le gradient en Y de l'image I + static void getGradY(const vpImage<unsigned char> &I, vpImage<double>& dIy); + static void getGradY(const vpImage<unsigned char> &I, vpImage<double>& dIy, const double *filter, unsigned int size); + static void getGradY(const vpImage<double> &I, vpImage<double>& dIy, const double *filter, unsigned int size); + static void getGradYGauss2D(const vpImage<unsigned char> &I, vpImage<double>& dIy, const double *gaussianKernel, + const double *gaussianDerivativeKernel,unsigned int size); + } ; #endif - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/image/vpImageIo.cpp b/src/image/vpImageIo.cpp index e93254ac936c14f45ce6c8e43ef4b0e8b665df14..69b470c79d379e1531ad7bb8ac9303b772fbd38b 100644 --- a/src/image/vpImageIo.cpp +++ b/src/image/vpImageIo.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageIo.cpp 4315 2013-07-16 20:32:52Z fspindle $ + * $Id: vpImageIo.cpp 5249 2015-02-03 13:04:27Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,6 +47,7 @@ #include <visp/vpImage.h> #include <visp/vpImageIo.h> #include <visp/vpImageConvert.h> //image conversion +#include <visp/vpIoTools.h> const int vpImageIo::vpMAX_LEN = 100; @@ -65,7 +66,7 @@ vpImageIo::openFileRead(const char *filename) FILE *fd ; // Lecture du nom du fichier image. - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("filename empty ") ; throw (vpImageException(vpImageException::noFileNameError, "filename empty ")) ; @@ -99,7 +100,7 @@ vpImageIo::openFileWrite(const char *filename, const char *mode) FILE *fd ; // Lecture du nom du fichier image. - if (filename == '\0') + if (!filename || *filename == '\0') { vpERROR_TRACE("filename empty ") ; throw (vpImageException(vpImageException::noFileNameError, @@ -276,6 +277,11 @@ std::string vpImageIo::getExtension(const std::string &filename) void vpImageIo::read(vpImage<unsigned char> &I, const char *filename) { + bool exist = vpIoTools::checkFilename(filename); + if (!exist) { + std::string message = "Cannot read file: \"" + std::string(filename) + "\" doesn't exist"; + throw (vpImageException(vpImageException::ioError, message)); + } bool try_opencv_reader = false; switch(getFormat(filename)){ @@ -309,14 +315,25 @@ vpImageIo::read(vpImage<unsigned char> &I, const char *filename) } if (try_opencv_reader) { -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + //std::cout << "Use opencv to read the image" << std::endl; + cv::Mat cvI = cv::imread(filename, cv::IMREAD_GRAYSCALE); + if (cvI.cols == 0 && cvI.rows == 0) { + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; + } + vpImageConvert::convert(cvI, I); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 //std::cout << "Use opencv to read the image" << std::endl; cv::Mat cvI = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + if (cvI.cols == 0 && cvI.rows == 0) { + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; + } vpImageConvert::convert(cvI, I); #else - vpCERROR << "Cannot read file: Image format not supported..." << std::endl; - throw (vpImageException(vpImageException::ioError, - "Cannot read file: Image format not supported")) ; + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; #endif } } @@ -360,6 +377,12 @@ vpImageIo::read(vpImage<unsigned char> &I, const std::string filename) void vpImageIo::read(vpImage<vpRGBa> &I, const char *filename) { + bool exist = vpIoTools::checkFilename(filename); + if (!exist) { + std::string message = "Cannot read file: \"" + std::string(filename) + "\" doesn't exist"; + throw (vpImageException(vpImageException::ioError, message)); + } + bool try_opencv_reader = false; switch(getFormat(filename)){ @@ -393,14 +416,25 @@ vpImageIo::read(vpImage<vpRGBa> &I, const char *filename) } if (try_opencv_reader) { -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + // std::cout << "Use opencv to read the image" << std::endl; + cv::Mat cvI = cv::imread(filename, cv::IMREAD_COLOR); + if (cvI.cols == 0 && cvI.rows == 0) { + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; + } + vpImageConvert::convert(cvI, I); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 // std::cout << "Use opencv to read the image" << std::endl; cv::Mat cvI = cv::imread(filename, CV_LOAD_IMAGE_COLOR); + if (cvI.cols == 0 && cvI.rows == 0) { + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; + } vpImageConvert::convert(cvI, I); #else - vpCERROR << "Cannot read file: Image format not supported..." << std::endl; - throw (vpImageException(vpImageException::ioError, - "Cannot read file: Image format not supported")) ; + std::string message = "Cannot read file \"" + std::string(filename) + "\": Image format not supported"; + throw (vpImageException(vpImageException::ioError, message)) ; #endif } } @@ -601,7 +635,7 @@ vpImageIo::writePFM(const vpImage<float> &I, FILE* fd; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -657,7 +691,7 @@ vpImageIo::writePGM(const vpImage<unsigned char> &I, FILE* fd; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -732,7 +766,7 @@ vpImageIo::writePGM(const vpImage<vpRGBa> &I, const char *filename) FILE* fd; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -774,9 +808,9 @@ vpImageIo::writePGM(const vpImage<vpRGBa> &I, const char *filename) } /*! - Read a PGM P5 file and initialize a scalar image. + Read a PFM P8 file and initialize a float image. - Read the contents of the portable gray pixmap (PGM P5) filename, allocate + Read the contents of the portable gray pixmap (PFM P8) filename, allocate memory for the corresponding image, and set the bitmap whith the content of the file. @@ -801,7 +835,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) unsigned int w, h; // Test the filename - if (filename == '\0') + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename") ; throw (vpImageException(vpImageException::ioError, @@ -818,7 +852,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) "couldn't read file")) ; } - // Read the first line with magic number P5 + // Read the first line with magic number P8 line = 0; err = fgets(str, vpMAX_LEN - 1, fd); @@ -834,9 +868,9 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) if (strlen(str) < 3) { fclose (fd); - vpERROR_TRACE("\"%s\" is not a PGM file\n", filename) ; + vpERROR_TRACE("\"%s\" is not a PFM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a pfm file")) ; + "this is not a PFM file")) ; } str[2] = '\0'; @@ -845,7 +879,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("\"%s\" is not a PFM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a pgm file")) ; + "this is not a PFM file")) ; } // Jump the possible comment, or empty line and read the following line @@ -855,11 +889,18 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) if (err == NULL) { fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read content of PFM file")) ; } } while ((str[0] == '#') || (str[0] == '\n')); // Extract image size ierr = sscanf(str, "%d %d", &w, &h); + if (w > 100000 || h>100000) { + fclose (fd); + throw(vpException(vpException::badValue, "Bad image size")); + } + if(ierr == 1){// the norm allows to have the two values on two separated lines. do { err = fgets(str, vpMAX_LEN - 1, fd); @@ -867,6 +908,8 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) if (err == NULL) { fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read content of PFM file")) ; } } while ((str[0] == '#') || (str[0] == '\n')); ierr = sscanf(str, "%d", &h); @@ -876,20 +919,20 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read content of PFM file")) ; } if ((h != I.getHeight())||( w != I.getWidth())) { - try { I.resize(h,w) ; } catch(...) { - vpERROR_TRACE(" ") ; - throw ; + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read content of PFM file")) ; } } @@ -900,7 +943,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read content of PFM file")) ; } ierr = sscanf(str, "%d", &is255); @@ -908,7 +951,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("couldn't read line %d of file \"%s\"\n", line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read content of PFM file")) ; } if (is255 != 255) @@ -916,7 +959,7 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("MAX_VAL is not 255 in file \"%s\"\n", filename) ; throw (vpImageException(vpImageException::ioError, - "error reading pfm file")) ; + "Cannot read content of PFM file")) ; } unsigned int nbyte = I.getHeight()*I.getWidth(); @@ -925,12 +968,10 @@ vpImageIo::readPFM(vpImage<float> &I, const char *filename) fclose (fd); vpERROR_TRACE("couldn't read %d bytes in file \"%s\"\n", nbyte, filename) ; throw (vpImageException(vpImageException::ioError, - "error reading pfm file")) ; + "Cannot read content of PFM file")) ; } fclose (fd); - - } @@ -956,143 +997,189 @@ vpImageIo::readPGM(vpImage<unsigned char> &I, const char *filename) { FILE* fd = NULL; // File descriptor int ierr; - int line; - int is255; char* err ; char str[vpMAX_LEN]; - unsigned int w, h; + unsigned int magic=5, w=0, h=0, maxval=255; // Test the filename - if (filename == '\0') - { - vpERROR_TRACE("no filename") ; + if (!filename || *filename == '\0') { throw (vpImageException(vpImageException::ioError, - " no filename")) ; - + "No filename")) ; } // Open the filename - fd = fopen(filename, "rb"); - if (fd == NULL) - { - vpERROR_TRACE("couldn't read file \"%s\"", filename) ; + if ((fd = fopen(filename, "rb")) == NULL) { throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read file \"%s\"", filename)) ; } - // Read the first line with magic number P5 - line = 0; - - err = fgets(str, vpMAX_LEN - 1, fd); - line++; - if (err == NULL) - { + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n", line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read header of file \"%s\"", filename)); } - - if (strlen(str) < 3) - { + if ((ierr = sscanf(str, "P%u %u %u %u", &magic, &w, &h, &maxval)) == 0) { fclose (fd); - vpERROR_TRACE("\"%s\" is not a PGM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a pgm file")) ; + "Cannot read header of file \"%s\"", filename)); } - str[2] = '\0'; - if (strcmp(str, "P5") != 0) - { + if (magic != 5) { fclose (fd); - vpERROR_TRACE("\"%s\" is not a PGM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a pgm file")) ; + "\"%s\" is not a PGM P5 file", filename)); } - // Jump the possible comment, or empty line and read the following line - do { - err = fgets(str, vpMAX_LEN - 1, fd); - line++; + // Depending on ierr the line may contain: + // 1 : P5 + // 2 : P5 w + // 3 : P5 w h + // 4 : P5 w h maxval + + if (ierr == 1) { +// std::cout << "magic: " << magic << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; if (err == NULL) { - fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - } while ((str[0] == '#') || (str[0] == '\n')); + if (((ierr = sscanf(str, "%u %u %u", &w, &h, &maxval)) == 0) || (ierr != 1 && ierr != 2 && ierr != 3)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + // Depending on ierr the line may contain: + // 1 : w + // 2 : w h + // 3 : w h maxval + if (ierr == 1) { +// std::cout << "w: " << w << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (((ierr = sscanf(str, "%u %u", &h, &maxval)) == 0) || (ierr != 1 && ierr != 2)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (ierr == 1) { +// std::cout << "h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + } +// else { +// std::cout << "h: " << h << " maxval: " << maxval << std::endl; +// } + } + else if (ierr == 2) { +// std::cout << "w: " << w << " h: " << h << std::endl; - // Extract image size - ierr = sscanf(str, "%d %d", &w, &h); - if(ierr == 1){// the norm allows to have the two values on two separated lines. - do { - err = fgets(str, vpMAX_LEN - 1, fd); - line++; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; if (err == NULL) { - fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - } while ((str[0] == '#') || (str[0] == '\n')); - ierr = sscanf(str, "%d", &h); + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } +// std::cout << "maxval: " << maxval << std::endl; + } +// else { +// std::cout << "w: " << w << " h: " << h << " maxval: " << maxval << std::endl; +// } } - if (ierr == EOF) - { - fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; - throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + else if (ierr == 2) { +// std::cout << "magic: " << magic << " w: " << w << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (((ierr = sscanf(str, "%u %u", &h, &maxval)) == 0) || (ierr != 1 && ierr != 2)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (ierr == 1) { +// std::cout << "h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } +// std::cout << "maxval: " << maxval << std::endl; + } +// else { +// std::cout << "h: " << h << " maxval: " << maxval << std::endl; +// } } - - if ((h != I.getHeight())||( w != I.getWidth())) - { - - try - { - I.resize(h,w) ; + else if (ierr == 3) { +// std::cout << "magic: " << magic << " w: " << w << " h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } +// std::cout << "maxval: " << maxval << std::endl; } +// else if (ierr == 4) { +// std::cout << "magic: " << magic << " w: " << w << " h: " << h << " maxval: " << maxval << std::endl; +// } - // Read 255 - err = fgets(str, vpMAX_LEN - 1, fd); - line++; - if (err == NULL) { + if (w > 100000 || h>100000) { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; - throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename)); } - - ierr = sscanf(str, "%d", &is255); - if (ierr == EOF) { + if (maxval != 255) + { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n", line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Bad maxval in \"%s\"", filename)); } - if (is255 != 255) - { - fclose (fd); - vpERROR_TRACE("MAX_VAL is not 255 in file \"%s\"\n", filename) ; - throw (vpImageException(vpImageException::ioError, - "error reading pgm file")) ; + if ((h != I.getHeight())||( w != I.getWidth())) { + I.resize(h,w) ; } unsigned int nbyte = I.getHeight()*I.getWidth(); - if (fread (I.bitmap, sizeof(unsigned char), nbyte, fd ) != nbyte) - { + size_t n; + if ((n = fread (I.bitmap, sizeof(unsigned char), nbyte, fd)) != nbyte) { fclose (fd); - vpERROR_TRACE("couldn't read %d bytes in file \"%s\"\n", nbyte, filename) ; throw (vpImageException(vpImageException::ioError, - "error reading pgm file")) ; + "Read only %d of %d bytes in file \"%s\"", n, nbyte, filename)); } fclose (fd); - - } @@ -1117,14 +1204,12 @@ vpImageIo::readPGM(vpImage<unsigned char> &I, const char *filename) void vpImageIo::readPGM(vpImage<vpRGBa> &I, const char *filename) { - try { vpImage<unsigned char> Itmp ; vpImageIo::readPGM(Itmp, filename) ; - vpImageConvert::convert(Itmp, I) ; } @@ -1190,132 +1275,180 @@ vpImageIo::readPPM(vpImage<unsigned char> &I, const char *filename) void vpImageIo::readPPM(vpImage<vpRGBa> &I, const char *filename) { - FILE* fd = NULL; // File descriptor int ierr; - int line; - int is255; char* err ; char str[vpMAX_LEN]; - unsigned int w, h; + unsigned int magic=5, w=0, h=0, maxval=255; // Test the filename - if (filename == '\0') - { - vpERROR_TRACE("no filename") ; + if (!filename || *filename == '\0') { throw (vpImageException(vpImageException::ioError, - " no filename")) ; - + "No filename")) ; } // Open the filename - fd = fopen(filename, "rb"); - if (fd == NULL) - { - vpERROR_TRACE("couldn't read file \"%s\"", filename) ; + if ((fd = fopen(filename, "rb")) == NULL) { throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read file \"%s\"", filename)) ; } - // Read the first line with magic number P5 - line = 0; - - err = fgets(str, vpMAX_LEN - 1, fd); - line++; - if (err == NULL) - { + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n", line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Cannot read header of file \"%s\"", filename)); } - - if (strlen(str) < 3) - { + if ((ierr = sscanf(str, "P%u %u %u %u", &magic, &w, &h, &maxval)) == 0) { fclose (fd); - vpERROR_TRACE("\"%s\" is not a PPM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a ppm file")) ; + "Cannot read header of file \"%s\"", filename)); } - str[2] = '\0'; - if (strcmp(str, "P6") != 0) - { + if (magic != 6) { fclose (fd); - vpERROR_TRACE("\"%s\" is not a PPM file\n", filename) ; throw (vpImageException(vpImageException::ioError, - "this is not a ppm file")) ; + "\"%s\" is not a PGM P6 file", filename)); } - // Jump the possible comment, or empty line and read the following line - do { - err = fgets(str, vpMAX_LEN - 1, fd); - line++; + // Depending on ierr the line may contain: + // 1 : P6 + // 2 : P6 w + // 3 : P6 w h + // 4 : P6 w h maxval + + if (ierr == 1) { +// std::cout << "magic: " << magic << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; if (err == NULL) { - fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - } while ((str[0] == '#') || (str[0] == '\n')); + if (((ierr = sscanf(str, "%u %u %u", &w, &h, &maxval)) == 0) || (ierr != 1 && ierr != 2 && ierr != 3)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + // Depending on ierr the line may contain: + // 1 : w + // 2 : w h + // 3 : w h maxval + if (ierr == 1) { +// std::cout << "w: " << w << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (((ierr = sscanf(str, "%u %u", &h, &maxval)) == 0) || (ierr != 1 && ierr != 2)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (ierr == 1) { +// std::cout << "h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + } +// else { +// std::cout << "h: " << h << " maxval: " << maxval << std::endl; +// } + } + else if (ierr == 2) { +// std::cout << "w: " << w << " h: " << h << std::endl; - // Extract image size - ierr = sscanf(str, "%d %d", &w, &h); - if(ierr == 1){// the norm allows to have the two values on two separated lines. - do { - err = fgets(str, vpMAX_LEN - 1, fd); - line++; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; if (err == NULL) { - fprintf(stderr, "couldn't read line %d of file \"%s\"\n", line, filename); fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - } while ((str[0] == '#') || (str[0] == '\n')); - ierr = sscanf(str, "%d", &h); + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } +// std::cout << "maxval: " << maxval << std::endl; + } +// else { +// std::cout << "w: " << w << " h: " << h << " maxval: " << maxval << std::endl; +// } } - if (ierr == EOF) - { - fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; - throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + else if (ierr == 2) { +// std::cout << "magic: " << magic << " w: " << w << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (((ierr = sscanf(str, "%u %u", &h, &maxval)) == 0) || (ierr != 1 && ierr != 2)) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if (ierr == 1) { +// std::cout << "h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); + } +// std::cout << "maxval: " << maxval << std::endl; + } +// else { +// std::cout << "h: " << h << " maxval: " << maxval << std::endl; +// } } - - if ((h != I.getHeight())||( w != I.getWidth())) - { - - try - { - I.resize(h,w) ; + else if (ierr == 3) { +// std::cout << "magic: " << magic << " w: " << w << " h: " << h << std::endl; + while ((err = fgets(str, vpMAX_LEN - 1, fd)) != NULL && ((str[0] == '#') || (str[0] == '\n'))) {}; + if (err == NULL) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } - catch(...) - { - vpERROR_TRACE(" ") ; - throw ; + if ((ierr = sscanf(str, "%u", &maxval)) != 1) { + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read header of file \"%s\"", filename)); } +// std::cout << "maxval: " << maxval << std::endl; } +// else if (ierr == 4) { +// std::cout << "magic: " << magic << " w: " << w << " h: " << h << " maxval: " << maxval << std::endl; +// } - // Read 255 - err = fgets(str, vpMAX_LEN - 1, fd); - line++; - if (err == NULL) { + if (w > 100000 || h>100000) { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n",line, filename) ; - throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename)); } - - ierr = sscanf(str, "%d", &is255); - if (ierr == EOF) { + if (maxval != 255) + { fclose (fd); - vpERROR_TRACE("couldn't read line %d of file \"%s\"\n", line, filename) ; throw (vpImageException(vpImageException::ioError, - "couldn't read file")) ; + "Bad maxval in \"%s\"", filename)); } - if (is255 != 255) - { - fclose (fd); - vpERROR_TRACE("MAX_VAL is not 255 in file \"%s\"\n", filename) ; - throw (vpImageException(vpImageException::ioError, - "error reading ppm file")) ; + if ((h != I.getHeight())||( w != I.getWidth())) { + I.resize(h,w) ; } for(unsigned int i=0;i<I.getHeight();i++) @@ -1328,16 +1461,15 @@ vpImageIo::readPPM(vpImage<vpRGBa> &I, const char *filename) res |= fread(&v.B,sizeof(v.B),1,fd) ; if (res==0) { - fclose (fd); - vpERROR_TRACE("couldn't read bytes in file \"%s\"\n", filename) ; - throw (vpImageException(vpImageException::ioError, - "error reading ppm file")) ; + fclose (fd); + throw (vpImageException(vpImageException::ioError, + "Cannot read bytes in file \"%s\"\n", filename)); } I[i][j] = v ; } } - fclose(fd) ; + fclose (fd); } /*! @@ -1385,7 +1517,7 @@ vpImageIo::writePPM(const vpImage<vpRGBa> &I, const char *filename) // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -1635,7 +1767,7 @@ vpImageIo::writeJPEG(const vpImage<unsigned char> &I, const char *filename) jpeg_create_compress(&cinfo); // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -1714,7 +1846,7 @@ vpImageIo::writeJPEG(const vpImage<vpRGBa> &I, const char *filename) jpeg_create_compress(&cinfo); // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -1804,7 +1936,7 @@ vpImageIo::readJPEG(vpImage<unsigned char> &I, const char *filename) jpeg_create_decompress(&cinfo); // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -1917,7 +2049,7 @@ vpImageIo::readJPEG(vpImage<vpRGBa> &I, const char *filename) jpeg_create_decompress(&cinfo); // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -2019,12 +2151,18 @@ vpImageIo::readJPEG(vpImage<vpRGBa> &I, const std::string filename) void vpImageIo::writeJPEG(const vpImage<unsigned char> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + cv::imwrite(filename, Ip); +#else IplImage* Ip = NULL; vpImageConvert::convert(I, Ip); cvSaveImage(filename, Ip); cvReleaseImage(&Ip); +#endif } @@ -2052,12 +2190,18 @@ vpImageIo::writeJPEG(const vpImage<unsigned char> &I, const std::string filename void vpImageIo::writeJPEG(const vpImage<vpRGBa> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + cv::imwrite(filename, Ip); +#else IplImage* Ip = NULL; vpImageConvert::convert(I, Ip); cvSaveImage(filename, Ip); cvReleaseImage(&Ip); +#endif } @@ -2094,6 +2238,19 @@ vpImageIo::writeJPEG(const vpImage<vpRGBa> &I, const std::string filename) void vpImageIo::readJPEG(vpImage<unsigned char> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::Mat Ip = cv::imread(filename, cv::IMREAD_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#else IplImage* Ip = NULL; Ip = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE); if (Ip != NULL) @@ -2102,6 +2259,7 @@ vpImageIo::readJPEG(vpImage<unsigned char> &I, const char *filename) throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; cvReleaseImage(&Ip); +#endif } @@ -2149,14 +2307,27 @@ vpImageIo::readJPEG(vpImage<unsigned char> &I, const std::string filename) void vpImageIo::readJPEG(vpImage<vpRGBa> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::Mat Ip = cv::imread(filename, cv::IMREAD_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#else IplImage* Ip = NULL; Ip = cvLoadImage(filename, CV_LOAD_IMAGE_COLOR); if (Ip != NULL) vpImageConvert::convert(Ip, I); else - throw (vpImageException(vpImageException::ioError, - "Can't read the image")) ; + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; cvReleaseImage(&Ip); +#endif } @@ -2210,7 +2381,7 @@ vpImageIo::writePNG(const vpImage<unsigned char> &I, const char *filename) FILE *file; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -2362,7 +2533,7 @@ vpImageIo::writePNG(const vpImage<vpRGBa> &I, const char *filename) FILE *file; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -2524,7 +2695,7 @@ vpImageIo::readPNG(vpImage<unsigned char> &I, const char *filename) FILE *file; png_byte magic[8]; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -2743,7 +2914,7 @@ vpImageIo::readPNG(vpImage<vpRGBa> &I, const char *filename) png_byte magic[8]; // Test the filename - if (filename == '\0') { + if (!filename || *filename == '\0') { vpERROR_TRACE("no filename\n"); throw (vpImageException(vpImageException::ioError, "no filename")) ; @@ -2950,12 +3121,18 @@ vpImageIo::readPNG(vpImage<vpRGBa> &I, const std::string filename) void vpImageIo::writePNG(const vpImage<unsigned char> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + cv::imwrite(filename, Ip); +#else IplImage* Ip = NULL; vpImageConvert::convert(I, Ip); cvSaveImage(filename, Ip); cvReleaseImage(&Ip); +#endif } @@ -2983,12 +3160,18 @@ vpImageIo::writePNG(const vpImage<unsigned char> &I, const std::string filename) void vpImageIo::writePNG(const vpImage<vpRGBa> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + cv::imwrite(filename, Ip); +#else IplImage* Ip = NULL; vpImageConvert::convert(I, Ip); cvSaveImage(filename, Ip); cvReleaseImage(&Ip); +#endif } @@ -3025,6 +3208,19 @@ vpImageIo::writePNG(const vpImage<vpRGBa> &I, const std::string filename) void vpImageIo::readPNG(vpImage<unsigned char> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::Mat Ip = cv::imread(filename, cv::IMREAD_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#else IplImage* Ip = NULL; Ip = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE); if (Ip != NULL) @@ -3033,6 +3229,7 @@ vpImageIo::readPNG(vpImage<unsigned char> &I, const char *filename) throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; cvReleaseImage(&Ip); +#endif } @@ -3080,6 +3277,19 @@ vpImageIo::readPNG(vpImage<unsigned char> &I, const std::string filename) void vpImageIo::readPNG(vpImage<vpRGBa> &I, const char *filename) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + cv::Mat Ip = cv::imread(filename, cv::IMREAD_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip = cv::imread(filename, CV_LOAD_IMAGE_GRAYSCALE); + if ( ! Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; +#else IplImage* Ip = NULL; Ip = cvLoadImage(filename, CV_LOAD_IMAGE_COLOR); if (Ip != NULL) @@ -3088,6 +3298,7 @@ vpImageIo::readPNG(vpImage<vpRGBa> &I, const char *filename) throw (vpImageException(vpImageException::ioError, "Can't read the image")) ; cvReleaseImage(&Ip); +#endif } diff --git a/src/image/vpImageIo.h b/src/image/vpImageIo.h index 69acede104dbb590bc116568b8725c276b502c02..f08f6d8f0098c6074b1e76285b58305d871cd923 100644 --- a/src/image/vpImageIo.h +++ b/src/image/vpImageIo.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageIo.h 4314 2013-07-16 17:41:21Z fspindle $ + * $Id: vpImageIo.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,7 +56,7 @@ #include <stdio.h> #include <iostream> -#ifdef WIN32 +#if defined(_WIN32) # include <windows.h> #endif @@ -89,10 +89,10 @@ int main() { vpImage<unsigned char> I; -#ifdef UNIX - std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.ppm"); -#elif WIN32 +#if defined(_WIN32) std::string filename("C:/temp/ViSP-images/Klimt/Klimt.ppm"); +#else // UNIX + std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.ppm"); #endif vpImageIo::read(I, filename); // Convert the color image in a gray level image diff --git a/src/image/vpImageMorphology.h b/src/image/vpImageMorphology.h index 8f0385fdac9a8f24c1786695f6676aa098b3e77a..076e0dc34bd20369fe4184bb2016d25bdceb645e 100644 --- a/src/image/vpImageMorphology.h +++ b/src/image/vpImageMorphology.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageMorphology.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImageMorphology.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/image/vpImagePoint.cpp b/src/image/vpImagePoint.cpp index 6bffc2e47a4159265498303ba5d7c6ba96e43f73..c0a833969da8b61002db6b81ba5da7ae3d2876ea 100644 --- a/src/image/vpImagePoint.cpp +++ b/src/image/vpImagePoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImagePoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImagePoint.cpp 5244 2015-02-03 06:44:17Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -86,3 +86,330 @@ vpImagePoint::projection(const vpHomography& aHb) return ap; } + +/*! + + Operator +=. + + This operator can be used to compute the center of gravity of a set of image points. + + \code +#include <iostream> +#include <vector> +#include <visp/vpImagePoint.h> + +int main() +{ +std::vector<vpImagePoint> ip(2); + +ip[0].set_ij(100, 200); +ip[1].set_ij(300, 400); + +vpImagePoint cog(0,0); +for(unsigned int i=0; i<ip.size(); i++) + cog += ip[i]; +cog /= ip.size(); +std::cout << "cog: " << cog << std::endl; +} + \endcode + +*/ +vpImagePoint& vpImagePoint::operator+=(const vpImagePoint &ip) { + this->i += ip.i; + this->j += ip.j; + return *this; +} + +/*! + + Operator /=. + + This operator can be used to compute the center of gravity of a set of image points. + \code +#include <iostream> +#include <vector> +#include <visp/vpImagePoint.h> + +int main() +{ +std::vector<vpImagePoint> ip(2); + +ip[0].set_ij(100, 200); +ip[1].set_ij(300, 400); + +vpImagePoint cog(0,0); +for(unsigned int i=0; i<ip.size(); i++) + cog += ip[i]; +cog /= ip.size(); +std::cout << "cog: " << cog << std::endl; +} + \endcode + +*/ +vpImagePoint& vpImagePoint::operator/=(const double scale) { + this->i /= scale; + this->j /= scale; + return *this; +} + +/*! + \relates vpImagePoint + + Returns true if ip1 and ip2 are equal; otherwire returns false. + +*/ +VISP_EXPORT bool operator==( const vpImagePoint &ip1, const vpImagePoint &ip2 ) { + //return ( ( ip1.get_i() == ip2.get_i() ) && ( ip1.get_j() == ip2.get_j() ) ); + + double i1 = ip1.get_i(); + double j1 = ip1.get_j(); + double i2 = ip2.get_i(); + double j2 = ip2.get_j(); + + return ( + ( std::fabs(i1-i2) <= std::fabs(vpMath::maximum(i1, i2))*std::numeric_limits<double>::epsilon() ) + && + ( std::fabs(j1-j2) <= std::fabs(vpMath::maximum(j1, j2))*std::numeric_limits<double>::epsilon() ) + ); +} + +/*! + + \relates vpImagePoint + + Returns true if ip1 and ip2 are different; otherwire returns true. + +*/ +VISP_EXPORT bool operator!=( const vpImagePoint &ip1, const vpImagePoint &ip2 ) { + //return ( ( ip1.get_i() != ip2.get_i() ) || ( ip1.get_j() != ip2.get_j() ) ); + double i1 = ip1.get_i(); + double j1 = ip1.get_j(); + double i2 = ip2.get_i(); + double j2 = ip2.get_j(); + + return ( + ( std::fabs(i1-i2) > std::fabs(vpMath::maximum(i1, i2))*std::numeric_limits<double>::epsilon() ) + || + ( std::fabs(j1-j2) > std::fabs(vpMath::maximum(j1, j2))*std::numeric_limits<double>::epsilon() ) + ); +} + +/*! + + \relates vpImagePoint + + Returns a vpImagePoint wich is the sum of \f$ ip1 \f$ and \f$ ip2 \f$. + +*/ +VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const vpImagePoint &ip2 ) { + return ( vpImagePoint(ip1.get_i()+ip2.get_i(), ip1.get_j()+ip2.get_j())); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint wich is the sum of \f$ ip1 \f$ and \f$ ip2 \f$. + +*/ +VISP_EXPORT vpImagePoint operator+=( const vpImagePoint &ip1, const vpImagePoint &ip2 ) { + return ( vpImagePoint(ip1.get_i()+ip2.get_i(), ip1.get_j()+ip2.get_j())); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with an offset added to the two coordinates. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip+10: " << ip+10 << std::endl; // new coordinates (110, 210) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const int offset ) { + return ( vpImagePoint(ip1.get_i()+offset, ip1.get_j()+offset)); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with an offset added to the two coordinates. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip+12.34: " << ip+12.34 << std::endl; // new coordinates (112.34, 212.34) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const double offset ) { + return ( vpImagePoint(ip1.get_i()+offset, ip1.get_j()+offset)); +} + +/*! + + \relates vpImagePoint + + Returns a vpImagePoint wich is the difference between \f$ ip1 \f$ and \f$ ip2 \f$. + +*/ +VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const vpImagePoint &ip2 ) { + return ( vpImagePoint(ip1.get_i()-ip2.get_i(), ip1.get_j()-ip2.get_j())); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with an offset substracted to the two coordinates. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip-10: " << ip-10 << std::endl; // new coordinates (90, 190) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const int offset ) { + return ( vpImagePoint(ip1.get_i()-offset, ip1.get_j()-offset)); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with an offset substracted to the two coordinates. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip-12.34: " << ip-12.34 << std::endl; // new coordinates (87.66, 187.66) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const double offset ) { + return ( vpImagePoint(ip1.get_i()-offset, ip1.get_j()-offset)); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with coordinates multiplied by a scale factor. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip*2: " << ip*2 << std::endl; // new coordinates (200, 400) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator*( const vpImagePoint &ip1, const double scale ) { + return ( vpImagePoint(ip1.get_i()*scale, ip1.get_j()*scale)); +} +/*! + + \relates vpImagePoint + + Returns a vpImagePoint with coordinates divided by a scale factor. + + \code +#include <iostream> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 + std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) + std::cout << "ip/2: " << ip/2 << std::endl; // new coordinates (50, 100) + + return 0; +} + \endcode +*/ +VISP_EXPORT vpImagePoint operator/( const vpImagePoint &ip1, const double scale ) { + return ( vpImagePoint(ip1.get_i()/scale, ip1.get_j()/scale)); +} + +/*! + + \relates vpImagePoint + + Writes the image point coordinates \e ip to the stream \e os, and + returns a reference to the stream. Writes the first coordinate along + the \e i axis and then the second one along the \e j axis. The + coordinates are separated by a comma. + + The following code + \code +#include <iostream> + +#include <visp/vpImagePoint.h> +int main() +{ + vpImagePoint ip; + + ip.set_i(10); + ip.set_j(11.1); + + std::cout << "Image point with coordinates: " << ip << std::endl; + + return 0; +} + \endcode + + The previous sample code produces the output: + \verbatim +Image point with coordinates: 10, 11.1 + \endverbatim +*/ +VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpImagePoint& ip) +{ + os << ip.get_i() << ", " << ip.get_j(); + return os; +} + +/** + * Computes and returns the bounding box. + * @param ipVec : Vector of input image points. + * @return Bounding box of the points. + */ +vpRect vpImagePoint::getBBox(const std::vector<vpImagePoint>& ipVec) +{ + vpRect rec(ipVec); + + return rec; +} diff --git a/src/image/vpImagePoint.h b/src/image/vpImagePoint.h index 1b3e7f11ff766e2c7e4b1ec7780357dd9cc9306a..9fae9de20ad18340ee22892b8799f8da01479faf 100644 --- a/src/image/vpImagePoint.h +++ b/src/image/vpImagePoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImagePoint.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImagePoint.h 5175 2015-01-15 17:06:07Z nmeriaux $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,6 +55,7 @@ #include <ostream> #include <cmath> // std::fabs #include <limits> // numeric_limits +#include <vector> class vpHomography; class vpRect; @@ -96,18 +97,12 @@ class VISP_EXPORT vpImagePoint Default constructor that initialize the coordinates of the image point to zero. */ - inline vpImagePoint() { - i = 0; - j = 0; - } + inline vpImagePoint() : i(0), j(0) {} /*! Default constructor that initialize the coordinates of the image - thanks to the parameters \f$ i \f$ and \f$ j \f$. + thanks to the parameters \f$ ii \f$ and \f$ jj \f$. */ - inline vpImagePoint(double i, double j) { - this->i = i; - this->j = j; - } + inline vpImagePoint(double ii, double jj) : i(ii), j(jj) {} /*! Copy constructor. @@ -115,15 +110,12 @@ class VISP_EXPORT vpImagePoint \param ip : An image point. */ - inline vpImagePoint(const vpImagePoint &ip) { - this->i = ip.i; - this->j = ip.j; - } + inline vpImagePoint(const vpImagePoint &ip) : i(ip.i), j(ip.j) {} //! Destructor. inline virtual ~vpImagePoint() { ; } /*! - + Copy operator. */ @@ -132,41 +124,63 @@ class VISP_EXPORT vpImagePoint this->j = ip.j; return *this; } + vpImagePoint& operator+=(const vpImagePoint &ip); + + /*! + + Operator -=. + + */ + inline vpImagePoint& operator-=(const vpImagePoint &ip) { + this->i -= ip.i; + this->j -= ip.j; + return *this; + } + vpImagePoint& operator/=(const double scale); + /*! + + Operator *=. +*/ + inline vpImagePoint& operator*=(const double scale) { + this->i *= scale; + this->j *= scale; + return *this; + } /*! Sets the point coordinate corresponding to the \f$ i \f$ axes in the frame (i,j). - \param i : The desired value for the coordinate along the \f$ i \f$ axes. + \param ii : The desired value for the coordinate along the \f$ i \f$ axes. \sa set_j(), set_u(), set_v() */ - inline void set_i(const double i) { this->i = i ; } + inline void set_i(const double ii) { this->i = ii ; } /*! Sets the point coordinate corresponding to the \f$ j \f$ axes in the frame (i,j). - \param j : The desired value for the coordinate along the \f$ j \f$ axes. + \param jj : The desired value for the coordinate along the \f$ j \f$ axes. \sa set_i(), set_u(), set_v() */ - inline void set_j(const double j) { this->j = j ; } + inline void set_j(const double jj) { this->j = jj ; } /*! Sets the point coordinates in the frame (i,j). - \param i : The desired value for the coordinate along the \f$ i \f$ axes. - \param j : The desired value for the coordinate along the \f$ j \f$ axes. + \param ii : The desired value for the coordinate along the \f$ i \f$ axes. + \param jj : The desired value for the coordinate along the \f$ j \f$ axes. \sa set_i(), set_j(), set_u(), set_v() */ - inline void set_ij(const double i, const double j) { - this->i = i ; - this->j = j ; + inline void set_ij(const double ii, const double jj) { + this->i = ii ; + this->j = jj ; } /*! @@ -260,6 +274,9 @@ class VISP_EXPORT vpImagePoint */ static double distance (const vpImagePoint &iP1, const vpImagePoint &iP2) { return(sqrt(vpMath::sqr(iP1.get_i()-iP2.get_i())+vpMath::sqr(iP1.get_j()-iP2.get_j())));} + + + static vpRect getBBox(const std::vector<vpImagePoint>& ipVec); /*! @@ -276,268 +293,23 @@ class VISP_EXPORT vpImagePoint bool inRectangle( const vpRect &rect ) const; + friend VISP_EXPORT bool operator==( const vpImagePoint &ip1, const vpImagePoint &ip2 ); + friend VISP_EXPORT bool operator!=( const vpImagePoint &ip1, const vpImagePoint &ip2 ); + friend VISP_EXPORT vpImagePoint operator+=( const vpImagePoint &ip1, const vpImagePoint &ip2 ); + friend VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const vpImagePoint &ip2 ); + friend VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const int offset ); + friend VISP_EXPORT vpImagePoint operator+( const vpImagePoint &ip1, const double offset ); + friend VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const vpImagePoint &ip2 ); + friend VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const int offset ); + friend VISP_EXPORT vpImagePoint operator-( const vpImagePoint &ip1, const double offset ); + friend VISP_EXPORT vpImagePoint operator*( const vpImagePoint &ip1, const double scale ); + friend VISP_EXPORT vpImagePoint operator/( const vpImagePoint &ip1, const double scale ); + friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpImagePoint& ip); + vpImagePoint projection(const vpHomography& aHb); private: double i,j; }; -/*! - \relates vpImagePoint - - Returns true if ip1 and ip2 are equal; otherwire returns false. - -*/ -VISP_EXPORT inline bool operator==( const vpImagePoint &ip1, - const vpImagePoint &ip2 ) { - //return ( ( ip1.get_i() == ip2.get_i() ) && ( ip1.get_j() == ip2.get_j() ) ); - - double i1 = ip1.get_i(); - double j1 = ip1.get_j(); - double i2 = ip2.get_i(); - double j2 = ip2.get_j(); - - return ( - ( std::fabs(i1-i2) <= std::fabs(vpMath::maximum(i1, i2))*std::numeric_limits<double>::epsilon() ) - && - ( std::fabs(j1-j2) <= std::fabs(vpMath::maximum(j1, j2))*std::numeric_limits<double>::epsilon() ) - ); -} - -/*! - - \relates vpImagePoint - - Returns true if ip1 and ip2 are different; otherwire returns true. - -*/ -VISP_EXPORT inline bool operator!=( const vpImagePoint &ip1, - const vpImagePoint &ip2 ) { - //return ( ( ip1.get_i() != ip2.get_i() ) || ( ip1.get_j() != ip2.get_j() ) ); - double i1 = ip1.get_i(); - double j1 = ip1.get_j(); - double i2 = ip2.get_i(); - double j2 = ip2.get_j(); - - return ( - ( std::fabs(i1-i2) > std::fabs(vpMath::maximum(i1, i2))*std::numeric_limits<double>::epsilon() ) - || - ( std::fabs(j1-j2) > std::fabs(vpMath::maximum(j1, j2))*std::numeric_limits<double>::epsilon() ) - ); -} - -/*! - - \relates vpImagePoint - - Returns a vpImagePoint wich is the sum of \f$ ip1 \f$ and \f$ ip2 \f$. - -*/ -VISP_EXPORT inline vpImagePoint operator+( const vpImagePoint &ip1, - const vpImagePoint &ip2 ) { - return ( vpImagePoint(ip1.get_i()+ip2.get_i(), ip1.get_j()+ip2.get_j())); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with an offset added to the two coordinates. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip+10: " << ip+10 << std::endl; // new coordinates (110, 210) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator+( const vpImagePoint &ip1, - const int offset ) { - return ( vpImagePoint(ip1.get_i()+offset, ip1.get_j()+offset)); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with an offset added to the two coordinates. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip+12.34: " << ip+12.34 << std::endl; // new coordinates (112.34, 212.34) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator+( const vpImagePoint &ip1, - const double offset ) { - return ( vpImagePoint(ip1.get_i()+offset, ip1.get_j()+offset)); -} - -/*! - - \relates vpImagePoint - - Returns a vpImagePoint wich is the difference between \f$ ip1 \f$ and \f$ ip2 \f$. - -*/ -VISP_EXPORT inline vpImagePoint operator-( const vpImagePoint &ip1, - const vpImagePoint &ip2 ) { - return ( vpImagePoint(ip1.get_i()-ip2.get_i(), ip1.get_j()-ip2.get_j())); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with an offset substracted to the two coordinates. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip-10: " << ip-10 << std::endl; // new coordinates (90, 190) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator-( const vpImagePoint &ip1, - const int offset ) { - return ( vpImagePoint(ip1.get_i()-offset, ip1.get_j()-offset)); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with an offset substracted to the two coordinates. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip-12.34: " << ip-12.34 << std::endl; // new coordinates (87.66, 187.66) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator-( const vpImagePoint &ip1, - const double offset ) { - return ( vpImagePoint(ip1.get_i()-offset, ip1.get_j()-offset)); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with coordinates multiplied by a scale factor. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip*2: " << ip*2 << std::endl; // new coordinates (200, 400) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator*( const vpImagePoint &ip1, - const double scale ) { - return ( vpImagePoint(ip1.get_i()*scale, ip1.get_j()*scale)); -} -/*! - - \relates vpImagePoint - - Returns a vpImagePoint with coordinates divided by a scale factor. - - \code -#include <iostream> -#include <visp/vpImagePoint.h> - -int main() -{ - vpImagePoint ip(100, 200); // Create an image point with coordinates i=100, j=200 - std::cout << "ip: " << ip << std::endl; // coordinates (100, 200) - std::cout << "ip/2: " << ip/2 << std::endl; // new coordinates (50, 100) - - return 0; -} - \endcode -*/ -VISP_EXPORT inline vpImagePoint operator/( const vpImagePoint &ip1, - const double scale ) { - return ( vpImagePoint(ip1.get_i()/scale, ip1.get_j()/scale)); -} - -/*! - - \relates vpImagePoint - - Writes the image point coordinates \e ip to the stream \e os, and - returns a reference to the stream. Writes the first coordinate along - the \e i axis and then the second one along the \e j axis. The - coordinates are separated by a comma. - - The following code - \code -#include <iostream> - -#include <visp/vpImagePoint.h> -int main() -{ - vpImagePoint ip; - - ip.set_i(10); - ip.set_j(11.1); - - std::cout << "Image point with coordinates: " << ip << std::endl; - - return 0; -} - \endcode - - The previous sample code produces the output: - \verbatim -Image point with coordinates: 10, 11.1 - \endverbatim -*/ -VISP_EXPORT inline std::ostream& operator<< (std::ostream &os, - const vpImagePoint& ip) - { - os << ip.get_i() << ", " << ip.get_j(); - return os; -} - - #endif - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/image/vpImageTools.cpp b/src/image/vpImageTools.cpp index 59a4eef42031ddd12568b89c6e43ce7492c398e9..091bef3f7275b811d0b6798f2bb5188afbe29cc5 100644 --- a/src/image/vpImageTools.cpp +++ b/src/image/vpImageTools.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageTools.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImageTools.cpp 5236 2015-01-30 13:51:42Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,18 +74,18 @@ \code #include <visp/vpImageTools.h> - #include <visp/vpImage.h> #include <visp/vpImageIo.h> int main() { vpImage<unsigned char> I; -#ifdef UNIX - std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.pgm"); -#elif WIN32 - std::string filename("C:/temp/ViSP-images/Klimt/Klimt.pgm"); +#ifdef _WIN32 + std::string filename("C:/temp/ViSP-images/Klimt/Klimt.ppm"); +#else + std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.ppm"); #endif + // Read an image from the disk vpImageIo::read(I, filename); @@ -113,19 +113,19 @@ void vpImageTools::changeLUT(vpImage<unsigned char>& I, } unsigned char v; - double factor = (B_star - A_star)/(B - A); + double factor = (double)(B_star - A_star)/(double)(B - A); for (unsigned int i=0 ; i < I.getHeight(); i++) for (unsigned int j=0 ; j < I.getWidth(); j++) { v = I[i][j]; if (v <= A) - I[i][j] = A_star; + I[i][j] = A_star; else if (v >= B) - I[i][j] = B_star; + I[i][j] = B_star; else - I[i][j] = (unsigned char)(A_star + factor*(v-A)); - } + I[i][j] = (unsigned char)(A_star + factor*(v-A)); + } } /*! diff --git a/src/image/vpImageTools.h b/src/image/vpImageTools.h index 4a9842733ee816242163e489a5b38f7a063dc237..e9723577a1a5f0648341933a25ff5d984d529230 100644 --- a/src/image/vpImageTools.h +++ b/src/image/vpImageTools.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImageTools.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpImageTools.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -249,7 +249,11 @@ public: unsigned int nthreads; unsigned int threadid; public: - vpUndistortInternalType() {}; + vpUndistortInternalType() { + src = dst = NULL; + width = height = 0; + nthreads = threadid = 0; + }; vpUndistortInternalType(const vpUndistortInternalType<Type> &u) { src = u.src; dst = u.dst; @@ -548,6 +552,31 @@ void vpImageTools::flip(const vpImage<Type> &I, Flip vertically the input image. \param I : Input image which is flipped and modified in output. + + The following example shows how to use this function: + \code +#include <visp/vpImageTools.h> +#include <visp/vpImage.h> +#include <visp/vpImageIo.h> + +int main() +{ + vpImage<vpRGBa> I; +#ifdef _WIN32 + std::string filename("C:/temp/ViSP-images/Klimt/Klimt.ppm"); +#else + std::string filename("/local/soft/ViSP/ViSP-images/Klimt/Klimt.ppm"); +#endif + + // Read an image from the disk + vpImageIo::read(I, filename); + + // Flip the image + vpImageTools::flip(I); + + vpImageIo::write(I, "Klimt-flip.ppm"); // Write the image in a PGM P5 image file format +} + \endcode */ template<class Type> void vpImageTools::flip(vpImage<Type> &I) diff --git a/src/image/vpRGBa.cpp b/src/image/vpRGBa.cpp index 8360c08ce4ceb0ca4ca5144ff5cc430d99b74de8..297b320586f873876472acdce125428fc27d4f72 100644 --- a/src/image/vpRGBa.cpp +++ b/src/image/vpRGBa.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRGBa.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRGBa.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,25 +58,27 @@ \param v : Input color ( R = G = B = v ) */ -void +vpRGBa & vpRGBa::operator=(const unsigned char &v) { this->R = v; this->G = v; this->B = v; this->A = v; + return *this; } /*! Copy operator. */ -void +vpRGBa & vpRGBa::operator=(const vpRGBa &v) { this->R = v.R; this->G = v.G; this->B = v.B; this->A = v.A; + return *this; } /*! @@ -88,7 +90,7 @@ vpRGBa::operator=(const vpRGBa &v) \exception vpException::dimensionError : If v is not a 4 four dimention vector. */ -void +vpRGBa & vpRGBa::operator=(const vpColVector &v) { if (v.getRows() != 4) { @@ -99,6 +101,7 @@ vpRGBa::operator=(const vpColVector &v) G = (unsigned char)v[1]; B = (unsigned char)v[2]; A = (unsigned char)v[3]; + return *this; } /*! diff --git a/src/image/vpRGBa.h b/src/image/vpRGBa.h index 5307f17b3c3640ca338e07e97186b46f13460aa2..5566e5677197e67eeae132ee1993fbb0f1ec2bcd 100644 --- a/src/image/vpRGBa.h +++ b/src/image/vpRGBa.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRGBa.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRGBa.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,27 +74,22 @@ public: Build a black value. */ - inline vpRGBa() - : R(0), G(0), B(0), A(0) - { - }; + inline vpRGBa() : R(0), G(0), B(0), A(0) {}; /*! Constructor. Initialize the color with R, G, B, A values. - \param R : Red value. - \param G : Green value. - \param B : Blue value. - \param A : Additional value. + \param r : Red value. + \param g : Green value. + \param b : Blue value. + \param a : Additional value. */ - inline vpRGBa(const unsigned char &R, const unsigned char &G, - const unsigned char &B, const unsigned char &A=0) - : R(R), G(G), B(B), A(A) - { - }; + inline vpRGBa(const unsigned char &r, const unsigned char &g, + const unsigned char &b, const unsigned char &a=0) + : R(r), G(g), B(b), A(a) {}; /*! @@ -105,19 +100,13 @@ public: \param v : Value to set. */ - inline vpRGBa(const unsigned char &v) - : R(v), G(v), B(v), A(v) - { - }; + inline vpRGBa(const unsigned char &v) : R(v), G(v), B(v), A(v) {}; /*! Copy constructor. */ - inline vpRGBa(const vpRGBa &v) - { - *this = v ; - }; + inline vpRGBa(const vpRGBa &v) : R(v.R), G(v.G), B(v.B), A(v.A) {}; /*! Create a RGBa value from a 4 dimension column vector. @@ -128,14 +117,18 @@ public: A=v[3] */ - inline vpRGBa(const vpColVector &v) + inline vpRGBa(const vpColVector &v) : R(0), G(0), B(0), A(0) { - *this = v ; - } + *this = v; + }; + + // We cannot add here the following destructor without changing the hypothesis that the size of this class is 4. + // With the destructor it becomes 16 that does break a lot of things arround image conversions + // virtual ~vpRGBa() {}; // Not to implement - void operator=(const unsigned char &v) ; - void operator=(const vpRGBa &v) ; - void operator=(const vpColVector &v) ; + vpRGBa & operator=(const unsigned char &v) ; + vpRGBa & operator=(const vpRGBa &v) ; + vpRGBa & operator=(const vpColVector &v) ; bool operator==(const vpRGBa &v); bool operator!=(const vpRGBa &v); diff --git a/src/key-point/vpBasicKeyPoint.cpp b/src/key-point/vpBasicKeyPoint.cpp index 19e8dfb20b84de5a7c07a2843f02734316744f22..68ad68b1f71044aae478a024d210abed2548bd11 100644 --- a/src/key-point/vpBasicKeyPoint.cpp +++ b/src/key-point/vpBasicKeyPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBasicKeyPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBasicKeyPoint.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,23 +42,12 @@ #include <visp/vpBasicKeyPoint.h> -#include <vector> - /*! Basic constructor. */ vpBasicKeyPoint::vpBasicKeyPoint() + : referenceImagePointsList(), currentImagePointsList(), matchedReferencePoints(), _reference_computed(false) { - _reference_computed = false; - matchedReferencePoints.resize(0); - currentImagePointsList.resize(0); - referenceImagePointsList.resize(0); } -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ - diff --git a/src/key-point/vpBasicKeyPoint.h b/src/key-point/vpBasicKeyPoint.h index 3cf81c00b5a27573dad2c337885e0308524644bd..375aa74911432fadecfa1b3ba63af7069fb2a2b8 100644 --- a/src/key-point/vpBasicKeyPoint.h +++ b/src/key-point/vpBasicKeyPoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBasicKeyPoint.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpBasicKeyPoint.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/key-point/vpFernClassifier.cpp b/src/key-point/vpFernClassifier.cpp index bfd3a784e0e32f9c5735161025b22c7e5afe9f01..a14886f194a8f34f2625cdfda2eb1b2334000d23 100644 --- a/src/key-point/vpFernClassifier.cpp +++ b/src/key-point/vpFernClassifier.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFernClassifier.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpFernClassifier.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,7 +42,7 @@ #include <visp/vpConfig.h> -#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) // Require opencv >= 2.0.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000) // Require opencv >= 2.0.0 and < 3.0.0 #include <visp/vpFernClassifier.h> #include <visp/vpImageTools.h> @@ -54,9 +54,20 @@ Basic constructor */ -vpFernClassifier::vpFernClassifier():vpBasicKeyPoint(), gen(0, 256, 5, true, 0.6, 1.5, -CV_PI/2, CV_PI/2, -CV_PI/2, CV_PI/2) +vpFernClassifier::vpFernClassifier() + : vpBasicKeyPoint(), + ldetector(), fernClassifier(), gen(0, 256, 5, true, 0.6, 1.5, -CV_PI/2, CV_PI/2, -CV_PI/2, CV_PI/2), + hasLearn(false), threshold(20), nbView(2000), dist(2), nbClassfier(100), ClassifierSize(11), + nbOctave(2), patchSize(32), radius(7), nbPoints(200), blurImage(true), radiusBlur(7), + sigmaBlur(1), nbMinPoint(10), + #if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + curImg(), + #else + curImg(NULL), + #endif + objKeypoints(), modelROI_Ref(), modelROI(), + modelPoints(), imgKeypoints(), refPt(), curPt() { - init(); } /*! @@ -70,9 +81,20 @@ vpFernClassifier::vpFernClassifier():vpBasicKeyPoint(), gen(0, 256, 5, true, 0.6 \param _objectName : the name of the object to load */ vpFernClassifier::vpFernClassifier(const std::string& _dataFile, const std::string& _objectName) + : vpBasicKeyPoint(), + ldetector(), fernClassifier(), gen(0, 256, 5, true, 0.6, 1.5, -CV_PI/2, CV_PI/2, -CV_PI/2, CV_PI/2), + hasLearn(false), threshold(20), nbView(2000), dist(2), nbClassfier(100), ClassifierSize(11), + nbOctave(2), patchSize(32), radius(7), nbPoints(200), blurImage(true), radiusBlur(7), + sigmaBlur(1), nbMinPoint(10), + #if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + curImg(), + #else + curImg(NULL), + #endif + objKeypoints(), modelROI_Ref(), modelROI(), + modelPoints(), imgKeypoints(), refPt(), curPt() { - init(); - this->load(_dataFile, _objectName); + this->load(_dataFile, _objectName); } /*! @@ -81,15 +103,17 @@ vpFernClassifier::vpFernClassifier(const std::string& _dataFile, const std::stri */ vpFernClassifier::~vpFernClassifier() { - if(curIplImg != NULL){ - if(curIplImg->width % 8 == 0){ - curIplImg->imageData = NULL; - cvReleaseImageHeader(&curIplImg); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + if(curImg != NULL){ + if(curImg->width % 8 == 0){ + curImg->imageData = NULL; + cvReleaseImageHeader(&curImg); }else{ - cvReleaseImage(&curIplImg); + cvReleaseImage(&curImg); } - curIplImg = NULL; + curImg = NULL; } +#endif } /*! @@ -104,7 +128,9 @@ vpFernClassifier::init() nbClassfier = 100; ClassifierSize = 11; nbPoints = 200; - curIplImg = NULL; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + curImg = NULL; +#endif blurImage = true; radiusBlur = 7; sigmaBlur = 1; @@ -130,7 +156,7 @@ vpFernClassifier::train() cv::LDetector d(radius, threshold, nbOctave, nbView, patchSize, dist); //blur - cv::Mat obj = (cv::Mat)curIplImg; + cv::Mat obj = (cv::Mat)curImg; if(this->getBlurSetting()){ cv::GaussianBlur(obj, obj, cv::Size(getBlurSize(), getBlurSize()), getBlurSigma(), getBlurSigma()); @@ -289,7 +315,7 @@ vpFernClassifier::matchPoint(const vpImage<unsigned char> &_I) setImage(_I); // resize image // cv::resize(_image, image, Size(), 1./imgscale, 1./imgscale, INTER_CUBIC); - cv::Mat img = this->curIplImg; + cv::Mat img = this->curImg; if(this->getBlurSetting()){ cv::GaussianBlur(img, img, cv::Size(this->getBlurSize(), this->getBlurSize()), this->getBlurSigma(), this->getBlurSigma()); @@ -518,36 +544,40 @@ vpFernClassifier::record(const std::string& _objectName, const std::string& _dat Set the current image. This method allows to convert a image from the ViSP format to the OpenCV one. - \param _I : the input image. + \param I : the input image. */ void -vpFernClassifier::setImage(const vpImage<unsigned char>& _I) +vpFernClassifier::setImage(const vpImage<unsigned char>& I) { - if(curIplImg != NULL){ - cvResetImageROI(curIplImg); - if((curIplImg->width % 8) == 0){ - curIplImg->imageData = NULL; - cvReleaseImageHeader(&curIplImg); +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + vpImageConvert::convert(I, curImg); +#else + if(curImg != NULL){ + cvResetImageROI(curImg); + if((curImg->width % 8) == 0){ + curImg->imageData = NULL; + cvReleaseImageHeader(&curImg); }else{ - cvReleaseImage(&curIplImg); + cvReleaseImage(&curImg); } - curIplImg = NULL; + curImg = NULL; } - - if((_I.getWidth()%8) == 0){ - curIplImg = cvCreateImageHeader(cvSize((int)_I.getWidth(), (int)_I.getHeight()), IPL_DEPTH_8U, 1); - if(curIplImg != NULL){ - curIplImg->imageData = (char*)_I.bitmap; + if((I.getWidth()%8) == 0){ + curImg = cvCreateImageHeader(cvSize((int)I.getWidth(), (int)I.getHeight()), IPL_DEPTH_8U, 1); + if(curImg != NULL){ + curImg->imageData = (char*)I.bitmap; }else{ throw vpException(vpException::memoryAllocationError, "Could not create the image in the OpenCV format."); } }else{ - vpImageConvert::convert(_I, curIplImg); + vpImageConvert::convert(I, curImg); } - if(curIplImg == NULL){ + if(curImg == NULL){ std::cout << "!> conversion failed" << std::endl; throw vpException(vpException::notInitialized , "conversion failed"); } +#endif + } #endif diff --git a/src/key-point/vpFernClassifier.h b/src/key-point/vpFernClassifier.h index 399cbc6c0aef597197b504020313bf62161bfb44..118105b68e6453fde526982282e9c1f4a69a4948 100644 --- a/src/key-point/vpFernClassifier.h +++ b/src/key-point/vpFernClassifier.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFernClassifier.h 4201 2013-04-08 08:20:47Z fspindle $ + * $Id: vpFernClassifier.h 5203 2015-01-24 09:33:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,7 +47,7 @@ #include <string> -#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) // Require opencv >= 2.0.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000) // Require opencv >= 2.0.0 and < 3.0.0 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1 # include <opencv2/imgproc/imgproc.hpp> # include <opencv2/features2d/features2d.hpp> @@ -65,7 +65,8 @@ \brief Class that implements the Fern classifier and the YAPE detector thanks to the OpenCV library. - + \deprecated This class is deprecated with OpenCV 3.0.0 or more recent. + This class provides a way to detect and match point using the YAPE and a Fern Classifiers, thanks to the OpenCV library (version >= 2.0) @@ -73,13 +74,8 @@ image. The points of interests belonging to the model and the points detected in the current image are given in pixels thanks to the vpImagePoint class. - For more details about the Ferns Classifier and the point detector, see - - Mustafa Özuysal, Michael Calonder, Vincent Lepetit, Pascal Fua, "Fast - KeyPoint Recognition Using Random Ferns", IEEE Transactions on Pattern - Analysis and Machine Intelligence, 15 Jan. 2009. - - - Vincent Lepetit, Pascal Fua, “Towards Recognizing Feature Points Using - Classification Treesâ€, Technical Report IC/2004/74, EPFL, 2004. + For more details about the Ferns Classifier and the point detector, + see \cite Ozuysal10 and \cite Lepetit04c. To use this class, you first have to detect points in the model and train the associated Fern classifier. Then, for each new grabbed image, You can detect @@ -191,7 +187,6 @@ protected: //! The patch generator (OpenCV format). cv::PatchGenerator gen; - //! Flag to indicate whether the classifier has been trained or not. bool hasLearn; @@ -227,7 +222,11 @@ protected: unsigned int nbMinPoint; //! The current image in the OpenCV format. - IplImage* curIplImg; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat curImg; +#else + IplImage* curImg; +#endif //! keypoints detected in the reference image. std::vector<cv::KeyPoint> objKeypoints; @@ -248,29 +247,29 @@ public: vpFernClassifier(const std::string& _dataFile, const std::string& _objectName); virtual ~vpFernClassifier(); - /* build reference */ + /* build reference */ virtual unsigned int buildReference(const vpImage<unsigned char> &I); - virtual unsigned int buildReference(const vpImage<unsigned char> &I, - const vpImagePoint &iP, - const unsigned int height, const unsigned int width); - virtual unsigned int buildReference(const vpImage<unsigned char> &I, - const vpRect& rectangle); - - /* matching */ + virtual unsigned int buildReference(const vpImage<unsigned char> &I, + const vpImagePoint &iP, + const unsigned int height, const unsigned int width); + virtual unsigned int buildReference(const vpImage<unsigned char> &I, + const vpRect& rectangle); + + /* matching */ virtual unsigned int matchPoint(const vpImage<unsigned char> &I); - virtual unsigned int matchPoint(const vpImage<unsigned char> &I, - const vpImagePoint &iP, - const unsigned int height, const unsigned int width); - virtual unsigned int matchPoint(const vpImage<unsigned char> &I, - const vpRect& rectangle); - - /* display */ - virtual void display(const vpImage<unsigned char> &Iref, + virtual unsigned int matchPoint(const vpImage<unsigned char> &I, + const vpImagePoint &iP, + const unsigned int height, const unsigned int width); + virtual unsigned int matchPoint(const vpImage<unsigned char> &I, + const vpRect& rectangle); + + /* display */ + virtual void display(const vpImage<unsigned char> &Iref, const vpImage<unsigned char> &Icurrent, unsigned int size=3); virtual void display(const vpImage<unsigned char> &Icurrent, unsigned int size=3, const vpColor &color=vpColor::green); - /* io methods */ + /* io methods */ void load(const std::string& _dataFile, const std::string& /*_objectName*/); void record(const std::string& _objectName, const std::string& _dataFile); @@ -329,7 +328,7 @@ public: cv::Rect getModelROI() const { return modelROI;} protected: - void setImage(const vpImage<unsigned char>& _I); + void setImage(const vpImage<unsigned char>& I); void train(); virtual void init(); }; diff --git a/src/key-point/vpKeyPoint.cpp b/src/key-point/vpKeyPoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18e7d68d747b7b3e6d4b9c3b6370cec9d025f69d --- /dev/null +++ b/src/key-point/vpKeyPoint.cpp @@ -0,0 +1,3008 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Key point functionalities. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ + +#include <limits> + +#include <visp/vpKeyPoint.h> + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) +# include <opencv2/calib3d/calib3d.hpp> +#endif + + +//Specific Type transformation functions +///*! +// Convert a list of cv::DMatch to a cv::DMatch (extract the first cv::DMatch, the nearest neighbor). +// +// \param knnMatches : List of cv::DMatch. +// \return The nearest neighbor. +// */ +inline cv::DMatch knnToDMatch(const std::vector<cv::DMatch> &knnMatches) { + if(knnMatches.size() > 0) { + return knnMatches[0]; + } + + return cv::DMatch(); +} + +///*! +// Convert a cv::DMatch to an index (extract the train index). +// +// \param match : Point to convert in ViSP type. +// \return The train index. +// */ +inline vpImagePoint matchRansacToVpImage(const std::pair<cv::KeyPoint, cv::Point3f> &pair) { + return vpImagePoint(pair.first.pt.y, pair.first.pt.x); +} + +//TODO: Try to implement a pyramidal feature detection +//#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) +//class MaskPredicate +//{ +//public: +// MaskPredicate( const cv::Mat& _mask ) : mask(_mask) {} +// bool operator() (const cv::KeyPoint& key_pt) const +// { +// return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0; +// } +// +//private: +// const cv::Mat mask; +// MaskPredicate& operator=(const MaskPredicate&); +//}; +// +//void vpKeyPoint::runByPixelsMask( std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask ) +//{ +// if( mask.empty() ) +// { +// return; +// } +// +// keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end()); +//} +// +//void vpKeyPoint::pyramidFeatureDetection(cv::Ptr<cv::FeatureDetector> &detector, const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask, +// const int maxLevel) +//{ +// cv::Mat src = image; +// cv::Mat src_mask = mask; +// +// cv::Mat dilated_mask; +// if( !mask.empty() ) +// { +// dilate( mask, dilated_mask, cv::Mat() ); +// cv::Mat mask255( mask.size(), CV_8UC1, cv::Scalar(0) ); +// mask255.setTo( cv::Scalar(255), dilated_mask != 0 ); +// dilated_mask = mask255; +// } +// +// for( int l = 0, multiplier = 1; l <= maxLevel; ++l, multiplier *= 2 ) +// { +// // Detect on current level of the pyramid +// std::vector<cv::KeyPoint> new_pts; +// detector->detect( src, new_pts, src_mask ); +// std::vector<cv::KeyPoint>::iterator it = new_pts.begin(), +// end = new_pts.end(); +// for( ; it != end; ++it) +// { +// it->pt.x *= multiplier; +// it->pt.y *= multiplier; +// it->size *= multiplier; +// it->octave = l; +// } +// keypoints.insert( keypoints.end(), new_pts.begin(), new_pts.end() ); +// +// // Downsample +// if( l < maxLevel ) +// { +// cv::Mat dst; +// pyrDown( src, dst ); +// src = dst; +// +// if( !mask.empty() ) +// resize( dilated_mask, src_mask, src.size(), 0, 0, CV_INTER_AREA ); +// } +// } +// +// if( !mask.empty() ) +// { +// //KeyPointsFilter::runByPixelsMask( keypoints, mask ); +// } +//} +//#endif + +/*! + Constructor to initialize specified detector, extractor, matcher and filtering method. + + \param detectorName : Name of the detector. + \param extractorName : Name of the extractor. + \param matcherName : Name of the matcher. + \param filterType : Filtering matching method chosen. + */ +vpKeyPoint::vpKeyPoint(const std::string &detectorName, const std::string &extractorName, + const std::string &matcherName, const vpFilterMatchingType &filterType) + : m_computeCovariance(false), m_covarianceMatrix(), m_currentImageId(0), m_detectionMethod(detectionScore), + m_detectionScore(0.15), m_detectionThreshold(100.0), m_detectionTime(0.), m_detectorNames(), + m_detectors(), m_extractionTime(0.), m_extractorNames(), m_extractors(), m_filteredMatches(), m_filterType(filterType), + m_knnMatches(), m_mapOfImageId(), m_mapOfImages(), m_matcher(), m_matcherName(matcherName), m_matches(), + m_matchingFactorThreshold(2.0), m_matchingRatioThreshold(0.85), m_matchingTime(0.), + m_matchQueryToTrainKeyPoints(), m_matchRansacKeyPointsToPoints(), m_matchRansacQueryToTrainKeyPoints(), + m_nbRansacIterations(200), m_nbRansacMinInlierCount(100), m_objectFilteredPoints(), + m_poseTime(0.), m_queryDescriptors(), m_queryFilteredKeyPoints(), m_queryKeyPoints(), + m_ransacConsensusPercentage(20.0), m_ransacInliers(), m_ransacOutliers(), m_ransacReprojectionError(6.0), + m_ransacThreshold(0.001), m_trainDescriptors(), m_trainKeyPoints(), m_trainPoints(), + m_trainVpPoints(), m_useAffineDetection(false), m_useBruteForceCrossCheck(true), + m_useConsensusPercentage(false), m_useKnn(false), m_useRansacVVS(false) +{ + //Use k-nearest neighbors (knn) to retrieve the two best matches for a keypoint + //So this is useful only for ratioDistanceThreshold method + if(filterType == ratioDistanceThreshold || filterType == stdAndRatioDistanceThreshold) { + m_useKnn = true; + } + + m_detectorNames.push_back(detectorName); + m_extractorNames.push_back(extractorName); + + init(); +} + +/*! + Constructor to initialize specified detector, extractor, matcher and filtering method. + + \param detectorNames : List of name detector for allowing multiple detectors. + \param extractorNames : List of name extractor for allowing multiple extractors. + \param matcherName : Name of the matcher. + \param filterType : Filtering matching method chosen. + */ +vpKeyPoint::vpKeyPoint(const std::vector<std::string> &detectorNames, const std::vector<std::string> &extractorNames, + const std::string &matcherName, const vpFilterMatchingType &filterType) + : m_computeCovariance(false), m_covarianceMatrix(), m_currentImageId(0), m_detectionMethod(detectionScore), + m_detectionScore(0.15), m_detectionThreshold(100.0), m_detectionTime(0.), m_detectorNames(detectorNames), + m_detectors(), m_extractionTime(0.), m_extractorNames(extractorNames), m_extractors(), m_filteredMatches(), + m_filterType(filterType), m_knnMatches(), m_mapOfImageId(), m_mapOfImages(), m_matcher(), m_matcherName(matcherName), + m_matches(), m_matchingFactorThreshold(2.0), m_matchingRatioThreshold(0.85), m_matchingTime(0.), + m_matchQueryToTrainKeyPoints(), m_matchRansacKeyPointsToPoints(), m_matchRansacQueryToTrainKeyPoints(), + m_nbRansacIterations(200), m_nbRansacMinInlierCount(100), m_objectFilteredPoints(), + m_poseTime(0.), m_queryDescriptors(), m_queryFilteredKeyPoints(), m_queryKeyPoints(), + m_ransacConsensusPercentage(20.0), m_ransacInliers(), m_ransacOutliers(), m_ransacReprojectionError(6.0), + m_ransacThreshold(0.001), m_trainDescriptors(), m_trainKeyPoints(), m_trainPoints(), + m_trainVpPoints(), m_useAffineDetection(false), m_useBruteForceCrossCheck(true), + m_useConsensusPercentage(false), m_useKnn(false), m_useRansacVVS(false) +{ + //Use k-nearest neighbors (knn) to retrieve the two best matches for a keypoint + //So this is useful only for ratioDistanceThreshold method + if(filterType == ratioDistanceThreshold || filterType == stdAndRatioDistanceThreshold) { + m_useKnn = true; + } + + init(); +} + +/*! + Apply an affine and skew transformation to an image. + \param tilt : Tilt value in the direction of x + \param phi : Rotation value + \param img : Modified image after the transformation + \param mask : Mask containing the location of the image pixels after the transformation + \param Ai : Inverse affine matrix + */ +void vpKeyPoint::affineSkew(double tilt, double phi, cv::Mat& img, + cv::Mat& mask, cv::Mat& Ai) { + int h = img.rows; + int w = img.cols; + + mask = cv::Mat(h, w, CV_8UC1, cv::Scalar(255)); + + cv::Mat A = cv::Mat::eye(2, 3, CV_32F); + + //if (phi != 0.0) { + if (std::fabs(phi) > std::numeric_limits<double>::epsilon()) { + phi *= M_PI / 180.; + double s = sin(phi); + double c = cos(phi); + + A = (cv::Mat_<float>(2, 2) << c, -s, s, c); + + cv::Mat corners = (cv::Mat_<float>(4, 2) << 0, 0, w, 0, w, h, 0, h); + cv::Mat tcorners = corners * A.t(); + cv::Mat tcorners_x, tcorners_y; + tcorners.col(0).copyTo(tcorners_x); + tcorners.col(1).copyTo(tcorners_y); + std::vector<cv::Mat> channels; + channels.push_back(tcorners_x); + channels.push_back(tcorners_y); + merge(channels, tcorners); + + cv::Rect rect = boundingRect(tcorners); + A = (cv::Mat_<float>(2, 3) << c, -s, -rect.x, s, c, -rect.y); + + cv::warpAffine(img, img, A, cv::Size(rect.width, rect.height), + cv::INTER_LINEAR, cv::BORDER_REPLICATE); + } + //if (tilt != 1.0) { + if (std::fabs(tilt - 1.0) > std::numeric_limits<double>::epsilon()) { + double s = 0.8 * sqrt(tilt * tilt - 1); + cv::GaussianBlur(img, img, cv::Size(0, 0), s, 0.01); + cv::resize(img, img, cv::Size(0, 0), 1.0 / tilt, 1.0, cv::INTER_NEAREST); + A.row(0) = A.row(0) / tilt; + } + //if (tilt != 1.0 || phi != 0.0) { + if (std::fabs(tilt - 1.0) > std::numeric_limits<double>::epsilon() || std::fabs(phi) > std::numeric_limits<double>::epsilon() ) { + h = img.rows; + w = img.cols; + cv::warpAffine(mask, mask, A, cv::Size(w, h), cv::INTER_NEAREST); + } + invertAffineTransform(A, Ai); +} + +/*! + Build the reference keypoints list. + + \param I : Input reference image. + \return The number of detected keypoints in the image \p I. + */ +unsigned int vpKeyPoint::buildReference(const vpImage<unsigned char> &I) { + return buildReference(I, vpRect()); +} + +/*! + Build the reference keypoints list in a region of interest in the image. + + \param I : Input reference image + \param iP : Position of the top-left corner of the region of interest. + \param height : Height of the region of interest. + \param width : Width of the region of interest. + \return The number of detected keypoints in the current image I. + */ +unsigned int vpKeyPoint::buildReference(const vpImage<unsigned char> &I, + const vpImagePoint &iP, + const unsigned int height, const unsigned int width) { + + return buildReference(I, vpRect(iP, width, height)); +} + +/*! + Build the reference keypoints list in a region of interest in the image. + + \param I : Input image. + \param rectangle : Rectangle of the region of interest. + \return The number of detected keypoints in the current image I. + */ +unsigned int vpKeyPoint::buildReference(const vpImage<unsigned char> &I, + const vpRect &rectangle) { + //Reset variables used when dealing with 3D models + //So as no 3D point list is passed, we dont need this variables + m_trainPoints.clear(); + m_mapOfImageId.clear(); + m_mapOfImages.clear(); + m_currentImageId = 1; + + if(m_useAffineDetection) { + std::vector<std::vector<cv::KeyPoint> > listOfTrainKeyPoints; + std::vector<cv::Mat> listOfTrainDescriptors; + + //Detect keypoints and extract descriptors on multiple images + detectExtractAffine(I, listOfTrainKeyPoints, listOfTrainDescriptors); + + //Flatten the different train lists + m_trainKeyPoints.clear(); + for(std::vector<std::vector<cv::KeyPoint> >::const_iterator it = listOfTrainKeyPoints.begin(); + it != listOfTrainKeyPoints.end(); ++it) { + m_trainKeyPoints.insert(m_trainKeyPoints.end(), it->begin(), it->end()); + } + + bool first = true; + for(std::vector<cv::Mat>::const_iterator it = listOfTrainDescriptors.begin(); it != listOfTrainDescriptors.end(); ++it) { + if(first) { + first = false; + it->copyTo(m_trainDescriptors); + } else { + m_trainDescriptors.push_back(*it); + } + } + } else { + detect(I, m_trainKeyPoints, m_detectionTime, rectangle); + extract(I, m_trainKeyPoints, m_trainDescriptors, m_extractionTime); + } + + //Save the correspondence keypoint class_id with the training image_id in a map + //Used to display the matching with all the training images + for(std::vector<cv::KeyPoint>::const_iterator it = m_trainKeyPoints.begin(); it != m_trainKeyPoints.end(); ++it) { + m_mapOfImageId[it->class_id] = m_currentImageId; + } + + //Save the image in a map at a specific image_id + m_mapOfImages[m_currentImageId] = I; + + //Convert OpenCV type to ViSP type for compatibility + vpConvert::convertFromOpenCV(m_trainKeyPoints, referenceImagePointsList); + + _reference_computed = true; + + return static_cast<unsigned int>(m_trainKeyPoints.size()); +} + +/*! + Build the reference keypoints list and compute the 3D position corresponding of the keypoints locations. + + \param I : Input image + \param trainKeyPoints : List of the train keypoints. + \param points3f : Output list of the 3D position corresponding of the keypoints locations. + \param append : If true, append the supply train keypoints with those already present. + */ +void vpKeyPoint::buildReference(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &trainKeyPoints, + std::vector<cv::Point3f> &points3f, bool append) { + cv::Mat trainDescriptors; + extract(I, trainKeyPoints, trainDescriptors, m_extractionTime); + + buildReference(I, trainKeyPoints, trainDescriptors, points3f, append); +} + +/*! + Build the reference keypoints list and compute the 3D position corresponding of the keypoints locations. + + \param I : Input image + \param trainKeyPoints : List of the train keypoints. + \param points3f : List of the 3D position corresponding of the keypoints locations. + \param trainDescriptors : List of the train descriptors. + \param append : If true, append the supply train keypoints with those already present. + */ +void vpKeyPoint::buildReference(const vpImage<unsigned char> &I, const std::vector<cv::KeyPoint> &trainKeyPoints, + const cv::Mat &trainDescriptors, const std::vector<cv::Point3f> &points3f, bool append) { + if(!append) { + m_currentImageId = 0; + m_mapOfImageId.clear(); + m_mapOfImages.clear(); + this->m_trainKeyPoints.clear(); + this->m_trainPoints.clear(); + } + + m_currentImageId++; + + //Save the correspondence keypoint class_id with the training image_id in a map + //Used to display the matching with all the training images + for(std::vector<cv::KeyPoint>::const_iterator it = trainKeyPoints.begin(); it != trainKeyPoints.end(); ++it) { + m_mapOfImageId[it->class_id] = m_currentImageId; + } + + //Save the image in a map at a specific image_id + m_mapOfImages[m_currentImageId] = I; + + //Append reference lists + this->m_trainKeyPoints.insert(this->m_trainKeyPoints.end(), trainKeyPoints.begin(), trainKeyPoints.end()); + if(!append) { + trainDescriptors.copyTo(this->m_trainDescriptors); + } else { + this->m_trainDescriptors.push_back(trainDescriptors); + } + this->m_trainPoints.insert(this->m_trainPoints.end(), points3f.begin(), points3f.end()); + + + //Convert OpenCV type to ViSP type for compatibility + vpConvert::convertFromOpenCV(this->m_trainKeyPoints, referenceImagePointsList); + vpConvert::convertFromOpenCV(this->m_trainPoints, m_trainVpPoints); + + _reference_computed = true; +} + +/*! + Compute the 3D coordinate given the 2D image coordinate and under the assumption that the point is located on a plane + whose the plane equation is known in the camera frame. + The Z-coordinate is retrieved according to the proportional relation between the plane equation expressed in the + normalized camera frame (derived from the image coordinate) and the same plane equation expressed in the camera frame. + + \param candidate : Keypoint we want to compute the 3D coordinate. + \param roi : List of 3D points representing a planar face. + \param cam : Camera parameters. + \param cMo : Homogeneous matrix between the world and the camera frames. + \param point : 3D coordinate computed. + */ +void vpKeyPoint::compute3D(const cv::KeyPoint &candidate, const std::vector<vpPoint> &roi, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, cv::Point3f &point) { + /* compute plane equation */ + std::vector<vpPoint>::const_iterator it_roi = roi.begin(); + vpPoint pts[3]; + pts[0] = *it_roi; + ++it_roi; + pts[1] = *it_roi; + ++it_roi; + pts[2] = *it_roi; + vpPlane Po(pts[0], pts[1], pts[2]); + double xc = 0.0, yc = 0.0; + vpPixelMeterConversion::convertPoint(cam, candidate.pt.x, candidate.pt.y, xc, yc); + double Z = -Po.getD() / (Po.getA() * xc + Po.getB() * yc + Po.getC()); + double X = xc * Z; + double Y = yc * Z; + vpColVector point_cam(4); + point_cam[0] = X; + point_cam[1] = Y; + point_cam[2] = Z; + point_cam[3] = 1; + vpColVector point_obj(4); + point_obj = cMo.inverse() * point_cam; + point = cv::Point3f((float) point_obj[0], (float) point_obj[1], (float) point_obj[2]); +} + +/*! + Compute the 3D coordinate given the 2D image coordinate and under the assumption that the point is located on a plane + whose the plane equation is known in the camera frame. + The Z-coordinate is retrieved according to the proportional relation between the plane equation expressed in the + normalized camera frame (derived from the image coordinate) and the same plane equation expressed in the camera frame. + + \param candidate : vpImagePoint we want to compute the 3D coordinate. + \param roi : List of 3D points representing a planar face. + \param cam : Camera parameters. + \param cMo : Homogeneous matrix between the world and the camera frames. + \param point : 3D coordinate computed. + */ +void vpKeyPoint::compute3D(const vpImagePoint &candidate, const std::vector<vpPoint> &roi, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, vpPoint &point) { + /* compute plane equation */ + std::vector<vpPoint>::const_iterator it_roi = roi.begin(); + vpPoint pts[3]; + pts[0] = *it_roi; + ++it_roi; + pts[1] = *it_roi; + ++it_roi; + pts[2] = *it_roi; + vpPlane Po(pts[0], pts[1], pts[2]); + double xc = 0.0, yc = 0.0; + vpPixelMeterConversion::convertPoint(cam, candidate, xc, yc); + double Z = -Po.getD() / (Po.getA() * xc + Po.getB() * yc + Po.getC()); + double X = xc * Z; + double Y = yc * Z; + vpColVector point_cam(4); + point_cam[0] = X; + point_cam[1] = Y; + point_cam[2] = Z; + point_cam[3] = 1; + vpColVector point_obj(4); + point_obj = cMo.inverse() * point_cam; + point.setWorldCoordinates(point_obj); +} + +/*! + Keep only keypoints located on faces and compute for those keypoints the 3D coordinate given the 2D image coordinate + and under the assumption that the point is located on a plane. + + \param cMo : Homogeneous matrix between the world and the camera frames. + \param cam : Camera parameters. + \param candidates : In input, list of keypoints detected in the whole image, in output, list of keypoints only located + on planes. + \param polygons : List of 2D polygons representing the projection of the faces in the image plane. + \param roisPt : List of faces. + \param points : Output list of computed 3D coordinates of keypoints located only on faces. + \param descriptors : Optional parameter, pointer to the descriptors to filter + */ +void vpKeyPoint::compute3DForPointsInPolygons(const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, + std::vector<cv::KeyPoint> &candidates, std::vector<vpPolygon> &polygons, std::vector<std::vector<vpPoint> > &roisPt, + std::vector<cv::Point3f> &points, cv::Mat *descriptors) { + + std::vector<cv::KeyPoint> candidateToCheck = candidates; + candidates.clear(); + vpImagePoint iPt; + cv::Point3f pt; + cv::Mat desc; + + size_t cpt1 = 0; + for (std::vector<vpPolygon>::iterator it1 = polygons.begin(); it1 != polygons.end(); ++it1, cpt1++) { + int cpt2 = 0; + + for (std::vector<cv::KeyPoint>::iterator it2 = candidateToCheck.begin(); it2 != candidateToCheck.end(); cpt2++) { + iPt.set_ij(it2->pt.y, it2->pt.x); + if (it1->isInside(iPt)) { + candidates.push_back(*it2); + vpKeyPoint::compute3D(*it2, roisPt[cpt1], cam, cMo, pt); + points.push_back(pt); + + if(descriptors != NULL) { + desc.push_back(descriptors->row(cpt2)); + } + + //Remove candidate keypoint which is located on the current polygon + candidateToCheck.erase(it2); + } else { + ++it2; + } + } + } + + if(descriptors != NULL) { + desc.copyTo(*descriptors); + } +} + +/*! + Keep only keypoints located on faces and compute for those keypoints the 3D coordinate given the 2D image coordinate + and under the assumption that the point is located on a plane. + + \param cMo : Homogeneous matrix between the world and the camera frames. + \param cam : Camera parameters. + \param candidates : In input, list of vpImagePoint located in the whole image, in output, list of vpImagePoint only located + on planes. + \param polygons : List of 2D polygons representing the projection of the faces in the image plane. + \param roisPt : List of faces. + \param points : Output list of computed 3D coordinates of vpImagePoint located only on faces. + \param descriptors : Optional parameter, pointer to the descriptors to filter + */ +void vpKeyPoint::compute3DForPointsInPolygons(const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, + std::vector<vpImagePoint> &candidates, std::vector<vpPolygon> &polygons, std::vector<std::vector<vpPoint> > &roisPt, + std::vector<vpPoint> &points, cv::Mat *descriptors) { + + std::vector<vpImagePoint> candidateToCheck = candidates; + candidates.clear(); + vpPoint pt; + cv::Mat desc; + + size_t cpt1 = 0; + for (std::vector<vpPolygon>::iterator it1 = polygons.begin(); it1 != polygons.end(); ++it1, cpt1++) { + int cpt2 = 0; + + for (std::vector<vpImagePoint>::iterator it2 = candidateToCheck.begin(); it2 != candidateToCheck.end(); cpt2++) { + if (it1->isInside(*it2)) { + candidates.push_back(*it2); + vpKeyPoint::compute3D(*it2, roisPt[cpt1], cam, cMo, pt); + points.push_back(pt); + + if(descriptors != NULL) { + desc.push_back(descriptors->row(cpt2)); + } + + //Remove candidate keypoint which is located on the current polygon + candidateToCheck.erase(it2); + } else { + ++it2; + } + } + } +} + +/*! + Compute the pose using the correspondence between 2D points and 3D points using OpenCV function with RANSAC method. + + \param imagePoints : List of 2D points corresponding to the location of the detected keypoints. + \param objectPoints : List of the 3D points in the object frame matched. + \param cam : Camera parameters. + \param cMo : Homogeneous matrix between the object frame and the camera frame. + \param inlierIndex : List of indexes of inliers. + \param elapsedTime : Elapsed time. + \param func : Function pointer to filter the final pose returned by OpenCV pose estimation method. + \return True if the pose has been computed, false otherwise (not enough points, or size list mismatch). + */ +bool vpKeyPoint::computePose(const std::vector<cv::Point2f> &imagePoints, const std::vector<cv::Point3f> &objectPoints, + const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, std::vector<int> &inlierIndex, + double &elapsedTime, bool (*func)(vpHomogeneousMatrix *)) { + double t = vpTime::measureTimeMs(); + + if(imagePoints.size() < 4 || objectPoints.size() < 4 || imagePoints.size() != objectPoints.size()) { + elapsedTime = (vpTime::measureTimeMs() - t); + std::cerr << "Not enough points to compute the pose (at least 4 points are needed)." << std::endl; + + return false; + } + + cv::Mat cameraMatrix = + (cv::Mat_<double>(3, 3) << cam.get_px(), 0, cam.get_u0(), 0, cam.get_py(), cam.get_v0(), 0, 0, 1); + cv::Mat rvec, tvec; + cv::Mat distCoeffs; + + try { +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + //OpenCV 3.0.0 (2014/12/12) + cv::solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, + rvec, tvec, false, m_nbRansacIterations, (float) m_ransacReprojectionError, + 0.99,//confidence=0.99 (default) – The probability that the algorithm produces a useful result. + inlierIndex, + cv::SOLVEPNP_ITERATIVE); + //SOLVEPNP_ITERATIVE (default): Iterative method is based on Levenberg-Marquardt optimization. + //In this case the function finds such a pose that minimizes reprojection error, that is the sum of squared distances + //between the observed projections imagePoints and the projected (using projectPoints() ) objectPoints . + //SOLVEPNP_P3P: Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang “Complete Solution Classification + //for the Perspective-Three-Point Problemâ€. In this case the function requires exactly four object and image points. + //SOLVEPNP_EPNP: Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the paper “EPnP: Efficient + //Perspective-n-Point Camera Pose Estimationâ€. + //SOLVEPNP_DLS: Method is based on the paper of Joel A. Hesch and Stergios I. Roumeliotis. “A Direct Least-Squares (DLS) + //Method for PnPâ€. + //SOLVEPNP_UPNP Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto, F.Moreno-Noguer. + //“Exhaustive Linearization for Robust Camera Pose and Focal Length Estimationâ€. In this case the function also + //estimates the parameters f_x and f_y assuming that both have the same value. Then the cameraMatrix is updated with the + //estimated focal length. +#else + int nbInlierToReachConsensus = m_nbRansacMinInlierCount; + if(m_useConsensusPercentage) { + nbInlierToReachConsensus = (int) (m_ransacConsensusPercentage / 100.0 * (double) m_queryFilteredKeyPoints.size()); + } + + cv::solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, + rvec, tvec, false, m_nbRansacIterations, + (float) m_ransacReprojectionError, nbInlierToReachConsensus, + inlierIndex); +#endif + } catch (cv::Exception &e) { + std::cerr << e.what() << std::endl; + elapsedTime = (vpTime::measureTimeMs() - t); + return false; + } + vpTranslationVector translationVec(tvec.at<double>(0), + tvec.at<double>(1), tvec.at<double>(2)); + vpThetaUVector thetaUVector(rvec.at<double>(0), rvec.at<double>(1), + rvec.at<double>(2)); + cMo = vpHomogeneousMatrix(translationVec, thetaUVector); + + if(func != NULL) { + //Check the final pose returned by the Ransac VVS pose estimation as in rare some cases + //we can converge toward a final cMo that does not respect the pose criterion even + //if the 4 minimal points picked to respect the pose criterion. + if(!func(&cMo)) { + elapsedTime = (vpTime::measureTimeMs() - t); + return false; + } + } + + elapsedTime = (vpTime::measureTimeMs() - t); + return true; +} + +/*! + Compute the pose using the correspondence between 2D points and 3D points using ViSP function with RANSAC method. + + \param objectVpPoints : List of vpPoint with coordinates expressed in the object and in the camera frame. + \param cMo : Homogeneous matrix between the object frame and the camera frame. + \param inliers : List of inliers. + \param elapsedTime : Elapsed time. + \return True if the pose has been computed, false otherwise (not enough points, or size list mismatch). + \param func : Function pointer to filter the pose in Ransac pose estimation, if we want to eliminate + the poses which do not respect some criterion + */ +bool vpKeyPoint::computePose(const std::vector<vpPoint> &objectVpPoints, vpHomogeneousMatrix &cMo, + std::vector<vpPoint> &inliers, double &elapsedTime, bool (*func)(vpHomogeneousMatrix *)) { + double t = vpTime::measureTimeMs(); + + if(objectVpPoints.size() < 4) { + elapsedTime = (vpTime::measureTimeMs() - t); + std::cerr << "Not enough points to compute the pose (at least 4 points are needed)." << std::endl; + + return false; + } + + vpPose pose; + pose.setCovarianceComputation(true); + + for(std::vector<vpPoint>::const_iterator it = objectVpPoints.begin(); it != objectVpPoints.end(); ++it) { + pose.addPoint(*it); + } + + unsigned int nbInlierToReachConsensus = (unsigned int) m_nbRansacMinInlierCount; + if(m_useConsensusPercentage) { + nbInlierToReachConsensus = (unsigned int) (m_ransacConsensusPercentage / 100.0 * + (double) m_queryFilteredKeyPoints.size()); + } + + pose.setRansacNbInliersToReachConsensus(nbInlierToReachConsensus); + pose.setRansacThreshold(m_ransacThreshold); + pose.setRansacMaxTrials(m_nbRansacIterations); + + bool isRansacPoseEstimationOk = false; + try { + pose.setCovarianceComputation(m_computeCovariance); + isRansacPoseEstimationOk = pose.computePose(vpPose::RANSAC, cMo, func); + inliers = pose.getRansacInliers(); + if(m_computeCovariance) { + m_covarianceMatrix = pose.getCovarianceMatrix(); + } + } catch(vpException &e) { + std::cerr << "e=" << e.what() << std::endl; + elapsedTime = (vpTime::measureTimeMs() - t); + return false; + } + + if(func != NULL && isRansacPoseEstimationOk) { + //Check the final pose returned by the Ransac VVS pose estimation as in rare some cases + //we can converge toward a final cMo that does not respect the pose criterion even + //if the 4 minimal points picked to respect the pose criterion. + if(!func(&cMo)) { + elapsedTime = (vpTime::measureTimeMs() - t); + return false; + } + } + + elapsedTime = (vpTime::measureTimeMs() - t); + return isRansacPoseEstimationOk; +} + +/*! + Compute the pose estimation error, the mean square error (in pixel) between the location of the detected keypoints + and the location of the projection of the 3D model with the estimated pose. + + \param matchKeyPoints : List of pairs between the detected keypoints and the corresponding 3D points. + \param cam : Camera parameters. + \param cMo_est : Estimated pose of the object. + + \return The mean square error (in pixel) between the location of the detected keypoints + and the location of the projection of the 3D model with the estimated pose. + */ +double vpKeyPoint::computePoseEstimationError(const std::vector<std::pair<cv::KeyPoint, cv::Point3f> > &matchKeyPoints, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo_est) { + if(matchKeyPoints.size() == 0) { + //return std::numeric_limits<double>::max(); // create an error under Windows. To fix it we have to add #undef max + return DBL_MAX; + } + + std::vector<double> errors(matchKeyPoints.size()); + size_t cpt = 0; + vpPoint pt; + for(std::vector<std::pair<cv::KeyPoint, cv::Point3f> >::const_iterator it = matchKeyPoints.begin(); + it != matchKeyPoints.end(); ++it, cpt++) { + pt.set_oX(it->second.x); + pt.set_oY(it->second.y); + pt.set_oZ(it->second.z); + pt.project(cMo_est); + double u = 0.0, v = 0.0; + vpMeterPixelConversion::convertPoint(cam, pt.get_x(), pt.get_y(), u, v); + errors[cpt] = std::sqrt((u-it->first.pt.x)*(u-it->first.pt.x) + (v-it->first.pt.y)*(v-it->first.pt.y)); + } + + return std::accumulate(errors.begin(), errors.end(), 0.0) / errors.size(); +} + +/*! + Initialize the size of the matching image (case with a matching side by side between IRef and ICurrent). + + \param IRef : Reference image. + \param ICurrent : Current image. + \param IMatching : Image matching. + */ +void vpKeyPoint::createImageMatching(vpImage<unsigned char> &IRef, vpImage<unsigned char> &ICurrent, + vpImage<unsigned char> &IMatching) { + //Image matching side by side + unsigned int width = IRef.getWidth() + ICurrent.getWidth(); + unsigned int height = (std::max)(IRef.getHeight(), ICurrent.getHeight()); + + IMatching = vpImage<unsigned char>(height, width); +} + +/*! + Initialize the size of the matching image with appropriate size according to the number of training images. + Used to display the matching of keypoints detected in the current image with those detected in multiple + training images. + + \param ICurrent : Current image. + \param IMatching : Image initialized with appropriate size. + */ +void vpKeyPoint::createImageMatching(vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching) { + //Nb images in the training database + the current image we want to detect the object + unsigned int nbImg = (unsigned int) (m_mapOfImages.size() + 1); + + if(m_mapOfImages.empty()) { + return; + } + + if(nbImg == 2) { + //Only one training image, so we display them side by side + createImageMatching(m_mapOfImages.begin()->second, ICurrent, IMatching); + } else { + //Multiple training images, display them as a mosaic image + //round(std::sqrt((double) nbImg)); VC++ compiler does not have round so the next line is used + //Different implementations of round exist, here round to the closest integer but will not work for negative numbers + unsigned int nbImgSqrt = (unsigned int) std::floor(std::sqrt((double) nbImg) + 0.5); + + //Number of columns in the mosaic grid + unsigned int nbWidth = nbImgSqrt; + //Number of rows in the mosaic grid + unsigned int nbHeight = nbImgSqrt; + + //Deals with non square mosaic grid and the total number of images + if(nbImgSqrt * nbImgSqrt < nbImg) { + nbWidth++; + } + + unsigned int maxW = ICurrent.getWidth(); + unsigned int maxH = ICurrent.getHeight(); + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it) { + if(maxW < it->second.getWidth()) { + maxW = it->second.getWidth(); + } + + if(maxH < it->second.getHeight()) { + maxH = it->second.getHeight(); + } + } + + IMatching = vpImage<unsigned char>(maxH * nbHeight, maxW * nbWidth); + } +} + +/*! + Detect keypoints in the image. + + \param I : Input image. + \param keyPoints : Output list of the detected keypoints. + \param elapsedTime : Elapsed time. + \param rectangle : Optional rectangle of the region of interest. + */ +void vpKeyPoint::detect(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &keyPoints, double &elapsedTime, + const vpRect &rectangle) { + cv::Mat matImg; + vpImageConvert::convert(I, matImg, false); + cv::Mat mask = cv::Mat::zeros(matImg.rows, matImg.cols, CV_8U); + + if(rectangle.getWidth() > 0 && rectangle.getHeight() > 0) { + cv::Point leftTop((int) rectangle.getLeft(), (int) rectangle.getTop()), rightBottom((int) rectangle.getRight(), + (int) rectangle.getBottom()); + cv::rectangle(mask, leftTop, rightBottom, cv::Scalar(255), CV_FILLED); + } else { + mask = cv::Mat::ones(matImg.rows, matImg.cols, CV_8U); + } + + detect(matImg, keyPoints, elapsedTime, mask); +} + +/*! + Detect keypoints in the image. + + \param matImg : Input image. + \param keyPoints : Output list of the detected keypoints. + \param elapsedTime : Elapsed time. + \param mask : Optional mask to detect only where mask[i][j] == 1. + */ +void vpKeyPoint::detect(const cv::Mat &matImg, std::vector<cv::KeyPoint> &keyPoints, double &elapsedTime, + const cv::Mat &mask) { + double t = vpTime::measureTimeMs(); + keyPoints.clear(); + + for(std::map<std::string, cv::Ptr<cv::FeatureDetector> >::const_iterator it = m_detectors.begin(); it != m_detectors.end(); ++it) { + std::vector<cv::KeyPoint> kp; + it->second->detect(matImg, kp, mask); + keyPoints.insert(keyPoints.end(), kp.begin(), kp.end()); + } + + elapsedTime = vpTime::measureTimeMs() - t; +} + +/*! + Display the reference and the detected keypoints in the images. + + \param IRef : Input reference image. + \param ICurrent : Input current image. + \param size : Size of the displayed cross. + */ +void vpKeyPoint::display(const vpImage<unsigned char> &IRef, + const vpImage<unsigned char> &ICurrent, unsigned int size) { + std::vector<vpImagePoint> vpQueryImageKeyPoints; + getQueryKeyPoints(vpQueryImageKeyPoints); + std::vector<vpImagePoint> vpTrainImageKeyPoints; + getTrainKeyPoints(vpTrainImageKeyPoints); + + for(std::vector<cv::DMatch>::const_iterator it = m_filteredMatches.begin(); it != m_filteredMatches.end(); ++it) { + vpDisplay::displayCross(IRef, vpTrainImageKeyPoints[(size_t)(it->trainIdx)], size, vpColor::red); + vpDisplay::displayCross(ICurrent, vpQueryImageKeyPoints[(size_t)(it->queryIdx)], size, vpColor::green); + } +} + +/*! + Display the reference keypoints. + + \param ICurrent : Input current image. + \param size : Size of the displayed crosses. + \param color : Color of the crosses. + */ +void vpKeyPoint::display(const vpImage<unsigned char> &ICurrent, unsigned int size, const vpColor &color) { + std::vector<vpImagePoint> vpQueryImageKeyPoints; + getQueryKeyPoints(vpQueryImageKeyPoints); + + for(std::vector<cv::DMatch>::const_iterator it = m_filteredMatches.begin(); it != m_filteredMatches.end(); ++it) { + vpDisplay::displayCross (ICurrent, vpQueryImageKeyPoints[(size_t)(it->queryIdx)], size, color); + } +} + +/*! + Display the matching lines between the detected keypoints with those detected in one training image. + + \param IRef : Reference image, used to have the x-offset. + \param IMatching : Resulting image matching. + \param crossSize : Size of the displayed crosses. + \param lineThickness : Thickness of the displayed lines. + \param color : Color to use, if none, we pick randomly a color for each pair of matching. + */ +void vpKeyPoint::displayMatching(const vpImage<unsigned char> &IRef, vpImage<unsigned char> &IMatching, + unsigned int crossSize, unsigned int lineThickness, const vpColor &color) { + bool randomColor = (color == vpColor::none); + srand((unsigned int) time(NULL)); + vpColor currentColor = color; + + std::vector<vpImagePoint> queryImageKeyPoints; + getQueryKeyPoints(queryImageKeyPoints); + std::vector<vpImagePoint> trainImageKeyPoints; + getTrainKeyPoints(trainImageKeyPoints); + + vpImagePoint leftPt, rightPt; + for(std::vector<cv::DMatch>::const_iterator it = m_filteredMatches.begin(); it != m_filteredMatches.end(); ++it) { + if(randomColor) { + currentColor = vpColor((rand() % 256), (rand() % 256), (rand() % 256)); + } + + leftPt = trainImageKeyPoints[(size_t)(it->trainIdx)]; + rightPt = vpImagePoint(queryImageKeyPoints[(size_t)(it->queryIdx)].get_i(), + queryImageKeyPoints[(size_t)it->queryIdx].get_j() + IRef.getWidth()); + vpDisplay::displayCross(IMatching, leftPt, crossSize, currentColor); + vpDisplay::displayCross(IMatching, rightPt, crossSize, currentColor); + vpDisplay::displayLine(IMatching, leftPt, rightPt, currentColor, lineThickness); + } +} + +/*! + Display matching between keypoints detected in the current image and with those detected in the multiple training + images. Display also RANSAC inliers if the list is supplied. + + \param ICurrent : Current image. + \param IMatching : Resulting matching image. + \param ransacInliers : List of Ransac inliers or empty list if not available. + \param crossSize : Size of the displayed crosses. + \param lineThickness : Thickness of the displayed line. + */ +void vpKeyPoint::displayMatching(const vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching, + const std::vector<vpImagePoint> &ransacInliers, unsigned int crossSize, unsigned int lineThickness) { + if(m_mapOfImages.empty()) { + //No training images so return + return; + } + + //Nb images in the training database + the current image we want to detect the object + int nbImg = (int) (m_mapOfImages.size() + 1); + + if(nbImg == 2) { + //Only one training image, so we display the matching result side-by-side + displayMatching(m_mapOfImages.begin()->second, IMatching, crossSize); + } else { + //Multiple training images, display them as a mosaic image + //round(std::sqrt((double) nbImg)); VC++ compiler does not have round so the next line is used + //Different implementations of round exist, here round to the closest integer but will not work for negative numbers + int nbImgSqrt = (int) std::floor(std::sqrt((double) nbImg) + 0.5); + int nbWidth = nbImgSqrt; + int nbHeight = nbImgSqrt; + + if(nbImgSqrt * nbImgSqrt < nbImg) { + nbWidth++; + } + + std::map<int, int> mapOfImageIdIndex; + int cpt = 0; + unsigned int maxW = ICurrent.getWidth(), maxH = ICurrent.getHeight(); + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it, cpt++) { + mapOfImageIdIndex[it->first] = cpt; + + if(maxW < it->second.getWidth()) { + maxW = it->second.getWidth(); + } + + if(maxH < it->second.getHeight()) { + maxH = it->second.getHeight(); + } + } + + //Indexes of the current image in the grid made in order to the image is in the center square in the mosaic grid + int medianI = nbHeight / 2; + int medianJ = nbWidth / 2; + int medianIndex = medianI * nbWidth + medianJ; + for(std::vector<cv::KeyPoint>::const_iterator it = m_trainKeyPoints.begin(); it != m_trainKeyPoints.end(); ++it) { + vpImagePoint topLeftCorner; + int current_class_id_index = 0; + if(mapOfImageIdIndex[m_mapOfImageId[it->class_id]] < medianIndex) { + current_class_id_index = mapOfImageIdIndex[m_mapOfImageId[it->class_id]]; + } else { + //Shift of one unity the index of the training images which are after the current image + current_class_id_index = mapOfImageIdIndex[m_mapOfImageId[it->class_id]] + 1; + } + + int indexI = current_class_id_index / nbWidth; + int indexJ = current_class_id_index - (indexI * nbWidth); + topLeftCorner.set_ij((int)maxH*indexI, (int)maxW*indexJ); + + //Display cross for keypoints in the learning database + vpDisplay::displayCross(IMatching, (int) (it->pt.y + topLeftCorner.get_i()), (int) (it->pt.x + topLeftCorner.get_j()), + crossSize, vpColor::red); + } + + vpImagePoint topLeftCorner((int)maxH*medianI, (int)maxW*medianJ); + for(std::vector<cv::KeyPoint>::const_iterator it = m_queryKeyPoints.begin(); it != m_queryKeyPoints.end(); ++it) { + //Display cross for keypoints detected in the current image + vpDisplay::displayCross(IMatching, (int) (it->pt.y + topLeftCorner.get_i()), (int) (it->pt.x + topLeftCorner.get_j()), + crossSize, vpColor::red); + } + for(std::vector<vpImagePoint>::const_iterator it = ransacInliers.begin(); + it != ransacInliers.end(); ++it) { + //Display green circle for RANSAC inliers + vpDisplay::displayCircle(IMatching, (int) (it->get_v() + topLeftCorner.get_i()), (int) (it->get_u() + + topLeftCorner.get_j()), 4, vpColor::green); + } + for(std::vector<vpImagePoint>::const_iterator it = m_ransacOutliers.begin(); it != m_ransacOutliers.end(); ++it) { + //Display red circle for RANSAC outliers + vpDisplay::displayCircle(IMatching, (int) (it->get_i() + topLeftCorner.get_i()), (int) (it->get_j() + + topLeftCorner.get_j()), 4, vpColor::red); + } + + for(std::vector<std::pair<cv::KeyPoint, cv::KeyPoint> >::const_iterator it = m_matchQueryToTrainKeyPoints.begin(); + it != m_matchQueryToTrainKeyPoints.end(); ++it) { + int current_class_id = 0; + if(mapOfImageIdIndex[m_mapOfImageId[it->second.class_id]] < medianIndex) { + current_class_id = mapOfImageIdIndex[m_mapOfImageId[it->second.class_id]]; + } else { + //Shift of one unity the index of the training images which are after the current image + current_class_id = mapOfImageIdIndex[m_mapOfImageId[it->second.class_id]] + 1; + } + + int indexI = current_class_id / nbWidth; + int indexJ = current_class_id - (indexI * nbWidth); + + vpImagePoint end((int)maxH*indexI + it->second.pt.y, (int)maxW*indexJ + it->second.pt.x); + vpImagePoint start((int)maxH*medianI + it->first.pt.y, (int)maxW*medianJ + it->first.pt.x); + + //Draw line for matching keypoints detected in the current image and those detected + //in the training images + vpDisplay::displayLine(IMatching, start, end, vpColor::green, lineThickness); + } + } +} + +/*! + Extract the descriptors for each keypoints of the list. + + \param I : Input image. + \param keyPoints : List of keypoints we want to extract their descriptors. + \param descriptors : Descriptors matrix with at each row the descriptors values for each keypoint. + \param elapsedTime : Elapsed time. + */ +void vpKeyPoint::extract(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &keyPoints, cv::Mat &descriptors, + double &elapsedTime) { + cv::Mat matImg; + vpImageConvert::convert(I, matImg, false); + extract(matImg, keyPoints, descriptors, elapsedTime); +} + +/*! + Extract the descriptors for each keypoints of the list. + + \param matImg : Input image. + \param keyPoints : List of keypoints we want to extract their descriptors. + \param descriptors : Descriptors matrix with at each row the descriptors values for each keypoint. + \param elapsedTime : Elapsed time. + */ +void vpKeyPoint::extract(const cv::Mat &matImg, std::vector<cv::KeyPoint> &keyPoints, cv::Mat &descriptors, + double &elapsedTime) { + double t = vpTime::measureTimeMs(); + bool first = true; + + for(std::map<std::string, cv::Ptr<cv::DescriptorExtractor> >::const_iterator it = m_extractors.begin(); + it != m_extractors.end(); ++it) { + if(first) { + first = false; + it->second->compute(matImg, keyPoints, descriptors); + } else { + cv::Mat desc; + it->second->compute(matImg, keyPoints, desc); + if(descriptors.empty()) { + desc.copyTo(descriptors); + } else { + cv::hconcat(descriptors, desc, descriptors); + } + } + } + + if(keyPoints.size() != (size_t) descriptors.rows) { + std::cerr << "keyPoints.size() != (size_t) descriptors.rows" << std::endl; + } + elapsedTime = vpTime::measureTimeMs() - t; +} + +/*! + Filter the matches using the desired filtering method. + */ +void vpKeyPoint::filterMatches() { + m_matchQueryToTrainKeyPoints.clear(); + + std::vector<cv::KeyPoint> queryKpts; + std::vector<cv::Point3f> trainPts; + std::vector<cv::DMatch> m; + + if(m_useKnn) { + double max_dist = 0; + //double min_dist = std::numeric_limits<double>::max(); // create an error under Windows. To fix it we have to add #undef max + double min_dist = DBL_MAX; + double mean = 0.0; + std::vector<double> distance_vec(m_knnMatches.size()); + + if(m_filterType == stdAndRatioDistanceThreshold) { + for(size_t i = 0; i < m_knnMatches.size(); i++) { + double dist = m_knnMatches[i][0].distance; + mean += dist; + distance_vec[i] = dist; + + if (dist < min_dist) { + min_dist = dist; + } + if (dist > max_dist) { + max_dist = dist; + } + } + mean /= m_queryDescriptors.rows; + } + + double sq_sum = std::inner_product(distance_vec.begin(), distance_vec.end(), distance_vec.begin(), 0.0); + double stdev = std::sqrt(sq_sum / distance_vec.size() - mean * mean); + double threshold = min_dist + stdev; + + for(size_t i = 0; i < m_knnMatches.size(); i++) { + if(m_knnMatches[i].size() >= 2) { + //Calculate ratio of the descriptor distance between the two nearest neighbors of the keypoint + float ratio = m_knnMatches[i][0].distance / m_knnMatches[i][1].distance; +// float ratio = std::sqrt((vecMatches[i][0].distance * vecMatches[i][0].distance) +// / (vecMatches[i][1].distance * vecMatches[i][1].distance)); + double dist = m_knnMatches[i][0].distance; + + if(ratio < m_matchingRatioThreshold || (m_filterType == stdAndRatioDistanceThreshold && dist < threshold)) { + m.push_back(cv::DMatch((int) queryKpts.size(), m_knnMatches[i][0].trainIdx, m_knnMatches[i][0].distance)); + + if(!m_trainPoints.empty()) { + trainPts.push_back(m_trainPoints[(size_t)m_knnMatches[i][0].trainIdx]); + } + queryKpts.push_back(m_queryKeyPoints[(size_t)m_knnMatches[i][0].queryIdx]); + +// //Add the pair with the correspondence between the detected keypoints and the one matched in the train keypoints list +// m_matchQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>( +// m_queryKeyPoints[(size_t)m_knnMatches[i][0].queryIdx], +// m_trainKeyPoints[(size_t)m_knnMatches[i][0].trainIdx])); + } + } + } + } else { + double max_dist = 0; + //double min_dist = std::numeric_limits<double>::max(); // create an error under Windows. To fix it we have to add #undef max + double min_dist = DBL_MAX; + double mean = 0.0; + std::vector<double> distance_vec(m_matches.size()); + for(size_t i = 0; i < m_matches.size(); i++) { + double dist = m_matches[i].distance; + mean += dist; + distance_vec[i] = dist; + + if (dist < min_dist) { + min_dist = dist; + } + if (dist > max_dist) { + max_dist = dist; + } + } + mean /= m_queryDescriptors.rows; + + double sq_sum = std::inner_product(distance_vec.begin(), distance_vec.end(), distance_vec.begin(), 0.0); + double stdev = std::sqrt(sq_sum / distance_vec.size() - mean * mean); + + //Define a threshold where we keep all keypoints whose the descriptor distance falls below a factor of the + //minimum descriptor distance (for all the query keypoints) + //or below the minimum descriptor distance + the standard deviation (calculated on all the query descriptor distances) + double threshold = m_filterType == constantFactorDistanceThreshold ? m_matchingFactorThreshold * min_dist : min_dist + stdev; + + for (size_t i = 0; i < m_matches.size(); i++) { + if(m_matches[i].distance <= threshold) { + m.push_back(cv::DMatch((int) queryKpts.size(), m_matches[i].trainIdx, m_matches[i].distance)); + + if(!m_trainPoints.empty()) { + trainPts.push_back(m_trainPoints[(size_t)m_matches[i].trainIdx]); + } + queryKpts.push_back(m_queryKeyPoints[(size_t)m_matches[i].queryIdx]); + +// //Add the pair with the correspondence between the detected keypoints and the one matched in the train keypoints list +// m_matchQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>( +// m_queryKeyPoints[(size_t)m_matches[i].queryIdx], m_trainKeyPoints[(size_t)m_matches[i].trainIdx])); + } + } + } + + //Eliminate matches where multiple query keypoints are matched to the same train keypoint + std::vector<cv::DMatch> mTmp; + std::vector<cv::Point3f> trainPtsTmp; + std::vector<cv::KeyPoint> queryKptsTmp; + + std::map<int, int> mapOfTrainIdx; + //Count the number of query points matched to the same train point + for(std::vector<cv::DMatch>::const_iterator it = m.begin(); it != m.end(); ++it) { + mapOfTrainIdx[it->trainIdx]++; + } + + //Keep matches with only one correspondence + for(std::vector<cv::DMatch>::const_iterator it = m.begin(); it != m.end(); ++it) { + if(mapOfTrainIdx[it->trainIdx] == 1) { + mTmp.push_back(cv::DMatch((int) queryKptsTmp.size(), it->trainIdx, it->distance)); + + if(!m_trainPoints.empty()) { + trainPtsTmp.push_back(m_trainPoints[(size_t) it->trainIdx]); + } + queryKptsTmp.push_back(queryKpts[(size_t) it->queryIdx]); + + //Add the pair with the correspondence between the detected keypoints and the one matched in the train keypoints list + m_matchQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>( + queryKpts[(size_t) it->queryIdx], + m_trainKeyPoints[(size_t) it->trainIdx])); + } + } + + m_filteredMatches = mTmp; + m_objectFilteredPoints = trainPtsTmp; + m_queryFilteredKeyPoints = queryKptsTmp; +} + +/*! + Get the 3D coordinates of the object points matched (the corresponding 3D coordinates in the object frame + of the keypoints detected in the current image after the matching). + + \param objectPoints : List of 3D coordinates in the object frame. + */ +void vpKeyPoint::getObjectPoints(std::vector<cv::Point3f> &objectPoints) const { + objectPoints = m_objectFilteredPoints; +} + +/*! + Get the 3D coordinates of the object points matched (the corresponding 3D coordinates in the object frame + of the keypoints detected in the current image after the matching). + + \param objectPoints : List of 3D coordinates in the object frame. + */ +void vpKeyPoint::getObjectPoints(std::vector<vpPoint> &objectPoints) const { + vpConvert::convertFromOpenCV(m_objectFilteredPoints, objectPoints); +} + +/*! + Get the query keypoints list in OpenCV type. + + \param keyPoints : List of query keypoints (or keypoints detected in the current image). + */ +void vpKeyPoint::getQueryKeyPoints(std::vector<cv::KeyPoint> &keyPoints) const { + keyPoints = m_queryFilteredKeyPoints; +} + +/*! + Get the query keypoints list in ViSP type. + + \param keyPoints : List of query keypoints (or keypoints detected in the current image). + */ +void vpKeyPoint::getQueryKeyPoints(std::vector<vpImagePoint> &keyPoints) const { + keyPoints = currentImagePointsList; +} + +/*! + Get the train keypoints list in OpenCV type. + + \param keyPoints : List of train keypoints (or reference keypoints). + */ +void vpKeyPoint::getTrainKeyPoints(std::vector<cv::KeyPoint> &keyPoints) const { + keyPoints = m_trainKeyPoints; +} + +/*! + Get the train keypoints list in ViSP type. + + \param keyPoints : List of train keypoints (or reference keypoints). + */ +void vpKeyPoint::getTrainKeyPoints(std::vector<vpImagePoint> &keyPoints) const { + keyPoints = referenceImagePointsList; +} + +/*! + Get the train points (the 3D coordinates in the object frame) list in OpenCV type. + + \param points : List of train points (or reference points). + */ +void vpKeyPoint::getTrainPoints(std::vector<cv::Point3f> &points) const { + points = m_trainPoints; +} + +/*! + Get the train points (the 3D coordinates in the object frame) list in ViSP type. + + \param points : List of train points (or reference points). + */ +void vpKeyPoint::getTrainPoints(std::vector<vpPoint> &points) const { + points = m_trainVpPoints; +} + +/*! + Initialize method for RANSAC parameters and for detectors, extractors and matcher, and for others parameters. + */ +void vpKeyPoint::init() { +#if defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION >= 0x020400) && (VISP_HAVE_OPENCV_VERSION < 0x030000) // Require 2.4.0 <= opencv < 3.0.0 + //The following line must be called in order to use SIFT or SURF + if (!cv::initModule_nonfree()) { + std::cerr << "Cannot init module non free, SIFT or SURF cannot be used." + << std::endl; + } +#endif + + initDetectors(m_detectorNames); + initExtractors(m_extractorNames); + initMatcher(m_matcherName); +} + +/*! + Initialize a keypoint detector based on its name. + + \param detectorName : Name of the detector (e.g FAST, SIFT, SURF, etc.). + */ +void vpKeyPoint::initDetector(const std::string &detectorName) { + // TODO: Add function to process detection in pyramid images with OpenCV 3.0.0 + // since there seems to have no equivalent function (2014/12/11) + // std::string pyramid = "Pyramid"; + // std::size_t pos = detectorName.find(pyramid); + // if(pos != std::string::npos) { + // std::string sub = detectorName.substr(pos + pyramid.size()); + // detectors[detectorName] = cv::Ptr<cv::FeatureDetector>( + // new cv::PyramidAdaptedFeatureDetector(cv::FeatureDetector::create<cv::FeatureDetector>(sub), 2)); + // } else { + // detectors[detectorName] = cv::FeatureDetector::create<cv::FeatureDetector>(detectorName); + // } +#if (VISP_HAVE_OPENCV_VERSION < 0x030000) + m_detectors[detectorName] = cv::FeatureDetector::create(detectorName); + + if(m_detectors[detectorName] == NULL) { + std::stringstream ss_msg; + ss_msg << "Fail to initialize the detector: " << detectorName << " or it is not available in OpenCV version: " + << std::hex << VISP_HAVE_OPENCV_VERSION << "."; + throw vpException(vpException::fatalError, ss_msg.str()); + } +#else + //TODO: Add a pyramidal feature detection + std::string detectorNameTmp = detectorName; + std::string pyramid = "Pyramid"; + std::size_t pos = detectorName.find(pyramid); + if(pos != std::string::npos) { + detectorNameTmp = detectorName.substr(pos + pyramid.size()); + } + + if(detectorNameTmp == "SIFT") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_detectors[detectorNameTmp] = cv::xfeatures2d::SIFT::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the detector: SIFT. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else if(detectorNameTmp == "SURF") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_detectors[detectorNameTmp] = cv::xfeatures2d::SURF::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the detector: SURF. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else if(detectorNameTmp == "FAST") { + m_detectors[detectorNameTmp] = cv::FastFeatureDetector::create(); + } else if(detectorNameTmp == "MSER") { + m_detectors[detectorNameTmp] = cv::MSER::create(); + } else if(detectorNameTmp == "ORB") { + m_detectors[detectorNameTmp] = cv::ORB::create(); + } else if(detectorNameTmp == "BRISK") { + m_detectors[detectorNameTmp] = cv::BRISK::create(); + } else if(detectorNameTmp == "KAZE") { + m_detectors[detectorNameTmp] = cv::KAZE::create(); + } else if(detectorNameTmp == "AKAZE") { + m_detectors[detectorNameTmp] = cv::AKAZE::create(); + } else if(detectorNameTmp == "GFFT") { + m_detectors[detectorNameTmp] = cv::GFTTDetector::create(); + } else if(detectorNameTmp == "SimpleBlob") { + m_detectors[detectorNameTmp] = cv::SimpleBlobDetector::create(); + } else if(detectorNameTmp == "STAR") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_detectors[detectorNameTmp] = cv::xfeatures2d::StarDetector::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the detector: STAR. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else { + std::cerr << "The detector:" << detectorNameTmp << " is not available." << std::endl; + } + + if(m_detectors[detectorNameTmp] == NULL) { + std::stringstream ss_msg; + ss_msg << "Fail to initialize the detector: " << detectorNameTmp << " or it is not available in OpenCV version: " + << std::hex << VISP_HAVE_OPENCV_VERSION << "."; + throw vpException(vpException::fatalError, ss_msg.str()); + } +// m_detectors[detectorName] = cv::FeatureDetector::create<cv::FeatureDetector>(detectorName); +#endif +} + +/*! + Initialize a list of keypoints detectors if we want to concatenate multiple detectors. + + \param detectorNames : List of detector names. + */ +void vpKeyPoint::initDetectors(const std::vector<std::string> &detectorNames) { + for(std::vector<std::string>::const_iterator it = detectorNames.begin(); it != detectorNames.end(); ++it) { + initDetector(*it); + } +} + +/*! + Initialize a descriptor extractor based on its name. + + \param extractorName : Name of the extractor (e.g SIFT, SURF, ORB, etc.). + */ +void vpKeyPoint::initExtractor(const std::string &extractorName) { +#if (VISP_HAVE_OPENCV_VERSION < 0x030000) + m_extractors[extractorName] = cv::DescriptorExtractor::create(extractorName); +#else + if(extractorName == "SIFT") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_extractors[extractorName] = cv::xfeatures2d::SIFT::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the extractor: SIFT. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else if(extractorName == "SURF") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_extractors[extractorName] = cv::xfeatures2d::SURF::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the extractor: SURF. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else if(extractorName == "ORB") { + m_extractors[extractorName] = cv::ORB::create(); + } else if(extractorName == "BRISK") { + m_extractors[extractorName] = cv::BRISK::create(); + } else if(extractorName == "FREAK") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_extractors[extractorName] = cv::xfeatures2d::FREAK::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the extractor: FREAK. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else if(extractorName == "BRIEF") { +#ifdef VISP_HAVE_OPENCV_XFEATURES2D + m_extractors[extractorName] = cv::xfeatures2d::BriefDescriptorExtractor::create(); +#else + std::stringstream ss_msg; + ss_msg << "Fail to initialize the extractor: BRIEF. OpenCV version " + << std::hex << VISP_HAVE_OPENCV_VERSION << " was not build with xFeatures2d module."; + throw vpException(vpException::fatalError, ss_msg.str()); +#endif + } else { + std::cerr << "The extractor:" << extractorName << " is not available." << std::endl; + } +// m_extractors[extractorName] = cv::DescriptorExtractor::create<cv::DescriptorExtractor>(extractorName); +#endif + + if(m_extractors[extractorName] == NULL) { + std::stringstream ss_msg; + ss_msg << "Fail to initialize the extractor: " << extractorName << " or it is not available in OpenCV version: " + << std::hex << VISP_HAVE_OPENCV_VERSION << "."; + throw vpException(vpException::fatalError, ss_msg.str()); + } +} + +/*! + Initialize a list of descriptor extractors if we want to concatenate multiple extractors. + + \param extractorNames : List of extractor names. + */ +void vpKeyPoint::initExtractors(const std::vector<std::string> &extractorNames) { + for(std::vector<std::string>::const_iterator it = extractorNames.begin(); it != extractorNames.end(); ++it) { + initExtractor(*it); + } +} + +/*! + Initialize a matcher based on its name. + + \param matcherName : Name of the matcher (e.g BruteForce, FlannBased). + */ +void vpKeyPoint::initMatcher(const std::string &matcherName) { + m_matcher = cv::DescriptorMatcher::create(matcherName); + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020400) + if(m_matcher != NULL && !m_useKnn && matcherName == "BruteForce") { + m_matcher->set("crossCheck", m_useBruteForceCrossCheck); + } +#endif + + if(m_matcher == NULL) { + std::stringstream ss_msg; + ss_msg << "Fail to initialize the matcher: " << matcherName << " or it is not available in OpenCV version: " + << std::hex << VISP_HAVE_OPENCV_VERSION << "."; + throw vpException(vpException::fatalError, ss_msg.str()); + } +} + +/*! + Insert a reference image and a current image side-by-side. + + \param IRef : Reference image. + \param ICurrent : Current image. + \param IMatching : Matching image for displaying all the matching between the query keypoints and those + detected in the training images. + */ +void vpKeyPoint::insertImageMatching(const vpImage<unsigned char> &IRef, const vpImage<unsigned char> &ICurrent, + vpImage<unsigned char> &IMatching) { + vpImagePoint topLeftCorner(0, 0); + IMatching.insert(IRef, topLeftCorner); + topLeftCorner = vpImagePoint(0, IRef.getWidth()); + IMatching.insert(ICurrent, topLeftCorner); +} + +/*! + Insert the different training images in the matching image. + + \param ICurrent : Current image. + \param IMatching : Matching image for displaying all the matching between the query keypoints and those + detected in the training images + */ +void vpKeyPoint::insertImageMatching(const vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching) { + //Nb images in the training database + the current image we want to detect the object + int nbImg = (int) (m_mapOfImages.size() + 1); + + if(nbImg == 2) { + //Only one training image, so we display them side by side + insertImageMatching(m_mapOfImages.begin()->second, ICurrent, IMatching); + } else { + //Multiple training images, display them as a mosaic image + //round(std::sqrt((double) nbImg)); VC++ compiler does not have round so the next line is used + //Different implementations of round exist, here round to the closest integer but will not work for negative numbers + int nbImgSqrt = (int) std::floor(std::sqrt((double) nbImg) + 0.5); + int nbWidth = nbImgSqrt; + int nbHeight = nbImgSqrt; + + if(nbImgSqrt * nbImgSqrt < nbImg) { + nbWidth++; + } + + unsigned int maxW = ICurrent.getWidth(), maxH = ICurrent.getHeight(); + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it) { + if(maxW < it->second.getWidth()) { + maxW = it->second.getWidth(); + } + + if(maxH < it->second.getHeight()) { + maxH = it->second.getHeight(); + } + } + + //Indexes of the current image in the grid made in order to the image is in the center square in the mosaic grid + int medianI = nbHeight / 2; + int medianJ = nbWidth / 2; + int medianIndex = medianI * nbWidth + medianJ; + + int cpt = 0; + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it, cpt++) { + int local_cpt = cpt; + if(cpt >= medianIndex) { + //Shift of one unity the index of the training images which are after the current image + local_cpt++; + } + int indexI = local_cpt / nbWidth; + int indexJ = local_cpt - (indexI * nbWidth); + vpImagePoint topLeftCorner((int)maxH*indexI, (int)maxW*indexJ); + + IMatching.insert(it->second, topLeftCorner); + } + + vpImagePoint topLeftCorner((int)maxH*medianI, (int)maxW*medianJ); + IMatching.insert(ICurrent, topLeftCorner); + } +} + +#ifdef VISP_HAVE_XML2 +/*! + Load configuration parameters from an XML config file. + + \param configFile : Path to the XML config file. + */ +void vpKeyPoint::loadConfigFile(const std::string &configFile) { + vpXmlConfigParserKeyPoint xmlp; + + try { + //Reset detector and extractor + m_detectorNames.clear(); + m_extractorNames.clear(); + m_detectors.clear(); + m_extractors.clear(); + + std::cout << " *********** Parsing XML for configuration for vpKeyPoint ************ " << std::endl; + xmlp.parse(configFile); + + m_detectorNames.push_back(xmlp.getDetectorName()); + m_extractorNames.push_back(xmlp.getExtractorName()); + m_matcherName = xmlp.getMatcherName(); + + switch(xmlp.getMatchingMethod()) { + case vpXmlConfigParserKeyPoint::constantFactorDistanceThreshold: + m_filterType = constantFactorDistanceThreshold; + break; + + case vpXmlConfigParserKeyPoint::stdDistanceThreshold: + m_filterType = stdDistanceThreshold; + break; + + case vpXmlConfigParserKeyPoint::ratioDistanceThreshold: + m_filterType = ratioDistanceThreshold; + break; + + case vpXmlConfigParserKeyPoint::stdAndRatioDistanceThreshold: + m_filterType = stdAndRatioDistanceThreshold; + break; + + case vpXmlConfigParserKeyPoint::noFilterMatching: + m_filterType = noFilterMatching; + break; + + default: + break; + } + + m_matchingFactorThreshold = xmlp.getMatchingFactorThreshold(); + m_matchingRatioThreshold = xmlp.getMatchingRatioThreshold(); + + m_useRansacVVS = xmlp.getUseRansacVVSPoseEstimation(); + m_useConsensusPercentage = xmlp.getUseRansacConsensusPercentage(); + m_nbRansacIterations = xmlp.getNbRansacIterations(); + m_ransacReprojectionError = xmlp.getRansacReprojectionError(); + m_nbRansacMinInlierCount = xmlp.getNbRansacMinInlierCount(); + m_ransacThreshold = xmlp.getRansacThreshold(); + m_ransacConsensusPercentage = xmlp.getRansacConsensusPercentage(); + + if(m_filterType == ratioDistanceThreshold || m_filterType == stdAndRatioDistanceThreshold) { + m_useKnn = true; + } else { + m_useKnn = false; + } + + init(); + } + catch(...) { + throw vpException(vpException::ioError, "Can't open XML file \"%s\"\n ", configFile.c_str()); + } +} +#endif + +/*! + Load learning data saved on disk. + + \param filename : Path of the learning file. + \param binaryMode : If true, the learning file is in a binary mode, otherwise it is in XML mode. + \param append : If true, concatenate the learning data, otherwise reset the variables. + */ +void vpKeyPoint::loadLearningData(const std::string &filename, const bool binaryMode, const bool append) { + int startClassId = 0; + int startImageId = 0; + if(!append) { + m_trainKeyPoints.clear(); + m_trainPoints.clear(); + m_mapOfImageId.clear(); + m_mapOfImages.clear(); + } else { + //In append case, find the max index of keypoint class Id + for(std::map<int, int>::const_iterator it = m_mapOfImageId.begin(); it != m_mapOfImageId.end(); ++it) { + if(startClassId < it->first) { + startClassId = it->first; + } + } + + //In append case, find the max index of images Id + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it) { + if(startImageId < it->first) { + startImageId = it->first; + } + } + } + + //Get parent directory + std::string parent = vpIoTools::getParent(filename); + if(!parent.empty()) { + parent += "/"; + } + + if(binaryMode) { + std::ifstream file(filename.c_str(), std::ifstream::binary); + if(!file.is_open()){ + throw vpException(vpException::ioError, "Cannot open the file."); + } + + //Read info about training images + int nbImgs = 0; + file.read((char *)(&nbImgs), sizeof(nbImgs)); + + for(int i = 0; i < nbImgs; i++) { + //Read image_id + int id = 0; + file.read((char *)(&id), sizeof(id)); + + int length = 0; + file.read((char *)(&length), sizeof(length)); + //Will contain the path to the training images + char* path = new char[length + 1];//char path[length + 1]; + + for(int cpt = 0; cpt < length; cpt++) { + char c; + file.read((char *)(&c), sizeof(c)); + path[cpt] = c; + } + path[length] = '\0'; + + vpImage<unsigned char> I; + if(vpIoTools::isAbsolutePathname(std::string(path))) { + vpImageIo::read(I, path); + } else { + vpImageIo::read(I, parent + path); + } + + m_mapOfImages[id + startImageId] = I; + } + + //Read if 3D point information are saved or not + int have3DInfoInt = 0; + file.read((char *)(&have3DInfoInt), sizeof(have3DInfoInt)); + bool have3DInfo = have3DInfoInt != 0; + + //Read the number of descriptors + int nRows = 0; + file.read((char *)(&nRows), sizeof(nRows)); + + //Read the size of the descriptor + int nCols = 0; + file.read((char *)(&nCols), sizeof(nCols)); + + //Read the type of the descriptor + int descriptorType = 5; //CV_32F + file.read((char *)(&descriptorType), sizeof(descriptorType)); + + cv::Mat trainDescriptorsTmp = cv::Mat(nRows, nCols, descriptorType); + for(int i = 0; i < nRows; i++) { + //Read information about keyPoint + float u, v, size, angle, response; + int octave, class_id, image_id; + file.read((char *)(&u), sizeof(u)); + file.read((char *)(&v), sizeof(v)); + file.read((char *)(&size), sizeof(size)); + file.read((char *)(&angle), sizeof(angle)); + file.read((char *)(&response), sizeof(response)); + file.read((char *)(&octave), sizeof(octave)); + file.read((char *)(&class_id), sizeof(class_id)); + file.read((char *)(&image_id), sizeof(image_id)); + cv::KeyPoint keyPoint(cv::Point2f(u, v), size, angle, response, octave, (class_id + startClassId)); + m_trainKeyPoints.push_back(keyPoint); + + if(image_id != -1) { + //No training images if image_id == -1 + m_mapOfImageId[class_id] = image_id + startImageId; + } + + if(have3DInfo) { + //Read oX, oY, oZ + float oX, oY, oZ; + file.read((char *)(&oX), sizeof(oX)); + file.read((char *)(&oY), sizeof(oY)); + file.read((char *)(&oZ), sizeof(oZ)); + m_trainPoints.push_back(cv::Point3f(oX, oY, oZ)); + } + + for(int j = 0; j < nCols; j++) { + //Read the value of the descriptor + switch(descriptorType) { + case CV_8U: + { + unsigned char value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<unsigned char>(i, j) = value; + } + break; + + case CV_8S: + { + char value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<char>(i, j) = value; + } + break; + + case CV_16U: + { + unsigned short int value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<unsigned short int>(i, j) = value; + } + break; + + case CV_16S: + { + short int value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<short int>(i, j) = value; + } + break; + + case CV_32S: + { + int value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<int>(i, j) = value; + } + break; + + case CV_32F: + { + float value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<float>(i, j) = value; + } + break; + + case CV_64F: + { + double value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<double>(i, j) = value; + } + break; + + default: + { + float value; + file.read((char *)(&value), sizeof(value)); + trainDescriptorsTmp.at<float>(i, j) = value; + } + break; + } + } + } + + if(!append || m_trainDescriptors.empty()) { + trainDescriptorsTmp.copyTo(m_trainDescriptors); + } else { + cv::vconcat(m_trainDescriptors, trainDescriptorsTmp, m_trainDescriptors); + } + + file.close(); + } else { +#ifdef VISP_HAVE_XML2 + xmlDocPtr doc = NULL; + xmlNodePtr root_element = NULL; + + /* + * this initialize the library and check potential ABI mismatches + * between the version it was compiled for and the actual shared + * library used. + */ + LIBXML_TEST_VERSION + + /*parse the file and get the DOM */ + doc = xmlReadFile(filename.c_str(), NULL, 0); + + if (doc == NULL) { + throw vpException(vpException::ioError, "Error with file " + filename); + } + + root_element = xmlDocGetRootElement(doc); + + xmlNodePtr first_level_node = NULL; + char *pEnd = NULL; + + int descriptorType = CV_32F; //float + int nRows = 0, nCols = 0; + int i = 0, j = 0; + + cv::Mat trainDescriptorsTmp; + + for (first_level_node = root_element->children; first_level_node; + first_level_node = first_level_node->next) { + + std::string name((char *) first_level_node->name); + if (first_level_node->type == XML_ELEMENT_NODE && name == "TrainingImageInfo") { + xmlNodePtr image_info_node = NULL; + + for (image_info_node = first_level_node->children; image_info_node; image_info_node = + image_info_node->next) { + name = std::string ((char *) image_info_node->name); + + if(name == "trainImg") { + //Read image_id + int id = std::atoi((char *) xmlGetProp(image_info_node, BAD_CAST "image_id")); + + vpImage<unsigned char> I; + std::string path((char *) image_info_node->children->content); + //Read path to the training images + if(vpIoTools::isAbsolutePathname(std::string(path))) { + vpImageIo::read(I, path); + } else { + vpImageIo::read(I, parent + path); + } + + m_mapOfImages[id + startImageId] = I; + } + } + } else if(first_level_node->type == XML_ELEMENT_NODE && name == "DescriptorsInfo") { + xmlNodePtr descriptors_info_node = NULL; + for (descriptors_info_node = first_level_node->children; descriptors_info_node; descriptors_info_node = + descriptors_info_node->next) { + if (descriptors_info_node->type == XML_ELEMENT_NODE) { + name = std::string ((char *) descriptors_info_node->name); + + if(name == "nrows") { + nRows = std::atoi((char *) descriptors_info_node->children->content); + } else if(name == "ncols") { + nCols = std::atoi((char *) descriptors_info_node->children->content); + } else if(name == "type") { + descriptorType = std::atoi((char *) descriptors_info_node->children->content); + } + } + } + + trainDescriptorsTmp = cv::Mat(nRows, nCols, descriptorType); + } else if (first_level_node->type == XML_ELEMENT_NODE && name == "DescriptorInfo") { + xmlNodePtr point_node = NULL; + double u = 0.0, v = 0.0, size = 0.0, angle = 0.0, response = 0.0; + int octave = 0, class_id = 0, image_id = 0; + double oX = 0.0, oY = 0.0, oZ = 0.0; + + std::stringstream ss; + + for (point_node = first_level_node->children; point_node; point_node = + point_node->next) { + if (point_node->type == XML_ELEMENT_NODE) { + name = std::string ((char *) point_node->name); + + //Read information about keypoints + if(name == "u") { + u = std::strtod((char *) point_node->children->content, &pEnd); + } else if(name == "v") { + v = std::strtod((char *) point_node->children->content, &pEnd); + } else if(name == "size") { + size = std::strtod((char *) point_node->children->content, &pEnd); + } else if(name == "angle") { + angle = std::strtod((char *) point_node->children->content, &pEnd); + } else if(name == "response") { + response = std::strtod((char *) point_node->children->content, &pEnd); + } else if(name == "octave") { + octave = std::atoi((char *) point_node->children->content); + } else if(name == "class_id") { + class_id = std::atoi((char *) point_node->children->content); + cv::KeyPoint keyPoint(cv::Point2f((float) u, (float) v), (float) size, + (float) angle, (float) response, octave, (class_id + startClassId)); + m_trainKeyPoints.push_back(keyPoint); + } else if(name == "image_id") { + image_id = std::atoi((char *) point_node->children->content); + if(image_id != -1) { + //No training images if image_id == -1 + m_mapOfImageId[m_trainKeyPoints.back().class_id] = image_id + startImageId; + } + } else if (name == "oX") { + oX = std::atof((char *) point_node->children->content); + } else if (name == "oY") { + oY = std::atof((char *) point_node->children->content); + } else if (name == "oZ") { + oZ = std::atof((char *) point_node->children->content); + m_trainPoints.push_back(cv::Point3f((float) oX, (float) oY, (float) oZ)); + } else if (name == "desc") { + xmlNodePtr descriptor_value_node = NULL; + j = 0; + + for (descriptor_value_node = point_node->children; + descriptor_value_node; descriptor_value_node = + descriptor_value_node->next) { + + if (descriptor_value_node->type == XML_ELEMENT_NODE) { + //Read descriptors values + std::string parseStr((char *) descriptor_value_node->children->content); + ss.clear(); + ss.str(parseStr); + + if(!ss.fail()) { + switch(descriptorType) { + case CV_8U: + { + //Parse the numeric value [0 ; 255] to an int + int parseValue; + ss >> parseValue; + trainDescriptorsTmp.at<unsigned char>(i, j) = (unsigned char) parseValue; + } + break; + + case CV_8S: + //Parse the numeric value [-128 ; 127] to an int + int parseValue; + ss >> parseValue; + trainDescriptorsTmp.at<char>(i, j) = (char) parseValue; + break; + + case CV_16U: + ss >> trainDescriptorsTmp.at<unsigned short int>(i, j); + break; + + case CV_16S: + ss >> trainDescriptorsTmp.at<short int>(i, j); + break; + + case CV_32S: + ss >> trainDescriptorsTmp.at<int>(i, j); + break; + + case CV_32F: + ss >> trainDescriptorsTmp.at<float>(i, j); + break; + + case CV_64F: + ss >> trainDescriptorsTmp.at<double>(i, j); + break; + + default: + ss >> trainDescriptorsTmp.at<float>(i, j); + break; + } + } else { + std::cerr << "Error when converting:" << ss.str() << std::endl; + } + + j++; + } + } + } + } + } + i++; + } + } + + if(!append || m_trainDescriptors.empty()) { + trainDescriptorsTmp.copyTo(m_trainDescriptors); + } else { + cv::vconcat(m_trainDescriptors, trainDescriptorsTmp, m_trainDescriptors); + } + + /*free the document */ + xmlFreeDoc(doc); + + /* + *Free the global variables that may + *have been allocated by the parser. + */ + xmlCleanupParser(); +#else + std::cout << "Error: libxml2 is required !" << std::endl; +#endif + } + + //Convert OpenCV type to ViSP type for compatibility + vpConvert::convertFromOpenCV(m_trainKeyPoints, referenceImagePointsList); + vpConvert::convertFromOpenCV(this->m_trainPoints, m_trainVpPoints); + + //Set _reference_computed to true as we load learning file + _reference_computed = true; +} + +/*! + Match keypoints based on distance between their descriptors. + + \param trainDescriptors : Train descriptors (or reference descriptors). + \param queryDescriptors : Query descriptors. + \param matches : Output list of matches. + \param elapsedTime : Elapsed time. + */ +void vpKeyPoint::match(const cv::Mat &trainDescriptors, const cv::Mat &queryDescriptors, + std::vector<cv::DMatch> &matches, double &elapsedTime) { + double t = vpTime::measureTimeMs(); + + if(m_useKnn) { + m_knnMatches.clear(); + m_matcher->knnMatch(queryDescriptors, trainDescriptors, m_knnMatches, 2); + matches.resize(m_knnMatches.size()); + std::transform(m_knnMatches.begin(), m_knnMatches.end(), matches.begin(), knnToDMatch); + } else { + matches.clear(); + m_matcher->match(queryDescriptors, trainDescriptors, matches); + } + elapsedTime = vpTime::measureTimeMs() - t; +} + +/*! + Match keypoints detected in the image with those built in the reference list. + + \param I : Input current image. + \return The number of matched keypoints. + */ +unsigned int vpKeyPoint::matchPoint(const vpImage<unsigned char> &I) { + return matchPoint(I, vpRect()); +} + +/*! + Match keypoints detected in a region of interest of the image with those + built in the reference list. + + \param I : Input image + \param iP : Coordinate of the top-left corner of the region of interest + \param height : Height of the region of interest + \param width : Width of the region of interest + \return The number of matched keypoints + */ +unsigned int vpKeyPoint::matchPoint(const vpImage<unsigned char> &I, + const vpImagePoint &iP, + const unsigned int height, const unsigned int width) { + return matchPoint(I, vpRect(iP, width, height)); +} + +/*! + Match keypoints detected in a region of interest of the image with those + built in the reference list. + + \param I : Input image + \param rectangle : Rectangle of the region of interest + \return The number of matched keypoints + */ +unsigned int vpKeyPoint::matchPoint(const vpImage<unsigned char> &I, + const vpRect& rectangle) { + if(m_trainDescriptors.empty()) { + std::cerr << "Reference is empty." << std::endl; + if(!_reference_computed) { + std::cerr << "Reference is not computed." << std::endl; + } + std::cerr << "Matching is not possible." << std::endl; + + return 0; + } + + if(m_useAffineDetection) { + std::vector<std::vector<cv::KeyPoint> > listOfQueryKeyPoints; + std::vector<cv::Mat> listOfQueryDescriptors; + + //Detect keypoints and extract descriptors on multiple images + detectExtractAffine(I, listOfQueryKeyPoints, listOfQueryDescriptors); + + //Flatten the different train lists + m_queryKeyPoints.clear(); + for(std::vector<std::vector<cv::KeyPoint> >::const_iterator it = listOfQueryKeyPoints.begin(); + it != listOfQueryKeyPoints.end(); ++it) { + m_queryKeyPoints.insert(m_queryKeyPoints.end(), it->begin(), it->end()); + } + + bool first = true; + for(std::vector<cv::Mat>::const_iterator it = listOfQueryDescriptors.begin(); it != listOfQueryDescriptors.end(); ++it) { + if(first) { + first = false; + it->copyTo(m_queryDescriptors); + } else { + m_queryDescriptors.push_back(*it); + } + } + } else { + detect(I, m_queryKeyPoints, m_detectionTime, rectangle); + extract(I, m_queryKeyPoints, m_queryDescriptors, m_extractionTime); + } + + match(m_trainDescriptors, m_queryDescriptors, m_matches, m_matchingTime); + + if(m_filterType != noFilterMatching) { + m_queryFilteredKeyPoints.clear(); + m_objectFilteredPoints.clear(); + m_filteredMatches.clear(); + + filterMatches(); + } else { + m_queryFilteredKeyPoints = m_queryKeyPoints; + m_objectFilteredPoints = m_trainPoints; + m_filteredMatches = m_matches; + + m_matchQueryToTrainKeyPoints.clear(); + for (size_t i = 0; i < m_matches.size(); i++) { + m_matchQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>( + m_queryKeyPoints[(size_t) m_matches[i].queryIdx], + m_trainKeyPoints[(size_t) m_matches[i].trainIdx])); + } + } + + //Convert OpenCV type to ViSP type for compatibility + vpConvert::convertFromOpenCV(m_queryFilteredKeyPoints, currentImagePointsList); + vpConvert::convertFromOpenCV(m_filteredMatches, matchedReferencePoints); + + return static_cast<unsigned int>(m_filteredMatches.size()); +} + +/*! + Match keypoints detected in the image with those built in the reference list and compute the pose. + + \param I : Input image + \param cam : Camera parameters + \param cMo : Homogeneous matrix between the object frame and the camera frame + \param error : Reprojection mean square error (in pixel) between the 2D points and the projection of the 3D points with + the estimated pose + \param elapsedTime : Time to detect, extract, match and compute the pose + \param func : Function pointer to filter the pose in Ransac pose estimation, if we want to eliminate + the poses which do not respect some criterion + \return True if the matching and the pose estimation are OK, false otherwise + */ +bool vpKeyPoint::matchPoint(const vpImage<unsigned char> &I, const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, + double &error, double &elapsedTime, bool (*func)(vpHomogeneousMatrix *)) { + //Check if we have training descriptors + if(m_trainDescriptors.empty()) { + std::cerr << "Reference is empty." << std::endl; + if(!_reference_computed) { + std::cerr << "Reference is not computed." << std::endl; + } + std::cerr << "Matching is not possible." << std::endl; + + return false; + } + + if(m_useAffineDetection) { + std::vector<std::vector<cv::KeyPoint> > listOfQueryKeyPoints; + std::vector<cv::Mat> listOfQueryDescriptors; + + //Detect keypoints and extract descriptors on multiple images + detectExtractAffine(I, listOfQueryKeyPoints, listOfQueryDescriptors); + + //Flatten the different train lists + m_queryKeyPoints.clear(); + for(std::vector<std::vector<cv::KeyPoint> >::const_iterator it = listOfQueryKeyPoints.begin(); + it != listOfQueryKeyPoints.end(); ++it) { + m_queryKeyPoints.insert(m_queryKeyPoints.end(), it->begin(), it->end()); + } + + bool first = true; + for(std::vector<cv::Mat>::const_iterator it = listOfQueryDescriptors.begin(); it != listOfQueryDescriptors.end(); ++it) { + if(first) { + first = false; + it->copyTo(m_queryDescriptors); + } else { + m_queryDescriptors.push_back(*it); + } + } + } else { + detect(I, m_queryKeyPoints, m_detectionTime); + extract(I, m_queryKeyPoints, m_queryDescriptors, m_extractionTime); + } + + match(m_trainDescriptors, m_queryDescriptors, m_matches, m_matchingTime); + + elapsedTime = m_detectionTime + m_extractionTime + m_matchingTime; + + if(m_filterType != noFilterMatching) { + m_queryFilteredKeyPoints.clear(); + m_objectFilteredPoints.clear(); + m_filteredMatches.clear(); + + filterMatches(); + } else { + m_queryFilteredKeyPoints = m_queryKeyPoints; + m_objectFilteredPoints = m_trainPoints; + m_filteredMatches = m_matches; + + m_matchQueryToTrainKeyPoints.clear(); + for (size_t i = 0; i < m_matches.size(); i++) { + m_matchQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>( + m_queryKeyPoints[(size_t) m_matches[i].queryIdx], + m_trainKeyPoints[(size_t) m_matches[i].trainIdx])); + } + } + + //Convert OpenCV type to ViSP type for compatibility + vpConvert::convertFromOpenCV(m_queryFilteredKeyPoints, currentImagePointsList); + vpConvert::convertFromOpenCV(m_filteredMatches, matchedReferencePoints); + + //error = std::numeric_limits<double>::max(); // create an error under Windows. To fix it we have to add #undef max + error = DBL_MAX; + m_ransacInliers.clear(); + m_ransacOutliers.clear(); + + if(m_useRansacVVS) { + std::vector<vpPoint> objectVpPoints(m_objectFilteredPoints.size()); + size_t cpt = 0; + //Create a list of vpPoint with 2D coordinates (current keypoint location) + 3D coordinates (world/object coordinates) + for(std::vector<cv::Point3f>::const_iterator it = m_objectFilteredPoints.begin(); it != m_objectFilteredPoints.end(); + ++it, cpt++) { + vpPoint pt; + pt.setWorldCoordinates(it->x, it->y, it->z); + + vpImagePoint imP(m_queryFilteredKeyPoints[cpt].pt.y, m_queryFilteredKeyPoints[cpt].pt.x); + + double x = 0.0, y = 0.0; + vpPixelMeterConversion::convertPoint(cam, imP, x, y); + pt.set_x(x); + pt.set_y(y); + + objectVpPoints[cpt] = pt; + } + + std::vector<vpPoint> inliers; + bool res = computePose(objectVpPoints, cMo, inliers, m_poseTime, func); + m_ransacInliers.resize(inliers.size()); + for(size_t i = 0; i < m_ransacInliers.size(); i++) { + vpMeterPixelConversion::convertPoint(cam, inliers[i].get_x(), inliers[i].get_y(), m_ransacInliers[i]); + } + + elapsedTime += m_poseTime; + + return res; + } else { + std::vector<cv::Point2f> imageFilteredPoints; + cv::KeyPoint::convert(m_queryFilteredKeyPoints, imageFilteredPoints); + std::vector<int> inlierIndex; + bool res = computePose(imageFilteredPoints, m_objectFilteredPoints, cam, cMo, inlierIndex, m_poseTime); + + std::map<int, bool> mapOfInlierIndex; + m_matchRansacKeyPointsToPoints.clear(); + m_matchRansacQueryToTrainKeyPoints.clear(); + for (std::vector<int>::const_iterator it = inlierIndex.begin(); it != inlierIndex.end(); ++it) { + m_matchRansacKeyPointsToPoints.push_back(std::pair<cv::KeyPoint, cv::Point3f>(m_queryFilteredKeyPoints[(size_t)(*it)], + m_objectFilteredPoints[(size_t)(*it)])); + m_matchRansacQueryToTrainKeyPoints.push_back(std::pair<cv::KeyPoint, cv::KeyPoint>(m_queryFilteredKeyPoints[(size_t)(*it)], + m_trainKeyPoints[(size_t)m_matches[(size_t)(*it)].trainIdx])); + mapOfInlierIndex[*it] = true; + } + + for(size_t i = 0; i < m_queryFilteredKeyPoints.size(); i++) { + if(mapOfInlierIndex.find((int) i) == mapOfInlierIndex.end()) { + m_ransacOutliers.push_back(vpImagePoint(m_queryFilteredKeyPoints[i].pt.y, m_queryFilteredKeyPoints[i].pt.x)); + } + } + + error = computePoseEstimationError(m_matchRansacKeyPointsToPoints, cam, cMo); + + m_ransacInliers.resize(m_matchRansacKeyPointsToPoints.size()); + std::transform(m_matchRansacKeyPointsToPoints.begin(), m_matchRansacKeyPointsToPoints.end(), m_ransacInliers.begin(), + matchRansacToVpImage); + + elapsedTime += m_poseTime; + + return res; + } +} + +/*! + Match keypoints detected in the image with those built in the reference list and return the bounding box and the center + of gravity. + + \param I : Input image + \param boundingBox : Bounding box that contains the good matches + \param centerOfGravity : Center of gravity computed from the location of the good matches (could differ of the center of + the bounding box) + \param isPlanarObject : If the object is planar, the homography matrix is estimated to eliminate outliers, otherwise + it is the fundamental matrix which is estimated + \param imPts1 : Pointer to the list of reference keypoints if not null + \param imPts2 : Pointer to the list of current keypoints if not null + \param meanDescriptorDistance : Pointer to the value of the average distance of the descriptors if not null + \param detectionScore : Pointer to the value of the detection score if not null + \return True if the object is present, false otherwise + */ +bool vpKeyPoint::matchPointAndDetect(const vpImage<unsigned char> &I, vpRect &boundingBox, vpImagePoint ¢erOfGravity, + const bool isPlanarObject, std::vector<vpImagePoint> *imPts1, std::vector<vpImagePoint> *imPts2, + double *meanDescriptorDistance, double *detectionScore) { + if(imPts1 != NULL && imPts2 != NULL) { + imPts1->clear(); + imPts2->clear(); + } + + matchPoint(I); + + double meanDescriptorDistanceTmp = 0.0; + for(std::vector<cv::DMatch>::const_iterator it = m_filteredMatches.begin(); it != m_filteredMatches.end(); ++it) { + meanDescriptorDistanceTmp += (double) it->distance; + } + + meanDescriptorDistanceTmp /= (double) m_filteredMatches.size(); + double score = (double) m_filteredMatches.size() / meanDescriptorDistanceTmp; + + if(meanDescriptorDistance != NULL) { + *meanDescriptorDistance = meanDescriptorDistanceTmp; + } + if(detectionScore != NULL) { + *detectionScore = score; + } + + if(m_filteredMatches.size() >= 4) { + //Training / Reference 2D points + std::vector<cv::Point2f> points1(m_filteredMatches.size()); + //Query / Current 2D points + std::vector<cv::Point2f> points2(m_filteredMatches.size()); + + for(size_t i = 0; i < m_filteredMatches.size(); i++) { + points1[i] = cv::Point2f(m_trainKeyPoints[(size_t)m_filteredMatches[i].trainIdx].pt); + points2[i] = cv::Point2f(m_queryFilteredKeyPoints[(size_t)m_filteredMatches[i].queryIdx].pt); + } + + std::vector<vpImagePoint> inliers; + if(isPlanarObject) { +#if (VISP_HAVE_OPENCV_VERSION < 0x030000) + cv::Mat homographyMatrix = cv::findHomography(points1, points2, CV_RANSAC); +#else + cv::Mat homographyMatrix = cv::findHomography(points1, points2, cv::RANSAC); +#endif + + for(size_t i = 0; i < m_filteredMatches.size(); i++ ) { + //Compute reprojection error + cv::Mat realPoint = cv::Mat(3, 1, CV_64F); + realPoint.at<double>(0,0) = points1[i].x; + realPoint.at<double>(1,0) = points1[i].y; + realPoint.at<double>(2,0) = 1.f; + + cv::Mat reprojectedPoint = homographyMatrix * realPoint; + double err_x = (reprojectedPoint.at<double>(0,0) / reprojectedPoint.at<double>(2,0)) - points2[i].x; + double err_y = (reprojectedPoint.at<double>(1,0) / reprojectedPoint.at<double>(2,0)) - points2[i].y; + double reprojectionError = std::sqrt(err_x*err_x + err_y*err_y); + + if(reprojectionError < 6.0) { + inliers.push_back(vpImagePoint((double) points2[i].y, (double) points2[i].x)); + if(imPts1 != NULL) { + imPts1->push_back(vpImagePoint((double) points1[i].y, (double) points1[i].x)); + } + + if(imPts2 != NULL) { + imPts2->push_back(vpImagePoint((double) points2[i].y, (double) points2[i].x)); + } + } + } + } else if(m_filteredMatches.size() >= 8) { + cv::Mat fundamentalInliers; + cv::Mat fundamentalMatrix = cv::findFundamentalMat(points1, points2, cv::FM_RANSAC, 3, 0.99, fundamentalInliers); + + for(size_t i = 0; i < (size_t) fundamentalInliers.rows; i++) { + if(fundamentalInliers.at<uchar>((int) i, 0)) { + inliers.push_back(vpImagePoint((double) points2[i].y, (double) points2[i].x)); + + if(imPts1 != NULL) { + imPts1->push_back(vpImagePoint((double) points1[i].y, (double) points1[i].x)); + } + + if(imPts2 != NULL) { + imPts2->push_back(vpImagePoint((double) points2[i].y, (double) points2[i].x)); + } + } + } + } + + if(!inliers.empty()) { + //Build a polygon with the list of inlier keypoints detected in the current image to get the bounding box + vpPolygon polygon(inliers); + boundingBox = polygon.getBoundingBox(); + + //Compute the center of gravity + double meanU = 0.0, meanV = 0.0; + for(std::vector<vpImagePoint>::const_iterator it = inliers.begin(); it != inliers.end(); ++it) { + meanU += it->get_u(); + meanV += it->get_v(); + } + + meanU /= (double) inliers.size(); + meanV /= (double) inliers.size(); + + centerOfGravity.set_u(meanU); + centerOfGravity.set_v(meanV); + } + } else { + //Too few matches + return false; + } + + if(m_detectionMethod == detectionThreshold) { + return meanDescriptorDistanceTmp < m_detectionThreshold; + } else { + return score > m_detectionScore; + } +} + +/*! + Match keypoints detected in the image with those built in the reference list, compute the pose and return also + the bounding box and the center of gravity. + + \param I : Input image + \param cam : Camera parameters + \param cMo : Homogeneous matrix between the object frame and the camera frame + \param error : Reprojection mean square error (in pixel) between the 2D points and the projection of the 3D points with + the estimated pose + \param elapsedTime : Time to detect, extract, match and compute the pose + \param boundingBox : Bounding box that contains the good matches + \param centerOfGravity : Center of gravity computed from the location of the good matches (could differ of the center of + the bounding box) + \param func : Function pointer to filter the pose in Ransac pose estimation, if we want to eliminate + the poses which do not respect some criterion + \return True if the matching and the pose estimation are OK, false otherwise. + */ +bool vpKeyPoint::matchPointAndDetect(const vpImage<unsigned char> &I, const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, + double &error, double &elapsedTime, vpRect &boundingBox, vpImagePoint ¢erOfGravity, + bool (*func)(vpHomogeneousMatrix *)) { + bool isMatchOk = matchPoint(I, cam, cMo, error, elapsedTime, func); + if(isMatchOk) { + //Use the pose estimated to project the model points in the image + vpPoint pt; + vpImagePoint imPt; + std::vector<vpImagePoint> modelImagePoints(m_trainVpPoints.size()); + size_t cpt = 0; + for(std::vector<vpPoint>::const_iterator it = m_trainVpPoints.begin(); it != m_trainVpPoints.end(); ++it, cpt++) { + pt = *it; + pt.project(cMo); + vpMeterPixelConversion::convertPoint(cam, pt.get_x(), pt.get_y(), imPt); + modelImagePoints[cpt] = imPt; + } + + //Build a polygon with the list of model image points to get the bounding box + vpPolygon polygon(modelImagePoints); + boundingBox = polygon.getBoundingBox(); + + //Compute the center of gravity of the current inlier keypoints + double meanU = 0.0, meanV = 0.0; + for(std::vector<vpImagePoint>::const_iterator it = m_ransacInliers.begin(); it != m_ransacInliers.end(); + ++it) { + meanU += it->get_u(); + meanV += it->get_v(); + } + + meanU /= (double) m_ransacInliers.size(); + meanV /= (double) m_ransacInliers.size(); + + centerOfGravity.set_u(meanU); + centerOfGravity.set_v(meanV); + } + + return isMatchOk; +} + +/*! + Apply a set of affine transormations to the image, detect keypoints and + reproject them into initial image coordinates. + See http://www.ipol.im/pub/algo/my_affine_sift/ for the details. + See https://github.com/Itseez/opencv/blob/master/samples/python2/asift.py for the Python implementation by Itseez + and Matt Sheckells for the current implementation in C++. + \param I : Input image + \param listOfKeypoints : List of detected keypoints in the multiple images after affine transformations + \param listOfDescriptors : Corresponding list of descriptors + \param listOfAffineI : Optional parameter, list of images after affine transformations + */ +void vpKeyPoint::detectExtractAffine(const vpImage<unsigned char> &I,std::vector<std::vector<cv::KeyPoint> >& listOfKeypoints, + std::vector<cv::Mat>& listOfDescriptors, std::vector<vpImage<unsigned char> > *listOfAffineI) { + cv::Mat img; + vpImageConvert::convert(I, img); + listOfKeypoints.clear(); + listOfDescriptors.clear(); + + for (int tl = 1; tl < 6; tl++) { + double t = pow(2, 0.5 * tl); + for (int phi = 0; phi < 180; phi += (int)(72.0 / t)) { + std::vector<cv::KeyPoint> keypoints; + cv::Mat descriptors; + + cv::Mat timg, mask, Ai; + img.copyTo(timg); + + affineSkew(t, phi, timg, mask, Ai); + + + if(listOfAffineI != NULL) { + cv::Mat img_disp; + bitwise_and(mask, timg, img_disp); + vpImage<unsigned char> tI; + vpImageConvert::convert(img_disp, tI); + listOfAffineI->push_back(tI); + } +#if 0 + cv::namedWindow( "Skew", cv::WINDOW_AUTOSIZE ); // Create a window for display. + cv::imshow( "Skew", img_disp ); + cv::waitKey(0); +#endif + + for(std::map<std::string, cv::Ptr<cv::FeatureDetector> >::const_iterator it = m_detectors.begin(); it != m_detectors.end(); ++it) { + std::vector<cv::KeyPoint> kp; + it->second->detect(timg, kp, mask); + keypoints.insert(keypoints.end(), kp.begin(), kp.end()); + } + + double elapsedTime; + extract(timg, keypoints, descriptors, elapsedTime); + + for(unsigned int i = 0; i < keypoints.size(); i++) { + cv::Point3f kpt(keypoints[i].pt.x, keypoints[i].pt.y, 1.f); + cv::Mat kpt_t = Ai * cv::Mat(kpt); + keypoints[i].pt.x = kpt_t.at<float>(0, 0); + keypoints[i].pt.y = kpt_t.at<float>(1, 0); + } + + listOfKeypoints.push_back(keypoints); + listOfDescriptors.push_back(descriptors); + } + } +} + +/*! + Save the learning data in a file in XML or binary mode. + + \param filename : Path of the save file + \param binaryMode : If true, the data are saved in binary mode, otherwise in XML mode + \param saveTrainingImages : If true, save also the training images on disk + */ +void vpKeyPoint::saveLearningData(const std::string &filename, bool binaryMode, const bool saveTrainingImages) { + std::string parent = vpIoTools::getParent(filename); + if(!parent.empty()) { + vpIoTools::makeDirectory(parent); + } + + std::map<int, std::string> mapOfImgPath; + if(saveTrainingImages) { + //Save the training image files in the same directory + int cpt = 0; + + for(std::map<int, vpImage<unsigned char> >::const_iterator it = m_mapOfImages.begin(); it != m_mapOfImages.end(); ++it, cpt++) { + char buffer[4]; + sprintf(buffer, "%03d", cpt); + std::stringstream ss; + ss << "train_image_" << buffer << ".jpg"; + std::string filename_ = ss.str(); + mapOfImgPath[it->first] = filename_; + vpImageIo::write(it->second, parent + (!parent.empty() ? "/" : "") + filename_); + } + } + + bool have3DInfo = m_trainPoints.size() > 0; + if(have3DInfo && m_trainPoints.size() != m_trainKeyPoints.size()) { + throw vpException(vpException::fatalError, "List of keypoints and list of 3D points have different size !"); + } + + if(binaryMode) { + std::ofstream file(filename.c_str(), std::ofstream::binary); + if(!file.is_open()) { + throw vpException(vpException::ioError, "Cannot create the file."); + } + + //Write info about training images + int nbImgs = (int) mapOfImgPath.size(); + file.write((char *)(&nbImgs), sizeof(nbImgs)); + + for(std::map<int, std::string>::const_iterator it = mapOfImgPath.begin(); it != mapOfImgPath.end(); ++it) { + //Write image_id + int id = it->first; + file.write((char *)(&id), sizeof(id)); + + //Write image path + std::string path = it->second; + int length = (int) path.length(); + file.write((char *)(&length), sizeof(length)); + + for(int cpt = 0; cpt < length; cpt++) { + file.write((char *) (&path[(size_t)cpt]), sizeof(path[(size_t)cpt])); + } + } + + //Write if we have 3D point information + int have3DInfoInt = have3DInfo ? 1 : 0; + file.write((char *)(&have3DInfoInt), sizeof(have3DInfoInt)); + + + int nRows = m_trainDescriptors.rows, + nCols = m_trainDescriptors.cols; + int descriptorType = m_trainDescriptors.type(); + + //Write the number of descriptors + file.write((char *)(&nRows), sizeof(nRows)); + + //Write the size of the descriptor + file.write((char *)(&nCols), sizeof(nCols)); + + //Write the type of the descriptor + file.write((char *)(&descriptorType), sizeof(descriptorType)); + + for (int i = 0; i < nRows; i++) { + unsigned int i_ = (unsigned int) i; + //Write u + float u = m_trainKeyPoints[i_].pt.x; + file.write((char *)(&u), sizeof(u)); + + //Write v + float v = m_trainKeyPoints[i_].pt.y; + file.write((char *)(&v), sizeof(v)); + + //Write size + float size = m_trainKeyPoints[i_].size; + file.write((char *)(&size), sizeof(size)); + + //Write angle + float angle = m_trainKeyPoints[i_].angle; + file.write((char *)(&angle), sizeof(angle)); + + //Write response + float response = m_trainKeyPoints[i_].response; + file.write((char *)(&response), sizeof(response)); + + //Write octave + int octave = m_trainKeyPoints[i_].octave; + file.write((char *)(&octave), sizeof(octave)); + + //Write class_id + int class_id = m_trainKeyPoints[i_].class_id; + file.write((char *)(&class_id), sizeof(class_id)); + + //Write image_id + int image_id = (saveTrainingImages && m_mapOfImageId.size() > 0) ? m_mapOfImageId[m_trainKeyPoints[i_].class_id] : -1; + file.write((char *)(&image_id), sizeof(image_id)); + + if(have3DInfo) { + float oX = m_trainPoints[i_].x, oY = m_trainPoints[i_].y, oZ = m_trainPoints[i_].z; + //Write oX + file.write((char *)(&oX), sizeof(oX)); + + //Write oY + file.write((char *)(&oY), sizeof(oY)); + + //Write oZ + file.write((char *)(&oZ), sizeof(oZ)); + } + + for (int j = 0; j < nCols; j++) { + //Write the descriptor value + switch(descriptorType) { + case CV_8U: + file.write((char *)(&m_trainDescriptors.at<unsigned char>(i, j)), sizeof(m_trainDescriptors.at<unsigned char>(i, j))); + break; + + case CV_8S: + file.write((char *)(&m_trainDescriptors.at<char>(i, j)), sizeof(m_trainDescriptors.at<char>(i, j))); + break; + + case CV_16U: + file.write((char *)(&m_trainDescriptors.at<unsigned short int>(i, j)), sizeof(m_trainDescriptors.at<unsigned short int>(i, j))); + break; + + case CV_16S: + file.write((char *)(&m_trainDescriptors.at<short int>(i, j)), sizeof(m_trainDescriptors.at<short int>(i, j))); + break; + + case CV_32S: + file.write((char *)(&m_trainDescriptors.at<int>(i, j)), sizeof(m_trainDescriptors.at<int>(i, j))); + break; + + case CV_32F: + file.write((char *)(&m_trainDescriptors.at<float>(i, j)), sizeof(m_trainDescriptors.at<float>(i, j))); + break; + + case CV_64F: + file.write((char *)(&m_trainDescriptors.at<double>(i, j)), sizeof(m_trainDescriptors.at<double>(i, j))); + break; + + default: + file.write((char *)(&m_trainDescriptors.at<float>(i, j)), sizeof(m_trainDescriptors.at<float>(i, j))); + break; + } + } + } + + + file.close(); + } else { +#ifdef VISP_HAVE_XML2 + xmlDocPtr doc = NULL; + xmlNodePtr root_node = NULL, image_node = NULL, image_info_node = NULL, descriptors_info_node = NULL, + descriptor_node = NULL, desc_node = NULL; + + /* + * this initialize the library and check potential ABI mismatches + * between the version it was compiled for and the actual shared + * library used. + */ + LIBXML_TEST_VERSION + + doc = xmlNewDoc(BAD_CAST "1.0"); + if (doc == NULL) { + throw vpException(vpException::ioError, "Error with file " + filename); + } + + root_node = xmlNewNode(NULL, BAD_CAST "LearningData"); + xmlDocSetRootElement(doc, root_node); + + std::stringstream ss; + + //Write the training images info + image_node = xmlNewChild(root_node, NULL, BAD_CAST "TrainingImageInfo", NULL); + + for(std::map<int, std::string>::const_iterator it = mapOfImgPath.begin(); it != mapOfImgPath.end(); ++it) { + image_info_node = xmlNewChild(image_node, NULL, BAD_CAST "trainImg", + BAD_CAST it->second.c_str()); + ss.str(""); + ss << it->first; + xmlNewProp(image_info_node, BAD_CAST "image_id", BAD_CAST ss.str().c_str()); + } + + //Write information about descriptors + descriptors_info_node = xmlNewChild(root_node, NULL, BAD_CAST "DescriptorsInfo", NULL); + + int nRows = m_trainDescriptors.rows, + nCols = m_trainDescriptors.cols; + int descriptorType = m_trainDescriptors.type(); + + //Write the number of rows + ss.str(""); + ss << nRows; + xmlNewChild(descriptors_info_node, NULL, BAD_CAST "nrows", BAD_CAST ss.str().c_str()); + + //Write the number of cols + ss.str(""); + ss << nCols; + xmlNewChild(descriptors_info_node, NULL, BAD_CAST "ncols", BAD_CAST ss.str().c_str()); + + //Write the descriptors type + ss.str(""); + ss << descriptorType; + xmlNewChild(descriptors_info_node, NULL, BAD_CAST "type", BAD_CAST ss.str().c_str()); + + for (int i = 0; i < nRows; i++) { + unsigned int i_ = (unsigned int) i; + descriptor_node = xmlNewChild(root_node, NULL, BAD_CAST "DescriptorInfo", + NULL); + + ss.str(""); + ss << m_trainKeyPoints[i_].pt.x; + xmlNewChild(descriptor_node, NULL, BAD_CAST "u", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].pt.y; + xmlNewChild(descriptor_node, NULL, BAD_CAST "v", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].size; + xmlNewChild(descriptor_node, NULL, BAD_CAST "size", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].angle; + xmlNewChild(descriptor_node, NULL, BAD_CAST "angle", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].response; + xmlNewChild(descriptor_node, NULL, BAD_CAST "response", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].octave; + xmlNewChild(descriptor_node, NULL, BAD_CAST "octave", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainKeyPoints[i_].class_id; + xmlNewChild(descriptor_node, NULL, BAD_CAST "class_id", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << ((saveTrainingImages && m_mapOfImageId.size() > 0) ? m_mapOfImageId[m_trainKeyPoints[i_].class_id] : -1); + xmlNewChild(descriptor_node, NULL, BAD_CAST "image_id", + BAD_CAST ss.str().c_str()); + + if (have3DInfo) { + ss.str(""); + ss << m_trainPoints[i_].x; + xmlNewChild(descriptor_node, NULL, BAD_CAST "oX", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainPoints[i_].y; + xmlNewChild(descriptor_node, NULL, BAD_CAST "oY", + BAD_CAST ss.str().c_str()); + + ss.str(""); + ss << m_trainPoints[i_].z; + xmlNewChild(descriptor_node, NULL, BAD_CAST "oZ", + BAD_CAST ss.str().c_str()); + } + + desc_node = xmlNewChild(descriptor_node, NULL, BAD_CAST "desc", NULL); + + for (int j = 0; j < nCols; j++) { + ss.str(""); + + switch(descriptorType) { + case CV_8U: + { + //Promote an unsigned char to an int + //val_tmp holds the numeric value that will be written + //We save the value in numeric form otherwise libxml2 will not be able to parse + //A better solution could be possible + int val_tmp = m_trainDescriptors.at<unsigned char>(i, j); + ss << val_tmp; + } + break; + + case CV_8S: + { + //Promote a char to an int + //val_tmp holds the numeric value that will be written + //We save the value in numeric form otherwise libxml2 will not be able to parse + //A better solution could be possible + int val_tmp = m_trainDescriptors.at<char>(i, j); + ss << val_tmp; + } + break; + + case CV_16U: + ss << m_trainDescriptors.at<unsigned short int>(i, j); + break; + + case CV_16S: + ss << m_trainDescriptors.at<short int>(i, j); + break; + + case CV_32S: + ss << m_trainDescriptors.at<int>(i, j); + break; + + case CV_32F: + ss << m_trainDescriptors.at<float>(i, j); + break; + + case CV_64F: + ss << m_trainDescriptors.at<double>(i, j); + break; + + default: + ss << m_trainDescriptors.at<float>(i, j); + break; + } + xmlNewChild(desc_node, NULL, BAD_CAST "val", + BAD_CAST ss.str().c_str()); + } + } + + xmlSaveFormatFileEnc(filename.c_str(), doc, "UTF-8", 1); + + /*free the document */ + xmlFreeDoc(doc); + + /* + *Free the global variables that may + *have been allocated by the parser. + */ + xmlCleanupParser(); +#else + std::cerr << "Error: libxml2 is required !" << std::endl; +#endif + } +} + +#endif diff --git a/src/key-point/vpKeyPoint.h b/src/key-point/vpKeyPoint.h new file mode 100644 index 0000000000000000000000000000000000000000..7d1e70f5c44834b725391fd9f0a08988cd1a4824 --- /dev/null +++ b/src/key-point/vpKeyPoint.h @@ -0,0 +1,861 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Key point functionalities. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ +#ifndef __vpKeyPoint_h__ +#define __vpKeyPoint_h__ + +#include <algorithm> // std::transform +#include <vector> // std::vector +#include <stdlib.h> // srand, rand +#include <time.h> // time +#include <fstream> // std::ofstream +#include <numeric> // std::accumulate +#include <float.h> // DBL_MAX +#include <map> // std::map +#include <limits> + +#include <visp/vpConfig.h> +#include <visp/vpBasicKeyPoint.h> +#include <visp/vpImageConvert.h> +#include <visp/vpPoint.h> +#include <visp/vpDisplay.h> +#include <visp/vpPlane.h> +#include <visp/vpPixelMeterConversion.h> +#include <visp/vpMbEdgeTracker.h> +#include <visp/vpIoTools.h> +#include <visp/vpPose.h> +#include <visp/vpImageIo.h> +#include <visp/vpPolygon.h> +#include <visp/vpXmlConfigParserKeyPoint.h> +#include <visp/vpConvert.h> + +// Require at least OpenCV >= 2.1.1 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + +#include <opencv2/features2d/features2d.hpp> +#include <opencv2/calib3d/calib3d.hpp> + +#if defined(VISP_HAVE_OPENCV_XFEATURES2D) // OpenCV >= 3.0.0 +# include <opencv2/xfeatures2d.hpp> +#elif defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION >= 0x020400) && (VISP_HAVE_OPENCV_VERSION < 0x030000) +# include <opencv2/nonfree/nonfree.hpp> +#endif + +#ifdef VISP_HAVE_XML2 +# include <libxml/xmlwriter.h> +#endif + +/*! + \class vpKeyPoint + + \brief Class that allows keypoints detection (and descriptors extraction) and matching thanks to OpenCV library. + This class permits to use different types of detectors, extractors and matchers easily. + So, the classical SIFT and SURF keypoints could be used, as well as ORB, FAST, (etc.) keypoints, + depending of the version of OpenCV you use. + + \note Due to some patents, SIFT and SURF are packaged in an external module called nonfree module + in OpenCV version before 3.0.0 and in xfeatures2d from 3.0.0. You have to check you have the + corresponding module to use SIFT and SURF. + + The goal of this class is to provide a tool to match reference keypoints from a + reference image (or train keypoints in OpenCV terminology) and detected keypoints from a current image (or query + keypoints in OpenCV terminology). + + If you supply the corresponding 3D coordinates corresponding to the 2D coordinates of the reference keypoints, + you can also estimate the pose of the object by matching a set of detected keypoints in the current image with + the reference keypoints. + + + If you use this class, the first thing you have to do is to build + the reference keypoints by detecting keypoints in a reference image which contains the + object to detect. Then you match keypoints detected in a current image with those detected in a reference image + by calling matchPoint() methods. + You can access to the lists of matched points thanks to the + methods getMatchedPointsInReferenceImage() and + getMatchedPointsInCurrentImage(). These two methods return a list of + matched points. The nth element of the first list is matched with + the nth element of the second list. + To provide easy compatibility with OpenCV terminology, getTrainKeyPoints() give you access to the list + of keypoints detected in train images (or reference images) and getQueryKeyPoints() give you access to the list + of keypoints detected in a query image (or current image). + The method getMatches() give you access to a list of cv::DMatch with the correspondence between the index of the + train keypoints and the index of the query keypoints. + + The following small example shows how to use the class to do the matching between current and reference keypoints. + + \code +#include <visp/vpConfig.h> +#include <visp/vpImage.h> +#include <visp/vpKeyPoint.h> + +int main() +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020300) + vpImage<unsigned char> Irefrence; + vpImage<unsigned char> Icurrent; + + vpKeyPoint::vpFilterMatchingType filterType = vpKeyPoint::ratioDistanceThreshold; + vpKeyPoint keypoint("ORB", "ORB", "BruteForce-Hamming", filterType); + + // First grab the reference image Irefrence + // Add your code to load the reference image in Ireference + + // Build the reference ORB points. + keypoint.buildReference(Irefrence); + + // Then grab another image which represents the current image Icurrent + + // Match points between the reference points and the ORB points computed in the current image. + keypoint.matchPoint(Icurrent); + + // Display the matched points + keypoint.display(Irefrence, Icurrent); +#endif + + return (0); +} + \endcode + + It is also possible to build the reference keypoints in a region of interest (ROI) of an image + and find keypoints to match in only a part of the current image. The small following example shows how to do this: + + \code +#include <visp/vpConfig.h> +#include <visp/vpImage.h> +#include <visp/vpDisplay.h> +#include <visp/vpKeyPoint.h> + +int main() +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020300) + vpImage<unsigned char> Ireference; + vpImage<unsigned char> Icurrent; + + vpKeyPoint::vpFilterMatchingType filterType = vpKeyPoint::ratioDistanceThreshold; + vpKeyPoint keypoint("ORB", "ORB", "BruteForce-Hamming", filterType); + + //First grab the reference image Irefrence + //Add your code to load the reference image in Ireference + + //Select a part of the image by clincking on two points which define a rectangle + vpImagePoint corners[2]; + for (int i=0 ; i < 2 ; i++) + { + vpDisplay::getClick(Ireference, corners[i]); + } + + //Build the reference ORB points. + int nbrRef; + unsigned int height, width; + height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); + width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); + nbrRef = keypoint.buildReference(Ireference, corners[0], height, width); + + //Then grab another image which represents the current image Icurrent + + //Select a part of the image by clincking on two points which define a rectangle + for (int i=0 ; i < 2 ; i++) + { + vpDisplay::getClick(Icurrent, corners[i]); + } + + //Match points between the reference points and the ORB points computed in the current image. + int nbrMatched; + height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); + width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); + nbrMatched = keypoint.matchPoint(Icurrent, corners[0], height, width); + + //Display the matched points + keypoint.display(Ireference, Icurrent); +#endif + + return(0); +} + \endcode + + This class is also described in \ref tutorial-matching. +*/ +class VISP_EXPORT vpKeyPoint : public vpBasicKeyPoint { + +public: + + /*! Predefined filtering method identifier. */ + typedef enum { + constantFactorDistanceThreshold, /*!< Keep all the points below a constant factor threshold. */ + stdDistanceThreshold, /*!< Keep all the points below a minimal distance + the standard deviation. */ + ratioDistanceThreshold, /*!< Keep all the points enough discriminated (the ratio distance between the two best matches is below the threshold). */ + stdAndRatioDistanceThreshold, /*!< Keep all the points which fall with the two conditions above. */ + noFilterMatching /*!< No filtering. */ + } vpFilterMatchingType; + + /*! Predefined detection method identifier. */ + typedef enum { + detectionThreshold, /*!< The object is present if the average of the descriptor distances is below the threshold. */ + detectionScore /*!< Same condition than the previous but with a formula taking into account the number of matches, + the object is present if the score is above the threshold. */ + } vpDetectionMethodType; + + + vpKeyPoint(const std::string &detectorName="ORB", const std::string &extractorName="ORB", + const std::string &matcherName="BruteForce-Hamming", const vpFilterMatchingType &filterType=ratioDistanceThreshold); + vpKeyPoint(const std::vector<std::string> &detectorNames, const std::vector<std::string> &extractorNames, + const std::string &matcherName="BruteForce", const vpFilterMatchingType &filterType=ratioDistanceThreshold); + + unsigned int buildReference(const vpImage<unsigned char> &I); + unsigned int buildReference(const vpImage<unsigned char> &I, + const vpImagePoint &iP, const unsigned int height, const unsigned int width); + unsigned int buildReference(const vpImage<unsigned char> &I, const vpRect& rectangle); + + void buildReference(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &trainKeyPoint, + std::vector<cv::Point3f> &points3f, bool append=false); + void buildReference(const vpImage<unsigned char> &I, const std::vector<cv::KeyPoint> &trainKeyPoint, + const cv::Mat &trainDescriptors, const std::vector<cv::Point3f> &points3f, bool append=false); + + static void compute3D(const cv::KeyPoint &candidate, const std::vector<vpPoint> &roi, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, cv::Point3f &point); + + static void compute3D(const vpImagePoint &candidate, const std::vector<vpPoint> &roi, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, vpPoint &point); + + static void compute3DForPointsInPolygons(const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, + std::vector<cv::KeyPoint> &candidate, std::vector<vpPolygon> &polygons, std::vector<std::vector<vpPoint> > &roisPt, + std::vector<cv::Point3f> &points, cv::Mat *descriptors=NULL); + + static void compute3DForPointsInPolygons(const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, + std::vector<vpImagePoint> &candidate, std::vector<vpPolygon> &polygons, std::vector<std::vector<vpPoint> > &roisPt, + std::vector<vpPoint> &points, cv::Mat *descriptors=NULL); + + bool computePose(const std::vector<cv::Point2f> &imagePoints, const std::vector<cv::Point3f> &objectPoints, + const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, std::vector<int> &inlierIndex, double &elapsedTime, + bool (*func)(vpHomogeneousMatrix *)=NULL); + + bool computePose(const std::vector<vpPoint> &objectVpPoints, vpHomogeneousMatrix &cMo, + std::vector<vpPoint> &inliers, double &elapsedTime, bool (*func)(vpHomogeneousMatrix *)=NULL); + + void createImageMatching(vpImage<unsigned char> &IRef, vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching); + void createImageMatching(vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching); + + void detect(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &keyPoints, double &elapsedTime, + const vpRect& rectangle=vpRect()); + void detect(const cv::Mat &matImg, std::vector<cv::KeyPoint> &keyPoints, double &elapsedTime, + const cv::Mat &mask=cv::Mat()); + + void detectExtractAffine(const vpImage<unsigned char> &I, std::vector<std::vector<cv::KeyPoint> >& listOfKeypoints, + std::vector<cv::Mat>& listOfDescriptors, + std::vector<vpImage<unsigned char> > *listOfAffineI=NULL); + + void display(const vpImage<unsigned char> &IRef, const vpImage<unsigned char> &ICurrent, unsigned int size=3); + void display(const vpImage<unsigned char> &ICurrent, unsigned int size=3, const vpColor &color=vpColor::green); + + void displayMatching(const vpImage<unsigned char> &IRef, vpImage<unsigned char> &IMatching, + unsigned int crossSize, unsigned int lineThickness=1, + const vpColor &color=vpColor::green); + void displayMatching(const vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching, + const std::vector<vpImagePoint> &ransacInliers=std::vector<vpImagePoint>(), unsigned int crossSize=3, + unsigned int lineThickness=1); + + void extract(const vpImage<unsigned char> &I, std::vector<cv::KeyPoint> &keyPoints, cv::Mat &descriptors, double &elapsedTime); + void extract(const cv::Mat &matImg, std::vector<cv::KeyPoint> &keyPoints, cv::Mat &descriptors, double &elapsedTime); + + /*! + Get the covariance matrix when estimating the pose using the Virtual Visual Servoing approach. + + \warning The compute covariance flag has to be true if you want to compute the covariance matrix. + + \sa setCovarianceComputation + */ + inline vpMatrix getCovarianceMatrix() const { + if(!m_computeCovariance) { + std::cout << "Warning : The covariance matrix has not been computed. See setCovarianceComputation() to do it." << std::endl; + return vpMatrix(); + } + + if(m_computeCovariance && !m_useRansacVVS) { + std::cout << "Warning : The covariance matrix can only be computed with a Virtual Visual Servoing approach." << std::endl + << "Use setUseRansacVVS(true) to choose to use a pose estimation method based on a Virtual Visual Servoing approach." + << std::endl; + return vpMatrix(); + } + + return m_covarianceMatrix; + } + + /*! + Get the elapsed time to compute the keypoint detection. + + \return The elapsed time. + */ + inline double getDetectionTime() const { + return m_detectionTime; + } + + /*! + Get the elapsed time to compute the keypoint extraction. + + \return The elapsed time. + */ + inline double getExtractionTime() const { + return m_extractionTime; + } + + /*! + Get the elapsed time to compute the matching. + + \return The elapsed time. + */ + inline double getMatchingTime() const { + return m_matchingTime; + } + + /*! + Get the list of matches (correspondences between the indexes of the detected keypoints and the train keypoints). + + \return The list of matches. + */ + inline std::vector<cv::DMatch> getMatches() const { + return m_filteredMatches; + } + + /*! + Get the list of pairs with the correspondence between the matched query and train keypoints. + + \return The list of pairs with the correspondence between the matched query and train keypoints. + */ + inline std::vector<std::pair<cv::KeyPoint, cv::KeyPoint> > getMatchQueryToTrainKeyPoints() const { + return m_matchQueryToTrainKeyPoints; + } + + /*! + Get the number of train images. + + \return The number of train images. + */ + inline unsigned int getNbImages() const { + return static_cast<unsigned int>(m_mapOfImages.size()); + } + + void getObjectPoints(std::vector<cv::Point3f> &objectPoints) const; + void getObjectPoints(std::vector<vpPoint> &objectPoints) const; + + /*! + Get the elapsed time to compute the pose. + + \return The elapsed time. + */ + inline double getPoseTime() const { + return m_poseTime; + } + + /*! + Get the descriptors matrix for the query keypoints. + + \return Matrix with descriptors values at each row for each query keypoints. + */ + inline cv::Mat getQueryDescriptors() const { + return m_queryDescriptors; + } + + void getQueryKeyPoints(std::vector<cv::KeyPoint> &keyPoints) const; + void getQueryKeyPoints(std::vector<vpImagePoint> &keyPoints) const; + + /*! + Get the list of Ransac inliers. + + \return The list of Ransac inliers. + */ + inline std::vector<vpImagePoint> getRansacInliers() const { + return m_ransacInliers; + } + + /*! + Get the list of Ransac outliers. + + \return The list of Ransac outliers. + */ + inline std::vector<vpImagePoint> getRansacOutliers() const { + return m_ransacOutliers; + } + + /*! + Get the train descriptors matrix. + + \return : Matrix with descriptors values at each row for each train keypoints (or reference keypoints). + */ + inline cv::Mat getTrainDescriptors() const { + return m_trainDescriptors; + } + + void getTrainKeyPoints(std::vector<cv::KeyPoint> &keyPoints) const; + void getTrainKeyPoints(std::vector<vpImagePoint> &keyPoints) const; + + void getTrainPoints(std::vector<cv::Point3f> &points) const; + void getTrainPoints(std::vector<vpPoint> &points) const; + + void initMatcher(const std::string &matcherName); + + void insertImageMatching(const vpImage<unsigned char> &IRef, const vpImage<unsigned char> &ICurrent, + vpImage<unsigned char> &IMatching); + void insertImageMatching(const vpImage<unsigned char> &ICurrent, vpImage<unsigned char> &IMatching); + +#ifdef VISP_HAVE_XML2 + void loadConfigFile(const std::string &configFile); +#endif + + void loadLearningData(const std::string &filename, const bool binaryMode=false, const bool append=false); + + void match(const cv::Mat &trainDescriptors, const cv::Mat &queryDescriptors, + std::vector<cv::DMatch> &matches, double &elapsedTime); + + unsigned int matchPoint(const vpImage<unsigned char> &I); + unsigned int matchPoint(const vpImage<unsigned char> &I, const vpImagePoint &iP, + const unsigned int height, const unsigned int width); + unsigned int matchPoint(const vpImage<unsigned char> &I, const vpRect& rectangle); + + bool matchPoint(const vpImage<unsigned char> &I, const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, + double &error, double &elapsedTime, bool (*func)(vpHomogeneousMatrix *)=NULL); + + bool matchPointAndDetect(const vpImage<unsigned char> &I, vpRect &boundingBox, vpImagePoint ¢erOfGravity, + const bool isPlanarObject=true, std::vector<vpImagePoint> *imPts1=NULL, + std::vector<vpImagePoint> *imPts2=NULL, double *meanDescriptorDistance=NULL, + double *detectionScore=NULL); + + bool matchPointAndDetect(const vpImage<unsigned char> &I, const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, + double &error, double &elapsedTime, vpRect &boundingBox, vpImagePoint ¢erOfGravity, + bool (*func)(vpHomogeneousMatrix *)=NULL); + + void saveLearningData(const std::string &filename, const bool binaryMode=false, const bool saveTrainingImages=true); + + /*! + Set if the covariance matrix has to be computed in the Virtual Visual Servoing approach. + + \param flag : True if the covariance has to be computed, false otherwise. + */ + inline void setCovarianceComputation(const bool& flag) { + m_computeCovariance = flag; + if(!m_useRansacVVS) { + std::cout << "Warning : The covariance matrix can only be computed with a Virtual Visual Servoing approach." << std::endl + << "Use setUseRansacVVS(true) to choose to use a pose estimation method based on a Virtual " + "Visual Servoing approach." << std::endl; + } + } + + /*! + Set the method to decide if the object is present or not. + + \param method : Detection method (detectionThreshold or detectionScore). + */ + inline void setDetectionMethod(const vpDetectionMethodType &method) { + m_detectionMethod = method; + } + + /*! + Set and initialize a detector denominated by his name \p detectorName. + + \param detectorName : Name of the detector. + */ + inline void setDetector(const std::string &detectorName) { + m_detectorNames.clear(); + m_detectorNames.push_back(detectorName); + m_detectors.clear(); + initDetector(detectorName); + } + + /*! + Template function to set to a \p parameterName a value for a specific detector named by his \p detectorName. + + \param detectorName : Name of the detector + \param parameterName : Name of the parameter + \param value : Value to set + */ + template<typename T1, typename T2, typename T3> inline void setDetectorParameter(const T1 detectorName, + const T2 parameterName, const T3 value) { + if(m_detectors.find(detectorName) != m_detectors.end()) { + m_detectors[detectorName]->set(parameterName, value); + } + } + + /*! + Set and initialize a list of detectors denominated by their names \p detectorNames. + + \param detectorNames : List of detector names. + */ + inline void setDetectors(const std::vector<std::string> &detectorNames) { + m_detectorNames.clear(); + m_detectors.clear(); + m_detectorNames = detectorNames; + initDetectors(m_detectorNames); + } + + /*! + Set and initialize an extractor denominated by his name \p extractorName. + + \param extractorName : Name of the extractor. + */ + inline void setExtractor(const std::string &extractorName) { + m_extractorNames.clear(); + m_extractorNames.push_back(extractorName); + m_extractors.clear(); + initExtractor(extractorName); + } + + /*! + Template function to set to a \p parameterName a value for a specific extractor named by his \p extractorName. + + \param extractorName : Name of the extractor + \param parameterName : Name of the parameter + \param value : Value to set + */ + template<typename T1, typename T2, typename T3> inline void setExtractorParameter(const T1 extractorName, + const T2 parameterName, const T3 value) { + if(m_extractors.find(extractorName) != m_extractors.end()) { + m_extractors[extractorName]->set(parameterName, value); + } + } + + /*! + Set and initialize a list of extractors denominated by their names \p extractorNames. + + \param extractorNames : List of extractor names. + */ + inline void setExtractors(const std::vector<std::string> &extractorNames) { + m_extractorNames.clear(); + m_extractorNames = extractorNames; + m_extractors.clear(); + initExtractors(m_extractorNames); + } + + /*! + Set and initialize a matcher denominated by his name \p matcherName. + The different matchers are: + - BruteForce (it uses L2 distance) + - BruteForce-L1 + - BruteForce-Hamming + - BruteForce-Hamming(2) + - FlannBased + + L1 and L2 norms are preferable choices for SIFT and SURF descriptors, NORM_HAMMING should be used with ORB, + BRISK and BRIEF, NORM_HAMMING2 should be used with ORB when WTA_K==3 or 4. + + \param matcherName : Name of the matcher. + */ + inline void setMatcher(const std::string &matcherName) { + m_matcherName = matcherName; + initMatcher(m_matcherName); + } + + /*! + Set the filtering method to eliminate false matching. + The different methods are: + - constantFactorDistanceThreshold (keep matches whose the descriptor distance is below dist_min * factor) + - stdDistanceThreshold (keep matches whose the descriptor distance is below dist_min + standard_deviation) + - ratioDistanceThreshold (keep matches enough discriminated: the ratio distance between the 2 best matches is below the threshold) + - stdAndRatioDistanceThreshold (keep matches that agree with at least one of the two conditions) + - noFilterMatching + + \param filterType : Type of the filtering method + */ + inline void setFilterMatchingType(const vpFilterMatchingType &filterType) { + m_filterType = filterType; + + //Use k-nearest neighbors (knn) to retrieve the two best matches for a keypoint + //So this is useful only for ratioDistanceThreshold method + if(filterType == ratioDistanceThreshold || filterType == stdAndRatioDistanceThreshold) { + m_useKnn = true; + } else { + m_useKnn = false; + } + } + + /*! + Set the factor value for the filtering method: constantFactorDistanceThreshold. + + \param factor : Factor value + */ + inline void setMatchingFactorThreshold(const double factor) { + if(factor > 0.0) { + m_matchingFactorThreshold = factor; + } else { + throw vpException(vpException::badValue, "The factor must be positive."); + } + } + + /*! + Set the ratio value for the filtering method: ratioDistanceThreshold. + + \param ratio : Ratio value (]0 ; 1]) + */ + inline void setMatchingRatioThreshold(const double ratio) { + if(ratio > 0.0 && (ratio < 1.0 || std::fabs(ratio - 1.0) < std::numeric_limits<double>::epsilon())) { + m_matchingRatioThreshold = ratio; + } else { + throw vpException(vpException::badValue, "The ratio must be in the interval ]0 ; 1]."); + } + } + + /*! + Set the percentage value for defining the cardinality of the consensus group. + + \param percentage : Percentage value (]0 ; 100]) + */ + inline void setRansacConsensusPercentage(const double percentage) { + if(percentage > 0.0 && (percentage < 100.0 || std::fabs(percentage - 100.0) < std::numeric_limits<double>::epsilon())) { + m_ransacConsensusPercentage = percentage; + } else { + throw vpException(vpException::badValue, "The percentage must be in the interval ]0 ; 100]."); + } + } + + /*! + Set the maximum number of iterations for the Ransac pose estimation method. + + \param nbIter : Maximum number of iterations for the Ransac + */ + inline void setRansacIteration(const int nbIter) { + if(nbIter > 0) { + m_nbRansacIterations = nbIter; + } else { + throw vpException(vpException::badValue, "The number of iterations must be greater than zero."); + } + } + + /*! + Set the maximum reprojection error (in pixel) to determine if a point is an inlier or not. + + \param reprojectionError : Maximum reprojection error in pixel (used by OpenCV function) + */ + inline void setRansacReprojectionError(const double reprojectionError) { + if(reprojectionError > 0.0) { + m_ransacReprojectionError = reprojectionError; + } else { + throw vpException(vpException::badValue, "The Ransac reprojection threshold must be positive as we deal with distance."); + } + } + + /*! + Set the minimum number of inlier for the Ransac pose estimation method. + + \param minCount : Minimum number of inlier for the consensus + */ + inline void setRansacMinInlierCount(const int minCount) { + if(minCount > 0) { + m_nbRansacMinInlierCount = minCount; + } else { + throw vpException(vpException::badValue, "The minimum number of inliers must be greater than zero."); + } + } + + /*! + Set the maximum error (in meter) to determine if a point is an inlier or not. + + \param threshold : Maximum error in meter for ViSP function + */ + inline void setRansacThreshold(const double threshold) { + if(threshold > 0.0) { + m_ransacThreshold = threshold; + } else { + throw vpException(vpException::badValue, "The Ransac threshold must be positive as we deal with distance."); + } + } + + /*! + Set if multiple affine transformations must be used to detect and extract keypoints. + + \param useAffine : True to use multiple affine transformations, false otherwise + */ + inline void setUseAffineDetection(const bool useAffine) { + m_useAffineDetection = useAffine; + } + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020400) + /*! + Set if cross check method must be used to eliminate some false matches with a brute-force matching method. + + \param useCrossCheck : True to use cross check, false otherwise + */ + inline void setUseBruteForceCrossCheck(const bool useCrossCheck) { + //Only available with BruteForce and with k=1 (i.e not used with a ratioDistanceThreshold method) + if(m_matcher != NULL && !m_useKnn && m_matcherName == "BruteForce") { + m_matcher->set("crossCheck", useCrossCheck); + } + } +#endif + + /*! + Set the flag to choose between a percentage value of inliers for the cardinality of the consensus group + or a minimum number. + + \param usePercentage : True to a percentage ratio of inliers, otherwise use a specified number of inliers + */ + inline void setUseRansacConsensusPercentage(const bool usePercentage) { + m_useConsensusPercentage = usePercentage; + } + + /*! + Set the flag to choose between the OpenCV or ViSP Ransac pose estimation function. + + \param ransacVVS : True to use ViSP function, otherwise use OpenCV function + */ + inline void setUseRansacVVS(const bool ransacVVS) { + m_useRansacVVS = ransacVVS; + } + +private: + //! If true, compute covariance matrix if the user select the pose estimation method using ViSP + bool m_computeCovariance; + //! Covariance matrix + vpMatrix m_covarianceMatrix; + //! Current id associated to the training image used for the learning. + int m_currentImageId; + //! Method (based on descriptor distances) to decide if the object is present or not. + vpDetectionMethodType m_detectionMethod; + //! Detection score to decide if the object is present or not. + double m_detectionScore; + //! Detection threshold based on average of descriptor distances to decide if the object is present or not. + double m_detectionThreshold; + //! Elapsed time to detect keypoints. + double m_detectionTime; + //! List of detector names. + std::vector<std::string> m_detectorNames; + //! Map of smart reference-counting pointers (similar to shared_ptr in Boost) detectors, + // with a key based upon the detector name. + std::map<std::string, cv::Ptr<cv::FeatureDetector> > m_detectors; + //! Elapsed time to extract descriptors for the detected keypoints. + double m_extractionTime; + //! List of extractor name. + std::vector<std::string> m_extractorNames; + //! Map of smart reference-counting pointers (similar to shared_ptr in Boost) extractors, + // with a key based upon the extractor name. + std::map<std::string, cv::Ptr<cv::DescriptorExtractor> > m_extractors; + //! List of filtered matches between the detected and the trained keypoints. + std::vector<cv::DMatch> m_filteredMatches; + //! Chosen method of filtering to eliminate false matching. + vpFilterMatchingType m_filterType; + //! List of k-nearest neighbors for each detected keypoints (if the method chosen is based upon on knn). + std::vector<std::vector<cv::DMatch> > m_knnMatches; + //! Map of image id to know to which training image is related a training keypoints. + std::map<int, int> m_mapOfImageId; + //! Map of images to have access to the image buffer according to his image id. + std::map<int, vpImage<unsigned char> > m_mapOfImages; + //! Smart reference-counting pointer (similar to shared_ptr in Boost) of descriptor matcher (e.g. BruteForce or FlannBased). + cv::Ptr<cv::DescriptorMatcher> m_matcher; + //! Name of the matcher. + std::string m_matcherName; + //! List of matches between the detected and the trained keypoints. + std::vector<cv::DMatch> m_matches; + //! Factor value for the filtering method: constantFactorDistanceThreshold. + double m_matchingFactorThreshold; + //! Ratio value for the filtering method: ratioDistanceThreshold. + double m_matchingRatioThreshold; + //! Elapsed time to do the matching. + double m_matchingTime; + //! List of pairs between the matched query and train keypoints. + std::vector<std::pair<cv::KeyPoint, cv::KeyPoint> > m_matchQueryToTrainKeyPoints; + //! List of pairs between the keypoint and the 3D point after the Ransac. + std::vector<std::pair<cv::KeyPoint, cv::Point3f> > m_matchRansacKeyPointsToPoints; + //! List of pairs between the matched query and train keypoints after the Ransac. + std::vector<std::pair<cv::KeyPoint, cv::KeyPoint> > m_matchRansacQueryToTrainKeyPoints; + //! Maximum number of iterations for the Ransac method. + int m_nbRansacIterations; + //! Minimum number of inliers for the Ransac method. + int m_nbRansacMinInlierCount; + //! List of 3D points (in the object frame) filtered after the matching to compute the pose. + std::vector<cv::Point3f> m_objectFilteredPoints; + //! Elapsed time to compute the pose. + double m_poseTime; + /*! Matrix of descriptors (each row contains the descriptors values for each keypoints + detected in the current image). */ + cv::Mat m_queryDescriptors; + //! List of detected keypoints filtered after the matching. + std::vector<cv::KeyPoint> m_queryFilteredKeyPoints; + //! List of keypoints detected in the current image. + std::vector<cv::KeyPoint> m_queryKeyPoints; + //! Percentage value to determine the number of inliers for the Ransac method. + double m_ransacConsensusPercentage; + //! List of inliers. + std::vector<vpImagePoint> m_ransacInliers; + //! List of outliers. + std::vector<vpImagePoint> m_ransacOutliers; + //! Maximum reprojection error (in pixel for the OpenCV method) to decide if a point is an inlier or not. + double m_ransacReprojectionError; + //! Maximum error (in meter for the ViSP method) to decide if a point is an inlier or not. + double m_ransacThreshold; + //! Matrix of descriptors (each row contains the descriptors values for each keypoints + //detected in the train images). + cv::Mat m_trainDescriptors; + //! List of keypoints detected in the train images. + std::vector<cv::KeyPoint> m_trainKeyPoints; + //! List of 3D points (in the object frame) corresponding to the train keypoints. + std::vector<cv::Point3f> m_trainPoints; + //! List of 3D points in vpPoint format (in the object frame) corresponding to the train keypoints. + std::vector<vpPoint> m_trainVpPoints; + //! If true, use multiple affine transformations to cober the 6 affine parameters + bool m_useAffineDetection; + //! If true, some false matches will be eliminate by keeping only pairs (i,j) such that for i-th + //! query descriptor the j-th descriptor in the matcher’s collection is the nearest and vice versa. + bool m_useBruteForceCrossCheck; + //! Flag set if a percentage value is used to determine the number of inliers for the Ransac method. + bool m_useConsensusPercentage; + //! Flag set if a knn matching method must be used. + bool m_useKnn; + //! Flag set if a Ransac VVS pose estimation must be used. + bool m_useRansacVVS; + + + void affineSkew(double tilt, double phi, cv::Mat& img, cv::Mat& mask, cv::Mat& Ai); + + double computePoseEstimationError(const std::vector<std::pair<cv::KeyPoint, cv::Point3f> > &matchKeyPoints, + const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo_est); + + void filterMatches(); + + void init(); + void initDetector(const std::string &detectorNames); + void initDetectors(const std::vector<std::string> &detectorNames); + + void initExtractor(const std::string &extractorName); + void initExtractors(const std::vector<std::string> &extractorNames); + +//TODO: Try to implement a pyramidal feature detection +//#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) +// void pyramidFeatureDetection(cv::Ptr<cv::FeatureDetector> &detector, const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask, +// const int maxLevel=2); +// void runByPixelsMask(std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask); +//#endif +}; + +#endif +#endif diff --git a/src/key-point/vpKeyPointSurf.cpp b/src/key-point/vpKeyPointSurf.cpp index 126fed3b4711c5c28f297f20a9cce4930fa9c155..f75051d8405ea016caa5a50afc0784af646f952b 100644 --- a/src/key-point/vpKeyPointSurf.cpp +++ b/src/key-point/vpKeyPointSurf.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyPointSurf.cpp 4182 2013-03-27 13:20:58Z fspindle $ + * $Id: vpKeyPointSurf.cpp 5202 2015-01-24 09:29:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ #include <visp/vpKeyPointSurf.h> #if defined (VISP_HAVE_OPENCV_NONFREE) -#if VISP_HAVE_OPENCV_VERSION >= 0x010100 // Require opencv >= 1.1.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x010100) && VISP_HAVE_OPENCV_VERSION < 0x030000// Require opencv >= 1.1.0 < 3.0.0 #include <visp/vpImageConvert.h> #include <visp/vpImageTools.h> @@ -56,9 +56,21 @@ #include <vector> #include <list> +double compareSURFDescriptors( const float* d1, const float* d2, double best, int length ); +int naiveNearestNeighbor( const float *vec, int laplacian, + const CvSeq *model_keypoints, + const CvSeq *model_descriptors ); +int naiveNearestNeighbor( const float *vec, + const CvSeq *ref_keypoints, + const CvSeq *ref_descriptors ); +void findPairs( const CvSeq* objectKeypoints, + const CvSeq* objectDescriptors, + const CvSeq* imageKeypoints, + const CvSeq* imageDescriptors, + std::vector<int>& ptpairs ); + // Compare two surf descriptors. -double compareSURFDescriptors( const float* d1, const float* d2, - double best, int length ) +double compareSURFDescriptors( const float* d1, const float* d2, double best, int length ) { double total_cost = 0; int i; @@ -79,8 +91,8 @@ double compareSURFDescriptors( const float* d1, const float* d2, //Find for a point candidate the most similar point in the reference point list. int naiveNearestNeighbor( const float *vec, int laplacian, - const CvSeq *model_keypoints, - const CvSeq *model_descriptors ) + const CvSeq *model_keypoints, + const CvSeq *model_descriptors ) { int length = (int)(model_descriptors->elem_size/(int)sizeof(float)); int i, neighbor = -1; @@ -115,8 +127,8 @@ int naiveNearestNeighbor( const float *vec, int laplacian, //Find for a point candidate the most similar point in the reference point list. int naiveNearestNeighbor( const float *vec, - const CvSeq *ref_keypoints, - const CvSeq *ref_descriptors ) + const CvSeq *ref_keypoints, + const CvSeq *ref_descriptors ) { int length = (int)(ref_descriptors->elem_size/(int)sizeof(float)); int i, neighbor = -1; @@ -149,10 +161,10 @@ int naiveNearestNeighbor( const float *vec, //Find all the matched points void findPairs( const CvSeq* objectKeypoints, - const CvSeq* objectDescriptors, - const CvSeq* imageKeypoints, - const CvSeq* imageDescriptors, - std::vector<int>& ptpairs ) + const CvSeq* objectDescriptors, + const CvSeq* imageKeypoints, + const CvSeq* imageDescriptors, + std::vector<int>& ptpairs ) { int i; CvSeqReader reader, kreader; @@ -187,18 +199,12 @@ void findPairs( const CvSeq* objectKeypoints, type to extended. */ -vpKeyPointSurf::vpKeyPointSurf():vpBasicKeyPoint() +vpKeyPointSurf::vpKeyPointSurf() + : vpBasicKeyPoint(), + storage(NULL), params(), storage_cur(NULL), image_keypoints(NULL), image_descriptors(NULL), + ref_keypoints(NULL), ref_descriptors(NULL), hessianThreshold(500), descriptorType(extendedDescriptor) { - descriptorType = extendedDescriptor; - hessianThreshold = 500; init(); - - image_keypoints = NULL; - image_descriptors = NULL; - ref_keypoints = NULL; - ref_descriptors = NULL; - - storage_cur = NULL; } /*! diff --git a/src/key-point/vpKeyPointSurf.h b/src/key-point/vpKeyPointSurf.h index 4002854ca5f0d66f07c767c712ddf552d5d16fdb..4b7035eb0fa6e7404ed162562696f5c3dff739b3 100644 --- a/src/key-point/vpKeyPointSurf.h +++ b/src/key-point/vpKeyPointSurf.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyPointSurf.h 4201 2013-04-08 08:20:47Z fspindle $ + * $Id: vpKeyPointSurf.h 5205 2015-01-26 08:56:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,10 +58,9 @@ #include <list> #include <vector> -#if defined (VISP_HAVE_OPENCV_NONFREE) +#if defined (VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000) - -#if (VISP_HAVE_OPENCV_VERSION >= 0x020400) // Require opencv >= 2.4.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020400) // Require opencv >= 1.1.0 < 3.0.0 # include <opencv2/features2d/features2d.hpp> # include <opencv2/legacy/compat.hpp> # include <opencv2/nonfree/nonfree.hpp> @@ -76,14 +75,17 @@ \class vpKeyPointSurf \brief Class that implements the SURF key points and technics thanks - to the OpenCV library. + to OpenCV library. + + \deprecated This class is deprecated with OpenCV 3.0.0 or more recent. + You should rather use vpKeyPoint class that is more generic. The goal of this class is to provide a tool to match points from a model and points belonging to an image in which the model appears. The coordinates of the different reference points and matched points are given in pixel thanks to the class vpImagePoint. In this documentation we do not explain the SURF technics. So if you want to - learn more about it you can refere to the following article : + learn more about it you can refer to the following article : Herbert Bay, Tinne Tuytelaars and Luc Van Gool "SURF: Speeded Up Robust Features", Proceedings of the 9th European Conference on Computer Vision, Springer LNCS volume 3951, part 1, pp 404--417, @@ -105,31 +107,29 @@ #include <visp/vpImage.h> #include <visp/vpKeyPointSurf.h> -#if VISP_HAVE_OPENCV_VERSION >= 0x010100 // Surf key-points only available since OpenCV-1.1.0 int main() { +#if defined (VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000) vpImage<unsigned char> Ireference; vpImage<unsigned char> Icurrent; vpKeyPointSurf surf; - //First grab the reference image Ireference + // First grab the reference image Ireference - //Build the reference SURF points. + // Build the reference SURF points. surf.buildReference(Ireference); - //Then grab another image which represents the current image Icurrent + // Then grab another image which represents the current image Icurrent - //Match points between the reference points and the SURF points computed in the current image. + // Match points between the reference points and the SURF points computed in the current image. surf.matchPoint(Icurrent); - //Display the matched points + // Display the matched points surf.display(Ireference, Icurrent); return (0); -} -#else -int main() {} #endif +} \endcode It is also possible to create the reference thanks to only a part of the @@ -142,9 +142,9 @@ int main() {} #include <visp/vpDisplay.h> #include <visp/vpKeyPointSurf.h> -#if VISP_HAVE_OPENCV_VERSION >= 0x010100 // Surf key-points only available since OpenCV-1.1.0 int main() { +#if defined (VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000) vpImage<unsigned char> Ireference; vpImage<unsigned char> Icurrent; vpKeyPointSurf surf; @@ -183,11 +183,11 @@ int main() surf.display(Ireference, Icurrent); return(0); -} -#else -int main() {} #endif +} \endcode + + This class is also described in \ref tutorial-matching. */ class VISP_EXPORT vpKeyPointSurf : public vpBasicKeyPoint @@ -239,10 +239,10 @@ class VISP_EXPORT vpKeyPointSurf : public vpBasicKeyPoint Computer Vision, Springer LNCS volume 3951, part 1, pp 404--417, 2006. - \param hessianThreshold : Desired hessian threshold value. + \param hessian_threshold : Desired hessian threshold value. */ - void setHessianThreshold (double hessianThreshold) { - this->hessianThreshold = hessianThreshold; + void setHessianThreshold (double hessian_threshold) { + this->hessianThreshold = hessian_threshold; params = cvSURFParams(this->hessianThreshold, this->descriptorType); } ; @@ -250,10 +250,10 @@ class VISP_EXPORT vpKeyPointSurf : public vpBasicKeyPoint Sets the type of descriptors to use. - \param descriptorType : Type of descriptor to use. + \param descriptor_type : Type of descriptor to use. */ - void setDescriptorType (vpDescriptorType descriptorType) { - this->descriptorType = descriptorType; + void setDescriptorType (vpDescriptorType descriptor_type) { + this->descriptorType = descriptor_type; params = cvSURFParams(this->hessianThreshold, this->descriptorType); } ; diff --git a/src/key-point/vpPlanarObjectDetector.cpp b/src/key-point/vpPlanarObjectDetector.cpp index ddbdaf03e975b9e03300f6a0830d8a614c5024c9..818d12394e5f9b7ba4992b699c23a3acabad48f5 100755 --- a/src/key-point/vpPlanarObjectDetector.cpp +++ b/src/key-point/vpPlanarObjectDetector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlanarObjectDetector.cpp 4182 2013-03-27 13:20:58Z fspindle $ + * $Id: vpPlanarObjectDetector.cpp 4976 2014-11-18 10:17:38Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ #include <visp/vpPlanarObjectDetector.h> -#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) // Require opencv >= 2.0.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000) // Require opencv >= 2.0.0 and < 3.0.0 #include <visp/vpImageConvert.h> #include <visp/vpException.h> @@ -60,13 +60,9 @@ */ vpPlanarObjectDetector::vpPlanarObjectDetector() + : fern(), homography(), H(), dst_corners(), isCorrect(false), ref_corners(), modelROI(), currentImagePoints(), + refImagePoints(), minNbMatching(10) { - isCorrect = false; - dst_corners.resize(0); - ref_corners.resize(0); - currentImagePoints.resize(0); - refImagePoints.resize(0); - minNbMatching = 10; } /*! @@ -78,19 +74,18 @@ vpPlanarObjectDetector::vpPlanarObjectDetector() */ vpPlanarObjectDetector::vpPlanarObjectDetector(const std::string& _dataFile, const std::string& _objectName) + : fern(), homography(), H(), dst_corners(), isCorrect(false), ref_corners(), modelROI(), currentImagePoints(), + refImagePoints(), minNbMatching(10) { - isCorrect = false; load(_dataFile, _objectName); } /*! - initialise stuff - + Initialise stuff. For the moment does nothing. */ void vpPlanarObjectDetector::init() { - } /*! diff --git a/src/key-point/vpPlanarObjectDetector.h b/src/key-point/vpPlanarObjectDetector.h index 2613dcf8168a25ea50c31da2cc84b3e1dfeaeb7a..ad772537a77655f5639a466b9ff5e581564f2022 100755 --- a/src/key-point/vpPlanarObjectDetector.h +++ b/src/key-point/vpPlanarObjectDetector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlanarObjectDetector.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPlanarObjectDetector.h 5203 2015-01-24 09:33:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,7 +44,7 @@ #include <visp/vpConfig.h> -#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) // Require opencv >= 2.0.0 +#if (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000) // Require opencv >= 2.0.0 and < 3.0.0 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1 # include <opencv2/imgproc/imgproc.hpp> @@ -69,6 +69,8 @@ \class vpPlanarObjectDetector \ingroup PlanarSurfaceDetector \brief Class used to detect a planar surface. + + \deprecated This class is deprecated with OpenCV 3.0.0 or more recent. This class allows to learn and recognise a surface in an image based on the Fern Classifier or any other point of interest matching class. diff --git a/src/key-point/vpXmlConfigParserKeyPoint.cpp b/src/key-point/vpXmlConfigParserKeyPoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ee1811923cb93cff0f0c313a5357f78c760f3f2 --- /dev/null +++ b/src/key-point/vpXmlConfigParserKeyPoint.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * XML parser to load configuration for vpKeyPoint class. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ + +/*! + \file vpXmlConfigParserKeyPoint.cpp + \brief Definition of the vpXmlConfigParserKeyPoint class member functions. + Class vpXmlConfigParserKeyPoint permits to load configuration defined in a XML file for vpKeyPoint class. + +*/ + +#include <iostream> + +#include <visp/vpXmlConfigParserKeyPoint.h> + +#ifdef VISP_HAVE_XML2 + +vpXmlConfigParserKeyPoint::vpXmlConfigParserKeyPoint() : m_detectorName("SIFT"), m_extractorName("SIFT"), m_matcherName("BruteForce"), +m_matchingFactorThreshold(2.0), m_matchingMethod(stdDistanceThreshold), m_matchingRatioThreshold(0.85), m_nbRansacIterations(200), +m_nbRansacMinInlierCount(100), m_ransacConsensusPercentage(20.0), m_ransacReprojectionError(6.0), m_ransacThreshold(0.001), +m_useRansacConsensusPercentage(false), m_useRansacVVS(false) +{ + init(); +} + +/*! + Initialize the nodeMap for the node parsing. +*/ +void +vpXmlConfigParserKeyPoint::init() +{ + setMainTag("conf"); + + nodeMap["conf"] = conf; + nodeMap["detector"] = detector; + nodeMap["extractor"] = extractor; + nodeMap["matcher"] = matcher; + nodeMap["name"] = name; + nodeMap["matching_method"] = matching_method; + nodeMap["constantFactorDistanceThreshold"] = constant_factor_distance_threshold; + nodeMap["stdDistanceThreshold"] = std_distance_threshold; + nodeMap["ratioDistanceThreshold"] = ratio_distance_threshold; + nodeMap["stdAndRatioDistanceThreshold"] = std_and_ratio_distance_threshold; + nodeMap["noFilterMatching"] = no_filter_matching; + nodeMap["matchingFactorThreshold"] = matching_factor_threshold; + nodeMap["matchingRatioThreshold"] = matching_ratio_threshold; + nodeMap["ransac"] = ransac; + nodeMap["useRansacVVS"] = use_ransac_vvs; + nodeMap["useRansacConsensusPercentage"] = use_ransac_consensus_percentage; + nodeMap["nbRansacIterations"] = nb_ransac_iterations; + nodeMap["ransacReprojectionError"] = ransac_reprojection_error; + nodeMap["nbRansacMinInlierCount"] = nb_ransac_min_inlier_count; + nodeMap["ransacThreshold"] = ransac_threshold; + nodeMap["ransacConsensusPercentage"] = ransac_consensus_percentage; +} + +/*! + Parse an XML file to load configuration for vpKeyPoint class. + \param filename : filename of the XML file to parse. +*/ +void +vpXmlConfigParserKeyPoint::parse(const std::string &filename) +{ + vpXmlParser::parse(filename); +} + +/*! + Read the parameters of the class from the file given by its document pointer + and by its root node. + + \param doc : Document to parse. + \param node : Root node. +*/ +void +vpXmlConfigParserKeyPoint::readMainClass(xmlDocPtr doc, xmlNodePtr node) +{ + bool detector_node = false; + bool extractor_node = false; + bool matcher_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case detector: + this->read_detector(doc, dataNode); + detector_node = true; + break; + + case extractor: + this->read_extractor(doc, dataNode); + extractor_node = true; + break; + + case matcher: + this->read_matcher(doc, dataNode); + matcher_node = true; + break; + + case ransac: + this->read_ransac(doc, dataNode); + break; + + default: + break; + } + } + } + } + + if(!detector_node) { + std::cout << "detector: name: "<< m_detectorName << " (default)" << std::endl; + } + + if(!extractor_node) { + std::cout << "extractor: name: "<< m_extractorName << " (default)" << std::endl; + } + + if(!matcher_node) { + std::cout << "matcher: name: "<< m_matcherName << " (default)" << std::endl; + } +} + +/*! + Parse detector tag part. + + \param doc : Document to parse. + \param node : Detector node. +*/ +void +vpXmlConfigParserKeyPoint::read_detector(xmlDocPtr doc, xmlNodePtr node) +{ + bool detector_name_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE) { + std::map<std::string, int>::iterator iter_data = this->nodeMap.find((char*) dataNode->name); + if (iter_data != nodeMap.end()) { + switch(iter_data->second) { + case name: + m_detectorName = xmlReadStringChild(doc, dataNode); + detector_name_node = true; + break; + + default: + break; + } + } + } + } + + if(!detector_name_node) + std::cout << "detector : Name : "<< m_detectorName << " (default)" << std::endl; + else + std::cout << "detector : Name : "<< m_detectorName << std::endl; +} + +/*! + Parse extractor tag part. + + \param doc : Document to parse. + \param node : Extractor node. +*/ +void +vpXmlConfigParserKeyPoint::read_extractor(xmlDocPtr doc, xmlNodePtr node) +{ + bool extractor_name_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE) { + std::map<std::string, int>::iterator iter_data = this->nodeMap.find((char*) dataNode->name); + if (iter_data != nodeMap.end()) { + switch(iter_data->second) { + case name: + m_extractorName = xmlReadStringChild(doc, dataNode); + extractor_name_node = true; + break; + + default: + break; + } + } + } + } + + if(!extractor_name_node) + std::cout << "extractor : Name : "<< m_extractorName << " (default)" << std::endl; + else + std::cout << "extractor : Name : "<< m_extractorName << std::endl; +} + +/*! + Parse matcher tag part. + + \param doc : Document to parse. + \param node : Matcher node. +*/ +void +vpXmlConfigParserKeyPoint::read_matcher(xmlDocPtr doc, xmlNodePtr node) +{ + bool matcher_name_node = false; + bool matching_method_node = false; + std::string matchingMethodName = "stdDistanceThreshold"; + bool matching_factor_threshold_node = false; + bool matching_ratio_threshold_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE) { + std::map<std::string, int>::iterator iter_data = this->nodeMap.find((char*) dataNode->name); + if(iter_data != nodeMap.end()) { + switch(iter_data->second) { + case name: + m_matcherName = xmlReadStringChild(doc, dataNode); + matcher_name_node = true; + break; + + case matching_method: + { + matchingMethodName = xmlReadStringChild(doc, dataNode); + + std::map<std::string, int>::iterator iter_data2 = nodeMap.find(matchingMethodName); + if(iter_data2 != nodeMap.end()) { + matching_method_node = true; + switch(iter_data2->second) { + case constant_factor_distance_threshold: + m_matchingMethod = constantFactorDistanceThreshold; + break; + + case std_distance_threshold: + m_matchingMethod = stdDistanceThreshold; + break; + + case ratio_distance_threshold: + m_matchingMethod = ratioDistanceThreshold; + break; + + case std_and_ratio_distance_threshold: + m_matchingMethod = stdAndRatioDistanceThreshold; + break; + + case no_filter_matching: + m_matchingMethod = noFilterMatching; + break; + + default: + matching_method_node = false; + break; + } + } + break; + } + + case matching_factor_threshold: + m_matchingFactorThreshold = xmlReadDoubleChild(doc, dataNode); + matching_factor_threshold_node = true; + break; + + case matching_ratio_threshold: + m_matchingRatioThreshold = xmlReadDoubleChild(doc, dataNode); + matching_ratio_threshold_node = true; + break; + + default: + break; + } + } + } + } + + if(!matcher_name_node) + std::cout << "matcher : Name : "<< m_matcherName << " (default)" << std::endl; + else + std::cout << "matcher : Name : "<< m_matcherName <<std::endl; + + if(!matching_method_node) + std::cout << "matcher : Filter method : "<< matchingMethodName << " (default)" << std::endl; + else + std::cout << "matcher : Filter method : "<< matchingMethodName << std::endl; + + if(!matching_factor_threshold_node) + std::cout << "matcher : matching factor threshold : "<< m_matchingFactorThreshold << " (default)" << std::endl; + else + std::cout << "matcher : matching factor threshold : "<< m_matchingFactorThreshold << std::endl; + + if(!matching_ratio_threshold_node) + std::cout << "matcher : matching ratio threshold : "<< m_matchingRatioThreshold << " (default)" << std::endl; + else + std::cout << "matcher : matching ratio threshold : "<< m_matchingRatioThreshold << std::endl; +} + +/*! + Parse ransac tag part. + + \param doc : Document to parse. + \param node : Ransac node. +*/ +void +vpXmlConfigParserKeyPoint::read_ransac(xmlDocPtr doc, xmlNodePtr node) +{ + bool use_ransac_vvs_node = false; + bool use_ransac_consensus_percentage_node = false; + bool nb_ransac_iterations_node = false; + bool ransac_reprojection_error_node = false; + bool nb_ransac_min_inlier_count_node = false; + bool ransac_threshold_node = false; + bool ransac_consensus_percentage_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE) { + std::map<std::string, int>::iterator iter_data = this->nodeMap.find((char*) dataNode->name); + if(iter_data != nodeMap.end()) { + switch(iter_data->second) { + case use_ransac_vvs: + m_useRansacVVS = xmlReadIntChild(doc, dataNode) != 0; + use_ransac_vvs_node = true; + break; + + case use_ransac_consensus_percentage: + m_useRansacConsensusPercentage = xmlReadIntChild(doc, dataNode) != 0; + use_ransac_consensus_percentage_node = true; + break; + + case nb_ransac_iterations: + m_nbRansacIterations = xmlReadIntChild(doc, dataNode); + nb_ransac_iterations_node = true; + break; + + case ransac_reprojection_error: + m_ransacReprojectionError = xmlReadDoubleChild(doc, dataNode); + ransac_reprojection_error_node = true; + break; + + case nb_ransac_min_inlier_count: + m_nbRansacMinInlierCount = xmlReadIntChild(doc, dataNode); + nb_ransac_min_inlier_count_node = true; + break; + + case ransac_threshold: + m_ransacThreshold = xmlReadDoubleChild(doc, dataNode); + ransac_threshold_node = true; + break; + + case ransac_consensus_percentage: + m_ransacConsensusPercentage = xmlReadDoubleChild(doc, dataNode); + ransac_consensus_percentage_node = true; + break; + + default: + break; + } + } + } + } + + if(!use_ransac_vvs_node) + std::cout << "ransac: use ransac vvs pose estimation: "<< m_useRansacVVS << " (default)" << std::endl; + else + std::cout << "ransac: use ransac vvs pose estimation: "<< m_useRansacVVS <<std::endl; + + if(!use_ransac_consensus_percentage_node) + std::cout << "ransac: use consensus percentage: "<< m_useRansacConsensusPercentage << " (default)" << std::endl; + else + std::cout << "ransac: use consensus percentage: "<< m_useRansacConsensusPercentage <<std::endl; + + if(!nb_ransac_iterations_node) + std::cout << "ransac: nb ransac iterations: "<< m_nbRansacIterations << " (default)" << std::endl; + else + std::cout << "ransac: nb ransac iterations: "<< m_nbRansacIterations <<std::endl; + + if(!ransac_reprojection_error_node) + std::cout << "ransac: ransac reprojection error in pixel (for OpenCV function): "<< m_ransacReprojectionError << " (default)" << std::endl; + else + std::cout << "ransac: ransac reprojection error in pixel (for OpenCV function): "<< m_ransacReprojectionError <<std::endl; + + if(!nb_ransac_min_inlier_count_node) + std::cout << "ransac: nb ransac min inlier count: "<< m_nbRansacMinInlierCount << " (default)" << std::endl; + else + std::cout << "ransac: nb ransac min inlier count: "<< m_nbRansacMinInlierCount <<std::endl; + + if(!ransac_threshold_node) + std::cout << "ransac: ransac threshold in meter (for ViSP function): "<< m_ransacThreshold << " (default)" << std::endl; + else + std::cout << "ransac: ransac threshold in meter (for ViSP function): "<< m_ransacThreshold <<std::endl; + + if(!ransac_consensus_percentage_node) + std::cout << "ransac: consensus percentage: "<< m_ransacConsensusPercentage << " (default)" << std::endl; + else + std::cout << "ransac: consensus percentage: "<< m_ransacConsensusPercentage <<std::endl; +} + +#endif //VISP_HAVE_XML2 diff --git a/src/key-point/vpXmlConfigParserKeyPoint.h b/src/key-point/vpXmlConfigParserKeyPoint.h new file mode 100644 index 0000000000000000000000000000000000000000..3d6e8b588b71460d407f659507ea4bf17a551748 --- /dev/null +++ b/src/key-point/vpXmlConfigParserKeyPoint.h @@ -0,0 +1,269 @@ +/**************************************************************************** + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * XML parser to load configuration for vpKeyPoint class. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ + +/*! + \file vpXmlConfigParserKeyPoint.cpp + \brief Definition of the vpXmlConfigParserKeyPoint class member functions. + Class vpXmlConfigParserKeyPoint allows to load configuration defined in a XML file for vpKeyPoint class. + +*/ + +#ifndef __vpXmlConfigParserKeyPoint_h__ +#define __vpXmlConfigParserKeyPoint_h__ + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_XML2 + +#include <iostream> +#include <stdlib.h> + +#include <libxml/xmlmemory.h> // Functions of libxml. + +#include <visp/vpXmlParser.h> + + +class VISP_EXPORT vpXmlConfigParserKeyPoint: public vpXmlParser +{ + +public: + /*! Predefined xml node identifier. */ + typedef enum { + conf, /*!< Identifier associated to the root tag. */ + detector, /*!< Identifier associated to the detector tag. */ + extractor, /*!< Identifier associated to the extractor tag. */ + matcher, /*!< Identifier associated to the matcher tag. */ + name, /*!< Identifier associated to the name tag. */ + matching_method, /*!< Identifier associated to the matching_method tag. */ + constant_factor_distance_threshold, /*!< Identifier associated to the constant_factor_distance_threshold tag. */ + std_distance_threshold, /*!< Identifier associated to the std_distance_threshold tag. */ + ratio_distance_threshold, /*!< Identifier associated to the ratio_distance_threshold tag. */ + std_and_ratio_distance_threshold, /*!< Identifier associated to the std_and_ratio_distance_threshold tag. */ + no_filter_matching, /*!< Identifier associated to the no_filter_matching tag. */ + matching_factor_threshold, /*!< Identifier associated to the matching_factor_threshold tag. */ + matching_ratio_threshold, /*!< Identifier associated to the matching_ratio_threshold tag. */ + ransac, /*!< Identifier associated to the ransac tag. */ + use_ransac_vvs, /*!< Identifier associated to the use_ransac_vvs tag. */ + use_ransac_consensus_percentage, /*!< Identifier associated to the use_ransac_consensus_percentage tag. */ + nb_ransac_iterations, /*!< Identifier associated to the nb_ransac_iterations tag. */ + ransac_reprojection_error, /*!< Identifier associated to the ransac_reprojection_error tag. */ + nb_ransac_min_inlier_count, /*!< Identifier associated to the nb_ransac_min_inlier_count tag. */ + ransac_threshold, /*!< Identifier associated to the ransac_threshold tag. */ + ransac_consensus_percentage /*!< Identifier associated to the ransac_consensus_percentage tag. */ + } vpNodeIdentifier; + + /*! Enumerator for the different filtering matching method. */ + typedef enum { + constantFactorDistanceThreshold, /*!< Keep all the points below a constant factor threshold. */ + stdDistanceThreshold, /*!< Keep all the points below a minimal distance + the standard deviation. */ + ratioDistanceThreshold, /*!< Keep all the points enough discriminated. */ + stdAndRatioDistanceThreshold, /*!< Keep all the points which fall with the two conditions. */ + noFilterMatching /*!< No filtering. */ + } vpMatchingMethodEnum; + +private : + //! Name of the keypoint detector. + std::string m_detectorName; + //! Name of the keypoint extractor. + std::string m_extractorName; + //! Name of the keypoint matcher. + std::string m_matcherName; + //! Factor value for filtering method: constantFactorDistanceThreshold. + double m_matchingFactorThreshold; + //! Filtering method. + vpMatchingMethodEnum m_matchingMethod; + //! Ratio value for filtering method: ratioDistanceThreshold. + double m_matchingRatioThreshold; + //! Maximum number of iterations for the Ransac method. + int m_nbRansacIterations; + //! Minimum number of inliers for the Ransac method. + int m_nbRansacMinInlierCount; + //! Percentage value of inliers compared to outliers for the Ransac method. + double m_ransacConsensusPercentage; + //! Maximum reprojection error (in pixel for OpenCV method) to consider a point as an inlier. + double m_ransacReprojectionError; + //! Maximum error (in meter for ViSP method) to consider a point as an inlier. + double m_ransacThreshold; + //! If true, the cardinality of the consensus is based upon a percentage, otherwise + //it is based on a fixed number. + bool m_useRansacConsensusPercentage; + //! If true, use ViSP Ransac VVS pose estimation method, otherwise use OpenCV method. + bool m_useRansacVVS; + + +public: + + vpXmlConfigParserKeyPoint(); + + /*! + Get the detector name. + + \return The detector name. + */ + inline std::string getDetectorName() const { + return m_detectorName; + } + + /*! + Get the extractor name. + + \return The extractor name. + */ + inline std::string getExtractorName() const { + return m_extractorName; + } + /*! + Get the matcher name. + + \return The detector name. + */ + inline std::string getMatcherName() const { + return m_matcherName; + } + /*! + Get the factor value. + + \return The factor value for the filtering method: constantFactorDistanceThreshold. + */ + inline double getMatchingFactorThreshold() const { + return m_matchingFactorThreshold; + } + + /*! + Get the filtering method. + + \return The filtering method. + */ + inline vpMatchingMethodEnum getMatchingMethod() const { + return m_matchingMethod; + } + + /*! + Get the ratio value. + + \return The factor value for the filtering method: ratioDistanceThreshold. + */ + inline double getMatchingRatioThreshold() const { + return m_matchingRatioThreshold; + } + + /*! + Get the maximum number of iterations for the Ransac method. + + \return The maximum number of iterations for the Ransac method. + */ + inline int getNbRansacIterations() const { + return m_nbRansacIterations; + } + + /*! + Get the minimum number of inliers for the Ransac method. + + \return The minimum number of inliers for the Ransac method. + */ + inline int getNbRansacMinInlierCount() const { + return m_nbRansacMinInlierCount; + } + + /*! + Get the percentage value of inliers for the Ransac method. + + \return The percentage value of inliers for the Ransac method. + */ + inline double getRansacConsensusPercentage() const { + return m_ransacConsensusPercentage; + } + + /*! + Get the maximum reprojection error for a candidate inlier for the Ransac method. + + \return The maximum reprojection error for the Ransac method. + */ + inline double getRansacReprojectionError() const { + return m_ransacReprojectionError; + } + + /*! + Get the maximum error for a candidate inlier for the Ransac method. + + \return The maximum error for the Ransac method. + */ + inline double getRansacThreshold() const { + return m_ransacThreshold; + } + + /*! + Get the flag state to choose between a percentage of inliers or a fixed number. + + \return True to use a percentage value for inliers, false otherwise. + */ + inline bool getUseRansacConsensusPercentage() const { + return m_useRansacConsensusPercentage; + } + + /*! + Get the flag state to choose between OpenCV Ransac pose estimation or ViSP + Ransac VVS pose estimation. + + \return True to use ViSP method, false otherwise. + */ + inline bool getUseRansacVVSPoseEstimation() const { + return m_useRansacVVS; + } + + /*! + Parse an XML file to get configuration value for vpKeyPoint class. + + \param filename : filename of the XML file to parse + */ + void parse(const std::string &filename); + + +private: + + void init(); + void read_detector(xmlDocPtr doc, xmlNodePtr node); + void read_extractor(xmlDocPtr doc, xmlNodePtr node); + void read_matcher(xmlDocPtr doc, xmlNodePtr node); + virtual void readMainClass(xmlDocPtr doc, xmlNodePtr node); + void read_ransac(xmlDocPtr doc, xmlNodePtr node); + virtual void writeMainClass(xmlNodePtr ){}; + +}; +#endif //VISP_HAVE_XML2 +#endif diff --git a/src/math/kalman/vpKalmanFilter.cpp b/src/math/kalman/vpKalmanFilter.cpp index 336a6d7a881aa34430f04a1e5ddb1671d1b3d7d6..8fe3cd916e03168397b2b6cc58ec50eac1009a9a 100644 --- a/src/math/kalman/vpKalmanFilter.cpp +++ b/src/math/kalman/vpKalmanFilter.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKalmanFilter.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpKalmanFilter.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,20 +53,20 @@ /*! Initialize the Kalman filter. - \param size_state : Size of the state vector \f${\bf x}_{k}\f$ for one signal. + \param size_state_vector : Size of the state vector \f${\bf x}_{k}\f$ for one signal. - \param size_measure : Size of the measure vector \f${\bf z}_{k}\f$ + \param size_measure_vector : Size of the measure vector \f${\bf z}_{k}\f$ for one signal. - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. */ void -vpKalmanFilter::init(unsigned int size_state, unsigned int size_measure, - unsigned int nsignal) +vpKalmanFilter::init(unsigned int size_state_vector, unsigned int size_measure_vector, + unsigned int n_signal) { - this->size_state = size_state; - this->size_measure = size_measure ; - this->nsignal = nsignal ; + this->size_state = size_state_vector; + this->size_measure = size_measure_vector ; + this->nsignal = n_signal ; F.resize(size_state*nsignal, size_state*nsignal) ; H.resize(size_measure*nsignal, size_state*nsignal) ; @@ -91,14 +91,9 @@ vpKalmanFilter::init(unsigned int size_state, unsigned int size_measure, */ vpKalmanFilter::vpKalmanFilter() + : iter(0), size_state(0), size_measure(0), nsignal(0), verbose_mode(false), + Xest(), Xpre(), F(), H(), R(), Q(), dt(-1), Ppre(), Pest(), W(), I() { - verbose(false); - // init_done = false ; - this->size_state = 0; - this->size_measure = 0 ; - this->nsignal = 0 ; - iter = 0 ; - dt = -1 ; } /*! @@ -106,17 +101,12 @@ vpKalmanFilter::vpKalmanFilter() The verbose mode is by default desactivated. - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. */ -vpKalmanFilter::vpKalmanFilter(unsigned int nsignal) +vpKalmanFilter::vpKalmanFilter(unsigned int n_signal) + : iter(0), size_state(0), size_measure(0), nsignal(n_signal), verbose_mode(false), + Xest(), Xpre(), F(), H(), R(), Q(), dt(-1), Ppre(), Pest(), W(), I() { - verbose(false); - // init_done = false; - this->size_state = 0; - this->size_measure = 0 ; - this->nsignal = nsignal; - iter = 0 ; - dt = -1 ; } /*! @@ -124,17 +114,18 @@ vpKalmanFilter::vpKalmanFilter(unsigned int nsignal) The verbose mode is by default desactivated. - \param size_state : Size of the state vector \f${\bf x}_{(k)}\f$ for one signal. + \param size_state_vector : Size of the state vector \f${\bf x}_{(k)}\f$ for one signal. - \param size_measure : Size of the measure vector \f${\bf z}_{(k)}\f$ + \param size_measure_vector : Size of the measure vector \f${\bf z}_{(k)}\f$ for one signal. - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. */ -vpKalmanFilter::vpKalmanFilter(unsigned int size_state, unsigned int size_measure, unsigned int nsignal) +vpKalmanFilter::vpKalmanFilter(unsigned int size_state_vector, unsigned int size_measure_vector, unsigned int n_signal) + : iter(0), size_state(0), size_measure(0), nsignal(0), verbose_mode(false), + Xest(), Xpre(), F(), H(), R(), Q(), dt(-1), Ppre(), Pest(), W(), I() { - verbose(false); - init( size_state, size_measure, nsignal) ; + init( size_state_vector, size_measure_vector, n_signal) ; } /*! @@ -264,11 +255,11 @@ vpKalmanFilter::filtering(vpColVector &z) \param Vn : Variance of measure noise \param Vw : Variance of the state noise - modèle d'état + State model: \f[ S = \left[\; y \quad \frac{\partial y}{\partial t}\;\right]^T = \left[\; y \quad \dot{ y}\;\right]^T \f] - Modélisation des filtres + Filter model: \f[ \begin{array}{rclll} \\ S(t+1) &= & F S(t) + W(t)&~~~~~~~~~~~& S(t+1) \mbox{ est un vecteur} \left[\; @@ -278,16 +269,15 @@ vpKalmanFilter::filtering(vpColVector &z) \f] - La matrice F décrit le modèle d'évolution de l'état. Dans le cas présent elle - est donnée par : + Matrix F describes the evolution of the state. This matrix is given by: \f[ F= \left( \begin{array}{cc} 1 & \Delta t \\ 0 & 1 \end{array} \right) \f] - Le bruit \f$W = \left( \begin{array}{c} \;W_1 \quad W_2\; \end{array} \right)^T\f$ - vient modéliser les variations sur le modèle à vitesse constante (dues aux accélérations) + The noise \f$W = \left( \begin{array}{c} \;W_1 \quad W_2\; \end{array} \right)^T\f$ + take into account the variations of the constant velocity model due to the accelerations. - En effet on a: + Thus we have: \f[ \left\{ \begin{array}{rcl} diff --git a/src/math/kalman/vpKalmanFilter.h b/src/math/kalman/vpKalmanFilter.h index d7e1e08010cba28d7d72f694bdad043ca6f9df52..823dfe8a934e9e8cd164aa74e9fa1b8d80dabdb9 100644 --- a/src/math/kalman/vpKalmanFilter.h +++ b/src/math/kalman/vpKalmanFilter.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKalmanFilter.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpKalmanFilter.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -128,18 +128,20 @@ protected : public: vpKalmanFilter() ; - vpKalmanFilter(unsigned int nsignal) ; - vpKalmanFilter(unsigned int size_state, unsigned int size_measure, unsigned int nsignal) ; + vpKalmanFilter(unsigned int n_signal) ; + vpKalmanFilter(unsigned int size_state, unsigned int size_measure, unsigned int n_signal) ; + /*! Destructor that does noting. */ + virtual ~vpKalmanFilter() {}; /*! Set the number of signal to filter. */ - void setNumberOfSignal(unsigned int nsignal) + void setNumberOfSignal(unsigned int n_signal) { - this->nsignal = nsignal; + this->nsignal = n_signal; } // int init() { return init_done ; } - void init(unsigned int size_state, unsigned int size_measure, unsigned int nsignal) ; + void init(unsigned int size_state, unsigned int size_measure, unsigned int n_signal) ; void prediction() ; void filtering(vpColVector &z) ; /*! diff --git a/src/math/kalman/vpLinearKalmanFilterInstantiation.cpp b/src/math/kalman/vpLinearKalmanFilterInstantiation.cpp index 492b4b8ab1248be099e3f250ddba83ba2b0ee058..dbd55d47eabd5a36f9e399faf1055e84deb5b36e 100644 --- a/src/math/kalman/vpLinearKalmanFilterInstantiation.cpp +++ b/src/math/kalman/vpLinearKalmanFilterInstantiation.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLinearKalmanFilterInstantiation.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLinearKalmanFilterInstantiation.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,7 @@ \warning It is requiered to set the state model before using this method. - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. \param sigma_state : Vector that contains the variance of the state noise. The dimension of this vector is equal to the state vector @@ -79,7 +79,7 @@ \param rho : Degree of correlation between successive accelerations. Values are in [0:1[. - \param dt : Sampling time \f$\Delta t\f$ expressed is + \param delta_t : Sampling time \f$\Delta t\f$ expressed is second. Depending on the filter modelization, this value may not be used. This is for example the case for the vpLinearKalmanFilterInstantiation::stateConstVelWithColoredNoise_MeasureVel model @@ -174,23 +174,23 @@ int main() \endcode */ void -vpLinearKalmanFilterInstantiation::initFilter(unsigned int nsignal, - vpColVector &sigma_state, - vpColVector &sigma_measure, - double rho, double dt) +vpLinearKalmanFilterInstantiation::initFilter(unsigned int n_signal, + vpColVector &sigma_state, + vpColVector &sigma_measure, + double rho, double delta_t) { switch (model) { case stateConstVelWithColoredNoise_MeasureVel: - initStateConstVelWithColoredNoise_MeasureVel(nsignal, sigma_state, + initStateConstVelWithColoredNoise_MeasureVel(n_signal, sigma_state, sigma_measure, rho); break; case stateConstVel_MeasurePos: - initStateConstVel_MeasurePos(nsignal, sigma_state, - sigma_measure, dt); + initStateConstVel_MeasurePos(n_signal, sigma_state, + sigma_measure, delta_t); break; case stateConstAccWithColoredNoise_MeasureVel: - initStateConstAccWithColoredNoise_MeasureVel(nsignal, sigma_state, - sigma_measure, rho, dt); + initStateConstAccWithColoredNoise_MeasureVel(n_signal, sigma_state, + sigma_measure, rho, delta_t); break; case unknown: vpERROR_TRACE("Kalman state model is not set") ; @@ -282,7 +282,7 @@ vpLinearKalmanFilterInstantiation::initFilter(unsigned int nsignal, \right] \f] - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. \param sigma_state : Vector that fix the variance of the state covariance matrix \f$[\sigma^2_Q \; 0]^T\f$. The dimension of @@ -292,19 +292,19 @@ vpLinearKalmanFilterInstantiation::initFilter(unsigned int nsignal, noise. The dimension of this vector is equal to the number of signal to filter. - \param dt : Sampling time \f$\Delta t\f$ expressed is second. + \param delta_t : Sampling time \f$\Delta t\f$ expressed is second. */ void -vpLinearKalmanFilterInstantiation::initStateConstVel_MeasurePos(unsigned int nsignal, +vpLinearKalmanFilterInstantiation::initStateConstVel_MeasurePos(unsigned int n_signal, vpColVector &sigma_state, vpColVector &sigma_measure, - double dt ) + double delta_t ) { // init_done = true ; setStateModel(stateConstVel_MeasurePos); - init(size_state, size_measure, nsignal); + init(size_state, size_measure, n_signal); iter = 0; Pest = 0; @@ -313,12 +313,12 @@ vpLinearKalmanFilterInstantiation::initStateConstVel_MeasurePos(unsigned int nsi H = 0; R = 0; Q = 0; - this->dt = dt ; + this->dt = delta_t ; double dt2 = dt*dt ; double dt3 = dt2*dt ; - for (unsigned int i=0; i < size_measure*nsignal ; i++ ) { + for (unsigned int i=0; i < size_measure*n_signal ; i++ ) { // State model // | 1 dt | // F = | | @@ -443,7 +443,7 @@ vpLinearKalmanFilterInstantiation::initStateConstVel_MeasurePos(unsigned int nsi \right] \f] - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. \param sigma_state : Vector that fix the variance of the state covariance matrix \f$[0 \; \sigma^2_Q]^T\f$. The dimension of @@ -505,7 +505,7 @@ int main() \endcode */ void -vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel(unsigned int nsignal, +vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel(unsigned int n_signal, vpColVector &sigma_state, vpColVector &sigma_measure, double rho) @@ -517,7 +517,7 @@ vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel( setStateModel(stateConstVelWithColoredNoise_MeasureVel); - init(size_state, size_measure, nsignal); + init(size_state, size_measure, n_signal); iter = 0; Pest = 0; @@ -527,7 +527,7 @@ vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel( R = 0; Q = 0; - for (unsigned int i=0; i < size_measure*nsignal ; i++ ) { + for (unsigned int i=0; i < size_measure*n_signal ; i++ ) { // State model // | 1 1 | // F = | | @@ -658,7 +658,7 @@ vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel( \right] \f] - \param nsignal : Number of signal to filter. + \param n_signal : Number of signal to filter. \param sigma_state : Vector that fix the variance of the state covariance matrix \f$[0 \; \sigma^2_{Q_1} \; @@ -672,7 +672,7 @@ vpLinearKalmanFilterInstantiation::initStateConstVelWithColoredNoise_MeasureVel( \param rho : Degree of correlation between successive accelerations. Values are in [0:1[. - \param dt : Sampling time \f$\Delta t\f$ expressed is second. + \param delta_t : Sampling time \f$\Delta t\f$ expressed is second. \exception vpException::badValue : Bad rho value wich is not in [0:1[. @@ -728,11 +728,11 @@ int main() */ void -vpLinearKalmanFilterInstantiation::initStateConstAccWithColoredNoise_MeasureVel(unsigned int nsignal, +vpLinearKalmanFilterInstantiation::initStateConstAccWithColoredNoise_MeasureVel(unsigned int n_signal, vpColVector &sigma_state, vpColVector &sigma_measure, double rho, - double dt) + double delta_t) { if ((rho < 0) || (rho >= 1)) { vpERROR_TRACE("Bad rho value %g; should be in [0:1[", rho) ; @@ -740,7 +740,7 @@ vpLinearKalmanFilterInstantiation::initStateConstAccWithColoredNoise_MeasureVel( } setStateModel(stateConstAccWithColoredNoise_MeasureVel); - init(size_state, size_measure, nsignal); + init(size_state, size_measure, n_signal); iter = 0; Pest = 0; @@ -749,7 +749,7 @@ vpLinearKalmanFilterInstantiation::initStateConstAccWithColoredNoise_MeasureVel( H = 0; R = 0; Q = 0; - this->dt = dt; + this->dt = delta_t; // initialise les matrices decrivant les modeles for (unsigned int i=0; i < size_measure*nsignal ; i++ ) { // State model diff --git a/src/math/kalman/vpLinearKalmanFilterInstantiation.h b/src/math/kalman/vpLinearKalmanFilterInstantiation.h index 8429699533b7b17c8bb9506f58b73f90841cb7ce..5ff208bb0361f75af98f173a64b15741d825c232 100644 --- a/src/math/kalman/vpLinearKalmanFilterInstantiation.h +++ b/src/math/kalman/vpLinearKalmanFilterInstantiation.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLinearKalmanFilterInstantiation.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLinearKalmanFilterInstantiation.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -88,11 +88,12 @@ class VISP_EXPORT vpLinearKalmanFilterInstantiation : public vpKalmanFilter By default the state model is unknown and set to vpLinearKalmanFilterInstantiation::unknown. */ - vpLinearKalmanFilterInstantiation() : vpKalmanFilter() + vpLinearKalmanFilterInstantiation() : model(unknown) { - setStateModel(unknown); }; + /*! Destructor that does nothng. */ + virtual ~vpLinearKalmanFilterInstantiation() {}; /*! Return the current state model. */ @@ -155,8 +156,8 @@ int main() } \endcode */ -void vpLinearKalmanFilterInstantiation::setStateModel(vpStateModel model) { - this->model = model; +void vpLinearKalmanFilterInstantiation::setStateModel(vpStateModel mdl) { + this->model = mdl; switch(model) { case stateConstVel_MeasurePos: case stateConstVelWithColoredNoise_MeasureVel: diff --git a/src/math/matrix/vpColVector.cpp b/src/math/matrix/vpColVector.cpp index b765b2b9c0c4ab48d14c6495b8cd7a6fc99def52..7f35664fb295f9b36eb062256898039f84e37a05 100644 --- a/src/math/matrix/vpColVector.cpp +++ b/src/math/matrix/vpColVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpColVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpColVector.cpp 5218 2015-01-28 10:27:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,22 +64,15 @@ vpColVector vpColVector::operator+(const vpColVector &m) const { - vpColVector v ; - - try { - v.resize(rowNum) ; - } - catch(vpException me) - { - vpERROR_TRACE("Error caught") ; - throw ; + if (getRows() != m.getRows() ) { + throw(vpException(vpException::dimensionError, + "Bad size during vpColVector (%dx1) and vpColVector (%dx1) addition", + getRows(), m.getRows())) ; } - - double *vd = v.data ; double *d = data ; double *md = m.data ; + vpColVector v(rowNum); for (unsigned int i=0;i<rowNum;i++) - *(vd++) = *(d++) + *(md++); - + v[i] = (*this)[i] + m[i]; return v; } @@ -104,19 +97,15 @@ vpMatrix vpColVector::operator*(const vpRowVector &m) const //! operator substraction of two vectors V = A-v vpColVector vpColVector::operator-(const vpColVector &m) const { - vpColVector v ; - - try { - v.resize(rowNum) ; - } - catch(vpException me) - { - vpERROR_TRACE("Error caught") ; - throw ; + if (getRows() != m.getRows() ) { + throw(vpException(vpException::dimensionError, + "Bad size during vpColVector (%dx1) and vpColVector (%dx1) substraction", + getRows(), m.getRows())) ; } + vpColVector v(rowNum); for (unsigned int i=0;i<rowNum;i++) - v[i] = (*this)[i] - m[i]; + v[i] = (*this)[i] - m[i]; return v; } @@ -138,8 +127,8 @@ vpColVector::vpColVector (const vpRotationVector &v){ } - //! operator A = -A -vpColVector vpColVector::operator-() + //! Operator A = -A +vpColVector vpColVector::operator-() const { vpColVector A ; try { @@ -180,17 +169,15 @@ vpColVector vpColVector::operator*(double x) const } /*! - \brief copy from a matrix - \warning Handled with care m should be a 1 row matrix + Copy from a matrix + \warning Handled with care m should be a 1 row matrix. */ vpColVector &vpColVector::operator=(const vpMatrix &m) { - - if (m.getCols() !=1) { vpTRACE(" m should be a 1 cols matrix ") ; - throw (vpException(vpException::dimensionError)," m should be a 1 cols matrix "); + throw (vpException(vpException::dimensionError,"m should be a 1 cols matrix ")); } try { @@ -250,27 +237,66 @@ vpColVector &vpColVector::operator=(const vpColVector &v) return *this; } -//! Copy operator. Allow operation such as A = v +/*! + Copy operator. + Allows operation such as A << v + \code +#include <visp/vpColVector.h> + +int main() +{ + vpColVector A, B(5); + for (unsigned int i=0; i<B.size(); i++) + B[i] = i; + A << B; + std::cout << "A: \n" << A << std::endl; +} + \endcode + In column vector A we get: + \code +A: +0 +1 +2 +3 +4 + \endcode + */ vpColVector & vpColVector::operator<<(const vpColVector &v) { - try { - resize(v.getRows()); - } - catch(vpException me) - { - vpERROR_TRACE("Error caught") ; - throw ; - } - - for (unsigned int i=0; i<rowNum; i++) { - for (unsigned int j=0; j<colNum; j++) { - rowPtrs[i][j] = v.rowPtrs[i][j]; - } - } + *this = v; return *this; } -//! Assigment operator. Allow operation such as A = *v +/*! + Assigment operator. Allow operation such as A = *v + + The following example shows how to use this operator. + \code +#include <visp/vpColVector.h> + +int main() +{ + size_t n = 5; + vpColVector A(n); + double *B = new double [n]; + for (unsigned int i = 0; i < n; i++) + B[i] = i; + A << B; + std::cout << "A: \n" << A << std::endl; + delete [] B; +} + \endcode + It produces the following output: + \code +A: +0 +1 +2 +3 +4 + \endcode + */ vpColVector & vpColVector::operator<<( double *x ) { for (unsigned int i=0; i<rowNum; i++) { @@ -404,11 +430,11 @@ vpColVector::dotProd(const vpColVector &a, const vpColVector &b) vpColVector &vpColVector::normalize() { - double sum = sumSquare() ; + double sum_square = sumSquare() ; //if (sum != 0.0) - if (std::fabs(sum) > std::numeric_limits<double>::epsilon()) - *this /= sqrt(sum) ; + if (std::fabs(sum_square) > std::numeric_limits<double>::epsilon()) + *this /= sqrt(sum_square) ; // If sum = 0, we have a nul vector. So we return just. return *this; @@ -784,8 +810,54 @@ void vpColVector::reshape(vpMatrix & m,const unsigned int &nrows,const unsigned m[i][j]=data[j*ncols+i]; } -/* - * Local variables: - * c-basic-offset: 2 - * End: +/*! + Insert a column vector. + \param i : Index of the first element to introduce. This index starts from 0. + \param v : Column vector to insert. + + The following example shows how to use this function: + \code +#include <visp/vpColVector.h> + +int main() +{ + vpColVector v(4); + for (unsigned int i=0; i < v.size(); i++) + v[i] = i; + std::cout << "v:\n" << v << std::endl; + + vpColVector w(2); + for (unsigned int i=0; i < w.size(); i++) + w[i] = i+10; + std::cout << "w:\n" << w << std::endl; + + v.insert(1, w); + std::cout << "v:\n" << v << std::endl; +} + \endcode + It produces the following output: + \code +v: +0 +1 +2 +3 + +w: +10 +11 + +v: +0 +10 +11 +3 + \endcode */ +void vpColVector::insert(unsigned int i, const vpColVector &v) +{ + if (i+v.size() > this->size()) + throw(vpException(vpException::dimensionError, "Unable to insert a column vector")); + for (unsigned int j=0; j < v.size(); j++) + (*this)[i+j] = v[j]; +} diff --git a/src/math/matrix/vpColVector.h b/src/math/matrix/vpColVector.h index 3868f4e4c5d7505b4557cc87aed2ab926dddca26..b732f117e3237d40219071721dfa4b15de5a07cb 100644 --- a/src/math/matrix/vpColVector.h +++ b/src/math/matrix/vpColVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpColVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpColVector.h 5185 2015-01-21 14:36:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,22 +78,27 @@ protected: public: - //! basic constructor + //! Basic constructor. vpColVector() : vpMatrix() {}; - //! constructor of vector of size n - vpColVector(unsigned int nn) : vpMatrix(nn,1){}; - //! copy constructor + //! Constructor of vector of size n. Each element is set to 0. + vpColVector(unsigned int n) : vpMatrix(n,1){}; + //! Constructor of vector of size n. Each element is set to \e val. + vpColVector(unsigned int n, double val) : vpMatrix(n, 1, val){}; + //! Copy constructor. vpColVector (const vpColVector &v); - //! constructor initialize a vpColVector from a vpRotationVector + //! Constructor that initialize a vpColVector from a vpRotationVector. vpColVector (const vpRotationVector &v); + void insert(unsigned int i, const vpColVector &v); + /*! Set the size of the column vector. \param i : Column vector size. \param flagNullify : If true, set the data to zero. */ void resize(const unsigned int i, const bool flagNullify = true) - { vpMatrix::resize(i, 1, flagNullify); } - + { + vpMatrix::resize(i, 1, flagNullify); + } //! Access V[i] = x inline double &operator [](unsigned int n) { return *(data + n); } @@ -101,27 +106,27 @@ public: inline const double &operator [](unsigned int n) const { return *(data+n); } //! Copy operator. Allow operation such as A = v vpColVector &operator=(const vpColVector &v); - //! Copy operator. Allow operation such as A = v - vpColVector &operator<<(const vpColVector &v); - //! Assigment operator. Allow operation such as A = *v - vpColVector &operator<<(double *); - //! copy from a matrix + // Copy from a matrix. vpColVector &operator=(const vpMatrix &m); - //! initialisation each element of the vector is x + //! Initialize each element of the vector to x vpColVector &operator=(double x); + // Copy operator. Allow operation such as A << v + vpColVector &operator<<(const vpColVector &v); + // Assigment operator. Allow operation such as A = *v + vpColVector &operator<<(double *); - //! operator addition of two vectors V = A+v + //! Addition of two vectors V = A+v vpColVector operator+(const vpColVector &v) const; - //! operator substraction of two vectors V = A-v + //! Substraction of two vectors V = A-v vpColVector operator-(const vpColVector &v) const; - //! operator dot product + //! Operator A = -A + vpColVector operator-() const; + //! Dot product double operator*(const vpColVector &x) const; - //! operator dot product + //! Dot product vpMatrix operator*(const vpRowVector &x) const; - //! operator multiplication by a scalar V = A * x + //! Multiplication by a scalar V = A * x vpColVector operator*(const double x) const; - //! operator A = -A - vpColVector operator-() ; vpColVector rows(unsigned int first_row, unsigned int last_row) { @@ -138,22 +143,22 @@ public: static vpColVector stack(const vpColVector &A, const vpColVector &B); static void stack(const vpColVector &A, const vpColVector &B, vpColVector &C); - //! transpose of Vector + //! Transpose of a vector vpRowVector t() const; - //! normalise the vector + //! Normalise the vector vpColVector &normalize() ; - //! normalise the vector + // normalise the vector // vpColVector &normalize(vpColVector &x) const ; - //! compute the cross product of two vectors C = a x b + //! Compute the cross product of two vectors C = a x b static vpColVector crossProd(const vpColVector &a, const vpColVector &b) ; - // compute the cross product of two vectors C = a x b + // Compute the cross product of two vectors C = a x b inline static vpColVector cross(const vpColVector &a, const vpColVector &b){ return crossProd(a,b);} - // compute the skew matrix [v]x + // Compute the skew matrix [v]x static vpMatrix skew(const vpColVector &v); //! Dot Product @@ -175,20 +180,20 @@ public: */ inline void rad2deg() { - double rad2deg = 180.0/M_PI; + double r2d = 180.0/M_PI; for (unsigned int i=0; i < rowNum; i++) - (*this)[i] *= rad2deg; + (*this)[i] *= r2d; } /*! Convert a column vector containing angles in degrees into radians. */ inline void deg2rad() { - double deg2rad = M_PI/180.0; + double d2r = M_PI/180.0; for (unsigned int i=0; i < rowNum; i++) - (*this)[i] *= deg2rad; + (*this)[i] *= d2r; } /*! diff --git a/src/math/matrix/vpGEMM.h b/src/math/matrix/vpGEMM.h index c266fa6265240c4ede4312168d17c5c5466857aa..0360d0ea8556f25647ea1a7a3ec945154efc5a74 100644 --- a/src/math/matrix/vpGEMM.h +++ b/src/math/matrix/vpGEMM.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpGEMM.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpGEMM.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/matrix/vpMatrix.cpp b/src/math/matrix/vpMatrix.cpp index 674e6e4e336df113ca85aad8d08d059f93ba3aec..99acb536dda66768535d5e12c0feee6c1a18d31a 100644 --- a/src/math/matrix/vpMatrix.cpp +++ b/src/math/matrix/vpMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpMatrix.cpp 4210 2013-04-16 08:57:46Z fspindle $ +* $Id: vpMatrix.cpp 5238 2015-01-30 13:52:25Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,6 +56,7 @@ #include <visp/vpMatrix.h> #include <visp/vpMath.h> +#include <visp/vpHomography.h> #include <visp/vpTranslationVector.h> // Exception @@ -77,6 +78,10 @@ #include <cmath> // std::fabs #include <limits> // numeric_limits +#ifdef VISP_HAVE_GSL +#include <gsl/gsl_linalg.h> +#endif + #define DEBUG_LEVEL1 0 #define DEBUG_LEVEL2 0 @@ -109,8 +114,8 @@ Construction of the object matrix. Number of columns and rows are zero. */ vpMatrix::vpMatrix() + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) { - init() ; } @@ -123,9 +128,31 @@ Initialize a matrix with 0. \param c : Matrix number of columns. */ vpMatrix::vpMatrix(unsigned int r, unsigned int c) + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) +{ + resize(r, c); +} + +/*! +Constructor. + +Initialize a matrix with \e val. + +\param r : Matrix number of rows. +\param c : Matrix number of columns. +\param val : Each element of the matrix is set to \e val. +*/ +vpMatrix::vpMatrix(unsigned int r, unsigned int c, double val) + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) { - init() ; resize(r, c); + *this = val; +} + +vpMatrix::vpMatrix(const vpHomography& H) + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) +{ + (*this) = H; } /*! @@ -134,9 +161,8 @@ vpMatrix::vpMatrix(unsigned int r, unsigned int c) vpMatrix::vpMatrix(const vpMatrix &m, unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) { - init() ; - if (((r + nrows) > m.rowNum) || ((c + ncols) > m.colNum)) { vpERROR_TRACE("\n\t\t SubvpMatrix larger than vpMatrix") ; @@ -149,9 +175,8 @@ vpMatrix::vpMatrix(const vpMatrix &m, //! copie constructor vpMatrix::vpMatrix(const vpMatrix& m) + : rowNum(0), colNum(0), data(NULL), rowPtrs(NULL), dsize(0), trsize(0) { - init() ; - resize(m.rowNum,m.colNum); memcpy(data,m.data,rowNum*colNum*sizeof(double)) ; @@ -177,7 +202,7 @@ void vpMatrix::resize(const unsigned int nrows, const unsigned int ncols, if ((nrows == rowNum) && (ncols == colNum)) { - if (flagNullify) + if (flagNullify && this->data != NULL) { memset(this->data,0,this->dsize*sizeof(double)) ;} } else @@ -189,7 +214,7 @@ void vpMatrix::resize(const unsigned int nrows, const unsigned int ncols, vpDEBUG_TRACE (25, "Recopy case per case is required iff number of " "cols has changed (structure of double array is not " "the same in this case."); - if (recopyNeeded) + if (recopyNeeded && this->data != NULL) { copyTmp = new double[this->dsize]; memcpy (copyTmp, this ->data, sizeof(double)*this->dsize); @@ -222,8 +247,8 @@ void vpMatrix::resize(const unsigned int nrows, const unsigned int ncols, vpDEBUG_TRACE (25, "Recomputation this->trsize array values."); { - double **t= rowPtrs; - for (unsigned int i=0; i<dsize; i+=ncols) { *t++ = this->data + i; } + double **t_= rowPtrs; + for (unsigned int i=0; i<dsize; i+=ncols) { *t_++ = this->data + i; } } this->rowNum = nrows; this->colNum = ncols; @@ -233,7 +258,7 @@ void vpMatrix::resize(const unsigned int nrows, const unsigned int ncols, { memset(this->data,0,this->dsize*sizeof(double)) ;} else { - if (recopyNeeded) + if (recopyNeeded && this->rowPtrs != NULL) { vpDEBUG_TRACE (25, "Recopy..."); const unsigned int minRow = (this->rowNum<rowTmp)?this->rowNum:rowTmp; @@ -258,25 +283,69 @@ void vpMatrix::resize(const unsigned int nrows, const unsigned int ncols, } +/*! + Initialize the matrix from a part of an input matrix M. + + \param M : Input matrix used for initialization. + \param r : row index in matrix M. + \param c : column index in matrix M. + \param nrows : Number of rows of the matrix that should be initialized. + \param ncols : Number of columns of the matrix that should be initialized. -void -vpMatrix::init(const vpMatrix &m,unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) + The sub-matrix starting from M[r][c] element and ending on M[r+nrows-1][c+ncols-1] element + is used to initialize the matrix. + + The following code shows how to use this function: +\code +#include <visp/vpMatrix.h> + +int main() { - try { - resize(nrows, ncols) ; - } - catch(vpException me) - { - vpERROR_TRACE("Error caught") ; - std::cout << me << std::endl ; - throw ; + vpMatrix M(4,5); + int val = 0; + for(size_t i=0; i<M.getRows(); i++) { + for(size_t j=0; j<M.getCols(); j++) { + M[i][j] = val++; + } } + M.print (std::cout, 4, "M "); + vpMatrix N; + N.init(M, 0, 1, 2, 3); + N.print (std::cout, 4, "N "); +} +\endcode + It produces the following output: + \code +M [4,5]= + 0 1 2 3 4 + 5 6 7 8 9 + 10 11 12 13 14 + 15 16 17 18 19 +N [2,3]= + 1 2 3 + 6 7 8 + \endcode + */ +void +vpMatrix::init(const vpMatrix &M,unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) +{ unsigned int rnrows = r+nrows ; unsigned int cncols = c+ncols ; + + if (rnrows > M.getRows()) + throw(vpException(vpException::dimensionError, + "Bad row dimension (%d > %d) used to initialize vpMatrix", rnrows, M.getRows())); + if (cncols > M.getCols()) + throw(vpException(vpException::dimensionError, + "Bad column dimension (%d > %d) used to initialize vpMatrix", cncols, M.getCols())); + resize(nrows, ncols); + + if (this->rowPtrs == NULL) // Fix coverity scan: explicit null dereferenced + return; // Noting to do for (unsigned int i=r ; i < rnrows; i++) for (unsigned int j=c ; j < cncols; j++) - (*this)[i-r][j-c] = m[i][j] ; + (*this)[i-r][j-c] = M[i][j] ; } /*! @@ -332,6 +401,24 @@ vpMatrix::operator=(const vpMatrix &B) return *this; } +/*! +\brief Copy operator. +Allow operation such as A = H + +\param H : homography matrix to be copied. +*/ +vpMatrix & +vpMatrix::operator=(const vpHomography& H) +{ + init() ; + resize(3,3); + for(unsigned int i=0; i<3; i++) + for(unsigned int j=0; j<3; j++) + (*this)[i][j] = H[i][j]; + + return *this; +} + //! set all the element of the matrix A to x vpMatrix & vpMatrix::operator=(double x) @@ -441,6 +528,28 @@ vpMatrix vpMatrix::operator*(const vpMatrix &B) const return C; } +/*! + Allows to multiply a matrix by an homography. + Operation M = K * H (H is unchanged). + +*/ +vpMatrix vpMatrix::operator*(const vpHomography &H) const +{ + if (colNum != 3) + throw(vpException(vpMatrixException::dimensionError, "Cannot multiply the matrix by the homography; bad matrix size")); + vpMatrix M(rowNum, 3); + + for (unsigned int i=0;i<rowNum;i++){ + for (unsigned int j=0;j<3;j++) { + double s = 0; + for (unsigned int k=0;k<3;k++) s += (*this)[i][k] * H[k][j]; + M[i][j] = s; + } + } + + return M; +} + /*! Operation C = A*wA + B*wB @@ -755,11 +864,10 @@ vpMatrix vpMatrix::operator-() const //negate return C; } -//!return sum of the Aij^2 (for all i, for all j) double vpMatrix::sumSquare() const { - double sum=0.0; + double sum_square=0.0; double x ; // double t0,t1; @@ -770,28 +878,40 @@ vpMatrix::sumSquare() const // while (d < n ) // { // x = *d++ ; - // sum += x*x ; + // sum_square += x*x ; // } // t1=vpTime::measureTimeMicros(); // std::cout<< t1-t0<<" "<< sum << " "; // - // sum= 0.0; + // sum_square= 0.0; // t0=vpTime::measureTimeMicros(); for (unsigned int i=0;i<rowNum;i++) for(unsigned int j=0;j<colNum;j++) { x=rowPtrs[i][j]; - sum+=x*x; + sum_square += x*x; } // t1=vpTime::measureTimeMicros(); // std::cout<< t1-t0<<" "<< sum << std::endl; + return sum_square; +} +double +vpMatrix::sum() const +{ + double s=0.0; + for (unsigned int i=0;i<rowNum;i++) + { + for(unsigned int j=0;j<colNum;j++) + { + s += rowPtrs[i][j]; + } + } - - return sum; + return s; } @@ -859,7 +979,7 @@ vpMatrix::operator*(const vpTranslationVector &b) const if (rowNum != 3 || colNum != 3) { vpERROR_TRACE("vpMatrix mismatch in vpMatrix::operator*(const vpTranslationVector)") ; - throw(vpMatrixException::incorrectMatrixSizeError) ; + throw(vpMatrixException(vpMatrixException::incorrectMatrixSizeError, "vpMatrix mismatch in vpMatrix::operator*()")) ; } for (unsigned int j=0;j<3;j++) c[j]=0 ; @@ -978,7 +1098,7 @@ vpMatrix vpMatrix::operator/(double x) const //if (x == 0) { if (std::fabs(x) <= std::numeric_limits<double>::epsilon()) { - vpERROR_TRACE("Divide by zero in method /(double x)") ; + //vpERROR_TRACE("Divide by zero in method /(double x)") ; throw vpMatrixException(vpMatrixException::divideByZeroError, "Divide by zero in method /(double x)"); } @@ -1232,10 +1352,10 @@ void vpMatrix::transpose(vpMatrix & At )const{ for( unsigned int i = 0; i < colNum; i++ ) { - double * row = AtRowPtrs[i]; + double * row_ = AtRowPtrs[i]; double * col = rowPtrs[0]+i; for( unsigned int j = 0; j < rowNum; j++, col+=A_step ) - *(row++)=*col; + *(row_++)=*col; } } @@ -2153,7 +2273,7 @@ vpMatrix::pseudoInverse(vpMatrix &Ap, for (j = 0; j < ncols_orig ; j++) { //if ( cons.row(j+1).sumSquare() != 0) - if ( std::fabs(cons.row(j+1).sumSquare()) > std::numeric_limits<double>::epsilon()) + if ( std::fabs(cons.getRow(j).sumSquare()) > std::numeric_limits<double>::epsilon()) { for (i = 0; i < cons.getCols(); i++) Ker[k][i] = cons[j][i]; @@ -2215,27 +2335,29 @@ vpMatrix::pseudoInverse(vpMatrix &Ap, return rank ; } - +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! -\brief Return the ith rows of the matrix -\warning notice row(1) is the 0th row. -*/ + \deprecated This method is deprecated. You should use getRow(). + Return the i-th row of the matrix. + \warning notice row(1) is the 0th row. +*/ vpRowVector -vpMatrix::row(const unsigned int j) +vpMatrix::row(const unsigned int i) { vpRowVector c(getCols()) ; - for (unsigned int i =0 ; i < getCols() ; i++) c[i] = (*this)[j-1][i] ; + for (unsigned int j =0 ; j < getCols() ; j++) c[j] = (*this)[i-1][j] ; return c ; } - /*! -\brief Return the ith columns of the matrix -\warning notice column(1) is the 0th column. -*/ + \deprecated This method is deprecated. You should use getCol(). + Return the j-th columns of the matrix. + \warning notice column(1) is the 0-th column. + \param j : Index of the column to extract. +*/ vpColVector vpMatrix::column(const unsigned int j) { @@ -2244,9 +2366,205 @@ vpMatrix::column(const unsigned int j) for (unsigned int i =0 ; i < getRows() ; i++) c[i] = (*this)[i][j-1] ; return c ; } +#endif + + +/*! + Extract a column vector from a matrix. + \warning All the indexes start from 0 in this function. + \param j : Index of the column to extract. If col=0, the first column is extracted. + \param i_begin : Index of the row that gives the location of the first element of the column vector to extract. + \param column_size : Size of the column vector to extract. + \return The extracted column vector. + + The following example shows how to use this function: + \code +#include <visp/vpColVector.h> +#include <visp/vpMatrix.h> + +int main() +{ + vpMatrix A(4,4); + + for(unsigned int i=0; i < A.getRows(); i++) + for(unsigned int j=0; j < A.getCols(); j++) + A[i][j] = i*A.getCols()+j; + + A.print(std::cout, 4); + + vpColVector cv = A.getCol(1, 1, 3); + std::cout << "Column vector: \n" << cv << std::endl; +} + \endcode +It produces the following output: + \code +[4,4]= + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15 +column vector: +5 +9 +13 + \endcode + */ +vpColVector +vpMatrix::getCol(const unsigned int j, const unsigned int i_begin, const unsigned int column_size) const +{ + if (i_begin + column_size > getRows() || j >= getCols()) + throw(vpException(vpException::dimensionError, "Unable to extract a column vector from the matrix")); + vpColVector c(column_size); + for (unsigned int i=0 ; i < column_size ; i++) + c[i] = (*this)[i_begin+i][j]; + return c; +} + +/*! + Extract a column vector from a matrix. + \warning All the indexes start from 0 in this function. + \param j : Index of the column to extract. If j=0, the first column is extracted. + \return The extracted column vector. + + The following example shows how to use this function: + \code +#include <visp/vpColVector.h> +#include <visp/vpMatrix.h> +int main() +{ + vpMatrix A(4,4); + + for(unsigned int i=0; i < A.getRows(); i++) + for(unsigned int j=0; j < A.getCols(); j++) + A[i][j] = i*A.getCols()+j; + + A.print(std::cout, 4); + + vpColVector cv = A.getCol(1); + std::cout << "Column vector: \n" << cv << std::endl; +} + \endcode +It produces the following output: + \code +[4,4]= + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15 +column vector: +1 +5 +9 +13 + \endcode + */ +vpColVector +vpMatrix::getCol(const unsigned int j) const +{ + if (j >= getCols()) + throw(vpException(vpException::dimensionError, "Unable to extract a column vector from the matrix")); + unsigned int nb_rows = getRows(); + vpColVector c(nb_rows); + for (unsigned int i=0 ; i < nb_rows ; i++) + c[i] = (*this)[i][j]; + return c; +} + +/*! + Extract a row vector from a matrix. + \warning All the indexes start from 0 in this function. + \param i : Index of the row to extract. If i=0, the first row is extracted. + \return The extracted row vector. + + The following example shows how to use this function: + \code +#include <visp/vpMatrix.h> +#include <visp/vpRowVector.h> + +int main() +{ + vpMatrix A(4,4); + + for(unsigned int i=0; i < A.getRows(); i++) + for(unsigned int j=0; j < A.getCols(); j++) + A[i][j] = i*A.getCols()+j; + + A.print(std::cout, 4); + + vpRowVector rv = A.getRow(1); + std::cout << "Row vector: \n" << rv << std::endl; +} \endcode +It produces the following output: + \code +[4,4]= + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15 +Row vector: +4 5 6 7 + \endcode + */ +vpRowVector +vpMatrix::getRow(const unsigned int i) const +{ + if (i >= getRows()) + throw(vpException(vpException::dimensionError, "Unable to extract a row vector from the matrix")); + unsigned int nb_cols = getCols(); + vpRowVector r( nb_cols ); + for (unsigned int j=0 ; j < nb_cols ; j++) + r[j] = (*this)[i][j]; + return r; +} +/*! + Extract a row vector from a matrix. + \warning All the indexes start from 0 in this function. + \param i : Index of the row to extract. If i=0, the first row is extracted. + \param j_begin : Index of the column that gives the location of the first element of the row vector to extract. + \param row_size : Size of the row vector to extract. + \return The extracted row vector. + + The following example shows how to use this function: + \code +#include <visp/vpMatrix.h> +#include <visp/vpRowVector.h> +int main() +{ + vpMatrix A(4,4); + + for(unsigned int i=0; i < A.getRows(); i++) + for(unsigned int j=0; j < A.getCols(); j++) + A[i][j] = i*A.getCols()+j; + + A.print(std::cout, 4); + + vpRowVector rv = A.getRow(1, 1, 3); + std::cout << "Row vector: \n" << rv << std::endl; +} \endcode +It produces the following output: + \code +[4,4]= + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15 +Row vector: +5 6 7 + \endcode + */ +vpRowVector +vpMatrix::getRow(const unsigned int i, const unsigned int j_begin, const unsigned int row_size) const +{ + if (j_begin + row_size > getCols() || i >= getRows()) + throw(vpException(vpException::dimensionError, "Unable to extract a row vector from the matrix")); + vpRowVector r(row_size); + for (unsigned int j=0 ; j < row_size ; j++) + r[j] = (*this)[i][j_begin+i]; + return r; +} /*! \brief Stack matrices. "Stack" two matrices C = [ A B ]^T @@ -2380,7 +2698,7 @@ vpMatrix::insert(const vpMatrix &A, const vpMatrix &B, vpMatrix &C, const unsigned int r, const unsigned int c) { if( ( (r + B.getRows()) <= A.getRows() ) && - ( (c + B.getCols()) <= A.getRows() ) ){ + ( (c + B.getCols()) <= A.getCols() ) ){ try { C.resize(A.getRows(),A.getCols() ) ; } @@ -2573,8 +2891,10 @@ vpMatrix::createDiagonalMatrix(const vpColVector &A, vpMatrix &DA) /*! \brief std::cout a matrix */ -std::ostream &operator <<(std::ostream &s,const vpMatrix &m) +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpMatrix &m) { + std::ios_base::fmtflags original_flags = s.flags(); + s.precision(10) ; for (unsigned int i=0;i<m.getRows();i++) { for (unsigned int j=0;j<m.getCols();j++){ @@ -2585,6 +2905,8 @@ std::ostream &operator <<(std::ostream &s,const vpMatrix &m) s << std::endl; } + s.flags(original_flags); // restore s to standard state + return s; } @@ -2622,6 +2944,8 @@ vpMatrix::print(std::ostream& s, unsigned int length, char const* intro) std::vector<std::string> values(m*n); std::ostringstream oss; std::ostringstream ossFixed; + std::ios_base::fmtflags original_flags = oss.flags(); + // ossFixed <<std::fixed; ossFixed.setf ( std::ios::fixed, std::ios::floatfield ); @@ -2660,7 +2984,7 @@ vpMatrix::print(std::ostream& s, unsigned int length, char const* intro) if (maxAfter==1) maxAfter=0; // the following line is useful for debugging - std::cerr <<totalLength <<" " <<maxBefore <<" " <<maxAfter <<"\n"; + //std::cerr <<totalLength <<" " <<maxBefore <<" " <<maxAfter <<"\n"; if (intro) s <<intro; s <<"["<<m<<","<<n<<"]=\n"; @@ -2690,6 +3014,8 @@ vpMatrix::print(std::ostream& s, unsigned int length, char const* intro) s <<std::endl; } + s.flags(original_flags); // restore s to standard state + return (int)(maxBefore+maxAfter); } @@ -2703,7 +3029,7 @@ d,e,f; g,h,i] */ std::ostream & vpMatrix:: -matlabPrint(std::ostream & os) +matlabPrint(std::ostream & os) const { unsigned int i,j; @@ -2732,7 +3058,7 @@ Print using the following way so that this output can be directly copied into MA ]) */ std::ostream & vpMatrix:: -maplePrint(std::ostream & os) +maplePrint(std::ostream & os) const { unsigned int i,j; @@ -2749,6 +3075,32 @@ maplePrint(std::ostream & os) return os; }; +/*! +\brief Print matrix in csv format. + +Print as comma separated values so that this output can be imported into any program which has a csv data import option: +0.939846, 0.0300754, 0.340272 +0.0300788, 0.984961, -0.170136 +-0.340272, 0.170136, 0.924807 +*/ +std::ostream & vpMatrix:: +csvPrint(std::ostream & os) const +{ + unsigned int i,j; + + for (i=0; i < this->getRows(); ++ i) + { + for (j=0; j < this->getCols(); ++ j) + { + os << (*this)[i][j]; + if (!(j==(this->getCols()-1))) + os << ", "; + } + os << std::endl; + } + return os; +}; + /*! \brief Print to be used as part of a C++ code later. @@ -2765,7 +3117,7 @@ the line vpMatrix A(6,7) (see example). each bytes of the double array. */ std::ostream & vpMatrix:: -cppPrint(std::ostream & os, const char * matrixName, bool octet) +cppPrint(std::ostream & os, const char * matrixName, bool octet) const { unsigned int i,j; @@ -2863,7 +3215,7 @@ See the Numerical Recipes in C page 43 for further explanations. double vpMatrix::detByLU() const { - double det(0); + double det_ = 0; // Test wether the matrix is squred if (rowNum == colNum) @@ -2884,10 +3236,10 @@ double vpMatrix::detByLU() const delete[]perm; // compute the determinant that is the product of the eigen values - det = (double) d; + det_ = (double) d; for(unsigned int i=0;i<rowNum;i++) { - det*=tmp[i][i]; + det_*=tmp[i][i]; } } @@ -2898,7 +3250,7 @@ double vpMatrix::detByLU() const } - return det ; + return det_ ; } @@ -3336,10 +3688,10 @@ vpMatrix::kernel(vpMatrix &kerA, double svThreshold) } // if (noyau == 1) { - double maxsv = 0 ; + maxsv = 0 ; for (i=0 ; i < ddl ; i++) if (fabs(sv[i]) > maxsv) maxsv = fabs(sv[i]) ; - unsigned int rank = 0 ; + rank = 0 ; for (i=0 ; i < ddl ; i++) if (fabs(sv[i]) > maxsv*svThreshold) rank++ ; vpMatrix cons(ddl,ddl) ; @@ -3363,7 +3715,7 @@ vpMatrix::kernel(vpMatrix &kerA, double svThreshold) // cout << cons.Row(j+1) << " = " << cons.Row(j+1).SumSquare() << endl ; //if (cons.row(j+1).sumSquare() !=0) - if (std::fabs(cons.row(j+1).sumSquare()) > std::numeric_limits<double>::epsilon()) + if (std::fabs(cons.getRow(j).sumSquare()) > std::numeric_limits<double>::epsilon()) { for (i=0 ; i < cons.getCols() ; i++) Ker[k][i] = cons[j][i] ; @@ -3412,14 +3764,14 @@ A.det(vpMatrix::LU_DECOMPOSITION ) << std::endl; */ double vpMatrix::det(vpDetMethod method) const { - double det = 0; + double det_ = 0; if ( method == LU_DECOMPOSITION ) { - det = this->detByLU(); + det_ = this->detByLU(); } - return (det); + return (det_); } @@ -3429,7 +3781,7 @@ Save a matrix to a file. \param filename : Absolute file name. \param M : Matrix to be saved. \param binary : If true the matrix is saved in a binary file, else a text file. -\param Header : Optional line that will be saved at the beginning of the file. +\param header : Optional line that will be saved at the beginning of the file. \return Returns true if no problem happened. @@ -3438,7 +3790,7 @@ less than if you save it in a binary file. */ bool vpMatrix::saveMatrix(const char *filename, const vpMatrix &M, - const bool binary, const char *Header) + const bool binary, const char *header) { std::fstream file; @@ -3459,10 +3811,10 @@ vpMatrix::saveMatrix(const char *filename, const vpMatrix &M, { unsigned int i = 0; file << "# "; - while (Header[i] != '\0') + while (header[i] != '\0') { - file << Header[i]; - if (Header[i] == '\n') + file << header[i]; + if (header[i] == '\n') file << "# "; i++; } @@ -3474,8 +3826,8 @@ vpMatrix::saveMatrix(const char *filename, const vpMatrix &M, else { int headerSize = 0; - while (Header[headerSize] != '\0') headerSize++; - file.write(Header,headerSize+1); + while (header[headerSize] != '\0') headerSize++; + file.write(header,headerSize+1); unsigned int matrixSize; matrixSize = M.getRows(); file.write((char*)&matrixSize,sizeof(int)); @@ -3497,21 +3849,119 @@ vpMatrix::saveMatrix(const char *filename, const vpMatrix &M, return true; } +/*! + Save a matrix in a YAML-formatted file. + + \param filename : absolute file name. + \param M : matrix to be saved in the file. + \param header : optional lines that will be saved at the beginning of the file. Should be YAML-formatted and will adapt to the indentation if any. + + \return Returns true if success. + + Here is an example of outputs. +\code +vpMatrix M(3,4); +vpMatrix::saveMatrixYAML("matrix.yml", M, "example: a YAML-formatted header"); +vpMatrix::saveMatrixYAML("matrixIndent.yml", M, "example:\n - a YAML-formatted header\n - with inner indentation"); +\endcode +Content of matrix.yml: +\code +example: a YAML-formatted header +rows: 3 +cols: 4 +data: + - [0, 0, 0, 0] + - [0, 0, 0, 0] + - [0, 0, 0, 0] +\endcode +Content of matrixIndent.yml: +\code +example: + - a YAML-formatted header + - with inner indentation +rows: 3 +cols: 4 +data: + - [0, 0, 0, 0] + - [0, 0, 0, 0] + - [0, 0, 0, 0] +\endcode + + \sa loadMatrixYAML() +*/ +bool vpMatrix::saveMatrixYAML(const char *filename, const vpMatrix &M, const char *header) +{ + std::fstream file; + + file.open(filename, std::fstream::out); + + if(!file) + { + file.close(); + return false; + } + + unsigned int i = 0; + bool inIndent = false; + std::string indent = ""; + bool checkIndent = true; + while (header[i] != '\0') + { + file << header[i]; + if(checkIndent) + { + if (inIndent) + { + if(header[i] == ' ') + indent += " "; + else if (indent.length() > 0) + checkIndent = false; + } + if (header[i] == '\n' || (inIndent && header[i] == ' ')) + inIndent = true; + else + inIndent = false; + } + i++; + } + + if(i != 0) + file << std::endl; + file << "rows: " << M.getRows() << std::endl; + file << "cols: " << M.getCols() << std::endl; + + if(indent.length() == 0) + indent = " "; + + file << "data: " << std::endl; + unsigned int j; + for(i=0;i<M.getRows();++i) + { + file << indent << "- ["; + for(j=0;j<M.getCols()-1;++j) + file << M[i][j] << ", "; + file << M[i][j] << "]" << std::endl; + } + + file.close(); + return true; +} + /*! - Load a matrix to a file. + Load a matrix from a file. \param filename : Absolute file name. \param M : Matrix to be loaded. \param binary : If true the matrix is loaded from a binary file, else from a text file. - \param Header : Header of the file loaded in this parameter. + \param header : header of the file loaded in this parameter. \return Returns true if no problem happened. */ bool vpMatrix::loadMatrix(const char *filename, vpMatrix &M, const bool binary, - char *Header) + char *header) { std::fstream file; @@ -3537,12 +3987,17 @@ vpMatrix::loadMatrix(const char *filename, vpMatrix &M, const bool binary, file.read(&c,1); h+=c; } - if (Header != NULL) - strncpy(Header, h.c_str(), h.size() + 1); + if (header != NULL) + strncpy(header, h.c_str(), h.size() + 1); unsigned int rows, cols; file >> rows; file >> cols; + + if (rows > std::numeric_limits<unsigned int>::max() + || cols > std::numeric_limits<unsigned int>::max()) + throw vpException(vpException::badValue, "Matrix exceed the max size."); + M.resize(rows,cols); double value; @@ -3564,8 +4019,8 @@ vpMatrix::loadMatrix(const char *filename, vpMatrix &M, const bool binary, file.read(&c,1); h+=c; } - if (Header != NULL) - strncpy(Header, h.c_str(), h.size() + 1); + if (header != NULL) + strncpy(header, h.c_str(), h.size() + 1); unsigned int rows, cols; file.read((char*)&rows,sizeof(unsigned int)); @@ -3589,6 +4044,88 @@ vpMatrix::loadMatrix(const char *filename, vpMatrix &M, const bool binary, } +/*! + Load a matrix from a YAML-formatted file. + + \param filename : absolute file name. + \param M : matrix to be loaded from the file. + \param header : header of the file is loaded in this parameter. + + \return Returns true on success. + + \sa saveMatrixYAML() + +*/ +bool +vpMatrix::loadMatrixYAML(const char *filename, vpMatrix &M, char *header) +{ + std::fstream file; + + file.open(filename, std::fstream::in); + + if(!file) + { + file.close(); + return false; + } + + unsigned int rows = 0,cols = 0; + std::string h; + std::string line,subs; + bool inheader = true; + unsigned int i=0, j; + unsigned int lineStart = 0; + + while ( getline (file,line) ) + { + if(inheader) + { + if(rows == 0 && line.compare(0,5,"rows:") == 0) + { + std::stringstream ss(line); + ss >> subs; + ss >> rows; + } + else if (cols == 0 && line.compare(0,5,"cols:") == 0) + { + std::stringstream ss(line); + ss >> subs; + ss >> cols; + } + else if (line.compare(0,5,"data:") == 0) + inheader = false; + else + h += line + "\n"; + } + else + { + // if i == 0, we just got out of the header: initialize matrix dimensions + if(i == 0) + { + if(rows == 0 || cols == 0) + { + file.close(); + return false; + } + M.resize(rows, cols); + // get indentation level which is common to all lines + lineStart = (unsigned int)line.find("[") + 1; + } + std::stringstream ss(line.substr(lineStart, line.find("]") - lineStart)); + j = 0; + while(getline(ss, subs, ',')) + M[i][j++] = atof(subs.c_str()); + i++; + } + } + + if (header != NULL) + strncpy(header, h.substr(0,h.length()-1).c_str(), h.size()); + + file.close(); + return true; +} + /*! Compute the exponential matrix of a square matrix. @@ -3607,6 +4144,21 @@ vpMatrix::expm() } else { +#ifdef VISP_HAVE_GSL + size_t size_ = rowNum * colNum; + double *b = new double [size_]; + for (size_t i=0; i< size_; i++) + b[i] = 0.; + gsl_matrix_view m = gsl_matrix_view_array(this->data, rowNum, colNum); + gsl_matrix_view em = gsl_matrix_view_array(b, rowNum, colNum); + gsl_linalg_exponential_ss(&m.matrix, &em.matrix, 0); + //gsl_matrix_fprintf(stdout, &em.matrix, "%g"); + vpMatrix expA(rowNum, colNum); + memcpy(expA.data, b, size_ * sizeof(double)); + + delete [] b; + return expA; +#else vpMatrix _expE(rowNum, colNum); vpMatrix _expD(rowNum, colNum); vpMatrix _expX(rowNum, colNum); @@ -3664,6 +4216,7 @@ vpMatrix::expm() exp=_expE; } return exp; +#endif } } @@ -3736,6 +4289,56 @@ vpMatrix subblock(const vpMatrix &M, unsigned int col, unsigned int row) return M_comp; } +/*! + \return The condition number, the ratio of the largest singular value of the matrix to the smallest. + */ +double vpMatrix::cond() +{ + vpMatrix v; + vpColVector w; + + vpMatrix M; + M = *this; + + M.svd(w, v); + double min=w[0]; + double max=w[0]; + for(unsigned int i=0;i<M.getCols();i++) + { + if(min>w[i])min=w[i]; + if(max<w[i])max=w[i]; + } + return max/min; +} + +/*! + Compute \f${\bf H} + \alpha * diag({\bf H})\f$ + \param H : input Matrix \f${\bf H}\f$. This matrix should be square. + \param alpha : Scalar \f$\alpha\f$ + \param HLM : Resulting operation. + */ +void vpMatrix::computeHLM(const vpMatrix &H, const double &alpha, vpMatrix &HLM) +{ + if(H.getCols() != H.getRows()) + { + vpTRACE("The matrix must be square"); + throw(vpMatrixException(vpMatrixException::incorrectMatrixSizeError, + "The matrix must be square" )); + } + HLM.resize(H.getRows(), H.getCols()); + + for(unsigned int i=0;i<H.getCols();i++) + { + for(unsigned int j=0;j<H.getCols();j++) + { + HLM[i][j]=H[i][j]; + if(i==j) + HLM[i][j]+= alpha*H[i][j]; + } + } + +} + #undef DEBUG_LEVEL1 #undef DEBUG_LEVEL2 diff --git a/src/math/matrix/vpMatrix.h b/src/math/matrix/vpMatrix.h index 6e574e4e5787a8a3bef23c1761e0f2a6aaa9ba2d..3f3659d688b22b8c925d429cc1a78ea2b5a1674f 100644 --- a/src/math/matrix/vpMatrix.h +++ b/src/math/matrix/vpMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMatrix.h 5238 2015-01-30 13:52:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,6 +45,7 @@ #define vpMatrix_H #include <visp/vpConfig.h> +#include <visp/vpException.h> #include <visp/vpTime.h> #ifdef VISP_HAVE_GSL @@ -63,6 +64,7 @@ class vpTranslationVector; class vpColVector; class vpTranslationVector; class vpRowVector; +class vpHomography; @@ -128,11 +130,13 @@ protected: vpMatrix() ; //! Constructor. Initialization of A as an r x c matrix with 0. vpMatrix(unsigned int r, unsigned int c) ; - + vpMatrix(unsigned int r, unsigned int c, double val); //! sub vpMatrix constructor vpMatrix(const vpMatrix &m, unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) ; + vpMatrix(const vpHomography& H); + //! Destructor (Memory de-allocation) virtual ~vpMatrix(); @@ -157,6 +161,8 @@ protected: inline unsigned int getRows() const { return rowNum ;} //! Return the number of columns of the matrix inline unsigned int getCols() const { return colNum; } + //! Return the number of elements of the matrix. + inline unsigned int size() const { return colNum*rowNum; } // Set the size of the matrix A, initialization with a zero matrix void resize(const unsigned int nrows, const unsigned int ncols, @@ -176,50 +182,14 @@ protected: //@{ int print(std::ostream& s, unsigned int length, char const* intro=0); - std::ostream & matlabPrint(std::ostream & os); - std::ostream & maplePrint(std::ostream & os); - std::ostream & cppPrint(std::ostream & os, const char * matrixName = NULL, bool octet = false); + std::ostream & matlabPrint(std::ostream & os) const; + std::ostream & maplePrint(std::ostream & os) const; + std::ostream & csvPrint(std::ostream & os) const; + std::ostream & cppPrint(std::ostream & os, const char * matrixName = NULL, bool octet = false) const; void printSize() { std::cout << getRows() <<" x " << getCols() <<" " ; } //@} - static bool saveMatrix(const char *filename, const vpMatrix &M, const bool binary = false, const char *Header = ""); - static bool loadMatrix(const char *filename, vpMatrix &M, const bool binary = false, char *Header = NULL); - - /*! - Save a matrix to a file. - - \param filename : absolute file name - \param M : matrix to be saved - \param binary :If true the matrix is save in a binary file, else a text file. - \param Header : optional line that will be saved at the beginning of the file - - \return Returns true if no problem appends. - - Warning : If you save the matrix as in a text file the precision is less than if you save it in a binary file. - */ - static inline bool saveMatrix(std::string filename, const vpMatrix &M, - const bool binary = false, - const char *Header = "") - { - return vpMatrix::saveMatrix(filename.c_str(), M, binary, Header); - } - - /*! - Load a matrix to a file. - - \param filename : absolute file name - \param M : matrix to be loaded - \param binary :If true the matrix is load from a binary file, else from a text file. - \param Header : Header of the file is load in this parameter - - \return Returns true if no problem appends. - */ - static inline bool loadMatrix(std::string filename, vpMatrix &M, - const bool binary = false, char *Header = NULL) - { - return vpMatrix::loadMatrix(filename.c_str(), M, binary, Header); - } //--------------------------------- // Copy / assignment @@ -234,6 +204,8 @@ protected: //! Copy operator. Allow operation such as A = B vpMatrix &operator=(const vpMatrix &B); + //! Copy operator. Allow operation such as A = H + vpMatrix &operator=(const vpHomography &H); //! Set all the element of the matrix A to x vpMatrix &operator=(const double x); void diag(const vpColVector &A); @@ -245,24 +217,118 @@ protected: /** @name Access/modification operators */ //@{ //! write elements Aij (usage : A[i][j] = x ) - inline double *operator[](unsigned int n) { return rowPtrs[n]; } + inline double *operator[](unsigned int i) { return rowPtrs[i]; } //! read elements Aij (usage : x = A[i][j] ) - inline double *operator[](unsigned int n) const {return rowPtrs[n];} + inline double *operator[](unsigned int i) const {return rowPtrs[i];} //@} //--------------------------------- // Matrix operations (Static). - //--------------------------------- + //--------------------------------- - static void mult2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C); static void add2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C); static void add2WeightedMatrices(const vpMatrix &A, const double &wA, const vpMatrix &B,const double &wB, vpMatrix &C); - static void sub2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C); - static void negateMatrix(const vpMatrix &A, vpMatrix &C); - static void multMatrixVector(const vpMatrix &A, const vpColVector &b, vpColVector &c); - static vpMatrix computeCovarianceMatrix(const vpMatrix &A, const vpColVector &x, const vpColVector &b); static vpMatrix computeCovarianceMatrix(const vpMatrix &A, const vpColVector &x, const vpColVector &b, const vpMatrix &w); + static void computeHLM(const vpMatrix &H, const double &alpha, vpMatrix &HLM); + + // Create a diagonal matrix with the element of a vector DAii = Ai + static void createDiagonalMatrix(const vpColVector &A, vpMatrix &DA) ; + // Insert matrix A in the current matrix at the given position (r, c). + void insert(const vpMatrix&A, const unsigned int r, const unsigned int c); + // Insert matrix B in matrix A at the given position (r, c). + static vpMatrix insert(const vpMatrix &A,const vpMatrix &B, const unsigned int r, const unsigned int c) ; + // Insert matrix B in matrix A (not modified) at the given position (r, c), the result is given in matrix C. + static void insert(const vpMatrix &A, const vpMatrix &B, vpMatrix &C, const unsigned int r, const unsigned int c) ; + + // Juxtapose to matrices C = [ A B ] + static vpMatrix juxtaposeMatrices(const vpMatrix &A,const vpMatrix &B) ; + // Juxtapose to matrices C = [ A B ] + static void juxtaposeMatrices(const vpMatrix &A,const vpMatrix &B, vpMatrix &C) ; + // Compute Kronecker product matrix + static void kron(const vpMatrix &m1, const vpMatrix &m2 , vpMatrix &out); + + // Compute Kronecker product matrix + static vpMatrix kron(const vpMatrix &m1, const vpMatrix &m2 ); + + /*! + Load a matrix from a file. + + \param filename : absolute file name + \param M : matrix to be loaded + \param binary :If true the matrix is loaded from a binary file, else from a text file. + \param Header : Header of the file is loaded in this parameter + + \return Returns true if no problem appends. + */ + static inline bool loadMatrix(std::string filename, vpMatrix &M, + const bool binary = false, char *Header = NULL) + { + return vpMatrix::loadMatrix(filename.c_str(), M, binary, Header); + } + static bool loadMatrix(const char *filename, vpMatrix &M, const bool binary = false, char *Header = NULL); + static void mult2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C); + static void multMatrixVector(const vpMatrix &A, const vpColVector &b, vpColVector &c); + static void negateMatrix(const vpMatrix &A, vpMatrix &C); + /*! + Save a matrix to a file. + + \param filename : absolute file name + \param M : matrix to be saved + \param binary :If true the matrix is save in a binary file, else a text file. + \param Header : optional line that will be saved at the beginning of the file + + \return Returns true if no problem appends. + + Warning : If you save the matrix as in a text file the precision is less than if you save it in a binary file. + */ + static inline bool saveMatrix(std::string filename, const vpMatrix &M, + const bool binary = false, + const char *Header = "") + { + return vpMatrix::saveMatrix(filename.c_str(), M, binary, Header); + } + static bool saveMatrix(const char *filename, const vpMatrix &M, const bool binary = false, const char *Header = ""); + + /*! + Save a matrix in a YAML-formatted file. + + \param filename : absolute file name. + \param M : matrix to be saved in the file. + \param header : optional lines that will be saved at the beginning of the file. Should be YAML-formatted and will adapt to the indentation if any. + + \return Returns true if success. + + */ + static inline bool saveMatrixYAML(std::string filename, const vpMatrix &M, const char *header = "") + { + return vpMatrix::saveMatrixYAML(filename.c_str(), M, header); + } + static bool saveMatrixYAML(const char *filename, const vpMatrix &M, const char *header = ""); + /*! + Load a matrix from a YAML-formatted file. + + \param filename : absolute file name. + \param M : matrix to be loaded from the file. + \param header : Header of the file is loaded in this parameter. + + \return Returns true if no problem appends. + */ + static inline bool loadMatrixYAML(std::string filename, vpMatrix &M, char *header = NULL) + { + return vpMatrix::loadMatrixYAML(filename.c_str(), M, header); + } + static bool loadMatrixYAML(const char *filename, vpMatrix &M, char *header = NULL); + + + // Stack the matrix A below the current one, copy if not initialized this = [ this A ]^T + void stackMatrices(const vpMatrix &A); + //! Stack two Matrices C = [ A B ]^T + static vpMatrix stackMatrices(const vpMatrix &A,const vpMatrix &B) ; + //! Stack two Matrices C = [ A B ]^T + static void stackMatrices(const vpMatrix &A,const vpMatrix &B, vpMatrix &C) ; + static void sub2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C); + //--------------------------------- // Matrix operations. @@ -275,6 +341,7 @@ protected: vpMatrix &operator-=(const vpMatrix &B); vpMatrix operator*(const vpMatrix &B) const; + vpMatrix operator*(const vpHomography &H) const; vpMatrix operator+(const vpMatrix &B) const; vpMatrix operator-(const vpMatrix &B) const; vpMatrix operator-() const; @@ -304,7 +371,17 @@ protected: // Cij = Aij / x (A is unchanged) vpMatrix operator/(const double x) const; - //!return sum of the Aij^2 (for all i, for all j) + /*! + Return the sum of all the \f$a_{ij}\f$ elements of the matrix. + + \return \f$\sum a_{ij}\f$ + */ + double sum() const; + /*! + Return the sum square of all the \f$a_{ij}\f$ elements of the matrix. + + \return \f$\sum a_{ij}^{2}\f$ + */ double sumSquare() const; // return the determinant of the matrix. @@ -318,13 +395,11 @@ protected: //------------------------------------------------- /** @name Columns, Rows extraction, Submatrix */ //@{ - //! Row extraction - vpRowVector row(const unsigned int i); - //! Column extraction - vpColVector column(const unsigned int j); - //! subvpMatrix extraction - void init(const vpMatrix &m, unsigned int r, unsigned int c, - unsigned int nrows, unsigned int ncols); + vpRowVector getRow(const unsigned int i) const; + vpRowVector getRow(const unsigned int i, const unsigned int j_begin, const unsigned int size) const; + vpColVector getCol(const unsigned int j) const; + vpColVector getCol(const unsigned int j, const unsigned int i_begin, const unsigned int size) const; + void init(const vpMatrix &M, unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols); //@} //------------------------------------------------- @@ -372,13 +447,6 @@ protected: vpMatrix kron(const vpMatrix &m1); //@} - // Compute Kronecker product matrix - static void kron(const vpMatrix &m1, const vpMatrix &m2 , vpMatrix &out); - - // Compute Kronecker product matrix - static vpMatrix kron(const vpMatrix &m1, const vpMatrix &m2 ); - - //------------------------------------------------- // Matrix inversion //------------------------------------------------- @@ -460,6 +528,7 @@ protected: vpColVector solveBySVD(const vpColVector &B) const ; unsigned int kernel(vpMatrix &KerA, double svThreshold=1e-6); + double cond(); //@} //------------------------------------------------- @@ -474,28 +543,6 @@ protected: void eigenValues(vpColVector &evalue, vpMatrix &evector); //@} - //! Stack two Matrices C = [ A B ]^T - static vpMatrix stackMatrices(const vpMatrix &A,const vpMatrix &B) ; - //! Stack two Matrices C = [ A B ]^T - static void stackMatrices(const vpMatrix &A,const vpMatrix &B, vpMatrix &C) ; - // Juxtapose to matrices C = [ A B ] - static vpMatrix juxtaposeMatrices(const vpMatrix &A,const vpMatrix &B) ; - // Juxtapose to matrices C = [ A B ] - static void juxtaposeMatrices(const vpMatrix &A,const vpMatrix &B, vpMatrix &C) ; - - // Create a diagonal matrix with the element of a vector DAii = Ai - static void createDiagonalMatrix(const vpColVector &A, vpMatrix &DA) ; - - // Stack the matrix A below the current one, copy if not initialized this = [ this A ]^T - void stackMatrices(const vpMatrix &A); - - // Insert matrix A in the current matrix at the given position (r, c). - void insert(const vpMatrix&A, const unsigned int r, const unsigned int c); - // Insert matrix B in matrix A at the given position (r, c). - static vpMatrix insert(const vpMatrix &A,const vpMatrix &B, const unsigned int r, const unsigned int c) ; - // Insert matrix B in matrix A (not modified) at the given position (r, c), the result is given in matrix C. - static void insert(const vpMatrix &A, const vpMatrix &B, vpMatrix &C, const unsigned int r, const unsigned int c) ; - // ------------------------- // Norms // ------------------------- @@ -507,6 +554,14 @@ protected: double infinityNorm () const; //@} +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + /*! + @name Deprecated functions + */ + vp_deprecated vpRowVector row(const unsigned int i); + vp_deprecated vpColVector column(const unsigned int j); +#endif + private: double detByLU() const; diff --git a/src/math/matrix/vpMatrixException.h b/src/math/matrix/vpMatrixException.h index 60ba790d15d44f5140969def874727cf97ad2ebf..2d23f349c90cb545d4fc5b53918835c99f66ae02 100644 --- a/src/math/matrix/vpMatrixException.h +++ b/src/math/matrix/vpMatrixException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMatrixException.h 4195 2013-04-05 08:28:41Z marchand $ + * $Id: vpMatrixException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,12 +68,12 @@ */ class VISP_EXPORT vpMatrixException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpMatrix member */ - enum errorCodeEnum + enum errorCodeEnum { //! error returns by a constructor constructionError, @@ -92,25 +92,20 @@ public: rankDeficient } ; -public: - vpMatrixException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpMatrixException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpMatrixException (const int code) - : vpException(code){ ; } - // vpMatrixException() : vpException() { ;} + public: + vpMatrixException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpMatrixException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpMatrixException (const int id) + : vpException(id){ ; } + // vpMatrixException() : vpException() { ;} }; - - - - -#endif /* #ifndef __vpMatrixException_ERROR_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/math/matrix/vpMatrix_cholesky.cpp b/src/math/matrix/vpMatrix_cholesky.cpp index b3200842991697c27efeedc27ea27dca8bf785dd..ec4af3f496e163d8116c16b3010599b1ed790d03 100644 --- a/src/math/matrix/vpMatrix_cholesky.cpp +++ b/src/math/matrix/vpMatrix_cholesky.cpp @@ -3,7 +3,7 @@ * $Id: vpMatrix_lu.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,30 +60,27 @@ extern "C" int dpotri_(char *uplo, int *n, double *a, int *lda, int *info); #ifdef VISP_HAVE_LAPACK vpMatrix vpMatrix::inverseByCholeskyLapack() const{ - int rowNum = (int)this->getRows(); - int lda = (int)rowNum; //lda is the number of rows because we don't use a submatrix + int rowNum_ = (int)this->getRows(); + int lda = (int)rowNum_; //lda is the number of rows because we don't use a submatrix int info; vpMatrix A = *this; - dpotrf_((char*)"L",&rowNum,A.data,&lda,&info); + dpotrf_((char*)"L",&rowNum_,A.data,&lda,&info); if(info!=0) std::cout << "cholesky:dpotrf_:error" << std::endl; - dpotri_((char*)"L",&rowNum,A.data,&lda,&info); + dpotri_((char*)"L",&rowNum_,A.data,&lda,&info); if(info!=0){ std::cout << "cholesky:dpotri_:error" << std::endl; throw vpMatrixException::badValue; } - for(unsigned int i=0;i<A.getRows();i++) for(unsigned int j=0;j<A.getCols();j++) if(i>j) A[i][j] = A[j][i]; - return A; - } #endif @@ -102,9 +99,10 @@ int main() { vpMatrix A(4,4); - A[0][0] = 1/1.; A[0][1] = 1/2.; A[0][2] = 1/3.; A[0][3] = 1/4.; + // Symmetric matrix + A[0][0] = 1/1.; A[0][1] = 1/5.; A[0][2] = 1/6.; A[0][3] = 1/7.; A[1][0] = 1/5.; A[1][1] = 1/3.; A[1][2] = 1/3.; A[1][3] = 1/5.; - A[2][0] = 1/6.; A[2][1] = 1/4.; A[2][2] = 1/2.; A[2][3] = 1/6.; + A[2][0] = 1/6.; A[2][1] = 1/3.; A[2][2] = 1/2.; A[2][3] = 1/6.; A[3][0] = 1/7.; A[3][1] = 1/5.; A[3][2] = 1/6.; A[3][3] = 1/7.; // Compute the inverse diff --git a/src/math/matrix/vpMatrix_covariance.cpp b/src/math/matrix/vpMatrix_covariance.cpp index 12ebed274902429d4d949b44dc1394989176886d..c8490109a1c149316683090d92359ed2b74e7db3 100644 --- a/src/math/matrix/vpMatrix_covariance.cpp +++ b/src/math/matrix/vpMatrix_covariance.cpp @@ -3,7 +3,7 @@ * $Id: vpMatrix_lu.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,16 +32,21 @@ * * * Description: - * Matrix LU decomposition. + * Covariance matrix computation. * * Authors: * Aurelien Yol * *****************************************************************************/ +#include <limits> // numeric_limits +#include <cmath> // std::fabs() + #include <visp/vpConfig.h> #include <visp/vpMatrix.h> #include <visp/vpColVector.h> +#include <visp/vpMatrixException.h> + /*! Compute the covariance matrix of the parameters x from a least squares minimisation defined as: @@ -55,8 +60,18 @@ */ vpMatrix vpMatrix::computeCovarianceMatrix(const vpMatrix &A, const vpColVector &x, const vpColVector &b) { - double sigma2 = ( ((b.t())*b) - ( (b.t())*A*x ) ); - return (A.t()*A).pseudoInverse()*sigma2; +// double denom = ((double)(A.getRows()) - (double)(A.getCols())); // To consider OLS Estimate for sigma + double denom = ((double)(A.getRows())); // To consider MLE Estimate for sigma + + if(denom <= std::numeric_limits<double>::epsilon()) + throw vpMatrixException(vpMatrixException::divideByZeroError, "Impossible to compute covariance matrix: not enough data"); + +// double sigma2 = ( ((b.t())*b) - ( (b.t())*A*x ) ); // Should be equivalent to line bellow. + double sigma2 = (b - (A * x)).t() * (b - (A * x)); + + sigma2 /= denom; + + return (A.t()*A).pseudoInverse(A.getCols()*std::numeric_limits<double>::epsilon())*sigma2; } /*! @@ -73,6 +88,19 @@ vpMatrix vpMatrix::computeCovarianceMatrix(const vpMatrix &A, const vpColVector */ vpMatrix vpMatrix::computeCovarianceMatrix(const vpMatrix &A, const vpColVector &x, const vpColVector &b, const vpMatrix &W) { - double sigma2 = ( ((W*b).t())*W*b - ( ((W*b).t())*W*A*x ) ); - return (A.t()*W*A).pseudoInverse()*sigma2; + double denom = 0.0; + vpMatrix W2(W.getCols(),W.getCols()); + for(unsigned int i = 0 ; i < W.getCols() ; i++){ + denom += W[i][i]; + W2[i][i] = W[i][i]*W[i][i]; + } + + if(denom <= std::numeric_limits<double>::epsilon()) + throw vpMatrixException(vpMatrixException::divideByZeroError, "Impossible to compute covariance matrix: not enough data"); + +// double sigma2 = ( ((W*b).t())*W*b - ( ((W*b).t())*W*A*x ) ); // Should be equivalent to line bellow. + double sigma2 = (W * b - (W * A * x)).t() * (W*b - (W * A * x)); + sigma2 /= denom; + + return (A.t()*(W2)*A).pseudoInverse(A.getCols()*std::numeric_limits<double>::epsilon())*sigma2; } diff --git a/src/math/matrix/vpMatrix_lu.cpp b/src/math/matrix/vpMatrix_lu.cpp index 8833e6d878d338a824ee5944296f4dd772a69b16..18be650f3eca81487f4b8826b1ef370e078ee12c 100644 --- a/src/math/matrix/vpMatrix_lu.cpp +++ b/src/math/matrix/vpMatrix_lu.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMatrix_lu.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMatrix_lu.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -88,7 +88,7 @@ vpMatrix::LUDcmp(unsigned int *perm, int& d) unsigned int n = rowNum; unsigned int i,imax=0,j,k; - double big,dum,sum,temp; + double big,dum,sum_,temp; vpColVector vv(n); d=1; @@ -99,41 +99,41 @@ vpMatrix::LUDcmp(unsigned int *perm, int& d) //if (big == 0.0) if (std::fabs(big) <= std::numeric_limits<double>::epsilon()) { - vpERROR_TRACE("Singular vpMatrix in LUDcmp") ; - throw(vpMatrixException(vpMatrixException::matrixError, - "\n\t\tSingular vpMatrix in LUDcmp")) ; + //vpERROR_TRACE("Singular vpMatrix in LUDcmp") ; + throw(vpMatrixException(vpMatrixException::matrixError, + "Singular vpMatrix in LUDcmp")) ; } vv[i]=1.0/big; } for (j=0;j<n;j++) { for (i=0;i<j;i++) { - sum=rowPtrs[i][j]; - for (k=0;k<i;k++) sum -= rowPtrs[i][k]*rowPtrs[k][j]; - rowPtrs[i][j]=sum; + sum_=rowPtrs[i][j]; + for (k=0;k<i;k++) sum_ -= rowPtrs[i][k]*rowPtrs[k][j]; + rowPtrs[i][j]=sum_; } big=0.0; for (i=j;i<n;i++) { - sum=rowPtrs[i][j]; + sum_=rowPtrs[i][j]; for (k=0;k<j;k++) - sum -= rowPtrs[i][k]*rowPtrs[k][j]; - rowPtrs[i][j]=sum; - if ( (dum=vv[i]*fabs(sum)) >= big) { - big=dum; - imax=i; + sum_ -= rowPtrs[i][k]*rowPtrs[k][j]; + rowPtrs[i][j]=sum_; + if ( (dum=vv[i]*fabs(sum_)) >= big) { + big=dum; + imax=i; } } if (j != imax) { for (k=0;k<n;k++) { - dum=rowPtrs[imax][k]; - rowPtrs[imax][k]=rowPtrs[j][k]; - rowPtrs[j][k]=dum; + dum=rowPtrs[imax][k]; + rowPtrs[imax][k]=rowPtrs[j][k]; + rowPtrs[j][k]=dum; } d *= -1; vv[imax]=vv[j]; } perm[j]=imax; - //if (rowPtrs[j][j] == 0.0) - if (std::fabs(rowPtrs[j][j]) <= std::numeric_limits<double>::epsilon()) + //if (rowPtrs[j][j] == 0.0) + if (std::fabs(rowPtrs[j][j]) <= std::numeric_limits<double>::epsilon()) rowPtrs[j][j]=TINY; if (j != n) { dum=1.0/(rowPtrs[j][j]); @@ -169,36 +169,36 @@ void vpMatrix::LUBksb(unsigned int *perm, vpColVector& b) unsigned int ii=0; unsigned int ip; - double sum; + double sum_; bool flag = false; unsigned int i; for (i=0;i<n;i++) { ip=perm[i]; - sum=b[ip]; + sum_=b[ip]; b[ip]=b[i]; if (flag) { - for (unsigned int j=ii;j<=i-1;j++) sum -= rowPtrs[i][j]*b[j]; + for (unsigned int j=ii;j<=i-1;j++) sum_ -= rowPtrs[i][j]*b[j]; } - //else if (sum) { - else if (std::fabs(sum) > std::numeric_limits<double>::epsilon()) { + //else if (sum_) { + else if (std::fabs(sum_) > std::numeric_limits<double>::epsilon()) { ii=i; flag = true; } - b[i]=sum; + b[i]=sum_; } // for (int i=n-1;i>=0;i--) { - // sum=b[i]; - // for (int j=i+1;j<n;j++) sum -= rowPtrs[i][j]*b[j]; - // b[i]=sum/rowPtrs[i][i]; + // sum_=b[i]; + // for (int j=i+1;j<n;j++) sum_ -= rowPtrs[i][j]*b[j]; + // b[i]=sum_/rowPtrs[i][i]; // } i=n; do { i --; - sum=b[i]; - for (unsigned int j=i+1;j<n;j++) sum -= rowPtrs[i][j]*b[j]; - b[i]=sum/rowPtrs[i][i]; + sum_=b[i]; + for (unsigned int j=i+1;j<n;j++) sum_ -= rowPtrs[i][j]*b[j]; + b[i]=sum_/rowPtrs[i][i]; } while(i != 0); } #endif // doxygen should skip this @@ -260,7 +260,13 @@ vpMatrix::inverseByLU() const unsigned int *perm = new unsigned int[rowNum]; int p; - A.LUDcmp(perm, p); + try { + A.LUDcmp(perm, p); + } + catch(vpException e) { + delete [] perm; + throw(e); + } vpColVector c_tmp(rowNum) ; for (j=1; j<=rowNum; j++) diff --git a/src/math/matrix/vpMatrix_qr.cpp b/src/math/matrix/vpMatrix_qr.cpp index 8708bed3ad4e4bdf8590f6c2a3ecfc3dfaf13c18..e56d02943a86d047d74d82e9e606d51ab2cd8b7b 100644 --- a/src/math/matrix/vpMatrix_qr.cpp +++ b/src/math/matrix/vpMatrix_qr.cpp @@ -3,7 +3,7 @@ * $Id: vpMatrix_lu.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,6 +39,7 @@ * *****************************************************************************/ +#include <algorithm> // for std::min and std::max #include <visp/vpConfig.h> #include <visp/vpMatrix.h> @@ -52,6 +53,7 @@ // Debug trace #include <visp/vpDebug.h> +int allocate_work(double** work); #ifdef VISP_HAVE_LAPACK extern "C" int dgeqrf_(int *m, int *n, double*a, int *lda, double *tau, double *work, int *lwork, int *info); @@ -63,18 +65,19 @@ extern "C" int dorgqr_(int *, int *, int *, double *, int *, extern "C" int dtrtri_(char *uplo, char *diag, int *n, double *a, int *lda, int *info); #endif -int allocate_work(double** work){ - int dimWork = (int)((*work)[0]); +int allocate_work(double** work) +{ + unsigned int dimWork = (unsigned int)((*work)[0]); delete[] *work; *work = new double[dimWork]; - return dimWork; + return (int)dimWork; } #ifdef VISP_HAVE_LAPACK vpMatrix vpMatrix::inverseByQRLapack() const{ - int rowNum = (int)this->getRows(); - int colNum = (int)this->getCols(); - int lda = (int)rowNum; //lda is the number of rows because we don't use a submatrix - int dimTau = std::min(rowNum,colNum); + int rowNum_ = (int)this->getRows(); + int colNum_ = (int)this->getCols(); + int lda = (int)rowNum_; //lda is the number of rows because we don't use a submatrix + int dimTau = std::min(rowNum_,colNum_); int dimWork = -1; double *tau = new double[dimTau]; double *work = new double[1]; @@ -85,8 +88,8 @@ vpMatrix vpMatrix::inverseByQRLapack() const{ try{ //1) Extract householder reflections (useful to compute Q) and R dgeqrf_( - &rowNum, //The number of rows of the matrix A. M >= 0. - &colNum, //The number of columns of the matrix A. N >= 0. + &rowNum_, //The number of rows of the matrix A. M >= 0. + &colNum_, //The number of columns of the matrix A. N >= 0. A.data, /*On entry, the M-by-N matrix A. On exit, the elements on and above the diagonal of the array contain the min(M,N)-by-N upper trapezoidal matrix R (R is @@ -110,8 +113,8 @@ vpMatrix vpMatrix::inverseByQRLapack() const{ dimWork = allocate_work(&work); dgeqrf_( - &rowNum, //The number of rows of the matrix A. M >= 0. - &colNum, //The number of columns of the matrix A. N >= 0. + &rowNum_, //The number of rows of the matrix A. M >= 0. + &colNum_, //The number of columns of the matrix A. N >= 0. A.data, /*On entry, the M-by-N matrix A. On exit, the elements on and above the diagonal of the array contain the min(M,N)-by-N upper trapezoidal matrix R (R is @@ -156,7 +159,6 @@ vpMatrix vpMatrix::inverseByQRLapack() const{ for(unsigned int j=0;j<C.getRows();j++) if(j>i) C[i][j] = 0.; - dimWork = -1; int ldc = lda; @@ -164,14 +166,14 @@ vpMatrix vpMatrix::inverseByQRLapack() const{ //get R^-1*tQ //C contains R^-1 //A contains Q - dormqr_((char*)"R", (char*)"T", &rowNum, &colNum, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork, &info); + dormqr_((char*)"R", (char*)"T", &rowNum_, &colNum_, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork, &info); if(info != 0){ std::cout << "dormqr_:Preparation"<< -info << "th element had an illegal value" << std::endl; throw vpMatrixException::badValue; } dimWork = allocate_work(&work); - dormqr_((char*)"R", (char*)"T", &rowNum, &colNum, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork, &info); + dormqr_((char*)"R", (char*)"T", &rowNum_, &colNum_, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork, &info); if(info != 0){ std::cout << "dormqr_:"<< -info << "th element had an illegal value" << std::endl; diff --git a/src/math/matrix/vpMatrix_svd.cpp b/src/math/matrix/vpMatrix_svd.cpp index f6783b594a76ea15940f4da39cf16e6502154cce..97ff0d6f2fb38798e4b887ba86ed0c2a7045badb 100644 --- a/src/math/matrix/vpMatrix_svd.cpp +++ b/src/math/matrix/vpMatrix_svd.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMatrix_svd.cpp 4210 2013-04-16 08:57:46Z fspindle $ + * $Id: vpMatrix_svd.cpp 4896 2014-09-10 16:41:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -297,8 +297,10 @@ void vpMatrix::svdNr(vpColVector& W, vpMatrix& V) g=c*g; z=pythag(f,h); rv1[j]=z; - c=f/z; - s=h/z; + if ((std::fabs(z) > epsilon) /*|| (fabs(z) > EPS_SVD)*/) { + c=f/z; + s=h/z; + } f=x*c+g*s; g=g*c-x*s; h=y*s; @@ -593,7 +595,9 @@ void vpMatrix::svdLapack(vpColVector& W, vpMatrix& V){ dgesdd_( (char*)"S", &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, iwork, &info ); if( info > 0 ) { - std::cout << "The algorithm computing SVD failed to converge." << std::endl; + vpTRACE("The algorithm computing SVD failed to converge."); + throw(vpMatrixException(vpMatrixException::fatalError, + "The algorithm computing SVD failed to converge.")) ; } diff --git a/src/math/matrix/vpRowVector.cpp b/src/math/matrix/vpRowVector.cpp index 03a4adc104b3dbecc1d8d70bd633399b2b1b93af..2f8ca5e88319649b5a6553b1da9870adf16ea50b 100644 --- a/src/math/matrix/vpRowVector.cpp +++ b/src/math/matrix/vpRowVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRowVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRowVector.cpp 5185 2015-01-21 14:36:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -115,9 +115,9 @@ double vpRowVector::operator*(const vpColVector &x) const { unsigned int nelements = x.getRows(); if (getCols() != nelements ) { - vpERROR_TRACE("\n\t\t Illegal matrix operation") ; - throw(vpMatrixException(vpMatrixException::matrixError, - "\n\t\t Illegal matrix operation, bad vector size")) ; + throw(vpException(vpException::dimensionError, + "Bad size during vpRowVector (1x%d) by vpColVector (%dx1) multiplication", + colNum, x.getRows())) ; } double scalar = 0.0; @@ -144,13 +144,13 @@ double vpRowVector::operator*(const vpColVector &x) const */ vpRowVector vpRowVector::operator*(const vpMatrix &A) const { - vpRowVector c(A.getCols()); if (colNum != A.getRows()) { vpERROR_TRACE("vpMatrix mismatch in vpRowVector/matrix multiply") ; - throw(vpMatrixException::incorrectMatrixSizeError) ; + throw(vpMatrixException(vpMatrixException::incorrectMatrixSizeError, + "vpMatrix mismatch in vpRowVector/matrix multiply")) ; } c = 0.0; @@ -167,6 +167,87 @@ vpRowVector vpRowVector::operator*(const vpMatrix &A) const return c ; } +//! Operator A = -A +vpRowVector vpRowVector::operator-() const +{ + vpRowVector A ; + try { + A.resize(colNum) ; + } + catch(vpException me) + { + vpERROR_TRACE("Error caught") ; + throw ; + } + + double *vd = A.data ; double *d = data ; + + for (unsigned int i=0; i<colNum; i++) + *(vd++)= - (*d++); + + return A; +} + +//! Substraction of two vectors V = A-v +vpRowVector vpRowVector::operator-(const vpRowVector &m) const +{ + if (getCols() != m.getCols() ) { + throw(vpException(vpException::dimensionError, + "Bad size during vpRowVector (1x%d) and vpRowVector (1x%d) substraction", + getCols(), m.getCols())) ; + } + + vpRowVector v(colNum) ; + + for (unsigned int i=0;i<colNum;i++) + v[i] = (*this)[i] - m[i]; + return v; +} + +//! Addition of two vectors V = A-v +vpRowVector vpRowVector::operator+(const vpRowVector &m) const +{ + if (getCols() != m.getCols() ) { + throw(vpException(vpException::dimensionError, + "Bad size during vpRowVector (1x%d) and vpRowVector (1x%d) substraction", + getCols(), m.getCols())) ; + } + + vpRowVector v(colNum) ; + + for (unsigned int i=0;i<colNum;i++) + v[i] = (*this)[i] + m[i]; + return v; +} + +/*! + Copy operator. + Allows operation such as A << v + \code +#include <visp/vpRowVector.h> + +int main() +{ + vpRowVector A, B(5); + for (unsigned int i=0; i<B.size(); i++) + B[i] = i; + A << B; + std::cout << "A: \n" << A << std::endl; +} + \endcode + In row vector A we get: + \code +A: +0 1 2 3 4 + \endcode + + */ +vpRowVector & vpRowVector::operator<<(const vpRowVector &v) +{ + *this = v; + return *this; +} + /*! \brief Transpose the row vector A @@ -221,9 +302,8 @@ vpRowVector &vpRowVector::normalize(vpRowVector &x) const */ vpRowVector &vpRowVector::normalize() { - - double sum = sumSquare() ; - *this /= sum ; + double sum_square = sumSquare() ; + *this /= sum_square ; return *this; } @@ -267,3 +347,46 @@ void vpRowVector::reshape(vpMatrix & m,const unsigned int &nrows,const unsigned for(unsigned int j =0; j< ncols; j++) m[i][j]=data[i*nrows+j]; } + +/*! + Insert a row vector. + \param i : Index of the first element to introduce. This index starts from 0. + \param v : Row vector to insert. + + The following example shows how to use this function: + \code +#include <visp/vpRowVector.h> + +int main() +{ + vpRowVector v(4); + for (unsigned int i=0; i < v.size(); i++) + v[i] = i; + std::cout << "v:\n" << v << std::endl; + + vpRowVector w(2); + for (unsigned int i=0; i < w.size(); i++) + w[i] = i+10; + std::cout << "w:\n" << w << std::endl; + + v.insert(1, w); + std::cout << "v:\n" << v << std::endl; +} + \endcode + It produces the following output: + \code +v: +0 1 2 3 +w: +10 11 +v: +0 10 11 3 + \endcode + */ +void vpRowVector::insert(unsigned int i, const vpRowVector &v) +{ + if (i+v.size() > this->size()) + throw(vpException(vpException::dimensionError, "Unable to insert a row vector")); + for (unsigned int j=0; j < v.size(); j++) + (*this)[i+j] = v[j]; +} diff --git a/src/math/matrix/vpRowVector.h b/src/math/matrix/vpRowVector.h index 562a1fa127124db7a099f3d65044509714547af3..e16a36db12e657be6b38e5dc1ea992b82b7e03a8 100644 --- a/src/math/matrix/vpRowVector.h +++ b/src/math/matrix/vpRowVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRowVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRowVector.h 5185 2015-01-21 14:36:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,15 +80,25 @@ protected: vpRowVector(vpMatrix &m, unsigned int i); public: - //! basic constructor + //! Basic constructor. vpRowVector() : vpMatrix() {}; - //! constructor of vector of size n - vpRowVector(unsigned int nn) : vpMatrix(1,nn){}; - //! copy constructor + //! Constructor of vector of size n. + vpRowVector(unsigned int n) : vpMatrix(1, n){}; + //! Constructor of vector of size n. Each element is set to \e val. + vpRowVector(unsigned int n, double val) : vpMatrix(1, n, val){}; + //! Copy constructor. vpRowVector(const vpRowVector &v); - //! Set the size of the Row vector - inline void resize(unsigned int i) { vpMatrix::resize(1, i) ; } + void insert(unsigned int i, const vpRowVector &v); + + /*! Set the size of the row vector. + \param i : Column vector size. + \param flagNullify : If true, set the data to zero. + */ + inline void resize(const unsigned int i, const bool flagNullify = true) + { + vpMatrix::resize(1, i, flagNullify); + } //! Access V[i] = x inline double &operator [](unsigned int n) { return *(data+n); } //! Access x = V[i] @@ -98,25 +108,35 @@ public: vpRowVector &operator=(const vpRowVector &v); //! copy from a matrix vpRowVector & operator=(const vpMatrix &m) ; + //! Initialize each element of the vector to x + vpRowVector& operator=(const double x); //!operator dot product double operator*(const vpColVector &x) const; //!operator dot product vpRowVector operator*(const vpMatrix &A) const; - - //! initialisation each element of the vector is x - vpRowVector& operator=(const double x); - //! Reshape methods + //! Addition of two vectors V = A+v + vpRowVector operator+(const vpRowVector &v) const; + + //! Substraction of two vectors V = A-v + vpRowVector operator-(const vpRowVector &v) const; + + //! Operator A = -A + vpRowVector operator-() const; + // Copy operator. Allow operation such as A << v + vpRowVector &operator<<(const vpRowVector &v); + + //! Reshape methods. void reshape(vpMatrix & m,const unsigned int &nrows,const unsigned int &ncols); vpMatrix reshape(const unsigned int &nrows,const unsigned int &ncols); - //! Transpose the vector + //! Transpose the vector. vpColVector t() const; - //! normalise the vector + //! Normalise the vector. vpRowVector &normalize() ; - //! normalise the vector + //! Normalise the vector. vpRowVector &normalize(vpRowVector &x) const ; /*! diff --git a/src/math/matrix/vpSubColVector.cpp b/src/math/matrix/vpSubColVector.cpp index 4141fe426a6e27f371e70003ca2d2dd52cc34e80..4b93b5f0b0c9861a22fefb9002451c5df7f12b92 100644 --- a/src/math/matrix/vpSubColVector.cpp +++ b/src/math/matrix/vpSubColVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSubColVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSubColVector.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,15 +46,9 @@ #include <visp/vpDebug.h> #include <stdlib.h> -vpSubColVector::vpSubColVector(){ - data=NULL; - parent=NULL; - rowPtrs=NULL; - rowNum=0; - colNum=0; - pRowNum=0; - dsize=0; - trsize=0; +vpSubColVector::vpSubColVector() + : pRowNum(0), parent(NULL) +{ } /*! @@ -63,7 +57,9 @@ vpSubColVector::vpSubColVector(){ \param offset : offset where subColVector start in the parent colVector \param nrows : size of the subColVector */ -vpSubColVector::vpSubColVector(vpColVector &v, const unsigned int & offset,const unsigned int & nrows){ +vpSubColVector::vpSubColVector(vpColVector &v, const unsigned int & offset, const unsigned int & nrows) + : pRowNum(0), parent(NULL) +{ init(v,offset,nrows); } diff --git a/src/math/matrix/vpSubColVector.h b/src/math/matrix/vpSubColVector.h index 055091e8234077dd52f9e3baecb922ef58e48f5d..59cd355c284c03a7d7184e2eedc190a06e891754 100644 --- a/src/math/matrix/vpSubColVector.h +++ b/src/math/matrix/vpSubColVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSubColVector.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpSubColVector.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/matrix/vpSubMatrix.cpp b/src/math/matrix/vpSubMatrix.cpp index 9a6939c1e9e8bcca4a580b0d5781513cf35ee7c4..6ba81a2f45d1684ed5457eaa774f386f342a49ba 100644 --- a/src/math/matrix/vpSubMatrix.cpp +++ b/src/math/matrix/vpSubMatrix.cpp @@ -1,11 +1,11 @@ /**************************************************************************** * - * $Id: vpSubMatrix.cpp 4057 2013-01-05 13:10:29Z fspindle $ + * $Id: vpSubMatrix.cpp 4649 2014-02-07 14:57:11Z fspindle $ * - * Copyright (C) 2005 - 2013 Inria. All rights reserved. + * Copyright (C) 2005 - 2014 Inria. All rights reserved. * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,39 +46,35 @@ #include <visp/vpDebug.h> #include <stdlib.h> -vpSubMatrix::vpSubMatrix(){ - data=NULL; - parent=NULL; - rowPtrs=NULL; - rowNum=0; - colNum=0; - pRowNum=0; - pColNum=0; - dsize=0; - trsize=0; +vpSubMatrix::vpSubMatrix() + : pRowNum(0), pColNum(0), parent(NULL) +{ } /*! \brief Constructor \param m : parent matrix - \param row : row offset - \param col : col offset + \param row_offset : row offset + \param col_offset : col offset \param nrows : number of rows of the sub matrix \param ncols : number of columns of the sub matrix */ -vpSubMatrix::vpSubMatrix(vpMatrix &m, const unsigned int & row, const unsigned int &col , const unsigned int & nrows , const unsigned int & ncols){ - init(m,row,col,nrows,ncols); +vpSubMatrix::vpSubMatrix(vpMatrix &m, const unsigned int &row_offset, const unsigned int &col_offset, + const unsigned int & nrows, const unsigned int & ncols) + : pRowNum(0), pColNum(0), parent(NULL) +{ + init(m,row_offset,col_offset,nrows,ncols); } /*! \brief Initialisation of a sub matrix \param m : parent matrix - \param row : row offset - \param col : col offset + \param row_offset : row offset + \param col_offset : col offset \param nrows : number of rows of the sub matrix \param ncols : number of columns of the sub matrix */ -void vpSubMatrix::init(vpMatrix &m, const unsigned int & row, const unsigned int &col , const unsigned int & nrows , const unsigned int & ncols){ +void vpSubMatrix::init(vpMatrix &m, const unsigned int &row_offset, const unsigned int &col_offset , const unsigned int & nrows , const unsigned int & ncols){ if(! m.data){ vpERROR_TRACE("\n\t\t SubMatrix parent matrix is not allocated") ; @@ -86,7 +82,7 @@ void vpSubMatrix::init(vpMatrix &m, const unsigned int & row, const unsigned int "\n\t\t SubMatrix parent matrix is not allocated")) ; } - if(row+nrows <= m.getRows() && col+ncols <= m.getCols()){ + if(row_offset+nrows <= m.getRows() && col_offset+ncols <= m.getCols()){ data=m.data; parent =&m; rowNum = nrows; @@ -99,7 +95,7 @@ void vpSubMatrix::init(vpMatrix &m, const unsigned int & row, const unsigned int rowPtrs=(double**) malloc(nrows * sizeof(double*)); for(unsigned int r=0;r<nrows;r++) - rowPtrs[r]= m.data+col+(r+row)*pColNum; + rowPtrs[r]= m.data+col_offset+(r+row_offset)*pColNum; dsize = pRowNum*pColNum ; trsize =0 ; diff --git a/src/math/matrix/vpSubMatrix.h b/src/math/matrix/vpSubMatrix.h index b33af1bdb76a71c1180d7010a6d7c012c5e1c9c0..9dcae3e89a0e38f86b72357c73bec42b1d79765d 100644 --- a/src/math/matrix/vpSubMatrix.h +++ b/src/math/matrix/vpSubMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSubMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSubMatrix.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/matrix/vpSubRowVector.cpp b/src/math/matrix/vpSubRowVector.cpp index c206161152edcdbb092b5b6c5ff8940df3f359ee..935f0cb27685f170005c84250a700f8fc3a2f5d0 100644 --- a/src/math/matrix/vpSubRowVector.cpp +++ b/src/math/matrix/vpSubRowVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSubRowVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSubRowVector.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,16 +46,10 @@ #include <visp/vpDebug.h> #include <stdlib.h> - vpSubRowVector::vpSubRowVector(){ - data=NULL; - parent=NULL; - rowPtrs=NULL; - rowNum=0; - colNum=0; - pColNum=0; - dsize=0; - trsize=0; - } +vpSubRowVector::vpSubRowVector() + : pColNum(0), parent(NULL) +{ +} /*! \brief Constructor @@ -63,8 +57,10 @@ \param offset : offset where subRowVector start in the parent vector \param ncols : size of the subRowVector */ -vpSubRowVector::vpSubRowVector(vpRowVector &v, const unsigned int & offset,const unsigned int & ncols){ - init(v,offset,ncols); +vpSubRowVector::vpSubRowVector(vpRowVector &v, const unsigned int & offset,const unsigned int & ncols) + : pColNum(0), parent(NULL) +{ + init(v, offset, ncols); } /*! diff --git a/src/math/matrix/vpSubRowVector.h b/src/math/matrix/vpSubRowVector.h index 331220e4848373dc211910e207837bf2fb5f2389..eb91bfaa420a8ac19b6b16389c4e6698b2f093b9 100644 --- a/src/math/matrix/vpSubRowVector.h +++ b/src/math/matrix/vpSubRowVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSubRowVector.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpSubRowVector.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/misc/vpHinkley.cpp b/src/math/misc/vpHinkley.cpp index 73b490aa3a8345fbc5468e5c5f4fa3627c04d4c1..c24c1689366c79373d58ec70ed8104033e52963f 100755 --- a/src/math/misc/vpHinkley.cpp +++ b/src/math/misc/vpHinkley.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHinkley.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHinkley.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,11 +78,8 @@ */ vpHinkley::vpHinkley() + : dmin2(0.1), alpha(0.2), nsignal(0), mean(0), Sk(0), Mk(0), Tk(0), Nk(0) { - init(); - - setAlpha(0.2); - setDelta(0.2); } /*! @@ -92,21 +89,18 @@ vpHinkley::vpHinkley() Call init() to initialise the Hinkley's test and set \f$\alpha\f$ and \f$\delta\f$ thresholds. - \param alpha : \f$\alpha\f$ is a predefined threshold. + \param alpha_val : \f$\alpha\f$ is a predefined threshold. - \param delta : \f$\delta\f$ denotes the jump minimal magnitude that + \param delta_val : \f$\delta\f$ denotes the jump minimal magnitude that we want to detect. \sa setAlpha(), setDelta() */ -vpHinkley::vpHinkley(double alpha, double delta) +vpHinkley::vpHinkley(double alpha_val, double delta_val) + : dmin2(delta_val/2.), alpha(alpha_val), nsignal(0), mean(0), Sk(0), Mk(0), Tk(0), Nk(0) { - init(); - - setAlpha(alpha); - setDelta(delta); } /*! @@ -114,21 +108,21 @@ vpHinkley::vpHinkley(double alpha, double delta) Call init() to initialise the Hinkley's test and set \f$\alpha\f$ and \f$\delta\f$ thresholds. - \param alpha : \f$\alpha\f$ is a predefined threshold. + \param alpha_val : \f$\alpha\f$ is a predefined threshold. - \param delta : \f$\delta\f$ denotes the jump minimal magnitude that + \param delta_val : \f$\delta\f$ denotes the jump minimal magnitude that we want to detect. \sa setAlpha(), setDelta() */ void -vpHinkley::init(double alpha, double delta) +vpHinkley::init(double alpha_val, double delta_val) { init(); - setAlpha(alpha); - setDelta(delta); + setAlpha(alpha_val); + setDelta(delta_val); } /*! @@ -178,9 +172,9 @@ void vpHinkley::setDelta(double delta) \sa setDelta() */ -void vpHinkley::setAlpha(double alpha) +void vpHinkley::setAlpha(double alpha_val) { - this->alpha = alpha; + this->alpha = alpha_val; } /*! @@ -202,7 +196,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testDownwardJump(double signal) if (nsignal == 1) mean = signal; - // Calcul des variables cumulées + // Calcul des variables cumulees computeSk(signal); computeMk(); @@ -210,7 +204,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testDownwardJump(double signal) vpCDEBUG(2) << "alpha: " << alpha << " dmin2: " << dmin2 << " signal: " << signal << " Sk: " << Sk << " Mk: " << Mk; - // teste si les variables cumulées excèdent le seuil + // teste si les variables cumulees excedent le seuil if ((Mk - Sk) > alpha) jump = downwardJump; @@ -260,7 +254,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testUpwardJump(double signal) if (nsignal == 1) mean = signal; - // Calcul des variables cumulées + // Calcul des variables cumulees computeTk(signal); computeNk(); @@ -268,7 +262,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testUpwardJump(double signal) vpCDEBUG(2) << "alpha: " << alpha << " dmin2: " << dmin2 << " signal: " << signal << " Tk: " << Tk << " Nk: " << Nk; - // teste si les variables cumulées excèdent le seuil + // teste si les variables cumulees excedent le seuil if ((Tk - Nk) > alpha) jump = upwardJump; @@ -317,7 +311,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testDownUpwardJump(double signal) if (nsignal == 1) mean = signal; - // Calcul des variables cumulées + // Calcul des variables cumulees computeSk(signal); computeTk(signal); @@ -329,7 +323,7 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testDownUpwardJump(double signal) << " Sk: " << Sk << " Mk: " << Mk << " Tk: " << Tk << " Nk: " << Nk << std::endl; - // teste si les variables cumulées excèdent le seuil + // teste si les variables cumulees excedent le seuil if ((Mk - Sk) > alpha) jump = downwardJump; else if ((Tk - Nk) > alpha) @@ -376,10 +370,10 @@ vpHinkley::vpHinkleyJumpType vpHinkley::testDownUpwardJump(double signal) void vpHinkley::computeMean(double signal) { // Debut modif FS le 03/09/2003 - // Lors d'une chute ou d'une remontée lente du signal, pariculièrement - // après un saut, la moyenne a tendance à "dériver". Pour réduire ces - // dérives de la moyenne, elle n'est remise à jour avec la valeur - // courante du signal que si un début de saut potentiel n'est pas détecté. + // Lors d'une chute ou d'une remontee lente du signal, pariculierement + // apres un saut, la moyenne a tendance a "deriver". Pour reduire ces + // derives de la moyenne, elle n'est remise a jour avec la valeur + // courante du signal que si un debut de saut potentiel n'est pas detecte. //if ( ((Mk-Sk) == 0) && ((Tk-Nk) == 0) ) if ( ( std::fabs(Mk-Sk) <= std::fabs(vpMath::maximum(Mk,Sk))*std::numeric_limits<double>::epsilon() ) && @@ -400,7 +394,7 @@ void vpHinkley::computeMean(double signal) void vpHinkley::computeSk(double signal) { - // Calcul des variables cumulées + // Calcul des variables cumulees Sk += signal - mean + dmin2; } /*! @@ -421,7 +415,7 @@ void vpHinkley::computeMk() void vpHinkley::computeTk(double signal) { - // Calcul des variables cumulées + // Calcul des variables cumulees Tk += signal - mean - dmin2; } /*! diff --git a/src/math/misc/vpHinkley.h b/src/math/misc/vpHinkley.h index 197e6a17e8b3ac7ea39a0dfaad2f6dfb7c3c5996..97fad8379916e277e155049fffb05577c56e6b09 100755 --- a/src/math/misc/vpHinkley.h +++ b/src/math/misc/vpHinkley.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHinkley.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHinkley.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/misc/vpMath.cpp b/src/math/misc/vpMath.cpp index 692c83ffd270e5d661c18c993ad168b32362e649..ac637a1169ca617766b6d441cb45eb5d39c0d102 100644 --- a/src/math/misc/vpMath.cpp +++ b/src/math/misc/vpMath.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMath.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMath.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/misc/vpMath.h b/src/math/misc/vpMath.h index 2d991ea937ef5d7379d3997a055b9acad1031cc3..491fcf6a4aeeeea6d4bced41d78473309e1f31c3 100644 --- a/src/math/misc/vpMath.h +++ b/src/math/misc/vpMath.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMath.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMath.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ #include <math.h> -#ifdef WIN32 // Not defined in Microsoft math.h +#if defined(_WIN32) // Not defined in Microsoft math.h # ifndef M_PI # define M_PI 3.14159265358979323846f diff --git a/src/math/misc/vpNoise.cpp b/src/math/misc/vpNoise.cpp index 72553f880655d26e14ed9105e97248c5fc2b3890..39f9ef766b34ccd29aedcbb23e83418d2d743da8 100644 --- a/src/math/misc/vpNoise.cpp +++ b/src/math/misc/vpNoise.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNoise.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpNoise.cpp 4975 2014-11-17 15:59:42Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,23 +44,17 @@ /*! \file vpNoise.cpp - \brief Classe for generating random number - with uniform and normal probability density + \brief Class for generating random numbers with uniform and normal probability density. - The algorithms and notations used are described in + The algorithms and notations used are described in \cite Gentle:2004. - James E. Gentle, Random Number Generation and Monte Carlo Methods, - Springer 1998 */ -/*! \brief Minimal random number generator of Park and Miller. Returns a +/*! + Minimal random number generator of Park and Miller \cite Park:1988. Returns a uniform random deviate between 0.0 and 1.0. - S.K. Park and K.W. Miller, " Random Number Generators: Good Ones Are Hard To - Find", Communications of the ACM, October 1988, pp. 1192-1201. - - */ inline void vpUniRand::draw0() @@ -73,7 +67,7 @@ vpUniRand::draw0() } /*! - \brief Bays-Durham Shuffling of Park-Miller generator + Bays-Durham Shuffling of Park-Miller generator. Minimal random number generator of Park and Miller with Bays-Durham shuffle. Returns a uniform random deviate between 0.0 and 1.0 (exclusive of @@ -118,11 +112,11 @@ vpUniRand::draw1() } /*! - \brief Generate a normal random variable using the Box-Muller generator + Generate a normal random variable using the Box-Muller generator. Generate a normal random variable with mean 0 and standard deviation of 1. - To adjust to some other distribution, multiply by the standard deviation and - add the mean. Box-Muller method + To adjust to some other distribution, multiply by the standard deviation and + add the mean. Box-Muller method */ double vpGaussRand::gaussianDraw() diff --git a/src/math/misc/vpNoise.h b/src/math/misc/vpNoise.h index ac9289d7c95c8ee7b091ac412a693766d48269d2..bb0e0582e59c5c7d57674e886f66f1bc8a2fd99c 100644 --- a/src/math/misc/vpNoise.h +++ b/src/math/misc/vpNoise.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNoise.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpNoise.h 5179 2015-01-16 09:42:33Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,10 +49,8 @@ \brief Class for generating random number with uniform and normal probability density - The algorithms and notations used are described in + The algorithms and notations used are described in \cite Gentle:2004. - James E. Gentle, Random Number Generation and Monte Carlo Methods, - Springer 1998 */ #include <visp/vpConfig.h> @@ -64,44 +62,31 @@ \ingroup Random \brief Class for generating random numbers with uniform probability density. - The algorithms and notations used are described in - Random Number Generation and Monte Carlo Methods - James E. Gentle, Springer 1998 + The algorithms and notations used are described in \cite Gentle:2004. */ class VISP_EXPORT vpUniRand { - - - /*unsigned*/ long a ; - /*unsigned*/ long m ; //2^31-1 - /*unsigned*/ long q ; //integer part of m/a - /*unsigned*/ long r ;//r=m mod a - double normalizer ; //we use a normalizer > m to ensure ans will never be 1 (it is the case if x = 739806647) - - -private: - void draw0(); -protected: - long x; - double draw1(); - void init() - { - a = 16807 ; - m = /*(unsigned long)*/2147483647 ; //2^31-1 - q = 127773 ; //integer part of m/a - r = 2836 ;//r=m mod a - //we use a normalizer > m to ensure ans will never be - // 1 (it is the case if x = 739806647) - normalizer = 2147484721.0 ; - } - -public: - vpUniRand(const long seed = 0):x((seed)? seed : 739806647) - { - init() ; - } - double operator()() {return draw1();} + /*unsigned*/ long a ; + /*unsigned*/ long m ; //2^31-1 + /*unsigned*/ long q ; //integer part of m/a + /*unsigned*/ long r ;//r=m mod a + double normalizer ; //we use a normalizer > m to ensure ans will never be 1 (it is the case if x = 739806647) + + private: + void draw0(); + protected: + long x; + double draw1(); + + public: + vpUniRand(const long seed = 0) + : a(16807), m(2147483647), q(127773), r(2836), normalizer(2147484721.0), x((seed)? seed : 739806647) + { + } + virtual ~vpUniRand() {}; + + double operator()() {return draw1();} }; @@ -110,60 +95,125 @@ public: \ingroup Random \brief Class for generating random number with normal probability density. - The algorithms and notations used are described in "Random Number Generation and Monte Carlo Methods", - James E. Gentle, Springer 1998 + The algorithms and notations used are described in \cite Gentle:2004. - The code below shows how to use the random generator. + The code below shows how to use the random generator to get values that have their mean equal to + 10 with a standart deviation equal to 0.5. \code #include <iostream> +#include <visp/vpNoise.h> + +int main() +{ + vpGaussRand noise(0.5, 10); + for(int i=0; i< 10; i++) { + std::cout << "noise " << i << ": " << noise() << std::endl; + } + return 0; +} + \endcode + The previous example produces the following printings: +\code +noise 0: 9.43873 +noise 1: 10.1977 +noise 2: 10.8145 +noise 3: 9.13729 +noise 4: 8.86476 +noise 5: 9.83382 +noise 6: 9.43609 +noise 7: 9.34311 +noise 8: 9.62742 +noise 9: 9.37701 +\endcode + + Note that the previous example produces always the same "random" results. To produce real random + values, you need to initialize the random generator with different values using seed(). For example, + this could be done using the current time. The code becomes: + +\code +#include <iostream> #include <visp/vpNoise.h> +#include <visp/vpTime.h> int main() { - vpGaussRand noise(0.5, 0); + vpGaussRand noise(0.5, 10); + long seed = (long)vpTime::measureTimeMs(); + + noise.seed(seed); for(int i=0; i< 10; i++) { std::cout << "noise " << i << ": " << noise() << std::endl; } return 0; } +\endcode + + Now if you run the previous example you will always get different values: + \code +noise 0: 10.5982 +noise 1: 9.19111 +noise 2: 9.82498 +noise 3: 9.07857 +noise 4: 9.9285 +noise 5: 10.3688 +noise 6: 9.75621 +noise 7: 10.3259 +noise 8: 10.4238 +noise 9: 10.2391 \endcode */ -class vpGaussRand : public vpUniRand +class VISP_EXPORT vpGaussRand : public vpUniRand { - double mean; - double sigma; - -public: - - // Initialization - vpGaussRand() {init();mean=0;sigma=0;x=739806647;} - vpGaussRand(const double sqrtvariance, - const double _mean, - const long seed = 0):mean(_mean), sigma(sqrtvariance) - { - init() ; - mean = 0 ; - if (seed) x=seed; else x=739806647; - } - /*! - Set the standard deviation and mean for gaussian noise - - \param _s new standard deviation - \param _m new mean - */ - inline void setSigmaMean(const double _s, const double _m) {mean=_m;sigma=_s;} - /*! - Set the seed of the noise - - \param seed new seed - */ - inline void seed(const long seed) {x=seed;} - inline double operator()() {return sigma*gaussianDraw()+mean;} - -private : - double gaussianDraw(); + private : + double mean; + double sigma; + double gaussianDraw(); + + public: + + /*! + Default noise generator constructor. + */ + vpGaussRand() : vpUniRand(), mean(0), sigma(0) {} + + /*! + Gaussian noise random generator constructor. + + \param sigma_val : Standard deviation. + \param mean_val : Mean value. + \param noise_seed : Seed of the noise + */ + vpGaussRand(const double sigma_val, const double mean_val, const long noise_seed = 0) + : vpUniRand(noise_seed), mean(mean_val), sigma(sigma_val) {} + + /*! + Set the standard deviation and mean for gaussian noise. + + \param sigma_val : New standard deviation sigma. + \param mean_val : New mean value. + */ + void setSigmaMean(const double sigma_val, const double mean_val) { + this->mean = mean_val; + this->sigma = sigma_val; + } + + /*! + Set the seed of the noise. + + \param seed_val : New seed. + */ + void seed(const long seed_val) { + x=seed_val; + } + + /*! + Return a random value from the Gaussian noise generator. + */ + double operator()() { + return sigma*gaussianDraw()+mean; + } }; #endif diff --git a/src/math/robust/vpRansac.h b/src/math/robust/vpRansac.h index 5f4d1d347350395a87debb018bf64944aba3048e..b3029d905e8f3f54faa3e083e25933eba79bd334 100644 --- a/src/math/robust/vpRansac.h +++ b/src/math/robust/vpRansac.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRansac.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRansac.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,16 +63,7 @@ \brief This class is a generic implementation of the Ransac algorithm. It cannot be used alone. - Creation: june, 15 2005 - - RANSAC is described in : - - M.A. Fishler and R.C. Boles. "Random sample consensus: A paradigm for model - fitting with applications to image analysis and automated cartography". Comm. - Assoc. Comp, Mach., Vol 24, No 6, pp 381-395, 1981 - - Richard Hartley and Andrew Zisserman. "Multiple View Geometry in - Computer Vision". pp 101-113. Cambridge University Press, 2001 + RANSAC is described in \cite Fischler81 and \cite Hartley01a. The code of this class is inspired by : Peter Kovesi @@ -81,14 +72,9 @@ pk at csse uwa edu au http://www.csse.uwa.edu.au/~pk - \sa vpHomography - */ - - - template <class vpTransformation> class vpRansac { @@ -114,7 +100,7 @@ public: npts is the number of data points. \param s : The minimum number of samples from x required by fitting fn to - fit a model. + fit a model. Value should be greater or equal to 4. \param t : The distance threshold between data point and the model used to decide whether a point is an inlier or not. @@ -141,22 +127,22 @@ vpRansac<vpTransformation>::ransac(unsigned int npts, vpColVector &x, vpColVector &inliers, int consensus, double /* areaThreshold */, - const int maxNbumbersOfTrials - ) + const int maxNbumbersOfTrials) { - - -/* bool isplanar; */ -/* if (s == 4) isplanar = true; */ -/* else isplanar = false; */ + /* bool isplanar; */ + /* if (s == 4) isplanar = true; */ + /* else isplanar = false; */ double eps = 1e-6 ; double p = 0.99; // Desired probability of choosing at least one sample - // free from outliers + // free from outliers int maxTrials = maxNbumbersOfTrials; // Maximum number of trials before we give up. int maxDataTrials = 1000; // Max number of attempts to select a non-degenerate - // data set. + // data set. + + if (s<4) + s = 4; // Sentinel value allowing detection of solution failure. bool solutionFind = false ; @@ -184,7 +170,7 @@ vpRansac<vpTransformation>::ransac(unsigned int npts, vpColVector &x, { // Generate s random indicies in the range 1..npts for (unsigned int i=0 ; i < s ; i++) - ind[i] = (unsigned int)ceil(random()*npts) -1; + ind[i] = (unsigned int)ceil(random()*npts) -1; // Test that these points are not a degenerate configuration. degenerate = vpTransformation::degenerateConfiguration(x,ind) ; @@ -194,10 +180,10 @@ vpRansac<vpTransformation>::ransac(unsigned int npts, vpColVector &x, count = count + 1; if (count > maxDataTrials) { - delete [] ind; - vpERROR_TRACE("Unable to select a nondegenerate data set"); - throw(vpException(vpException::fatalError, "Unable to select a nondegenerate data set")); - //return false; //Useless after a throw() function + delete [] ind; + vpERROR_TRACE("Unable to select a nondegenerate data set"); + throw(vpException(vpException::fatalError, "Unable to select a nondegenerate data set")); + //return false; //Useless after a throw() function } } // Fit model to this random selection of data points. @@ -215,9 +201,9 @@ vpRansac<vpTransformation>::ransac(unsigned int npts, vpColVector &x, double resid = fabs(d[i]); if (resid < t) { - inliers[i] = 1 ; - ninliers++ ; - residual += fabs(d[i]); + inliers[i] = 1 ; + ninliers++ ; + residual += fabs(d[i]); } else inliers[i] = 0; } diff --git a/src/math/robust/vpRobust.cpp b/src/math/robust/vpRobust.cpp index d404fcf28a0ae86ff2ef906b5749ae464096a749..86c0aab3e1741cc98d58e4e285948cb62a22b8e2 100644 --- a/src/math/robust/vpRobust.cpp +++ b/src/math/robust/vpRobust.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobust.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobust.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,16 +70,14 @@ */ vpRobust::vpRobust(unsigned int n_data) + : normres(), sorted_normres(), sorted_residues(), NoiseThreshold(0.0017), sig_prev(0), it(0), swap(0), size(n_data) { vpCDEBUG(2) << "vpRobust constructor reached" << std::endl; - size=n_data; normres.resize(n_data); sorted_normres.resize(n_data); sorted_residues.resize(n_data); - it=0; - NoiseThreshold=0.0017; //Can not be more accurate than 1 pixel - + // NoiseThreshold=0.0017; //Can not be more accurate than 1 pixel } /*! @@ -153,7 +151,7 @@ void vpRobust::MEstimator(const vpRobustEstimatorType method, unsigned int ind_med = (unsigned int)(ceil(n_data/2.0))-1; // Calculate median - med = select(sorted_residues, 0, n_data-1, ind_med/*(int)n_data/2*/); + med = select(sorted_residues, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); //residualMedian = med ; // Normalize residues @@ -165,7 +163,7 @@ void vpRobust::MEstimator(const vpRobustEstimatorType method, } // Calculate MAD - normmedian = select(sorted_normres, 0, n_data-1, ind_med/*(int)n_data/2*/); + normmedian = select(sorted_normres, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); //normalizedResidualMedian = normmedian ; // 1.48 keeps scale estimate consistent for a normal probability dist. sigma = 1.4826*normmedian; // median Absolute Deviation @@ -314,7 +312,7 @@ double vpRobust::computeNormalizedMedian(vpColVector &all_normres, // calculation. unsigned int ind_med = (unsigned int)(ceil(n_data/2.0))-1; - med = select(sorted_residues, 0, n_data-1, ind_med/*(int)n_data/2*/); + med = select(sorted_residues, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); unsigned int i; // Normalize residues @@ -331,7 +329,7 @@ double vpRobust::computeNormalizedMedian(vpColVector &all_normres, //normmedian = Median(normres, weights); //normmedian = Median(normres); - normmedian = select(sorted_normres, 0, n_data-1, ind_med/*(int)n_data/2*/); + normmedian = select(sorted_normres, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); return normmedian; } @@ -354,7 +352,7 @@ vpRobust::simultMEstimator(vpColVector &residues) double sigma=0; // Standard Deviation unsigned int n_data = residues.getRows(); - vpColVector normres(n_data); // Normalized Residue + vpColVector norm_res(n_data); // Normalized Residue vpColVector w(n_data); vpCDEBUG(2) << "vpRobust MEstimator reached. No. data = " << n_data @@ -362,18 +360,18 @@ vpRobust::simultMEstimator(vpColVector &residues) // Calculate Median unsigned int ind_med = (unsigned int)(ceil(n_data/2.0))-1; - med = select(residues, 0, n_data-1, ind_med/*(int)n_data/2*/); + med = select(residues, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); // Normalize residues for(unsigned int i=0; i<n_data; i++) - normres[i] = (fabs(residues[i]- med)); + norm_res[i] = (fabs(residues[i]- med)); // Check for various methods. // For Huber compute Simultaneous scale estimate // For Others use MAD calculated on first iteration if(it==0) { - normmedian = select(normres, 0, n_data-1, ind_med/*(int)n_data/2*/); + normmedian = select(norm_res, 0, (int)n_data-1, (int)ind_med/*(int)n_data/2*/); // 1.48 keeps scale estimate consistent for a normal probability dist. sigma = 1.4826*normmedian; // Median Absolute Deviation } @@ -390,10 +388,9 @@ vpRobust::simultMEstimator(vpColVector &residues) sigma= NoiseThreshold; } - vpCDEBUG(2) << "MAD and C computed" << std::endl; - psiHuber(sigma, normres,w); + psiHuber(sigma, norm_res,w); sig_prev = sigma; @@ -724,21 +721,21 @@ void vpRobust::psiMcLure(double sig, vpColVector &r,vpColVector &weights) \param l : first value to be considered \param r : last value to be considered */ -unsigned int -vpRobust::partition(vpColVector &a, unsigned int l, unsigned int r) +int +vpRobust::partition(vpColVector &a, int l, int r) { - unsigned int i = l-1; - unsigned int j = r; - double v = a[r]; + int i = l-1; + int j = r; + double v = a[(unsigned int)r]; for (;;) { - while (a[++i] < v) ; - while (v < a[--j]) if (j == l) break; + while (a[(unsigned int)++i] < v) ; + while (v < a[(unsigned int)--j]) if (j == l) break; if (i >= j) break; - exch(a[i], a[j]); + exch(a[(unsigned int)i], a[(unsigned int)j]); } - exch(a[i], a[r]); + exch(a[(unsigned int)i], a[(unsigned int)r]); return i; } @@ -750,15 +747,15 @@ vpRobust::partition(vpColVector &a, unsigned int l, unsigned int r) \param k : value to be selected */ double -vpRobust::select(vpColVector &a, unsigned int l, unsigned int r, unsigned int k) +vpRobust::select(vpColVector &a, int l, int r, int k) { while (r > l) { - unsigned int i = partition(a, l, r); + int i = partition(a, l, r); if (i >= k) r = i-1; if (i <= k) l = i+1; } - return a[k]; + return a[(unsigned int)k]; } diff --git a/src/math/robust/vpRobust.h b/src/math/robust/vpRobust.h index 39168c0434c954b5083f926c6514a0e87ce79e15..28f3c1d15beb21a866b706c4483f43b3575ef5ee 100644 --- a/src/math/robust/vpRobust.h +++ b/src/math/robust/vpRobust.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobust.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobust.h 4796 2014-07-23 15:51:16Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -201,9 +201,9 @@ public: //! Swap two value void exch(double &A, double &B){swap = A; A = B; B = swap;} //! Sort function using partition method - unsigned int partition(vpColVector &a, unsigned int l, unsigned int r); + int partition(vpColVector &a, int l, int r); //! Sort the vector and select a value in the sorted vector - double select(vpColVector &a, unsigned int l, unsigned int r, unsigned int k); + double select(vpColVector &a, int l, int r, int k); //@} }; diff --git a/src/math/robust/vpScale.cpp b/src/math/robust/vpScale.cpp index 15e3758f949c4c7f89bb8c3e1fabfb16cdddf974..d8d4553f5a161ad34d0d0439fe96ea5956a7d235 100644 --- a/src/math/robust/vpScale.cpp +++ b/src/math/robust/vpScale.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpScale.cpp 4312 2013-07-16 15:12:42Z ayol $ + * $Id: vpScale.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,14 +60,11 @@ //! Constructor vpScale::vpScale() + : bandwidth(0.02), dimension(1), kernel_type(EPANECHNIKOV) { #if (DEBUG_LEVEL2) std::cout << "vpScale constructor reached" << std::endl; #endif - bandwidth = 0.02; - dimension = 1; - kernel_type = EPANECHNIKOV; - #if (DEBUG_LEVEL2) std::cout << "vpScale constructor finished" << std::endl; #endif @@ -75,31 +72,23 @@ vpScale::vpScale() } //! Constructor -vpScale::vpScale(double kernel_bandwidth, - int dimension=1, int kernel_type=EPANECHNIKOV) +vpScale::vpScale(double kernel_bandwidth, unsigned int dim, int type) + : bandwidth(kernel_bandwidth), dimension(dim), kernel_type(type) + { #if (DEBUG_LEVEL2) std::cout << "vpScale constructor reached" << std::endl; #endif - - bandwidth = kernel_bandwidth; - this->dimension = (unsigned)dimension; - this->kernel_type = kernel_type; - #if (DEBUG_LEVEL2) std::cout << "vpScale constructor finished" << std::endl; #endif - } //! Destructor vpScale::~vpScale() { - } - - // Calculate the modes of the density for the distribution // and their associated errors double diff --git a/src/math/robust/vpScale.h b/src/math/robust/vpScale.h index 9c9c6dd90a72de3544fbab3c2d6e1604df89a2ad..97a60bc6807ef47c8a894dead2385979a44f18d0 100644 --- a/src/math/robust/vpScale.h +++ b/src/math/robust/vpScale.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpScale.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpScale.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -88,7 +88,7 @@ public: //! Constructor vpScale(); - vpScale(double, int, int); + vpScale(double kernel_bandwidth, unsigned int dim=1, int type=EPANECHNIKOV); //! Destructor virtual ~vpScale(void); diff --git a/src/math/spline/vpBSpline.cpp b/src/math/spline/vpBSpline.cpp index f0ef2887c872646b5bcc3e9520f3f359d6a888f5..c78e194db1f83d286abb2b5caa39fc0d74652cc1 100644 --- a/src/math/spline/vpBSpline.cpp +++ b/src/math/spline/vpBSpline.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBSpline.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpBSpline.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,8 +49,9 @@ compute cubic B-Spline. */ vpBSpline::vpBSpline() + : controlPoints(), knots(), p(3), // By default : p=3 for clubic spline + crossingPoints() { - p = 3; //By default : clubic spline } /*! @@ -58,6 +59,8 @@ vpBSpline::vpBSpline() */ vpBSpline::vpBSpline(const vpBSpline &bspline) + : controlPoints(), knots(), p(3), // By default : p=3 for clubic spline + crossingPoints() { controlPoints = bspline.controlPoints; knots = bspline.knots; diff --git a/src/math/spline/vpBSpline.h b/src/math/spline/vpBSpline.h index cb7d93d603df92b68b75313e02dbf951487f5d55..c1c371f8521a4f69a6bde8d2e24c255fdc2c38f6 100644 --- a/src/math/spline/vpBSpline.h +++ b/src/math/spline/vpBSpline.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBSpline.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBSpline.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -168,9 +168,9 @@ class VISP_EXPORT vpBSpline /*! Sets the degree of the B-Spline. - \param p : the degree of the B-Spline. + \param degree : the degree of the B-Spline. */ - inline void set_p(unsigned int p) {this->p = p;} + inline void set_p(unsigned int degree) {this->p = degree;} /*! diff --git a/src/math/spline/vpNurbs.cpp b/src/math/spline/vpNurbs.cpp index 163a2887dd975dbae8a73003850e78b545b685ae..63ba0d0b7d040867907b2b410f232d5712f7c398 100644 --- a/src/math/spline/vpNurbs.cpp +++ b/src/math/spline/vpNurbs.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNurbs.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpNurbs.cpp 5235 2015-01-30 13:51:21Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,6 +64,7 @@ inline double distance(const vpImagePoint &iP1, const double w1, const vpImagePo compute cubic NURBS. */ vpNurbs::vpNurbs() + : weights() { p = 3; } @@ -71,7 +72,7 @@ vpNurbs::vpNurbs() /*! Copy constructor. */ -vpNurbs::vpNurbs(const vpNurbs &nurbs) : vpBSpline(nurbs) +vpNurbs::vpNurbs(const vpNurbs &nurbs) : vpBSpline(nurbs), weights() { weights = nurbs.weights; } @@ -549,7 +550,7 @@ vpNurbs::removeCurveKnot(double l_u, unsigned int l_r, unsigned int l_num, doubl } unsigned int ord = l_p + 1; - double fout = (2*l_r-l_s-l_p)/2; + double fout = (2*l_r-l_s-l_p)/2.; unsigned int last = l_r - l_s; unsigned int first = l_r - l_p; unsigned int tblSize = 2*l_p+1; @@ -691,13 +692,19 @@ vpNurbs::removeCurveKnot(double u, unsigned int r, unsigned int num, double TOL) The result of the method is composed by a knot vector, a set of control points and a set of associated weights. \param l_crossingPoints : The list of data points which have to be interpolated. - \param l_p : Degree of the NURBS basis functions. + \param l_p : Degree of the NURBS basis functions. This value need to be > 0. \param l_knots : The knot vector \param l_controlPoints : the list of control points. \param l_weights : the list of weights. */ void vpNurbs::globalCurveInterp(std::vector<vpImagePoint> &l_crossingPoints, unsigned int l_p, std::vector<double> &l_knots, std::vector<vpImagePoint> &l_controlPoints, std::vector<double> &l_weights) { + if (l_p == 0) { + //vpERROR_TRACE("Bad degree of the NURBS basis functions"); + throw(vpException(vpException::badValue, + "Bad degree of the NURBS basis functions")) ; + } + l_knots.clear(); l_controlPoints.clear(); l_weights.clear(); @@ -786,8 +793,8 @@ void vpNurbs::globalCurveInterp(vpList<vpMeSite> &l_crossingPoints) l_crossingPoints.next(); while(!l_crossingPoints.outside()) { - vpMeSite s = l_crossingPoints.value(); - vpImagePoint pt(s.ifloat,s.jfloat); + s = l_crossingPoints.value(); + pt.set_ij(s.ifloat,s.jfloat); if (vpImagePoint::distance(pt_1,pt) >= 10) { v_crossingPoints.push_back(pt); @@ -890,11 +897,10 @@ void vpNurbs::globalCurveApprox(std::vector<vpImagePoint> &l_crossingPoints, uns d = (double)(m+1)/(double)(l_n-l_p+1); - double i; double alpha; for(unsigned int j = 1; j <= l_n-l_p; j++) { - i = floor(j*d); + double i = floor(j*d); alpha = j*d-i; l_knots.push_back((1.0-alpha)*ubar[(unsigned int)i-1]+alpha*ubar[(unsigned int)i]); } diff --git a/src/math/spline/vpNurbs.h b/src/math/spline/vpNurbs.h index f896637b6babd6568e400317178061df6e3f875e..f0d5fdb674c6fee39d432fb524feb12caa04c6b9 100644 --- a/src/math/spline/vpNurbs.h +++ b/src/math/spline/vpNurbs.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNurbs.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpNurbs.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpExponentialMap.cpp b/src/math/transformation/vpExponentialMap.cpp index 2a66fe844852ee3452bb50f8ca7926877f10de9b..128df2245fe113c8bec358cf473b18b9be3bac64 100644 --- a/src/math/transformation/vpExponentialMap.cpp +++ b/src/math/transformation/vpExponentialMap.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpExponentialMap.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpExponentialMap.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpExponentialMap.h b/src/math/transformation/vpExponentialMap.h index 0ab20ef8bb4bfd556dc5d9781953aab28ae67b13..f8ed340d9efe1d1bab1e1cf4cf169558fec66421 100644 --- a/src/math/transformation/vpExponentialMap.h +++ b/src/math/transformation/vpExponentialMap.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpExponentialMap.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpExponentialMap.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpForceTwistMatrix.cpp b/src/math/transformation/vpForceTwistMatrix.cpp index 550c8f09902815f8f7c1d0e6d28eb2675b0b57cb..96b3acad657253d13f7403f211ec2e812bd11ba7 100644 --- a/src/math/transformation/vpForceTwistMatrix.cpp +++ b/src/math/transformation/vpForceTwistMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpForceTwistMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpForceTwistMatrix.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -162,16 +162,16 @@ vpForceTwistMatrix::vpForceTwistMatrix(const vpHomogeneousMatrix &M) : vpMatrix( Initialize a force/torque twist transformation matrix from a translation vector \e t and a rotation vector with \f$\theta u \f$ parametrization. - \param t : Translation vector. + \param tv : Translation vector. \param thetau : \f$\theta u\f$ rotation vector. */ -vpForceTwistMatrix::vpForceTwistMatrix(const vpTranslationVector &t, - const vpThetaUVector &thetau) : vpMatrix() +vpForceTwistMatrix::vpForceTwistMatrix(const vpTranslationVector &tv, + const vpThetaUVector &thetau) : vpMatrix() { init() ; - buildFrom(t, thetau) ; + buildFrom(tv, thetau) ; } /*! @@ -179,16 +179,16 @@ vpForceTwistMatrix::vpForceTwistMatrix(const vpTranslationVector &t, Initialize a force/torque twist transformation matrix from a translation vector \e t and a rotation matrix R. - \param t : Translation vector. + \param tv : Translation vector. \param R : Rotation matrix. */ -vpForceTwistMatrix::vpForceTwistMatrix(const vpTranslationVector &t, +vpForceTwistMatrix::vpForceTwistMatrix(const vpTranslationVector &tv, const vpRotationMatrix &R) { init() ; - buildFrom(t,R) ; + buildFrom(tv,R) ; } /*! @@ -365,17 +365,17 @@ vpForceTwistMatrix::operator*(const vpColVector &H) const Build a force/torque twist transformation matrix from a translation vector \e t and a rotation matrix M. - \param t : Translation vector. + \param tv : Translation vector. \param R : Rotation matrix. */ vpForceTwistMatrix -vpForceTwistMatrix::buildFrom(const vpTranslationVector &t, - const vpRotationMatrix &R) +vpForceTwistMatrix::buildFrom(const vpTranslationVector &tv, + const vpRotationMatrix &R) { unsigned int i, j; - vpMatrix skewaR = t.skew(t)*R ; + vpMatrix skewaR = tv.skew(tv)*R ; for (i=0 ; i < 3 ; i++) { for (j=0 ; j < 3 ; j++) { @@ -392,18 +392,18 @@ vpForceTwistMatrix::buildFrom(const vpTranslationVector &t, Initialize a force/torque twist transformation matrix from a translation vector \e t and a rotation vector with \f$\theta u \f$ parametrization. - \param t : Translation vector. + \param tv : Translation vector. \param thetau : \f$\theta u\f$ rotation vector. */ vpForceTwistMatrix -vpForceTwistMatrix::buildFrom(const vpTranslationVector &t, - const vpThetaUVector &thetau) +vpForceTwistMatrix::buildFrom(const vpTranslationVector &tv, + const vpThetaUVector &thetau) { vpRotationMatrix R ; R.buildFrom(thetau) ; - buildFrom(t,R) ; + buildFrom(tv,R) ; return (*this) ; } @@ -421,12 +421,12 @@ vpForceTwistMatrix::buildFrom(const vpTranslationVector &t, vpForceTwistMatrix vpForceTwistMatrix::buildFrom(const vpHomogeneousMatrix &M) { - vpTranslationVector t ; + vpTranslationVector tv ; vpRotationMatrix R ; M.extract(R) ; - M.extract(t) ; + M.extract(tv) ; - buildFrom(t, R) ; + buildFrom(tv, R) ; return (*this) ; } diff --git a/src/math/transformation/vpForceTwistMatrix.h b/src/math/transformation/vpForceTwistMatrix.h index e337a132ef1d52dc7a77641fb007436e053bfe92..33a797a2f058837146527b1b45def59b1b7a9c1b 100644 --- a/src/math/transformation/vpForceTwistMatrix.h +++ b/src/math/transformation/vpForceTwistMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpForceTwistMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpForceTwistMatrix.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpHomogeneousMatrix.cpp b/src/math/transformation/vpHomogeneousMatrix.cpp index 3dc2428041e296e763fa1d43a966075106c01836..10783a9ed7cc65d1a6841c619e90dbbcc18843a7 100644 --- a/src/math/transformation/vpHomogeneousMatrix.cpp +++ b/src/math/transformation/vpHomogeneousMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomogeneousMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHomogeneousMatrix.cpp 5130 2015-01-06 18:50:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -86,11 +86,11 @@ vpHomogeneousMatrix::init() } -vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &t, - const vpQuaternionVector &q) +vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &tv, + const vpQuaternionVector &q) : vpMatrix() { init(); - buildFrom(t,q); + buildFrom(tv,q); } /*! @@ -111,19 +111,19 @@ vpHomogeneousMatrix::vpHomogeneousMatrix(const vpHomogeneousMatrix &M) : vpMatri *this = M ; } -vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &t, +vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &tv, const vpThetaUVector &tu) : vpMatrix() { init() ; - buildFrom(t,tu) ; + buildFrom(tv,tu) ; } -vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &t, +vpHomogeneousMatrix::vpHomogeneousMatrix(const vpTranslationVector &tv, const vpRotationMatrix &R) : vpMatrix() { init() ; insert(R) ; - insert(t) ; + insert(tv) ; } vpHomogeneousMatrix::vpHomogeneousMatrix(const vpPoseVector &p) : vpMatrix() @@ -133,6 +133,94 @@ vpHomogeneousMatrix::vpHomogeneousMatrix(const vpPoseVector &p) : vpMatrix() buildFrom(p[0],p[1],p[2],p[3],p[4],p[5]) ; } +/*! + Creates an homogeneous matrix from a vector. + \param v : Vector of 12 or 16 values corresponding to the values of the homogeneous matrix. + + The following example shows how to use this function: + \code +#include <visp/vpHomogeneousMatrix.h> + +int main() +{ + std::vector<float> v(12, 0); + v[1] = -1.; // ry=-90 + v[4] = 1.; // rx=90 + v[10] = -1.; // rz=-90 + v[3] = 0.3; // tx + v[7] = 0.4; // ty + v[11] = 0.5; // tz + + std::cout << "v: "; + for(unsigned int i=0; i<v.size(); i++) + std::cout << v[i] << " "; + std::cout << std::endl; + + vpHomogeneousMatrix M(v); + std::cout << "M:\n" << M << std::endl; +} + \endcode + + It produces the following printings: + \code +v: 0 -1 0 0.3 1 0 0 0.4 0 0 -1 0.5 +M: +0 -1 0 0.3000000119 +1 0 0 0.400000006 +0 0 -1 0.5 +0 0 0 1 + \endcode + */ +vpHomogeneousMatrix::vpHomogeneousMatrix(const std::vector<float> &v) : vpMatrix() +{ + init() ; + buildFrom(v) ; +} + +/*! + Creates an homogeneous matrix from a vector. + \param v : Vector of 12 or 16 values corresponding to the values of the homogeneous matrix. + + The following example shows how to use this function: + \code +#include <visp/vpHomogeneousMatrix.h> + +int main() +{ + std::vector<double> v(12, 0); + v[1] = -1.; // ry=-90 + v[4] = 1.; // rx=90 + v[10] = -1.; // rz=-90 + v[3] = 0.3; // tx + v[7] = 0.4; // ty + v[11] = 0.5; // tz + + std::cout << "v: "; + for(unsigned int i=0; i<v.size(); i++) + std::cout << v[i] << " "; + std::cout << std::endl; + + vpHomogeneousMatrix M(v); + std::cout << "M:\n" << M << std::endl; +} + \endcode + + It produces the following printings: + \code +v: 0 -1 0 0.3 1 0 0 0.4 0 0 -1 0.5 +M: +0 -1 0 0.3 +1 0 0 0.4 +0 0 -1 0.5 +0 0 0 1 + \endcode + */ +vpHomogeneousMatrix::vpHomogeneousMatrix(const std::vector<double> &v) : vpMatrix() +{ + init() ; + buildFrom(v) ; +} + vpHomogeneousMatrix::vpHomogeneousMatrix(const double tx, const double ty, const double tz, @@ -145,20 +233,20 @@ vpHomogeneousMatrix::vpHomogeneousMatrix(const double tx, } void -vpHomogeneousMatrix::buildFrom(const vpTranslationVector &t, +vpHomogeneousMatrix::buildFrom(const vpTranslationVector &tv, const vpThetaUVector &tu) { insert(tu) ; - insert(t) ; + insert(tv) ; } void -vpHomogeneousMatrix::buildFrom(const vpTranslationVector &t, +vpHomogeneousMatrix::buildFrom(const vpTranslationVector &tv, const vpRotationMatrix &R) { init() ; insert(R) ; - insert(t) ; + insert(tv) ; } @@ -166,17 +254,17 @@ void vpHomogeneousMatrix::buildFrom(const vpPoseVector &p) { - vpTranslationVector t(p[0],p[1],p[2]) ; + vpTranslationVector tv(p[0],p[1],p[2]) ; vpThetaUVector tu(p[3],p[4],p[5]) ; insert(tu) ; - insert(t) ; + insert(tv) ; } -void vpHomogeneousMatrix::buildFrom(const vpTranslationVector &t, +void vpHomogeneousMatrix::buildFrom(const vpTranslationVector &tv, const vpQuaternionVector &q) { - insert(t); + insert(tv); insert(q); } @@ -189,10 +277,110 @@ vpHomogeneousMatrix::buildFrom(const double tx, const double tuz) { vpRotationMatrix R(tux, tuy, tuz) ; - vpTranslationVector t(tx, ty, tz) ; + vpTranslationVector tv(tx, ty, tz) ; insert(R) ; - insert(t) ; + insert(tv) ; +} + +/*! + Converts a vector to an homogeneous matrix. + \param v : Vector of 12 or 16 values corresponding to the values of the homogeneous matrix. + + The following example shows how to use this function: + \code +#include <visp/vpHomogeneousMatrix.h> + +int main() +{ + std::vector<float> v(12, 0); + v[1] = -1.; // ry=-90 + v[4] = 1.; // rx=90 + v[10] = -1.; // rz=-90 + v[3] = 0.3; // tx + v[7] = 0.4; // ty + v[11] = 0.5; // tz + + std::cout << "v: "; + for(unsigned int i=0; i<v.size(); i++) + std::cout << v[i] << " "; + std::cout << std::endl; + + vpHomogeneousMatrix M; + M.buildFrom(v); + std::cout << "M:\n" << M << std::endl; +} + \endcode + + It produces the following printings: + \code +v: 0 -1 0 0.3 1 0 0 0.4 0 0 -1 0.5 +M: +0 -1 0 0.3000000119 +1 0 0 0.400000006 +0 0 -1 0.5 +0 0 0 1 + \endcode + */ +void +vpHomogeneousMatrix::buildFrom(const std::vector<float> &v) +{ + if (v.size() != 12 && v.size() != 16) { + throw(vpException(vpException::dimensionError, "Cannot convert std::vector<float> to vpHomogeneousMatrix")); + } + + for (unsigned int i=0; i < 12; i++) + this->data[i] = (double)v[i]; +} + +/*! + Converts a vector to an homogeneous matrix. + \param v : Vector of 12 or 16 values corresponding to the values of the homogeneous matrix. + + The following example shows how to use this function: + \code +#include <visp/vpHomogeneousMatrix.h> + +int main() +{ + std::vector<double> v(12, 0); + v[1] = -1.; // ry=-90 + v[4] = 1.; // rx=90 + v[10] = -1.; // rz=-90 + v[3] = 0.3; // tx + v[7] = 0.4; // ty + v[11] = 0.5; // tz + + std::cout << "v: "; + for(unsigned int i=0; i<v.size(); i++) + std::cout << v[i] << " "; + std::cout << std::endl; + + vpHomogeneousMatrix M; + M.buildFrom(v); + std::cout << "M:\n" << M << std::endl; +} + \endcode + + It produces the following printings: + \code +v: 0 -1 0 0.3 1 0 0 0.4 0 0 -1 0.5 +M: +0 -1 0 0.3 +1 0 0 0.4 +0 0 -1 0.5 +0 0 0 1 + \endcode + */ +void +vpHomogeneousMatrix::buildFrom(const std::vector<double> &v) +{ + if (v.size() != 12 && v.size() != 16) { + throw(vpException(vpException::dimensionError, "Cannot convert std::vector<double> to vpHomogeneousMatrix")); + } + + for (unsigned int i=0; i < 12; i++) + this->data[i] = v[i]; } /*! @@ -259,7 +447,7 @@ vpHomogeneousMatrix::operator*(const vpHomogeneousMatrix &M) const } vpColVector -vpHomogeneousMatrix::operator*(vpColVector &v) const +vpHomogeneousMatrix::operator*(const vpColVector &v) const { vpColVector p(rowNum); @@ -308,11 +496,11 @@ vpHomogeneousMatrix::extract(vpRotationMatrix &R) const Extract the translation vector from the homogeneous matrix. */ void -vpHomogeneousMatrix::extract(vpTranslationVector &t) const +vpHomogeneousMatrix::extract(vpTranslationVector &tv) const { - t[0] = (*this)[0][3] ; - t[1] = (*this)[1][3] ; - t[2] = (*this)[2][3] ; + tv[0] = (*this)[0][3] ; + tv[1] = (*this)[1][3] ; + tv[2] = (*this)[2][3] ; } /*! Extract the rotation as a Theta U vector. @@ -481,7 +669,7 @@ vpHomogeneousMatrix::inverse(vpHomogeneousMatrix &M) const void vpHomogeneousMatrix::save(std::ofstream &f) const { - if (f != NULL) + if (! f.fail()) { f << *this ; } @@ -514,12 +702,12 @@ vpHomogeneousMatrix::save(std::ofstream &f) const void vpHomogeneousMatrix::load(std::ifstream &f) { - if (f != NULL) + if (! f.fail()) { for (unsigned int i=0 ; i < 4 ; i++) for (unsigned int j=0 ; j < 4 ; j++) { - f>> (*this)[i][j] ; + f >> (*this)[i][j] ; } } else @@ -543,9 +731,24 @@ vpHomogeneousMatrix::setIdentity() init() ; } +/*! + Converts an homogenous matrix to a vector of 12 floats. + \param M : Converted matrix. + */ +void vpHomogeneousMatrix::convert(std::vector<float> &M) +{ + M.resize(12); + for(unsigned int i=0; i < 12; i++) + M[i] = (float)(this->data[i]); +} -/* - * Local variables: - * c-basic-offset: 2 - * End: +/*! + Converts an homogenous matrix to a vector of 12 doubles. + \param M : Converted matrix. */ +void vpHomogeneousMatrix::convert(std::vector<double> &M) +{ + M.resize(12); + for(unsigned int i=0; i < 12; i++) + M[i] = this->data[i]; +} diff --git a/src/math/transformation/vpHomogeneousMatrix.h b/src/math/transformation/vpHomogeneousMatrix.h index 43c7b5f20f15f7ac3fd40937ce875f27ad702901..a12a8bc3c7ab76299c28e45808814f7d62a460e0 100644 --- a/src/math/transformation/vpHomogeneousMatrix.h +++ b/src/math/transformation/vpHomogeneousMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHomogeneousMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHomogeneousMatrix.h 5130 2015-01-06 18:50:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,8 +56,9 @@ class vpRotationMatrix; class vpPoseVector; class vpThetaUVector; -#include <visp/vpMatrix.h> +#include <vector> +#include <visp/vpMatrix.h> #include <visp/vpRotationMatrix.h> #include <visp/vpThetaUVector.h> #include <visp/vpTranslationVector.h> @@ -121,13 +122,17 @@ class VISP_EXPORT vpHomogeneousMatrix : public vpMatrix //! Construction from translation and rotation defined as a theta u vector. vpHomogeneousMatrix(const double tx, const double ty, const double tz, const double tux, const double tuy, const double tuz) ; + vpHomogeneousMatrix(const std::vector<float> &v); + vpHomogeneousMatrix(const std::vector<double> &v); //! Construction from translation vector and rotation matrix. void buildFrom(const vpTranslationVector &t, const vpRotationMatrix &R) ; //! Construction from translation vector and theta u rotation vector. void buildFrom(const vpTranslationVector &t, const vpThetaUVector &tu) ; //! Construction from translation vector and quaternion rotation vector. - void buildFrom(const vpTranslationVector &t, const vpQuaternionVector& q ) ; + void buildFrom(const vpTranslationVector &t, const vpQuaternionVector& q) ; + void buildFrom(const std::vector<float> &v) ; + void buildFrom(const std::vector<double> &v) ; /*! Construction from translation vector and theta u rotation vector @@ -139,6 +144,29 @@ class VISP_EXPORT vpHomogeneousMatrix : public vpMatrix void buildFrom(const double tx,const double ty, const double tz, const double tux,const double tuy, const double tuz ) ; + void convert(std::vector<float> &M); + void convert(std::vector<double> &M); + + /*! + Return the translation vector from the homogeneous transformation matrix. + */ + vpTranslationVector getTranslationVector() + { + vpTranslationVector tr; + this->extract(tr); + return tr; + } +// /*! +// Return the rotation matrix from the homogeneous transformation matrix. +// */ +// vpThetaUVector getThetaUVector() +// vpRotationMatrix getRotationMatrix() +// { +// vpRotationMatrix R; +// this->extract(R); +// return R; +// } + //! Copy operator from vpHomogeneousMatrix. vpHomogeneousMatrix &operator=(const vpHomogeneousMatrix &M); @@ -146,7 +174,7 @@ class VISP_EXPORT vpHomogeneousMatrix : public vpMatrix vpHomogeneousMatrix operator*(const vpHomogeneousMatrix &M) const; //! Multiply by a vector ! size 4 !!! - vpColVector operator*(vpColVector &v) const; + vpColVector operator*(const vpColVector &v) const; // Invert the homogeneous matrix. vpHomogeneousMatrix inverse() const ; diff --git a/src/math/transformation/vpPoseVector.cpp b/src/math/transformation/vpPoseVector.cpp index b265652795a066fbecc85ddd2fa6c60f14e5a9e4..bb472e20a2e6758edfd759fa0ddc1b5438fde7f8 100644 --- a/src/math/transformation/vpPoseVector.cpp +++ b/src/math/transformation/vpPoseVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPoseVector.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -119,15 +119,15 @@ vpPoseVector::vpPoseVector(const double tx, u]^\top\f$ from a translation vector \f$ \bf t \f$ and a \f$\Theta \bf u\f$ vector. - \param t : Translation vector \f$ \bf t \f$. + \param tv : Translation vector \f$ \bf t \f$. \param tu : \f$\Theta \bf u\f$ rotation vector. */ -vpPoseVector::vpPoseVector(const vpTranslationVector& t, +vpPoseVector::vpPoseVector(const vpTranslationVector& tv, const vpThetaUVector& tu) { init() ; - buildFrom(t,tu) ; + buildFrom(tv,tu) ; } /*! @@ -136,17 +136,17 @@ vpPoseVector::vpPoseVector(const vpTranslationVector& t, u]^\top\f$ from a translation vector \f$ \bf t \f$ and a rotation matrix \f$ \bf R \f$. - \param t : Translation vector \f$ \bf t \f$. + \param tv : Translation vector \f$ \bf t \f$. \param R : Rotation matrix \f$ \bf R \f$ from which \f$\Theta \bf u\f$ vector is extracted to initialise the pose vector. */ -vpPoseVector::vpPoseVector(const vpTranslationVector& t, +vpPoseVector::vpPoseVector(const vpTranslationVector& tv, const vpRotationMatrix& R) { init() ; - buildFrom(t,R) ; + buildFrom(tv,R) ; } /*! @@ -180,8 +180,8 @@ vpPoseVector vpPoseVector::buildFrom(const vpHomogeneousMatrix& M) { vpRotationMatrix R ; M.extract(R) ; - vpTranslationVector t ; M.extract(t) ; - buildFrom(t,R) ; + vpTranslationVector tv ; M.extract(tv) ; + buildFrom(tv,R) ; return *this ; } @@ -191,18 +191,18 @@ vpPoseVector::buildFrom(const vpHomogeneousMatrix& M) from a translation vector \f$ \bf t \f$ and a \f$\Theta \bf u\f$ vector. - \param t : Translation vector \f$ \bf t \f$. + \param tv : Translation vector \f$ \bf t \f$. \param tu : \f$\Theta \bf u\f$ rotation vector. \return The build pose vector. */ vpPoseVector -vpPoseVector::buildFrom(const vpTranslationVector& t, +vpPoseVector::buildFrom(const vpTranslationVector& tv, const vpThetaUVector& tu) { for (unsigned int i =0 ; i < 3 ; i++) { - (*this)[i] = t[i] ; + (*this)[i] = tv[i] ; (*this)[i+3] = tu[i] ; } return *this ; @@ -214,7 +214,7 @@ vpPoseVector::buildFrom(const vpTranslationVector& t, from a translation vector \f$ \bf t \f$ and a rotation matrix \f$ \bf R \f$. - \param t : Translation vector \f$ \bf t \f$. + \param tv : Translation vector \f$ \bf t \f$. \param R : Rotation matrix \f$ \bf R \f$ from which \f$\Theta \bf u\f$ vector is extracted to initialise the pose vector. @@ -222,13 +222,13 @@ vpPoseVector::buildFrom(const vpTranslationVector& t, \return The build pose vector. */ vpPoseVector -vpPoseVector::buildFrom(const vpTranslationVector& t, +vpPoseVector::buildFrom(const vpTranslationVector& tv, const vpRotationMatrix& R) { vpThetaUVector tu ; tu.buildFrom(R) ; - buildFrom(t,tu) ; + buildFrom(tv,tu) ; return *this ; } @@ -276,7 +276,7 @@ vpPoseVector::print() void vpPoseVector::save(std::ofstream &f) const { - if (f != NULL) + if (! f.fail()) { f << *this ; } @@ -301,18 +301,18 @@ vpPoseVector::save(std::ofstream &f) const void vpPoseVector::load(std::ifstream &f) { - if (f != NULL) + if (! f.fail()) + { + for (unsigned int i=0 ; i < 6 ; i++) { - for (unsigned int i=0 ; i < 6 ; i++) - { - f>> (*this)[i] ; - } + f >> (*this)[i] ; } + } else - { - vpERROR_TRACE("\t\t file not open " ); - throw(vpException(vpException::ioError, "\t\t file not open")) ; - } + { + vpERROR_TRACE("\t\t file not open " ); + throw(vpException(vpException::ioError, "\t\t file not open")) ; + } } diff --git a/src/math/transformation/vpPoseVector.h b/src/math/transformation/vpPoseVector.h index dd502301f471835b81a0366525b0bce8c86caebe..607fdcdd6e50a4a7dd9a8d02114d07c76dca2f82 100644 --- a/src/math/transformation/vpPoseVector.h +++ b/src/math/transformation/vpPoseVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoseVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPoseVector.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpQuaternionVector.cpp b/src/math/transformation/vpQuaternionVector.cpp index 07beeca3413b3bd2b4c8a116a25073b04a493789..f00cab04692f4468c6812f5e2082b1293a6830e4 100644 --- a/src/math/transformation/vpQuaternionVector.cpp +++ b/src/math/transformation/vpQuaternionVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpQuaternionVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpQuaternionVector.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,14 +54,13 @@ const double vpQuaternionVector::minimum = 0.0001; \file vpQuaternionVector.cpp \brief Defines a quaternion and common operations on it. */ -vpQuaternionVector::vpQuaternionVector() : vpRotationVector(4) { } //! Constructor from doubles. -vpQuaternionVector::vpQuaternionVector(const double x, const double y, - const double z,const double w) +vpQuaternionVector::vpQuaternionVector(const double x_, const double y_, + const double z_,const double w_) : vpRotationVector(4) { - set(x, y, z, w); + set(x_, y_, z_, w_); } /*! @@ -74,30 +73,21 @@ vpQuaternionVector::vpQuaternionVector(const vpRotationMatrix &R) { buildFrom(R); } -/*! - Copy constructor. - \param q : quaternion to construct from. -*/ -vpQuaternionVector::vpQuaternionVector(const vpQuaternionVector &q) - : vpRotationVector(4) -{ - for(unsigned int i=0;i<size();i++) (*this)[i]=q.r[i]; -} - + /*! Manually change values of a quaternion. - \param x : x quaternion parameter. - \param y : y quaternion parameter. - \param z : z quaternion parameter. - \param w : w quaternion parameter. + \param x_ : x quaternion parameter. + \param y_ : y quaternion parameter. + \param z_ : z quaternion parameter. + \param w_ : w quaternion parameter. */ -void vpQuaternionVector::set(const double x, const double y, - const double z,const double w) +void vpQuaternionVector::set(const double x_, const double y_, + const double z_,const double w_) { - r[0]=x; - r[1]=y; - r[2]=z; - r[3]=w; + r[0]=x_; + r[1]=y_; + r[2]=z_; + r[3]=w_; } @@ -144,13 +134,6 @@ vpQuaternionVector vpQuaternionVector::operator* ( vpQuaternionVector &rq) { w() * rq.w() - x() * rq.x() - y() * rq.y() - z() * rq.z()); } -//! Copy operator. Allow operation such as Q = q. -vpQuaternionVector &vpQuaternionVector::operator=( vpQuaternionVector &q) -{ - for(unsigned int i=0;i<size();i++) (*this)[i]=q.r[i]; - return *this; -} - /*! Constructs a quaternion from a rotation matrix. diff --git a/src/math/transformation/vpQuaternionVector.h b/src/math/transformation/vpQuaternionVector.h index a98224bbc666f97b48812be9ccea4935cfcb672e..a41a41ff79ec0f4df8d424230f32aa71de911428 100644 --- a/src/math/transformation/vpQuaternionVector.h +++ b/src/math/transformation/vpQuaternionVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpQuaternionVector.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpQuaternionVector.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -82,9 +82,11 @@ private: static const double minimum; public: - vpQuaternionVector() ; + /*! Default constructor that initialize all the angles to zero. */ + vpQuaternionVector() : vpRotationVector(4) {} + /*! Copy constructor. */ + vpQuaternionVector(const vpQuaternionVector &q) : vpRotationVector(q) {} vpQuaternionVector(const double x, const double y, const double z,const double w) ; - vpQuaternionVector(const vpQuaternionVector &q); vpQuaternionVector(const vpRotationMatrix &R); void buildFrom(const vpRotationMatrix& R); @@ -104,8 +106,7 @@ public: vpQuaternionVector operator-( vpQuaternionVector &q) ; vpQuaternionVector operator-() ; vpQuaternionVector operator*(const double l) ; - vpQuaternionVector operator* ( vpQuaternionVector &rq) ; - vpQuaternionVector &operator=( vpQuaternionVector &q); + vpQuaternionVector operator*( vpQuaternionVector &rq) ; } ; #endif diff --git a/src/math/transformation/vpRotationMatrix.cpp b/src/math/transformation/vpRotationMatrix.cpp index f3894e97cef075fa2deb5e267178fe330e067ef5..ee2dbdb78bca4e499fb58154fc9df4fe95f8b98d 100644 --- a/src/math/transformation/vpRotationMatrix.cpp +++ b/src/math/transformation/vpRotationMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRotationMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRotationMatrix.cpp 4900 2014-09-11 09:16:21Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -359,12 +359,19 @@ vpRotationMatrix::vpRotationMatrix() : vpMatrix() /*! \brief initialize a rotation matrix from another rotation matrix */ - vpRotationMatrix::vpRotationMatrix(const vpRotationMatrix &M) : vpMatrix() { init() ; (*this) = M ; } +/*! + Initialize a rotation matrix from an homogenous matrix. +*/ +vpRotationMatrix::vpRotationMatrix(const vpHomogeneousMatrix &M) : vpMatrix() +{ + init() ; + buildFrom(M); +} //! Construction from rotation (Theta U parameterization) vpRotationMatrix::vpRotationMatrix(const vpThetaUVector &tu) : vpMatrix() @@ -372,6 +379,12 @@ vpRotationMatrix::vpRotationMatrix(const vpThetaUVector &tu) : vpMatrix() init() ; buildFrom(tu) ; } +//! Construction from a pose vector. +vpRotationMatrix::vpRotationMatrix(const vpPoseVector &p) : vpMatrix() +{ + init() ; + buildFrom(p) ; +} //! Construction from rotation (Euler parameterization, ie Rzyz parameterization) @@ -456,7 +469,7 @@ vpRotationMatrix::inverse(vpRotationMatrix &M) const //! std::cout an rotation matrix [thetaU] -std::ostream &operator <<(std::ostream &s,const vpRotationMatrix &R) +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpRotationMatrix &R) { for (unsigned int i=0; i<3; i++) { @@ -573,6 +586,32 @@ vpRotationMatrix::buildFrom(const vpThetaUVector &v) return *this ; } +/*! + Build a rotation matrix from an homogenous matrix. +*/ +vpRotationMatrix +vpRotationMatrix::buildFrom(const vpHomogeneousMatrix &M) +{ + for (unsigned int i=0 ; i < 3 ; i++) + for (unsigned int j=0 ; j < 3; j++) + (*this)[i][j] = M[i][j] ; + + return *this ; +} + +/* + \relates vpRotationMatrix + Transform a pose vector into a rotation matrix. + + \sa buildFrom(const vpThetaUVector &) +*/ +vpRotationMatrix +vpRotationMatrix::buildFrom(const vpPoseVector &p) +{ + vpThetaUVector tu(p); + return buildFrom(tu); +} + /*! Transform a vector representing the euler angle into a rotation matrix. diff --git a/src/math/transformation/vpRotationMatrix.h b/src/math/transformation/vpRotationMatrix.h index 04b47d9667a549c707baa1835f7e216fcb30dfd7..c45f0879d7df8ba71fd1296c212f7a4521e44084 100644 --- a/src/math/transformation/vpRotationMatrix.h +++ b/src/math/transformation/vpRotationMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRotationMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRotationMatrix.h 5037 2014-12-05 19:06:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,6 +48,7 @@ \brief Class that consider the particular case of rotation matrix */ +#include <visp/vpHomogeneousMatrix.h> #include <visp/vpMatrix.h> #include <visp/vpRxyzVector.h> #include <visp/vpRzyxVector.h> @@ -55,6 +56,7 @@ #include <visp/vpThetaUVector.h> #include <visp/vpTranslationVector.h> #include <visp/vpQuaternionVector.h> +#include <visp/vpPoseVector.h> /*! \class vpRotationMatrix @@ -71,12 +73,14 @@ */ class VISP_EXPORT vpRotationMatrix : public vpMatrix { - friend class vpMatrix; + friend class vpMatrix; + friend class vpHomogeneousMatrix; friend class vpRxyzVector; friend class vpRzyzVector; friend class vpRzyxVector; friend class vpThetaUVector; friend class vpTranslationVector; + friend class vpPoseVector; public: //! Basic initialisation (identity) void init() ; @@ -84,23 +88,36 @@ public: //! Basic initialisation (identity) void setIdentity() ; void eye(); - //! basic constructor + //! Default constructor. vpRotationMatrix() ; - //! copy constructor + //! Copy constructor. vpRotationMatrix(const vpRotationMatrix &R) ; - //! Construction from rotation (theta U parameterization) + //! Copy constructor. + vpRotationMatrix(const vpHomogeneousMatrix &M) ; + //! Construction from rotation (theta U parameterization) vpRotationMatrix(const vpThetaUVector &r) ; - //! Construction from rotation (Euler parameterization) + //! Construction from a pose vector. + vpRotationMatrix(const vpPoseVector &p) ; + //! Construction from rotation (Euler parameterization) vpRotationMatrix(const vpRzyzVector &r) ; - //! Construction from rotation Rxyz + //! Construction from rotation Rxyz vpRotationMatrix(const vpRxyzVector &r) ; - //! Construction from rotation Rzyx + //! Construction from rotation Rzyx vpRotationMatrix(const vpRzyxVector &r) ; - //! Construction from rotation (theta U parameterization) + //! Construction from rotation (theta U parameterization) vpRotationMatrix(const double tux, const double tuy, const double tuz) ; vpRotationMatrix(const vpQuaternionVector& q); +// /*! +// Return the \f$\theta u\f$ vector that corresponds to tha rotation matrix. +// */ +// vpThetaUVector getThetaUVector() +// { +// vpThetaUVector tu; +// tu.buildFrom(*this); +// return tu; +// } //! copy operator from vpRotationMatrix vpRotationMatrix &operator=(const vpRotationMatrix &R); @@ -134,8 +151,12 @@ public: void printVector() ; friend VISP_EXPORT std::ostream &operator << (std::ostream &s, const vpRotationMatrix &m); - //! Transform a vector vpThetaUVector into an rotation matrix + //! Build a rotation matrix from an homogeneous matrix. + vpRotationMatrix buildFrom(const vpHomogeneousMatrix &M) ; + //! Transform a vector vpThetaUVector into a rotation matrix vpRotationMatrix buildFrom(const vpThetaUVector &v) ; + //! Transform a pose vector into a rotation matrix + vpRotationMatrix buildFrom(const vpPoseVector &p) ; //! Transform a vector reprensenting the euler (Rzyz) angle //! into a rotation matrix vpRotationMatrix buildFrom(const vpRzyzVector &v) ; diff --git a/src/math/transformation/vpRotationVector.cpp b/src/math/transformation/vpRotationVector.cpp index 6c95e25ff643d59feab2bc91b4318be14959eb05..5829b3ed209a8e8dd6ffc0f66a25b6ccd7ade41c 100644 --- a/src/math/transformation/vpRotationVector.cpp +++ b/src/math/transformation/vpRotationVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRotationVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRotationVector.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -97,8 +97,9 @@ Rxyz rotation vector: 0 \endcode */ -std::ostream &operator <<(std::ostream &s,const vpRotationVector &m) +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpRotationVector &m) { + std::ios::fmtflags original_flags( s.flags() ); s.precision(10) ; for (unsigned int i=0; i < m.size(); i++) @@ -106,12 +107,15 @@ std::ostream &operator <<(std::ostream &s,const vpRotationVector &m) s << std::endl; + // Restore ostream format + s.flags(original_flags); + return s; } -void vpRotationVector::init(const unsigned int size){ - this->_size = size; +void vpRotationVector::init(const unsigned int vector_size){ + this->_size = vector_size; r = new double[this->_size]; std::fill(r,r+this->_size,0.); } @@ -119,9 +123,3 @@ void vpRotationVector::init(const unsigned int size){ vpRotationVector::~vpRotationVector(){ delete[] r; } - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/math/transformation/vpRotationVector.h b/src/math/transformation/vpRotationVector.h index 118b41ab5ce0b747128cd1d6dad39c076eaace93..965ee37d2d27142e1ca97b3a09e0651333f9d147 100644 --- a/src/math/transformation/vpRotationVector.h +++ b/src/math/transformation/vpRotationVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRotationVector.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRotationVector.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -83,11 +83,10 @@ int main() std::cout << "Rxyz rotation vector: " << r << std::endl; - double rx = r[1]; // Get the value of the angle around x axis - double ry = r[2]; // Get the value of the angle around y axis - double rz = r[3]; // Get the value of the angle around z axis + double rx = r[0]; // Get the value of the angle around x axis + double ry = r[1]; // Get the value of the angle around y axis + double rz = r[2]; // Get the value of the angle around z axis } - \endcode */ @@ -100,18 +99,29 @@ protected: unsigned int _size; void init(const unsigned int size); public: - //! Constructor that constructs a vector of size 3 initialize three vector values to zero. - vpRotationVector() { - init(3); + //! Constructor that constructs a vector of size 3 and initialize all values to zero. + vpRotationVector() + : r(NULL), _size(0) + { + init(3); } - - //! Constructor that constructs a vector of size n initialize three vector values to zero. - vpRotationVector(const unsigned int n) { - init(n); + //! Constructor that constructs a vector of size n and initialize all values to zero. + vpRotationVector(const unsigned int n) + : r(NULL), _size(n) + { + init(n); + } + /*! + Copy operator. + */ + vpRotationVector(const vpRotationVector &v) + : r(NULL), _size(0) + { + *this = v; } - - ~vpRotationVector(); + + virtual ~vpRotationVector(); /*! Operator that allows to set the value of an element of the rotation @@ -124,7 +134,19 @@ public: */ inline const double &operator [](unsigned int n) const { return *(r+n); } - + /*! + Affectation of two vectors. + */ + vpRotationVector &operator=(const vpRotationVector &v) + { + init(v.size()); + for (unsigned int i=0; i<_size; i++) + { + r[i] = v.r[i] ; + } + return *this; + } + /*! Returns the size of the rotation vector */ unsigned int size() const; @@ -132,8 +154,7 @@ public: // Transpose of the rotation vector. vpRowVector t() const; - friend VISP_EXPORT std::ostream &operator << (std::ostream &s, - const vpRotationVector &m); + friend VISP_EXPORT std::ostream &operator << (std::ostream &s, const vpRotationVector &m); } ; diff --git a/src/math/transformation/vpRxyzVector.cpp b/src/math/transformation/vpRxyzVector.cpp index 6fe57a9ad52898e38859a50912f62c356d52adc3..cc2218ebbbeced06f55104b9531996d10106778e 100644 --- a/src/math/transformation/vpRxyzVector.cpp +++ b/src/math/transformation/vpRxyzVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRxyzVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRxyzVector.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,27 +51,6 @@ */ -/*! - Affectation of two vectors. -*/ -vpRxyzVector & -vpRxyzVector::operator=(const vpRxyzVector &m) -{ - - for (int i=0; i<3; i++) - { - r[i] = m.r[i] ; - } - return *this; -} - - -//! Copy constructor. -vpRxyzVector::vpRxyzVector(const vpRxyzVector &m) : vpRotationVector() -{ - *this = m ; -} - /*! Constructor that initialize \f$R_{xyz}=(\varphi,\theta,\psi)\f$ Euler angles from a rotation matrix. diff --git a/src/math/transformation/vpRxyzVector.h b/src/math/transformation/vpRxyzVector.h index a52f2f6d7ac38b9202a6e414b62c2bb7ec5f99dd..ec1f258ab2bc4c192d1ec28343a45cfd431b7291 100644 --- a/src/math/transformation/vpRxyzVector.h +++ b/src/math/transformation/vpRxyzVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRxyzVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRxyzVector.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -155,12 +155,11 @@ class VISP_EXPORT vpRxyzVector : public vpRotationVector friend class vpThetaUVector; public: - //! Default constructor. Initialize the angles to zero. - vpRxyzVector() { ; } - - // Copy constructor. - vpRxyzVector(const vpRxyzVector &m); - + /*! Default constructor that initialize all the angles to zero. */ + vpRxyzVector() {} + /*! Copy constructor. */ + vpRxyzVector(const vpRxyzVector &rxyz) : vpRotationVector(rxyz) {} + /*! Constructor from 3 angles (in radian). \param phi : \f$\varphi\f$ angle around the \f$x\f$ axis. @@ -176,9 +175,6 @@ class VISP_EXPORT vpRxyzVector : public vpRotationVector // initialize a Rxyz vector from a ThetaU vector vpRxyzVector(const vpThetaUVector& tu) ; - // Affectation of two vectors. - vpRxyzVector &operator=(const vpRxyzVector &m); - /*! Construction from 3 angles (in radian). \param phi : \f$\varphi\f$ angle around the \f$x\f$ axis. @@ -201,9 +197,3 @@ class VISP_EXPORT vpRxyzVector : public vpRotationVector } ; #endif - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/math/transformation/vpRzyxVector.cpp b/src/math/transformation/vpRzyxVector.cpp index d2e5b18b95b31030117f31c8e6d374a1fcad8a60..abeab6c93b1581c986a702e39986904f7de4ba00 100644 --- a/src/math/transformation/vpRzyxVector.cpp +++ b/src/math/transformation/vpRzyxVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRzyxVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRzyxVector.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,29 +51,6 @@ */ - -/*! - Affectation of two vectors. -*/ -vpRzyxVector & -vpRzyxVector::operator=(const vpRzyxVector &m) -{ - - for (int i=0; i<3; i++) - { - r[i] = m.r[i] ; - } - return *this; -} - - -//! Copy constructor. -vpRzyxVector::vpRzyxVector(const vpRzyxVector &m) : vpRotationVector() -{ - *this = m ; -} - - /*! Constructor that initialize \f$R_{zyx}=(\varphi,\theta,\psi)\f$ Euler angles from a rotation matrix. diff --git a/src/math/transformation/vpRzyxVector.h b/src/math/transformation/vpRzyxVector.h index 5d2af9bd8f56b9dcae66229948f6fef0bb27905c..329c4b4965de5258b0dc28afbde5ee6c9195f541 100644 --- a/src/math/transformation/vpRzyxVector.h +++ b/src/math/transformation/vpRzyxVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRzyxVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRzyxVector.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -154,11 +154,10 @@ class VISP_EXPORT vpRzyxVector : public vpRotationVector friend class vpThetaUVector; public: - //! Default constructor. Initialize the angles to zero. - vpRzyxVector() { ; } - - // Copy constructor. - vpRzyxVector(const vpRzyxVector &m); + /*! Default constructor that initialize all the angles to zero. */ + vpRzyxVector() {} + /*! Copy constructor. */ + vpRzyxVector(const vpRzyxVector &rzyx) : vpRotationVector(rzyx) {} /*! Constructor from 3 angles (in radian). @@ -173,10 +172,7 @@ public: vpRzyxVector(const vpRotationMatrix& R) ; // initialize a Rzyx vector from a ThetaU vector - vpRzyxVector(const vpThetaUVector& tu) ; - - // Affectation of two vectors. - vpRzyxVector &operator=(const vpRzyxVector &m); + vpRzyxVector(const vpThetaUVector& tu) ; /*! Construction from 3 angles (in radian). diff --git a/src/math/transformation/vpRzyzVector.cpp b/src/math/transformation/vpRzyzVector.cpp index 63c1f3f69602efdf42d3c8cae61c846c2c2a7187..d0d0eb2fb61a79db98076d30e9c5a4fd2654ff00 100644 --- a/src/math/transformation/vpRzyzVector.cpp +++ b/src/math/transformation/vpRzyzVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRzyzVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRzyzVector.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,28 +51,6 @@ Rzyz(phi,theta,psi) = Rot(z,phi)Rot(y,theta)Rot(z,psi) */ - -/*! - Affectation of two vectors. -*/ -vpRzyzVector & -vpRzyzVector::operator=(const vpRzyzVector &m) -{ - - for (int i=0; i<3; i++) - { - r[i] = m.r[i] ; - } - return *this; -} - - -//! Copy constructor. -vpRzyzVector::vpRzyzVector(const vpRzyzVector &m) : vpRotationVector() -{ - *this = m ; -} - /*! Constructor that initialize \f$R_{zyz}=(\varphi,\theta,\psi)\f$ Euler angles from a rotation matrix. @@ -83,7 +61,6 @@ vpRzyzVector::vpRzyzVector(const vpRotationMatrix& R) buildFrom(R) ; } - /*! Constructor that initialize \f$R_{zyz}=(\varphi,\theta,\psi)\f$ Euler angles vector from a \f$\theta u\f$ vector. @@ -144,11 +121,30 @@ vpRzyzVector::buildFrom(const vpThetaUVector& tu) return *this ; } +/*! + + Initialize each element of the vector to the same angle value \e v. + + \param v : Angle value to set for each element of the vector. + +\code +#include <visp/vpMath.h> +#include <visp/vpRzyzVector.h> + +int main() +{ + vpRzyzVector r; + // Initialise the rotation vector + r = vpMath::rad( 45.f); // All the 3 angles are set to 45 degrees +} +\endcode +*/ +vpRzyzVector &vpRzyzVector::operator=(double v) +{ + for (int i=0; i< 3; i++) + r[i] = v; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ + return *this; +} diff --git a/src/math/transformation/vpRzyzVector.h b/src/math/transformation/vpRzyzVector.h index 801f04c473f9f46af63dfaa979593ea5dedc52d5..90865e95aa611b135c647236a9220ef604135682 100644 --- a/src/math/transformation/vpRzyzVector.h +++ b/src/math/transformation/vpRzyzVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRzyzVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRzyzVector.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -153,12 +153,11 @@ class VISP_EXPORT vpRzyzVector : public vpRotationVector friend class vpThetaUVector; public: - //! Default constructor. Initialize the angles to zero. - vpRzyzVector() { ; } + /*! Default constructor that initialize all the angles to zero. */ + vpRzyzVector() {} + /*! Copy constructor. */ + vpRzyzVector(const vpRzyzVector &rzyz) : vpRotationVector(rzyz) {} - // Copy constructor. - vpRzyzVector(const vpRzyzVector &m); - /*! Constructor from 3 angles (in radian). \param phi : \f$\varphi\f$ angle around the \f$z\f$ axis. @@ -174,9 +173,6 @@ class VISP_EXPORT vpRzyzVector : public vpRotationVector // initialize a Rzyz vector from a ThetaU vector vpRzyzVector(const vpThetaUVector& tu); - // Affectation of two vectors. - vpRzyzVector &operator=(const vpRzyzVector &m); - /*! Construction from 3 angles (in radian). \param phi : \f$\varphi\f$ angle around the \f$z\f$ axis. @@ -195,11 +191,9 @@ class VISP_EXPORT vpRzyzVector : public vpRotationVector // convert a ThetaU vector into a Rzyz vector vpRzyzVector buildFrom(const vpThetaUVector& R) ; + + vpRzyzVector &operator=(double x) ; + } ; #endif -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/math/transformation/vpThetaUVector.cpp b/src/math/transformation/vpThetaUVector.cpp index 4e5b6905901d06207f92c8c89c0212a4147b63ce..3ceb8ed3948d269930292284f9ec866522b9bbf6 100644 --- a/src/math/transformation/vpThetaUVector.cpp +++ b/src/math/transformation/vpThetaUVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpThetaUVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ +* $Id: vpThetaUVector.cpp 4792 2014-07-18 11:56:02Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,33 +55,18 @@ rotation const double vpThetaUVector::minimum = 0.0001; /*! -Affectation of two \f$\theta {\bf u}\f$ vector. +Initialize a \f$\theta {\bf u}\f$ vector from an homogeneous matrix. */ -vpThetaUVector & -vpThetaUVector::operator=(const vpThetaUVector &m) +vpThetaUVector::vpThetaUVector(const vpHomogeneousMatrix& M) { - for (int i=0; i<3; i++) - { - r[i] = m.r[i] ; - } - return *this; + buildFrom(M) ; } - - /*! -Copy constructor. +Initialize a \f$\theta {\bf u}\f$ vector from a pose vector. */ -vpThetaUVector::vpThetaUVector(const vpThetaUVector &m) : vpRotationVector() +vpThetaUVector::vpThetaUVector(const vpPoseVector& p) { - *this = m ; -} - -/*! -Initialize a \f$\theta {\bf u}\f$ vector from an homogeneous matrix. -*/ -vpThetaUVector::vpThetaUVector(const vpHomogeneousMatrix& M) -{ - buildFrom(M) ; + buildFrom(p) ; } /*! Initialize a \f$\theta {\bf u}\f$ vector from a rotation matrix. @@ -129,6 +114,17 @@ vpThetaUVector::buildFrom(const vpHomogeneousMatrix& M) return *this ; } +/*! +Converts a pose vector into a \f$\theta {\bf u}\f$ vector. +*/ +vpThetaUVector +vpThetaUVector::buildFrom(const vpPoseVector& p) +{ + for(unsigned int i=0; i<3; i++) + r[i] = p[i+3]; + + return *this ; +} /*! Converts a rotation matrix into a \f$\theta {\bf u}\f$ vector. @@ -271,7 +267,7 @@ vpThetaUVector::buildFrom(const vpRxyzVector& rxyz) return *this ; } -/*! +/*! Initialize each element of the \f$\theta {\bf u}\f$ vector to the same angle value \e v. @@ -327,8 +323,3 @@ vpThetaUVector::extract(double &theta, vpColVector &u) const } #undef vpDEBUG_LEVEL1 -/* -* Local variables: -* c-basic-offset: 2 -* End: -*/ diff --git a/src/math/transformation/vpThetaUVector.h b/src/math/transformation/vpThetaUVector.h index 43e2f5f961f8c19cd46de28a8d43c38c15324b9a..354e7b1df8a40769bdd2be840e17d2a80f1ba9fb 100644 --- a/src/math/transformation/vpThetaUVector.h +++ b/src/math/transformation/vpThetaUVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpThetaUVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpThetaUVector.h 4792 2014-07-18 11:56:02Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -148,13 +148,15 @@ private: public: - // constructor - vpThetaUVector() { ; } - // copy constructor - vpThetaUVector(const vpThetaUVector &tu) ; + /*! Default constructor that initialize all the angles to zero. */ + vpThetaUVector() {} + /*! Copy constructor. */ + vpThetaUVector(const vpThetaUVector &tu) : vpRotationVector(tu) {} // constructor initialize a Theta U vector from a homogeneous matrix vpThetaUVector(const vpHomogeneousMatrix & M) ; + // constructor initialize a Theta U vector from a pose vector + vpThetaUVector(const vpPoseVector & p) ; // constructor initialize a Theta U vector from a rotation matrix vpThetaUVector(const vpRotationMatrix& R) ; // constructor initialize a Theta U vector from a RzyxVector @@ -172,6 +174,8 @@ public: // convert an homogeneous matrix into Theta U vector vpThetaUVector buildFrom(const vpHomogeneousMatrix& M) ; + // convert a pose vector into Theta U vector + vpThetaUVector buildFrom(const vpPoseVector& p) ; // convert a rotation matrix into Theta U vector vpThetaUVector buildFrom(const vpRotationMatrix& R) ; // convert an Rzyx vector into Theta U vector @@ -181,8 +185,6 @@ public: // convert an Rxyz vector into Theta U vector vpThetaUVector buildFrom(const vpRxyzVector &xyz) ; - // copy operator - vpThetaUVector &operator=(const vpThetaUVector &tu); vpThetaUVector &operator=(double x) ; // extract the angle and the axis from the ThetaU representation diff --git a/src/math/transformation/vpTranslationVector.cpp b/src/math/transformation/vpTranslationVector.cpp index 46e35fac39a8c67afa940f0ee05a1652057f2a91..00e7945ad3c5da7115cfaf4ef7abd5bf5d03a350 100644 --- a/src/math/transformation/vpTranslationVector.cpp +++ b/src/math/transformation/vpTranslationVector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTranslationVector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTranslationVector.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -82,7 +82,7 @@ vpTranslationVector::vpTranslationVector(const double tx, /*! Copy constructor. - \param t : Translation vector to copy. + \param tv : Translation vector to copy. \code vpTranslationVector t1(1,2,3); // Create and initialize a translation vector @@ -91,7 +91,7 @@ vpTranslationVector::vpTranslationVector(const double tx, \endcode */ -vpTranslationVector::vpTranslationVector (const vpTranslationVector &t) : vpColVector(t) +vpTranslationVector::vpTranslationVector (const vpTranslationVector &tv) : vpColVector(tv) { } @@ -114,7 +114,7 @@ vpTranslationVector::set(const double tx, /*! Operator that allows to add two translation vectors. - \param t : Translation vector to add. + \param tv : Translation vector to add. \return The sum of the current translation vector (*this) and the one to add. \code @@ -129,19 +129,19 @@ vpTranslationVector::set(const double tx, */ vpTranslationVector -vpTranslationVector::operator+(const vpTranslationVector &t) const +vpTranslationVector::operator+(const vpTranslationVector &tv) const { - vpTranslationVector sum ; + vpTranslationVector s; - for (unsigned int i=0;i<3;i++) sum[i] = (*this)[i]+t[i] ; + for (unsigned int i=0;i<3;i++) s[i] = (*this)[i]+tv[i] ; - return sum; + return s; } /*! Operator that allows to substract two translation vectors. - \param t : Translation vector to substract. + \param tv : Translation vector to substract. \return The substraction of the current translation vector (*this) and the one to substract. \code @@ -156,11 +156,11 @@ vpTranslationVector::operator+(const vpTranslationVector &t) const */ vpTranslationVector -vpTranslationVector::operator-(const vpTranslationVector &t) const +vpTranslationVector::operator-(const vpTranslationVector &tv) const { vpTranslationVector sub ; - for (unsigned int i=0;i<3;i++) sub[i] = (*this)[i]-t[i] ; + for (unsigned int i=0;i<3;i++) sub[i] = (*this)[i]-tv[i] ; return sub; } @@ -182,13 +182,13 @@ vpTranslationVector::operator-(const vpTranslationVector &t) const */ vpTranslationVector vpTranslationVector::operator-() const //negate { - vpTranslationVector t ; - for (unsigned int i=0;i<dsize;i++) - { - *(t.data + i) = -*(data + i) ; - } + vpTranslationVector tv ; + for (unsigned int i=0;i<dsize;i++) + { + *(tv.data + i) = -*(data + i) ; + } - return t; + return tv; } /*! @@ -208,18 +208,18 @@ vpTranslationVector vpTranslationVector::operator-() const //negate */ vpTranslationVector vpTranslationVector::operator*(const double x) const { - vpTranslationVector t ; - for (unsigned int i=0;i<dsize;i++) - { - *(t.data + i) = (*(data + i)) * x ; - } + vpTranslationVector tv ; + for (unsigned int i=0;i<dsize;i++) + { + *(tv.data + i) = (*(data + i)) * x ; + } - return t; + return tv; } /*! Copy operator. - \param t : Translation vector to copy + \param tv : Translation vector to copy \return A copy of t. \code @@ -230,10 +230,10 @@ vpTranslationVector vpTranslationVector::operator*(const double x) const // t2 is now equal to t1 : 1, 2, 3 \endcode */ -vpTranslationVector &vpTranslationVector::operator=(const vpTranslationVector &t) +vpTranslationVector &vpTranslationVector::operator=(const vpTranslationVector &tv) { - unsigned int k = t.rowNum ; + unsigned int k = tv.rowNum ; if (rowNum != k){ try { resize(k); @@ -245,7 +245,7 @@ vpTranslationVector &vpTranslationVector::operator=(const vpTranslationVector &t } } - memcpy(data, t.data, rowNum*sizeof(double)) ; + memcpy(data, tv.data, rowNum*sizeof(double)) ; return *this; } diff --git a/src/math/transformation/vpTranslationVector.h b/src/math/transformation/vpTranslationVector.h index 53d4e552e182a7ffaacf02bf628bbb9c879de79e..16c5d06f34de3a9fed58265f8a55afdb16eb22ef 100644 --- a/src/math/transformation/vpTranslationVector.h +++ b/src/math/transformation/vpTranslationVector.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTranslationVector.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTranslationVector.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpVelocityTwistMatrix.cpp b/src/math/transformation/vpVelocityTwistMatrix.cpp index 3bf506756e814136fb263247b2441570b408f986..add5bec6568b3d147fd76e614d1d37a8a1939893 100644 --- a/src/math/transformation/vpVelocityTwistMatrix.cpp +++ b/src/math/transformation/vpVelocityTwistMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpVelocityTwistMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpVelocityTwistMatrix.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -146,16 +146,16 @@ vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpHomogeneousMatrix &M) : vpM Initialize a velocity twist transformation matrix from a translation vector \e t and a rotation vector with \f$\theta u \f$ parametrization. - \param t : Translation vector. + \param tv : Translation vector. \param thetau : \f$\theta u\f$ rotation vector. */ -vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpTranslationVector &t, +vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpTranslationVector &tv, const vpThetaUVector &thetau) : vpMatrix() { init() ; - buildFrom(t, thetau) ; + buildFrom(tv, thetau) ; } /*! @@ -163,16 +163,16 @@ vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpTranslationVector &t, Initialize a velocity twist transformation matrix from a translation vector \e t and a rotation matrix M. - \param t : Translation vector. + \param tv : Translation vector. \param R : Rotation matrix. */ -vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpTranslationVector &t, +vpVelocityTwistMatrix::vpVelocityTwistMatrix(const vpTranslationVector &tv, const vpRotationMatrix &R) { init() ; - buildFrom(t,R) ; + buildFrom(tv,R) ; } /*! @@ -313,17 +313,18 @@ vpVelocityTwistMatrix::operator*(const vpColVector &v) const vpColVector c(6); if (6 != v.getRows()) - { - vpERROR_TRACE("vpVelocityTwistMatrix mismatch in vpVelocityTwistMatrix/vector multiply") ; - throw(vpMatrixException::incorrectMatrixSizeError) ; - } + { + vpERROR_TRACE("vpVelocityTwistMatrix mismatch in vpVelocityTwistMatrix/vector multiply") ; + throw(vpMatrixException(vpMatrixException::incorrectMatrixSizeError, + "Mismatch in vpVelocityTwistMatrix/vector multiply")) ; + } c = 0.0; for (unsigned int i=0;i<6;i++) { for (unsigned int j=0;j<6;j++) { { - c[i]+=rowPtrs[i][j] * v[j]; + c[i]+=rowPtrs[i][j] * v[j]; } } } @@ -337,26 +338,26 @@ vpVelocityTwistMatrix::operator*(const vpColVector &v) const Build a velocity twist transformation matrix from a translation vector \e t and a rotation matrix M. - \param t : Translation vector. + \param tv : Translation vector. \param R : Rotation matrix. */ vpVelocityTwistMatrix -vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &t, +vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &tv, const vpRotationMatrix &R) { unsigned int i, j; - vpMatrix skewaR = t.skew(t)*R ; + vpMatrix skewaR = tv.skew(tv)*R ; for (i=0 ; i < 3 ; i++) for (j=0 ; j < 3 ; j++) - { - (*this)[i][j] = R[i][j] ; - (*this)[i+3][j+3] = R[i][j] ; - (*this)[i][j+3] = skewaR[i][j] ; + { + (*this)[i][j] = R[i][j] ; + (*this)[i+3][j+3] = R[i][j] ; + (*this)[i][j+3] = skewaR[i][j] ; - } + } return (*this) ; } @@ -365,18 +366,18 @@ vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &t, Initialize a velocity twist transformation matrix from a translation vector \e t and a rotation vector with \f$\theta u \f$ parametrization. - \param t : Translation vector. + \param tv : Translation vector. \param thetau : \f$\theta u\f$ rotation vector. */ vpVelocityTwistMatrix -vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &t, +vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &tv, const vpThetaUVector &thetau) { vpRotationMatrix R ; R.buildFrom(thetau) ; - buildFrom(t,R) ; + buildFrom(tv,R) ; return (*this) ; } @@ -395,12 +396,12 @@ vpVelocityTwistMatrix::buildFrom(const vpTranslationVector &t, vpVelocityTwistMatrix vpVelocityTwistMatrix::buildFrom(const vpHomogeneousMatrix &M) { - vpTranslationVector t ; + vpTranslationVector tv ; vpRotationMatrix R ; M.extract(R) ; - M.extract(t) ; + M.extract(tv) ; - buildFrom(t, R) ; + buildFrom(tv, R) ; return (*this) ; } @@ -438,7 +439,7 @@ vpVelocityTwistMatrix::extract( vpRotationMatrix &R) const //! extract the translation vector from the twist matrix void -vpVelocityTwistMatrix::extract(vpTranslationVector &t) const +vpVelocityTwistMatrix::extract(vpTranslationVector &tv) const { vpRotationMatrix R;extract(R); vpMatrix skTR(3,3); @@ -447,9 +448,9 @@ vpVelocityTwistMatrix::extract(vpTranslationVector &t) const skTR[i][j] = (*this)[i][j+3]; vpMatrix skT = skTR*R.t(); - t[0] = skT[2][1]; - t[1] = skT[0][2]; - t[2] = skT[1][0]; + tv[0] = skT[2][1]; + tv[1] = skT[0][2]; + tv[2] = skT[1][0]; } /* diff --git a/src/math/transformation/vpVelocityTwistMatrix.h b/src/math/transformation/vpVelocityTwistMatrix.h index b8d0221eac5140bdc1f57e6571eb5c035bd1269b..1ca88bc486caed47ae1990ee4210b8ffbe2d3497 100644 --- a/src/math/transformation/vpVelocityTwistMatrix.h +++ b/src/math/transformation/vpVelocityTwistMatrix.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpVelocityTwistMatrix.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpVelocityTwistMatrix.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/math/transformation/vpXmlParserHomogeneousMatrix.cpp b/src/math/transformation/vpXmlParserHomogeneousMatrix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c9befe835d32b82dad2ec26811d071b7e3ea687 --- /dev/null +++ b/src/math/transformation/vpXmlParserHomogeneousMatrix.cpp @@ -0,0 +1,608 @@ +/**************************************************************************** + * + * $Id: vpXmlParserHomogeneousMatrix.cpp 4920 2014-10-09 08:18:30Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * XML parser to load and save Homogeneous Matrix in a XML file + * + * Authors: + * Giovanni Claudio + * + *****************************************************************************/ + + +/*! + \file vpXmlParserHomogeneousMatrix.cpp + \brief Definition of the vpXmlParserHomogeneousMatrix class member functions. + Class vpXmlParserHomogeneousMatrix allowed to load and save an homogeneous matrix in a XML file. + +*/ +#include <visp/vpXmlParserHomogeneousMatrix.h> +#ifdef VISP_HAVE_XML2 + +#include <stdlib.h> +#include <string.h> + +#include <visp/vpDebug.h> +#include <visp/vpThetaUVector.h> +/* -------------------------------------------------------------------------- */ +/* --- LABEL XML ------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ + +#define LABEL_XML_ROOT "root" +#define LABEL_XML_M "homogeneous_transformation" +#define LABEL_XML_M_NAME "name" +#define LABEL_XML_VALUE "values" +#define LABEL_XML_TRANSLATION "translation" +#define LABEL_XML_TX "tx" +#define LABEL_XML_TY "ty" +#define LABEL_XML_TZ "tz" +#define LABEL_XML_ROTATION "rotation" +#define LABEL_XML_TUX "theta_ux" +#define LABEL_XML_TUY "theta_uy" +#define LABEL_XML_TUZ "theta_uz" + +/*! + Default constructor +*/ +vpXmlParserHomogeneousMatrix::vpXmlParserHomogeneousMatrix() + : vpXmlParser(), m_M(), m_name() +{ +} +/*! + Copy constructor + \param twinParser : parser object to copy +*/ +vpXmlParserHomogeneousMatrix::vpXmlParserHomogeneousMatrix(vpXmlParserHomogeneousMatrix& twinParser) + : vpXmlParser(twinParser), m_M(), m_name() +{ + *this = twinParser; +} + +/*! + Copy operator + \param twinParser : parser object to copy + \return a copy of the input. +*/ +vpXmlParserHomogeneousMatrix& +vpXmlParserHomogeneousMatrix::operator =(const vpXmlParserHomogeneousMatrix& twinParser) +{ + this->m_M = twinParser.m_M; + this->m_name = twinParser.m_name; + + return *this ; +} + +/*! + Parse an xml file to load an homogeneous matrix + \param M : homogeneous matrix to fill. + \param filename : name of the xml file to parse. + \param name : name of the homogeneous matrix to find in the xml file. + + \return error code. +*/ +int +vpXmlParserHomogeneousMatrix::parse(vpHomogeneousMatrix &M, const std::string &filename, + const std::string &name) +{ + xmlDocPtr doc; + xmlNodePtr node; + + doc = xmlParseFile(filename.c_str()); + if (doc == NULL) + { + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " I cannot open the file "<< filename << std::endl; + + return SEQUENCE_ERROR; + } + + node = xmlDocGetRootElement(doc); + if (node == NULL) + { + xmlFreeDoc(doc); + return SEQUENCE_ERROR; + } + + int ret = this ->read (doc, node, name); + + M = m_M ; + + xmlFreeDoc(doc); + + return ret; +} + +/*! + Save an homogenous matrix in an xml file. + \param M : homogenous matrix to save. + \param filename : name of the xml file to fill. + \param name : name of the homogenous matrix. + + \return error code. +*/ +int +vpXmlParserHomogeneousMatrix::save(const vpHomogeneousMatrix &M, const std::string &filename, + const std::string &name) +{ + xmlDocPtr doc; + xmlNodePtr node; + + doc = xmlReadFile(filename.c_str(), NULL, XML_PARSE_NOWARNING + XML_PARSE_NOERROR + + XML_PARSE_NOBLANKS); + if (doc == NULL){ + doc = xmlNewDoc ((xmlChar*)"1.0"); + node = xmlNewNode(NULL,(xmlChar*)LABEL_XML_ROOT); + xmlDocSetRootElement(doc,node); + xmlNodePtr node_tmp = xmlNewComment((xmlChar*) + "This file stores homogeneous matrix used\n" + " in the vpHomogeneousMatrix Class of ViSP available\n" + " at http://www.irisa.fr/lagadic/visp/visp.html .\n" + " It can be read with the parse method of\n" + " the vpXmlParserHomogeneousMatrix class."); + xmlAddChild(node,node_tmp); + } + + node = xmlDocGetRootElement(doc); + if (node == NULL) + { + xmlFreeDoc(doc); + return SEQUENCE_ERROR; + } + + this->m_M = M; + + int M_isFound = count(doc, node, name); + + if( M_isFound > 0){ + //vpCERROR + std::cout << "There is already an homogeneous matrix "<< std::endl + << "available in the file with the input name: "<< name << "."<< std::endl + << "Please delete it manually from the xml file."<< std::endl; + xmlFreeDoc(doc); + return SEQUENCE_ERROR; + } + + write(node, name); + + xmlSaveFormatFile(filename.c_str(), doc, 1); + xmlFreeDoc(doc); +// std::cout << "Homogeneous matrix '"<< name << "' saved in the file named "<< filename << " correctly." << std::endl; + + return SEQUENCE_OK; +} + + + +/*! + Read Homogeneous matrix values from a XML file. + + \param doc : XML file. + \param node : XML tree, pointing on a marker equipement. + \param name : name of the Homogeneous Matrix + \return error code. + */ +int +vpXmlParserHomogeneousMatrix::read (xmlDocPtr doc, xmlNodePtr node, + const std::string& name) +{ + // char * val_char; + vpXmlCodeType prop; + + vpXmlCodeSequenceType back = SEQUENCE_OK; + unsigned int nbM = 0; + + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + if (node->type != XML_ELEMENT_NODE) continue; + if (SEQUENCE_OK != str2xmlcode ((char*)(node ->name), prop)) + { + prop = CODE_XML_OTHER; + back = SEQUENCE_ERROR; + } + + if (prop == CODE_XML_M){ + if (SEQUENCE_OK == this->read_matrix (doc, node, name)) + nbM++; + } + else back = SEQUENCE_ERROR; + } + + if (nbM == 0){ + back = SEQUENCE_ERROR; + vpCERROR << "No Homogeneous matrix is available" << std::endl + << "with name: " << name << std::endl; + } + else if(nbM > 1){ + back = SEQUENCE_ERROR; + vpCERROR << nbM << " There are more Homogeneous matrix" << std::endl + << "with the same name : " << std::endl + << "precise your choice..." << std::endl; + } + + return back; +} +/*! + Read homogeneous matrix names from a XML file and read if there is already a homogeneous matrix + with the same name. + + \param doc : XML file. + \param node : XML tree, pointing on a marker equipement. + \param name : name of the homogeneous matrix. + + \return 1 if there is an homogeneous matrix corresponding with the input name, 0 otherwise. + */ +int +vpXmlParserHomogeneousMatrix::count (xmlDocPtr doc, xmlNodePtr node, + const std::string& name) +{ + // char * val_char; + vpXmlCodeType prop; + int nbM = 0; + + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + if (node->type != XML_ELEMENT_NODE) continue; + if (SEQUENCE_OK != str2xmlcode ((char*)(node ->name), prop)) + { + prop = CODE_XML_OTHER; + } + if (prop== CODE_XML_M) { + if (SEQUENCE_OK == this->read_matrix (doc, node, name)) + nbM++; + } + } + + return nbM; +} + +/*! + Read Homogeneous Matrix fields from a XML file. + + \param doc : XML file. + \param node : XML tree, pointing on a marker equipement. + \param name : name of the Homogeneous matrix + + \return error code. + + */ +int +vpXmlParserHomogeneousMatrix::read_matrix (xmlDocPtr doc, xmlNodePtr node, + const std::string& name) +{ + vpXmlCodeType prop; + /* read value in the XML file. */ + std::string M_name_tmp = ""; + vpHomogeneousMatrix M_tmp; + + vpXmlCodeSequenceType back = SEQUENCE_OK; + + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + // vpDEBUG_TRACE (15, "Carac : %s.", node ->name); + if (node->type != XML_ELEMENT_NODE) continue; + if (SEQUENCE_OK != str2xmlcode ((char*)(node ->name), prop)) + { + prop = CODE_XML_OTHER; + back = SEQUENCE_ERROR; + } + + + switch (prop) + { + case CODE_XML_M_NAME: { + char * val_char = xmlReadCharChild(doc, node); + M_name_tmp = val_char; + xmlFree(val_char); + break; + } + + case CODE_XML_VALUE: //VALUE + if (name == M_name_tmp) + { + std::cout << "Found Homogeneous Matrix with name: \"" << M_name_tmp << "\"" << std::endl; + back = read_values(doc, node, M_tmp); + } + break; + + case CODE_XML_BAD: + case CODE_XML_OTHER: + case CODE_XML_M: + case CODE_XML_TX: + case CODE_XML_TY: + case CODE_XML_TZ: + case CODE_XML_TUX: + case CODE_XML_TUY: + case CODE_XML_TUZ: + + default: + back = SEQUENCE_ERROR; + break; + } + + } + + if( !(name == M_name_tmp)){ + back = SEQUENCE_ERROR; + } + else{ + this-> m_M = M_tmp; + //std::cout << "Convert in Homogeneous Matrix:"<< std::endl; + //std::cout << this-> M << std::endl; + this-> m_name = M_name_tmp; + + } + return back; +} + + +/*! + Read homogeneous matrix fields from a XML file. + + \param doc : XML file. + \param node : XML tree, pointing on a marker equipement. + \param M_tmp : homogeneous matrix to fill with read data (output). + + \return error code. + + */ +vpXmlParserHomogeneousMatrix::vpXmlCodeSequenceType +vpXmlParserHomogeneousMatrix::read_values (xmlDocPtr doc, xmlNodePtr node, + vpHomogeneousMatrix &M) +{ + // counter of the number of read parameters + int nb = 0; + vpXmlCodeType prop; + /* read value in the XML file. */ + + double tx_=0.; + double ty_=0.; + double tz_=0.; + double tux_=0.; + double tuy_=0.; + double tuz_=0.; + + vpXmlCodeSequenceType back = SEQUENCE_OK; + //int validation = 0; + + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + // vpDEBUG_TRACE (15, "Carac : %s.", node ->name); + if (node->type != XML_ELEMENT_NODE) continue; + if (SEQUENCE_OK != str2xmlcode ((char*)(node ->name), prop)) + { + prop = CODE_XML_OTHER; + back = SEQUENCE_ERROR; + } + + switch (prop) + { + + case CODE_XML_TX: + tx_ = xmlReadDoubleChild(doc, node); + nb++; + break; + case CODE_XML_TY: + ty_ = xmlReadDoubleChild(doc, node); + nb++; + break; + case CODE_XML_TZ: + tz_ = xmlReadDoubleChild(doc, node); + nb++; + break; + case CODE_XML_TUX: + tux_ = xmlReadDoubleChild(doc, node); + nb++; + break; + case CODE_XML_TUY: + tuy_ = xmlReadDoubleChild(doc, node); + nb++; + break; + case CODE_XML_TUZ: + tuz_ = xmlReadDoubleChild(doc, node); + nb++; + break; + + case CODE_XML_BAD: + case CODE_XML_OTHER: + case CODE_XML_M: + case CODE_XML_M_NAME: + case CODE_XML_VALUE: + + default: + back = SEQUENCE_ERROR; + break; + } + } + + if (nb != 6) + { + vpCERROR <<"ERROR in 'model' field:\n"; + vpCERROR << "it must contain 6 parameters\n"; + + return SEQUENCE_ERROR; + } + + // Create the Homogeneous matrix + M.buildFrom(tx_,ty_,tz_,tux_,tuy_,tuz_); + + // std::cout << "Read values from file:" << std::endl; + // std::cout << "tx:" << tx_<< std::endl; + // std::cout << "ty:" << ty_<< std::endl; + // std::cout << "tz:" << tz_<< std::endl; + // std::cout << "tux:" << tux_<< std::endl; + // std::cout << "tuy:" << tuy_<< std::endl; + // std::cout << "tuz:" << tuz_<< std::endl; + + return back; +} + +/*! + Write Homogeneous Matrix in an XML Tree. + + \param node : XML tree, pointing on a marker equipement. + \param name : name of the Homogeneous Matrix. + + + \return error code. + */ +int vpXmlParserHomogeneousMatrix:: +write (xmlNodePtr node, const std::string& name) +{ + int back = SEQUENCE_OK; + + xmlNodePtr node_tmp; + xmlNodePtr node_matrix; + xmlNodePtr node_values; + char str[11]; + + // Convert from Rotational matrix to Theta-U vector + vpRotationMatrix R; + m_M.extract(R); + + vpThetaUVector tu(R); + + // <homogenous_transformation> + node_tmp = xmlNewComment((xmlChar*)"Homogeneous Matrix"); + xmlAddChild(node,node_tmp); + node_matrix = xmlNewNode(NULL,(xmlChar*)LABEL_XML_M); + xmlAddChild(node,node_matrix); + { + //<name> + + if(!name.empty()){ + node_tmp = xmlNewComment((xmlChar*)"Name of the homogeneous matrix"); + xmlAddChild(node_matrix,node_tmp); + xmlNewTextChild(node_matrix,NULL,(xmlChar*)LABEL_XML_M_NAME, (xmlChar*)name.c_str()); + } + + //<values> + + node_values = xmlNewNode(NULL,(xmlChar*)LABEL_XML_VALUE); + xmlAddChild(node_matrix,node_values); + { + node_tmp = xmlNewComment((xmlChar*)"Translation vector with values in meters"); + xmlAddChild(node_values,node_tmp); + + //<tx> + sprintf(str,"%f", m_M[0][3]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TX,(xmlChar*)str); + + //<ty> + sprintf(str,"%f", m_M[1][3]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TY,(xmlChar*)str); + + //<tz> + sprintf(str,"%f", m_M[2][3]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TZ,(xmlChar*)str); + + node_tmp = xmlNewComment((xmlChar*)"Rotational vector expressed in angle axis representation with values in radians"); + xmlAddChild(node_values,node_tmp); + + //<tux> + sprintf(str,"%f", tu[0]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TUX,(xmlChar*)str); + + //<tuy> + sprintf(str,"%f", tu[1]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TUY,(xmlChar*)str); + + //<tuz> + sprintf(str,"%f", tu[2]); + xmlNewTextChild(node_values,NULL,(xmlChar*)LABEL_XML_TUZ,(xmlChar*)str); + } + } + return back; +} + +/*! + Translate a string (label) to a xml code. + \param str : string to translate. + \param res : resulting code. + + \return error code. +*/ + +vpXmlParserHomogeneousMatrix::vpXmlCodeSequenceType +vpXmlParserHomogeneousMatrix::str2xmlcode (char * str, vpXmlCodeType & res) +{ + vpXmlCodeType val_int = CODE_XML_BAD; + vpXmlCodeSequenceType back = vpXmlParserHomogeneousMatrix::SEQUENCE_OK; + + // DEBUG_TRACE (9, "# Entree :str=%s.", str); + + if (! strcmp (str, LABEL_XML_M)) + { + val_int = CODE_XML_M; + } + else if (! strcmp (str, LABEL_XML_M_NAME)) + { + val_int = CODE_XML_M_NAME; + } + else if (! strcmp (str, LABEL_XML_VALUE)) + { + val_int = CODE_XML_VALUE; + } + else if (! strcmp (str, LABEL_XML_TX)) + { + val_int = CODE_XML_TX; + } + else if (! strcmp (str, LABEL_XML_TY)) + { + val_int = CODE_XML_TY; + } + else if (! strcmp (str, LABEL_XML_TZ)) + { + val_int = CODE_XML_TZ; + } + else if (! strcmp (str, LABEL_XML_TUX)) + { + val_int = CODE_XML_TUX; + } + else if (! strcmp (str, LABEL_XML_TUY)) + { + val_int = CODE_XML_TUY; + } + else if (! strcmp (str, LABEL_XML_TUZ)) + { + val_int = CODE_XML_TUZ; + } + else + { + val_int = CODE_XML_OTHER; + } + res = val_int; + + return back; +} +#endif //VISP_HAVE_XML2 diff --git a/src/math/transformation/vpXmlParserHomogeneousMatrix.h b/src/math/transformation/vpXmlParserHomogeneousMatrix.h new file mode 100644 index 0000000000000000000000000000000000000000..27d288126c7a92af4ddbaf447ff192a34bf937b2 --- /dev/null +++ b/src/math/transformation/vpXmlParserHomogeneousMatrix.h @@ -0,0 +1,262 @@ +/**************************************************************************** + * + * $Id: vpXmlParserHomogeneousMatrix.h 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * XML parser to load and save Homogeneous Matrix in a XML file + * + * Authors: + * Giovanni Claudio + * + *****************************************************************************/ + +/*! + \file vpXmlParserHomogeneousMatrix.h + \brief Declaration of the vpXmlParserHomogeneousMatrix class. + Class vpXmlParserHomogeneousMatrix allowed to load and save Homogeneous Matrixes in a file XML + +*/ + +#ifndef vpXMLPARSERHOMOGENEOUSMATRIX_H +#define vpXMLPARSERHOMOGENEOUSMATRIX_H + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_XML2 + +#include <string> +#include <visp/vpXmlParser.h> +#include <visp/vpHomogeneousMatrix.h> +#include <libxml/xmlmemory.h> /* Functions of libxml. */ + +/*! + \class vpXmlParserHomogeneousMatrix + + \ingroup PoseTransformation + + \brief XML parser to load and save an homogenous matrix in a file. + + To have a complete description of the homogeneous matrix implemented in ViSP, see + vpHomogeneousMatrix. + + Example of an XML file "homogenous_matrixes.xml" containing a Pose vector + that will be converted in an homogeneous matrix: + + \code +<?xml version="1.0"?> +<root> + <homogeneous_transformation> + <!--Name of the homogeneous matrix--> + <name>eMc</name> + <values> + <!--Translation vector with values in meters --> + <tx>1.00</tx> + <ty>1.30</ty> + <tz>3.50</tz> + <!--Rotational vector expressed in angle axis representation with values in radians --> + <theta_ux>0.20</theta_ux> + <theta_uy>0.30</theta_uy> + <theta_uz>0.50</theta_uz> + </values> + </homogeneous_transformation> +</root> + \endcode + + Example of loading an existing homogeneous matrix from an XML file. + \code + +#include <iostream> +#include <string> + +#include <visp/vpXmlParserHomogeneousMatrix.h> + +int main(int argc, char* argv[]) +{ +#ifdef VISP_HAVE_XML2 + vpHomogeneousMatrix eMc; + + // Create a XML parser + vpXmlParserHomogeneousMatrix p; + + // Define the name of the matrix to load + std::string name = "eMc"; + + if (p.parse(eMc,"homogenous_matrixes.xml", name) != vpXmlParserHomogeneousMatrix::SEQUENCE_OK) { + std::cout << "Cannot found the Homogeneous matrix named " << name << "." << std::endl; + } + else + std::cout << "Homogeneous matrix " << name <<": " << std::endl << eMc << std::endl; +#endif + + return 0; +} + \endcode + + Example of writing an homogenoeus matrix in a XML file. + \note Before writing an homogeneous matrix check if there + is already in the xml file a matrix with the same name. + If you are sure to overwrite it please delete it manually + from the file before. + + \code +#include <iostream> +#include <string> + +#include <visp/vpXmlParserHomogeneousMatrix.h> + +int main(int argc, char* argv[]) +{ +#ifdef VISP_HAVE_XML2 + // Create Pose Vector and convert to homogeneous matrix + vpPoseVector r(1.0,1.3,3.5,0.2,0.3,0.5); + vpHomogeneousMatrix M(r); + + // Create a XML parser + vpXmlParserHomogeneousMatrix p; + + // Define the name of the matrix + std::string name_M = "eMe"; + + // Define name of the file xml to fill + char filename[FILENAME_MAX]; + sprintf(filename, "%s", "homogenous_matrixes.xml"); + + if (p.save(M, filename, name_M) != vpXmlParserHomogeneousMatrix::SEQUENCE_OK) { + std::cout << "Cannot save the Homogeneous matrix" << std::endl; + } + + vpXmlParser::cleanup(); +#endif + return 0; +} + \endcode +*/ + +class VISP_EXPORT vpXmlParserHomogeneousMatrix: public vpXmlParser +{ + +public: + + /* --- XML Code------------------------------------------------------------ */ + typedef enum + { + CODE_XML_BAD = -1, + CODE_XML_OTHER, + CODE_XML_M, + CODE_XML_M_NAME, + CODE_XML_VALUE, + CODE_XML_TX, + CODE_XML_TY, + CODE_XML_TZ, + CODE_XML_TUX, + CODE_XML_TUY, + CODE_XML_TUZ + } vpXmlCodeType; + + typedef enum + { + SEQUENCE_OK, + SEQUENCE_ERROR + } vpXmlCodeSequenceType; + +private : + + vpHomogeneousMatrix m_M; + std::string m_name; + +public: + + vpXmlParserHomogeneousMatrix(); + vpXmlParserHomogeneousMatrix(vpXmlParserHomogeneousMatrix& twinParser); + ~vpXmlParserHomogeneousMatrix(){} + + // get/set functions + vpHomogeneousMatrix getHomogeneousMatrix() const {return this->m_M;} + std::string getHomogeneousMatrixName() const {return this->m_name;} + + vpXmlParserHomogeneousMatrix& operator =(const vpXmlParserHomogeneousMatrix& twinparser); + int parse(vpHomogeneousMatrix &M, const std::string &filename, const std::string &name); + + int save(const vpHomogeneousMatrix &M, const std::string &filename, const std::string &name); + + void setHomogeneousMatrixName(const std::string& name){ + this->m_name = name; + } + +private: + int read (xmlDocPtr doc, xmlNodePtr node, + const std::string& name); + + int count (xmlDocPtr doc, xmlNodePtr node, + const std::string& name); + + int read_matrix (xmlDocPtr doc, xmlNodePtr node, + const std::string& name); + + vpXmlCodeSequenceType read_values (xmlDocPtr doc, xmlNodePtr node, + vpHomogeneousMatrix &M); + + static vpXmlCodeSequenceType str2xmlcode (char * str, vpXmlCodeType & res); + void myXmlReadIntChild (xmlDocPtr doc, + xmlNodePtr node, + int &res, + vpXmlCodeSequenceType &code_error); + + void myXmlReadDoubleChild (xmlDocPtr doc, + xmlNodePtr node, + double &res, + vpXmlCodeSequenceType &code_error); + + void myXmlReadCharChild (xmlDocPtr doc, + xmlNodePtr node, + char **res); + int write (xmlNodePtr node, const std::string& name); + +private: + + /*! + + \param 2doc : a pointer representing the document + \param node : the root node of the document + */ + virtual void readMainClass(xmlDocPtr , xmlNodePtr ){}; + + /*! + + + \param node2 : the root node of the document + */ + virtual void writeMainClass(xmlNodePtr ){}; + +}; +#endif //VISP_HAVE_XML2 +#endif diff --git a/src/network/vpClient.cpp b/src/network/vpClient.cpp index fb887c2734d8756e566b2a14b493b16265263f01..66e5e2a304c982bb0598bfe920856a349e4ab9e0 100644 --- a/src/network/vpClient.cpp +++ b/src/network/vpClient.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpClient.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpClient.cpp 4661 2014-02-10 19:34:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,8 +42,9 @@ #include <visp/vpClient.h> -vpClient::vpClient() : vpNetwork() -{} +vpClient::vpClient() : vpNetwork(), numberOfAttempts(0) +{ +} /*! Disconnect the client from all the servers, and close the sockets. @@ -82,7 +83,7 @@ bool vpClient::connectToHostname(const std::string &hostname, const unsigned int serv.socketFileDescriptorReceptor = socket( AF_INET, SOCK_STREAM, 0 ); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( serv.socketFileDescriptorReceptor < 0){ #else if ( serv.socketFileDescriptorReceptor == INVALID_SOCKET){ @@ -117,7 +118,7 @@ bool vpClient::connectToIP(const std::string &ip, const unsigned int &port_serv) serv.socketFileDescriptorReceptor = socket( AF_INET, SOCK_STREAM, 0 ); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( serv.socketFileDescriptorReceptor < 0){ #else if ( serv.socketFileDescriptorReceptor == INVALID_SOCKET){ @@ -144,9 +145,9 @@ void vpClient::deconnect(const unsigned int &index) { if(index < receptor_list.size()) { -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX shutdown( receptor_list[index].socketFileDescriptorReceptor, SHUT_RDWR ); -#else // WIN32 +#else // _WIN32 shutdown( receptor_list[index].socketFileDescriptorReceptor, SD_BOTH ); #endif receptor_list.erase(receptor_list.begin()+(int)index); @@ -159,9 +160,9 @@ void vpClient::deconnect(const unsigned int &index) void vpClient::stop() { for(unsigned int i = 0 ; i < receptor_list.size() ; i++){ -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX shutdown( receptor_list[i].socketFileDescriptorReceptor, SHUT_RDWR ); -#else // WIN32 +#else // _WIN32 shutdown( receptor_list[i].socketFileDescriptorReceptor, SD_BOTH ); #endif receptor_list.erase(receptor_list.begin()+(int)i); @@ -184,7 +185,7 @@ bool vpClient::connectServer(vpNetwork::vpReceptor &serv) numberOfAttempts = 15; unsigned int ind = 1; - int connectionResult; + int connectionResult=-1; while(ind <= numberOfAttempts){ std::cout << "Attempt number " << ind << "..." << std::endl; diff --git a/src/network/vpClient.h b/src/network/vpClient.h index 2197739eea5ec74bf522c7c150165c29e108ba53..0f457799a2ae37dde13bd3bbb809ac1798baa310 100644 --- a/src/network/vpClient.h +++ b/src/network/vpClient.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpClient.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpClient.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/network/vpNetwork.cpp b/src/network/vpNetwork.cpp index 579e79e2ed03dd76ef46306e720c76ff2e00cd0f..459e6e5c203502d272df85de9ba73c912c9bb977 100644 --- a/src/network/vpNetwork.cpp +++ b/src/network/vpNetwork.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNetwork.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpNetwork.cpp 4794 2014-07-21 17:43:22Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,19 +43,15 @@ #include <visp/vpNetwork.h> vpNetwork::vpNetwork() -{ - separator = "[*@*]"; - beginning = "[*start*]"; - end = "[*end*]"; - param_sep = "[*|*]"; - max_size_message = 999999; - - tv_sec = 0; - tv_usec = 10; + : emitter(), receptor_list(), readFileDescriptor(), socketMax(0), request_list(), + max_size_message(999999), separator("[*@*]"), beginning("[*start*]"), end("[*end*]"), + param_sep("[*|*]"), currentMessageReceived(), tv(), tv_sec(0), tv_usec(10), + verboseMode(false) +{ + tv.tv_sec = tv_sec; + tv.tv_usec = tv_usec; - verboseMode = false; - -#ifdef WIN32 +#if defined(_WIN32) //Enable the sockets to be used //Note that: if we were using "winsock.h" instead of "winsock2.h" we would had to use: //WSAStartup(MAKEWORD(1,0), &WSAData); @@ -66,7 +62,7 @@ vpNetwork::vpNetwork() vpNetwork::~vpNetwork() { -#ifdef WIN32 +#if defined(_WIN32) WSACleanup(); #endif } @@ -135,7 +131,7 @@ void vpNetwork::print(const char *id) \param name : Name of the receptor. - \return Index of the receptor. + \return Index of the receptor, or -1 if an error occurs. */ int vpNetwork::getReceptorIndex(const char *name) { @@ -146,7 +142,8 @@ int vpNetwork::getReceptorIndex(const char *name) std::string noSuchHostMessage( "ERROR, " ); noSuchHostMessage.append( name ); noSuchHostMessage.append( ": no such host\n" ); - vpERROR_TRACE( noSuchHostMessage.c_str(), "vpClient::getReceptorIndex()" ); + vpERROR_TRACE( noSuchHostMessage.c_str(), "vpNetwork::getReceptorIndex()" ); + return -1; } std::string ip = inet_ntoa(*(in_addr *)server->h_addr); @@ -216,11 +213,12 @@ int vpNetwork::sendRequestTo(vpRequest &req, const unsigned int &dest) message += end; int flags = 0; -#if ! defined(APPLE) && ! defined(WIN32) +//#if ! defined(APPLE) && ! defined(SOLARIS) && ! defined(_WIN32) +#if defined(__linux__) flags = MSG_NOSIGNAL; // Only for Linux #endif -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX int value = sendto(receptor_list[dest].socketFileDescriptorReceptor, message.c_str(), message.size(), flags, (sockaddr*) &receptor_list[dest].receptorAddress,receptor_list[dest].receptorAddressSize); #else @@ -578,7 +576,7 @@ int vpNetwork::_handleFirstRequest() size_t indEndParam = currentMessageReceived.find(param_sep,indDebParam); std::string param; - while(indEndParam != std::string::npos) + while(indEndParam != std::string::npos || indEndParam < indEnd) { param = currentMessageReceived.substr((unsigned)indDebParam,(unsigned)(indEndParam - indDebParam)); request_list[(unsigned)indRequest]->addParameter(param); @@ -689,17 +687,17 @@ int vpNetwork::_receiveRequestOnce() for(unsigned int i=0; i<receptor_list.size(); i++){ if(FD_ISSET((unsigned int)receptor_list[i].socketFileDescriptorReceptor,&readFileDescriptor)){ char *buf = new char [max_size_message]; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX numbytes=recv(receptor_list[i].socketFileDescriptorReceptor, buf, max_size_message, 0); #else numbytes=recv((unsigned int)receptor_list[i].socketFileDescriptorReceptor, buf, (int)max_size_message, 0); #endif - - + if(numbytes <= 0) { std::cout << "Disconnected : " << inet_ntoa(receptor_list[i].receptorAddress.sin_addr) << std::endl; receptor_list.erase(receptor_list.begin()+(int)i); + delete [] buf; return numbytes; } else if(numbytes > 0){ @@ -768,7 +766,7 @@ int vpNetwork::_receiveRequestOnceFrom(const unsigned int &receptorEmitting) else{ if(FD_ISSET((unsigned int)receptor_list[receptorEmitting].socketFileDescriptorReceptor,&readFileDescriptor)){ char *buf = new char [max_size_message]; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX numbytes=recv(receptor_list[receptorEmitting].socketFileDescriptorReceptor, buf, max_size_message, 0); #else numbytes=recv((unsigned int)receptor_list[receptorEmitting].socketFileDescriptorReceptor, buf, (int)max_size_message, 0); @@ -777,6 +775,7 @@ int vpNetwork::_receiveRequestOnceFrom(const unsigned int &receptorEmitting) { std::cout << "Disconnected : " << inet_ntoa(receptor_list[receptorEmitting].receptorAddress.sin_addr) << std::endl; receptor_list.erase(receptor_list.begin()+(int)receptorEmitting); + delete [] buf; return numbytes; } else if(numbytes > 0){ diff --git a/src/network/vpNetwork.h b/src/network/vpNetwork.h index 8a8407708e601aaca6f1e9ec93bd48896af13ffb..4be624c6b6253687f149d50b64f7cc23ab0f7b30 100644 --- a/src/network/vpNetwork.h +++ b/src/network/vpNetwork.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpNetwork.h 4135 2013-02-13 16:48:19Z fspindle $ + * $Id: vpNetwork.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,7 @@ #include <string.h> #include <iostream> -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> # include <sys/socket.h> # include <netinet/in.h> @@ -85,7 +85,7 @@ class VISP_EXPORT vpNetwork protected: struct vpReceptor{ -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX int socketFileDescriptorReceptor; socklen_t receptorAddressSize; #else @@ -94,15 +94,24 @@ protected: #endif struct sockaddr_in receptorAddress; std::string receptorIP; + + vpReceptor() : socketFileDescriptorReceptor(0), receptorAddressSize(), receptorAddress(), receptorIP() {} }; struct vpEmitter{ - struct sockaddr_in emitterAdress; -#ifdef UNIX + struct sockaddr_in emitterAddress; +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX int socketFileDescriptorEmitter; #else SOCKET socketFileDescriptorEmitter; #endif + vpEmitter() : emitterAddress(), socketFileDescriptorEmitter(0) + { + emitterAddress.sin_family = AF_INET; + emitterAddress.sin_addr.s_addr = INADDR_ANY; + emitterAddress.sin_port = 0; + socketFileDescriptorEmitter = 0; + } }; //######## PARAMETERS ######## @@ -112,7 +121,7 @@ protected: vpEmitter emitter; std::vector<vpReceptor> receptor_list; fd_set readFileDescriptor; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX int socketMax; #else SOCKET socketMax; @@ -305,7 +314,7 @@ int vpNetwork::receive(T* object, const unsigned int &sizeOfObject) else{ for(unsigned int i=0; i<receptor_list.size(); i++){ if(FD_ISSET((unsigned int)receptor_list[i].socketFileDescriptorReceptor,&readFileDescriptor)){ -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX numbytes = recv(receptor_list[i].socketFileDescriptorReceptor, (char*)(void*)object, sizeOfObject, 0); #else numbytes = recv((unsigned int)receptor_list[i].socketFileDescriptorReceptor, (char*)(void*)object, (int)sizeOfObject, 0); @@ -378,7 +387,7 @@ int vpNetwork::receiveFrom(T* object, const unsigned int &receptorEmitting, cons } else{ if(FD_ISSET((unsigned int)receptor_list[receptorEmitting].socketFileDescriptorReceptor,&readFileDescriptor)){ -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX numbytes = recv(receptor_list[receptorEmitting].socketFileDescriptorReceptor, (char*)(void*)object, sizeOfObject, 0); #else numbytes = recv((unsigned int)receptor_list[receptorEmitting].socketFileDescriptorReceptor, (char*)(void*)object, (int)sizeOfObject, 0); @@ -426,11 +435,12 @@ int vpNetwork::send(T* object, const unsigned int &sizeOfObject) } int flags = 0; -#if ! defined(APPLE) && ! defined(WIN32) +//#if ! defined(APPLE) && ! defined(SOLARIS) && ! defined(_WIN32) +#if defined(__linux__) flags = MSG_NOSIGNAL; // Only for Linux #endif -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX return sendto(receptor_list[0].socketFileDescriptorReceptor, (const char*)(void*)object, sizeOfObject, flags, (sockaddr*) &receptor_list[0].receptorAddress,receptor_list[0].receptorAddressSize); #else @@ -472,11 +482,12 @@ int vpNetwork::sendTo(T* object, const unsigned int &dest, const unsigned int &s } int flags = 0; -#if ! defined(APPLE) && ! defined(WIN32) +//#if ! defined(APPLE) && ! defined(SOLARIS) && ! defined(_WIN32) +#if defined(__linux__) flags = MSG_NOSIGNAL; // Only for Linux #endif -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX return sendto(receptor_list[dest].socketFileDescriptorReceptor, (const char*)(void*)object, sizeOfObject, flags, (sockaddr*) &receptor_list[dest].receptorAddress,receptor_list[dest].receptorAddressSize); #else diff --git a/src/network/vpRequest.cpp b/src/network/vpRequest.cpp index b300c787cc596688c7c2dd5109f3d40e0a53e255..4195c39a66974eb2d56d1b83188165a4a889dc41 100644 --- a/src/network/vpRequest.cpp +++ b/src/network/vpRequest.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRequest.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRequest.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,6 +42,7 @@ #include <visp/vpRequest.h> vpRequest::vpRequest() + : request_id(""), listOfParams() {} vpRequest::~vpRequest() diff --git a/src/network/vpRequest.h b/src/network/vpRequest.h index 0a0ea6bf3a820427adb5ca4f196d56b6b543c9c1..a155160535f318f3bd58632b749def0c7742dda2 100644 --- a/src/network/vpRequest.h +++ b/src/network/vpRequest.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRequest.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpRequest.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/network/vpServer.cpp b/src/network/vpServer.cpp index 2d85309366c78ce12ed4459515d04e83b12c80e5..01647acc34f818a0230c8f687e39877ff8e6c4a6 100644 --- a/src/network/vpServer.cpp +++ b/src/network/vpServer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServer.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,11 +45,11 @@ /*! Construct a server on the machine launching it. */ -vpServer::vpServer( ) : vpNetwork(), started(false) +vpServer::vpServer( ) : adress(), port(0), started(false), max_clients(10) { int protocol = 0; emitter.socketFileDescriptorEmitter = socket(AF_INET, SOCK_STREAM, protocol); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if (emitter.socketFileDescriptorEmitter < 0) #else if (emitter.socketFileDescriptorEmitter == INVALID_SOCKET) @@ -57,12 +57,12 @@ vpServer::vpServer( ) : vpNetwork(), started(false) { vpERROR_TRACE( "vpServer::vpServer(), cannot open socket." ); } - emitter.emitterAdress.sin_family = AF_INET; - emitter.emitterAdress.sin_addr.s_addr = INADDR_ANY; - emitter.emitterAdress.sin_port = 0; + emitter.emitterAddress.sin_family = AF_INET; + emitter.emitterAddress.sin_addr.s_addr = INADDR_ANY; + emitter.emitterAddress.sin_port = 0; - adress = inet_ntoa(emitter.emitterAdress.sin_addr); - port = emitter.emitterAdress.sin_port; + adress = inet_ntoa(emitter.emitterAddress.sin_addr); + port = emitter.emitterAddress.sin_port; } /*! @@ -70,11 +70,11 @@ vpServer::vpServer( ) : vpNetwork(), started(false) \param port_serv : server's port. */ -vpServer::vpServer( const int &port_serv ) : vpNetwork(), started(false) +vpServer::vpServer( const int &port_serv ) : adress(), port(0), started(false), max_clients(10) { int protocol = 0; emitter.socketFileDescriptorEmitter = socket(AF_INET, SOCK_STREAM, protocol); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if (emitter.socketFileDescriptorEmitter < 0) #else if (emitter.socketFileDescriptorEmitter == INVALID_SOCKET) @@ -82,11 +82,11 @@ vpServer::vpServer( const int &port_serv ) : vpNetwork(), started(false) { vpERROR_TRACE( "vpServer::vpServer(const int &port_serv), cannot open socket." ); } - emitter.emitterAdress.sin_family = AF_INET; - emitter.emitterAdress.sin_addr.s_addr = INADDR_ANY; //inet_addr("127.0.0.1");; - emitter.emitterAdress.sin_port = htons( (unsigned short)port_serv ); + emitter.emitterAddress.sin_family = AF_INET; + emitter.emitterAddress.sin_addr.s_addr = INADDR_ANY; //inet_addr("127.0.0.1");; + emitter.emitterAddress.sin_port = htons( (unsigned short)port_serv ); - adress = inet_ntoa(emitter.emitterAdress.sin_addr); + adress = inet_ntoa(emitter.emitterAddress.sin_addr); port = port_serv; } @@ -96,11 +96,12 @@ vpServer::vpServer( const int &port_serv ) : vpNetwork(), started(false) \param adress_serv : server's adress. \param port_serv : server's port. */ -vpServer::vpServer( const std::string &adress_serv,const int &port_serv ) : vpNetwork(), started(false) +vpServer::vpServer( const std::string &adress_serv,const int &port_serv ) + : adress(), port(0), started(false), max_clients(10) { int protocol = 0; emitter.socketFileDescriptorEmitter = socket(AF_INET, SOCK_STREAM, protocol); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if (emitter.socketFileDescriptorEmitter < 0) #else if (emitter.socketFileDescriptorEmitter == INVALID_SOCKET) @@ -108,9 +109,9 @@ vpServer::vpServer( const std::string &adress_serv,const int &port_serv ) : vpNe { vpERROR_TRACE( "vpServer::vpServer(const std::string &adress_serv,const int &port_serv), cannot open socket." ); } - emitter.emitterAdress.sin_family = AF_INET; - emitter.emitterAdress.sin_addr.s_addr = inet_addr(adress_serv.c_str()); - emitter.emitterAdress.sin_port = htons( (unsigned short)port_serv ); + emitter.emitterAddress.sin_family = AF_INET; + emitter.emitterAddress.sin_addr.s_addr = inet_addr(adress_serv.c_str()); + emitter.emitterAddress.sin_port = htons( (unsigned short)port_serv ); adress = adress_serv; port = port_serv; @@ -121,14 +122,14 @@ vpServer::vpServer( const std::string &adress_serv,const int &port_serv ) : vpNe */ vpServer::~vpServer() { -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX close( emitter.socketFileDescriptorEmitter ); #else //Win32 closesocket( (unsigned)emitter.socketFileDescriptorEmitter ); #endif for(unsigned int i = 0 ; i < receptor_list.size() ; i++) -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX close( receptor_list[i].socketFileDescriptorReceptor ); #else //Win32 closesocket( (unsigned)receptor_list[i].socketFileDescriptorReceptor ); @@ -143,11 +144,11 @@ vpServer::~vpServer() */ bool vpServer::start() { - int serverStructLength = sizeof(emitter.emitterAdress); -#ifdef UNIX - int bindResult = bind( emitter.socketFileDescriptorEmitter, (struct sockaddr *) &emitter.emitterAdress, (unsigned)serverStructLength ); + int serverStructLength = sizeof(emitter.emitterAddress); +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + int bindResult = bind( emitter.socketFileDescriptorEmitter, (struct sockaddr *) &emitter.emitterAddress, (unsigned)serverStructLength ); #else //Win32 - int bindResult = bind( (unsigned)emitter.socketFileDescriptorEmitter, (struct sockaddr *) &emitter.emitterAdress, serverStructLength ); + int bindResult = bind( (unsigned)emitter.socketFileDescriptorEmitter, (struct sockaddr *) &emitter.emitterAddress, serverStructLength ); #endif @@ -176,7 +177,7 @@ bool vpServer::start() } #endif // SO_NOSIGPIPE -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX listen( emitter.socketFileDescriptorEmitter, (int)max_clients ); #else //Win32 listen( (unsigned)emitter.socketFileDescriptorEmitter, (int)max_clients ); @@ -230,13 +231,13 @@ bool vpServer::checkForConnections() if(FD_ISSET((unsigned int)emitter.socketFileDescriptorEmitter,&readFileDescriptor)){ vpNetwork::vpReceptor client; client.receptorAddressSize = sizeof(client.receptorAddress); -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX client.socketFileDescriptorReceptor = accept(emitter.socketFileDescriptorEmitter,(struct sockaddr*) &client.receptorAddress, &client.receptorAddressSize); #else //Win32 client.socketFileDescriptorReceptor = accept((unsigned int)emitter.socketFileDescriptorEmitter,(struct sockaddr*) &client.receptorAddress, &client.receptorAddressSize); #endif -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if((client.socketFileDescriptorReceptor) == -1) #else if((client.socketFileDescriptorReceptor) == INVALID_SOCKET) @@ -253,7 +254,7 @@ bool vpServer::checkForConnections() for(unsigned int i=0; i<receptor_list.size(); i++){ if(FD_ISSET((unsigned int)receptor_list[i].socketFileDescriptorReceptor,&readFileDescriptor)){ char deco; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX int numbytes = recv(receptor_list[i].socketFileDescriptorReceptor, &deco, 1, MSG_PEEK); #else //Win32 int numbytes = recv((unsigned int)receptor_list[i].socketFileDescriptorReceptor, &deco, 1, MSG_PEEK); diff --git a/src/network/vpServer.h b/src/network/vpServer.h index 5adf80704c15a35f20549a2966063ffc58033698..b1873e2cea824fc70ffeabcd275a4e7734760a74 100644 --- a/src/network/vpServer.h +++ b/src/network/vpServer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServer.h 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpServer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/real-robot/afma4/vpAfma4.cpp b/src/robot/real-robot/afma4/vpAfma4.cpp index 66c2751f47d73df4b1e5787643cbbc17dd2f2534..32ee3c9d46e13d21a6e3a6c2f0189823b27cf06c 100644 --- a/src/robot/real-robot/afma4/vpAfma4.cpp +++ b/src/robot/real-robot/afma4/vpAfma4.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAfma4.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAfma4.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,6 +69,7 @@ const unsigned int vpAfma4::njoint = 4; */ vpAfma4::vpAfma4() + : _a1(0), _d3(0), _d4(0), _etc(), _erc(), _eMc() { // Set the default parameters in case of the config files on the NAS // at Inria are not available. @@ -148,7 +149,7 @@ vpAfma4::init (void) */ vpHomogeneousMatrix -vpAfma4::getForwardKinematics(const vpColVector & q) +vpAfma4::getForwardKinematics(const vpColVector & q) const { vpHomogeneousMatrix fMc; fMc = get_fMc(q); @@ -186,7 +187,7 @@ vpAfma4::getForwardKinematics(const vpColVector & q) \sa getForwardKinematics(const vpColVector & q) */ vpHomogeneousMatrix -vpAfma4::get_fMc (const vpColVector & q) +vpAfma4::get_fMc (const vpColVector & q) const { vpHomogeneousMatrix fMc; get_fMc(q, fMc); @@ -221,7 +222,7 @@ vpAfma4::get_fMc (const vpColVector & q) */ void -vpAfma4::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) +vpAfma4::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const { // Compute the direct geometric model: fMe = transformation between @@ -269,7 +270,7 @@ vpAfma4::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) */ void -vpAfma4::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) +vpAfma4::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const { double q1 = q[0]; // rot touret double q2 = q[1]; // vertical translation @@ -320,7 +321,7 @@ vpAfma4::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) */ void -vpAfma4::get_cMe(vpHomogeneousMatrix &cMe) +vpAfma4::get_cMe(vpHomogeneousMatrix &cMe) const { cMe = this->_eMc.inverse(); } @@ -335,7 +336,7 @@ vpAfma4::get_cMe(vpHomogeneousMatrix &cMe) */ void -vpAfma4::get_cVe(vpVelocityTwistMatrix &cVe) +vpAfma4::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; get_cMe(cMe) ; @@ -364,7 +365,7 @@ vpAfma4::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpAfma4::get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf) +vpAfma4::get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf) const { vpHomogeneousMatrix fMc, cMf ; get_fMc(q, fMc) ; @@ -375,8 +376,6 @@ vpAfma4::get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf) return; } - - /*! Get the robot jacobian expressed in the end-effector frame: @@ -414,7 +413,7 @@ vpAfma4::get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf) \sa get_fJe() */ void -vpAfma4::get_eJe(const vpColVector &q, vpMatrix &eJe) +vpAfma4::get_eJe(const vpColVector &q, vpMatrix &eJe) const { double q4 = q[2]; // pan double q5 = q[3]; // tilt @@ -468,7 +467,7 @@ vpAfma4::get_eJe(const vpColVector &q, vpMatrix &eJe) */ void -vpAfma4::get_fJe(const vpColVector &q, vpMatrix &fJe) +vpAfma4::get_fJe(const vpColVector &q, vpMatrix &fJe) const { fJe.resize(6,4) ; @@ -524,7 +523,7 @@ vpAfma4::get_fJe(const vpColVector &q, vpMatrix &fJe) \sa get_eJe() and get_fJe() */ -void vpAfma4::get_fJe_inverse(const vpColVector &q, vpMatrix &fJe_inverse) +void vpAfma4::get_fJe_inverse(const vpColVector &q, vpMatrix &fJe_inverse) const { fJe_inverse.resize(4, 6) ; fJe_inverse = 0; @@ -560,7 +559,7 @@ void vpAfma4::get_fJe_inverse(const vpColVector &q, vpMatrix &fJe_inverse) */ vpColVector -vpAfma4::getJointMin() +vpAfma4::getJointMin() const { vpColVector qmin(4); for (unsigned int i=0; i < 4; i ++) @@ -577,7 +576,7 @@ vpAfma4::getJointMin() */ vpColVector -vpAfma4::getJointMax() +vpAfma4::getJointMax() const { vpColVector qmax(4); for (unsigned int i=0; i < 4; i ++) @@ -596,7 +595,7 @@ vpAfma4::getJointMax() \param os : Output stream. \param afma4 : Robot parameters. */ -std::ostream & operator << (std::ostream & os, +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpAfma4 & afma4) { vpRotationMatrix eRc; @@ -649,9 +648,3 @@ std::ostream & operator << (std::ostream & os, return os; } - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/robot/real-robot/afma4/vpAfma4.h b/src/robot/real-robot/afma4/vpAfma4.h index 4acab0cd0c6a6a3ad6270573d47ef5003578c7af..376314a65fddc8058939eae38ff3518a32f319bf 100644 --- a/src/robot/real-robot/afma4/vpAfma4.h +++ b/src/robot/real-robot/afma4/vpAfma4.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAfma4.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAfma4.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -113,28 +113,29 @@ class VISP_EXPORT vpAfma4 { public: vpAfma4(); + /*! Destructor that does nothing. */ + virtual ~vpAfma4() {}; void init (void); - vpHomogeneousMatrix getForwardKinematics(const vpColVector & q); + vpHomogeneousMatrix getForwardKinematics(const vpColVector & q) const; /* int getInverseKinematics(const vpHomogeneousMatrix & fMc, */ /* vpColVector & q, const bool &nearest=true); */ - vpHomogeneousMatrix get_fMc (const vpColVector & q); - void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe); - void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc); - - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf); - void get_eJe(const vpColVector &q, vpMatrix &eJe) ; - void get_fJe(const vpColVector &q, vpMatrix &fJe) ; - void get_fJe_inverse(const vpColVector &q, vpMatrix &fJe_inverse) ; - - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpAfma4 & afma4); - - vpColVector getJointMin(); - vpColVector getJointMax(); + vpHomogeneousMatrix get_fMc (const vpColVector & q) const; + void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const; + void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const; + + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_cVf(const vpColVector & q, vpVelocityTwistMatrix &cVf) const; + void get_eJe(const vpColVector &q, vpMatrix &eJe) const; + void get_fJe(const vpColVector &q, vpMatrix &fJe) const; + void get_fJe_inverse(const vpColVector &q, vpMatrix &fJe_inverse) const; + + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpAfma4 & afma4); + + vpColVector getJointMin() const; + vpColVector getJointMax() const; public: diff --git a/src/robot/real-robot/afma4/vpRobotAfma4.cpp b/src/robot/real-robot/afma4/vpRobotAfma4.cpp index d79c9d7cd4b943eacb0c894cb43e4e6a6ec25b53..731a35792c5dbda2c03cdd05de36c6593c090dc1 100644 --- a/src/robot/real-robot/afma4/vpRobotAfma4.cpp +++ b/src/robot/real-robot/afma4/vpRobotAfma4.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotAfma4.cpp 4107 2013-02-06 10:04:49Z fspindle $ + * $Id: vpRobotAfma4.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -554,7 +554,7 @@ vpRobotAfma4::getPowerState(void) */ void -vpRobotAfma4::get_cVe(vpVelocityTwistMatrix &cVe) +vpRobotAfma4::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; vpAfma4::get_cMe(cMe) ; @@ -572,7 +572,7 @@ vpRobotAfma4::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpRobotAfma4::get_cVf(vpVelocityTwistMatrix &cVf) +vpRobotAfma4::get_cVf(vpVelocityTwistMatrix &cVf) const { double position[this->njoint]; double timestamp; @@ -604,7 +604,7 @@ vpRobotAfma4::get_cVf(vpVelocityTwistMatrix &cVf) */ void -vpRobotAfma4::get_cMe(vpHomogeneousMatrix &cMe) +vpRobotAfma4::get_cMe(vpHomogeneousMatrix &cMe) const { vpAfma4::get_cMe(cMe) ; } diff --git a/src/robot/real-robot/afma4/vpRobotAfma4.h b/src/robot/real-robot/afma4/vpRobotAfma4.h index e61dea0eb7df9d1ec59fada69aa6aa01370ace68..b7d19e0f6a135b00e784f4eee894a60f91ba785b 100644 --- a/src/robot/real-robot/afma4/vpRobotAfma4.h +++ b/src/robot/real-robot/afma4/vpRobotAfma4.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotAfma4.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotAfma4.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -251,11 +251,11 @@ public: /* Methode publiques */ vpColVector getVelocity (const vpRobot::vpControlFrameType frame); vpColVector getVelocity (const vpRobot::vpControlFrameType frame, double ×tamp); - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_cVf(vpVelocityTwistMatrix &cVf) ; - void get_eJe(vpMatrix &eJe) ; - void get_fJe(vpMatrix &fJe) ; + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_cVf(vpVelocityTwistMatrix &cVf) const; + void get_eJe(vpMatrix &eJe); + void get_fJe(vpMatrix &fJe); void init (void); diff --git a/src/robot/real-robot/afma4/vpServolens.cpp b/src/robot/real-robot/afma4/vpServolens.cpp index 1fe21d8fbf14812e8e6b5960d336376a065869af..1104be29907175fcfadbc2b30b6348d07de054a8 100644 --- a/src/robot/real-robot/afma4/vpServolens.cpp +++ b/src/robot/real-robot/afma4/vpServolens.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServolens.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServolens.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,7 @@ */ -#if defined(UNIX) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX #include <unistd.h> #include <termios.h> @@ -71,9 +71,8 @@ \sa open() */ -vpServolens::vpServolens() +vpServolens::vpServolens() : remfd(0), isinit(false) { - isinit = false; } /*! @@ -84,10 +83,8 @@ vpServolens::vpServolens() \sa open() */ -vpServolens::vpServolens(const char *port) +vpServolens::vpServolens(const char *port) : remfd(0), isinit(false) { - isinit = false; - this->open(port); } @@ -131,7 +128,7 @@ vpServolens::open(const char *port) "Cannot open Servolens serial port."); } - // Lecture des paramètres courants de la liaison série. + // Lecture des parametres courants de la liaison serie. if (tcgetattr(this->remfd, &info) < 0) { ::close(this->remfd); vpERROR_TRACE ("Error using TCGETS in ioctl."); @@ -144,11 +141,11 @@ vpServolens::open(const char *port) // 9600 bauds, 1 bit de stop, parite paire, 7 bits de donnee // - // Traitement sur les caractères recus + // Traitement sur les caracteres recus info.c_iflag = 0; info.c_iflag |= INLCR; - // Traitement sur les caractères envoyés sur la RS232. + // Traitement sur les caracteres envoyes sur la RS232. info.c_oflag = 0; // idem // Traitement des lignes @@ -159,7 +156,7 @@ vpServolens::open(const char *port) info.c_cflag |= CREAD; // Validation reception info.c_cflag |= B9600 | CS7 | PARENB; // 9600 baus, 7 data, parite paire - // Caractères immédiatement disponibles. + // Caracteres immediatement disponibles. // info.c_cc[VMIN] = 1; // info.c_cc[VTIME] = 0; @@ -210,7 +207,7 @@ vpServolens::close() with Servolens. */ void -vpServolens::reset() +vpServolens::reset() const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -248,7 +245,7 @@ vpServolens::reset() \sa open() */ void -vpServolens::init() +vpServolens::init() const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -283,7 +280,7 @@ vpServolens::init() */ void -vpServolens::enableCmdComplete(vpServoType servo, bool active) +vpServolens::enableCmdComplete(vpServoType servo, bool active) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -331,7 +328,7 @@ vpServolens::enableCmdComplete(vpServoType servo, bool active) */ void -vpServolens::enablePrompt(bool active) +vpServolens::enablePrompt(bool active) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -358,7 +355,7 @@ vpServolens::enablePrompt(bool active) with Servolens. */ void -vpServolens::setController(vpControllerType controller) +vpServolens::setController(vpControllerType controller) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -399,7 +396,7 @@ vpServolens::setController(vpControllerType controller) */ void -vpServolens::setAutoIris(bool enable) +vpServolens::setAutoIris(bool enable) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -427,7 +424,7 @@ vpServolens::setAutoIris(bool enable) */ void -vpServolens::setPosition(vpServoType servo, unsigned int position) +vpServolens::setPosition(vpServoType servo, unsigned int position) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -442,15 +439,15 @@ vpServolens::setPosition(vpServoType servo, unsigned int position) this->wait(); */ -#if FINSERVO +#ifdef FINSERVO /* envoie des commandes pour qu'en fin de mouvement servolens renvoie */ /* une commande de fin de mouvement (ex: ZF, FF, DF). */ this->enableCommandComplete(); #endif /* FINSERVO */ // 08/08/00 Fabien S. - Correction de la consigne demandee - // pour prendre en compte l'erreur entre la consigne demandée - // et la consigne mesurée. + // pour prendre en compte l'erreur entre la consigne demandee + // et la consigne mesuree. // A la consigne du zoom on retranche 1. // A la consigne du focus on ajoute 1. // A la consigne du iris on ajoute 1. @@ -488,13 +485,13 @@ vpServolens::setPosition(vpServoType servo, unsigned int position) break; } /* envoie de la commande */ -#if PRINT +#ifdef PRINT printf("\ncommande: %s", commande); #endif this->write(commande); -#if FINSERVO +#ifdef FINSERVO /* on attend la fin du mouvement des objectifs */ this->wait(servo); /* on attend les codes ZF, FF, DF */ #endif @@ -511,7 +508,7 @@ vpServolens::setPosition(vpServoType servo, unsigned int position) */ bool -vpServolens::getPosition(vpServoType servo, unsigned int &position) +vpServolens::getPosition(vpServoType servo, unsigned int &position) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -634,7 +631,7 @@ vpServolens::getPosition(vpServoType servo, unsigned int &position) int main() { -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX vpServolens servolens("/dev/ttyS0"); vpImage<unsigned char> I(240, 320); @@ -649,7 +646,7 @@ vpServolens::getPosition(vpServoType servo, unsigned int &position) */ vpCameraParameters -vpServolens::getCameraParameters(vpImage<unsigned char> &I) +vpServolens::getCameraParameters(vpImage<unsigned char> &I) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -697,7 +694,7 @@ vpServolens::getCameraParameters(vpImage<unsigned char> &I) with Servolens. */ char -vpServolens::wait() +vpServolens::wait() const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -732,7 +729,7 @@ vpServolens::wait() */ void -vpServolens::wait(vpServoType servo) +vpServolens::wait(vpServoType servo) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -798,7 +795,7 @@ vpServolens::wait(vpServoType servo) with Servolens. */ bool -vpServolens::read(char *c, long timeout_s) +vpServolens::read(char *c, long timeout_s) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -835,7 +832,7 @@ vpServolens::read(char *c, long timeout_s) with Servolens. */ void -vpServolens::write(const char *s) +vpServolens::write(const char *s) const { if (!isinit) { vpERROR_TRACE ("Cannot dial with Servolens."); @@ -874,7 +871,7 @@ vpServolens::write(const char *s) */ bool -vpServolens::clean(const char *in, char *out) +vpServolens::clean(const char *in, char *out) const { short nb_car, i=0; bool error = false; diff --git a/src/robot/real-robot/afma4/vpServolens.h b/src/robot/real-robot/afma4/vpServolens.h index f7e98de4aa79780db191838654da7bafd32626cb..fcf4e64a2739255d62a2af30f29cddfc420c1d59 100644 --- a/src/robot/real-robot/afma4/vpServolens.h +++ b/src/robot/real-robot/afma4/vpServolens.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServolens.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServolens.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,7 +45,7 @@ #include <visp/vpConfig.h> -#if defined(UNIX) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) /*! @@ -120,39 +120,34 @@ class VISP_EXPORT vpServolens void open(const char *port="/dev/ttyS0"); void close(); - void reset(); + void reset() const; - void setController(vpControllerType controller); - void setAutoIris(bool enable); - void setPosition(vpServoType servo, unsigned int position); - bool getPosition(vpServoType servo, unsigned int &position); - vpCameraParameters getCameraParameters(vpImage<unsigned char> &I); + void setController(vpControllerType controller) const; + void setAutoIris(bool enable) const; + void setPosition(vpServoType servo, unsigned int position) const; + bool getPosition(vpServoType servo, unsigned int &position) const; + vpCameraParameters getCameraParameters(vpImage<unsigned char> &I) const; - void enablePrompt(bool active); + void enablePrompt(bool active) const; private: - void init(); + void init() const; - void enableCmdComplete(vpServoType servo, bool active); + void enableCmdComplete(vpServoType servo, bool active) const; - char wait(); - void wait(vpServoType servo); + char wait() const; + void wait(vpServoType servo) const; - bool read(char *c, long timeout_s); - void write(const char *s); + bool read(char *c, long timeout_s) const; + void write(const char *s) const; - bool clean(const char *in, char *out); + bool clean(const char *in, char *out) const; int remfd; // file pointer of the host's tty bool isinit; }; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ #endif #endif diff --git a/src/robot/real-robot/afma6/vpAfma6.cpp b/src/robot/real-robot/afma6/vpAfma6.cpp index 67879f29b246092d6b321e0d380c09825640b090..38008ff1b2f6198719d0abdd6790781cb8d969f1 100644 --- a/src/robot/real-robot/afma6/vpAfma6.cpp +++ b/src/robot/real-robot/afma6/vpAfma6.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAfma6.cpp 4210 2013-04-16 08:57:46Z fspindle $ + * $Id: vpAfma6.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,70 +68,70 @@ static const char *opt_Afma6[] = {"JOINT_MAX","JOINT_MIN","LONG_56","COUPL_56", NULL}; const char * const vpAfma6::CONST_AFMA6_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_CCMOP_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_ccmop_without_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_ccmop_without_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_CCMOP_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_ccmop_with_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_ccmop_with_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_GRIPPER_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_gripper_without_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_gripper_without_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_GRIPPER_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_gripper_with_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_gripper_with_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_VACUUM_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_vacuum_without_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_vacuum_without_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_VACUUM_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_vacuum_with_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_vacuum_with_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_generic_without_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_generic_without_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_eMc_generic_with_distortion_Afma6.cnf"; #else = "/udd/fspindle/robot/Afma6/current/include/const_eMc_generic_with_distortion_Afma6.cnf"; #endif const char * const vpAfma6::CONST_CAMERA_AFMA6_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Afma6/current/include/const_camera_Afma6.xml"; #else = "/udd/fspindle/robot/Afma6/current/include/const_camera_Afma6.xml"; @@ -154,6 +154,9 @@ const unsigned int vpAfma6::njoint = 6; */ vpAfma6::vpAfma6() + : _coupl_56(0), _long_56(0), _etc(), _erc(), _eMc(), + tool_current(vpAfma6::defaultTool), + projModel(vpCameraParameters::perspectiveProjWithoutDistortion) { // Set the default parameters in case of the config files on the NAS // at Inria are not available. @@ -237,15 +240,15 @@ vpAfma6::init (const char * paramAfma6, \param tool : Camera in use. - \param projModel : Projection model of the camera. + \param proj_model : Projection model of the camera. */ void vpAfma6::init (vpAfma6::vpAfma6ToolType tool, - vpCameraParameters::vpCameraParametersProjType projModel) + vpCameraParameters::vpCameraParametersProjType proj_model) { - this->projModel = projModel; + this->projModel = proj_model; #ifdef VISP_HAVE_ACCESS_TO_NAS // Read the robot parameters from files @@ -254,19 +257,19 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, case vpAfma6::TOOL_CCMOP: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_CCMOP_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_CCMOP_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_CCMOP_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_CCMOP_WITH_DISTORTION_FILENAME); #endif @@ -277,19 +280,19 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, case vpAfma6::TOOL_GRIPPER: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GRIPPER_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GRIPPER_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GRIPPER_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GRIPPER_WITH_DISTORTION_FILENAME); #endif @@ -300,19 +303,19 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, case vpAfma6::TOOL_VACUUM: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_VACUUM_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_VACUUM_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_VACUUM_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_VACUUM_WITH_DISTORTION_FILENAME); #endif @@ -323,19 +326,19 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, case vpAfma6::TOOL_GENERIC_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); #endif @@ -382,6 +385,7 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, _etc[2] = 0.2286; // tz break; } + break; } case vpAfma6::TOOL_GRIPPER: { switch(projModel) { @@ -402,6 +406,7 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, _etc[2] = 0.1642; // tz break; } + break; } case vpAfma6::TOOL_VACUUM: { switch(projModel) { @@ -422,6 +427,7 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, _etc[2] = 0.1658; // tz break; } + break; } case vpAfma6::TOOL_GENERIC_CAMERA: { switch(projModel) { @@ -436,6 +442,7 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, _etc[2] = 0; // tz break; } + break; } } vpRotationMatrix eRc(_erc); @@ -472,7 +479,7 @@ vpAfma6::init (vpAfma6::vpAfma6ToolType tool, */ vpHomogeneousMatrix -vpAfma6::getForwardKinematics(const vpColVector & q) +vpAfma6::getForwardKinematics(const vpColVector & q) const { vpHomogeneousMatrix fMc; fMc = get_fMc(q); @@ -555,7 +562,7 @@ int main() */ int vpAfma6::getInverseKinematics(const vpHomogeneousMatrix & fMc, - vpColVector & q, const bool &nearest, const bool &verbose) + vpColVector & q, const bool &nearest, const bool &verbose) const { vpHomogeneousMatrix fMe; double q_[2][6],d[2],t; @@ -735,7 +742,7 @@ vpAfma6::getInverseKinematics(const vpHomogeneousMatrix & fMc, \sa getForwardKinematics(const vpColVector & q) */ vpHomogeneousMatrix -vpAfma6::get_fMc (const vpColVector & q) +vpAfma6::get_fMc (const vpColVector & q) const { vpHomogeneousMatrix fMc; get_fMc(q, fMc); @@ -763,7 +770,7 @@ vpAfma6::get_fMc (const vpColVector & q) */ void -vpAfma6::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) +vpAfma6::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const { // Compute the direct geometric model: fMe = transformation between @@ -797,7 +804,7 @@ vpAfma6::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) */ void -vpAfma6::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) +vpAfma6::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const { double q0 = q[0]; // meter double q1 = q[1]; // meter @@ -851,7 +858,7 @@ vpAfma6::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) */ void -vpAfma6::get_cMe(vpHomogeneousMatrix &cMe) +vpAfma6::get_cMe(vpHomogeneousMatrix &cMe) const { cMe = this->_eMc.inverse(); } @@ -866,7 +873,7 @@ vpAfma6::get_cMe(vpHomogeneousMatrix &cMe) */ void -vpAfma6::get_cVe(vpVelocityTwistMatrix &cVe) +vpAfma6::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; get_cMe(cMe) ; @@ -889,40 +896,41 @@ vpAfma6::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpAfma6::get_eJe(const vpColVector &q, vpMatrix &eJe) +vpAfma6::get_eJe(const vpColVector &q, vpMatrix &eJe) const { eJe.resize(6,6) ; - double s3,c3,s4,c4,s5,c5 ; + double s4,c4,s5,c5,s6,c6 ; - s3=sin(q[3]); c3=cos(q[3]); - s4=sin(q[4]); c4=cos(q[4]); - s5=sin(q[5]); c5=cos(q[5]); + s4=sin(q[3]); c4=cos(q[3]); + s5=sin(q[4]); c5=cos(q[4]); + s6=sin(q[5]-this->_coupl_56*q[4]); c6=cos(q[5]-this->_coupl_56*q[4]); eJe = 0; - eJe[0][0] = s3*s4*c5+c3*s5; - eJe[0][1] = -c3*s4*c5+s3*s5; - eJe[0][2] = c4*c5; - eJe[0][3] = - this->_long_56*s4*c5; + eJe[0][0] = s4*s5*c6+c4*s6; + eJe[0][1] = -c4*s5*c6+s4*s6; + eJe[0][2] = c5*c6; + eJe[0][3] = - this->_long_56*s5*c6; - eJe[1][0] = -s3*s4*s5+c3*c5; - eJe[1][1] = c3*s4*s5+s3*c5; - eJe[1][2] = -c4*s5; - eJe[1][3] = this->_long_56*s4*s5; + eJe[1][0] = -s4*s5*s6+c4*c6; + eJe[1][1] = c4*s5*s6+s4*c6; + eJe[1][2] = -c5*s6; + eJe[1][3] = this->_long_56*s5*s6; - eJe[2][0] = -s3*c4; - eJe[2][1] = c3*c4; - eJe[2][2] = s4; - eJe[2][3] = this->_long_56*c4; + eJe[2][0] = -s4*c5; + eJe[2][1] = c4*c5; + eJe[2][2] = s5; + eJe[2][3] = this->_long_56*c5; - eJe[3][3] = c4*c5; - eJe[3][4] = s5; + eJe[3][3] = c5*c6; + eJe[3][4] = s6; - eJe[4][3] = -c4*s5; - eJe[4][4] = c5; + eJe[4][3] = -c5*s6; + eJe[4][4] = c6; - eJe[5][3] = s4; + eJe[5][3] = s5; + eJe[5][4] = -this->_coupl_56; eJe[5][5] = 1; return; @@ -939,12 +947,13 @@ vpAfma6::get_eJe(const vpColVector &q, vpMatrix &eJe) 1 & 0 & 0 & -Ls4 & 0 & 0 \\ 0 & 1 & 0 & Lc4 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 \\ - 0 & 0 & 0 & 0 & c4 & -s4c5 \\ - 0 & 0 & 0 & 0 & s4 & c4c5 \\ - 0 & 0 & 0 & 1 & 0 & s5 \\ + 0 & 0 & 0 & 0 & c4+\gamma s4c5 & -s4c5 \\ + 0 & 0 & 0 & 0 & s4-\gamma c4c5 & c4c5 \\ + 0 & 0 & 0 & 1 & -gamma s5 & s5 \\ \end{array} \right) \f] + where \f$\gamma\f$ is the coupling factor between join 5 and 6. \param q : Articular joint position of the robot. q[0], q[1], q[2] correspond to the first 3 translations expressed in meter, while @@ -956,7 +965,7 @@ vpAfma6::get_eJe(const vpColVector &q, vpMatrix &eJe) */ void -vpAfma6::get_fJe(const vpColVector &q, vpMatrix &fJe) +vpAfma6::get_fJe(const vpColVector &q, vpMatrix &fJe) const { fJe.resize(6,6) ; @@ -964,8 +973,8 @@ vpAfma6::get_fJe(const vpColVector &q, vpMatrix &fJe) // block superieur gauche fJe[0][0] = fJe[1][1] = fJe[2][2] = 1 ; - double s4 = sin(q[4]) ; - double c4 = cos(q[4]) ; + double s4 = sin(q[3]) ; + double c4 = cos(q[3]) ; // block superieur droit @@ -973,13 +982,18 @@ vpAfma6::get_fJe(const vpColVector &q, vpMatrix &fJe) fJe[1][3] = this->_long_56*c4 ; - double s5 = sin(q[5]) ; - double c5 = cos(q[5]) ; + double s5 = sin(q[4]) ; + double c5 = cos(q[4]) ; // block inferieur droit fJe[3][4] = c4 ; fJe[3][5] = -s4*c5 ; fJe[4][4] = s4 ; fJe[4][5] = c4*c5 ; fJe[5][3] = 1 ; fJe[5][5] = s5 ; + // coupling between joint 5 and 6 + fJe[3][4] += this->_coupl_56*s4*c5; + fJe[4][4] += -this->_coupl_56*c4*c5; + fJe[5][4] += -this->_coupl_56*s5; + return; } @@ -993,7 +1007,7 @@ vpAfma6::get_fJe(const vpColVector &q, vpMatrix &fJe) */ vpColVector -vpAfma6::getJointMin() +vpAfma6::getJointMin() const { vpColVector qmin(6); for (unsigned int i=0; i < 6; i ++) @@ -1010,7 +1024,7 @@ vpAfma6::getJointMin() */ vpColVector -vpAfma6::getJointMax() +vpAfma6::getJointMax() const { vpColVector qmax(6); for (unsigned int i=0; i < 6; i ++) @@ -1025,7 +1039,7 @@ vpAfma6::getJointMax() \return Coupling factor between join 5 and 6. */ double -vpAfma6::getCoupl56() +vpAfma6::getCoupl56() const { return _coupl_56; } @@ -1037,7 +1051,7 @@ vpAfma6::getCoupl56() \return Distance between join 5 and 6. */ double -vpAfma6::getLong56() +vpAfma6::getLong56() const { return _long_56; } @@ -1233,7 +1247,7 @@ int main() void vpAfma6::getCameraParameters (vpCameraParameters &cam, const unsigned int &image_width, - const unsigned int &image_height) + const unsigned int &image_height) const { #if defined(VISP_HAVE_XML2) && defined (VISP_HAVE_ACCESS_TO_NAS) vpXmlParserCamera parser; @@ -1451,7 +1465,7 @@ int main() */ void vpAfma6::getCameraParameters (vpCameraParameters &cam, - const vpImage<unsigned char> &I) + const vpImage<unsigned char> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } @@ -1500,7 +1514,7 @@ int main() void vpAfma6::getCameraParameters (vpCameraParameters &cam, - const vpImage<vpRGBa> &I) + const vpImage<vpRGBa> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } @@ -1515,7 +1529,7 @@ vpAfma6::getCameraParameters (vpCameraParameters &cam, \param os : Output stream. \param afma6 : Robot parameters. */ -std::ostream & operator << (std::ostream & os, +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpAfma6 & afma6) { vpRotationMatrix eRc; @@ -1568,8 +1582,4 @@ std::ostream & operator << (std::ostream & os, return os; } -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ + diff --git a/src/robot/real-robot/afma6/vpAfma6.h b/src/robot/real-robot/afma6/vpAfma6.h index 4351f36aea949cef76b01f9741e3605a0138e29d..0b5f173ee3cac44f41efdd6db9c63e843c447153 100644 --- a/src/robot/real-robot/afma6/vpAfma6.h +++ b/src/robot/real-robot/afma6/vpAfma6.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAfma6.h 4191 2013-04-01 07:46:05Z fspindle $ + * $Id: vpAfma6.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -118,6 +118,8 @@ class VISP_EXPORT vpAfma6 public: vpAfma6(); + /*! Destructor that does nothing. */ + virtual ~vpAfma6() {}; void init (void); #ifdef VISP_HAVE_ACCESS_TO_NAS @@ -127,45 +129,45 @@ class VISP_EXPORT vpAfma6 vpCameraParameters::vpCameraParametersProjType projModel = vpCameraParameters::perspectiveProjWithoutDistortion); - vpHomogeneousMatrix getForwardKinematics(const vpColVector & q); + vpHomogeneousMatrix getForwardKinematics(const vpColVector & q) const; int getInverseKinematics(const vpHomogeneousMatrix & fMc, - vpColVector & q, const bool &nearest=true, const bool &verbose=false); - vpHomogeneousMatrix get_fMc (const vpColVector & q); - void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe); - void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc); + vpColVector & q, const bool &nearest=true, + const bool &verbose=false) const; + vpHomogeneousMatrix get_fMc (const vpColVector & q) const; + void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const; + void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const; - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_eJe(const vpColVector &q, vpMatrix &eJe) ; - void get_fJe(const vpColVector &q, vpMatrix &fJe) ; + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_eJe(const vpColVector &q, vpMatrix &eJe) const; + void get_fJe(const vpColVector &q, vpMatrix &fJe) const; #ifdef VISP_HAVE_ACCESS_TO_NAS void parseConfigFile (const char * filename); #endif //! Get the current tool type - vpAfma6ToolType getToolType(){ + vpAfma6ToolType getToolType() const { return tool_current; }; //! Get the current camera model projection type - vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType(){ + vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType() const{ return projModel; }; void getCameraParameters(vpCameraParameters &cam, - const unsigned int &image_width, - const unsigned int &image_height); + const unsigned int &image_width, + const unsigned int &image_height) const; void getCameraParameters(vpCameraParameters &cam, - const vpImage<unsigned char> &I); - void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I); + const vpImage<unsigned char> &I) const; + void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I) const; - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpAfma6 & afma6); + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpAfma6 & afma6); - vpColVector getJointMin(); - vpColVector getJointMax(); - double getCoupl56(); - double getLong56(); + vpColVector getJointMin() const; + vpColVector getJointMax() const; + double getCoupl56() const; + double getLong56() const; protected: //! Set the current tool type diff --git a/src/robot/real-robot/afma6/vpRobotAfma6.cpp b/src/robot/real-robot/afma6/vpRobotAfma6.cpp index 40479e3594b9f8beaaf793aff44771d18a04f173..ba60fa50602bf192216e9329ad849c04a2da0691 100644 --- a/src/robot/real-robot/afma6/vpRobotAfma6.cpp +++ b/src/robot/real-robot/afma6/vpRobotAfma6.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotAfma6.cpp 4107 2013-02-06 10:04:49Z fspindle $ + * $Id: vpRobotAfma6.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -649,7 +649,7 @@ vpRobotAfma6::getPowerState(void) */ void -vpRobotAfma6::get_cVe(vpVelocityTwistMatrix &cVe) +vpRobotAfma6::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; vpAfma6::get_cMe(cMe) ; @@ -668,7 +668,7 @@ vpRobotAfma6::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpRobotAfma6::get_cMe(vpHomogeneousMatrix &cMe) +vpRobotAfma6::get_cMe(vpHomogeneousMatrix &cMe) const { vpAfma6::get_cMe(cMe) ; } diff --git a/src/robot/real-robot/afma6/vpRobotAfma6.h b/src/robot/real-robot/afma6/vpRobotAfma6.h index 6713b36c1d7e80d37854e55b06690b182f8425f5..f7b48b0dd7a147b7d31e0a6f834b66e6c7ab68e4 100644 --- a/src/robot/real-robot/afma6/vpRobotAfma6.h +++ b/src/robot/real-robot/afma6/vpRobotAfma6.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotAfma6.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotAfma6.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -294,10 +294,10 @@ public: /* Methode publiques */ vpColVector getVelocity (const vpRobot::vpControlFrameType frame); vpColVector getVelocity (const vpRobot::vpControlFrameType frame, double ×tamp); - void get_cMe(vpHomogeneousMatrix &_cMe) ; - void get_cVe(vpVelocityTwistMatrix &_cVe) ; - void get_eJe(vpMatrix &_eJe) ; - void get_fJe(vpMatrix &_fJe) ; + void get_cMe(vpHomogeneousMatrix &_cMe) const; + void get_cVe(vpVelocityTwistMatrix &_cVe) const; + void get_eJe(vpMatrix &_eJe); + void get_fJe(vpMatrix &_fJe); void init (void); void init (vpAfma6::vpAfma6ToolType tool, diff --git a/src/robot/real-robot/biclops/vpBiclops.cpp b/src/robot/real-robot/biclops/vpBiclops.cpp index b30fdbf97b44992442c6bf6a3bffcc498c9c243b..92a2baec474241fe7b2e6981374786fa8bc502a5 100644 --- a/src/robot/real-robot/biclops/vpBiclops.cpp +++ b/src/robot/real-robot/biclops/vpBiclops.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBiclops.cpp 4321 2013-07-17 19:26:03Z fspindle $ + * $Id: vpBiclops.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,7 +77,7 @@ const float vpBiclops::speedLimit = (float)(M_PI/3.0); /*!< Maximum speed (in ra \sa get_fMc(const vpColVector &, vpHomogeneousMatrix &) */ void -vpBiclops::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) +vpBiclops::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) const { vpHomogeneousMatrix fMe = get_fMe(q); fMc = fMe * cMe_.inverse(); @@ -98,7 +98,7 @@ vpBiclops::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) */ void -vpBiclops::get_fMc (const vpColVector &q, vpHomogeneousMatrix &fMc) +vpBiclops::get_fMc (const vpColVector &q, vpHomogeneousMatrix &fMc) const { vpHomogeneousMatrix fMe = get_fMe(q); fMc = fMe * cMe_.inverse(); @@ -123,7 +123,7 @@ vpBiclops::get_fMc (const vpColVector &q, vpHomogeneousMatrix &fMc) \sa get_fMc(const vpColVector &) */ vpHomogeneousMatrix -vpBiclops::computeMGD (const vpColVector & q) +vpBiclops::computeMGD (const vpColVector & q) const { vpHomogeneousMatrix fMc; @@ -143,7 +143,7 @@ vpBiclops::computeMGD (const vpColVector & q) */ vpHomogeneousMatrix -vpBiclops::get_fMc (const vpColVector & q) +vpBiclops::get_fMc (const vpColVector & q) const { vpHomogeneousMatrix fMc; @@ -163,7 +163,7 @@ vpBiclops::get_fMc (const vpColVector & q) */ vpHomogeneousMatrix -vpBiclops::get_fMe (const vpColVector & q) +vpBiclops::get_fMe (const vpColVector & q) const { vpHomogeneousMatrix fMe; @@ -243,7 +243,7 @@ vpBiclops::get_fMe (const vpColVector & q) */ void -vpBiclops::computeMGD (const vpColVector &q, vpPoseVector &fvc) +vpBiclops::computeMGD (const vpColVector &q, vpPoseVector &fvc) const { vpHomogeneousMatrix fMc; @@ -264,7 +264,7 @@ vpBiclops::computeMGD (const vpColVector &q, vpPoseVector &fvc) */ void -vpBiclops::get_fMc (const vpColVector &q, vpPoseVector &fvc) +vpBiclops::get_fMc (const vpColVector &q, vpPoseVector &fvc) const { vpHomogeneousMatrix fMc; @@ -285,6 +285,7 @@ vpBiclops::get_fMc (const vpColVector &q, vpPoseVector &fvc) */ vpBiclops::vpBiclops (void) + : dh_model_(DH1), cMe_() { init(); } @@ -300,6 +301,7 @@ vpBiclops::vpBiclops (void) void vpBiclops::init () { + dh_model_ = DH1; set_cMe(); return ; } @@ -309,10 +311,8 @@ vpBiclops::init () /* --- DISPLAY ----------------------------------------------------------- */ /* ----------------------------------------------------------------------- */ -std::ostream & operator << (std::ostream & os, - const vpBiclops & /*constant*/) +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpBiclops & /*constant*/) { - os << "Geometric parameters: " << std::endl << "h: " @@ -333,7 +333,7 @@ std::ostream & operator << (std::ostream & os, */ void -vpBiclops::get_cVe(vpVelocityTwistMatrix &cVe) +vpBiclops::get_cVe(vpVelocityTwistMatrix &cVe) const { cVe.buildFrom(cMe_) ; } @@ -386,10 +386,8 @@ vpBiclops::set_cMe() */ void -vpBiclops::get_eJe(const vpColVector &q, vpMatrix &eJe) +vpBiclops::get_eJe(const vpColVector &q, vpMatrix &eJe) const { - - eJe.resize(6,2) ; if (q.getRows() != 2) { @@ -428,9 +426,8 @@ vpBiclops::get_eJe(const vpColVector &q, vpMatrix &eJe) */ void -vpBiclops::get_fJe(const vpColVector &q, vpMatrix &fJe) +vpBiclops::get_fJe(const vpColVector &q, vpMatrix &fJe) const { - if (q.getRows() != 2) { vpERROR_TRACE("Bad dimension for biclops articular vector"); throw(vpException(vpException::dimensionError, "Bad dimension for biclops articular vector")); @@ -456,4 +453,3 @@ vpBiclops::get_fJe(const vpColVector &q, vpMatrix &fJe) fJe[5][0] = 1; } } - diff --git a/src/robot/real-robot/biclops/vpBiclops.h b/src/robot/real-robot/biclops/vpBiclops.h index 2c6ee8d78ea1426acdb02a41a585467fd21cfc7e..2461b4d0583c24d98598a025bd909b31f8ac4dc2 100644 --- a/src/robot/real-robot/biclops/vpBiclops.h +++ b/src/robot/real-robot/biclops/vpBiclops.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBiclops.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBiclops.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -140,12 +140,14 @@ protected: public: vpBiclops (void); - void init (void); + /*! Destructor that does nothing. */ + virtual ~vpBiclops() {}; - void computeMGD (const vpColVector &q, vpHomogeneousMatrix & fMc); + void init (void); - vpHomogeneousMatrix computeMGD (const vpColVector & q); - void computeMGD (const vpColVector &q, vpPoseVector &fvc); + void computeMGD (const vpColVector &q, vpHomogeneousMatrix & fMc) const; + vpHomogeneousMatrix computeMGD (const vpColVector & q) const; + void computeMGD (const vpColVector &q, vpPoseVector &fvc) const; /*! Return the tranformation \f${^c}{\bf M}_e\f$ between the camera frame and the end @@ -156,20 +158,20 @@ public: return cMe_; } - void get_cVe(vpVelocityTwistMatrix &_cVe) ; - void get_fMc (const vpColVector &q, vpHomogeneousMatrix &fMc); - void get_fMc (const vpColVector &q, vpPoseVector &fvc); - vpHomogeneousMatrix get_fMc (const vpColVector &q); - vpHomogeneousMatrix get_fMe (const vpColVector &q); + void get_cVe(vpVelocityTwistMatrix &_cVe) const; + void get_fMc (const vpColVector &q, vpHomogeneousMatrix &fMc) const; + void get_fMc (const vpColVector &q, vpPoseVector &fvc) const; + vpHomogeneousMatrix get_fMc (const vpColVector &q) const; + vpHomogeneousMatrix get_fMe (const vpColVector &q) const; - void get_eJe(const vpColVector &q, vpMatrix &eJe); - void get_fJe(const vpColVector &q, vpMatrix &fJe); + void get_eJe(const vpColVector &q, vpMatrix &eJe) const; + void get_fJe(const vpColVector &q, vpMatrix &fJe) const; /*! Return the Denavit Hartenberg representation used to model the head. \sa vpBiclops::DenavitHartenbergModel */ - inline vpBiclops::DenavitHartenbergModel getDenavitHartenbergModel() + inline vpBiclops::DenavitHartenbergModel getDenavitHartenbergModel() const { return dh_model_; } @@ -192,8 +194,7 @@ public: dh_model_ = m; } - friend std::ostream & operator << (std::ostream & os, - const vpBiclops & constant); + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpBiclops & constant); }; diff --git a/src/robot/real-robot/biclops/vpRobotBiclops.cpp b/src/robot/real-robot/biclops/vpRobotBiclops.cpp index 5c58fe0593c71870f3383f82ca064ab66ea2f869..f1ef8fb5d46aac741c94dfac1a0f5c244df76bfa 100644 --- a/src/robot/real-robot/biclops/vpRobotBiclops.cpp +++ b/src/robot/real-robot/biclops/vpRobotBiclops.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotBiclops.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotBiclops.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -127,6 +127,8 @@ vpRobotBiclops::vpRobotBiclops () pthread_mutex_init (&vpShm_mutex, NULL); pthread_mutex_init (&vpEndThread_mutex, NULL); pthread_mutex_init (&vpMeasure_mutex, NULL); + + positioningVelocity = defaultPositioningVelocity ; } /*! @@ -175,8 +177,9 @@ vpRobotBiclops::vpRobotBiclops (const char * filename) // Initialize the mutex dedicated to she shm protection pthread_mutex_init (&vpShm_mutex, NULL); pthread_mutex_init (&vpEndThread_mutex, NULL); - pthread_mutex_init (&vpMeasure_mutex -, NULL); + pthread_mutex_init (&vpMeasure_mutex, NULL); + + positioningVelocity = defaultPositioningVelocity ; init(); @@ -275,8 +278,6 @@ vpRobotBiclops::init () vpRobotBiclops::robotAlreadyCreated = true; - positioningVelocity = defaultPositioningVelocity ; - // Initialize previous articular position to manage getDisplacement() q_previous.resize(vpBiclops::ndof); q_previous = 0; @@ -670,7 +671,7 @@ vpRobotBiclops::stopMotion(void) */ void -vpRobotBiclops::get_cVe(vpVelocityTwistMatrix &cVe) +vpRobotBiclops::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; cMe = vpBiclops::get_cMe() ; @@ -688,7 +689,7 @@ vpRobotBiclops::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpRobotBiclops::get_cMe(vpHomogeneousMatrix &cMe) +vpRobotBiclops::get_cMe(vpHomogeneousMatrix &cMe) const { cMe = vpBiclops::get_cMe() ; } @@ -1279,16 +1280,19 @@ vpRobotBiclops::readPositionFile(const char *filename, vpColVector &q) // skip lines begining with # for comments if (fgets (line, 100, pt_f) != NULL) { if ( strncmp (line, "#", 1) != 0) { - // this line is not a comment - if ( fscanf (pt_f, "%s", line) != EOF) { - if ( strcmp (line, head) == 0) - end = true; // robot position was found - } - else - return (false); // end of file without position + // this line is not a comment + if ( fscanf (pt_f, "%s", line) != EOF) { + if ( strcmp (line, head) == 0) + end = true; // robot position was found + } + else { + fclose(pt_f); + return (false); // end of file without position + } } } else { + fclose(pt_f); return (false);// end of file } @@ -1298,6 +1302,7 @@ vpRobotBiclops::readPositionFile(const char *filename, vpColVector &q) double q1,q2; // Read positions if (fscanf(pt_f, "%lf %lf", &q1, &q2) == EOF) { + fclose(pt_f); std::cout << "Cannot read joint positions." << std::endl; return false; } diff --git a/src/robot/real-robot/biclops/vpRobotBiclops.h b/src/robot/real-robot/biclops/vpRobotBiclops.h index c402d226dce4a77d97f546c691775566addbfcf4..cc749a4900a112617cb6aeccabaa71f545030af0 100644 --- a/src/robot/real-robot/biclops/vpRobotBiclops.h +++ b/src/robot/real-robot/biclops/vpRobotBiclops.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotBiclops.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotBiclops.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -136,8 +136,8 @@ public: void init (void); - void get_cMe(vpHomogeneousMatrix &_cMe) ; - void get_cVe(vpVelocityTwistMatrix &_cVe) ; + void get_cMe(vpHomogeneousMatrix &_cMe) const; + void get_cVe(vpVelocityTwistMatrix &_cVe) const; void get_eJe(vpMatrix &_eJe) ; void get_fJe(vpMatrix &_fJe) ; diff --git a/src/robot/real-robot/biclops/vpRobotBiclopsController.cpp b/src/robot/real-robot/biclops/vpRobotBiclopsController.cpp index 57ac29ca9870fe5be8931b6241459adef55caaca..1be2483f3261223aa7ec352b96ad744e94030fb1 100644 --- a/src/robot/real-robot/biclops/vpRobotBiclopsController.cpp +++ b/src/robot/real-robot/biclops/vpRobotBiclopsController.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotBiclopsController.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotBiclopsController.cpp 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ #include <signal.h> #include <string.h> -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> #endif #include <visp/vpConfig.h> @@ -89,6 +89,7 @@ vpRobotBiclopsController::vpRobotBiclopsController() shm.q_dot[i] = 0.; shm.actual_q[i] = 0.; shm.jointLimit[i] = false; + shm.status[i] = STOP; } } diff --git a/src/robot/real-robot/biclops/vpRobotBiclopsController.h b/src/robot/real-robot/biclops/vpRobotBiclopsController.h index f8236e6b8880e6abeb12f8204dddc3532cea411f..b615ccf804b69fa55d94de99b3ec5930da6c4f59 100644 --- a/src/robot/real-robot/biclops/vpRobotBiclopsController.h +++ b/src/robot/real-robot/biclops/vpRobotBiclopsController.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotBiclopsController.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotBiclopsController.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,7 +61,7 @@ #include "Biclops.h" // Contrib for Biclops robot #include "PMDUtils.h" // Contrib for Biclops robot -#ifdef WIN32 +#if defined(_WIN32) class VISP_EXPORT Biclops; //needed for dll creation #endif diff --git a/src/robot/real-robot/cycab/vpRobotCycab.cpp b/src/robot/real-robot/cycab/vpRobotCycab.cpp deleted file mode 100644 index 4d2b0ab36bf840ef4c70e6ba730fab0bf20a417e..0000000000000000000000000000000000000000 --- a/src/robot/real-robot/cycab/vpRobotCycab.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/**************************************************************************** - * - * $Id: vpRobotCycab.cpp 4056 2013-01-05 13:04:42Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Interface for the car-like Cycab mobile robot. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ - -#include <visp/vpConfig.h> -#include <visp/vpRobotCycab.h> - -#ifdef VISP_HAVE_CYCAB - -/*! - Initialize the client to be able to communicate with the Cycab server. - - By default, the steering mode is set such as only the front steering - angle is controlled. - - To change this behavior, use setDualSteering(true) function. -*/ -vpRobotCycab::vpRobotCycab() -{ -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - end = false; - cycab = NULL; - sprintf(servername, "cycab-hf1"); - cycab = new EtherCycab(servername, <R); - if (!cycab->doInitialization(&end, 50, 0, false)) { - printf("ERROR: Can't connect to %s\n", servername); - exit(-1); - } - cycab->releaseSecurity(); - printf("Control limits : phi in [%f:%f], V in [%f:%f]\n", - cycab->minAllowedSteering(), - cycab->maxAllowedSteering(), - cycab->minAllowedSpeed(), - cycab->maxAllowedSpeed()); -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - std::string name = "cycab"; - CycabCarCommand cycab_command; - setDualSteering(false); - store.connect(); - cycab_commandId = store.lookupVariable( name + cycabCarCommandSuffix ); - cycab_stateId = store.lookupVariable( name + cycabStateSuffix ); - store.writeVariable( cycab_commandId, cycab_command ); -#endif -} - -/*! - Close the connection with the Cycab server. -*/ -vpRobotCycab::~vpRobotCycab() -{ -#ifdef VISP_HAVE_CYCABTK_OLD - if (cycab != NULL) delete cycab; - cycab = NULL; -#endif -} - -/*! - Set the steering mode. - - \param dual : If false, only the front steering angle is - conntrolled. If true, front and read steering angles are controlled. - -*/ -void vpRobotCycab::setDualSteering(bool dual) -{ - dualSteering = dual; -} - -/*! - Send the command to the cycab server. - - \param v : Velocity in m/s. - \param phi : Front steering angle in rad. - */ -void vpRobotCycab::setCommand(double v, double phi) -{ -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - cycab->sendCommands(v, phi); -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - CycabCarCommand cycab_command; - store.readVariable( cycab_commandId, cycab_command ); - cycab_command.v = v; - cycab_command.phi = phi; - cycab_command.manualDriving = false; - cycab_command.dualSteering = dualSteering; - store.writeVariable( cycab_commandId, cycab_command ); -#endif -} - -/*! - Get measures from odometry. - - \param vmean : Measured mean velocity in m/s. This value is computed by considering - the mean velocity between rear-left and rear-right wheel velocities. - - \param phi : Measured front steering angle in rad. - -*/ -void vpRobotCycab::getOdometry(double &vmean, double &phi) -{ - double timestamp; - getOdometry(vmean, phi, timestamp); -} - -/*! - Get measures from odometry. - - \param v : Measured mean velocity in m/s. This value is computed by considering - the mean velocity between rear-left and rear-right wheel velocities. - - \param phi : Measured front steering angle in rad. - - \param timestamp : Time stamp in milli second associates to the measured - values of \e vmean and \e phi. -*/ -void vpRobotCycab::getOdometry(double &vmean, double &phi, double ×tamp) -{ - vmean = 0.; - phi=0.; - timestamp=0.; - -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - Record rec; - double dsl,dsr,lphi,ltime; - cycab->waitUpdate(); - cycab->getState(&rec, &dsl, &dsr, &lphi, <ime); - // printf("Ktate %f : phi %f vl %f vr %f\n\t",ltime, - // lphi,rec.vmsec[REAR][LEFT],rec.vmsec[REAR][RIGHT]); - // calculate speed - vmean = (rec.vmsec[REAR][LEFT] + rec.vmsec[REAR][RIGHT])*0.5; - phi = lphi; - timestamp = ltime; -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - CycabState cycab_state; - store.readVariable(cycab_stateId, cycab_state); - vmean = (cycab_state.v_rear_left + cycab_state.v_rear_right)*0.5; - //printf("vl %f vr %f\n", cycab_state.v_rear_left, cycab_state.v_rear_right); - phi = cycab_state.phi_front; - timeval tp = store.getTimestamp(cycab_stateId); - timestamp = 1000.0*tp.tv_sec + tp.tv_usec/1000.0; -#endif -} -/*! - Get measures from odometry. - - \param vfl : Measured front-left wheel velocity in m/s. - \param vfr : Measured front-right wheel velocity in m/s. - \param vrl : Measured rear-left wheel velocity in m/s. - \param vrr : Measured rear-right wheel velocity in m/s. - - \param phi : Measured front steering angle in rad. - -*/ -void vpRobotCycab::getOdometry(double &vfl, double &vfr, - double &vrl, double &vrr, - double &phi) -{ - double timestamp; - getOdometry(vfl, vfr, vrl, vrr, phi, timestamp); -} - -/*! - Get measures from odometry. - - \param vfl : Measured front-left wheel velocity in m/s. - \param vfr : Measured front-right wheel velocity in m/s. - \param vrl : Measured rear-left wheel velocity in m/s. - \param vrr : Measured rear-right wheel velocity in m/s. - - \param phi : Measured front steering angle in rad. - - \param timestamp : Time stamp in milli second associates to the measured - values of \e vmean and \e phi. -*/ -void vpRobotCycab::getOdometry(double &vfl, double &vfr, - double &vrl, double &vrr, - double &phi, double ×tamp) -{ - vfr = vfr = vrl = vrr = 0.; - phi=0.; - timestamp=0.; - -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - Record rec; - double dsl,dsr,lphi,ltime; - cycab->waitUpdate(); - cycab->getState(&rec, &dsl, &dsr, &lphi, <ime); - // printf("Ktate %f : phi %f vl %f vr %f\n\t",ltime, - // lphi,rec.vmsec[REAR][LEFT],rec.vmsec[REAR][RIGHT]); - // calculate speed - vfl = rec.vmsec[FRONT][LEFT]; - vfr = rec.vmsec[FRONT][RIGHT]; - vrl = rec.vmsec[REAR][LEFT]; - vrr = rec.vmsec[REAR][RIGHT]; - phi = lphi; - timestamp = ltime; -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - CycabState cycab_state; - store.readVariable(cycab_stateId, cycab_state); - vfl = cycab_state.v_front_left; - vfr = cycab_state.v_front_right; - vrl = cycab_state.v_rear_left; - vrr = cycab_state.v_rear_right; - - phi = cycab_state.phi_front; - timeval tp = store.getTimestamp(cycab_stateId); - timestamp = 1000.0*tp.tv_sec + tp.tv_usec/1000.0; -#endif -} - -/*! - Get the joystick positions. - - \param x : Joystick left/right position. - \param y : Joystick front/rear position. - */ -void vpRobotCycab::getJoystickPosition(double &x, double &y) -{ - double timestamp; - getJoystickPosition(x, y, timestamp); -} - -/*! - Get the joystick positions. - - \param x : Joystick left/right position. - \param y : Joystick front/rear position. - \param timestamp : Time stamp in milli second associates to the measured - values of \e x and \e y. - */ -void vpRobotCycab::getJoystickPosition(double &x, double &y, double ×tamp) -{ -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - x = 0.; - y = 0.; - timestamp = 0.; -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - CycabState cycab_state; - store.readVariable(cycab_stateId, cycab_state); - x = cycab_state.joy_x; - y = cycab_state.joy_y; - timeval tp = store.getTimestamp(cycab_stateId); - timestamp = 1000.0*tp.tv_sec + tp.tv_usec/1000.0; -#endif -} - - -#endif diff --git a/src/robot/real-robot/cycab/vpRobotCycab.h b/src/robot/real-robot/cycab/vpRobotCycab.h deleted file mode 100644 index d83b32d4a29dceb302580c7cb01b16e2b5c6f2c3..0000000000000000000000000000000000000000 --- a/src/robot/real-robot/cycab/vpRobotCycab.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** - * - * $Id: vpRobotCycab.h 4056 2013-01-05 13:04:42Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Interface for the car-like Cycab mobile robot. - * - * Authors: - * Fabien Spindler - * - *****************************************************************************/ -#ifndef vpRobotCycab_h -#define vpRobotCycab_h - -/*! - - \file vpRobotCycab.h - - Interface for the car-like Cycab mobile robot. - -*/ - -/*! - - \class vpRobotCycab - - \ingroup Cycab RobotDriver - - \brief Interface for the car-like Cycab mobile robot. - -*/ - -#include <visp/vpConfig.h> - -#ifdef VISP_HAVE_CYCAB - -#ifdef VISP_HAVE_CYCABTK_OLD -// Old low level controller based on Syndex (obsolete) -# include <EtherCycab/EtherCycab.hpp> -#elif defined VISP_HAVE_CYCABTK -// New low level controller based on Syndex (to use) -# include <hugr.hpp> -# include <CycabStructs.hpp> - -using namespace hugr; - -#endif //VISP_HAVE_CYCABTK_OLD - -class VISP_EXPORT vpRobotCycab -{ - public: - vpRobotCycab(); - virtual ~vpRobotCycab(); - - void setDualSteering(bool dual); - void setCommand(double v, double phi); - void getOdometry(double &vmean, double &phi); - void getOdometry(double &vmean, double &phi, double ×tamp); - void getOdometry(double &vfl, double &vfr, double &vrl, double &vrr, - double &phi); - void getOdometry(double &vfl, double &vfr, double &vrl, double &vrr, - double &phi, double ×tamp); - void getJoystickPosition(double &x, double &y); - void getJoystickPosition(double &x, double &y, double ×tamp); - - private: - bool dualSteering; -#ifdef VISP_HAVE_CYCABTK_OLD - // Old low level controller based on Syndex (obsolete) - bool end; - LockedTimeReq LTR; - char servername[1024]; - EtherCycab *cycab; -#elif defined VISP_HAVE_CYCABTK - // New low level controller based on Syndex (to use) - Store store; - VariableId cycab_stateId, cycab_commandId; -#endif -}; - -#endif -#endif diff --git a/src/robot/real-robot/pioneer/vpPioneer.h b/src/robot/real-robot/pioneer/vpPioneer.h index da99e5086dd72bb735a8669b4edef9575ed33ab1..9dc27277328180f3afe22dcc9e6ec615698cb40f 100644 --- a/src/robot/real-robot/pioneer/vpPioneer.h +++ b/src/robot/real-robot/pioneer/vpPioneer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPioneer.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPioneer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/real-robot/pioneer/vpPioneerPan.h b/src/robot/real-robot/pioneer/vpPioneerPan.h index 969c9afbfdb84adb9aa0af5f759e2d6f6df6e230..5d1f6d986677bee43e60b26bc2ab2920e9dc4fae 100644 --- a/src/robot/real-robot/pioneer/vpPioneerPan.h +++ b/src/robot/real-robot/pioneer/vpPioneerPan.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPioneerPan.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPioneerPan.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -102,7 +102,7 @@ public: /*! Create a pioneer mobile robot equiped with a pan head. */ - vpPioneerPan() : vpUnicycle() + vpPioneerPan() : mMp_(), pMe_() { double q = 0; // Initial position of the pan axis set_mMp(); diff --git a/src/robot/real-robot/pioneer/vpRobotPioneer.cpp b/src/robot/real-robot/pioneer/vpRobotPioneer.cpp index 4d3888e0ab7eaa3712247156cf2d14e531229eae..bd72577e67a19cab2670b729ea202b28de4953ae 100644 --- a/src/robot/real-robot/pioneer/vpRobotPioneer.cpp +++ b/src/robot/real-robot/pioneer/vpRobotPioneer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotPioneer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotPioneer.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/real-robot/pioneer/vpRobotPioneer.h b/src/robot/real-robot/pioneer/vpRobotPioneer.h index 77190e2017adc1999a4ed1456099e71161b47efd..a18e1982795ca6695bfe8397543398d000aade91 100644 --- a/src/robot/real-robot/pioneer/vpRobotPioneer.h +++ b/src/robot/real-robot/pioneer/vpRobotPioneer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotPioneer.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotPioneer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/real-robot/pioneer/vpUnicycle.h b/src/robot/real-robot/pioneer/vpUnicycle.h index fb3012c5612a2aeff52fa99dffece3df79fab5cb..eb4328da9e7404a270150a7ac5e708a535fc009b 100644 --- a/src/robot/real-robot/pioneer/vpUnicycle.h +++ b/src/robot/real-robot/pioneer/vpUnicycle.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpUnicycle.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpUnicycle.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,7 +63,7 @@ public: /*! Default constructor that does nothing. */ - vpUnicycle() + vpUnicycle() : cMe_(), eJe_() { }; /*! diff --git a/src/robot/real-robot/ptu46/vpPtu46.cpp b/src/robot/real-robot/ptu46/vpPtu46.cpp index 3b2fb96bf4f2e97ad6d411b562f37bf8cc92a735..2fea9f60eb6cb32e6448bb2f33192c0c06846184 100644 --- a/src/robot/real-robot/ptu46/vpPtu46.cpp +++ b/src/robot/real-robot/ptu46/vpPtu46.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPtu46.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPtu46.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,10 +55,6 @@ #include <math.h> #include <visp/vpMath.h> - - - - /* ------------------------------------------------------------------------ */ /* --- COMPUTE ------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ @@ -80,7 +76,7 @@ const float vpPtu46::h = 0.068f; /*<! Vertical offset from last joint to */ void -vpPtu46::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) +vpPtu46::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) const { if (q.getRows() != 2) { vpERROR_TRACE("Bad dimension for ptu-46 articular vector"); @@ -131,7 +127,7 @@ vpPtu46::computeMGD (const vpColVector & q, vpHomogeneousMatrix & fMc) */ vpHomogeneousMatrix -vpPtu46::computeMGD (const vpColVector & q) +vpPtu46::computeMGD (const vpColVector & q) const { vpHomogeneousMatrix fMc; @@ -150,7 +146,7 @@ vpPtu46::computeMGD (const vpColVector & q) */ void -vpPtu46::computeMGD (const vpColVector & q, vpPoseVector & r) +vpPtu46::computeMGD (const vpColVector & q, vpPoseVector & r) const { vpHomogeneousMatrix fMc; @@ -195,10 +191,8 @@ vpPtu46::init () /* --- DISPLAY ----------------------------------------------------------- */ /* ----------------------------------------------------------------------- */ -std::ostream & operator << (std::ostream & os, - const vpPtu46 & /* constant */) +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpPtu46 & /* constant */) { - os << "Geometric parameters: " << std::endl << "L: " @@ -221,7 +215,7 @@ std::ostream & operator << (std::ostream & os, */ void -vpPtu46::get_cVe(vpVelocityTwistMatrix &cVe) +vpPtu46::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; get_cMe(cMe) ; @@ -239,7 +233,7 @@ vpPtu46::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpPtu46::get_cMe(vpHomogeneousMatrix &cMe) +vpPtu46::get_cMe(vpHomogeneousMatrix &cMe) const { vpHomogeneousMatrix eMc ; @@ -279,7 +273,7 @@ vpPtu46::get_cMe(vpHomogeneousMatrix &cMe) */ void -vpPtu46::get_eJe(const vpColVector &q, vpMatrix &eJe) +vpPtu46::get_eJe(const vpColVector &q, vpMatrix &eJe) const { @@ -298,7 +292,6 @@ vpPtu46::get_eJe(const vpColVector &q, vpMatrix &eJe) eJe[3][0] = c2; eJe[4][1] = 1; eJe[5][0] = s2; - } /*! @@ -311,7 +304,7 @@ vpPtu46::get_eJe(const vpColVector &q, vpMatrix &eJe) */ void -vpPtu46::get_fJe(const vpColVector &q, vpMatrix &fJe) +vpPtu46::get_fJe(const vpColVector &q, vpMatrix &fJe) const { if (q.getRows() != 2) { @@ -330,12 +323,3 @@ vpPtu46::get_fJe(const vpColVector &q, vpMatrix &fJe) fJe[4][1] = -c1; fJe[5][0] = 1; } - - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ - diff --git a/src/robot/real-robot/ptu46/vpPtu46.h b/src/robot/real-robot/ptu46/vpPtu46.h index 076a506b7848ebf39599baa1121c2808561ada40..554fe89f840e55b79e2125eed06aeaf70428aba6 100644 --- a/src/robot/real-robot/ptu46/vpPtu46.h +++ b/src/robot/real-robot/ptu46/vpPtu46.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPtu46.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPtu46.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -93,20 +93,21 @@ public: /* Constants */ public: /* Methodes publiques */ vpPtu46 (void); - void init (void); + /*! Destructor that does nothing. */ + virtual ~vpPtu46() {}; - void computeMGD (const vpColVector &q, vpHomogeneousMatrix & fMc); + void init (void); - vpHomogeneousMatrix computeMGD (const vpColVector & q); - void computeMGD (const vpColVector & q, vpPoseVector & r); + void computeMGD (const vpColVector &q, vpHomogeneousMatrix & fMc) const; + vpHomogeneousMatrix computeMGD (const vpColVector & q) const; + void computeMGD (const vpColVector & q, vpPoseVector & r) const; - void get_cMe(vpHomogeneousMatrix &_cMe) ; - void get_cVe(vpVelocityTwistMatrix &_cVe) ; - void get_eJe(const vpColVector &q, vpMatrix &eJe); - void get_fJe(const vpColVector &q, vpMatrix &fJe); + void get_cMe(vpHomogeneousMatrix &_cMe) const; + void get_cVe(vpVelocityTwistMatrix &_cVe) const; + void get_eJe(const vpColVector &q, vpMatrix &eJe) const; + void get_fJe(const vpColVector &q, vpMatrix &fJe) const; - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpPtu46 & constant); + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpPtu46 & constant); }; diff --git a/src/robot/real-robot/ptu46/vpRobotPtu46.cpp b/src/robot/real-robot/ptu46/vpRobotPtu46.cpp index 2f8d2fd2a8545c2556a0f282ce125a0c36d647df..0edcb335cb8afc2284451ab2aef26673f03aa328 100644 --- a/src/robot/real-robot/ptu46/vpRobotPtu46.cpp +++ b/src/robot/real-robot/ptu46/vpRobotPtu46.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotPtu46.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotPtu46.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -235,7 +235,7 @@ vpRobotPtu46::stopMotion(void) */ void -vpRobotPtu46::get_cVe(vpVelocityTwistMatrix &cVe) +vpRobotPtu46::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; vpPtu46::get_cMe(cMe) ; @@ -253,7 +253,7 @@ vpRobotPtu46::get_cVe(vpVelocityTwistMatrix &cVe) */ void -vpRobotPtu46::get_cMe(vpHomogeneousMatrix &cMe) +vpRobotPtu46::get_cMe(vpHomogeneousMatrix &cMe) const { vpPtu46::get_cMe(cMe) ; } diff --git a/src/robot/real-robot/ptu46/vpRobotPtu46.h b/src/robot/real-robot/ptu46/vpRobotPtu46.h index 4f7ccbfbdc86180a1509011f9cba706f78a4bb19..a5702d0e1fab3dc7882ad79337312d3c76253d73 100644 --- a/src/robot/real-robot/ptu46/vpRobotPtu46.h +++ b/src/robot/real-robot/ptu46/vpRobotPtu46.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotPtu46.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotPtu46.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -112,8 +112,8 @@ public: vpRobotPtu46 (vpRobotPtu46 * pub); virtual ~vpRobotPtu46 (void); - void get_cMe(vpHomogeneousMatrix &_cMe) ; - void get_cVe(vpVelocityTwistMatrix &_cVe) ; + void get_cMe(vpHomogeneousMatrix &_cMe) const; + void get_cVe(vpVelocityTwistMatrix &_cVe) const; void get_eJe(vpMatrix &_eJe); void get_fJe(vpMatrix &_fJe); diff --git a/src/robot/real-robot/viper/vpRobotViper650.cpp b/src/robot/real-robot/viper/vpRobotViper650.cpp index 461f80e550544582b03b318596dc4e923500b3d1..bf2780b25c61f115eaa11a582527cdf6b52d5b19 100644 --- a/src/robot/real-robot/viper/vpRobotViper650.cpp +++ b/src/robot/real-robot/viper/vpRobotViper650.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotViper650.cpp 4218 2013-04-17 09:55:47Z fspindle $ + * $Id: vpRobotViper650.cpp 4793 2014-07-21 15:10:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -218,6 +218,8 @@ vpRobotViper650::vpRobotViper650 (bool verbose) } positioningVelocity = defaultPositioningVelocity ; + maxRotationVelocity_joint6 = maxRotationVelocity; + vpRobotViper650::robotAlreadyCreated = true; return ; @@ -679,7 +681,7 @@ void \sa powerOn(), powerOff() */ bool - vpRobotViper650::getPowerState(void) + vpRobotViper650::getPowerState(void) const { InitTry; bool status = false; @@ -712,7 +714,7 @@ bool */ void - vpRobotViper650::get_cVe(vpVelocityTwistMatrix &cVe) + vpRobotViper650::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; vpViper650::get_cMe(cMe) ; @@ -732,7 +734,7 @@ void */ void - vpRobotViper650::get_cMe(vpHomogeneousMatrix &cMe) + vpRobotViper650::get_cMe(vpHomogeneousMatrix &cMe) const { vpViper650::get_cMe(cMe) ; } @@ -861,7 +863,7 @@ void \sa setPositioningVelocity() */ double - vpRobotViper650::getPositioningVelocity (void) + vpRobotViper650::getPositioningVelocity (void) const { return positioningVelocity; } @@ -1524,13 +1526,21 @@ void } // saturation in joint space case vpRobot::ARTICULAR_FRAME : { - vpColVector vel_max(6); + vpColVector vel_max(6); + if (getMaxRotationVelocity() == getMaxRotationVelocityJoint6()) { for (unsigned int i=0; i<6; i++) vel_max[i] = getMaxRotationVelocity(); - - vel_sat = vpRobot::saturateVelocities(vel, vel_max, true); } + else { + for (unsigned int i=0; i<5; i++) + vel_max[i] = getMaxRotationVelocity(); + vel_max[5] = getMaxRotationVelocityJoint6(); + } + + vel_sat = vpRobot::saturateVelocities(vel, vel_max, true); + + } } InitTry; @@ -1992,7 +2002,7 @@ bool return false; fprintf(fd, "\ -#Viper - Position - Version 1.0\n\ +#Viper650 - Position - Version 1.00\n\ #\n\ # R: A B C D E F\n\ # Joint position in degrees\n\ @@ -2167,7 +2177,7 @@ void */ void - vpRobotViper650::biasForceTorqueSensor() + vpRobotViper650::biasForceTorqueSensor() const { InitTry; @@ -2224,7 +2234,7 @@ int main() */ void - vpRobotViper650::getForceTorque(vpColVector &H) + vpRobotViper650::getForceTorque(vpColVector &H) const { InitTry; @@ -2245,7 +2255,7 @@ void \sa disbleJoint6Limits() */ -void vpRobotViper650::enableJoint6Limits() +void vpRobotViper650::enableJoint6Limits() const { InitTry; Try( PrimitiveREMOVE_JOINT6_LIMITS_Viper650(0) ); @@ -2267,7 +2277,7 @@ void vpRobotViper650::enableJoint6Limits() \sa enableJoint6Limits() */ -void vpRobotViper650::disableJoint6Limits() +void vpRobotViper650::disableJoint6Limits() const { InitTry; Try( PrimitiveREMOVE_JOINT6_LIMITS_Viper650(1) ); @@ -2279,5 +2289,60 @@ void vpRobotViper650::disableJoint6Limits() "Cannot disable joint limits on axis 6."); } } + +/*! + + Set the maximal rotation velocity that can be sent to the robot during a velocity control. + + \param w_max : Maximum rotation velocity expressed in rad/s. +*/ + +void +vpRobotViper650::setMaxRotationVelocity (double w_max) +{ + vpRobot::setMaxRotationVelocity(w_max); + setMaxRotationVelocityJoint6(w_max); + + return; +} + +/*! + + Set the maximal rotation velocity on joint 6 that is used only during velocity joint control. + + This function affects only the velocities that are sent as joint velocities. + + \code + vpRobotViper650 robot; + robot.setMaxRotationVelocity( vpMath::rad(20) ); + robot.setMaxRotationVelocityJoint6( vpMath::rad(50) ); + + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + robot.setVelocity(ARTICULAR_FRAME, v); + \endcode + + + \param w6_max : Maximum rotation velocity expressed in rad/s on joint 6. +*/ + +void +vpRobotViper650::setMaxRotationVelocityJoint6 (const double w6_max) +{ + maxRotationVelocity_joint6 = w6_max; + return; +} + +/*! + + Get the maximal rotation velocity on joint 6 that is used only during velocity joint control. + + \return Maximum rotation velocity on joint 6 expressed in rad/s. +*/ +double +vpRobotViper650::getMaxRotationVelocityJoint6() const +{ + return maxRotationVelocity_joint6; +} + #endif diff --git a/src/robot/real-robot/viper/vpRobotViper650.h b/src/robot/real-robot/viper/vpRobotViper650.h index 3b363bbed951b66890cf471b4bebc5c2aac57cf8..d9ad2bff8789ee9a1694c30635850c9bb97e440f 100644 --- a/src/robot/real-robot/viper/vpRobotViper650.h +++ b/src/robot/real-robot/viper/vpRobotViper650.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotViper650.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotViper650.h 4793 2014-07-21 15:10:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -336,22 +336,24 @@ public: /* Methode publiques */ virtual ~vpRobotViper650 (void); // Force/Torque control - void biasForceTorqueSensor(); + void biasForceTorqueSensor() const; - void disableJoint6Limits(); - void enableJoint6Limits(); + void disableJoint6Limits() const; + void enableJoint6Limits() const; /*! \return The control mode indicating if the robot is in automatic, manual (usage of the dead man switch) or emergnecy stop mode. */ - vpControlModeType getControlMode() { + vpControlModeType getControlMode() const { return controlMode; } void getDisplacement(vpRobot::vpControlFrameType frame, vpColVector &displacement); - void getForceTorque(vpColVector &H); + void getForceTorque(vpColVector &H) const; + + double getMaxRotationVelocityJoint6() const; void getPosition (const vpRobot::vpControlFrameType frame, vpColVector &position); @@ -364,8 +366,8 @@ public: /* Methode publiques */ vpPoseVector &position, double ×tamp); - double getPositioningVelocity (void); - bool getPowerState(); + double getPositioningVelocity (void) const; + bool getPowerState() const; double getTime () const; void getVelocity (const vpRobot::vpControlFrameType frame, @@ -376,23 +378,26 @@ public: /* Methode publiques */ vpColVector getVelocity (const vpRobot::vpControlFrameType frame); vpColVector getVelocity (const vpRobot::vpControlFrameType frame, double ×tamp); - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_eJe(vpMatrix &eJe) ; - void get_fJe(vpMatrix &fJe) ; + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_eJe(vpMatrix &eJe); + void get_fJe(vpMatrix &fJe); void init (void); void init (vpViper650::vpToolType tool, vpCameraParameters::vpCameraParametersProjType projModel = vpCameraParameters::perspectiveProjWithoutDistortion); - void move(const char *filename) ; + void move(const char *filename); + + void powerOn(); + void powerOff(); - void powerOn() ; - void powerOff() ; + static bool readPosFile(const char *filename, vpColVector &q); + static bool savePosFile(const char *filename, const vpColVector &q); - static bool readPosFile(const char *filename, vpColVector &q) ; - static bool savePosFile(const char *filename, const vpColVector &q) ; + void setMaxRotationVelocity(double w_max); + void setMaxRotationVelocityJoint6(double w6_max); // Position control void setPosition(const vpRobot::vpControlFrameType frame, @@ -409,11 +414,12 @@ public: /* Methode publiques */ void setVelocity (const vpRobot::vpControlFrameType frame, const vpColVector & velocity); - void stopMotion() ; + void stopMotion(); private: void getArticularDisplacement(vpColVector &displacement); void getCameraDisplacement(vpColVector &displacement); + double maxRotationVelocity_joint6; }; #endif diff --git a/src/robot/real-robot/viper/vpRobotViper850.cpp b/src/robot/real-robot/viper/vpRobotViper850.cpp index d422612dc43c9eb9f63419ae072ad8b0f13b8333..f68efb584ba1a48a38f718efe63609d36de74a07 100644 --- a/src/robot/real-robot/viper/vpRobotViper850.cpp +++ b/src/robot/real-robot/viper/vpRobotViper850.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotViper850.cpp 4218 2013-04-17 09:55:47Z fspindle $ + * $Id: vpRobotViper850.cpp 4594 2014-01-20 15:08:07Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -218,6 +218,8 @@ vpRobotViper850::vpRobotViper850 (bool verbose) } positioningVelocity = defaultPositioningVelocity ; + maxRotationVelocity_joint6 = maxRotationVelocity; + vpRobotViper850::robotAlreadyCreated = true; return ; @@ -680,7 +682,7 @@ void \sa powerOn(), powerOff() */ bool - vpRobotViper850::getPowerState(void) + vpRobotViper850::getPowerState(void) const { InitTry; bool status = false; @@ -713,7 +715,7 @@ bool */ void - vpRobotViper850::get_cVe(vpVelocityTwistMatrix &cVe) + vpRobotViper850::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; vpViper850::get_cMe(cMe) ; @@ -733,7 +735,7 @@ void */ void - vpRobotViper850::get_cMe(vpHomogeneousMatrix &cMe) + vpRobotViper850::get_cMe(vpHomogeneousMatrix &cMe) const { vpViper850::get_cMe(cMe) ; } @@ -862,7 +864,7 @@ void \sa setPositioningVelocity() */ double - vpRobotViper850::getPositioningVelocity (void) + vpRobotViper850::getPositioningVelocity (void) const { return positioningVelocity; } @@ -1537,8 +1539,15 @@ void vpRobotViper850::setVelocity(const vpRobot::vpControlFrameType frame, case vpRobot::ARTICULAR_FRAME : { vpColVector vel_max(6); - for (unsigned int i=0; i<6; i++) - vel_max[i] = getMaxRotationVelocity(); + if (getMaxRotationVelocity() == getMaxRotationVelocityJoint6()) { + for (unsigned int i=0; i<6; i++) + vel_max[i] = getMaxRotationVelocity(); + } + else { + for (unsigned int i=0; i<5; i++) + vel_max[i] = getMaxRotationVelocity(); + vel_max[5] = getMaxRotationVelocityJoint6(); + } vel_sat = vpRobot::saturateVelocities(vel, vel_max, true); } @@ -2006,7 +2015,7 @@ bool return false; fprintf(fd, "\ -#Viper - Position - Version 1.0\n\ +#Viper850 - Position - Version 1.00\n\ #\n\ # R: A B C D E F\n\ # Joint position in degrees\n\ @@ -2181,7 +2190,7 @@ void */ void - vpRobotViper850::biasForceTorqueSensor() + vpRobotViper850::biasForceTorqueSensor() const { InitTry; @@ -2238,7 +2247,7 @@ int main() */ void - vpRobotViper850::getForceTorque(vpColVector &H) + vpRobotViper850::getForceTorque(vpColVector &H) const { InitTry; @@ -2280,7 +2289,7 @@ void \sa openGripper() */ -void vpRobotViper850::closeGripper() +void vpRobotViper850::closeGripper() const { InitTry; Try( PrimitiveGripper_Viper850(0) ); @@ -2298,7 +2307,7 @@ void vpRobotViper850::closeGripper() \sa disbleJoint6Limits() */ -void vpRobotViper850::enableJoint6Limits() +void vpRobotViper850::enableJoint6Limits() const { InitTry; Try( PrimitiveREMOVE_JOINT6_LIMITS_Viper850(0) ); @@ -2320,7 +2329,7 @@ void vpRobotViper850::enableJoint6Limits() \sa enableJoint6Limits() */ -void vpRobotViper850::disableJoint6Limits() +void vpRobotViper850::disableJoint6Limits() const { InitTry; Try( PrimitiveREMOVE_JOINT6_LIMITS_Viper850(1) ); @@ -2333,5 +2342,59 @@ void vpRobotViper850::disableJoint6Limits() } } +/*! + + Set the maximal rotation velocity that can be sent to the robot during a velocity control. + + \param w_max : Maximum rotation velocity expressed in rad/s. +*/ + +void +vpRobotViper850::setMaxRotationVelocity (double w_max) +{ + vpRobot::setMaxRotationVelocity(w_max); + setMaxRotationVelocityJoint6(w_max); + + return; +} + +/*! + + Set the maximal rotation velocity on joint 6 that is used only during velocity joint control. + + This function affects only the velocities that are sent as joint velocities. + + \code + vpRobotViper850 robot; + robot.setMaxRotationVelocity( vpMath::rad(20) ); + robot.setMaxRotationVelocityJoint6( vpMath::rad(50) ); + + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + robot.setVelocity(ARTICULAR_FRAME, v); + \endcode + + + \param w6_max : Maximum rotation velocity expressed in rad/s on joint 6. +*/ + +void +vpRobotViper850::setMaxRotationVelocityJoint6 (const double w6_max) +{ + maxRotationVelocity_joint6 = w6_max; + return; +} + +/*! + + Get the maximal rotation velocity on joint 6 that is used only during velocity joint control. + + \return Maximum rotation velocity on joint 6 expressed in rad/s. +*/ +double +vpRobotViper850::getMaxRotationVelocityJoint6() const +{ + return maxRotationVelocity_joint6; +} + #endif diff --git a/src/robot/real-robot/viper/vpRobotViper850.h b/src/robot/real-robot/viper/vpRobotViper850.h index 161f778cca932679bc5e713911cb22da8dc4c06a..5d5d7d006fedcd9a31d15fdd8bc5e5e5b261051d 100644 --- a/src/robot/real-robot/viper/vpRobotViper850.h +++ b/src/robot/real-robot/viper/vpRobotViper850.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotViper850.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpRobotViper850.h 4594 2014-01-20 15:08:07Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -336,12 +336,12 @@ public: /* Methode publiques */ virtual ~vpRobotViper850 (void); // Force/Torque control - void biasForceTorqueSensor(); + void biasForceTorqueSensor() const; - void closeGripper(); + void closeGripper() const; - void disableJoint6Limits(); - void enableJoint6Limits(); + void disableJoint6Limits() const; + void enableJoint6Limits() const; void getDisplacement(vpRobot::vpControlFrameType frame, vpColVector &displacement); @@ -349,12 +349,13 @@ public: /* Methode publiques */ \return The control mode indicating if the robot is in automatic, manual (usage of the dead man switch) or emergnecy stop mode. */ - vpControlModeType getControlMode() { + vpControlModeType getControlMode() const { return controlMode; } - void getForceTorque(vpColVector &H); + void getForceTorque(vpColVector &H) const; + double getMaxRotationVelocityJoint6() const; void getPosition (const vpRobot::vpControlFrameType frame, vpColVector &position); void getPosition (const vpRobot::vpControlFrameType frame, @@ -364,8 +365,8 @@ public: /* Methode publiques */ void getPosition (const vpRobot::vpControlFrameType frame, vpPoseVector &position, double ×tamp); - double getPositioningVelocity (void); - bool getPowerState(); + double getPositioningVelocity (void) const; + bool getPowerState() const; void getVelocity (const vpRobot::vpControlFrameType frame, vpColVector & velocity); @@ -377,10 +378,10 @@ public: /* Methode publiques */ double getTime() const; - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_eJe(vpMatrix &eJe) ; - void get_fJe(vpMatrix &fJe) ; + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_eJe(vpMatrix &eJe); + void get_fJe(vpMatrix &fJe); void init (void); void init (vpViper850::vpToolType tool, @@ -397,6 +398,9 @@ public: /* Methode publiques */ static bool readPosFile(const char *filename, vpColVector &q) ; static bool savePosFile(const char *filename, const vpColVector &q) ; + void setMaxRotationVelocity(double w_max); + void setMaxRotationVelocityJoint6(double w6_max); + // Position control void setPosition(const vpRobot::vpControlFrameType frame, const vpColVector &position) ; @@ -418,6 +422,8 @@ public: /* Methode publiques */ private: void getArticularDisplacement(vpColVector &displacement); void getCameraDisplacement(vpColVector &displacement); + + double maxRotationVelocity_joint6; }; diff --git a/src/robot/real-robot/viper/vpViper.cpp b/src/robot/real-robot/viper/vpViper.cpp index b3bbae7d7f261f6ef57654be0bc667d1f6ca758a..250410c89fefbfccf30aefab94862f30689c9617 100644 --- a/src/robot/real-robot/viper/vpViper.cpp +++ b/src/robot/real-robot/viper/vpViper.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper.cpp 4206 2013-04-13 07:29:06Z fspindle $ + * $Id: vpViper.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,6 +68,7 @@ const unsigned int vpViper::njoint = 6; */ vpViper::vpViper() + : eMc(), etc(), erc(), a1(0), d1(0), a2(), a3(), d4(0), d6(0), c56(0), joint_max(), joint_min() { // Default values are initialized @@ -125,7 +126,7 @@ vpViper::vpViper() */ vpHomogeneousMatrix -vpViper::getForwardKinematics(const vpColVector & q) +vpViper::getForwardKinematics(const vpColVector & q) const { vpHomogeneousMatrix fMc; fMc = get_fMc(q); @@ -148,7 +149,7 @@ vpViper::getForwardKinematics(const vpColVector & q) \return true if the joint position is in the joint limits. false otherwise. */ bool -vpViper::convertJointPositionInLimits(unsigned int joint, const double &q, double &q_mod, const bool &verbose) +vpViper::convertJointPositionInLimits(unsigned int joint, const double &q, double &q_mod, const bool &verbose) const { double eps = 0.01; if (q >= joint_min[joint]-eps && q <= joint_max[joint]+eps ) { @@ -228,7 +229,7 @@ vpViper::convertJointPositionInLimits(unsigned int joint, const double &q, doubl */ unsigned int -vpViper::getInverseKinematicsWrist(const vpHomogeneousMatrix & fMw, vpColVector & q, const bool &verbose) +vpViper::getInverseKinematicsWrist(const vpHomogeneousMatrix & fMw, vpColVector & q, const bool &verbose) const { vpColVector q_sol[8]; @@ -573,14 +574,14 @@ vpViper::getInverseKinematicsWrist(const vpHomogeneousMatrix & fMw, vpColVector */ unsigned int -vpViper::getInverseKinematics(const vpHomogeneousMatrix & fMc, vpColVector & q, const bool &verbose) +vpViper::getInverseKinematics(const vpHomogeneousMatrix & fMc, vpColVector & q, const bool &verbose) const { vpHomogeneousMatrix fMw; vpHomogeneousMatrix wMe; - vpHomogeneousMatrix eMc; + vpHomogeneousMatrix eMc_; this->get_wMe(wMe); - this->get_eMc(eMc); - fMw = fMc * eMc.inverse() * wMe.inverse(); + this->get_eMc(eMc_); + fMw = fMc * eMc_.inverse() * wMe.inverse(); return (getInverseKinematicsWrist(fMw, q, verbose)); } @@ -611,7 +612,7 @@ vpViper::getInverseKinematics(const vpHomogeneousMatrix & fMc, vpColVector & q, */ vpHomogeneousMatrix -vpViper::get_fMc (const vpColVector & q) +vpViper::get_fMc (const vpColVector & q) const { vpHomogeneousMatrix fMc; get_fMc(q, fMc); @@ -641,7 +642,7 @@ vpViper::get_fMc (const vpColVector & q) \sa get_fMe(), get_eMc() */ void -vpViper::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) +vpViper::get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const { // Compute the direct geometric model: fMe = transformation between @@ -728,7 +729,7 @@ int main() */ void -vpViper::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) +vpViper::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const { double q1 = q[0]; double q2 = q[1]; @@ -824,7 +825,7 @@ vpViper::get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) */ void -vpViper::get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw) +vpViper::get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw) const { double q1 = q[0]; double q2 = q[1]; @@ -857,7 +858,6 @@ vpViper::get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw) double c23 = cos(q2+q3); double s23 = sin(q2+q3); - fMw[0][0] = c1*(c23*(c4*c5*c6-s4*s6)-s23*s5*c6)-s1*(s4*c5*c6+c4*s6); fMw[1][0] = -s1*(c23*(-c4*c5*c6+s4*s6)+s23*s5*c6)+c1*(s4*c5*c6+c4*s6); fMw[2][0] = s23*(s4*s6-c4*c5*c6)-c23*s5*c6; @@ -890,7 +890,7 @@ vpViper::get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw) */ void -vpViper::get_wMe(vpHomogeneousMatrix & wMe) +vpViper::get_wMe(vpHomogeneousMatrix & wMe) const { // Set the rotation as identity wMe.setIdentity(); @@ -905,15 +905,15 @@ vpViper::get_wMe(vpHomogeneousMatrix & wMe) the camera frame. This transformation is constant and correspond to the extrinsic camera parameters estimated by calibration. - \param eMc : Transformation between the the + \param eMc_ : Transformation between the the end-effector frame and the camera frame. \sa get_cMe() */ void -vpViper::get_eMc(vpHomogeneousMatrix &eMc) +vpViper::get_eMc(vpHomogeneousMatrix &eMc_) const { - eMc = this->eMc; + eMc_ = this->eMc; } /*! @@ -928,7 +928,7 @@ vpViper::get_eMc(vpHomogeneousMatrix &eMc) \sa get_eMc() */ void -vpViper::get_cMe(vpHomogeneousMatrix &cMe) +vpViper::get_cMe(vpHomogeneousMatrix &cMe) const { cMe = this->eMc.inverse(); } @@ -949,7 +949,7 @@ vpViper::get_cMe(vpHomogeneousMatrix &cMe) */ void -vpViper::get_cVe(vpVelocityTwistMatrix &cVe) +vpViper::get_cVe(vpVelocityTwistMatrix &cVe) const { vpHomogeneousMatrix cMe ; get_cMe(cMe) ; @@ -982,7 +982,7 @@ vpViper::get_cVe(vpVelocityTwistMatrix &cVe) \sa get_fJw() */ void -vpViper::get_eJe(const vpColVector &q, vpMatrix &eJe) +vpViper::get_eJe(const vpColVector &q, vpMatrix &eJe) const { vpMatrix V(6,6); V = 0; @@ -1068,7 +1068,7 @@ vpViper::get_eJe(const vpColVector &q, vpMatrix &eJe) */ void -vpViper::get_fJw(const vpColVector &q, vpMatrix &fJw) +vpViper::get_fJw(const vpColVector &q, vpMatrix &fJw) const { double q1 = q[0]; double q2 = q[1]; @@ -1174,7 +1174,7 @@ vpViper::get_fJw(const vpColVector &q, vpMatrix &fJw) \sa get_fJw */ void -vpViper::get_fJe(const vpColVector &q, vpMatrix &fJe) +vpViper::get_fJe(const vpColVector &q, vpMatrix &fJe) const { vpMatrix V(6,6); V = 0; @@ -1216,7 +1216,7 @@ vpViper::get_fJe(const vpColVector &q, vpMatrix &fJe) */ vpColVector -vpViper::getJointMin() +vpViper::getJointMin() const { return joint_min; } @@ -1229,7 +1229,7 @@ vpViper::getJointMin() */ vpColVector -vpViper::getJointMax() +vpViper::getJointMax() const { return joint_max; } @@ -1245,13 +1245,11 @@ vpViper::getJointMax() */ double -vpViper::getCoupl56() +vpViper::getCoupl56() const { return c56; } - - /*! Print on the output stream \e os the robot parameters (joint @@ -1261,7 +1259,7 @@ vpViper::getCoupl56() \param os : Output stream. \param viper : Robot parameters. */ -std::ostream & operator << (std::ostream & os, const vpViper & viper) +VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpViper & viper) { vpRotationMatrix eRc; viper.eMc.extract(eRc); @@ -1302,8 +1300,3 @@ std::ostream & operator << (std::ostream & os, const vpViper & viper) return os; } -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/robot/real-robot/viper/vpViper.h b/src/robot/real-robot/viper/vpViper.h index a2b0a0f6956f9c406cc389aa160f4eabc691e06a..e67984c63f60b7f2c3017ed355cdd88a1cfb3a7a 100644 --- a/src/robot/real-robot/viper/vpViper.h +++ b/src/robot/real-robot/viper/vpViper.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper.h 4191 2013-04-01 07:46:05Z fspindle $ + * $Id: vpViper.h 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -114,31 +114,30 @@ class VISP_EXPORT vpViper vpViper(); virtual ~vpViper() {}; - vpHomogeneousMatrix getForwardKinematics(const vpColVector & q); - unsigned int getInverseKinematicsWrist(const vpHomogeneousMatrix & fMw, vpColVector & q, const bool &verbose=false); - unsigned int getInverseKinematics(const vpHomogeneousMatrix & fMc, vpColVector & q, const bool &verbose=false); - vpHomogeneousMatrix get_fMc (const vpColVector & q); - void get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw); - void get_wMe(vpHomogeneousMatrix & wMe); - void get_eMc(vpHomogeneousMatrix & eMc); - void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe); - void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc); - - void get_cMe(vpHomogeneousMatrix &cMe) ; - void get_cVe(vpVelocityTwistMatrix &cVe) ; - void get_fJw(const vpColVector &q, vpMatrix &fJw) ; - void get_fJe(const vpColVector &q, vpMatrix &fJe) ; - void get_eJe(const vpColVector &q, vpMatrix &eJe) ; - - friend VISP_EXPORT std::ostream & operator << (std::ostream & os, - const vpViper & viper); - - vpColVector getJointMin(); - vpColVector getJointMax(); - double getCoupl56(); + vpHomogeneousMatrix getForwardKinematics(const vpColVector & q) const; + unsigned int getInverseKinematicsWrist(const vpHomogeneousMatrix & fMw, vpColVector & q, const bool &verbose=false) const; + unsigned int getInverseKinematics(const vpHomogeneousMatrix & fMc, vpColVector & q, const bool &verbose=false) const; + vpHomogeneousMatrix get_fMc (const vpColVector & q) const; + void get_fMw(const vpColVector & q, vpHomogeneousMatrix & fMw) const; + void get_wMe(vpHomogeneousMatrix & wMe) const; + void get_eMc(vpHomogeneousMatrix & eMc) const; + void get_fMe(const vpColVector & q, vpHomogeneousMatrix & fMe) const; + void get_fMc(const vpColVector & q, vpHomogeneousMatrix & fMc) const; + + void get_cMe(vpHomogeneousMatrix &cMe) const; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_fJw(const vpColVector &q, vpMatrix &fJw) const; + void get_fJe(const vpColVector &q, vpMatrix &fJe) const; + void get_eJe(const vpColVector &q, vpMatrix &eJe) const; + + friend VISP_EXPORT std::ostream & operator << (std::ostream & os, const vpViper & viper); + + vpColVector getJointMin() const; + vpColVector getJointMax() const; + double getCoupl56() const; private: - bool convertJointPositionInLimits(unsigned int joint, const double &q, double &q_mod, const bool &verbose=false); + bool convertJointPositionInLimits(unsigned int joint, const double &q, double &q_mod, const bool &verbose=false) const; public: static const unsigned int njoint; ///< Number of joint. @@ -160,15 +159,7 @@ class VISP_EXPORT vpViper // Software joint limits in radians vpColVector joint_max; // Maximal value of the joints vpColVector joint_min; // Minimal value of the joints - - }; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ - #endif diff --git a/src/robot/real-robot/viper/vpViper650.cpp b/src/robot/real-robot/viper/vpViper650.cpp index 0535055f70cab0bb7071c93822b0302b21739125..0e8c2df6ca48fcf0cf4b131d0acb428e6c038338 100644 --- a/src/robot/real-robot/viper/vpViper650.cpp +++ b/src/robot/real-robot/viper/vpViper650.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper650.cpp 4210 2013-04-16 08:57:46Z fspindle $ + * $Id: vpViper650.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,56 +57,56 @@ static const char *opt_viper650[] = {"CAMERA", "eMc_ROT_XYZ","eMc_TRANS_XYZ", NULL}; const char * const vpViper650::CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_MarlinF033C_without_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_MarlinF033C_without_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_MarlinF033C_with_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_MarlinF033C_with_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_PTGreyFlea2_without_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_PTGreyFlea2_without_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_PTGreyFlea2_with_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_PTGreyFlea2_with_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_schunk_gripper_without_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_schunk_gripper_without_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_schunk_gripper_with_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_schunk_gripper_with_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_generic_without_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_generic_without_distortion_Viper650.cnf"; #endif const char * const vpViper650::CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_eMc_generic_with_distortion_Viper650.cnf"; #else = "/udd/fspindle/robot/Viper650/current/include/const_eMc_generic_with_distortion_Viper650.cnf"; @@ -114,7 +114,7 @@ const char * const vpViper650::CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME const char * const vpViper650::CONST_CAMERA_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper650/current/include/const_camera_Viper650.xml"; #else = "/udd/fspindle/robot/Viper650/current/include/const_camera_Viper650.xml"; @@ -139,6 +139,7 @@ const vpViper650::vpToolType vpViper650::defaultTool = vpViper650::TOOL_PTGREY_F */ vpViper650::vpViper650() + : tool_current(vpViper650::defaultTool), projModel(vpCameraParameters::perspectiveProjWithoutDistortion) { // Denavit Hartenberg parameters a1 = 0.075; @@ -209,15 +210,15 @@ vpViper650::init (const char *camera_extrinsic_parameters) \param tool : Camera in use. - \param projModel : Projection model of the camera. + \param proj_model : Projection model of the camera. */ void vpViper650::init (vpViper650::vpToolType tool, - vpCameraParameters::vpCameraParametersProjType projModel) + vpCameraParameters::vpCameraParametersProjType proj_model) { - this->projModel = projModel; + this->projModel = proj_model; #ifdef VISP_HAVE_ACCESS_TO_NAS // Read the robot parameters from files @@ -226,19 +227,19 @@ vpViper650::init (vpViper650::vpToolType tool, case vpViper650::TOOL_MARLIN_F033C_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME); #endif @@ -249,19 +250,19 @@ vpViper650::init (vpViper650::vpToolType tool, case vpViper650::TOOL_PTGREY_FLEA2_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME); #endif @@ -272,19 +273,19 @@ vpViper650::init (vpViper650::vpToolType tool, case vpViper650::TOOL_SCHUNK_GRIPPER_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME); #endif @@ -295,19 +296,19 @@ vpViper650::init (vpViper650::vpToolType tool, case vpViper650::TOOL_GENERIC_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); #endif @@ -354,6 +355,7 @@ vpViper650::init (vpViper650::vpToolType tool, etc[2] = 0.1022; // tz break; } + break; } case vpViper650::TOOL_PTGREY_FLEA2_CAMERA: case vpViper650::TOOL_SCHUNK_GRIPPER_CAMERA: { @@ -375,6 +377,7 @@ vpViper650::init (vpViper650::vpToolType tool, etc[2] = 0.078; // tz break; } + break; } case vpViper650::TOOL_GENERIC_CAMERA: { // Set eMc to identity @@ -389,6 +392,7 @@ vpViper650::init (vpViper650::vpToolType tool, etc[2] = 0; // tz break; } + break; } } vpRotationMatrix eRc(erc); @@ -574,7 +578,7 @@ int main() void vpViper650::getCameraParameters (vpCameraParameters &cam, const unsigned int &image_width, - const unsigned int &image_height) + const unsigned int &image_height) const { #if defined(VISP_HAVE_XML2) && defined (VISP_HAVE_ACCESS_TO_NAS) vpXmlParserCamera parser; @@ -796,7 +800,7 @@ int main() */ void vpViper650::getCameraParameters (vpCameraParameters &cam, - const vpImage<unsigned char> &I) + const vpImage<unsigned char> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } @@ -866,15 +870,7 @@ int main() void vpViper650::getCameraParameters (vpCameraParameters &cam, - const vpImage<vpRGBa> &I) + const vpImage<vpRGBa> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } - - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/robot/real-robot/viper/vpViper650.h b/src/robot/real-robot/viper/vpViper650.h index 883a6634aedd11e3db82b45ed99419c944f76f55..b19a7aaa439736f5b0cd994f2a11926afbdc1732 100644 --- a/src/robot/real-robot/viper/vpViper650.h +++ b/src/robot/real-robot/viper/vpViper650.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper650.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpViper650.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -111,19 +111,19 @@ class VISP_EXPORT vpViper650: public vpViper //! Get the current camera model projection type - vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType(){ + vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType() const { return projModel; }; void getCameraParameters(vpCameraParameters &cam, const unsigned int &image_width, - const unsigned int &image_height); + const unsigned int &image_height) const; void getCameraParameters(vpCameraParameters &cam, - const vpImage<unsigned char> &I); - void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I); + const vpImage<unsigned char> &I) const; + void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I) const; //! Get the current tool type - vpToolType getToolType(){ + vpToolType getToolType() const{ return tool_current; }; diff --git a/src/robot/real-robot/viper/vpViper850.cpp b/src/robot/real-robot/viper/vpViper850.cpp index f465ee8f2243e5229b835da1328f49f5755cecf4..3a29dc4cac44825cd0f0fce58fc1ad6cfc1f6d2a 100644 --- a/src/robot/real-robot/viper/vpViper850.cpp +++ b/src/robot/real-robot/viper/vpViper850.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper850.cpp 4210 2013-04-16 08:57:46Z fspindle $ + * $Id: vpViper850.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,56 +57,56 @@ static const char *opt_viper850[] = {"CAMERA", "eMc_ROT_XYZ","eMc_TRANS_XYZ", NULL}; const char * const vpViper850::CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_MarlinF033C_without_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_MarlinF033C_without_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_MarlinF033C_with_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_MarlinF033C_with_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_PTGreyFlea2_without_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_PTGreyFlea2_without_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_PTGreyFlea2_with_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_PTGreyFlea2_with_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_schunk_gripper_without_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_schunk_gripper_without_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_schunk_gripper_with_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_schunk_gripper_with_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_generic_without_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_generic_without_distortion_Viper850.cnf"; #endif const char * const vpViper850::CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_eMc_generic_with_distortion_Viper850.cnf"; #else = "/udd/fspindle/robot/Viper850/current/include/const_eMc_generic_with_distortion_Viper850.cnf"; @@ -114,7 +114,7 @@ const char * const vpViper850::CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME const char * const vpViper850::CONST_CAMERA_FILENAME -#ifdef WIN32 +#if defined(_WIN32) = "Z:/robot/Viper850/current/include/const_camera_Viper850.xml"; #else = "/udd/fspindle/robot/Viper850/current/include/const_camera_Viper850.xml"; @@ -139,6 +139,8 @@ const vpViper850::vpToolType vpViper850::defaultTool = vpViper850::TOOL_PTGREY_F */ vpViper850::vpViper850() + : tool_current(vpViper850::defaultTool), projModel(vpCameraParameters::perspectiveProjWithoutDistortion) + { // Denavit Hartenberg parameters a1 = 0.075; @@ -209,15 +211,15 @@ vpViper850::init (const char *camera_extrinsic_parameters) \param tool : Camera in use. - \param projModel : Projection model of the camera. + \param proj_model : Projection model of the camera. */ void vpViper850::init (vpViper850::vpToolType tool, - vpCameraParameters::vpCameraParametersProjType projModel) + vpCameraParameters::vpCameraParametersProjType proj_model) { - this->projModel = projModel; + this->projModel = proj_model; #ifdef VISP_HAVE_ACCESS_TO_NAS // Read the robot parameters from files @@ -226,19 +228,19 @@ vpViper850::init (vpViper850::vpToolType tool, case vpViper850::TOOL_MARLIN_F033C_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_MARLIN_F033C_WITH_DISTORTION_FILENAME); #endif @@ -249,19 +251,19 @@ vpViper850::init (vpViper850::vpToolType tool, case vpViper850::TOOL_PTGREY_FLEA2_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_PTGREY_FLEA2_WITH_DISTORTION_FILENAME); #endif @@ -272,19 +274,19 @@ vpViper850::init (vpViper850::vpToolType tool, case vpViper850::TOOL_SCHUNK_GRIPPER_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_SCHUNK_GRIPPER_WITH_DISTORTION_FILENAME); #endif @@ -295,19 +297,19 @@ vpViper850::init (vpViper850::vpToolType tool, case vpViper850::TOOL_GENERIC_CAMERA: { switch(projModel) { case vpCameraParameters::perspectiveProjWithoutDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITHOUT_DISTORTION_FILENAME); #endif break; case vpCameraParameters::perspectiveProjWithDistortion : -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); -#else // WIN32 +#else // _WIN32 _snprintf(filename_eMc, FILENAME_MAX, "%s", CONST_EMC_GENERIC_WITH_DISTORTION_FILENAME); #endif @@ -354,6 +356,7 @@ vpViper850::init (vpViper850::vpToolType tool, etc[2] = 0.1022; // tz break; } + break; } case vpViper850::TOOL_PTGREY_FLEA2_CAMERA: case vpViper850::TOOL_SCHUNK_GRIPPER_CAMERA: { @@ -375,6 +378,7 @@ vpViper850::init (vpViper850::vpToolType tool, etc[2] = 0.078; // tz break; } + break; } case vpViper850::TOOL_GENERIC_CAMERA: { // Set eMc to identity @@ -389,6 +393,7 @@ vpViper850::init (vpViper850::vpToolType tool, etc[2] = 0; // tz break; } + break; } } vpRotationMatrix eRc(erc); @@ -574,7 +579,7 @@ int main() void vpViper850::getCameraParameters (vpCameraParameters &cam, const unsigned int &image_width, - const unsigned int &image_height) + const unsigned int &image_height) const { #if defined(VISP_HAVE_XML2) && defined (VISP_HAVE_ACCESS_TO_NAS) vpXmlParserCamera parser; @@ -796,7 +801,7 @@ int main() */ void vpViper850::getCameraParameters (vpCameraParameters &cam, - const vpImage<unsigned char> &I) + const vpImage<unsigned char> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } @@ -866,15 +871,7 @@ int main() void vpViper850::getCameraParameters (vpCameraParameters &cam, - const vpImage<vpRGBa> &I) + const vpImage<vpRGBa> &I) const { getCameraParameters(cam,I.getWidth(),I.getHeight()); } - - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/robot/real-robot/viper/vpViper850.h b/src/robot/real-robot/viper/vpViper850.h index a9d1a3437279555acb8a7ab1cacc42ac3cf627e0..8418cf0efa3239e1ec2a5e56e4ecf61b330f20e7 100644 --- a/src/robot/real-robot/viper/vpViper850.h +++ b/src/robot/real-robot/viper/vpViper850.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViper850.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpViper850.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -111,19 +111,19 @@ class VISP_EXPORT vpViper850: public vpViper //! Get the current camera model projection type - vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType(){ + vpCameraParameters::vpCameraParametersProjType getCameraParametersProjType() const{ return projModel; }; void getCameraParameters(vpCameraParameters &cam, const unsigned int &image_width, - const unsigned int &image_height); + const unsigned int &image_height) const; void getCameraParameters(vpCameraParameters &cam, - const vpImage<unsigned char> &I); - void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I); + const vpImage<unsigned char> &I) const; + void getCameraParameters(vpCameraParameters &cam, const vpImage<vpRGBa> &I) const; //! Get the current tool type - vpToolType getToolType(){ + vpToolType getToolType() const { return tool_current; }; diff --git a/src/robot/robot/vpRobot.cpp b/src/robot/robot/vpRobot.cpp index 9b1945c9c85a0114268bc49ed72c562779c87545..e8addbcd7fadae892c19287865054a3dbef1f529 100644 --- a/src/robot/robot/vpRobot.cpp +++ b/src/robot/robot/vpRobot.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobot.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobot.cpp 5238 2015-01-30 13:52:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,14 +53,65 @@ const double vpRobot::maxRotationVelocityDefault = 0.7; vpRobot::vpRobot (void) : + stateRobot(vpRobot::STATE_STOP), frameRobot(vpRobot::CAMERA_FRAME), maxTranslationVelocity (maxTranslationVelocityDefault), - maxRotationVelocity (maxRotationVelocityDefault) + maxRotationVelocity (maxRotationVelocityDefault), + nDof(0), + eJe(), eJeAvailable(false), fJe(), fJeAvailable(false), areJointLimitsAvailable(false), + qmin(NULL), qmax(NULL), verbose_(true) { - frameRobot = vpRobot::CAMERA_FRAME; - stateRobot = vpRobot::STATE_STOP ; - verbose_ = true; } +vpRobot::vpRobot (const vpRobot &robot) + : + stateRobot(vpRobot::STATE_STOP), frameRobot(vpRobot::CAMERA_FRAME), + maxTranslationVelocity (maxTranslationVelocityDefault), + maxRotationVelocity (maxRotationVelocityDefault), + nDof(0), + eJe(), eJeAvailable(false), fJe(), fJeAvailable(false), areJointLimitsAvailable(false), + qmin(NULL), qmax(NULL), verbose_(true) +{ + *this = robot; +} + +/*! + Destructor that free allocated memory. + */ +vpRobot::~vpRobot() +{ + if (qmin != NULL) { + delete [] qmin; + qmin = NULL; + } + if (qmax != NULL) { + delete [] qmax; + qmax = NULL; + } +} + +/*! Copy operator. */ +vpRobot & vpRobot::operator=(const vpRobot &robot) +{ + stateRobot = robot.stateRobot; + frameRobot = robot.frameRobot; + maxTranslationVelocity = robot.maxTranslationVelocity; + maxRotationVelocity = robot.maxRotationVelocity; + nDof = robot.nDof; + eJe = robot.eJe; + eJeAvailable = robot.eJeAvailable; + fJe= robot.fJe; + fJeAvailable = robot.fJeAvailable; + areJointLimitsAvailable = robot.areJointLimitsAvailable; + qmin = new double [nDof]; + qmax = new double [nDof]; + for (int i = 0; i< nDof; i++) { + qmin[i] = robot.qmin[i]; + qmax[i] = robot.qmax[i]; + } + verbose_ = robot.verbose_; + + return (*this); +} /*! Saturate velocities. diff --git a/src/robot/robot/vpRobot.h b/src/robot/robot/vpRobot.h index 70bbb1f14c196b2e452b3c9b28dcfebdf5687d37..4987af7d9f530f3d67d4fd12a407f4ca1c93dcb6 100644 --- a/src/robot/robot/vpRobot.h +++ b/src/robot/robot/vpRobot.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobot.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobot.h 5238 2015-01-30 13:52:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -116,13 +116,14 @@ protected: public: vpRobot (void); - virtual ~vpRobot() { ; } + vpRobot (const vpRobot &robot); + virtual ~vpRobot(); //---------- Jacobian ----------------------------- //! Get the robot Jacobian expressed in the end-effector frame - virtual void get_eJe(vpMatrix &_eJe) = 0 ; + virtual void get_eJe(vpMatrix &_eJe) = 0 ; //! Get the robot Jacobian expressed in the robot reference (or world) frame. - virtual void get_fJe(vpMatrix &_fJe) = 0 ; + virtual void get_fJe(vpMatrix &_fJe) = 0 ; //! Get a displacement (frame as to ve specified) between two successive position control. virtual void getDisplacement(const vpRobot::vpControlFrameType frame, @@ -136,10 +137,12 @@ public: // Return the robot position (frame has to be specified). vpColVector getPosition (const vpRobot::vpControlFrameType frame); - virtual vpRobotStateType getRobotState (void) { return stateRobot ; } + virtual vpRobotStateType getRobotState (void) const { return stateRobot ; } virtual void init() = 0 ; + vpRobot & operator=(const vpRobot &robot); + static vpColVector saturateVelocities(const vpColVector &v_in, const vpColVector &v_max, bool verbose=false); void setMaxRotationVelocity (const double maxVr); @@ -156,7 +159,7 @@ public: protected: vpControlFrameType setRobotFrame (vpRobot::vpControlFrameType newFrame); - vpControlFrameType getRobotFrame (void) { return frameRobot ; } + vpControlFrameType getRobotFrame (void) const { return frameRobot ; } } ; #endif diff --git a/src/robot/robot/vpRobotException.h b/src/robot/robot/vpRobotException.h index 969f385194a7271f668c950270d0d7e6ce536d1b..7fb9a36c72a5d697a442ea41d771e73f162c762f 100644 --- a/src/robot/robot/vpRobotException.h +++ b/src/robot/robot/vpRobotException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,85 +70,78 @@ */ class VISP_EXPORT vpRobotException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpRobot member */ - enum errorRobotCodeEnum + enum errorRobotCodeEnum { - /** Erreur lancee par le constructor. */ - constructionError, - - /** Erreur lancee lors de la construction d'un objet CRobot - * correspondant a un robot reel si l'objet de la classe en - * question doit etre unique. */ - uniqueRobotError, - - /** Erreur lancee par les fonctions de commande si le - * robot n'est pas dans le bon etat au moment du passage - * d'ordre. */ - wrongStateError, - - /** Erreur lancee par les fonctions de changement d'etat - * si le changement demandee n'est pas possible. */ - stateModificationError, - - /** Erreur generee lors d'un retour non nulle d'une fonction - * de communication de la lib Afma6. */ - communicationError, - - /** Erreur lancee apres un appel a une fonction de la lib - * bas-niveau de control de l'afma6 ayant renvoye une erreur. */ - lowLevelError, - - /** Erreur lancee par la fonction de parsing des parametres du - * robot, si le fichier donne en entree n'est pas valide. - */ - readingParametersError, - - /** Erreur lancee par les methodes d'une classe qui necessite - * un appel a une fonction d'initialisation apres la - * construction si l'init n'a pas ete fait. */ - notInitializedError, - - /** Erreur lancee par les fonctions decrites dans lAPI mais - * pas completement implementee. Dans ce cas, la fonction - * affiche simplement un message d'erreur avant de sortir - * par le 'throw'. - */ - notImplementedError, - /** Position is out of range. - */ - positionOutOfRangeError + /** Erreur lancee par le constructor. */ + constructionError, + + /** Erreur lancee lors de la construction d'un objet CRobot + * correspondant a un robot reel si l'objet de la classe en + * question doit etre unique. */ + uniqueRobotError, + + /** Erreur lancee par les fonctions de commande si le + * robot n'est pas dans le bon etat au moment du passage + * d'ordre. */ + wrongStateError, + + /** Erreur lancee par les fonctions de changement d'etat + * si le changement demandee n'est pas possible. */ + stateModificationError, + + /** Erreur generee lors d'un retour non nulle d'une fonction + * de communication de la lib Afma6. */ + communicationError, + + /** Erreur lancee apres un appel a une fonction de la lib + * bas-niveau de control de l'afma6 ayant renvoye une erreur. */ + lowLevelError, + + /** Erreur lancee par la fonction de parsing des parametres du + * robot, si le fichier donne en entree n'est pas valide. + */ + readingParametersError, + + /** Erreur lancee par les methodes d'une classe qui necessite + * un appel a une fonction d'initialisation apres la + * construction si l'init n'a pas ete fait. */ + notInitializedError, + + /** Erreur lancee par les fonctions decrites dans lAPI mais + * pas completement implementee. Dans ce cas, la fonction + * affiche simplement un message d'erreur avant de sortir + * par le 'throw'. + */ + notImplementedError, + /** Position is out of range. + */ + positionOutOfRangeError } ; -public: - vpRobotException (const int code, const char * msg) - : vpException(code, msg) + public: + vpRobotException (const int id, const char* format, ...) { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); } - vpRobotException (const int code, const std::string & msg) - : vpException(code, msg) + vpRobotException (const int id, const std::string & msg) + : vpException(id, msg) + { + } + vpRobotException (const int id) + : vpException(id) { } - vpRobotException (const int code) - : vpException(code) - { - } - -}; - - - - - -#endif /* #ifndef __vpRobotException_H */ +}; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/robot/robot/vpRobotTemplate.cpp b/src/robot/robot/vpRobotTemplate.cpp index 3a77f0ee75beb0169d4e9081f0ca4e185cca10a7..441da2247258033f61b216d75b81edcd90cd64f4 100644 --- a/src/robot/robot/vpRobotTemplate.cpp +++ b/src/robot/robot/vpRobotTemplate.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotTemplate.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotTemplate.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/robot/vpRobotTemplate.h b/src/robot/robot/vpRobotTemplate.h index aac9e9eaf11965a8b069ea0493b90cb38b7e123e..8d527b8ae4388a901c378c77d424511ecb55f826 100644 --- a/src/robot/robot/vpRobotTemplate.h +++ b/src/robot/robot/vpRobotTemplate.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotTemplate.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotTemplate.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/simulator-robot/arms/CMakeRobotArmsList.cmake b/src/robot/simulator-robot/arms/CMakeRobotArmsList.cmake index d4a4ca203145b6846d67615744bddc719544aa8c..4cf0a103fcf2b89f0fcc63ae08a0e266ff62890a 100644 --- a/src/robot/simulator-robot/arms/CMakeRobotArmsList.cmake +++ b/src/robot/simulator-robot/arms/CMakeRobotArmsList.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeRobotArmsList.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeRobotArmsList.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/src/robot/simulator-robot/vpRobotCamera.cpp b/src/robot/simulator-robot/vpRobotCamera.cpp index a17d410cc38ca5e4ee3b2376a9c2ffecd3958c28..b9b206f93ed8fe38f3191999d1f3c0659d4482d9 100644 --- a/src/robot/simulator-robot/vpRobotCamera.cpp +++ b/src/robot/simulator-robot/vpRobotCamera.cpp @@ -3,7 +3,7 @@ * $Id: vpRobotCamera.cpp 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -72,6 +72,7 @@ */ vpRobotCamera::vpRobotCamera() + : cMw_() { init() ; } @@ -118,7 +119,7 @@ vpRobotCamera::~vpRobotCamera() */ void -vpRobotCamera::get_cVe(vpVelocityTwistMatrix &cVe) +vpRobotCamera::get_cVe(vpVelocityTwistMatrix &cVe) const { vpVelocityTwistMatrix cVe_; cVe = cVe_; @@ -128,13 +129,13 @@ vpRobotCamera::get_cVe(vpVelocityTwistMatrix &cVe) Get the robot jacobian expressed in the end-effector frame. For that simple robot the Jacobian is the identity. - \param eJe : A 6 by 6 matrix representing the robot jacobian \f$ {^e}{\bf + \param eJe_ : A 6 by 6 matrix representing the robot jacobian \f$ {^e}{\bf J}_e\f$ expressed in the end-effector frame. */ void -vpRobotCamera::get_eJe(vpMatrix &eJe) +vpRobotCamera::get_eJe(vpMatrix &eJe_) { - eJe = this->eJe ; + eJe_ = this->eJe ; } /*! diff --git a/src/robot/simulator-robot/vpRobotCamera.h b/src/robot/simulator-robot/vpRobotCamera.h index 34016109b7b90a3249e0b8ebd95083c1272a864f..4c34837cf37a33d0eff91b8a4405bc59f81e2934 100644 --- a/src/robot/simulator-robot/vpRobotCamera.h +++ b/src/robot/simulator-robot/vpRobotCamera.h @@ -3,7 +3,7 @@ * $Id: vpRobotCamera.h 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -116,8 +116,8 @@ public: vpRobotCamera() ; virtual ~vpRobotCamera() ; - void get_cVe(vpVelocityTwistMatrix &cVe); - void get_eJe(vpMatrix &eJe) ; + void get_cVe(vpVelocityTwistMatrix &cVe) const; + void get_eJe(vpMatrix &eJe); void getPosition(vpHomogeneousMatrix &cMw) const ; void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q); diff --git a/src/robot/simulator-robot/vpRobotSimulator.cpp b/src/robot/simulator-robot/vpRobotSimulator.cpp index 41c906959f5899276a1527bfc01fa3b675510d90..92cf1ec747b80cf6f6d51ec18a94d024bc89fc9d 100644 --- a/src/robot/simulator-robot/vpRobotSimulator.cpp +++ b/src/robot/simulator-robot/vpRobotSimulator.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotSimulator.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotSimulator.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,7 +46,6 @@ /*! Basic constructor that sets the sampling time by default to 40ms. */ -vpRobotSimulator::vpRobotSimulator() : vpRobot() +vpRobotSimulator::vpRobotSimulator() : vpRobot(), delta_t_(0.040f) { - setSamplingTime(0.040f); } diff --git a/src/robot/simulator-robot/vpRobotSimulator.h b/src/robot/simulator-robot/vpRobotSimulator.h index 9b9663e4663a8e41e570bb6c0a716262509d9ee2..7c193b90e36d06068557aae75fc04a6289cb098f 100644 --- a/src/robot/simulator-robot/vpRobotSimulator.h +++ b/src/robot/simulator-robot/vpRobotSimulator.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRobotSimulator.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRobotSimulator.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/simulator-robot/vpRobotWireFrameSimulator.cpp b/src/robot/simulator-robot/vpRobotWireFrameSimulator.cpp index ebce5539e381561c7ba151c109ff2a212e19fd46..e69b9bfb6c18c8bccb27b12da04ea86e47817dd1 100644 --- a/src/robot/simulator-robot/vpRobotWireFrameSimulator.cpp +++ b/src/robot/simulator-robot/vpRobotWireFrameSimulator.cpp @@ -3,7 +3,7 @@ * $Id: vpRobotWireFrameSimulator.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,14 +43,28 @@ -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) #include <visp/vpRobotWireFrameSimulator.h> #include <visp/vpSimulatorViper850.h> /*! Basic constructor */ -vpRobotWireFrameSimulator::vpRobotWireFrameSimulator():vpWireFrameSimulator(), vpRobotSimulator() +vpRobotWireFrameSimulator::vpRobotWireFrameSimulator() + : vpWireFrameSimulator(), vpRobotSimulator(), + I(), tcur(0), tprev(0), robotArms(NULL), size_fMi(8), fMi(NULL), artCoord(), artVel(), velocity(), +#if defined(_WIN32) +#elif defined(VISP_HAVE_PTHREAD) + thread(), attr(), +#endif + mutex_fMi(), mutex_artVel(), mutex_artCoord(), mutex_velocity(), mutex_display(), + displayBusy(false), robotStop(false), jointLimit(false), jointLimitArt(false), singularityManagement(true), + cameraParam(), +#if defined(VISP_HAVE_DISPLAY) + display(), +#endif + displayType(MODEL_3D), displayAllowed(true), constantSamplingTimeMode(false), + setVelocityCalled(false), verbose_(false) { setSamplingTime(0.010); velocity.resize(6); @@ -59,48 +73,41 @@ vpRobotWireFrameSimulator::vpRobotWireFrameSimulator():vpWireFrameSimulator(), v #if defined(VISP_HAVE_DISPLAY) display.init(I, 0, 0,"The External view"); #endif - robotStop = false; - jointLimit = false; - displayBusy = false; - displayType = MODEL_3D; - displayAllowed = true; - singularityManagement = true; - robotArms = NULL; - - constantSamplingTimeMode = false; - setVelocityCalled = false; - - verbose_ = false; + //pid_t pid = getpid(); // setpriority (PRIO_PROCESS, pid, 19); } /*! Default constructor. - \param display : When true, enables the display of the external view. + \param do_display : When true, enables the display of the external view. */ -vpRobotWireFrameSimulator::vpRobotWireFrameSimulator(bool display) : vpWireFrameSimulator(), vpRobotSimulator() +vpRobotWireFrameSimulator::vpRobotWireFrameSimulator(bool do_display) + : vpWireFrameSimulator(), vpRobotSimulator(), + I(), tcur(0), tprev(0), robotArms(NULL), size_fMi(8), fMi(NULL), artCoord(), artVel(), velocity(), +#if defined(_WIN32) +#elif defined(VISP_HAVE_PTHREAD) + thread(), attr(), +#endif + /* thread(), attr(), */ mutex_fMi(), mutex_artVel(), mutex_artCoord(), mutex_velocity(), mutex_display(), + displayBusy(false), robotStop(false), jointLimit(false), jointLimitArt(false), singularityManagement(true), + cameraParam(), +#if defined(VISP_HAVE_DISPLAY) + display(), +#endif + displayType(MODEL_3D), displayAllowed(do_display), constantSamplingTimeMode(false), + setVelocityCalled(false), verbose_(false) { setSamplingTime(0.010); velocity.resize(6); I.resize(480,640); I = 255; - displayAllowed = display; #if defined(VISP_HAVE_DISPLAY) - if (display) + if (do_display) this->display.init(I, 0, 0,"The External view"); #endif - robotStop = false; - jointLimit = false; - displayBusy = false; - displayType = MODEL_3D; - singularityManagement = true; - robotArms = NULL; - - constantSamplingTimeMode = false; - setVelocityCalled = false; - + //pid_t pid = getpid(); // setpriority (PRIO_PROCESS, pid, 19); } @@ -121,15 +128,15 @@ vpRobotWireFrameSimulator::~vpRobotWireFrameSimulator() It exists several default scenes you can use. Use the vpSceneObject and the vpSceneDesiredObject attributes to use them in this method. The corresponding files are stored in the "data" folder which is in the ViSP build directory. \param obj : Type of scene used to display the object at the current position. - \param desiredObject : Type of scene used to display the object at the desired pose (in the internal view). + \param desired_object : Type of scene used to display the object at the desired pose (in the internal view). */ void -vpRobotWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desiredObject) +vpRobotWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desired_object) { if(displayCamera){ free_Bound_scene (&(this->camera)); } - vpWireFrameSimulator::initScene(obj, desiredObject); + vpWireFrameSimulator::initScene(obj, desired_object); if(displayCamera){ free_Bound_scene (&(this->camera)); } @@ -144,15 +151,15 @@ vpRobotWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesi It is also possible to use a vrml (.wrl) file. \param obj : Path to the scene file you want to use. - \param desiredObject : Path to the scene file you want to use. + \param desired_object : Path to the scene file you want to use. */ void -vpRobotWireFrameSimulator::initScene(const char* obj, const char* desiredObject) +vpRobotWireFrameSimulator::initScene(const char* obj, const char* desired_object) { if(displayCamera){ free_Bound_scene (&(this->camera)); } - vpWireFrameSimulator::initScene(obj, desiredObject); + vpWireFrameSimulator::initScene(obj, desired_object); if(displayCamera){ free_Bound_scene (&(this->camera)); } @@ -206,12 +213,12 @@ vpRobotWireFrameSimulator::initScene(const char* obj) According to the initialisation method you used, the current position and maybee the desired position of the object are displayed. - \param I : The image where the internal view is displayed. + \param I_ : The image where the internal view is displayed. \warning : The objects are displayed thanks to overlays. The image I is not modified. */ void -vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I) +vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I_) { if (!sceneInitialized) @@ -224,13 +231,13 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I) if( (std::fabs(px_int-1.) > vpMath::maximum(px_int,1.)*std::numeric_limits<double>::epsilon()) && (std::fabs(py_int-1) > vpMath::maximum(py_int,1.)*std::numeric_limits<double>::epsilon())) { - u = (double)I.getWidth()/(2*px_int); - v = (double)I.getHeight()/(2*py_int); + u = (double)I_.getWidth()/(2*px_int); + v = (double)I_.getHeight()/(2*py_int); } else { - u = (double)I.getWidth()/(vpMath::minimum(I.getWidth(),I.getHeight())); - v = (double)I.getHeight()/(vpMath::minimum(I.getWidth(),I.getHeight())); + u = (double)I_.getWidth()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); + v = (double)I_.getHeight()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); } float o44c[4][4],o44cd[4][4],x,y,z; @@ -255,7 +262,7 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I) add_vwstack ("start","vup", o44c[1][0],o44c[1][1],o44c[1][2]); add_vwstack ("start","window", -u, u, -v, v); if (displayObject) - display_scene(id,this->scene,I, curColor); + display_scene(id,this->scene,I_, curColor); add_vwstack ("start","cop", o44cd[3][0],o44cd[3][1],o44cd[3][2]); x = o44cd[2][0] + o44cd[3][0]; @@ -267,8 +274,8 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I) add_vwstack ("start","window", -u, u, -v, v); if (displayDesiredObject) { - if (desiredObject == D_TOOL) display_scene(o44cd,desiredScene,I, vpColor::red); - else display_scene(id,desiredScene,I, desColor); + if (desiredObject == D_TOOL) display_scene(o44cd,desiredScene,I_, vpColor::red); + else display_scene(id,desiredScene,I_, desColor); } delete[] fMit; set_displayBusy(false); @@ -279,12 +286,12 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<vpRGBa> &I) According to the initialisation method you used, the current position and maybee the desired position of the object are displayed. - \param I : The image where the internal view is displayed. + \param I_ : The image where the internal view is displayed. \warning : The objects are displayed thanks to overlays. The image I is not modified. */ void -vpRobotWireFrameSimulator::getInternalView(vpImage<unsigned char> &I) +vpRobotWireFrameSimulator::getInternalView(vpImage<unsigned char> &I_) { if (!sceneInitialized) @@ -302,8 +309,8 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<unsigned char> &I) } else { - u = (double)I.getWidth()/(vpMath::minimum(I.getWidth(),I.getHeight())); - v = (double)I.getHeight()/(vpMath::minimum(I.getWidth(),I.getHeight())); + u = (double)I_.getWidth()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); + v = (double)I_.getHeight()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); } float o44c[4][4],o44cd[4][4],x,y,z; @@ -329,7 +336,7 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<unsigned char> &I) add_vwstack ("start","window", -u, u, -v, v); if (displayObject) { - display_scene(id,this->scene,I, curColor); + display_scene(id,this->scene,I_, curColor); } add_vwstack ("start","cop", o44cd[3][0],o44cd[3][1],o44cd[3][2]); @@ -342,8 +349,8 @@ vpRobotWireFrameSimulator::getInternalView(vpImage<unsigned char> &I) add_vwstack ("start","window", -u, u, -v, v); if (displayDesiredObject) { - if (desiredObject == D_TOOL) display_scene(o44cd,desiredScene,I, vpColor::red); - else display_scene(id,desiredScene,I, desColor); + if (desiredObject == D_TOOL) display_scene(o44cd,desiredScene,I_, vpColor::red); + else display_scene(id,desiredScene,I_, desColor); } delete[] fMit; set_displayBusy(false); diff --git a/src/robot/simulator-robot/vpRobotWireFrameSimulator.h b/src/robot/simulator-robot/vpRobotWireFrameSimulator.h index 106f7c62837e39dc4d40a13581e9c3e37612b4a1..7a4435ecc90fed61dabe41cc688938e2bf35ae55 100644 --- a/src/robot/simulator-robot/vpRobotWireFrameSimulator.h +++ b/src/robot/simulator-robot/vpRobotWireFrameSimulator.h @@ -3,7 +3,7 @@ * $Id: vpRobotWireFrameSimulator.h 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,10 +51,10 @@ -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) #include <cmath> // std::fabs #include <limits> // numeric_limits -#if defined(WIN32) +#if defined(_WIN32) # include <windows.h> #elif defined(VISP_HAVE_PTHREAD) # include <pthread.h> @@ -121,14 +121,14 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu /*! The velocity in the current frame (articular, camera or reference)*/ vpColVector velocity; - #if defined(WIN32) - HANDLE hThread; - HANDLE mutex_fMi; +#if defined(_WIN32) + HANDLE hThread; + HANDLE mutex_fMi; HANDLE mutex_artVel; HANDLE mutex_artCoord; HANDLE mutex_velocity; HANDLE mutex_display; - #elif defined(VISP_HAVE_PTHREAD) +#elif defined(VISP_HAVE_PTHREAD) pthread_t thread; pthread_attr_t attr; pthread_mutex_t mutex_fMi; @@ -136,7 +136,7 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu pthread_mutex_t mutex_artCoord; pthread_mutex_t mutex_velocity; pthread_mutex_t mutex_display; - #endif +#endif bool displayBusy; @@ -260,11 +260,11 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu /*! Set the desired position of the robot's camera relative to the object. - \param cdMo : The desired pose of the camera. + \param cdMo_ : The desired pose of the camera. */ - void setDesiredCameraPosition(const vpHomogeneousMatrix cdMo) + void setDesiredCameraPosition(const vpHomogeneousMatrix cdMo_) { - this->vpWireFrameSimulator::setDesiredCameraPosition(cdMo); + this->vpWireFrameSimulator::setDesiredCameraPosition(cdMo_); } /*! @@ -276,11 +276,11 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu /*! Set the external camera point of view. - \param camMf : The pose of the external camera relative to the world reference frame. + \param camMf_ : The pose of the external camera relative to the world reference frame. */ - void setExternalCameraPosition(const vpHomogeneousMatrix camMf) + void setExternalCameraPosition(const vpHomogeneousMatrix camMf_) { - this->vpWireFrameSimulator::setExternalCameraPosition(camMf); + this->vpWireFrameSimulator::setExternalCameraPosition(camMf_); } /*! Specify the thickness of the graphics drawings. @@ -321,15 +321,15 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu /*! Set the pose between the object and the fixed world frame. - \param fMo : The pose between the object and the fixed world frame. + \param fMo_ : The pose between the object and the fixed world frame. */ - void set_fMo(const vpHomogeneousMatrix &fMo) {this->fMo = fMo;} + void set_fMo(const vpHomogeneousMatrix &fMo_) {this->fMo = fMo_;} protected: /*! Function used to launch the thread which moves the robot. */ - #if defined(WIN32) + #if defined(_WIN32) static DWORD WINAPI launcher( LPVOID lpParam ) { ((vpRobotWireFrameSimulator *)lpParam)->updateArticularPosition(); @@ -357,7 +357,7 @@ class VISP_EXPORT vpRobotWireFrameSimulator : protected vpWireFrameSimulator, pu void initDisplay() {;} virtual void initArms() = 0; - #if defined(WIN32) + #if defined(_WIN32) vpColVector get_artCoord() const { WaitForSingleObject(mutex_artCoord,INFINITE); vpColVector artCoordTmp (6); diff --git a/src/robot/simulator-robot/vpSimulatorAfma6.cpp b/src/robot/simulator-robot/vpSimulatorAfma6.cpp index b318cab99d31ab7c5a76d92b6c55c5ee054b9272..2a380134e58ea183f76f85b2f380885b2cb8a09a 100644 --- a/src/robot/simulator-robot/vpSimulatorAfma6.cpp +++ b/src/robot/simulator-robot/vpSimulatorAfma6.cpp @@ -3,7 +3,7 @@ * $Id: vpSimulatorAfma6.cpp 2595 2010-06-02 08:50:59Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,7 +42,7 @@ #include <visp/vpConfig.h> -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) #include <visp/vpSimulatorAfma6.h> #include <visp/vpTime.h> #include <visp/vpImagePoint.h> @@ -59,14 +59,17 @@ const double vpSimulatorAfma6::defaultPositioningVelocity = 25.0; /*! Basic constructor */ -vpSimulatorAfma6::vpSimulatorAfma6():vpRobotWireFrameSimulator(), vpAfma6() +vpSimulatorAfma6::vpSimulatorAfma6() + : vpRobotWireFrameSimulator(), vpAfma6(), + q_prev_getdis(), first_time_getdis(true), positioningVelocity(defaultPositioningVelocity), + zeroPos(), reposPos(), toolCustom(false), arm_dir() { init(); initDisplay(); tcur = vpTime::measureTimeMs(); - #if defined(WIN32) + #if defined(_WIN32) mutex_fMi = CreateMutex(NULL,FALSE,NULL); mutex_artVel = CreateMutex(NULL,FALSE,NULL); mutex_artCoord = CreateMutex(NULL,FALSE,NULL); @@ -101,17 +104,20 @@ vpSimulatorAfma6::vpSimulatorAfma6():vpRobotWireFrameSimulator(), vpAfma6() /*! Constructor used to enable or disable the external view of the robot. - \param display : When true, enables the display of the external view. + \param do_display : When true, enables the display of the external view. */ -vpSimulatorAfma6::vpSimulatorAfma6(bool display):vpRobotWireFrameSimulator(display) +vpSimulatorAfma6::vpSimulatorAfma6(bool do_display) + : vpRobotWireFrameSimulator(do_display), + q_prev_getdis(), first_time_getdis(true), positioningVelocity(defaultPositioningVelocity), + zeroPos(), reposPos(), toolCustom(false), arm_dir() { init(); initDisplay(); tcur = vpTime::measureTimeMs(); - #if defined(WIN32) + #if defined(_WIN32) mutex_fMi = CreateMutex(NULL,FALSE,NULL); mutex_artVel = CreateMutex(NULL,FALSE,NULL); mutex_artCoord = CreateMutex(NULL,FALSE,NULL); @@ -150,7 +156,7 @@ vpSimulatorAfma6::~vpSimulatorAfma6() { robotStop = true; - #if defined(WIN32) + #if defined(_WIN32) WaitForSingleObject(hThread,INFINITE); CloseHandle(hThread); CloseHandle(mutex_fMi); @@ -276,16 +282,22 @@ vpSimulatorAfma6::initDisplay() \param tool : Tool to use. Note that the generic camera is not handled. - \param projModel : Projection model associated to the camera. + \param proj_model : Projection model associated to the camera. \sa vpCameraParameters, init() */ void vpSimulatorAfma6::init (vpAfma6::vpAfma6ToolType tool, - vpCameraParameters::vpCameraParametersProjType projModel) + vpCameraParameters::vpCameraParametersProjType proj_model) { - this->projModel = projModel; - + this->projModel = proj_model; + unsigned int name_length = 30; // the size of this kind of string "/afma6_tool_vacuum.bnd" + if (arm_dir.size() > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + unsigned int full_length = (unsigned int)arm_dir.size() + name_length; + if (full_length > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + // Use here default values of the robot constant parameters. switch (tool) { case vpAfma6::TOOL_CCMOP: { @@ -302,11 +314,12 @@ vpSimulatorAfma6::init (vpAfma6::vpAfma6ToolType tool, { while (get_displayBusy()) vpTime::wait(2); free_Bound_scene (&(robotArms[5])); - char name_arm[FILENAME_MAX]; + char *name_arm = new char [full_length]; strcpy(name_arm, arm_dir.c_str()); strcat(name_arm,"/afma6_tool_ccmop.bnd"); set_scene(name_arm, robotArms+5, 1.0); set_displayBusy(false); + delete [] name_arm; } break; } @@ -324,11 +337,12 @@ vpSimulatorAfma6::init (vpAfma6::vpAfma6ToolType tool, { while (get_displayBusy()) vpTime::wait(2); free_Bound_scene (&(robotArms[5])); - char name_arm[FILENAME_MAX]; + char *name_arm = new char [full_length]; strcpy(name_arm, arm_dir.c_str()); strcat(name_arm,"/afma6_tool_gripper.bnd"); set_scene(name_arm, robotArms+5, 1.0); set_displayBusy(false); + delete [] name_arm; } break; } @@ -346,11 +360,14 @@ vpSimulatorAfma6::init (vpAfma6::vpAfma6ToolType tool, { while (get_displayBusy()) vpTime::wait(2); free_Bound_scene (&(robotArms[5])); - char name_arm[FILENAME_MAX]; + + char *name_arm = new char [full_length]; + strcpy(name_arm, arm_dir.c_str()); strcat(name_arm,"/afma6_tool_vacuum.bnd"); set_scene(name_arm, robotArms+5, 1.0); set_displayBusy(false); + delete [] name_arm; } break; } @@ -412,6 +429,7 @@ vpSimulatorAfma6::getCameraParameters (vpCameraParameters &cam, } break; } + case vpAfma6::TOOL_GENERIC_CAMERA: case vpAfma6::TOOL_VACUUM: { std::cout << "The generic camera is not handled in vpSimulatorAfma6.cpp" << std::endl; break; @@ -427,30 +445,30 @@ vpSimulatorAfma6::getCameraParameters (vpCameraParameters &cam, Get the current intrinsic camera parameters obtained by calibration. \param cam : In output, camera parameters to fill. - \param I : A B&W image send by the current camera in use. + \param I_ : A B&W image send by the current camera in use. \warning The image size must be : 640x480 ! */ void vpSimulatorAfma6::getCameraParameters (vpCameraParameters &cam, - const vpImage<unsigned char> &I) + const vpImage<unsigned char> &I_) { - getCameraParameters(cam,I.getWidth(),I.getHeight()); + getCameraParameters(cam,I_.getWidth(),I_.getHeight()); } /*! Get the current intrinsic camera parameters obtained by calibration. \param cam : In output, camera parameters to fill. - \param I : A B&W image send by the current camera in use. + \param I_ : A B&W image send by the current camera in use. \warning The image size must be : 640x480 ! */ void vpSimulatorAfma6::getCameraParameters (vpCameraParameters &cam, - const vpImage<vpRGBa> &I) + const vpImage<vpRGBa> &I_) { - getCameraParameters(cam,I.getWidth(),I.getHeight()); + getCameraParameters(cam,I_.getWidth(),I_.getHeight()); } @@ -722,7 +740,7 @@ vpSimulatorAfma6::compute_fMi() // fMit[7] = fMit[6] * cMe; vpAfma6::get_fMc(q,fMit[7]); - #if defined(WIN32) + #if defined(_WIN32) WaitForSingleObject(mutex_fMi,INFINITE); for (int i = 0; i < 8; i++) fMi[i] = fMit[i]; @@ -970,24 +988,24 @@ vpSimulatorAfma6::computeArticularVelocity() { case vpRobot::CAMERA_FRAME : { - vpMatrix eJe; + vpMatrix eJe_; vpVelocityTwistMatrix eVc(_eMc); - vpAfma6::get_eJe(articularCoordinates,eJe); - eJe = eJe.pseudoInverse(); + vpAfma6::get_eJe(articularCoordinates,eJe_); + eJe_ = eJe_.pseudoInverse(); if (singularityManagement) - singularityTest(articularCoordinates,eJe); - articularVelocity = eJe*eVc*velocityframe; + singularityTest(articularCoordinates,eJe_); + articularVelocity = eJe_*eVc*velocityframe; set_artVel (articularVelocity); break; } case vpRobot::REFERENCE_FRAME : { - vpMatrix fJe; - vpAfma6::get_fJe(articularCoordinates,fJe); - fJe = fJe.pseudoInverse(); + vpMatrix fJe_; + vpAfma6::get_fJe(articularCoordinates,fJe_); + fJe_ = fJe_.pseudoInverse(); if (singularityManagement) - singularityTest(articularCoordinates,fJe); - articularVelocity = fJe*velocityframe; + singularityTest(articularCoordinates,fJe_); + articularVelocity = fJe_*velocityframe; set_artVel (articularVelocity); break; } @@ -1095,10 +1113,10 @@ vpSimulatorAfma6::getVelocity (const vpRobot::vpControlFrameType frame, vpColVec { case vpRobot::CAMERA_FRAME : { - vpMatrix eJe; + vpMatrix eJe_; vpVelocityTwistMatrix cVe(_eMc); - vpAfma6::get_eJe(articularCoordinates,eJe); - vel = cVe*eJe*articularVelocity; + vpAfma6::get_eJe(articularCoordinates,eJe_); + vel = cVe*eJe_*articularVelocity; break ; } case vpRobot::ARTICULAR_FRAME : @@ -1108,9 +1126,9 @@ vpSimulatorAfma6::getVelocity (const vpRobot::vpControlFrameType frame, vpColVec } case vpRobot::REFERENCE_FRAME : { - vpMatrix fJe; - vpAfma6::get_fJe(articularCoordinates,fJe); - vel = fJe*articularVelocity; + vpMatrix fJe_; + vpAfma6::get_fJe(articularCoordinates,fJe_); + vel = fJe_*articularVelocity; break ; } case vpRobot::MIXT_FRAME : @@ -1126,6 +1144,29 @@ vpSimulatorAfma6::getVelocity (const vpRobot::vpControlFrameType frame, vpColVec } } +/*! + Get the robot time stamped velocities. + + \param frame : Frame in wich velocities are mesured. + + \param vel : Measured velocities. Translations are expressed in m/s + and rotations in rad/s. + + \param timestamp : Unix time in second since January 1st 1970. + + \warning In camera frame, reference frame and mixt frame, the representation + of the rotation is ThetaU. In that cases, \f$velocity = [\dot x, \dot y, \dot + z, \dot {\theta U}_x, \dot {\theta U}_y, \dot {\theta U}_z]\f$. + + \sa getVelocity(const vpRobot::vpControlFrameType frame, vpColVector & vel) +*/ +void +vpSimulatorAfma6::getVelocity (const vpRobot::vpControlFrameType frame, vpColVector & vel, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getVelocity(frame, vel); +} + /*! Get the robot velocities. @@ -1171,12 +1212,33 @@ int main() vpColVector vpSimulatorAfma6::getVelocity (vpRobot::vpControlFrameType frame) { - vpColVector velocity(6); - getVelocity (frame, velocity); + vpColVector vel(6); + getVelocity (frame, vel); - return velocity; + return vel; } +/*! + Get the time stamped robot velocities. + + \param frame : Frame in wich velocities are mesured. + + \param timestamp : Unix time in second since January 1st 1970. + + \return Measured velocities. Translations are expressed in m/s + and rotations in rad/s. + + \sa getVelocity(vpRobot::vpControlFrameType frame) +*/ +vpColVector +vpSimulatorAfma6::getVelocity (vpRobot::vpControlFrameType frame, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + vpColVector vel(6); + getVelocity (frame, vel); + + return vel; +} void vpSimulatorAfma6::findHighestPositioningSpeed(vpColVector &q) @@ -1299,10 +1361,10 @@ vpSimulatorAfma6::setPosition(const vpRobot::vpControlFrameType frame,const vpCo vpRotationMatrix cRc2(rxyz); vpHomogeneousMatrix cMc2(txyz, cRc2); - vpHomogeneousMatrix fMc; - vpAfma6::get_fMc(articularCoordinates, fMc); + vpHomogeneousMatrix fMc_; + vpAfma6::get_fMc(articularCoordinates, fMc_); - vpHomogeneousMatrix fMc2 = fMc * cMc2; + vpHomogeneousMatrix fMc2 = fMc_ * cMc2; do { @@ -1372,13 +1434,13 @@ vpSimulatorAfma6::setPosition(const vpRobot::vpControlFrameType frame,const vpCo } vpRotationMatrix fRc(rxyz); - vpHomogeneousMatrix fMc(txyz, fRc); + vpHomogeneousMatrix fMc_(txyz, fRc); do { articularCoordinates = get_artCoord(); qdes = articularCoordinates; - nbSol = getInverseKinematics(fMc, qdes, true, verbose_); + nbSol = getInverseKinematics(fMc_, qdes, true, verbose_); setVelocityCalled = true; if (nbSol > 0) { @@ -1606,8 +1668,8 @@ int main() } \endcode - \sa setPosition(const vpRobot::vpControlFrameType frame, const - vpColVector & r) + \sa getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp) + \sa setPosition(const vpRobot::vpControlFrameType frame, const vpColVector & r) */ void @@ -1631,20 +1693,20 @@ vpSimulatorAfma6::getPosition(const vpRobot::vpControlFrameType frame, vpColVect case vpRobot::REFERENCE_FRAME: { - vpHomogeneousMatrix fMc; - vpAfma6::get_fMc (get_artCoord(), fMc); + vpHomogeneousMatrix fMc_; + vpAfma6::get_fMc (get_artCoord(), fMc_); vpRotationMatrix fRc; - fMc.extract(fRc); + fMc_.extract(fRc); vpRxyzVector rxyz(fRc); vpTranslationVector txyz; - fMc.extract(txyz); + fMc_.extract(txyz); for (unsigned int i=0; i <3; i++) { q[i] = txyz[i]; - q[i+3] = rxyz[i]; + q[i+3] = rxyz[i]; } break ; } @@ -1660,6 +1722,39 @@ vpSimulatorAfma6::getPosition(const vpRobot::vpControlFrameType frame, vpColVect } } +/*! + + Get the current time stamped position of the robot. + + \param frame : Control frame type in which to get the position, either : + - in the camera cartesien frame, + - joint (articular) coordinates of each axes + - in a reference or fixed cartesien frame attached to the robot base + - in a mixt cartesien frame (translation in reference + frame, and rotation in camera frame) + + \param q : Measured position of the robot: + - in camera cartesien frame, a 6 dimension vector, set to 0. + + - in articular, a 6 dimension vector corresponding to the joint + position of each dof in radians. + + - in reference frame, a 6 dimension vector, the first 3 values correspond to + the translation tx, ty, tz in meters (like a vpTranslationVector), and the + last 3 values to the rx, ry, rz rotation (like a vpRxyzVector). The code + below show how to convert this position into a vpHomogenousMatrix: + + \param timestamp : Unix time in second since January 1st 1970. + + \sa getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q) + */ +void +vpSimulatorAfma6::getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getPosition(frame, q); +} + /*! Get the current position of the robot. @@ -1672,8 +1767,7 @@ vpSimulatorAfma6::getPosition(const vpRobot::vpControlFrameType frame, vpColVect \sa getPosition(const vpRobot::vpControlFrameType frame, vpColVector &) */ void -vpSimulatorAfma6::getPosition (const vpRobot::vpControlFrameType frame, - vpPoseVector &position) +vpSimulatorAfma6::getPosition (const vpRobot::vpControlFrameType frame, vpPoseVector &position) { vpColVector posRxyz; //recupere position en Rxyz @@ -1690,6 +1784,24 @@ vpSimulatorAfma6::getPosition (const vpRobot::vpControlFrameType frame, } } +/*! + + Get the current time stamped position of the robot. + + Similar as getPosition(const vpRobot::vpControlFrameType frame, vpColVector &, double &) + + The difference is here that the position is returned using a ThetaU + representation. + + */ +void +vpSimulatorAfma6::getPosition(const vpRobot::vpControlFrameType frame, + vpPoseVector &position, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getPosition(frame, position); +} + /*! This method enables to set the minimum and maximum joint limits for all the six axis of the robot. The three first values have to be given in meter and the others in radian. @@ -1943,7 +2055,6 @@ robot.setPosition(vpRobot::ARTICULAR_FRAME, q); // Move to the joint position bool vpSimulatorAfma6::readPosFile(const char *filename, vpColVector &q) { - FILE * fd ; fd = fopen(filename, "r") ; if (fd == NULL) @@ -1958,15 +2069,16 @@ vpSimulatorAfma6::readPosFile(const char *filename, vpColVector &q) // Saut des lignes commencant par # if (fgets (line, FILENAME_MAX, fd) != NULL) { if ( strncmp (line, "#", 1) != 0) { - // La ligne n'est pas un commentaire - if ( strncmp (line, head, sizeof(head)-1) == 0) { - sortie = true; // Position robot trouvee. - } -// else -// return (false); // fin fichier sans position robot. + // La ligne n'est pas un commentaire + if ( strncmp (line, head, sizeof(head)-1) == 0) { + sortie = true; // Position robot trouvee. + } + // else + // return (false); // fin fichier sans position robot. } } else { + fclose(fd) ; return (false); /* fin fichier */ } @@ -1975,10 +2087,14 @@ vpSimulatorAfma6::readPosFile(const char *filename, vpColVector &q) // Lecture des positions q.resize(njoint); - sscanf(line, "%s %lf %lf %lf %lf %lf %lf", - dummy, - &q[0], &q[1], &q[2], - &q[3], &q[4], &q[5]); + int ret = sscanf(line, "%s %lf %lf %lf %lf %lf %lf", + dummy, + &q[0], &q[1], &q[2], &q[3], &q[4], &q[5]); + + if (ret != 7) { + fclose(fd) ; + return false; + } // converts rotations from degrees into radians //q.deg2rad(); @@ -2105,15 +2221,15 @@ vpSimulatorAfma6::get_cVe(vpVelocityTwistMatrix &cVe) To compute \f$^e{\bf J}_e\f$, we communicate with the low level controller to get the joint position of the robot. - \param eJe : Robot jacobian \f$^e{\bf J}_e\f$ expressed in the + \param eJe_ : Robot jacobian \f$^e{\bf J}_e\f$ expressed in the end-effector frame. */ void -vpSimulatorAfma6::get_eJe(vpMatrix &eJe) +vpSimulatorAfma6::get_eJe(vpMatrix &eJe_) { try { - vpAfma6::get_eJe(get_artCoord(), eJe) ; + vpAfma6::get_eJe(get_artCoord(), eJe_) ; } catch(...) { @@ -2129,16 +2245,16 @@ vpSimulatorAfma6::get_eJe(vpMatrix &eJe) To compute \f$^f{\bf J}_e\f$, we communicate with the low level controller to get the joint position of the robot. - \param fJe : Robot jacobian \f$^f{\bf J}_e\f$ expressed in the + \param fJe_ : Robot jacobian \f$^f{\bf J}_e\f$ expressed in the reference frame. */ void -vpSimulatorAfma6::get_fJe(vpMatrix &fJe) +vpSimulatorAfma6::get_fJe(vpMatrix &fJe_) { try { vpColVector articularCoordinates = get_artCoord(); - vpAfma6::get_fJe(articularCoordinates, fJe) ; + vpAfma6::get_fJe(articularCoordinates, fJe_) ; } catch(...) { @@ -2160,7 +2276,7 @@ vpSimulatorAfma6::stopMotion() stop = 0; set_artVel(stop); set_velocity(stop); - setRobotState (vpRobot::STATE_STOP); + vpRobot::setRobotState (vpRobot::STATE_STOP); } @@ -2182,26 +2298,39 @@ void vpSimulatorAfma6::initArms() { // set scene_dir from #define VISP_SCENE_DIR if it exists - std::string scene_dir; + std::string scene_dir_; if (vpIoTools::checkDirectory(VISP_SCENES_DIR) == true) // directory exists - scene_dir = VISP_SCENES_DIR; + scene_dir_ = VISP_SCENES_DIR; else { try { - scene_dir = vpIoTools::getenv("VISP_SCENES_DIR"); - std::cout << "The simulator uses data from VISP_SCENES_DIR=" << scene_dir << std::endl; + scene_dir_ = vpIoTools::getenv("VISP_SCENES_DIR"); + std::cout << "The simulator uses data from VISP_SCENES_DIR=" << scene_dir_ << std::endl; } catch (...) { std::cout << "Cannot get VISP_SCENES_DIR environment variable" << std::endl; } } - char name_cam[FILENAME_MAX]; + unsigned int name_length = 30; // the size of this kind of string "/afma6_arm2.bnd" + if (scene_dir_.size() > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + unsigned int full_length = (unsigned int)scene_dir_.size() + name_length; + if (full_length > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + + char *name_cam = new char [full_length]; - strcpy(name_cam, scene_dir.c_str()); + strcpy(name_cam, scene_dir_.c_str()); strcat(name_cam,"/camera.bnd"); set_scene(name_cam,&camera,cameraFactor); - char name_arm[FILENAME_MAX]; + if (arm_dir.size() > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + full_length = (unsigned int)arm_dir.size() + name_length; + if (full_length > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Afma6 simulator"); + + char *name_arm = new char [full_length]; strcpy(name_arm, arm_dir.c_str()); strcat(name_arm,"/afma6_gate.bnd"); set_scene(name_arm, robotArms, 1.0); @@ -2250,14 +2379,17 @@ vpSimulatorAfma6::initArms() // sceneInitialized = true; // displayObject = true; displayCamera = true; + + delete [] name_cam; + delete [] name_arm; } void -vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I) +vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I_) { bool changed = false; - vpHomogeneousMatrix displacement = navigation(I,changed); + vpHomogeneousMatrix displacement = navigation(I_,changed); //if (displacement[2][3] != 0) if (std::fabs(displacement[2][3]) > std::numeric_limits<double>::epsilon()) @@ -2274,13 +2406,13 @@ vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I) if( (std::fabs(px_ext-1.) > vpMath::maximum(px_ext,1.)*std::numeric_limits<double>::epsilon()) && (std::fabs(py_ext-1) > vpMath::maximum(py_ext,1.)*std::numeric_limits<double>::epsilon())) { - u = (double)I.getWidth()/(2*px_ext); - v = (double)I.getHeight()/(2*py_ext); + u = (double)I_.getWidth()/(2*px_ext); + v = (double)I_.getHeight()/(2*py_ext); } else { - u = (double)I.getWidth()/(vpMath::minimum(I.getWidth(),I.getHeight())); - v = (double)I.getHeight()/(vpMath::minimum(I.getWidth(),I.getHeight())); + u = (double)I_.getWidth()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); + v = (double)I_.getHeight()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); } float w44o[4][4],w44cext[4][4],x,y,z; @@ -2300,22 +2432,22 @@ vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I) get_fMi(fMit); vp2jlc_matrix(vpHomogeneousMatrix(0,0,0,0,0,0),w44o); - display_scene(w44o,robotArms[0],I, curColor); + display_scene(w44o,robotArms[0],I_, curColor); vp2jlc_matrix(fMit[0],w44o); - display_scene(w44o,robotArms[1],I, curColor); + display_scene(w44o,robotArms[1],I_, curColor); vp2jlc_matrix(fMit[2],w44o); - display_scene(w44o,robotArms[2],I, curColor); + display_scene(w44o,robotArms[2],I_, curColor); vp2jlc_matrix(fMit[3],w44o); - display_scene(w44o,robotArms[3],I, curColor); + display_scene(w44o,robotArms[3],I_, curColor); vp2jlc_matrix(fMit[4],w44o); - display_scene(w44o,robotArms[4],I, curColor); + display_scene(w44o,robotArms[4],I_, curColor); vp2jlc_matrix(fMit[5],w44o); - display_scene(w44o,robotArms[5],I, curColor); + display_scene(w44o,robotArms[5],I_, curColor); if (displayCamera) { @@ -2324,13 +2456,13 @@ vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I) cMe = cMe.inverse(); cMe = fMit[6] * cMe; vp2jlc_matrix(cMe,w44o); - display_scene(w44o,camera, I, camColor); + display_scene(w44o,camera, I_, camColor); } if (displayObject) { vp2jlc_matrix(fMo,w44o); - display_scene(w44o,scene,I, curColor); + display_scene(w44o,scene,I_, curColor); } } @@ -2345,24 +2477,24 @@ vpSimulatorAfma6::getExternalImage(vpImage<vpRGBa> &I) \f${^f}{\bf M}_c = {^f}{\bf M}_o \; ({^c}{\bf M}_o)^{-1}\f$, and from the inverse kinematics set the joint positions \f${\bf q}\f$ that corresponds to the \f${^f}{\bf M}_c\f$ transformation. - \param cMo : the desired pose of the camera. + \param cMo_ : the desired pose of the camera. \return false if the robot kinematics is not able to reach the cMo position. */ bool -vpSimulatorAfma6::initialiseCameraRelativeToObject(const vpHomogeneousMatrix &cMo) +vpSimulatorAfma6::initialiseCameraRelativeToObject(const vpHomogeneousMatrix &cMo_) { vpColVector stop(6); bool status = true; stop = 0; set_artVel(stop); set_velocity(stop); - vpHomogeneousMatrix fMc; - fMc = fMo * cMo.inverse(); + vpHomogeneousMatrix fMc_; + fMc_ = fMo * cMo_.inverse(); vpColVector articularCoordinates = get_artCoord(); - int nbSol = getInverseKinematics(fMc, articularCoordinates, true, verbose_); + int nbSol = getInverseKinematics(fMc_, articularCoordinates, true, verbose_); if (nbSol == 0) { status = false; @@ -2389,10 +2521,10 @@ vpSimulatorAfma6::initialiseCameraRelativeToObject(const vpHomogeneousMatrix &cM \f${^f}{\bf M}_o = {^f}{\bf M}_c \; {^c}{\bf M}_o\f$ where \f$ {^f}{\bf M}_c = f({\bf q})\f$ with \f${\bf q}\f$ the robot joint position - \param cMo : the desired pose of the camera. + \param cMo_ : the desired pose of the camera. */ void -vpSimulatorAfma6::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix &cMo) +vpSimulatorAfma6::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix &cMo_) { vpColVector stop(6); stop = 0; @@ -2400,21 +2532,21 @@ vpSimulatorAfma6::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix &cM set_velocity(stop); vpHomogeneousMatrix fMit[8]; get_fMi(fMit); - fMo = fMit[7] * cMo; + fMo = fMit[7] * cMo_; } /*! This method enable to move the robot with respect to the initialized object. The robot trajectory is a straight line from the current position to the one corresponding to the desired pose (3D visual servoing). - \param cdMo : the desired pose of the camera wrt. the object + \param cdMo_ : the desired pose of the camera wrt. the object \param Iint : pointer to the image where the internal view is displayed \param errMax : maximum error to consider the pose is reached \return True is the pose is reached, False else */ bool -vpSimulatorAfma6::setPosition(const vpHomogeneousMatrix &cdMo, vpImage<unsigned char> *Iint, const double &errMax) +vpSimulatorAfma6::setPosition(const vpHomogeneousMatrix &cdMo_, vpImage<unsigned char> *Iint, const double &errMax) { // get rid of max velocity double vMax = getMaxTranslationVelocity(); @@ -2430,7 +2562,6 @@ vpSimulatorAfma6::setPosition(const vpHomogeneousMatrix &cdMo, vpImage<unsigned double t; vpVelocityTwistMatrix cVe; - vpMatrix eJe; unsigned int i,iter=0; while((iter++<300) & (err.euclideanNorm()>errMax)) @@ -2446,7 +2577,7 @@ vpSimulatorAfma6::setPosition(const vpHomogeneousMatrix &cdMo, vpImage<unsigned } // update pose error - cdMc = cdMo*get_cMo().inverse(); + cdMc = cdMo_*get_cMo().inverse(); cdMc.extract(cdRc); cdMc.extract(cdTc); cdTUc.buildFrom(cdRc); diff --git a/src/robot/simulator-robot/vpSimulatorAfma6.h b/src/robot/simulator-robot/vpSimulatorAfma6.h index f515bd4dd69df2176e477c33cbc9930cc6b74cec..c2e86813539d2f8938dff97e73d974f2f260ddee 100644 --- a/src/robot/simulator-robot/vpSimulatorAfma6.h +++ b/src/robot/simulator-robot/vpSimulatorAfma6.h @@ -3,7 +3,7 @@ * $Id: vpSimulatorAfma6.h 2598 2010-06-02 09:20:22Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,7 +52,7 @@ #include <string> -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) /*! \class vpSimulatorAfma6 @@ -209,11 +209,14 @@ public: void getDisplacement(const vpRobot::vpControlFrameType frame, vpColVector &displacement); void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q); - void getPosition (const vpRobot::vpControlFrameType frame, - vpPoseVector &position); + void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp); + void getPosition(const vpRobot::vpControlFrameType frame, vpPoseVector &position); + void getPosition(const vpRobot::vpControlFrameType frame, vpPoseVector &position, double ×tamp); double getPositioningVelocity (void){return positioningVelocity;} void getVelocity(const vpRobot::vpControlFrameType frame, vpColVector &q); + void getVelocity(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp); vpColVector getVelocity (const vpRobot::vpControlFrameType frame); + vpColVector getVelocity (const vpRobot::vpControlFrameType frame, double ×tamp); void get_cMe(vpHomogeneousMatrix &cMe); void get_cVe(vpVelocityTwistMatrix &cVe); @@ -240,7 +243,7 @@ public: const double pos5, const double pos6); void setPosition(const char *filename); - void setPositioningVelocity (const double velocity) {positioningVelocity = velocity;} + void setPositioningVelocity (const double vel) {positioningVelocity = vel;} bool setPosition(const vpHomogeneousMatrix &cdMo, vpImage<unsigned char> *Iint=NULL, const double &errMax = 0.001); vpRobot::vpRobotStateType setRobotState (const vpRobot::vpRobotStateType newState); @@ -255,7 +258,7 @@ protected: void findHighestPositioningSpeed(vpColVector &q); void getExternalImage(vpImage<vpRGBa> &I); inline void get_fMi(vpHomogeneousMatrix *fMit) { -#if defined(WIN32) +#if defined(_WIN32) WaitForSingleObject(mutex_fMi,INFINITE); for (int i = 0; i < 8; i++) fMit[i] = fMi[i]; diff --git a/src/robot/simulator-robot/vpSimulatorCamera.cpp b/src/robot/simulator-robot/vpSimulatorCamera.cpp index af70bb3c9bce50c1b1b7fd586419aa90f4766698..f88eda38bdf6005445906c2c37a8b9a57c3c4fcd 100644 --- a/src/robot/simulator-robot/vpSimulatorCamera.cpp +++ b/src/robot/simulator-robot/vpSimulatorCamera.cpp @@ -3,7 +3,7 @@ * $Id: vpSimulatorCamera.cpp 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,7 +57,7 @@ world frame and camera frame to identity. */ -vpSimulatorCamera::vpSimulatorCamera() +vpSimulatorCamera::vpSimulatorCamera() : wMc_() { init() ; } @@ -104,7 +104,7 @@ vpSimulatorCamera::~vpSimulatorCamera() */ void -vpSimulatorCamera::get_cVe(vpVelocityTwistMatrix &cVe) +vpSimulatorCamera::get_cVe(vpVelocityTwistMatrix &cVe) const { vpVelocityTwistMatrix cVe_; cVe = cVe_; @@ -114,13 +114,13 @@ vpSimulatorCamera::get_cVe(vpVelocityTwistMatrix &cVe) Get the robot jacobian expressed in the end-effector frame. For that simple robot the Jacobian is the identity. - \param eJe : A 6 by 6 matrix representing the robot jacobian \f$ {^e}{\bf + \param eJe_ : A 6 by 6 matrix representing the robot jacobian \f$ {^e}{\bf J}_e\f$ expressed in the end-effector frame. Yhis matrix is equal to identity. */ void -vpSimulatorCamera::get_eJe(vpMatrix &eJe) +vpSimulatorCamera::get_eJe(vpMatrix &eJe_) { - eJe = this->eJe ; + eJe_ = this->eJe ; } /*! diff --git a/src/robot/simulator-robot/vpSimulatorCamera.h b/src/robot/simulator-robot/vpSimulatorCamera.h index 942cc320d3451607b563663a09b74a5763221632..70f22cf6254fde534a0d37f79ad443e6635463f9 100644 --- a/src/robot/simulator-robot/vpSimulatorCamera.h +++ b/src/robot/simulator-robot/vpSimulatorCamera.h @@ -3,7 +3,7 @@ * $Id: vpSimulatorCamera.h 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -117,7 +117,7 @@ public: virtual ~vpSimulatorCamera() ; public: - void get_cVe(vpVelocityTwistMatrix &cVe); + void get_cVe(vpVelocityTwistMatrix &cVe) const; void get_eJe(vpMatrix &eJe); void getPosition(vpHomogeneousMatrix &wMc) const; diff --git a/src/robot/simulator-robot/vpSimulatorPioneer.cpp b/src/robot/simulator-robot/vpSimulatorPioneer.cpp index a3b3e7c2add16cbb40c81aaf00d5ba5475672c79..eb664593fbcc1813b66c7c14c1124aa948ba559f 100644 --- a/src/robot/simulator-robot/vpSimulatorPioneer.cpp +++ b/src/robot/simulator-robot/vpSimulatorPioneer.cpp @@ -3,7 +3,7 @@ * $Id: vpSimulatorPioneer.cpp 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,6 +58,7 @@ */ vpSimulatorPioneer::vpSimulatorPioneer() + : wMc_(), wMe_(), xm_(0), ym_(0), theta_(0) { init() ; } @@ -98,13 +99,13 @@ vpSimulatorPioneer::~vpSimulatorPioneer() Get the robot jacobian expressed in the end-effector frame. The jacobian expression is given in vpPioneer class. - \param eJe : A 6 by 2 matrix representing the robot jacobian \f$ {^e}{\bf + \param _eJe : A 6 by 2 matrix representing the robot jacobian \f$ {^e}{\bf J}_e\f$ expressed in the end-effector frame. */ void -vpSimulatorPioneer::get_eJe(vpMatrix &eJe) +vpSimulatorPioneer::get_eJe(vpMatrix &_eJe) { - eJe = vpUnicycle::get_eJe(); + _eJe = vpUnicycle::get_eJe(); } diff --git a/src/robot/simulator-robot/vpSimulatorPioneer.h b/src/robot/simulator-robot/vpSimulatorPioneer.h index d7854605b5e91d6130b402693de98c057d62e7eb..1357e2b72ec0ef148e537c1a5fdbc38ee8803370 100644 --- a/src/robot/simulator-robot/vpSimulatorPioneer.h +++ b/src/robot/simulator-robot/vpSimulatorPioneer.h @@ -3,7 +3,7 @@ * $Id: vpSimulatorPioneer.h 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/simulator-robot/vpSimulatorPioneerPan.cpp b/src/robot/simulator-robot/vpSimulatorPioneerPan.cpp index 791333f5b4164e02e522faff6ea5c6f2cf45bfab..25c82817dc4163674d6e5455da46ab7ae34b3576 100644 --- a/src/robot/simulator-robot/vpSimulatorPioneerPan.cpp +++ b/src/robot/simulator-robot/vpSimulatorPioneerPan.cpp @@ -3,7 +3,7 @@ * $Id: vpSimulatorPioneerPan.cpp 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,7 @@ setSamplingTime(). */ -vpSimulatorPioneerPan::vpSimulatorPioneerPan() +vpSimulatorPioneerPan::vpSimulatorPioneerPan() : wMc_(), wMm_(), xm_(0), ym_(0), theta_(0), q_pan_() { init() ; } @@ -107,13 +107,13 @@ vpSimulatorPioneerPan::~vpSimulatorPioneerPan() Get the robot jacobian expressed in the end-effector frame. The jacobian expression is given in vpPioneerPan class. - \param eJe : A 6 by 3 matrix representing the robot jacobian \f$ {^e}{\bf + \param _eJe : A 6 by 3 matrix representing the robot jacobian \f$ {^e}{\bf J}_e\f$ expressed in the end-effector frame. */ void -vpSimulatorPioneerPan::get_eJe(vpMatrix &eJe) +vpSimulatorPioneerPan::get_eJe(vpMatrix &_eJe) { - eJe = vpUnicycle::get_eJe(); + _eJe = vpUnicycle::get_eJe(); } /*! diff --git a/src/robot/simulator-robot/vpSimulatorPioneerPan.h b/src/robot/simulator-robot/vpSimulatorPioneerPan.h index c3facc8da855564082a12192bc7245bebedfabb3..d01e5d591d86b577dfdfeff6a3d4b767f1f7a86e 100644 --- a/src/robot/simulator-robot/vpSimulatorPioneerPan.h +++ b/src/robot/simulator-robot/vpSimulatorPioneerPan.h @@ -3,7 +3,7 @@ * $Id: vpSimulatorPioneerPan.h 2456 2010-01-07 10:33:12Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/robot/simulator-robot/vpSimulatorViper850.cpp b/src/robot/simulator-robot/vpSimulatorViper850.cpp index 91c966cfe70ddb81b27b5934a8b2e07416ba0bb8..9286b2a3ba374640d04ecad06b8f22258ecf93bc 100644 --- a/src/robot/simulator-robot/vpSimulatorViper850.cpp +++ b/src/robot/simulator-robot/vpSimulatorViper850.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSimulatorViper850.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpSimulatorViper850.cpp 5171 2015-01-15 15:11:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,21 +50,24 @@ #include <cmath> // std::fabs #include <limits> // numeric_limits #include <string> -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) const double vpSimulatorViper850::defaultPositioningVelocity = 25.0; /*! Basic constructor */ -vpSimulatorViper850::vpSimulatorViper850():vpRobotWireFrameSimulator(), vpViper850() +vpSimulatorViper850::vpSimulatorViper850() + : vpRobotWireFrameSimulator(), vpViper850(), + q_prev_getdis(), first_time_getdis(true), positioningVelocity(defaultPositioningVelocity), + zeroPos(), reposPos(), toolCustom(false), arm_dir() { init(); initDisplay(); tcur = vpTime::measureTimeMs(); - #if defined(WIN32) + #if defined(_WIN32) mutex_fMi = CreateMutex(NULL,FALSE,NULL); mutex_artVel = CreateMutex(NULL,FALSE,NULL); mutex_artCoord = CreateMutex(NULL,FALSE,NULL); @@ -99,17 +102,20 @@ vpSimulatorViper850::vpSimulatorViper850():vpRobotWireFrameSimulator(), vpViper8 /*! Constructor used to enable or disable the external view of the robot. - \param display : When true, enables the display of the external view. + \param do_display : When true, enables the display of the external view. */ -vpSimulatorViper850::vpSimulatorViper850(bool display):vpRobotWireFrameSimulator(display) +vpSimulatorViper850::vpSimulatorViper850(bool do_display) + : vpRobotWireFrameSimulator(do_display), + q_prev_getdis(), first_time_getdis(true), positioningVelocity(defaultPositioningVelocity), + zeroPos(), reposPos(), toolCustom(false), arm_dir() { init(); initDisplay(); tcur = vpTime::measureTimeMs(); - #if defined(WIN32) + #if defined(_WIN32) mutex_fMi = CreateMutex(NULL,FALSE,NULL); mutex_artVel = CreateMutex(NULL,FALSE,NULL); mutex_artCoord = CreateMutex(NULL,FALSE,NULL); @@ -148,7 +154,7 @@ vpSimulatorViper850::~vpSimulatorViper850() { robotStop = true; - #if defined(WIN32) + #if defined(_WIN32) WaitForSingleObject(hThread,INFINITE); CloseHandle(hThread); CloseHandle(mutex_fMi); @@ -276,15 +282,15 @@ vpSimulatorViper850::initDisplay() \param tool : Tool to use. - \param projModel : Projection model associated to the camera. + \param proj_model : Projection model associated to the camera. \sa vpCameraParameters, init() */ void vpSimulatorViper850::init (vpViper850::vpToolType tool, - vpCameraParameters::vpCameraParametersProjType projModel) + vpCameraParameters::vpCameraParametersProjType proj_model) { - this->projModel = projModel; + this->projModel = proj_model; // Use here default values of the robot constant parameters. switch (tool) { @@ -383,30 +389,30 @@ vpSimulatorViper850::getCameraParameters (vpCameraParameters &cam, Get the current intrinsic camera parameters obtained by calibration. \param cam : In output, camera parameters to fill. - \param I : A B&W image send by the current camera in use. + \param I_ : A B&W image send by the current camera in use. \warning The image size must be : 640x480 ! */ void vpSimulatorViper850::getCameraParameters (vpCameraParameters &cam, - const vpImage<unsigned char> &I) + const vpImage<unsigned char> &I_) { - getCameraParameters(cam,I.getWidth(),I.getHeight()); + getCameraParameters(cam,I_.getWidth(),I_.getHeight()); } /*! Get the current intrinsic camera parameters obtained by calibration. \param cam : In output, camera parameters to fill. - \param I : A B&W image send by the current camera in use. + \param I_ : A B&W image send by the current camera in use. \warning The image size must be : 640x480 ! */ void vpSimulatorViper850::getCameraParameters (vpCameraParameters &cam, - const vpImage<vpRGBa> &I) + const vpImage<vpRGBa> &I_) { - getCameraParameters(cam,I.getWidth(),I.getHeight()); + getCameraParameters(cam,I_.getWidth(),I_.getHeight()); } @@ -689,7 +695,7 @@ vpSimulatorViper850::compute_fMi() // fMit[7] = fMit[6] * cMe; vpViper::get_fMc(q,fMit[7]); - #if defined(WIN32) + #if defined(_WIN32) WaitForSingleObject(mutex_fMi,INFINITE); for (int i = 0; i < 8; i++) fMi[i] = fMit[i]; @@ -937,24 +943,24 @@ vpSimulatorViper850::computeArticularVelocity() { case vpRobot::CAMERA_FRAME : { - vpMatrix eJe; + vpMatrix eJe_; vpVelocityTwistMatrix eVc(eMc); - vpViper850::get_eJe(articularCoordinates,eJe); - eJe = eJe.pseudoInverse(); + vpViper850::get_eJe(articularCoordinates,eJe_); + eJe_ = eJe_.pseudoInverse(); if (singularityManagement) - singularityTest(articularCoordinates,eJe); - articularVelocity = eJe*eVc*velocityframe; + singularityTest(articularCoordinates,eJe_); + articularVelocity = eJe_*eVc*velocityframe; set_artVel (articularVelocity); break; } case vpRobot::REFERENCE_FRAME : { - vpMatrix fJe; - vpViper850::get_fJe(articularCoordinates,fJe); - fJe = fJe.pseudoInverse(); + vpMatrix fJe_; + vpViper850::get_fJe(articularCoordinates,fJe_); + fJe_ = fJe_.pseudoInverse(); if (singularityManagement) - singularityTest(articularCoordinates,fJe); - articularVelocity = fJe*velocityframe; + singularityTest(articularCoordinates,fJe_); + articularVelocity = fJe_*velocityframe; set_artVel (articularVelocity); break; } @@ -1062,10 +1068,10 @@ vpSimulatorViper850::getVelocity (const vpRobot::vpControlFrameType frame, vpCol { case vpRobot::CAMERA_FRAME : { - vpMatrix eJe; + vpMatrix eJe_; vpVelocityTwistMatrix cVe(eMc); - vpViper850::get_eJe(articularCoordinates,eJe); - vel = cVe*eJe*articularVelocity; + vpViper850::get_eJe(articularCoordinates,eJe_); + vel = cVe*eJe_*articularVelocity; break ; } case vpRobot::ARTICULAR_FRAME : @@ -1075,9 +1081,9 @@ vpSimulatorViper850::getVelocity (const vpRobot::vpControlFrameType frame, vpCol } case vpRobot::REFERENCE_FRAME : { - vpMatrix fJe; - vpViper850::get_fJe(articularCoordinates,fJe); - vel = fJe*articularVelocity; + vpMatrix fJe_; + vpViper850::get_fJe(articularCoordinates,fJe_); + vel = fJe_*articularVelocity; break ; } case vpRobot::MIXT_FRAME : @@ -1093,6 +1099,29 @@ vpSimulatorViper850::getVelocity (const vpRobot::vpControlFrameType frame, vpCol } } +/*! + Get the robot time stamped velocities. + + \param frame : Frame in wich velocities are mesured. + + \param vel : Measured velocities. Translations are expressed in m/s + and rotations in rad/s. + + \param timestamp : Unix time in second since January 1st 1970. + + \warning In camera frame, reference frame and mixt frame, the representation + of the rotation is ThetaU. In that cases, \f$velocity = [\dot x, \dot y, \dot + z, \dot {\theta U}_x, \dot {\theta U}_y, \dot {\theta U}_z]\f$. + + \sa getVelocity(const vpRobot::vpControlFrameType frame, vpColVector & vel) +*/ +void +vpSimulatorViper850::getVelocity (const vpRobot::vpControlFrameType frame, vpColVector & vel, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getVelocity(frame, vel); +} + /*! Get the robot velocities. @@ -1138,12 +1167,33 @@ int main() vpColVector vpSimulatorViper850::getVelocity (vpRobot::vpControlFrameType frame) { - vpColVector velocity(6); - getVelocity (frame, velocity); + vpColVector vel(6); + getVelocity (frame, vel); - return velocity; + return vel; } +/*! + Get the time stamped robot velocities. + + \param frame : Frame in wich velocities are mesured. + + \param timestamp : Unix time in second since January 1st 1970. + + \return Measured velocities. Translations are expressed in m/s + and rotations in rad/s. + + \sa getVelocity(vpRobot::vpControlFrameType frame) +*/ +vpColVector +vpSimulatorViper850::getVelocity (vpRobot::vpControlFrameType frame, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + vpColVector vel(6); + getVelocity (frame, vel); + + return vel; +} void vpSimulatorViper850::findHighestPositioningSpeed(vpColVector &q) @@ -1266,10 +1316,10 @@ vpSimulatorViper850::setPosition(const vpRobot::vpControlFrameType frame,const v vpRotationMatrix cRc2(rxyz); vpHomogeneousMatrix cMc2(txyz, cRc2); - vpHomogeneousMatrix fMc; - vpViper850::get_fMc(articularCoordinates, fMc); + vpHomogeneousMatrix fMc_; + vpViper850::get_fMc(articularCoordinates, fMc_); - vpHomogeneousMatrix fMc2 = fMc * cMc2; + vpHomogeneousMatrix fMc2 = fMc_ * cMc2; do { @@ -1339,13 +1389,13 @@ vpSimulatorViper850::setPosition(const vpRobot::vpControlFrameType frame,const v } vpRotationMatrix fRc(rxyz); - vpHomogeneousMatrix fMc(txyz, fRc); + vpHomogeneousMatrix fMc_(txyz, fRc); do { articularCoordinates = get_artCoord(); qdes = articularCoordinates; - nbSol = getInverseKinematics(fMc, qdes, verbose_); + nbSol = getInverseKinematics(fMc_, qdes, verbose_); if (nbSol > 0) { error = qdes - articularCoordinates; @@ -1573,8 +1623,8 @@ int main() } \endcode - \sa setPosition(const vpRobot::vpControlFrameType frame, const - vpColVector & r) + \sa getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp) + \sa setPosition(const vpRobot::vpControlFrameType frame, const vpColVector & r) */ void @@ -1598,15 +1648,15 @@ vpSimulatorViper850::getPosition(const vpRobot::vpControlFrameType frame, vpColV case vpRobot::REFERENCE_FRAME: { - vpHomogeneousMatrix fMc; - vpViper::get_fMc (get_artCoord(), fMc); + vpHomogeneousMatrix fMc_; + vpViper::get_fMc (get_artCoord(), fMc_); vpRotationMatrix fRc; - fMc.extract(fRc); + fMc_.extract(fRc); vpRxyzVector rxyz(fRc); vpTranslationVector txyz; - fMc.extract(txyz); + fMc_.extract(txyz); for (unsigned int i=0; i <3; i++) { @@ -1627,6 +1677,39 @@ vpSimulatorViper850::getPosition(const vpRobot::vpControlFrameType frame, vpColV } } +/*! + + Get the current time stamped position of the robot. + + \param frame : Control frame type in which to get the position, either : + - in the camera cartesien frame, + - joint (articular) coordinates of each axes + - in a reference or fixed cartesien frame attached to the robot base + - in a mixt cartesien frame (translation in reference + frame, and rotation in camera frame) + + \param q : Measured position of the robot: + - in camera cartesien frame, a 6 dimension vector, set to 0. + + - in articular, a 6 dimension vector corresponding to the joint + position of each dof in radians. + + - in reference frame, a 6 dimension vector, the first 3 values correspond to + the translation tx, ty, tz in meters (like a vpTranslationVector), and the + last 3 values to the rx, ry, rz rotation (like a vpRxyzVector). The code + below show how to convert this position into a vpHomogenousMatrix: + + \param timestamp : Unix time in second since January 1st 1970. + + \sa getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q) + */ +void +vpSimulatorViper850::getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getPosition(frame, q); +} + /*! Get the current position of the robot. @@ -1640,7 +1723,7 @@ vpSimulatorViper850::getPosition(const vpRobot::vpControlFrameType frame, vpColV */ void vpSimulatorViper850::getPosition (const vpRobot::vpControlFrameType frame, - vpPoseVector &position) + vpPoseVector &position) { vpColVector posRxyz; //recupere position en Rxyz @@ -1657,6 +1740,24 @@ vpSimulatorViper850::getPosition (const vpRobot::vpControlFrameType frame, } } +/*! + + Get the current time stamped position of the robot. + + Similar as getPosition(const vpRobot::vpControlFrameType frame, vpColVector &, double &) + + The difference is here that the position is returned using a ThetaU + representation. + + */ +void +vpSimulatorViper850::getPosition(const vpRobot::vpControlFrameType frame, + vpPoseVector &position, double ×tamp) +{ + timestamp = vpTime::measureTimeSecond(); + getPosition(frame, position); +} + /*! This method enables to set the minimum and maximum joint limits for all the six axis of the robot. All the values have to be given in radian. @@ -1954,7 +2055,6 @@ int main() bool vpSimulatorViper850::readPosFile(const char *filename, vpColVector &q) { - FILE * fd ; fd = fopen(filename, "r") ; if (fd == NULL) @@ -1969,27 +2069,30 @@ vpSimulatorViper850::readPosFile(const char *filename, vpColVector &q) // Saut des lignes commencant par # if (fgets (line, FILENAME_MAX, fd) != NULL) { if ( strncmp (line, "#", 1) != 0) { - // La ligne n'est pas un commentaire - if ( strncmp (line, head, sizeof(head)-1) == 0) { - sortie = true; // Position robot trouvee. - } -// else -// return (false); // fin fichier sans position robot. + // La ligne n'est pas un commentaire + if ( strncmp (line, head, sizeof(head)-1) == 0) { + sortie = true; // Position robot trouvee. + } + // else + // return (false); // fin fichier sans position robot. } } else { + fclose(fd) ; return (false); /* fin fichier */ } - } while ( sortie != true ); // Lecture des positions q.resize(njoint); - sscanf(line, "%s %lf %lf %lf %lf %lf %lf", - dummy, - &q[0], &q[1], &q[2], - &q[3], &q[4], &q[5]); + int ret = sscanf(line, "%s %lf %lf %lf %lf %lf %lf", + dummy, + &q[0], &q[1], &q[2], &q[3], &q[4], &q[5]); + if (ret != 7) { + fclose(fd) ; + return false; + } // converts rotations from degrees into radians q.deg2rad(); @@ -2110,15 +2213,15 @@ vpSimulatorViper850::get_cVe(vpVelocityTwistMatrix &cVe) To compute \f$^e{\bf J}_e\f$, we communicate with the low level controller to get the joint position of the robot. - \param eJe : Robot jacobian \f$^e{\bf J}_e\f$ expressed in the + \param eJe_ : Robot jacobian \f$^e{\bf J}_e\f$ expressed in the end-effector frame. */ void -vpSimulatorViper850::get_eJe(vpMatrix &eJe) +vpSimulatorViper850::get_eJe(vpMatrix &eJe_) { try { - vpViper850::get_eJe(get_artCoord(), eJe) ; + vpViper850::get_eJe(get_artCoord(), eJe_) ; } catch(...) { @@ -2134,16 +2237,16 @@ vpSimulatorViper850::get_eJe(vpMatrix &eJe) To compute \f$^f{\bf J}_e\f$, we communicate with the low level controller to get the joint position of the robot. - \param fJe : Robot jacobian \f$^f{\bf J}_e\f$ expressed in the + \param fJe_ : Robot jacobian \f$^f{\bf J}_e\f$ expressed in the reference frame. */ void -vpSimulatorViper850::get_fJe(vpMatrix &fJe) +vpSimulatorViper850::get_fJe(vpMatrix &fJe_) { try { vpColVector articularCoordinates = get_artCoord(); - vpViper850::get_fJe(articularCoordinates, fJe) ; + vpViper850::get_fJe(articularCoordinates, fJe_) ; } catch(...) { @@ -2165,7 +2268,7 @@ vpSimulatorViper850::stopMotion() stop = 0; set_artVel(stop); set_velocity(stop); - setRobotState (vpRobot::STATE_STOP); + vpRobot::setRobotState (vpRobot::STATE_STOP); } @@ -2187,26 +2290,40 @@ void vpSimulatorViper850::initArms() { // set scene_dir from #define VISP_SCENE_DIR if it exists - std::string scene_dir; + std::string scene_dir_; if (vpIoTools::checkDirectory(VISP_SCENES_DIR) == true) // directory exists - scene_dir = VISP_SCENES_DIR; + scene_dir_ = VISP_SCENES_DIR; else { try { - scene_dir = vpIoTools::getenv("VISP_SCENES_DIR"); - std::cout << "The simulator uses data from VISP_SCENES_DIR=" << scene_dir << std::endl; + scene_dir_ = vpIoTools::getenv("VISP_SCENES_DIR"); + std::cout << "The simulator uses data from VISP_SCENES_DIR=" << scene_dir_ << std::endl; } catch (...) { std::cout << "Cannot get VISP_SCENES_DIR environment variable" << std::endl; } } - char name_cam[FILENAME_MAX]; + unsigned int name_length = 30; // the size of this kind of string "/viper850_arm2.bnd" + if (scene_dir_.size() > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Viper850 simulator"); - strcpy(name_cam, scene_dir.c_str()); + unsigned int full_length = (unsigned int)scene_dir_.size() + name_length; + if (full_length > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Viper850 simulator"); + + char *name_cam = new char [full_length]; + + strcpy(name_cam, scene_dir_.c_str()); strcat(name_cam,"/camera.bnd"); set_scene(name_cam,&camera,cameraFactor); - char name_arm[FILENAME_MAX]; + if (arm_dir.size() > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Viper850 simulator"); + full_length = (unsigned int)arm_dir.size() + name_length; + if (full_length > FILENAME_MAX) + throw vpException (vpException::dimensionError, "Cannot initialize Viper850 simulator"); + + char *name_arm = new char [full_length]; strcpy(name_arm, arm_dir.c_str()); strcat(name_arm,"/viper850_arm1.bnd"); set_scene(name_arm, robotArms, 1.0); @@ -2241,14 +2358,17 @@ vpSimulatorViper850::initArms() // sceneInitialized = true; // displayObject = true; displayCamera = true; + + delete [] name_cam; + delete [] name_arm; } void -vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I) +vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I_) { bool changed = false; - vpHomogeneousMatrix displacement = navigation(I,changed); + vpHomogeneousMatrix displacement = navigation(I_,changed); //if (displacement[2][3] != 0) if (std::fabs(displacement[2][3]) > std::numeric_limits<double>::epsilon()) @@ -2265,13 +2385,13 @@ vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I) if( (std::fabs(px_ext-1.) > vpMath::maximum(px_ext,1.)*std::numeric_limits<double>::epsilon()) && (std::fabs(py_ext-1) > vpMath::maximum(py_ext,1.)*std::numeric_limits<double>::epsilon())) { - u = (double)I.getWidth()/(2*px_ext); - v = (double)I.getHeight()/(2*py_ext); + u = (double)I_.getWidth()/(2*px_ext); + v = (double)I_.getHeight()/(2*py_ext); } else { - u = (double)I.getWidth()/(vpMath::minimum(I.getWidth(),I.getHeight())); - v = (double)I.getHeight()/(vpMath::minimum(I.getWidth(),I.getHeight())); + u = (double)I_.getWidth()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); + v = (double)I_.getHeight()/(vpMath::minimum(I_.getWidth(),I_.getHeight())); } float w44o[4][4],w44cext[4][4],x,y,z; @@ -2291,22 +2411,22 @@ vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I) get_fMi(fMit); vp2jlc_matrix(vpHomogeneousMatrix(0,0,0,0,0,0),w44o); - display_scene(w44o,robotArms[0],I, curColor); + display_scene(w44o,robotArms[0],I_, curColor); vp2jlc_matrix(fMit[0],w44o); - display_scene(w44o,robotArms[1],I, curColor); + display_scene(w44o,robotArms[1],I_, curColor); vp2jlc_matrix(fMit[1],w44o); - display_scene(w44o,robotArms[2],I, curColor); + display_scene(w44o,robotArms[2],I_, curColor); vp2jlc_matrix(fMit[2],w44o); - display_scene(w44o,robotArms[3],I, curColor); + display_scene(w44o,robotArms[3],I_, curColor); vp2jlc_matrix(fMit[3],w44o); - display_scene(w44o,robotArms[4],I, curColor); + display_scene(w44o,robotArms[4],I_, curColor); vp2jlc_matrix(fMit[6],w44o); - display_scene(w44o,robotArms[5],I, curColor); + display_scene(w44o,robotArms[5],I_, curColor); if (displayCamera) { @@ -2315,13 +2435,13 @@ vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I) cMe = cMe.inverse(); cMe = fMit[6] * cMe; vp2jlc_matrix(cMe,w44o); - display_scene(w44o,camera, I, camColor); + display_scene(w44o,camera, I_, camColor); } if (displayObject) { vp2jlc_matrix(fMo,w44o); - display_scene(w44o,scene,I, curColor); + display_scene(w44o,scene,I_, curColor); } } @@ -2336,23 +2456,23 @@ vpSimulatorViper850::getExternalImage(vpImage<vpRGBa> &I) \f${^f}{\bf M}_c = {^f}{\bf M}_o \; ({^c}{\bf M}{_o})^{-1}\f$, and from the inverse kinematics set the joint positions \f${\bf q}\f$ that corresponds to the \f${^f}{\bf M}_c\f$ transformation. - \param cMo : the desired pose of the camera. + \param cMo_ : the desired pose of the camera. \return false if the robot kinematics is not able to reach the cMo position. */ bool -vpSimulatorViper850::initialiseCameraRelativeToObject(const vpHomogeneousMatrix &cMo) +vpSimulatorViper850::initialiseCameraRelativeToObject(const vpHomogeneousMatrix &cMo_) { vpColVector stop(6); bool status = true; stop = 0; set_artVel(stop); set_velocity(stop); - vpHomogeneousMatrix fMc; - fMc = fMo * cMo.inverse(); + vpHomogeneousMatrix fMc_; + fMc_ = fMo * cMo_.inverse(); vpColVector articularCoordinates = get_artCoord(); - unsigned int nbSol = getInverseKinematics(fMc, articularCoordinates, verbose_); + unsigned int nbSol = getInverseKinematics(fMc_, articularCoordinates, verbose_); if (nbSol == 0) { status = false; @@ -2379,10 +2499,10 @@ vpSimulatorViper850::initialiseCameraRelativeToObject(const vpHomogeneousMatrix \f${^f}{\bf M}_o = {^f}{\bf M}_c \; {^c}{\bf M}_o\f$ where \f$ {^f}{\bf M}_c = f({\bf q})\f$ with \f${\bf q}\f$ the robot joint position - \param cMo : the desired pose of the camera. + \param cMo_ : the desired pose of the camera. */ void -vpSimulatorViper850::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix &cMo) +vpSimulatorViper850::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix &cMo_) { vpColVector stop(6); stop = 0; @@ -2390,7 +2510,7 @@ vpSimulatorViper850::initialiseObjectRelativeToCamera(const vpHomogeneousMatrix set_velocity(stop); vpHomogeneousMatrix fMit[8]; get_fMi(fMit); - fMo = fMit[7] * cMo; + fMo = fMit[7] * cMo_; } #endif diff --git a/src/robot/simulator-robot/vpSimulatorViper850.h b/src/robot/simulator-robot/vpSimulatorViper850.h index c20c1a6b885f9d11c6366dc9f52f96e96fc6917e..6cccdcacb49564c9110c45b8833729d86a707d79 100644 --- a/src/robot/simulator-robot/vpSimulatorViper850.h +++ b/src/robot/simulator-robot/vpSimulatorViper850.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSimulatorViper850.h 4252 2013-05-14 13:44:49Z fspindle $ + * $Id: vpSimulatorViper850.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,7 +52,7 @@ #include <visp/vpRobotWireFrameSimulator.h> #include <visp/vpViper850.h> #include <string> -#if defined(WIN32) || defined(VISP_HAVE_PTHREAD) +#if defined(_WIN32) || defined(VISP_HAVE_PTHREAD) /*! \class vpSimulatorViper850 @@ -236,13 +236,15 @@ class VISP_EXPORT vpSimulatorViper850 : public vpRobotWireFrameSimulator, public vpColVector &displacement); void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q); - void getPosition (const vpRobot::vpControlFrameType frame, - vpPoseVector &position); + void getPosition(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp); + void getPosition(const vpRobot::vpControlFrameType frame, vpPoseVector &position); + void getPosition(const vpRobot::vpControlFrameType frame, vpPoseVector &position, double ×tamp); double getPositioningVelocity (void){return positioningVelocity;} - void getVelocity(const vpRobot::vpControlFrameType frame, vpColVector &q); + void getVelocity(const vpRobot::vpControlFrameType frame, vpColVector &q, double ×tamp); vpColVector getVelocity (const vpRobot::vpControlFrameType frame); + vpColVector getVelocity (const vpRobot::vpControlFrameType frame, double ×tamp); void get_cMe(vpHomogeneousMatrix &cMe); void get_cVe(vpVelocityTwistMatrix &cVe); @@ -269,7 +271,7 @@ class VISP_EXPORT vpSimulatorViper850 : public vpRobotWireFrameSimulator, public const double pos5, const double pos6); void setPosition(const char *filename); - void setPositioningVelocity (const double velocity) {positioningVelocity = velocity;} + void setPositioningVelocity (const double vel) {positioningVelocity = vel;} vpRobot::vpRobotStateType setRobotState (const vpRobot::vpRobotStateType newState); void setVelocity (const vpRobot::vpControlFrameType frame, const vpColVector & velocity); @@ -283,7 +285,7 @@ protected: void getExternalImage(vpImage<vpRGBa> &I); inline void get_fMi(vpHomogeneousMatrix *fMit) { -#if defined(WIN32) +#if defined(_WIN32) WaitForSingleObject(mutex_fMi,INFINITE); for (int i = 0; i < 8; i++) fMit[i] = fMi[i]; diff --git a/src/servo/vpAdaptativeGain.cpp b/src/servo/vpAdaptativeGain.cpp deleted file mode 100644 index 62918d4ebaad8ec76697694dc18318054cf5f51a..0000000000000000000000000000000000000000 --- a/src/servo/vpAdaptativeGain.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/**************************************************************************** - * - * $Id: vpAdaptativeGain.cpp 4317 2013-07-17 09:40:17Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Adaptative gain. - * - * Authors: - * Nicolas Mansard - * - *****************************************************************************/ -/*! - \file vpAdaptativeGain.cpp - - \brief Adaptative gain. The content of this file is deprecated. You - should better use vpAdaptiveGain. - -*/ - -#include <visp/vpConfig.h> - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - -/* --- VISP --- */ -#include <visp/vpAdaptativeGain.h> -#include <visp/vpColVector.h> -#include <visp/vpDebug.h> - -#include <iostream> -#include <cmath> // std::fabs -#include <limits> // numeric_limits - -const double vpAdaptativeGain::DEFAULT_LAMBDA_ZERO = 1.666; -const double vpAdaptativeGain::DEFAULT_LAMBDA_INFINI = 0.1666; -const double vpAdaptativeGain::DEFAULT_LAMBDA_PENTE = 1.666; - -/* -------------------------------------------------------------------------- */ -/* --- CONSTRUCTION --------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/*! - \deprecated This class is deprecated. You should use - vpAdaptive class instead. - Basic constructor which initializes the parameters with a default value. -*/ -vpAdaptativeGain:: -vpAdaptativeGain (void) - : - coeff_a (), - coeff_b (), - coeff_c () -{ - vpDEBUG_TRACE (10, "# Entree constructeur par default."); - this ->initFromVoid (); - - vpDEBUG_TRACE (10, "# Sortie constructeur par default."); - return; -} - -/* -------------------------------------------------------------------------- */ -/* --- INIT ----------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/*! - Initializes the parameters to have a constant gain. - Thus \f$ a = 0 \f$, \f$ b = 1 \f$ and \f$ c = lambda \f$ - - \param lambda : the expected constant gain. -*/ -void vpAdaptativeGain:: -initFromConstant (const double lambda) -{ - vpDEBUG_TRACE (10, "# Entree."); - - this ->coeff_a = 0; - this ->coeff_b = 1; - this ->coeff_c = lambda; - - vpDEBUG_TRACE (10, "# Sortie."); - return; -} - - -/*! - Initializes the parameters with the default value : - - \f$ lambda(0) = 1.666 \f$ - - \f$ lambda(inf) = 0.1666 \f$ - - \f$ lambda'(0) = 1.666 \f$ -*/ -void vpAdaptativeGain:: -initFromVoid (void) -{ - vpDEBUG_TRACE (10, "# Entree."); - - this ->initStandard (vpAdaptativeGain::DEFAULT_LAMBDA_ZERO, - vpAdaptativeGain::DEFAULT_LAMBDA_INFINI, - vpAdaptativeGain::DEFAULT_LAMBDA_PENTE); - - vpDEBUG_TRACE (10, "# Sortie."); - return; -} - - -/*! - Computes the parameters thanks to the given \f$ lambda(0)\f$, \f$ lambda(inf)\f$ and \f$ lambda'(0)\f$. - - \f$ lambda(0)\f$ represents the gain in 0, \f$ lambda(inf)\f$ represents the gain to infinity and \f$ lambda'(0)\f$ represents the slope in 0. - - \param en_zero : the expected gain in 0. - \param en_infini : the expected gain to infinity. - \param pente_en_zero : the expected slope in 0. -*/ -void vpAdaptativeGain:: -initStandard (const double en_zero, - const double en_infini, - const double pente_en_zero) -{ - vpDEBUG_TRACE (10, "# Entree."); - - this ->coeff_a = en_zero - en_infini; - //if (0 == this ->coeff_a) - if (std::fabs(this ->coeff_a) <= std::numeric_limits<double>::epsilon()) - { - this ->coeff_b = 0; - } - else - { - this ->coeff_b = pente_en_zero / ( this ->coeff_a); - } - this ->coeff_c = en_infini; - - vpDEBUG_TRACE (10, "# Sortie :a,b,c= %.3f,%.3f,%.3f.", - this ->coeff_a, this ->coeff_b, this ->coeff_c); - return; -} - - - -/* -------------------------------------------------------------------------- */ -/* --- MODIFICATOR ---------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/*! - Sets the parameters in order to obtain a constant gain equal to the gain in 0. - - \return It returns the value of the computed constant gain. -*/ -double vpAdaptativeGain:: -setConstant (void) -{ - vpDEBUG_TRACE (10, "# Entree."); - - double res = this ->coeff_a + this ->coeff_c; - - this ->coeff_a = 0; - this ->coeff_b = 1; - this ->coeff_c = res; - - vpDEBUG_TRACE (10, "# Sortie: %.3f.", res); - return res; -} - -/* -------------------------------------------------------------------------- */ -/* --- VALEUR --------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/*! - Computes the value of the adaptive gain \f$\lambda\f$ corresponding to - the norm of the task function. The formula is the following: - - \f[\lambda = a * exp(-b*val_e) + c\f] - - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. - - \return It returns the value of the computed gain. -*/ -double vpAdaptativeGain:: -value_const (const double val_e) const -{ - vpDEBUG_TRACE (10, "# Entree."); - - double res = this ->coeff_a * exp (- this ->coeff_b * val_e) - + this ->coeff_c; - - vpDEBUG_TRACE (10, "# Sortie: %.3f.", res); - return res; -} - -/*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$). - - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). - */ -double vpAdaptativeGain:: -limitValue_const (void) const -{ - vpDEBUG_TRACE (10, "# Entree."); - - double res = this ->coeff_c; - - vpDEBUG_TRACE (10, "# Sortie: %.3f.", res); - return res; -} - - - -/*! - Computes the value of the adaptive gain \f$\lambda\f$ corresponding to - the norm of the task function and stores it as a parameter of the class. - The formula used for the gain computation is the following: - - \f[\lambda = a * exp(-b*val_e) + c\f] - - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. - - \return It returns the value of the computed gain. -*/ -double vpAdaptativeGain:: -value (const double val_e) const -{ - vpDEBUG_TRACE (10, "# Entree."); - - this ->lambda = this ->value_const (val_e); - - vpDEBUG_TRACE (10, "# Sortie: %.3f.", this ->lambda); - return lambda; -} - - -/*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$)and stores it - as a parameter of the class. - - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). - */ -double vpAdaptativeGain:: -limitValue (void) const -{ - vpDEBUG_TRACE (10, "# Entree."); - - this ->lambda = this ->limitValue_const (); - - vpDEBUG_TRACE (10, "# Sortie: %.3f.", this ->lambda); - return lambda; -} - -/* -------------------------------------------------------------------------- */ -/* --- ACCESSORS ------------------------------------------------------------ */ -/* -------------------------------------------------------------------------- */ - - - -// double vpAdaptativeGain:: -// getLastValue (void) const -// { -// return this ->lambda; -// } - -/*! - Operator which calls the value(double val_e) method with \e val_e in parameter in order - to compute the adaptative gain corresponding to \e val_e. - - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. - - \return It returns the value of the computed gain. -*/ -double vpAdaptativeGain:: -operator() (const double val_e) const -{ - return this ->value (val_e); -} - -/*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$). - - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). - */ -double vpAdaptativeGain:: -operator() (void) const -{ - return this ->limitValue (); -} - -/*! - Operator which calls the value(double val_e) method with the infinity norm of \e e in parameter in order - to compute the adaptative gain corresponding to \f$ |e| \f$. - - \param e : the task function \f$\mid s - s^*\mid\f$. - - \return It returns the value of the computed gain. -*/ -double vpAdaptativeGain:: -operator() (const vpColVector & e) const -{ - return this ->value (e .infinityNorm()); -} - - -// double operator() (double val_e) const; -// double operator() (const CColVector & e) const; - -/* -------------------------------------------------------------------------- */ -/* --- OUTPUT --------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - - -/*! - Prints the adaptative gain coefficients. It prints the gain in 0, - the gain to infinity and the slope in 0. - - \param os : The stream where to print the adaptative gain parameters. - \param lambda : The adaptative gain containing the parameters to print. -*/ -std::ostream& -operator<< (std::ostream &os, const vpAdaptativeGain& lambda) -{ - os << "Zero= " << lambda .coeff_a + lambda .coeff_c - << "\tInf= " << lambda .coeff_c - << "\tDeriv= " << lambda .coeff_a * lambda .coeff_b; - - return os; -} - -#endif - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff --git a/src/servo/vpAdaptativeGain.h b/src/servo/vpAdaptativeGain.h deleted file mode 100644 index 7b638911953303bfb8a349bbf673338ae5597b8a..0000000000000000000000000000000000000000 --- a/src/servo/vpAdaptativeGain.h +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************** - * - * $Id: vpAdaptativeGain.h 4317 2013-07-17 09:40:17Z fspindle $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Adaptative gain. - * - * Authors: - * Nicolas Mansard - * - *****************************************************************************/ -/*! - \file vpAdaptativeGain.h - - \brief Adaptative gain. The content of this file is deprecated. You - should better use vpAdaptiveGain. -*/ - -#ifndef __VP_ADAPTATIVE_GAIN_H -#define __VP_ADAPTATIVE_GAIN_H - -#include <visp/vpConfig.h> -#include <iostream> - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - -class vpColVector; -/*! - \class vpAdaptativeGain - - \ingroup VsTask - - \brief Adaptative gain computation. - - \deprecated This class is deprecated. You should use - vpAdaptive class instead. - - The formula used to compute the gain is the following : - - \f[ lambda (x) = a * exp (-b*x) + c \f] - - where \f$ a \f$, \f$ b \f$ and \f$ c \f$ are parameters which must be set - and \f$ x \f$ is the vector error of the task. - - By default, the parameters are set with default values: - \f[ a = lambda(0) - lambda(inf) \f] - \f[ b = lambda'(0) / a \f] - \f[ c = lambda(inf) \f] - - with \f$ lambda(0) = 1.666 \f$, \f$ lambda(inf) = 0.1666 \f$ and \f$ lambda'(0) = 1.666 \f$. - - \f$ lambda(0)\f$ represents the gain in 0, \f$ lambda(inf)\f$ represents the gain to infinity and \f$ lambda'(0)\f$ represents the slope in 0. - -*/ - -class VISP_EXPORT vpAdaptativeGain -{ - -public: /* constantes */ - - static const double DEFAULT_LAMBDA_ZERO; - static const double DEFAULT_LAMBDA_INFINI; - static const double DEFAULT_LAMBDA_PENTE; - - -private: /* Attributs*/ - /* Coefficient de la fonction de calcul de lambda. - * lambda (x) = a * exp (-b*x) + c. */ - double coeff_a; - double coeff_b; - double coeff_c; - - /* Derniere valeur calculee. */ - mutable double lambda; - - - -public: /* Methodes*/ - - /* --- CONSTRUCTOR -------------------------------------------------------- */ - - vp_deprecated vpAdaptativeGain (void); - - /* --- INIT --------------------------------------------------------------- */ - void initFromConstant (double lambda); - void initFromVoid (void); - void initStandard (double en_zero, - double en_infini, - double pente_en_zero); - - - /* --- MODIFIORS ---------------------------------------------------------- */ - double setConstant (void); - - - /* --- COMPUTE ------------------------------------------------------------ */ - /* \brief Calcule la valeur de lambda au point courrant. - * - * Determine la valeur du lambda adaptatif en fonction de la valeur - * de la norme de la fonction de tache e par extrapolation exponentielle. - * La fonction est : (en_infini - en_zero) * exp (-pente * ||e|| ) + en_infini. - * On a bien : - * - lambda(10^5) = en_infini ; - * - lambda(0) = en_zero ; - * - lambda(x ~ 0) ~ - pente * x + en_zero. - * \param val_e: valeur de la norme de l'erreur. - * \return: valeur de gain au point courrant. - */ - double value_const (double val_e) const; - - /* \brief Calcule la valeur de lambda au point courrant et stockage du - * resultat. - * - * La fonction calcule la valeur de lambda d'apres la valeur de la norme - * de l'erreur, comme le fait la fonction valeur_const. - * La fonction non constante stocke de plus le resultat dans this ->lambda. - * \param val_e: valeur de la norme de l'erreur. - * \return: valeur de gain au point courrant. - */ - double value (double val_e) const; - - double limitValue_const (void) const; - - double limitValue (void) const; - - /* --- ACCESSORS ---------------------------------------------------------- */ - - /*! - Gets the last adaptative gain value which was stored in the class. - - \return It returns the last adaptative gain value which was stored in the class. - */ - inline double getLastValue (void) const {return this ->lambda;} - - double operator() (double val_e) const; - - /* \brief Lance la fonction valeur avec la norme INFINIE du vecteur. */ - double operator() (const vpColVector & e) const; - - /* \brief Idem function limitValue. */ - double operator() (void) const; - - - /* --- IOSTREAM ----------------------------------------------------------- */ - - friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, - const vpAdaptativeGain& lambda); -}; - -#endif - -#endif /* __VP_ADAPTATIVE_GAIN_H */ - - - - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff --git a/src/servo/vpAdaptiveGain.cpp b/src/servo/vpAdaptiveGain.cpp index 283fbf38164aea1519c46fcfecfeb0ebc23894ea..6bc54f7c935bd9a12899938bb45aac44951c3dc9 100644 --- a/src/servo/vpAdaptiveGain.cpp +++ b/src/servo/vpAdaptiveGain.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAdaptiveGain.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAdaptiveGain.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,22 +52,26 @@ #include <limits> // numeric_limits const double vpAdaptiveGain::DEFAULT_LAMBDA_ZERO = 1.666; -const double vpAdaptiveGain::DEFAULT_LAMBDA_INFINI = 0.1666; -const double vpAdaptiveGain::DEFAULT_LAMBDA_PENTE = 1.666; +const double vpAdaptiveGain::DEFAULT_LAMBDA_INFINITY = 0.1666; +const double vpAdaptiveGain::DEFAULT_LAMBDA_SLOPE = 1.666; /* -------------------------------------------------------------------------- */ /* --- CONSTRUCTION --------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /*! - Basic constructor which initializes the parameters with a default value. + Basic constructor which initializes all the parameters with their default value: + - \f$ \lambda(0) = 1.666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_ZERO + - \f$ \lambda(\infty) = 0.1666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_INFINITY + - \f$ {\dot \lambda}(0) = 1.666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_SLOPE + */ -vpAdaptiveGain:: -vpAdaptiveGain (void) +vpAdaptiveGain::vpAdaptiveGain () : coeff_a (), coeff_b (), - coeff_c () + coeff_c (), + lambda(1.) { vpDEBUG_TRACE (10, "# Entree constructeur par default."); this ->initFromVoid (); @@ -76,24 +80,55 @@ vpAdaptiveGain (void) return; } +/*! + Constructor that initializes the gain as constant. In that case \f$\lambda(x) = c\f$. + + \param c : Value of the constant gain. +*/ +vpAdaptiveGain::vpAdaptiveGain (double c) + : + coeff_a (), + coeff_b (), + coeff_c (), + lambda(1.) +{ + initFromConstant(c); +} + +/*! + Constructor that initializes the gain as adaptive. + + \param gain_at_zero : the expected gain when \f$x=0\f$: \f$\lambda(0)\f$. + \param gain_at_infinity : the expected gain when \f$x=\infty\f$: \f$\lambda(\infty)\f$. + \param slope_at_zero : the expected slope of \f$\lambda(x)\f$ when \f$x=0\f$: \f${\dot \lambda}(0)\f$. + +*/ +vpAdaptiveGain::vpAdaptiveGain (double gain_at_zero, double gain_at_infinity, double slope_at_zero) + : + coeff_a (), + coeff_b (), + coeff_c (), + lambda(1.) +{ + initStandard(gain_at_zero, gain_at_infinity, slope_at_zero); +} + /* -------------------------------------------------------------------------- */ /* --- INIT ----------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /*! - Initializes the parameters to have a constant gain. - Thus \f$ a = 0 \f$, \f$ b = 1 \f$ and \f$ c = lambda \f$ - - \param lambda : the expected constant gain. + Initializes the parameters to have a constant gain. In that case \f$\lambda(x) = c\f$. + + \param c : Value of the constant gain. */ -void vpAdaptiveGain:: -initFromConstant (const double lambda) +void vpAdaptiveGain::initFromConstant (const double c) { vpDEBUG_TRACE (10, "# Entree."); this ->coeff_a = 0; this ->coeff_b = 1; - this ->coeff_c = lambda; + this ->coeff_c = c; vpDEBUG_TRACE (10, "# Sortie."); return; @@ -102,18 +137,17 @@ initFromConstant (const double lambda) /*! Initializes the parameters with the default value : - - \f$ lambda(0) = 1.666 \f$ - - \f$ lambda(inf) = 0.1666 \f$ - - \f$ lambda'(0) = 1.666 \f$ + - \f$ \lambda(0) = 1.666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_ZERO + - \f$ \lambda(\infty) = 0.1666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_INFINITY + - \f$ {\dot \lambda}(0) = 1.666 \f$ using vpAdaptiveGain::DEFAULT_LAMBDA_SLOPE */ -void vpAdaptiveGain:: -initFromVoid (void) +void vpAdaptiveGain::initFromVoid (void) { vpDEBUG_TRACE (10, "# Entree."); this ->initStandard (vpAdaptiveGain::DEFAULT_LAMBDA_ZERO, - vpAdaptiveGain::DEFAULT_LAMBDA_INFINI, - vpAdaptiveGain::DEFAULT_LAMBDA_PENTE); + vpAdaptiveGain::DEFAULT_LAMBDA_INFINITY, + vpAdaptiveGain::DEFAULT_LAMBDA_SLOPE); vpDEBUG_TRACE (10, "# Sortie."); return; @@ -121,22 +155,19 @@ initFromVoid (void) /*! - Computes the parameters thanks to the given \f$ lambda(0)\f$, \f$ lambda(inf)\f$ and \f$ lambda'(0)\f$. - - \f$ lambda(0)\f$ represents the gain in 0, \f$ lambda(inf)\f$ represents the gain to infinity and \f$ lambda'(0)\f$ represents the slope in 0. + Set the parameters \f$\lambda(0), \lambda(\infty), {\dot \lambda}(0)\f$ used to compute \f$\lambda(x)\f$. - \param en_zero : the expected gain in 0. - \param en_infini : the expected gain to infinity. - \param pente_en_zero : the expected slope in 0. + \param gain_at_zero : the expected gain when \f$x=0\f$: \f$\lambda(0)\f$. + \param gain_at_infinity : the expected gain when \f$x=\infty\f$: \f$\lambda(\infty)\f$. + \param slope_at_zero : the expected slope of \f$\lambda(x)\f$ when \f$x=0\f$: \f${\dot \lambda}(0)\f$. */ -void vpAdaptiveGain:: -initStandard (const double en_zero, - const double en_infini, - const double pente_en_zero) +void vpAdaptiveGain::initStandard (const double gain_at_zero, + const double gain_at_infinity, + const double slope_at_zero) { vpDEBUG_TRACE (10, "# Entree."); - this ->coeff_a = en_zero - en_infini; + this ->coeff_a = gain_at_zero - gain_at_infinity; //if (0 == this ->coeff_a) if (std::fabs(this ->coeff_a) <= std::numeric_limits<double>::epsilon()) { @@ -144,9 +175,9 @@ initStandard (const double en_zero, } else { - this ->coeff_b = pente_en_zero / ( this ->coeff_a); + this ->coeff_b = slope_at_zero / ( this ->coeff_a); } - this ->coeff_c = en_infini; + this ->coeff_c = gain_at_infinity; vpDEBUG_TRACE (10, "# Sortie :a,b,c= %.3f,%.3f,%.3f.", this ->coeff_a, this ->coeff_b, this ->coeff_c); @@ -160,12 +191,12 @@ initStandard (const double en_zero, /* -------------------------------------------------------------------------- */ /*! - Sets the parameters in order to obtain a constant gain equal to the gain in 0. + Sets the internal parameters \f$a,b,c\f$ in order to obtain a constant gain equal to + the gain in 0 set through the parameter \f$\lambda(0)\f$. - \return It returns the value of the computed constant gain. + \return It returns the value of the constant gain \f$\lambda(0)\f$. */ -double vpAdaptiveGain:: -setConstant (void) +double vpAdaptiveGain::setConstant (void) { vpDEBUG_TRACE (10, "# Entree."); @@ -184,34 +215,32 @@ setConstant (void) /* -------------------------------------------------------------------------- */ /*! - Computes the value of the adaptive gain \f$\lambda\f$ corresponding to - the norm of the task function. The formula is the following: + Computes the value of the adaptive gain \f$\lambda(x)\f$ using: - \f[\lambda = a * exp(-b*val_e) + c\f] + \f[\lambda(x) = a * exp(-b*x) + c\f] - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. + \param x : Input value to consider. During a visual servo this value can be the euclidian + norm \f$||s - s^*||\f$ or the infinity norm \f$||s - s^*||_{\infty}\f$ of the task function. \return It returns the value of the computed gain. */ -double vpAdaptiveGain:: -value_const (const double val_e) const +double vpAdaptiveGain::value_const (const double x) const { vpDEBUG_TRACE (10, "# Entree."); - double res = this ->coeff_a * exp (- this ->coeff_b * val_e) - + this ->coeff_c; + double res = this ->coeff_a * exp (- this ->coeff_b * x) + this ->coeff_c; vpDEBUG_TRACE (10, "# Sortie: %.3f.", res); return res; } /*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$). + Gets the value of the gain at infinity (ie the value of \f$ \lambda(\infty) = c \f$). + Similar to limitValue() except that here the value is not stored as a parameter of the class. - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). + \return It returns the value of the gain at infinity. */ -double vpAdaptiveGain:: -limitValue_const (void) const +double vpAdaptiveGain::limitValue_const (void) const { vpDEBUG_TRACE (10, "# Entree."); @@ -221,25 +250,23 @@ limitValue_const (void) const return res; } +/*! + Computes the value of the adaptive gain \f$\lambda(x)\f$ using: + \f[\lambda(x) = a * exp(-b*x) + c\f] -/*! - Computes the value of the adaptive gain \f$\lambda\f$ corresponding to - the norm of the task function and stores it as a parameter of the class. - The formula used for the gain computation is the following: + This value is stored as a parameter of the class. - \f[\lambda = a * exp(-b*val_e) + c\f] + \param x : Input value to consider. During a visual servo this value can be the euclidian + norm \f$||s - s^*||\f$ or the infinity norm \f$||s - s^*||_{\infty}\f$ of the task function. - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. - \return It returns the value of the computed gain. -*/ -double vpAdaptiveGain:: -value (const double val_e) const + */ +double vpAdaptiveGain::value (const double x) const { vpDEBUG_TRACE (10, "# Entree."); - this ->lambda = this ->value_const (val_e); + this ->lambda = this ->value_const (x); vpDEBUG_TRACE (10, "# Sortie: %.3f.", this ->lambda); return lambda; @@ -247,13 +274,12 @@ value (const double val_e) const /*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$)and stores it + Gets the value of the gain at infinity (ie the value of \f$\lambda(\infty) = c \f$) and stores it as a parameter of the class. - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). + \return It returns the value of the gain at infinity. */ -double vpAdaptiveGain:: -limitValue (void) const +double vpAdaptiveGain:: limitValue (void) const { vpDEBUG_TRACE (10, "# Entree."); @@ -276,42 +302,42 @@ limitValue (void) const // } /*! - Operator which calls the value(double val_e) method with \e val_e in parameter in order - to compute the adaptive gain corresponding to \e val_e. + Operator that computes \f$\lambda(x)\f$. - \param val_e : Norm of the task function \f$\mid s - s^*\mid\f$. - + \param x : Input value to consider. During a visual servo this value can be the euclidian + norm \f$||s - s^*||\f$ or the infinity norm \f$||s - s^*||_{\infty}\f$ of the task function. + \return It returns the value of the computed gain. + + \sa value() */ -double vpAdaptiveGain:: -operator() (const double val_e) const +double vpAdaptiveGain::operator() (const double x) const { - return this ->value (val_e); + return this ->value (x); } /*! - Gets the value of the gain at infinity (ie the value of \f$ c \f$). + Gets the value of the gain at infinity (ie the value of \f$\lambda(\infty) = c \f$). + + \return It returns the value of the gain at infinity. - \return It returns the value of the gain at infinity (ie the value of \f$ c \f$). + \sa limitValue() */ -double vpAdaptiveGain:: -operator() (void) const +double vpAdaptiveGain::operator() (void) const { return this ->limitValue (); } /*! - Operator which calls the value(double val_e) method with the infinity norm of \e e in parameter in order - to compute the adaptive gain corresponding to \f$ |e| \f$. + Operator which computes \f$\lambda({||x||}_{\infty})\f$. - \param e : the task function \f$\mid s - s^*\mid\f$. + \param x : Input vector to consider. \return It returns the value of the computed gain. */ -double vpAdaptiveGain:: -operator() (const vpColVector & e) const +double vpAdaptiveGain::operator() (const vpColVector & x) const { - return this ->value (e .infinityNorm()); + return this ->value (x .infinityNorm()); } @@ -325,14 +351,12 @@ operator() (const vpColVector & e) const /*! - Prints the adaptive gain coefficients. It prints the gain in 0, - the gain to infinity and the slope in 0. + Prints the adaptive gain parameters \f$\lambda(0), \lambda(\infty), {\dot \lambda}(0)\f$. \param os : The stream where to print the adaptive gain parameters. \param lambda : The adaptive gain containing the parameters to print. */ -std::ostream& -operator<< (std::ostream &os, const vpAdaptiveGain& lambda) +VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpAdaptiveGain& lambda) { os << "Zero= " << lambda .coeff_a + lambda .coeff_c << "\tInf= " << lambda .coeff_c @@ -340,11 +364,3 @@ operator<< (std::ostream &os, const vpAdaptiveGain& lambda) return os; } - - - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff --git a/src/servo/vpAdaptiveGain.h b/src/servo/vpAdaptiveGain.h index aa28a122102b9aae8715c7b1191eb4e34d59d8a1..766cc8dc086195fe949255764191162ef963f0de 100644 --- a/src/servo/vpAdaptiveGain.h +++ b/src/servo/vpAdaptiveGain.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAdaptiveGain.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAdaptiveGain.h 4641 2014-02-05 12:42:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,19 +59,18 @@ class vpColVector; The formula used to compute the gain is the following : - \f[ lambda (x) = a * exp (-b*x) + c \f] + \f[ \lambda (x) = a * exp (-b*x) + c \f] - where \f$ a \f$, \f$ b \f$ and \f$ c \f$ are parameters which must be set - and \f$ x \f$ is the vector error of the task. + where \f$ a \f$, \f$ b \f$ and \f$ c \f$ are constant parameters and \f$ x \f$ is the entry to consider. - By default, the parameters are set with default values: - \f[ a = lambda(0) - lambda(inf) \f] - \f[ b = lambda'(0) / a \f] - \f[ c = lambda(inf) \f] + The parameters \f$a,b,c\f$ are not set directly. They are computed from three other parameters + \f$\lambda(0), \lambda(\infty), {\dot \lambda}(0)\f$ that are more intuitive to tune: + \f[ a = \lambda(0) - \lambda(\infty) \f] + \f[ b = {\dot \lambda}(0) / a \f] + \f[ c = \lambda(\infty) \f] - with \f$ lambda(0) = 1.666 \f$, \f$ lambda(inf) = 0.1666 \f$ and \f$ lambda'(0) = 1.666 \f$. - - \f$ lambda(0)\f$ represents the gain in 0, \f$ lambda(inf)\f$ represents the gain to infinity and \f$ lambda'(0)\f$ represents the slope in 0. + where \f$ \lambda(0)\f$ represents the gain when \f$x=0\f$, \f$ \lambda(\infty)\f$ represents the gain when \f$x=\infty\f$ + and \f$ {\dot \lambda}(0)\f$ represents the slope of \f$\lambda(x)\f$ when \f$x=0\f$. */ @@ -81,8 +80,8 @@ class VISP_EXPORT vpAdaptiveGain public: /* constantes */ static const double DEFAULT_LAMBDA_ZERO; - static const double DEFAULT_LAMBDA_INFINI; - static const double DEFAULT_LAMBDA_PENTE; + static const double DEFAULT_LAMBDA_INFINITY; + static const double DEFAULT_LAMBDA_SLOPE; private: /* Attributs*/ @@ -101,14 +100,19 @@ public: /* Methodes*/ /* --- CONSTRUCTOR -------------------------------------------------------- */ - vpAdaptiveGain (void); + vpAdaptiveGain (); + vpAdaptiveGain (double c); + vpAdaptiveGain (double gain_at_zero, + double gain_at_infinity, + double slope_at_zero); + /* --- INIT --------------------------------------------------------------- */ - void initFromConstant (double lambda); + void initFromConstant (double c); void initFromVoid (void); - void initStandard (double en_zero, - double en_infini, - double pente_en_zero); + void initStandard (double gain_at_zero, + double gain_at_infinity, + double slope_at_zero); /* --- MODIFIORS ---------------------------------------------------------- */ @@ -128,7 +132,7 @@ public: /* Methodes*/ * \param val_e: valeur de la norme de l'erreur. * \return: valeur de gain au point courrant. */ - double value_const (double val_e) const; + double value_const (double x) const; /* \brief Calcule la valeur de lambda au point courrant et stockage du * resultat. @@ -139,7 +143,7 @@ public: /* Methodes*/ * \param val_e: valeur de la norme de l'erreur. * \return: valeur de gain au point courrant. */ - double value (double val_e) const; + double value (double x) const; double limitValue_const (void) const; @@ -152,12 +156,12 @@ public: /* Methodes*/ \return It returns the last adaptive gain value which was stored in the class. */ - inline double getLastValue (void) const {return this ->lambda;} + inline double getLastValue (void) const {return this ->lambda;} - double operator() (double val_e) const; + double operator() (double x) const; /* \brief Lance la fonction valeur avec la norme INFINIE du vecteur. */ - double operator() (const vpColVector & e) const; + double operator() (const vpColVector & x) const; /* \brief Idem function limitValue. */ double operator() (void) const; @@ -165,17 +169,7 @@ public: /* Methodes*/ /* --- IOSTREAM ----------------------------------------------------------- */ - friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, - const vpAdaptiveGain& lambda); + friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpAdaptiveGain& lambda); }; #endif /* __VP_ADAPTIVE_GAIN_H */ - - - - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff --git a/src/servo/vpServo.cpp b/src/servo/vpServo.cpp index 0e378b3a6e2b55dc85107d73e5b5cf109eeaefe1..04ebe2c7f0d1caddf6a39bd2dff1001d49dfa504 100644 --- a/src/servo/vpServo.cpp +++ b/src/servo/vpServo.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServo.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServo.cpp 5219 2015-01-28 10:29:21Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,30 +58,46 @@ /*! - Default constructor. - - By default: - - - the interaction matrix \f$ L \f$ is computed with the desired - features \f$ s^*\f$ (\f$ L=L_{s^*}\f$). With - setInteractionMatrixType() you can also use the interaction matrix - with the current visual features \f$L_s\f$, or the mean \f$(L_s / - 2 + L_{s^*} / 2)\f$. - - - the control law is build from the pseudo inverse of the - interaction matrix \f$ v = - \lambda \; L^+ \; e\f$. With - setInteractionMatrixType() can also set the usage of the transpose - \f$ v = \lambda \; L^t \; e\f$ . + Default constructor that initializes the following settings: + - No control law is specified. The user has to call setServo() to specify the control law. + - In the control law, the interaction matrix \f${\widehat {\bf L}}_e \f$ is computed with the desired + features \f${\bf s}^*\f$. Using setInteractionMatrixType() you can also compute the + interaction matrix with the current visual features, or from the mean + \f$\left({\widehat {\bf L}}_s + {\widehat {\bf L}}_{s^*}\right)/2\f$. + - In the control law the pseudo inverse will be used. The method setInteractionMatrixType() + allows to use the transpose instead. */ vpServo::vpServo() + : L(), error(), J1(), J1p(), s(), sStar(), e1(), e(), q_dot(), v(), servoType(vpServo::NONE), + rankJ1(0), featureList(), desiredFeatureList(), featureSelectionList(), lambda(), signInteractionMatrix(1), + interactionMatrixType(DESIRED), inversionType(PSEUDO_INVERSE), cVe(), init_cVe(false), + cVf(), init_cVf(false), fVe(), init_fVe(false), eJe(), init_eJe(false), fJe(), init_fJe(false), + errorComputed(false), interactionMatrixComputed(false), dim_task(0), taskWasKilled(false), + forceInteractionMatrixComputation(false), WpW(), I_WpW(), sv(), mu(4.) { - init() ; } +/*! + Constructor that allows to choose the visual servoing control law. + \param servo_type : Visual servoing control law. -vpServo::vpServo(vpServoType _servoType) + The other settings are the following: + - In the control law, the interaction matrix \f${\widehat {\bf L}}_e \f$ is computed with the desired + features \f${\bf s}^*\f$. Using setInteractionMatrixType() you can also compute the + interaction matrix with the current visual features, or from the mean + \f$\left({\widehat {\bf L}}_s + {\widehat {\bf L}}_{s^*}\right)/2\f$. + - In the control law the pseudo inverse will be used. The method setInteractionMatrixType() + allows to use the transpose instead. + + */ +vpServo::vpServo(vpServoType servo_type) + : L(), error(), J1(), J1p(), s(), sStar(), e1(), e(), q_dot(), v(), servoType(servo_type), + rankJ1(0), featureList(), desiredFeatureList(), featureSelectionList(), lambda(), signInteractionMatrix(1), + interactionMatrixType(DESIRED), inversionType(PSEUDO_INVERSE), cVe(), init_cVe(false), + cVf(), init_cVf(false), fVe(), init_fVe(false), eJe(), init_eJe(false), fJe(), init_fJe(false), + errorComputed(false), interactionMatrixComputed(false), dim_task(0), taskWasKilled(false), + forceInteractionMatrixComputation(false), WpW(), I_WpW(), sv(), mu(4) { - setServo(_servoType); } /*! @@ -108,24 +124,19 @@ vpServo::~vpServo() /*! - Initialize the servo. - - By default: + Initialize the servo with the following settings: - - the interaction matrix \f$ L \f$ is computed with the desired - features \f$ s^*\f$ (\f$ L=L_{s^*}\f$). With - setInteractionMatrixType() you can also use the interaction matrix - with the current visual features \f$L_s\f$, or the mean \f$(L_s / - 2 + L_{s^*} / 2)\f$. + - No control law is specified. The user has to call setServo() to specify the control law. + - In the control law, the interaction matrix \f${\widehat {\bf L}}_e \f$ is computed with the desired + features \f${\bf s}^*\f$. Using setInteractionMatrixType() you can also compute the + interaction matrix with the current visual features, or from the mean + \f$\left({\widehat {\bf L}}_s + {\widehat {\bf L}}_{s^*}\right)/2\f$. + - In the control law the pseudo inverse will be used. The method setInteractionMatrixType() + allows to use the transpose instead. - - the control law is build from the pseudo inverse of the - interaction matrix \f$ v = - \lambda \; L^+ \; e\f$. With - setInteractionMatrixType() can also set the usage of the transpose - \f$ v = \lambda \; L^t \; e\f$ . */ -void - vpServo::init() +void vpServo::init() { // type of visual servoing servoType = vpServo::NONE ; @@ -140,9 +151,9 @@ void dim_task = 0 ; - featureList.kill() ; - desiredFeatureList.kill() ; - featureSelectionList.kill(); + featureList.clear() ; + desiredFeatureList.clear() ; + featureSelectionList.clear(); signInteractionMatrix = 1 ; @@ -155,6 +166,8 @@ void taskWasKilled = false; forceInteractionMatrixComputation = false; + + rankJ1 = 0; } /*! @@ -173,56 +186,46 @@ void \endcode */ -void - vpServo::kill() +void vpServo::kill() { if (taskWasKilled == false) { // kill the current and desired feature lists - featureList.front() ; - desiredFeatureList.front() ; - while (!featureList.outside()) { - vpBasicFeature *s_ptr = NULL; - - // current list - s_ptr= featureList.value() ; - if (s_ptr->getDeallocate() == vpBasicFeature::vpServo) - { - delete s_ptr ; - s_ptr = NULL ; + // current list + for(std::list<vpBasicFeature *>::iterator it = featureList.begin(); it != featureList.end(); ++it) { + if ((*it)->getDeallocate() == vpBasicFeature::vpServo) { + delete (*it) ; + (*it) = NULL ; } - + } //desired list - s_ptr= desiredFeatureList.value() ; - if (s_ptr->getDeallocate() == vpBasicFeature::vpServo) - { - // s_ptr->print() ; - delete s_ptr ; - s_ptr = NULL ; + for(std::list<vpBasicFeature *>::iterator it = desiredFeatureList.begin(); it != desiredFeatureList.end(); ++it) { + if ((*it)->getDeallocate() == vpBasicFeature::vpServo) { + delete (*it) ; + (*it) = NULL ; } - - desiredFeatureList.next() ; - featureList.next() ; } - featureList.kill() ; - desiredFeatureList.kill() ; + + featureList.clear() ; + desiredFeatureList.clear() ; taskWasKilled = true; } } -void - vpServo::setServo(vpServoType _servoType) +/*! + Set the visual servoing control law. + \param servo_type : Control law that will be considered. + See vpServo::vpServoType to see the possible values. + */ +void vpServo::setServo(const vpServoType &servo_type) { - - servoType = _servoType ; + this->servoType = servo_type ; if ((servoType==EYEINHAND_CAMERA) ||(servoType==EYEINHAND_L_cVe_eJe)) signInteractionMatrix = 1 ; else signInteractionMatrix = -1 ; - - // when the control is directly compute in the camera frame // we relieve the end-user to initialize cVa and aJe if (servoType==EYEINHAND_CAMERA) @@ -233,7 +236,6 @@ void _eJe.eye(6) ; set_eJe(_eJe) ; }; - } @@ -241,16 +243,7 @@ void Prints on \e os stream information about the task: - \param displayLevel : If vpServo::ALL prints - - Type of control law (eye-in-hand, eye-to-hand) - - Control frame (camera frame, joint space) - - List of current visual features : s - - List of desired visual features : s* - - Interaction Matrix Ls - - Error vector (s-s*) - - Gain of the controller - - \param displayLevel : If vpServo::MINIMUM prints only the error vector (s-s*). + \param displayLevel : Indicates which are the task informations to print. See vpServo::vpServoPrintType for more details. \param os : Output stream. */ @@ -259,11 +252,8 @@ void { switch (displayLevel) { - case vpServo::ALL: - { - os << "Visual servoing task: " <<std::endl ; os << "Type of control law " <<std::endl ; @@ -295,30 +285,21 @@ void } os << "List of visual features : s" <<std::endl ; - for (featureList.front(), - featureSelectionList.front() ; - !featureList.outside() ; - featureList.next(), - featureSelectionList.next() ) + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<vpBasicFeature *>::const_iterator it_s_star; + std::list<unsigned int>::const_iterator it_select; + + for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end(); ++it_s, ++it_select) { - vpBasicFeature *s ; - s = featureList.value(); os << "" ; - s->print(featureSelectionList.value()) ; + (*it_s)->print( (*it_select) ) ; } - os << "List of desired visual features : s*" <<std::endl ; - for (desiredFeatureList.front(), - featureSelectionList.front() ; - !desiredFeatureList.outside() ; - desiredFeatureList.next(), - featureSelectionList.next() ) + for (it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin(); it_s_star != desiredFeatureList.end(); ++it_s_star, ++it_select) { - vpBasicFeature *s ; - s = desiredFeatureList.value(); os << "" ; - s->print(featureSelectionList.value()) ; + (*it_s_star)->print( (*it_select) ) ; } os <<"Interaction Matrix Ls "<<std::endl ; @@ -327,7 +308,9 @@ void os << L << std::endl; } else - {os << "not yet computed "<<std::endl ;} + { + os << "not yet computed "<<std::endl ; + } os <<"Error vector (s-s*) "<<std::endl ; if (errorComputed) @@ -335,7 +318,9 @@ void os << error.t() << std::endl; } else - {os << "not yet computed "<<std::endl ;} + { + os << "not yet computed "<<std::endl ; + } os << "Gain : " << lambda <<std::endl ; @@ -377,33 +362,28 @@ void case vpServo::FEATURE_CURRENT: { os << "List of visual features : s" <<std::endl ; - for (featureList.front(), - featureSelectionList.front() ; - !featureList.outside() ; - featureList.next(), - featureSelectionList.next() ) + + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<unsigned int>::const_iterator it_select; + + for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end(); ++it_s, ++it_select) { - vpBasicFeature *s ; - s = featureList.value(); os << "" ; - s->print(featureSelectionList.value()) ; + (*it_s)->print( (*it_select) ) ; } - break; } case vpServo::FEATURE_DESIRED: { os << "List of desired visual features : s*" <<std::endl ; - for (desiredFeatureList.front(), - featureSelectionList.front() ; - !desiredFeatureList.outside() ; - desiredFeatureList.next(), - featureSelectionList.next() ) + + std::list<vpBasicFeature *>::const_iterator it_s_star; + std::list<unsigned int>::const_iterator it_select; + + for (it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin(); it_s_star != desiredFeatureList.end(); ++it_s_star, ++it_select) { - vpBasicFeature *s ; - s = desiredFeatureList.value(); os << "" ; - s->print(featureSelectionList.value()) ; + (*it_s_star)->print( (*it_select) ) ; } break; } @@ -415,12 +395,12 @@ void case vpServo::INTERACTION_MATRIX: { os <<"Interaction Matrix Ls "<<std::endl ; - if (interactionMatrixComputed) - { + if (interactionMatrixComputed) { os << L << std::endl; } - else - {os << "not yet computed "<<std::endl ;} + else { + os << "not yet computed "<<std::endl ; + } break; } @@ -429,33 +409,76 @@ void { os <<"Error vector (s-s*) "<<std::endl ; - if (errorComputed) - { os << error.t() << std::endl; } - else - {os << "not yet computed "<<std::endl ;} + if (errorComputed) { + os << error.t() << std::endl; + } + else { + os << "not yet computed "<<std::endl ; + } break; } } } -//! create a new set of 2 features in the task -void - vpServo::addFeature(vpBasicFeature& s, vpBasicFeature &s_star, unsigned int select) +/*! + Add a new set of 2 features \f$\bf s\f$ and \f${\bf s}^*\f$ in the task. + + \param s_cur : Current visual feature denoted \f$\bf s\f$. + \param s_star : Desired visual feature denoted \f${\bf s}^*\f$. + \param select : Feature selector. By default all the features in \e s and \e s_star are used, + but is is possible to specify which one is used in case of multiple features. + + The following sample code explain how to use this method to add a visual feature point \f$(x,y)\f$: + \code + vpFeaturePoint s, s_star; + ... + vpServo task; + task.addFeature(s, s_star); + \endcode + + For example to use only the \f$x\f$ visual feature, the previous code becomes: + \code + vpFeaturePoint s, s_star; + ... + vpServo task; + task.addFeature(s, s_star, vpFeaturePoint::selectX()); + \endcode + + */ +void vpServo::addFeature(vpBasicFeature& s_cur, vpBasicFeature &s_star, unsigned int select) { - featureList += &s ; - desiredFeatureList += &s_star ; - featureSelectionList+= select ; + featureList.push_back( &s_cur ); + desiredFeatureList.push_back( &s_star ); + featureSelectionList.push_back( select ); } /*! - \brief create a new visual features in the task - (the desired feature is then null) -*/ -void - vpServo::addFeature(vpBasicFeature& s, unsigned int select) + Add a new features \f$\bf s\f$ in the task. The desired visual feature denoted \f${\bf s}^*\f$ is equal to zero. + + \param s_cur : Current visual feature denoted \f$\bf s\f$. + \param select : Feature selector. By default all the features in \e s are used, + but is is possible to specify which one is used in case of multiple features. + + The following sample code explain how to use this method to add a \f$\theta {\bf u} =(\theta u_x, \theta u_y, \theta u_z)\f$ feature: + \code + vpFeatureThetaU s(vpFeatureThetaU::cRcd); + ... + vpServo task; + task.addFeature(s); + \endcode + + For example to use only the \f$\theta u_x\f$ feature, the previous code becomes: + \code + vpFeatureThetaU s(vpFeatureThetaU::cRcd); + ... + vpServo task; + task.addFeature(s, vpFeatureThetaU::selectTUx); + \endcode + */ +void vpServo::addFeature(vpBasicFeature& s_cur, unsigned int select) { - featureList += &s ; + featureList.push_back( &s_cur ); // in fact we have a problem with s_star that is not defined // by the end user. @@ -472,51 +495,41 @@ void // vpServo must deallocate the memory (see ~vpServo and kill() ) vpBasicFeature *s_star ; - s_star = s.duplicate() ; + s_star = s_cur.duplicate() ; s_star->init() ; s_star->setDeallocate(vpBasicFeature::vpServo) ; - desiredFeatureList += s_star ; - featureSelectionList+= select ; + desiredFeatureList.push_back( s_star ); + featureSelectionList.push_back( select ); } -//! get the task dimension -unsigned int - vpServo::getDimension() +//! Return the task dimension. +unsigned int vpServo::getDimension() const { + unsigned int dim = 0 ; + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<unsigned int>::const_iterator it_select; - featureList.front() ; - featureSelectionList.front() ; - - dim_task =0 ; - while (!featureList.outside()) + for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end(); ++it_s, ++it_select) { - vpBasicFeature *s_ptr = NULL; - s_ptr= featureList.value() ; - unsigned int select = (unsigned int)featureSelectionList.value() ; - - dim_task += (unsigned int)s_ptr->getDimension(select) ; - - featureSelectionList.next() ; - featureList.next() ; + dim += (*it_s)->getDimension(*it_select) ; } - return dim_task ; + return dim; } -void - vpServo::setInteractionMatrixType(const vpServoIteractionMatrixType &_interactionMatrixType, const vpServoInversionType &_interactionMatrixInversion) +void vpServo::setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrix_type, + const vpServoInversionType &interactionMatrixInversion) { - interactionMatrixType = _interactionMatrixType ; - inversionType = _interactionMatrixInversion ; + this->interactionMatrixType = interactionMatrix_type ; + this->inversionType = interactionMatrixInversion ; } -static void - computeInteractionMatrixFromList (/*const*/ vpList<vpBasicFeature *> & featureList, - /*const*/ vpList<unsigned int> & featureSelectionList, - vpMatrix & L) +static void computeInteractionMatrixFromList (const std::list<vpBasicFeature *> & featureList, + const std::list<unsigned int> & featureSelectionList, + vpMatrix & L) { if (featureList.empty()) { @@ -539,7 +552,10 @@ static void * initialized to dim 1.*/ unsigned int rowL = L.getRows(); const unsigned int colL = 6; - if (0 == rowL) { rowL = 1; L .resize(rowL, colL);} + if (0 == rowL) { + rowL = 1; + L .resize(rowL, colL); + } /* vectTmp is used to store the return values of functions get_s() and * error(). */ @@ -551,27 +567,29 @@ static void * is out of the vector-array range.*/ unsigned int cursorL = 0; - for (featureList.front() ,featureSelectionList.front() ; - !featureList.outside(); - featureSelectionList.next(),featureList.next() ) - { - vpBasicFeature * sPTR = featureList.value() ; - const unsigned int select = featureSelectionList.value() ; + std::list<vpBasicFeature *>::const_iterator it; + std::list<unsigned int>::const_iterator it_select; + for (it = featureList.begin(), it_select = featureSelectionList.begin(); it != featureList.end(); ++it, ++it_select) + { /* Get s. */ - matrixTmp = sPTR->interaction(select); + matrixTmp = (*it)->interaction( *it_select ); rowMatrixTmp = matrixTmp .getRows(); colMatrixTmp = matrixTmp .getCols(); /* Check the matrix L size, and realloc if needed. */ - while (rowMatrixTmp + cursorL > rowL) - { rowL *= 2; L .resize (rowL,colL,false); vpDEBUG_TRACE(15,"Realloc!"); } + while (rowMatrixTmp + cursorL > rowL) { + rowL *= 2; + L.resize (rowL,colL,false); + vpDEBUG_TRACE(15,"Realloc!"); + } /* Copy the temporarily matrix into L. */ - for (unsigned int k = 0; k < rowMatrixTmp; ++k, ++cursorL) - for (unsigned int j = 0; j < colMatrixTmp; ++j) - { L[cursorL][j] = matrixTmp[k][j]; } - + for (unsigned int k = 0; k < rowMatrixTmp; ++k, ++cursorL) { + for (unsigned int j = 0; j < colMatrixTmp; ++j) { + L[cursorL][j] = matrixTmp[k][j]; + } + } } L.resize (cursorL,colL,false); @@ -582,14 +600,12 @@ static void /*! - Compute the interaction matrix related to the set of visual features. + Compute and return the interaction matrix related to the set of visual features. - \return Ls + \return The interaction matrix \f${\widehat {\bf L}}_e\f$ used in the control law specified using setServo(). */ -vpMatrix - vpServo::computeInteractionMatrix() +vpMatrix vpServo::computeInteractionMatrix() { - try { switch (interactionMatrixType) @@ -671,15 +687,13 @@ vpMatrix /*! - Compute the error \f$(s - s^*)\f$ between the current set of visual - features \f$s\f$ and the desired set of visual features \f$s^*\f$. + Compute the error \f$\bf e =(s - s^*)\f$ between the current set of visual + features \f$\bf s\f$ and the desired set of visual features \f$\bf s^*\f$. - \return The error \f$(s - s^*)\f$. To access to the computed error - you can also use the public vpServo::error class member. + \return The error vector \f$\bf e\f$. */ -vpColVector - vpServo::computeError() +vpColVector vpServo::computeError() { if (featureList.empty()) { @@ -732,19 +746,17 @@ vpColVector unsigned int cursorError = 0; /* For each cell of the list, copy value of s, s_star and error. */ - for (featureList.front(), - desiredFeatureList.front(), - featureSelectionList.front() ; - - !featureList.outside() ; + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<vpBasicFeature *>::const_iterator it_s_star; + std::list<unsigned int>::const_iterator it_select; - featureList.next(), - desiredFeatureList.next(), - featureSelectionList.next() ) + for (it_s = featureList.begin(), it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin(); + it_s != featureList.end(); + ++it_s, ++it_s_star, ++it_select) { - current_s = featureList.value() ; - desired_s = desiredFeatureList.value() ; - unsigned int select = featureSelectionList.value() ; + current_s = (*it_s); + desired_s = (*it_s_star); + unsigned int select = (*it_select); /* Get s, and store it in the s vector. */ vectTmp = current_s->get_s(select); @@ -753,22 +765,27 @@ vpColVector { dimS *= 2; s .resize (dimS,false); vpDEBUG_TRACE(15,"Realloc!"); } for (unsigned int k = 0; k < dimVectTmp; ++k) { s[cursorS++] = vectTmp[k]; } - /* Get s_star, and store it in the s vector. */ vectTmp = desired_s->get_s(select); dimVectTmp = vectTmp .getRows(); - while (dimVectTmp + cursorSStar > dimSStar) - { dimSStar *= 2; sStar .resize (dimSStar,false); } - for (unsigned int k = 0; k < dimVectTmp; ++k) - { sStar[cursorSStar++] = vectTmp[k]; } + while (dimVectTmp + cursorSStar > dimSStar) { + dimSStar *= 2; + sStar.resize (dimSStar,false); + } + for (unsigned int k = 0; k < dimVectTmp; ++k) { + sStar[cursorSStar++] = vectTmp[k]; + } /* Get error, and store it in the s vector. */ vectTmp = current_s->error(*desired_s, select) ; dimVectTmp = vectTmp .getRows(); - while (dimVectTmp + cursorError > dimError) - { dimError *= 2; error .resize (dimError,false); } - for (unsigned int k = 0; k < dimVectTmp; ++k) - { error[cursorError++] = vectTmp[k]; } + while (dimVectTmp + cursorError > dimError) { + dimError *= 2; + error.resize (dimError,false); + } + for (unsigned int k = 0; k < dimVectTmp; ++k) { + error[cursorError++] = vectTmp[k]; + } } /* If too much memory has been allocated, realloc. */ @@ -792,8 +809,7 @@ vpColVector return error ; } -bool - vpServo::testInitialization() +bool vpServo::testInitialization() { switch (servoType) { @@ -813,7 +829,7 @@ bool break ; case EYETOHAND_L_cVf_fVe_eJe: if (!init_cVf) vpERROR_TRACE("cVf not initialized") ; - if (!init_fJe) vpERROR_TRACE("fVe not initialized") ; + if (!init_fVe) vpERROR_TRACE("fVe not initialized") ; if (!init_eJe) vpERROR_TRACE("eJe not initialized") ; return (init_cVf && init_fVe && init_eJe) ; break ; @@ -827,8 +843,7 @@ bool return false ; } -bool - vpServo::testUpdated() +bool vpServo::testUpdated() { switch (servoType) { @@ -863,26 +878,197 @@ bool return false ; } /*! - \brief compute the desired control law - Compute the control law according to the equation: + Compute the control law specified using setServo(). See vpServo::vpServoType for more + details concerning the control laws that are available. The \ref tutorial-ibvs and \ref tutorial-boost-vs + are also useful to illustrate the usage of this function. + + The general form of the control law is the following: + \f[ - -\lambda {\bf W^+W\widehat J_s^+(s-s^*)} + {\bf \dot q} = \pm \lambda {{\bf \widehat J}_e}^+ {\bf e} \f] - or + + where : + - \f${\bf \dot q}\f$ is the resulting velocity command to apply to the robot. + - the sign of the control law depends on the eye in hand or eye to hand configuration. + - \f$\bf J\f$ is the Jacobian of the task. It is function of the interaction matrix and of the robot + Jacobian. + - \f$\bf e = (s-s^*)\f$ is the error to regulate. + + To ensure continuous sequencing the computeControlLaw(double) function can be used. It will ensure that + the velocities that are computed are continuous. +*/ +vpColVector vpServo::computeControlLaw() +{ + static int iteration =0; + + try + { + vpVelocityTwistMatrix cVa ; // Twist transformation matrix + vpMatrix aJe ; // Jacobian + + if (iteration==0) + { + if (testInitialization() == false) { + vpERROR_TRACE("All the matrices are not correctly initialized") ; + throw(vpServoException(vpServoException::servoError, + "Cannot compute control law " + "All the matrices are not correctly" + "initialized")) ; + } + } + if (testUpdated() == false) { + vpERROR_TRACE("All the matrices are not correctly updated") ; + } + + // test if all the required initialization have been done + switch (servoType) + { + case NONE : + vpERROR_TRACE("No control law have been yet defined") ; + throw(vpServoException(vpServoException::servoError, + "No control law have been yet defined")) ; + break ; + case EYEINHAND_CAMERA: + case EYEINHAND_L_cVe_eJe: + case EYETOHAND_L_cVe_eJe: + + cVa = cVe ; + aJe = eJe ; + + init_cVe = false ; + init_eJe = false ; + break ; + case EYETOHAND_L_cVf_fVe_eJe: + cVa = cVf*fVe ; + aJe = eJe ; + init_fVe = false ; + init_eJe = false ; + break ; + case EYETOHAND_L_cVf_fJe : + cVa = cVf ; + aJe = fJe ; + init_fJe = false ; + break ; + } + + computeInteractionMatrix() ; + computeError() ; + + // compute task Jacobian + J1 = L*cVa*aJe ; + + // handle the eye-in-hand eye-to-hand case + J1 *= signInteractionMatrix ; + + // pseudo inverse of the task Jacobian + // and rank of the task Jacobian + // the image of J1 is also computed to allows the computation + // of the projection operator + vpMatrix imJ1t, imJ1 ; + bool imageComputed = false ; + + if (inversionType==PSEUDO_INVERSE) + { + rankJ1 = J1.pseudoInverse(J1p, sv, 1e-6, imJ1, imJ1t) ; + + imageComputed = true ; + } + else + J1p = J1.t() ; + + if (rankJ1 == J1.getCols()) + { + /* if no degrees of freedom remains (rank J1 = ndof) + WpW = I, multiply by WpW is useless + */ + e1 = J1p*error ;// primary task + + WpW.resize(J1.getCols(), J1.getCols()) ; + WpW.setIdentity() ; + } + else + { + if (imageComputed!=true) + { + vpMatrix Jtmp ; + // image of J1 is computed to allows the computation + // of the projection operator + rankJ1 = J1.pseudoInverse(Jtmp, sv, 1e-6, imJ1, imJ1t) ; + imageComputed = true ; + } + WpW = imJ1t*imJ1t.t() ; + +#ifdef DEBUG + std::cout << "rank J1 " << rankJ1 <<std::endl ; + std::cout << "imJ1t"<<std::endl << imJ1t ; + std::cout << "imJ1"<<std::endl << imJ1 ; + + std::cout << "WpW" <<std::endl <<WpW ; + std::cout << "J1" <<std::endl <<J1 ; + std::cout << "J1p" <<std::endl <<J1p ; +#endif + e1 = WpW*J1p*error ; + } + e = - lambda(e1) * e1 ; + + vpMatrix I ; + + I.resize(J1.getCols(),J1.getCols()) ; + I.setIdentity() ; + + I_WpW = (I - WpW) ; + } + catch(vpMatrixException me) + { + vpERROR_TRACE("Caught a matrix related error") ; + std::cout << me << std::endl ; + throw me; + } + catch(vpException me) + { + vpERROR_TRACE("Error caught") ; + std::cout << me << std::endl ; + throw me ; + } + + iteration++ ; + return e ; +} + +/*! + Compute the control law specified using setServo(). See vpServo::vpServoType for more + details concerning the control laws that are available. The \ref tutorial-boost-vs is also useful to + illustrate the usage of this function. + + To the general form of the control law given in computeControlLaw(), we add here an additional term that comes from + the task sequencing approach described in \cite Mansard07e equation (17). This additional term allows to compute + continuous velocities by avoiding abrupt changes in the command. + + The form of the control law considered here is the following: + \f[ - -\lambda {\bf\widehat J_s^+(s-s^*)} + {\bf \dot q} = \pm \lambda {{\bf \widehat J}_e}^+ {\bf e} \mp \lambda {{\bf \widehat J}_{e(0)}}^+ {{\bf e}(0)} \exp(-\mu t) \f] - if the dimension of the task is equal to number of available degrees of freedom (in that case \f${\bf W^+W = I}\f$) - in this equation Js is function of the interaction matrix and of the robot - Jacobian. It is also built according to the chosen configuration eye-in-hand - or eye-to-hand (see vpServo::setServo method) + where : + - \f${\bf \dot q}\f$ is the resulting continuous velocity command to apply to the robot. + - the sign of the control law depends on the eye in hand or eye to hand configuration. + - \f$\bf J\f$ is the Jacobian of the task. It is function of the interaction matrix and of the robot + Jacobian. + - \f$\bf e = (s-s^*)\f$ is the error to regulate. + - \f$t\f$ is the time given as parameter of this method. + - \f$\mu\f$ is a gain that is set by default to 4 and that could be modified using setMu(). + - \f${\bf \widehat J}_{e(0)}^+ {\bf e}(0)\f$ is the value of \f${\bf \widehat J}_e^+ {\bf e}\f$ when \f$t=0\f$. + This value is internally stored either at the first call of this method, or when \e t parameter is set to 0. + + \param t : Time in second. When set to zero, \f${{\bf \widehat J}_{e(0)}}^+ {{\bf e}(0)}\f$ is refreshed internally. */ -vpColVector - vpServo::computeControlLaw() +vpColVector vpServo::computeControlLaw(double t) { static int iteration =0; + //static vpColVector e1_initial; try { @@ -891,24 +1077,190 @@ vpColVector if (iteration==0) { - if (testInitialization() == true) - { - } - else - { - vpERROR_TRACE("All the matrices are not correctly initialized") ; - throw(vpServoException(vpServoException::servoError, + if (testInitialization() == false) { + vpERROR_TRACE("All the matrices are not correctly initialized") ; + throw(vpServoException(vpServoException::servoError, "Cannot compute control law " "All the matrices are not correctly" "initialized")) ; - } + } + } + if (testUpdated() == false) { + vpERROR_TRACE("All the matrices are not correctly updated") ; + } + + // test if all the required initialization have been done + switch (servoType) + { + case NONE : + vpERROR_TRACE("No control law have been yet defined") ; + throw(vpServoException(vpServoException::servoError, + "No control law have been yet defined")) ; + break ; + case EYEINHAND_CAMERA: + case EYEINHAND_L_cVe_eJe: + case EYETOHAND_L_cVe_eJe: + + cVa = cVe ; + aJe = eJe ; + + init_cVe = false ; + init_eJe = false ; + break ; + case EYETOHAND_L_cVf_fVe_eJe: + cVa = cVf*fVe ; + aJe = eJe ; + init_fVe = false ; + init_eJe = false ; + break ; + case EYETOHAND_L_cVf_fJe : + cVa = cVf ; + aJe = fJe ; + init_fJe = false ; + break ; + } + + computeInteractionMatrix() ; + computeError() ; + + // compute task Jacobian + J1 = L*cVa*aJe ; + + // handle the eye-in-hand eye-to-hand case + J1 *= signInteractionMatrix ; + + // pseudo inverse of the task Jacobian + // and rank of the task Jacobian + // the image of J1 is also computed to allows the computation + // of the projection operator + vpMatrix imJ1t, imJ1 ; + bool imageComputed = false ; + + if (inversionType==PSEUDO_INVERSE) + { + rankJ1 = J1.pseudoInverse(J1p, sv, 1e-6, imJ1, imJ1t) ; + + imageComputed = true ; } - if (testUpdated() == true) + else + J1p = J1.t() ; + + if (rankJ1 == J1.getCols()) { - // os << " Init OK " << std::endl ; + /* if no degrees of freedom remains (rank J1 = ndof) + WpW = I, multiply by WpW is useless + */ + e1 = J1p*error ;// primary task + + WpW.resize(J1.getCols(), J1.getCols()) ; + WpW.setIdentity() ; } else { + if (imageComputed!=true) + { + vpMatrix Jtmp ; + // image of J1 is computed to allows the computation + // of the projection operator + rankJ1 = J1.pseudoInverse(Jtmp, sv, 1e-6, imJ1, imJ1t) ; + imageComputed = true ; + } + WpW = imJ1t*imJ1t.t() ; + +#ifdef DEBUG + std::cout << "rank J1 " << rankJ1 <<std::endl ; + std::cout << "imJ1t"<<std::endl << imJ1t ; + std::cout << "imJ1"<<std::endl << imJ1 ; + + std::cout << "WpW" <<std::endl <<WpW ; + std::cout << "J1" <<std::endl <<J1 ; + std::cout << "J1p" <<std::endl <<J1p ; +#endif + e1 = WpW*J1p*error ; + } + + // memorize the initial e1 value if the function is called the first time or if the time given as parameter is equal to 0. + if (iteration==0 || std::fabs(t) < std::numeric_limits<double>::epsilon()) { + e1_initial = e1; + } + // Security check. If size of e1_initial and e1 differ, that means that e1_initial was not set + if (e1_initial.getRows() != e1.getRows()) + e1_initial = e1; + + e = - lambda(e1) * e1 + lambda(e1) * e1_initial*exp(-mu*t); + + vpMatrix I ; + + I.resize(J1.getCols(), J1.getCols()) ; + I.setIdentity() ; + + I_WpW = (I - WpW) ; + } + catch(vpMatrixException me) + { + vpERROR_TRACE("Caught a matrix related error") ; + std::cout << me << std::endl ; + throw me; + } + catch(vpException me) + { + vpERROR_TRACE("Error caught") ; + std::cout << me << std::endl ; + throw me ; + } + + iteration++ ; + return e ; +} + +/*! + Compute the control law specified using setServo(). See vpServo::vpServoType for more + details concerning the control laws that are available. + + To the general form of the control law given in computeControlLaw(), we add here an additional term that comes from + the task sequencing approach described in \cite Mansard07e equation (17). This additional term allows to compute + continuous velocities by avoiding abrupt changes in the command. + + The form of the control law considered here is the following: + + \f[ + {\bf \dot q} = \pm \lambda {{\bf \widehat J}_e}^+ {\bf e} + \left({\bf \dot e}(0) \mp \lambda {{\bf \widehat J}_{e(0)}}^+ {{\bf e}(0)}\right) \exp(-\mu t) + \f] + + where : + - \f${\bf \dot q}\f$ is the resulting continuous velocity command to apply to the robot. + - the sign of the control law depends on the eye in hand or eye to hand configuration. + - \f$\bf J\f$ is the Jacobian of the task. It is function of the interaction matrix and of the robot + Jacobian. + - \f$\bf e = (s-s^*)\f$ is the error to regulate. + - \f$t\f$ is the time given as parameter of this method. + - \f$\mu\f$ is a gain that is set by default to 4 and that could be modified using setMu(). + - \f${\bf \widehat J}_{e(0)}^+ {\bf e}(0)\f$ is the value of \f${\bf \widehat J}_e^+ {\bf e}\f$ when \f$t=0\f$. + This value is internally stored either at the first call of this method, or when \e t parameter is set to 0. + + \param t : Time in second. When set to zero, \f${{\bf \widehat J}_{e(0)}}^+ {{\bf e}(0)}\f$ is refreshed internally. + \param e_dot_init : Initial value of \f${\bf \dot e}(0)\f$. +*/ +vpColVector vpServo::computeControlLaw(double t, const vpColVector &e_dot_init) +{ + static int iteration =0; + + try + { + vpVelocityTwistMatrix cVa ; // Twist transformation matrix + vpMatrix aJe ; // Jacobian + + if (iteration==0) + { + if (testInitialization() == false) { + vpERROR_TRACE("All the matrices are not correctly initialized") ; + throw(vpServoException(vpServoException::servoError, + "Cannot compute control law " + "All the matrices are not correctly" + "initialized")) ; + } + } + if (testUpdated() == false) { vpERROR_TRACE("All the matrices are not correctly updated") ; } @@ -968,11 +1320,11 @@ vpColVector else J1p = J1.t() ; - if (rankJ1 == L.getCols()) + if (rankJ1 == J1.getCols()) { /* if no degrees of freedom remains (rank J1 = ndof) - WpW = I, multiply by WpW is useless - */ + WpW = I, multiply by WpW is useless + */ e1 = J1p*error ;// primary task WpW.resize(J1.getCols(), J1.getCols()) ; @@ -981,13 +1333,13 @@ vpColVector else { if (imageComputed!=true) - { - vpMatrix Jtmp ; - // image of J1 is computed to allows the computation - // of the projection operator + { + vpMatrix Jtmp ; + // image of J1 is computed to allows the computation + // of the projection operator rankJ1 = J1.pseudoInverse(Jtmp, sv, 1e-6, imJ1, imJ1t) ; - imageComputed = true ; - } + imageComputed = true ; + } WpW = imJ1t*imJ1t.t() ; #ifdef DEBUG @@ -1001,11 +1353,20 @@ vpColVector #endif e1 = WpW*J1p*error ; } - e = - lambda(e1) * e1 ; + + // memorize the initial e1 value if the function is called the first time or if the time given as parameter is equal to 0. + if (iteration==0 || std::fabs(t) < std::numeric_limits<double>::epsilon()) { + e1_initial = e1; + } + // Security check. If size of e1_initial and e1 differ, that means that e1_initial was not set + if (e1_initial.getRows() != e1.getRows()) + e1_initial = e1; + + e = - lambda(e1) * e1 + (e_dot_init + lambda(e1) * e1_initial)*exp(-mu*t); vpMatrix I ; - I.resize(J1.getCols(),J1.getCols()) ; + I.resize(J1.getCols(), J1.getCols()) ; I.setIdentity() ; I_WpW = (I - WpW) ; @@ -1029,30 +1390,37 @@ vpColVector /*! - Compute the secondary task according to the projection operator. - See equation (7) of the IEEE RA magazine, dec 2005 paper. + Compute and return the secondary task vector according to the projection operator \f${\bf I-W^+W}\f$. For more details, + see equation(7) in the paper \cite Marchand05b. - \warning computeControlLaw() must be call prior to this function. + \param de2dt : Value of \f$\frac{\partial {\bf e_2}}{\partial t}\f$ the derivative of the secondary task \f${\bf e}_2\f$. - Compute the vector: - \f[ - + ({\bf I-W^+W})\frac{\partial {\bf e_2}}{\partial t} + \return The secondary task vector: \f[ + ({\bf I-W^+W})\frac{\partial {\bf e_2}}{\partial t} \f] - to be added to the primary task + + Note that the secondary task vector need than to be added to the primary task which can be in the general case written as: \f[ - -\lambda {\bf W^+W\widehat J_s^+(s-s^*)} + -\lambda {\bf W^+W {\widehat {\bf J}}_e^+({\bf s-s^*})} \f] - which is computed using the computeControlLaw() method. - \warning The projection operator \f$ \bf W^+W \f$ is computed in - computeControlLaw() which must be called prior to this function. + \warning computeControlLaw() must be call prior to this function since it updates the projection operator \f$ \bf W^+W \f$. + + The following sample code shows how to use this method: + \code + vpColVector v; // Velocity applied to the robot + vpColVector de2dt; + vpServo task; + ... + v = task.computeControlLaw(); // Compute the primary task + v += task.secondaryTask(de2dt) // Compute and add the secondary task + \endcode \sa computeControlLaw() */ -vpColVector - vpServo::secondaryTask(vpColVector &de2dt) +vpColVector vpServo::secondaryTask(const vpColVector &de2dt) { - if (rankJ1 == L.getCols()) + if (rankJ1 == J1.getCols()) { vpERROR_TRACE("no degree of freedom is free, cannot use secondary task") ; throw(vpServoException(vpServoException::noDofFree, @@ -1077,30 +1445,40 @@ vpColVector } /*! - Compute the secondary task according to the projection operator. - See equation (7) of the IEEE RA magazine, dec 2005 paper. + Compute and return the secondary task vector according to the projection operator \f${\bf I-W^+W}\f$. For more details, + see equation(7) in the paper \cite Marchand05b. - \warning computeControlLaw() must be call prior to this function. + \param e2 : Value of the secondary task \f${\bf e}_2\f$. + \param de2dt : Value of \f$\frac{\partial {\bf e_2}}{\partial t}\f$ the derivative of the secondary task \f${\bf e}_2\f$. - Compute the vector: + \return The secondary task vector: \f[ -\lambda ({\bf I-W^+W}) {\bf e_2} + ({\bf I-W^+W})\frac{\partial {\bf e_2}}{\partial t} \f] - to be added to the primary task + + Note that the secondary task vector need than to be added to the primary task which can be in the general case written as: \f[ - -\lambda {\bf W^+W\widehat J_s^+(s-s^*)} + -\lambda {\bf W^+W {\widehat {\bf J}}_e^+({\bf s-s^*})} \f] - which is computed using computeControlLaw() method - \warning The projection operator \f$ \bf W^+W \f$ is computed in - computeControlLaw() which must be called prior to this function. + \warning computeControlLaw() must be call prior to this function since it updates the projection operator \f$ \bf W^+W \f$. + + The following sample code shows how to use this method: + \code + vpColVector v; // Velocity applied to the robot + vpColVector e2; + vpColVector de2dt; + vpServo task; + ... + v = task.computeControlLaw(); // Compute the primary task + v += task.secondaryTask(e2, de2dt) // Compute and add the secondary task + \endcode \sa computeControlLaw() */ -vpColVector - vpServo::secondaryTask(vpColVector &e2, vpColVector &de2dt) +vpColVector vpServo::secondaryTask(const vpColVector &e2, const vpColVector &de2dt) { - if (rankJ1 == L.getCols()) + if (rankJ1 == J1.getCols()) { vpERROR_TRACE("no degree of freedom is free, cannot use secondary task") ; throw(vpServoException(vpServoException::noDofFree, @@ -1127,9 +1505,91 @@ vpColVector } } +/*! + Return the projection operator \f${\bf I}-{\bf W}^+{\bf W}\f$. This operator is updated + after a call of computeControlLaw(). + +\code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + vpMatrix I_WpW = task.getI_WpW(); // Get the projection operator +\endcode + \sa getWpW() + */ +vpMatrix vpServo::getI_WpW() const +{ + return I_WpW; +} + +/*! + Return the task jacobian \f$J\f$. The task jacobian is updated after a call of computeControlLaw(). + + In the general case, the task jacobian is given by \f${\bf J} = {\widehat {\bf L}} {^c}{\bf V}_a {^a}{\bf J}_e\f$. +\code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + vpMatrix J = task.getTaskJacobian(); // Get the task jacobian used to compute v +\endcode + \sa getTaskJacobianPseudoInverse(), getInteractionMatrix() + */ +vpMatrix vpServo::getTaskJacobian() const +{ + return J1; +} +/*! + Return the pseudo inverse of the task jacobian \f$J\f$. + + In the general case, the task jacobian is given by \f${\bf J} = {\widehat {\bf L}} {^c}{\bf V}_a {^a}{\bf J}_e\f$. + + The task jacobian and its pseudo inverse are updated after a call of computeControlLaw(). -/* - * Local variables: - * c-basic-offset: 2 - * End: + \return Pseudo inverse \f${J}^{+}\f$ of the task jacobian. +\code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + vpMatrix Jp = task.getTaskJacobianPseudoInverse(); // Get the pseudo inverse of task jacobian used to compute v +\endcode + + \sa getTaskJacobian() + */ +vpMatrix vpServo::getTaskJacobianPseudoInverse() const +{ + return J1p; +} +/*! + Return the rank of the task jacobian. The rank is updated after a call of computeControlLaw(). + +\code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + unsigned int rank = task.getTaskRank(); // Get the rank of the task jacobian +\endcode + */ +unsigned int vpServo::getTaskRank() const +{ + return rankJ1; +} + +/*! + Return the projection operator \f${\bf W}^+{\bf W}\f$. This operator is updated + after a call of computeControlLaw(). + + When the dimension of the task is equal to the number of degrees of freedom available + \f${\bf W^+W = I}\f$. + +\code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + vpMatrix WpW = task.getWpW(); // Get the projection operator +\endcode + \sa getI_WpW() */ +vpMatrix vpServo::getWpW() const +{ + return WpW; +} diff --git a/src/servo/vpServo.h b/src/servo/vpServo.h index 38fb6600ef4e52d06e5e9fa13456a388c7b0e275..9c80a30513d45a39e888bf9419b1c7d0a45a0f8f 100644 --- a/src/servo/vpServo.h +++ b/src/servo/vpServo.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServo.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpServo.h 5219 2015-01-28 10:29:21Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,12 +50,12 @@ \brief Class required to compute the visual servoing control law */ +#include <list> + #include <visp/vpMatrix.h> #include <visp/vpVelocityTwistMatrix.h> #include <visp/vpBasicFeature.h> #include <visp/vpServoException.h> - -#include <visp/vpList.h> #include <visp/vpAdaptiveGain.h> @@ -63,7 +63,7 @@ \class vpServo \ingroup VsTask - \brief Class required to compute the visual servoing control law descbribed + Class required to compute the visual servoing control law descbribed in \cite Chaumette06a and \cite Chaumette07a. \warning To avoid potential memory leaks, it is mendatory to call @@ -72,6 +72,8 @@ vpServoException::notKilledProperly. To learn how to use this class, we suggest first to follow the \ref tutorial-ibvs. + The \ref tutorial-simu-robot-pioneer and \ref tutorial-boost-vs are also useful + for advanced usage of this class. The example below shows how to build a position-based visual servo from 3D visual features \f$s=({^{c^*}}t_c,\theta u)\f$. In that @@ -159,137 +161,91 @@ public: typedef enum { NONE, + /*!< No control law is specified. */ EYEINHAND_CAMERA, + /*!< Eye in hand visual servoing with the following control law + \f[{\bf v}_c = -\lambda {\widehat {\bf L}}^{+}_{e} {\bf e}\f] + where camera velocities are computed. */ EYEINHAND_L_cVe_eJe, + /*!< Eye in hand visual servoing with the following control law + \f[{\dot {\bf q}} = -\lambda \left( {{\widehat {\bf L}}_{e} {^c}{\bf V}_e {^e}{\bf J}_e} \right)^{+} {\bf e}\f] + where joint velocities are computed. */ EYETOHAND_L_cVe_eJe, + /*!< Eye to hand visual servoing with the following control law + \f[{\dot {\bf q}} = \lambda \left( {{\widehat {\bf L}}_{e} {^c}{\bf V}_e {^e}{\bf J}_e} \right)^{+} {\bf e}\f] + where joint velocities are computed. */ EYETOHAND_L_cVf_fVe_eJe, + /*!< Eye to hand visual servoing with the following control law + \f[{\dot {\bf q}} = \lambda \left( {{\widehat {\bf L}}_{e} {^c}{\bf V}_f {^f}{\bf V}_e {^e}{\bf J}_e} \right)^{+} {\bf e}\f] + where joint velocities are computed. */ EYETOHAND_L_cVf_fJe + /*!< Eye to hand visual servoing with the following control law + \f[{\dot {\bf q}} = \lambda \left( {{\widehat {\bf L}}_{e} {^c}{\bf V}_f {^f}{\bf J}_e} \right)^{+} {\bf e}\f] + where joint velocities are computed. */ } vpServoType; typedef enum { CURRENT, + /*!< In the control law (see vpServo::vpServoType), uses the interaction matrix \f${\widehat {\bf L}}_s \f$computed using the current features \f$\bf s\f$. */ DESIRED, + /*!< In the control law (see vpServo::vpServoType), uses the interaction matrix \f${\widehat {\bf L}}_{s^*} \f$computed using the desired features \f${\bf s}^*\f$. */ MEAN, + /*!< In the control law (see vpServo::vpServoType), uses the interaction matrix \f${\widehat {\bf L}} = \left({\widehat {\bf L}}_s + {\widehat {\bf L}}_{s^*}\right)/2 \f$. */ USER_DEFINED + /*!< In the control law (see vpServo::vpServoType), uses an interaction matrix set by the user. */ } vpServoIteractionMatrixType; typedef enum { - TRANSPOSE, - PSEUDO_INVERSE + TRANSPOSE, /*!< In the control law (see vpServo::vpServoType), uses the transpose instead of the pseudo inverse. */ + PSEUDO_INVERSE /*!< In the control law (see vpServo::vpServoType), uses the pseudo inverse. */ } vpServoInversionType; typedef enum { ALL, /*!< Print all the task information. */ CONTROLLER, /*!< Print the type of controller law. */ - ERROR_VECTOR, /*!< Print the error vector \f$(s-s^*)\f$. */ - FEATURE_CURRENT, /*!< Print the current features \f$s\f$. */ - FEATURE_DESIRED, /*!< Print the desired features \f$s^*\f$. */ + ERROR_VECTOR, /*!< Print the error vector \f$\bf e = (s-s^*)\f$. */ + FEATURE_CURRENT, /*!< Print the current features \f$\bf s\f$. */ + FEATURE_DESIRED, /*!< Print the desired features \f${\bf s}^*\f$. */ GAIN, /*!< Print the gain \f$\lambda\f$. */ INTERACTION_MATRIX, /*!< Print the interaction matrix. */ - MINIMUM /*!< Same as vpServoPrintType::ERROR_VECTOR. */ + MINIMUM /*!< Same as vpServo::vpServoPrintType::ERROR_VECTOR. */ } vpServoPrintType; public: // default constructor vpServo(); - //! constructor with Choice of the visual servoing control law - vpServo(vpServoType _servoType) ; - //! destructor + // constructor with Choice of the visual servoing control law + vpServo(vpServoType servoType) ; + // destructor virtual ~vpServo() ; - /*! - Return the velocity twist matrix used to transform a velocity skew vector from end-effector frame into the camera frame. - */ - vpVelocityTwistMatrix get_cVe() const { return cVe; } - /*! - Return the velocity twist matrix used to transform a velocity skew vector from robot fixed frame (also called world or base frame) into the camera frame. - */ - vpVelocityTwistMatrix get_cVf() const { return cVf; } - /*! - Return the velocity twist matrix used to transform a velocity skew vector from robot end-effector frame into the fixed frame (also called world or base frame). - */ - vpVelocityTwistMatrix set_fVe() const { return fVe; } - /*! - Return the robot jacobian expressed in the end-effector frame. - */ - vpMatrix get_eJe() const { return eJe; } - /*! - Return the robot jacobian expressed in the robot fixed frame (also called world or base frame). - */ - vpMatrix get_fJe() const { return fJe; } - - //! destruction (memory deallocation if required) - void kill() ; - - //! Choice of the visual servoing control law - void setServo(vpServoType _servo_type) ; - - void set_cVe(vpVelocityTwistMatrix &_cVe) { cVe = _cVe ; init_cVe = true ; } - void set_cVf(vpVelocityTwistMatrix &_cVf) { cVf = _cVf ; init_cVf = true ; } - void set_fVe(vpVelocityTwistMatrix &_fVe) { fVe = _fVe ; init_fVe = true ; } - - void set_cVe(vpHomogeneousMatrix &cMe) { cVe.buildFrom(cMe); init_cVe=true ;} - void set_cVf(vpHomogeneousMatrix &cMf) { cVf.buildFrom(cMf); init_cVf=true ;} - void set_fVe(vpHomogeneousMatrix &fMe) { fVe.buildFrom(fMe); init_fVe=true ;} - - void set_eJe(vpMatrix &_eJe) { eJe = _eJe ; init_eJe = true ; } - void set_fJe(vpMatrix &_fJe) { fJe = _fJe ; init_fJe = true ; } - - - //! Set the type of the interaction matrix (current, mean, desired, user). - void setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrixType, - const vpServoInversionType &interactionMatrixInversion=PSEUDO_INVERSE) ; - - /*! - Set a variable which enable to compute the interaction matrix for each iteration. - \param forceInteractionMatrixComputation: If true it forces the interaction matrix computation even if it is already done. - */ - void setForceInteractionMatrixComputation(bool forceInteractionMatrixComputation) {this->forceInteractionMatrixComputation = forceInteractionMatrixComputation;} - - //! set the gain lambda - void setLambda(double _lambda) { lambda .initFromConstant (_lambda) ; } - void setLambda(const double at_zero, - const double at_infinity, - const double deriv_at_zero) - { lambda .initStandard (at_zero, at_infinity, deriv_at_zero) ; } - void setLambda(const vpAdaptiveGain& _l){lambda=_l;} - - //! create a new ste of two visual features + // create a new ste of two visual features void addFeature(vpBasicFeature& s, vpBasicFeature& s_star, - const unsigned int select=vpBasicFeature::FEATURE_ALL) ; - //! create a new ste of two visual features + const unsigned int select=vpBasicFeature::FEATURE_ALL) ; + // create a new ste of two visual features void addFeature(vpBasicFeature& s, - const unsigned int select=vpBasicFeature::FEATURE_ALL) ; + const unsigned int select=vpBasicFeature::FEATURE_ALL) ; + + // compute the desired control law + vpColVector computeControlLaw() ; + // compute the desired control law + vpColVector computeControlLaw(double t) ; + vpColVector computeControlLaw(double t, const vpColVector &e_dot_init); - //! compute the interaction matrix related to the set of visual features - vpMatrix computeInteractionMatrix() ; // compute the error between the current set of visual features and // the desired set of visual features vpColVector computeError() ; - //! compute the desired control law - vpColVector computeControlLaw() ; - //! test if all the initialization are correct if true control law can - //! be computed - bool testInitialization() ; - //! test if all the update are correct if true control law can - //! be computed - bool testUpdated() ; - - - //! Add a secondary task. - vpColVector secondaryTask(vpColVector &de2dt) ; - //! Add a secondary task. - vpColVector secondaryTask(vpColVector &e2, vpColVector &de2dt) ; - - //! Return the task dimension. - unsigned int getDimension() ; + // compute the interaction matrix related to the set of visual features + vpMatrix computeInteractionMatrix() ; + // Return the task dimension. + unsigned int getDimension() const ; /*! - Return the error \f$(s - s^*)\f$ between the current set of visual features - \f$s\f$ and the desired set of visual features \f$s^*\f$. + Return the error \f$\bf e = (s - s^*)\f$ between the current set of visual features + \f$\bf s\f$ and the desired set of visual features \f$\bf s^*\f$. The error vector is updated after a call of computeError() or computeControlLaw(). \code vpServo task; @@ -303,129 +259,203 @@ public: return error ; } /* - Return the interaction matrix \f$L\f$ used to compute the task jacobian \f$J_1\f$. - The interaction matrix is updated after a call to computeInteractionMatrix() or computeControlLaw(). - -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - vpMatrix L = task.getInteractionMatrix(); // Get the interaction matrix used to compute v -\endcode - \sa getTaskJacobian() - */ - inline vpMatrix getInteractionMatrix() + Return the interaction matrix \f$L\f$ used to compute the task jacobian \f$J_1\f$. + The interaction matrix is updated after a call to computeInteractionMatrix() or computeControlLaw(). + + \code + vpServo task; + ... + vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing + vpMatrix L = task.getInteractionMatrix(); // Get the interaction matrix used to compute v + \endcode + \sa getTaskJacobian() + */ + inline vpMatrix getInteractionMatrix() const { return L; } + vpMatrix getI_WpW() const; /*! - Return the projection operator \f${\bf I}-{\bf W}^+{\bf W}\f$. This operator is updated - after a call of computeControlLaw(). - -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - vpMatrix I_WpW = task.getI_WpW(); // Get the projection operator -\endcode - \sa getWpW() - */ - inline vpMatrix getI_WpW() const - { - return I_WpW; - } - /*! - Return the visual servo type. - */ + Return the visual servo type. + */ inline vpServoType getServoType() const { return servoType; } + + vpMatrix getTaskJacobian() const; + vpMatrix getTaskJacobianPseudoInverse() const; + unsigned int getTaskRank() const; + /*! - Return the task jacobian \f$J_1\f$. The task jacobian is updated after a call of computeControlLaw(). + Get task singular values. - In the general case, the task jacobian is given by \f$J_1 = L {^c}V_a {^a}J_e\f$. -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - vpMatrix J1 = task.getTaskJacobian(); // Get the task jacobian used to compute v -\endcode - \sa getTaskJacobianPseudoInverse(), getInteractionMatrix() - */ - inline vpMatrix getTaskJacobian() const + \return Singular values that relies on the task jacobian pseudo inverse. + */ + inline vpColVector getTaskSingularValues() const { - return J1; + return sv; } + + vpMatrix getWpW() const; + + /*! + Return the velocity twist matrix used to transform a velocity skew vector from end-effector frame into the camera frame. + */ + vpVelocityTwistMatrix get_cVe() const { return cVe; } + /*! + Return the velocity twist matrix used to transform a velocity skew vector from robot fixed frame (also called world or base frame) into the camera frame. + */ + vpVelocityTwistMatrix get_cVf() const { return cVf; } /*! - Return the pseudo inverse of the task jacobian \f$J_1\f$. The task jacobian - and its pseudo inverse are updated after a call of computeControlLaw(). + Return the velocity twist matrix used to transform a velocity skew vector from robot end-effector frame into the fixed frame (also called world or base frame). + */ + vpVelocityTwistMatrix get_fVe() const { return fVe; } + /*! + Return the robot jacobian expressed in the end-effector frame. + */ + vpMatrix get_eJe() const { return eJe; } + /*! + Return the robot jacobian expressed in the robot fixed frame (also called world or base frame). + */ + vpMatrix get_fJe() const { return fJe; } - \return Pseudo inverse \f${J_1}^{+}\f$ of the task jacobian. -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - vpMatrix J1p = task.getTaskJacobianPseudoInverse(); // Get the pseudo inverse of task jacobian used to compute v -\endcode + // destruction (memory deallocation if required) + void kill() ; + + void print(const vpServo::vpServoPrintType display_level=ALL, + std::ostream &os = std::cout) ; + + // Add a secondary task. + vpColVector secondaryTask(const vpColVector &de2dt) ; + // Add a secondary task. + vpColVector secondaryTask(const vpColVector &e2, const vpColVector &de2dt) ; + + /*! + Set a variable which enables to compute the interaction matrix at each iteration. + + When the interaction matrix is computed from the desired features \f${\bf s}^*\f$ which are in general constant, + the interaction matrix \f${\widehat {\bf L}}_{s^*}\f$ is computed just at the first iteration of the servo loop. + Sometimes, when the desired features are time dependent \f${{\bf s}(t)}^*\f$ or varying, the interaction matrix + need to be computed at each iteration of the servo loop. This method allows to force the computation of + \f${\widehat {\bf L}}\f$ in this particular case. + + \param force_computation: If true it forces the interaction matrix computation even if it is already done. - \sa getTaskJacobian() */ - inline vpMatrix getTaskJacobianPseudoInverse() const + void setForceInteractionMatrixComputation(bool force_computation) { - return J1p; + this->forceInteractionMatrixComputation = force_computation; } + /*! - Return the rank of the task jacobian. The rank is updated after a call of computeControlLaw(). + Set the interaction matrix type (current, desired, mean or user defined) and how its inverse is computed. + \param interactionMatrixType : The interaction matrix type. See vpServo::vpServoIteractionMatrixType for + more details. + \param interactionMatrixInversion : How is the inverse computed. See vpServo::vpServoInversionType for + more details. + */ + void setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrixType, + const vpServoInversionType &interactionMatrixInversion=PSEUDO_INVERSE) ; -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - double rank = task.getTaskRank(); // Get the rank of the task jacobian -\endcode - */ - inline double getTaskRank() const + /*! + Set the gain \f$\lambda\f$ used in the control law (see vpServo::vpServoType) as constant. + + The usage of an adaptive gain allows to reduce the convergence time, see setLambda(const vpAdaptiveGain&). + + \param c : Constant gain. Values are in general between 0.1 and 1. Higher is the gain, higher are the velocities + that may be applied to the robot. + */ + void setLambda(double c) { - return rankJ1; + lambda .initFromConstant (c) ; } /*! - Get task singular values. + Set the gain \f$\lambda\f$ used in the control law (see vpServo::vpServoType) as adaptive. + Value of \f$\lambda\f$ that is used in computeControlLaw() depend on the infinity norm of the task Jacobian. - \return Singular values that relies on the task jacobian pseudo inverse. - */ - inline vpColVector getTaskSingularValues() + The usage of an adaptive gain rather than a constant gain allows to reduce the convergence time. + + \param gain_at_zero : the expected gain when \f$x=0\f$: \f$\lambda(0)\f$. + \param gain_at_infinity : the expected gain when \f$x=\infty\f$: \f$\lambda(\infty)\f$. + \param slope_at_zero : the expected slope of \f$\lambda(x)\f$ when \f$x=0\f$: \f${\dot \lambda}(0)\f$. + + For more details on these parameters see vpAdaptiveGain class. + */ + void setLambda(const double gain_at_zero, + const double gain_at_infinity, + const double slope_at_zero) { - return sv; + lambda .initStandard (gain_at_zero, gain_at_infinity, slope_at_zero) ; } + /*! + Set the gain \f$\lambda\f$ used in the control law (see vpServo::vpServoType) as adaptive. + Value of \f$\lambda\f$ that is used in computeControlLaw() depend on the infinity norm of the task Jacobian. + The usage of an adaptive gain rather than a constant gain allows to reduce the convergence time. + \sa vpAdaptiveGain + */ + void setLambda(const vpAdaptiveGain& l){lambda=l;} /*! - Return the projection operator \f${\bf W}^+{\bf W}\f$. This operator is updated - after a call of computeControlLaw(). + Set the value of the parameter \f$\mu\f$ used to ensure the continuity of the velocities + computed using computeControlLaw(double). -\code - vpServo task; - ... - vpColVector v = task.computeControlLaw(); // Compute the velocity corresponding to the visual servoing - vpMatrix WpW = task.getWpW(); // Get the projection operator -\endcode - \sa getI_WpW() + A recommended value is 4. */ - inline vpMatrix getWpW() const - { - return WpW; - } + void setMu(double mu_){this->mu=mu_;} + // Choice of the visual servoing control law + void setServo(const vpServoType &servo_type) ; + + /*! + Set the velocity twist matrix used to transform a velocity skew vector from end-effector frame into the camera frame. + */ + void set_cVe(const vpVelocityTwistMatrix &cVe_) { this->cVe = cVe_ ; init_cVe = true ; } + /*! + Set the velocity twist matrix used to transform a velocity skew vector from end-effector frame into the camera frame. + */ + void set_cVe(const vpHomogeneousMatrix &cMe) { cVe.buildFrom(cMe); init_cVe=true ;} + /*! + Set the velocity twist matrix used to transform a velocity skew vector from robot fixed frame (also called world or base frame) into the camera frame. + */ + void set_cVf(const vpVelocityTwistMatrix &cVf_) { this->cVf = cVf_ ; init_cVf = true ; } + /*! + Set the velocity twist matrix used to transform a velocity skew vector from robot fixed frame (also called world or base frame) into the camera frame. + */ + void set_cVf(const vpHomogeneousMatrix &cMf) { cVf.buildFrom(cMf); init_cVf=true ;} + /*! + Set the velocity twist matrix used to transform a velocity skew vector from robot end-effector frame into the fixed frame (also called world or base frame). + */ + void set_fVe(const vpVelocityTwistMatrix &fVe_) { this->fVe = fVe_ ; init_fVe = true ; } + /*! + Set the velocity twist matrix used to transform a velocity skew vector from robot end-effector frame into the fixed frame (also called world or base frame). + */ + void set_fVe(const vpHomogeneousMatrix &fMe) { fVe.buildFrom(fMe); init_fVe=true ;} + /*! + Set the robot jacobian expressed in the end-effector frame. + */ + void set_eJe(const vpMatrix &eJe_) { this->eJe = eJe_ ; init_eJe = true ; } + /*! + Set the robot jacobian expressed in the robot fixed frame (also called world or base frame). + */ + void set_fJe(const vpMatrix &fJe_) { this->fJe = fJe_ ; init_fJe = true ; } - void print(const vpServo::vpServoPrintType display_level=ALL, - std::ostream &os = std::cout) ; -protected: - //! basic initialization + /*! + Test if all the initialization are correct. If true, the control law can be computed. + */ + bool testInitialization() ; + /*! + Test if all the update are correct. If true control law can be computed. + */ + bool testUpdated() ; + + protected: + //! Basic initialization. void init() ; -public: + public: //! Interaction matrix vpMatrix L ; //! Error \f$(s - s^*)\f$ between the current set of visual features @@ -449,7 +479,6 @@ public: //! Task \f$e = e_1 + (I-{J_1}^{+} J_1) e_2\f$ vpColVector e ; - //! Articular velocity vpColVector q_dot ; //! Camera velocity @@ -461,15 +490,15 @@ public: //! Rank of the task Jacobian unsigned int rankJ1 ; - //! List of visual features (produce \f$s\f$) - vpList<vpBasicFeature *> featureList ; - //! List of desired visual features (produce \f$s^*\f$) - vpList<vpBasicFeature *> desiredFeatureList ; + //! List of current visual features \f$\bf s\f$. + std::list<vpBasicFeature *> featureList ; + //! List of desired visual features \f$\bf s^*\f$. + std::list<vpBasicFeature *> desiredFeatureList ; //! List of selection among visual features - //! used for selection of a subset of each visual feature if required - vpList<unsigned int> featureSelectionList ; + //! used for selection of a subset of each visual feature if required. + std::list<unsigned int> featureSelectionList ; - //! Gain + //! Gain used in the control law. vpAdaptiveGain lambda ; //! Sign of the interaction +/- 1 (1 for eye-in-hand, -1 for @@ -478,21 +507,21 @@ public: //! Type of the interaction matrox (current, mean, desired, user) vpServoIteractionMatrixType interactionMatrixType ; //! Indicates if the transpose or the pseudo inverse of the - //!interaction matrix should be used to compute the task + //! interaction matrix should be used to compute the task. vpServoInversionType inversionType ; -protected: + protected: /* Twist transformation matrix */ - //! Twist transformation matrix between Re and Rc + //! Twist transformation matrix between Re and Rc. vpVelocityTwistMatrix cVe ; bool init_cVe ; - //! Twist transformation matrix between Rf and Rc + //! Twist transformation matrix between Rf and Rc. vpVelocityTwistMatrix cVf ; bool init_cVf ; - //! Twist transformation matrix between Re and Rf + //! Twist transformation matrix between Re and Rf. vpVelocityTwistMatrix fVe ; bool init_fVe ; @@ -511,29 +540,29 @@ protected: Task building */ - //! true if the error has been computed + //! true if the error has been computed. bool errorComputed ; - //! true if the interaction matrix has been computed + //! true if the interaction matrix has been computed. bool interactionMatrixComputed ; - //! dimension of the task + //! Dimension of the task updated during computeControlLaw(). unsigned int dim_task ; - bool taskWasKilled; // flag to indicate if the task was killed + //! Flag to indicate if the task was killed + bool taskWasKilled; //! Force the interaction matrix computation even if it is already done. bool forceInteractionMatrixComputation; - //! projection operators WpW + //! Projection operators \f$\bf WpW\f$. vpMatrix WpW ; - //! projection operators I-WpW + //! Projection operators \f$\bf I-WpW\f$. vpMatrix I_WpW ; - vpColVector sv ; // singular values from the pseudo inverse -} ; + //! Singular values from the pseudo inverse. + vpColVector sv ; + double mu; -#endif + vpColVector e1_initial; -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +} ; + +#endif diff --git a/src/servo/vpServoData.cpp b/src/servo/vpServoData.cpp index 9ec92ef2e8b3a179f7505aec1e86cca7564b3594..183bcc285fb03fffbd31c74dbdbe87fc0079382f 100644 --- a/src/servo/vpServoData.cpp +++ b/src/servo/vpServoData.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServoData.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServoData.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/servo/vpServoData.h b/src/servo/vpServoData.h index e0fd793444905eb4be0db5979d0513744f329910..1f5ab8a67558fe2b91e3a25ba7c3125be29b2bc1 100644 --- a/src/servo/vpServoData.h +++ b/src/servo/vpServoData.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServoData.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServoData.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -81,15 +81,17 @@ private: public: #ifndef DOXYGEN_SHOULD_SKIP_THIS - vpServoData(const vpServoData &) { - throw vpException(vpException::functionNotImplementedError,"Not implemented!"); + vpServoData(const vpServoData &sd) + : velocityFile(), errorFile(), errorNormFile(), sFile(), + sStarFile(), vNormFile(), cmDeg(false) { + *this = sd; } - void operator=(const vpServoData &){ + vpServoData & operator=(const vpServoData &) { throw vpException(vpException::functionNotImplementedError,"Not implemented!"); } #endif - vpServoData() { ; } + vpServoData() : velocityFile(), errorFile(), errorNormFile(), sFile(), sStarFile(), vNormFile(), cmDeg(false) { ; } virtual ~vpServoData() { ; } //! velocity output in cm and deg diff --git a/src/servo/vpServoDisplay.cpp b/src/servo/vpServoDisplay.cpp index 5178bec3ac6c8ba71d22d8cab437aa22b22c2204..b0f39606e5fac9b9e0cc1d2554e1621ad2a5fbea 100644 --- a/src/servo/vpServoDisplay.cpp +++ b/src/servo/vpServoDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServoDisplay.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServoDisplay.cpp 4645 2014-02-05 17:44:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,34 +80,27 @@ \param thickness : Thickness of the feature representation. */ -void -vpServoDisplay::display(vpServo &s, - const vpCameraParameters &cam, - vpImage<unsigned char> &I, - vpColor currentColor, - vpColor desiredColor, - unsigned int thickness) +void vpServoDisplay::display(const vpServo &s, + const vpCameraParameters &cam, + const vpImage<unsigned char> &I, + vpColor currentColor, + vpColor desiredColor, + unsigned int thickness) { + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<vpBasicFeature *>::const_iterator it_s_star; - - - for (s.featureList.front(), - s.desiredFeatureList.front() ; - !s.featureList.outside() ; - s.featureList.next(), - s.desiredFeatureList.next() ) + for (it_s = s.featureList.begin(), it_s_star = s.desiredFeatureList.begin(); + it_s != s.featureList.end(); + ++it_s, ++it_s_star) { - vpBasicFeature *s_ptr = NULL; - if (desiredColor != vpColor::none) { // desired list - s_ptr = s.desiredFeatureList.value() ; - s_ptr->display(cam, I, desiredColor, thickness ) ; + (*it_s_star)->display(cam, I, desiredColor, thickness ) ; } if (currentColor != vpColor::none) { // current list - s_ptr = s.featureList.value() ; - s_ptr->display(cam, I, currentColor, thickness ) ; + (*it_s)->display(cam, I, currentColor, thickness ) ; } } // vpDisplay::flush(I) ; @@ -134,40 +127,28 @@ vpServoDisplay::display(vpServo &s, */ void -vpServoDisplay::display(vpServo &s, +vpServoDisplay::display(const vpServo &s, const vpCameraParameters &cam, - vpImage<vpRGBa> &I, + const vpImage<vpRGBa> &I, vpColor currentColor, vpColor desiredColor, - unsigned int thickness) + unsigned int thickness) { + std::list<vpBasicFeature *>::const_iterator it_s; + std::list<vpBasicFeature *>::const_iterator it_s_star; - - - for (s.featureList.front(), - s.desiredFeatureList.front() ; - !s.featureList.outside() ; - s.featureList.next(), - s.desiredFeatureList.next() ) + for (it_s = s.featureList.begin(), it_s_star = s.desiredFeatureList.begin(); + it_s != s.featureList.end(); + ++it_s, ++it_s_star) { - vpBasicFeature *s_ptr = NULL; - if (desiredColor != vpColor::none) { // desired list - s_ptr = s.desiredFeatureList.value() ; - s_ptr->display(cam, I, desiredColor, thickness ) ; + (*it_s_star)->display(cam, I, desiredColor, thickness ) ; } if (currentColor != vpColor::none) { // current list - s_ptr = s.featureList.value() ; - s_ptr->display(cam, I, currentColor, thickness ) ; + (*it_s)->display(cam, I, currentColor, thickness ) ; } } // vpDisplay::flush(I) ; } - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/servo/vpServoDisplay.h b/src/servo/vpServoDisplay.h index eb4cd373fd7d7f225c4e3e35f57b9ce9c34b0489..130f8433836a7c921643e0c9b780af5466a71938 100644 --- a/src/servo/vpServoDisplay.h +++ b/src/servo/vpServoDisplay.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServoDisplay.h 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpServoDisplay.h 4645 2014-02-05 17:44:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,15 +65,15 @@ class VISP_EXPORT vpServoDisplay { public: - static void display(vpServo &s, + static void display(const vpServo &s, const vpCameraParameters &cam, - vpImage<unsigned char> &I, + const vpImage<unsigned char> &I, vpColor currentColor = vpColor::green, vpColor desiredColor = vpColor::red, unsigned int thickness=1) ; - static void display(vpServo &s, + static void display(const vpServo &s, const vpCameraParameters &cam, - vpImage<vpRGBa> &I, + const vpImage<vpRGBa> &I, vpColor currentColor = vpColor::green, vpColor desiredColor = vpColor::red, unsigned int thickness=1) ; diff --git a/src/servo/vpServoException.h b/src/servo/vpServoException.h index 164d88c29f74cd96ae49e9942043f4167cfc382f..f74fc67614a5ee2b4cc1fa9eaee9031452fda55f 100644 --- a/src/servo/vpServoException.h +++ b/src/servo/vpServoException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpServoException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpServoException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,13 +64,13 @@ */ class VISP_EXPORT vpServoException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpServo member */ - enum errorServoCodeEnum + enum errorServoCodeEnum { //! Current or desired feature list is empty noFeatureError, @@ -82,25 +82,20 @@ public: servoError } ; -public: - vpServoException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpServoException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpServoException (const int code) - : vpException(code){ ; } + public: + vpServoException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpServoException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpServoException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpServoException_ERROR_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/simulator/coin-simulator/vpAR.cpp b/src/simulator/coin-simulator/vpAR.cpp index 36dd8810bfb0a7af1431765a4268afa5aa955ded..48698e28ee43a3f4e4ae02b2f0421b5ef9b8a966 100644 --- a/src/simulator/coin-simulator/vpAR.cpp +++ b/src/simulator/coin-simulator/vpAR.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAR.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAR.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/coin-simulator/vpAR.h b/src/simulator/coin-simulator/vpAR.h index 6233e3616a16ce49bae951328642606fbb156d66..47ca36008182fc3d6d98eefc9e5a91f6edf77697 100644 --- a/src/simulator/coin-simulator/vpAR.h +++ b/src/simulator/coin-simulator/vpAR.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAR.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAR.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/coin-simulator/vpProjectionDisplay.cpp b/src/simulator/coin-simulator/vpProjectionDisplay.cpp index 42035d69152ece01cfcf22af0d7244ee7e3e7a21..b4fe61b96964c4d51780bbf16c861b2f1fe76653 100644 --- a/src/simulator/coin-simulator/vpProjectionDisplay.cpp +++ b/src/simulator/coin-simulator/vpProjectionDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpProjectionDisplay.cpp 4238 2013-05-06 13:29:30Z fspindle $ + * $Id: vpProjectionDisplay.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/coin-simulator/vpProjectionDisplay.h b/src/simulator/coin-simulator/vpProjectionDisplay.h index bbd645d52a30cf5a8bac92f9eb9c2dcd7789b675..6c137692de43043e5c467e2d7360c4257d72279a 100644 --- a/src/simulator/coin-simulator/vpProjectionDisplay.h +++ b/src/simulator/coin-simulator/vpProjectionDisplay.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpProjectionDisplay.h 4238 2013-05-06 13:29:30Z fspindle $ + * $Id: vpProjectionDisplay.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,8 +96,25 @@ public: static int internalView() { return 0x01 ; } static int externalView() { return 0x02 ; } - vpProjectionDisplay() { init() ;} - vpProjectionDisplay(int select) { init(select) ;} + /*! Default constructor. */ + vpProjectionDisplay() + : Icam(), Iext(), +#if defined (VISP_HAVE_DISPLAY) + dIcam(), dIext(), +#endif + listFp(), o(), x(), y(), z(), traj() + { + init(); + } + vpProjectionDisplay(int select) + : Icam(), Iext(), +#if defined (VISP_HAVE_DISPLAY) + dIcam(), dIext(), +#endif + listFp(), o(), x(), y(), z(), traj() + { + init(select) ; + } void insert( vpForwardProjection &fp) ; void display(vpImage<unsigned char> &I, diff --git a/src/simulator/coin-simulator/vpSimulator.cpp b/src/simulator/coin-simulator/vpSimulator.cpp index 68671f242b11998a42d6ebb1a7f38848f166e2d7..684669c2c11b559959a02543249f32e024e4b8b4 100644 --- a/src/simulator/coin-simulator/vpSimulator.cpp +++ b/src/simulator/coin-simulator/vpSimulator.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSimulator.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpSimulator.cpp 5263 2015-02-04 13:43:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -291,6 +291,24 @@ vpSimulator::init() offScreenRenderer = NULL ; bufferView = NULL; get = 1 ; + typeImage = grayImage; + mainThread = NULL; + scene = NULL; + internalRoot = NULL; + externalRoot = NULL; + internalCamera = NULL; + externalCamera = NULL; + internalCameraPosition = NULL; + extrenalCameraPosition = NULL; + internalCameraObject = NULL; +#if defined(VISP_HAVE_SOWIN) + // mainWindow = ?; +#elif defined(VISP_HAVE_SOQT) + mainWindow = NULL; +#elif defined(VISP_HAVE_SOXT) + // mainWindow = ?; +#endif + } void vpSimulator::kill() @@ -306,6 +324,23 @@ vpSimulator::kill() } vpSimulator::vpSimulator() + : +#if defined(VISP_HAVE_SOWIN) + mainWindow(), +#elif defined(VISP_HAVE_SOQT) + mainWindow(NULL), +#elif defined(VISP_HAVE_SOXT) + mainWindow(), +#endif + mainWindowInitialized(false), typeImage(vpSimulator::grayImage), + image_background(NULL), internalView(NULL), externalView(NULL), + mainThread(NULL), internal_width(0), internal_height(0), + external_width(0), external_height(0), scene(NULL), internalRoot(NULL), + externalRoot(NULL), internalCamera(NULL), externalCamera(NULL), + internalCameraPosition(NULL), extrenalCameraPosition(NULL), internalCameraObject(NULL), + zoomFactor(0.), cameraPositionInitialized(false), cMf(), internalCameraParameters(), + externalCameraParameters(), realtime(NULL), offScreenRenderer(NULL), bufferView(NULL), + get(0) { vpSimulator::init() ; } @@ -831,7 +866,6 @@ vpSimulator::addObject(SoSeparator * object, if (identity==true) { - vpTRACE("identity ") ; root->addChild (object); } else diff --git a/src/simulator/coin-simulator/vpSimulator.h b/src/simulator/coin-simulator/vpSimulator.h index bdc9d88c7f0c22118255574117a1497cbf491d37..6f1c4d410dfe95024a41dda59e23ccdf9f45ac0c 100644 --- a/src/simulator/coin-simulator/vpSimulator.h +++ b/src/simulator/coin-simulator/vpSimulator.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSimulator.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSimulator.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/coin-simulator/vpSimulatorException.h b/src/simulator/coin-simulator/vpSimulatorException.h index 5a97ef49bc8f9eff008b1fff4868afc5e9d29f33..887b749d4ebc743b40ec864ad908fca98848a3b7 100644 --- a/src/simulator/coin-simulator/vpSimulatorException.h +++ b/src/simulator/coin-simulator/vpSimulatorException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSimulatorException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSimulatorException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,12 +73,12 @@ */ class VISP_EXPORT vpSimulatorException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpSimulator member */ - enum errorSimulatorCodeEnum + enum errorSimulatorCodeEnum { ioError, noFileNameError, @@ -87,25 +87,20 @@ public: badInitializationError } ; -public: - vpSimulatorException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpSimulatorException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpSimulatorException (const int code) - : vpException(code){ ; } + public: + vpSimulatorException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpSimulatorException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpSimulatorException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpSimulatorException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/simulator/coin-simulator/vpViewer.cpp b/src/simulator/coin-simulator/vpViewer.cpp index cd5b6d0b21d35235c34421018d3132c1ed9cfb64..69e66e6508ce2d2c88ef68ca8ce297a2555e4072 100644 --- a/src/simulator/coin-simulator/vpViewer.cpp +++ b/src/simulator/coin-simulator/vpViewer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViewer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpViewer.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/coin-simulator/vpViewer.h b/src/simulator/coin-simulator/vpViewer.h index 97ef3586f3d51f64e8c2f70aeb85df6480c05789..c02bc4d18ae9f2ce43c0816778ca97482832e479 100644 --- a/src/simulator/coin-simulator/vpViewer.h +++ b/src/simulator/coin-simulator/vpViewer.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViewer.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpViewer.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/image-simulator/vpImageSimulator.cpp b/src/simulator/image-simulator/vpImageSimulator.cpp index 2c60c48ef7718a83f29b9b4a21e36c9b0a5652e3..462a2c7371dfad622beee8e76b79ef2ea30ffd83 100644 --- a/src/simulator/image-simulator/vpImageSimulator.cpp +++ b/src/simulator/image-simulator/vpImageSimulator.cpp @@ -3,7 +3,7 @@ * $Id: vpPose.h 2453 2010-01-07 10:01:10Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,6 +48,7 @@ #include <visp/vpPixelMeterConversion.h> #include <visp/vpMeterPixelConversion.h> #include <visp/vpMatrixException.h> +#include <visp/vpMbtPolygon.h> /*! Basic constructor. @@ -59,6 +60,12 @@ By default the class uses colored images. */ vpImageSimulator::vpImageSimulator(const vpColorPlan &col) + : cMt(), pt(), ptClipped(), interp(SIMPLE), normal_obj(), normal_Cam(), normal_Cam_optim(), + distance(1.), visible_result(1.), visible(false), X0_2_optim(NULL), + euclideanNorm_u(0.), euclideanNorm_v(0.), vbase_u(), vbase_v(), + vbase_u_optim(NULL), vbase_v_optim(NULL), Xinter_optim(NULL), listTriangle(), + colorI(col), Ig(), Ic(), rect(), cleanPrevImage(false), + setBackgroundTexture(false), bgColor(vpColor::white), focal(), needClipping(false) { for(int i=0;i<4;i++) X[i].resize(3); @@ -84,12 +91,8 @@ vpImageSimulator::vpImageSimulator(const vpColorPlan &col) vbase_u_optim = new double[3]; vbase_v_optim = new double[3]; Xinter_optim = new double[3]; - - colorI = col; - interp = SIMPLE; - bgColor = vpColor::white; - cleanPrevImage = false; - setBackgroundTexture = false; + + pt.resize(4); } @@ -97,8 +100,15 @@ vpImageSimulator::vpImageSimulator(const vpColorPlan &col) Copy constructor */ vpImageSimulator::vpImageSimulator(const vpImageSimulator &text) + : cMt(), pt(), ptClipped(), interp(SIMPLE), normal_obj(), normal_Cam(), normal_Cam_optim(), + distance(1.), visible_result(1.), visible(false), X0_2_optim(NULL), + euclideanNorm_u(0.), euclideanNorm_v(0.), vbase_u(), vbase_v(), + vbase_u_optim(NULL), vbase_v_optim(NULL), Xinter_optim(NULL), listTriangle(), + colorI(GRAY_SCALED), Ig(), Ic(), rect(), cleanPrevImage(false), + setBackgroundTexture(false), bgColor(vpColor::white), focal(), needClipping(false) { - for(int i=0;i<4;i++) + pt.resize(4); + for(unsigned int i=0;i<4;i++) { X[i] = text.X[i]; pt[i] = text.pt[i]; @@ -154,7 +164,7 @@ vpImageSimulator::~vpImageSimulator() vpImageSimulator& vpImageSimulator::operator=(const vpImageSimulator& sim) { - for(int i=0;i<4;i++) + for(unsigned int i=0;i<4;i++) { X[i] = sim.X[i]; pt[i] = sim.pt[i]; @@ -211,7 +221,10 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -221,7 +234,7 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, unsigned char *bitmap = I.bitmap; unsigned int width = I.getWidth(); vpImagePoint ip; - + for (unsigned int i = (unsigned int)top; i < (unsigned int)bottom; i++) { for (unsigned int j = (unsigned int)left; j < (unsigned int)right; j++) @@ -281,7 +294,10 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, } if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -341,7 +357,10 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, } if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -415,7 +434,10 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, const vpCameraParameters &cam) if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -487,7 +509,10 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, vpImage<vpRGBa> &Isrc, if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -546,7 +571,10 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, const vpCameraParameters &cam, } if(visible) { - getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + if(!needClipping) + getRoi(I.getWidth(),I.getHeight(),cam,pt,rect); + else + getRoi(I.getWidth(),I.getHeight(),cam,ptClipped,rect); double top = rect.getTop(); double bottom = rect.getBottom(); @@ -751,8 +779,10 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, for (unsigned int i = 0; i < nbsimList; i++) { - - simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + if(!simList[i]->needClipping) + simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + else + simList[i]->getRoi(width,height,cam,simList[i]->ptClipped,simList[i]->rect); if (topFinal > simList[i]->rect.getTop()) topFinal = simList[i]->rect.getTop(); if (bottomFinal < simList[i]->rect.getBottom()) bottomFinal = simList[i]->rect.getBottom(); @@ -956,8 +986,10 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, vpList<vpImageSimulator> &list, for (unsigned int i = 0; i < nbsimList; i++) { - - simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + if(!simList[i]->needClipping) + simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + else + simList[i]->getRoi(width,height,cam,simList[i]->ptClipped,simList[i]->rect); if (topFinal > simList[i]->rect.getTop()) topFinal = simList[i]->rect.getTop(); if (bottomFinal < simList[i]->rect.getBottom()) bottomFinal = simList[i]->rect.getBottom(); @@ -1159,8 +1191,10 @@ vpImageSimulator::getImage(vpImage<unsigned char> &I, for (unsigned int i = 0; i < nbsimList; i++) { - - simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + if(!simList[i]->needClipping) + simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + else + simList[i]->getRoi(width,height,cam,simList[i]->ptClipped,simList[i]->rect); if (topFinal > simList[i]->rect.getTop()) topFinal = simList[i]->rect.getTop(); if (bottomFinal < simList[i]->rect.getBottom()) bottomFinal = simList[i]->rect.getBottom(); @@ -1359,8 +1393,10 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, for (unsigned int i = 0; i < nbsimList; i++) { - - simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + if(!simList[i]->needClipping) + simList[i]->getRoi(width,height,cam,simList[i]->pt,simList[i]->rect); + else + simList[i]->getRoi(width,height,cam,simList[i]->ptClipped,simList[i]->rect); if (topFinal > simList[i]->rect.getTop()) topFinal = simList[i]->rect.getTop(); if (bottomFinal < simList[i]->rect.getBottom()) bottomFinal = simList[i]->rect.getBottom(); @@ -1423,20 +1459,21 @@ vpImageSimulator::getImage(vpImage<vpRGBa> &I, /*! Enable to set the position of the 3D plane relative to the virtual camera. - \param _cMt : The pose of the plane relative to the virtual camera. + \param cMt_ : The pose of the plane relative to the virtual camera. */ void -vpImageSimulator::setCameraPosition(const vpHomogeneousMatrix &_cMt) +vpImageSimulator::setCameraPosition(const vpHomogeneousMatrix &cMt_) { - cMt = _cMt; + cMt = cMt_; vpRotationMatrix R; cMt.extract(R); + needClipping = false; normal_Cam = R * normal_obj; visible_result = vpColVector::dotProd(normal_Cam,focal); - for(int i = 0; i < 4; i++) + for(unsigned int i = 0; i < 4; i++) pt[i].track(cMt); vpColVector e1(3) ; @@ -1455,17 +1492,21 @@ vpImageSimulator::setCameraPosition(const vpHomogeneousMatrix &_cMt) double angle = pt[0].get_X()*facenormal[0] + pt[0].get_Y()*facenormal[1] + pt[0].get_Z()*facenormal[2] ; - if (angle > 0) + if (angle > 0){ visible=true; - else + } + else { visible=false; + } if(visible) { - for(int i = 0; i < 4; i++) + for(unsigned int i = 0; i < 4; i++) { project(X[i],cMt,X2[i]); pt[i].track(cMt); + if(pt[i].get_Z() < 0) + needClipping = true; } vbase_u = X2[1]-X2[0]; @@ -1473,6 +1514,7 @@ vpImageSimulator::setCameraPosition(const vpHomogeneousMatrix &_cMt) distance = vpColVector::dotProd(normal_Cam,X2[1]); + if(distance < 0) { visible = false; @@ -1486,26 +1528,38 @@ vpImageSimulator::setCameraPosition(const vpHomogeneousMatrix &_cMt) vbase_u_optim[i] = vbase_u[i]; vbase_v_optim[i] = vbase_v[i]; } - - vpImagePoint iPa[4]; - for(unsigned int i = 0; i < 4; i++) - { - iPa[i].set_j(X2[i][0]/X2[i][2]); - iPa[i].set_i(X2[i][1]/X2[i][2]); + + std::vector<vpPoint> *ptPtr = &pt; + if(needClipping){ + vpMbtPolygon::getClippedPolygon(pt,ptClipped,cMt,vpMbtPolygon::NEAR_CLIPPING); + ptPtr = &ptClipped; + } + + listTriangle.clear(); + for(unsigned int i = 1 ; i < (*ptPtr).size()-1 ; i++){ + vpImagePoint ip1, ip2, ip3; + ip1.set_j((*ptPtr)[0].get_x()); + ip1.set_i((*ptPtr)[0].get_y()); + + ip2.set_j((*ptPtr)[i].get_x()); + ip2.set_i((*ptPtr)[i].get_y()); + + ip3.set_j((*ptPtr)[i+1].get_x()); + ip3.set_i((*ptPtr)[i+1].get_y()); + + vpTriangle tri(ip1,ip2,ip3); + listTriangle.push_back(tri); } - - T1.buildFrom(iPa[0],iPa[1],iPa[3]); - T2.buildFrom(iPa[2],iPa[1],iPa[3]); } } void -vpImageSimulator::initPlan(vpColVector* _X) +vpImageSimulator::initPlan(vpColVector* X_) { for (unsigned int i = 0; i < 4; i++) { - X[i]=_X[i]; - pt[i].setWorldCoordinates(_X[i][0],_X[i][1],_X[i][2]); + X[i]=X_[i]; + pt[i].setWorldCoordinates(X_[i][0],X_[i][1],X_[i][2]); } normal_obj=vpColVector::crossProd(X[1]-X[0],X[3]-X[0]); @@ -1526,14 +1580,14 @@ vpImageSimulator::initPlan(vpColVector* _X) - \f$ X[3] \f$ :Bottom left corner. \param I : The image which is projected. - \param _X : table of the 3D coordinates corresponding to the image corners. + \param X_ : table of the 3D coordinates corresponding to the image corners. */ void -vpImageSimulator::init(const vpImage<unsigned char> &I,vpColVector* _X) +vpImageSimulator::init(const vpImage<unsigned char> &I,vpColVector* X_) { Ig = I; vpImageConvert::convert(I,Ic); - initPlan(_X); + initPlan(X_); } /*! @@ -1547,14 +1601,14 @@ vpImageSimulator::init(const vpImage<unsigned char> &I,vpColVector* _X) - \f$ X[3] \f$ :Bottom left corner. \param I : The image which is projected. - \param _X : table of the 3D coordinates corresponding to the image corners. + \param X_ : table of the 3D coordinates corresponding to the image corners. */ void -vpImageSimulator::init(const vpImage<vpRGBa> &I,vpColVector* _X) +vpImageSimulator::init(const vpImage<vpRGBa> &I,vpColVector* X_) { Ic = I; vpImageConvert::convert(I,Ig); - initPlan(_X); + initPlan(X_); } /*! @@ -1568,14 +1622,14 @@ vpImageSimulator::init(const vpImage<vpRGBa> &I,vpColVector* _X) - \f$ X[3] \f$ :Bottom left corner. \param file_image : The adress of an image file. - \param _X : table of the 3D coordinates corresponding to the image corners. + \param X_ : table of the 3D coordinates corresponding to the image corners. */ void -vpImageSimulator::init(const char* file_image,vpColVector* _X) +vpImageSimulator::init(const char* file_image,vpColVector* X_) { vpImageIo::read(Ig,file_image); vpImageIo::read(Ic,file_image); - initPlan(_X); + initPlan(X_); } /*! @@ -1589,21 +1643,21 @@ vpImageSimulator::init(const char* file_image,vpColVector* _X) - \f$ X[3] \f$ :Bottom left corner. \param I : The image which is projected. - \param _X : Vector of the 3D coordinates in the object frame (oX, oY, oZ) + \param X_ : Vector of the 3D coordinates in the object frame (oX, oY, oZ) corresponding to the image corners. */ void -vpImageSimulator::init(const vpImage<unsigned char> &I, const std::vector<vpPoint>& _X) +vpImageSimulator::init(const vpImage<unsigned char> &I, const std::vector<vpPoint>& X_) { - if(_X.size() != 4){ + if(X_.size() != 4){ throw vpException(vpException::dimensionError, "the vector must contains 4 points to initialise the simulator"); } vpColVector Xvec[4]; for(unsigned int i=0; i<4; ++i){ Xvec[i].resize(3); - Xvec[i][0] = _X[i].get_oX(); - Xvec[i][1] = _X[i].get_oY(); - Xvec[i][2] = _X[i].get_oZ(); + Xvec[i][0] = X_[i].get_oX(); + Xvec[i][1] = X_[i].get_oY(); + Xvec[i][2] = X_[i].get_oZ(); } Ig = I; @@ -1613,7 +1667,7 @@ vpImageSimulator::init(const vpImage<unsigned char> &I, const std::vector<vpPoin /*! Initialise the image thanks to an image \f$ I \f$ and a table of vector containing the 3D coordinates of the image's corners. - \throw vpException::dimensionError if the _X vector is not of size 4. + \throw vpException::dimensionError if the X_ vector is not of size 4. - \f$ X[0] \f$ :Top left corner. - \f$ X[1] \f$ :Top right corner. @@ -1621,21 +1675,21 @@ vpImageSimulator::init(const vpImage<unsigned char> &I, const std::vector<vpPoin - \f$ X[3] \f$ :Bottom left corner. \param I : The image which is projected. - \param _X : Vector of the 3D coordinates in the object frame (oX, oY, oZ) + \param X_ : Vector of the 3D coordinates in the object frame (oX, oY, oZ) corresponding to the image corners. */ void -vpImageSimulator::init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& _X) +vpImageSimulator::init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& X_) { - if(_X.size() != 4){ + if(X_.size() != 4){ throw vpException(vpException::dimensionError, "the vector must contains 4 points to initialise the simulator"); } vpColVector Xvec[4]; for(unsigned int i=0; i<4; ++i){ Xvec[i].resize(3); - Xvec[i][0] = _X[i].get_oX(); - Xvec[i][1] = _X[i].get_oY(); - Xvec[i][2] = _X[i].get_oZ(); + Xvec[i][0] = X_[i].get_oX(); + Xvec[i][1] = X_[i].get_oY(); + Xvec[i][2] = X_[i].get_oZ(); } Ic = I; @@ -1645,7 +1699,7 @@ vpImageSimulator::init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& _X) /*! Initialise the image thanks to an image whose adress is given by \f$ file_image \f$ and a table of vector containing the 3D coordinates of the image's corners. - \throw vpException::dimensionError if the _X vector is not of size 4. + \throw vpException::dimensionError if the X_ vector is not of size 4. - \f$ X[0] \f$ :Top left corner. - \f$ X[1] \f$ :Top right corner. @@ -1653,21 +1707,21 @@ vpImageSimulator::init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& _X) - \f$ X[3] \f$ :Bottom left corner. \param file_image : The adress of an image file. - \param _X : Vector of the 3D coordinates in the object frame (oX, oY, oZ) + \param X_ : Vector of the 3D coordinates in the object frame (oX, oY, oZ) corresponding to the image corners. */ void -vpImageSimulator::init(const char* file_image, const std::vector<vpPoint>& _X) +vpImageSimulator::init(const char* file_image, const std::vector<vpPoint>& X_) { - if(_X.size() != 4){ + if(X_.size() != 4){ throw vpException(vpException::dimensionError, "the vector must contains 4 points to initialise the simulator"); } vpColVector Xvec[4]; for(unsigned int i=0; i<4; ++i){ Xvec[i].resize(3); - Xvec[i][0] = _X[i].get_oX(); - Xvec[i][1] = _X[i].get_oY(); - Xvec[i][2] = _X[i].get_oZ(); + Xvec[i][0] = X_[i].get_oX(); + Xvec[i][1] = X_[i].get_oY(); + Xvec[i][2] = X_[i].get_oZ(); } vpImageIo::read(Ig,file_image); @@ -1678,9 +1732,19 @@ vpImageSimulator::init(const char* file_image, const std::vector<vpPoint>& _X) bool vpImageSimulator::getPixel(const vpImagePoint &iP, unsigned char &Ipixelplan) { +// std::cout << "In get Pixel" << std::endl; //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; + +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)){ +//// std::cout << "The pixel is inside the projected area" << std::endl; +// return false;} //methoed algebrique double z; @@ -1728,8 +1792,16 @@ vpImageSimulator::getPixel(vpImage<unsigned char> &Isrc, const vpImagePoint &iP, unsigned char &Ipixelplan) { //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; + +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) +// return false; //methoed algebrique double z; @@ -1777,8 +1849,15 @@ bool vpImageSimulator::getPixel(const vpImagePoint &iP, vpRGBa &Ipixelplan) { //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) +// return false; //methoed algebrique double z; @@ -1826,8 +1905,15 @@ vpImageSimulator::getPixel(vpImage<vpRGBa> &Isrc, const vpImagePoint &iP, vpRGBa &Ipixelplan) { //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) +// return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; //methoed algebrique double z; @@ -1874,8 +1960,15 @@ bool vpImageSimulator::getPixelDepth(const vpImagePoint &iP, double &Zpixelplan) { //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) +// return false; Zpixelplan = distance/(normal_Cam_optim[0]*iP.get_u()+normal_Cam_optim[1]*iP.get_v()+normal_Cam_optim[2]); return true; @@ -1886,8 +1979,15 @@ vpImageSimulator::getPixelVisibility(const vpImagePoint &iP, double &Visipixelplan) { //test si pixel dans zone projetee - if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) - return false; + bool inside = false; + for(unsigned int i = 0 ; i < listTriangle.size() ; i++) + if(listTriangle[i].inTriangle(iP)){ + inside = true; + break; + } + if(!inside) return false; +// if(!T1.inTriangle(iP) && !T2.inTriangle(iP)) +// return false; Visipixelplan = visible_result; return true; @@ -1922,13 +2022,15 @@ void vpImageSimulator::getRoi(const unsigned int &Iwidth, const unsigned int &Iheight, const vpCameraParameters &cam, - vpPoint* point, vpRect &rectangle) + const std::vector<vpPoint> &point, + vpRect &rectangle) { double top = Iheight+1; double bottom = -1; double right = -1; double left= Iwidth+1; - for( int i = 0; i < 4; i++) + + for( unsigned int i = 0; i < point.size(); i++) { double u=0,v=0; vpMeterPixelConversion::convertPoint(cam,point[i].get_x(),point[i].get_y(),u,v); @@ -1951,3 +2053,18 @@ vpImageSimulator::getRoi(const unsigned int &Iwidth, rectangle.setLeft(left); rectangle.setRight(right); } + +std::vector<vpColVector> +vpImageSimulator::get3DcornersTextureRectangle() +{ + std::vector<vpColVector> X_; + for (int i=0; i<4; i++) + X_.push_back(X[i]); + return X_; +} + +VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpImageSimulator& /*ip*/) +{ + os << ""; + return os; +} diff --git a/src/simulator/image-simulator/vpImageSimulator.h b/src/simulator/image-simulator/vpImageSimulator.h index d9357ef42d3994b3c0ff8e231fd4fc98b3267ae4..054a519b5b94e76db03751033cda69d7bb2bcd29 100644 --- a/src/simulator/image-simulator/vpImageSimulator.h +++ b/src/simulator/image-simulator/vpImageSimulator.h @@ -3,7 +3,7 @@ * $Id: vpPose.h 2453 2010-01-07 10:01:10Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -152,9 +152,10 @@ class VISP_EXPORT vpImageSimulator private: vpColVector X[4]; - vpPoint pt[4]; vpHomogeneousMatrix cMt; vpColVector X2[4]; + std::vector<vpPoint> pt; + std::vector<vpPoint> ptClipped; vpInterpolationType interp; @@ -187,7 +188,7 @@ class VISP_EXPORT vpImageSimulator double *Xinter_optim; //triangles de projection du plan - vpTriangle T1,T2; + std::vector<vpTriangle> listTriangle; //image de texture vpColorPlan colorI; @@ -200,6 +201,9 @@ class VISP_EXPORT vpImageSimulator vpColor bgColor; vpColVector focal; + + //boolean to tell if the points in the camera frame have to be clipped + bool needClipping; public: vpImageSimulator(const vpColorPlan &col = COLORED); @@ -210,17 +214,17 @@ class VISP_EXPORT vpImageSimulator //creation du plan a partir de ses coordonnees 3D ds repere objet et de son image texture - void init(const vpImage<unsigned char> &I,vpColVector* _X); - void init(const vpImage<vpRGBa> &I,vpColVector* _X); - void init(const char* file_image,vpColVector* _X); - void init(const vpImage<unsigned char> &I, const std::vector<vpPoint>& _X); - void init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& _X); - void init(const char* file_image, const std::vector<vpPoint>& _X); + void init(const vpImage<unsigned char> &I,vpColVector* X); + void init(const vpImage<vpRGBa> &I,vpColVector* X); + void init(const char* file_image,vpColVector* X); + void init(const vpImage<unsigned char> &I, const std::vector<vpPoint>& X); + void init(const vpImage<vpRGBa> &I, const std::vector<vpPoint>& X); + void init(const char* file_image, const std::vector<vpPoint>& X); //projection du plan par cMo => creation des deux triangles definissant projection du plan sur plan image (coord en metre) - void setCameraPosition(const vpHomogeneousMatrix &_cMt); + void setCameraPosition(const vpHomogeneousMatrix &cMt); - void setInterpolationType (const vpInterpolationType interp) {this->interp = interp;} + void setInterpolationType (const vpInterpolationType interplt) {this->interp = interplt;} void getImage(vpImage<unsigned char> &I, const vpCameraParameters &cam); void getImage(vpImage<vpRGBa> &I, const vpCameraParameters &cam); @@ -242,6 +246,10 @@ class VISP_EXPORT vpImageSimulator std::list <vpImageSimulator> &list, const vpCameraParameters &cam); + std::vector<vpColVector> get3DcornersTextureRectangle(); + + friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpImageSimulator& /*ip*/); + /*! As it can be time consuming to reset all the image to a default baground value, this function enable to reset only the pixel which changed the previous time. @@ -278,11 +286,11 @@ class VISP_EXPORT vpImageSimulator #endif private: - void initPlan(vpColVector* _X); + void initPlan(vpColVector* X); //result = plan est visible. //ie: un plan est oriente dans si normal_plan.focal < 0 => plan est visible sinon invisible. - bool isVisible() {return visible;}; + bool isVisible() {return visible;} //function that project a point x,y on the plane, return true if the projection is on the limited plane // and in this case return the corresponding image pixel Ipixelplan @@ -304,15 +312,9 @@ class VISP_EXPORT vpImageSimulator void getCoordFromHomog(const vpColVector &_vH, vpColVector &_v); void getRoi(const unsigned int &Iwidth, const unsigned int &Iheight, - const vpCameraParameters &cam, vpPoint* pt, vpRect &rect); + const vpCameraParameters &cam, const std::vector<vpPoint> &point, vpRect &rect); }; -VISP_EXPORT inline std::ostream& operator<< (std::ostream &os, const vpImageSimulator& /*ip*/) -{ - os << ""; - return os; -} - #endif diff --git a/src/simulator/ogre-simulator/vpAROgre.cpp b/src/simulator/ogre-simulator/vpAROgre.cpp index bcedcc320d24d7659d3821c62d407261aa533c5d..074a0e6bc3cece07e494d4fdf0c0d94fab09f6ab 100644 --- a/src/simulator/ogre-simulator/vpAROgre.cpp +++ b/src/simulator/ogre-simulator/vpAROgre.cpp @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpAROgre.cpp 4251 2013-05-14 12:19:56Z fspindle $ + * $Id: vpAROgre.cpp 5234 2015-01-30 13:51:02Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -72,36 +72,24 @@ telling Ogre where to look for renderer plugins. */ -vpAROgre::vpAROgre(const vpCameraParameters &cam, - unsigned int width, unsigned int height, +vpAROgre::vpAROgre(const vpCameraParameters &cam, + unsigned int width, unsigned int height, const char *resourcePath, const char *pluginsPath) - : mRoot(0), mCamera(0), mSceneMgr(0), mWindow(0) + : name("ViSP - Augmented Reality"),mRoot(0), mCamera(0), mSceneMgr(0), mWindow(0), + mResourcePath(resourcePath), mPluginsPath(pluginsPath), #ifdef VISP_HAVE_OIS - , mInputManager(0), mKeyboard(0) + mInputManager(0), mKeyboard(0), #endif + keepOn(true), // When created no reason to stop displaying + mImageRGBA(), mImage(), mPixelBuffer(NULL), mBackground(NULL), mBackgroundHeight(0), + mBackgroundWidth(0), mWindowHeight(height), mWindowWidth(width), windowHidden(false), + mNearClipping(0.001), mFarClipping(200), mcam(cam), mshowConfigDialog(true), + mOptionnalResourceLocation() { - // Get resources.cfg path - mResourcePath = resourcePath; - //std::cout << "mResourcePath: " << mResourcePath<< std::endl; - // Get plugins.cfg path - mPluginsPath = pluginsPath; - //std::cout << "mPluginsPath: " << mPluginsPath<< std::endl; - // Set intrinsic camera parameters - mcam = cam; - // When created no reason to stop displaying - keepOn = true; - // Set Dimensions - mWindowWidth = width; - mWindowHeight = height; - windowHidden = false; - mshowConfigDialog = true; - mOptionnalResourceLocation.clear(); - - name = "ViSP - Augmented Reality"; } /*! - Initialisation of Ogre with a grey level background. + Initialisation of Ogre with a grey level background. Load the plugins that are specified in the plugins.cfg or plugins_d.cfg files. These files are located in @@ -109,27 +97,27 @@ vpAROgre::vpAROgre(const vpCameraParameters &cam, Note that plugins.cfg file is always considered under Unix platforms. The file plugins_d.cfg is only considered under Windows when the build type is Debug. - + Load also the resources that are defined in the resources.cfg file. This file is located in VISP_HAVE_OGRE_RESOURCES_PATH folder that is defined in vpConfig.h. - - Create also the grey level background used to display the image. + + Create also the grey level background used to display the image. \param I : Image that is displayed in the background. \param bufferedKeys : If true, use of buffered input for the keyboard (see Ogre documentation). Note that this parameter is only useful if OIS is used. - - \param hidden : If true, the created window will be hidden. + + \param hidden : If true, the created window will be hidden. Note that this functionnality requires Ogre3D 1.8.1 at least. - + \exception vpException::ioError : If the required plugins.cfg / plugins_d.cfg or resources.cfg files are not accessible. */ void vpAROgre::init(vpImage<unsigned char> &I, - bool + bool #ifdef VISP_HAVE_OIS bufferedKeys #endif @@ -138,21 +126,21 @@ void vpAROgre::init(vpImage<unsigned char> &I, { mBackgroundWidth = I.getWidth(); mBackgroundHeight = I.getHeight(); - + init( #ifdef VISP_HAVE_OIS bufferedKeys, #else false, #endif - hidden + hidden ); // Create the background image which will come from the grabber createBackground(I); } /*! - Initialisation of Ogre with a color background. + Initialisation of Ogre with a color background. Load the plugins that are specified in the plugins.cfg or plugins_d.cfg files. These files are located in @@ -160,27 +148,27 @@ void vpAROgre::init(vpImage<unsigned char> &I, Note that plugins.cfg file is always considered under Unix platforms. The file plugins_d.cfg is only considered under Windows when the build type is Debug. - + Load also the resources that are defined in the resources.cfg file. This file is located in VISP_HAVE_OGRE_RESOURCES_PATH folder that is defined in vpConfig.h. - - Create also a color background used to display the image. + + Create also a color background used to display the image. \param I : Image that is displayed in the background. \param bufferedKeys : If true, use of buffered input for the keyboard (see Ogre documentation). Note that this parameter is only useful if OIS is used. - - \param hidden : If true, the created window will be hidden. + + \param hidden : If true, the created window will be hidden. Note that this functionnality requires Ogre3D 1.8.1 at least. - + \exception vpException::ioError : If the required plugins.cfg / plugins_d.cfg or resources.cfg files are not accessible. */ void vpAROgre::init(vpImage<vpRGBa> &I, - bool + bool #ifdef VISP_HAVE_OIS bufferedKeys #endif @@ -189,7 +177,7 @@ void vpAROgre::init(vpImage<vpRGBa> &I, { mBackgroundWidth = I.getWidth(); mBackgroundHeight = I.getHeight(); - + init( #ifdef VISP_HAVE_OIS bufferedKeys, @@ -203,7 +191,7 @@ void vpAROgre::init(vpImage<vpRGBa> &I, } /*! - Initialisation of Ogre. + Initialisation of Ogre. Load the plugins that are specified in the plugins.cfg or plugins_d.cfg files. These files are located in @@ -211,22 +199,22 @@ void vpAROgre::init(vpImage<vpRGBa> &I, Note that plugins.cfg file is always considered under Unix platforms. The file plugins_d.cfg is only considered under Windows when the build type is Debug. - + Load also the resources that are defined in the resources.cfg file. This file is located in VISP_HAVE_OGRE_RESOURCES_PATH folder that is defined in vpConfig.h. - + \param bufferedKeys : If true, use of buffered input for the keyboard (see Ogre documentation). Note that this parameter is only useful if OIS is used. - - \param hidden : If true, the created window will be hidden. + + \param hidden : If true, the created window will be hidden. Note that this functionnality requires Ogre3D 1.8.1 at least. - + \exception vpException::ioError : If the required plugins.cfg / plugins_d.cfg or resources.cfg files are not accessible. */ -void vpAROgre::init(bool +void vpAROgre::init(bool #ifdef VISP_HAVE_OIS bufferedKeys #endif @@ -234,20 +222,20 @@ void vpAROgre::init(bool ) { // Create the root -#if defined(NDEBUG) || !defined(WIN32) +#if defined(NDEBUG) || !defined(_WIN32) std::string pluginFile = mPluginsPath+"/plugins.cfg"; #else std::string pluginFile = mPluginsPath+"/plugins_d.cfg"; #endif if(!vpIoTools::checkFilename(pluginFile)){ - std::string errorMsg = "Error: the requested plugins file \"" + std::string errorMsg = "Error: the requested plugins file \"" + pluginFile + "\" doesn't exist."; std::cout << errorMsg << std::endl; throw (vpException(vpException::ioError, errorMsg)); } std::cout << "######################### Load plugin file: " << pluginFile << std::endl; - + if(Ogre::Root::getSingletonPtr() == NULL) mRoot = new Ogre::Root(pluginFile, "ogre.cfg", "Ogre.log"); else @@ -266,7 +254,7 @@ void vpAROgre::init(bool Ogre::ConfigFile cf; std::string resourceFile = mResourcePath+"/resources.cfg"; if(!vpIoTools::checkFilename(resourceFile)){ - std::string errorMsg = "Error: the requested resource file \"" + std::string errorMsg = "Error: the requested resource file \"" + resourceFile + "\" doesn't exist."; std::cout << errorMsg << std::endl; @@ -297,7 +285,7 @@ void vpAROgre::init(bool for(std::list<std::string>::const_iterator iter = mOptionnalResourceLocation.begin(); iter != mOptionnalResourceLocation.end(); ++iter){ Ogre::ResourceGroupManager::getSingleton().addResourceLocation(*iter, "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); } - + // Create the window bool canInit = true; if(mshowConfigDialog){ @@ -309,33 +297,36 @@ void vpAROgre::init(bool if(!mRoot->restoreConfig()) canInit = false; } - + if(!mRoot->isInitialised()){ if(!canInit){ //We set the default renderer system const Ogre::RenderSystemList& lRenderSystemList = mRoot->getAvailableRenderers(); if( lRenderSystemList.size() == 0 ) - throw "ConfigDialog aborted"; // Exit the application on cancel - + throw "ConfigDialog aborted"; // Exit the application on cancel + Ogre::RenderSystem *lRenderSystem = lRenderSystemList.at(0); std::cout << "Using " << lRenderSystem->getName() << " as renderer." << std::endl; mRoot->setRenderSystem(lRenderSystem); } - - mRoot->initialise(false); + + mRoot->initialise(false); } - - bool fullscreen = false; + + bool fullscreen = false; Ogre::NameValuePairList misc; Ogre::ConfigOptionMap config = mRoot->getRenderSystem()->getConfigOptions(); Ogre::ConfigOptionMap::const_iterator it = config.begin(); - + while( it != config.end() ){ Ogre::String leftconf = (*it).first; Ogre::String rightconf = (*it).second.currentValue; - + if(leftconf == "Video Mode"){ - if(canInit) - sscanf(rightconf.c_str(), "%d %*s %d", &mWindowWidth, &mWindowHeight); + if(canInit) { + int ret = sscanf(rightconf.c_str(), "%d %*s %d", &mWindowWidth, &mWindowHeight); + if (ret == 0) + std::cout << "Cannot read Ogre video mode" << std::endl; + } else{ if(mWindowWidth == 0 && mWindowHeight == 0){ mWindowWidth = mBackgroundWidth; @@ -350,19 +341,19 @@ void vpAROgre::init(bool } else misc[leftconf] = rightconf; - + it++; } - + // With Ogre version >= 1.8.1 we hide the window - if( hidden && ((OGRE_VERSION_MAJOR << 16 | OGRE_VERSION_MINOR << 8 | OGRE_VERSION_PATCH) >= (1 << 16 | 8 << 8 | 1)) ){ + if( hidden ){ +#if ( OGRE_VERSION >= (1 << 16 | 8 << 8 | 1) ) misc["hidden"] = "true"; windowHidden = true; - mWindow = mRoot->createRenderWindow(name, mWindowWidth, mWindowHeight, fullscreen, &misc); +#endif } - else - mWindow = mRoot->createRenderWindow(name, mWindowWidth, mWindowHeight, fullscreen, &misc); - + mWindow = mRoot->createRenderWindow(name, mWindowWidth, mWindowHeight, fullscreen, &misc); + // Initialise resources Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); //----------------------------------------------------- @@ -374,12 +365,12 @@ void vpAROgre::init(bool // ST_EXTERIOR_REAL_FAR = paging landscape // ST_INTERIOR = Quake3 BSP //----------------------------------------------------- - + mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC); - + // Create the camera createCamera(); - + // Create a viewport Ogre::Viewport* viewPort = mWindow->addViewport(mCamera); // Ogre::Viewport* viewPort = mCamera->getViewport(); @@ -408,7 +399,7 @@ void vpAROgre::init(bool windowHndStr << windowHnd; pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); // Let the user use the keyboard elsewhere -#if defined (UNIX) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false"))); #endif @@ -420,12 +411,12 @@ void vpAROgre::init(bool if ( !bufferedKeys ) mKeyboard->setEventCallback ( this); #endif - // Initialise a render to texture to be able to retrieve a screenshot + // Initialise a render to texture to be able to retrieve a screenshot Ogre::TexturePtr Texture = Ogre::TextureManager::getSingleton().createManual("rtf", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,Ogre::TEX_TYPE_2D, mWindow->getWidth(),mWindow->getHeight(), 0, Ogre::PF_R8G8B8A8, Ogre::TU_RENDERTARGET); - - - + + + // Ogre::TexturePtr Texture = Ogre::TextureManager::getSingleton().createManual("rtf", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,Ogre::TEX_TYPE_2D, // 640,480, 0, Ogre::PF_R8G8B8A8, Ogre::TU_RENDERTARGET); Ogre::RenderTexture* RTarget = Texture->getBuffer()->getRenderTarget(); @@ -507,7 +498,7 @@ bool vpAROgre::frameEnded(const Ogre::FrameEvent& evt) else return result; } -/*! +/*! Function telling what to do before each frame. This method is called before every frame rendered by Ogre. @@ -558,7 +549,7 @@ void vpAROgre::windowClosed(Ogre::RenderWindow* rw) \param I : Grey level image to show in background. \param cMw : Camera pose as an homogeneous matrix. */ -bool vpAROgre::renderOneFrame(const vpImage<unsigned char> &I, +bool vpAROgre::renderOneFrame(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMw) { // Update the background to match the situation @@ -576,7 +567,7 @@ bool vpAROgre::renderOneFrame(const vpImage<unsigned char> &I, \param I : RGBa image to show in background. \param cMw : Camera pose as an homogeneous matrix. */ -bool vpAROgre::renderOneFrame(const vpImage<vpRGBa> &I, +bool vpAROgre::renderOneFrame(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMw) { // Update the background to match the situation @@ -594,7 +585,7 @@ bool vpAROgre::renderOneFrame(const vpImage<vpRGBa> &I, \param I : Grey level image to show in background. \param cMw : Camera pose as an homogeneous matrix. */ -void vpAROgre::display(const vpImage<unsigned char> &I, +void vpAROgre::display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMw) { // Display on Ogre Window @@ -635,7 +626,7 @@ bool vpAROgre::continueRendering(void) Set the camera intrinsic parameters */ void vpAROgre::setCameraParameters(const vpCameraParameters &cameraP ) -{ +{ mcam = cameraP; } @@ -657,7 +648,7 @@ void vpAROgre::load(const std::string &name, const std::string &model) \param wTo : New position of the node (translation between object frame and world frame). */ -void vpAROgre::setPosition(const std::string &name, +void vpAROgre::setPosition(const std::string &name, const vpTranslationVector &wTo) { // Reset the position @@ -686,7 +677,7 @@ void vpAROgre::setRotation(const std::string &name, const vpRotationMatrix &wRo) // Get the node in its original position mSceneMgr->getSceneNode(name)->resetOrientation(); // Apply the new rotation - Ogre::Matrix3 rotationOgre + Ogre::Matrix3 rotationOgre = Ogre::Matrix3( (Ogre::Real)wRo[0][0], (Ogre::Real)wRo[0][1], (Ogre::Real)wRo[0][2], (Ogre::Real)wRo[1][0], (Ogre::Real)wRo[1][1], (Ogre::Real)wRo[1][2], (Ogre::Real)wRo[2][0], (Ogre::Real)wRo[2][1], (Ogre::Real)wRo[2][2]); @@ -699,18 +690,18 @@ void vpAROgre::setRotation(const std::string &name, const vpRotationMatrix &wRo) \param name : Name of the SceneNode to rotate. \param wRo : The rotation matrix representing the rotation to apply. */ -void vpAROgre::addRotation(const std::string &name, +void vpAROgre::addRotation(const std::string &name, const vpRotationMatrix &wRo) { - // Apply the new rotation - Ogre::Matrix3 rotationOgre + // Apply the new rotation + Ogre::Matrix3 rotationOgre = Ogre::Matrix3( (Ogre::Real)wRo[0][0], (Ogre::Real)wRo[0][1], (Ogre::Real)wRo[0][2], (Ogre::Real)wRo[1][0], (Ogre::Real)wRo[1][1], (Ogre::Real)wRo[1][2], (Ogre::Real)wRo[2][0], (Ogre::Real)wRo[2][1], (Ogre::Real)wRo[2][2]); Ogre::Quaternion q(rotationOgre); mSceneMgr->getSceneNode(name)->rotate(q); - - + + } /*! @@ -721,7 +712,7 @@ void vpAROgre::addRotation(const std::string &name, translation to apply. */ -void vpAROgre::setPosition(const std::string &name, +void vpAROgre::setPosition(const std::string &name, const vpHomogeneousMatrix &wMo) { // Extract the position and orientation data @@ -808,14 +799,22 @@ void vpAROgre::createBackground(vpImage<unsigned char> & /* I */) // Pointer to the dynamic texture Ogre::TexturePtr dynTexPtr = Ogre::TextureManager::getSingleton().getByName("BackgroundTexture"); - - // Get the pixel buffer +//#if ( OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) ) +// .dynamicCast<Ogre::Texture>();// Get the pixel buffer +//#else +// ; +//#endif mPixelBuffer = dynTexPtr->getBuffer(); // Material to apply the texture to the background - Ogre::MaterialPtr Backgroundmaterial + Ogre::MaterialPtr Backgroundmaterial = Ogre::MaterialManager::getSingleton().create("BackgroundMaterial", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); +//#if ( OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) ) +// .dynamicCast<Ogre::Material>(); +//#else +// ; +//#endif Ogre::Technique *Backgroundtechnique = Backgroundmaterial->createTechnique(); Backgroundtechnique->createPass(); Backgroundmaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); @@ -873,15 +872,26 @@ void vpAROgre::createBackground(vpImage<vpRGBa> & /* I */) // Pointer to the dynamic texture - Ogre::TexturePtr dynTexPtr = Ogre::TextureManager::getSingleton().getByName("BackgroundTexture"); + Ogre::TexturePtr dynTexPtr = + Ogre::TextureManager::getSingleton().getByName("BackgroundTexture"); +//#if ( OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) ) +// .dynamicCast<Ogre::Texture>();// Get the pixel buffer +//#else +// ; +//#endif // Get the pixel buffer mPixelBuffer = dynTexPtr->getBuffer(); // Material to apply the texture to the background - Ogre::MaterialPtr Backgroundmaterial + Ogre::MaterialPtr Backgroundmaterial = Ogre::MaterialManager::getSingleton().create("BackgroundMaterial", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); +//#if ( OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) ) +// .dynamicCast<Ogre::Material>(); +//#else +// ; +//#endif Ogre::Technique *Backgroundtechnique = Backgroundmaterial->createTechnique(); Backgroundtechnique->createPass(); Backgroundmaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); @@ -919,23 +929,27 @@ void vpAROgre::closeOIS(void) /*! Update the projection parameters of the camera. */ +// Note: equation taken from: +// http://strawlab.org/2011/11/05/augmented-reality-with-OpenGL/ void vpAROgre::updateCameraProjection(void) { - Ogre::Real f,n,f_m_n,f_p_n,px,py,u0,v0; - f = (Ogre::Real)200.0; // Far clip distance - n = (Ogre::Real)0.001; // Near clip distance - f_m_n = (Ogre::Real)(f-n); - f_p_n = (Ogre::Real)(f+n); - px = (Ogre::Real)mcam.get_px(); - py = (Ogre::Real)mcam.get_py(); - u0 = (Ogre::Real)mcam.get_u0(); - v0 = (Ogre::Real)mcam.get_v0(); - Ogre::Matrix4 Projection - = Ogre::Matrix4( (Ogre::Real)(2.0*px/mBackgroundWidth), 0, (Ogre::Real)(2.0*(u0/mBackgroundWidth)-1.0), 0, - 0, (Ogre::Real)(2.0*py/mBackgroundHeight), (Ogre::Real)(2.0*(v0/mBackgroundHeight)-1.0),0, - 0, 0, (Ogre::Real)(-1.0*f_p_n/f_m_n), (Ogre::Real)(-2.0*f*n/f_m_n), - 0, 0, -1.0, 0); - mCamera->setCustomProjectionMatrix(true, Projection); + if(mCamera != 0){ + Ogre::Real f,n,f_m_n,f_p_n,px,py,u0,v0; + f = (Ogre::Real)(mFarClipping); // Far clip distance + n = (Ogre::Real)(mNearClipping); // Near clip distance + f_m_n = (Ogre::Real)(f-n); + f_p_n = (Ogre::Real)(f+n); + px = (Ogre::Real)mcam.get_px(); + py = (Ogre::Real)mcam.get_py(); + u0 = (Ogre::Real)mcam.get_u0(); + v0 = (Ogre::Real)mcam.get_v0(); + Ogre::Matrix4 Projection + = Ogre::Matrix4( (Ogre::Real)(2.0*px/mBackgroundWidth), 0, (Ogre::Real)(1.0 - 2.0*(u0/mBackgroundWidth)), 0, + 0, (Ogre::Real)(2.0*py/mBackgroundHeight), (Ogre::Real)(-1.0 + 2.0*(v0/mBackgroundHeight)),0, + 0, 0, (Ogre::Real)(-1.0*f_p_n/f_m_n), (Ogre::Real)(-2.0*f*n/f_m_n), + 0, 0, -1.0, 0); + mCamera->setCustomProjectionMatrix(true, Projection); + } } /*! @@ -944,7 +958,7 @@ void vpAROgre::updateCameraProjection(void) void vpAROgre::updateBackgroundTexture(const vpImage<unsigned char> &I) { // Inspired from Ogre wiki : http://www.ogre3d.org/tikiwiki/Creating+dynamic+textures - // Lock the pixel buffer and get a pixel box. HBL_DISCARD is to use for best + // Lock the pixel buffer and get a pixel box. HBL_DISCARD is to use for best // performance than HBL_NORMAL mPixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); // Lock the buffer const Ogre::PixelBox& pixelBox = mPixelBuffer->getCurrentLock(); @@ -963,7 +977,7 @@ void vpAROgre::updateBackgroundTexture(const vpImage<unsigned char> &I) void vpAROgre::updateBackgroundTexture(const vpImage<vpRGBa> &I) { // Inspired from Ogre wiki : http://www.ogre3d.org/tikiwiki/Creating+dynamic+textures - // Lock the pixel buffer and get a pixel box. HBL_DISCARD is to use for best + // Lock the pixel buffer and get a pixel box. HBL_DISCARD is to use for best // performance than HBL_NORMAL mPixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); // Lock the buffer const Ogre::PixelBox& pixelBox = mPixelBuffer->getCurrentLock(); @@ -977,18 +991,18 @@ void vpAROgre::updateBackgroundTexture(const vpImage<vpRGBa> &I) // *pDest++=I[i][mBackgroundWidth-j].B; // Blue component // *pDest++=I[i][mBackgroundWidth-j].G; // Green component // *pDest++=I[i][mBackgroundWidth-j].R; // Red component - + *pDest++=I[i][j].B; // Blue component *pDest++=I[i][j].G; // Green component *pDest++=I[i][j].R; // Red component - + *pDest++ = 255; // Alpha component } } #else // if texture in RGBa format which is the format of the input image memcpy(pDest, I.bitmap, mBackgroundHeight*mBackgroundWidth*sizeof(vpRGBa)); #endif - + // Unlock the pixel buffer mPixelBuffer->unlock(); } @@ -999,7 +1013,7 @@ void vpAROgre::updateBackgroundTexture(const vpImage<vpRGBa> &I) void vpAROgre::updateCameraParameters (const vpHomogeneousMatrix &cMw) { // The matrix is given to Ogre with some changes to fit with the world projection - Ogre::Matrix4 ModelView + Ogre::Matrix4 ModelView // = Ogre::Matrix4( (Ogre::Real)-cMo[0][0], (Ogre::Real)-cMo[0][1], (Ogre::Real)-cMo[0][2], (Ogre::Real)-cMo[0][3], = Ogre::Matrix4( (Ogre::Real)cMw[0][0], (Ogre::Real)cMw[0][1], (Ogre::Real)cMw[0][2], (Ogre::Real)cMw[0][3], (Ogre::Real)-cMw[1][0], (Ogre::Real)-cMw[1][1], (Ogre::Real)-cMw[1][2], (Ogre::Real)-cMw[1][3], @@ -1018,6 +1032,11 @@ void vpAROgre::getRenderingOutput(vpImage<vpRGBa> &I, const vpHomogeneousMatrix { updateCameraParameters(cMo); Ogre::TexturePtr dynTexPtr = Ogre::TextureManager::getSingleton().getByName("rtf"); +//#if ( OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) ) +// .dynamicCast<Ogre::Texture>(); +//#else +// ; +//#endif Ogre::RenderTexture* RTarget = dynTexPtr->getBuffer()->getRenderTarget(); mWindow->update(); RTarget->update(); @@ -1032,7 +1051,7 @@ void vpAROgre::getRenderingOutput(vpImage<vpRGBa> &I, const vpHomogeneousMatrix #if 1 // if texture in BGRa format for(unsigned int i=0; i<I.getHeight(); i++){ for(unsigned int j=0; j<I.getWidth(); j++){ - // Color Image + // Color Image I[i][j].B = *pDest++; // Blue component I[i][j].G = *pDest++; // Green component I[i][j].R = *pDest++; // Red component diff --git a/src/simulator/ogre-simulator/vpAROgre.h b/src/simulator/ogre-simulator/vpAROgre.h index 1c1c5f07115495b7a1b67590b7b84d796a98178b..7ce79b51b0d8a22aed1d6f09442d85ac8f4bbfc5 100644 --- a/src/simulator/ogre-simulator/vpAROgre.h +++ b/src/simulator/ogre-simulator/vpAROgre.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAROgre.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpAROgre.h 4931 2014-10-09 14:02:52Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -140,6 +140,20 @@ class VISP_EXPORT vpAROgre : public Ogre::FrameListener, public Ogre::WindowEven const vpHomogeneousMatrix &cMw); inline Ogre::Camera* getCamera(){ return mCamera; } + + /*! + Get the far distance for clipping. + + \return Far clipping value. + */ + inline double getFarClippingDistance() const { return mFarClipping; } + + /*! + Get the near distance for clipping. + + \return Near clipping value. + */ + inline double getNearClippingDistance() const { return mNearClipping; } vpTranslationVector getPosition(const std::string &name)const; @@ -175,6 +189,26 @@ class VISP_EXPORT vpAROgre : public Ogre::FrameListener, public Ogre::WindowEven bool renderOneFrame(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMw); void setCameraParameters(const vpCameraParameters &cameraP); + + /*! + Set the far distance for clipping. + + \param dist : Far clipping value. + */ + void setFarClippingDistance(const double &dist){ + mFarClipping = dist; + updateCameraProjection(); + } + + /*! + Set the near distance for clipping. + + \param dist : Near clipping value. + */ + void setNearClippingDistance(const double &dist){ + mNearClipping = dist; + updateCameraProjection(); + } /*! Set the plugins path used to locate the plugins.cfg file. @@ -287,7 +321,15 @@ class VISP_EXPORT vpAROgre : public Ogre::FrameListener, public Ogre::WindowEven \return Always true. */ - virtual bool destroyScene(void) {return true;}; + virtual bool destroyScene(void) { + if(!mSceneMgr) + return false; + + mSceneMgr->destroyAllCameras (); + mSceneMgr->clearScene(); + mRoot->destroySceneManager(mSceneMgr); + return true; + } virtual void updateCameraParameters (const vpHomogeneousMatrix &cMo); @@ -340,6 +382,8 @@ class VISP_EXPORT vpAROgre : public Ogre::FrameListener, public Ogre::WindowEven bool windowHidden; /** Is window hidden */ // Camera calculations + double mNearClipping; /** Near Clipping Distance **/ + double mFarClipping; /** Far Clipping Distance **/ vpCameraParameters mcam; /** The intrinsic camera parameters */ bool mshowConfigDialog; /** if true, shows the dialog window (used to set the display options) */ diff --git a/src/simulator/wireframe-simulator/core/vpArit.c b/src/simulator/wireframe-simulator/core/vpArit.cpp similarity index 99% rename from src/simulator/wireframe-simulator/core/vpArit.c rename to src/simulator/wireframe-simulator/core/vpArit.cpp index f804f4b9cf0e84d3392e2800f1f151ae34af9902..d4ca6a5ced84f7d73de20a3d0c36698e2727955c 100755 --- a/src/simulator/wireframe-simulator/core/vpArit.c +++ b/src/simulator/wireframe-simulator/core/vpArit.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpArit.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpArit.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpArit.h b/src/simulator/wireframe-simulator/core/vpArit.h index b0790fe3b31096231e10641eab8821be77b1d9ff..6424b1cd32a016a3ed166937915be59ac5870a4c 100755 --- a/src/simulator/wireframe-simulator/core/vpArit.h +++ b/src/simulator/wireframe-simulator/core/vpArit.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpArit.h 4310 2013-07-08 08:48:34Z fspindle $ + * $Id: vpArit.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpAritio.c b/src/simulator/wireframe-simulator/core/vpAritio.cpp similarity index 95% rename from src/simulator/wireframe-simulator/core/vpAritio.c rename to src/simulator/wireframe-simulator/core/vpAritio.cpp index efd96a9562436d44fe3a1b7252412d2e5b111bbe..716db6ea61ce1326673413a1eb7e5f32fd240d8d 100755 --- a/src/simulator/wireframe-simulator/core/vpAritio.c +++ b/src/simulator/wireframe-simulator/core/vpAritio.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpAritio.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpAritio.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ void fprintf_Position (FILE *f, AritPosition *pp) */ void fscanf_Point3f (Point3f *pp) { -static char *err_tbl[] = { +static const char *err_tbl[] = { "float expected (coordinate ", " of point)" }; @@ -106,7 +106,7 @@ static char *err_tbl[] = { */ void fscanf_Vector (Vector *vp) { -static char *err_tbl[] = { +static const char *err_tbl[] = { "float expected (coordinate ", " of vector)" }; diff --git a/src/simulator/wireframe-simulator/core/vpBound.c b/src/simulator/wireframe-simulator/core/vpBound.cpp similarity index 98% rename from src/simulator/wireframe-simulator/core/vpBound.c rename to src/simulator/wireframe-simulator/core/vpBound.cpp index 11904bd3647762d045904b6baa330f14fee8d5f4..1a875159a37c35c80114771462f57d25b917b67e 100755 --- a/src/simulator/wireframe-simulator/core/vpBound.c +++ b/src/simulator/wireframe-simulator/core/vpBound.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBound.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBound.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpBound.h b/src/simulator/wireframe-simulator/core/vpBound.h index ae03f6528d790cb22a7c0e44f876d103853e3649..d05b2a9b1035721b6282d4410713d9f094e79a1f 100755 --- a/src/simulator/wireframe-simulator/core/vpBound.h +++ b/src/simulator/wireframe-simulator/core/vpBound.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBound.h 4332 2013-07-22 14:23:46Z fspindle $ + * $Id: vpBound.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpBoundio.c b/src/simulator/wireframe-simulator/core/vpBoundio.cpp similarity index 96% rename from src/simulator/wireframe-simulator/core/vpBoundio.c rename to src/simulator/wireframe-simulator/core/vpBoundio.cpp index 0f191a98ae3b13d79f457ec7cc4191beb641cbf4..fe971a0ac15ecde7ebb09d1909dd805608ed24be 100755 --- a/src/simulator/wireframe-simulator/core/vpBoundio.c +++ b/src/simulator/wireframe-simulator/core/vpBoundio.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBoundio.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBoundio.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -166,9 +166,9 @@ void fscanf_Face_list (Face_list *lp) */ void fscanf_Point3f_list (Point3f_list *lp) { - static char proc_name[] = "fscanf_Point3f_list"; + static const char proc_name[] = "fscanf_Point3f_list"; -static char *err_tbl[] = { +static const char *err_tbl[] = { "float expected (coordinate ", " of point)" }; diff --git a/src/simulator/wireframe-simulator/core/vpCgiconstants.h b/src/simulator/wireframe-simulator/core/vpCgiconstants.h index f01ad4ee24cb09d1c2716fd3eb416feb5228d95e..c4b9727ecea3a2462da99bca541520521aa3b991 100755 --- a/src/simulator/wireframe-simulator/core/vpCgiconstants.h +++ b/src/simulator/wireframe-simulator/core/vpCgiconstants.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCgiconstants.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCgiconstants.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpCgidefs.h b/src/simulator/wireframe-simulator/core/vpCgidefs.h index b01bdc205b69b20787160a385ffcb09e36368291..7ed5c3b64294b4ded270aadc52c0ef90ba001fbd 100755 --- a/src/simulator/wireframe-simulator/core/vpCgidefs.h +++ b/src/simulator/wireframe-simulator/core/vpCgidefs.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCgidefs.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCgidefs.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpClipping.c b/src/simulator/wireframe-simulator/core/vpClipping.cpp similarity index 94% rename from src/simulator/wireframe-simulator/core/vpClipping.c rename to src/simulator/wireframe-simulator/core/vpClipping.cpp index ea7c833ed7c227b51107ed67c3e6acda5ea7e8cf..63c34136e4007d5eecc73a3429522b3ee5bd77af 100755 --- a/src/simulator/wireframe-simulator/core/vpClipping.c +++ b/src/simulator/wireframe-simulator/core/vpClipping.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpClipping.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpClipping.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,8 +58,8 @@ #include <visp/vpView.h> #include <stdio.h> #include <stdlib.h> -#include <float.h> //DBL_EPSILON -#include <math.h> //DBL_EPSILON +#include <limits> +#include <cmath> void open_clipping (void); void close_clipping (void); @@ -337,7 +337,7 @@ inter (Byte mask, Index v0, Index v1) /* t = (p0->w - p0->y) / ((p0->w - p0->y) - (p1->w - p1->y)); */ t = (p0->w - p0->y) - (p1->w - p1->y); //t = (t == 0) ? (float)1.0 : (p0->w - p0->y) / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : (p0->w - p0->y) / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->y) / t; PAR_COORD3(*p,t,*p0,*p1); p->w = p->y; /* propriete du point d'intersection */ break; @@ -346,7 +346,7 @@ inter (Byte mask, Index v0, Index v1) /* t = (p0->w + p0->y) / ((p0->w + p0->y) - (p1->w + p1->y)); */ t = (p0->w + p0->y) - (p1->w + p1->y); //t = (t == 0) ? (float)1.0 : (p0->w + p0->y) / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : (p0->w + p0->y) / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w + p0->y) / t; PAR_COORD3(*p,t,*p0,*p1); p->w = - p->y; /* propriete du point d'intersection */ break; @@ -355,7 +355,7 @@ inter (Byte mask, Index v0, Index v1) /* t = (p0->w - p0->x) / ((p0->w - p0->x) - (p1->w - p1->x)); */ t = (p0->w - p0->x) - (p1->w - p1->x); //t = (t == 0) ? (float)1.0 : (p0->w - p0->x) / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : (p0->w - p0->x) / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->x) / t; PAR_COORD3(*p,t,*p0,*p1); p->w = p->x; /* propriete du point d'intersection */ break; @@ -364,7 +364,7 @@ inter (Byte mask, Index v0, Index v1) /* t = (p0->w + p0->x) / ((p0->w + p0->x) - (p1->w + p1->x)); */ t = (p0->w + p0->x) - (p1->w + p1->x); //t = (t == 0) ? (float)1.0 : (p0->w + p0->x) / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : (p0->w + p0->x) / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w + p0->x) / t; PAR_COORD3(*p,t,*p0,*p1); p->w = - p->x; /* propriete du point d'intersection */ break; @@ -373,7 +373,7 @@ inter (Byte mask, Index v0, Index v1) /* t = (p0->w - p0->z) / ((p0->w - p0->z) - (p1->w - p1->z)); */ t = (p0->w - p0->z) - (p1->w - p1->z); //t = (t == 0) ? (float)1.0 : (p0->w - p0->z) / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : (p0->w - p0->z) / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->z) / t; PAR_COORD3(*p,t,*p0,*p1); p->w = p->z; /* propriete du point d'intersection */ break; @@ -382,7 +382,7 @@ inter (Byte mask, Index v0, Index v1) /* t = p0->z / (p0->z - p1->z); */ t = (p0->z - p1->z); //t = (t == 0) ? (float)1.0 : p0->z / t; - t = (fabs(t) <= DBL_EPSILON) ? (float)1.0 : p0->z / t; + t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : p0->z / t; p->x = (p1->x - p0->x) * t + p0->x; p->y = (p1->y - p0->y) * t + p0->y; p->w = (p1->w - p0->w) * t + p0->w; diff --git a/src/simulator/wireframe-simulator/core/vpDisplay.c b/src/simulator/wireframe-simulator/core/vpCoreDisplay.cpp similarity index 98% rename from src/simulator/wireframe-simulator/core/vpDisplay.c rename to src/simulator/wireframe-simulator/core/vpCoreDisplay.cpp index a1458c703ceddb8841d21f015a5842694c994ba9..6cc784b043f255def543a13867e77d17c455cc18 100755 --- a/src/simulator/wireframe-simulator/core/vpDisplay.c +++ b/src/simulator/wireframe-simulator/core/vpCoreDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDisplay.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCoreDisplay.cpp 5294 2015-02-10 09:41:38Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpImstack.h b/src/simulator/wireframe-simulator/core/vpImstack.h index 99eda9251dfbd873b1c49ae31f568f3b4ac36584..1fa1061f735ab18e97a4e701316baa79d9cdd944 100755 --- a/src/simulator/wireframe-simulator/core/vpImstack.h +++ b/src/simulator/wireframe-simulator/core/vpImstack.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpImstack.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpImstack.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpKeyword.c b/src/simulator/wireframe-simulator/core/vpKeyword.cpp similarity index 92% rename from src/simulator/wireframe-simulator/core/vpKeyword.c rename to src/simulator/wireframe-simulator/core/vpKeyword.cpp index 72cdb7f2d1f11c118b9d415c34d9972c3b26a9f1..d89143ab201744d41f7151febf499cb790c5d9d0 100755 --- a/src/simulator/wireframe-simulator/core/vpKeyword.c +++ b/src/simulator/wireframe-simulator/core/vpKeyword.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyword.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpKeyword.cpp 5310 2015-02-11 11:57:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,8 +53,8 @@ void open_keyword (Keyword *kwp); static void open_hash (void); static void close_hash (void); -static int hashpjw (char *str); -static void insert_keyword (char *str, Index token); +static int hashpjw (const char *str); +static void insert_keyword (const char *str, Index token); Index get_symbol (char *ident, int length); #ifdef debug @@ -68,7 +68,7 @@ static char *get_keyword (void); typedef struct bucket { struct bucket *next; /* element suivant */ - char *ident; /* identifateur */ + char *ident; /* identifateur */ Byte length; /* longueur de "ident" */ Index token; /* code du jeton */ } Bucket; @@ -115,7 +115,7 @@ open_hash (void) } head = hash_tbl; bend = head + PRIME; - for (; head < bend; *head++ = NULL); + for (; head < bend; *head++ = NULL) {}; } /* @@ -151,7 +151,7 @@ close_hash (void) * Le code de la chaine. */ static int -hashpjw (char *str) +hashpjw (const char *str) { unsigned h = 0; /* "hash value" */ unsigned g; @@ -176,9 +176,9 @@ hashpjw (char *str) * token Valeur du jeton associe au mot cle. */ static void -insert_keyword (char *str, Index token) +insert_keyword (const char *str, Index token) { - static char proc_name[] = "insert_keyword"; + static const char proc_name[] = "insert_keyword"; Bucket **head = hash_tbl + hashpjw (str); Bucket *bp; @@ -212,7 +212,7 @@ insert_keyword (char *str, Index token) Index get_symbol (char *ident, int length) { Bucket *bp; - char *kwd; + const char *kwd; char *idn = ident; int len = length; diff --git a/src/simulator/wireframe-simulator/core/vpKeyword.h b/src/simulator/wireframe-simulator/core/vpKeyword.h index 0cfd08ff5bc48b730e49235646d4881ebf856d7f..f75bf11d747f176fda2a694beafae9fc43065639 100644 --- a/src/simulator/wireframe-simulator/core/vpKeyword.h +++ b/src/simulator/wireframe-simulator/core/vpKeyword.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyword.h 4332 2013-07-22 14:23:46Z fspindle $ + * $Id: vpKeyword.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpLex.c b/src/simulator/wireframe-simulator/core/vpLex.cpp similarity index 73% rename from src/simulator/wireframe-simulator/core/vpLex.c rename to src/simulator/wireframe-simulator/core/vpLex.cpp index 366dfc3a2fe3f5c78c81558bf154fdf349dbacb1..108ac694dd050ad53fb54fcf3227b452588ae97d 100755 --- a/src/simulator/wireframe-simulator/core/vpLex.c +++ b/src/simulator/wireframe-simulator/core/vpLex.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLex.c 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpLex.cpp 5310 2015-02-11 11:57:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -134,7 +134,7 @@ void lexerr (const char* path, ...); #define E_9 9 -char *lex_errtbl[] = { /* table des messages d'erreur */ +const char *lex_errtbl[] = { /* table des messages d'erreur */ "error unknown", "symbol undefined", "unexpected EOF in comment", @@ -239,9 +239,9 @@ void close_lex (void) #define ECHO printf ("%c", *(mysptr)) -#define CURC (*mysptr) /* caractere courant */ -#define NEXTC (*(mysptr+1)) /* caractere suivant */ -#define PREVC (*(mysptr-1)) /* caractere precedent */ +#define CURC (*((signed char *)mysptr)) /* caractere courant */ +#define NEXTC (*((signed char *)mysptr+1)) /* caractere suivant */ +#define PREVC (*((signed char *)mysptr-1)) /* caractere precedent */ /* @@ -257,7 +257,7 @@ int lex (void) { lex_loop : - for (; chtbl[(int)CURC] == SPCT; mysptr++); /* saute les espaces */ + for (; chtbl[(int)CURC] == SPCT; mysptr++) {}; /* saute les espaces */ switch (chtbl[(int)CURC]) { @@ -288,7 +288,7 @@ lex_loop : return (*mytext); mysptr++; comment : - for (; iscmtt((int)CURC); mysptr++); + for (; iscmtt((int)CURC); mysptr++) {}; switch (chtbl[(int)CURC]) { case EOBT : next_source (); @@ -318,7 +318,7 @@ comment : case IDNT : mytext = mysptr; /* sauvegarde le jeton */ mysptr++; - for (; isidnt((int)CURC); mysptr++); + for (; isidnt((int)CURC); mysptr++) {}; mylength = (int)(mysptr - mytext); return (get_symbol (mytext, mylength)); break; @@ -333,7 +333,7 @@ int_part : case '.' : /* lecture fraction */ float_part : mysptr++; - for (; isintt((int)CURC); mysptr++); + for (; isintt((int)CURC); mysptr++) {}; if (CURC != 'E' && CURC != 'e') { myfloat = (float) atof (mytext); /* FC @@ -341,6 +341,7 @@ printf("mytext %s, myfloat %f\n",mytext,myfloat); */ return (T_FLOAT); } + break; case 'E' : /* lecture exposant */ case 'e' : mysptr++; @@ -353,7 +354,7 @@ printf("mytext %s, myfloat %f\n",mytext,myfloat); myfloat = (float) atof (mytext); return (T_FLOAT); } - for (; isintt((int)CURC); mysptr++); + for (; isintt((int)CURC); mysptr++) {}; myfloat = (float) atof (mytext); return (T_FLOAT); break; @@ -384,7 +385,7 @@ printf("mytext %s, myfloat %f\n",mytext,myfloat); mytext = mysptr; /* sauvegarde le jeton */ mysptr++; string : - for (; isstgt((int)CURC); mysptr++); + for (; isstgt((int)CURC); mysptr++) {}; switch (chtbl[(int)CURC]) { case EOBT : next_source (); @@ -437,179 +438,180 @@ string : int lexecho (FILE *f, int token) { lex_loop : - for (; chtbl[(int)CURC] == SPCT; mysptr++) /* saute les espaces */ - fwrite (mysptr, 1, 1, f); - - switch (chtbl[(int)CURC]) { - - case NULT : - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; - if (token != *mytext) - fwrite (mytext, 1, 1, f); - return (*mytext); - break; - case EOBT : - next_source (); - goto lex_loop; - break; - case EOFT : - mytext = mysptr; /* sauvegarde le jeton */ - return (T_EOF); - break; - case EOLT : - fwrite (mysptr, 1, 1, f); - if (mysptr == lastline) next_source (); - else mysptr++; - mylineno++; - myline = mysptr; - goto lex_loop; - break; - case CMTT : - fwrite (mysptr, 1, 1, f); - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; - if (CURC != '*') - return (*mytext); - fwrite (mysptr, 1, 1, f); - mysptr++; + for (; chtbl[(int)CURC] == SPCT; mysptr++) /* saute les espaces */ + fwrite (mysptr, 1, 1, f); + + switch (chtbl[(int)CURC]) { + + case NULT : + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; + if (token != *mytext) + fwrite (mytext, 1, 1, f); + return (*mytext); + break; + case EOBT : + next_source (); + goto lex_loop; + break; + case EOFT : + mytext = mysptr; /* sauvegarde le jeton */ + return (T_EOF); + break; + case EOLT : + fwrite (mysptr, 1, 1, f); + if (mysptr == lastline) next_source (); + else mysptr++; + mylineno++; + myline = mysptr; + goto lex_loop; + break; + case CMTT : + fwrite (mysptr, 1, 1, f); + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; + if (CURC != '*') + return (*mytext); + fwrite (mysptr, 1, 1, f); + mysptr++; comment : - for (; iscmtt((int)CURC); mysptr++) - fwrite (mysptr, 1, 1, f); - switch (chtbl[(int)CURC]) { - case EOBT : - next_source (); - goto comment; - break; - case EOFT : - lexerr ("start", lex_errtbl[E_CMT_EOF], NULL); - return (T_EOF); - break; - case EOLT : - fwrite (mysptr, 1, 1, f); - if (mysptr == lastline) next_source (); - else mysptr++; - mylineno++; - myline = mysptr; - goto comment; - break; - case CMTT : - fwrite (mysptr, 1, 1, f); - if (PREVC == '*') { /* veritable fin */ - mysptr++; - goto lex_loop; - } - mysptr++; /* pseudo fin */ - goto comment; - break; - } - break; - case IDNT : - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; - for (; isidnt((int)CURC); mysptr++); - mylength = (int)(mysptr - mytext); - if (token != get_symbol (mytext, mylength)) - fwrite (mytext, mylength, 1, f); - return (get_symbol (mytext, mylength)); - break; - case INTT : - mytext = mysptr; /* sauvegarde le jeton */ + for (; iscmtt((int)CURC); mysptr++) + fwrite (mysptr, 1, 1, f); + switch (chtbl[(int)CURC]) { + case EOBT : + next_source (); + goto comment; + break; + case EOFT : + lexerr ("start", lex_errtbl[E_CMT_EOF], NULL); + return (T_EOF); + break; + case EOLT : + fwrite (mysptr, 1, 1, f); + if (mysptr == lastline) next_source (); + else mysptr++; + mylineno++; + myline = mysptr; + goto comment; + break; + case CMTT : + fwrite (mysptr, 1, 1, f); + if (PREVC == '*') { /* veritable fin */ + mysptr++; + goto lex_loop; + } + mysptr++; /* pseudo fin */ + goto comment; + break; + } + break; + case IDNT : + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; + for (; isidnt((int)CURC); mysptr++) {}; + mylength = (int)(mysptr - mytext); + if (token != get_symbol (mytext, mylength)) + fwrite (mytext, mylength, 1, f); + return (get_symbol (mytext, mylength)); + break; + case INTT : + mytext = mysptr; /* sauvegarde le jeton */ int_part : - mysptr++; - for (; isintt((int)CURC); mysptr++); - switch (CURC) { - case '.' : /* lecture fraction */ + mysptr++; + for (; isintt((int)CURC); mysptr++) {}; + switch (CURC) { + case '.' : /* lecture fraction */ float_part : - mysptr++; - for (; isintt((int)CURC); mysptr++); - if (CURC != 'E' && CURC != 'e') { - if (token != T_FLOAT) - fwrite (mytext, mysptr - mytext, 1, f); - return (T_FLOAT); - } - case 'E' : /* lecture exposant */ - case 'e' : - mysptr++; - if (isintt((int)CURC)) mysptr++; - else if (issgnt((int)CURC) && isintt((int)NEXTC)) mysptr +=2; - else { - mysptr--; - if (token != T_FLOAT) - fwrite (mytext, mysptr - mytext, 1, f); - return (T_FLOAT); - } - for (; isintt((int)CURC); mysptr++); - if (token != T_FLOAT) - fwrite (mytext, mysptr - mytext, 1, f); - return (T_FLOAT); - break; - default : - if (token != T_INT) - fwrite (mytext, mysptr - mytext, 1, f); - return (T_INT); - break; - } - break; - case FPTT : - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; - if (! isintt((int)CURC)) { /* pas de fraction */ - if (token != *mytext) - fwrite (mytext, 1, 1, f); - return (*mytext); - } - goto float_part; - break; - case SGNT : - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; - if (isintt((int)CURC)) goto int_part; - if (isfptt((int)CURC) && isintt((int)NEXTC)) goto float_part; - if (token != *mytext) - fwrite (mytext, 1, 1, f); - return (*mytext); - break; - case STGT : - fwrite (mysptr, 1, 1, f); - mytext = mysptr; /* sauvegarde le jeton */ - mysptr++; + mysptr++; + for (; isintt((int)CURC); mysptr++) {}; + if (CURC != 'E' && CURC != 'e') { + if (token != T_FLOAT) + fwrite (mytext, mysptr - mytext, 1, f); + return (T_FLOAT); + } + break; + case 'E' : /* lecture exposant */ + case 'e' : + mysptr++; + if (isintt((int)CURC)) mysptr++; + else if (issgnt((int)CURC) && isintt((int)NEXTC)) mysptr +=2; + else { + mysptr--; + if (token != T_FLOAT) + fwrite (mytext, mysptr - mytext, 1, f); + return (T_FLOAT); + } + for (; isintt((int)CURC); mysptr++) {}; + if (token != T_FLOAT) + fwrite (mytext, mysptr - mytext, 1, f); + return (T_FLOAT); + break; + default : + if (token != T_INT) + fwrite (mytext, mysptr - mytext, 1, f); + return (T_INT); + break; + } + break; + case FPTT : + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; + if (! isintt((int)CURC)) { /* pas de fraction */ + if (token != *mytext) + fwrite (mytext, 1, 1, f); + return (*mytext); + } + goto float_part; + break; + case SGNT : + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; + if (isintt((int)CURC)) goto int_part; + if (isfptt((int)CURC) && isintt((int)NEXTC)) goto float_part; + if (token != *mytext) + fwrite (mytext, 1, 1, f); + return (*mytext); + break; + case STGT : + fwrite (mysptr, 1, 1, f); + mytext = mysptr; /* sauvegarde le jeton */ + mysptr++; string : - for (; isstgt((int)CURC); mysptr++) - fwrite (mysptr, 1, 1, f); - switch (chtbl[(int)CURC]) { - case EOBT : - next_source (); - goto comment; - break; - case EOFT : - lexerr ("start", lex_errtbl[E_STG_EOF], NULL); - return (T_EOF); - break; - case EOLT : - lexerr ("start", lex_errtbl[E_STG_EOL], NULL); - return ('\n'); - break; - case STGT : - fwrite (mysptr, 1, 1, f); - if (PREVC != '\\') { /* veritable fin */ - mytext++; - mylength = (int)(mysptr - mytext); - mysptr++; - return (T_STRING); - } - mysptr++; /* pseudo fin */ - goto string; - break; - } - break; - default : - fwrite (mysptr, 1, 1, f); - mysptr++; - goto lex_loop; - break; - } - return (T_EOF); + for (; isstgt((int)CURC); mysptr++) + fwrite (mysptr, 1, 1, f); + switch (chtbl[(int)CURC]) { + case EOBT : + next_source (); + goto comment; + break; + case EOFT : + lexerr ("start", lex_errtbl[E_STG_EOF], NULL); + return (T_EOF); + break; + case EOLT : + lexerr ("start", lex_errtbl[E_STG_EOL], NULL); + return ('\n'); + break; + case STGT : + fwrite (mysptr, 1, 1, f); + if (PREVC != '\\') { /* veritable fin */ + mytext++; + mylength = (int)(mysptr - mytext); + mysptr++; + return (T_STRING); + } + mysptr++; /* pseudo fin */ + goto string; + break; + } + break; + default : + fwrite (mysptr, 1, 1, f); + mysptr++; + goto lex_loop; + break; + } + return (T_EOF); } @@ -686,14 +688,10 @@ static void next_source (void) /* recopie la derniere ligne devant "buf" */ *bot = EOL; /* evite le debordement de "buf" */ - while ((*--bot = *--top) != EOL); + while ((*--bot = *--top) != EOL) {}; myline = mysptr = bot + 1; - //if ((size = read (fds, buf, BUFSIZE)) < 0) { - if ((size = fread (buf,sizeof (char), BUFSIZE,fds)) < 0) { - perror (source); - exit (1); - } + size = fread (buf,sizeof (char), BUFSIZE,fds); if (size == 0) { topbuf = buf + 1; *buf = EOF; @@ -706,7 +704,7 @@ static void next_source (void) /* recherche de la derniere ligne */ top = topbuf; - while (*--top != EOL); + while (*--top != EOL) {}; lastline = top; } } @@ -720,7 +718,7 @@ static void next_source (void) #define ERR_STACK_MAX 32 -static char *err_stack[ERR_STACK_MAX]; +static const char *err_stack[ERR_STACK_MAX]; static int size_stack = 0; @@ -796,9 +794,9 @@ void poperr (void) /* * La procedure "popup_error" remplace le message d'erreur du sommet de pile. */ -void popuperr (/*const */char *str) +void popuperr (const char *str) { - static char proc_name[] = "popuerr"; + static const char proc_name[] = "popuerr"; if (size_stack <= 0) { fprintf (stderr, "%s: error stack underflow\n", proc_name); @@ -810,9 +808,9 @@ void popuperr (/*const */char *str) /* * La procedure "pusherr" empile le message d'erreur. */ -void pusherr (/*const */char *str) +void pusherr (const char *str) { - static char proc_name[] = "pusherr"; + static const char proc_name[] = "pusherr"; if (size_stack >= ERR_STACK_MAX) { fprintf (stderr, "%s: error stack overflow\n", proc_name); diff --git a/src/simulator/wireframe-simulator/core/vpLex.h b/src/simulator/wireframe-simulator/core/vpLex.h index f213fa4eaebcd1788bd24dfc1a77adfff4477cd1..859426df074cb8114dcf825eff3357449d708621 100644 --- a/src/simulator/wireframe-simulator/core/vpLex.h +++ b/src/simulator/wireframe-simulator/core/vpLex.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLex.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLex.h 5297 2015-02-10 11:19:24Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,9 +53,9 @@ int lex(void); void unlex (void); void lexerr (const char* path, ...); -int pusherr (char *str); -int popuperr (char *str); -int poperr (void); +void pusherr (const char *str); +void popuperr (const char *str); +void poperr (void); int lexecho (FILE *f, int token); #endif diff --git a/src/simulator/wireframe-simulator/core/vpMy.h b/src/simulator/wireframe-simulator/core/vpMy.h index 941382cb42cc6cf9b80f8de996b2a0799f5a0369..ac303f78c32e5fb5e80b0066eb877290b45da2b7 100755 --- a/src/simulator/wireframe-simulator/core/vpMy.h +++ b/src/simulator/wireframe-simulator/core/vpMy.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMy.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMy.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpMyio.c b/src/simulator/wireframe-simulator/core/vpMyio.cpp similarity index 91% rename from src/simulator/wireframe-simulator/core/vpMyio.c rename to src/simulator/wireframe-simulator/core/vpMyio.cpp index da4008c314f01b4eefed3542c9227b172e44d039..3eca168a6dc36ad04d99ad294f9fd6418e7c269e 100755 --- a/src/simulator/wireframe-simulator/core/vpMyio.c +++ b/src/simulator/wireframe-simulator/core/vpMyio.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMyio.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMyio.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -107,7 +107,14 @@ void fscanf_string (char **str) lexerr ("start", "string expected", NULL); if (*str == NULL) *str = (char *) malloc ((mylength + 1) * sizeof (char)); - else *str = (char *) realloc (*str, (mylength + 1) * sizeof (char)); + else + *str = (char *) realloc (*str, (mylength + 1) * sizeof (char)); + + if (*str == NULL) { + printf("Unable to read the string: bad memory allocation"); + return; + } + strncpy (*str, mytext, mylength); } diff --git a/src/simulator/wireframe-simulator/core/vpParser.c b/src/simulator/wireframe-simulator/core/vpParser.cpp similarity index 95% rename from src/simulator/wireframe-simulator/core/vpParser.c rename to src/simulator/wireframe-simulator/core/vpParser.cpp index 8682dad822e75ea8525006d631755bc676d72a18..e5092b64ddc83632ad78f68eb86c1ce185c86f5d 100755 --- a/src/simulator/wireframe-simulator/core/vpParser.c +++ b/src/simulator/wireframe-simulator/core/vpParser.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpParser.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpParser.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpProjection.c b/src/simulator/wireframe-simulator/core/vpProjection.cpp similarity index 97% rename from src/simulator/wireframe-simulator/core/vpProjection.c rename to src/simulator/wireframe-simulator/core/vpProjection.cpp index f6c7f262115bceb60d58e2ed333114134c10b987..f0f2acacfc0a8e32ce7ddfda502c09989c33cc76 100755 --- a/src/simulator/wireframe-simulator/core/vpProjection.c +++ b/src/simulator/wireframe-simulator/core/vpProjection.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpProjection.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpProjection.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -120,7 +120,7 @@ static void set_zy (Matrix m, Vector *v0, Vector *v1) */ void set_parallel (View_parameters *vp, Matrix wc) { - Matrix m; + Matrix m = IDENTITY_MATRIX; Point3f cop; Point4f doprim; Vector dop, v; @@ -190,7 +190,7 @@ void set_parallel (View_parameters *vp, Matrix wc) */ void set_perspective (View_parameters *vp, Matrix wc) { - Matrix m; + Matrix m = IDENTITY_MATRIX; Point4f vrprim, cw; float zmin; Vector v; diff --git a/src/simulator/wireframe-simulator/core/vpRfstack.c b/src/simulator/wireframe-simulator/core/vpRfstack.cpp similarity index 86% rename from src/simulator/wireframe-simulator/core/vpRfstack.c rename to src/simulator/wireframe-simulator/core/vpRfstack.cpp index d8b4ed52b9a45cb5d3579d6fdb2b16ce4a06f106..c784b7d3b92a072f01a949b8877d73b1b79b4eb0 100755 --- a/src/simulator/wireframe-simulator/core/vpRfstack.c +++ b/src/simulator/wireframe-simulator/core/vpRfstack.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRfstack.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRfstack.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,7 +69,8 @@ static int *sp = stack; /* sommet */ void fprintf_rfstack (FILE *fp) { - int flg = 0; /* nul si element unique */ + int flg; + flg = 0; /* nul si element unique */ if (*sp == IS_INSIDE) { fprintf (fp, "(null)\n"); @@ -77,28 +78,34 @@ fprintf_rfstack (FILE *fp) } fprintf (fp, "("); if (*sp & IS_ABOVE) { - if (flg++) fprintf (fp, " "); + if (flg) fprintf (fp, " "); + flg ++; fprintf (fp, "above"); } if (*sp & IS_BELOW) { - if (flg++) fprintf (fp, " "); - fprintf (fp, "below"); + if (flg) fprintf (fp, " "); + flg ++; + fprintf (fp, "below"); } if (*sp & IS_RIGHT) { - if (flg++) fprintf (fp, " "); - fprintf (fp, "right"); + if (flg) fprintf (fp, " "); + flg ++; + fprintf (fp, "right"); } if (*sp & IS_LEFT) { - if (flg++) fprintf (fp, " "); - fprintf (fp, "left"); + if (flg) fprintf (fp, " "); + flg ++; + fprintf (fp, "left"); } if (*sp & IS_BACK) { - if (flg++) fprintf (fp, " "); - fprintf (fp, "back"); + if (flg) fprintf (fp, " "); + flg ++; + fprintf (fp, "back"); } if (*sp & IS_FRONT) { - if (flg++) fprintf (fp, " "); - fprintf (fp, "front"); + /*if (flg)*/ fprintf (fp, " "); + flg ++; + fprintf (fp, "front"); } fprintf (fp, ")\n"); } diff --git a/src/simulator/wireframe-simulator/core/vpRfstack.h b/src/simulator/wireframe-simulator/core/vpRfstack.h index a8ad1b2f363ce53609246f4095d857b6353422db..80d6610b139d3e16c274db7c4f3c85d5d2d2b4d7 100755 --- a/src/simulator/wireframe-simulator/core/vpRfstack.h +++ b/src/simulator/wireframe-simulator/core/vpRfstack.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRfstack.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRfstack.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpSkipio.c b/src/simulator/wireframe-simulator/core/vpSkipio.cpp similarity index 91% rename from src/simulator/wireframe-simulator/core/vpSkipio.c rename to src/simulator/wireframe-simulator/core/vpSkipio.cpp index c19965943467172a3b6950d80afec7ceb888fd71..24ee2741e4975f9fdf94be23acd5637212a63c61 100755 --- a/src/simulator/wireframe-simulator/core/vpSkipio.c +++ b/src/simulator/wireframe-simulator/core/vpSkipio.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSkipio.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSkipio.cpp 5310 2015-02-11 11:57:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,7 +65,7 @@ void skip_cmd (void) fprintf (stderr, "\n$ "); fwrite (mytext, mylength, 1, stderr); - while ((token = lexecho (stderr, '$')) != T_EOF && token != '$'); + while ((token = lexecho (stderr, '$')) != T_EOF && token != '$') {}; unlex (); } @@ -76,7 +76,7 @@ void skip_cmd (void) * token Jeton du mot cle a reconnaitre. * err Message d'erreur si le mot cle n'est pas reconnu. */ -void skip_keyword (int token, char *err) +void skip_keyword (int token, const char *err) { int t; diff --git a/src/simulator/wireframe-simulator/core/vpSkipio.h b/src/simulator/wireframe-simulator/core/vpSkipio.h index eaffbc2f6d8dbfc6e8581618c06e9e021fccbf4f..d4d4cc514ba52c4af946f6ef5ae1472b30ffd2e3 100644 --- a/src/simulator/wireframe-simulator/core/vpSkipio.h +++ b/src/simulator/wireframe-simulator/core/vpSkipio.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSkipio.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSkipio.h 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,7 +49,7 @@ #ifndef DOXYGEN_SHOULD_SKIP_THIS void skip_cmd (void); -void skip_keyword (int token, char *err); +void skip_keyword (int token, const char *err); #endif #endif diff --git a/src/simulator/wireframe-simulator/core/vpTmstack.c b/src/simulator/wireframe-simulator/core/vpTmstack.cpp similarity index 97% rename from src/simulator/wireframe-simulator/core/vpTmstack.c rename to src/simulator/wireframe-simulator/core/vpTmstack.cpp index 1ed738a9d74cb08a973ee836ca7cf32e36fbb77d..61e4461cfae4d27249c832f79c6ba94ad8adc2ed 100755 --- a/src/simulator/wireframe-simulator/core/vpTmstack.c +++ b/src/simulator/wireframe-simulator/core/vpTmstack.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTmstack.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTmstack.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpTmstack.h b/src/simulator/wireframe-simulator/core/vpTmstack.h index ae33445178b80161e1130edd0e57c3f7141df832..af03b7293e8877e9fb7b59db3f8219be0d2670a5 100755 --- a/src/simulator/wireframe-simulator/core/vpTmstack.h +++ b/src/simulator/wireframe-simulator/core/vpTmstack.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTmstack.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTmstack.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpToken.c b/src/simulator/wireframe-simulator/core/vpToken.cpp similarity index 94% rename from src/simulator/wireframe-simulator/core/vpToken.c rename to src/simulator/wireframe-simulator/core/vpToken.cpp index ed815aec8aeb74c8dab1aa6dc8aa01dc6d368f58..989f94206049ec89affabbf4573979ed67176e40 100755 --- a/src/simulator/wireframe-simulator/core/vpToken.c +++ b/src/simulator/wireframe-simulator/core/vpToken.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpToken.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpToken.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpToken.h b/src/simulator/wireframe-simulator/core/vpToken.h index b0188bfc0da1019496bd9440da544b301d3a9b16..e7c251d94edbcbc2b9d87f1205a00f2007075a7a 100755 --- a/src/simulator/wireframe-simulator/core/vpToken.h +++ b/src/simulator/wireframe-simulator/core/vpToken.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpToken.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpToken.h 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,7 +48,7 @@ typedef struct { - char *ident; /* identifateur */ + const char *ident; /* identifateur */ Index token; /* code du jeton */ } Keyword; diff --git a/src/simulator/wireframe-simulator/core/vpView.h b/src/simulator/wireframe-simulator/core/vpView.h index b9b0555b831bfbfe2ea2b2a665478bc4a584b398..8f95d20e4943d5d44b1b8952a07df80b5922856a 100755 --- a/src/simulator/wireframe-simulator/core/vpView.h +++ b/src/simulator/wireframe-simulator/core/vpView.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpView.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpView.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpViewio.c b/src/simulator/wireframe-simulator/core/vpViewio.cpp similarity index 97% rename from src/simulator/wireframe-simulator/core/vpViewio.c rename to src/simulator/wireframe-simulator/core/vpViewio.cpp index 1f0313ce5b9af968d0ae73ce285b8beaee22c3f7..6c19df08874ccab6e94b80aa91ed72a8ff0d54ff 100755 --- a/src/simulator/wireframe-simulator/core/vpViewio.c +++ b/src/simulator/wireframe-simulator/core/vpViewio.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpViewio.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpViewio.cpp 5284 2015-02-09 14:24:10Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/core/vpVwstack.c b/src/simulator/wireframe-simulator/core/vpVwstack.cpp similarity index 92% rename from src/simulator/wireframe-simulator/core/vpVwstack.c rename to src/simulator/wireframe-simulator/core/vpVwstack.cpp index 7a9b3db7ea6f8e5acc60ce628f80cb0c7e7efbf8..e0dfbc101b56f59a23833954a668648bcd31cfd8 100644 --- a/src/simulator/wireframe-simulator/core/vpVwstack.c +++ b/src/simulator/wireframe-simulator/core/vpVwstack.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpVwstack.c 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpVwstack.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,8 +45,8 @@ #include <stdio.h> #include <string.h> -#include <float.h> -#include <math.h> +#include <limits> +#include <cmath> // std::fabs() // #include <varargs.h> /* modif Fedora #include <stdarg.h> @@ -77,7 +77,7 @@ fprintf_vwstack (FILE *fp, char *argv) static char proc_name[] = "fprintf_vwstack"; if (argv == NULL || strcmp (argv, "type") == 0) { - char *typetoa; + const char *typetoa; switch (sp->type) { case PARALLEL : @@ -239,7 +239,7 @@ add_vwstack (const char* path, ... ) float z = (float) va_arg (ap, double); //if (x == 0 && y == 0 && z == 0) - if (fabs(x) <= DBL_EPSILON && fabs(y) <= DBL_EPSILON && fabs(z) <= DBL_EPSILON) + if (std::fabs(x) <= std::numeric_limits<double>::epsilon() && std::fabs(y) <= std::numeric_limits<double>::epsilon() && std::fabs(z) <= std::numeric_limits<double>::epsilon()) fprintf (stderr, "%s: bad vpn\n", proc_name); else { SET_COORD3(sp->vpn,x,y,z); @@ -259,7 +259,7 @@ add_vwstack (const char* path, ... ) float z = (float) va_arg (ap, double); //if (x == 0 && y == 0 && z == 0) - if (fabs(x) <= DBL_EPSILON && fabs(y) <= DBL_EPSILON && fabs(z) <= DBL_EPSILON) + if (std::fabs(x) <= std::numeric_limits<double>::epsilon() && std::fabs(y) <= std::numeric_limits<double>::epsilon() && std::fabs(z) <= std::numeric_limits<double>::epsilon()) fprintf (stderr, "%s: bad vup\n", proc_name); else { SET_COORD3(sp->vup,x,y,z); diff --git a/src/simulator/wireframe-simulator/core/vpVwstack.h b/src/simulator/wireframe-simulator/core/vpVwstack.h index ec7d76004470a27500ab8f4488aa7ab5f76f57f8..83f2f74d4c3676030384df1e59b3b8a1a35661a1 100755 --- a/src/simulator/wireframe-simulator/core/vpVwstack.h +++ b/src/simulator/wireframe-simulator/core/vpVwstack.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpVwstack.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpVwstack.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/scene/CMakeWireframeScenesList.cmake b/src/simulator/wireframe-simulator/scene/CMakeWireframeScenesList.cmake index 9e540178ded26f2415e4aafdf006db833c323b13..5b605fa34bd01ab1938b6231af8f965e11eb5c3c 100644 --- a/src/simulator/wireframe-simulator/scene/CMakeWireframeScenesList.cmake +++ b/src/simulator/wireframe-simulator/scene/CMakeWireframeScenesList.cmake @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeWireframeScenesList.cmake 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeWireframeScenesList.cmake 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/src/simulator/wireframe-simulator/vpWireFrameSimulator.cpp b/src/simulator/wireframe-simulator/vpWireFrameSimulator.cpp index ad755829b352998e9cd7ca48bbe2a9d0e355ed35..e5e38ddfbf38e96abeb3c4f0e07b9be74290237a 100644 --- a/src/simulator/wireframe-simulator/vpWireFrameSimulator.cpp +++ b/src/simulator/wireframe-simulator/vpWireFrameSimulator.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWireFrameSimulator.cpp 4227 2013-04-19 07:55:22Z fspindle $ + * $Id: vpWireFrameSimulator.cpp 5297 2015-02-10 11:19:24Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,11 +57,6 @@ #include <visp/vpPoint.h> #include <visp/vpIoTools.h> -#if defined(WIN32) -#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) -#endif - - //Inventor includes #if defined(VISP_HAVE_COIN) #include <Inventor/nodes/SoSeparator.h> @@ -78,8 +73,8 @@ #include <Inventor/VRMLnodes/SoVRMLShape.h> #endif -extern "C"{extern Point2i *point2i;} -extern "C"{extern Point2i *listpoint2i;} +extern Point2i *point2i; +extern Point2i *listpoint2i; typedef enum { @@ -88,6 +83,9 @@ typedef enum UNKNOWN_MODEL } Model_3D; +Model_3D getExtension(const char* file); +void set_scene_wrl (const char* str, Bound_scene *sc, float factor); + /* Get the extension of the file and return it */ @@ -127,11 +125,9 @@ void set_scene (const char* str, Bound_scene *sc, float factor) //if ((fd = fopen (str, 0)) == -1) if ((fd = fopen (str, "r")) == NULL) { - char strerr[80]; - strcpy (strerr,"The file "); - strcat (strerr,str); - strcat (strerr," can not be opened"); - throw(vpException(vpSimulatorException::ioError,strerr)) ; + std::string error = "The file " + std::string(str) + " can not be opened"; + + throw(vpException(vpSimulatorException::ioError, error.c_str())) ; } open_keyword (keyword_tbl); open_lex (); @@ -162,8 +158,9 @@ void set_scene (const char* str, Bound_scene *sc, float factor) #if defined(VISP_HAVE_COIN) #ifndef DOXYGEN_SHOULD_SKIP_THIS -typedef struct +typedef struct indexFaceSet { + indexFaceSet() : nbPt(0), pt(), nbIndex(0), index() {}; int nbPt; std::vector<vpPoint> pt; int nbIndex; @@ -205,7 +202,10 @@ void set_scene_wrl (const char* str, Bound_scene *sc, float factor) else { sceneGraphVRML2 = SoDB::readAllVRML(&in); - if (sceneGraphVRML2 == NULL) { /*return -1;*/ } + if (sceneGraphVRML2 == NULL) { + /*return -1;*/ + throw(vpException(vpException::notInitialized, "Cannot read VRML file")); + } sceneGraphVRML2->ref(); } @@ -463,7 +463,8 @@ vpWireFrameSimulator::display_scene(Matrix mat, Bound_scene &sc, const vpImage<u Byte b = (Byte) *get_rfstack (); Matrix m; - bcopy ((char *) mat, (char *) m, sizeof (Matrix)); + //bcopy ((char *) mat, (char *) m, sizeof (Matrix)); + memmove((char *) m, (char *) mat, sizeof (Matrix)); View_to_Matrix (get_vwstack (), *(get_tmstack ())); postmult_matrix (m, *(get_tmstack ())); bp = sc.bound.ptr; @@ -510,6 +511,15 @@ vpWireFrameSimulator::display_scene(Matrix mat, Bound_scene &sc, const vpImage<u variable that the user has to set. */ vpWireFrameSimulator::vpWireFrameSimulator() + : scene(), desiredScene(), camera(), objectImage(), fMo(), fMc(), camMf(), + refMo(), cMo(), cdMo(), object(PLATE), desiredObject(D_STANDARD), + camColor(vpColor::green), camTrajColor(vpColor::green), curColor(vpColor::blue), + desColor(vpColor::red), sceneInitialized(false), displayCameraTrajectory(true), + cameraTrajectory(), poseList(), fMoList(), nbrPtLimit(1000), old_iPr(), old_iPz(), + old_iPt(), blockedr(false), blockedz(false), blockedt(false), blocked(false), + camMf2(), f2Mf(), px_int(1), py_int(1), px_ext(1), py_ext(1), displayObject(false), + displayDesiredObject(false), displayCamera(false), displayImageSimulator(false), + cameraFactor(1.), camTrajType(CT_LINE), extCamChanged(false), rotz(), thickness_(1), scene_dir() { // set scene_dir from #define VISP_SCENE_DIR if it exists if (vpIoTools::checkDirectory(VISP_SCENES_DIR) == true) // directory exists @@ -527,51 +537,23 @@ vpWireFrameSimulator::vpWireFrameSimulator() open_display(); open_clipping(); - camColor = vpColor::green; - camTrajColor = vpColor::green; - curColor = vpColor::blue; - desColor = vpColor::red; - - sceneInitialized = false; - - displayCameraTrajectory = true; - cameraTrajectory.clear(); - poseList.clear(); - fMoList.clear(); - - fMo.setIdentity(); - old_iPr = vpImagePoint(-1,-1); old_iPz = vpImagePoint(-1,-1); old_iPt = vpImagePoint(-1,-1); - blockedr = false; - blockedz = false; - blockedt = false; - blocked = false; - - nbrPtLimit = 1000; - - px_int = 1; - py_int = 1; - px_ext = 1; - py_ext = 1; - - displayObject = false; - displayDesiredObject = false; - displayCamera = false; - - cameraFactor = 1.0; - - camTrajType = CT_LINE; - - extCamChanged = false; - + rotz.buildFrom(0,0,0,0,0,vpMath::rad(180)); - displayImageSimulator = false; - objectImage.clear(); + scene.name = NULL; + scene.bound.ptr = NULL; + scene.bound.nbr = 0; - thickness_ = 1; + desiredScene.name = NULL; + desiredScene.bound.ptr = NULL; + desiredScene.bound.nbr = 0; + + camera.name = NULL; + camera.bound.ptr = NULL; + camera.bound.nbr = 0; } @@ -606,18 +588,24 @@ vpWireFrameSimulator::~vpWireFrameSimulator() It exists several default scenes you can use. Use the vpSceneObject and the vpSceneDesiredObject attributes to use them in this method. The corresponding files are stored in the "data" folder which is in the ViSP build directory. \param obj : Type of scene used to display the object at the current position. - \param desiredObject : Type of scene used to display the object at the desired pose (in the internal view). + \param desired_object : Type of scene used to display the object at the desired pose (in the internal view). */ void -vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desiredObject) +vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desired_object) { char name_cam[FILENAME_MAX]; char name[FILENAME_MAX]; object = obj; - this->desiredObject = desiredObject; + this->desiredObject = desired_object; + + const char *scene_dir_ = scene_dir.c_str(); + if (strlen(scene_dir_) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the camera name")); + } - strcpy(name_cam, scene_dir.c_str()); + strcpy(name_cam, scene_dir_); if (desiredObject != D_TOOL) { strcat(name_cam,"/camera.bnd"); @@ -629,7 +617,7 @@ vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredOb set_scene(name_cam,&(this->camera),1.0); } - strcpy(name, scene_dir.c_str()); + strcpy(name, scene_dir_); switch (obj) { case THREE_PTS : {strcat(name,"/3pts.bnd"); break; } @@ -652,19 +640,25 @@ vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredOb } set_scene(name,&(this->scene),1.0); + scene_dir_ = scene_dir.c_str(); + if (strlen(scene_dir_) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the desired object name")); + } + switch (desiredObject) { case D_STANDARD : { break; } case D_CIRCLE : { - strcpy(name, scene_dir.c_str()); + strcpy(name, scene_dir_); strcat(name, "/circle_sq2.bnd"); break; } case D_TOOL : { - strcpy(name, scene_dir.c_str()); + strcpy(name, scene_dir_); strcat(name, "/tool.bnd"); break; } } - set_scene(name,&(this->desiredScene),1.0); + set_scene(name, &(this->desiredScene), 1.0); if (obj == PIPE) load_rfstack(IS_INSIDE); else add_rfstack(IS_BACK); @@ -694,13 +688,13 @@ vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredOb It is also possible to add a list of vpImageSimulator instances. They will be automatically projected into the image. The position of the four corners have to be given in the object frame. \param obj : Type of scene used to display the object at the current position. - \param desiredObject : Type of scene used to display the object at the desired pose (in the internal view). + \param desired_object : Type of scene used to display the object at the desired pose (in the internal view). \param imObj : A list of vpImageSimulator instances. */ void -vpWireFrameSimulator::initScene(vpSceneObject obj, vpSceneDesiredObject desiredObject, vpList<vpImageSimulator> &imObj) +vpWireFrameSimulator::initScene(vpSceneObject obj, vpSceneDesiredObject desired_object, vpList<vpImageSimulator> &imObj) { - initScene(obj, desiredObject); + initScene(obj, desired_object); objectImage.clear(); for(imObj.front(); !imObj.outside(); imObj.next()){ objectImage.push_back(imObj.value()); @@ -718,13 +712,13 @@ vpWireFrameSimulator::initScene(vpSceneObject obj, vpSceneDesiredObject desiredO It is also possible to add a list of vpImageSimulator instances. They will be automatically projected into the image. The position of the four corners have to be given in the object frame. \param obj : Type of scene used to display the object at the current position. - \param desiredObject : Type of scene used to display the object at the desired pose (in the internal view). + \param desired_object : Type of scene used to display the object at the desired pose (in the internal view). \param imObj : A list of vpImageSimulator instances. */ void -vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desiredObject, const std::list<vpImageSimulator> &imObj) +vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desired_object, const std::list<vpImageSimulator> &imObj) { - initScene(obj, desiredObject); + initScene(obj, desired_object); objectImage = imObj; displayImageSimulator = true; } @@ -737,10 +731,10 @@ vpWireFrameSimulator::initScene(const vpSceneObject &obj, const vpSceneDesiredOb Here you can use the scene you want. You have to set the path to a .bnd or a .wrl file which is a 3D model file. \param obj : Path to the scene file you want to use. - \param desiredObject : Path to the scene file you want to use. + \param desired_object : Path to the scene file you want to use. */ void -vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject) +vpWireFrameSimulator::initScene(const char* obj, const char* desired_object) { char name_cam[FILENAME_MAX]; char name[FILENAME_MAX]; @@ -748,10 +742,21 @@ vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject) object = THREE_PTS; this->desiredObject = D_STANDARD; - strcpy(name_cam, scene_dir.c_str()); + const char *scene_dir_ = scene_dir.c_str(); + if (strlen(scene_dir_) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the camera name")); + } + + strcpy(name_cam, scene_dir_); strcat(name_cam,"/camera.bnd"); set_scene(name_cam,&camera,cameraFactor); + if (strlen(obj) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the name")); + } + strcpy(name,obj); Model_3D model; model = getExtension(obj); @@ -764,8 +769,13 @@ vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject) vpERROR_TRACE("Unknown file extension for the 3D model"); } - strcpy(name,desiredObject); - model = getExtension(desiredObject); + if (strlen(desired_object) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the camera name")); + } + + strcpy(name,desired_object); + model = getExtension(desired_object); if (model == BND_MODEL) set_scene(name,&(this->desiredScene),1.0); else if (model == WRL_MODEL) @@ -800,13 +810,13 @@ vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject) It is also possible to add a list of vpImageSimulator instances. They will be automatically projected into the image. The position of the four corners have to be given in the object frame. \param obj : Path to the scene file you want to use. - \param desiredObject : Path to the scene file you want to use. + \param desired_object : Path to the scene file you want to use. \param imObj : A list of vpImageSimulator instances. */ void -vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject, vpList<vpImageSimulator> &imObj) +vpWireFrameSimulator::initScene(const char* obj, const char* desired_object, vpList<vpImageSimulator> &imObj) { - initScene(obj, desiredObject); + initScene(obj, desired_object); objectImage.clear(); for(imObj.front(); !imObj.outside(); imObj.next()){ objectImage.push_back(imObj.value()); @@ -824,13 +834,13 @@ vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject, vpLi It is also possible to add a list of vpImageSimulator instances. They will be automatically projected into the image. The position of the four corners have to be given in the object frame. \param obj : Path to the scene file you want to use. - \param desiredObject : Path to the scene file you want to use. + \param desired_object : Path to the scene file you want to use. \param imObj : A list of vpImageSimulator instances. */ void -vpWireFrameSimulator::initScene(const char* obj, const char* desiredObject, const std::list<vpImageSimulator> &imObj) +vpWireFrameSimulator::initScene(const char* obj, const char* desired_object, const std::list<vpImageSimulator> &imObj) { - initScene(obj, desiredObject); + initScene(obj, desired_object); objectImage = imObj; displayImageSimulator = true; } @@ -851,11 +861,17 @@ vpWireFrameSimulator::initScene(const vpSceneObject &obj) object = obj; - strcpy(name_cam, scene_dir.c_str()); + const char *scene_dir_ = scene_dir.c_str(); + if (strlen(scene_dir_) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the camera name")); + } + + strcpy(name_cam, scene_dir_); strcat(name_cam,"/camera.bnd"); set_scene(name_cam,&camera,cameraFactor); - strcpy(name, scene_dir.c_str()); + strcpy(name, scene_dir_); switch (obj) { case THREE_PTS : {strcat(name,"/3pts.bnd"); break; } @@ -955,10 +971,21 @@ vpWireFrameSimulator::initScene(const char* obj) object = THREE_PTS; - strcpy(name_cam, scene_dir.c_str()); + const char *scene_dir_ = scene_dir.c_str(); + if (strlen(scene_dir_) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the camera name")); + } + + strcpy(name_cam, scene_dir_); strcat(name_cam,"/camera.bnd"); set_scene(name_cam,&camera,cameraFactor); + if (strlen(obj) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the name")); + } + strcpy(name,obj); Model_3D model; model = getExtension(obj); @@ -1252,16 +1279,16 @@ vpWireFrameSimulator::getExternalImage(vpImage<vpRGBa> &I) Get an external view. The point of view is set thanks to the pose between the camera camMf and the fixed world frame. \param I : The image where the external view is displayed. - \param camMf : The pose between the point of view and the fixed world frame. + \param cam_Mf : The pose between the point of view and the fixed world frame. \warning : The objects are displayed thanks to overlays. The image I is not modified. */ void -vpWireFrameSimulator::getExternalImage(vpImage<vpRGBa> &I, const vpHomogeneousMatrix &camMf) +vpWireFrameSimulator::getExternalImage(vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cam_Mf) { float w44o[4][4],w44cext[4][4],w44c[4][4],x,y,z; - vpHomogeneousMatrix camMft = rotz * camMf; + vpHomogeneousMatrix camMft = rotz * cam_Mf; double u; double v; @@ -1298,7 +1325,7 @@ vpWireFrameSimulator::getExternalImage(vpImage<vpRGBa> &I, const vpHomogeneousMa for(std::list<vpImageSimulator>::iterator it=objectImage.begin(); it!=objectImage.end(); ++it){ vpImageSimulator* imSim = &(*it); - imSim->setCameraPosition(rotz*camMf*fMo); + imSim->setCameraPosition(rotz*cam_Mf*fMo); imSim->getImage(I,getInternalCameraParameters(I)); } @@ -1535,16 +1562,16 @@ vpWireFrameSimulator::getExternalImage(vpImage<unsigned char> &I) Get an external view. The point of view is set thanks to the pose between the camera camMf and the fixed world frame. \param I : The image where the external view is displayed. - \param camMf : The pose between the point of view and the fixed world frame. + \param cam_Mf : The pose between the point of view and the fixed world frame. \warning : The objects are displayed thanks to overlays. The image I is not modified. */ void -vpWireFrameSimulator::getExternalImage(vpImage<unsigned char> &I, const vpHomogeneousMatrix &camMf) +vpWireFrameSimulator::getExternalImage(vpImage<unsigned char> &I, const vpHomogeneousMatrix &cam_Mf) { float w44o[4][4],w44cext[4][4],w44c[4][4],x,y,z; - vpHomogeneousMatrix camMft = rotz * camMf; + vpHomogeneousMatrix camMft = rotz * cam_Mf; double u; double v; @@ -1580,7 +1607,7 @@ vpWireFrameSimulator::getExternalImage(vpImage<unsigned char> &I, const vpHomoge I = 255; for(std::list<vpImageSimulator>::iterator it=objectImage.begin(); it!=objectImage.end(); ++it){ vpImageSimulator* imSim = &(*it); - imSim->setCameraPosition(rotz*camMf*fMo); + imSim->setCameraPosition(rotz*cam_Mf*fMo); imSim->getImage(I,getInternalCameraParameters(I)); } if (I.display != NULL) @@ -1954,12 +1981,13 @@ vpWireFrameSimulator::navigation(const vpImage<unsigned char> &I, bool &changed) Project the center of the internal camera into the external camera view. */ vpImagePoint -vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpHomogeneousMatrix &fMo) +vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo_, + const vpHomogeneousMatrix &fMo_) { vpPoint point; point.setWorldCoordinates(0,0,0); - point.track(rotz*(camMf*fMo*cMo.inverse())) ; + point.track(rotz*(camMf*fMo_*cMo_.inverse())) ; vpImagePoint iP; @@ -1972,12 +2000,14 @@ vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const v Project the center of the internal camera into the external camera view. */ vpImagePoint -vpWireFrameSimulator::projectCameraTrajectory (const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpHomogeneousMatrix &fMo) +vpWireFrameSimulator::projectCameraTrajectory (const vpImage<unsigned char> &I, + const vpHomogeneousMatrix &cMo_, + const vpHomogeneousMatrix &fMo_) { vpPoint point; point.setWorldCoordinates(0,0,0); - point.track(rotz*(camMf*fMo*cMo.inverse())) ; + point.track(rotz*(camMf*fMo_*cMo_.inverse())) ; vpImagePoint iP; @@ -1990,12 +2020,13 @@ vpWireFrameSimulator::projectCameraTrajectory (const vpImage<unsigned char> &I, Project the center of the internal camera into the external camera view. */ vpImagePoint -vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpHomogeneousMatrix &fMo, const vpHomogeneousMatrix &cMf) +vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo_, + const vpHomogeneousMatrix &fMo_, const vpHomogeneousMatrix &cMf) { vpPoint point; point.setWorldCoordinates(0,0,0); - point.track(rotz*(cMf*fMo*cMo.inverse())) ; + point.track(rotz*(cMf*fMo_*cMo_.inverse())) ; vpImagePoint iP; @@ -2008,12 +2039,13 @@ vpWireFrameSimulator::projectCameraTrajectory (const vpImage<vpRGBa> &I, const v Project the center of the internal camera into the external camera view. */ vpImagePoint -vpWireFrameSimulator::projectCameraTrajectory (const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpHomogeneousMatrix &fMo, const vpHomogeneousMatrix &cMf) +vpWireFrameSimulator::projectCameraTrajectory (const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo_, + const vpHomogeneousMatrix &fMo_, const vpHomogeneousMatrix &cMf) { vpPoint point; point.setWorldCoordinates(0,0,0); - point.track(rotz*(cMf*fMo*cMo.inverse())) ; + point.track(rotz*(cMf*fMo_*cMo_.inverse())) ; vpImagePoint iP; diff --git a/src/simulator/wireframe-simulator/vpWireFrameSimulator.h b/src/simulator/wireframe-simulator/vpWireFrameSimulator.h index 6a8de1589db026b00a3452445a155624187c41e2..4c30224d5e8250fb67f737fdd0ca7afa36914a52 100644 --- a/src/simulator/wireframe-simulator/vpWireFrameSimulator.h +++ b/src/simulator/wireframe-simulator/vpWireFrameSimulator.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpWireFrameSimulator.h 4334 2013-07-22 19:13:05Z fspindle $ + * $Id: vpWireFrameSimulator.h 5297 2015-02-10 11:19:24Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,6 @@ #include <list> #include <string> -extern "C" { #include <visp/vpMy.h> #include <visp/vpArit.h> #include <visp/vpBound.h> @@ -66,26 +65,25 @@ extern "C" { #include <visp/vpRfstack.h> #include <visp/vpArit.h> -int open_display(); -int close_display(); -int open_clipping(); -int close_clipping(); -int open_keyword (Keyword *kwp); -int open_lex (void); -int open_source (FILE *fd, const char *str); -int malloc_Bound_scene (Bound_scene *bsp, const char *name,Index bn); -int free_Bound_scene (Bound_scene *bsp); -int parser (Bound_scene *bsp); -int close_source (void); -int close_lex (void); -int close_keyword (void); +void open_display(); +void close_display(); +void open_clipping(); +void close_clipping(); +void open_keyword (Keyword *kwp); +void open_lex (void); +void open_source (FILE *fd, const char *str); +void malloc_Bound_scene (Bound_scene *bsp, const char *name,Index bn); +void free_Bound_scene (Bound_scene *bsp); +void parser (Bound_scene *bsp); +void close_source (void); +void close_lex (void); +void close_keyword (void); void display_scene(Matrix mat, Bound_scene sc); -int View_to_Matrix (View_parameters *vp, Matrix m); +void View_to_Matrix (View_parameters *vp, Matrix m); Bound *clipping_Bound (Bound *bp, Matrix m); -int set_Bound_face_display (Bound *bp, Byte b); -int point_3D_2D (Point3f *p3, Index size, unsigned int xsize, unsigned int ysize, Point2i *p2); -int wireframe_Face (Face *fp, Point2i *pp); -} +void set_Bound_face_display (Bound *bp, Byte b); +void point_3D_2D (Point3f *p3, Index size, int xsize, int ysize, Point2i *p2); +void wireframe_Face (Face *fp, Point2i *pp); #include <visp/vpConfig.h> #include <visp/vpImage.h> @@ -447,16 +445,16 @@ public: /*! Set the position of the camera relative to the object. - \param cMo : The pose of the camera. + \param cMo_ : The pose of the camera. */ - void setCameraPositionRelObj(const vpHomogeneousMatrix &cMo) {this->cMo = rotz * cMo; fMc = fMo*this->cMo.inverse();} + void setCameraPositionRelObj(const vpHomogeneousMatrix &cMo_) {this->cMo = rotz * cMo_; fMc = fMo*this->cMo.inverse();} /*! Set the position of the the world reference frame relative to the camera. - \param fMc : The pose of the camera. + \param fMc_ : The pose of the camera. */ - void setCameraPositionRelWorld(const vpHomogeneousMatrix &fMc) {this->fMc = fMc*rotz; cMo = this->fMc.inverse()*fMo;} + void setCameraPositionRelWorld(const vpHomogeneousMatrix &fMc_) {this->fMc = fMc_*rotz; cMo = this->fMc.inverse()*fMo;} /*! Set the parameter which enables to choose the size of the main camera in the external camera views. By default this parameter is set to 1. @@ -475,9 +473,9 @@ public: /*! Set the way to display the history of the main camera trajectory in the main external view. The choice is given between displaying lines and points. - \param camTrajType : The chosen way to display the camera trajectory. + \param camTraj_type : The chosen way to display the camera trajectory. */ - inline void setCameraTrajectoryDisplayType (const vpCameraTrajectoryDisplayType &camTrajType) {this->camTrajType = camTrajType;} + inline void setCameraTrajectoryDisplayType (const vpCameraTrajectoryDisplayType &camTraj_type) {this->camTrajType = camTraj_type;} /*! Set the color used to display the object at the current position. @@ -488,9 +486,9 @@ public: /*! Set the desired position of the camera relative to the object. - \param cdMo : The desired pose of the camera. + \param cdMo_ : The desired pose of the camera. */ - void setDesiredCameraPosition(const vpHomogeneousMatrix &cdMo) {this->cdMo = rotz * cdMo;} + void setDesiredCameraPosition(const vpHomogeneousMatrix &cdMo_) {this->cdMo = rotz * cdMo_;} /*! Set the color used to display the object at the desired position. @@ -502,9 +500,9 @@ public: By default the trajectory is displayed. - \param displayCameraTrajectory : Set to true to display the camera trajectory. + \param do_display : Set to true to display the camera trajectory. */ - void setDisplayCameraTrajectory (const bool &displayCameraTrajectory) {this->displayCameraTrajectory = displayCameraTrajectory;} + void setDisplayCameraTrajectory (const bool &do_display) {this->displayCameraTrajectory = do_display;} /*! Set the internal camera parameters. @@ -518,11 +516,11 @@ public: /*! Set the external camera point of view. - \param camMf : The pose of the external camera relative to the world reference frame. + \param cam_Mf : The pose of the external camera relative to the world reference frame. */ - void setExternalCameraPosition(const vpHomogeneousMatrix &camMf) + void setExternalCameraPosition(const vpHomogeneousMatrix &cam_Mf) { - this->camMf = rotz * camMf; + this->camMf = rotz * cam_Mf; vpTranslationVector T; this->camMf.extract (T); this->camMf2.buildFrom(0,0,T[2],0,0,0); @@ -558,9 +556,9 @@ public: /*! Set the pose between the object and the fixed world frame. - \param fMo : The pose between the object and the fixed world frame. + \param fMo_ : The pose between the object and the fixed world frame. */ - void set_fMo(const vpHomogeneousMatrix &fMo) {this->fMo = fMo;/*this->cMo = fMc.inverse()*fMo;*/} + void set_fMo(const vpHomogeneousMatrix &fMo_) {this->fMo = fMo_;/*this->cMo = fMc.inverse()*fMo;*/} protected: void display_scene(Matrix mat, Bound_scene &sc, const vpImage<vpRGBa> &I, const vpColor &color); diff --git a/src/tools/convert/vpConvert.cpp b/src/tools/convert/vpConvert.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a5294c51bc473b12a47d0c2b5014c0caabbf0596 --- /dev/null +++ b/src/tools/convert/vpConvert.cpp @@ -0,0 +1,435 @@ +/**************************************************************************** + * + * $Id: vpIoTools.cpp 5214 2015-01-27 18:33:01Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Directory management. + * + * Authors: + * Fabien Spindler + * Souriya Trinh + * + *****************************************************************************/ + +/*! + \file vpConvert.cpp + \brief Tools for type or general conversion. +*/ + +#include <vector> // std::vector +#include <algorithm> // std::transform + +#include <visp/vpConvert.h> + + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + /**! + Unary function used to transform a cv::KeyPoint to a vpImagePoint. + \param keypoint : KeyPoint to convert. + + \return A vpImagePoint with the 2D coordinates corresponding to the location of the KeyPoint. + */ + vpImagePoint vpConvert::keyPointToVpImagePoint(const cv::KeyPoint &keypoint) { + return vpImagePoint(keypoint.pt.y, keypoint.pt.x); + } + + /*! + Unary function to convert a cv::Point2f to a vpImagePoint. + \param point : Point to convert. + + \return A vpImagePoint with the 2D coordinates stored in cv::Point2f. + */ + vpImagePoint vpConvert::point2fToVpImagePoint(const cv::Point2f &point) { + return vpImagePoint(point.y, point.x); + } + + /*! + Unary function to convert a cv::Point2d to a vpImagePoint. + \param point : Point to convert. + + \return A vpImagePoint with the 2D coordinates stored in cv::Point2d. + */ + vpImagePoint vpConvert::point2dToVpImagePoint(const cv::Point2d &point) { + return vpImagePoint(point.y, point.x); + } + + /*! + Unary function to convert a cv::Point3f to a vpPoint (object frame). + \param point3f : Point to convert. + \return A vpPoint with 3D coordinates in the object frame from from a cv::Point3f. + */ + vpPoint vpConvert::point3fToVpObjectPoint(const cv::Point3f &point3f) { + vpPoint pt; + pt.set_oX(point3f.x); + pt.set_oY(point3f.y); + pt.set_oZ(point3f.z); + pt.set_oW(1.0); + return pt; + } + + /*! + Unary function to convert a cv::Point3f to a vpPoint (camera frame). + \param point3f : Point to convert. + \return A vpPoint with 3D coordinates in the camera frame from from a cv::Point3f. + */ + vpPoint vpConvert::point3fToVpCamPoint(const cv::Point3f &point3f) { + vpPoint pt; + pt.set_X(point3f.x); + pt.set_Y(point3f.y); + pt.set_Z(point3f.z); + pt.set_W(1.0); + return pt; + } + + /*! + Unary function to convert a cv::Point3d to a vpPoint (object frame). + \param point3d : Point to convert. + \return A vpPoint with 3D coordinates in the object frame from from a cv::Point3d. + */ + vpPoint vpConvert::point3dToVpObjectPoint(const cv::Point3d &point3d) { + vpPoint pt; + pt.set_oX(point3d.x); + pt.set_oY(point3d.y); + pt.set_oZ(point3d.z); + pt.set_oW(1.0); + return pt; + } + + /*! + Unary function to convert a cv::Point3d to a vpPoint (camera frame). + \param point3d : Point to convert. + \return A vpPoint with 3D coordinates in the camera frame from from a cv::Point3d. + */ + vpPoint vpConvert::point3dToVpCamPoint(const cv::Point3d &point3d) { + vpPoint pt; + pt.set_X(point3d.x); + pt.set_Y(point3d.y); + pt.set_Z(point3d.z); + pt.set_W(1.0); + return pt; + } + + /*! + Unary function to convert a vpImagePoint to a cv::Point2f. + \param point : Image point to convert. + + \return A cv::Point2f with the 2D coordinates stored in vpImagePoint. + */ + cv::Point2f vpConvert::vpImagePointToPoint2f(const vpImagePoint &point) { + return cv::Point2f((float) point.get_u(), (float) point.get_v()); + } + + /*! + Unary function to convert a vpImagePoint to a cv::Point2d. + \param point : Image point to convert. + + \return A cv::Point2d with the 2D coordinates stored in vpImagePoint. + */ + cv::Point2d vpConvert::vpImagePointToPoint2d(const vpImagePoint &point) { + return cv::Point2d(point.get_u(), point.get_v()); + } + + /*! + Unary function to convert the 3D coordinates in the camera frame to a cv::Point3f. + \param point : Point to convert. + + \return A cv::Point3f with the 3D coordinates stored in vpPoint in the camera frame. + */ + cv::Point3f vpConvert::vpCamPointToPoint3f(const vpPoint &point) { + return cv::Point3f((float) point.get_X(), (float) point.get_Y(), (float) point.get_Z()); + } + + /*! + Unary function to convert the 3D coordinates in the camera frame to a cv::Point3d. + \param point : Point to convert. + + \return A cv::Point3d with the 3D coordinates stored in vpPoint in the camera frame. + */ + cv::Point3d vpConvert::vpCamPointToPoint3d(const vpPoint &point) { + return cv::Point3d(point.get_X(), point.get_Y(), point.get_Z()); + } + + /*! + Unary function to convert the 3D coordinates in the object frame to a cv::Point3f. + \param point : Point to convert. + + \return A cv::Point3f with the 3D coordinates stored in vpPoint in the object frame. + */ + cv::Point3f vpConvert::vpObjectPointToPoint3f(const vpPoint &point) { + return cv::Point3f((float) point.get_oX(), (float) point.get_oY(), (float) point.get_oZ()); + } + + /*! + Unary function to convert the 3D coordinates in the object frame to a cv::Point3d. + \param point : Point to convert. + + \return A cv::Point3d with the 3D coordinates stored in vpPoint in the object frame. + */ + cv::Point3d vpConvert::vpObjectPointToPoint3d(const vpPoint &point) { + return cv::Point3d(point.get_oX(), point.get_oY(), point.get_oZ()); + } + + /*! + Unary function to return the train index stored in a cv::DMatch. + \param match : The cv::DMatch we want to get the train index. + + \return The train index stored in a cv::DMatch. + */ + int vpConvert::dMatchToTrainIndex(const cv::DMatch &match) { + return match.trainIdx; + } + + + /*! + Convert a cv::KeyPoint to a vpImagePoint. + \param from : cv::KeyPoint to convert. + \param to : vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const cv::KeyPoint &from, vpImagePoint &to) { + to = keyPointToVpImagePoint(from); + } + + /*! + Convert a cv::Point2f to a vpImagePoint. + \param from : cv::Point2f to convert. + \param to : vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const cv::Point2f &from, vpImagePoint &to) { + to = point2fToVpImagePoint(from); + } + + /*! + Convert a cv::Point2d to a vpImagePoint. + \param from : cv::Point2d to convert. + \param to : vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const cv::Point2d &from, vpImagePoint &to) { + to = point2dToVpImagePoint(from); + } + + /*! + Convert a cv::Point3f to a vpPoint. + \param from : cv::Point3f to convert. + \param to : vpPoint converted. + \param cameraFrame : If true, convert into the camera frame, otherwise in the object frame. + */ + void vpConvert::convertFromOpenCV(const cv::Point3f &from, vpPoint &to, const bool cameraFrame) { + if(cameraFrame) { + to = point3fToVpCamPoint(from); + } else { + to = point3fToVpObjectPoint(from); + } + } + + /*! + Convert a cv::Point3d to a vpPoint. + \param from : cv::Point3d to convert. + \param to : vpPoint converted. + \param cameraFrame : If true, convert into the camera frame, otherwise in the object frame. + */ + void vpConvert::convertFromOpenCV(const cv::Point3d &from, vpPoint &to, const bool cameraFrame) { + if(cameraFrame) { + to = point3dToVpCamPoint(from); + } else { + to = point3dToVpObjectPoint(from); + } + } + + /*! + Convert a vector of cv::KeyPoint to a vector of vpImagePoint. + \param from : Vector of cv::KeyPoint to convert. + \param to : Vector of vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::KeyPoint> &from, std::vector<vpImagePoint> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), keyPointToVpImagePoint); + } + + /*! + Convert a vector of cv::Point2f to a vector of vpImagePoint. + \param from : Vector of cv::Point2f to convert. + \param to : Vector of vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::Point2f> &from, std::vector<vpImagePoint> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), point2fToVpImagePoint); + } + + /*! + Convert a vector of cv::Point2d to a vector of vpImagePoint. + \param from : Vector of cv::Point2d to convert. + \param to : Vector of vpImagePoint converted. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::Point2d> &from, std::vector<vpImagePoint> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), point2dToVpImagePoint); + } + + /*! + Convert a vector of cv::Point3f to a vector of vpPoint. + \param from : Vector of cv::Point3f to convert. + \param to : Vector of vpPoint converted. + \param cameraFrame : If true, convert into the camera frame, otherwise in the object frame. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::Point3f> &from, std::vector<vpPoint> &to, const bool cameraFrame) { + to.resize(from.size()); + if(cameraFrame) { + std::transform(from.begin(), from.end(), to.begin(), point3fToVpCamPoint); + } else { + std::transform(from.begin(), from.end(), to.begin(), point3fToVpObjectPoint); + } + } + + /*! + Convert a vector of cv::Point3d to a vector of vpPoint. + \param from : Vector of cv::Point3d to convert. + \param to : Vector of vpPoint converted. + \param cameraFrame : If true, convert into the camera frame, otherwise in the object frame. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::Point3d> &from, std::vector<vpPoint> &to, const bool cameraFrame) { + to.resize(from.size()); + if(cameraFrame) { + std::transform(from.begin(), from.end(), to.begin(), point3dToVpCamPoint); + } else { + std::transform(from.begin(), from.end(), to.begin(), point3dToVpObjectPoint); + } + } + + /*! + Convert a vector of cv::DMatch to a vector of unsigned int (for a query index 0, to[0] ==> train index). + + \warning The list of query indexes in DMatch must be ordered in a way that from[i].queryIdx == i. + + \param from : Vector of cv::DMatch to convert. + \param to : Vector of unsigned int converted. + */ + void vpConvert::convertFromOpenCV(const std::vector<cv::DMatch> &from, std::vector<unsigned int> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), dMatchToTrainIndex); + } + + /*! + Convert a vpImagePoint to a cv::Point2f. + \param from : vpImagePoint to convert. + \param to : cv::Point2f converted. + */ + void vpConvert::convertToOpenCV(const vpImagePoint &from, cv::Point2f &to) { + to = vpImagePointToPoint2f(from); + } + + /*! + Convert a vpImagePoint to a cv::Point2d. + \param from : vpImagePoint to convert. + \param to : cv::Point2d converted. + */ + void vpConvert::convertToOpenCV(const vpImagePoint &from, cv::Point2d &to) { + to = vpImagePointToPoint2d(from); + } + + /*! + Convert a vpPoint to a cv::Point3f. + \param from : vpPoint to convert. + \param to : cv::Point3f converted. + \param cameraFrame : If true, convert from coordinates in the camera frame, otherwise in the object frame. + */ + void vpConvert::convertToOpenCV(const vpPoint &from, cv::Point3f &to, const bool cameraFrame) { + if(cameraFrame) { + to = vpCamPointToPoint3f(from); + } else { + to = vpObjectPointToPoint3f(from); + } + } + + /*! + Convert a vpPoint to a cv::Point3d. + \param from : vpPoint to convert. + \param to : cv::Point3d converted. + \param cameraFrame : If true, convert from coordinates in the camera frame, otherwise in the object frame. + */ + void vpConvert::convertToOpenCV(const vpPoint &from, cv::Point3d &to, const bool cameraFrame) { + if(cameraFrame) { + to = vpCamPointToPoint3d(from); + } else { + to = vpObjectPointToPoint3d(from); + } + } + + /*! + Convert a vector of vpImagePoint to a vector of cv::Point2f. + \param from : Vector of vpImagePoint to convert. + \param to : Vector of cv::Point2f converted. + */ + void vpConvert::convertToOpenCV(const std::vector<vpImagePoint> &from, std::vector<cv::Point2f> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), vpImagePointToPoint2f); + } + + /*! + Convert a vector of vpImagePoint to a vector of cv::Point2d. + \param from : Vector of vpImagePoint to convert. + \param to : Vector of cv::Point2d converted. + */ + void vpConvert::convertToOpenCV(const std::vector<vpImagePoint> &from, std::vector<cv::Point2d> &to) { + to.resize(from.size()); + std::transform(from.begin(), from.end(), to.begin(), vpImagePointToPoint2d); + } + + /*! + Convert a vector of vpPoint to a vector of cv::Point3f. + \param from : Vector of vpPoint to convert. + \param to : Vector of cv::Point3f converted. + \param cameraFrame : If true, the camera frame is considered, otherwise the object frame. + */ + void vpConvert::convertToOpenCV(const std::vector<vpPoint> &from, std::vector<cv::Point3f> &to, const bool cameraFrame) { + to.resize(from.size()); + if(cameraFrame) { + std::transform(from.begin(), from.end(), to.begin(), vpCamPointToPoint3f); + } else { + std::transform(from.begin(), from.end(), to.begin(), vpObjectPointToPoint3f); + } + } + + /*! + Convert a vector of vpPoint to a vector of cv::Point3d. + \param from : Vector of vpPoint to convert. + \param to : Vector of cv::Point3d converted. + \param cameraFrame : If true, the camera frame is considered, otherwise the object frame. + */ + void vpConvert::convertToOpenCV(const std::vector<vpPoint> &from, std::vector<cv::Point3d> &to, const bool cameraFrame) { + to.resize(from.size()); + if(cameraFrame) { + std::transform(from.begin(), from.end(), to.begin(), vpCamPointToPoint3d); + } else { + std::transform(from.begin(), from.end(), to.begin(), vpObjectPointToPoint3d); + } + } +#endif diff --git a/src/tools/convert/vpConvert.h b/src/tools/convert/vpConvert.h new file mode 100644 index 0000000000000000000000000000000000000000..2a871f28df36e49eff444cde3063bd6969a7740b --- /dev/null +++ b/src/tools/convert/vpConvert.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * + * $Id: vpIoTools.h 5197 2015-01-23 17:24:22Z strinh $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Directory management. + * + * Authors: + * Fabien Spindler + * Souriya Trinh + * + *****************************************************************************/ + + +#ifndef __vpConvert_h__ +#define __vpConvert_h__ + +/*! + \file vpConvert.h + \brief Tools for type or general conversion. + */ + +#include <visp/vpConfig.h> + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + #include <opencv2/core/core.hpp> + #include <opencv2/features2d/features2d.hpp> +#endif + +#include <visp/vpImagePoint.h> +#include <visp/vpPoint.h> + + +class VISP_EXPORT vpConvert +{ + +public: +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + static void convertFromOpenCV(const cv::KeyPoint &from, vpImagePoint &to); + static void convertFromOpenCV(const cv::Point2f &from, vpImagePoint &to); + static void convertFromOpenCV(const cv::Point2d &from, vpImagePoint &to); + static void convertFromOpenCV(const cv::Point3f &from, vpPoint &to, const bool cameraFrame=false); + static void convertFromOpenCV(const cv::Point3d &from, vpPoint &to, const bool cameraFrame=false); + + static void convertFromOpenCV(const std::vector<cv::KeyPoint> &from, std::vector<vpImagePoint> &to); + static void convertFromOpenCV(const std::vector<cv::Point2f> &from, std::vector<vpImagePoint> &to); + static void convertFromOpenCV(const std::vector<cv::Point2d> &from, std::vector<vpImagePoint> &to); + static void convertFromOpenCV(const std::vector<cv::Point3f> &from, std::vector<vpPoint> &to, const bool cameraFrame=false); + static void convertFromOpenCV(const std::vector<cv::Point3d> &from, std::vector<vpPoint> &to, const bool cameraFrame=false); + static void convertFromOpenCV(const std::vector<cv::DMatch> &from, std::vector<unsigned int> &to); + + static void convertToOpenCV(const vpImagePoint &from, cv::Point2f &to); + static void convertToOpenCV(const vpImagePoint &from, cv::Point2d &to); + static void convertToOpenCV(const vpPoint &from, cv::Point3f &to, const bool cameraFrame=false); + static void convertToOpenCV(const vpPoint &from, cv::Point3d &to, const bool cameraFrame=false); + + static void convertToOpenCV(const std::vector<vpImagePoint> &from, std::vector<cv::Point2f> &to); + static void convertToOpenCV(const std::vector<vpImagePoint> &from, std::vector<cv::Point2d> &to); + static void convertToOpenCV(const std::vector<vpPoint> &from, std::vector<cv::Point3f> &to, const bool cameraFrame=false); + static void convertToOpenCV(const std::vector<vpPoint> &from, std::vector<cv::Point3d> &to, const bool cameraFrame=false); + +private: + static vpImagePoint keyPointToVpImagePoint(const cv::KeyPoint &keypoint); + static vpImagePoint point2fToVpImagePoint(const cv::Point2f &point); + static vpImagePoint point2dToVpImagePoint(const cv::Point2d &point); + static vpPoint point3fToVpObjectPoint(const cv::Point3f &point3f); + static vpPoint point3dToVpObjectPoint(const cv::Point3d &point3d); + static vpPoint point3fToVpCamPoint(const cv::Point3f &point3f); + static vpPoint point3dToVpCamPoint(const cv::Point3d &point3d); + static int dMatchToTrainIndex(const cv::DMatch &match); + + static cv::Point2f vpImagePointToPoint2f(const vpImagePoint &point); + static cv::Point2d vpImagePointToPoint2d(const vpImagePoint &point); + static cv::Point3f vpCamPointToPoint3f(const vpPoint &point); + static cv::Point3d vpCamPointToPoint3d(const vpPoint &point); + static cv::Point3f vpObjectPointToPoint3f(const vpPoint &point); + static cv::Point3d vpObjectPointToPoint3d(const vpPoint &point); + +#endif + +}; + +#endif diff --git a/src/tools/geometry/vpPlane.cpp b/src/tools/geometry/vpPlane.cpp index e44ffb683a5f4b8aa8d8419781444fb0fa6ef3cb..58055c0b4cb49b53a15ddffa71b1125e2fc0fd35 100644 --- a/src/tools/geometry/vpPlane.cpp +++ b/src/tools/geometry/vpPlane.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlane.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPlane.cpp 4740 2014-05-26 07:15:44Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,13 +68,7 @@ vpPlane::operator =(const vpPlane& p) /*! Basic constructor that set the plane parameters A, B, C, D to zero. */ -vpPlane::vpPlane() -{ - setA(0) ; - setB(0) ; - setC(0) ; - setD(0) ; -} +vpPlane::vpPlane() : A(0), B(0), C(0), D(0) {} /*! Plane constructor from A, B, C, D parameters. @@ -83,21 +77,17 @@ vpPlane::vpPlane() (x,y,z) are the coordinates of a point and \f$[A,B,C]^T\f$ is the normal vector of the plane. - \param A, B, C, D : Parameters of the plane. + \param a, b, c, d : Parameters of the plane. */ -vpPlane::vpPlane(const double A,const double B,const double C, const double D) -{ - setA(A) ; - setB(B) ; - setC(C) ; - setD(D) ; -} +vpPlane::vpPlane(const double a,const double b,const double c, const double d) + : A(a), B(b), C(c), D(d) {} /*! Copy constructor. */ vpPlane::vpPlane(const vpPlane& P) + : A(0), B(0), C(0), D(0) { setA(P.getA()) ; setB(P.getB()) ; @@ -114,19 +104,28 @@ vpPlane::vpPlane(const vpPlane& P) (x,y,z) are the coordinates of a point and \f$[A,B,C]^T\f$ is the normal vector of the plane. - \param P : A point with coordinates (x,y,z) on the plane. + \param P : A point with coordinates (x,y,z) on the plane. The \e frame parameter indicates + if the coordinates of this points that are used are expressed in the camera of object frame. \param n : The normal to the plane. + \param frame: Indicates if the plane should be initialized from the point P + coordinates expressed in the camera or object frame. + */ -vpPlane::vpPlane(const vpPoint& P, const vpColVector &n) +vpPlane::vpPlane(const vpPoint& P, const vpColVector &n, vpPlaneFrame frame) + : A(0), B(0), C(0), D(0) { //Equation of the plane is given by: A = n[0]; B = n[1]; C = n[2]; - D=-(A*P.get_X()+B*P.get_Y()+C*P.get_Z()); + if (frame == vpPlane::camera_frame) + D=-(A*P.get_X()+B*P.get_Y()+C*P.get_Z()); + else + D=-(A*P.get_oX()+B*P.get_oY()+C*P.get_oZ()); + } /*! @@ -147,6 +146,7 @@ void vpPlane::init(const vpPlane& P) \e n to the plane. \param P : A point with coordinates (x,y,z) on the plane. + The size of the vector should be 3, with P[0]=x, with P[1]=y, with P[2]=z. \param n : The normal to the plane. @@ -165,26 +165,42 @@ void vpPlane::init(const vpColVector & P, const vpColVector &n) /*! Compute the equation of a plane given three point P, Q, R. - The normal to the plane is given by + The normal to the plane is given by: n = PQ x PR + \param P,Q,R: Three points on the plane. + \param frame: Indicates if the plane should be initialized from the points + coordinates expressed in the camera or object frame. + */ void -vpPlane::init(const vpPoint &P, const vpPoint &Q, const vpPoint &R) +vpPlane::init(const vpPoint &P, const vpPoint &Q, const vpPoint &R, vpPlaneFrame frame) { vpColVector a(3); vpColVector b(3); vpColVector n(3); - //Calculate vector corresponding to PQ - a[0]=P.get_X()-Q.get_X(); - a[1]=P.get_Y()-Q.get_Y(); - a[2]=P.get_Z()-Q.get_Z(); - - //Calculate vector corresponding to PR - b[0]=P.get_X()-R.get_X(); - b[1]=P.get_Y()-R.get_Y(); - b[2]=P.get_Z()-R.get_Z(); - + if (frame == vpPlane::camera_frame) { + //Calculate vector corresponding to PQ + a[0]=P.get_X()-Q.get_X(); + a[1]=P.get_Y()-Q.get_Y(); + a[2]=P.get_Z()-Q.get_Z(); + + //Calculate vector corresponding to PR + b[0]=P.get_X()-R.get_X(); + b[1]=P.get_Y()-R.get_Y(); + b[2]=P.get_Z()-R.get_Z(); + } + else { + //Calculate vector corresponding to PQ + a[0]=P.get_oX()-Q.get_oX(); + a[1]=P.get_oY()-Q.get_oY(); + a[2]=P.get_oZ()-Q.get_oZ(); + + //Calculate vector corresponding to PR + b[0]=P.get_oX()-R.get_oX(); + b[1]=P.get_oY()-R.get_oY(); + b[2]=P.get_oZ()-R.get_oZ(); + } //Calculate normal vector to plane PQ x PR n=vpColVector::cross(a,b); @@ -192,7 +208,10 @@ vpPlane::init(const vpPoint &P, const vpPoint &Q, const vpPoint &R) A = n[0]; B = n[1]; C = n[2]; - D=-(A*P.get_X()+B*P.get_Y()+C*P.get_Z()); + if (frame == vpPlane::camera_frame) + D=-(A*P.get_X()+B*P.get_Y()+C*P.get_Z()); + else + D=-(A*P.get_oX()+B*P.get_oY()+C*P.get_oZ()); double norm = sqrt(A*A+B*B+C*C) ; A /= norm ; @@ -205,14 +224,19 @@ vpPlane::init(const vpPoint &P, const vpPoint &Q, const vpPoint &R) /*! Compute the equation of a plane given three point P, Q, R. - The normal to the plane is given by + The normal to the plane is given by: n = PQ x PR + \param P,Q,R: Three points on the plane. + \param frame: Indicates if the plane should be initialized from the points + coordinates expressed in the camera or object frame. + \sa init(const vpPoint &, const vpPoint &, const vpPoint &) */ -vpPlane::vpPlane(const vpPoint &P, const vpPoint &Q, const vpPoint &R) +vpPlane::vpPlane(const vpPoint &P, const vpPoint &Q, const vpPoint &R, vpPlaneFrame frame) + : A(0), B(0), C(0), D(0) { - init(P,Q,R) ; + init(P, Q, R, frame) ; } /*! @@ -359,3 +383,14 @@ void vpPlane::changeFrame(const vpHomogeneousMatrix &cMo) D = Do - (cMo[0][3]*A + cMo[1][3]*B + cMo[2][3]*C); } +/*! + + Print the plane parameters as a stream like "(A,B,C,D) " where + A,B,C and D correspond to the parameters of the plane. + +*/ +VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpPlane& p) +{ + return (os << "("<<p.getA() << ","<<p.getB() + << ","<<p.getC()<< ","<<p.getD() <<") ") ; +} ; diff --git a/src/tools/geometry/vpPlane.h b/src/tools/geometry/vpPlane.h index e41eebe9f078b1af319fc04ec70f44db8a55f4ba..c55e08f38279eb41822a5a8cb9f19a66b2ababbd 100644 --- a/src/tools/geometry/vpPlane.h +++ b/src/tools/geometry/vpPlane.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlane.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPlane.h 4702 2014-03-27 15:33:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ \brief This class defines the container for a plane geometrical structure. A plane is given by the equation \f$Ax + By + Cz + D = 0\f$ where - (x,y,z) are the coordinates of a point and \f$[A,B,C]^T\f$ is normal + (x,y,z) are the coordinates of a point and where \f$[A,B,C]^T\f$ is a normal vector of the plane. */ @@ -75,34 +75,37 @@ class VISP_EXPORT vpPlane #endif double A,B,C,D ; - public: + typedef enum { + object_frame, + camera_frame + } vpPlaneFrame; vpPlane() ; vpPlane(const vpPlane& P) ; vpPlane(const double A, const double B,const double C,const double D) ; - vpPlane(const vpPoint& P, const vpColVector &n) ; - vpPlane(const vpPoint &P, const vpPoint &Q, const vpPoint &R) ; - void init(const vpPoint& P, const vpPoint& Q, const vpPoint& R) ; + vpPlane(const vpPoint& P, const vpColVector &n, vpPlaneFrame frame=camera_frame) ; + vpPlane(const vpPoint &P, const vpPoint &Q, const vpPoint &R, vpPlaneFrame frame=camera_frame) ; + void init(const vpPoint& P, const vpPoint& Q, const vpPoint& R, vpPlaneFrame frame=camera_frame) ; void init(const vpColVector& P, const vpColVector &n) ; void init(const vpPlane& P) ; // SET the parameter /*! Set plane parameter A. */ - inline void setA(const double A) { this->A = A ; } + inline void setA(const double a) { this->A = a ; } /*! Set plane parameter B. */ - inline void setB(const double B) { this->B = B ; } + inline void setB(const double b) { this->B = b ; } /*! Set plane parameter C. */ - inline void setC(const double C) { this->C = C ; } + inline void setC(const double c) { this->C = c ; } /*! Set plane parameter D. */ - inline void setD(const double D) { this->D = D ; } + inline void setD(const double d) { this->D = d ; } /*! Set plane parameters A, B, C, D. */ - inline void setABCD(const double A, const double B, - const double C, const double D) + inline void setABCD(const double a, const double b, + const double c, const double d) { - this->A = A; - this->B = B; - this->C = C; - this->D = D; + this->A = a; + this->B = b; + this->C = c; + this->D = d; } vpPlane& operator =(const vpPlane& f) ; @@ -155,19 +158,7 @@ public: vpColVector getNormal() const; void getNormal(vpColVector &n) const; - - /*! - - Print the plane parameters as a stream like "(A,B,C,D) " where - A,B,C and D correspond to the parameters of the plane. - - */ - friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpPlane& p) - { - return (os << "("<<p.getA() << ","<<p.getB() - << ","<<p.getC()<< ","<<p.getD() <<") ") ; - } ; - + friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpPlane& p); // Operation with Plane void projectionPointOnPlan(const vpPoint& P, vpPoint& Pproj) const ; diff --git a/src/tools/geometry/vpPolygon.cpp b/src/tools/geometry/vpPolygon.cpp index 71bc4615d71a9acc8aa8583b5c09b2823ade8e48..4cef509fd821dd6d0c63c08c79066ecb52cf6257 100644 --- a/src/tools/geometry/vpPolygon.cpp +++ b/src/tools/geometry/vpPolygon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPolygon.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPolygon.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,8 +57,8 @@ \f$ (0,0) \f$, \f$ (1,0) \f$ and \f$ (0,1) \f$. */ vpPolygon::vpPolygon() + : _corners(), _center(), _area(0.), _goodPoly(true), _bbox() { - _goodPoly = true; std::vector<vpImagePoint> corners; corners.push_back(vpImagePoint(0,0)); corners.push_back(vpImagePoint(1,0)); @@ -74,6 +74,7 @@ vpPolygon::vpPolygon() \param corners : The Points defining the corners. */ vpPolygon::vpPolygon(const std::vector<vpImagePoint>& corners) + : _corners(), _center(), _area(0.), _goodPoly(true), _bbox() { if(corners.size() < 3){ _goodPoly = false; @@ -87,11 +88,13 @@ vpPolygon::vpPolygon(const std::vector<vpImagePoint>& corners) \param poly : The polygon used for the initialisation. */ vpPolygon::vpPolygon(const vpPolygon &poly) + : _corners(), _center(), _area(0.), _goodPoly(true), _bbox() { _corners = poly._corners; _center = poly._center; _area = poly._area; _goodPoly = poly._goodPoly; + _bbox = poly._bbox; } /*! @@ -165,8 +168,8 @@ vpPolygon::initClick(const vpImage<unsigned char>& I) std::vector<vpImagePoint> cornersClick; while(button == vpMouseButton::button1){ - vpDisplay::getClick(I, ip, button, true); - if(button == vpMouseButton::button1){ + bool ret = vpDisplay::getClick(I, ip, button, true); + if(ret && button == vpMouseButton::button1){ vpDisplay::displayCross(I, ip, 5, vpColor::red); cornersClick.push_back(ip); vpDisplay::flush(I); diff --git a/src/tools/geometry/vpPolygon.h b/src/tools/geometry/vpPolygon.h index ffa4dc49d378f5160ae53d8e042886478a9548f0..13cf64477f34aa75da457ff7d59dbb862b3cacec 100644 --- a/src/tools/geometry/vpPolygon.h +++ b/src/tools/geometry/vpPolygon.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPolygon.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPolygon.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tools/geometry/vpRect.cpp b/src/tools/geometry/vpRect.cpp index 1fce4050459d1b1802a5385738d9e4de3c8e52f1..1dba245ec789388ea5fb282078783c8b6fe69676 100755 --- a/src/tools/geometry/vpRect.cpp +++ b/src/tools/geometry/vpRect.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRect.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRect.cpp 4917 2014-10-06 08:35:22Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,26 +56,22 @@ */ -vpRect::vpRect() -{ - this->left = 0; - this->top = 0; - this->width = 0; - this->height = 0; -}; +vpRect::vpRect() : left(0), top(0), width(0), height(0) {}; /*! Constructs a rectangle with the \e top, \e left corner and \e width and \e height. + \param l : coordinate of the top/left corner along the horizontal axis. + \param t : coordinate of the top/left corner along the vertical axis. + \param w : rectangle width. + \param h : rectangle height. + */ -vpRect::vpRect(double left, double top, double width, double height) +vpRect::vpRect(double l, double t, double w, double h) + : left(l), top(t), width(w), height(h) { - this->left = left; - this->top = top; - this->width = width; - this->height = height; }; /*! @@ -83,13 +79,14 @@ vpRect::vpRect(double left, double top, double width, double height) Constructs a rectangle with \e topLeft the top-left corner location and \e width and \e height the rectangle size. + \param topLeft : coordinates of the top/left corner. + \param w : rectangle width. + \param h : rectangle height. + */ -vpRect::vpRect(const vpImagePoint &topLeft, double width, double height) +vpRect::vpRect(const vpImagePoint &topLeft, double w, double h) + : left(topLeft.get_u()), top(topLeft.get_v()), width(w), height(h) { - this->left = topLeft.get_u(); - this->top = topLeft.get_v(); - this->width = width; - this->height = height; }; /*! @@ -99,6 +96,7 @@ vpRect::vpRect(const vpImagePoint &topLeft, double width, double height) */ vpRect::vpRect(const vpImagePoint &topLeft, const vpImagePoint &bottomRight) + : left(topLeft.get_u()), top(topLeft.get_v()), width(0), height(0) { this->left = topLeft.get_u(); this->top = topLeft.get_v(); @@ -113,11 +111,9 @@ vpRect::vpRect(const vpImagePoint &topLeft, const vpImagePoint &bottomRight) */ vpRect::vpRect(const vpRect& r) + : left(0), top(0), width(0), height(0) { - this->left = r.left; - this->top = r.top; - this->width = r.width; - this->height = r.height; + *this = r; }; /*! @@ -132,3 +128,129 @@ vpRect &vpRect::operator=(const vpRect& r) this->height = r.height; return *this; }; + +/*! + + Create a rectangle as the bounding box of a vector of image points. + \param ip : Vector of image points. At least 1 points is mandatory, + otherwise an exception is thrown. +*/ +vpRect::vpRect(const std::vector<vpImagePoint> &ip) + : left(0), top(0), width(0), height(0) +{ + set(ip); +} + +/*! + + Set the rectangle with the \e top, \e left corner and \e width + and \e height. + \param l : coordinate of the top/left corner along the horizontal axis. + \param t : coordinate of the top/left corner along the vertical axis. + \param w : rectangle width. + \param h : rectangle height. + +*/ +void vpRect::set(double l, double t, double w, double h) +{ + left = l; + top = t; + width = w; + height = h; +}; + +/*! + + Set the rectangle with \e topLeft the top-left corner location + and \e width and \e height the rectangle size. + + \param topLeft : coordinates of the top-left corner. + \param w : rectangle width. + \param h : rectangle height. +*/ +void vpRect::set(const vpImagePoint &topLeft, double w, double h) +{ + left = topLeft.get_u(); + top = topLeft.get_v(); + width = w; + height = h; +}; + +/*! + + Set the rectangle as the bounding box of a vector of image points. + \param ip : Vector of image points. At least 1 points is mandatory, + otherwise an exception is thrown. +*/ +void vpRect::set(const std::vector<vpImagePoint> &ip) +{ + if (ip.size() < 1) + throw (vpException(vpException::dimensionError, + "At least 1 point is requested to build a rectangle")); + double minu, maxu; + double minv, maxv; + minu = maxu = ip[0].get_u(); + minv = maxv = ip[0].get_v(); + + for(size_t i=1; i<ip.size(); i++) { + double u = ip[i].get_u(); + double v = ip[i].get_v(); + if ( u < minu ) minu = u; + else if (u > maxu) maxu = u; + if ( v < minv ) minv = v; + else if (v > maxv) maxv = v; + } + + setLeft (minu); + setTop (minv); + setRight (maxu); + setBottom(maxv); +}; + +/*! + + Set the rectangle with \e topLeft the top-left corner location + and \e bottomRight the bottom-right corner. + + \param topLeft : coordinates of the top-left corner. + \param bottomRight : coordinates of the bottom-right corner. + +*/ +void vpRect::set(const vpImagePoint &topLeft, const vpImagePoint &bottomRight) +{ + this->left = topLeft.get_u(); + this->top = topLeft.get_v(); + + setBottom( bottomRight.get_v() ); + setRight( bottomRight.get_u() ); +}; + +/*! + + Set the rectangle from \e r. + +*/ +void vpRect::set(const vpRect& r) +{ + *this = r; +}; + +/*! + + Check if an image point belongs to a rectangle. + + \param ip : the image point. + \param rect : the rectangle. + + \return Returns true if the point belongs to the rectangle. + +*/ +VISP_EXPORT bool inRectangle( const vpImagePoint &ip, const vpRect &rect ) { + return ( ip.get_i() <= rect.getBottom() && ip.get_i() >= rect.getTop() && ip.get_j() <= rect.getRight() && ip.get_j() >= rect.getLeft()); +} + +VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpRect& r) + { + os << r.getLeft() << ", " << r.getTop() << ", " << r.getWidth() << ", " << r.getHeight(); + return os; +} diff --git a/src/tools/geometry/vpRect.h b/src/tools/geometry/vpRect.h index dc88eb1e5855c688bef3f3de6f3956e7a277e0c1..9ae88e5687c9d4c3469d101ea526c8b1491b0579 100755 --- a/src/tools/geometry/vpRect.h +++ b/src/tools/geometry/vpRect.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpRect.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpRect.h 5009 2014-11-25 18:00:13Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,8 +77,11 @@ */ +#include <vector> +#include <visp/vpException.h> #include <visp/vpImagePoint.h> + class VISP_EXPORT vpRect { public: @@ -88,6 +91,8 @@ public: vpRect(const vpImagePoint &topLeft, double width, double height); vpRect(const vpImagePoint &topLeft, const vpImagePoint &bottomRight); vpRect(const vpRect& r); + vpRect(const std::vector<vpImagePoint> &ip); + vpRect &operator=(const vpRect& r); @@ -161,6 +166,12 @@ public: */ inline double getRight() const { return (this->left + this->width - 1.0); }; + /*! + Returns the size of the rectangle. + \sa getWidth(), getHeight() + */ + inline double getSize() const { return (this->width * this->height); }; + /*! Returns the top coordinate of the rectangle. @@ -187,6 +198,14 @@ public: */ inline double getWidth() const { return this->width; }; + friend VISP_EXPORT bool inRectangle( const vpImagePoint &ip, const vpRect &rect ); + friend VISP_EXPORT std::ostream& operator<< (std::ostream &os, const vpRect& r); + void set(double left, double top, double width, double height); + void set(const vpImagePoint &topLeft, double width, double height); + void set(const vpImagePoint &topLeft, const vpImagePoint &bottomRight); + void set(const vpRect& r); + void set(const std::vector<vpImagePoint> &ip); + /*! Sets the bottom edge position of the rectangle to pos. May change @@ -230,12 +249,15 @@ public: Sets the coordinates of the rectangle's top left corner to (left, top), and its size to (width, height). + \param l,t : (left, top) corner position. + \param w,h : (width, height) rectangle size. + */ - inline void setRect(double left, double top, double width, double height) { - this->left = left; - this->top = top; - this->width = width; - this->height = height; + inline void setRect(double l, double t, double w, double h) { + this->left = l; + this->top = t; + this->width = w; + this->height = h; }; /*! @@ -290,6 +312,19 @@ public: this->top = y - this->height/2 + 0.5; }; + /*! + + Center the rectangle to the image point given as parameter, leaving + the size unchanged. + + \sa getCenter() + + */ + inline void moveCenter(const vpImagePoint ¢er) { + this->left = center.get_u() - this->width/2 + 0.5; + this->top = center.get_v() - this->height/2 + 0.5; + }; + private: double left; // Upper left corner position along the columns axis double top; // Upper left corner position along the rows axis @@ -297,20 +332,4 @@ private: double height; // Rectangle height }; - -/*! - - Check if an image point belongs to a rectangle. - - \param ip : the image point. - \param rect : the rectangle. - - \return Returns true if the point belongs to the rectangle. - -*/ -VISP_EXPORT inline bool inRectangle( const vpImagePoint &ip, const vpRect &rect ) { - return ( ip.get_i() <= rect.getBottom() && ip.get_i() >= rect.getTop() && ip.get_j() <= rect.getRight() && ip.get_j() >= rect.getLeft()); -} - - #endif diff --git a/src/tools/geometry/vpTriangle.cpp b/src/tools/geometry/vpTriangle.cpp index 848eb59a42874a56cda4252eee93de8054452e77..891037145b9fccd86fedb9966390f7f4105ce0c6 100644 --- a/src/tools/geometry/vpTriangle.cpp +++ b/src/tools/geometry/vpTriangle.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTriangle.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTriangle.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,8 +49,9 @@ By default, the three 2D points coordinates which define the triangle are \f$ (0,0) \f$, \f$ (1,0) \f$ and \f$ (0,1) \f$. */ vpTriangle::vpTriangle() + : goodTriange(true), S1(), uvinv00(0), uvinv01(0), uvinv10(0), uvinv11(0), + ptempo0(0), ptempo1(0), area(0), apex1(), apex2(), apex3() { - goodTriange = true; init (vpImagePoint(0,0),vpImagePoint(1,0),vpImagePoint(0,1)); } @@ -62,9 +63,9 @@ vpTriangle::vpTriangle() \param iP3 : The first apex of the triangle. */ vpTriangle::vpTriangle(const vpImagePoint &iP1, const vpImagePoint &iP2, const vpImagePoint &iP3) + : goodTriange(true), S1(), uvinv00(0), uvinv01(0), uvinv10(0), uvinv11(0), + ptempo0(0), ptempo1(0), area(0), apex1(), apex2(), apex3() { - goodTriange = true; - init(iP1,iP2,iP3); } @@ -74,16 +75,10 @@ vpTriangle::vpTriangle(const vpImagePoint &iP1, const vpImagePoint &iP2, const v \param tri : The triangle used for the initialisation. */ vpTriangle::vpTriangle(const vpTriangle &tri) + : goodTriange(true), S1(), uvinv00(0), uvinv01(0), uvinv10(0), uvinv11(0), + ptempo0(0), ptempo1(0), area(0), apex1(), apex2(), apex3() { - goodTriange = tri.goodTriange; - S1 = tri.S1; - uvinv00 = tri.uvinv00; - uvinv01 = tri.uvinv01; - uvinv10 = tri.uvinv10; - uvinv11 = tri.uvinv11; - ptempo0 = tri.ptempo0; - ptempo1 = tri.ptempo1; - area = tri.area; + *this = tri; } /*! @@ -128,6 +123,7 @@ vpTriangle::buildFrom(const vpImagePoint &iP1, const vpImagePoint &iP2, const vp void vpTriangle::init(const vpImagePoint &iP1, const vpImagePoint &iP2, const vpImagePoint &iP3) { + ptempo0 = ptempo1 = 0.; apex1 = iP1; apex2 = iP2; apex3 = iP3; diff --git a/src/tools/geometry/vpTriangle.h b/src/tools/geometry/vpTriangle.h index e7cd0a0dba4655318f88c303dab34d0b69eccfd4..98f1aa9d16862e71298aab6945882e2276379476 100644 --- a/src/tools/geometry/vpTriangle.h +++ b/src/tools/geometry/vpTriangle.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTriangle.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTriangle.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tools/histogram/vpHistogram.cpp b/src/tools/histogram/vpHistogram.cpp index 544c779e3def43a988964a16dfa0f288ae448a5a..b45b37ac28ffabb6dd1908527d17a6d1894ab357 100644 --- a/src/tools/histogram/vpHistogram.cpp +++ b/src/tools/histogram/vpHistogram.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogram.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpHistogram.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,6 +51,8 @@ #include <visp/vpHistogram.h> #include <stdlib.h> +bool compare_vpHistogramPeak (vpHistogramPeak first, vpHistogramPeak second); + // comparison, bool compare_vpHistogramPeak (vpHistogramPeak first, vpHistogramPeak second) { @@ -63,20 +65,16 @@ bool compare_vpHistogramPeak (vpHistogramPeak first, vpHistogramPeak second) /*! Defaut constructor for a gray level histogram. */ -vpHistogram::vpHistogram() +vpHistogram::vpHistogram() : histogram(NULL), size(256) { - histogram = NULL; - size = 256; init(); } /*! Copy constructor of a gray level histogram. */ -vpHistogram::vpHistogram(const vpHistogram &h) +vpHistogram::vpHistogram(const vpHistogram &h) : histogram(NULL), size(256) { - // vpTRACE("copy"); - histogram = NULL; init(h.size); memcpy(histogram, h.histogram, size * sizeof(unsigned)); } @@ -89,9 +87,8 @@ vpHistogram::vpHistogram(const vpHistogram &h) \sa calculate() */ vpHistogram::vpHistogram(const vpImage<unsigned char> &I) + : histogram(NULL), size(256) { - histogram = NULL; - size = 256; init(); calculate(I); @@ -138,14 +135,14 @@ vpHistogram::operator=(const vpHistogram &h) - Initialise all the values to zero. */ void -vpHistogram::init(unsigned size) +vpHistogram::init(unsigned size_) { if (histogram != NULL) { delete [] histogram; histogram = NULL; } - this->size = size; + this->size = size_; histogram = new unsigned [size]; memset(histogram, 0, size * sizeof(unsigned)); diff --git a/src/tools/histogram/vpHistogram.h b/src/tools/histogram/vpHistogram.h index 68076c3fae097804d58102769ddfd23580d55c66..db90687a64b9468b4b5795fc7a49cc368af3d89b 100644 --- a/src/tools/histogram/vpHistogram.h +++ b/src/tools/histogram/vpHistogram.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogram.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHistogram.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tools/histogram/vpHistogramPeak.cpp b/src/tools/histogram/vpHistogramPeak.cpp index 6ff741759f3a160d7dcabafad6a44ec33ea38c31..e7d22268ed8432864fa35756681e42ce261988ee 100644 --- a/src/tools/histogram/vpHistogramPeak.cpp +++ b/src/tools/histogram/vpHistogramPeak.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogramPeak.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHistogramPeak.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,27 +52,21 @@ /*! Defaut constructor for a gray level histogram peak. */ -vpHistogramPeak::vpHistogramPeak() -{ - level = 0; - value = 0; -} +vpHistogramPeak::vpHistogramPeak() : level(0), value(0) {} /*! Defaut constructor for a gray level histogram peak. */ -vpHistogramPeak::vpHistogramPeak(unsigned char level, unsigned value) -{ - setLevel(level); - setValue(value); -} +vpHistogramPeak::vpHistogramPeak(unsigned char lvl, unsigned val) + : level(lvl), value(val) {} + /*! Copy constructor of a gray level histogram peak. */ vpHistogramPeak::vpHistogramPeak(const vpHistogramPeak &p) + : level(0), value(0) { - setLevel(p.level); - setValue(p.value); + *this = p; } /*! @@ -110,7 +104,7 @@ vpHistogramPeak::operator==(const vpHistogramPeak &p) const /*! \brief std::cout a peak */ -std::ostream &operator <<(std::ostream &s,const vpHistogramPeak &p) +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpHistogramPeak &p) { s << (int)p.getLevel() << " " << p.getValue(); diff --git a/src/tools/histogram/vpHistogramPeak.h b/src/tools/histogram/vpHistogramPeak.h index 3ba1371fc46f5e67571a2ed7df39094e699ba7b4..3b1e0ea34b74d33c75b52e00431a5f889099dd07 100644 --- a/src/tools/histogram/vpHistogramPeak.h +++ b/src/tools/histogram/vpHistogramPeak.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogramPeak.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHistogramPeak.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,6 +74,9 @@ public : vpHistogramPeak(unsigned char level, unsigned value); vpHistogramPeak(const vpHistogramPeak & p); + /*! Destructor that does nothing. */ + virtual ~vpHistogramPeak() {} + vpHistogramPeak & operator=(const vpHistogramPeak &p); bool operator==(const vpHistogramPeak &p) const; @@ -82,43 +85,43 @@ public : Set the peak gray \e level. To set the number of pixels having this gray level use setValue(). - \param level : Location of the peak or gray \e level. + \param lvl : Location of the peak or gray \e level. \sa setValue(), set() */ - inline void setLevel(unsigned char level) + inline void setLevel(unsigned char lvl) { - this->level = level; + this->level = lvl; }; /*! Set the peak number of pixels having a same gray level. To set the gray level of this peak use setLevel(). - \param value : Number of pixels having the same location or gray level. + \param val : Number of pixels having the same location or gray level. \sa setLevel(), set() */ - inline void setValue(unsigned value) + inline void setValue(unsigned val) { - this->value = value; + this->value = val; }; /*! Set the peak gray \e level and number of pixels at this gray level. - \param level : Location of the peak or gray level. - \param value : Number of pixels having the same location or gray level. + \param lvl : Location of the peak or gray level. + \param val : Number of pixels having the same location or gray level. \sa setLevel(), setValue() */ - inline void set(unsigned char level, unsigned value) + inline void set(unsigned char lvl, unsigned val) { - this->level = level; - this->value = value; + this->level = lvl; + this->value = val; }; /*! @@ -153,8 +156,7 @@ public : //--------------------------------- // Printing //--------------------------------- - friend VISP_EXPORT std::ostream &operator << (std::ostream &s, - const vpHistogramPeak &p); + friend VISP_EXPORT std::ostream &operator << (std::ostream &s, const vpHistogramPeak &p); protected : diff --git a/src/tools/histogram/vpHistogramValey.cpp b/src/tools/histogram/vpHistogramValey.cpp index bbf55627c754e7cd721468ece1d5d2bd31b9375a..cfffac76fd7a6d8e0ff09ed0cdadd5e02174a934 100644 --- a/src/tools/histogram/vpHistogramValey.cpp +++ b/src/tools/histogram/vpHistogramValey.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogramValey.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHistogramValey.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -83,7 +83,7 @@ vpHistogramValey::operator==(const vpHistogramValey &v) const /*! \brief std::cout a valey */ -std::ostream &operator <<(std::ostream &s,const vpHistogramValey &v) +VISP_EXPORT std::ostream &operator <<(std::ostream &s,const vpHistogramValey &v) { s << (int)v.getLevel() << " " << v.getValue(); diff --git a/src/tools/histogram/vpHistogramValey.h b/src/tools/histogram/vpHistogramValey.h index 5c83b21a4580ac381354a0f415aa2070b0956a84..2c950d12a9ccef43840e0b04f46a54df7339950e 100644 --- a/src/tools/histogram/vpHistogramValey.h +++ b/src/tools/histogram/vpHistogramValey.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpHistogramValey.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpHistogramValey.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,12 +71,15 @@ class VISP_EXPORT vpHistogramValey : vpHistogramPeak vpHistogramValey() : vpHistogramPeak() {}; - vpHistogramValey(unsigned char level, unsigned value) : - vpHistogramPeak(level, value) {}; + vpHistogramValey(unsigned char lvl, unsigned val) : + vpHistogramPeak(lvl, val) {}; vpHistogramValey(const vpHistogramValey & v) : vpHistogramPeak(v) {}; + /*! Destructor that does nothing. */ + virtual ~vpHistogramValey() {} + vpHistogramValey & operator=(const vpHistogramValey &v); bool operator==(const vpHistogramValey &v) const; @@ -85,43 +88,43 @@ class VISP_EXPORT vpHistogramValey : vpHistogramPeak Set the valey gray \e level. To set the number of pixels having this gray level use setValue(). - \param level : Location of the valey or gray level. + \param lvl : Location of the valey or gray level. \sa setValue(), set() */ - inline void setLevel(unsigned char level) + inline void setLevel(unsigned char lvl) { - this->level = level; + this->level = lvl; }; /*! Set the valey number of pixels having a same gray level. To set the gray level of this valey use setLevel(). - \param value : Number of pixels having the same location or gray level. + \param val : Number of pixels having the same location or gray level. \sa setPosition(), set() */ - inline void setValue(unsigned value) + inline void setValue(unsigned val) { - this->value = value; + this->value = val; }; /*! Set the valey gray \e level and number of pixels at this location. - \param level : Location of the valey or gray level. - \param value : Number of pixels having the same location or gray level. + \param lvl : Location of the valey or gray level. + \param val : Number of pixels having the same location or gray level. \sa setLevel(), setValue() */ - inline void set(unsigned char level, unsigned value) + inline void set(unsigned char lvl, unsigned val) { - this->level = level; - this->value = value; + this->level = lvl; + this->value = val; }; /*! @@ -143,7 +146,7 @@ class VISP_EXPORT vpHistogramValey : vpHistogramPeak Get the valey number of pixels having a same gray level. The corresponding gray level is available through getLevel(). - \return : Number of pixels having the same location or gray level. + \return Number of pixels having the same location or gray level. \sa getLevel() @@ -156,8 +159,7 @@ class VISP_EXPORT vpHistogramValey : vpHistogramPeak //--------------------------------- // Printing //--------------------------------- - friend VISP_EXPORT std::ostream &operator << (std::ostream &s, - const vpHistogramValey &v); + friend VISP_EXPORT std::ostream &operator << (std::ostream &s, const vpHistogramValey &v); }; /* diff --git a/src/tools/io/vpIoException.h b/src/tools/io/vpIoException.h index a2aa32f000e92d8f7cbfb9a06795e793e97e5fb7..2c03ed785776ee529d843de0d000c310537dcb4c 100644 --- a/src/tools/io/vpIoException.h +++ b/src/tools/io/vpIoException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpIoException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpIoException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,12 +71,12 @@ */ class VISP_EXPORT vpIoException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpIo member. */ - enum error + enum error { invalidDirectoryName, /*! Directory name is invalid. */ cantCreateDirectory, /*! Unable to create a directory. */ @@ -84,25 +84,20 @@ public: cantGetenv /*! Cannot get environment variable value. */ } ; -public: - vpIoException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpIoException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpIoException (const int code) - : vpException(code){ ; } + public: + vpIoException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpIoException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpIoException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpIoException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/tools/io/vpIoTools.cpp b/src/tools/io/vpIoTools.cpp index d3b154d41be8d70d3e3c631bc5612a6419b02fd2..ae85396e9684da12656efde4f65c03d84fe98c8c 100644 --- a/src/tools/io/vpIoTools.cpp +++ b/src/tools/io/vpIoTools.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpIoTools.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpIoTools.cpp 5214 2015-01-27 18:33:01Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,13 +55,14 @@ #include <fcntl.h> #include <limits> #include <cmath> -#if defined UNIX +#include <algorithm> +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> -#elif defined WIN32 +#elif defined(_WIN32) # include <windows.h> # include <direct.h> #endif -#ifndef WIN32 +#if !defined(_WIN32) # include <wordexp.h> #endif @@ -92,8 +93,8 @@ std::vector<std::string> vpIoTools::configValues = std::vector<std::string>(); void vpIoTools::getUserName(std::string &username) { - // With MinGW, UNIX and WIN32 are defined -#if defined UNIX + // With MinGW, UNIX and _WIN32 are defined +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX // Get the user name. char *_username = NULL; _username = ::getenv("LOGNAME"); @@ -103,7 +104,7 @@ vpIoTools::getUserName(std::string &username) "Cannot get the username")) ; } username = _username; -#elif defined WIN32 +#elif defined(_WIN32) unsigned int info_buffer_size = 1024; TCHAR *infoBuf = new TCHAR [info_buffer_size]; DWORD bufCharCount = (DWORD) info_buffer_size; @@ -140,7 +141,7 @@ std::string vpIoTools::getUserName() { std::string username; -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX // Get the user name. char *_username = NULL; _username = ::getenv("LOGNAME"); @@ -150,7 +151,7 @@ vpIoTools::getUserName() "Cannot get the username")) ; } username = _username; -#elif defined WIN32 +#elif defined(_WIN32) unsigned int info_buffer_size = 1024; TCHAR *infoBuf = new TCHAR [info_buffer_size]; DWORD bufCharCount = (DWORD) info_buffer_size; @@ -171,16 +172,11 @@ vpIoTools::getUserName() /*! Get the content of an environment variable. - \warning Under windows, this function is not implemented yet. - \param env : Environment variable name (HOME, LOGNAME...). - \return Value of the environment variable - - \exception vpException::notImplementedError : If this method is - called under Windows. + \return Value of the environment variable. \exception vpIoException::cantGetenv : If an error occur while - getting the environement variable value. + getting the environment variable value. \code #include <iostream> @@ -194,8 +190,8 @@ int main() envvalue = vpIoTools::getenv("HOME"); std::cout << "$HOME = \"" << envvalue << "\"" << std::endl; } - catch (...) { - std::cout << "Cannot get the environment variable value" << std::endl; + catch (vpException &e) { + std::cout << e.getMessage() << std::endl; return -1; } return 0; @@ -205,14 +201,9 @@ int main() \sa getenv(std::string &) */ std::string -vpIoTools::getenv(const char * -#if defined UNIX - env -#endif - ) +vpIoTools::getenv(const char *env) { std::string value; -#if defined UNIX // Get the environment variable value. char *_value = NULL; _value = ::getenv(env); @@ -224,27 +215,16 @@ vpIoTools::getenv(const char * value = _value; return value; -#elif defined WIN32 - - vpERROR_TRACE( "Not implemented!" ); - throw(vpIoException(vpException::notImplementedError, - "Not implemented!")) ; -#endif } /*! Get the content of an environment variable. - \warning Under windows, this function is not implemented yet. - \param env : Environment variable name (HOME, LOGNAME...). \return Value of the environment variable - \exception vpException::notImplementedError : If this method is - called under Windows. - \exception vpIoException::cantGetenv : If an error occur while - getting the environement variable value. + getting the environment variable value. \code #include <iostream> @@ -259,8 +239,8 @@ int main() envvalue = vpIoTools::getenv(env); std::cout << "$HOME = \"" << envvalue << "\"" << std::endl; } - catch (...) { - std::cout << "Cannot get the environment variable value" << std::endl; + catch (vpException &e) { + std::cout << e.getMessage() << std::endl; return -1; } return 0; @@ -270,7 +250,7 @@ int main() \sa getenv(const char *) */ std::string -vpIoTools::getenv(std::string &env) +vpIoTools::getenv(const std::string &env) { return (vpIoTools::getenv(env.c_str())); } @@ -334,9 +314,9 @@ vpIoTools::getVersion(const std::string &version, unsigned int &major, unsigned bool vpIoTools::checkDirectory(const char *dirname ) { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX struct stat stbuf; -#elif defined WIN32 +#elif defined(_WIN32) struct _stat stbuf; #endif @@ -346,9 +326,9 @@ vpIoTools::checkDirectory(const char *dirname ) std::string _dirname = path(dirname); -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( stat( _dirname.c_str(), &stbuf ) != 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( _stat( _dirname.c_str(), &stbuf ) != 0 ) #endif { @@ -357,9 +337,9 @@ vpIoTools::checkDirectory(const char *dirname ) if ( (stbuf.st_mode & S_IFDIR) == 0 ) { return false; } -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( (stbuf.st_mode & S_IWUSR) == 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( (stbuf.st_mode & S_IWRITE) == 0 ) #endif { @@ -403,9 +383,9 @@ vpIoTools::checkDirectory(const std::string &dirname ) void vpIoTools::makeDirectory(const char *dirname ) { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX struct stat stbuf; -#elif defined WIN32 +#elif defined(_WIN32) struct _stat stbuf; #endif @@ -417,15 +397,15 @@ vpIoTools::makeDirectory(const char *dirname ) std::string _dirname = path(dirname); -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( stat( _dirname.c_str(), &stbuf ) != 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( _stat( _dirname.c_str(), &stbuf ) != 0 ) #endif { -#if ( defined(UNIX) && !defined(WIN32) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) if ( mkdir( _dirname.c_str(), (mode_t)0755 ) != 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( _mkdir( _dirname.c_str()) != 0 ) #endif { @@ -484,9 +464,9 @@ vpIoTools::makeDirectory(const std::string &dirname ) bool vpIoTools::checkFilename(const char *filename) { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX struct stat stbuf; -#elif defined WIN32 +#elif defined(_WIN32) struct _stat stbuf; #endif @@ -495,9 +475,9 @@ vpIoTools::checkFilename(const char *filename) } std::string _filename = path(filename); -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( stat( _filename.c_str(), &stbuf ) != 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( _stat( _filename.c_str(), &stbuf ) != 0 ) #endif { @@ -506,9 +486,9 @@ vpIoTools::checkFilename(const char *filename) if ( (stbuf.st_mode & S_IFREG) == 0 ) { return false; } -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if ( (stbuf.st_mode & S_IRUSR) == 0 ) -#elif defined WIN32 +#elif defined(_WIN32) if ( (stbuf.st_mode & S_IREAD) == 0 ) #endif { @@ -555,9 +535,9 @@ vpIoTools::copy(const char *src, const char *dst) // Check if we have to consider a file or a directory if ( vpIoTools::checkFilename(src) ) { //std::cout << "copy file: " << src << " in " << dst << std::endl; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX sprintf(cmd, "cp -p %s %s", src, dst); -#elif WIN32 +#elif defined(_WIN32) std::string src_ = vpIoTools::path(src); std::string dst_ = vpIoTools::path(dst); sprintf(cmd, "copy %s %s", src_.c_str(), dst_.c_str()); @@ -568,9 +548,9 @@ vpIoTools::copy(const char *src, const char *dst) } else if ( vpIoTools::checkDirectory(src) ) { //std::cout << "copy directory: " << src << " in " << dst << std::endl; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX sprintf(cmd, "cp -p -r %s %s", src, dst); -#elif WIN32 +#elif defined(_WIN32) std::string src_ = vpIoTools::path(src); std::string dst_ = vpIoTools::path(dst); sprintf(cmd, "copy %s %s", src_.c_str(), dst_.c_str()); @@ -627,9 +607,9 @@ vpIoTools::remove(const char *file_or_dir) else if ( vpIoTools::checkDirectory(file_or_dir) ) { //std::cout << "remove directory: " << file_or_dir << std::endl; char cmd[FILENAME_MAX]; -#ifdef UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX sprintf(cmd, "rm -rf %s", file_or_dir); -#elif WIN32 +#elif defined(_WIN32) std::string file_or_dir_ = vpIoTools::path(file_or_dir); sprintf(cmd, "rmdir /S /Q %s", file_or_dir_.c_str()); #endif @@ -716,7 +696,7 @@ vpIoTools::path(const char *pathname) { std::string path(pathname); -#ifdef WIN32 +#if defined(_WIN32) for(unsigned int i=0 ; i<path.length() ; i++) if( path[i] == '/') path[i] = '\\'; #else @@ -1076,3 +1056,352 @@ void vpIoTools::saveConfigFile(const bool &actuallySave) vpIoTools::copy(configFile, dest); } } + +/*! + Get ViSP images data path. ViSP images data can be installed from Debian or Ubuntu \e visp-images-data package. + It can be also installed from ViSP-images.zip that can be found on http://team.inria.fr/lagadic/visp/download.html page. + + This function returns the path to the folder that contains the data. + - It checks first if \e visp-images-data package is installed. In that case returns then \e /usr/share/visp-images-data". + - Then it checks if VISP_INPUT_IMAGE_PATH environment variable that gives the location of the data is set. In that + case returns the content of this environment var. + + If the path is not found, returns an empty string. + */ +std::string vpIoTools::getViSPImagesDataPath() +{ + std::string data_path; + std::string file_to_test("ViSP-images/mbt/cube.cao"); + std::string filename; +#ifdef UNIX + // Test if visp-images-data package is u-installed (Ubuntu and Debian) + data_path = "/usr/share/visp-images-data"; + filename = data_path + "/" + file_to_test; + if (vpIoTools::checkFilename(filename)) + return data_path; +#endif + // Test if VISP_INPUT_IMAGE_PATH env var is set + try { + data_path = vpIoTools::getenv("VISP_INPUT_IMAGE_PATH"); + filename = data_path + "/" + file_to_test; + if (vpIoTools::checkFilename(filename)) + return data_path; + } + catch(...) { + } + data_path = ""; + return data_path; +} + +/*! + Returns the extension of the file or an empty string if the file has no extension. If checkFile flag is set, + it will check first if the pathname denotes a directory and so return an empty string and second it will check + if the file denoted by the pathanme exists. If so, it will return the extension if present. + \param pathname : The pathname of the file we want to get the extension. + \param checkFile : If true, the file must exist otherwise an empty string will be returned. + \return The extension of the file or an empty string if the file has no extension. + or if the pathname is empty. + */ +std::string vpIoTools::getFileExtension(const std::string& pathname, const bool checkFile) +{ + if(checkFile && (vpIoTools::checkDirectory(pathname) || !vpIoTools::checkFilename(pathname))) { + return ""; + } + + //On Unix, or on the Mac + std::string sep = "/"; + std::string altsep = ""; + std::string extsep = "."; + +#if defined(_WIN32) + sep = "\\"; + altsep = "/"; + extsep = "."; +#endif + + //Python 2.7.8 module. +//# Split a path in root and extension. +//# The extension is everything starting at the last dot in the last +//# pathname component; the root is everything before that. +//# It is always true that root + ext == p. +// +//# Generic implementation of splitext, to be parametrized with +//# the separators +//def _splitext(p, sep, altsep, extsep): +// """Split the extension from a pathname. +// +// Extension is everything from the last dot to the end, ignoring +// leading dots. Returns "(root, ext)"; ext may be empty.""" +// +// sepIndex = p.rfind(sep) +// if altsep: +// altsepIndex = p.rfind(altsep) +// sepIndex = max(sepIndex, altsepIndex) +// +// dotIndex = p.rfind(extsep) +// if dotIndex > sepIndex: +// # skip all leading dots +// filenameIndex = sepIndex + 1 +// while filenameIndex < dotIndex: +// if p[filenameIndex] != extsep: +// return p[:dotIndex], p[dotIndex:] +// filenameIndex += 1 +// +// return p, '' + + int sepIndex = (int)pathname.rfind(sep); + if(!altsep.empty()) { + int altsepIndex = (int)pathname.rfind(altsep); + sepIndex = (std::max)(sepIndex, altsepIndex); + } + + size_t dotIndex = pathname.rfind(extsep); + if(dotIndex != std::string::npos) { + //The extsep character exists + if((sepIndex != (int)std::string::npos && (int)dotIndex > sepIndex) || sepIndex == (int)std::string::npos) { + if(sepIndex == (int)std::string::npos) { + sepIndex = -1; + } + size_t filenameIndex = (size_t)(sepIndex + 1); + + while(filenameIndex < dotIndex) { + if(pathname.compare(filenameIndex, 1, extsep) != 0) { + return pathname.substr(dotIndex); + } + filenameIndex++; + } + } + } + + + return ""; +} + +/*! + Returns the name of the file or directory denoted by this pathname. + \return The name of the file or directory denoted by this pathname, or an + empty string if this pathname's name sequence is empty. + */ +std::string vpIoTools::getName(const std::string& pathname) +{ + if(pathname.size() > 0) + { + std::string convertedPathname = vpIoTools::path(pathname); + + size_t index = convertedPathname.find_last_of(vpIoTools::separator); + if(index != std::string::npos) { + return convertedPathname.substr(index + 1); + } + + return convertedPathname; + } + + return ""; +} + +/*! + Returns the name of the file without extension or directory denoted by this pathname. + \return The name of the file without extension or directory denoted by this pathname, or an + empty string if this pathname's name sequence is empty. + */ +std::string vpIoTools::getNameWE(const std::string& pathname) +{ + std::string name = vpIoTools::getName(pathname); + size_t found = name.find_last_of("."); + std::string name_we = name.substr(0, found); + return name_we; +} + +/*! + Returns the pathname string of this pathname's parent. + \return The pathname string of this pathname's parent, or + an empty string if this pathname does not name a parent directory. + */ +std::string vpIoTools::getParent(const std::string& pathname) +{ + if(pathname.size() > 0) + { + std::string convertedPathname = vpIoTools::path(pathname); + + size_t index = convertedPathname.find_last_of(vpIoTools::separator); + if(index != std::string::npos) { + return convertedPathname.substr(0, index); + } + } + + return ""; +} + +/*! + Return the file path that corresponds to the concatenated + \e parent and \e child string files + by adding the corresponding separator for unix or windows. + + The corresponding path is also converted. Under + windows, all the "/" characters are converted + into "\\" characters. Under Unix systems all the "\\" + characters are converted into "/" characters. + */ +std::string vpIoTools::createFilePath(const std::string& parent, const std::string child) +{ + if(child.size() == 0 && parent.size() == 0) + { + return ""; + } + + if(child.size() == 0) + { + return vpIoTools::path(parent); + } + + if(parent.size() == 0) + { + return vpIoTools::path(child); + } + + std::string convertedParent = vpIoTools::path(parent); + std::string convertedChild = vpIoTools::path(child); + + std::stringstream ss; + ss << vpIoTools::separator; + std::string stringSeparator; + ss >> stringSeparator; + + std::string lastConvertedParentChar = convertedParent.substr(convertedParent.size() - 1); + std::string firstConvertedChildChar = convertedChild.substr(0, 1); + + if(lastConvertedParentChar == stringSeparator) + { + convertedParent = convertedParent.substr(0, convertedParent.size() - 1); + } + + if(firstConvertedChildChar == stringSeparator) + { + convertedChild = convertedChild.substr(1); + } + + return std::string(convertedParent + vpIoTools::separator + convertedChild); +} + +/*! + Return whether a path is absolute. + + \return true if the pathname is absolute, false otherwise. + */ +bool vpIoTools::isAbsolutePathname(const std::string& pathname) +{ + //# Inspired by the Python 2.7.8 module. + //# Return whether a path is absolute. + //# Trivial in Posix, harder on the Mac or MS-DOS. + //# For DOS it is absolute if it starts with a slash or backslash (current + //# volume), or if a pathname after the volume letter and colon / UNC resource + //# starts with a slash or backslash. + // + //def isabs(s): + // """Test whether a path is absolute""" + // s = splitdrive(s)[1] + // return s != '' and s[:1] in '/\\' + std::string path = splitDrive(pathname).second; + return path.size() > 0 && (path.substr(0, 1) == "/" || path.substr(0, 1) == "\\"); +} + +/*! + Split a path in a drive specification (a drive letter followed by a colon) and the path specification. + It is always true that drivespec + pathspec == p + Inspired by the Python 2.7.8 module. + \return a pair whose the first element is the drive specification and the second element + the path specification + */ +std::pair<std::string, std::string> vpIoTools::splitDrive(const std::string& pathname) +{ +//# Split a path in a drive specification (a drive letter followed by a +//# colon) and the path specification. +//# It is always true that drivespec + pathspec == p +//def splitdrive(p): +// """Split a pathname into drive/UNC sharepoint and relative path specifiers. +// Returns a 2-tuple (drive_or_unc, path); either part may be empty. +// +// If you assign +// result = splitdrive(p) +// It is always true that: +// result[0] + result[1] == p +// +// If the path contained a drive letter, drive_or_unc will contain everything +// up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir") +// +// If the path contained a UNC path, the drive_or_unc will contain the host name +// and share up to but not including the fourth directory separator character. +// e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") +// +// Paths cannot contain both a drive letter and a UNC path. +// +// """ +// if len(p) > 1: +// normp = p.replace(altsep, sep) +// if (normp[0:2] == sep*2) and (normp[2] != sep): +// # is a UNC path: +// # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path +// # \\machine\mountpoint\directory\etc\... +// # directory ^^^^^^^^^^^^^^^ +// index = normp.find(sep, 2) +// if index == -1: +// return '', p +// index2 = normp.find(sep, index + 1) +// # a UNC path can't have two slashes in a row +// # (after the initial two) +// if index2 == index + 1: +// return '', p +// if index2 == -1: +// index2 = len(p) +// return p[:index2], p[index2:] +// if normp[1] == ':': +// return p[:2], p[2:] +// return '', p + + + //On Unix, the drive is always empty. + //On the Mac, the drive is always empty (don't use the volume name -- it doesn't have the same + //syntactic and semantic oddities as DOS drive letters, such as there being a separate current directory per drive). +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) + return std::pair<std::string, std::string>("", pathname); +#else + const std::string sep = "\\"; + const std::string sepsep = "\\\\"; + const std::string altsep = "/"; + + if(pathname.size() > 1) { + std::string normPathname = pathname; + std::replace(normPathname.begin(), normPathname.end(), *altsep.c_str(), *sep.c_str()); + + if(normPathname.substr(0, 2) == sepsep && normPathname.substr(2, 1) != sep) { + // is a UNC path: + // vvvvvvvvvvvvvvvvvvvv drive letter or UNC path + // \\machine\mountpoint\directory\etc\... + // directory ^^^^^^^^^^^^^^^ + size_t index = normPathname.find(sep, 2); + if(index == std::string::npos) { + return std::pair<std::string, std::string>("", pathname); + } + + size_t index2 = normPathname.find(sep, index + 1); + //# a UNC path can't have two slashes in a row + //# (after the initial two) + if(index2 == index + 1) { + return std::pair<std::string, std::string>("", pathname); + } + + if(index2 == std::string::npos) { + index2 = pathname.size(); + } + + return std::pair<std::string, std::string>(pathname.substr(0, index2), pathname.substr(index2)); + } + + if(normPathname[1] == ':') { + return std::pair<std::string, std::string>(pathname.substr(0, 2), pathname.substr(2)); + } + } + + return std::pair<std::string, std::string>("", pathname); +#endif +} diff --git a/src/tools/io/vpIoTools.h b/src/tools/io/vpIoTools.h index 1a3142f396fed23d8df6ede30d0132c651fb885c..7d7ef707538410f390777d849c8918d56b27f7e8 100644 --- a/src/tools/io/vpIoTools.h +++ b/src/tools/io/vpIoTools.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpIoTools.h 4275 2013-06-25 12:35:42Z fspindle $ + * $Id: vpIoTools.h 5197 2015-01-23 17:24:22Z strinh $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -159,8 +159,9 @@ public: static void getUserName(std::string &username); static std::string getUserName(); static std::string getenv(const char *env); - static std::string getenv(std::string &env); - static void getVersion(const std::string &version, unsigned int &major, unsigned int &minor, unsigned int &patch); + static std::string getenv(const std::string &env); + static std::string getViSPImagesDataPath(); + static void getVersion(const std::string &version, unsigned int &major, unsigned int &minor, unsigned int &patch); static bool checkDirectory(const char *dirname); static bool checkDirectory(const std::string &dirname); static bool checkFilename(const char *filename); @@ -172,12 +173,29 @@ public: static bool remove(const char *filename); static bool remove(const std::string &filename); static bool rename(const char *oldfilename, const char *newfilename); - static bool rename(const std::string &oldfilename, - const std::string &newfilename); + static bool rename(const std::string &oldfilename, const std::string &newfilename); static std::string path(const char * pathname); static std::string path(const std::string &pathname); + /*! + Define the directory separator character, backslash ('\') for windows platform or slash ('/') otherwise. + */ + static const char separator = + #if defined(_WIN32) + '\\'; + #else + '/'; + #endif + + static std::string getFileExtension(const std::string &pathname, const bool checkFile=false); + static std::string getName(const std::string &pathname); + static std::string getNameWE(const std::string &pathname); + static std::string getParent(const std::string& pathname); + static std::string createFilePath(const std::string& parent, const std::string child); + static bool isAbsolutePathname(const std::string& pathname); + static std::pair<std::string, std::string> splitDrive(const std::string& pathname); + // read configuration file static bool loadConfigFile(const std::string &confFile); static bool readConfigVar(const std::string &var, float &value); @@ -224,7 +242,7 @@ public: // write files static void saveConfigFile(const bool &actuallySave = true); static void createBaseNamePath(const bool &empty = false); - + protected: static std::string baseName; static std::string baseDir; diff --git a/src/tools/io/vpKeyboard.cpp b/src/tools/io/vpKeyboard.cpp index da4fdc0cdc34288b059683d8b988b00f8b392ce7..b18696ec2be10e26ca8e03ab9d3a9c7a07288a97 100644 --- a/src/tools/io/vpKeyboard.cpp +++ b/src/tools/io/vpKeyboard.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyboard.cpp 4267 2013-06-14 18:07:13Z fspindle $ + * $Id: vpKeyboard.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ -#if ( defined(UNIX) && !defined(WIN32) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) # include <stdio.h> # include <visp/vpKeyboard.h> @@ -54,7 +54,7 @@ /*! Activates the raw mode to read keys in an non blocking way. */ -vpKeyboard::vpKeyboard() +vpKeyboard::vpKeyboard() : initial_settings(), new_settings() { init(); } diff --git a/src/tools/io/vpKeyboard.h b/src/tools/io/vpKeyboard.h index 844d86f6a188cfea9614298be32fbf7205962901..c1097ca4143a7f0d4af2621b038f99ee8f55f34e 100644 --- a/src/tools/io/vpKeyboard.h +++ b/src/tools/io/vpKeyboard.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpKeyboard.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpKeyboard.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,7 +50,7 @@ #include <visp/vpConfig.h> -#if ( defined(UNIX) && !defined(WIN32) ) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) # include <iostream> # include <termios.h> diff --git a/src/tools/io/vpParallelPort.cpp b/src/tools/io/vpParallelPort.cpp index ea1736b0f9638d4ff565cca2ec03d0589ed39053..2ff06281429f10fc2659d6b584c4bc8bd285c340 100644 --- a/src/tools/io/vpParallelPort.cpp +++ b/src/tools/io/vpParallelPort.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpParallelPort.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpParallelPort.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,7 +74,7 @@ static unsigned char vpParallelPortData; write access. */ -vpParallelPort::vpParallelPort() +vpParallelPort::vpParallelPort() : fd(0), device() { sprintf(device, "/dev/parport0"); diff --git a/src/tools/io/vpParallelPort.h b/src/tools/io/vpParallelPort.h index 9dd876885b9be078fc3b7e0acce5577d4e75c494..d778e40dbc4ccb88d33157614a5e8c64d923bdb7 100644 --- a/src/tools/io/vpParallelPort.h +++ b/src/tools/io/vpParallelPort.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpParallelPort.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpParallelPort.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tools/io/vpParallelPortException.h b/src/tools/io/vpParallelPortException.h index ef971f0f038a88ee3c0cfa2b3ebc01ab73783214..c022185edbb5cbf2e0c6901e4bb573f71dfc1f88 100644 --- a/src/tools/io/vpParallelPortException.h +++ b/src/tools/io/vpParallelPortException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpParallelPortException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpParallelPortException.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,36 +78,31 @@ */ class VISP_EXPORT vpParallelPortException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible errors than can be emmited while calling vpParallelPort member */ - enum error + enum error { opening, /*!< Cannot access to the parallel port device. */ closing /*!< Cannot close the parallel port device. */ } ; -public: - vpParallelPortException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpParallelPortException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpParallelPortException (const int code) - : vpException(code){ ; } + public: + vpParallelPortException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpParallelPortException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpParallelPortException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpParallelPortException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/tools/io/vpParseArgv.cpp b/src/tools/io/vpParseArgv.cpp index 3e3f2b04e548de79596b0206df544b86ccc299bb..a6622263bcc259a503e3ea8ea09da3779fd38a14 100644 --- a/src/tools/io/vpParseArgv.cpp +++ b/src/tools/io/vpParseArgv.cpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * $Id: vpParseArgv.cpp 3789 2012-06-13 21:01:08Z fspindle $ + * $Id: vpParseArgv.cpp 5311 2015-02-11 17:42:01Z fspindle $ * * This file contains a procedure that handles table-based * argv-argc parsing. @@ -33,6 +33,7 @@ #include <visp/vpParseArgv.h> #include <stdio.h> #include <stdlib.h> +#include <stdint.h> #include <string.h> #include <math.h> #include <ctype.h> @@ -95,7 +96,7 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, * than srcIndex). */ int argc; /* # arguments in argv still to process. */ size_t length; /* Number of characters in current argument. */ - unsigned long nargs; /* Number of following arguments to get. */ + unsigned long long nargs; /* Number of following arguments to get. */ /* Macro to optionally print errors */ #define FPRINTF if (!(flags&ARGV_NO_PRINT)) (void) fprintf @@ -172,10 +173,10 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, infoPtr = matchPtr; switch (infoPtr->type) { case ARGV_CONSTANT: - *((int *) infoPtr->dst) = (long) infoPtr->src; + *((long long *) infoPtr->dst) = 1; break; case ARGV_INT: - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long i=0; i<nargs; i++) { if (argc == 0) { @@ -197,7 +198,7 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, } break; case ARGV_LONG: - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long i=0; i<nargs; i++) { if (argc == 0) { @@ -219,7 +220,7 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, } break; case ARGV_STRING: - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long i=0; i<nargs; i++) { if (argc == 0) { @@ -235,7 +236,7 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, *((int *) infoPtr->dst) = dstIndex; goto argsDone; case ARGV_FLOAT: - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long i=0; i<nargs; i++) { if (argc == 0) { @@ -257,7 +258,7 @@ vpParseArgv::parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, } break; case ARGV_DOUBLE: - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long i=0; i<nargs; i++) { if (argc == 0) { @@ -356,7 +357,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) #define NUM_SPACES 20 static char spaces[] = " "; /* char tmp[30]; */ - unsigned long nargs; + unsigned long long nargs; /* Macro to optionally print errors */ #define FPRINTF if (!(flags&ARGV_NO_PRINT)) (void) fprintf @@ -403,7 +404,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) switch (infoPtr->type) { case ARGV_INT: { FPRINTF(stderr, "\n\t\tDefault value:"); - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long j=0; j<nargs; j++) { FPRINTF(stderr, " %d", *(((int *) infoPtr->dst)+j)); @@ -412,7 +413,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) } case ARGV_LONG: { FPRINTF(stderr, "\n\t\tDefault value:"); - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long j=0; j<nargs; j++) { FPRINTF(stderr, " %ld", *(((long *) infoPtr->dst)+j)); @@ -421,7 +422,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) } case ARGV_FLOAT: { FPRINTF(stderr, "\n\t\tDefault value:"); - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long j=0; j<nargs; j++) { FPRINTF(stderr, " %f", *(((float *) infoPtr->dst)+j)); @@ -430,7 +431,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) } case ARGV_DOUBLE: { FPRINTF(stderr, "\n\t\tDefault value:"); - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (unsigned long j=0; j<nargs; j++) { FPRINTF(stderr, " %g", *(((double *) infoPtr->dst)+j)); @@ -440,7 +441,7 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) case ARGV_STRING: { const char *string; - nargs = (unsigned long) infoPtr->src; + nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; string = *((const char **) infoPtr->dst); if ((nargs==1) && (string == NULL)) break; @@ -449,9 +450,6 @@ vpParseArgv::printUsage(vpArgvInfo * argTable, int flags) if (string != NULL) { FPRINTF(stderr, " \"%s\"", string); } - else { - FPRINTF(stderr, " \"%s\"", string); - } } break; diff --git a/src/tools/io/vpParseArgv.h b/src/tools/io/vpParseArgv.h index 8b9e3e432afaaf561595a95d9e7c4e760705cffd..c466daaffe04ab19726d814deed1576a00d18b57 100644 --- a/src/tools/io/vpParseArgv.h +++ b/src/tools/io/vpParseArgv.h @@ -1,6 +1,6 @@ /**************************************************************************** * - * $Id: vpParseArgv.h 2780 2010-09-08 08:37:15Z fspindle $ + * $Id: vpParseArgv.h 4473 2013-09-27 16:50:44Z fspindle $ * * Declarations for Tk-related things that are visible * outside of the Tk module itself. @@ -35,6 +35,7 @@ #include <visp/vpConfig.h> +#include <visp/vpException.h> /*! \class vpParseArgv diff --git a/src/tools/mutex/vpMutex.h b/src/tools/mutex/vpMutex.h index 2dbb15eaca8bc9e4aeedfb366ad69f857b8e8bd3..c0bfd0f7b294350df7404018546a106f50034849 100644 --- a/src/tools/mutex/vpMutex.h +++ b/src/tools/mutex/vpMutex.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpMutex.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMutex.h 5126 2015-01-05 22:07:11Z fspindle $ * * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,7 @@ */ class VISP_EXPORT vpMutex { public: - vpMutex() { + vpMutex() : m_mutex() { pthread_mutex_init( &m_mutex, NULL ); } void lock() { diff --git a/src/tools/plot/vpPlot.cpp b/src/tools/plot/vpPlot.cpp index e458136c1774532907e6965c81ffef32c964d003..dcf216c49c38b95f5454bacc33c626aa95bba7c7 100644 --- a/src/tools/plot/vpPlot.cpp +++ b/src/tools/plot/vpPlot.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlot.cpp 4151 2013-03-11 06:52:18Z fspindle $ + * $Id: vpPlot.cpp 5226 2015-01-29 17:05:35Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,13 +64,9 @@ Needs then a call to init(). */ -vpPlot::vpPlot() +vpPlot::vpPlot() : I(), display(NULL), graphNbr(1), graphList(NULL), margei(30), margej(40), + factori(1.f), factorj(1.) { - graphList = NULL; - display = NULL; - - margei = 30; - margej = 40; } /*! This constructor creates a new window where the curves @@ -82,23 +78,19 @@ vpPlot::vpPlot() default font is set to "-adobe-times-medium-i-normal--10-100-75-75-p-52-iso8859-*". Note that you can chose an other one using "xfontsel". - \param graphNbr : The number of graph in the window. + \param graph_nbr : The number of graph in the window. \param height : Height of the window. \param width : Width of the window. \param x,y : The window is set at position x,y (column index, row index). \param title : Window title. */ -vpPlot::vpPlot(const unsigned int graphNbr, +vpPlot::vpPlot(const unsigned int graph_nbr, const unsigned int height, const unsigned int width, const int x, const int y, const char *title) -{ - graphList = NULL; - display = NULL; - - margei = 30; - margej = 40; - - init(graphNbr, height, width, x, y, title); + : I(), display(NULL), graphNbr(1), graphList(NULL), margei(30), margej(40), + factori(1.f), factorj(1.) +{ + init(graph_nbr, height, width, x, y, title); } /*! @@ -107,13 +99,13 @@ vpPlot::vpPlot(const unsigned int graphNbr, \warning You can modify the default window size, but this is not advised. - \param graphNbr : The number of graph in the window. + \param graph_nbr : The number of graph in the window. \param height : Height of the window. \param width : Width of the window. \param x,y : The window is set at position x,y (column index, row index). \param title : Window title. */ -void vpPlot::init(const unsigned int graphNbr, +void vpPlot::init(const unsigned int graph_nbr, const unsigned int height, const unsigned int width, const int x, const int y, const char *title) { @@ -138,7 +130,7 @@ void vpPlot::init(const unsigned int graphNbr, factori = height/700.0f; factorj = width/700.0f; - initNbGraph(graphNbr); + initNbGraph(graph_nbr); } /*! @@ -203,7 +195,6 @@ vpPlot::initNbGraph (unsigned int nbGraph) strcpy(graphList[i].unitx, ""); strcpy(graphList[i].unity, ""); strcpy(graphList[i].unitz, ""); - graphList[i].textdispayed=false; } } @@ -331,10 +322,10 @@ void vpPlot::plot(const unsigned int graphNum, \param y : The coordinate of the new point along the y axis and given in the user unit system. \param z : The coordinate of the new point along the z axis and given in the user unit system. */ -void +vpMouseButton::vpMouseButtonType vpPlot::plot (const unsigned int graphNum, const unsigned int curveNum, const double x, const double y, const double z) { - (graphList+graphNum)->plot(I,curveNum,x,y,z); + return (graphList+graphNum)->plot(I,curveNum,x,y,z); } /*! @@ -345,25 +336,31 @@ vpPlot::plot (const unsigned int graphNum, const unsigned int curveNum, const do \param v_y : y coordinates vector. The coordinates of the new points along the y axis and given in the user unit system. \param v_z : z coordinates vector. The coordinates of the new points along the z axis and given in the user unit system. */ -void vpPlot::plot(const unsigned int graphNum, const double x, const vpColVector &v_y, const vpColVector &v_z) +vpMouseButton::vpMouseButtonType +vpPlot::plot(const unsigned int graphNum, const double x, const vpColVector &v_y, const vpColVector &v_z) { + vpMouseButton::vpMouseButtonType button = vpMouseButton::none; if((graphList+graphNum)->curveNbr == v_y.getRows() && (graphList+graphNum)->curveNbr == v_z.getRows()) { for(unsigned int i = 0;i < v_y.getRows();++i) - this->plot(graphNum, i, x, v_y[i], v_z[i]); + button = this->plot(graphNum, i, x, v_y[i], v_z[i]); } else vpTRACE("error in plot vector : not the right dimension"); + return button; } /*! - This method unable to move the point of view if you have a 3D graphic. + This method allows to change the point of view with the mouse if you have a 3D graphic. + The navigation is performed using the mouse. + - A click on left mouse button allows rotations + - A click on middle mouse button allows zoom + - A click on rigt mouse button quit the infinite navigation loop. */ void vpPlot::navigate() { - vpImagePoint trash; - vpMouseButton::vpMouseButtonType b = vpMouseButton::button1; + vpMouseButton::vpMouseButtonType b = vpMouseButton::none; bool blocked = false; unsigned int iblocked = 0; @@ -376,27 +373,27 @@ vpPlot::navigate() vpDisplay::getPointerPosition(I,iP); for (unsigned int i = 0; i < graphNbr ; i++) { - if (iP.inRectangle((graphList+i)->graphZone)) - { - iblocked = i; - break; - } + if (iP.inRectangle((graphList+i)->graphZone)) + { + iblocked = i; + break; + } } - if ((graphList+iblocked)->move(I)) + if ((graphList+iblocked)->move(I, b)) { - (graphList+iblocked)->replot3D(I); + (graphList+iblocked)->replot3D(I); } - blocked = (graphList+iblocked)->blocked; } else { - if ((graphList+iblocked)->move(I)) + if ((graphList+iblocked)->move(I, b)) { - (graphList+iblocked)->replot3D(I); + (graphList+iblocked)->replot3D(I); } blocked = (graphList+iblocked)->blocked; } + vpTime::sleepMs(20); } } diff --git a/src/tools/plot/vpPlot.h b/src/tools/plot/vpPlot.h index 32668e3e3b161f572f7aac5b7ba8130ea935da05..a5d672b1e916cccb61bab4e4b37966a572b0f5f4 100644 --- a/src/tools/plot/vpPlot.h +++ b/src/tools/plot/vpPlot.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlot.h 4233 2013-05-02 13:46:42Z fspindle $ + * $Id: vpPlot.h 5226 2015-01-29 17:05:35Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -151,8 +151,8 @@ class VISP_EXPORT vpPlot void plot (const unsigned int graphNum, const unsigned int curveNum, const double x, const double y); void plot(const unsigned int graphNum, const double x, const vpColVector &v_y); - void plot (const unsigned int graphNum, const unsigned int curveNum, const double x, const double y, const double z); - void plot(const unsigned int graphNum, const double x, const vpColVector &v_y, const vpColVector &v_z); + vpMouseButton::vpMouseButtonType plot (const unsigned int graphNum, const unsigned int curveNum, const double x, const double y, const double z); + vpMouseButton::vpMouseButtonType plot(const unsigned int graphNum, const double x, const vpColVector &v_y, const vpColVector &v_z); void resetPointList (const unsigned int graphNum); void resetPointList (const unsigned int graphNum, const unsigned int curveNum); diff --git a/src/tools/plot/vpPlotCurve.cpp b/src/tools/plot/vpPlotCurve.cpp index 0106ef9aba9cc1eb5176fbea673cb939e2bd210c..245fb4078b83edb3d9534e703db3a5be589475d4 100644 --- a/src/tools/plot/vpPlotCurve.cpp +++ b/src/tools/plot/vpPlotCurve.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlotCurve.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPlotCurve.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,14 +50,10 @@ #include <visp/vpDisplayD3D.h> #if defined(VISP_HAVE_DISPLAY) -vpPlotCurve::vpPlotCurve() +vpPlotCurve::vpPlotCurve() : + color(vpColor::red), curveStyle(point), thickness(1), nbPoint(0), lastPoint(), + pointListx(), pointListy(), pointListz(), xmin(0), xmax(0), ymin(0), ymax(0) { - color = vpColor::red; - pointListx.clear(); - pointListy.clear(); - pointListz.clear(); - nbPoint = 0; - thickness = 1 ; } vpPlotCurve::~vpPlotCurve() diff --git a/src/tools/plot/vpPlotCurve.h b/src/tools/plot/vpPlotCurve.h index 8fe6b8acbba165eda8c268c5193f3eea52a562ec..32f6eaceebd317af99bcc8da114d0dd2107e3311 100644 --- a/src/tools/plot/vpPlotCurve.h +++ b/src/tools/plot/vpPlotCurve.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlotCurve.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPlotCurve.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tools/plot/vpPlotGraph.cpp b/src/tools/plot/vpPlotGraph.cpp index 30a8bab87ccbcb66d7cbb37e97c058d6091c522b..49c2fc023cb338ceb7c947fb6e68b6be8f795c8c 100644 --- a/src/tools/plot/vpPlotGraph.cpp +++ b/src/tools/plot/vpPlotGraph.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPlotGraph.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpPlotGraph.cpp 5226 2015-01-29 17:05:35Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,41 +61,23 @@ #if defined(VISP_HAVE_DISPLAY) +int laFonctionSansNom (const double delta); +void getGrid3DPoint(const double pente, vpImagePoint &iPunit, vpImagePoint &ip1, vpImagePoint &ip2, vpImagePoint &ip3); + + vpPlotGraph::vpPlotGraph() + : xorg(0.), yorg(0.), zoomx(1.), zoomy(1.), xmax(10), ymax(10), xmin(0), ymin(-10), + xdelt(1), ydelt(1), gridx(true), gridy(true), gridColor(), curveNbr(1), curveList(NULL), + scaleInitialized(false), firstPoint(true), nbDivisionx(10), nbDivisiony(10), topLeft(), + width(0), height(0), graphZone(), dTopLeft(), dWidth(0), dHeight(0), dGraphZone(), + dTopLeft3D(), dGraphZone3D(), cam(), cMo(), cMf(), w_xval(0), w_xsize(0), w_yval(0), w_ysize(0), + w_zval(0), w_zsize(0), ptXorg(0), ptYorg(0), ptZorg(0), zoomx_3D(1.), zoomy_3D(1.), zoomz_3D(1.), + nbDivisionz(10), zorg(1.), zoomz(1.), zmax(10), zmin(-10), zdelt(1), old_iPr(), old_iPz(), + blockedr(false), blockedz(false), blocked(false), epsi(5), epsj(6), + dispUnit(false), dispTitle(false), dispLegend(false), gridThickness(1) { - curveList = NULL; - gridColor.setColor(200,200,200); - nbDivisionx = 10; - nbDivisiony = 10; - nbDivisionz = 10; - - xmax = 10; - xmin = 0; - ymax = 10; - ymin = -10; - zmax = 10; - zmin = -10; - xdelt = 1; - ydelt = 1; - zdelt = 1; - gridx = true; - gridy = true; - scaleInitialized = false; - firstPoint = true; - - dispUnit = false; - dispTitle = false; - dispLegend = false; - - blockedr = false; - blockedz = false; - blocked = false; - - epsi = 5; - epsj = 6; - old_iPr = vpImagePoint(-1,-1); old_iPz = vpImagePoint(-1,-1); @@ -130,11 +112,11 @@ vpPlotGraph::initGraph (unsigned int nbCurve) } void -vpPlotGraph::initSize (vpImagePoint topLeft, unsigned int width, unsigned int height, unsigned int margei, unsigned int margej) +vpPlotGraph::initSize (vpImagePoint top_left, unsigned int w, unsigned int h, unsigned int margei, unsigned int margej) { - this->topLeft = topLeft; - this->width = width; - this->height = height; + this->topLeft = top_left; + this->width = w; + this->height = h; graphZone.setTopLeft(topLeft); graphZone.setWidth(width); graphZone.setHeight(height); @@ -237,36 +219,61 @@ vpPlotGraph::setCurveColor(const unsigned int curveNum, const vpColor color) } void -vpPlotGraph::setTitle (const char *title) +vpPlotGraph::setTitle (const char *title_) { - strcpy(this->title, title); + if (strlen(title_) >= 256) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the title")); + } + + strcpy(this->title, title_); dispTitle = true; } void -vpPlotGraph::setUnitX (const char *unitx) +vpPlotGraph::setUnitX (const char *unit_x) { - strcpy(this->unitx, unitx); + if (strlen(unit_x) >= 256) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the unit along x axis")); + } + + strcpy(this->unitx, unit_x); dispUnit = true; } void -vpPlotGraph::setUnitY (const char *unity) +vpPlotGraph::setUnitY (const char *unit_y) { - strcpy(this->unity, unity); + if (strlen(unit_y) >= 256) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the unit along y axis")); + } + + strcpy(this->unity, unit_y); dispUnit = true; } void -vpPlotGraph::setUnitZ (const char *unitz) +vpPlotGraph::setUnitZ (const char *unit_z) { - strcpy(this->unitz, unitz); + if (strlen(unit_z) >= 256) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the unit along z axis")); + } + + strcpy(this->unitz, unit_z); dispUnit = true; } void vpPlotGraph::setLegend (const unsigned int curveNum, const char *legend) { + if (strlen(legend) >= 256) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the legend")); + } + strcpy((curveList+curveNum)->legend,legend); dispLegend = true; } @@ -480,12 +487,13 @@ vpPlotGraph::rescaley(unsigned int side, double extremity) } void -vpPlotGraph::initScale(vpImage<unsigned char> &I, const double xmin, const double xmax, const int nbDivx, const double ymin, const double ymax, const int nbDivy, const bool gx, const bool gy) +vpPlotGraph::initScale(vpImage<unsigned char> &I, const double x_min, const double x_max, const int nbDivx, + const double y_min, const double y_max, const int nbDivy, const bool gx, const bool gy) { - this->xmin = xmin; - this->xmax = xmax; - this->ymin = ymin; - this->ymax = ymax; + this->xmin = x_min; + this->xmax = x_max; + this->ymin = y_min; + this->ymax = y_max; this->gridx = gx; this->gridy = gy; this->nbDivisionx = nbDivx; @@ -499,14 +507,16 @@ vpPlotGraph::initScale(vpImage<unsigned char> &I, const double xmin, const doubl void -vpPlotGraph::initScale(vpImage<unsigned char> &I, const double xmin, const double xmax, const int nbDivx, const double ymin, const double ymax, const int nbDivy, const double zmin, const double zmax, const int nbDivz, const bool gx, const bool gy) +vpPlotGraph::initScale(vpImage<unsigned char> &I, const double x_min, const double x_max, const int nbDivx, + const double y_min, const double y_max, const int nbDivy, + const double z_min, const double z_max, const int nbDivz, const bool gx, const bool gy) { - this->xmin = xmin; - this->xmax = xmax; - this->ymin = ymin; - this->ymax = ymax; - this->zmin = zmin; - this->zmax = zmax; + this->xmin = x_min; + this->xmax = x_max; + this->ymin = y_min; + this->ymax = y_max; + this->zmin = z_min; + this->zmax = z_max; this->gridx = gx; this->gridy = gy; this->nbDivisionx = nbDivx; @@ -881,7 +891,7 @@ vpPlotGraph::check3Dpoint(vpImagePoint &iP) if (iP.get_j() <dTopLeft3D.get_j()) iP.set_j(dTopLeft3D.get_j()); else if (iP.get_j() > dTopLeft3D.get_j()+dWidth) - iP.set_i(dTopLeft3D.get_j()+dWidth-1); + iP.set_j(dTopLeft3D.get_j()+dWidth-1); return false; } return true; @@ -1089,7 +1099,7 @@ vpPlotGraph::displayGrid3D (vpImage<unsigned char> &I) vpDisplay::displayArrow(I,iP[0],iP[1], vpColor::black, gridThickness); if (dispUnit) { - vpImagePoint iPunit(iP[1].get_i(),iP[1].get_j()-10*epsj); + iPunit.set_ij(iP[1].get_i(),iP[1].get_j()-10*epsj); check3Dpoint (iPunit); vpDisplay::displayCharString(I,iPunit,unitx, vpColor::black); } @@ -1099,7 +1109,7 @@ vpPlotGraph::displayGrid3D (vpImage<unsigned char> &I) vpDisplay::displayArrow(I,iP[3],iP[2], vpColor::black, gridThickness); if (dispUnit) { - vpImagePoint iPunit(iP[2].get_i(),iP[2].get_j()-10*epsj); + iPunit.set_ij(iP[2].get_i(),iP[2].get_j()-10*epsj); check3Dpoint (iPunit); vpDisplay::displayCharString(I,iPunit,unity, vpColor::black); } @@ -1109,7 +1119,7 @@ vpPlotGraph::displayGrid3D (vpImage<unsigned char> &I) vpDisplay::displayArrow(I,iP[4],iP[5], vpColor::black, gridThickness); if (dispUnit) { - vpImagePoint iPunit(iP[5].get_i(),iP[5].get_j()-10*epsj); + iPunit.set_ij(iP[5].get_i(),iP[5].get_j()-10*epsj); check3Dpoint (iPunit); vpDisplay::displayCharString(I,iPunit,unitz, vpColor::black); } @@ -1121,7 +1131,7 @@ vpPlotGraph::displayGrid3D (vpImage<unsigned char> &I) displayLegend(I); } -void +vpMouseButton::vpMouseButtonType vpPlotGraph::plot (vpImage<unsigned char> &I, const unsigned int curveNb, const double x, const double y, const double z) { if (!scaleInitialized) @@ -1183,7 +1193,9 @@ vpPlotGraph::plot (vpImage<unsigned char> &I, const unsigned int curveNb, const if (z > zmax) {rescalez(1,z);changed = true;} else if(z < zmin) {rescalez(0,z);changed = true;} - if (changed || move(I)) + vpMouseButton::vpMouseButtonType button = vpMouseButton::none; + + if (changed || move(I, button)) { computeGraphParameters3D(); replot3D(I); @@ -1209,14 +1221,14 @@ vpPlotGraph::plot (vpImage<unsigned char> &I, const unsigned int curveNb, const #if( defined VISP_HAVE_X11 || defined VISP_HAVE_GDI ) double top; double left; - double width; - double height; + double width_; + double height_; - if (iP.get_i() <= (curveList+curveNb)->lastPoint.get_i()) {top = iP.get_i()-5; height = (curveList+curveNb)->lastPoint.get_i() - top+10;} - else {top = (curveList+curveNb)->lastPoint.get_i()-5; height = iP.get_i() - top+10;} - if (iP.get_j() <= (curveList+curveNb)->lastPoint.get_j()) {left = iP.get_j()-5; width = (curveList+curveNb)->lastPoint.get_j() - left+10;} - else {left = (curveList+curveNb)->lastPoint.get_j()-5; width = iP.get_j() - left+10;} - vpDisplay::flushROI(I,vpRect(left,top,width,height)); + if (iP.get_i() <= (curveList+curveNb)->lastPoint.get_i()) {top = iP.get_i()-5; height_ = (curveList+curveNb)->lastPoint.get_i() - top+10;} + else {top = (curveList+curveNb)->lastPoint.get_i()-5; height_ = iP.get_i() - top+10;} + if (iP.get_j() <= (curveList+curveNb)->lastPoint.get_j()) {left = iP.get_j()-5; width_ = (curveList+curveNb)->lastPoint.get_j() - left+10;} + else {left = (curveList+curveNb)->lastPoint.get_j()-5; width_ = iP.get_j() - left+10;} + vpDisplay::flushROI(I,vpRect(left,top,width_,height_)); #endif (curveList+curveNb)->lastPoint = iP; @@ -1225,10 +1237,10 @@ vpPlotGraph::plot (vpImage<unsigned char> &I, const unsigned int curveNb, const (curveList+curveNb)->pointListz.push_back(z); (curveList+curveNb)->nbPoint++; - #if( !defined VISP_HAVE_X11 && defined FLUSH_ON_PLOT) vpDisplay::flushROI(I,graphZone); #endif + return button; } void @@ -1296,12 +1308,16 @@ vpPlotGraph::rescalez(unsigned int side, double extremity) zdelt = (zmax-zmin)/(double)nbDivisionz; } +/*! + \param I : Image used to display the graph. + \param button : Mouse button used during navigation. + */ bool -vpPlotGraph::move(const vpImage<unsigned char> &I) +vpPlotGraph::move(const vpImage<unsigned char> &I, vpMouseButton::vpMouseButtonType &button) { bool changed = false; - vpHomogeneousMatrix displacement = navigation(I,changed); - + vpHomogeneousMatrix displacement = navigation(I, changed, button); + //if (displacement[2][3] != 0) if (std::fabs(displacement[2][3]) > std::numeric_limits<double>::epsilon()) cMf = cMf*displacement; @@ -1312,13 +1328,12 @@ vpPlotGraph::move(const vpImage<unsigned char> &I) } vpHomogeneousMatrix -vpPlotGraph::navigation(const vpImage<unsigned char> &I, bool &changed) +vpPlotGraph::navigation(const vpImage<unsigned char> &I, bool &changed, vpMouseButton::vpMouseButtonType &b) { vpImagePoint iP; vpImagePoint trash; bool clicked = false; bool clickedUp = false; - vpMouseButton::vpMouseButtonType b = vpMouseButton::button1; vpHomogeneousMatrix mov(0,0,0,0,0,0); changed = false; @@ -1360,7 +1375,7 @@ vpPlotGraph::navigation(const vpImage<unsigned char> &I, bool &changed) //while (vpDisplay::getClick(I,trash,b,false)) {}; } } - + vpDisplay::getPointerPosition(I,iP); double anglei = 0; @@ -1368,13 +1383,13 @@ vpPlotGraph::navigation(const vpImage<unsigned char> &I, bool &changed) if (old_iPr != vpImagePoint(-1,-1) && blockedr) { - double width = vpMath::minimum(I.getWidth(),I.getHeight()); + double width_ = vpMath::minimum(I.getWidth(),I.getHeight()); double diffi = iP.get_i() - old_iPr.get_i(); double diffj = iP.get_j() - old_iPr.get_j(); - anglei = diffi*360/width; - anglej = diffj*360/width; + anglei = diffi*360/width_; + anglej = diffj*360/width_; mov.buildFrom(0,0,0,vpMath::rad(anglei),vpMath::rad(-anglej),0); changed = true; } diff --git a/src/tools/plot/vpPlotGraph.h b/src/tools/plot/vpPlotGraph.h index 88fb8084bf2de2eabb48dba65ab6985e4225065d..2da1a0fb1fada3c0d3e69da46c88f11bc7756a20 100644 --- a/src/tools/plot/vpPlotGraph.h +++ b/src/tools/plot/vpPlotGraph.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpPlotGraph.h 4151 2013-03-11 06:52:18Z fspindle $ + * $Id: vpPlotGraph.h 5226 2015-01-29 17:05:35Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,6 +49,7 @@ #include <visp/vpImage.h> #include <visp/vpPlotCurve.h> +#include <visp/vpMouseButton.h> #include <visp/vpHomogeneousMatrix.h> #include <visp/vpRect.h> @@ -79,7 +80,6 @@ class vpPlotGraph char unity[256]; unsigned int curveNbr; vpPlotCurve* curveList; - bool textdispayed; bool scaleInitialized; bool firstPoint; @@ -169,11 +169,11 @@ class vpPlotGraph bool getPixelValue(vpImage<unsigned char> &I, vpImagePoint &iP); - bool move(const vpImage<unsigned char> &I); - vpHomogeneousMatrix navigation(const vpImage<unsigned char> &I, bool &changed); + bool move(const vpImage<unsigned char> &I, vpMouseButton::vpMouseButtonType &button); + vpHomogeneousMatrix navigation(const vpImage<unsigned char> &I, bool &changed, vpMouseButton::vpMouseButtonType &b); void plot (vpImage<unsigned char> &I, const unsigned int curveNb, const double x, const double y); - void plot (vpImage<unsigned char> &I, const unsigned int curveNb, const double x, const double y, const double z); + vpMouseButton::vpMouseButtonType plot (vpImage<unsigned char> &I, const unsigned int curveNb, const double x, const double y, const double z); void replot (vpImage<unsigned char> &I); void replot3D (vpImage<unsigned char> &I); diff --git a/src/tools/time/vpTime.cpp b/src/tools/time/vpTime.cpp index 8c2dcb124b3a02312c1892578f565730bdae753e..e1d5adc84d83656e6a3cf3c525b54726f22ba3d7 100644 --- a/src/tools/time/vpTime.cpp +++ b/src/tools/time/vpTime.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTime.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTime.cpp 4622 2014-01-28 17:40:36Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,10 +56,10 @@ // Unix depend version -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <sys/time.h> # include <unistd.h> -#elif defined WIN32 +#elif defined(_WIN32) # include <windows.h> # include <winbase.h> #endif @@ -78,14 +78,14 @@ double vpTime::minTimeForUsleepCall = 4; /*! This time is in /*! - Return the time in milliseconds. + Return the time in milliseconds since January 1st 1970. \sa measureTimeMicros(), measureTimeSecond() */ double vpTime::measureTimeMs() { -#if defined(WIN32) +#if defined(_WIN32) LARGE_INTEGER time, frequency; QueryPerformanceFrequency(&frequency); if(frequency.QuadPart == 0){ @@ -95,7 +95,7 @@ vpTime::measureTimeMs() QueryPerformanceCounter(&time); return (double)(1000.0*time.QuadPart/frequency.QuadPart); } -#elif defined UNIX +#elif !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX struct timeval tp; gettimeofday(&tp,0); return(1000.0*tp.tv_sec + tp.tv_usec/1000.0); @@ -104,14 +104,14 @@ vpTime::measureTimeMs() /*! - Return the time in microseconds. + Return the time in microseconds since January 1st 1970. \sa measureTimeMs(), measureTimeSecond() */ double vpTime::measureTimeMicros() { -#if defined(WIN32) +#if defined(_WIN32) LARGE_INTEGER time, frequency; QueryPerformanceFrequency(&frequency); if(frequency.QuadPart == 0){ @@ -156,11 +156,11 @@ vpTime::wait(double t0, double t) if ( timeToWait <= 0. ) // no need to wait return(1); else { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if (timeToWait > vpTime::minTimeForUsleepCall) { usleep((unsigned long )((timeToWait-vpTime::minTimeForUsleepCall)*1000)); } -#elif defined WIN32 +#elif defined(_WIN32) if (timeToWait > vpTime::minTimeForUsleepCall) { Sleep((DWORD)(timeToWait-vpTime::minTimeForUsleepCall)); } @@ -196,11 +196,11 @@ void vpTime::wait(double t) if ( timeToWait <= 0. ) // no need to wait return; else { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX if (timeToWait > vpTime::minTimeForUsleepCall) { usleep((unsigned long )((timeToWait-vpTime::minTimeForUsleepCall)*1000)); } -#elif defined WIN32 +#elif defined(_WIN32) if (timeToWait > vpTime::minTimeForUsleepCall) { Sleep((DWORD)(timeToWait-vpTime::minTimeForUsleepCall)); } @@ -218,7 +218,7 @@ void vpTime::wait(double t) /*! - Return the measured time in seconds. + Return the measured time in seconds since January 1st 1970. \sa measureTimeMs() */ @@ -235,9 +235,9 @@ double vpTime::measureTimeSecond() */ void vpTime::sleepMs(double t) { -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX usleep((unsigned long )(t*1000)); -#elif defined WIN32 +#elif defined(_WIN32) Sleep((DWORD)(t)); #endif } diff --git a/src/tools/time/vpTime.h b/src/tools/time/vpTime.h index 8460744921bd44ec95f48f2e7e5aacb6c1e035b5..b21b2f194000672ea8a5f5d032143512a18ad354 100644 --- a/src/tools/time/vpTime.h +++ b/src/tools/time/vpTime.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTime.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTime.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,6 +51,7 @@ */ #include <visp/vpConfig.h> +#include <visp/vpException.h> /*! \class vpTime diff --git a/src/tools/trace/vpDebug.h b/src/tools/trace/vpDebug.h index 03e061be3b926b6b6b03ff14a8e4e47f5eb41bf9..4f3970ca8036f85dd47ae81c9815c0418bd8bfa7 100644 --- a/src/tools/trace/vpDebug.h +++ b/src/tools/trace/vpDebug.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDebug.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpDebug.h 4604 2014-01-21 14:15:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,7 +57,7 @@ #include <iostream> -#ifdef WIN32 +#if defined(_WIN32) # ifndef __FUNCTION__ # define __FUNCTION__ " " # endif @@ -82,21 +82,23 @@ This class is used to define the following macros: - - Macros for tracing: vpTRACE() and vpERROR_TRACE() work like printf - with carreer return at the end of the string. vpCERROR() et - vpCTRACE() work like the C++ output streams std::cout and + - Macros for tracing: vpTRACE(), vpERROR_TRACE(), vpIN_FCT() and + vpOUT_FCT() work like printf + with carreer return at the end of the string, while vpCTRACE() and + vpCERROR() work like the C++ output streams std::cout and std::cerr. - - Macros for debuging: vpDEBUG_TRACE(niv) and vpDERROR_TRACE(niv) - work like printf, but print only if the tracing level \e niv is - greater than the debug level VP_DEBUG_MODE macro. vpCDEBUG(niv) - work like the C++ output stream std::cout. vpDEBUG_ENABLE(niv) is - equal to 1 if the debug level niv is greater than the debug mode + - Macros for debuging: vpDEBUG_TRACE(level) and vpDERROR_TRACE(level) + work like printf, but print only if the tracing level \e level is + greater than the debug level VP_DEBUG_MODE macro. vpCDEBUG(level) + work like the C++ output stream std::cout. vpDEBUG_ENABLE(level) is + equal to 1 if the debug level level is greater than the debug mode VP_DEBUG_MODE, 0 else. The example below shows how to use these macros. \code +#define VP_TRACE // Activate the trace mode #define VP_DEBUG // Activate the debug mode #define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 @@ -123,7 +125,7 @@ int main() vpDERROR_TRACE(2, "C-like error trace level 2"); // stderr // C++-like debug printings - vpCTRACE << "C++-like debug trace" << std::endl; // stdout + vpCTRACE << "C++-like trace" << std::endl; // stdout vpCERROR << "C++-like error trace" << std::endl; // stderr // Printing if VP_DEBUG defined and VP_DEBUG_MODE value >= 2 @@ -136,7 +138,7 @@ int main() */ class vpTraceOutput { -private: + private: const char* currentFile; //Name of the file to use in the displays const char* currentFunc; //Name of the function to use in the displays int currentLine; //Line to use in the displays @@ -146,7 +148,7 @@ private: //string to display before anything else const char* header; -public: + public: /*! Constructor. \param file Should be the name of the file where this constructor was called. @@ -157,37 +159,37 @@ public: \note Call the constructor with something like vpTraceOutput(__FILE__,__LINE__, __FUNCTION__). */ vpTraceOutput(const char* file, int line, const char* func, bool error=false, const char * s=NULL) : - currentFile(file), - currentFunc(func), - currentLine(line), - err(error), - header(s) + currentFile(file), + currentFunc(func), + currentLine(line), + err(error), + header(s) {} /*! Displays a string if the debug level is inferior to VP_DEBUG_MODE. - \param niv Level of this message. + \param level Level of this message. \param format Formating string. */ - void operator()(int niv, const char* format, ...) + void operator()(int level, const char* format, ...) { - //if the niv level is inferior to VP_DEBUG_MODE - if(VP_DEBUG_MODE >= niv) - { - //gets the variable list of arguments - va_list args; - va_start(args, format); - - if (err) - std::cerr << "(N" << niv << ") " ; - else - std::cout << "(N" << niv << ") " ; - - //calls display with it - display(format, args); - - va_end(args); - } + //if the level is inferior to VP_DEBUG_MODE + if(VP_DEBUG_MODE >= level) + { + //gets the variable list of arguments + va_list args; + va_start(args, format); + + if (err) + std::cerr << "(L" << level << ") " ; + else + std::cout << "(L" << level << ") " ; + + //calls display with it + display(format, args); + + va_end(args); + } } /*! @@ -196,18 +198,18 @@ public: */ void operator()(const char* format, ...) { - //gets the variable list of arguments - va_list args; - va_start(args, format); + //gets the variable list of arguments + va_list args; + va_start(args, format); #ifdef VP_DEBUG - std::cout<<"(N0) "; + std::cout<<"(L0) "; #endif - //calls display with it - display(format, args); + //calls display with it + display(format, args); - va_end(args); + va_end(args); } /*! @@ -221,31 +223,31 @@ public: */ void display(const char* format, va_list args) { - //if we want to write to std::cerr/stderr - if(err) - { - //first writes the header if there is one - if(header != NULL) std::cerr<<header; - //then writes the recorded namefile, function and line - std::cerr << "!!\t" << currentFile << ": " <<currentFunc << "(#" << currentLine << ") :" ; - //and finally writes the message passed to () operator. - vfprintf (stderr, format, args); - fprintf (stderr, "\n"); - //flushes the buffer - fflush (stderr); - } - else - { - //first writes the header if there is one - if(header != NULL) std::cout<<header; - //then writes the recorded namefile, function and line - std::cout <<currentFile << ": " << currentFunc << "(#" << currentLine << ") :" ; - //and finally writes the message passed to () operator. - vprintf (format, args); - printf ("\n"); - //flushes the buffer - fflush (stdout); - } + //if we want to write to std::cerr/stderr + if(err) + { + //first writes the header if there is one + if(header != NULL) std::cerr<<header; + //then writes the recorded namefile, function and line + std::cerr << "!!\t" << currentFile << ": " <<currentFunc << "(#" << currentLine << ") : " ; + //and finally writes the message passed to () operator. + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + //flushes the buffer + fflush (stderr); + } + else + { + //first writes the header if there is one + if(header != NULL) std::cout<<header; + //then writes the recorded namefile, function and line + std::cout <<currentFile << ": " << currentFunc << "(#" << currentLine << ") : " ; + //and finally writes the message passed to () operator. + vprintf (format, args); + printf ("\n"); + //flushes the buffer + fflush (stdout); + } } }; @@ -255,11 +257,14 @@ public: /* --- vpTRACE IN/OUT FONCTION --------------------------------------------- */ /* ------------------------------------------------------------------------- */ +#ifdef VP_TRACE // Activate the trace mode + /*! \ingroup Debug Works like vpTRACE() and should be used at the beginning of a function. \code +#define VP_TRACE // To activate the trace mode #include <visp/vpDebug.h> int main() @@ -280,6 +285,7 @@ int main() Works like vpTRACE() and should be used at the end of a function. \code +#define VP_TRACE // To activate the trace mode #include <visp/vpDebug.h> int main() @@ -294,27 +300,35 @@ int main() */ #define vpOUT_FCT (vpTraceOutput(__FILE__,__LINE__, __FUNCTION__, false, "end ")) +#else // #ifdef VP_TRACE + +inline void vpIN_FCT (const char * /* a */, ...){} +inline void vpOUT_FCT (const char * /* a */, ...){} +#endif // #ifdef VP_TRACE /* -------------------------------------------------------------------------- */ /* --- vpTRACE -------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +#ifdef VP_TRACE + /*! \ingroup Debug Used to display trace messages on the standard stream (C++). Use like this : vpCTRACE<<"my message"<<std::endl; \code -#define VP_DEBUG // Activate the debug mode -#define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 +#define VP_TRACE // To activate trace mode +#define VP_DEBUG // To activate the debug mode +#define VP_DEBUG_MODE 2 // To activate debug level 1 and 2 #include <visp/vpDebug.h> int main() { // C++-like debug printings - vpCTRACE << "C++-like debug trace" << std::endl; // stdout + vpCTRACE << "C++-like trace" << std::endl; // stdout vpCERROR << "C++-like error trace" << std::endl; // stderr // Printing if VP_DEBUG defined and VP_DEBUG_MODE value >= 2 @@ -324,7 +338,7 @@ int main() \sa vpTRACE(), vpCERROR(), vpCDEBUG() */ -#define vpCTRACE std::cout << __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") :" +#define vpCTRACE std::cout << "(L0) " << __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") : " /*! @@ -333,15 +347,16 @@ int main() Use like this : vpCERROR<<"my message"<<std::endl; \code -#define VP_DEBUG // Activate the debug mode -#define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 +#define VP_TRACE // To activate trace mode +#define VP_DEBUG // To activate the debug mode +#define VP_DEBUG_MODE 2 // To activate debug level 1 and 2 #include <visp/vpDebug.h> int main() { // C++-like debug printings - vpCTRACE << "C++-like debug trace" << std::endl; // stdout + vpCTRACE << "C++-like trace" << std::endl; // stdout vpCERROR << "C++-like error trace" << std::endl; // stderr // Printing if VP_DEBUG defined and VP_DEBUG_MODE value >= 2 @@ -351,7 +366,7 @@ int main() \sa vpCTRACE(), vpCDEBUG() */ -#define vpCERROR std::cerr << "!!\t" << __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") :" +#define vpCERROR std::cerr << "(L0) " << "!!\t" << __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") : " /*! \ingroup Debug @@ -362,6 +377,7 @@ int main() with any "printf" string. \code +#define VP_TRACE // To activate trace mode #define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 #include <visp/vpDebug.h> @@ -387,6 +403,7 @@ int main() with any "printf" string. \code +#define VP_TRACE // To activate trace mode #include <visp/vpDebug.h> int main() @@ -400,6 +417,17 @@ int main() */ #define vpTRACE (vpTraceOutput( __FILE__,__LINE__, __FUNCTION__, false)) +#else // #ifdef VP_TRACE + +#define vpCTRACE if(false) std::cout // Warning C4127 +#define vpCERROR if(false) std::cerr // Warning C4127 + +inline void vpERROR_TRACE (const char * /* a */, ...){} +inline void vpERROR_TRACE (int /* level */, const char * /* a */, ...){} +inline void vpTRACE (const char * /* a */, ...){} +inline void vpTRACE (int /* level */, const char * /* a */, ...){} + +#endif // #ifdef VP_TRACE /* ------------------------------------------------------------------------- */ /* --- VP_DEBUG ------------------------------------------------------------ */ @@ -410,7 +438,7 @@ int main() /*! \ingroup Debug vpDERROR_TRACE works like printf, but prints only if the - tracing level niv is greater than the debug level VP_DEBUG_MODE. + tracing level is smaller than the debug level VP_DEBUG_MODE. \code #define VP_DEBUG // Activate the debug mode @@ -433,7 +461,7 @@ int main() /*! \ingroup Debug vpDEBUG_TRACE works like printf, but prints only if the - tracing level niv is greater than the debug level VP_DEBUG_MODE. + tracing level level is greater than the debug level VP_DEBUG_MODE. \code #define VP_DEBUG // Activate the debug mode @@ -455,7 +483,7 @@ int main() /*! \ingroup Debug - vpCDEBUG(niv) work like the C++ output stream std::cout. + vpCDEBUG(level) work like the C++ output stream std::cout. \code #define VP_DEBUG // Activate the debug mode #define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 @@ -465,7 +493,7 @@ int main() int main() { // C++-like debug printings - vpCTRACE << "C++-like debug trace" << std::endl; // stdout + vpCTRACE << "C++-like trace" << std::endl; // stdout vpCERROR << "C++-like error trace" << std::endl; // stderr // Printing if VP_DEBUG defined and VP_DEBUG_MODE value >= 2 @@ -475,13 +503,13 @@ int main() \sa vpCTRACE(), vpCERROR() */ -#define vpCDEBUG(niv) if (VP_DEBUG_MODE < niv) ; else \ - std::cout << "(N" << niv << ") "<< __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") :" +#define vpCDEBUG(level) if (VP_DEBUG_MODE < level) ; else \ + std::cout << "(L" << level << ") "<< __FILE__ << ": " << __FUNCTION__ << "(#" << __LINE__ << ") : " /*! \ingroup Debug - vpDEBUG_ENABLE(niv) is equal to 1 if the debug level \e niv is greater than + vpDEBUG_ENABLE(level) is equal to 1 if the debug level \e level is greater than the debug mode VP_DEBUG_MODE, 0 else. \code @@ -499,17 +527,19 @@ int main() } \endcode */ -#define vpDEBUG_ENABLE(niv) (VP_DEBUG_MODE >= niv) +#define vpDEBUG_ENABLE(level) (VP_DEBUG_MODE >= level) -#else +#else // #ifdef VP_DEBUG -inline void vpDERROR_TRACE(int /* niv */, const char * /* a */, ...){} -inline void vpDEBUG_TRACE(int /* niv */, const char * /* a */, ...){} +inline void vpDERROR_TRACE(const char * /* a */, ...){} +inline void vpDEBUG_TRACE(const char * /* a */, ...){} +inline void vpDERROR_TRACE(int /* level */, const char * /* a */, ...){} +inline void vpDEBUG_TRACE(int /* level */, const char * /* a */, ...){} -#define vpCDEBUG(niv) if(false) std::cout // Warning C4127 -#define vpDEBUG_ENABLE(niv) (false) // Warning C4127 +#define vpCDEBUG(level) if(false) std::cout // Warning C4127 +#define vpDEBUG_ENABLE(level) (false) // Warning C4127 -#endif +#endif // #ifdef VP_DEBUG /* -------------------------------------------------------------------------- */ /* --- DEFENSIF ------------------------------------------------------------- */ @@ -522,9 +552,3 @@ inline void vpDEBUG_TRACE(int /* niv */, const char * /* a */, ...){} #endif /* #ifdef __DEBUG_HH */ - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff --git a/src/tools/xml/vpXmlParser.cpp b/src/tools/xml/vpXmlParser.cpp index 086d9af154499d2e36530d590da6a06b26404580..28a59ec7c959b52fe37eceba95de078668e0dad8 100755 --- a/src/tools/xml/vpXmlParser.cpp +++ b/src/tools/xml/vpXmlParser.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpXmlParser.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpXmlParser.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,9 +58,8 @@ Initialise the main tag with default value. */ -vpXmlParser::vpXmlParser() +vpXmlParser::vpXmlParser() : nodeMap(), main_tag("config") { - main_tag = "config"; } /*! @@ -88,6 +87,7 @@ vpXmlParser::~vpXmlParser() \param _twin : The parser to copy. */ vpXmlParser::vpXmlParser(const vpXmlParser& _twin) + : nodeMap(), main_tag("config") { main_tag = _twin.main_tag; nodeMap = _twin.nodeMap; @@ -387,7 +387,9 @@ vpXmlParser::save(const std::string& filename, const bool append) else{ if(!append){ xmlFreeDoc(doc); - remove(filename.c_str()); + if (remove(filename.c_str()) != 0) + throw vpException(vpException::ioError, "Cannot remove existing xml file"); + doc = xmlNewDoc ((xmlChar*)"1.0"); root_node = xmlNewNode(NULL, (xmlChar*)main_tag.c_str()); xmlDocSetRootElement(doc, root_node); diff --git a/src/tools/xml/vpXmlParser.h b/src/tools/xml/vpXmlParser.h index 97c0b6ecc27c4e82edd7e3432f0685bd2f0f58b4..76d6bff3904560b36051be334bb47d6b47872bf1 100755 --- a/src/tools/xml/vpXmlParser.h +++ b/src/tools/xml/vpXmlParser.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpXmlParser.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpXmlParser.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/dots/vpDot.cpp b/src/tracking/dots/vpDot.cpp index b681392054320219fdfcbe3436a89bd655f7cf18..2a926ff9fd93a1791c7735e500d8ec733e4a9b61 100644 --- a/src/tracking/dots/vpDot.cpp +++ b/src/tracking/dots/vpDot.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDot.cpp 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpDot.cpp 4943 2014-11-03 13:51:09Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,12 +47,10 @@ */ #include <visp/vpDot.h> - #include <visp/vpDisplay.h> #include <visp/vpColor.h> - -// exception handling #include <visp/vpTrackingException.h> + #include <vector> /* @@ -95,9 +93,13 @@ void vpDot::init() nbMaxPoint = 0; } -vpDot::vpDot() : vpTracker() +vpDot::vpDot() + : m00(0.), m01(0.), m10(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), ip_connexities_list(), ip_edges_list(), connexityType(CONNEXITY_4), + cog(), u_min(0), u_max(0), v_min(0), v_max(0), graphics(false), thickness(1), maxDotSizePercentage(0.25), + gray_level_out(0), mean_gray_level(0), gray_level_min(128), gray_level_max(255), grayLevelPrecision(0.85), + gamma(1.5), compute_moment(false), nbMaxPoint(0) { - init() ; } /*! @@ -105,17 +107,26 @@ vpDot::vpDot() : vpTracker() \param ip : An image point with sub-pixel coordinates. */ -vpDot::vpDot(const vpImagePoint &ip) : vpTracker() +vpDot::vpDot(const vpImagePoint &ip) + : m00(0.), m01(0.), m10(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), ip_connexities_list(), ip_edges_list(), connexityType(CONNEXITY_4), + cog(), u_min(0), u_max(0), v_min(0), v_max(0), graphics(false), thickness(1), maxDotSizePercentage(0.25), + gray_level_out(0), mean_gray_level(0), gray_level_min(128), gray_level_max(255), grayLevelPrecision(0.85), + gamma(1.5), compute_moment(false), nbMaxPoint(0) { - init() ; - cog = ip; } /*! \brief Copy constructor. */ -vpDot::vpDot(const vpDot& d) : vpTracker() +vpDot::vpDot(const vpDot& d) + : vpTracker(d), + m00(0.), m01(0.), m10(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), ip_connexities_list(), ip_edges_list(), connexityType(CONNEXITY_4), + cog(), u_min(0), u_max(0), v_min(0), v_max(0), graphics(false), thickness(1), maxDotSizePercentage(0.25), + gray_level_out(0), mean_gray_level(0), gray_level_min(128), gray_level_max(255), grayLevelPrecision(0.85), + gamma(1.5), compute_moment(false), nbMaxPoint(0) { *this = d ; } @@ -125,7 +136,6 @@ vpDot::vpDot(const vpDot& d) : vpTracker() */ vpDot::~vpDot() { - ip_connexities_list.clear() ; } @@ -200,7 +210,7 @@ vpDot::setGrayLevelOut() if (gray_level_min == 0) { if (gray_level_max == 255) { // gray_level_min = 0 and gray_level_max = 255: this should not occur - vpERROR_TRACE("Unable to choose a good \"out\" level") ; + //vpERROR_TRACE("Unable to choose a good \"out\" level") ; throw(vpTrackingException(vpTrackingException::initializationError, "Unable to choose a good \"out\" level")) ; } @@ -280,14 +290,18 @@ bool vpDot::connexe(const vpImage<unsigned char>& I,unsigned int u,unsigned int n+=1 ; if (n > nbMaxPoint) { - vpERROR_TRACE("Too many point %lf (%lf%% of image size). " - "This threshold can be modified using the setMaxDotSize() " - "method.", - n, n / (I.getWidth() * I.getHeight()), - nbMaxPoint, maxDotSizePercentage) ; +// vpERROR_TRACE("Too many point %lf (%lf%% of image size). " +// "This threshold can be modified using the setMaxDotSize() " +// "method.", +// n, n / (I.getWidth() * I.getHeight()), +// nbMaxPoint, maxDotSizePercentage) ; throw(vpTrackingException(vpTrackingException::featureLostError, - "Dot to big")) ; + "Too many point %lf (%lf%% of image size). " + "This threshold can be modified using the setMaxDotSize() " + "method.", + n, n / (I.getWidth() * I.getHeight()), + nbMaxPoint, maxDotSizePercentage)) ; } // Bounding box update @@ -448,7 +462,7 @@ vpDot::COG(const vpImage<unsigned char> &I, double& u, double& v) } if (sol == false) { - vpERROR_TRACE("Dot has been lost") ; + //vpERROR_TRACE("Dot has been lost") ; throw(vpTrackingException(vpTrackingException::featureLostError, "Dot has been lost")) ; } @@ -529,7 +543,7 @@ vpDot::COG(const vpImage<unsigned char> &I, double& u, double& v) } if (sol == false) { - vpERROR_TRACE("Dot has been lost") ; + //vpERROR_TRACE("Dot has been lost") ; throw(vpTrackingException(vpTrackingException::featureLostError, "Dot has been lost")) ; } @@ -573,19 +587,21 @@ vpDot::COG(const vpImage<unsigned char> &I, double& u, double& v) if (npoint < 5) { - vpERROR_TRACE("Dot to small") ; + //vpERROR_TRACE("Dot to small") ; throw(vpTrackingException(vpTrackingException::featureLostError, "Dot to small")) ; } if (npoint > nbMaxPoint) { - vpERROR_TRACE("Too many point %lf (%lf%%). Max allowed is %lf (%lf%%). This threshold can be modified using the setMaxDotSize() method.", - npoint, npoint / (I.getWidth() * I.getHeight()), - nbMaxPoint, maxDotSizePercentage) ; +// vpERROR_TRACE("Too many point %lf (%lf%%). Max allowed is %lf (%lf%%). This threshold can be modified using the setMaxDotSize() method.", +// npoint, npoint / (I.getWidth() * I.getHeight()), +// nbMaxPoint, maxDotSizePercentage) ; - throw(vpTrackingException(vpTrackingException::featureLostError, - "Dot to big")) ; + throw(vpTrackingException(vpTrackingException::featureLostError, + "Too many point %lf (%lf%%). Max allowed is %lf (%lf%%). This threshold can be modified using the setMaxDotSize() method.", + npoint, npoint / (I.getWidth() * I.getHeight()), + nbMaxPoint, maxDotSizePercentage)) ; } } @@ -663,10 +679,9 @@ vpDot::initTracking(const vpImage<unsigned char>& I) try { track( I ); } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e) ; } } @@ -718,10 +733,9 @@ vpDot::initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip) try { track( I ); } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e) ; } } @@ -743,10 +757,10 @@ vpDot::initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip) \param ip : Location of the starting point from which the dot will be tracked in the image. - \param gray_level_min : Minimum gray level threshold used to segment the dot; + \param level_min : Minimum gray level threshold used to segment the dot; value comprised between 0 and 255. - \param gray_level_max : Maximum gray level threshold used to segment the + \param level_max : Maximum gray level threshold used to segment the dot; value comprised between 0 and 255. \e gray_level_max should be greater than \e gray_level_min. @@ -754,21 +768,20 @@ vpDot::initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip) */ void vpDot::initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip, - unsigned int gray_level_min, unsigned int gray_level_max) + unsigned int level_min, unsigned int level_max) { cog = ip ; - this->gray_level_min = gray_level_min; - this->gray_level_max = gray_level_max; + this->gray_level_min = level_min; + this->gray_level_max = level_max; try { track( I ); } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e) ; } } @@ -813,10 +826,9 @@ vpDot::track(const vpImage<unsigned char> &I) } } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e) ; } } @@ -832,14 +844,14 @@ vpDot::track(const vpImage<unsigned char> &I) \param I : Image to process. - \param cog [out] : Sub pixel coordinate of the tracked dot. + \param ip [out] : Sub pixel coordinate of the tracked dot center of gravity. */ void -vpDot::track(const vpImage<unsigned char> &I, vpImagePoint &cog) +vpDot::track(const vpImage<unsigned char> &I, vpImagePoint &ip) { track( I ) ; - cog = this->cog; + ip = this->cog; } /*! @@ -847,12 +859,11 @@ vpDot::track(const vpImage<unsigned char> &I, vpImagePoint &cog) \param I : Image. \param color : The color used for the display. - \param thickness : Thickness of the displayed cross located at the dot cog. + \param thick : Thickness of the displayed cross located at the dot cog. */ -void vpDot::display(const vpImage<unsigned char>& I, vpColor color, - unsigned int thickness) +void vpDot::display(const vpImage<unsigned char>& I, vpColor color, unsigned int thick) const { - vpDisplay::displayCross(I, cog, 3*thickness+8, color, thickness); + vpDisplay::displayCross(I, cog, 3*thickness+8, color, thick); std::list<vpImagePoint>::const_iterator it; for (it = ip_edges_list.begin(); it != ip_edges_list.end(); ++it) @@ -865,7 +876,7 @@ void vpDot::display(const vpImage<unsigned char>& I, vpColor color, Set the precision of the gray level of the dot. - \param grayLevelPrecision : It is a double precision float which value is + \param precision : It is a double precision float which value is in ]0,1]: - 1 means full precision, whereas values close to 0 show a very bad accuracy. - Values lower or equal to 0 are brought back to an epsion>0 @@ -878,7 +889,7 @@ void vpDot::display(const vpImage<unsigned char>& I, vpColor color, \sa setWidth(), setHeight(), setGrayLevelMin(), setGrayLevelMax() */ -void vpDot::setGrayLevelPrecision( const double & grayLevelPrecision ) +void vpDot::setGrayLevelPrecision( const double & precision ) { double epsilon = 0.05; if( grayLevelPrecision<epsilon ) @@ -891,7 +902,7 @@ void vpDot::setGrayLevelPrecision( const double & grayLevelPrecision ) } else { - this->grayLevelPrecision = grayLevelPrecision; + this->grayLevelPrecision = precision; } } @@ -949,5 +960,13 @@ void vpDot::display(const vpImage<vpRGBa>& I,const vpImagePoint &cog, } } +/*! + Writes the dot center of gravity coordinates in the frame (i,j) (For more details + about the orientation of the frame see the vpImagePoint documentation) to the stream \e os, + and returns a reference to the stream. +*/ +VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot& d) { + return (os << "(" << d.getCog() << ")" ) ; +} ; diff --git a/src/tracking/dots/vpDot.h b/src/tracking/dots/vpDot.h index bd4debb0670fc6c38d2e49496fec1c9e5d2d19e8..558deeeb6b745ea3b7521c721b531520c7b05a93 100644 --- a/src/tracking/dots/vpDot.h +++ b/src/tracking/dots/vpDot.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpDot.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpDot.h 4943 2014-11-03 13:51:09Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,21 +48,22 @@ #ifndef vpDot_hh #define vpDot_hh +#include <visp/vpConfig.h> #include <visp/vpImage.h> #include <visp/vpDisplay.h> #include <visp/vpTracker.h> #include <visp/vpRect.h> #include <visp/vpImagePoint.h> -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -# include <visp/vpList.h> -#endif - #include <math.h> #include <fstream> #include <list> #include <vector> +#ifdef VISP_USE_MSVC +# pragma comment(linker, "/STACK:256000000") // Increase max recursion depth +#endif + /*! \class vpDot @@ -200,7 +201,7 @@ public : virtual ~vpDot() ; void display(const vpImage<unsigned char>& I, vpColor color = vpColor::red, - unsigned int thickness=1); + unsigned int thickness=1) const; /*! @@ -209,7 +210,7 @@ public : \sa getWidth(), getHeight() */ - inline vpRect getBBox() { + inline vpRect getBBox() const { vpRect bbox; bbox.setRect(this->u_min, @@ -233,7 +234,7 @@ public : \warning Doesn't return the image points inside the dot anymore. To get those points see getConnexities(). */ - inline std::list<vpImagePoint> getEdges() { + inline std::list<vpImagePoint> getEdges() const { return this->ip_edges_list; }; @@ -245,11 +246,11 @@ public : This list is updated after a call to track(). */ - inline std::list<vpImagePoint> getConnexities() { + inline std::list<vpImagePoint> getConnexities() const { return this->ip_connexities_list; }; - inline double getGamma() {return this->gamma;}; + inline double getGamma() const {return this->gamma;}; /*! Return the precision of the gray level of the dot. It is a double @@ -258,13 +259,13 @@ public : */ double getGrayLevelPrecision() const {return grayLevelPrecision;} - double getMaxDotSize(){ + double getMaxDotSize() const { return this->maxDotSizePercentage; } /*! Return the mean gray level value of the dot. */ - double getMeanGrayLevel() { + double getMeanGrayLevel() const { return (this->mean_gray_level); }; @@ -298,22 +299,15 @@ public : vpDot& operator =(const vpDot& d) ; bool operator ==(const vpDot& d); bool operator !=(const vpDot& d); - /*! - Writes the dot center of gravity coordinates in the frame (i,j) (For more details - about the orientation of the frame see the vpImagePoint documentation) to the stream \e os, - and returns a reference to the stream. - */ - friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot& d) { - return (os << "(" << d.getCog() << ")" ) ; - } ; + friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot& d); void print(std::ostream& os) { os << *this << std::endl ; } /*! - Initialize the dot coordinates with \e cog. + Initialize the dot coordinates with \e ip. */ - inline void setCog(const vpImagePoint &cog) { - this->cog = cog; + inline void setCog(const vpImagePoint &ip) { + this->cog = ip; } /*! @@ -335,13 +329,13 @@ public : /*! Set the type of connexity: 4 or 8. */ - void setConnexity(vpConnexityType connexityType) {this->connexityType = connexityType; }; + void setConnexity(vpConnexityType type) {this->connexityType = type; }; void setMaxDotSize(double percentage) ; - void setGrayLevelMin( const unsigned int &gray_level_min ) { - this->gray_level_min = gray_level_min; + void setGrayLevelMin( const unsigned int &level_min ) { + this->gray_level_min = level_min; }; - void setGrayLevelMax( const unsigned int &gray_level_max ) { - this->gray_level_max = gray_level_max; + void setGrayLevelMax( const unsigned int &level_max ) { + this->gray_level_max = level_max; }; void setGrayLevelPrecision( const double & grayLevelPrecision ); @@ -364,7 +358,7 @@ public : \sa setGraphics() */ - void setGraphicsThickness(unsigned int thickness) {this->thickness = thickness;}; + void setGraphicsThickness(unsigned int t) {this->thickness = t;}; void track(const vpImage<unsigned char> & I) ; void track(const vpImage<unsigned char> & I, vpImagePoint &ip) ; @@ -423,45 +417,6 @@ public: static void display(const vpImage<vpRGBa>& I,const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list, vpColor color = vpColor::red, unsigned int thickness=1); - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - /*! - @name Deprecated functions - */ - /*! - - \deprecated This method is deprecated since the naming is not representative regarding to its functionality. \n - Previously it returned all the points inside the dot. To get the equivalent, use getConnexities(). \n \n - If you rather want to get the points on the dot border use getEdges(). - - \param edges_list : The list of all the images points on the dot - border. This list is update after a call to track(). - - */ - vp_deprecated void getEdges(std::list<vpImagePoint> &edges_list) { - edges_list = this->ip_edges_list; - }; - - /*! - - \deprecated This method is deprecated. You should use - getEdges(std::list<vpImagePoint> &) instead.\n \n - Return the list of all the image points on the dot - border. - - \param connexities_list : The list of all the images points on the dot - border. This list is update after a call to track(). - - */ - vp_deprecated void getConnexities(vpList<vpImagePoint> &connexities_list) { - // convert a vpList in a std::list - connexities_list.kill(); - std::list<vpImagePoint>::const_iterator it; - for (it = ip_connexities_list.begin(); it != ip_connexities_list.end(); ++it) { - connexities_list += *it; - } - }; -#endif } ; #endif diff --git a/src/tracking/dots/vpDot2.cpp b/src/tracking/dots/vpDot2.cpp index a2bf66a692b1fb6e011575887941062ffbc36038..5edc64a4ae963a184bce2d7fe0350f0479a67643 100644 --- a/src/tracking/dots/vpDot2.cpp +++ b/src/tracking/dots/vpDot2.cpp @@ -3,7 +3,7 @@ * $Id: vpDot2.cpp 2135 2009-04-29 13:51:31Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -89,7 +89,8 @@ void vpDot2::init() ellipsoidShapePrecision = 0.65; maxSizeSearchDistancePrecision = 0.65; setEllipsoidBadPointsPercentage(); - m00 = m11 = m02 = m20 = m10 = m01 = 0 ; + m00 = m11 = m02 = m20 = m10 = m01 = 0.; + mu11 = mu02 = mu20 = 0.; bbox_u_min = bbox_u_max = bbox_v_min = bbox_v_max = 0; @@ -104,9 +105,15 @@ void vpDot2::init() /*! Default constructor. Just do basic default initialization. */ -vpDot2::vpDot2() : vpTracker() +vpDot2::vpDot2() + : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), cog(), width(0), height(0), surface(0), + gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5), + sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65), + allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), + graphics(false), thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), + firstBorder_u(0), firstBorder_v() { - init(); } /*! @@ -117,17 +124,30 @@ vpDot2::vpDot2() : vpTracker() \param ip : An image point with sub-pixel coordinates. */ -vpDot2::vpDot2(const vpImagePoint &ip) : vpTracker() +vpDot2::vpDot2(const vpImagePoint &ip) + : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), cog(), width(0), height(0), surface(0), + gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5), + sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65), + allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), + graphics(false), thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), + firstBorder_u(0), firstBorder_v() { - init() ; - cog = ip; } /*! Copy constructor. */ -vpDot2::vpDot2(const vpDot2& twinDot ) : vpTracker() +vpDot2::vpDot2(const vpDot2& twinDot ) + : vpTracker(twinDot), + m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), + mu11(0.), mu20(0.), mu02(0.), cog(), width(0), height(0), surface(0), + gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5), + sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65), + allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), + graphics(false), thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), + firstBorder_u(0), firstBorder_v() { *this = twinDot; } @@ -135,7 +155,7 @@ vpDot2::vpDot2(const vpDot2& twinDot ) : vpTracker() /*! = operator. */ -void vpDot2::operator=(const vpDot2& twinDot ) +vpDot2& vpDot2::operator=(const vpDot2& twinDot ) { cog = twinDot.cog; @@ -178,6 +198,8 @@ void vpDot2::operator=(const vpDot2& twinDot ) mu11 = twinDot.mu11; mu20 = twinDot.mu20; mu02 = twinDot.mu02; + + return (*this); } /*! @@ -196,12 +218,11 @@ vpDot2::~vpDot2(){} \param I : Image. \param color : The color used for the display. - \param thickness : Thickness of the displayed cross located at the dot cog. + \param t : Thickness of the displayed cross located at the dot cog. */ -void vpDot2::display(const vpImage<unsigned char>& I, vpColor color, - unsigned int thickness) +void vpDot2::display(const vpImage<unsigned char>& I, vpColor color, unsigned int t) const { - vpDisplay::displayCross(I, cog, 3*thickness+8, color, thickness); + vpDisplay::displayCross(I, cog, 3*t+8, color, t); std::list<vpImagePoint>::const_iterator it; for (it = ip_edges_list.begin(); it != ip_edges_list.end(); ++it) @@ -269,10 +290,10 @@ void vpDot2::initTracking(const vpImage<unsigned char>& I, unsigned int size) try { track( I ); } - catch(...) + catch(vpException e) { - vpERROR_TRACE("Error caught") ; - throw ; + //vpERROR_TRACE("Error caught") ; + throw(e) ; } } @@ -331,10 +352,10 @@ void vpDot2::initTracking(const vpImage<unsigned char>& I, try { track( I ); } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + //vpERROR_TRACE("Error caught") ; + throw(e) ; } } @@ -353,10 +374,10 @@ void vpDot2::initTracking(const vpImage<unsigned char>& I, \param ip : Location of the starting point from which the dot will be tracked in the image. - \param gray_level_min : Minimum gray level threshold used to segment the dot; + \param gray_lvl_min : Minimum gray level threshold used to segment the dot; value comprised between 0 and 255. - \param gray_level_max : Maximum gray level threshold used to segment the + \param gray_lvl_max : Maximum gray level threshold used to segment the dot; value comprised between 0 and 255. \e gray_level_max should be greater than \e gray_level_min. @@ -379,14 +400,14 @@ void vpDot2::initTracking(const vpImage<unsigned char>& I, */ void vpDot2::initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip, - unsigned int gray_level_min, - unsigned int gray_level_max, + unsigned int gray_lvl_min, + unsigned int gray_lvl_max, unsigned int size) { cog = ip ; - this->gray_level_min = gray_level_min; - this->gray_level_max = gray_level_max; + this->gray_level_min = gray_lvl_min; + this->gray_level_max = gray_lvl_max; setWidth(size); setHeight(size); @@ -394,10 +415,10 @@ void vpDot2::initTracking(const vpImage<unsigned char>& I, try { track( I ); } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + //vpERROR_TRACE("Error caught") ; + throw(e) ; } } @@ -509,7 +530,7 @@ void vpDot2::track(const vpImage<unsigned char> &I) // in the area, return an error tracking. if( candidates.empty() ) { - vpERROR_TRACE("No dot was found") ; + //vpERROR_TRACE("No dot was found") ; throw(vpTrackingException(vpTrackingException::featureLostError, "No dot was found")) ; } @@ -556,9 +577,9 @@ void vpDot2::track(const vpImage<unsigned char> &I) // if this dot is partially out of the image, return an error tracking. if( !isInImage( I ) ) { - vpERROR_TRACE("The center of gravity of the dot is not in the image") ; + //vpERROR_TRACE("The center of gravity of the dot is not in the image") ; throw(vpTrackingException(vpTrackingException::featureLostError, - "No dot was found")) ; + "The center of gravity of the dot is not in the image")) ; } // Get dots center of gravity @@ -598,7 +619,7 @@ void vpDot2::track(const vpImage<unsigned char> &I) \param I : Image to process. - \param cog [out] : Sub pixel coordinate of the tracked dot. + \param ip [out] : Sub pixel coordinate of the tracked dot center of gravity. The behavior of this method is similar to the following code: \code @@ -610,11 +631,11 @@ void vpDot2::track(const vpImage<unsigned char> &I) \sa track() */ void -vpDot2::track(const vpImage<unsigned char> &I, vpImagePoint &cog) +vpDot2::track(const vpImage<unsigned char> &I, vpImagePoint &ip) { track(I); - cog = this->cog; + ip = this->cog; } ///// GET METHODS ///////////////////////////////////////////////////////////// @@ -639,20 +660,6 @@ double vpDot2::getHeight() const return height; } -/*! - Return the area of the dot. - - \warning This function is deprecated since it is mis named. Surface means here area. - You should rather use getArea(). - - The area of the dot is also given by \f$|m00|\f$. - - \sa getArea() -*/ -double vpDot2::getSurface() const -{ - return fabs(surface); -} /*! Return the area of the dot. @@ -724,13 +731,13 @@ double vpDot2::getDistance( const vpDot2& distantDot ) const Set the width of the dot. This is meant to be used to search a dot in an area. - \param width : Width of a dot to search in a region of interest. + \param w : Width of a dot to search in a region of interest. \sa setHeight(), setArea(), setSizePrecision() */ -void vpDot2::setWidth( const double & width ) +void vpDot2::setWidth( const double & w ) { - this->width = width; + this->width = w; } /*! @@ -738,50 +745,35 @@ void vpDot2::setWidth( const double & width ) Set the height of the dot. This is meant to be used to search a dot in an area. - \param height : Height of a dot to search in a region of interest. + \param h : Height of a dot to search in a region of interest. \sa setWidth(), setArea(), setSizePrecision() */ -void vpDot2::setHeight( const double & height ) +void vpDot2::setHeight( const double & h ) { - this->height = height; + this->height = h; } -/*! - - Set the surface of the dot. This is meant to be used to search a dot in a region of interest. - - \warning This function is deprecated since it is mis named. You should rather use setArea() - - \param surface : Here surface means area. Area of a dot to search in a region of interest. - - \sa setWidth(), setHeight(), setArea(), setSizePrecision() - -*/ -void vpDot2::setSurface( const double & surface ) -{ - this->surface = surface; -} /*! Set the area of the dot. This is meant to be used to search a dot in a region of interest. - \param area : Area of a dot to search in a region of interest. + \param a : Area of a dot to search in a region of interest. \sa setWidth(), setHeight(), setSizePrecision() */ -void vpDot2::setArea( const double & area ) +void vpDot2::setArea( const double & a ) { - this->surface = area; + this->surface = a; } /*! Set the precision of the gray level of the dot. - \param grayLevelPrecision : It is a double precision float which value is in ]0,1]: + \param precision : It is a double precision float which value is in ]0,1]: - 1 means full precision, whereas values close to 0 show a very bad accuracy. - Values lower or equal to 0 are brought back to an epsilon>0 - Values higher than 1 are brought back to 1 @@ -793,7 +785,7 @@ void vpDot2::setArea( const double & area ) \sa setGrayLevelMin(), setGrayLevelMax() */ -void vpDot2::setGrayLevelPrecision( const double & grayLevelPrecision ) +void vpDot2::setGrayLevelPrecision( const double & precision ) { double epsilon = 0.05; if( grayLevelPrecision<epsilon ) @@ -806,14 +798,14 @@ void vpDot2::setGrayLevelPrecision( const double & grayLevelPrecision ) } else { - this->grayLevelPrecision = grayLevelPrecision; + this->grayLevelPrecision = precision; } } /*! Set the precision of the size of the dot. Used to test the validity of the dot - \param sizePrecision : It is a double precision float which value is in [0,1]: + \param precision : It is a double precision float which value is in [0,1]: - this is the limit ratio between the tested parameter and the measured one. minSize = sizePrecision*originalSize ; maxSize = originalSize/sizePrecision ; - 1 means full precision, whereas values close to 0 show a very bad accuracy. @@ -823,7 +815,7 @@ void vpDot2::setGrayLevelPrecision( const double & grayLevelPrecision ) \sa setWidth(), setHeight(), setArea() */ -void vpDot2::setSizePrecision( const double & sizePrecision ) +void vpDot2::setSizePrecision( const double & precision ) { if( sizePrecision<0 ) { @@ -835,13 +827,13 @@ void vpDot2::setSizePrecision( const double & sizePrecision ) } else { - this->sizePrecision = sizePrecision; + this->sizePrecision = precision; } } /*! Indicates if the dot should have an ellipsoid shape to be valid. - \param ellipsoidShapePrecision : It is a double precision float which value is in [0,1]: + \param precision : It is a double precision float which value is in [0,1]: - 1 means full precision, whereas values close to 0 show a very bad accuracy. - Values lower or equal to 0 are brought back to 0. @@ -868,7 +860,7 @@ void vpDot2::setSizePrecision( const double & sizePrecision ) \sa getEllipsoidShapePrecision() */ -void vpDot2::setEllipsoidShapePrecision(const double & ellipsoidShapePrecision) { +void vpDot2::setEllipsoidShapePrecision(const double & precision) { if( ellipsoidShapePrecision<0 ) { @@ -880,7 +872,7 @@ void vpDot2::setEllipsoidShapePrecision(const double & ellipsoidShapePrecision) } else { - this->ellipsoidShapePrecision = ellipsoidShapePrecision; + this->ellipsoidShapePrecision = precision; } } @@ -889,7 +881,7 @@ void vpDot2::setEllipsoidShapePrecision(const double & ellipsoidShapePrecision) Set the precision of the search maximum distance to get the starting point on a dot border. A too low value mean a large search area. - \param maxSizeSearchDistancePrecision : It is a double precision float which value is in [0.05,1]: + \param precision : It is a double precision float which value is in [0.05,1]: - this is the limit ratio between the tested parameter and the measured one. distance < getWidth()/(getSizePrecision()+epsilon); - 1 means full precision, whereas values close to 0 show a very bad accuracy. @@ -897,7 +889,7 @@ void vpDot2::setEllipsoidShapePrecision(const double & ellipsoidShapePrecision) - Values higher than 1 are brought back to 1. */ -void vpDot2::setMaxSizeSearchDistancePrecision( const double & maxSizeSearchDistancePrecision ) +void vpDot2::setMaxSizeSearchDistancePrecision( const double & precision ) { double epsilon = 0.05; if( maxSizeSearchDistancePrecision<epsilon ) @@ -910,7 +902,7 @@ void vpDot2::setMaxSizeSearchDistancePrecision( const double & maxSizeSearchDist } else { - this->maxSizeSearchDistancePrecision = maxSizeSearchDistancePrecision; + this->maxSizeSearchDistancePrecision = precision; } } @@ -975,572 +967,6 @@ void ///// CLASS FUNCTIONALITY //////////////////////////////////////////////////// -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -/*! - - \deprecated This method is deprecated. You should use - searchDotsInArea(vpImage<unsigned char>&, std::list<vpDot2> &) instead.\n \n - Look for a list of dot matching this dot parameters within the entire - image. - - \warning Allocates memory for the list of dots returned by this method. - Desallocation has to be done by yourself. - - \param I : Image. - - Before calling this method, dot characteristics to found have to be set like: - - \code - vpDot2 d; - - // Set dot characteristics for the auto detection - d.setWidth(15.0); - d.setHeight(12.0); - d.setArea(124); - d.setGrayLevelMin(164); - d.setGrayLevelMax(255); - d.setAccuracy(0.65); - \endcode - - To search dots in the whole image: - \code - vpList<vpDot2> * list_d; - list_d = d.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight()) ; - \endcode - - The number of dots found in the area is given by: - \code - std::cout << list_d->nbElement(); - \endcode - - To parse all the dots: - \code - list_d->front(); - while (! list_d->outside()) { - vpDot2 tmp_d; - tmp_d = list_d->value() ; // A matching dot found in the image - list_d->next() ; - } - \endcode - - To free memory associated to the list of dots: - \code - list_d->kill(); - delete list_d; - \endcode - -*/ -vpList<vpDot2>* vpDot2::searchDotsInArea(const vpImage<unsigned char>& I) -{ - vpList<vpDot2>* niceDotsVector = new vpList<vpDot2>(); - - // To avoid a warning due to the usage of the following deprecated function - // we duplicate the code - { - // niceDotsVector = searchDotsInArea( I, 0, 0, I.getWidth(), I.getHeight()); - } - { - // Fit the input area in the image; we keep only the common part between this - // area and the image. - int area_u = 0; - int area_v = 0; - unsigned int area_w = I.getWidth(); - unsigned int area_h = I.getHeight(); - setArea(I, area_u, area_v, area_w, area_h); - - // compute the size of the search grid - unsigned int gridWidth; - unsigned int gridHeight; - getGridSize( gridWidth, gridHeight ); - - if (graphics) { - // Display the area were the dot is search - vpDisplay::displayRectangle(I, area, vpColor::blue, false, thickness); - //vpDisplay::flush(I); - } - - // start the search loop; for all points of the search grid, - // test if the pixel belongs to a valid dot. - // if it is so eventually add it to the vector of valid dots. - // vpList<vpDot2>* niceDotsVector = new vpList<vpDot2>(); - vpList<vpDot2>* badDotsVector = new vpList<vpDot2>(); - - vpDot2* dotToTest = NULL; - vpDot2 tmpDot; - - unsigned int area_u_min = (unsigned int) area.getLeft(); - unsigned int area_u_max = (unsigned int) area.getRight(); - unsigned int area_v_min = (unsigned int) area.getTop(); - unsigned int area_v_max = (unsigned int) area.getBottom(); - - unsigned int u, v; - vpImagePoint cogTmpDot; - - for( v=area_v_min ; v<area_v_max ; v=v+gridHeight ) - { - for( u=area_u_min ; u<area_u_max ; u=u+gridWidth ) - { - // if the pixel we're in doesn't have the right color (outside the - // graylevel interval), no need to check futher, just get to the - // next grid intersection. - if( !hasGoodLevel(I, u, v) ) continue; - - // Test if an other germ is inside the bounding box of a dot previously - // detected - bool good_germ = true; - niceDotsVector->front(); - while( !niceDotsVector->outside() && good_germ == true) { - tmpDot = niceDotsVector->value(); - - cogTmpDot = tmpDot.getCog(); - double u0 = cogTmpDot.get_u(); - double v0 = cogTmpDot.get_v(); - double half_w = tmpDot.getWidth() / 2.; - double half_h = tmpDot.getHeight() / 2.; - - if ( u >= (u0-half_w) && u <= (u0+half_w) && - v >= (v0-half_h) && v <= (v0+half_h) ) { - // Germ is in a previously detected dot - good_germ = false; - } - niceDotsVector->next(); - } - - if (! good_germ) - continue; - - // Compute the right border position for this possible germ - unsigned int border_u; - unsigned int border_v; - if(findFirstBorder(I, u, v, border_u, border_v) == false){ - // germ is not good. - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - badDotsVector->front(); - #define vpBAD_DOT_VALUE (badDotsVector->value()) - vpImagePoint cogBadDot; - - std::list<vpImagePoint>::const_iterator it_edges; - while( !badDotsVector->outside() && good_germ == true) - { - if( (double)u >= vpBAD_DOT_VALUE.bbox_u_min - && (double)u <= vpBAD_DOT_VALUE.bbox_u_max && - (double)v >= vpBAD_DOT_VALUE.bbox_v_min - && (double)v <= vpBAD_DOT_VALUE.bbox_v_max) - { - - it_edges = ip_edges_list.begin(); - while (it_edges != ip_edges_list.end() && good_germ == true) - { - // Test if the germ belong to a previously detected dot: - // - from the germ go right to the border and compare this - // position to the list of pixels of previously detected dots - cogBadDot = *it_edges; - //if( border_u == cogBadDot.get_u() && v == cogBadDot.get_v()) { - if( (std::fabs(border_u - cogBadDot.get_u()) <= vpMath::maximum(std::fabs((double)border_u), std::fabs(cogBadDot.get_u()))*std::numeric_limits<double>::epsilon() ) - && - (std::fabs(v - cogBadDot.get_v()) <= vpMath::maximum(std::fabs((double)v), std::fabs(cogBadDot.get_v()))*std::numeric_limits<double>::epsilon() )) - { - good_germ = false; - } - ++ it_edges; - } - } - badDotsVector->next(); - } - #undef vpBAD_DOT_VALUE - - if (! good_germ) { - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - vpTRACE(4, "Try germ (%d, %d)", u, v); - - vpImagePoint germ; - germ.set_u( u ); - germ.set_v( v ); - - // otherwise estimate the width, height and surface of the dot we - // created, and test it. - if( dotToTest != NULL ) delete dotToTest; - dotToTest = getInstance(); - dotToTest->setCog( germ ); - dotToTest->setGrayLevelMin ( getGrayLevelMin() ); - dotToTest->setGrayLevelMax ( getGrayLevelMax() ); - dotToTest->setGrayLevelPrecision( getGrayLevelPrecision() ); - dotToTest->setSizePrecision( getSizePrecision() ); - dotToTest->setGraphics( graphics ); - dotToTest->setGraphicsThickness( thickness ); - dotToTest->setComputeMoments( true ); - dotToTest->setArea( area ); - dotToTest->setEllipsoidShapePrecision( ellipsoidShapePrecision ); - - // first compute the parameters of the dot. - // if for some reasons this caused an error tracking - // (dot partially out of the image...), check the next intersection - if( dotToTest->computeParameters( I ) == false ) { - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - // if the dot to test is valid, - if( dotToTest->isValid( I, *this ) ) - { - vpImagePoint cogDotToTest = dotToTest->getCog(); - // Compute the distance to the center. The center used here is not the - // area center available by area.getCenter(area_center_u, - // area_center_v) but the center of the input area which may be - // partially outside the image. - - double area_center_u = area_u + area_w/2.0 - 0.5; - double area_center_v = area_v + area_h/2.0 - 0.5; - - double thisDiff_u = cogDotToTest.get_u() - area_center_u; - double thisDiff_v = cogDotToTest.get_v() - area_center_v; - double thisDist = sqrt( thisDiff_u*thisDiff_u + thisDiff_v*thisDiff_v); - - bool stopLoop = false; - niceDotsVector->front(); - - while( !niceDotsVector->outside() && stopLoop == false ) - { - vpDot2 tmpDot = niceDotsVector->value(); - - //double epsilon = 0.001; // detecte +sieurs points - double epsilon = 3.0; - // if the center of the dot is the same than the current - // don't add it, test the next point of the grid - cogTmpDot = tmpDot.getCog(); - - if( fabs( cogTmpDot.get_u() - cogDotToTest.get_u() ) < epsilon && - fabs( cogTmpDot.get_v() - cogDotToTest.get_v() ) < epsilon ) - { - stopLoop = true; - // Jump all the pixels between v,u and v, tmpDot->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - double otherDiff_u = cogTmpDot.get_u() - area_center_u; - double otherDiff_v = cogTmpDot.get_v() - area_center_v; - double otherDist = sqrt( otherDiff_u*otherDiff_u + - otherDiff_v*otherDiff_v ); - - - // if the distance of the curent vector element to the center - // is greater than the distance of this dot to the center, - // then add this dot before the current vector element. - if( otherDist > thisDist ) - { - niceDotsVector->addLeft( *dotToTest ); - niceDotsVector->next(); - stopLoop = true; - // Jump all the pixels between v,u and v, tmpDot->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - niceDotsVector->next(); - } - vpTRACE(4, "End while (%d, %d)", u, v); - - // if we reached the end of the vector without finding the dot - // or inserting it, insert it now. - if( niceDotsVector->outside() && stopLoop == false ) - { - niceDotsVector->end(); - niceDotsVector->addRight( *dotToTest ); - } - } - else { - // Store bad dots - badDotsVector->front(); - badDotsVector->addRight( *dotToTest ); - } - } - } - if( dotToTest != NULL ) delete dotToTest; - - delete badDotsVector; - - //return niceDotsVector; - } - - - return niceDotsVector; - -} - -/*! - - \deprecated This method is deprecated. You should use - searchDotsInArea(vpImage<unsigned char>&, int, int, unsigned int, unsigned int, std::list<vpDot2> &) instead.\n \n - Look for a list of dot matching this dot parameters within a rectangle - search area in the image. The rectangle upper-left coordinates are given by - (\e area_u, \e area_v). The size of the rectangle is given by \e area_w and - \e area_h. - - \param I : Image to process. - \param area_u : Coordinate (column) of the upper-left area corner. - \param area_v : Coordinate (row) of the upper-left area corner. - - \param area_w : Width or the area in which a dot is searched. - \param area_h : Height or the area in which a dot is searched. - - \warning Allocates memory for the list of vpDot2 returned by this method. - Desallocation has to be done by yourself, see searchDotsInArea() - - \sa searchDotsInArea(vpImage<unsigned char>& I) -*/ -vpList<vpDot2>* -vpDot2::searchDotsInArea(const vpImage<unsigned char>& I, - int area_u, - int area_v, - unsigned int area_w, - unsigned int area_h) - -{ - // Fit the input area in the image; we keep only the common part between this - // area and the image. - setArea(I, area_u, area_v, area_w, area_h); - - // compute the size of the search grid - unsigned int gridWidth; - unsigned int gridHeight; - getGridSize( gridWidth, gridHeight ); - - if (graphics) { - // Display the area were the dot is search - vpDisplay::displayRectangle(I, area, vpColor::blue, false, thickness); - //vpDisplay::flush(I); - } - - // start the search loop; for all points of the search grid, - // test if the pixel belongs to a valid dot. - // if it is so eventually add it to the vector of valid dots. - vpList<vpDot2>* niceDotsVector = new vpList<vpDot2>(); - vpList<vpDot2>* badDotsVector = new vpList<vpDot2>(); - - vpDot2* dotToTest = NULL; - vpDot2 tmpDot; - - unsigned int area_u_min = (unsigned int) area.getLeft(); - unsigned int area_u_max = (unsigned int) area.getRight(); - unsigned int area_v_min = (unsigned int) area.getTop(); - unsigned int area_v_max = (unsigned int) area.getBottom(); - - unsigned int u, v; - vpImagePoint cogTmpDot; - - for( v=area_v_min ; v<area_v_max ; v=v+gridHeight ) - { - for( u=area_u_min ; u<area_u_max ; u=u+gridWidth ) - { - // if the pixel we're in doesn't have the right color (outside the - // graylevel interval), no need to check futher, just get to the - // next grid intersection. - if( !hasGoodLevel(I, u, v) ) continue; - - // Test if an other germ is inside the bounding box of a dot previoulsy - // detected - bool good_germ = true; - niceDotsVector->front(); - while( !niceDotsVector->outside() && good_germ == true) { - tmpDot = niceDotsVector->value(); - - cogTmpDot = tmpDot.getCog(); - double u0 = cogTmpDot.get_u(); - double v0 = cogTmpDot.get_v(); - double half_w = tmpDot.getWidth() / 2.; - double half_h = tmpDot.getHeight() / 2.; - - if ( u >= (u0-half_w) && u <= (u0+half_w) && - v >= (v0-half_h) && v <= (v0+half_h) ) { - // Germ is in a previously detected dot - good_germ = false; - } - niceDotsVector->next(); - } - - if (! good_germ) - continue; - - // Compute the right border position for this possible germ - unsigned int border_u; - unsigned int border_v; - if(findFirstBorder(I, u, v, border_u, border_v) == false){ - // germ is not good. - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - badDotsVector->front(); -#define vpBAD_DOT_VALUE (badDotsVector->value()) - vpImagePoint cogBadDot; - - std::list<vpImagePoint>::const_iterator it_edges; - while( !badDotsVector->outside() && good_germ == true) - { - if( (double)u >= vpBAD_DOT_VALUE.bbox_u_min - && (double)u <= vpBAD_DOT_VALUE.bbox_u_max && - (double)v >= vpBAD_DOT_VALUE.bbox_v_min - && (double)v <= vpBAD_DOT_VALUE.bbox_v_max) - { - - it_edges = ip_edges_list.begin(); - while (it_edges != ip_edges_list.end() && good_germ == true) - { - // Test if the germ belong to a previously detected dot: - // - from the germ go right to the border and compare this - // position to the list of pixels of previously detected dots - cogBadDot = *it_edges; - //if( border_u == cogBadDot.get_u() && v == cogBadDot.get_v()) { - if( (std::fabs(border_u - cogBadDot.get_u()) <= vpMath::maximum(std::fabs((double)border_u), std::fabs(cogBadDot.get_u()))*std::numeric_limits<double>::epsilon() ) - && - (std::fabs(v - cogBadDot.get_v()) <= vpMath::maximum(std::fabs((double)v), std::fabs(cogBadDot.get_v()))*std::numeric_limits<double>::epsilon() )) - { - good_germ = false; - } - ++ it_edges; - } - } - badDotsVector->next(); - } -#undef vpBAD_DOT_VALUE - - if (! good_germ) { - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - vpTRACE(4, "Try germ (%d, %d)", u, v); - - vpImagePoint germ; - germ.set_u( u ); - germ.set_v( v ); - - // otherwise estimate the width, height and surface of the dot we - // created, and test it. - if( dotToTest != NULL ) delete dotToTest; - dotToTest = getInstance(); - dotToTest->setCog( germ ); - dotToTest->setGrayLevelMin ( getGrayLevelMin() ); - dotToTest->setGrayLevelMax ( getGrayLevelMax() ); - dotToTest->setGrayLevelPrecision( getGrayLevelPrecision() ); - dotToTest->setSizePrecision( getSizePrecision() ); - dotToTest->setGraphics( graphics ); - dotToTest->setGraphicsThickness( thickness ); - dotToTest->setComputeMoments( true ); - dotToTest->setArea( area ); - dotToTest->setEllipsoidShapePrecision( ellipsoidShapePrecision ); - - // first compute the parameters of the dot. - // if for some reasons this caused an error tracking - // (dot partially out of the image...), check the next intersection - if( dotToTest->computeParameters( I ) == false ) { - // Jump all the pixels between v,u and v, dotToTest->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - // if the dot to test is valid, - if( dotToTest->isValid( I, *this ) ) - { - vpImagePoint cogDotToTest = dotToTest->getCog(); - // Compute the distance to the center. The center used here is not the - // area center available by area.getCenter(area_center_u, - // area_center_v) but the center of the input area which may be - // partially outside the image. - - double area_center_u = area_u + area_w/2.0 - 0.5; - double area_center_v = area_v + area_h/2.0 - 0.5; - - double thisDiff_u = cogDotToTest.get_u() - area_center_u; - double thisDiff_v = cogDotToTest.get_v() - area_center_v; - double thisDist = sqrt( thisDiff_u*thisDiff_u + thisDiff_v*thisDiff_v); - - bool stopLoop = false; - niceDotsVector->front(); - - while( !niceDotsVector->outside() && stopLoop == false ) - { - vpDot2 tmpDot = niceDotsVector->value(); - - //double epsilon = 0.001; // detecte +sieurs points - double epsilon = 3.0; - // if the center of the dot is the same than the current - // don't add it, test the next point of the grid - cogTmpDot = tmpDot.getCog(); - - if( fabs( cogTmpDot.get_u() - cogDotToTest.get_u() ) < epsilon && - fabs( cogTmpDot.get_v() - cogDotToTest.get_v() ) < epsilon ) - { - stopLoop = true; - // Jump all the pixels between v,u and v, tmpDot->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - - double otherDiff_u = cogTmpDot.get_u() - area_center_u; - double otherDiff_v = cogTmpDot.get_v() - area_center_v; - double otherDist = sqrt( otherDiff_u*otherDiff_u + - otherDiff_v*otherDiff_v ); - - - // if the distance of the curent vector element to the center - // is greater than the distance of this dot to the center, - // then add this dot before the current vector element. - if( otherDist > thisDist ) - { - niceDotsVector->addLeft( *dotToTest ); - niceDotsVector->next(); - stopLoop = true; - // Jump all the pixels between v,u and v, tmpDot->getFirstBorder_u() - u = border_u; - v = border_v; - continue; - } - niceDotsVector->next(); - } - vpTRACE(4, "End while (%d, %d)", u, v); - - // if we reached the end of the vector without finding the dot - // or inserting it, insert it now. - if( niceDotsVector->outside() && stopLoop == false ) - { - niceDotsVector->end(); - niceDotsVector->addRight( *dotToTest ); - } - } - else { - // Store bad dots - badDotsVector->front(); - badDotsVector->addRight( *dotToTest ); - } - } - } - if( dotToTest != NULL ) delete dotToTest; - - delete badDotsVector; - - return niceDotsVector; -} -#endif // VISP_BUILD_DEPRECATED_FUNCTIONS - /*! Look for a list of dot matching this dot parameters within the entire @@ -1795,7 +1221,7 @@ void vpDot2::searchDotsInArea(const vpImage<unsigned char>& I, while( itnice != niceDots.end() && stopLoop == false ) { - vpDot2 tmpDot = *itnice; + tmpDot = *itnice; //double epsilon = 0.001; // detecte +sieurs points double epsilon = 3.0; @@ -1874,8 +1300,8 @@ void vpDot2::searchDotsInArea(const vpImage<unsigned char>& I, */ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) { - double sizePrecision = wantedDot.getSizePrecision(); - double ellipsoidShapePrecision = wantedDot.getEllipsoidShapePrecision(); + double size_precision = wantedDot.getSizePrecision(); + double ellipsoidShape_precision = wantedDot.getEllipsoidShapePrecision(); double epsilon = 0.001; // @@ -1890,20 +1316,20 @@ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) (std::fabs(wantedDot.getHeight()) > std::numeric_limits<double>::epsilon()) && (std::fabs(wantedDot.getArea()) > std::numeric_limits<double>::epsilon()) ) - // if (sizePrecision!=0){ - if (std::fabs(sizePrecision) > std::numeric_limits<double>::epsilon()){ + // if (size_precision!=0){ + if (std::fabs(size_precision) > std::numeric_limits<double>::epsilon()){ #ifdef DEBUG std::cout << "test size precision......................\n"; std::cout << "wanted dot: " << "w=" << wantedDot.getWidth() << " h=" << wantedDot.getHeight() << " s=" << wantedDot.getArea() - << " precision=" << sizePrecision + << " precision=" << size_precision << " epsilon=" << epsilon << std::endl; std::cout << "dot found: " << "w=" << getWidth() << " h=" << getHeight() << " s=" << getArea() << std::endl; #endif - if( ( wantedDot.getWidth()*sizePrecision-epsilon < getWidth() ) == false ) + if( ( wantedDot.getWidth()*size_precision-epsilon < getWidth() ) == false ) { vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v()); @@ -1913,62 +1339,62 @@ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) return false; } - if( ( getWidth() < wantedDot.getWidth()/(sizePrecision+epsilon ) )== false ) + if( ( getWidth() < wantedDot.getWidth()/(size_precision+epsilon ) )== false ) { vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v()); #ifdef DEBUG printf("Bad width %g > %g for dot (%g, %g)\n", - getWidth(), wantedDot.getWidth()/(sizePrecision+epsilon), + getWidth(), wantedDot.getWidth()/(size_precision+epsilon), cog.get_u(), cog.get_v()); #endif return false; } - if( ( wantedDot.getHeight()*sizePrecision-epsilon < getHeight() ) == false ) + if( ( wantedDot.getHeight()*size_precision-epsilon < getHeight() ) == false ) { vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v()); #ifdef DEBUG printf("Bad height %g > %g for dot (%g, %g)\n", - wantedDot.getHeight()*sizePrecision-epsilon, getHeight(), + wantedDot.getHeight()*size_precision-epsilon, getHeight(), cog.get_u(), cog.get_v()); #endif return false; } - if( ( getHeight() < wantedDot.getHeight()/(sizePrecision+epsilon )) == false ) + if( ( getHeight() < wantedDot.getHeight()/(size_precision+epsilon )) == false ) { vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v()); #ifdef DEBUG printf("Bad height %g > %g for dot (%g, %g)\n", - getHeight(), wantedDot.getHeight()/(sizePrecision+epsilon), + getHeight(), wantedDot.getHeight()/(size_precision+epsilon), cog.get_u(), cog.get_v()); #endif return false; } - if( ( wantedDot.getArea()*(sizePrecision*sizePrecision)-epsilon < getArea() ) == false ) + if( ( wantedDot.getArea()*(size_precision*size_precision)-epsilon < getArea() ) == false ) { vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v()); #ifdef DEBUG printf("Bad surface %g > %g for dot (%g, %g)\n", - wantedDot.getArea()*(sizePrecision*sizePrecision)-epsilon, + wantedDot.getArea()*(size_precision*size_precision)-epsilon, getArea(), cog.get_u(), cog.get_v()); #endif return false; } - if( ( getArea() < wantedDot.getArea()/(sizePrecision*sizePrecision+epsilon )) == false ) + if( ( getArea() < wantedDot.getArea()/(size_precision*size_precision+epsilon )) == false ) { vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v()); #ifdef DEBUG printf("Bad surface %g < %g for dot (%g, %g)\n", - getArea(), wantedDot.getArea()/(sizePrecision*sizePrecision+epsilon), + getArea(), wantedDot.getArea()/(size_precision*size_precision+epsilon), cog.get_u(), cog.get_v()); #endif return false; @@ -1984,11 +1410,11 @@ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) int nb_max_bad_points = (int)(nb_point_to_test*allowedBadPointsPercentage_); double step_angle = 2*M_PI / nb_point_to_test; - // if (ellipsoidShapePrecision != 0 && compute_moment) { - if (std::fabs(ellipsoidShapePrecision) > std::numeric_limits<double>::epsilon() && compute_moment) { + // if (ellipsoidShape_precision != 0 && compute_moment) { + if (std::fabs(ellipsoidShape_precision) > std::numeric_limits<double>::epsilon() && compute_moment) { // std::cout << "test shape precision......................\n"; // See F. Chaumette. Image moments: a general and useful set of features - // for visual servoing. IEEE Trans. on Robotics, 20(4):713-723, Ao�t 2004. + // for visual servoing. IEEE Trans. on Robotics, 20(4):713-723, August 2004. // mu11 = m11 - m00 * xg * yg = m11 - m00 * m10/m00 * m01/m00 // = m11 - m10 * m01 / m00 @@ -2015,7 +1441,7 @@ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) a1 -= 1.0; a2 -= 1.0; - double innerCoef = ellipsoidShapePrecision ; + double innerCoef = ellipsoidShape_precision ; unsigned int u, v; double cog_u = this->cog.get_u(); double cog_v = this->cog.get_v(); @@ -2060,7 +1486,7 @@ bool vpDot2::isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot ) a1 += 2.0; a2 += 2.0; - double outCoef = 2-ellipsoidShapePrecision; //1.6; + double outCoef = 2-ellipsoidShape_precision; //1.6; nb_bad_points = 0; for( double theta=0. ; theta<2*M_PI ; theta+= step_angle ) { u = (unsigned int) (cog_u + outCoef*(a1*cos(alpha)*cos(theta)-a2*sin(alpha)*sin(theta))); @@ -2188,12 +1614,8 @@ vpDot2* vpDot2::getInstance() return new vpDot2(); } - -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! - \deprecated This method is deprecated. You should use - getFreemanChain(std::list<unsigned int> &) instead.\n \n Returns the list of Freeman chain code used to turn around the dot counterclockwise. @@ -2207,32 +1629,7 @@ vpDot2* vpDot2::getInstance() - 6 : down - 7 : down right */ -void vpDot2::getFreemanChain(vpList<unsigned int> &freeman_chain) -{ - std::list<unsigned int>::const_iterator it; - freeman_chain.kill(); - for (it = direction_list.begin(); it != direction_list.end(); ++it) { - freeman_chain += *it; - } -} -#endif - -/*! - - Returns the list of Freeman chain code used to turn around the dot - counterclockwise. - - \return List of Freeman chain list [0, ..., 7] - - 0 : right - - 1 : top right - - 2 : top - - 3 : top left - - 4 : left - - 5 : down left - - 6 : down - - 7 : down right -*/ -void vpDot2::getFreemanChain(std::list<unsigned int> &freeman_chain) +void vpDot2::getFreemanChain(std::list<unsigned int> &freeman_chain) const { freeman_chain = direction_list; } @@ -2583,59 +1980,59 @@ bool element = (element + 2) % 8; // turn right } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 1) %8 ); + unsigned int _u1 = u; + unsigned int _v1 = v; + updateFreemanPosition( _u1, _v1, (element + 1) %8 ); - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u1, _v1 )) { element = (element + 1) % 8; // turn diag right } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, element ); // same direction + unsigned int _u2 = u; + unsigned int _v2 = v; + updateFreemanPosition( _u2, _v2, element ); // same direction - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u2, _v2 )) { //element = element; // keep same dir } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 7) %8 ); // diag left + unsigned int _u3 = u; + unsigned int _v3 = v; + updateFreemanPosition( _u3, _v3, (element + 7) %8 ); // diag left - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u3, _v3 )) { element = (element + 7) %8; // turn diag left } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 6) %8 ); // left + unsigned int _u4 = u; + unsigned int _v4 = v; + updateFreemanPosition( _u4, _v4, (element + 6) %8 ); // left - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u4, _v4 )) { element = (element + 6) %8 ; // turn left } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 5) %8 ); // left + unsigned int _u5 = u; + unsigned int _v5 = v; + updateFreemanPosition( _u5, _v5, (element + 5) %8 ); // left - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u5, _v5 )) { element = (element + 5) %8 ; // turn diag down } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 4) %8 ); // left + unsigned int _u6 = u; + unsigned int _v6 = v; + updateFreemanPosition( _u6, _v6, (element + 4) %8 ); // left - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u6, _v6 )) { element = (element + 4) %8 ; // turn down } else { - unsigned int _u = u; - unsigned int _v = v; - updateFreemanPosition( _u, _v, (element + 3) %8 ); // diag + unsigned int _u7 = u; + unsigned int _v7 = v; + updateFreemanPosition( _u7, _v7, (element + 3) %8 ); // diag - if ( hasGoodLevel( I, _u, _v )) { + if ( hasGoodLevel( I, _u7, _v7 )) { element = (element + 3) %8 ; // turn diag right down } else { @@ -2879,16 +2276,15 @@ bool vpDot2::isInImage(const vpImage<unsigned char> &I) const \return true if the image point \e ip is in the image and false otherwise. */ -bool vpDot2::isInImage(const vpImage<unsigned char> &I, - const vpImagePoint &ip) const +bool vpDot2::isInImage(const vpImage<unsigned char> &I, const vpImagePoint &ip) const { - unsigned int height = I.getHeight(); - unsigned int width = I.getWidth(); + unsigned int h = I.getHeight(); + unsigned int w = I.getWidth(); double u = ip.get_u(); double v = ip.get_v(); - if( u < 0 || u >= width ) return false; - if( v < 0 || v >= height ) return false; + if( u < 0 || u >= w ) return false; + if( v < 0 || v >= h ) return false; return true; } @@ -3224,3 +2620,45 @@ void vpDot2::display(const vpImage<vpRGBa>& I,const vpImagePoint &cog, vpDisplay::displayPoint(I, *it, color); } } + +/*! + Writes the dot center of gravity coordinates in the frame (i,j) (For more details + about the orientation of the frame see the vpImagePoint documentation) to the stream \e os, + and returns a reference to the stream. +*/ +VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot2& d) { + return (os << "(" << d.getCog() << ")" ) ; +} ; + +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS +/*! + \deprecated Return the area of the dot. + + This function is deprecated since it is mis named. Surface means here area. + You should rather use getArea(). + + The area of the dot is also given by \f$|m00|\f$. + + \sa getArea() +*/ +double vpDot2::getSurface() const +{ + return fabs(surface); +} + +/*! + + \deprecated Set the surface of the dot. This is meant to be used to search a dot in a region of interest. + + This function is deprecated since it is mis named. You should rather use setArea() + + \param s : Here surface means area. Area of a dot to search in a region of interest. + + \sa setWidth(), setHeight(), setArea(), setSizePrecision() + +*/ +void vpDot2::setSurface( const double & s ) +{ + this->surface = s; +} +#endif diff --git a/src/tracking/dots/vpDot2.h b/src/tracking/dots/vpDot2.h index 6e68be89f45ea9c341c5c6d0977ef0dc5fcc42e8..15a0d304c9945cb276b03c2f9a790cad20209adf 100644 --- a/src/tracking/dots/vpDot2.h +++ b/src/tracking/dots/vpDot2.h @@ -1,9 +1,9 @@ - /**************************************************************************** +/**************************************************************************** * * $Id: vpDot2.h 2135 2009-04-29 13:51:31Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,10 +53,6 @@ #include <visp/vpColor.h> #include <visp/vpImagePoint.h> -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -# include <visp/vpList.h> -#endif - #include <vector> #include <list> @@ -109,11 +105,11 @@ is used when there was a problem performing basic tracking of the dot, but can also be used to find a certain type of dots in the full image. - The following sample code available in tutorial-blob-tracker.cpp shows how to + The following sample code available in tutorial-blob-tracker-live-firewire.cpp shows how to grab images from a firewire camera, track a blob and display the tracking results. - \include tutorial-blob-tracker.cpp + \include tutorial-blob-tracker-live-firewire.cpp A line by line explanation of the previous example is provided in \ref tutorial-tracking-blob. @@ -139,7 +135,7 @@ public: static vpMatrix defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage<unsigned char> &I, vpColor col = vpColor::blue, bool trackDot = true); void display(const vpImage<unsigned char>& I, vpColor color = vpColor::red, - unsigned int thickness=1); + unsigned int thickness=1) const; double getArea() const; /*! @@ -149,7 +145,7 @@ public: \sa getWidth(), getHeight() */ - inline vpRect getBBox() { + inline vpRect getBBox() const { vpRect bbox; bbox.setRect(this->bbox_u_min, @@ -178,24 +174,36 @@ public: border. This list is update after a call to track(). */ - void getEdges(std::list<vpImagePoint> &edges_list) { + void getEdges(std::list<vpImagePoint> &edges_list) const { edges_list = this->ip_edges_list; }; + /*! + + Return the list of all the image points on the dot + border. + + \return The list of all the images points on the dot + border. This list is update after a call to track(). + + */ + std::list<vpImagePoint> getEdges() const { + return(this->ip_edges_list); + }; /*! Get the percentage of sampled points that are considered non conform in terms of the gray level on the inner and the ouside ellipses. \sa setEllipsoidBadPointsPercentage() */ - double getEllipsoidBadPointsPercentage() + double getEllipsoidBadPointsPercentage() const { return allowedBadPointsPercentage_; } double getEllipsoidShapePrecision() const; - void getFreemanChain(std::list<unsigned int> &freeman_chain) ; + void getFreemanChain(std::list<unsigned int> &freeman_chain) const; - inline double getGamma() {return this->gamma;}; + inline double getGamma() const {return this->gamma;}; /*! Return the color level of pixels inside the dot. @@ -219,29 +227,21 @@ public: /*! \return The mean gray level value of the dot. */ - double getMeanGrayLevel() { + double getMeanGrayLevel() const { return (this->mean_gray_level); }; double getSizePrecision() const; - double getSurface() const; double getWidth() const; void initTracking(const vpImage<unsigned char>& I, unsigned int size = 0); void initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip, - unsigned int size = 0); + unsigned int size = 0); void initTracking(const vpImage<unsigned char>& I, const vpImagePoint &ip, - unsigned int gray_level_min, unsigned int gray_level_max, - unsigned int size = 0 ); + unsigned int gray_lvl_min, unsigned int gray_lvl_max, + unsigned int size = 0 ); - void operator=(const vpDot2& twinDot ); - /*! - Writes the dot center of gravity coordinates in the frame (i,j) (For more details - about the orientation of the frame see the vpImagePoint documentation) to the stream \e os, - and returns a reference to the stream. - */ - friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot2& d) { - return (os << "(" << d.getCog() << ")" ) ; - } ; + vpDot2& operator=(const vpDot2& twinDot ); + friend VISP_EXPORT std::ostream& operator<< (std::ostream& os, vpDot2& d); void print(std::ostream& os) { os << *this << std::endl ; } void searchDotsInArea(const vpImage<unsigned char>& I, @@ -252,10 +252,10 @@ public: void setArea( const double & area ); /*! - Initialize the dot coordinates with \e cog. + Initialize the dot coordinates with \e ip. */ - inline void setCog(const vpImagePoint &cog) { - this->cog = cog; + inline void setCog(const vpImagePoint &ip) { + this->cog = ip; } /*! @@ -315,7 +315,7 @@ public: \sa setGraphics() */ - void setGraphicsThickness(unsigned int thickness) {this->thickness = thickness;}; + void setGraphicsThickness(unsigned int t) {this->thickness = t;}; /*! Set the color level of the dot to search a dot in a region of interest. This level will be @@ -354,7 +354,6 @@ public: void setHeight( const double & height ); void setMaxSizeSearchDistancePrecision(const double & maxSizeSearchDistancePrecision); void setSizePrecision( const double & sizePrecision ); - void setSurface( const double & surface ); void setWidth( const double & width ); void track(const vpImage<unsigned char> &I); @@ -363,6 +362,14 @@ public: static void trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage<unsigned char> &I, std::vector<vpImagePoint> &cogs, vpImagePoint* cogStar = NULL); +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + /*! + @name Deprecated functions + */ + vp_deprecated double getSurface() const; + vp_deprecated void setSurface( const double & surface ); +#endif + public: double m00; /*!< Considering the general distribution moments for \f$ N \f$ points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} @@ -429,37 +436,6 @@ public: \sa setComputeMoments() */ -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - /*! - @name Deprecated functions - */ - /*! - - \deprecated This method is deprecated. You should use - getEdges(std::list<vpImagePoint> &) instead.\n \n - Return the list of all the image points on the dot - border. - - \param edges_list : The list of all the images points on the dot - border. This list is update after a call to track(). - - */ - vp_deprecated void getEdges(vpList<vpImagePoint> &edges_list) { - // convert a vpList in a std::list - edges_list.kill(); - std::list<vpImagePoint>::const_iterator it; - for (it = ip_edges_list.begin(); it != ip_edges_list.end(); ++it) { - edges_list += *it; - } - }; - vp_deprecated void getFreemanChain(vpList<unsigned int> &freeman_chain) ; - vp_deprecated vpList<vpDot2>* searchDotsInArea(const vpImage<unsigned char>& I, - int area_u, int area_v, - unsigned int area_w, unsigned int area_h ); - - /* vp_deprecated */ vpList<vpDot2>* searchDotsInArea(const vpImage<unsigned char>& I ); -#endif - private: virtual bool isValid(const vpImage<unsigned char>& I, const vpDot2& wantedDot); diff --git a/src/tracking/feature-builder/vpFeatureBuilder.h b/src/tracking/feature-builder/vpFeatureBuilder.h index 036448391bc31474bfa399e3d94744b45e4fcf27..004bc7b0327bf66e4680582873f3dc8d4efddd1e 100644 --- a/src/tracking/feature-builder/vpFeatureBuilder.h +++ b/src/tracking/feature-builder/vpFeatureBuilder.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilder.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilder.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderEllipse.cpp b/src/tracking/feature-builder/vpFeatureBuilderEllipse.cpp index 5618cfd82cda0d60a64719f70056c3110bd0fb04..e85ad42d378e55f28afecc31b543fc6bdbc6a6be 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderEllipse.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderEllipse.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderEllipse.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilderEllipse.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderLine.cpp b/src/tracking/feature-builder/vpFeatureBuilderLine.cpp index dd33f748c56f308461c37ed4699abb4b1fa81eb5..5093da249dc402bc81fafdc03f4daca2b3691655 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderLine.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderLine.cpp 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpFeatureBuilderLine.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -184,13 +184,13 @@ void vpFeatureBuilder::create(vpFeatureLine &s, The code below shows how to initialize a vpFeatureLine visual feature. First, we initialize the \f$(\rho,\theta)\f$, and lastly we - set the parameters of on equation plan which is generally the result + set the parameters of the plane which is generally the result of a pose estimation. \code vpImage<unsigned char> I; // Image container vpCameraParameters cam; // Default intrinsic camera parameters - vpMeLine line; // Dot tracker + vpMeLine line; // Moving-edges line tracker vpFeatureLine s; // Point feature ... @@ -247,8 +247,8 @@ vpFeatureBuilder::create(vpFeatureLine &s, // vpTRACE("pixel %f %f",rhop, thetap) ; vpPixelMeterConversion::convertLine(cam,rhop,thetap, rho,theta) ; - while (theta > M_PI) { thetap -= 2*M_PI ; } - while (theta < -M_PI) { thetap += 2*M_PI ; } + while (theta > M_PI) { theta -= 2*M_PI ; } + while (theta < -M_PI) { theta += 2*M_PI ; } // vpTRACE("meter %f %f",rho, theta) ; /* diff --git a/src/tracking/feature-builder/vpFeatureBuilderPoint.cpp b/src/tracking/feature-builder/vpFeatureBuilderPoint.cpp index 4cb2be3e86b8e9ac6eca29fe65fb3bcdb8f2a7a6..815376dc5e9730ee148772c9fdd12a8a8adf3623 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderPoint.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilderPoint.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderPoint3D.cpp b/src/tracking/feature-builder/vpFeatureBuilderPoint3D.cpp index 4344455556b80388125faff304bf826ef7cfd4ae..2367d4337220f9ce6c6cfdd5a459a38ba7ee1421 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderPoint3D.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderPoint3D.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderPoint3D.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilderPoint3D.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderPointPolar.cpp b/src/tracking/feature-builder/vpFeatureBuilderPointPolar.cpp index 03bc5828b1e2d1800bbf8733870c6840b6ef6352..fddd38933158912a8e8af8fc1212621c2e5aadf1 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderPointPolar.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderPointPolar.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderPointPolar.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilderPointPolar.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderSegment.cpp b/src/tracking/feature-builder/vpFeatureBuilderSegment.cpp index 22eeb277ecad67bf09cfbe878cc39375ddecc4af..7901bf71ecdbee979732f840bd46ad98c349a547 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderSegment.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderSegment.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureBuilderLine.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/feature-builder/vpFeatureBuilderVanishingPoint.cpp b/src/tracking/feature-builder/vpFeatureBuilderVanishingPoint.cpp index 135cbbf871f232e9c49e14b086225900a30d569d..d3d8dbb09fc480708f3954ed70bb483a5683a1d6 100644 --- a/src/tracking/feature-builder/vpFeatureBuilderVanishingPoint.cpp +++ b/src/tracking/feature-builder/vpFeatureBuilderVanishingPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureBuilderVanishingPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureBuilderVanishingPoint.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -116,8 +116,9 @@ vpFeatureBuilder::create(vpFeatureVanishingPoint &s, const vpFeatureLine &L1, co if(fabs(theta_r-theta_l)<min || fabs(fabs(theta_r-theta_l)-M_PI)<min \ || fabs(fabs(theta_r-theta_l)-2*M_PI)<min) { - vpCERROR<<"there is no vanishing point : the lines are parallel in the image plane"<<std::endl; - throw(" "); + vpCERROR<<"There is no vanishing point : the lines are parallel in the image plane"<<std::endl; + throw(vpFeatureException(vpFeatureException::badInitializationError, + "There is no vanishing point : the lines are parallel in the image plane")) ; } y = (rho_r *c_l - rho_l * c_r) / (-s_l * c_r + s_r * c_l ); diff --git a/src/tracking/forward-projection/vpCircle.cpp b/src/tracking/forward-projection/vpCircle.cpp index b6bebc600710ce162c738a0c9899bb38af13e960..58a22b1044207eefff7fc0f18dd9b23d93acae78 100644 --- a/src/tracking/forward-projection/vpCircle.cpp +++ b/src/tracking/forward-projection/vpCircle.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCircle.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCircle.cpp 4772 2014-07-10 17:05:53Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,14 +58,14 @@ vpCircle::init() Set the world coordinates of the circle from the intersection of a plane and a sphere. We mean here the coordinates of the circle in the object frame - \param oP : oP[0], oP[1], oP[2] correspond to A, B, C from the plane equation Ax + By + Cz = 0. + \param oP_ : oP[0], oP[1], oP[2] correspond to A, B, C from the plane equation Ax + By + Cz = 0. oP[3], oP[4], oP[5] correspond to X, Y, Z the coordinates of the center of the sphere. oP[6] corresponds to the radius of the sphere. */ void -vpCircle::setWorldCoordinates(const vpColVector& oP) +vpCircle::setWorldCoordinates(const vpColVector& oP_) { - this->oP = oP ; + this->oP = oP_ ; } /*! @@ -73,19 +73,17 @@ vpCircle::setWorldCoordinates(const vpColVector& oP) We mean here the coordinates of the circle in the object frame \param A : A from the plane equation Ax + By + Cz = 0. - \param B : A from the plane equation Ax + By + Cz = 0. - \param C : A from the plane equation Ax + By + Cz = 0. + \param B : B from the plane equation Ax + By + Cz = 0. + \param C : C from the plane equation Ax + By + Cz = 0. \param X0 : X Coordinate of the center of the sphere. \param Y0 : Y Coordinate of the center of the sphere. \param Z0 : Z Coordinate of the center of the sphere. \param R : Radius of the sphere. */ void -vpCircle::setWorldCoordinates(const double A, const double B, - const double C, - const double X0, const double Y0, - const double Z0, - const double R) +vpCircle::setWorldCoordinates(const double A, const double B, const double C, + const double X0, const double Y0, const double Z0, + const double R) { oP[0] = A ; oP[1] = B ; @@ -106,24 +104,24 @@ vpCircle::vpCircle() /*! Construct the circle from the intersection of a plane and a sphere. - \param oP : oP[0], oP[1], oP[2] correspond to A, B, C from the plane equation Ax + By + Cz = 0. + \param oP_ : oP[0], oP[1], oP[2] correspond to A, B, C from the plane equation Ax + By + Cz = 0. oP[3], oP[4], oP[5] correspond to X, Y, Z the coordinates of the center of the sphere. oP[6] corresponds to the radius of the sphere. \sa setWorldCoordinates() */ -vpCircle::vpCircle(const vpColVector& oP) +vpCircle::vpCircle(const vpColVector& oP_) { init() ; - setWorldCoordinates(oP) ; + setWorldCoordinates(oP_) ; } /*! Construct the circle from the intersection of a plane and a sphere. \param A : A from the plane equation Ax + By + Cz = 0. - \param B : A from the plane equation Ax + By + Cz = 0. - \param C : A from the plane equation Ax + By + Cz = 0. + \param B : B from the plane equation Ax + By + Cz = 0. + \param C : C from the plane equation Ax + By + Cz = 0. \param X0 : X Coordinate of the center of the sphere. \param Y0 : Y Coordinate of the center of the sphere. \param Z0 : Z Coordinate of the center of the sphere. @@ -150,30 +148,45 @@ vpCircle::~vpCircle() -//! perspective projection of the circle +/*! + Perspective projection of the circle. + + From the 3D parameters of the circle in the camera frame available in cP, computes the 2D parameters of the ellipse resulting from the perspective projection in the image plane. Those 2D parameters are available in p vector. + + See vpCircle::projection(const vpColVector &, vpColVector &) for a more detailed description of the parameters. + */ void vpCircle::projection() { projection(cP,p) ; } -//! perspective projection of the circle +/*! + Perspective projection of the circle. + \param cP_: 3D cercle input parameters. This vector is of dimension 7. It contains the following parameters: A, B, C, X0, Y0, Z0, r where + - A,B,C are the parameters of the plane with equation Ax+By+Cz+D=0 containing the circle + - X0,Y0,Z0 are the 3D coordinates of the cercle in the camera frame + - r is the circle radius. + + \param p_: 2D circle output parameters. This is a 5 dimension vector. It contains the following parameters: xc, yc, m20, m11, m02 where: + - xc,yc are the normalized coordinates of the center of the ellipse (ie the perspective projection of a 3D + circle becomes a 2D ellipse in the image) in the image plane. + - mu20,mu11,mu02 are the second order centered moments of the ellipse. + */ void -vpCircle::projection(const vpColVector &cP, vpColVector &p) +vpCircle::projection(const vpColVector &cP_, vpColVector &p_) { - vpColVector K(6) ; - { - double A = cP[0] ; - double B = cP[1] ; - double C = cP[2] ; + double A = cP_[0] ; + double B = cP_[1] ; + double C = cP_[2] ; - double X0 = cP[3] ; - double Y0 = cP[4] ; - double Z0 = cP[5] ; + double X0 = cP_[3] ; + double Y0 = cP_[4] ; + double Z0 = cP_[5] ; - double r = cP[6]; + double r = cP_[6]; // projection double s = X0*X0 + Y0*Y0 + Z0*Z0 - r*r ; @@ -190,16 +203,20 @@ vpCircle::projection(const vpColVector &cP, vpColVector &p) K[5] = 1 - 2*C*Z0 + C*C*s; } + +// { +// std::cout << "K dans vpCircle::projection(): " << std::endl; +// for (unsigned int i=1; i<6; i++) +// std::cout << K[i]/K[0] << std::endl; +// } double det = K[2]*K[2] -K[0]*K[1]; if (fabs(det) < 1e-8) { vpERROR_TRACE("division par 0") ; throw(vpException(vpException::divideByZeroError, "division par 0")) ; - } - double xc = (K[1]*K[3]-K[2]*K[4])/det; double yc = (K[0]*K[4]-K[2]*K[3])/det; @@ -208,7 +225,8 @@ vpCircle::projection(const vpColVector &cP, vpColVector &p) double A,B,E ; - if (fabs(K[2])<1e-6) + //if (fabs(K[2])<1e-6) + if (fabs(K[2])<std::numeric_limits<double>::epsilon()) { E = 0.0; if (K[0] > K[1]) @@ -239,20 +257,20 @@ vpCircle::projection(const vpColVector &cP, vpColVector &p) } det = (1.0 + vpMath::sqr(E)); - double m20 = (vpMath::sqr(A) + vpMath::sqr(B*E)) /det ; - double m11 = (vpMath::sqr(A) - vpMath::sqr(B)) *E / det ; - double m02 = (vpMath::sqr(B) + vpMath::sqr(A*E)) / det ; - - p[0] = xc ; - p[1] = yc ; - p[2] = m20 ; - p[3] = m11 ; - p[4] = m02 ; + double mu20 = (vpMath::sqr(A) + vpMath::sqr(B*E)) /det ; + double mu11 = (vpMath::sqr(A) - vpMath::sqr(B)) *E / det ; + double mu02 = (vpMath::sqr(B) + vpMath::sqr(A*E)) / det ; + + p_[0] = xc ; + p_[1] = yc ; + p_[2] = mu20 ; + p_[3] = mu11 ; + p_[4] = mu02 ; } //! perspective projection of the circle void -vpCircle::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) +vpCircle::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP_) { double A,B,C ; @@ -266,15 +284,15 @@ vpCircle::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) Z0 = cMo[2][3] + cMo[2][0]*oP[3] + cMo[2][1]*oP[4] + cMo[2][2]*oP[5]; double R = oP[6] ; - cP[0] = A ; - cP[1] = B ; - cP[2] = C ; + cP_[0] = A ; + cP_[1] = B ; + cP_[2] = C ; - cP[3] = X0 ; - cP[4] = Y0 ; - cP[5] = Z0 ; + cP_[3] = X0 ; + cP_[4] = Y0 ; + cP_[5] = Z0 ; - cP[6] = R ; + cP_[6] = R ; // vpTRACE("_cP :") ; std::cout << _cP.t() ; diff --git a/src/tracking/forward-projection/vpCircle.h b/src/tracking/forward-projection/vpCircle.h index 523bd664ed53ee7a2123351790fcca7ebae8af4b..86151b247669caf39314c3ac52aef3ce9b7cce2e 100644 --- a/src/tracking/forward-projection/vpCircle.h +++ b/src/tracking/forward-projection/vpCircle.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCircle.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCircle.h 4702 2014-03-27 15:33:52Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,25 +63,17 @@ class VISP_EXPORT vpCircle : public vpForwardProjection public: void init() ; vpCircle() ; - virtual ~vpCircle() ; - -public: - typedef enum - { - line1, - line2 - } vpLineCircleType; - vpCircle(const vpColVector& oP) ; - vpCircle(const double A, const double B1, + vpCircle(const double A, const double B, const double C, const double X0, const double Y0, const double Z0, const double R) ; + virtual ~vpCircle() ; void setWorldCoordinates(const vpColVector& oP) ; - void setWorldCoordinates(const double A, const double B1, + void setWorldCoordinates(const double A, const double B, const double C, const double X0, const double Y0, const double Z0, diff --git a/src/tracking/forward-projection/vpCylinder.cpp b/src/tracking/forward-projection/vpCylinder.cpp index 057b9ddf56f561a097df287e41d434145d4dc099..208538c0ecf0340551d087c6bb0d23d6aaade291 100644 --- a/src/tracking/forward-projection/vpCylinder.cpp +++ b/src/tracking/forward-projection/vpCylinder.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCylinder.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCylinder.cpp 4908 2014-09-19 06:55:24Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ vpCylinder::init() Set the cylinder parameters \f$^{o}{\bf P} = ({^o}A,{^o}B,{^o}C,{^o}X_0,{^o}Y_0,{^o}Z_0,R)\f$ expressed in the world frame. - \param oP : Vector of parameters \f$^{o}{\bf P}\f$. + \param o_P : Vector of parameters \f$^{o}{\bf P}\f$. \code vpCylinder cylinder; @@ -74,9 +74,9 @@ vpCylinder::init() \endcode */ void -vpCylinder::setWorldCoordinates(const vpColVector& oP) +vpCylinder::setWorldCoordinates(const vpColVector& o_P) { - this->oP = oP ; + this->oP = o_P ; } /*! @@ -115,7 +115,7 @@ vpCylinder::vpCylinder() Create and initialize a cylinder with parameters \f$^{o}{\bf P} = ({^o}A,{^o}B,{^o}C,{^o}X_0,{^o}Y_0,{^o}Z_0,R)\f$ expressed in the world frame. - \param oP : Vector of parameters \f$^{o}{\bf P}\f$. + \param o_P : Vector of parameters \f$^{o}{\bf P}\f$. \code vpCylinder cylinder; @@ -131,10 +131,10 @@ vpCylinder::vpCylinder() \endcode \sa setWorldCoordinates(const vpColVector&) */ -vpCylinder::vpCylinder(const vpColVector& oP) +vpCylinder::vpCylinder(const vpColVector& o_P) { init() ; - setWorldCoordinates(oP) ; + setWorldCoordinates(o_P) ; } /*! @@ -198,8 +198,8 @@ vpCylinder::projection() From the parameters of the cylinder in the camera frame \f$c{\bf P}\f$, compute the perspective projection of the cylinder in the image plane. - \param cP [in] : Cylinder parameters in the camera frame. - \param p [out] : Parameters of the cylinder in the image plane obtained by perspective projection. + \param cP_ [in] : Cylinder parameters in the camera frame. + \param p_ [out] : Parameters of the cylinder in the image plane obtained by perspective projection. \exception vpException::fatalError : The camera is inside the cylinder. @@ -220,7 +220,7 @@ vpCylinder::projection() \sa projection() */ void -vpCylinder::projection(const vpColVector &cP, vpColVector &p) +vpCylinder::projection(const vpColVector &cP_, vpColVector &p_) { //calcul de la scene 2-D @@ -228,19 +228,19 @@ vpCylinder::projection(const vpColVector &cP, vpColVector &p) double A,B,C, X0, Y0, Z0, R ; double s, a, b, c, zero; - A = cP[0] ; - B = cP[1] ; - C = cP[2] ; - X0 = cP[3] ; - Y0 = cP[4] ; - Z0 = cP[5] ; - R= cP[6] ; + A = cP_[0] ; + B = cP_[1] ; + C = cP_[2] ; + X0 = cP_[3] ; + Y0 = cP_[4] ; + Z0 = cP_[5] ; + R= cP_[6] ; zero = A*X0 + B*Y0 + C*Z0; // should be zero for a good reprensetation of the cylinder s = X0*X0 + Y0*Y0 + Z0*Z0 - R*R - zero*zero; if (s < 0) { - printf("The camera is inside the cylinder!\n"); + printf("The camera is inside the cylinder with s=%f !\n", s); throw vpException(vpException::fatalError, "The camera is inside the cylinder!"); } s = 1.0/sqrt(s); @@ -255,8 +255,8 @@ vpCylinder::projection(const vpColVector &cP, vpColVector &p) co = R*a*s-x0; si = R*b*s-y0; e = sqrt(co*co + si*si); - p[0] = -(R*c*s-z0)/e ; // rho1 - p[1] = atan2(si,co) ; // theta 1 + p_[0] = -(R*c*s-z0)/e ; // rho1 + p_[1] = atan2(si,co) ; // theta 1 // while (p[1] > M_PI/2) { p[1] -= M_PI ; p[0] *= -1 ; } // while (p[1] < -M_PI/2) { p[1] += M_PI ; p[0] *= -1 ; } @@ -265,8 +265,8 @@ vpCylinder::projection(const vpColVector &cP, vpColVector &p) co = R*a*s+x0; si = R*b*s+y0; e = sqrt(co*co + si*si); - p[2] = -( R*c*s+z0 )/e ; //rho2 - p[3] = atan2( si,co ) ; //theta2 + p_[2] = -( R*c*s+z0 )/e ; //rho2 + p_[3] = atan2( si,co ) ; //theta2 // while (p[3] > M_PI/2) { p[3] -= M_PI ; p[2] *= -1 ; } @@ -294,12 +294,12 @@ vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo) the cylinder parameters \f$^{c}{\bf P}\f$ expressed in the camera frame. \param cMo : Camera to world frame transformation. - \param cP [out] : Parameters \f$^{c}{\bf P}\f$ expressed in the camera frame. + \param cP_ [out] : Parameters \f$^{c}{\bf P}\f$ expressed in the camera frame. \sa changeFrame(const vpHomogeneousMatrix &) */ void -vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) +vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP_) { double X1, Y1, Z1; double X2, Y2, Z2; @@ -322,9 +322,9 @@ vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) c = Z1 / s; // set axis coordinates in camera frame - cP[0] = a ; - cP[1] = b ; - cP[2] = c ; + cP_[0] = a ; + cP_[1] = b ; + cP_[2] = c ; X2 = cMo[0][3] + cMo[0][0]*oX0 + cMo[0][1]*oY0 + cMo[0][2]*oZ0; Y2 = cMo[1][3] + cMo[1][0]*oX0 + cMo[1][1]*oY0 + cMo[1][2]*oZ0; @@ -332,9 +332,9 @@ vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) // adding the constraint X0 is the nearest point to the origin (A^T . X0 = 0) // using the projection operator (I - AA^T) orthogonal to A - cP[3] = (1-a*a)*X2 - a*b*Y2 - a*c*Z2; - cP[4] = -a*b*X2 + (1-b*b)*Y2 - b*c*Z2; - cP[5] = -a*c*X2 - b*c*Y2 + (1-c*c)*Z2; + cP_[3] = (1-a*a)*X2 - a*b*Y2 - a*c*Z2; + cP_[4] = -a*b*X2 + (1-b*b)*Y2 - b*c*Z2; + cP_[5] = -a*c*X2 - b*c*Y2 + (1-c*c)*Z2; /* old version for the same onstraint if ( fabs(a) > 0.25 ) @@ -385,7 +385,7 @@ vpCylinder::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) } */ //radius - cP[6] = oP[6] ; + cP_[6] = oP[6] ; } diff --git a/src/tracking/forward-projection/vpCylinder.h b/src/tracking/forward-projection/vpCylinder.h index 384f33c3d27657531c2e6f1aad33c01ade084a81..e8f0ebbf20d8ac7cb7f8524863acf03ee14b6479 100644 --- a/src/tracking/forward-projection/vpCylinder.h +++ b/src/tracking/forward-projection/vpCylinder.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpCylinder.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpCylinder.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/forward-projection/vpForwardProjection.cpp b/src/tracking/forward-projection/vpForwardProjection.cpp index b2767042ee588301e7054457ca6f20aac381967a..d1c30f352ae02e71f045e068361303bc28d13719 100644 --- a/src/tracking/forward-projection/vpForwardProjection.cpp +++ b/src/tracking/forward-projection/vpForwardProjection.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpForwardProjection.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpForwardProjection.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/forward-projection/vpForwardProjection.h b/src/tracking/forward-projection/vpForwardProjection.h index e030c059480119550d27ed27a4ce8d93ba0222e5..6b4a8ea36f6b1c244e22ca914db861807491ad9b 100644 --- a/src/tracking/forward-projection/vpForwardProjection.h +++ b/src/tracking/forward-projection/vpForwardProjection.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpForwardProjection.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpForwardProjection.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -225,6 +225,8 @@ public: private: vpForwardProjectionDeallocatorType deallocate ; public: + vpForwardProjection() : oP(), deallocate(user) {} + void setDeallocate(vpForwardProjectionDeallocatorType d) { deallocate = d ; } vpForwardProjectionDeallocatorType getDeallocate() { return deallocate ; } } ; diff --git a/src/tracking/forward-projection/vpLine.cpp b/src/tracking/forward-projection/vpLine.cpp index f4e082585052e3164b9424ac73eeacf833628f85..1b103840dbd068d1fd4613dbd1ecef0f912468e8 100644 --- a/src/tracking/forward-projection/vpLine.cpp +++ b/src/tracking/forward-projection/vpLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLine.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLine.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -124,19 +124,19 @@ vpLine::setWorldCoordinates(const double &A1, const double &B1, \f[ A2 X + B2 Y + C2 Z +D2 = 0 \f] Here \f$ (X, Y, Z) \f$ are the 3D coordinates in the object frame. - \param oP : The column vector which contains the eight parameters + \param oP_ : The column vector which contains the eight parameters needed to define the equations of the two planes in the object frame. \f[ oP = \left[\begin{array}{c}A1 \\ B1 \\ C1 \\ D1 \\ A2 \\ B2 \\ C2 \\ D2 \end{array}\right] \f] */ void -vpLine::setWorldCoordinates(const vpColVector &oP) +vpLine::setWorldCoordinates(const vpColVector &oP_) { - if (oP.getRows() != 8) + if (oP_.getRows() != 8) throw vpException(vpException::dimensionError, "Size of oP is not equal to 8 as it should be"); - this->oP = oP ; + this->oP = oP_ ; } @@ -226,18 +226,18 @@ vpLine::projection() image plane parameters \f$p=(\rho , \theta)\f$ are updated in output. - \param cP : The vector containing the line features relative to the + \param cP_ : The vector containing the line features relative to the camera frame. \f[ cP = \left[\begin{array}{c}A1 \\ B1 \\ C1 \\ D1 \\ A2 \\ B2 \\ C2 \\ D2 \end{array}\right] \f] - \param p : The vector which contains the 2D line features expressed + \param p_ : The vector which contains the 2D line features expressed in the image plane. \f[ p = \left[\begin{array}{c} \rho \\ \theta \end{array}\right] \f] \exception vpException::fatalError : Degenerate case, the image of the straight line is a point. */ void -vpLine::projection(const vpColVector &cP, vpColVector &p) +vpLine::projection(const vpColVector &cP_, vpColVector &p_) { //projection @@ -246,15 +246,15 @@ vpLine::projection(const vpColVector &cP, vpColVector &p) double A1, A2, B1, B2, C1, C2, D1, D2; - A1=cP[0] ; - B1=cP[1] ; - C1=cP[2] ; - D1=cP[3] ; + A1=cP_[0] ; + B1=cP_[1] ; + C1=cP_[2] ; + D1=cP_[3] ; - A2=cP[4] ; - B2=cP[5] ; - C2=cP[6] ; - D2=cP[7] ; + A2=cP_[4] ; + B2=cP_[5] ; + C2=cP_[6] ; + D2=cP_[7] ; double a, b, c, s; a = A2*D1 - A1*D2; @@ -274,8 +274,8 @@ vpLine::projection(const vpColVector &cP, vpColVector &p) if (p.getRows() != 2) p.resize(2); - p[0] = rho ; - p[1] = theta ; + p_[0] = rho ; + p_[1] = theta ; } @@ -336,7 +336,7 @@ vpLine::changeFrame(const vpHomogeneousMatrix &cMo) \param cMo : The homogeneous matrix relative to the pose between the desired frame and the object frame. - \param cP : The vector which will contain the parameters of the two + \param cP_ : The vector which will contain the parameters of the two planes in the camera frame. \f[ cP = \left[\begin{array}{c}A1 \\ B1 \\ C1 \\ D1 \\ A2 \\ B2 \\ C2 \\ D2 \end{array}\right] \f] @@ -363,7 +363,7 @@ vpLine::changeFrame(const vpHomogeneousMatrix &cMo) */ void -vpLine::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) +vpLine::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP_) { double a1, a2, b1, b2, c1, c2, d1, d2; @@ -424,10 +424,10 @@ vpLine::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) // Constraint A1^2 + B1^2 + C1^2 = 1 d1 = 1.0/sqrt(a1*a1 + b1*b1 + c1*c1); - cP[0] = A1 = a1*d1 ; - cP[1] = B1 = b1*d1 ; - cP[2] = C1 = c1*d1 ; - cP[3] = D1 = 0 ; + cP_[0] = A1 = a1*d1 ; + cP_[1] = B1 = b1*d1 ; + cP_[2] = C1 = c1*d1 ; + cP_[3] = D1 = 0 ; // Constraint A1 A2 + B1 B2 + C1 C2 = 0 (P2 orthogonal to P1) // N2_new = (N1 x N2) x N1_new @@ -458,10 +458,10 @@ vpLine::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) // vpERROR_TRACE("A1 B1 C1 D1 %f %f %f %f ", A1, B1, C1, D1) ; // vpERROR_TRACE("A2 B2 C2 D2 %f %f %f %f ", A2, B2, C2, D2) ; - cP[4] = A2; - cP[5] = B2; - cP[6] = C2; - cP[7] = D2; + cP_[4] = A2; + cP_[5] = B2; + cP_[6] = C2; + cP_[7] = D2; // in case of verification /* diff --git a/src/tracking/forward-projection/vpLine.h b/src/tracking/forward-projection/vpLine.h index 3c81a28b98b1ef40c8689718cc1ce6f448e1aacf..780a1a024ea7cc45c73702d180622f5c1090a09d 100644 --- a/src/tracking/forward-projection/vpLine.h +++ b/src/tracking/forward-projection/vpLine.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpLine.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpLine.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/forward-projection/vpPoint.cpp b/src/tracking/forward-projection/vpPoint.cpp index 800a9364e04addb5cdf6ab53d900cdaa2bf438f0..5c7e2dc4a99059046df677845bad454fa2a0e302 100644 --- a/src/tracking/forward-projection/vpPoint.cpp +++ b/src/tracking/forward-projection/vpPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpPoint.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -286,7 +286,7 @@ vpPoint::display(const vpImage<vpRGBa> &I, vpFeatureDisplay::displayPoint(_p[0],_p[1], cam, I, color, thickness) ; } -std::ostream& operator<<(std::ostream& os, vpPoint& /* vpp */) +VISP_EXPORT std::ostream& operator<<(std::ostream& os, vpPoint& /* vpp */) { return( os<<"vpPoint" ); } diff --git a/src/tracking/forward-projection/vpPoint.h b/src/tracking/forward-projection/vpPoint.h index 064d3fc67267db493baa90efb3bd29a6dcfe3258..ebe0a836093d9c988bd84df0d7538bc8ca279222 100644 --- a/src/tracking/forward-projection/vpPoint.h +++ b/src/tracking/forward-projection/vpPoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpPoint.h 4089 2013-02-04 08:17:31Z fspindle $ + * $Id: vpPoint.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/forward-projection/vpSphere.cpp b/src/tracking/forward-projection/vpSphere.cpp index 4d08a871a5334715f00dab7a0f59dfe871142392..4e380e443994c11ca6c678e72297b1759c342c8e 100644 --- a/src/tracking/forward-projection/vpSphere.cpp +++ b/src/tracking/forward-projection/vpSphere.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSphere.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSphere.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,9 +55,9 @@ vpSphere::init() } void -vpSphere::setWorldCoordinates(const vpColVector& oP) +vpSphere::setWorldCoordinates(const vpColVector& oP_) { - this->oP = oP ; + this->oP = oP_ ; } void @@ -78,10 +78,10 @@ vpSphere::vpSphere() } -vpSphere::vpSphere(const vpColVector& oP) +vpSphere::vpSphere(const vpColVector& oP_) { init() ; - setWorldCoordinates(oP) ; + setWorldCoordinates(oP_) ; } vpSphere::vpSphere(const double X0, const double Y0, @@ -103,9 +103,9 @@ vpSphere::projection() projection(cP,p) ; } -//! perspective projection of the circle +//! Perspective projection of the circle. void -vpSphere::projection(const vpColVector &cP, vpColVector &p) +vpSphere::projection(const vpColVector &cP_, vpColVector &p_) { double x0, y0, z0; //variables intermediaires // double k0, k1, k2, k3, k4; //variables intermediaires @@ -113,12 +113,11 @@ vpSphere::projection(const vpColVector &cP, vpColVector &p) //calcul des parametres M20, M11, M02 de l'ellipse double s, a, b, r, e; //variables intermediaires - r = cP[3]; - - x0 = cP[0] ; - y0 = cP[1] ; - z0 = cP[2] ; + r = cP_[3]; + x0 = cP_[0] ; + y0 = cP_[1] ; + z0 = cP_[2] ; s = r*r - y0*y0 -z0*z0; @@ -133,8 +132,8 @@ vpSphere::projection(const vpColVector &cP, vpColVector &p) vpERROR_TRACE("sphere derriere le plan image\n"); } - p[0] = x0*z0/s ; //x - p[1] = y0*z0/s ; //y + p_[0] = x0*z0/s ; //x + p_[1] = y0*z0/s ; //y if (fabs(x0) > 1e-6) { @@ -167,14 +166,13 @@ vpSphere::projection(const vpColVector &cP, vpColVector &p) B = r*sqrt(y0*y0+z0*z0-r*r)/s; } - p[2] = ( A*A + B*B * E*E) / (1.0 + E*E); // mu20 - p[3] = ( A*A - B*B) * E / (1.0 + E*E); // mu11 - p[4] = ( B*B + A*A * E*E) / (1.0 + E*E); // mu02 + p_[2] = ( A*A + B*B * E*E) / (1.0 + E*E); // mu20 + p_[3] = ( A*A - B*B) * E / (1.0 + E*E); // mu11 + p_[4] = ( B*B + A*A * E*E) / (1.0 + E*E); // mu02 // vpERROR_TRACE(" %f",r) ; // std::cout << p.t() ; - } //! perspective projection of the circle void @@ -183,25 +181,21 @@ vpSphere::changeFrame(const vpHomogeneousMatrix &cMo) changeFrame(cMo,cP) ; } -//! perspective projection of the circle +//! Perspective projection of the circle. void -vpSphere::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP) +vpSphere::changeFrame(const vpHomogeneousMatrix &cMo, vpColVector &cP_) { - double x0, y0, z0; //variables intermediaires - x0 = cMo[0][0]*oP[0] + cMo[0][1]*oP[1] + cMo[0][2]*oP[2] + cMo[0][3]; y0 = cMo[1][0]*oP[0] + cMo[1][1]*oP[1] + cMo[1][2]*oP[2] + cMo[1][3]; z0 = cMo[2][0]*oP[0] + cMo[2][1]*oP[1] + cMo[2][2]*oP[2] + cMo[2][3]; - cP[3] = oP[3]; - - cP[0] = x0 ; - cP[1] = y0 ; - cP[2] = z0 ; - + cP_[3] = oP[3]; + cP_[0] = x0 ; + cP_[1] = y0 ; + cP_[2] = z0 ; } //! for memory issue (used by the vpServo class only) diff --git a/src/tracking/forward-projection/vpSphere.h b/src/tracking/forward-projection/vpSphere.h index 7eb51a8aa3516df9c3697309abf7e1f573fa0aa7..dfef7c9539b0f41038121a8fa2b8e5e6f3a8b648 100644 --- a/src/tracking/forward-projection/vpSphere.h +++ b/src/tracking/forward-projection/vpSphere.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpSphere.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpSphere.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/general-tracking-issues/vpTracker.cpp b/src/tracking/general-tracking-issues/vpTracker.cpp index 3e90e10326d993cbc104b94227329a1e44a74c4c..4e3757115921cbfd90e20df5f7e0157afbc91034 100644 --- a/src/tracking/general-tracking-issues/vpTracker.cpp +++ b/src/tracking/general-tracking-issues/vpTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTracker.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTracker.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,12 +58,9 @@ vpTracker::init() -vpTracker::vpTracker() -{ - init() ; -} +vpTracker::vpTracker() : p(), cP(), cPAvailable(false) {} -vpTracker::vpTracker(const vpTracker &tracker) +vpTracker::vpTracker(const vpTracker &tracker) : p(), cP(), cPAvailable(false) { *this = tracker; } diff --git a/src/tracking/general-tracking-issues/vpTracker.h b/src/tracking/general-tracking-issues/vpTracker.h index 147136ee65802d14c5e86ba5a260151cf7351393..3acc3b76c7f3a8947e61c30600f7cc1fedcd843f 100644 --- a/src/tracking/general-tracking-issues/vpTracker.h +++ b/src/tracking/general-tracking-issues/vpTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTracker.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTracker.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/general-tracking-issues/vpTrackingException.h b/src/tracking/general-tracking-issues/vpTrackingException.h index fab9ca3a5c6e1a62da2d1a4d493bd247b7c9ef4e..4528593aa776fd194a5de7f36d9c051c82fc0bf0 100644 --- a/src/tracking/general-tracking-issues/vpTrackingException.h +++ b/src/tracking/general-tracking-issues/vpTrackingException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpTrackingException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTrackingException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,12 +68,12 @@ */ class VISP_EXPORT vpTrackingException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpTracking member */ - enum errorTrackingCodeEnum + enum errorTrackingCodeEnum { featureLostError, @@ -83,25 +83,20 @@ public: fatalError } ; -public: - vpTrackingException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpTrackingException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpTrackingException (const int code) - : vpException(code){ ; } + public: + vpTrackingException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpTrackingException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpTrackingException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpTrackingException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/tracking/klt/vpKltOpencv.cpp b/src/tracking/klt/vpKltOpencv.cpp index c1e8e339a5ae59d7177c89bfac56e313b49c7045..787c8437aa791f085a964af0c248be4e1880a5d1 100644 --- a/src/tracking/klt/vpKltOpencv.cpp +++ b/src/tracking/klt/vpKltOpencv.cpp @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpKltOpencv.cpp 4198 2013-04-05 12:13:23Z fspindle $ + * $Id: vpKltOpencv.cpp 5189 2015-01-21 16:42:18Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -48,13 +48,525 @@ implemented with opencv. */ -#include "visp/vpConfig.h" +#include <visp/vpConfig.h> -#ifdef VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020408) #include <string> -#include "vpKltOpencv.h" +#include <visp/vpDisplay.h> +#include <visp/vpKltOpencv.h> +#include <visp/vpTrackingException.h> + +/*! + Default constructor. + */ +vpKltOpencv::vpKltOpencv() + : m_gray(), m_prevGray(), m_points_id(), m_maxCount(500), m_termcrit(), m_winSize(10), m_qualityLevel(0.01), + m_minDistance(15), m_minEigThreshold(1e-4), m_harris_k(0.04), m_blockSize(3), m_useHarrisDetector(1), m_pyrMaxLevel(3), + m_next_points_id(0), m_initial_guess(false) +{ + m_termcrit = cv::TermCriteria(cv::TermCriteria::COUNT|cv::TermCriteria::EPS, 20, 0.03); + +} + +/*! + Copy constructor. + */ +vpKltOpencv::vpKltOpencv(const vpKltOpencv& copy) + : m_gray(), m_prevGray(), m_points_id(), m_maxCount(500), m_termcrit(), m_winSize(10), m_qualityLevel(0.01), + m_minDistance(15), m_minEigThreshold(1e-4), m_harris_k(0.04), m_blockSize(3), m_useHarrisDetector(1), m_pyrMaxLevel(3), + m_next_points_id(0), m_initial_guess(false) +{ + *this = copy; +} + +/*! + Copy operator. + */ +vpKltOpencv & vpKltOpencv::operator=(const vpKltOpencv& copy) +{ + m_gray = copy.m_gray; + m_prevGray = copy.m_prevGray; + m_points[0] = copy.m_points[0]; + m_points[1] = copy.m_points[1]; + m_points_id = copy.m_points_id; + m_maxCount = copy.m_maxCount; + m_termcrit = copy.m_termcrit; + m_winSize = copy.m_winSize; + m_qualityLevel = copy.m_qualityLevel; + m_minDistance = copy.m_minDistance; + m_minEigThreshold = copy.m_minEigThreshold; + m_harris_k = copy.m_harris_k; + m_blockSize = copy.m_blockSize; + m_useHarrisDetector = copy.m_useHarrisDetector; + m_pyrMaxLevel = copy.m_pyrMaxLevel; + m_next_points_id = copy.m_next_points_id; + m_initial_guess = copy.m_initial_guess; + + return *this; +} + +vpKltOpencv::~vpKltOpencv() +{ +} + +/*! + Initialise the tracking by extracting KLT keypoints on the provided image. + + \param I : Grey level image used as input. This image should have only 1 channel. + \param mask : Image mask used to restrict the keypoint detection area. + If mask is NULL, all the image will be considered. + + \exception vpTrackingException::initializationError : If the image I is not + initialized, or if the image or the mask have bad coding format. +*/ +void vpKltOpencv::initTracking(const cv::Mat &I, const cv::Mat &mask) +{ + m_next_points_id = 0; + + //cvtColor(I, m_gray, cv::COLOR_BGR2GRAY); + I.copyTo(m_gray); + + for (size_t i=0; i<2; i++) { + m_points[i].clear(); + } + + m_points_id.clear(); + + cv::goodFeaturesToTrack(m_gray, m_points[1], m_maxCount, m_qualityLevel, m_minDistance, mask, m_blockSize, 0, m_harris_k); + + if(m_points[1].size() > 0){ + cv::cornerSubPix(m_gray, m_points[1], cv::Size(m_winSize, m_winSize), cv::Size(-1,-1), m_termcrit); + + for (size_t i=0; i < m_points[1].size(); i++) + m_points_id.push_back(m_next_points_id++); + } +} + +/*! + Track KLT keypoints using the iterative Lucas-Kanade method with pyramids. + + \param I : Input image. + */ +void vpKltOpencv::track(const cv::Mat &I) +{ + if(m_points[1].size() == 0) + throw vpTrackingException(vpTrackingException::fatalError, "Not enough key points to track."); + + std::vector<float> err; + int flags = 0; + + cv::swap(m_prevGray, m_gray); + + if (m_initial_guess) { + flags |= cv::OPTFLOW_USE_INITIAL_FLOW; + m_initial_guess = false; + } + else { + std::swap(m_points[1], m_points[0]); + } + + //cvtColor(I, m_gray, cv::COLOR_BGR2GRAY); + I.copyTo(m_gray); + + if(m_prevGray.empty()){ + m_gray.copyTo(m_prevGray); + } + + std::vector<uchar> status; + + cv::calcOpticalFlowPyrLK(m_prevGray, m_gray, m_points[0], m_points[1], status, err, cv::Size(m_winSize, m_winSize), + m_pyrMaxLevel, m_termcrit, flags, m_minEigThreshold); + + // Remove points that are lost + for (int i=(int)status.size()-1; i>=0; i--) { + if (status[(size_t)i] == 0) { // point is lost + m_points[0].erase(m_points[0].begin()+i); + m_points[1].erase(m_points[1].begin()+i); + m_points_id.erase(m_points_id.begin()+i); + } + } +} + +/*! + + Get the 'index'th feature image coordinates. Beware that + getFeature(i,...) may not represent the same feature before and + after a tracking iteration (if a feature is lost, features are + shifted in the array). + + \param index : Index of feature. + \param id : id of the feature. + \param x : x coordinate. + \param y : y coordinate. + +*/ +void vpKltOpencv::getFeature(const int &index, int &id, float &x, float &y) const +{ + if ((size_t)index >= m_points[1].size()){ + throw(vpException(vpException::badValue, "Feature [%d] doesn't exist", index)); + } + + x = m_points[1][(size_t)index].x; + y = m_points[1][(size_t)index].y; + id = m_points_id[(size_t)index]; +} + +/*! + Display features position and id. + + \param I : Image used as background. Display should be initialized on it. + \param color : Color used to display the features. + \param thickness : Thickness of the drawings. + */ +void vpKltOpencv::display(const vpImage<unsigned char> &I, + const vpColor &color, unsigned int thickness) +{ + vpKltOpencv::display(I, m_points[1], m_points_id, color, thickness); +} + +/*! + + Display features list. + + \param I : The image used as background. + + \param features : Vector of features. + + \param color : Color used to display the points. + + \param thickness : Thickness of the points. +*/ +void vpKltOpencv::display(const vpImage<unsigned char> &I, const std::vector<cv::Point2f> &features, + const vpColor &color, unsigned int thickness) +{ + vpImagePoint ip; + for (size_t i = 0 ; i < features.size() ; i++) { + ip.set_u( vpMath::round(features[i].x ) ); + ip.set_v( vpMath::round(features[i].y ) ); + vpDisplay::displayCross(I, ip, 10+thickness, color, thickness); + } +} + +/*! + + Display features list. + + \param I : The image used as background. + + \param features : Vector of features. + + \param color : Color used to display the points. + + \param thickness : Thickness of the points. +*/ +void vpKltOpencv::display(const vpImage<vpRGBa> &I, const std::vector<cv::Point2f> &features, + const vpColor &color, unsigned int thickness) +{ + vpImagePoint ip; + for (size_t i = 0 ; i < features.size() ; i++) { + ip.set_u( vpMath::round(features[i].x ) ); + ip.set_v( vpMath::round(features[i].y ) ); + vpDisplay::displayCross(I, ip, 10+thickness, color, thickness); + } +} + +/*! + + Display features list with ids. + + \param I : The image used as background. + + \param features : Vector of features. + + \param featuresid : Vector of ids corresponding to the features. + + \param color : Color used to display the points. + + \param thickness : Thickness of the points +*/ +void vpKltOpencv::display(const vpImage<unsigned char> &I, const std::vector<cv::Point2f> &features, + const std::vector<long> &featuresid, + const vpColor &color, unsigned int thickness) +{ + vpImagePoint ip; + for (size_t i = 0; i < features.size(); i++) { + ip.set_u( vpMath::round(features[i].x ) ); + ip.set_v( vpMath::round(features[i].y ) ); + vpDisplay::displayCross(I, ip, 10, color, thickness); + + std::ostringstream id; + id << featuresid[i]; + ip.set_u( vpMath::round( features[i].x + 5 ) ); + vpDisplay::displayText(I, ip, id.str(), color); + } +} + +/*! + + Display features list with ids. + + \param I : The image used as background. + + \param features : Vector of features. + + \param featuresid : Vector of ids corresponding to the features. + + \param color : Color used to display the points. + + \param thickness : Thickness of the points +*/ +void vpKltOpencv::display(const vpImage<vpRGBa> &I, const std::vector<cv::Point2f> &features, + const std::vector<long> &featuresid, + const vpColor &color, unsigned int thickness) +{ + vpImagePoint ip; + for (size_t i = 0 ; i < features.size() ; i++) { + ip.set_u( vpMath::round(features[i].x ) ); + ip.set_v( vpMath::round(features[i].y ) ); + vpDisplay::displayCross(I, ip, 10, color, thickness); + + std::ostringstream id; + id << featuresid[i]; + ip.set_u( vpMath::round( features[i].x + 5 ) ); + vpDisplay::displayText(I, ip, id.str(), color); + } +} + +/*! + Set the maximum number of features to track in the image. + + \param maxCount : Maximum number of features to detect and track. Default value is set to 500. +*/ +void vpKltOpencv::setMaxFeatures(const int maxCount) +{ + m_maxCount = maxCount; +} + +/*! + Set the window size used to refine the corner locations. + + \param winSize : Half of the side length of the search window. Default value is set to 10. + For example, if \e winSize=5 , then a 5*2+1 \f$\times\f$ 5*2+1 = 11 \f$\times\f$ 11 search window is used. +*/ +void vpKltOpencv::setWindowSize(const int winSize) +{ + m_winSize = winSize; +} + +/*! + Set the parameter characterizing the minimal accepted quality of image corners. + + \param qualityLevel : Quality level parameter. Default value is set to 0.01. The parameter value is multiplied by the + best corner quality measure, which is the minimal eigenvalue or the Harris function response. The corners with + the quality measure less than the product are rejected. For example, if the best corner has the quality + measure = 1500, and the qualityLevel=0.01, then all the corners with the quality measure less than 15 are rejected. + */ +void vpKltOpencv::setQuality(double qualityLevel) +{ + m_qualityLevel = qualityLevel; +} + +/*! + Set the free parameter of the Harris detector. + + \param harris_k : Free parameter of the Harris detector. Default value is set to 0.04. +*/ +void vpKltOpencv::setHarrisFreeParameter(double harris_k) +{ + m_harris_k = harris_k; +} + +/*! + Set the parameter indicating whether to use a Harris detector or + the minimal eigenvalue of gradient matrices for corner detection. + \param useHarrisDetector : If 1 (default value), use the Harris detector. If 0 use the eigenvalue. +*/ +void vpKltOpencv::setUseHarris(const int useHarrisDetector) +{ + m_useHarrisDetector = useHarrisDetector; +} + +/*! + Set the minimal Euclidean distance between detected corners during initialization. + + \param minDistance : Minimal possible Euclidean distance between the detected corners. + Default value is set to 15. +*/ +void vpKltOpencv::setMinDistance(double minDistance) +{ + m_minDistance = minDistance; +} + +/*! + Set the minimal eigen value threshold used to reject a point during the tracking. + \param minEigThreshold : Minimal eigen value threshold. Default value is set to 1e-4. +*/ +void vpKltOpencv::setMinEigThreshold(double minEigThreshold) +{ + m_minEigThreshold = minEigThreshold; +} + +/*! + Set the size of the averaging block used to track the features. + + \warning The input is a signed integer to be compatible with OpenCV. However, + it must be a positive integer. + + \param blockSize : Size of an average block for computing a derivative covariation + matrix over each pixel neighborhood. Default value is set to 3. +*/ +void vpKltOpencv::setBlockSize(const int blockSize) +{ + m_blockSize = blockSize; +} + +/*! + Set the maximal pyramid level. If the level is zero, then no pyramid is + computed for the optical flow. + + \param pyrMaxLevel : 0-based maximal pyramid level number; if set to 0, pyramids are not used (single level), + if set to 1, two levels are used, and so on. Default value is set to 3. +*/ +void vpKltOpencv::setPyramidLevels(const int pyrMaxLevel) +{ + m_pyrMaxLevel = pyrMaxLevel; +} + +/*! + Set the points that will be used as initial guess during the next call to track(). + A typical usage of this function is to predict the position of the features before the + next call to track(). + + \param guess_pts : Vector of points that should be tracked. The size of this + vector should be the same as the one returned by getFeatures(). If this is not the case, + an exception is returned. Note also that the id of the points is not modified. + + \sa initTracking() +*/ +void +vpKltOpencv::setInitialGuess(const std::vector<cv::Point2f> &guess_pts) +{ + if(guess_pts.size() != m_points[1].size()){ + throw(vpException(vpException::badValue, + "Cannot set initial guess: size feature vector [%d] and guess vector [%d] doesn't match", + m_points[1].size(), guess_pts.size())); + } + + m_points[1] = guess_pts; + m_initial_guess = true; +} + +/*! + Set the points that will be used as initialization during the next call to track(). + + \param I : Input image. + \param pts : Vector of points that should be tracked. + +*/ +void +vpKltOpencv::initTracking(const cv::Mat &I, const std::vector<cv::Point2f> &pts) +{ + m_initial_guess = false; + m_points[1] = pts; + m_next_points_id = 0; + m_points_id.clear(); + for(size_t i=0; i < m_points[1].size(); i++) { + m_points_id.push_back(m_next_points_id ++); + } + + I.copyTo(m_gray); +} + +void +vpKltOpencv::initTracking(const cv::Mat &I, const std::vector<cv::Point2f> &pts, const std::vector<long> &ids) +{ + m_initial_guess = false; + m_points[1] = pts; + m_points_id.clear(); + + if(ids.size() != pts.size()){ + m_next_points_id = 0; + for(size_t i=0; i < m_points[1].size(); i++) + m_points_id.push_back(m_next_points_id ++); + } + else{ + long max = 0; + for(size_t i=0; i < m_points[1].size(); i++){ + m_points_id.push_back(ids[i]); + if(ids[i] > max) max = ids[i]; + } + m_next_points_id = max + 1; + } + + I.copyTo(m_gray); +} + +/*! + + Add a keypoint at the end of the feature list. The id of the feature is set to ensure that it is unique. + \param x,y : Coordinates of the feature in the image. + +*/ +void vpKltOpencv::addFeature(const float &x, const float &y) +{ + cv::Point2f f(x, y); + m_points[1].push_back(f); + m_points_id.push_back(m_next_points_id++); +} + +/*! + + \deprecated Add a keypoint at the end of the feature list. + This function is deprecated since it doesn't ensure that the id of the feature is unique. + You should rather use addFeature(const float &, const float &) or addFeature(const cv::Point2f &). + + \param id : Feature id. Should be unique + \param x,y : Coordinates of the feature in the image. + +*/ +void vpKltOpencv::addFeature(const long &id, const float &x, const float &y) +{ + cv::Point2f f(x, y); + m_points[1].push_back(f); + m_points_id.push_back(id); + if (id >= m_next_points_id) + m_next_points_id = id + 1; +} + +/*! + + Add a keypoint at the end of the feature list. The id of the feature is set to ensure that it is unique. + \param f : Coordinates of the feature in the image. + +*/ +void vpKltOpencv::addFeature(const cv::Point2f &f) +{ + m_points[1].push_back(f); + m_points_id.push_back(m_next_points_id++); +} + +/*! + Remove the feature with the given index as parameter. + \param index : Index of the feature to remove. + */ +void vpKltOpencv::suppressFeature(const int &index) +{ + if ((size_t)index >= m_points[1].size()){ + throw(vpException(vpException::badValue, "Feature [%d] doesn't exist", index)); + } + + m_points[1].erase(m_points[1].begin()+index); + m_points_id.erase(m_points_id.begin()+index); +} + + +#elif defined(VISP_HAVE_OPENCV) + +#include <string> + +#include <visp/vpKltOpencv.h> void vpKltOpencv::clean() { @@ -63,12 +575,12 @@ void vpKltOpencv::clean() if (pyramid) cvReleaseImage(&pyramid); if (prev_pyramid) cvReleaseImage(&prev_pyramid); - image = 0; - prev_image = 0; - pyramid = 0; - prev_pyramid = 0; + image = NULL; + prev_image = NULL; + pyramid = NULL; + prev_pyramid = NULL; - swap_temp = 0; + swap_temp = NULL; countFeatures = 0; countPrevFeatures = 0; flags = 0; @@ -85,12 +597,12 @@ void vpKltOpencv::cleanAll() if (lostDuringTrack) cvFree(&lostDuringTrack); if (featuresid) cvFree(&featuresid); if (prev_featuresid) cvFree(&prev_featuresid); - features = 0; - prev_features = 0; - status = 0; + features = NULL; + prev_features = NULL; + status = NULL; lostDuringTrack = 0; - featuresid = 0; - prev_featuresid = 0; + featuresid = NULL; + prev_featuresid = NULL; } void vpKltOpencv::reset() @@ -99,54 +611,45 @@ void vpKltOpencv::reset() } +/*! + Default constructor. + */ vpKltOpencv::vpKltOpencv() + : initialized(0), maxFeatures(50), globalcountFeatures(0), win_size(10), quality(0.01), + min_distance(10), harris_free_parameter(0.04), block_size(3), use_harris(1), + pyramid_level(3), _tid(-1), image(NULL), prev_image(NULL), pyramid(NULL), + prev_pyramid(NULL), swap_temp(NULL), countFeatures(0), countPrevFeatures(0), + features(NULL), prev_features(NULL), featuresid(NULL), prev_featuresid(NULL), + flags(0), initial_guess(false), lostDuringTrack(0), status(0), OnInitialize(0), + OnFeatureLost(0), OnNewFeature(0), OnMeasureFeature(0), IsFeatureValid(0) { - //Valeurs par d�faut pour le KLT - initialized = 0; - maxFeatures = 50; - countFeatures = 0; - countPrevFeatures = 0; - globalcountFeatures = 0; - win_size = 10; - quality = 0.01; - min_distance = 10; - block_size = 3; - use_harris = 1; - pyramid_level = 3; - harris_free_parameter = 0.04; - - //Zeroing pointers - image = 0; - prev_image = 0; - pyramid = 0; - prev_pyramid = 0; - swap_temp = 0; - features = 0; - prev_features = 0; - flags = 0; - status = 0; - lostDuringTrack = 0; - featuresid = 0; - prev_featuresid = 0; - OnInitialize = 0; - OnFeatureLost = 0; - OnNewFeature = 0; - OnMeasureFeature = 0; - IsFeatureValid = 0; - initial_guess = false; - features = (CvPoint2D32f*)cvAlloc((unsigned int)maxFeatures*sizeof(features[0])); prev_features = (CvPoint2D32f*)cvAlloc((unsigned int)maxFeatures*sizeof(prev_features[0])); status = (char*)cvAlloc((size_t)maxFeatures); lostDuringTrack = (bool*)cvAlloc((size_t)maxFeatures); featuresid = (long*)cvAlloc((unsigned int)maxFeatures*sizeof(long)); prev_featuresid = (long*)cvAlloc((unsigned int)maxFeatures*sizeof(long)); - - - _tid = -1; } +/*! + Copy constructor. + */ vpKltOpencv::vpKltOpencv(const vpKltOpencv& copy) + : initialized(0), maxFeatures(50), globalcountFeatures(0), win_size(10), quality(0.01), + min_distance(10), harris_free_parameter(0.04), block_size(3), use_harris(1), + pyramid_level(3), _tid(-1), image(NULL), prev_image(NULL), pyramid(NULL), + prev_pyramid(NULL), swap_temp(NULL), countFeatures(0), countPrevFeatures(0), + features(NULL), prev_features(NULL), featuresid(NULL), prev_featuresid(NULL), + flags(0), initial_guess(false), lostDuringTrack(0), status(0), OnInitialize(0), + OnFeatureLost(0), OnNewFeature(0), OnMeasureFeature(0), IsFeatureValid(0) +{ + *this = copy; +} + +/*! + Copy operator. + */ +vpKltOpencv & vpKltOpencv::operator=(const vpKltOpencv& copy) { //Shallow copy of primitives initialized = copy.initialized; @@ -170,18 +673,12 @@ vpKltOpencv::vpKltOpencv(const vpKltOpencv& copy) OnMeasureFeature = copy.OnMeasureFeature; IsFeatureValid = copy.IsFeatureValid; + initial_guess = copy.initial_guess; + lostDuringTrack = copy.lostDuringTrack; + if (!initialized) { - image = 0; - prev_image = 0; - pyramid = 0; - prev_pyramid = 0; - features = 0; - prev_features = 0; status = 0; lostDuringTrack = 0; - featuresid = 0; - prev_featuresid = 0; - swap_temp = 0; countFeatures = 0; countPrevFeatures = 0; flags = 0; @@ -189,72 +686,74 @@ vpKltOpencv::vpKltOpencv(const vpKltOpencv& copy) globalcountFeatures = 0; } - if (copy.image) - { - image = cvCreateImage(cvGetSize(copy.image), 8, 1); - // /*IplImage **/cvCopyImage(copy.image,image); - cvCopy(copy.image, image, 0); - } - - if (copy.prev_image) - { - prev_image = cvCreateImage(cvGetSize(copy.prev_image), IPL_DEPTH_8U, 1); - // /*IplImage **/ cvCopyImage(copy.prev_image,prev_image); - cvCopy(copy.prev_image, prev_image, 0); - } - - if (copy.pyramid) - { - pyramid = cvCreateImage(cvGetSize(copy.pyramid), IPL_DEPTH_8U, 1); - // /*IplImage **/cvCopyImage(copy.pyramid,pyramid); - cvCopy(copy.pyramid, pyramid, 0); - } - - if (copy.prev_pyramid) - { - prev_pyramid = cvCreateImage(cvGetSize(copy.prev_pyramid), IPL_DEPTH_8U, 1); - // /*IplImage **/cvCopyImage(copy.prev_pyramid,prev_pyramid); - cvCopy(copy.prev_pyramid, prev_pyramid, 0); - } + if (copy.image) + { + image = cvCreateImage(cvGetSize(copy.image), 8, 1); + // /*IplImage **/cvCopyImage(copy.image,image); + cvCopy(copy.image, image, 0); + } + + if (copy.prev_image) + { + prev_image = cvCreateImage(cvGetSize(copy.prev_image), IPL_DEPTH_8U, 1); + // /*IplImage **/ cvCopyImage(copy.prev_image,prev_image); + cvCopy(copy.prev_image, prev_image, 0); + } + + if (copy.pyramid) + { + pyramid = cvCreateImage(cvGetSize(copy.pyramid), IPL_DEPTH_8U, 1); + // /*IplImage **/cvCopyImage(copy.pyramid,pyramid); + cvCopy(copy.pyramid, pyramid, 0); + } + + if (copy.prev_pyramid) + { + prev_pyramid = cvCreateImage(cvGetSize(copy.prev_pyramid), IPL_DEPTH_8U, 1); + // /*IplImage **/cvCopyImage(copy.prev_pyramid,prev_pyramid); + cvCopy(copy.prev_pyramid, prev_pyramid, 0); + } //Deep copy of arrays if (copy.features) { - /*CvPoint2D32f **/features = - (CvPoint2D32f*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(CvPoint2D32f)); - for (int i = 0; i < copy.maxFeatures; i++) - features[i] = copy.features[i]; - } + /*CvPoint2D32f **/features = + (CvPoint2D32f*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(CvPoint2D32f)); + for (int i = 0; i < copy.maxFeatures; i++) + features[i] = copy.features[i]; + } if (copy.prev_features) { - /*CvPoint2D32f **/prev_features = - (CvPoint2D32f*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(CvPoint2D32f)); - for (int i = 0; i < copy.maxFeatures; i++) - prev_features[i] = copy.prev_features[i]; - } + /*CvPoint2D32f **/prev_features = + (CvPoint2D32f*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(CvPoint2D32f)); + for (int i = 0; i < copy.maxFeatures; i++) + prev_features[i] = copy.prev_features[i]; + } if (copy.featuresid) { - /*long **/featuresid = (long*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(long)); - for (int i = 0; i < copy.maxFeatures; i++) - featuresid[i] = copy.featuresid[i]; - } + /*long **/featuresid = (long*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(long)); + for (int i = 0; i < copy.maxFeatures; i++) + featuresid[i] = copy.featuresid[i]; + } if (copy.prev_featuresid) { - /*long **/prev_featuresid = (long*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(long)); - for (int i = 0; i < copy.maxFeatures; i++) - prev_featuresid[i] = copy.prev_featuresid[i]; - } + /*long **/prev_featuresid = (long*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(long)); + for (int i = 0; i < copy.maxFeatures; i++) + prev_featuresid[i] = copy.prev_featuresid[i]; + } if (copy.status) { - /*char **/status = (char*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(char)); - for (int i = 0; i < copy.maxFeatures; i++) - status[i] = copy.status[i]; - } + /*char **/status = (char*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(char)); + for (int i = 0; i < copy.maxFeatures; i++) + status[i] = copy.status[i]; + } if (copy.lostDuringTrack) { /*bool **/lostDuringTrack = (bool*)cvAlloc((unsigned int)copy.maxFeatures*sizeof(bool)); for (int i = 0; i < copy.maxFeatures; i++) lostDuringTrack[i] = copy.lostDuringTrack[i]; } + + return *this; } vpKltOpencv::~vpKltOpencv() @@ -278,7 +777,7 @@ void vpKltOpencv::setMaxFeatures(const int input) { if (lostDuringTrack) cvFree(&lostDuringTrack); if (featuresid) cvFree(&featuresid); if (prev_featuresid) cvFree(&prev_featuresid); - + features = (CvPoint2D32f*)cvAlloc((unsigned int)maxFeatures*sizeof(CvPoint2D32f)); prev_features = (CvPoint2D32f*)cvAlloc((unsigned int)maxFeatures*sizeof(CvPoint2D32f)); @@ -334,7 +833,7 @@ void vpKltOpencv::initTracking(const IplImage *I, const IplImage *mask) countPrevFeatures = 0; flags = 0; initialized = 0; - globalcountFeatures = 0; + globalcountFeatures = 0; } initialized = 1; @@ -342,17 +841,17 @@ void vpKltOpencv::initTracking(const IplImage *I, const IplImage *mask) //Import cvCopy(I, image, 0); - //Recherche de points d'int�rets + //Recherche de points d'interets countFeatures = maxFeatures; countPrevFeatures = 0; IplImage* eig = cvCreateImage(cvGetSize(image), 32, 1); IplImage* temp = cvCreateImage(cvGetSize(image), 32, 1); cvGoodFeaturesToTrack(image, eig, temp, features, - &countFeatures, quality, min_distance, - mask, block_size, use_harris, harris_free_parameter); + &countFeatures, quality, min_distance, + mask, block_size, use_harris, harris_free_parameter); cvFindCornerSubPix(image, features, countFeatures, cvSize(win_size, win_size), - cvSize(-1,-1),cvTermCriteria(CV_TERMCRIT_ITER| - CV_TERMCRIT_EPS,20,0.03)); + cvSize(-1,-1),cvTermCriteria(CV_TERMCRIT_ITER| + CV_TERMCRIT_EPS,20,0.03)); cvReleaseImage(&eig); cvReleaseImage(&temp); @@ -366,28 +865,111 @@ void vpKltOpencv::initTracking(const IplImage *I, const IplImage *mask) if (OnNewFeature){ OnNewFeature(_tid, boucle, featuresid[boucle], features[boucle].x, - features[boucle].y); + features[boucle].y); } } } - + +/*! + Set the points that will be used as initialization during the next call to track(). + + \param I : Input image. + \param pts : Vector of points that should be tracked. + +*/ +void +vpKltOpencv::initTracking(const IplImage *I, CvPoint2D32f *pts, int size) +{ + if (size > maxFeatures) + throw(vpException(vpTrackingException::initializationError, + "Cannot initialize tracker from points")); + + //Creation des buffers + CvSize Sizeim, SizeI; + SizeI = cvGetSize(I); + bool b_imOK = true; + if(image != NULL){ + Sizeim = cvGetSize(image); + if(SizeI.width != Sizeim.width || SizeI.height != Sizeim.height) b_imOK = false; + } + if(image == NULL || prev_image == NULL || pyramid==NULL || prev_pyramid ==NULL || !b_imOK){ + reset(); + image = cvCreateImage(cvGetSize(I), 8, 1);image->origin = I->origin; + prev_image = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + pyramid = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + prev_pyramid = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + } else { + flags = 0; + } + // Save current features as previous features + countFeatures = size; + for (int i=0; i<countFeatures;i++) { + features[i] = pts[i]; + featuresid[i] = i; + } + + globalcountFeatures = size; + initialized = 1; + + cvCopy(I, image, 0); +} + +void +vpKltOpencv::initTracking(const IplImage *I, CvPoint2D32f *pts, long *fid, int size) +{ + if (size > maxFeatures) + throw(vpException(vpTrackingException::initializationError, + "Cannot initialize tracker from points")); + + //Creation des buffers + CvSize Sizeim, SizeI; + SizeI = cvGetSize(I); + bool b_imOK = true; + if(image != NULL){ + Sizeim = cvGetSize(image); + if(SizeI.width != Sizeim.width || SizeI.height != Sizeim.height) b_imOK = false; + } + if(image == NULL || prev_image == NULL || pyramid==NULL || prev_pyramid ==NULL || !b_imOK){ + reset(); + image = cvCreateImage(cvGetSize(I), 8, 1);image->origin = I->origin; + prev_image = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + pyramid = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + prev_pyramid = cvCreateImage(cvGetSize(I), IPL_DEPTH_8U, 1); + } else { + flags = 0; + } + // Save current features as previous features + countFeatures = size; + long max = 0; + for (int i=0; i<countFeatures;i++) { + features[i] = pts[i]; + featuresid[i] = fid[i]; + if (fid[i] > max) + max = fid[i]; + } + + globalcountFeatures = max + 1; + initialized = 1; + + cvCopy(I, image, 0); +} void vpKltOpencv::track(const IplImage *I) { if (!initialized) { vpERROR_TRACE("KLT Not initialized") ; throw(vpException(vpTrackingException::initializationError, - "KLT Not initialized")) ; + "KLT Not initialized")) ; } if (!I) { throw(vpException(vpTrackingException::initializationError, - "Image Not initialized")) ; + "Image Not initialized")) ; } if (I->depth != IPL_DEPTH_8U || I->nChannels != 1) { throw(vpException(vpTrackingException::initializationError, - "Bad Image format")) ; + "Bad Image format")) ; } @@ -411,11 +993,11 @@ void vpKltOpencv::track(const IplImage *I) if (countFeatures <= 0) return; cvCalcOpticalFlowPyrLK( prev_image, image, prev_pyramid, pyramid, - prev_features, features, countFeatures, - cvSize(win_size, win_size), pyramid_level, - status, 0, cvTermCriteria(CV_TERMCRIT_ITER - |CV_TERMCRIT_EPS,20,0.03), - flags ); + prev_features, features, countFeatures, + cvSize(win_size, win_size), pyramid_level, + status, 0, cvTermCriteria(CV_TERMCRIT_ITER + |CV_TERMCRIT_EPS,20,0.03), + flags ); if(!initial_guess) flags |= CV_LKFLOW_PYR_A_READY; @@ -429,17 +1011,17 @@ void vpKltOpencv::track(const IplImage *I) if (!status[i]) { lostDuringTrack[i] = 1; if (OnFeatureLost) - OnFeatureLost(_tid, i, featuresid[i], features[i].x, - features[i].y); + OnFeatureLost(_tid, i, featuresid[i], features[i].x, + features[i].y); continue; } if (IsFeatureValid) { if (!IsFeatureValid(_tid, features[i].x, features[i].y)) { - lostDuringTrack[i] = 1; - if (OnFeatureLost) - OnFeatureLost(_tid, i, featuresid[i], features[i].x, features[i].y); - continue; + lostDuringTrack[i] = 1; + if (OnFeatureLost) + OnFeatureLost(_tid, i, featuresid[i], features[i].x, features[i].y); + continue; } } features[k] = features[i]; @@ -464,10 +1046,10 @@ void vpKltOpencv::display(const vpImage<unsigned char> &I, vpColor color, unsigned int thickness) { if ((features == 0) || (I.bitmap==0) || (!initialized)) - { - vpERROR_TRACE(" Memory problem "); - throw(vpException(vpException::memoryAllocationError," Memory problem")); - } + { + vpERROR_TRACE(" Memory problem "); + throw(vpException(vpException::memoryAllocationError," Memory problem")); + } vpKltOpencv::display(I, features, featuresid, countFeatures, color, thickness); } @@ -488,10 +1070,10 @@ void vpKltOpencv::display(const vpImage<unsigned char> &I, void vpKltOpencv::getFeature(int index, int &id, float &x, float &y) const { if (index >= countFeatures) - { - vpERROR_TRACE(" Memory problem "); - throw(vpException(vpException::memoryAllocationError," Memory problem")); - } + { + vpERROR_TRACE(" Memory problem "); + throw(vpException(vpException::memoryAllocationError," Memory problem")); + } x = features[index].x; y = features[index].y; @@ -536,10 +1118,10 @@ vpKltOpencv::setInitialGuess(CvPoint2D32f **guess_pts) void vpKltOpencv::getPrevFeature(int index, int &id, float &x, float &y) const { if (index >= countPrevFeatures) - { - vpERROR_TRACE(" Memory problem "); - throw(vpException(vpException::memoryAllocationError," Memory problem")); - } + { + vpERROR_TRACE(" Memory problem "); + throw(vpException(vpException::memoryAllocationError," Memory problem")); + } x = prev_features[index].x; y = prev_features[index].y; @@ -553,18 +1135,18 @@ Add at the end of the feauture list. If there is no space left, the feature is not added (just return) */ void vpKltOpencv::addFeature(const int &id, - const float &x, const float &y) + const float &x, const float &y) { if (maxFeatures == countFeatures) - { - vpERROR_TRACE(" Cannot add the feature "); - return; - } + { + vpERROR_TRACE(" Cannot add the feature "); + return; + } CvPoint2D32f f; f.x = x; f.y = y; - features[countFeatures] = f; + features[countFeatures] = f; featuresid[countFeatures] = id; countFeatures ++; } @@ -572,10 +1154,10 @@ void vpKltOpencv::addFeature(const int &id, void vpKltOpencv::suppressFeature(int index) { if (index >= countFeatures) - { - vpERROR_TRACE(" Memory problem "); - throw(vpException(vpException::memoryAllocationError," Memory problem")); - } + { + vpERROR_TRACE(" Memory problem "); + throw(vpException(vpException::memoryAllocationError," Memory problem")); + } countFeatures --; @@ -666,7 +1248,7 @@ void vpKltOpencv::display(const vpImage<unsigned char>& I,const CvPoint2D32f* fe char id[10]; sprintf(id, "%ld", featuresid_list[i]); ip.set_u( vpMath::round( features_list[i].x + 5 ) ); - vpDisplay::displayCharString(I, ip, id, color); + vpDisplay::displayText(I, ip, id, color); } } @@ -700,7 +1282,7 @@ void vpKltOpencv::display(const vpImage<vpRGBa>& I,const CvPoint2D32f* features_ char id[10]; sprintf(id, "%ld", featuresid_list[i]); ip.set_u( vpMath::round( features_list[i].x + 5 ) ); - vpDisplay::displayCharString(I, ip, id, color); + vpDisplay::displayText(I, ip, id, color); } } diff --git a/src/tracking/klt/vpKltOpencv.h b/src/tracking/klt/vpKltOpencv.h index b6e0cd809c8f3727426d228158b07bc53ee5b5b7..12fc28390004c1330b517b916fd16962421619f9 100644 --- a/src/tracking/klt/vpKltOpencv.h +++ b/src/tracking/klt/vpKltOpencv.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: vpKltOpencv.h 4231 2013-04-29 16:26:28Z fspindle $ + * $Id: vpKltOpencv.h 5148 2015-01-12 14:31:30Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,7 +26,7 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * @@ -52,9 +52,128 @@ #define vpKltOpencv_h #include <visp/vpConfig.h> +#include <visp/vpColor.h> +#include <visp/vpImage.h> + +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020408)) + +#include <opencv2/video/tracking.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/highgui/highgui.hpp> + +/*! + \class vpKltOpencv + + \ingroup TrackingImagePoint + + \brief Wrapper for the KLT (Kanade-Lucas-Tomasi) feature tracker + implemented in OpenCV. + + The following example available in tutorial-klt-tracker.cpp shows how to use + the main functions of the class. -#ifdef VISP_HAVE_OPENCV + \include tutorial-klt-tracker.cpp + + A line by line explanation is provided in \ref tutorial-tracking-keypoint. +*/ +class VISP_EXPORT vpKltOpencv +{ +public: + vpKltOpencv(); + vpKltOpencv(const vpKltOpencv& copy); + virtual ~vpKltOpencv(); + void addFeature(const float &x, const float &y); + void addFeature(const long &id, const float &x, const float &y); + void addFeature(const cv::Point2f &f); + + void display(const vpImage<unsigned char> &I, + const vpColor &color = vpColor::red, unsigned int thickness=1); + static void display(const vpImage<unsigned char> &I, const std::vector<cv::Point2f> &features, + const vpColor &color = vpColor::green, unsigned int thickness=1); + static void display(const vpImage<vpRGBa> &I, const std::vector<cv::Point2f> &features, + const vpColor &color = vpColor::green, unsigned int thickness=1); + static void display(const vpImage<unsigned char> &I, const std::vector<cv::Point2f> &features, + const std::vector<long> &featuresid, + const vpColor &color = vpColor::green, unsigned int thickness=1); + static void display(const vpImage<vpRGBa> &I, const std::vector<cv::Point2f> &features, + const std::vector<long> &featuresid, + const vpColor &color = vpColor::green, unsigned int thickness=1); + + //! Get the size of the averaging block used to track the features. + int getBlockSize() const {return m_blockSize;} + void getFeature(const int &index, int &id, float &x, float &y) const; + //! Get the list of current features. + std::vector<cv::Point2f> getFeatures() const {return m_points[1];} + //CvPoint2D32f* getFeatures() const {return features;} + //! Get the unique id of each feature. + std::vector<long> getFeaturesId() const {return m_points_id;} + //long* getFeaturesId() const {return featuresid;} + //! Get the free parameter of the Harris detector. + double getHarrisFreeParameter() const {return m_harris_k;} + //! Get the list of lost feature + //bool *getListOfLostFeature() const { return lostDuringTrack; } + //! Get the maximum number of features to track in the image. + int getMaxFeatures() const {return m_maxCount;} + //! Get the minimal Euclidean distance between detected corners during initialization. + double getMinDistance() const {return m_minDistance;} + //! Get the number of current features + int getNbFeatures() const { return (int)m_points[1].size(); } + //! Get the number of previous features. + int getNbPrevFeatures() const { return (int)m_points[0].size(); } + //void getPrevFeature(int index, int &id, float &x, float &y) const; + //! Get the list of previous features + std::vector<cv::Point2f> getPrevFeatures() const {return m_points[0];} + //CvPoint2D32f* getPrevFeatures() const {return prev_features;} + //! Get the list of features id + //long* getPrevFeaturesId() const {return prev_featuresid;} + //! Get the maximal pyramid level. + int getPyramidLevels() const {return m_pyrMaxLevel;} + //! Get the parameter characterizing the minimal accepted quality of image corners. + double getQuality() const {return m_qualityLevel;} + //! Get the window size used to refine the corner locations. + int getWindowSize() const {return m_winSize;} + + void initTracking(const cv::Mat &I, const cv::Mat &mask=cv::Mat()); + void initTracking(const cv::Mat &I, const std::vector<cv::Point2f> &pts); + void initTracking(const cv::Mat &I, const std::vector<cv::Point2f> &pts, const std::vector<long> &ids); + + vpKltOpencv & operator=(const vpKltOpencv& copy); + void track(const cv::Mat &I); + void setBlockSize(const int blockSize); + void setHarrisFreeParameter(double harris_k); + void setInitialGuess(const std::vector<cv::Point2f> &guess_pts); + void setMaxFeatures(const int maxCount); + void setMinDistance(double minDistance); + void setMinEigThreshold(double minEigThreshold); + void setPyramidLevels(const int pyrMaxLevel); + void setQuality(double qualityLevel); + //! Does nothing. Just here for compat with previous releases that use OpenCV C api to do the tracking. + void setTrackerId(int tid) {(void)tid;} + void setUseHarris(const int useHarrisDetector); + void setWindowSize(const int winSize); + void suppressFeature(const int &index); + +protected: + cv::Mat m_gray, m_prevGray; + std::vector<cv::Point2f> m_points[2]; //!< Previous [0] and current [1] keypoint location + std::vector<long> m_points_id; //!< Keypoint id + int m_maxCount; + cv::TermCriteria m_termcrit; + int m_winSize; + double m_qualityLevel; + double m_minDistance; + double m_minEigThreshold; + double m_harris_k; + int m_blockSize; + int m_useHarrisDetector; + int m_pyrMaxLevel; + long m_next_points_id; + bool m_initial_guess; + +}; + +#elif defined(VISP_HAVE_OPENCV) #ifdef _CH_ #pragma package <opencv> #endif @@ -102,7 +221,7 @@ typedef void (*funcevent)(int); */ class VISP_EXPORT vpKltOpencv { - private: +private: int initialized; //Is the tracker ready ? int maxFeatures; //Maximum number of features to track (Default 50) @@ -150,14 +269,14 @@ class VISP_EXPORT vpKltOpencv funcinfo OnMeasureFeature; funccheck IsFeatureValid; - private: +private: //Internal void clean(); void cleanAll(); void reset(); - public: +public: vpKltOpencv(); vpKltOpencv(const vpKltOpencv& copy); virtual ~vpKltOpencv(); @@ -201,7 +320,9 @@ class VISP_EXPORT vpKltOpencv //Detect corners in the image. Initialize the tracker void initTracking(const IplImage *I, const IplImage *mask = NULL); - + void initTracking(const IplImage *I, CvPoint2D32f *pts, int size); + void initTracking(const IplImage *I, CvPoint2D32f *pts, long *fid, int size); + vpKltOpencv & operator=(const vpKltOpencv& copy); //Track ! void track(const IplImage *I); @@ -274,7 +395,7 @@ class VISP_EXPORT vpKltOpencv void suppressFeature(int index); -//Static Functions + //Static Functions public: static void display(const vpImage<unsigned char>& I, const CvPoint2D32f* features_list, const int &nbFeatures, vpColor color = vpColor::green, diff --git a/src/tracking/mbt/edge/vpMbEdgeTracker.cpp b/src/tracking/mbt/edge/vpMbEdgeTracker.cpp index 8e474fb66e63f8fb8af68baae7fc98bb4226f805..259a0c1409fcd24c9b14e7d935873b8981323cd8 100644 --- a/src/tracking/mbt/edge/vpMbEdgeTracker.cpp +++ b/src/tracking/mbt/edge/vpMbEdgeTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbEdgeTracker.cpp 4337 2013-07-23 13:57:53Z ayol $ + * $Id: vpMbEdgeTracker.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,8 +46,6 @@ \brief Make the complete tracking of an object by using its CAD model. */ - - #include <visp/vpDebug.h> #include <visp/vpPose.h> #include <visp/vpExponentialMap.h> @@ -65,42 +63,32 @@ #include <visp/vpMbtDistanceLine.h> #include <visp/vpMbtXmlParser.h> #include <visp/vpMbtPolygon.h> +#include <visp/vpVelocityTwistMatrix.h> #include <limits> #include <string> #include <sstream> #include <float.h> +#include <map> + +bool samePoint(const vpPoint &P1, const vpPoint &P2, double threshold); /*! Basic constructor */ vpMbEdgeTracker::vpMbEdgeTracker() + : compute_interaction(1), lambda(1), me(), lines(1), circles(1), cylinders(1), nline(0), ncircle(0), ncylinder(0), + nbvisiblepolygone(0), percentageGdPt(0.4), scales(1), + Ipyramid(0), scaleLevel(0) { - index_polygon =0; - compute_interaction=1; - nline = 0; - ncylinder = 0; - lambda = 1; - nbvisiblepolygone = 0; - percentageGdPt = 0.4; - computeCovariance = false; + angleAppears = vpMath::rad(89); + angleDisappears = vpMath::rad(89); - lines.resize(1); - cylinders.resize(1); - scales.resize(1); scales[0] = true; - lines[0].clear(); - cylinders[0].clear(); - Ipyramid.resize(0); #ifdef VISP_HAVE_OGRE faces.getOgreContext()->setWindowName("MBT Edge"); #endif - useOgre = false; - - angleAppears = vpMath::rad(95); - angleDisappears = vpMath::rad(95); - clippingFlag = vpMbtPolygon::NO_CLIPPING; } /*! @@ -109,8 +97,9 @@ vpMbEdgeTracker::vpMbEdgeTracker() vpMbEdgeTracker::~vpMbEdgeTracker() { vpMbtDistanceLine *l ; - vpMbtDistanceCylinder *c; - + vpMbtDistanceCylinder *cy; + vpMbtDistanceCircle *ci; + for (unsigned int i = 0; i < lines.size(); i += 1){ if(scales[i]){ for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[i].begin(); it!=lines[i].end(); ++it){ @@ -122,29 +111,39 @@ vpMbEdgeTracker::~vpMbEdgeTracker() } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[i].begin(); it!=cylinders[i].end(); ++it){ - c = *it; - if (c!=NULL){ - delete c ; + cy = *it; + if (cy!=NULL){ + delete cy ; } - c = NULL ; + cy = NULL ; } lines[i].clear(); cylinders[i].clear(); } } + for (unsigned int i = 0; i < circles.size(); i += 1){ + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + if (ci!=NULL){ + delete ci ; + } + ci = NULL ; + } + circles[i].clear(); + } cleanPyramid(Ipyramid); } /*! Set the moving edge parameters. - \param me : an instance of vpMe containing all the desired parameters + \param p_me : an instance of vpMe containing all the desired parameters */ void -vpMbEdgeTracker::setMovingEdge(const vpMe &me) +vpMbEdgeTracker::setMovingEdge(const vpMe &p_me) { - this->me = me; + this->me = p_me; for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ @@ -157,26 +156,13 @@ vpMbEdgeTracker::setMovingEdge(const vpMe &me) cy = *it; cy->setMovingEdge(&(this->me)) ; } - } - } -} -/*! - Use Ogre3D for visibility tests - - \warning This function has to be called before the initialization of the tracker. - - \param v : True to use it, False otherwise -*/ -void -vpMbEdgeTracker::setOgreVisibilityTest(const bool &v) -{ - useOgre = v; - if(useOgre){ -#ifndef VISP_HAVE_OGRE - useOgre = false; - std::cout << "WARNING: ViSP doesn't have Ogre3D, basic visibility test will be used. setOgreVisibilityTest() set to false." << std::endl; -#endif + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + ci->setMovingEdge(&(this->me)) ; + } + } } } @@ -199,10 +185,13 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) // compute the interaction matrix and its pseudo inverse vpMbtDistanceLine *l ; vpMbtDistanceCylinder *cy ; + vpMbtDistanceCircle *ci ; - vpColVector w; - vpColVector weighted_error; + //vpColVector w; vpColVector factor; + //vpColVector error; // s-s* + vpColVector weighted_error; // Weighted error vector wi(s-s)* + vpVelocityTwistMatrix cVo; unsigned int iter = 0; @@ -210,12 +199,13 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) unsigned int nbrow = 0; unsigned int nberrors_lines = 0; unsigned int nberrors_cylinders = 0; + unsigned int nberrors_circles = 0; for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ l = *it; - nbrow += l->nbFeature ; + nbrow += l->nbFeature; nberrors_lines+=l->nbFeature; - l->initInteractionMatrixError() ; + l->initInteractionMatrixError(); } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ @@ -224,17 +214,23 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) nberrors_cylinders += cy->nbFeature ; cy->initInteractionMatrixError() ; } - + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + nbrow += ci->nbFeature ; + nberrors_circles += ci->nbFeature ; + ci->initInteractionMatrixError() ; + } + if (nbrow==0){ - vpERROR_TRACE("\n\t\t Error-> not enough data in the interaction matrix...") ; - throw vpTrackingException(vpTrackingException::notEnoughPointError, "\n\t\t Error-> not enough data in the interaction matrix..."); + throw vpTrackingException(vpTrackingException::notEnoughPointError, "No data found to compute the interaction matrix..."); } vpMatrix L(nbrow,6), Lp; // compute the error vector - vpColVector error(nbrow); - unsigned int nerror = error.getRows(); + m_error.resize(nbrow); + unsigned int nerror = m_error.getRows(); vpColVector v ; double limite = 3; //Une limite de 3 pixels @@ -245,14 +241,17 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) bool reloop = true; double count = 0; + bool isoJoIdentity_ = isoJoIdentity; // Backup since it can be modified if L is not full rank + /*** First phase ***/ + while ( reloop == true && iter<10) { if(iter==0) { weighted_error.resize(nerror) ; - w.resize(nerror); - w = 0; + m_w.resize(nerror); + m_w = 0; factor.resize(nerror); factor = 1; } @@ -268,8 +267,8 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) double fac = 1; if (iter == 0) { - for(std::list<int>::const_iterator it = l->Lindex_polygon.begin(); it!=l->Lindex_polygon.end(); ++it){ - int index = *it; + for(std::list<int>::const_iterator itindex = l->Lindex_polygon.begin(); itindex!=l->Lindex_polygon.end(); ++itindex){ + int index = *itindex; if (l->hiddenface->isAppearing((unsigned int)index)) { fac = 0.2; @@ -293,11 +292,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) { L[n+i][j] = l->L[i][j]; //On remplit la matrice d'interaction globale } - error[n+i] = l->error[i]; //On remplit la matrice d'erreur + m_error[n+i] = l->error[i]; //On remplit la matrice d'erreur - if (error[n+i] <= limite) count = count+1.0; //Si erreur proche de 0 on incremente cur + if (m_error[n+i] <= limite) count = count+1.0; //Si erreur proche de 0 on incremente cur - w[n+i] = 0; + m_w[n+i] = 0; if (iter == 0) { @@ -316,11 +315,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_next = l->error[1]; if ( fabs(e_cur - e_next) < limite && vpMath::sign(e_cur) == vpMath::sign(e_next) ) { - w[n+i] = 1/*0.5*/; + m_w[n+i] = 1/*0.5*/; } e_prev = e_cur; } - else w[n+i] = 1; + else m_w[n+i] = 1; } //If pour la derniere extremite des moving edges @@ -329,7 +328,7 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_cur = l->error[i]; if ( fabs(e_cur - e_prev) < limite && vpMath::sign(e_cur) == vpMath::sign(e_prev) ) { - w[n+i] += 1/*0.5*/; + m_w[n+i] += 1/*0.5*/; } } @@ -339,11 +338,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_next = l->error[i+1]; if ( fabs(e_cur - e_prev) < limite ) { - w[n+i] += 0.5; + m_w[n+i] += 0.5; } if ( fabs(e_cur - e_next) < limite ) { - w[n+i] += 0.5; + m_w[n+i] += 0.5; } e_prev = e_cur; } @@ -351,8 +350,6 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) n+= l->nbFeature ; } - - for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; cy->computeInteractionMatrixError(cMo, _I); @@ -369,11 +366,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) for(unsigned int j=0; j < 6 ; j++){ L[n+i][j] = cy->L[i][j]; //On remplit la matrice d'interaction globale } - error[n+i] = cy->error[i]; //On remplit la matrice d'erreur + m_error[n+i] = cy->error[i]; //On remplit la matrice d'erreur - if (error[n+i] <= limite) count = count+1.0; //Si erreur proche de 0 on incremente cur + if (m_error[n+i] <= limite) count = count+1.0; //Si erreur proche de 0 on incremente cur - w[n+i] = 0; + m_w[n+i] = 0; if (iter == 0) { @@ -399,11 +396,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_next = cy->error[1]; if ( fabs(e_cur - e_next) < limite && vpMath::sign(e_cur) == vpMath::sign(e_next) ) { - w[n+i] = 1/*0.5*/; + m_w[n+i] = 1/*0.5*/; } e_prev = e_cur; } - else w[n+i] = 1; + else m_w[n+i] = 1; } if (i == cy->nbFeaturel1) { @@ -413,11 +410,11 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_next = cy->error[i+1]; if ( fabs(e_cur - e_next) < limite && vpMath::sign(e_cur) == vpMath::sign(e_next) ) { - w[n+i] = 1/*0.5*/; + m_w[n+i] = 1/*0.5*/; } e_prev = e_cur; } - else w[n+i] = 1; + else m_w[n+i] = 1; } //If pour la derniere extremite des moving edges @@ -426,7 +423,7 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_cur = cy->error[i]; if ( fabs(e_cur - e_prev) < limite && vpMath::sign(e_cur) == vpMath::sign(e_prev) ) { - w[n+i] += 1/*0.5*/; + m_w[n+i] += 1/*0.5*/; } } //If pour la derniere extremite des moving edges @@ -435,7 +432,7 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_cur = cy->error[i]; if ( fabs(e_cur - e_prev) < limite && vpMath::sign(e_cur) == vpMath::sign(e_prev) ) { - w[n+i] += 1/*0.5*/; + m_w[n+i] += 1/*0.5*/; } } @@ -444,10 +441,10 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) e_cur = cy->error[i]; e_next = cy->error[i+1]; if ( fabs(e_cur - e_prev) < limite ){ - w[n+i] += 0.5; + m_w[n+i] += 0.5; } if ( fabs(e_cur - e_next) < limite ){ - w[n+i] += 0.5; + m_w[n+i] += 0.5; } e_prev = e_cur; } @@ -455,7 +452,78 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) n+= cy->nbFeature ; } - + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->computeInteractionMatrixError(cMo); + double fac = 1.0; + + std::list<vpMeSite>::const_iterator itCir; + if (iter == 0 && (ci->meEllipse != NULL)) { + itCir = ci->meEllipse->getMeList().begin(); + } + + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + for(unsigned int j=0; j < 6 ; j++){ + L[n+i][j] = ci->L[i][j]; //On remplit la matrice d'interaction globale + } + m_error[n+i] = ci->error[i]; //On remplit la matrice d'erreur + + if (m_error[n+i] <= limite) count = count+1.0; //Si erreur proche de 0 on incremente cur + + m_w[n+i] = 0; + + if (iter == 0) + { + factor[n+i] = fac; + vpMeSite site = *itCir; + if (site.getState() != vpMeSite::NO_SUPPRESSION) factor[n+i] = 0.2; + ++itCir; + } + + //If pour la premiere extremite des moving edges + if (i == 0) + { + e_cur = ci->error[0]; + if (ci->nbFeature > 1) + { + e_next = ci->error[1]; + if ( fabs(e_cur - e_next) < limite && vpMath::sign(e_cur) == vpMath::sign(e_next) ) + { + m_w[n+i] = 1/*0.5*/; + } + e_prev = e_cur; + } + else m_w[n+i] = 1; + } + + //If pour la derniere extremite des moving edges + else if(i == ci->nbFeature-1) + { + e_cur = ci->error[i]; + if ( fabs(e_cur - e_prev) < limite && vpMath::sign(e_cur) == vpMath::sign(e_prev) ) + { + m_w[n+i] += 1/*0.5*/; + } + } + + else + { + e_cur = ci->error[i]; + e_next = ci->error[i+1]; + if ( fabs(e_cur - e_prev) < limite ){ + m_w[n+i] += 0.5; + } + if ( fabs(e_cur - e_next) < limite ){ + m_w[n+i] += 0.5; + } + e_prev = e_cur; + } + } + + n+= ci->nbFeature ; + } + count = count / (double)nbrow; if (count < 0.85){ reloop = true; @@ -466,8 +534,8 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) double wi ; double eri ; for(unsigned int i = 0; i < nerror; i++){ - wi = w[i]*factor[i]; - eri = error[i]; + wi = m_w[i]*factor[i]; + eri = m_error[i]; num += wi*vpMath::sqr(eri); den += wi ; @@ -477,49 +545,89 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) if((iter==0) || compute_interaction){ for (unsigned int i=0 ; i < nerror ; i++){ for (unsigned int j=0 ; j < 6 ; j++){ - L[i][j] = w[i]*factor[i]*L[i][j] ; + L[i][j] = m_w[i]*factor[i]*L[i][j] ; } } } - LTL = L.AtA(); - computeJTR(L, weighted_error, LTR); - v = -0.7*LTL.pseudoInverse(LTL.getRows()*DBL_EPSILON)*LTR; + // If all the 6 dof should be estimated, we check if the interaction matrix is full rank. + // If not we remove automatically the dof that cannot be estimated + // This is particularly useful when consering circles (rank 5) and cylinders (rank 4) + if (isoJoIdentity_) { + cVo.buildFrom(cMo); + + vpMatrix K; // kernel + unsigned int rank = (L*cVo).kernel(K); + if(rank == 0) { + throw vpException(vpException::fatalError, "Rank=0, cannot estimate the pose !"); + } + if (rank != 6) { + vpMatrix I; // Identity + I.eye(6); + oJo = I-K.AtA(); + + isoJoIdentity_ = false; + } + } + + if(isoJoIdentity_){ + LTL = L.AtA(); + computeJTR(L, weighted_error, LTR); + v = -0.7*LTL.pseudoInverse(LTL.getRows()*std::numeric_limits<double>::epsilon())*LTR; + } + else{ + cVo.buildFrom(cMo); + vpMatrix LVJ = (L*cVo*oJo); + vpMatrix LVJTLVJ = (LVJ).AtA(); + vpMatrix LVJTR; + computeJTR(LVJ, weighted_error, LVJTR); + v = -0.7*LVJTLVJ.pseudoInverse(LVJTLVJ.getRows()*std::numeric_limits<double>::epsilon())*LVJTR; + v = cVo * v; + } + cMo = vpExponentialMap::direct(v).inverse() * cMo; iter++; } -// cout << "\t First minimization in " << iter << " iteration " << endl ; + +// std::cout << "\t First minimization in " << iter << " iteration give as initial cMo: \n" << cMo << std::endl ; + /*** Second phase ***/ vpRobust robust_lines(nberrors_lines); vpRobust robust_cylinders(nberrors_cylinders); + vpRobust robust_circles(nberrors_circles); robust_lines.setIteration(0) ; robust_cylinders.setIteration(0) ; + robust_circles.setIteration(0) ; iter = 0; vpColVector w_lines(nberrors_lines); vpColVector w_cylinders(nberrors_cylinders); + vpColVector w_circles(nberrors_circles); vpColVector error_lines(nberrors_lines); vpColVector error_cylinders(nberrors_cylinders); + vpColVector error_circles(nberrors_circles); - vpColVector error_vec; + vpHomogeneousMatrix cMoPrev; vpColVector W_true; vpMatrix L_true; + vpMatrix LVJ_true; while ( ((int)((residu_1 - r)*1e8) !=0 ) && (iter<30)) { unsigned int n = 0 ; unsigned int nlines = 0; unsigned int ncylinders = 0; + unsigned int ncircles = 0; for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ l = *it; l->computeInteractionMatrixError(cMo) ; for (unsigned int i=0 ; i < l->nbFeature ; i++){ for (unsigned int j=0; j < 6 ; j++){ L[n+i][j] = l->L[i][j]; - error[n+i] = l->error[i]; - error_lines[nlines+i] = error[n+i]; + m_error[n+i] = l->error[i]; + error_lines[nlines+i] = m_error[n+i]; } } n+= l->nbFeature; @@ -532,51 +640,75 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) for(unsigned int i=0 ; i < cy->nbFeature ; i++){ for(unsigned int j=0; j < 6 ; j++){ L[n+i][j] = cy->L[i][j]; - error[n+i] = cy->error[i]; - error_cylinders[ncylinders+i] = error[n+i]; + m_error[n+i] = cy->error[i]; + error_cylinders[ncylinders+i] = m_error[n+i]; } } n+= cy->nbFeature ; ncylinders+= cy->nbFeature ; } - + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->computeInteractionMatrixError(cMo) ; + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + for(unsigned int j=0; j < 6 ; j++){ + L[n+i][j] = ci->L[i][j]; + m_error[n+i] = ci->error[i]; + error_circles[ncircles+i] = m_error[n+i]; + } + } + + n+= ci->nbFeature ; + ncircles+= ci->nbFeature ; + } + if(iter==0) { weighted_error.resize(nerror); - w.resize(nerror); - w = 1; + m_w.resize(nerror); + m_w = 1; w_lines.resize(nberrors_lines); w_lines = 1; w_cylinders.resize(nberrors_cylinders); w_cylinders = 1; + w_circles.resize(nberrors_circles); + w_circles = 1; robust_lines.setThreshold(2/cam.get_px()); robust_cylinders.setThreshold(2/cam.get_px()); - if(nberrors_lines > 0) - robust_lines.MEstimator(vpRobust::TUKEY, error_lines,w_lines); - if(nberrors_cylinders > 0){ + robust_circles.setThreshold(vpMath::sqr(2/cam.get_px())); + if(nberrors_lines > 0) + robust_lines.MEstimator(vpRobust::TUKEY, error_lines,w_lines); + if(nberrors_cylinders > 0) robust_cylinders.MEstimator(vpRobust::TUKEY, error_cylinders,w_cylinders); - } + if(nberrors_circles > 0) + robust_circles.MEstimator(vpRobust::TUKEY, error_circles,w_circles); } else { robust_lines.setIteration(iter); robust_cylinders.setIteration(iter); - if(nberrors_lines > 0) - robust_lines.MEstimator(vpRobust::TUKEY, error_lines, w_lines); - if(nberrors_cylinders > 0){ + robust_circles.setIteration(iter); + if(nberrors_lines > 0) + robust_lines.MEstimator(vpRobust::TUKEY, error_lines, w_lines); + if(nberrors_cylinders > 0) robust_cylinders.MEstimator(vpRobust::TUKEY, error_cylinders,w_cylinders); - } + if(nberrors_circles > 0) + robust_circles.MEstimator(vpRobust::TUKEY, error_circles,w_circles); } unsigned int cpt = 0; while(cpt<nbrow){ if(cpt<nberrors_lines){ - w[cpt] = w_lines[cpt]; + m_w[cpt] = w_lines[cpt]; + } + else if (cpt<nberrors_lines+nberrors_cylinders){ + m_w[cpt] = w_cylinders[cpt-nberrors_lines]; } - else{ - w[cpt] = w_cylinders[cpt-nberrors_lines]; + else { + m_w[cpt] = w_circles[cpt-nberrors_lines-nberrors_cylinders]; } cpt++; } @@ -590,11 +722,19 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) L_true = L; W_true = vpColVector(nerror); + + if(computeCovariance){ + L_true = L; + if(!isoJoIdentity_){ + cVo.buildFrom(cMo); + LVJ_true = (L*cVo*oJo); + } + } for(unsigned int i=0; i<nerror; i++){ - wi = w[i]*factor[i]; - W_true[i] = wi*wi; - eri = error[i]; + wi = m_w[i]*factor[i]; + W_true[i] = wi; + eri = m_error[i]; num += wi*vpMath::sqr(eri); den += wi; @@ -606,23 +746,44 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) if((iter==0)|| compute_interaction){ for (unsigned int i=0 ; i < nerror ; i++){ for (unsigned int j=0 ; j < 6 ; j++){ - L[i][j] = w[i]*factor[i]*L[i][j]; + L[i][j] = m_w[i]*factor[i]*L[i][j]; } } } - LTL = L.AtA(); - computeJTR(L, weighted_error, LTR); - v = -lambda*LTL.pseudoInverse(LTL.getRows()*DBL_EPSILON)*LTR; + if(isoJoIdentity_){ + LTL = L.AtA(); + computeJTR(L, weighted_error, LTR); + v = -lambda*LTL.pseudoInverse(LTL.getRows()*std::numeric_limits<double>::epsilon())*LTR; + } + else{ + cVo.buildFrom(cMo); + vpMatrix LVJ = (L*cVo*oJo); + vpMatrix LVJTLVJ = (LVJ).AtA(); + vpMatrix LVJTR; + computeJTR(LVJ, weighted_error, LVJTR); + v = -lambda*LVJTLVJ.pseudoInverse(LVJTLVJ.getRows()*std::numeric_limits<double>::epsilon())*LVJTR; + v = cVo * v; + } + + cMoPrev = cMo; cMo = vpExponentialMap::direct(v).inverse() * cMo; iter++; } - + + // std::cout << "VVS estimate pose cMo:\n" << cMo << std::endl; if(computeCovariance){ - vpMatrix D; //Should be the M.diag(wi) * M.diag(wi).transpose() = (M.diag(wi^2)) which is more efficient + vpMatrix D; D.diag(W_true); - covarianceMatrix = vpMatrix::computeCovarianceMatrix(L_true,v,-lambda*error,D); + + // Note that here the covariance is computed on cMoPrev for time computation efficiency + if(isoJoIdentity_){ + computeCovarianceMatrix(cMoPrev,m_error,L_true,D); + } + else{ + computeCovarianceMatrix(cMoPrev,m_error,LVJ_true,D); + } } unsigned int n =0 ; @@ -634,9 +795,9 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) if (l->nbFeature > 0) itListLine = l->meline->getMeList().begin(); for (unsigned int i=0 ; i < l->nbFeature ; i++){ - wmean += w[n+i] ; + wmean += m_w[n+i] ; vpMeSite p = *itListLine; - if (w[n+i] < 0.5){ + if (m_w[n+i] < 0.5){ p.setState(vpMeSite::M_ESTIMATOR); *itListLine = p; @@ -658,13 +819,13 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) } } - // Same thing with cylinders as with lines for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; double wmean = 0 ; std::list<vpMeSite>::iterator itListCyl1; std::list<vpMeSite>::iterator itListCyl2; + if (cy->nbFeature > 0){ itListCyl1 = cy->meline1->getMeList().begin(); itListCyl2 = cy->meline2->getMeList().begin(); @@ -672,9 +833,9 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) wmean = 0; for(unsigned int i=0 ; i < cy->nbFeaturel1 ; i++){ - wmean += w[n+i] ; + wmean += m_w[n+i] ; vpMeSite p = *itListCyl1; - if (w[n+i] < 0.5){ + if (m_w[n+i] < 0.5){ p.setState(vpMeSite::M_ESTIMATOR); *itListCyl1 = p; @@ -696,9 +857,9 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) wmean = 0; for(unsigned int i=cy->nbFeaturel1 ; i < cy->nbFeature ; i++){ - wmean += w[n+i] ; + wmean += m_w[n+i] ; vpMeSite p = *itListCyl2; - if (w[n+i] < 0.5){ + if (m_w[n+i] < 0.5){ p.setState(vpMeSite::M_ESTIMATOR); *itListCyl2 = p; @@ -720,6 +881,43 @@ vpMbEdgeTracker::computeVVS(const vpImage<unsigned char>& _I) n+= cy->nbFeature ; } + + // Same thing with circles as with lines + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + double wmean = 0 ; + std::list<vpMeSite>::iterator itListCir; + + if (ci->nbFeature > 0){ + itListCir = ci->meEllipse->getMeList().begin(); + } + + wmean = 0; + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + wmean += m_w[n+i] ; + vpMeSite p = *itListCir; + if (m_w[n+i] < 0.5){ + p.setState(vpMeSite::M_ESTIMATOR); + + *itListCir = p; + } + + ++itListCir; + } + + if (ci->nbFeature!=0) + wmean /= ci->nbFeature ; + else + wmean = 1; + + ci->setMeanWeight(wmean); + + if (wmean < 0.8){ + ci->Reinit = true; + } + + n+= ci->nbFeature ; + } } /*! @@ -740,45 +938,65 @@ vpMbEdgeTracker::testTracking() if (l->isVisible() && l->meline != NULL) { nbExpectedPoint += (int)l->meline->expecteddensity; - for(std::list<vpMeSite>::const_iterator it=l->meline->getMeList().begin(); it!=l->meline->getMeList().end(); ++it){ - vpMeSite pix = *it; + for(std::list<vpMeSite>::const_iterator itme=l->meline->getMeList().begin(); itme!=l->meline->getMeList().end(); ++itme){ + vpMeSite pix = *itme; if (pix.getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoint++; else nbBadPoint++; } } } - vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; - if (cy->meline1 !=NULL && cy->meline2 != NULL) + if ((cy->meline1 !=NULL && cy->meline2 != NULL) && cy->isVisible()) { nbExpectedPoint += (int)cy->meline1->expecteddensity; - for(std::list<vpMeSite>::const_iterator it=cy->meline1->getMeList().begin(); it!=cy->meline1->getMeList().end(); ++it){ - vpMeSite pix = *it; + for(std::list<vpMeSite>::const_iterator itme1=cy->meline1->getMeList().begin(); itme1!=cy->meline1->getMeList().end(); ++itme1){ + vpMeSite pix = *itme1; if (pix.getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoint++; else nbBadPoint++; } nbExpectedPoint += (int)cy->meline2->expecteddensity; - for(std::list<vpMeSite>::const_iterator it=cy->meline2->getMeList().begin(); it!=cy->meline2->getMeList().end(); ++it){ - vpMeSite pix = *it; + for(std::list<vpMeSite>::const_iterator itme2=cy->meline2->getMeList().begin(); itme2!=cy->meline2->getMeList().end(); ++itme2){ + vpMeSite pix = *itme2; if (pix.getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoint++; else nbBadPoint++; } } } - //if (nbGoodPoint < percentageGdPt *(nbGoodPoint+nbBadPoint) || nbExpectedPoint < 2) - // Compare the number of good points with the min between the number of expected points and number of points that are tracked - if ( ( (nbGoodPoint < percentageGdPt *nbExpectedPoint) && (nbGoodPoint < percentageGdPt *(nbGoodPoint+nbBadPoint)) ) // Modif FS - || nbExpectedPoint < 2) - { - throw vpTrackingException(vpTrackingException::fatalError, "Not enough points to track the object"); + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + if (ci->isVisible() && ci->meEllipse !=NULL) + { + nbExpectedPoint += ci->meEllipse->getExpectedDensity(); + for(std::list<vpMeSite>::const_iterator itme=ci->meEllipse->getMeList().begin(); itme!=ci->meEllipse->getMeList().end(); ++itme){ + vpMeSite pix = *itme; + if (pix.getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoint++; + else nbBadPoint++; + } + } + } + + // Compare the number of good points with the min between the number of expected points and + // number of points that are tracked + int nb_min = (int)vpMath::minimum(percentageGdPt *nbExpectedPoint, percentageGdPt *(nbGoodPoint + nbBadPoint) ); + //int nb_min = std::min(val1, val2); + if (nbGoodPoint < nb_min || nbExpectedPoint < 2) { + std::ostringstream oss; + oss << "Not enough moving edges (" + << nbGoodPoint + << ") to track the object: expected " + << nb_min + << ". Try to reduce the threshold=" + << percentageGdPt + << " using vpMbTracker::setGoodMovingEdgesRatioThreshold()"; + throw vpTrackingException(vpTrackingException::fatalError, oss.str()); } } - /*! Compute each state of the tracking procedure for all the feature sets. @@ -824,28 +1042,38 @@ vpMbEdgeTracker::track(const vpImage<unsigned char> &I) vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[lvl].begin(); it!=cylinders[lvl].end(); ++it){ cy = *it; - cy->initInteractionMatrixError(); + if(cy->isVisible()) { + cy->initInteractionMatrixError(); + } + } + + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[lvl].begin(); it!=circles[lvl].end(); ++it){ + ci = *it; + if (ci->isVisible()){ + ci->initInteractionMatrixError(); + } } try { computeVVS(*Ipyramid[lvl]); } - catch(...) + catch(vpException &e) { - vpTRACE("Error in computeVVS") ; - throw vpException(vpException::fatalError, "Error in computeVVS"); + covarianceMatrix = -1; + throw e; } - + try { testTracking(); } - catch(...) + catch(vpException &e) { - throw vpTrackingException(vpTrackingException::fatalError, "test Tracking fail"); + throw e; } - + if (displayFeatures) { if(lvl == 0){ @@ -858,21 +1086,29 @@ vpMbEdgeTracker::track(const vpImage<unsigned char> &I) for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[lvl].begin(); it!=cylinders[lvl].end(); ++it){ cy = *it; - cy->displayMovingEdges(I); + if(cy->isVisible()) { + cy->displayMovingEdges(I); + } + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[lvl].begin(); it!=circles[lvl].end(); ++it){ + ci = *it; + if (ci->isVisible()){ + ci->displayMovingEdges(I); + } } } } - + try { updateMovingEdge(I); } - catch(...) + catch(vpException &e) { - vpTRACE("Error in moving edge updating") ; - throw ; + throw e; } - + // Looking for new visible face bool newvisibleface = false ; visibleFace(I, cMo, newvisibleface) ; @@ -882,7 +1118,7 @@ vpMbEdgeTracker::track(const vpImage<unsigned char> &I) reinitMovingEdge(I,cMo); upScale(lvl); } - catch(...) + catch(vpException &e) { if(lvl != 0){ cMo = cMo_1; @@ -891,7 +1127,7 @@ vpMbEdgeTracker::track(const vpImage<unsigned char> &I) } else{ upScale(lvl); - throw ; + throw(e) ; } } } @@ -907,13 +1143,18 @@ vpMbEdgeTracker::track(const vpImage<unsigned char> &I) */ void vpMbEdgeTracker::init(const vpImage<unsigned char>& I) { + if(!modelInitialised){ + throw vpException(vpException::fatalError, "model not initialized"); + } + bool a = false; #ifdef VISP_HAVE_OGRE if(useOgre){ - if(!faces.isOgreInitialised()) + if(!faces.isOgreInitialised()){ faces.setBackgroundSizeOgre(I.getHeight(), I.getWidth()); faces.initOgre(cam); + } } #endif @@ -948,16 +1189,40 @@ vpMbEdgeTracker::setPose( const vpImage<unsigned char> &I, const vpHomogeneousMa { cMo = cdMo; - vpMbtDistanceLine *l; - lines[scaleLevel].front() ; - for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - if(l->meline != NULL){ - delete l->meline; - l->meline = NULL; + if (! lines[scaleLevel].empty()) { + lines[scaleLevel].front() ; + for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ + if((*it)->meline != NULL){ + delete (*it)->meline; + (*it)->meline = NULL; + } } } - + + if (! cylinders[scaleLevel].empty()) { + cylinders[scaleLevel].front() ; + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ + if((*it)->meline1 != NULL){ + delete (*it)->meline1; + (*it)->meline1 = NULL; + } + if((*it)->meline2 != NULL){ + delete (*it)->meline2; + (*it)->meline2 = NULL; + } + } + } + + if (! circles[scaleLevel].empty()) { + circles[scaleLevel].front() ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + if((*it)->meEllipse != NULL){ + delete (*it)->meEllipse; + (*it)->meEllipse = NULL; + } + } + } + init(I); } @@ -1018,8 +1283,6 @@ vpMbEdgeTracker::loadConfigFile(const std::string& configFile) <fov_clipping>1</fov_clipping> </face> <camera> - <width>640</width> - <height>480</height> <u0>320</u0> <v0>240</v0> <px>686.24</px> @@ -1037,17 +1300,16 @@ vpMbEdgeTracker::loadConfigFile(const char* configFile) vpMbtXmlParser xmlp; xmlp.setCameraParameters(cam); - xmlp.setMovingEdge(me); xmlp.setAngleAppear(vpMath::deg(angleAppears)); xmlp.setAngleDisappear(vpMath::deg(angleDisappears)); - + xmlp.setMovingEdge(me); + try{ std::cout << " *********** Parsing XML for Mb Edge Tracker ************ " << std::endl; xmlp.parse(configFile); } catch(...){ - vpERROR_TRACE("Can't open XML file \"%s\"\n ", configFile); - throw vpException(vpException::ioError, "problem to parse configuration file."); + throw vpException(vpException::ioError, "Cannot open XML file \"%s\"", configFile); } vpCameraParameters camera; @@ -1067,7 +1329,20 @@ vpMbEdgeTracker::loadConfigFile(const char* configFile) setFarClippingDistance(xmlp.getFarClippingDistance()); if(xmlp.getFovClipping()) - clippingFlag = clippingFlag | vpMbtPolygon::FOV_CLIPPING; + setClipping(clippingFlag | vpMbtPolygon::FOV_CLIPPING); + + useLodGeneral = xmlp.getLodState(); + minLineLengthThresholdGeneral = xmlp.getMinLineLengthThreshold(); + minPolygonAreaThresholdGeneral = xmlp.getMinPolygonAreaThreshold(); + + applyLodSettingInConfig = false; + if(this->getNbPolygon() > 0) { + applyLodSettingInConfig = true; + setLod(useLodGeneral); + setMinLineLengthThresh(minLineLengthThresholdGeneral); + setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral); + } + #else vpTRACE("You need the libXML2 to read the config file %s", configFile); #endif @@ -1078,28 +1353,29 @@ vpMbEdgeTracker::loadConfigFile(const char* configFile) Display the 3D model from a given position of the camera. \param I : The image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). + \param displayFullModel : If true, the full model is displayed (even the non visible faces). */ void -vpMbEdgeTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, - const vpColor& col, - const unsigned int thickness, const bool displayFullModel) -{ - vpMbtDistanceLine *l ; - +vpMbEdgeTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo_, + const vpCameraParameters &camera, const vpColor& col, + const unsigned int thickness, const bool displayFullModel) +{ for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - l->display(I,cMo, cam, col, thickness, displayFullModel); + (*it)->display(I,cMo_, camera, col, thickness, displayFullModel); } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ - (*it)->display(I, cMo, cam, col, thickness); + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); } break ; //displaying model on one scale only @@ -1108,7 +1384,7 @@ vpMbEdgeTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMat #ifdef VISP_HAVE_OGRE if(useOgre) - faces.displayOgre(cMo); + faces.displayOgre(cMo_); #endif } @@ -1116,37 +1392,37 @@ vpMbEdgeTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMat Display the 3D model from a given position of the camera. \param I : The image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). */ void -vpMbEdgeTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, - const vpColor& col, - const unsigned int thickness, const bool displayFullModel) +vpMbEdgeTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo_, + const vpCameraParameters &camera, const vpColor& col, + const unsigned int thickness, const bool displayFullModel) { - vpMbtDistanceLine *l ; - for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - l->display(I, cMo, cam, col, thickness, displayFullModel) ; + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel) ; } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ - (*it)->display(I, cMo, cam, col, thickness) ; + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel) ; } + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); + } break ; //displaying model on one scale only } } #ifdef VISP_HAVE_OGRE if(useOgre) - faces.displayOgre(cMo); + faces.displayOgre(cMo_); #endif } @@ -1160,16 +1436,16 @@ vpMbEdgeTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cM */ void vpMbEdgeTracker::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo) -{ +{ vpMbtDistanceLine *l ; - + lines[scaleLevel].front() ; for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ l = *it; bool isvisible = false ; - for(std::list<int>::const_iterator it=l->Lindex_polygon.begin(); it!=l->Lindex_polygon.end(); ++it){ - int index = *it; + for(std::list<int>::const_iterator itindex=l->Lindex_polygon.begin(); itindex!=l->Lindex_polygon.end(); ++itindex){ + int index = *itindex; if (index ==-1) isvisible =true ; else { @@ -1178,7 +1454,7 @@ vpMbEdgeTracker::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogen } //Si la ligne n'appartient a aucune face elle est tout le temps visible - if (l->Lindex_polygon.empty()) isvisible = true; + if (l->Lindex_polygon.empty()) isvisible = true; // Not sure that this can occur if (isvisible) { @@ -1197,12 +1473,62 @@ vpMbEdgeTracker::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogen } } - vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; - if (cy->meline1==NULL || cy->meline2==NULL){ - cy->initMovingEdge(I, _cMo) ; + + bool isvisible = false ; + + int index = cy->index_polygon; + if (index ==-1) isvisible =true ; + else + { + if (cy->hiddenface->isVisible((unsigned int)index)) isvisible = true ; + } +// vpTRACE("cyl with index %d is visible: %d", index, isvisible); + + if (isvisible) + { + cy->setVisible(true) ; + if (cy->meline1==NULL || cy->meline2==NULL){ + cy->initMovingEdge(I, _cMo) ; + } + } + else + { + cy->setVisible(false) ; + if (cy->meline1!=NULL) delete cy->meline1; + if (cy->meline2!=NULL) delete cy->meline2; + cy->meline1=NULL; + cy->meline2=NULL; + } + } + + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + bool isvisible = false ; + + int index = ci->index_polygon; + if (index ==-1) isvisible =true ; + else + { + if (ci->hiddenface->isVisible((unsigned int)index)) isvisible = true ; + } + + if (isvisible) + { + ci->setVisible(true) ; + if (ci->meEllipse==NULL) + { + ci->initMovingEdge(I, _cMo) ; + } + } + else + { + ci->setVisible(false) ; + if (ci->meEllipse!=NULL) delete ci->meEllipse; + ci->meEllipse=NULL; } } } @@ -1230,10 +1556,23 @@ vpMbEdgeTracker::trackMovingEdge(const vpImage<unsigned char> &I) vpMbtDistanceCylinder *cy; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; - if(cy->meline1 == NULL || cy->meline2 == NULL){ - cy->initMovingEdge(I, cMo); + if(cy->isVisible()) { + if(cy->meline1 == NULL || cy->meline2 == NULL){ + cy->initMovingEdge(I, cMo); + } + cy->trackMovingEdge(I, cMo) ; + } + } + + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + if(ci->isVisible() == true){ + if(ci->meEllipse == NULL){ + ci->initMovingEdge(I, cMo); + } + ci->trackMovingEdge(I, cMo) ; } - cy->trackMovingEdge(I, cMo) ; } } @@ -1255,15 +1594,23 @@ vpMbEdgeTracker::updateMovingEdge(const vpImage<unsigned char> &I) } } - vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; cy->updateMovingEdge(I, cMo) ; - if(cy->nbFeaturel1 == 0 || cy->nbFeaturel2 == 0){ + if((cy->nbFeaturel1 == 0 || cy->nbFeaturel2 == 0) && cy->isVisible()){ cy->Reinit = true; } } + + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->updateMovingEdge(I, cMo) ; + if(ci->nbFeature == 0 && ci->isVisible()){ + ci->Reinit = true; + } + } } @@ -1288,27 +1635,35 @@ vpMbEdgeTracker::reinitMovingEdge(const vpImage<unsigned char> &I, const vpHomog vpMbtDistanceCylinder*cy; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; - if (cy->Reinit) + if (cy->Reinit && cy->isVisible()) cy->reinitMovingEdge(I, _cMo); } + + vpMbtDistanceCircle*ci; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + if (ci->Reinit && ci->isVisible()) + ci->reinitMovingEdge(I, _cMo); + } } /*! Check if two vpPoints are similar. - To be similar : \f$ (X_1 - X_2)^2 + (Y_1 - Y_2)^2 + (Z_1 - Z_2)^2 < threshold \f$. + To be similar : \f$ (X_1 - X_2)^2 + (Y_1 - Y_2)^2 + (Z_1 - Z_2)^2 < epsilon \f$. \param P1 : The first point to compare \param P2 : The second point to compare - \param threshold : The threshold used to decide if the points are similar or not. */ -bool samePoint(const vpPoint &P1, const vpPoint &P2, double threshold=1e-5) +bool +vpMbEdgeTracker::samePoint(const vpPoint &P1, const vpPoint &P2) { - double d = vpMath::sqr(P1.get_oX() - P2.get_oX())+ - vpMath::sqr(P1.get_oY() - P2.get_oY())+ - vpMath::sqr(P1.get_oZ() - P2.get_oZ()) ; - if (d < threshold) + double dx = fabs(P1.get_oX() - P2.get_oX()); + double dy = fabs(P1.get_oY() - P2.get_oY()); + double dz = fabs(P1.get_oZ() - P2.get_oZ()); + + if (dx <= std::numeric_limits<double>::epsilon() && dy <= std::numeric_limits<double>::epsilon() && dz <= std::numeric_limits<double>::epsilon()) return true ; else return false ; @@ -1316,20 +1671,19 @@ bool samePoint(const vpPoint &P1, const vpPoint &P2, double threshold=1e-5) /*! - Add a line belonging to the \f$ index \f$ th polygone to the list of lines. It is defined by its two extremities. + Add a line belonging to the \f$ index \f$ the polygon to the list of lines. It is defined by its two extremities. If the line already exists, the ploygone's index is added to the list of polygon to which it belongs. \param P1 : The first extremity of the line. \param P2 : The second extremity of the line. - \param polygone : The index of the polygon to which the line belongs. + \param polygon : The index of the polygon to which the line belongs. \param name : the optional name of the line */ void -vpMbEdgeTracker::addLine(vpPoint &P1, vpPoint &P2, int polygone, std::string name) +vpMbEdgeTracker::addLine(vpPoint &P1, vpPoint &P2, int polygon, std::string name) { //suppress line already in the model - bool already_here = false ; vpMbtDistanceLine *l ; @@ -1341,7 +1695,7 @@ vpMbEdgeTracker::addLine(vpPoint &P1, vpPoint &P2, int polygone, std::string nam if((samePoint(*(l->p1),P1) && samePoint(*(l->p2),P2)) || (samePoint(*(l->p1),P2) && samePoint(*(l->p2),P1)) ){ already_here = true ; - l->Lindex_polygon.push_back(polygone); + l->Lindex_polygon.push_back(polygon); l->hiddenface = &faces ; } } @@ -1351,9 +1705,10 @@ vpMbEdgeTracker::addLine(vpPoint &P1, vpPoint &P2, int polygone, std::string nam l->setCameraParameters(cam) ; l->buildFrom(P1,P2) ; - l->Lindex_polygon.push_back(polygone); + l->Lindex_polygon.push_back(polygon); l->setMovingEdge(&me) ; l->hiddenface = &faces ; + l->setIndex(nline) ; l->setName(name); @@ -1397,8 +1752,60 @@ vpMbEdgeTracker::removeLine(const std::string& name) } } +/*! + Add a circle to the list of circles. + + \param P1 : Center of the circle. + \param P2,P3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param r : Radius of the circle. + \param idFace : Id of the face that is associated to the circle to handle visibility test. + \param name : the optional name of the circle. +*/ +void +vpMbEdgeTracker::addCircle(const vpPoint &P1, const vpPoint &P2, const vpPoint &P3, const double r, int idFace, const std::string& name) +{ + bool already_here = false ; + vpMbtDistanceCircle *ci ; + + for (unsigned int i = 0; i < scales.size(); i += 1){ + if(scales[i]){ + downScale(i); + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + if((samePoint(*(ci->p1),P1) && samePoint(*(ci->p2),P2) && samePoint(*(ci->p3),P3)) || + (samePoint(*(ci->p1),P1) && samePoint(*(ci->p2),P3) && samePoint(*(ci->p3),P2)) ){ + already_here = (std::fabs(ci->radius - r) < std::numeric_limits<double>::epsilon() * vpMath::maximum(ci->radius, r)); + } + } + + if (!already_here){ + ci = new vpMbtDistanceCircle ; + + ci->setCameraParameters(cam); + ci->buildFrom(P1, P2, P3, r); + ci->setMovingEdge(&me); + ci->setIndex(ncircle); + ci->setName(name); + ci->index_polygon = idFace; + ci->hiddenface = &faces ; + +// if(clippingFlag != vpMbtPolygon::NO_CLIPPING) +// ci->getPolygon().setClipping(clippingFlag); + +// if((clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) +// ci->getPolygon().setNearClippingDistance(distNearClip); +// if((clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) +// ci->getPolygon().setFarClippingDistance(distFarClip); + ncircle +=1; + circles[i].push_back(ci); + } + upScale(i); + } + } +} /*! Add a cylinder to the list of cylinders. @@ -1406,10 +1813,11 @@ vpMbEdgeTracker::removeLine(const std::string& name) \param P1 : The first extremity of the axis. \param P2 : The second extremity of the axis. \param r : The radius of the cylinder. + \param idFace : The index of the face. \param name : the optional name of the cylinder */ void -vpMbEdgeTracker::addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, const std::string& name) +vpMbEdgeTracker::addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, int idFace, const std::string& name) { bool already_here = false ; vpMbtDistanceCylinder *cy ; @@ -1433,6 +1841,8 @@ vpMbEdgeTracker::addCylinder(const vpPoint &P1, const vpPoint &P2, const double cy->setMovingEdge(&me); cy->setIndex(ncylinder); cy->setName(name); + cy->index_polygon = idFace; + cy->hiddenface = &faces ; ncylinder +=1; cylinders[i].push_back(cy); } @@ -1441,7 +1851,6 @@ vpMbEdgeTracker::addCylinder(const vpPoint &P1, const vpPoint &P2, const double } } - /*! Remove a cylinder by its name. @@ -1465,6 +1874,29 @@ vpMbEdgeTracker::removeCylinder(const std::string& name) } } +/*! + Remove a circle by its name. + + \param name : The name of the circle to remove. +*/ +void +vpMbEdgeTracker::removeCircle(const std::string& name) +{ + vpMbtDistanceCircle *ci; + + for(unsigned int i=0; i<scales.size(); i++){ + if(scales[i]){ + for(std::list<vpMbtDistanceCircle*>::iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + if (name.compare(ci->getName()) == 0){ + circles[i].erase(it); + break; + } + } + } + } +} + /*! Add a polygon to the list of polygons. @@ -1473,17 +1905,12 @@ vpMbEdgeTracker::removeCylinder(const std::string& name) void vpMbEdgeTracker::addPolygon(vpMbtPolygon &p) { - p.setIndex(index_polygon) ; - faces.addPolygon(&p) ; - unsigned int nbpt = p.getNbPoint() ; if(nbpt > 0){ for (unsigned int i=0 ; i < nbpt-1 ; i++) - addLine(p.p[i], p.p[i+1], index_polygon) ; - addLine(p.p[nbpt-1], p.p[0], index_polygon) ; + addLine(p.p[i], p.p[i+1], p.getIndex()) ; + addLine(p.p[nbpt-1], p.p[0], p.getIndex()) ; } - - index_polygon++ ; } @@ -1545,17 +1972,18 @@ vpMbEdgeTracker::visibleFace(const vpImage<unsigned char> & _I, unsigned int n ; bool changed = false; - if(!useOgre) - n = faces.setVisible(_I, cam, _cMo, vpMath::rad(89), vpMath::rad(89), changed) ; + if(!useOgre) { + //n = faces.setVisible(_I, cam, _cMo, vpMath::rad(89), vpMath::rad(89), changed) ; + n = faces.setVisible(_I, cam, _cMo, angleAppears, angleDisappears, changed) ; + } else{ -#ifdef VISP_HAVE_OGRE +#ifdef VISP_HAVE_OGRE n = faces.setVisibleOgre(_I, cam, _cMo, angleAppears, angleDisappears, changed); #else - n = faces.setVisible(_I, cam, _cMo, vpMath::rad(89), vpMath::rad(89), changed) ; + n = faces.setVisible(_I, cam, _cMo, angleAppears, angleDisappears, changed) ; #endif } - -// cout << "visible face " << n << endl ; + if (n > nbvisiblepolygone) { //cout << "une nouvelle face est visible " << endl ; @@ -1567,76 +1995,83 @@ vpMbEdgeTracker::visibleFace(const vpImage<unsigned char> & _I, nbvisiblepolygone= n ; } - /*! - Load a 3D model contained in a file. This file is either a vrml or a cao file. - - \param file : Full name the file containing the 3D model description. - The extension of this file is either .wrl or .cao. + Add the lines to track from the polygon description. If the polygon has only + two points, it defines a single line that is always visible. If it has three or + more corners, it defines a face. In that case the visibility of the face is computed + in order to track the corresponding lines only if the face is visible. + + The id of the polygon is supposed to be set prior calling this function. - \sa vpMbTracker::loadModel() + This method is called from the loadModel() one to add a face of the object to track. + + \param polygon : The polygon describing the set of lines that has to be tracked. */ void -vpMbEdgeTracker::loadModel(const char* file) +vpMbEdgeTracker::initFaceFromCorners(vpMbtPolygon &polygon) { - std::string model(file); - vpMbTracker::loadModel(model); + unsigned int nbpt = polygon.getNbPoint() ; + if(nbpt > 0){ + for (unsigned int i=0 ; i < nbpt-1 ; i++) + addLine(polygon.p[i], polygon.p[i+1], polygon.getIndex(), polygon.getName()); + addLine(polygon.p[nbpt-1], polygon.p[0], polygon.getIndex(), polygon.getName()); + } } - /*! - Load a 3D model contained in a file. - - \param file : Full name the file containing the 3D model description. - The extension of this file is either .wrl or .cao. + Add the lines to track from the polygon description. If the polygon has only + two points, it defines a single line that is always visible. If it has three or + more corners, it defines a face. In that case the visibility of the face is computed + in order to track the corresponding lines only if the face is visible. - \sa vpMbTracker::loadModel() + The id of the polygon is supposed to be set prior calling this function. + + This method is called from the loadModel() one to add a face of the object to track. + + \param polygon : The polygon describing the set of lines that has to be tracked. */ void -vpMbEdgeTracker::loadModel(const std::string &file) +vpMbEdgeTracker::initFaceFromLines(vpMbtPolygon &polygon) { - vpMbTracker::loadModel(file); + unsigned int nbpt = polygon.getNbPoint() ; + if(nbpt > 0){ + for (unsigned int i=0 ; i < nbpt-1 ; i++) + addLine(polygon.p[i], polygon.p[i+1], polygon.getIndex(), polygon.getName()); + } } /*! - Add a face to track from its corners (in the object frame). This method is - called from the loadModel() one to add a face of the object to track. - The initialization of the face depends on the primitive to track. - - \param _corners : The vector of corners representing the face. - \param _indexFace : The index of the face. + Add a circle to track from its center, 3 points (including the center) defining the plane that contain + the circle and its radius. + + \param p1 : Center of the circle. + \param p2,p3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param radius : Radius of the circle. + \param idFace : Index of the face associated to the circle to handle visibility test. + \param name : The optional name of the circle. */ -void -vpMbEdgeTracker::initFaceFromCorners(const std::vector<vpPoint>& _corners, const unsigned int _indexFace) +void +vpMbEdgeTracker::initCircle(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int idFace, const std::string &name) { - vpMbtPolygon *polygon = NULL; - polygon = new vpMbtPolygon; - polygon->setNbPoint((unsigned int)_corners.size()); - polygon->setIndex((int)_indexFace); - for(unsigned int j = 0; j < _corners.size(); j++) { - polygon->addPoint(j, _corners[j]); - } - addPolygon(*polygon); - - delete polygon; - polygon = NULL; + addCircle(p1, p2, p3, radius, (int)idFace, name); } /*! - Add a cylinder to track from tow points on the axis (defining the length of + Add a cylinder to track from two points on the axis (defining the length of the cylinder) and its radius. - \param _p1 : First point on the axis. - \param _p2 : Second point on the axis. - \param _radius : Radius of the cylinder. - \param _indexCylinder : Index of the cylinder. + \param p1 : First point on the axis. + \param p2 : Second point on the axis. + \param radius : Radius of the cylinder. + \param idFace : Id of the face that is associated to the cylinder to handle visibility test. + \param name : The optional name of the cylinder. */ void -vpMbEdgeTracker::initCylinder(const vpPoint& _p1, const vpPoint _p2, const double _radius, const unsigned int _indexCylinder) +vpMbEdgeTracker::initCylinder(const vpPoint& p1, const vpPoint &p2, const double radius, const int idFace, + const std::string &name) { - if(_indexCylinder != 0){ - ncylinder = _indexCylinder; - } - addCylinder(_p1, _p2, _radius); + addCylinder(p1, p2, radius, (int)idFace, name); } /*! @@ -1650,6 +2085,7 @@ vpMbEdgeTracker::resetTracker() this->cMo.setIdentity(); vpMbtDistanceLine *l; vpMbtDistanceCylinder *cy; + vpMbtDistanceCircle *ci; for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ @@ -1664,14 +2100,24 @@ vpMbEdgeTracker::resetTracker() if (cy!=NULL) delete cy; cy = NULL; } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + if (ci!=NULL) delete ci; + ci = NULL; + } lines[i].clear(); cylinders[i].clear(); + circles[i].clear(); } } - + faces.reset(); + +#ifdef VISP_HAVE_OGRE + useOgre = false; +#endif - index_polygon =0; compute_interaction=1; nline = 0; ncylinder = 0; @@ -1679,29 +2125,85 @@ vpMbEdgeTracker::resetTracker() nbvisiblepolygone = 0; percentageGdPt = 0.4; - angleAppears = vpMath::rad(95); - angleDisappears = vpMath::rad(95); + angleAppears = vpMath::rad(89); + angleDisappears = vpMath::rad(89); clippingFlag = vpMbtPolygon::NO_CLIPPING; - + // reinitialization of the scales. this->setScales(scales); } +/*! + Re-initialize the model used by the tracker. + \param I : The image containing the object to initialize. + \param cad_name : Path to the file containing the 3D model description. + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbEdgeTracker::reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) +{ + reInitModel(I, cad_name.c_str(), cMo_, verbose); +} /*! Re-initialize the model used by the tracker. \param I : The image containing the object to initialize. \param cad_name : Path to the file containing the 3D model description. - \param cMo : The new vpHomogeneousMatrix between the camera and the new model + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. */ void -vpMbEdgeTracker::reInitModel(const vpImage<unsigned char>& I, const char* cad_name, const vpHomogeneousMatrix& cMo) +vpMbEdgeTracker::reInitModel(const vpImage<unsigned char>& I, const char* cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) { - resetTracker(); - loadModel(cad_name); - initFromPose(I, cMo); + this->cMo.setIdentity(); + vpMbtDistanceLine *l; + vpMbtDistanceCylinder *cy; + vpMbtDistanceCircle *ci; + + for (unsigned int i = 0; i < scales.size(); i += 1){ + if(scales[i]){ + for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[i].begin(); it!=lines[i].end(); ++it){ + l = *it; + if (l!=NULL) delete l ; + l = NULL ; + } + + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[i].begin(); it!=cylinders[i].end(); ++it){ + cy = *it; + if (cy!=NULL) delete cy; + cy = NULL; + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + ci = *it; + if (ci!=NULL) delete ci; + ci = NULL; + } + + lines[i].clear(); + cylinders[i].clear(); + circles[i].clear(); + } + } + + faces.reset(); + + //compute_interaction=1; + nline = 0; + ncylinder = 0; + ncircle = 0; + //lambda = 1; + nbvisiblepolygone = 0; + + loadModel(cad_name, verbose); + initFromPose(I, cMo_); } /*! @@ -1718,7 +2220,7 @@ unsigned int vpMbEdgeTracker::getNbPoints(const unsigned int level) const { if((level > scales.size()) || !scales[level]){ - throw vpException(vpException::dimensionError, "Level is not used"); + throw vpException(vpException::dimensionError, "Cannot get the number of points for level %d: level is not used", level); } unsigned int nbGoodPoints = 0; @@ -1727,8 +2229,8 @@ vpMbEdgeTracker::getNbPoints(const unsigned int level) const l = *it; if (l->isVisible() && l->meline != NULL) { - for(std::list<vpMeSite>::const_iterator it=l->meline->getMeList().begin(); it!=l->meline->getMeList().end(); ++it){ - if (it->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; + for(std::list<vpMeSite>::const_iterator itme=l->meline->getMeList().begin(); itme!=l->meline->getMeList().end(); ++itme){ + if (itme->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; } } } @@ -1736,49 +2238,29 @@ vpMbEdgeTracker::getNbPoints(const unsigned int level) const vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[level].begin(); it!=cylinders[level].end(); ++it){ cy = *it; - if (cy->meline1 != NULL || cy->meline2 != NULL) + if (cy->isVisible() && (cy->meline1 != NULL || cy->meline2 != NULL)) { - for(std::list<vpMeSite>::const_iterator it=cy->meline1->getMeList().begin(); it!=cy->meline1->getMeList().end(); ++it){ - if (it->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; + for(std::list<vpMeSite>::const_iterator itme1=cy->meline1->getMeList().begin(); itme1!=cy->meline1->getMeList().end(); ++itme1){ + if (itme1->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; } - for(std::list<vpMeSite>::const_iterator it=cy->meline2->getMeList().begin(); it!=cy->meline2->getMeList().end(); ++it){ - if (it->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; + for(std::list<vpMeSite>::const_iterator itme2=cy->meline2->getMeList().begin(); itme2!=cy->meline2->getMeList().end(); ++itme2){ + if (itme2->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; } } } - return nbGoodPoints; -} - - -/*! - Return the polygon (face) "index". - - \exception vpException::dimensionError if index does not represent a good - polygon. - - \param index : Index of the polygon to return. - \return Pointer to the polygon index. -*/ -vpMbtPolygon* -vpMbEdgeTracker::getPolygon(const unsigned int index) -{ - if(index >= static_cast<unsigned int>(faces.size()) ){ - throw vpException(vpException::dimensionError, "index out of range"); + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[level].begin(); it!=circles[level].end(); ++it){ + ci = *it; + if (ci->isVisible() && ci->meEllipse != NULL) + { + for(std::list<vpMeSite>::const_iterator itme=ci->meEllipse->getMeList().begin(); itme!=ci->meEllipse->getMeList().end(); ++itme){ + if (itme->getState() == vpMeSite::NO_SUPPRESSION) nbGoodPoints++; + } + } } - - return faces[index]; -} -/*! - Get the number of polygon (face) representing the object to track. - - \return Number of polygon. -*/ -unsigned int -vpMbEdgeTracker::getNbPolygon() const -{ - return static_cast<unsigned int>(faces.size()); + return nbGoodPoints; } /*! @@ -1799,18 +2281,18 @@ vpMbEdgeTracker::getNbPolygon() const \warning At least one level must be activated. - \param scales : The vector describing the levels to use. + \param scale : The vector describing the levels to use. */ void -vpMbEdgeTracker::setScales(const std::vector<bool>& scales) +vpMbEdgeTracker::setScales(const std::vector<bool>& scale) { unsigned int nbActivatedLevels = 0; - for (unsigned int i = 0; i < scales.size(); i += 1){ - if(scales[i]){ + for (unsigned int i = 0; i < scale.size(); i += 1){ + if(scale[i]){ nbActivatedLevels++; } } - if((scales.size() < 1) || (nbActivatedLevels == 0)){ + if((scale.size() < 1) || (nbActivatedLevels == 0)){ vpERROR_TRACE(" !! WARNING : must use at least one level for the tracking. Use the global one"); this->scales.resize(0); this->scales.push_back(true); @@ -1820,9 +2302,9 @@ vpMbEdgeTracker::setScales(const std::vector<bool>& scales) cylinders[0].clear(); } else{ - this->scales = scales; - lines.resize(scales.size()); - cylinders.resize(scales.size()); + this->scales = scale; + lines.resize(scale.size()); + cylinders.resize(scale.size()); for (unsigned int i = 0; i < lines.size(); i += 1){ lines[i].clear(); cylinders[i].clear(); @@ -1843,8 +2325,7 @@ vpMbEdgeTracker::setFarClippingDistance(const double &dist) else if ( dist < 0 ) vpTRACE("Far clipping value cannot be inferior than 0. Far clipping won't be considered."); else{ - distFarClip = dist; - clippingFlag = (clippingFlag | vpMbtPolygon::FAR_CLIPPING); + vpMbTracker::setFarClippingDistance(dist); vpMbtDistanceLine *l; for (unsigned int i = 0; i < scales.size(); i += 1){ @@ -1871,8 +2352,7 @@ vpMbEdgeTracker::setNearClippingDistance(const double &dist) else if ( dist < 0 ) vpTRACE("Near clipping value cannot be inferior than 0. Near clipping won't be considered."); else{ - distNearClip = dist; - clippingFlag = (clippingFlag | vpMbtPolygon::NEAR_CLIPPING); + vpMbTracker::setNearClippingDistance(dist); vpMbtDistanceLine *l; for (unsigned int i = 0; i < scales.size(); i += 1){ @@ -1896,7 +2376,7 @@ vpMbEdgeTracker::setNearClippingDistance(const double &dist) void vpMbEdgeTracker::setClipping(const unsigned int &flags) { - clippingFlag = flags; + vpMbTracker::setClipping(flags); vpMbtDistanceLine *l; @@ -1940,7 +2420,7 @@ vpMbEdgeTracker::initPyramid(const vpImage<unsigned char>& _I, std::vector< cons if(scales[i]){ unsigned int cScale = static_cast<unsigned int>(pow(2., (int)i)); vpImage<unsigned char>* I = new vpImage<unsigned char>(_I.getHeight() / cScale, _I.getWidth() / cScale); -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408)) IplImage* vpI0 = cvCreateImageHeader(cvSize((int)_I.getWidth(), (int)_I.getHeight()), IPL_DEPTH_8U, 1); vpI0->imageData = (char*)(_I.bitmap); IplImage* vpI = cvCreateImage(cvSize((int)(_I.getWidth() / cScale), (int)(_I.getHeight() / cScale)), IPL_DEPTH_8U, 1); @@ -2033,6 +2513,29 @@ vpMbEdgeTracker::getLcylinder(std::list<vpMbtDistanceCylinder *>& cylindersList, } +/*! + Get the list of the circles tracked for the specified level. Each circle + contains the list of the vpMeSite. + + \throw vpException::dimensionError if the second parameter does not correspond + to an used level. + + \param level : Level corresponding to the list to return. + \param circlesList : The list of the circles of the model. +*/ +void +vpMbEdgeTracker::getLcircle(std::list<vpMbtDistanceCircle *>& circlesList, const unsigned int level) +{ + if(level > scales.size() || !scales[level]){ + std::ostringstream oss; + oss << level; + std::string errorMsg = "level " + oss.str() + " is not used, cannot get its distance lines."; + throw vpException(vpException::dimensionError, errorMsg); + } + + circlesList = circles[level]; +} + /*! Modify the camera parameters to have them corresponding to the current scale. The new parameters are divided by \f$ 2^{\_scale} \f$. @@ -2074,6 +2577,7 @@ vpMbEdgeTracker::upScale(const unsigned int _scale) K[0][2] *= ratio; K[1][2] *= ratio; + cam.initFromCalibrationMatrix(K); } @@ -2096,13 +2600,18 @@ vpMbEdgeTracker::reInitLevel(const unsigned int _lvl) l->reinitMovingEdge(*Ipyramid[_lvl], cMo); } - vpMbtDistanceCylinder *cy; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ cy = *it; cy->reinitMovingEdge(*Ipyramid[_lvl], cMo); } + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->reinitMovingEdge(*Ipyramid[_lvl], cMo); + } + trackMovingEdge(*Ipyramid[_lvl]); updateMovingEdge(*Ipyramid[_lvl]); scaleLevel = scaleLevel_1; diff --git a/src/tracking/mbt/edge/vpMbEdgeTracker.h b/src/tracking/mbt/edge/vpMbEdgeTracker.h index ca5c29da2b8cb1ed4c3ddd79c272b31f14661ed1..e497e6e4b8012b2249d2835b1a7804ad7b91871d 100644 --- a/src/tracking/mbt/edge/vpMbEdgeTracker.h +++ b/src/tracking/mbt/edge/vpMbEdgeTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbEdgeTracker.h 4338 2013-07-23 14:29:30Z fspindle $ + * $Id: vpMbEdgeTracker.h 5217 2015-01-28 09:40:02Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,6 +54,7 @@ #include <visp/vpMe.h> #include <visp/vpMbtMeLine.h> #include <visp/vpMbtDistanceLine.h> +#include <visp/vpMbtDistanceCircle.h> #include <visp/vpMbtDistanceCylinder.h> #include <visp/vpXmlParser.h> @@ -88,10 +89,6 @@ # endif #endif -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS -# include <visp/vpList.h> -#endif - /*! \class vpMbEdgeTracker \ingroup ModelBasedTracking @@ -277,24 +274,25 @@ class VISP_EXPORT vpMbEdgeTracker: virtual public vpMbTracker double lambda; //! The moving edges parameters. - vpMe me; + vpMe me; //! Vector of list of all the lines tracked (each line is linked to a list of moving edges). Each element of the vector is for a scale (element 0 = level 0 = no subsampling). std::vector< std::list< vpMbtDistanceLine*> > lines; + + //! Vector of the tracked circles. + std::vector< std::list< vpMbtDistanceCircle*> > circles; + //! Vector of the tracked cylinders. std::vector< std::list< vpMbtDistanceCylinder*> > cylinders; //! Index of the polygon to add, and total number of polygon extracted so far. unsigned int nline; - //! Index of the cylinder to add, and total number of polygon extracted so far. + //! Index of the circle to add, and total number of circles extracted so far. + unsigned int ncircle; + + //! Index of the cylinder to add, and total number of cylinders extracted so far. unsigned int ncylinder; - //! Index of the polygon to add, and total number of polygon extracted so far. Cannot be unsigned because the default index of a polygon is -1. - int index_polygon; - - //! Set of faces describing the object. - vpMbHiddenFaces<vpMbtPolygon> faces; - //! Number of polygon (face) currently visible. unsigned int nbvisiblepolygone; @@ -309,25 +307,7 @@ class VISP_EXPORT vpMbEdgeTracker: virtual public vpMbTracker //! Current scale level used. This attribute must not be modified outside of the downScale() and upScale() methods, as it used to specify to some methods which set of distanceLine use. unsigned int scaleLevel; - - //! Use Ogre3d for visibility tests - bool useOgre; - - //! Angle used to detect a face appearance - double angleAppears; - - //! Angle used to detect a face disappearance - double angleDisappears; - - //! Distance for near clipping - double distNearClip; - - //! Distance for near clipping - double distFarClip; - - //! Flags specifying which clipping to used - unsigned int clippingFlag; - + public: vpMbEdgeTracker(); @@ -337,34 +317,7 @@ public: const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false); void display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false); - - /*! Return the angle used to test polygons appearance. */ - virtual inline double getAngleAppear() const { return angleAppears; } - - /*! Return the angle used to test polygons disappearance. */ - virtual inline double getAngleDisappear() const { return angleDisappears; } - - /*! - Get the clipping used. - - \sa vpMbtPolygonClipping - - \return Clipping flags. - */ - virtual inline unsigned int getClipping() const { return clippingFlag; } - - /*! Return a reference to the faces structure. */ - inline vpMbHiddenFaces<vpMbtPolygon>& getFaces() { return faces; } - - /*! - Get the far distance for clipping. - - \return Far clipping value. - */ - virtual inline double getFarClippingDistance() const { return distFarClip; } - - inline double getFirstThreshold() const { return percentageGdPt;} - + /*! Get the value of the gain used to compute the control law. @@ -373,25 +326,23 @@ public: virtual inline double getLambda() const {return lambda;} void getLline(std::list<vpMbtDistanceLine *>& linesList, const unsigned int level = 0); + void getLcircle(std::list<vpMbtDistanceCircle *>& circlesList, const unsigned int level = 0); void getLcylinder(std::list<vpMbtDistanceCylinder *>& cylindersList, const unsigned int level = 0); - + /*! Get the moving edge parameters. - + \return an instance of the moving edge parameters used by the tracker. */ - inline void getMovingEdge(vpMe &me ) const { me = this->me;} - + inline void getMovingEdge(vpMe &p_me ) const { p_me = this->me;} /*! - Get the near distance for clipping. - - \return Near clipping value. + Get the moving edge parameters. + + \return an instance of the moving edge parameters used by the tracker. */ - virtual inline double getNearClippingDistance() const { return distNearClip; } - + inline vpMe getMovingEdge() const { return this->me;} + unsigned int getNbPoints(const unsigned int level=0) const; - unsigned int getNbPolygon() const ; - vpMbtPolygon* getPolygon(const unsigned int index); /*! Return the scales levels used for the tracking. @@ -399,50 +350,31 @@ public: \return The scales levels used for the tracking. */ std::vector<bool> getScales() const {return scales;} - - void loadConfigFile(const std::string &configFile); - void loadConfigFile(const char* configFile); - void loadModel(const std::string &cad_name); - void loadModel(const char* cad_name); - - void reInitModel(const vpImage<unsigned char>& I, const char* cad_name, const vpHomogeneousMatrix& cMo); - void resetTracker(); - - /*! - Set the angle used to test polygons appearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value lower than - this parameter, the polygon is considered as appearing. - The polygon will then be tracked. - - \warning This angle will only be used when setOgreVisibilityTest(true) - is called. - - \param a : new angle in radian. - */ - virtual inline void setAngleAppear(const double &a) { angleAppears = a; } - /*! - Set the angle used to test polygons disappearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value greater than - this parameter, the polygon is considered as disappearing. - The tracking of the polygon will then be stopped. + \return The threshold value between 0 and 1 over good moving edges ratio. It allows to + decide if the tracker has enough valid moving edges to compute a pose. 1 means that all + moving edges should be considered as good to have a valid pose, while 0.1 means that + 10% of the moving edge are enough to declare a pose valid. - \warning This angle will only be used when setOgreVisibilityTest(true) - is called. + \sa setGoodMovingEdgesRatioThreshold() + */ + inline double getGoodMovingEdgesRatioThreshold() const { return percentageGdPt;} - \param a : new angle in radian. - */ - virtual inline void setAngleDisappear(const double &a) { angleDisappears = a; } + void loadConfigFile(const std::string &configFile); + void loadConfigFile(const char* configFile); + void reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, const vpHomogeneousMatrix& cMo_, + const bool verbose=false); + void reInitModel(const vpImage<unsigned char>& I, const char* cad_name, const vpHomogeneousMatrix& cMo, + const bool verbose=false); + void resetTracker(); /*! Set the camera parameters. - \param cam : the new camera parameters + \param camera : the new camera parameters */ - virtual void setCameraParameters(const vpCameraParameters& cam) { - this->cam = cam; + virtual void setCameraParameters(const vpCameraParameters& camera) { + this->cam = camera; for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ @@ -453,50 +385,57 @@ public: for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[i].begin(); it!=cylinders[i].end(); ++it){ (*it)->setCameraParameters(cam); } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[i].begin(); it!=circles[i].end(); ++it){ + (*it)->setCameraParameters(cam); + } } } } - - virtual void setClipping(const unsigned int &flags); - - /*! - Enable to display the points along the line with a color corresponding to their state. - - - If green : The vpMeSite is a good point. - - If blue : The point is removed because of the vpMeSite tracking phase (contrast problem). - - If purple : The point is removed because of the vpMeSite tracking phase (threshold problem). - - If red : The point is removed because of the robust method in the virtual visual servoing. - - \param displayMe : set it to true to display the points. - */ - void setDisplayMovingEdges(const bool displayMe) {displayFeatures = displayMe;} - + + virtual void setClipping(const unsigned int &flags); + virtual void setFarClippingDistance(const double &dist); - + + virtual void setNearClippingDistance(const double &dist); + /*! - Set the first threshold used to check if the tracking failed. It corresponds to the percentage of good point which is necessary. - - The condition which has to be be satisfied is the following : \f$ nbGoodPoint > threshold1 \times (nbGoodPoint + nbBadPoint)\f$. - - The threshold is ideally between 0 and 1. - - \param threshold1 : The new value of the threshold. + Use Ogre3D for visibility tests + + \warning This function has to be called before the initialization of the tracker. + + \param v : True to use it, False otherwise */ - void setFirstThreshold(const double threshold1) {percentageGdPt = threshold1;} - + virtual void setOgreVisibilityTest(const bool &v){ + vpMbTracker::setOgreVisibilityTest(v); +#ifdef VISP_HAVE_OGRE + faces.getOgreContext()->setWindowName("MBT Edge"); +#endif + } + + /*! + Set the threshold value between 0 and 1 over good moving edges ratio. It allows to + decide if the tracker has enough valid moving edges to compute a pose. 1 means that all + moving edges should be considered as good to have a valid pose, while 0.1 means that + 10% of the moving edge are enough to declare a pose valid. + + \param threshold : Value between 0 and 1 that corresponds to the ratio of good + moving edges that is necessary to consider that the estimated pose is valid. + Default value is 0.4. + + \sa getGoodMovingEdgesRatioThreshold() + */ + void setGoodMovingEdgesRatioThreshold(const double threshold) {percentageGdPt = threshold;} + /*! Set the value of the gain used to compute the control law. - \param lambda : the desired value for the gain. + \param gain : the desired value for the gain. */ - virtual inline void setLambda(const double lambda) {this->lambda = lambda;} + virtual inline void setLambda(const double gain) {this->lambda = gain;} void setMovingEdge(const vpMe &me); - - virtual void setNearClippingDistance(const double &dist); - - virtual void setOgreVisibilityTest(const bool &v); - + virtual void setPose(const vpImage<unsigned char> &I, const vpHomogeneousMatrix& cdMo); void setScales(const std::vector<bool>& _scales); @@ -504,19 +443,26 @@ public: void track(const vpImage<unsigned char> &I); protected: - void addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, const std::string& name = ""); - void addLine(vpPoint &p1, vpPoint &p2, int polygone = -1, std::string name = ""); + bool samePoint(const vpPoint &P1, const vpPoint &P2); + void addCircle(const vpPoint &P1, const vpPoint &P2, const vpPoint &P3, const double r, int idFace = -1, const std::string& name = ""); + void addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, int idFace = -1, const std::string& name = ""); + void addLine(vpPoint &p1, vpPoint &p2, int polygon = -1, std::string name = ""); void addPolygon(vpMbtPolygon &p) ; void cleanPyramid(std::vector<const vpImage<unsigned char>* >& _pyramid); void computeVVS(const vpImage<unsigned char>& _I); void downScale(const unsigned int _scale); void init(const vpImage<unsigned char>& I); - virtual void initCylinder(const vpPoint& _p1, const vpPoint _p2, const double _radius, const unsigned int _indexCylinder=0); - virtual void initFaceFromCorners(const std::vector<vpPoint>& _corners, const unsigned int _indexFace = -1); + virtual void initCircle(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int idFace=0, const std::string &name=""); + virtual void initCylinder(const vpPoint& p1, const vpPoint &p2, const double radius, const int idFace=0, + const std::string &name=""); + virtual void initFaceFromCorners(vpMbtPolygon &polygon); + virtual void initFaceFromLines(vpMbtPolygon &polygon); void initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo) ; void initPyramid(const vpImage<unsigned char>& _I, std::vector<const vpImage<unsigned char>* >& _pyramid); void reInitLevel(const unsigned int _lvl); void reinitMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &_cMo); + void removeCircle(const std::string& name); void removeCylinder(const std::string& name); void removeLine(const std::string& name); void testTracking(); @@ -530,7 +476,42 @@ protected: @name Deprecated functions */ vp_deprecated void visibleFace(const vpHomogeneousMatrix &_cMo, bool &newvisibleline); -#endif + +public: + /*! + \deprecated Since this function name is not explicit, use rather getGoodMovingEdgesRatioThreshold() + that does the same. + + \return The threshold value between 0 and 1 that allows to decide if the tracker + has enough valid moving edges to compute a pose. + */ + vp_deprecated inline double getFirstThreshold() const { return percentageGdPt;} + /*! + \deprecated Use vpMbTracker::setDisplayFeatures() instead. + Enable to display the points along the line with a color corresponding to their state. + + - If green : The vpMeSite is a good point. + - If blue : The point is removed because of the vpMeSite tracking phase (contrast problem). + - If purple : The point is removed because of the vpMeSite tracking phase (threshold problem). + - If red : The point is removed because of the robust method in the virtual visual servoing. + + \param displayMe : set it to true to display the points. + */ + vp_deprecated void setDisplayMovingEdges(const bool displayMe) {displayFeatures = displayMe;} + /*! + \deprecated Since this function name is not explicit, use rather setGoodMovingEdgesRatioThreshold() + that does the same. + Set the first threshold used to check if the tracking failed. It corresponds to the percentage + of good point which is necessary. + + The condition which has to be be satisfied is the following : \f$ nbGoodPoint > threshold1 \times (nbGoodPoint + nbBadPoint)\f$. + + The threshold is ideally between 0 and 1. + + \param threshold1 : The new value of the threshold. + */ + vp_deprecated void setFirstThreshold(const double threshold1) {percentageGdPt = threshold1;} + #endif }; #endif diff --git a/src/tracking/mbt/edge/vpMbtDistanceCircle.cpp b/src/tracking/mbt/edge/vpMbtDistanceCircle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60fd7ed9729663369310e61ee49d43c8a611799c --- /dev/null +++ b/src/tracking/mbt/edge/vpMbtDistanceCircle.cpp @@ -0,0 +1,430 @@ +/**************************************************************************** + * + * $Id: vpMbtDistanceCircle.cpp 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Make the complete tracking of an object by using its CAD model. Circle + * tracking. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +#include <visp/vpConfig.h> + +/*! + \file vpMbtDistanceCircle.cpp + \brief Make the complete tracking of an object by using its CAD model. +*/ + +#include <stdlib.h> +#include <algorithm> + +#include <visp/vpMbtDistanceCircle.h> +#include <visp/vpPlane.h> +#include <visp/vpMeterPixelConversion.h> +#include <visp/vpPixelMeterConversion.h> +#include <visp/vpFeatureBuilder.h> +#include <visp/vpFeatureEllipse.h> +#include <visp/vpPose.h> + +/*! + Basic constructor +*/ +vpMbtDistanceCircle::vpMbtDistanceCircle() + : name(), index(0), cam(), me(NULL), wmean(1), + featureEllipse(), meEllipse(NULL), + circle(NULL), radius(0.), p1(NULL), p2(NULL), p3(NULL), + L(), error(), nbFeature(0), Reinit(false), + hiddenface(NULL), index_polygon(-1), isvisible(false) +{ +} + +/*! + Basic destructor useful to deallocate the memory. +*/ +vpMbtDistanceCircle::~vpMbtDistanceCircle() +{ + if (meEllipse != NULL) delete meEllipse; + if (circle != NULL) delete circle; + if (p1 != NULL) delete p1; + if (p2 != NULL) delete p2; + if (p3 != NULL) delete p3; +} + +/*! + Project the circle into the image. + + \param cMo : The pose of the camera used to project the circle into the image. +*/ +void +vpMbtDistanceCircle::project(const vpHomogeneousMatrix &cMo) +{ + circle->project(cMo) ; +} + + +/*! + Build a vpMbtDistanceCircle thanks to its center, 3 points (including the center) with + coordinates expressed in the object frame and defining the plane that contain + the circle and its radius. + + \param _p1 : Center of the circle. + \param _p2,_p3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param r : Radius of the circle. +*/ +void +vpMbtDistanceCircle::buildFrom(const vpPoint &_p1, const vpPoint &_p2, const vpPoint &_p3, const double r) +{ + circle = new vpCircle ; + p1 = new vpPoint ; + p2 = new vpPoint ; + p3 = new vpPoint ; + + // Get the points + *p1 = _p1; + *p2 = _p2; + *p3 = _p3; + + // Get the radius + radius = r; + + vpPlane plane(*p1, *p2, *p3, vpPlane::object_frame); + + // Build our circle + circle->setWorldCoordinates(plane.getA(), plane.getB(), plane.getC(), _p1.get_oX(), _p1.get_oY(), _p1.get_oZ(), r); +} + + +/*! + Set the moving edge parameters. + + \param _me : an instance of vpMe containing all the desired parameters +*/ +void +vpMbtDistanceCircle::setMovingEdge(vpMe *_me) +{ + me = _me ; + if (meEllipse != NULL) + { + meEllipse->setMe(me) ; + } +} + +/*! + Initialize the moving edge thanks to a given pose of the camera. + The 3D model is projected into the image to create moving edges along the circle. + + \param I : The image. + \param cMo : The pose of the camera used to initialize the moving edges. + \return false if an error occur, true otherwise. +*/ +bool +vpMbtDistanceCircle::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) +{ + if(isvisible){ + // Perspective projection + circle->changeFrame(cMo); + + try{ + circle->projection(); + } + catch(...){ + std::cout<<"Problem when projecting circle\n"; + return false; + } + + // Create the moving edges containers + meEllipse = new vpMbtMeEllipse; + meEllipse->setMe(me) ; + + //meEllipse->setDisplay(vpMeSite::RANGE_RESULT) ; // TODO only for debug + meEllipse->setInitRange(me->getRange()); // TODO: check because set to zero for lines + + try + { + vpImagePoint ic; + double mu20_p, mu11_p, mu02_p; + vpMeterPixelConversion::convertEllipse(cam, *circle, ic, mu20_p, mu11_p, mu02_p); + meEllipse->initTracking(I, ic, mu20_p, mu11_p, mu02_p); + } + catch(...) + { + //vpTRACE("the circle can't be initialized"); + return false; + } + } + return true; +} + +/*! + Track the moving edges in the image. + + \param I : the image. + \param cMo : The pose of the camera. +*/ +void +vpMbtDistanceCircle::trackMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix & /*cMo*/) +{ + if(isvisible){ + try + { + meEllipse->track(I) ; + } + catch(...) + { + //std::cout << "Track meEllipse failed" << std::endl; + meEllipse->reset(); + Reinit = true; + } + + // Update the number of features + nbFeature = (unsigned int)meEllipse->getMeList().size(); + } +} + + +/*! + Update the moving edges internal parameters. + + \warning : Not implemented. + + \param I : the image. + \param cMo : The pose of the camera. +*/ +void +vpMbtDistanceCircle::updateMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) +{ + if(isvisible){ + // Perspective projection + circle->changeFrame(cMo); + + try{ + circle->projection(); + } + catch(...){std::cout<<"Problem when projecting circle\n";} + + try + { + + vpImagePoint ic; + double mu20_p, mu11_p, mu02_p; + vpMeterPixelConversion::convertEllipse(cam, *circle, ic, mu20_p, mu11_p, mu02_p); + meEllipse->updateParameters(I, ic, mu20_p, mu11_p, mu02_p); + } + catch(...) + { + Reinit = true; + } + nbFeature = (unsigned int)meEllipse->getMeList().size(); + } +} + + +/*! + Reinitialize the circle if it is required. + + A circle is reinitialized if the ellipse do not match enough with the projected 3D circle. + + \param I : the image. + \param cMo : The pose of the camera. +*/ +void +vpMbtDistanceCircle::reinitMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) +{ + if(meEllipse!= NULL) + delete meEllipse; + + meEllipse = NULL; + + if(initMovingEdge(I,cMo) == false) + Reinit = true; + + Reinit = false; +} + + +/*! + Display the circle. The 3D circle is projected into the image as an ellipse. + + \param I : The image. + \param cMo : Pose used to project the 3D model into the image. + \param camera : The camera parameters. + \param col : The desired color. + \param thickness : The thickness of the lines. + \param displayFullModel : When true, display the circle even if non visible. + If false, display the circle only if visible. +*/ +void +vpMbtDistanceCircle::display(const vpImage<unsigned char>&I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters &camera, const vpColor col, const unsigned int thickness, + const bool displayFullModel ) +{ + if(isvisible || displayFullModel){ + // Perspective projection + circle->changeFrame(cMo); + + try{ + circle->projection(); + } + catch(...){std::cout<<"Cannot project the circle";} + + vpImagePoint center; + double mu20_p, mu11_p, mu02_p; + vpMeterPixelConversion::convertEllipse(camera, *circle, center, mu20_p, mu11_p, mu02_p); + vpDisplay::displayEllipse(I, center, mu20_p, mu11_p, mu02_p, true, col, thickness); + } +} + +/*! + Display the cylinder. The 3D cylinder is projected into the image. + + \param I : The image. + \param cMo : Pose used to project the 3D model into the image. + \param camera : The camera parameters. + \param col : The desired color. + \param thickness : The thickness of the lines. + \param displayFullModel : When true, display the circle even if non visible. + If false, display the circle only if visible. +*/ +void +vpMbtDistanceCircle::display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters &camera, const vpColor col, + const unsigned int thickness, const bool displayFullModel) +{ + if(isvisible || displayFullModel){ + // Perspective projection + circle->changeFrame(cMo); + + try{ + circle->projection(); + } + catch(...){std::cout<<"Cannot project the circle";} + + vpImagePoint center; + double mu20_p, mu11_p, mu02_p; + vpMeterPixelConversion::convertEllipse(camera, *circle, center, mu20_p, mu11_p, mu02_p); + vpDisplay::displayEllipse(I, center, mu20_p, mu11_p, mu02_p, true, col, thickness); + } +} + + +/*! + Enable to display the points along the ellipse with a color corresponding to their state. + + - If green : The vpMeSite is a good point. + - If blue : The point is removed because of the vpMeSite tracking phase (constrast problem). + - If purple : The point is removed because of the vpMeSite tracking phase (threshold problem). + - If blue : The point is removed because of the robust method in the virtual visual servoing. + + \param I : The image. +*/ +void +vpMbtDistanceCircle::displayMovingEdges(const vpImage<unsigned char> &I) +{ + if (meEllipse != NULL) + { + meEllipse->display(I); // display the me + if (vpDEBUG_ENABLE(3)) + vpDisplay::flush(I); + } +} + +/*! + Initialize the size of the interaction matrix and the error vector. +*/ +void +vpMbtDistanceCircle::initInteractionMatrixError() +{ + if (isvisible == true) + { + nbFeature = (unsigned int)meEllipse->getMeList().size(); + L.resize(nbFeature, 6); + error.resize(nbFeature); + } + else + nbFeature = 0 ; +} + +/*! + Compute the interaction matrix and the error vector corresponding to the point to ellipse algebraic distance. +*/ +void +vpMbtDistanceCircle::computeInteractionMatrixError(const vpHomogeneousMatrix &cMo) +{ + if (isvisible) + { + // Perspective projection + circle->changeFrame(cMo) ; + try{ + circle->projection(); + } + catch(...){std::cout<<"Problem projection circle\n";} + + vpFeatureBuilder::create(featureEllipse, *circle); + + vpMatrix H1 = featureEllipse.interaction(); + + vpRowVector H(5); + double x=0, y=0; + + // Get the parameters of the ellipse in the image plane + double xg = circle->p[0]; + double yg = circle->p[1]; + double mu20 = circle->p[2]; + double mu11 = circle->p[3]; + double mu02 = circle->p[4]; + + unsigned int j = 0; + + for(std::list<vpMeSite>::const_iterator it=meEllipse->getMeList().begin(); it!=meEllipse->getMeList().end(); ++it){ + vpPixelMeterConversion::convertPoint(cam, it->j, it->i, x, y); + H[0] = 2*(mu11*(y-yg)+mu02*(xg-x)); + H[1] = 2*(mu20*(yg-y)+mu11*(x-xg)); + H[2] = vpMath::sqr(y-yg)-mu02; + H[3] = 2*(yg*(x-xg)+y*xg+mu11-x*y); + H[4] = vpMath::sqr(x-xg)-mu20; + + for (unsigned int k=0; k<6; k++) + L[j][k] = H[0]*H1[0][k] + H[1]*H1[1][k] + H[2]*H1[2][k] + H[3]*H1[3][k] + H[4]*H1[4][k]; + + error[j] = mu02*vpMath::sqr(x) + mu20*vpMath::sqr(y) - 2*mu11*x*y + + 2*(mu11*yg-mu02*xg)*x + 2*(mu11*xg-mu20*yg)*y + + mu02*vpMath::sqr(xg) + mu20*vpMath::sqr(yg) - 2*mu11*xg*yg + + vpMath::sqr(mu11) - mu20*mu02; + + j++; + } + } +} + diff --git a/src/tracking/mbt/edge/vpMbtDistanceCircle.h b/src/tracking/mbt/edge/vpMbtDistanceCircle.h new file mode 100644 index 0000000000000000000000000000000000000000..07aa075e9200d008d104a7324503901c2e8365e9 --- /dev/null +++ b/src/tracking/mbt/edge/vpMbtDistanceCircle.h @@ -0,0 +1,222 @@ +/**************************************************************************** + * + * $Id: vpMbtDistanceCircle.h 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Manage a circle used in the model-based tracker. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +/*! + \file vpMbtDistanceCircle.h + \brief Manage a circle used in the model-based tracker. +*/ + +#ifndef vpMbtDistanceCircle_HH +#define vpMbtDistanceCircle_HH + +#include <visp/vpPoint.h> +#include <visp/vpMbtMeEllipse.h> +#include <visp/vpHomogeneousMatrix.h> +#include <visp/vpFeatureEllipse.h> +#include <visp/vpCircle.h> +#include <visp/vpMbHiddenFaces.h> + +/*! + \class vpMbtDistanceCircle + + \brief Manage a circle used in the model-based tracker. + + \ingroup ModelBasedTracking +*/ +class VISP_EXPORT vpMbtDistanceCircle +{ + private : + std::string name; + unsigned int index; + vpCameraParameters cam; + vpMe *me; + //double alpha; + double wmean; + vpFeatureEllipse featureEllipse ; + //! Polygon describing the circle bbox +// vpMbtPolygon poly; + + public: + //! The moving edge containers + vpMbtMeEllipse *meEllipse; + + //! The circle to track + vpCircle *circle; + + //! The radius of the circle + double radius; + + //! The center of the circle + vpPoint *p1; + //! A point on the plane containing the circle + vpPoint *p2; + //! An other point on the plane containing the circle + vpPoint *p3; + + //! The interaction matrix + vpMatrix L; + //! The error vector + vpColVector error; + //! The number of moving edges + unsigned int nbFeature; + //! Indicates if the circle has to be reinitialized + bool Reinit; + //! Pointer to the list of faces + vpMbHiddenFaces<vpMbtPolygon> *hiddenface; + //! Index of the faces which contain the line + int index_polygon; + //! Indicates if the circle is visible or not + bool isvisible; + + public: + vpMbtDistanceCircle() ; + ~vpMbtDistanceCircle() ; + + void buildFrom(const vpPoint &_p1, const vpPoint &_p2, const vpPoint &_p3, const double r); + + void computeInteractionMatrixError(const vpHomogeneousMatrix &cMo); + + void display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1, const bool displayFullModel = false); + void display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1, const bool displayFullModel = false); + void displayMovingEdges(const vpImage<unsigned char> &I); + + /*! + Get the camera paramters. + + \param camera : The vpCameraParameters used to store the camera parameters. + */ + inline void getCameraParameters(vpCameraParameters& camera) {camera = this->cam;} + + /*! + Get the index of the circle. + + \return Return the index of the line. + */ + inline unsigned int getIndex() {return index ;} + + /*! + Get the mean weight of the circle. The mean weight is computed thanks to the weight of each moving edge. + Those weights are computed by the robust estimation method used during the virtual visual servoing. + + \return The mean weight of the circle. + */ + inline double getMeanWeight() const {return wmean;} + + /*! + Get the name of the circle. + + \return Return the name of the circle. + */ + inline std::string getName() const {return name;} + + /*! + Get the polygon associated to the circle. + + \return poly. + */ +// inline vpMbtPolygon& getPolygon() {return poly;} + + void initInteractionMatrixError(); + + bool initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + + /*! + Check if the circle is visible in the image or not. + + \return Return true if the circle is visible + */ + inline bool isVisible() const {return isvisible; } + + void reinitMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + + /*! + Set the camera paramters. + \param camera : The camera parameters. + */ + inline void setCameraParameters(const vpCameraParameters& camera) {this->cam = camera;} + + /*! + Set the index of the circle. + + \param i : The index number + */ + inline void setIndex(const unsigned int i) {index = i;} + + /*! + Set the mean weight of the circle. + + \param _wmean : The mean weight of the circle. + */ + inline void setMeanWeight(const double _wmean) {this->wmean = _wmean;} + + void setMovingEdge(vpMe *Me); + + /*! + Set the name of the circle. + + \param circle_name : The name of the circle. + */ + inline void setName(const std::string& circle_name) {this->name = circle_name;} + + /*! + Set the name of the circle. + + \param circle_name : The name of the circle. + */ + inline void setName(const char* circle_name) {this->name = std::string(circle_name);} + + /*! + Set a boolean parameter to indicates if the circle is visible in the image or not. + + \param _isvisible : Set to true if the circle is visible + */ + inline void setVisible(bool _isvisible) {isvisible = _isvisible ;} + + + void trackMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + + void updateMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + + private: + void project(const vpHomogeneousMatrix &cMo); +} ; + +#endif + diff --git a/src/tracking/mbt/edge/vpMbtDistanceCylinder.cpp b/src/tracking/mbt/edge/vpMbtDistanceCylinder.cpp index d7d08dcb025d30c7486bf13b78d41a61484f00f8..f664e31b773c3a512d344ae33c7037a920abb532 100755 --- a/src/tracking/mbt/edge/vpMbtDistanceCylinder.cpp +++ b/src/tracking/mbt/edge/vpMbtDistanceCylinder.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtDistanceCylinder.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpMbtDistanceCylinder.cpp 4914 2014-10-02 13:25:47Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,22 +64,12 @@ Basic constructor */ vpMbtDistanceCylinder::vpMbtDistanceCylinder() + : name(), index(0), cam(), me(NULL), alpha(0), wmean1(1), wmean2(1), + featureline1(), featureline2(), meline1(NULL), meline2(NULL), + cercle1(NULL), cercle2(NULL), radius(0), p1(NULL), p2(NULL), L(), + error(), nbFeature(0), nbFeaturel1(0), nbFeaturel2(0), Reinit(false), + c(NULL), hiddenface(NULL), index_polygon(-1), isvisible(false) { - name = ""; - p1 = NULL ; - p2 = NULL ; - c = NULL ; - meline1 = NULL ; - meline2 = NULL ; - wmean1 = 1 ; - wmean2 = 1 ; - nbFeaturel1 =0 ; - nbFeaturel2 =0 ; - nbFeature =0 ; - Reinit = false; - - cercle1 = NULL; - cercle2 = NULL; } /*! @@ -238,97 +228,109 @@ vpMbtDistanceCylinder::getCylinderLineExtremity(double &i, double &j,double rho, \param I : The image. \param cMo : The pose of the camera used to initialize the moving edges. + \return false if an error occur, true otherwise. */ -void +bool vpMbtDistanceCylinder::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) { - // Perspective projection - p1->changeFrame(cMo); - p2->changeFrame(cMo); - cercle1->changeFrame(cMo); - cercle2->changeFrame(cMo); - c->changeFrame(cMo); - - p1->projection(); - p2->projection(); - try{ - cercle1->projection(); - } - catch(...){std::cout<<"Problem when projecting circle 1\n";} - try{ - cercle2->projection(); - } - catch(...){std::cout<<"Problem when projecting circle 2\n";} - c->projection(); + if(isvisible){ + // Perspective projection + p1->changeFrame(cMo); + p2->changeFrame(cMo); + cercle1->changeFrame(cMo); + cercle2->changeFrame(cMo); + c->changeFrame(cMo); + + p1->projection(); + p2->projection(); + try{ + cercle1->projection(); + } + catch(...){ + //std::cout<<"Problem when projecting circle 1\n"; + return false; + } + try{ + cercle2->projection(); + } + catch(...){ + //std::cout<<"Problem when projecting circle 2\n"; + return false; + } + c->projection(); - double rho1,theta1; - double rho2,theta2; + double rho1,theta1; + double rho2,theta2; -// Create the moving edges containers - meline1 = new vpMbtMeLine ; - meline1->setMe(me) ; - meline2 = new vpMbtMeLine ; - meline2->setMe(me) ; + // Create the moving edges containers + meline1 = new vpMbtMeLine ; + meline1->setMe(me) ; + meline2 = new vpMbtMeLine ; + meline2->setMe(me) ; -// meline->setDisplay(vpMeSite::RANGE_RESULT) ; - meline1->setInitRange(0); - meline2->setInitRange(0); + // meline->setDisplay(vpMeSite::RANGE_RESULT) ; + meline1->setInitRange(0); + meline2->setInitRange(0); - // Conversion meter to pixels - vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); - vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); + // Conversion meter to pixels + vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); + vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); - // Determine intersections between circles and limbos - double i11,i12,i21,i22,j11,j12,j21,j22; - getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); - getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); - getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); - getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); + // Determine intersections between circles and limbos + double i11,i12,i21,i22,j11,j12,j21,j22; + getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); + getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); + getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); + getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); // Create the image points - vpImagePoint ip11,ip12,ip21,ip22; - ip11.set_ij(i11,j11); - ip12.set_ij(i12,j12); - ip21.set_ij(i21,j21); - ip22.set_ij(i22,j22); - - // update limits of the melines. - int marge = /*10*/5; //ou 5 normalement - if (ip11.get_j()<ip12.get_j()) { meline1->jmin = (int)ip11.get_j()-marge ; meline1->jmax = (int)ip12.get_j()+marge ; } else{ meline1->jmin = (int)ip12.get_j()-marge ; meline1->jmax = (int)ip11.get_j()+marge ; } - if (ip11.get_i()<ip12.get_i()) { meline1->imin = (int)ip11.get_i()-marge ; meline1->imax = (int)ip12.get_i()+marge ; } else{ meline1->imin = (int)ip12.get_i()-marge ; meline1->imax = (int)ip11.get_i()+marge ; } - - if (ip21.get_j()<ip22.get_j()) { meline2->jmin = (int)ip21.get_j()-marge ; meline2->jmax = (int)ip22.get_j()+marge ; } else{ meline2->jmin = (int)ip22.get_j()-marge ; meline2->jmax = (int)ip21.get_j()+marge ; } - if (ip21.get_i()<ip22.get_i()) { meline2->imin = (int)ip21.get_i()-marge ; meline2->imax = (int)ip22.get_i()+marge ; } else{ meline2->imin = (int)ip22.get_i()-marge ; meline2->imax = (int)ip21.get_i()+marge ; } - - // Initialize the tracking - while (theta1 > M_PI) { theta1 -= M_PI ; } - while (theta1 < -M_PI) { theta1 += M_PI ; } + vpImagePoint ip11,ip12,ip21,ip22; + ip11.set_ij(i11,j11); + ip12.set_ij(i12,j12); + ip21.set_ij(i21,j21); + ip22.set_ij(i22,j22); - if (theta1 < -M_PI/2.0) theta1 = -theta1 - 3*M_PI/2.0; - else theta1 = M_PI/2.0 - theta1; + // update limits of the melines. + int marge = /*10*/5; //ou 5 normalement + if (ip11.get_j()<ip12.get_j()) { meline1->jmin = (int)ip11.get_j()-marge ; meline1->jmax = (int)ip12.get_j()+marge ; } else{ meline1->jmin = (int)ip12.get_j()-marge ; meline1->jmax = (int)ip11.get_j()+marge ; } + if (ip11.get_i()<ip12.get_i()) { meline1->imin = (int)ip11.get_i()-marge ; meline1->imax = (int)ip12.get_i()+marge ; } else{ meline1->imin = (int)ip12.get_i()-marge ; meline1->imax = (int)ip11.get_i()+marge ; } - while (theta2 > M_PI) { theta2 -= M_PI ; } - while (theta2 < -M_PI) { theta2 += M_PI ; } + if (ip21.get_j()<ip22.get_j()) { meline2->jmin = (int)ip21.get_j()-marge ; meline2->jmax = (int)ip22.get_j()+marge ; } else{ meline2->jmin = (int)ip22.get_j()-marge ; meline2->jmax = (int)ip21.get_j()+marge ; } + if (ip21.get_i()<ip22.get_i()) { meline2->imin = (int)ip21.get_i()-marge ; meline2->imax = (int)ip22.get_i()+marge ; } else{ meline2->imin = (int)ip22.get_i()-marge ; meline2->imax = (int)ip21.get_i()+marge ; } - if (theta2 < -M_PI/2.0) theta2 = -theta2 - 3*M_PI/2.0; - else theta2 = M_PI/2.0 - theta2; + // Initialize the tracking + while (theta1 > M_PI) { theta1 -= M_PI ; } + while (theta1 < -M_PI) { theta1 += M_PI ; } - try - { - meline1->initTracking(I,ip11,ip12,rho1,theta1); - } - catch(...) - { - vpTRACE("the line can't be initialized"); - } - try - { - meline2->initTracking(I,ip21,ip22,rho2,theta2); - } - catch(...) - { - vpTRACE("the line can't be initialized"); + if (theta1 < -M_PI/2.0) theta1 = -theta1 - 3*M_PI/2.0; + else theta1 = M_PI/2.0 - theta1; + + while (theta2 > M_PI) { theta2 -= M_PI ; } + while (theta2 < -M_PI) { theta2 += M_PI ; } + + if (theta2 < -M_PI/2.0) theta2 = -theta2 - 3*M_PI/2.0; + else theta2 = M_PI/2.0 - theta2; + + try + { + meline1->initTracking(I,ip11,ip12,rho1,theta1); + } + catch(...) + { + //vpTRACE("the line can't be initialized"); + return false; + } + try + { + meline2->initTracking(I,ip21,ip22,rho2,theta2); + } + catch(...) + { + //vpTRACE("the line can't be initialized"); + return false; + } } + return true; } @@ -342,29 +344,33 @@ vpMbtDistanceCylinder::initMovingEdge(const vpImage<unsigned char> &I, const vpH void vpMbtDistanceCylinder::trackMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix & /*cMo*/) { - try - { - meline1->track(I) ; - } - catch(...) - { - std::cout << "Track meline1 failed" << std::endl; - Reinit = true; - } - try - { - meline2->track(I) ; - } - catch(...) - { - std::cout << "Track meline2 failed" << std::endl; - Reinit = true; - } + if(isvisible){ + try + { + meline1->track(I) ; + } + catch(...) + { + //std::cout << "Track meline1 failed" << std::endl; + meline1->reset(); + Reinit = true; + } + try + { + meline2->track(I) ; + } + catch(...) + { + //std::cout << "Track meline2 failed" << std::endl; + meline2->reset(); + Reinit = true; + } - // Update the number of features - nbFeaturel1 = (unsigned int)meline1->getMeList().size(); - nbFeaturel2 = (unsigned int)meline2->getMeList().size(); - nbFeature = nbFeaturel1 + nbFeaturel2; + // Update the number of features + nbFeaturel1 = (unsigned int)meline1->getMeList().size(); + nbFeaturel2 = (unsigned int)meline2->getMeList().size(); + nbFeature = nbFeaturel1 + nbFeaturel2; + } } @@ -377,93 +383,95 @@ vpMbtDistanceCylinder::trackMovingEdge(const vpImage<unsigned char> &I, const vp void vpMbtDistanceCylinder::updateMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) { - // Perspective projection - p1->changeFrame(cMo); - p2->changeFrame(cMo); - cercle1->changeFrame(cMo); - cercle2->changeFrame(cMo); - c->changeFrame(cMo); - - p1->projection(); - p2->projection(); - try{ - cercle1->projection(); - } - catch(...){std::cout<<"Probleme projection cercle 1\n";} - try{ - cercle2->projection(); - } - catch(...){std::cout<<"Probleme projection cercle 2\n";} - c->projection(); + if(isvisible){ + // Perspective projection + p1->changeFrame(cMo); + p2->changeFrame(cMo); + cercle1->changeFrame(cMo); + cercle2->changeFrame(cMo); + c->changeFrame(cMo); + + p1->projection(); + p2->projection(); + try{ + cercle1->projection(); + } + catch(...){std::cout<<"Probleme projection cercle 1\n";} + try{ + cercle2->projection(); + } + catch(...){std::cout<<"Probleme projection cercle 2\n";} + c->projection(); - // Get the limbos - double rho1,theta1; - double rho2,theta2; + // Get the limbos + double rho1,theta1; + double rho2,theta2; - // Conversion meter to pixels - vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); - vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); + // Conversion meter to pixels + vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); + vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); - // Determine intersections between circles and limbos - double i11,i12,i21,i22,j11,j12,j21,j22; + // Determine intersections between circles and limbos + double i11,i12,i21,i22,j11,j12,j21,j22; - getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); - getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); + getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); + getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); - getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); - getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); + getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); + getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); // Create the image points - vpImagePoint ip11,ip12,ip21,ip22; - ip11.set_ij(i11,j11); - ip12.set_ij(i12,j12); - ip21.set_ij(i21,j21); - ip22.set_ij(i22,j22); + vpImagePoint ip11,ip12,ip21,ip22; + ip11.set_ij(i11,j11); + ip12.set_ij(i12,j12); + ip21.set_ij(i21,j21); + ip22.set_ij(i22,j22); - // update limits of the meline. - int marge = /*10*/5; //ou 5 normalement - if (ip11.get_j()<ip12.get_j()) { meline1->jmin = (int)ip11.get_j()-marge ; meline1->jmax = (int)ip12.get_j()+marge ; } else{ meline1->jmin = (int)ip12.get_j()-marge ; meline1->jmax = (int)ip11.get_j()+marge ; } - if (ip11.get_i()<ip12.get_i()) { meline1->imin = (int)ip11.get_i()-marge ; meline1->imax = (int)ip12.get_i()+marge ; } else{ meline1->imin = (int)ip12.get_i()-marge ; meline1->imax = (int)ip11.get_i()+marge ; } + // update limits of the meline. + int marge = /*10*/5; //ou 5 normalement + if (ip11.get_j()<ip12.get_j()) { meline1->jmin = (int)ip11.get_j()-marge ; meline1->jmax = (int)ip12.get_j()+marge ; } else{ meline1->jmin = (int)ip12.get_j()-marge ; meline1->jmax = (int)ip11.get_j()+marge ; } + if (ip11.get_i()<ip12.get_i()) { meline1->imin = (int)ip11.get_i()-marge ; meline1->imax = (int)ip12.get_i()+marge ; } else{ meline1->imin = (int)ip12.get_i()-marge ; meline1->imax = (int)ip11.get_i()+marge ; } - if (ip21.get_j()<ip22.get_j()) { meline2->jmin = (int)ip21.get_j()-marge ; meline2->jmax = (int)ip22.get_j()+marge ; } else{ meline2->jmin = (int)ip22.get_j()-marge ; meline2->jmax = (int)ip21.get_j()+marge ; } - if (ip21.get_i()<ip22.get_i()) { meline2->imin = (int)ip21.get_i()-marge ; meline2->imax = (int)ip22.get_i()+marge ; } else{ meline2->imin = (int)ip22.get_i()-marge ; meline2->imax = (int)ip21.get_i()+marge ; } + if (ip21.get_j()<ip22.get_j()) { meline2->jmin = (int)ip21.get_j()-marge ; meline2->jmax = (int)ip22.get_j()+marge ; } else{ meline2->jmin = (int)ip22.get_j()-marge ; meline2->jmax = (int)ip21.get_j()+marge ; } + if (ip21.get_i()<ip22.get_i()) { meline2->imin = (int)ip21.get_i()-marge ; meline2->imax = (int)ip22.get_i()+marge ; } else{ meline2->imin = (int)ip22.get_i()-marge ; meline2->imax = (int)ip21.get_i()+marge ; } - // Initialize the tracking - while (theta1 > M_PI) { theta1 -= M_PI ; } - while (theta1 < -M_PI) { theta1 += M_PI ; } + // Initialize the tracking + while (theta1 > M_PI) { theta1 -= M_PI ; } + while (theta1 < -M_PI) { theta1 += M_PI ; } - if (theta1 < -M_PI/2.0) theta1 = -theta1 - 3*M_PI/2.0; - else theta1 = M_PI/2.0 - theta1; + if (theta1 < -M_PI/2.0) theta1 = -theta1 - 3*M_PI/2.0; + else theta1 = M_PI/2.0 - theta1; - while (theta2 > M_PI) { theta2 -= M_PI ; } - while (theta2 < -M_PI) { theta2 += M_PI ; } + while (theta2 > M_PI) { theta2 -= M_PI ; } + while (theta2 < -M_PI) { theta2 += M_PI ; } - if (theta2 < -M_PI/2.0) theta2 = -theta2 - 3*M_PI/2.0; - else theta2 = M_PI/2.0 - theta2; + if (theta2 < -M_PI/2.0) theta2 = -theta2 - 3*M_PI/2.0; + else theta2 = M_PI/2.0 - theta2; - try - { - //meline1->updateParameters(I,rho1,theta1) ; - meline1->updateParameters(I,ip11,ip12,rho1,theta1) ; - } - catch(...) - { - Reinit = true; - } - try - { - //meline2->updateParameters(I,rho2,theta2) ; - meline2->updateParameters(I,ip21,ip22,rho2,theta2) ; - } - catch(...) - { - Reinit = true; - } + try + { + //meline1->updateParameters(I,rho1,theta1) ; + meline1->updateParameters(I,ip11,ip12,rho1,theta1) ; + } + catch(...) + { + Reinit = true; + } + try + { + //meline2->updateParameters(I,rho2,theta2) ; + meline2->updateParameters(I,ip21,ip22,rho2,theta2) ; + } + catch(...) + { + Reinit = true; + } - // Update the numbers of features - nbFeaturel1 = (unsigned int)meline1->getMeList().size(); - nbFeaturel2 = (unsigned int)meline2->getMeList().size(); - nbFeature = nbFeaturel1 + nbFeaturel2; + // Update the numbers of features + nbFeaturel1 = (unsigned int)meline1->getMeList().size(); + nbFeaturel2 = (unsigned int)meline2->getMeList().size(); + nbFeature = nbFeaturel1 + nbFeaturel2; + } } @@ -486,7 +494,8 @@ vpMbtDistanceCylinder::reinitMovingEdge(const vpImage<unsigned char> &I, const v meline1 = NULL; meline2 = NULL; - initMovingEdge(I,cMo); + if (initMovingEdge(I,cMo) == false) + Reinit = true; Reinit = false; } @@ -497,58 +506,63 @@ vpMbtDistanceCylinder::reinitMovingEdge(const vpImage<unsigned char> &I, const v \param I : The image. \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. + \param displayFullModel : When true, display the circle even if non visible. */ void -vpMbtDistanceCylinder::display(const vpImage<unsigned char>&I, const vpHomogeneousMatrix &cMo, const vpCameraParameters&cam, const vpColor col, const unsigned int thickness) +vpMbtDistanceCylinder::display(const vpImage<unsigned char>&I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters&camera, const vpColor col, const unsigned int thickness, + const bool displayFullModel) { - // Perspective projection - p1->changeFrame(cMo); - p2->changeFrame(cMo); - cercle1->changeFrame(cMo); - cercle2->changeFrame(cMo); - c->changeFrame(cMo); - - p1->projection(); - p2->projection(); - try{ - cercle1->projection(); - } - catch(...){std::cout<<"Problem projection circle 1";} - try{ - cercle2->projection(); - } - catch(...){std::cout<<"Problem projection circle 2";} - c->projection(); + if(isvisible || displayFullModel){ + // Perspective projection + p1->changeFrame(cMo); + p2->changeFrame(cMo); + cercle1->changeFrame(cMo); + cercle2->changeFrame(cMo); + c->changeFrame(cMo); + + p1->projection(); + p2->projection(); + try{ + cercle1->projection(); + } + catch(...){std::cout<<"Problem projection circle 1";} + try{ + cercle2->projection(); + } + catch(...){std::cout<<"Problem projection circle 2";} + c->projection(); - double rho1,theta1; - double rho2,theta2; + double rho1,theta1; + double rho2,theta2; - // Meters to pixels conversion - vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); - vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); + // Meters to pixels conversion + vpMeterPixelConversion::convertLine(camera,c->getRho1(),c->getTheta1(),rho1,theta1); + vpMeterPixelConversion::convertLine(camera,c->getRho2(),c->getTheta2(),rho2,theta2); - // Determine intersections between circles and limbos - double i11,i12,i21,i22,j11,j12,j21,j22; + // Determine intersections between circles and limbos + double i11,i12,i21,i22,j11,j12,j21,j22; - getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); - getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); + getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); + getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); - getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); - getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); + getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); + getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); // Create the image points - vpImagePoint ip11,ip12,ip21,ip22; - ip11.set_ij(i11,j11); - ip12.set_ij(i12,j12); - ip21.set_ij(i21,j21); - ip22.set_ij(i22,j22); - - // Display - vpDisplay::displayLine(I,ip11,ip12,col, thickness); - vpDisplay::displayLine(I,ip21,ip22,col, thickness); + vpImagePoint ip11,ip12,ip21,ip22; + ip11.set_ij(i11,j11); + ip12.set_ij(i12,j12); + ip21.set_ij(i21,j21); + ip22.set_ij(i22,j22); + + // Display + vpDisplay::displayLine(I,ip11,ip12,col, thickness); + vpDisplay::displayLine(I,ip21,ip22,col, thickness); + } } /*! @@ -556,58 +570,63 @@ vpMbtDistanceCylinder::display(const vpImage<unsigned char>&I, const vpHomogeneo \param I : The image. \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. + \param displayFullModel : When true, display the circle even if non visible. */ void -vpMbtDistanceCylinder::display(const vpImage<vpRGBa>&I, const vpHomogeneousMatrix &cMo, const vpCameraParameters&cam, const vpColor col, const unsigned int thickness) +vpMbtDistanceCylinder::display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters &camera, const vpColor col, const unsigned int thickness, + const bool displayFullModel) { - // Perspective projection - p1->changeFrame(cMo); - p2->changeFrame(cMo); - cercle1->changeFrame(cMo); - cercle2->changeFrame(cMo); - c->changeFrame(cMo); - - p1->projection(); - p2->projection(); - try{ - cercle1->projection(); - } - catch(...){std::cout<<"Problem projection circle 1";} - try{ - cercle2->projection(); - } - catch(...){std::cout<<"Problem projection circle 2";} - c->projection(); + if(isvisible || displayFullModel){ + // Perspective projection + p1->changeFrame(cMo); + p2->changeFrame(cMo); + cercle1->changeFrame(cMo); + cercle2->changeFrame(cMo); + c->changeFrame(cMo); + + p1->projection(); + p2->projection(); + try{ + cercle1->projection(); + } + catch(...){std::cout<<"Problem projection circle 1";} + try{ + cercle2->projection(); + } + catch(...){std::cout<<"Problem projection circle 2";} + c->projection(); - double rho1,theta1; - double rho2,theta2; + double rho1,theta1; + double rho2,theta2; - // Meters to pixels conversion - vpMeterPixelConversion::convertLine(cam,c->getRho1(),c->getTheta1(),rho1,theta1); - vpMeterPixelConversion::convertLine(cam,c->getRho2(),c->getTheta2(),rho2,theta2); + // Meters to pixels conversion + vpMeterPixelConversion::convertLine(camera,c->getRho1(),c->getTheta1(),rho1,theta1); + vpMeterPixelConversion::convertLine(camera,c->getRho2(),c->getTheta2(),rho2,theta2); - // Determine intersections between circles and limbos - double i11,i12,i21,i22,j11,j12,j21,j22; + // Determine intersections between circles and limbos + double i11,i12,i21,i22,j11,j12,j21,j22; - getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); - getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); + getCylinderLineExtremity(i11, j11, rho1, theta1, cercle1); + getCylinderLineExtremity(i12, j12, rho1, theta1, cercle2); - getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); - getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); + getCylinderLineExtremity(i21, j21, rho2, theta2, cercle1); + getCylinderLineExtremity(i22, j22, rho2, theta2, cercle2); // Create the image points - vpImagePoint ip11,ip12,ip21,ip22; - ip11.set_ij(i11,j11); - ip12.set_ij(i12,j12); - ip21.set_ij(i21,j21); - ip22.set_ij(i22,j22); - - // Display - vpDisplay::displayLine(I,ip11,ip12,col, thickness); - vpDisplay::displayLine(I,ip21,ip22,col, thickness); + vpImagePoint ip11,ip12,ip21,ip22; + ip11.set_ij(i11,j11); + ip12.set_ij(i12,j12); + ip21.set_ij(i21,j21); + ip22.set_ij(i22,j22); + + // Display + vpDisplay::displayLine(I,ip11,ip12,col, thickness); + vpDisplay::displayLine(I,ip21,ip22,col, thickness); + } } @@ -640,11 +659,18 @@ vpMbtDistanceCylinder::displayMovingEdges(const vpImage<unsigned char> &I) void vpMbtDistanceCylinder::initInteractionMatrixError() { + if (isvisible == true) { nbFeaturel1 = (unsigned int)meline1->getMeList().size(); nbFeaturel2 = (unsigned int)meline2->getMeList().size(); nbFeature = nbFeaturel1 + nbFeaturel2; L.resize(nbFeature, 6); error.resize(nbFeature); + } + else { + nbFeature = 0 ; + nbFeaturel1 = 0; + nbFeaturel2 = 0; + } } /*! @@ -653,25 +679,26 @@ vpMbtDistanceCylinder::initInteractionMatrixError() void vpMbtDistanceCylinder::computeInteractionMatrixError(const vpHomogeneousMatrix &cMo, const vpImage<unsigned char> &I) { - // Perspective projection - c->changeFrame(cMo) ; - c->projection() ; - cercle1->changeFrame(cMo) ; - cercle1->changeFrame(cMo) ; - try{ - cercle1->projection(); - } - catch(...){std::cout<<"Problem projection circle 1\n";} - try{ - cercle2->projection(); - } - catch(...){std::cout<<"Problem projection circle 2\n";} + if (isvisible) { + // Perspective projection + c->changeFrame(cMo) ; + c->projection() ; + cercle1->changeFrame(cMo) ; + cercle1->changeFrame(cMo) ; + try{ + cercle1->projection(); + } + catch(...){std::cout<<"Problem projection circle 1\n";} + try{ + cercle2->projection(); + } + catch(...){std::cout<<"Problem projection circle 2\n";} - bool disp = false; - bool disp2 = false; - if (disp || disp2) vpDisplay::flush(I); + bool disp = false; + bool disp2 = false; + if (disp || disp2) vpDisplay::flush(I); - // Build the lines + // Build the lines vpFeatureBuilder::create(featureline2,*c,vpCylinder::line2) ; vpFeatureBuilder::create(featureline1,*c,vpCylinder::line1) ; @@ -726,8 +753,8 @@ vpMbtDistanceCylinder::computeInteractionMatrixError(const vpHomogeneousMatrix & x = (double)it->j; y = (double)it->i; - x = (x-xc)*mx ; - y = (y-yc)*my ; + x = (x-xc)*mx ; + y = (y-yc)*my ; alpha2 = x*si2 - y*co2; @@ -743,5 +770,6 @@ vpMbtDistanceCylinder::computeInteractionMatrixError(const vpHomogeneousMatrix & j++; } + } } diff --git a/src/tracking/mbt/edge/vpMbtDistanceCylinder.h b/src/tracking/mbt/edge/vpMbtDistanceCylinder.h index fe9423199b725927c9090ad2521fb1507d549117..0c272b9ce5090aeeb5374df7cbe754e89ca1fefe 100755 --- a/src/tracking/mbt/edge/vpMbtDistanceCylinder.h +++ b/src/tracking/mbt/edge/vpMbtDistanceCylinder.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtDistanceCylinder.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMbtDistanceCylinder.h 4914 2014-10-02 13:25:47Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,6 +56,7 @@ #include <visp/vpFeatureLine.h> #include <visp/vpCylinder.h> #include <visp/vpCircle.h> +#include <visp/vpMbHiddenFaces.h> /*! \class vpMbtDistanceCylinder @@ -111,6 +112,13 @@ class VISP_EXPORT vpMbtDistanceCylinder //! The cylinder vpCylinder *c; + //! Pointer to the list of faces + vpMbHiddenFaces<vpMbtPolygon> *hiddenface; + //! Index of the face which contains the cylinder + int index_polygon; + //! Indicates if the cylinder is visible or not + bool isvisible; + public: vpMbtDistanceCylinder() ; ~vpMbtDistanceCylinder() ; @@ -119,16 +127,16 @@ class VISP_EXPORT vpMbtDistanceCylinder void computeInteractionMatrixError(const vpHomogeneousMatrix &cMo, const vpImage<unsigned char> &I); - void display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1); - void display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1); + void display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1, const bool displayFullModel = false); + void display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor col, const unsigned int thickness = 1, const bool displayFullModel = false); void displayMovingEdges(const vpImage<unsigned char> &I); /*! Get the camera paramters. - \param cam : The vpCameraParameters used to store the camera parameters. + \param camera : The vpCameraParameters used to store the camera parameters. */ - inline void getCameraParameters(vpCameraParameters& cam) {cam = this->cam;} + inline void getCameraParameters(vpCameraParameters& camera) {camera = this->cam;} /*! Get the index of the cylinder. @@ -162,15 +170,22 @@ class VISP_EXPORT vpMbtDistanceCylinder void initInteractionMatrixError(); - void initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + bool initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + /*! + Check if the cylinder is visible in the image or not. + + \return Return true if the cylinder is visible + */ + inline bool isVisible() const {return isvisible; } + void reinitMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); /*! Set the camera paramters. - \param cam : The camera parameters. + \param camera : The camera parameters. */ - inline void setCameraParameters(const vpCameraParameters& cam) {this->cam = cam;} + inline void setCameraParameters(const vpCameraParameters& camera) {this->cam = camera;} /*! Set the index of the cylinder. @@ -198,18 +213,24 @@ class VISP_EXPORT vpMbtDistanceCylinder /*! Set the name of the cylinder. - \param name : The name of the cylinder. + \param cyl_name : The name of the cylinder. */ - inline void setName(const std::string& name) {this->name = name;} + inline void setName(const std::string& cyl_name) {this->name = cyl_name;} /*! Set the name of the cylinder. - \param name : The name of the cylinder + \param cyl_name : The name of the cylinder */ - inline void setName(const char* name) {this->name = name;} + inline void setName(const char* cyl_name) {this->name = std::string(cyl_name);} + + /*! + Set a boolean parameter to indicates if the cylinder is visible in the image or not. + + \param _isvisible : Set to true if the cylinder is visible + */ + inline void setVisible(bool _isvisible) {isvisible = _isvisible ;} - void trackMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); void updateMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); diff --git a/src/tracking/mbt/edge/vpMbtDistanceLine.cpp b/src/tracking/mbt/edge/vpMbtDistanceLine.cpp index afa9fc6bad11721e1cca5012cfee1ba9ad3ada83..78adf27278059948a3b63baf061b7e2100389cf7 100644 --- a/src/tracking/mbt/edge/vpMbtDistanceLine.cpp +++ b/src/tracking/mbt/edge/vpMbtDistanceLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtDistanceLine.cpp 4311 2013-07-16 15:02:57Z ayol $ + * $Id: vpMbtDistanceLine.cpp 5136 2015-01-08 10:09:04Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,21 +53,18 @@ #include <visp/vpFeatureBuilder.h> #include <stdlib.h> +void buildPlane(vpPoint &P, vpPoint &Q, vpPoint &R, vpPlane &plane); +void buildLine(vpPoint &P1, vpPoint &P2, vpPoint &P3, vpPoint &P4, vpLine &L); + /*! Basic constructor */ vpMbtDistanceLine::vpMbtDistanceLine() + : name(), index(0), cam(), me(NULL), alpha(0), wmean(1), + featureline(), poly(), meline(NULL), line(NULL), p1(NULL), p2(NULL), L(), + error(), nbFeature(0), Reinit(false), hiddenface(NULL), Lindex_polygon(), + isvisible(false) { - name = ""; - p1 = NULL ; - p2 = NULL ; - line = NULL ; - meline = NULL ; - hiddenface = NULL ; - wmean = 1 ; - nbFeature =0 ; - Reinit = false; - isvisible = false; } /*! @@ -231,6 +228,7 @@ vpMbtDistanceLine::setMovingEdge(vpMe *_me) me = _me ; if (meline != NULL) { + meline->reset(); meline->setMe(me) ; } } @@ -242,8 +240,9 @@ vpMbtDistanceLine::setMovingEdge(vpMe *_me) \param I : The image. \param cMo : The pose of the camera used to initialize the moving edges. + \return false if an error occur, true otherwise. */ -void +bool vpMbtDistanceLine::initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo) { if(isvisible){ @@ -290,6 +289,7 @@ vpMbtDistanceLine::initMovingEdge(const vpImage<unsigned char> &I, const vpHomog catch(...) { //vpTRACE("the line can't be initialized"); + return false; } } else{ @@ -299,6 +299,7 @@ vpMbtDistanceLine::initMovingEdge(const vpImage<unsigned char> &I, const vpHomog } } // trackMovingEdge(I,cMo) ; + return true; } @@ -338,6 +339,7 @@ vpMbtDistanceLine::trackMovingEdge(const vpImage<unsigned char> &I, const vpHomo } catch(...) { + meline->reset(); Reinit = true; } nbFeature =(unsigned int) meline->getMeList().size(); @@ -419,7 +421,8 @@ vpMbtDistanceLine::reinitMovingEdge(const vpImage<unsigned char> &I, const vpHom if(meline!= NULL) delete meline; - initMovingEdge(I,cMo); + if (initMovingEdge(I,cMo) == false) + Reinit = true; Reinit = false; } @@ -430,20 +433,21 @@ vpMbtDistanceLine::reinitMovingEdge(const vpImage<unsigned char> &I, const vpHom \param I : The image. \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the line. \param displayFullModel : If true, the line is displayed even if it is not visible. */ void -vpMbtDistanceLine::display(const vpImage<unsigned char>&I, const vpHomogeneousMatrix &cMo, const vpCameraParameters&cam, const vpColor col, const unsigned int thickness, const bool displayFullModel) +vpMbtDistanceLine::display(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters &camera, const vpColor col, const unsigned int thickness, const bool displayFullModel) { - p1->changeFrame(cMo); - p2->changeFrame(cMo); - if(isvisible || displayFullModel){ + p1->changeFrame(cMo); + p2->changeFrame(cMo); + vpImagePoint ip1, ip2; - vpCameraParameters c = cam; + vpCameraParameters c = camera; if(poly.getClipping() > 3) // Contains at least one FOV constraint c.computeFov(I.getWidth(), I.getHeight()); @@ -458,7 +462,7 @@ vpMbtDistanceLine::display(const vpImage<unsigned char>&I, const vpHomogeneousMa ((poly.roiPointsClip[1].second & poly.roiPointsClip[0].second & vpMbtPolygon::RIGHT_CLIPPING) == 0)){ vpMeterPixelConversion::convertPoint(cam,poly.roiPointsClip[0].first.get_x(),poly.roiPointsClip[0].first.get_y(),ip1); vpMeterPixelConversion::convertPoint(cam,poly.roiPointsClip[1].first.get_x(),poly.roiPointsClip[1].first.get_y(),ip2); - + vpDisplay::displayLine(I,ip1,ip2,col, thickness); } } @@ -470,20 +474,22 @@ vpMbtDistanceLine::display(const vpImage<unsigned char>&I, const vpHomogeneousMa \param I : The image. \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the line. \param displayFullModel : If true, the line is displayed even if it is not visible. */ void -vpMbtDistanceLine::display(const vpImage<vpRGBa>&I, const vpHomogeneousMatrix &cMo, const vpCameraParameters&cam, const vpColor col, const unsigned int thickness, const bool displayFullModel) +vpMbtDistanceLine::display(const vpImage<vpRGBa> &I, const vpHomogeneousMatrix &cMo, + const vpCameraParameters &camera, const vpColor col, + const unsigned int thickness, const bool displayFullModel) { - p1->changeFrame(cMo); - p2->changeFrame(cMo); - if(isvisible || displayFullModel){ + p1->changeFrame(cMo); + p2->changeFrame(cMo); + vpImagePoint ip1, ip2; - vpCameraParameters c = cam; + vpCameraParameters c = camera; if(poly.getClipping() > 3) // Contains at least one FOV constraint c.computeFov(I.getWidth(), I.getHeight()); @@ -566,7 +572,7 @@ vpMbtDistanceLine::computeInteractionMatrixError(const vpHomogeneousMatrix &cMo) double xc = cam.get_u0() ; double yc = cam.get_v0() ; - double alpha ; + double alpha_ ; vpMatrix H ; H = featureline.interaction() ; @@ -580,14 +586,14 @@ vpMbtDistanceLine::computeInteractionMatrixError(const vpHomogeneousMatrix &cMo) x = (x-xc)*mx ; y = (y-yc)*my ; - alpha = x*si - y*co; + alpha_ = x*si - y*co; double *Lrho = H[0] ; double *Ltheta = H[1] ; // Calculate interaction matrix for a distance for (unsigned int k=0 ; k < 6 ; k++) { - L[j][k] = (Lrho[k] + alpha*Ltheta[k]); + L[j][k] = (Lrho[k] + alpha_*Ltheta[k]); } error[j] = rho - ( x*co + y*si) ; j++; diff --git a/src/tracking/mbt/edge/vpMbtDistanceLine.h b/src/tracking/mbt/edge/vpMbtDistanceLine.h index 925504b12ab14124ab82a4c2a8900e4fd1a2b82f..336e27d52ff9fce713cbbdf7688ca8a8a75f6b8e 100644 --- a/src/tracking/mbt/edge/vpMbtDistanceLine.h +++ b/src/tracking/mbt/edge/vpMbtDistanceLine.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtDistanceLine.h 4337 2013-07-23 13:57:53Z ayol $ + * $Id: vpMbtDistanceLine.h 4802 2014-07-24 09:24:43Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -119,9 +119,9 @@ class VISP_EXPORT vpMbtDistanceLine /*! Get the camera paramters. - \param cam : The vpCameraParameters used to store the camera parameters. + \param camera : The vpCameraParameters used to store the camera parameters. */ - inline void getCameraParameters(vpCameraParameters& cam) const {cam = this->cam;} + inline void getCameraParameters(vpCameraParameters& camera) const {camera = this->cam;} /*! Get the index of the line. @@ -154,7 +154,7 @@ class VISP_EXPORT vpMbtDistanceLine void initInteractionMatrixError(); - void initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); + bool initMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); /*! Check if the line is visible in the image or not. @@ -167,9 +167,9 @@ class VISP_EXPORT vpMbtDistanceLine /*! Set the camera paramters. - \param cam : The camera parameters. + \param camera : The camera parameters. */ - inline void setCameraParameters(const vpCameraParameters& cam) {this->cam = cam;} + inline void setCameraParameters(const vpCameraParameters& camera) {this->cam = camera;} /*! Set the index of the line. @@ -181,25 +181,25 @@ class VISP_EXPORT vpMbtDistanceLine /*! Set the mean weight of the line. - \param wmean : The mean weight of the line. + \param w_mean : The mean weight of the line. */ - inline void setMeanWeight(const double wmean) {this->wmean = wmean;} + inline void setMeanWeight(const double w_mean) {this->wmean = w_mean;} void setMovingEdge(vpMe *Me); /*! Set the name of the line. - \param name : The name of the line. + \param line_name : The name of the line. */ - inline void setName(const std::string name) {this->name = name;} + inline void setName(const std::string line_name) {this->name = line_name;} /*! Set the name of the line. - \param name : The name of the line. + \param line_name : The name of the line. */ - inline void setName(const char* name) {this->name = name;} + inline void setName(const char* line_name) {this->name = std::string(line_name);} /*! Set a boolean parameter to indicates if the line is visible in the image or not. @@ -213,11 +213,7 @@ class VISP_EXPORT vpMbtDistanceLine void updateMovingEdge(const vpImage<unsigned char> &I, const vpHomogeneousMatrix &cMo); private: - void belongToPolygon(int index) { Lindex_polygon.push_back(index); } void project(const vpHomogeneousMatrix &cMo); - void setFace( vpMbHiddenFaces<vpMbtPolygon> *_hiddenface) { hiddenface = _hiddenface ; } - - } ; #endif diff --git a/src/tracking/mbt/edge/vpMbtMeEllipse.cpp b/src/tracking/mbt/edge/vpMbtMeEllipse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fe646b268b1ff05f37fdbedcdcfd810cdad62cf --- /dev/null +++ b/src/tracking/mbt/edge/vpMbtMeEllipse.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** + * + * $Id: vpMbtMeEllipse.cpp 4706 2014-03-28 07:52:00Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Moving edges. + * + * Authors: + * Eric Marchand + * + *****************************************************************************/ + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include <visp/vpMbtMeEllipse.h> + +#include <visp/vpMe.h> +#include <visp/vpRobust.h> +#include <visp/vpTrackingException.h> +#include <visp/vpDebug.h> +#include <visp/vpImagePoint.h> + +#include <cmath> // std::fabs +#include <limits> // numeric_limits + + +/*! + Basic constructor that calls the constructor of the class vpMeTracker. +*/ +vpMbtMeEllipse::vpMbtMeEllipse() + : iPc(), a(0.), b(0.), e(0.), + ce(0.), se(0.), mu11(0.), mu20(0.), mu02(0.), thresholdWeight(0.), expecteddensity(0.) +{ +} + +/*! + Copy constructor. +*/ +vpMbtMeEllipse::vpMbtMeEllipse(const vpMbtMeEllipse &meellipse) + : vpMeTracker(meellipse), iPc(), a(0.), b(0.), e(0.), + ce(0.), se(0.), mu11(0.), mu20(0.), mu02(0.), thresholdWeight(0.), expecteddensity(0.) +{ + iPc = meellipse.iPc; + a = meellipse.a; + b = meellipse.b; + e = meellipse.e; + + ce = meellipse.ce; + se = meellipse.se; + + mu11 = meellipse.mu11; + mu20 = meellipse.mu20; + mu02 = meellipse.mu02; + + expecteddensity = meellipse.expecteddensity; +} + +/*! + Basic destructor. +*/ +vpMbtMeEllipse::~vpMbtMeEllipse() +{ + list.clear(); +} + +/*! + Construct a list of vpMeSite moving edges at a particular sampling + step between the two extremities. The two extremities are defined by + the points with the smallest and the biggest \f$ alpha \f$ angle. + + \param I : Image in which the ellipse appears. + + \exception vpTrackingException::initializationError : Moving edges not initialized. + +*/ +void +vpMbtMeEllipse::sample(const vpImage<unsigned char> & I) +{ + if (!me) { + vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized"); + throw(vpTrackingException(vpTrackingException::initializationError, + "Moving edges not initialized")) ; + } + + int height = (int)I.getHeight() ; + int width = (int)I.getWidth() ; + + //if (me->getSampleStep()==0) + if (std::fabs(me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) + { + std::cout << "In vpMbtMeEllipse::sample: " ; + std::cout << "function called with sample step = 0" ; + //return fatalError ; + } + + double j, i;//, j11, i11; + vpImagePoint iP11; + j = i = 0.0 ; + + // Approximation of the circumference of an ellipse: + // [Ramanujan, S., "Modular Equations and Approximations to ," + // Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372] + double t = (a-b)/(a+b); + double circumference = M_PI*(a+b)*(1 + 3*vpMath::sqr(t)/(10 + sqrt(4 - 3*vpMath::sqr(t)))); + int nb_points_to_track = (int)(circumference / me->getSampleStep()); + double incr = 2*M_PI/nb_points_to_track; + + expecteddensity = 0;//nb_points_to_track; + + // Delete old list + list.clear(); + + // sample positions + double k = 0 ; + double iP_i, iP_j; + for (int pt=0; pt < nb_points_to_track; pt++) + { + j = a *cos(k) ; // equation of an ellipse + i = b *sin(k) ; // equation of an ellipse + + iP_j = iPc.get_j() + ce *j - se *i; + iP_i = iPc.get_i() + se *j + ce *i; + + //vpColor col = vpColor::red ; + //vpDisplay::displayCross(I, vpImagePoint(iP_i, iP_j), 5, col) ; //debug only + + // If point is in the image, add to the sample list + if(!outOfImage(vpMath::round(iP_i), vpMath::round(iP_j), 0, height, width)) + { + // The tangent angle to the ellipse at a site + double theta = atan( (-mu02*iP_j + mu02*iPc.get_j() + mu11*iP_i - mu11*iPc.get_i()) + / (mu20*iP_i - mu11*iP_j + mu11*iPc.get_j() - mu20*iPc.get_i())) + - M_PI/2; + + vpMeSite pix ; + pix.init((int)iP_i, (int)iP_j, theta) ; + pix.setDisplay(selectDisplay) ; + pix.setState(vpMeSite::NO_SUPPRESSION); + + list.push_back(pix); + expecteddensity ++; + } + k += incr ; + + } + + vpMeTracker::initTracking(I) ; +} + + +/*! + + Resample the ellipse if the number of sample is less than 90% of the + expected value. + + \note The expected value is computed thanks to the difference between the smallest and the biggest \f$ \alpha \f$ angles + and the parameter which indicates the number of degrees between + two points (vpMe::sample_step). + + \param I : Image in which the ellipse appears. + + \exception vpTrackingException::initializationError : Moving edges not initialized. + +*/ +void +vpMbtMeEllipse::reSample(const vpImage<unsigned char> &I) +{ + if (!me) { + vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized"); + throw(vpTrackingException(vpTrackingException::initializationError, + "Moving edges not initialized")) ; + } + + unsigned int n = numberOfSignal() ; + if ((double)n<0.9*expecteddensity){ + sample(I) ; + vpMeTracker::track(I) ; + } +} + +/*! + Compute the oriantation angle for each vpMeSite. + + \note The \f$ \theta \f$ angle is useful during the tracking part. +*/ +void +vpMbtMeEllipse::updateTheta() +{ + vpMeSite p_me; + double theta; + for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ + p_me = *it; + vpImagePoint iP; + iP.set_i(p_me.ifloat); + iP.set_j(p_me.jfloat); + + // The tangent angle to the ellipse at a site + theta = atan( (-mu02*p_me.jfloat + mu02*iPc.get_j() + mu11*p_me.ifloat - mu11*iPc.get_i()) + / (mu20*p_me.ifloat - mu11*p_me.jfloat + mu11*iPc.get_j() - mu20*iPc.get_i())) + - M_PI/2; + + p_me.alpha = theta ; + *it = p_me; + } +} + +/*! + Suppress the vpMeSite which are no more detected as point which belongs to the ellipse edge. +*/ +void +vpMbtMeEllipse::suppressPoints() +{ + // Loop through list of sites to track + for(std::list<vpMeSite>::iterator itList=list.begin(); itList!=list.end();){ + vpMeSite s = *itList;//current reference pixel + if (s.getState() != vpMeSite::NO_SUPPRESSION) + itList = list.erase(itList); + else + ++itList; + } +} + +/*! + Display the ellipse. + + \warning To effectively display the ellipse a call to + vpDisplay::flush() is needed. + + \param I : Image in which the ellipse appears. + \param col : Color of the displayed ellipse. + */ +void +vpMbtMeEllipse::display(const vpImage<unsigned char> &I, vpColor col) +{ + vpDisplay::displayEllipse(I, iPc, mu20, mu11, mu02, true, col); +} + +void +vpMbtMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ic, + double mu20_p, double mu11_p, double mu02_p) +{ + iPc = ic; + mu20 = mu20_p; + mu11 = mu11_p; + mu02 = mu02_p; + + if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) { + + double val_p = sqrt(vpMath::sqr(mu20_p-mu02_p) + 4*vpMath::sqr(mu11_p)); + a = sqrt((mu20_p + mu02_p + val_p)/2); + b = sqrt((mu20_p + mu02_p - val_p)/2); + + e = (mu02_p - mu20_p + val_p)/(2*mu11_p); + } + else { + a = sqrt(mu20_p); + b = sqrt(mu02_p); + e = 0.; + } + + e = atan(e); + + ce = cos(e); + se = sin(e); + + sample(I) ; + + vpMeTracker::initTracking(I) ; + + try{ + track(I) ; + } + catch(vpException &exception) + { + throw(exception) ; + } +} + +/*! + Track the ellipse in the image I. + + \param I : Image in which the ellipse appears. +*/ +void +vpMbtMeEllipse::track(const vpImage<unsigned char> &I) +{ + try{ + vpMeTracker::track(I) ; + } + catch(vpException &exception) + { + throw(exception) ; + } +} + +void +vpMbtMeEllipse::updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &ic, double mu20_p, double mu11_p, double mu02_p) +{ + iPc = ic; + mu20 = mu20_p; + mu11 = mu11_p; + mu02 = mu02_p; + + if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) { + + double val_p = sqrt(vpMath::sqr(mu20_p-mu02_p) + 4*vpMath::sqr(mu11_p)); + a = sqrt((mu20_p + mu02_p + val_p)/2); + b = sqrt((mu20_p + mu02_p - val_p)/2); + + e = (mu02_p - mu20_p + val_p)/(2*mu11_p); + } + else { + a = sqrt(mu20_p); + b = sqrt(mu02_p); + e = 0.; + } + + e = atan(e); + + ce = cos(e); + se = sin(e); + + suppressPoints(); + reSample(I); + + // remet a jour l'angle delta pour chaque point de la liste + updateTheta(); +} + +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/tracking/mbt/edge/vpMbtMeEllipse.h b/src/tracking/mbt/edge/vpMbtMeEllipse.h new file mode 100644 index 0000000000000000000000000000000000000000..87f11b72388be7a99387b0c700b146b558ff144a --- /dev/null +++ b/src/tracking/mbt/edge/vpMbtMeEllipse.h @@ -0,0 +1,245 @@ +/**************************************************************************** + * + * $Id: vpMbtMeEllipse.h 4705 2014-03-27 16:24:35Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Moving edges. + * + * Authors: + * Eric Marchand + * + *****************************************************************************/ + +/*! + \file vpMbtMeEllipse.h + \brief Moving edges on an ellipse +*/ + +#ifndef vpMbtMeEllipse_HH +#define vpMbtMeEllipse_HH + +#include <visp/vpMatrix.h> +#include <visp/vpColVector.h> + +#include <visp/vpMeTracker.h> +#include <visp/vpMeSite.h> +#include <visp/vpImagePoint.h> + +#include <visp/vpImage.h> +#include <visp/vpColor.h> + +#include <math.h> +#include <list> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +/*! + \class vpMbtMeEllipse + \ingroup TrackingImageME + + \brief Class that tracks an ellipse moving edges. + + In this class, an ellipse is defined as the set of points \f$ (i,j) \f$ of the image frame (For more information about the image frame see the vpImagePoint documentation) that satisfy the implicit equation : + + \f[ i^2 + K_0j^2 + 2K_1ij + 2K_2i + 2K_3j + K4 = 0 \f] + + If \f$ K_0 \f$ is equal to 1 and \f$ K_1 \f$ is equal to 0 the the set of points \f$ (i,j) \f$ represents a circle. + + The five parameters are stored in the public attribute K. + + An ellipse is also defined thanks to three other parameter which are \f$ a \f$, \f$ b \f$ and \f$ e \f$. \f$ a \f$ represents the semiminor axis and \f$ b \f$ is the semimajor axis. Here \f$ e \f$ is the angle made by the + major axis and the i axis of the image frame \f$ (i,j) \f$. The following figure shows better meaning of those parameters. + + \image html vpMbtMeEllipse.gif + \image latex vpMbtMeEllipse.ps width=10cm + + It is possible to compute the coordinates \f$ (i,j) \f$ of a point which belongs to the ellipse thanks to the following equations : + + \f[ i = i_c + b cos(e) cos(\alpha) - a sin(e) sin(\alpha) \f] + \f[ j = j_c + b sin(e) cos(\alpha) + a cos(e) sin(\alpha) \f] + + Here the coordinates \f$ (i_c,j_c) \f$ are the coordinates of the ellipse center in the image frame and \f$ \alpha \f$ is an angle beetween \f$ [0,2\pi] \f$ and which enables to describe all the points of the ellipse. + + \image html vpMbtMeEllipse2.gif + \image latex vpMbtMeEllipse2.ps width=10cm + + The example below available in tutorial-me-ellipse-tracker.cpp and described + in \ref tutorial-tracking-me, section \ref tracking_me_ellipse shows how to use this class. + + \include tutorial-me-ellipse-tracker.cpp + + */ +/* + The code below shows how to use this class. +\code +#include <visp/vpConfig.h> +#include <visp/vpImage.h> +#include <visp/vpMbtMeEllipse.h> +#include <visp/vpImagePoint.h> + +int main() +{ + vpImage<unsigned char> I; + + // I is the image containing the ellipse to track + + // Set the moving-edges tracker parameters + vpMe me; + me.setRange(25); + me.setPointsToTrack(20); + me.setThreshold(15000); + me.setSampleStep(10); + + // Initialize the moving-edges ellipse tracker parameters + vpMbtMeEllipse ellipse; + ellipse.setMe(&me); + + // Initialize the tracking. You have to click on five different points belonging to the ellipse. + ellipse.initTracking(I); + + while ( 1 ) + { + // ... Here the code to read or grab the next image. + + // Track the ellipse. + ellipse.track(I); + } + return 0; +} +\endcode + + \note It is possible to display the ellipse as an overlay. For that you + must use the display function of the class vpMbtMeEllipse. +*/ + +class VISP_EXPORT vpMbtMeEllipse : public vpMeTracker +{ +public: + vpMbtMeEllipse() ; + vpMbtMeEllipse(const vpMbtMeEllipse &meellipse) ; + virtual ~vpMbtMeEllipse() ; + + + void display(const vpImage<unsigned char>&I, vpColor col) ; + void display(const vpImage<unsigned char>& I) {vpMeTracker::display(I);} //Shouldn't be here since it's already in vpMeTracker + /*! + \return Expected number of moving edges to track along the ellipse. + */ + int getExpectedDensity() {return (int)expecteddensity;}; + + /*! + Gets the 2 order central moment \f$ \mu_{11} \f$. + + \return the value of \f$ \mu_{11} \f$. + */ + inline double get_mu11() const {return mu11;} + + /*! + Gets the 2 order central moment \f$ \mu_{02} \f$. + + \return the value of \f$ \mu_{02} \f$. + */ + inline double get_mu02() const {return mu02;} + + /*! + Gets the 2 order central moment \f$ \mu_{20} \f$. + + \return the value of \f$ \mu_{20} \f$. + */ + inline double get_mu20() const {return mu20;} + + /*! + Gets the center of the ellipse. + */ + inline vpImagePoint getCenter() const {return iPc; } + + /*! + Gets the semiminor axis of the ellipse. + */ + inline double getA() const {return a; } + + /*! + Gets the semimajor axis of the ellipse. + */ + inline double getB() const {return b; } + + /*! + Gets the angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$ + */ + inline double getE() const {return e; } + + /*! + Gets the parameters a, b, e of the ellipse. + */ + void getEquationParam(double &A, double &B, double &E) { A = a; B = b; E = e; } + + void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ic, + double mu20_p, double mu11_p, double mu02_p) ; + + void track(const vpImage<unsigned char>& Im); + + void updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &ic, + double mu20_p, double mu11_p, double mu02_p); + + +protected: + //! The coordinates of the ellipse center. + vpImagePoint iPc; + //! \f$ a \f$ is the semiminor axis of the ellipse. + double a; + //! \f$ b \f$ is the semimajor axis of the ellipse. + double b; + //! \f$ e \f$ is the angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$. + double e; + +protected: + //! Value of cos(e). + double ce; + //! Value of sin(e). + double se; + + //! Second order central moments + double mu11,mu20, mu02; + //! Threshold for the robust least square. + double thresholdWeight; + //! Expected number of me to track along the ellipse. + double expecteddensity; + +private: + void sample(const vpImage<unsigned char>&image); + void reSample(const vpImage<unsigned char> &I) ; + void updateTheta(); + void suppressPoints() ; +}; + +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS +#endif diff --git a/src/tracking/mbt/edge/vpMbtMeLine.cpp b/src/tracking/mbt/edge/vpMbtMeLine.cpp index 9754263a8eec73e1259f5fd2f520c228b9cda850..6a9c96fd6294fd9386e43838c6e25185ef6a0798 100644 --- a/src/tracking/mbt/edge/vpMbtMeLine.cpp +++ b/src/tracking/mbt/edge/vpMbtMeLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtMeLine.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpMbtMeLine.cpp 5216 2015-01-28 08:03:09Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,10 +66,11 @@ normalizeAngle(double &delta) /*! Basic constructor that calls the constructor of the class vpMeTracker. */ -vpMbtMeLine::vpMbtMeLine():vpMeTracker() +vpMbtMeLine::vpMbtMeLine() + : rho(0.), theta(0.), theta_1(M_PI/2), delta(0.), delta_1(0), sign(1), + a(0.), b(0.), c(0.), imin(0), imax(0), jmin(0), jmax(0), + expecteddensity(0.) { - sign = 1; - theta_1 = M_PI/2; } /*! @@ -84,16 +85,17 @@ vpMbtMeLine::~vpMbtMeLine() Initialization of the tracking. The line is defined thanks to the coordinates of two points corresponding to the extremities and its (\f$\rho \: \theta\f$) parameters. - Remeber the equation of a line : \f$ i \; cos(\theta) + j \; sin(\theta) - \rho = 0 \f$ + Remember the equation of a line : \f$ i \; cos(\theta) + j \; sin(\theta) - \rho = 0 \f$ \param I : Image in which the line appears. \param ip1 : Coordinates of the first point. \param ip2 : Coordinates of the second point. - \param rho : The \f$\rho\f$ parameter - \param theta : The \f$\theta\f$ parameter + \param rho_ : The \f$\rho\f$ parameter + \param theta_ : The \f$\theta\f$ parameter */ void -vpMbtMeLine::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip1, const vpImagePoint &ip2, double rho, double theta) +vpMbtMeLine::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip1, const vpImagePoint &ip2, + double rho_, double theta_) { vpCDEBUG(1) <<" begin vpMeLine::initTracking()"<<std::endl ; @@ -106,29 +108,26 @@ vpMbtMeLine::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &i PExt[1].ifloat = (float)ip2.get_i() ; PExt[1].jfloat = (float)ip2.get_j() ; - this->rho = rho; - this->theta = theta; + this->rho = rho_; + this->theta = theta_; + theta_1 = theta_; a = cos(theta); b = sin(theta); c = -rho; - - double d = sqrt(vpMath::sqr(ip1.get_i()-ip2.get_i())+vpMath::sqr(ip1.get_j()-ip2.get_j())) ; - - expecteddensity = d / (double)me->getSampleStep(); - + delta = - theta + M_PI/2.0; normalizeAngle(delta); delta_1 = delta; sample(I) ; + expecteddensity = (double)list.size(); vpMeTracker::track(I); } - catch(...) + catch(vpException &e) { -// vpERROR_TRACE("Error caught") ; - throw ; + throw e; } vpCDEBUG(1) <<" end vpMeLine::initTracking()"<<std::endl ; } @@ -150,9 +149,8 @@ vpMbtMeLine::sample(const vpImage<unsigned char>& I) //if (me->getSampleStep==0) if (std::fabs(me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) { - vpERROR_TRACE("function called with sample step = 0") ; throw(vpTrackingException(vpTrackingException::fatalError, - "sample step = 0")) ; + "Function vpMbtMeLine::sample() called with moving-edges sample step = 0")) ; } // i, j portions of the line_p @@ -181,12 +179,12 @@ vpMbtMeLine::sample(const vpImage<unsigned char>& I) for(int i=0; i<=vpMath::round(n_sample); i++) { // If point is in the image, add to the sample list - if(!outOfImage(vpMath::round(is), vpMath::round(js), 0, rows, cols)) + if(!outOfImage(vpMath::round(is), vpMath::round(js), (int)(me->getRange()+me->getMaskSize()+1), (int)rows, (int)cols)) { vpMeSite pix ; //= list.value(); pix.init((int)is, (int)js, delta, 0, sign) ; - pix.track(I,me,0); + pix.track(I, me, false); pix.setDisplay(selectDisplay) ; @@ -205,7 +203,7 @@ vpMbtMeLine::sample(const vpImage<unsigned char>& I) } vpCDEBUG(1) << "end vpMeLine::sample() : "; - vpCDEBUG(1) << n_sample << " point inserted in the list " << std::endl ; + vpCDEBUG(1) << list.size() << " point inserted in the list " << std::endl ; } @@ -215,39 +213,45 @@ vpMbtMeLine::sample(const vpImage<unsigned char>& I) \param I : The image. */ void -vpMbtMeLine::suppressPoints(const vpImage<unsigned char> & /*I*/) +vpMbtMeLine::suppressPoints(const vpImage<unsigned char> & I) { for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ){ vpMeSite s = *it;//current reference pixel - if (fabs(sin(theta)) > 0.9) // Vertical line management - { - if ((s.i < imin) ||(s.i > imax)) + if (fabs(sin(theta)) > 0.9) // Vertical line management { - s.setState(vpMeSite::CONSTRAST); + if ((s.i < imin) ||(s.i > imax)) + { + s.setState(vpMeSite::CONSTRAST); + } } - } - else if (fabs(cos(theta)) > 0.9) // Horizontal line management - { - if ((s.j < jmin) || (s.j > jmax)) + else if (fabs(cos(theta)) > 0.9) // Horizontal line management { - s.setState(vpMeSite::CONSTRAST); + if ((s.j < jmin) || (s.j > jmax)) + { + s.setState(vpMeSite::CONSTRAST); + } } - } - else - { - if ((s.i < imin) ||(s.i > imax) || (s.j < jmin) || (s.j > jmax) ) + else { - s.setState(vpMeSite::CONSTRAST); + if ((s.i < imin) ||(s.i > imax) || (s.j < jmin) || (s.j > jmax) ) + { + s.setState(vpMeSite::CONSTRAST); + } + } - } - if (s.getState() != vpMeSite::NO_SUPPRESSION) - it = list.erase(it); - else - ++it; + if (outOfImage(s.i, s.j, (int)(me->getRange()+me->getMaskSize()+1), (int)I.getHeight(), (int)I.getWidth())) + { + s.setState(vpMeSite::TOO_NEAR); + } + + if (s.getState() != vpMeSite::NO_SUPPRESSION) + it = list.erase(it); + else + ++it; } } @@ -269,9 +273,8 @@ vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I) //if (me->getSampleStep()==0) if (std::fabs(me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) { - - vpERROR_TRACE("function called with sample step = 0") ; - throw(vpTrackingException(vpTrackingException::fatalError,"sample step = 0")) ; + throw(vpTrackingException(vpTrackingException::fatalError, + "Function called with sample step = 0")) ; } // i, j portions of the line_p @@ -287,7 +290,7 @@ vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I) // number of samples along line_p n_sample = length_p/(double)me->getSampleStep(); - double sample = (double)me->getSampleStep(); + double sample_step = (double)me->getSampleStep(); vpMeSite P ; P.init((int) PExt[0].ifloat, (int)PExt[0].jfloat, delta_1, 0, sign) ; @@ -298,16 +301,15 @@ vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I) for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat + di*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat + dj*sample ; P.j = (int)P.jfloat ; + P.ifloat = P.ifloat + di*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat + dj*sample_step ; P.j = (int)P.jfloat ; - - if ((P.i < imin) ||(P.i > imax) || (P.j < jmin) || (P.j > jmax) ) + if ((P.i < imin) ||(P.i > imax) || (P.j < jmin) || (P.j > jmax) ) { if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j,5,vpColor::cyan) ; } else - if(!outOfImage(P.i, P.j, 5, rows, cols)) + if(!outOfImage(P.i, P.j, (int)(me->getRange()+me->getMaskSize()+1), (int)rows, (int)cols)) { P.track(I,me,false) ; @@ -325,27 +327,26 @@ vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I) P.setDisplay(selectDisplay) ; for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat - di*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat - dj*sample ; P.j = (int)P.jfloat ; - + P.ifloat = P.ifloat - di*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat - dj*sample_step ; P.j = (int)P.jfloat ; - if ((P.i < imin) ||(P.i > imax) || (P.j < jmin) || (P.j > jmax) ) + if ((P.i < imin) ||(P.i > imax) || (P.j < jmin) || (P.j > jmax) ) { if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j,5,vpColor::cyan) ; } - else - if(!outOfImage(P.i, P.j, 5, rows, cols)) + else + if(!outOfImage(P.i, P.j, (int)(me->getRange()+me->getMaskSize()+1), (int)rows, (int)cols)) { P.track(I,me,false) ; - + if (P.getState() == vpMeSite::NO_SUPPRESSION) { list.push_back(P); - if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j, 5, vpColor::green) ; + if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j, 5, vpColor::green) ; } else - if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j, 10, vpColor::blue) ; + if (vpDEBUG_ENABLE(3)) vpDisplay::displayCross(I,P.i,P.j, 10, vpColor::blue) ; } } @@ -368,16 +369,14 @@ vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I) void vpMbtMeLine::reSample(const vpImage<unsigned char> &I) { - double d = sqrt(vpMath::sqr(PExt[0].ifloat-PExt[1].ifloat)+vpMath::sqr(PExt[0].jfloat-PExt[1].jfloat)) ; - unsigned int n = numberOfSignal() ; - double expecteddensity = d / (double)me->getSampleStep(); if ((double)n<0.5*expecteddensity && n > 0) { double delta_new = delta; delta = delta_1; sample(I) ; + expecteddensity = (double)list.size(); delta = delta_new; // 2. On appelle ce qui n'est pas specifique { @@ -402,12 +401,9 @@ vpMbtMeLine::reSample(const vpImage<unsigned char> &I) void vpMbtMeLine::reSample(const vpImage<unsigned char> &I, vpImagePoint ip1, vpImagePoint ip2) { - double d = sqrt(vpMath::sqr(ip1.get_i()-ip2.get_i())+vpMath::sqr(ip1.get_j()-ip2.get_j())) ; - size_t n = list.size(); - expecteddensity = d / (double)me->getSampleStep(); - if ((double)n<0.5*expecteddensity && n > 0) + if ((double)n<0.5*expecteddensity /*&& n > 0*/) // n is always > 0 { double delta_new = delta; delta = delta_1; @@ -416,6 +412,7 @@ vpMbtMeLine::reSample(const vpImage<unsigned char> &I, vpImagePoint ip1, vpImage PExt[1].ifloat = (float)ip2.get_i() ; PExt[1].jfloat = (float)ip2.get_j() ; sample(I) ; + expecteddensity = (double)list.size(); delta = delta_new; vpMeTracker::track(I) ; } @@ -427,7 +424,7 @@ vpMbtMeLine::reSample(const vpImage<unsigned char> &I, vpImagePoint ip1, vpImage void vpMbtMeLine::updateDelta() { - vpMeSite p ; + vpMeSite p_me ; double diff = 0; @@ -447,10 +444,10 @@ vpMbtMeLine::updateDelta() normalizeAngle(delta); for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - p.alpha = delta ; - p.mask_sign = sign; - *it = p; + p_me = *it; + p_me.alpha = delta ; + p_me.mask_sign = sign; + *it = p_me; } delta_1 = delta; } @@ -468,9 +465,9 @@ vpMbtMeLine::track(const vpImage<unsigned char> &I) { vpMeTracker::track(I); } - catch(...) + catch(vpException &e) { - throw ; + throw e; } // supression des points rejetes par les ME @@ -483,14 +480,14 @@ vpMbtMeLine::track(const vpImage<unsigned char> &I) Update the moving edges parameters after the virtual visual servoing. \param I : The image. - \param rho : The \f$\rho\f$ parameter used in the line's polar equation. - \param theta : The \f$\theta\f$ parameter used in the line's polar equation. + \param rho_ : The \f$\rho\f$ parameter used in the line's polar equation. + \param theta_ : The \f$\theta\f$ parameter used in the line's polar equation. */ void -vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, double rho, double theta) +vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, double rho_, double theta_) { - this->rho = rho; - this->theta = theta; + this->rho = rho_; + this->theta = theta_; a = cos(theta); b = sin(theta); c = -rho; @@ -514,14 +511,15 @@ vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, double rho, doubl \param I : The image. \param ip1 : The first extremity of the line. \param ip2 : The second extremity of the line. - \param rho : The \f$\rho\f$ parameter used in the line's polar equation. - \param theta : The \f$\theta\f$ parameter used in the line's polar equation. + \param rho_ : The \f$\rho\f$ parameter used in the line's polar equation. + \param theta_ : The \f$\theta\f$ parameter used in the line's polar equation. */ void -vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, vpImagePoint ip1, vpImagePoint ip2, double rho, double theta) +vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, vpImagePoint ip1, vpImagePoint ip2, + double rho_, double theta_) { - this->rho = rho; - this->theta = theta; + this->rho = rho_; + this->theta = theta_; a = cos(theta); b = sin(theta); c = -rho; @@ -545,58 +543,58 @@ vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, vpImagePoint ip1, void vpMbtMeLine::setExtremities() { - double imin = +1e6 ; - double jmin = +1e6; - double imax = -1 ; - double jmax = -1 ; + double i_min = +1e6 ; + double j_min = +1e6; + double i_max = -1 ; + double j_max = -1 ; // Loop through list of sites to track for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ vpMeSite s = *it;//current reference pixel - if (s.ifloat < imin) + if (s.ifloat < i_min) { - imin = s.ifloat ; - jmin = s.jfloat ; + i_min = s.ifloat ; + j_min = s.jfloat ; } - if (s.ifloat > imax) + if (s.ifloat > i_max) { - imax = s.ifloat ; - jmax = s.jfloat ; + i_max = s.ifloat ; + j_max = s.jfloat ; } } if ( ! list.empty() ) { - PExt[0].ifloat = imin ; - PExt[0].jfloat = jmin ; - PExt[1].ifloat = imax ; - PExt[1].jfloat = jmax ; + PExt[0].ifloat = i_min ; + PExt[0].jfloat = j_min ; + PExt[1].ifloat = i_max ; + PExt[1].jfloat = j_max ; } - if (fabs(imin-imax) < 25) + if (fabs(i_min-i_max) < 25) { for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ vpMeSite s = *it;//current reference pixel - if (s.jfloat < jmin) + if (s.jfloat < j_min) { - imin = s.ifloat ; - jmin = s.jfloat ; + i_min = s.ifloat ; + j_min = s.jfloat ; } - if (s.jfloat > jmax) + if (s.jfloat > j_max) { - imax = s.ifloat ; - jmax = s.jfloat ; + i_max = s.ifloat ; + j_max = s.jfloat ; } } if (! list.empty()) { - PExt[0].ifloat = imin ; - PExt[0].jfloat = jmin ; - PExt[1].ifloat = imax ; - PExt[1].jfloat = jmax ; + PExt[0].ifloat = i_min ; + PExt[0].jfloat = j_min ; + PExt[1].ifloat = i_max ; + PExt[1].jfloat = j_max ; } bubbleSortJ(); } @@ -661,7 +659,7 @@ vpMbtMeLine::bubbleSortJ() void -vpMbtMeLine::findSignal(const vpImage<unsigned char>& I, const vpMe *me, double *conv) +vpMbtMeLine::findSignal(const vpImage<unsigned char>& I, const vpMe *p_me, double *conv) { vpImagePoint itest(PExt[0].ifloat+(PExt[1].ifloat-PExt[0].ifloat)/2, PExt[0].jfloat+(PExt[1].jfloat-PExt[0].jfloat)/2); @@ -670,7 +668,7 @@ vpMbtMeLine::findSignal(const vpImage<unsigned char>& I, const vpMe *me, double vpMeSite *list_query_pixels; // double convolution = 0; - unsigned int range = me->getRange(); + unsigned int range = p_me->getRange(); list_query_pixels = pix.getQueryList(I, (int)range); @@ -680,7 +678,7 @@ vpMbtMeLine::findSignal(const vpImage<unsigned char>& I, const vpMe *me, double for(unsigned int n = 0 ; n < 2 * range + 1 ; n++) { - conv[n] = list_query_pixels[n].convolution(I, me); + conv[n] = list_query_pixels[n].convolution(I, p_me); } delete [] list_query_pixels; } diff --git a/src/tracking/mbt/edge/vpMbtMeLine.h b/src/tracking/mbt/edge/vpMbtMeLine.h index a3ebfcb09bf128e81594dcbb9bdb40fdc533e886..dac7d9f6c48a5e7ae8e2282583c973c8c7ee0c8d 100644 --- a/src/tracking/mbt/edge/vpMbtMeLine.h +++ b/src/tracking/mbt/edge/vpMbtMeLine.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtMeLine.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMbtMeLine.h 4740 2014-05-26 07:15:44Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,6 +53,8 @@ #include <visp/vpMe.h> #include <visp/vpMeTracker.h> +#ifndef DOXYGEN_SHOULD_SKIP_THIS + /*! \class vpMbtMeLine \brief Implementation of a line used by the model-based tracker. @@ -121,5 +123,7 @@ class VISP_EXPORT vpMbtMeLine : public vpMeTracker void updateDelta(); } ; +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS + #endif diff --git a/src/tracking/mbt/edge/vpMbtXmlParser.cpp b/src/tracking/mbt/edge/vpMbtXmlParser.cpp index ace2eff552e817ce4989c3b951295150c8ff3412..fef84274535e58a7ee217fee8773972e7015e8ca 100644 --- a/src/tracking/mbt/edge/vpMbtXmlParser.cpp +++ b/src/tracking/mbt/edge/vpMbtXmlParser.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtXmlParser.cpp 4320 2013-07-17 15:37:27Z ayol $ + * $Id: vpMbtXmlParser.cpp 5238 2015-01-30 13:52:25Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,12 +32,13 @@ * * * Description: - * Make the complete tracking of an object by using its CAD model + * Load XML parameters of the Model based tracker (using edges). * * Authors: * Nicolas Melchior * Romain Tallonneau * Eric Marchand + * Aurelien Yol * *****************************************************************************/ #include <visp/vpConfig.h> @@ -57,11 +58,8 @@ Default constructor. */ -vpMbtXmlParser::vpMbtXmlParser() +vpMbtXmlParser::vpMbtXmlParser() : m_ecm(), useLod(false), minLineLengthThreshold(50.0), minPolygonAreaThreshold(2500.0) { - hasNearClipping = false; - hasFarClipping = false; - fovClipping = false; init(); } @@ -78,9 +76,8 @@ vpMbtXmlParser::~vpMbtXmlParser() void vpMbtXmlParser::init() { - setMainTag("conf"); + vpMbXmlParser::init(); - nodeMap["conf"] = conf; nodeMap["ecm"] = ecm; nodeMap["mask"] = mask; nodeMap["size"] = size; @@ -94,20 +91,10 @@ vpMbtXmlParser::init() nodeMap["sample"] = sample; nodeMap["step"] = step; nodeMap["nb_sample"] = nb_sample; - nodeMap["face"] = face; - nodeMap["angle_appear"] = angle_appear; - nodeMap["angle_disappear"] = angle_disappear; - nodeMap["near_clipping"] = near_clipping; - nodeMap["far_clipping"] = far_clipping; - nodeMap["fov_clipping"] = fov_clipping; - nodeMap["camera"] = camera; - nodeMap["height"] = height; - nodeMap["width"] = width; - nodeMap["u0"] = u0; - nodeMap["v0"] = v0; - nodeMap["px"] = px; - nodeMap["py"] = py; - + nodeMap["lod"] = lod; + nodeMap["use_lod"] = use_lod; + nodeMap["min_line_length_threshold"] = min_line_length_threshold; + nodeMap["min_polygon_area_threshold"] = min_polygon_area_threshold; } /*! @@ -144,16 +131,25 @@ vpMbtXmlParser::writeMainClass(xmlNodePtr /*node*/) void vpMbtXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) { - bool ecm_node = false; - bool sample_node = false; bool camera_node = false; bool face_node = false; + bool ecm_node = false; + bool sample_node = false; + bool lod_node = false; for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { if(dataNode->type == XML_ELEMENT_NODE){ std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); if(iter_data != nodeMap.end()){ switch (iter_data->second){ + case camera:{ + this->read_camera (doc, dataNode); + camera_node = true; + }break; + case face:{ + this->read_face(doc, dataNode); + face_node = true; + }break; case ecm:{ this->read_ecm (doc, dataNode); ecm_node = true; @@ -162,13 +158,9 @@ vpMbtXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) this->read_sample (doc, dataNode); sample_node = true; }break; - case camera:{ - this->read_camera (doc, dataNode); - camera_node = true; - }break; - case face:{ - this->read_face(doc, dataNode); - face_node = true; + case lod:{ + this->read_lod(doc, dataNode); + lod_node = true; }break; default:{ // vpTRACE("unknown tag in read_sample : %d, %s", iter_data->second, (iter_data->first).c_str()); @@ -178,17 +170,37 @@ vpMbtXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) } } - if(!ecm_node) - std::cout << "WARNING: ECM Node not specified, default values used" << std::endl; - - if(!sample_node) - std::cout << "WARNING: SAMPLE Node not specified, default values used" << std::endl; - - if(!camera_node) - std::cout << "WARNING: CAMERA Node not specified, default values used" << std::endl; + if(!camera_node) { + std::cout << "camera : u0 : "<< this->cam.get_u0() << " (default)" <<std::endl; + std::cout << "camera : v0 : "<< this->cam.get_v0() << " (default)" <<std::endl; + std::cout << "camera : px : "<< this->cam.get_px() << " (default)" <<std::endl; + std::cout << "camera : py : "<< this->cam.get_py() << " (default)" <<std::endl; + } + + if(!face_node) { + std::cout << "face : Angle Appear : "<< angleAppear <<" (default)" <<std::endl; + std::cout << "face : Angle Disappear : "<< angleDisappear <<" (default)" <<std::endl; + } + + if(!ecm_node) { + std::cout << "ecm : mask : size : "<< this->m_ecm.getMaskSize() << " (default)" <<std::endl; + std::cout << "ecm : mask : nb_mask : "<< this->m_ecm.getMaskNumber() << " (default)" <<std::endl; + std::cout <<"ecm : range : tracking : "<< this->m_ecm.getRange()<< " (default)" <<std::endl; + std::cout <<"ecm : contrast : threshold : " << this->m_ecm.getThreshold()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu1 : " << this->m_ecm.getMu1()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu2 : " << this->m_ecm.getMu2()<<" (default)" <<std::endl; + } - if(!face_node) - std::cout << "WARNING: FACE Node not specified, default values used" << std::endl; + if(!sample_node) { + std::cout <<"sample : sample_step : "<< this->m_ecm.getSampleStep()<< " (default)" << std::endl; + std::cout <<"sample : n_total_sample : "<< this->m_ecm.getNbTotalSample()<< " (default)"<<std::endl; + } + + if(!lod_node) { + std::cout << "lod : use lod : " << useLod << " (default)" << std::endl; + std::cout << "lod : min line length threshold : " << minLineLengthThreshold << " (default)" << std::endl; + std::cout << "lod : min polygon area threshold : " << minPolygonAreaThreshold << " (default)" << std::endl; + } } @@ -232,14 +244,20 @@ vpMbtXmlParser::read_ecm (xmlDocPtr doc, xmlNodePtr node) } } - if(!mask_node) - std::cout << "WARNING: In ECM Node, MASK Node not specified, default values used" << std::endl; + if(!mask_node) { + std::cout << "ecm : mask : size : "<< this->m_ecm.getMaskSize() << " (default)" <<std::endl; + std::cout << "ecm : mask : nb_mask : "<< this->m_ecm.getMaskNumber() << " (default)" <<std::endl; + } - if(!range_node) - std::cout << "WARNING: In ECM Node, RANGE Node not specified, default values used" << std::endl; + if(!range_node) { + std::cout <<"ecm : range : tracking : "<< this->m_ecm.getRange()<< " (default)" <<std::endl; + } - if(!contrast_node) - std::cout << "WARNING: In ECM Node, CONTRAST Node not specified, default values used" << std::endl; + if(!contrast_node) { + std::cout <<"ecm : contrast : threshold " << this->m_ecm.getThreshold()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu1 " << this->m_ecm.getMu1()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu2 " << this->m_ecm.getMu2()<<" (default)" <<std::endl; + } } /*! @@ -285,188 +303,14 @@ vpMbtXmlParser::read_sample (xmlDocPtr doc, xmlNodePtr node) this->m_ecm.setNbTotalSample(d_nb_sample); if(!step_node) - std::cout << "WARNING: In SAMPLE Node, STEP Node not specified, default value used : " << this->m_ecm.getSampleStep() << std::endl; + std::cout <<"sample : sample_step : "<< this->m_ecm.getSampleStep()<< " (default)" << std::endl; else - std::cout <<"sample : sample_step "<< this->m_ecm.getSampleStep()<<std::endl; + std::cout <<"sample : sample_step : "<< this->m_ecm.getSampleStep()<<std::endl; if(!nb_sample_node) - std::cout << "WARNING: In SAMPLE Node, NB_SAMPLE Node not specified, default value used : " << this->m_ecm.getNbTotalSample() << std::endl; - else - std::cout <<"sample : n_total_sample "<< this->m_ecm.getNbTotalSample()<<std::endl; -} - -/*! - Read camera information. - - \throw vpException::fatalError if there was an unexpected number of data. - - \param doc : Pointer to the document. - \param node : Pointer to the node of the camera information. -*/ -void -vpMbtXmlParser::read_camera (xmlDocPtr doc, xmlNodePtr node) -{ - bool height_node = false; - bool width_node = false; - bool u0_node = false; - bool v0_node = false; - bool px_node = false; - bool py_node = false; - - // current data values. -// int d_height=0 ; -// int d_width= 0 ; - double d_u0 = this->cam.get_u0(); - double d_v0 = this->cam.get_v0(); - double d_px = this->cam.get_px(); - double d_py = this->cam.get_py(); - - for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { - if(dataNode->type == XML_ELEMENT_NODE){ - std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); - if(iter_data != nodeMap.end()){ - switch (iter_data->second){ - case height:{ - /* d_height = */ xmlReadIntChild(doc, dataNode); - height_node = true; - }break; - case width:{ - /* d_width = */ xmlReadIntChild(doc, dataNode); - width_node = true; - }break; - case u0:{ - d_u0 = xmlReadDoubleChild(doc, dataNode); - u0_node = true; - }break; - case v0:{ - d_v0 = xmlReadDoubleChild(doc, dataNode); - v0_node = true; - }break; - case px:{ - d_px = xmlReadDoubleChild(doc, dataNode); - px_node = true; - }break; - case py:{ - d_py = xmlReadDoubleChild(doc, dataNode); - py_node = true; - }break; - default:{ -// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); - }break; - } - } - } - } - - this->cam.initPersProjWithoutDistortion(d_px, d_py, d_u0, d_v0) ; - - if(!height_node) - std::cout << "WARNING: In CAMERA Node, HEIGHT Node not specified, default value used" << std::endl; - - if(!width_node) - std::cout << "WARNING: In CAMERA Node, WIDTH Node not specified, default value used" << std::endl; - - if(!u0_node) - std::cout << "WARNING: In CAMERA Node, u0 Node not specified, default value used : " << this->cam.get_u0() << std::endl; + std::cout <<"sample : n_total_sample : "<< this->m_ecm.getNbTotalSample()<< " (default)"<<std::endl; else - std::cout << "camera : u0 "<< this->cam.get_u0() <<std::endl; - - if(!v0_node) - std::cout << "WARNING: In CAMERA Node, v0 Node not specified, default value used : " << this->cam.get_v0() << std::endl; - else - std::cout << "camera : v0 "<< this->cam.get_v0() <<std::endl; - - if(!px_node) - std::cout << "WARNING: In CAMERA Node, px Node not specified, default value used : " << this->cam.get_px() << std::endl; - else - std::cout << "camera : px "<< this->cam.get_px() <<std::endl; - - if(!py_node) - std::cout << "WARNING: In CAMERA Node, py Node not specified, default value used : " << this->cam.get_py() << std::endl; - else - std::cout << "camera : py "<< this->cam.get_py() <<std::endl; -} - -/*! - Read face information. - - \throw vpException::fatalError if there was an unexpected number of data. - - \param doc : Pointer to the document. - \param node : Pointer to the node of the camera information. -*/ -void -vpMbtXmlParser::read_face(xmlDocPtr doc, xmlNodePtr node) -{ - bool angle_appear_node = false; - bool angle_disappear_node = false; - bool near_clipping_node = false; - bool far_clipping_node = false; - bool fov_clipping_node = false; - - for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { - if(dataNode->type == XML_ELEMENT_NODE){ - std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); - if(iter_data != nodeMap.end()){ - switch (iter_data->second){ - case angle_appear:{ - angleAppear = xmlReadDoubleChild(doc, dataNode); - angle_appear_node = true; - }break; - case angle_disappear:{ - angleDisappear = xmlReadDoubleChild(doc, dataNode); - angle_disappear_node = true; - }break; - case near_clipping:{ - nearClipping = xmlReadDoubleChild(doc, dataNode); - near_clipping_node = true; - hasNearClipping = true; - }break; - case far_clipping:{ - farClipping = xmlReadDoubleChild(doc, dataNode); - far_clipping_node = true; - hasFarClipping = true; - }break; - case fov_clipping:{ - fovClipping = (bool)xmlReadIntChild(doc, dataNode); - fov_clipping_node = true; - }break; - default:{ -// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); - }break; - } - } - } - } - - if(!angle_appear_node) - std::cout << "WARNING: In FACE Node, ANGLE_APPEAR Node not specified, default value used : " << angleAppear << std::endl; - else - std::cout << "face : Angle Appear "<< angleAppear <<std::endl; - - if(!angle_disappear_node) - std::cout << "WARNING: In FACE Node, ANGLE_DESAPPEAR Node not specified, default value used : " << angleDisappear << std::endl; - else - std::cout << "face : Angle Disappear : "<< angleDisappear <<std::endl; - - if(!near_clipping_node) - std::cout << "WARNING: In FACE Node, NEAR_CLIPPING Node not specified, no near clipping used" << std::endl; - else - std::cout << "face : Near Clipping : "<< nearClipping <<std::endl; - - if(!far_clipping_node) - std::cout << "WARNING: In FACE Node, FAR_CLIPPING Node not specified, no far clipping used" << std::endl; - else - std::cout << "face : Far Clipping : "<< farClipping <<std::endl; - - if(!fov_clipping_node) - std::cout << "WARNING: In FACE Node, FOV_CLIPPING Node not specified, no fov clipping used" << std::endl; - else{ - if(fovClipping) - std::cout << "face : Fov Clipping : True" <<std::endl; - else - std::cout << "face : Fov Clipping : False" <<std::endl; - } + std::cout <<"sample : n_total_sample : "<< this->m_ecm.getNbTotalSample()<<std::endl; } /*! @@ -509,17 +353,20 @@ vpMbtXmlParser::read_mask (xmlDocPtr doc, xmlNodePtr node) } this->m_ecm.setMaskSize(d_size) ; + // Check to ensure that d_nb_mask > 0 + if (! d_nb_mask) + throw(vpException(vpException::badValue, "Model-based tracker mask size parameter should be different from zero in xml file")); this->m_ecm.setMaskNumber(d_nb_mask); if(!size_node) - std::cout << "WARNING: In MASK Node, SIZE Node not specified, default value used : " << this->m_ecm.getMaskSize() << std::endl; - else - std::cout << "ecm : mask : size "<< this->m_ecm.getMaskSize() <<std::endl; + std::cout << "ecm : mask : size : "<< this->m_ecm.getMaskSize() << " (default)" <<std::endl; + else + std::cout << "ecm : mask : size : "<< this->m_ecm.getMaskSize() <<std::endl; if(!nb_mask_node) - std::cout << "WARNING: In MASK Node, NB_MASK Node not specified, default value used : " << this->m_ecm.getMaskNumber() << std::endl; + std::cout << "ecm : mask : nb_mask : "<< this->m_ecm.getMaskNumber() << " (default)" <<std::endl; else - std::cout << "ecm : mask : nb_mask "<< this->m_ecm.getMaskNumber() <<std::endl; + std::cout << "ecm : mask : nb_mask : "<< this->m_ecm.getMaskNumber() <<std::endl; } /*! @@ -558,9 +405,9 @@ vpMbtXmlParser::read_range (xmlDocPtr doc, xmlNodePtr node) this->m_ecm.setRange(m_range_tracking); if(!tracking_node) - std::cout << "WARNING: In RANGE Node, TRACKING Node not specified, default value used : " << this->m_ecm.getRange() << std::endl; + std::cout <<"ecm : range : tracking : "<< this->m_ecm.getRange()<< " (default)" <<std::endl; else - std::cout <<"ecm : range : tracking "<< this->m_ecm.getRange()<<std::endl; + std::cout <<"ecm : range : tracking : "<< this->m_ecm.getRange()<<std::endl; } @@ -614,20 +461,68 @@ vpMbtXmlParser::read_contrast (xmlDocPtr doc, xmlNodePtr node) this->m_ecm.setThreshold(d_edge_threshold); if(!edge_threshold_node) - std::cout << "WARNING: In CONTRAST Node, EDGE_THRESHOLD Node not specified, default value used : " << this->m_ecm.getThreshold() << std::endl; + std::cout <<"ecm : contrast : threshold " << this->m_ecm.getThreshold()<<" (default)" <<std::endl; else std::cout <<"ecm : contrast : threshold " << this->m_ecm.getThreshold()<<std::endl; if(!mu1_node) - std::cout << "WARNING: In CONTRAST Node, mu1 Node not specified, default value used : " << this->m_ecm.getMu1() << std::endl; + std::cout <<"ecm : contrast : mu1 " << this->m_ecm.getMu1()<<" (default)" <<std::endl; else std::cout <<"ecm : contrast : mu1 " << this->m_ecm.getMu1()<<std::endl; if(!mu2_node) - std::cout << "WARNING: In CONTRAST Node, mu2 Node not specified, default value used : " << this->m_ecm.getMu2() << std::endl; + std::cout <<"ecm : contrast : mu2 " << this->m_ecm.getMu2()<<" (default)" <<std::endl; else std::cout <<"ecm : contrast : mu2 " << this->m_ecm.getMu2()<<std::endl; } +void +vpMbtXmlParser::read_lod (xmlDocPtr doc, xmlNodePtr node) { + bool use_lod_node = false; + bool min_line_length_threshold_node = false; + bool min_polygon_area_threshold_node = false; + + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case use_lod: + useLod = (xmlReadIntChild(doc, dataNode) != 0); + use_lod_node = true; + break; + case min_line_length_threshold: + minLineLengthThreshold = xmlReadDoubleChild(doc, dataNode); + min_line_length_threshold_node = true; + break; + case min_polygon_area_threshold: + minPolygonAreaThreshold = xmlReadDoubleChild(doc, dataNode); + min_polygon_area_threshold_node = true; + break; + default:{ +// vpTRACE("unknown tag in read_contrast : %d, %s", iter_data->second, (iter_data->first).c_str()); + }break; + } + } + } + } + + if(!use_lod_node) + std::cout << "lod : use lod : " << useLod << " (default)" <<std::endl; + else + std::cout << "lod : use lod : " << useLod << std::endl; + + if(!min_line_length_threshold_node) + std::cout <<"lod : min line length threshold : " << minLineLengthThreshold <<" (default)" <<std::endl; + else + std::cout <<"lod : min line length threshold : " << minLineLengthThreshold <<std::endl; + + if(!min_polygon_area_threshold_node) + std::cout <<"lod : min polygon area threshold : " << minPolygonAreaThreshold <<" (default)" <<std::endl; + else + std::cout <<"lod : min polygon area threshold : " << minPolygonAreaThreshold <<std::endl; +} + #endif diff --git a/src/tracking/mbt/edge/vpMbtXmlParser.h b/src/tracking/mbt/edge/vpMbtXmlParser.h index 9431ad0142fa27cbf545e314b8f2104f29774753..75e3dd8db1344c1c2001a6020033e4b6fc3d684f 100644 --- a/src/tracking/mbt/edge/vpMbtXmlParser.h +++ b/src/tracking/mbt/edge/vpMbtXmlParser.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtXmlParser.h 4320 2013-07-17 15:37:27Z ayol $ + * $Id: vpMbtXmlParser.h 5054 2014-12-11 17:50:44Z strinh $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,12 +32,13 @@ * * * Description: - * Make the complete tracking of an object by using its CAD model + * Load XML parameters of the Model based tracker (using edges). * * Authors: * Nicolas Melchior * Romain Tallonneau * Eric Marchand + * Aurelien Yol * *****************************************************************************/ @@ -55,11 +56,8 @@ #include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ -#include <visp/vpXmlParser.h> +#include <visp/vpMbXmlParser.h> #include <visp/vpMe.h> -#include <visp/vpCameraParameters.h> - - /*! \class vpMbtXmlParser @@ -69,31 +67,11 @@ Data parser for the model based tracker. */ -class VISP_EXPORT vpMbtXmlParser: public vpXmlParser +class VISP_EXPORT vpMbtXmlParser: virtual public vpMbXmlParser { protected: - //! Moving edges parameters. - vpMe m_ecm; - //! Camera parameters. - vpCameraParameters cam; - //! Angle to determine if a face appeared - double angleAppear; - //! Angle to determine if a face disappeared - double angleDisappear; - //! Is near clipping distance specified? - bool hasNearClipping; - //! Near clipping distance - double nearClipping; - //! Is far clipping distance specified? - bool hasFarClipping; - //! Near clipping distance - double farClipping; - //! Fov Clipping - bool fovClipping; - typedef enum{ - conf, - ecm, + ecm = vpMbXmlParser::last, mask, size, nb_mask, @@ -106,125 +84,71 @@ protected: sample, step, nb_sample, - face, - angle_appear, - angle_disappear, - near_clipping, - far_clipping, - fov_clipping, - camera, - height, - width, - u0, - v0, - px, - py - } dataToParse; - + lod, + use_lod, + min_line_length_threshold, + min_polygon_area_threshold, + last + } dataToParseMb; -public: + //! Moving edges parameters. + vpMe m_ecm; + //! If true, the LOD is enabled, otherwise it is not + bool useLod; + //! Minimum line length to track a segment when LOD is enabled + double minLineLengthThreshold; + //! Minimum polygon area to track a face when LOD is enabled + double minPolygonAreaThreshold; - vpMbtXmlParser(); - virtual ~vpMbtXmlParser(); - /*! - Get the angle to determine if a face appeared. +public: - \return angleAppear - */ - inline double getAngleAppear() const {return angleAppear;} - - /*! - Get the angle to determine if a face disappeared. + vpMbtXmlParser(); + virtual ~vpMbtXmlParser(); - \return angleDisappear - */ - inline double getAngleDisappear() const {return angleDisappear;} - - void getCameraParameters(vpCameraParameters& _cam) const { _cam = this->cam;} - /*! - Get the far clipping distance. + Get the state of LOD setting. - \return farClipping - */ - inline double getFarClippingDistance() const {return farClipping;} - - /*! - Use FOV clipping + \return True if LOD is enabled, false otherwise. + */ + inline bool getLodState() const { + return useLod; + } - \return True if yes, False otherwise. - */ - inline bool getFovClipping() const {return fovClipping;} - void getMe(vpMe& _ecm) const { _ecm = this->m_ecm;} /*! - Get the near clipping distance. + Get the minimum line length to track a segment when LOD is enabled. - \return nearClipping - */ - inline double getNearClippingDistance() const {return nearClipping;} - - /*! - Has Far clipping been specified? + \return The minimum line length. + */ + inline double getMinLineLengthThreshold() const { + return minLineLengthThreshold; + } - \return True if yes, False otherwise. - */ - inline bool hasFarClippingDistance() const {return hasFarClipping;} - /*! - Has Near clipping been specified? - - \return True if yes, False otherwise. - */ - inline bool hasNearClippingDistance() const {return hasNearClipping;} - - void parse(const char * filename); - - void readMainClass(xmlDocPtr doc, xmlNodePtr node); - void read_ecm (xmlDocPtr doc, xmlNodePtr node); - void read_sample (xmlDocPtr doc, xmlNodePtr node); - void read_camera (xmlDocPtr doc, xmlNodePtr node); - void read_mask (xmlDocPtr doc, xmlNodePtr node); - void read_range (xmlDocPtr doc, xmlNodePtr node); - void read_contrast (xmlDocPtr doc, xmlNodePtr node); - void read_face(xmlDocPtr doc, xmlNodePtr node); - - /*! - Set the angle to determine if a face appeared. - - \param aappear : New angleAppear - */ - inline void setAngleAppear(const double &aappear) {angleAppear = aappear;} - - /*! - Set the angle to determine if a face disappeared. - - \param adisappear : New angleDisappear - */ - inline void setAngleDisappear(const double &adisappear) {angleDisappear = adisappear;} - - void setCameraParameters(const vpCameraParameters &_cam){ cam = _cam; } - - /*! - Set the far clipping distance. - - \param fclip : New farClipping - */ - inline void setFarClippingDistance(const double &fclip) {farClipping = fclip;} + Get the minimum polygon area to track a face when LOD is enabled. + + \return The minimum polygon area. + */ + inline double getMinPolygonAreaThreshold() const { + return minPolygonAreaThreshold; + } + + void parse(const char * filename); + + virtual void readMainClass(xmlDocPtr doc, xmlNodePtr node); + void read_ecm (xmlDocPtr doc, xmlNodePtr node); + void read_sample (xmlDocPtr doc, xmlNodePtr node); + void read_mask (xmlDocPtr doc, xmlNodePtr node); + void read_range (xmlDocPtr doc, xmlNodePtr node); + void read_contrast (xmlDocPtr doc, xmlNodePtr node); + void read_lod (xmlDocPtr doc, xmlNodePtr node); void setMovingEdge(const vpMe &_ecm){ m_ecm = _ecm; } - - /*! - Set the near clipping distance. - \param nclip : New nearClipping - */ - inline void setNearClippingDistance(const double &nclip) {nearClipping = nclip;} - void writeMainClass(xmlNodePtr node); - + protected: void init(); diff --git a/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp b/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp index 11bb67f739f88754d622c77f184af9fa3e216ac3..3799fc3af79647c948da85a3654effee45a1f402 100644 --- a/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp +++ b/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbEdgeKltTracker.cpp 4327 2013-07-19 14:08:01Z fspindle $ + * $Id: vpMbEdgeKltTracker.cpp 5189 2015-01-21 16:42:18Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,23 +39,27 @@ * *****************************************************************************/ +//#define VP_DEBUG_MODE 1 // Activate debug level 1 + +#include <visp/vpDebug.h> #include <visp/vpMbEdgeKltTracker.h> +#include <visp/vpTrackingException.h> +#include <visp/vpVelocityTwistMatrix.h> -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) vpMbEdgeKltTracker::vpMbEdgeKltTracker() + : compute_interaction(true), lambda(0.8), thresholdKLT(2.), thresholdMBT(2.), maxIter(200) { - compute_interaction = true; computeCovariance = false; - lambda = 0.8; - thresholdKLT = 2.0; - thresholdMBT = 2.0; - maxIter = 200; vpMbKltTracker::setMaxIter(30); + + angleAppears = vpMath::rad(65); + angleDisappears = vpMath::rad(75); #ifdef VISP_HAVE_OGRE - vpMbKltTracker::faces.getOgreContext()->setWindowName("MBT Hybrid"); + faces.getOgreContext()->setWindowName("MBT Hybrid"); #endif } @@ -79,18 +83,7 @@ vpMbEdgeKltTracker::init(const vpImage<unsigned char>& I) vpMbKltTracker::init(I); initPyramid(I, Ipyramid); - - unsigned int n = 0; - for(unsigned int i = 0; i < vpMbKltTracker::faces.size() ; i++){ - if(vpMbKltTracker::faces[i]->isVisible()){ - vpMbEdgeTracker::faces[i]->isvisible = true; - n++; - } - else - vpMbEdgeTracker::faces[i]->isvisible = false; - } - vpMbEdgeTracker::nbvisiblepolygone = n; - + unsigned int i = (unsigned int)scales.size(); do { i--; @@ -116,32 +109,44 @@ vpMbEdgeKltTracker::init(const vpImage<unsigned char>& I) void vpMbEdgeKltTracker::setPose( const vpImage<unsigned char> &I, const vpHomogeneousMatrix& cdMo) { - if(firstTrack){ vpMbKltTracker::setPose(I, cdMo); - vpMbtDistanceLine *l; - lines[scaleLevel].front() ; - for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - if(l->meline != NULL){ - delete l->meline; - l->meline = NULL; + if (! lines[scaleLevel].empty()) { + lines[scaleLevel].front() ; + for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ + if((*it)->meline != NULL){ + delete (*it)->meline; + (*it)->meline = NULL; + } } } - initPyramid(I, Ipyramid); - - unsigned int n = 0; - for(unsigned int i = 0; i < vpMbKltTracker::faces.size() ; i++){ - if(vpMbKltTracker::faces[i]->isVisible()){ - vpMbEdgeTracker::faces[i]->isvisible = true; - n++; + if (! cylinders[scaleLevel].empty()) { + cylinders[scaleLevel].front() ; + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ + if((*it)->meline1 != NULL){ + delete (*it)->meline1; + (*it)->meline1 = NULL; + } + if((*it)->meline2 != NULL){ + delete (*it)->meline2; + (*it)->meline2 = NULL; + } + } + } + + if (! circles[scaleLevel].empty()) { + circles[scaleLevel].front() ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + if((*it)->meEllipse != NULL){ + delete (*it)->meEllipse; + (*it)->meEllipse = NULL; } - else - vpMbEdgeTracker::faces[i]->isvisible = false; + } } - vpMbEdgeTracker::nbvisiblepolygone = n; - + + initPyramid(I, Ipyramid); + unsigned int i = (unsigned int)scales.size(); do { i--; @@ -153,7 +158,6 @@ vpMbEdgeKltTracker::setPose( const vpImage<unsigned char> &I, const vpHomogeneou } while(i != 0); cleanPyramid(Ipyramid); - } } /*! @@ -172,6 +176,7 @@ vpMbEdgeKltTracker::initMbtTracking(const unsigned int lvl) { vpMbtDistanceLine *l ; vpMbtDistanceCylinder *cy ; + vpMbtDistanceCircle *ci ; if(lvl >= scales.size() || !scales[lvl]){ throw vpException(vpException::dimensionError, "lvl not used."); @@ -189,7 +194,13 @@ vpMbEdgeKltTracker::initMbtTracking(const unsigned int lvl) nbrow += cy->nbFeature ; cy->initInteractionMatrixError() ; } - + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[lvl].begin(); it!=circles[lvl].end(); ++it){ + ci = *it; + nbrow += ci->nbFeature ; + ci->initInteractionMatrixError() ; + } + return nbrow; } @@ -278,27 +289,64 @@ void vpMbEdgeKltTracker::loadConfigFile(const char* configFile) { #ifdef VISP_HAVE_XML2 - vpMbEdgeTracker::loadConfigFile(configFile); - vpMbKltTracker::loadConfigFile(configFile); -#else - vpTRACE("You need the libXML2 to read the config file %s", configFile); -#endif -} + vpMbtEdgeKltXmlParser xmlp; + + xmlp.setCameraParameters(cam); + xmlp.setAngleAppear(vpMath::deg(angleAppears)); + xmlp.setAngleDisappear(vpMath::deg(angleDisappears)); + + xmlp.setMovingEdge(me); + + xmlp.setMaxFeatures(10000); + xmlp.setWindowSize(5); + xmlp.setQuality(0.01); + xmlp.setMinDistance(5); + xmlp.setHarrisParam(0.01); + xmlp.setBlockSize(3); + xmlp.setPyramidLevels(3); + xmlp.setMaskBorder(maskBorder); + + try{ + std::cout << " *********** Parsing XML for Mb Edge Tracker ************ " << std::endl; + xmlp.parse(configFile); + } + catch(...){ + vpERROR_TRACE("Can't open XML file \"%s\"\n ", configFile); + throw vpException(vpException::ioError, "problem to parse configuration file."); + } -/*! - Load a 3D model from the file in parameter. This file must either be a vrml - file (.wrl) or a CAO file (.cao). CAO format is described in the - loadCAOModel() method. + vpCameraParameters camera; + xmlp.getCameraParameters(camera); + setCameraParameters(camera); - \throw vpException::ioError if the file cannot be open, or if its extension is - not wrl or cao. + angleAppears = vpMath::rad(xmlp.getAngleAppear()); + angleDisappears = vpMath::rad(xmlp.getAngleDisappear()); - \param modelFile : the file containing the model. -*/ -void -vpMbEdgeKltTracker::loadModel(const std::string& modelFile) -{ - vpMbTracker::loadModel(modelFile); + if(xmlp.hasNearClippingDistance()) + setNearClippingDistance(xmlp.getNearClippingDistance()); + + if(xmlp.hasFarClippingDistance()) + setFarClippingDistance(xmlp.getFarClippingDistance()); + + if(xmlp.getFovClipping()){ + setClipping(vpMbEdgeTracker::clippingFlag | vpMbtPolygon::FOV_CLIPPING); + } + + vpMe meParser; + xmlp.getMe(meParser); + vpMbEdgeTracker::setMovingEdge(meParser); + + tracker.setMaxFeatures((int)xmlp.getMaxFeatures()); + tracker.setWindowSize((int)xmlp.getWindowSize()); + tracker.setQuality(xmlp.getQuality()); + tracker.setMinDistance(xmlp.getMinDistance()); + tracker.setHarrisFreeParameter(xmlp.getHarrisParam()); + tracker.setBlockSize((int)xmlp.getBlockSize()); + tracker.setPyramidLevels((int)xmlp.getPyramidLevels()); + maskBorder = xmlp.getMaskBorder(); +#else + vpTRACE("You need the libXML2 to read the config file %s", configFile); +#endif } /*! @@ -311,7 +359,7 @@ vpMbEdgeKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w bool reInit = vpMbKltTracker::postTracking(I, w_klt); postTrackingMbt(w_mbt,lvl); - + if (displayFeatures) { if(lvl == 0){ @@ -326,8 +374,17 @@ vpMbEdgeKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w vpMbtDistanceCylinder *cy ; for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[lvl].begin(); it!=cylinders[lvl].end(); ++it){ cy = *it; + // A cylinder is always visible cy->displayMovingEdges(I); } + + vpMbtDistanceCircle *ci ; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[lvl].begin(); it!=circles[lvl].end(); ++it){ + ci = *it; + if (ci->isVisible()){ + ci->displayMovingEdges(I); + } + } } } @@ -335,16 +392,6 @@ vpMbEdgeKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w return true; vpMbEdgeTracker::updateMovingEdge(I); - unsigned int n = 0; - for(unsigned int i = 0; i < vpMbKltTracker::faces.size() ; i++){ - if(vpMbKltTracker::faces[i]->isVisible()){ - vpMbEdgeTracker::faces[i]->isvisible = true; - n++; - } - else - vpMbEdgeTracker::faces[i]->isvisible = false; - } - vpMbEdgeTracker::nbvisiblepolygone = n; vpMbEdgeTracker::initMovingEdge(I, cMo) ; vpMbEdgeTracker::reinitMovingEdge(I, cMo); @@ -353,7 +400,7 @@ vpMbEdgeKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w } /*! - post tracking computation. Compute the mean weight of a line and, check the + Post tracking computation. Compute the mean weight of a line and, check the weight associated to a site (to eventually remove an outlier) and eventually set a flag to re-initialize the line. @@ -365,7 +412,6 @@ vpMbEdgeKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w void vpMbEdgeKltTracker::postTrackingMbt(vpColVector &w, const unsigned int lvl) { - if(lvl >= scales.size() || !scales[lvl]){ throw vpException(vpException::dimensionError, "_lvl not used."); } @@ -465,6 +511,44 @@ vpMbEdgeKltTracker::postTrackingMbt(vpColVector &w, const unsigned int lvl) n+= cy->nbFeature ; } + + // Same thing with circles as with lines + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + double wmean = 0 ; + std::list<vpMeSite>::iterator itListCir; + + if (ci->nbFeature > 0){ + itListCir = ci->meEllipse->getMeList().begin(); + } + + wmean = 0; + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + wmean += w[n+i] ; + vpMeSite p = *itListCir; + if (w[n+i] < 0.5){ + p.setState(vpMeSite::M_ESTIMATOR); + + *itListCir = p; + } + + ++itListCir; + } + + if (ci->nbFeature!=0) + wmean /= ci->nbFeature ; + else + wmean = 1; + + ci->setMeanWeight(wmean); + + if (wmean < 0.8){ + ci->Reinit = true; + } + + n+= ci->nbFeature ; + } } /*! @@ -493,30 +577,31 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i double residu_1 = -1; unsigned int iter = 0; - vpMatrix *J; - vpMatrix J_mbt, J_klt; // interaction matrix + vpMatrix *L; + vpMatrix L_mbt, L_klt; // interaction matrix vpColVector *R; vpColVector R_mbt, R_klt; // residu - vpMatrix J_true; - vpColVector R_true; + vpMatrix L_true; + vpMatrix LVJ_true; + //vpColVector R_true; vpColVector w_true; if(nbrow != 0){ - J_mbt.resize(nbrow,6); + L_mbt.resize(nbrow,6); R_mbt.resize(nbrow); } if(nbInfos != 0){ - J_klt.resize(2*nbInfos,6); + L_klt.resize(2*nbInfos,6); R_klt.resize(2*nbInfos); } - vpColVector w; // weight from MEstimator + //vpColVector w; // weight from MEstimator vpColVector v; // "speed" for VVS vpRobust robust_mbt(0), robust_klt(0); vpHomography H; - vpMatrix JTJ, JTR; + vpMatrix LTL, LTR; double factorMBT = 1.0; double factorKLT = 1.0; @@ -527,41 +612,55 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i factorMBT = 0.35; factorKLT = 0.65; + if (nbrow < 4) + factorKLT = 1.; + if (nbInfos < 4) + factorMBT = 1.; + double residuMBT = 0; double residuKLT = 0; + + vpHomogeneousMatrix cMoPrev; while( ((int)((residu - residu_1)*1e8) !=0 ) && (iter<maxIter) ){ - J = new vpMatrix(); + L = new vpMatrix(); R = new vpColVector(); if(nbrow >= 4) - trackSecondLoop(I,J_mbt,R_mbt,cMo,lvl); + trackSecondLoop(I,L_mbt,R_mbt,cMo,lvl); if(nbInfos >= 4){ unsigned int shift = 0; - for (unsigned int i = 0; i < vpMbKltTracker::faces.size(); i += 1){ - if(vpMbKltTracker::faces[i]->isVisible() && vpMbKltTracker::faces[i]->hasEnoughPoints()){ - vpSubColVector subR(R_klt, shift, 2*vpMbKltTracker::faces[i]->getNbPointsCur()); - vpSubMatrix subJ(J_klt, shift, 0, 2*vpMbKltTracker::faces[i]->getNbPointsCur(), 6); - vpMbKltTracker::faces[i]->computeHomography(ctTc0, H); - vpMbKltTracker::faces[i]->computeInteractionMatrixAndResidu(subR, subJ); - shift += 2*vpMbKltTracker::faces[i]->getNbPointsCur(); + vpMbtDistanceKltPoints *kltpoly; + // for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=vpMbKltTracker::kltPolygons.begin(); it!=vpMbKltTracker::kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->hasEnoughPoints()){ + vpSubColVector subR(R_klt, shift, 2*kltpoly->getCurrentNumberPoints()); + vpSubMatrix subL(L_klt, shift, 0, 2*kltpoly->getCurrentNumberPoints(), 6); + kltpoly->computeHomography(ctTc0, H); + kltpoly->computeInteractionMatrixAndResidu(subR, subL); + shift += 2*kltpoly->getCurrentNumberPoints(); } } } if(iter == 0){ - w.resize(nbrow + 2*nbInfos); - w=1; - - w_mbt.resize(nbrow); - w_mbt = 1; - robust_mbt.resize(nbrow); - - w_klt.resize(2*nbInfos); - w_klt = 1; - robust_klt.resize(2*nbInfos); - + m_w.resize(nbrow + 2*nbInfos); + m_w=1; + + if(nbrow != 0){ + w_mbt.resize(nbrow); + w_mbt = 1; + robust_mbt.resize(nbrow); + } + + if(nbInfos != 0){ + w_klt.resize(2*nbInfos); + w_klt = 1; + robust_klt.resize(2*nbInfos); + } + w_true.resize(nbrow + 2*nbInfos); } @@ -575,7 +674,7 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i robust_mbt.setIteration(iter); robust_mbt.setThreshold(thresholdMBT/cam.get_px()); robust_mbt.MEstimator( vpRobust::TUKEY, R_mbt, w_mbt); - J->stackMatrices(J_mbt); + L->stackMatrices(L_mbt); R->stackMatrices(R_mbt); } @@ -589,23 +688,28 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i robust_klt.setThreshold(thresholdKLT/cam.get_px()); robust_klt.MEstimator( vpRobust::TUKEY, R_klt, w_klt); - J->stackMatrices(J_klt); + L->stackMatrices(L_klt); R->stackMatrices(R_klt); } unsigned int cpt = 0; while(cpt< (nbrow+2*nbInfos)){ if(cpt<(unsigned)nbrow){ - w[cpt] = ((w_mbt[cpt] * factor[cpt]) * factorMBT) ; + m_w[cpt] = ((w_mbt[cpt] * factor[cpt]) * factorMBT) ; } else - w[cpt] = (w_klt[cpt-nbrow] * factorKLT); + m_w[cpt] = (w_klt[cpt-nbrow] * factorKLT); cpt++; } + m_error = (*R); if(computeCovariance){ - R_true = (*R); - J_true = (*J); + L_true = (*L); + if(!isoJoIdentity){ + vpVelocityTwistMatrix cVo; + cVo.buildFrom(cMo); + LVJ_true = ((*L)*cVo*oJo); + } } residu_1 = residu; @@ -613,36 +717,57 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i double num = 0; double den = 0; for (unsigned int i = 0; i < static_cast<unsigned int>(R->getRows()); i++){ - num += w[i]*vpMath::sqr((*R)[i]); - den += w[i]; + num += m_w[i]*vpMath::sqr((*R)[i]); + den += m_w[i]; - w_true[i] = w[i]*w[i]; - (*R)[i] *= w[i]; + w_true[i] = m_w[i]; + (*R)[i] *= m_w[i]; if(compute_interaction){ for (unsigned int j = 0; j < 6; j += 1){ - (*J)[i][j] *= w[i]; + (*L)[i][j] *= m_w[i]; } } } residu = sqrt(num/den); - JTJ = J->AtA(); - computeJTR(*J, *R, JTR); - v = -lambda * JTJ.pseudoInverse() * JTR; + if(isoJoIdentity){ + LTL = L->AtA(); + computeJTR(*L, *R, LTR); + v = -lambda * LTL.pseudoInverse() * LTR; + } + else{ + vpVelocityTwistMatrix cVo; + cVo.buildFrom(cMo); + vpMatrix LVJ = ((*L)*cVo*oJo); + vpMatrix LVJTLVJ = (LVJ).AtA(); + vpMatrix LVJTR; + computeJTR(LVJ, *R, LVJTR); + v = -lambda*LVJTLVJ.pseudoInverse(1e-16)*LVJTR; + v = cVo * v; + } + + cMoPrev = cMo; cMo = vpExponentialMap::direct(v).inverse() * cMo; ctTc0 = vpExponentialMap::direct(v).inverse() * ctTc0; iter++; - delete J; + delete L; delete R; } if(computeCovariance){ vpMatrix D; D.diag(w_true); - covarianceMatrix = vpMatrix::computeCovarianceMatrix(J_true,v,-lambda*R_true,D); + + // Note that here the covariance is computed on cMoPrev for time computation efficiency + if(isoJoIdentity){ + computeCovarianceMatrix(cMoPrev,m_error,L_true,D); + } + else{ + computeCovarianceMatrix(cMoPrev,m_error,LVJ_true,D); + } } } @@ -656,11 +781,14 @@ vpMbEdgeKltTracker::computeVVS(const vpImage<unsigned char>& I, const unsigned i void vpMbEdgeKltTracker::track(const vpImage<unsigned char>& I) { - unsigned int nbInfos; - unsigned int nbFaceUsed; + unsigned int nbInfos = 0; + unsigned int nbFaceUsed = 0; vpColVector w_klt; - - vpMbKltTracker::preTracking(I, nbInfos, nbFaceUsed); + + try{ + vpMbKltTracker::preTracking(I, nbInfos, nbFaceUsed); + } + catch(...){} if(nbInfos >= 4) vpMbKltTracker::computeVVS(nbInfos, w_klt); @@ -673,22 +801,11 @@ vpMbEdgeKltTracker::track(const vpImage<unsigned char>& I) vpColVector w_mbt; computeVVS(I, nbInfos, w_mbt, w_klt); - + if(postTracking(I, w_mbt, w_klt)){ vpMbKltTracker::reinit(I); initPyramid(I, Ipyramid); - - unsigned int n = 0; - for(unsigned int i = 0; i < vpMbKltTracker::faces.size() ; i++){ - if(vpMbKltTracker::faces[i]->isVisible()){ - vpMbEdgeTracker::faces[i]->isvisible = true; - n++; - } - else - vpMbEdgeTracker::faces[i]->isvisible = false; - } - vpMbEdgeTracker::nbvisiblepolygone = n; unsigned int i = (unsigned int)scales.size(); do { @@ -709,6 +826,7 @@ vpMbEdgeKltTracker::trackFirstLoop(const vpImage<unsigned char>& I, vpColVector { vpMbtDistanceLine *l ; vpMbtDistanceCylinder *cy ; + vpMbtDistanceCircle *ci ; if(lvl >= scales.size() || !scales[lvl]){ throw vpException(vpException::dimensionError, "_lvl not used."); @@ -731,8 +849,8 @@ vpMbEdgeKltTracker::trackFirstLoop(const vpImage<unsigned char>& I, vpColVector l->computeInteractionMatrixError(cMo); double fac = 1; - for(std::list<int>::const_iterator it = l->Lindex_polygon.begin(); it!=l->Lindex_polygon.end(); ++it){ - int index = *it; + for(std::list<int>::const_iterator itindex = l->Lindex_polygon.begin(); itindex!=l->Lindex_polygon.end(); ++itindex){ + int index = *itindex; if (l->hiddenface->isAppearing((unsigned int)index)) { fac = 0.2; break; @@ -784,21 +902,42 @@ vpMbEdgeKltTracker::trackFirstLoop(const vpImage<unsigned char>& I, vpColVector n+= cy->nbFeature ; } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->computeInteractionMatrixError(cMo); + double fac = 1.0; + + std::list<vpMeSite>::const_iterator itCir; + if (ci->meEllipse != NULL) { + itCir = ci->meEllipse->getMeList().begin(); + } + + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + factor[n+i] = fac; + vpMeSite site = *itCir; + if (site.getState() != vpMeSite::NO_SUPPRESSION) factor[n+i] = 0.2; + ++itCir; + } + + n+= ci->nbFeature ; + } return nbrow; } void vpMbEdgeKltTracker::trackSecondLoop(const vpImage<unsigned char>& I, vpMatrix &L, vpColVector &error, - vpHomogeneousMatrix& cMo, const unsigned int lvl) + vpHomogeneousMatrix& cMo_, const unsigned int lvl) { vpMbtDistanceLine* l; vpMbtDistanceCylinder *cy ; - + vpMbtDistanceCircle *ci ; + unsigned int n = 0 ; for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[lvl].begin(); it!=lines[lvl].end(); ++it){ l = *it; - l->computeInteractionMatrixError(cMo) ; + l->computeInteractionMatrixError(cMo_) ; for (unsigned int i=0 ; i < l->nbFeature ; i++){ for (unsigned int j=0; j < 6 ; j++){ L[n+i][j] = l->L[i][j]; @@ -810,27 +949,38 @@ vpMbEdgeKltTracker::trackSecondLoop(const vpImage<unsigned char>& I, vpMatrix & for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[lvl].begin(); it!=cylinders[lvl].end(); ++it){ cy = *it; - cy->computeInteractionMatrixError(cMo, I) ; + cy->computeInteractionMatrixError(cMo_, I) ; for(unsigned int i=0 ; i < cy->nbFeature ; i++){ for(unsigned int j=0; j < 6 ; j++){ L[n+i][j] = cy->L[i][j]; error[n+i] = cy->error[i]; } } - n+= cy->nbFeature ; } + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + ci = *it; + ci->computeInteractionMatrixError(cMo) ; + for(unsigned int i=0 ; i < ci->nbFeature ; i++){ + for(unsigned int j=0; j < 6 ; j++){ + L[n+i][j] = ci->L[i][j]; + error[n+i] = ci->error[i]; + } + } + + n+= ci->nbFeature ; + } } /*! Set the camera parameters - \param cam : the new camera parameters + \param camera : the new camera parameters */ void -vpMbEdgeKltTracker::setCameraParameters(const vpCameraParameters& cam) +vpMbEdgeKltTracker::setCameraParameters(const vpCameraParameters& camera) { - this->cam = cam; + this->cam = camera; vpMbEdgeTracker::setCameraParameters(cam); vpMbKltTracker::setCameraParameters(cam); @@ -839,71 +989,104 @@ vpMbEdgeKltTracker::setCameraParameters(const vpCameraParameters& cam) /*! Initialise a new face from the coordinates given in parameter. - \param corners : Coordinates of the corners of the face in the object frame. - \param indexFace : index of the face (depends on the vrml file organization). + \param polygon : The polygon describing the set of lines that has to be tracked. */ void -vpMbEdgeKltTracker::initFaceFromCorners(const std::vector<vpPoint>& corners, const unsigned int indexFace) +vpMbEdgeKltTracker::initFaceFromCorners(vpMbtPolygon &polygon) { - vpMbEdgeTracker::initFaceFromCorners(corners, indexFace); - vpMbKltTracker::initFaceFromCorners(corners, indexFace); + vpMbEdgeTracker::initFaceFromCorners(polygon); + vpMbKltTracker::initFaceFromCorners(polygon); +} +/*! + Initialise a new face from the coordinates given in parameter. + + \param polygon : The polygon describing the set of lines that has to be tracked. +*/ +void +vpMbEdgeKltTracker::initFaceFromLines(vpMbtPolygon &polygon) +{ + vpMbEdgeTracker::initFaceFromLines(polygon); + vpMbKltTracker::initFaceFromLines(polygon); +} + +/*! + Add a circle to track from its center, 3 points (including the center) defining the plane that contain + the circle and its radius. + + \param p1 : Center of the circle. + \param p2,p3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param radius : Radius of the circle. + \param idFace : Id of the face associated to the circle. + \param name : The optional name of the circle. +*/ +void +vpMbEdgeKltTracker::initCircle(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int idFace, const std::string &name) +{ + vpMbEdgeTracker::initCircle(p1, p2, p3, radius, idFace, name); } /*! Add a cylinder to track from tow points on the axis (defining the length of the cylinder) and its radius. - \param _p1 : First point on the axis. - \param _p2 : Second point on the axis. - \param _radius : Radius of the cylinder. - \param _indexCylinder : Index of the cylinder. + \param p1 : First point on the axis. + \param p2 : Second point on the axis. + \param radius : Radius of the cylinder. + \param idFace : Id of the face associated to the cylinder. + \param name : The optional name of the cylinder. */ -void -vpMbEdgeKltTracker::initCylinder(const vpPoint& _p1, const vpPoint _p2, const double _radius, const unsigned int _indexCylinder) +void +vpMbEdgeKltTracker::initCylinder(const vpPoint& p1, const vpPoint &p2, const double radius, const int idFace, + const std::string &name) { - vpMbEdgeTracker::initCylinder(_p1, _p2, _radius, _indexCylinder); + vpMbEdgeTracker::initCylinder(p1, p2, radius, idFace, name); } /*! Display the 3D model at a given position using the given camera parameters \param I : The image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : boolean to say if all the model has to be displayed. + \param displayFullModel : boolean to say if all the model has to be displayed, even the faces that are not visible. */ void -vpMbEdgeKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters & cam, - const vpColor& col , const unsigned int thickness, const bool displayFullModel) +vpMbEdgeKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo_, const vpCameraParameters &camera, + const vpColor& col, const unsigned int thickness, const bool displayFullModel) { - vpMbtDistanceLine *l ; - for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - l->display(I,cMo, cam, col, thickness, displayFullModel); + (*it)->display(I,cMo_, camera, col, thickness, displayFullModel); } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ - (*it)->display(I, cMo, cam, col, thickness); + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); } break ; //displaying model on one scale only } } - for (unsigned int i = 0; i < vpMbKltTracker::faces.size(); i += 1){ - if(displayFeatures && vpMbKltTracker::faces[i]->hasEnoughPoints() && vpMbKltTracker::faces[i]->isVisible()) { - vpMbKltTracker::faces[i]->displayPrimitive(I); + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(displayFeatures && kltpoly->hasEnoughPoints() && kltpoly->polygon->isVisible()) { + kltpoly->displayPrimitive(I); } } #ifdef VISP_HAVE_OGRE - if(vpMbKltTracker::useOgre) - vpMbKltTracker::faces.displayOgre(cMo); + if(useOgre) + faces.displayOgre(cMo_); #endif } @@ -911,43 +1094,79 @@ vpMbEdgeKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneous Display the 3D model at a given position using the given camera parameters \param I : The color image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : boolean to say if all the model has to be displayed. + \param displayFullModel : boolean to say if all the model has to be displayed, even the faces that are not visible. */ void -vpMbEdgeKltTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters & cam, +vpMbEdgeKltTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo_, const vpCameraParameters &camera, const vpColor& col , const unsigned int thickness, const bool displayFullModel) -{ - vpMbtDistanceLine *l ; - +{ for (unsigned int i = 0; i < scales.size(); i += 1){ if(scales[i]){ for(std::list<vpMbtDistanceLine*>::const_iterator it=lines[scaleLevel].begin(); it!=lines[scaleLevel].end(); ++it){ - l = *it; - l->display(I,cMo, cam, col, thickness, displayFullModel); + (*it)->display(I,cMo_, camera, col, thickness, displayFullModel); } for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders[scaleLevel].begin(); it!=cylinders[scaleLevel].end(); ++it){ - (*it)->display(I, cMo, cam, col, thickness); + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles[scaleLevel].begin(); it!=circles[scaleLevel].end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness, displayFullModel); } break ; //displaying model on one scale only } } - for (unsigned int i = 0; i < vpMbKltTracker::faces.size(); i += 1){ - if(displayFeatures && vpMbKltTracker::faces[i]->hasEnoughPoints() && vpMbKltTracker::faces[i]->isVisible()) { - vpMbKltTracker::faces[i]->displayPrimitive(I); + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(displayFeatures && kltpoly->hasEnoughPoints() && kltpoly->polygon->isVisible()) { + kltpoly->displayPrimitive(I); } } #ifdef VISP_HAVE_OGRE - if(vpMbKltTracker::useOgre) - vpMbKltTracker::faces.displayOgre(cMo); + if(useOgre) + faces.displayOgre(cMo_); #endif } +/*! + Re-initialize the model used by the tracker. + + \param I : The image containing the object to initialize. + \param cad_name : Path to the file containing the 3D model description. + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbEdgeKltTracker::reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) +{ + reInitModel(I, cad_name.c_str(), cMo_, verbose); +} + +/*! + Re-initialize the model used by the tracker. + + \param I : The image containing the object to initialize. + \param cad_name : Path to the file containing the 3D model description. + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbEdgeKltTracker::reInitModel(const vpImage<unsigned char>& I, const char* cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) +{ + vpMbKltTracker::reInitModel(I, cad_name, cMo_, verbose); + vpMbEdgeTracker::reInitModel(I, cad_name, cMo_, verbose); +} + #endif //VISP_HAVE_OPENCV diff --git a/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.h b/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.h index fe6830fcfa029e71373ca9fb8907c35d80f63edb..8d694dacca84c27e7adc161828977f26c300e561 100644 --- a/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.h +++ b/src/tracking/mbt/hybrid/vpMbEdgeKltTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbEdgeKltTracker.h 4338 2013-07-23 14:29:30Z fspindle $ + * $Id: vpMbEdgeKltTracker.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,17 +49,17 @@ #include <visp/vpConfig.h> -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) #include <visp/vpRobust.h> #include <visp/vpSubMatrix.h> #include <visp/vpSubColVector.h> #include <visp/vpExponentialMap.h> -#include <visp/vpMbtXmlParser.h> #include <visp/vpMbTracker.h> #include <visp/vpKltOpencv.h> #include <visp/vpMbEdgeTracker.h> #include <visp/vpPoseVector.h> +#include <visp/vpMbtEdgeKltXmlParser.h> #include <visp/vpMbKltTracker.h> /*! @@ -246,31 +246,6 @@ public: const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false); virtual void display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false); - - /*! Return the angle used to test polygons appearance. */ - virtual inline double getAngleAppear() const { return vpMbKltTracker::getAngleAppear(); } - - /*! Return the angle used to test polygons disappearance. */ - virtual inline double getAngleDisappear() const { return vpMbKltTracker::getAngleDisappear(); } - - /*! - Get the clipping used. - - \sa vpMbtPolygonClipping - - \return Clipping flags. - */ - virtual inline unsigned int getClipping() const { return vpMbKltTracker::clippingFlag; } - - /*! Return a reference to the faces structure. */ - inline vpMbHiddenFaces<vpMbtKltPolygon>& getFaces() { return vpMbKltTracker::faces;} - - /*! - Get the far distance for clipping. - - \return Far clipping value. - */ - virtual inline double getFarClippingDistance() const { return vpMbKltTracker::getFarClippingDistance(); } /*! Get the value of the gain used to compute the control law. @@ -295,31 +270,12 @@ public: void loadConfigFile(const char* configFile); virtual void loadConfigFile(const std::string& configFile); - virtual void loadModel(const std::string& modelFile); + void reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, const vpHomogeneousMatrix& cMo_, + const bool verbose=false); + void reInitModel(const vpImage<unsigned char>& I, const char* cad_name, const vpHomogeneousMatrix& cMo, + const bool verbose=false); void resetTracker(); - - /*! - Set the angle used to test polygons appearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value lower than - this parameter, the polygon is considered as appearing. - The polygon will then be tracked. - - \param a : new angle in radian. - */ - virtual inline void setAngleAppear(const double &a) { vpMbKltTracker::setAngleAppear(a); } - - /*! - Set the angle used to test polygons disappearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value greater than - this parameter, the polygon is considered as disappearing. - The tracking of the polygon will then be stopped. - - \param a : new angle in radian. - */ - virtual inline void setAngleDisappear(const double &a) { vpMbKltTracker::setAngleDisappear(a); } virtual void setCameraParameters(const vpCameraParameters& cam); @@ -330,21 +286,21 @@ public: \param flags : New clipping flags. */ - virtual void setClipping(const unsigned int &flags) {vpMbEdgeTracker::setClipping(flags); vpMbKltTracker::setClipping(flags);} + virtual void setClipping(const unsigned int &flags) {vpMbEdgeTracker::setClipping(flags); } /*! Set the far distance for clipping. \param dist : Far clipping value. */ - virtual void setFarClippingDistance(const double &dist) { vpMbEdgeTracker::setFarClippingDistance(dist); vpMbKltTracker::setFarClippingDistance(dist); } + virtual void setFarClippingDistance(const double &dist) { vpMbEdgeTracker::setFarClippingDistance(dist); } /*! Set the value of the gain used to compute the control law. - \param lambda : the desired value for the gain. + \param gain : the desired value for the gain. */ - virtual inline void setLambda(const double lambda) {this->lambda = lambda; vpMbEdgeTracker::setLambda(lambda); vpMbKltTracker::setLambda(lambda);} + virtual inline void setLambda(const double gain) {this->lambda = gain; vpMbEdgeTracker::setLambda(lambda); vpMbKltTracker::setLambda(lambda);} /*! Set the maximum iteration of the virtual visual servoing stage. @@ -358,17 +314,22 @@ public: \param dist : Near clipping value. */ - virtual void setNearClippingDistance(const double &dist) { vpMbEdgeTracker::setNearClippingDistance(dist); vpMbKltTracker::setNearClippingDistance(dist); } - + virtual void setNearClippingDistance(const double &dist) { vpMbEdgeTracker::setNearClippingDistance(dist); } + /*! Use Ogre3D for visibility tests - + \warning This function has to be called before the initialization of the tracker. - + \param v : True to use it, False otherwise */ - virtual inline void setOgreVisibilityTest(const bool &v) { vpMbKltTracker::setOgreVisibilityTest(v); } - + virtual void setOgreVisibilityTest(const bool &v){ + vpMbTracker::setOgreVisibilityTest(v); +#ifdef VISP_HAVE_OGRE + faces.getOgreContext()->setWindowName("MBT Hybrid"); +#endif + } + virtual void setPose(const vpImage<unsigned char> &I, const vpHomogeneousMatrix& cdMo); virtual void testTracking(){}; @@ -379,8 +340,12 @@ protected: vpColVector &w_klt, const unsigned int lvl=0); virtual void init(const vpImage<unsigned char>& I); - virtual void initCylinder(const vpPoint& , const vpPoint , const double , const unsigned int ); - virtual void initFaceFromCorners(const std::vector<vpPoint>& corners, const unsigned int indexFace = -1); + virtual void initCircle(const vpPoint&, const vpPoint &, const vpPoint &, const double r, const int idFace=0, + const std::string &name=""); + virtual void initCylinder(const vpPoint&, const vpPoint &, const double r, const int idFace, + const std::string &name=""); + virtual void initFaceFromCorners(vpMbtPolygon &polygon); + virtual void initFaceFromLines(vpMbtPolygon &polygon); unsigned int initMbtTracking(const unsigned int level=0); bool postTracking(const vpImage<unsigned char>& I, vpColVector &w_mbt, vpColVector &w_klt, diff --git a/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.cpp b/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a61e627c233427cf522eb705f54f840955f45496 --- /dev/null +++ b/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** + * + * $Id: vpMbtEdgeKltXmlParser.cpp 4577 2014-01-10 16:19:41Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Load XML parameters of the Model based tracker (using edges and point features). + * + * Authors: + * Aurelien Yol + * + *****************************************************************************/ +#include <visp/vpConfig.h> + + +#ifdef VISP_HAVE_XML2 + +#include <iostream> +#include <map> + +#include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ + +#include <visp/vpMbtEdgeKltXmlParser.h> + + +/*! + Default constructor. + +*/ +vpMbtEdgeKltXmlParser::vpMbtEdgeKltXmlParser() +{ + init(); +} + +/*! + Default destructor. +*/ +vpMbtEdgeKltXmlParser::~vpMbtEdgeKltXmlParser() +{ +} + +/*! + Initialise internal variables (including the map). +*/ +void +vpMbtEdgeKltXmlParser::init() +{ + vpMbtXmlParser::init(); + vpMbtKltXmlParser::init(); + + nodeMap["camera"] = vpMbtEdgeKltXmlParser::camera; + nodeMap["face"] = vpMbtEdgeKltXmlParser::face; + nodeMap["klt"] = vpMbtEdgeKltXmlParser::klt; + nodeMap["ecm"] = vpMbtEdgeKltXmlParser::ecm; + nodeMap["sample"] = vpMbtEdgeKltXmlParser::sample; +} + +/*! + Parse the file in parameters. + This method is deprecated, use parse() instead. + + \param filename : File to parse. +*/ +void +vpMbtEdgeKltXmlParser::parse(const char * filename) +{ + std::string file = filename; + vpXmlParser::parse(file); +} + +/*! + Write info to file. + + \warning Useless, so not yet implemented => Throw exception. +*/ +void +vpMbtEdgeKltXmlParser::writeMainClass(xmlNodePtr /*node*/) +{ + throw vpException(vpException::notImplementedError, "Not yet implemented." ); +} + +/*! + Read the parameters of the class from the file given by its document pointer + and by its root node. + + \param doc : Document to parse. + \param node : Root node. +*/ +void +vpMbtEdgeKltXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) +{ + bool camera_node = false; + bool face_node = false; + bool ecm_node = false; + bool sample_node = false; + bool klt_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case vpMbtEdgeKltXmlParser::camera:{ + this->read_camera (doc, dataNode); + camera_node = true; + }break; + case vpMbtEdgeKltXmlParser::face:{ + this->read_face(doc, dataNode); + face_node = true; + }break; + case vpMbtEdgeKltXmlParser::klt:{ + this->read_klt(doc, dataNode); + klt_node = true; + }break; + case vpMbtEdgeKltXmlParser::ecm:{ + this->read_ecm (doc, dataNode); + ecm_node = true; + }break; + case vpMbtEdgeKltXmlParser::sample:{ + this->read_sample (doc, dataNode); + sample_node = true; + }break; + default:{ +// vpTRACE("unknown tag in read_sample : %d, %s", iter_data->second, (iter_data->first).c_str()); + }break; + } + } + } + } + + if(!camera_node) { + std::cout << "camera : u0 : "<< this->cam.get_u0() << " (default)" <<std::endl; + std::cout << "camera : v0 : "<< this->cam.get_v0() << " (default)" <<std::endl; + std::cout << "camera : px : "<< this->cam.get_px() << " (default)" <<std::endl; + std::cout << "camera : py : "<< this->cam.get_py() << " (default)" <<std::endl; + } + + if(!face_node) { + std::cout << "face : Angle Appear : "<< angleAppear <<" (default)" <<std::endl; + std::cout << "face : Angle Disappear : "<< angleDisappear <<" (default)" <<std::endl; + } + + if(!klt_node) { + std::cout << "klt : Mask Border : "<< maskBorder <<" (default)" <<std::endl; + std::cout << "klt : Max Features : "<< maxFeatures <<" (default)" <<std::endl; + std::cout << "klt : Windows Size : "<< winSize <<" (default)" <<std::endl; + std::cout << "klt : Quality : "<< qualityValue <<" (default)" <<std::endl; + std::cout << "klt : Min Distance : "<< minDist <<" (default)" <<std::endl; + std::cout << "klt : Harris Parameter : "<< harrisParam <<" (default)" <<std::endl; + std::cout << "klt : Block Size : "<< blockSize <<" (default)" <<std::endl; + std::cout << "klt : Pyramid Levels : "<< pyramidLevels <<" (default)" <<std::endl; + } + + if(!ecm_node) { + std::cout << "ecm : mask : size : "<< this->m_ecm.getMaskSize() << " (default)" <<std::endl; + std::cout << "ecm : mask : nb_mask : "<< this->m_ecm.getMaskNumber() << " (default)" <<std::endl; + std::cout <<"ecm : range : tracking : "<< this->m_ecm.getRange()<< " (default)" <<std::endl; + std::cout <<"ecm : contrast : threshold : " << this->m_ecm.getThreshold()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu1 : " << this->m_ecm.getMu1()<<" (default)" <<std::endl; + std::cout <<"ecm : contrast : mu2 : " << this->m_ecm.getMu2()<<" (default)" <<std::endl; + } + + if(!sample_node) { + std::cout <<"sample : sample_step : "<< this->m_ecm.getSampleStep()<< " (default)" << std::endl; + std::cout <<"sample : n_total_sample : "<< this->m_ecm.getNbTotalSample()<< " (default)"<<std::endl; + } +} + +#endif + diff --git a/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.h b/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.h new file mode 100644 index 0000000000000000000000000000000000000000..06208bebd1ac4347522f080044dd45e36aa9d0cc --- /dev/null +++ b/src/tracking/mbt/hybrid/vpMbtEdgeKltXmlParser.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * + * $Id: vpMbtEdgeKltXmlParser.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Load XML parameters of the Model based tracker (using edges and point features). + * + * Authors: + * Aurelien Yol + * + *****************************************************************************/ + +/*! + * \file vpMbtEdgeKltXmlParser.h + * \brief Parse an Xml file to extract configuration parameters of a mbtConfig object. +*/ + +#ifndef vpMbtEdgeKltXmlParser_HH +#define vpMbtEdgeKltXmlParser_HH + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_XML2 + +#include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ + +#include <visp/vpMbtXmlParser.h> +#include <visp/vpMbtKltXmlParser.h> + +/*! + \class vpMbtEdgeKltXmlParser + \brief Parse an Xml file to extract configuration parameters of a mbtConfig object. + \ingroup ModelBasedTracking + + Data parser for the model based tracker. + + */ +class VISP_EXPORT vpMbtEdgeKltXmlParser: public vpMbtXmlParser, public vpMbtKltXmlParser +{ +protected: + typedef enum{ + camera, + face, + klt, + ecm, + sample + } dataToParseMbtEdgeKlt; +public: + + vpMbtEdgeKltXmlParser(); + virtual ~vpMbtEdgeKltXmlParser(); + + void parse(const char * filename); + + virtual void readMainClass(xmlDocPtr doc, xmlNodePtr node); + + void writeMainClass(xmlNodePtr node); + +protected: + void init(); + +}; + +#endif + +#endif /* NMBTEDGEKLTXMLPARSER_H_ */ + + + diff --git a/src/tracking/mbt/klt/vpMbKltTracker.cpp b/src/tracking/mbt/klt/vpMbKltTracker.cpp index 0dd96ef59f8caaeb3426420e33a7a949fbb26a93..10eb319430b6e9ff74319835deaeea5465e90845 100644 --- a/src/tracking/mbt/klt/vpMbKltTracker.cpp +++ b/src/tracking/mbt/klt/vpMbKltTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbKltTracker.cpp 4337 2013-07-23 13:57:53Z ayol $ + * $Id: vpMbKltTracker.cpp 5189 2015-01-21 16:42:18Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,22 +39,26 @@ * *****************************************************************************/ +#include <visp/vpImageConvert.h> #include <visp/vpMbKltTracker.h> +#include <visp/vpVelocityTwistMatrix.h> +#include <visp/vpTrackingException.h> -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) vpMbKltTracker::vpMbKltTracker() -{ - - cur = NULL; - compute_interaction = true; - firstInitialisation = true; - computeCovariance = false; - firstTrack = false; - + : +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cur(), +#else + cur(NULL), +#endif + c0Mo(), compute_interaction(true), + firstInitialisation(true), maskBorder(5), lambda(0.8), maxIter(200), threshold_outlier(0.5), + percentGood(0.6), ctTc0(), tracker(), firstTrack(false), kltPolygons(), cylinders_disp(), circles_disp() +{ tracker.setTrackerId(1); tracker.setUseHarris(1); - tracker.setMaxFeatures(10000); tracker.setWindowSize(5); tracker.setQuality(0.01); @@ -65,21 +69,10 @@ vpMbKltTracker::vpMbKltTracker() angleAppears = vpMath::rad(65); angleDisappears = vpMath::rad(75); - - clippingFlag = vpMbtPolygon::NO_CLIPPING; - - maskBorder = 5; - threshold_outlier = 0.5; - percentGood = 0.6; - - lambda = 0.8; - maxIter = 200; #ifdef VISP_HAVE_OGRE - faces.getOgreContext()->setWindowName("MBT KLT"); + faces.getOgreContext()->setWindowName("MBT Klt"); #endif - - useOgre = false; } /*! @@ -88,10 +81,45 @@ vpMbKltTracker::vpMbKltTracker() */ vpMbKltTracker::~vpMbKltTracker() { +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) if(cur != NULL){ cvReleaseImage(&cur); cur = NULL; } +#endif + + // delete the Klt Polygon features + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if (kltpoly!=NULL){ + delete kltpoly ; + } + kltpoly = NULL ; + } + kltPolygons.clear(); + + // delete the structures used to display cylinders and circles + vpMbtDistanceCylinder *cy; + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders_disp.begin(); it!=cylinders_disp.end(); ++it){ + cy = *it; + if (cy!=NULL){ + delete cy ; + } + cy = NULL ; + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles_disp.begin(); it!=circles_disp.end(); ++it){ + ci = *it; + if (ci!=NULL){ + delete ci ; + } + ci = NULL ; + } + + cylinders_disp.clear(); + circles_disp.clear(); } void @@ -117,53 +145,102 @@ vpMbKltTracker::init(const vpImage<unsigned char>& I) faces.setVisible(I, cam, cMo, angleAppears, angleDisappears, reInitialisation); #endif } - reinit(I); } void vpMbKltTracker::reinit(const vpImage<unsigned char>& I) -{ +{ c0Mo = cMo; ctTc0.setIdentity(); firstTrack = false; vpImageConvert::convert(I, cur); + + cam.computeFov(I.getWidth(), I.getHeight()); // mask +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat mask((int)I.getRows(), (int)I.getCols(), CV_8UC1, cv::Scalar(0)); +#else IplImage* mask = cvCreateImage(cvSize((int)I.getWidth(), (int)I.getHeight()), IPL_DEPTH_8U, 1); cvZero(mask); - - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible()) - faces[i]->updateMask(mask, 255/* - i*15*/, maskBorder); +#endif + unsigned char val = 255/* - i*15*/; + + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + kltpoly->polygon->computeRoiClipped(cam); + kltpoly->updateMask(mask, val, maskBorder); + } } tracker.initTracking(cur, mask); - - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible()){ - faces[i]->init(tracker); +// tracker.track(cur); // AY: Not sure to be usefull but makes sure that the points are valid for tracking and avoid too fast reinitialisations. +// vpCTRACE << "init klt. detected " << tracker.getNbFeatures() << " points" << std::endl; + + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + kltpoly->init(tracker); } } - +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) cvReleaseImage(&mask); +#endif } /*! Reset the tracker. The model is removed and the pose is set to identity. - The tracker needs to be initialized with a new model and a new pose. + The tracker needs to be initialized with a new model and a new pose. */ void vpMbKltTracker::resetTracker() { cMo.setIdentity(); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) if(cur != NULL){ cvReleaseImage(&cur); cur = NULL; } - +#endif + + // delete the Klt Polygon features + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if (kltpoly!=NULL){ + delete kltpoly ; + } + kltpoly = NULL ; + } + kltPolygons.clear(); + + // delete the structures used to display cylinders and circles + vpMbtDistanceCylinder *cy; + vpMbtDistanceCircle *ci; + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders_disp.begin(); it!=cylinders_disp.end(); ++it){ + cy = *it; + if (cy!=NULL){ + delete cy ; + } + cy = NULL ; + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles_disp.begin(); it!=circles_disp.end(); ++it){ + ci = *it; + if (ci!=NULL){ + delete ci ; + } + ci = NULL ; + } + + cylinders_disp.clear(); + circles_disp.clear(); + compute_interaction = true; firstInitialisation = true; computeCovariance = false; @@ -191,7 +268,7 @@ vpMbKltTracker::resetTracker() lambda = 0.8; maxIter = 200; - + faces.reset(); #ifdef VISP_HAVE_OGRE @@ -260,34 +337,22 @@ vpMbKltTracker::setKltOpencv(const vpKltOpencv& t){ /*! Set the camera parameters. - \param cam : the new camera parameters. + \param camera : the new camera parameters. */ void -vpMbKltTracker::setCameraParameters(const vpCameraParameters& cam) +vpMbKltTracker::setCameraParameters(const vpCameraParameters& camera) { - for (unsigned int i = 0; i < faces.size(); i += 1){ - faces[i]->setCameraParameters(cam); - } - this->cam = cam; -} - -/*! - Use Ogre3D for visibility tests - - \warning This function has to be called before the initialization of the tracker. - - \param v : True to use it, False otherwise -*/ -void -vpMbKltTracker::setOgreVisibilityTest(const bool &v) -{ - useOgre = v; - if(useOgre){ -#ifndef VISP_HAVE_OGRE - useOgre = false; - std::cout << "WARNING: ViSP doesn't have Ogre3D, basic visibility test will be used. setOgreVisibilityTest() set to false." << std::endl; -#endif +// for (unsigned int i = 0; i < faces.size(); i += 1){ +// faces[i]->setCameraParameters(camera); +// } + + vpMbtDistanceKltPoints *kltpoly; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + kltpoly->setCameraParameters(camera); } + + this->cam = camera; } /*! @@ -302,61 +367,82 @@ vpMbKltTracker::setOgreVisibilityTest(const bool &v) void vpMbKltTracker::setPose(const vpImage<unsigned char> &I, const vpHomogeneousMatrix& cdMo) { - if(firstTrack){ + if(firstTrack) + { bool reInitialisation = false; if(!useOgre) faces.setVisible(I, cam, cdMo, angleAppears, angleDisappears, reInitialisation); else{ - #ifdef VISP_HAVE_OGRE +#ifdef VISP_HAVE_OGRE faces.setVisibleOgre(I, cam, cdMo, angleAppears, angleDisappears, reInitialisation); - #else +#else faces.setVisible(I, cam, cdMo, angleAppears, angleDisappears, reInitialisation); - #endif +#endif } - if(reInitialisation){ std::cout << "WARNING: Visibility changed, must reinitialize to update pose" << std::endl; cMo = cdMo; reinit(I); } else{ + vpMbtDistanceKltPoints *kltpoly; + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + std::vector<cv::Point2f> initial_pts; + std::vector<long> initial_ids; +#else + unsigned int nbp = 0; + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it) { + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2 && kltpoly->hasEnoughPoints() ) + nbp += (*it)->getCurrentNumberPoints(); + } + + CvPoint2D32f* initial_pts = NULL; + initial_pts = (CvPoint2D32f*)cvAlloc(nbp*sizeof(initial_pts[0])); + long *initial_ids = new long [nbp]; + unsigned int iter_points = 0; +#endif vpHomogeneousMatrix cdMc = cdMo * cMo.inverse(); vpHomogeneousMatrix cMcd = cdMc.inverse(); - + vpRotationMatrix cdRc; vpTranslationVector cdtc; - + cdMc.extract(cdRc); cdMc.extract(cdtc); - - CvPoint2D32f* initial_guess = NULL; - initial_guess = (CvPoint2D32f*)cvAlloc((unsigned int)tracker.getMaxFeatures()*sizeof(initial_guess[0])); - - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible() && faces[i]->hasEnoughPoints()){ + + unsigned int nbCur = 0; + + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it) { + kltpoly = *it; + + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2 && kltpoly->hasEnoughPoints() ) { + //Get the normal to the face at the current state cMo - vpPlane plan(faces[i]->p[0], faces[i]->p[1], faces[i]->p[2]); + vpPlane plan(kltpoly->polygon->p[0], kltpoly->polygon->p[1], kltpoly->polygon->p[2]); plan.changeFrame(cMcd); - - vpColVector Nc = plan.getNormal(); + + vpColVector Nc = plan.getNormal(); Nc.normalize(); - + double invDc = 1.0 / plan.getD(); - + //Create the homography - vpHomography cdHc; + vpMatrix cdHc; vpGEMM(cdtc, Nc, -invDc, cdRc, 1.0, cdHc, VP_GEMM_B_T); cdHc /= cdHc[2][2]; - + //Create the 2D homography vpMatrix cdGc = cam.get_K() * cdHc * cam.get_K_inverse(); - + //Points displacement - std::map<int, vpImagePoint>::const_iterator iter = faces[i]->getCurrentPoints().begin(); - for( ; iter != faces[i]->getCurrentPoints().end(); iter++){ + std::map<int, vpImagePoint>::const_iterator iter = kltpoly->getCurrentPoints().begin(); + nbCur+= (unsigned int)kltpoly->getCurrentPoints().size(); + for( ; iter != kltpoly->getCurrentPoints().end(); iter++){ vpColVector cdp(3); cdp[0] = iter->second.get_j(); cdp[1] = iter->second.get_i(); cdp[2] = 1.0; - + double p_mu_t_2 = cdp[0] * cdGc[2][0] + cdp[1] * cdGc[2][1] + cdGc[2][2]; if( fabs(p_mu_t_2) < std::numeric_limits<double>::epsilon()){ @@ -367,117 +453,82 @@ vpMbKltTracker::setPose(const vpImage<unsigned char> &I, const vpHomogeneousMatr cdp[0] = (cdp[0] * cdGc[0][0] + cdp[1] * cdGc[0][1] + cdGc[0][2]) / p_mu_t_2; cdp[1] = (cdp[0] * cdGc[1][0] + cdp[1] * cdGc[1][1] + cdGc[1][2]) / p_mu_t_2; - + //Set value to the KLT tracker - initial_guess[(faces[i]->getCurrentPointsInd())[iter->first]].x = (float)cdp[0]; - initial_guess[(faces[i]->getCurrentPointsInd())[iter->first]].y = (float)cdp[1]; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Point2f p((float)cdp[0], (float)cdp[1]); + initial_pts.push_back(p); + initial_ids.push_back((size_t)(kltpoly->getCurrentPointsInd())[(size_t)iter->first]); +#else + initial_pts[iter_points].x = (float)cdp[0]; + initial_pts[iter_points].y = (float)cdp[1]; + initial_ids[iter_points++] = (kltpoly->getCurrentPointsInd())[(size_t)iter->first]; +#endif } } - } - - tracker.setInitialGuess(&initial_guess); - - if(initial_guess) cvFree(&initial_guess); - initial_guess = NULL; - + } + + vpImageConvert::convert(I, cur); + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + tracker.initTracking(cur,initial_pts,initial_ids); // false to keep the id of the points +#else + tracker.initTracking(cur, initial_pts, initial_ids, iter_points); + + if(initial_pts) cvFree(&initial_pts); + initial_pts = NULL; + delete [] initial_ids; +#endif + + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + kltpoly->polygon->computeRoiClipped(cam); + kltpoly->init(tracker); + } + } + cMo = cdMo; + c0Mo = cMo; + ctTc0.setIdentity(); + firstTrack = false; } } -} - -/*! - Set the far distance for clipping. - - \param dist : Far clipping value. -*/ -void -vpMbKltTracker::setFarClippingDistance(const double &dist) -{ - if( (clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING && dist <= distNearClip) - vpTRACE("Far clipping value cannot be inferior than near clipping value. Far clipping won't be considered."); - else if ( dist < 0 ) - vpTRACE("Far clipping value cannot be inferior than 0. Far clipping won't be considered."); - else{ - clippingFlag = (clippingFlag | vpMbtPolygon::FAR_CLIPPING); - distFarClip = dist; - for (unsigned int i = 0; i < faces.size(); i ++){ - faces[i]->setFarClippingDistance(distFarClip); - } - } -} - -/*! - Set the near distance for clipping. - - \param dist : Near clipping value. -*/ -void -vpMbKltTracker::setNearClippingDistance(const double &dist) -{ - if( (clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING && dist >= distFarClip) - vpTRACE("Near clipping value cannot be superior than far clipping value. Near clipping won't be considered."); - else if ( dist < 0 ) - vpTRACE("Near clipping value cannot be inferior than 0. Near clipping won't be considered."); else{ - clippingFlag = (clippingFlag | vpMbtPolygon::NEAR_CLIPPING); - distNearClip = dist; - for (unsigned int i = 0; i < faces.size(); i ++){ - faces[i]->setNearClippingDistance(distNearClip); - } + cMo = cdMo; + init(I); } } /*! - Specify which clipping to use. - - \sa vpMbtPolygonClipping - - \param flags : New clipping flags. + Initialise a new face from the coordinates given in parameter. + + \param polygon : The polygon describing the set of lines that has to be tracked. */ -void -vpMbKltTracker::setClipping(const unsigned int &flags) -{ - clippingFlag = flags; - for (unsigned int i = 0; i < faces.size(); i ++) - faces[i]->setClipping(clippingFlag); +void +vpMbKltTracker::initFaceFromCorners(vpMbtPolygon &polygon) +{ + vpMbtDistanceKltPoints *kltPoly = new vpMbtDistanceKltPoints(); + kltPoly->setCameraParameters(cam) ; + kltPoly->polygon = &polygon; + kltPolygons.push_back(kltPoly); } - /*! Initialise a new face from the coordinates given in parameter. - \param corners : Coordinates of the corners of the face in the object frame. - \param indexFace : index of the face (depends on the vrml file organization). + \param polygon : The polygon describing the set of lines that has to be tracked. */ void -vpMbKltTracker::initFaceFromCorners(const std::vector<vpPoint>& corners, const unsigned int indexFace) -{ - if( corners.size() > 2){ // This tracker can't handle lignes - vpMbtKltPolygon *polygon = new vpMbtKltPolygon; - // polygon->setCameraParameters(cam); - polygon->setNbPoint((unsigned int)corners.size()); - polygon->setIndex((int)indexFace); - for(unsigned int j = 0; j < corners.size(); j++) { - polygon->addPoint(j, corners[j]); - } - faces.addPolygon(polygon); - faces.getPolygon().back()->setCameraParameters(cam); - - if(clippingFlag != vpMbtPolygon::NO_CLIPPING) - faces.getPolygon().back()->setClipping(clippingFlag); - - if((clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) - faces.getPolygon().back()->setNearClippingDistance(distNearClip); - - if((clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) - faces.getPolygon().back()->setFarClippingDistance(distFarClip); - - delete polygon; - polygon = NULL; - } +vpMbKltTracker::initFaceFromLines(vpMbtPolygon &polygon) +{ + vpMbtDistanceKltPoints *kltPoly = new vpMbtDistanceKltPoints(); + kltPoly->setCameraParameters(cam) ; + kltPoly->polygon = &polygon; + kltPolygons.push_back(kltPoly); } /*! - Realize the pre tracking operations + Achieve the tracking of the KLT features and associate the features to the faces. \param I : The input image. \param nbInfos : Size of the features. @@ -488,22 +539,25 @@ vpMbKltTracker::preTracking(const vpImage<unsigned char>& I, unsigned int &nbInf { vpImageConvert::convert(I, cur); tracker.track(cur); - + if(!firstTrack) firstTrack = true; - nbInfos = 0; + nbInfos = 0; nbFaceUsed = 0; - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible()){ - faces[i]->computeNbDetectedCurrent(tracker); + vpMbtDistanceKltPoints *kltpoly; +// for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + kltpoly->computeNbDetectedCurrent(tracker); // faces[i]->ransac(); - if(faces[i]->hasEnoughPoints()){ - nbInfos += faces[i]->getNbPointsCur(); + if(kltpoly->hasEnoughPoints()){ + nbInfos += kltpoly->getCurrentNumberPoints(); nbFaceUsed++; } } - } + } } /*! @@ -519,15 +573,18 @@ vpMbKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w) unsigned int initialNumber = 0; unsigned int currentNumber = 0; unsigned int shift = 0; - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible()){ - initialNumber += faces[i]->getInitialNumberPoint(); - if(faces[i]->hasEnoughPoints()){ - vpSubColVector sub_w(w, shift, 2*faces[i]->getNbPointsCur()); - faces[i]->removeOutliers(sub_w, threshold_outlier); - shift += 2*faces[i]->getNbPointsCur(); + vpMbtDistanceKltPoints *kltpoly; +// for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + initialNumber += kltpoly->getInitialNumberPoint(); + if(kltpoly->hasEnoughPoints()){ + vpSubColVector sub_w(w, shift, 2*kltpoly->getCurrentNumberPoints()); + kltpoly->removeOutliers(sub_w, threshold_outlier); + shift += 2*kltpoly->getCurrentNumberPoints(); - currentNumber += faces[i]->getNbPointsCur(); + currentNumber += kltpoly->getCurrentNumberPoints(); } // else{ // reInitialisation = true; @@ -570,40 +627,45 @@ vpMbKltTracker::postTracking(const vpImage<unsigned char>& I, vpColVector &w) void vpMbKltTracker::computeVVS(const unsigned int &nbInfos, vpColVector &w) { - vpMatrix J; // interaction matrix + vpMatrix L; // interaction matrix vpColVector R; // residu - vpMatrix J_true; // interaction matrix - vpColVector R_true; // residu + vpMatrix L_true; // interaction matrix + vpMatrix LVJ_true; + //vpColVector R_true; // residu vpColVector v; // "speed" for VVS vpHomography H; vpColVector w_true; vpRobust robust(2*nbInfos); - vpMatrix JTJ, JTR; + vpMatrix LTL, LTR; + vpHomogeneousMatrix cMoPrev; double normRes = 0; double normRes_1 = -1; unsigned int iter = 0; R.resize(2*nbInfos); - J.resize(2*nbInfos, 6, 0); + L.resize(2*nbInfos, 6, 0); while( ((int)((normRes - normRes_1)*1e8) != 0 ) && (iter<maxIter) ){ unsigned int shift = 0; - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible() && faces[i]->hasEnoughPoints()){ - vpSubColVector subR(R, shift, 2*faces[i]->getNbPointsCur()); - vpSubMatrix subJ(J, shift, 0, 2*faces[i]->getNbPointsCur(), 6); + vpMbtDistanceKltPoints *kltpoly; + // for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2 && + kltpoly->hasEnoughPoints()){ + vpSubColVector subR(R, shift, 2*kltpoly->getCurrentNumberPoints()); + vpSubMatrix subL(L, shift, 0, 2*kltpoly->getCurrentNumberPoints(), 6); try{ - faces[i]->computeHomography(ctTc0, H); - faces[i]->computeInteractionMatrixAndResidu(subR, subJ); + kltpoly->computeHomography(ctTc0, H); + kltpoly->computeInteractionMatrixAndResidu(subR, subL); }catch(...){ - std::cerr << "exception while tracking face " << i << std::endl; - throw ; + throw vpTrackingException(vpTrackingException::fatalError, "Cannot compute interaction matrix"); } - shift += 2*faces[i]->getNbPointsCur(); + shift += 2*kltpoly->getCurrentNumberPoints(); } } @@ -612,20 +674,26 @@ vpMbKltTracker::computeVVS(const unsigned int &nbInfos, vpColVector &w) w_true.resize(2*nbInfos); w.resize(2*nbInfos); w = 1; + w_true = 1; } robust.setIteration(iter); robust.setThreshold(2/cam.get_px()); robust.MEstimator( vpRobust::TUKEY, R, w); + m_error = R; if(computeCovariance){ - R_true = R; - J_true = J; + L_true = L; + if(!isoJoIdentity){ + vpVelocityTwistMatrix cVo; + cVo.buildFrom(cMo); + LVJ_true = (L*cVo*oJo); + } } normRes_1 = normRes; normRes = 0; for (unsigned int i = 0; i < static_cast<unsigned int>(R.getRows()); i += 1){ - w_true = w[i] * w[i]; + w_true[i] = w[i]; R[i] = R[i] * w[i]; normRes += R[i]; } @@ -633,16 +701,30 @@ vpMbKltTracker::computeVVS(const unsigned int &nbInfos, vpColVector &w) if((iter == 0) || compute_interaction){ for(unsigned int i=0; i<static_cast<unsigned int>(R.getRows()); i++){ for(unsigned int j=0; j<6; j++){ - J[i][j] *= w[i]; + L[i][j] *= w[i]; } } } - - JTJ = J.AtA(); - computeJTR(J, R, JTR); - v = -lambda * JTJ.pseudoInverse(1e-16) * JTR; - + + if(isoJoIdentity){ + LTL = L.AtA(); + computeJTR(L, R, LTR); + v = -lambda * LTL.pseudoInverse(1e-16) * LTR; + } + else{ + vpVelocityTwistMatrix cVo; + cVo.buildFrom(cMo); + vpMatrix LVJ = (L*cVo*oJo); + vpMatrix LVJTLVJ = (LVJ).AtA(); + vpMatrix LVJTR; + computeJTR(LVJ, R, LVJTR); + v = -lambda*LVJTLVJ.pseudoInverse(1e-16)*LVJTR; + v = cVo * v; + } + ctTc0 = vpExponentialMap::direct(v).inverse() * ctTc0; + cMoPrev = cMo; + cMo = ctTc0 * c0Mo; iter++; } @@ -650,10 +732,15 @@ vpMbKltTracker::computeVVS(const unsigned int &nbInfos, vpColVector &w) if(computeCovariance){ vpMatrix D; D.diag(w_true); - covarianceMatrix = vpMatrix::computeCovarianceMatrix(J_true,v,-lambda*R_true,D); + + // Note that here the covariance is computed on cMoPrev for time computation efficiency + if(isoJoIdentity){ + computeCovarianceMatrix(cMoPrev,m_error,L_true,D); + } + else{ + computeCovarianceMatrix(cMoPrev,m_error,LVJ_true,D); + } } - - cMo = ctTc0 * c0Mo; } /*! @@ -666,19 +753,25 @@ vpMbKltTracker::computeVVS(const unsigned int &nbInfos, vpColVector &w) void vpMbKltTracker::track(const vpImage<unsigned char>& I) { - unsigned int nbInfos; - unsigned int nbFaceUsed; - preTracking(I, nbInfos, nbFaceUsed); + unsigned int nbInfos = 0; + unsigned int nbFaceUsed = 0; + + try{ + preTracking(I, nbInfos, nbFaceUsed); + } + catch(vpException &e){ + throw e; + } if(nbInfos < 4 || nbFaceUsed == 0){ vpERROR_TRACE("\n\t\t Error-> not enough data") ; throw vpTrackingException(vpTrackingException::notEnoughPointError, "\n\t\t Error-> not enough data"); } - vpColVector w; - computeVVS(nbInfos, w); + //vpColVector w; + computeVVS(nbInfos, m_w); - if(postTracking(I, w)) + if(postTracking(I, m_w)) reinit(I); } @@ -707,7 +800,7 @@ vpMbKltTracker::loadConfigFile(const std::string& configFile) vpXmlParser::cleanup() before the exit(). \throw vpException::ioError if the file has not been properly parsed (file not - found or wrong format for the data). + found or wrong format for the data). \param configFile : full name of the xml file. @@ -764,7 +857,6 @@ vpMbKltTracker::loadConfigFile(const char* configFile) try{ std::cout << " *********** Parsing XML for MBT KLT Tracker ************ " << std::endl; - xmlp.parse(configFile); } catch(...){ @@ -774,7 +866,7 @@ vpMbKltTracker::loadConfigFile(const char* configFile) vpCameraParameters camera; xmlp.getCameraParameters(camera); - setCameraParameters(camera); + setCameraParameters(camera); tracker.setMaxFeatures((int)xmlp.getMaxFeatures()); tracker.setWindowSize((int)xmlp.getWindowSize()); @@ -794,7 +886,8 @@ vpMbKltTracker::loadConfigFile(const char* configFile) setFarClippingDistance(xmlp.getFarClippingDistance()); if(xmlp.getFovClipping()) - clippingFlag = clippingFlag | vpMbtPolygon::FOV_CLIPPING; + setClipping(clippingFlag = clippingFlag | vpMbtPolygon::FOV_CLIPPING); + #else vpTRACE("You need the libXML2 to read the config file %s", configFile); #endif @@ -804,35 +897,38 @@ vpMbKltTracker::loadConfigFile(const char* configFile) Display the 3D model at a given position using the given camera parameters \param I : The image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : Boolean to say if all the model has to be displayed. + \param displayFullModel : Boolean to say if all the model has to be displayed, even the faces that are visible. */ void -vpMbKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters & cam, - const vpColor& col , const unsigned int thickness, const bool displayFullModel) +vpMbKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo_, const vpCameraParameters & camera, + const vpColor& col, const unsigned int thickness, const bool displayFullModel) { - vpCameraParameters c = cam; + vpCameraParameters c = camera; if(clippingFlag > 3) // Contains at least one FOV constraint c.computeFov(I.getWidth(), I.getHeight()); - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(displayFullModel || faces[i]->isVisible()) + vpMbtDistanceKltPoints *kltpoly; +// for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(displayFullModel || kltpoly->polygon->isVisible()) { - faces[i]->changeFrame(cMo); - faces[i]->computeRoiClipped(c); + kltpoly->polygon->changeFrame(cMo_); + kltpoly->polygon->computeRoiClipped(c); std::vector<std::pair<vpImagePoint,unsigned int> > roi; - faces[i]->getRoiClipped(c, roi); + kltpoly->polygon->getRoiClipped(c, roi); for (unsigned int j = 0; j < roi.size(); j += 1){ - if(((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::NEAR_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::FAR_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::DOWN_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::UP_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::LEFT_CLIPPING) == 0) && + if(((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::NEAR_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::FAR_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::DOWN_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::UP_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::LEFT_CLIPPING) == 0) && ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::RIGHT_CLIPPING) == 0)){ vpImagePoint ip1, ip2; ip1 = roi[j].first; @@ -842,15 +938,23 @@ vpMbKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatr } } } - if(displayFeatures && faces[i]->hasEnoughPoints() && faces[i]->isVisible()) { - faces[i]->displayPrimitive(I); + if(displayFeatures && kltpoly->hasEnoughPoints() && kltpoly->polygon->isVisible()) { + kltpoly->displayPrimitive(I); // faces[i]->displayNormal(I); } } + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders_disp.begin(); it!=cylinders_disp.end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness); + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles_disp.begin(); it!=circles_disp.end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness); + } + #ifdef VISP_HAVE_OGRE if(useOgre) - faces.displayOgre(cMo); + faces.displayOgre(cMo_); #endif } @@ -858,53 +962,64 @@ vpMbKltTracker::display(const vpImage<unsigned char>& I, const vpHomogeneousMatr Display the 3D model at a given position using the given camera parameters \param I : The color image. - \param cMo : Pose used to project the 3D model into the image. - \param cam : The camera parameters. + \param cMo_ : Pose used to project the 3D model into the image. + \param camera : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : Boolean to say if all the model has to be displayed. + \param displayFullModel : Boolean to say if all the model has to be displayed, even the faces that are not visible. */ void -vpMbKltTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters & cam, +vpMbKltTracker::display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo_, const vpCameraParameters & camera, const vpColor& col , const unsigned int thickness, const bool displayFullModel) { - vpCameraParameters c = cam; + vpCameraParameters c = camera; if(clippingFlag > 3) // Contains at least one FOV constraint c.computeFov(I.getWidth(), I.getHeight()); - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(displayFullModel || faces[i]->isVisible()) + vpMbtDistanceKltPoints *kltpoly; +// for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(displayFullModel || kltpoly->polygon->isVisible()) { - faces[i]->changeFrame(cMo); - faces[i]->computeRoiClipped(c); + kltpoly->polygon->changeFrame(cMo_); + kltpoly->polygon->computeRoiClipped(c); std::vector<std::pair<vpImagePoint,unsigned int> > roi; - faces[i]->getRoiClipped(c, roi); - + kltpoly->polygon->getRoiClipped(c, roi); + for (unsigned int j = 0; j < roi.size(); j += 1){ - if(((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::NEAR_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::FAR_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::DOWN_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::UP_CLIPPING) == 0) && - ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::LEFT_CLIPPING) == 0) && + if(((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::NEAR_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::FAR_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::DOWN_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::UP_CLIPPING) == 0) && + ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::LEFT_CLIPPING) == 0) && ((roi[(j+1)%roi.size()].second & roi[j].second & vpMbtPolygon::RIGHT_CLIPPING) == 0)){ vpImagePoint ip1, ip2; ip1 = roi[j].first; ip2 = roi[(j+1)%roi.size()].first; - + vpDisplay::displayLine (I, ip1, ip2, col, thickness); } } } - if(displayFeatures && faces[i]->hasEnoughPoints() && faces[i]->isVisible()) { - faces[i]->displayPrimitive(I); + if(displayFeatures && kltpoly->hasEnoughPoints() && kltpoly->polygon->isVisible()) { + kltpoly->displayPrimitive(I); // faces[i]->displayNormal(I); } } + for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders_disp.begin(); it!=cylinders_disp.end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness); + } + + for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles_disp.begin(); it!=circles_disp.end(); ++it){ + (*it)->display(I, cMo_, camera, col, thickness); + } + #ifdef VISP_HAVE_OGRE if(useOgre) - faces.displayOgre(cMo); + faces.displayOgre(cMo_); #endif } @@ -920,9 +1035,12 @@ void vpMbKltTracker::testTracking() { unsigned int nbTotalPoints = 0; - for (unsigned int i = 0; i < faces.size(); i += 1){ - if(faces[i]->isVisible()){ - nbTotalPoints += faces[i]->getNbPointsCur(); + vpMbtDistanceKltPoints *kltpoly; +// for (unsigned int i = 0; i < faces.size(); i += 1){ + for(std::list<vpMbtDistanceKltPoints*>::const_iterator it=kltPolygons.begin(); it!=kltPolygons.end(); ++it){ + kltpoly = *it; + if(kltpoly->polygon->isVisible() && kltpoly->polygon->getNbPoint() > 2){ + nbTotalPoints += kltpoly->getCurrentNumberPoints(); } } @@ -933,4 +1051,149 @@ vpMbKltTracker::testTracking() } } +/*! + Add a cylinder to display (not for tracking) from two points on the axis (defining the length of + the cylinder) and its radius. + + \param p1 : First point on the axis. + \param p2 : Second point on the axis. + \param radius : Radius of the cylinder. + \param name : The optional name of the cylinder. +*/ +void +vpMbKltTracker::initCylinder(const vpPoint& p1, const vpPoint &p2, const double radius, const int /*idFace*/, + const std::string &name) +{ + addCylinder(p1, p2, radius, name); +} + +/*! + Add a cylinder to the list of cylinders. + + \param P1 : The first extremity of the axis. + \param P2 : The second extremity of the axis. + \param r : The radius of the cylinder. + \param name : The optional name of the cylinder. +*/ +void +vpMbKltTracker::addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, const std::string &name) +{ + bool already_here = false ; + vpMbtDistanceCylinder *cy ; + +// for(std::list<vpMbtDistanceCylinder*>::const_iterator it=cylinders_disp.begin(); it!=cylinders_disp.end(); ++it){ +// cy = *it; +// if((samePoint(*(cy->p1),P1) && samePoint(*(cy->p2),P2)) || +// (samePoint(*(cy->p1),P2) && samePoint(*(cy->p2),P1)) ){ +// already_here = (std::fabs(cy->radius - r) < std::numeric_limits<double>::epsilon() * vpMath::maximum(cy->radius, r)); +// } +// } + + if (!already_here){ + cy = new vpMbtDistanceCylinder ; + + cy->setCameraParameters(cam); + cy->setName(name); + cy->buildFrom(P1, P2, r); + cylinders_disp.push_back(cy); + } +} + +/*! + Add a circle to display (not for tracking) from its center, 3 points (including the center) defining the plane that contain + the circle and its radius. + + \param p1 : Center of the circle. + \param p2,p3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param radius : Radius of the circle. + \param name : The optional name of the circle. +*/ +void +vpMbKltTracker::initCircle(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int /*idFace*/, const std::string &name) +{ + addCircle(p1, p2, p3, radius, name); +} + +/*! + Add a circle to the list of circles. + + \param P1 : Center of the circle. + \param P2,P3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param r : Radius of the circle. + \param name : Name of the circle. +*/ +void +vpMbKltTracker::addCircle(const vpPoint &P1, const vpPoint &P2, const vpPoint &P3, const double r, const std::string &name) +{ + bool already_here = false ; + vpMbtDistanceCircle *ci ; + +// for(std::list<vpMbtDistanceCircle*>::const_iterator it=circles_disp.begin(); it!=circles_disp[i].end(); ++it){ +// ci = *it; +// if((samePoint(*(ci->p1),P1) && samePoint(*(ci->p2),P2) && samePoint(*(ci->p3),P3)) || +// (samePoint(*(ci->p1),P1) && samePoint(*(ci->p2),P3) && samePoint(*(ci->p3),P2)) ){ +// already_here = (std::fabs(ci->radius - r) < std::numeric_limits<double>::epsilon() * vpMath::maximum(ci->radius, r)); +// } +// } + + if (!already_here){ + ci = new vpMbtDistanceCircle ; + + ci->setCameraParameters(cam); + ci->setName(name); + ci->buildFrom(P1, P2, P3, r); + circles_disp.push_back(ci); + } +} + +/*! + Re-initialize the model used by the tracker. + + \param I : The image containing the object to initialize. + \param cad_name : Path to the file containing the 3D model description. + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbKltTracker::reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) +{ + reInitModel(I, cad_name.c_str(), cMo_, verbose); +} + +/*! + Re-initialize the model used by the tracker. + + \param I : The image containing the object to initialize. + \param cad_name : Path to the file containing the 3D model description. + \param cMo_ : The new vpHomogeneousMatrix between the camera and the new model + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbKltTracker::reInitModel(const vpImage<unsigned char>& I, const char* cad_name, + const vpHomogeneousMatrix& cMo_, const bool verbose) +{ + this->cMo.setIdentity(); + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + if(cur != NULL){ + cvReleaseImage(&cur); + cur = NULL; + } +#endif + + firstInitialisation = true; + firstTrack = false; + + faces.reset(); + + loadModel(cad_name, verbose); + initFromPose(I, cMo_); +} + #endif //VISP_HAVE_OPENCV diff --git a/src/tracking/mbt/klt/vpMbKltTracker.h b/src/tracking/mbt/klt/vpMbKltTracker.h index 80060b8b93a54d14623ce25bee8ad221989bd7f0..4fdaa2b1344440c7afa99931886f2d26cff42cb5 100644 --- a/src/tracking/mbt/klt/vpMbKltTracker.h +++ b/src/tracking/mbt/klt/vpMbKltTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbKltTracker.h 4338 2013-07-23 14:29:30Z fspindle $ + * $Id: vpMbKltTracker.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,11 +48,10 @@ #include <visp/vpConfig.h> -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) #include <visp/vpMbTracker.h> #include <visp/vpKltOpencv.h> -#include <visp/vpMbtKltPolygon.h> #include <visp/vpMeterPixelConversion.h> #include <visp/vpPixelMeterConversion.h> #include <visp/vpDisplayX.h> @@ -62,7 +61,10 @@ #include <visp/vpSubColVector.h> #include <visp/vpSubMatrix.h> #include <visp/vpExponentialMap.h> -#include <visp/vpMbtKltPolygon.h> +//#include <visp/vpMbtKltPolygon.h> +#include <visp/vpMbtDistanceKltPoints.h> +#include <visp/vpMbtDistanceCircle.h> +#include <visp/vpMbtDistanceCylinder.h> /*! \class vpMbKltTracker @@ -228,13 +230,13 @@ class VISP_EXPORT vpMbKltTracker: virtual public vpMbTracker { protected: //! Temporary OpenCV image for fast conversion. - IplImage* cur; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat cur; +#else + IplImage *cur; +#endif //! Initial pose. vpHomogeneousMatrix c0Mo; - //! Angle used to detect a face appearance - double angleAppears; - //! Angle used to detect a face disappearance - double angleDisappears; //! If true, compute the interaction matrix at each iteration of the minimization. Otherwise, compute it only on the first iteration. bool compute_interaction; //! Flag to specify whether the init method is called the first or not (specific calls to realize in this case). @@ -249,28 +251,25 @@ protected: double threshold_outlier; //! Percentage of good points, according to the initial number, that must have the tracker. double percentGood; - //! Use Ogre3d for visibility tests - bool useOgre; //! The estimated displacement of the pose between the current instant and the initial position. vpHomogeneousMatrix ctTc0; //! Points tracker. vpKltOpencv tracker; - //! Set of faces describing the object. - vpMbHiddenFaces<vpMbtKltPolygon> faces; //! First track() called bool firstTrack; - //! Distance for near clipping - double distNearClip; - //! Distance for near clipping - double distFarClip; - //! Flags specifying which clipping to used - unsigned int clippingFlag; - + //! Vector of the cylinders used here only to display the full model. + std::list<vpMbtDistanceKltPoints*> kltPolygons; + //! Vector of the cylinders used here only to display the full model. + std::list<vpMbtDistanceCylinder*> cylinders_disp; + //! Vector of the circles used here only to display the full model. + std::list<vpMbtDistanceCircle*> circles_disp; + public: - vpMbKltTracker(); virtual ~vpMbKltTracker(); + void addCircle(const vpPoint &P1, const vpPoint &P2, const vpPoint &P3, const double r, const std::string &name=""); + void addCylinder(const vpPoint &P1, const vpPoint &P2, const double r, const std::string &name=""); virtual void display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor& col, const unsigned int thickness=1, const bool displayFullModel = false); @@ -282,40 +281,25 @@ protected: virtual void reinit(const vpImage<unsigned char>& I); public: + /*! Return the address of the circle feature list. */ + std::list<vpMbtDistanceCircle*> &getFeaturesCircle() { return circles_disp; } + /*! Return the address of the cylinder feature list. */ + std::list<vpMbtDistanceCylinder*> &getFeaturesCylinder() { return cylinders_disp; } + /*! Return the address of the Klt feature list. */ + std::list<vpMbtDistanceKltPoints*> &getFeaturesKlt() { return kltPolygons; } virtual void loadConfigFile(const std::string& configFile); void loadConfigFile(const char* configFile); - /*! Return the angle used to test polygons appearance. */ - virtual inline double getAngleAppear() const { return angleAppears; } - - /*! Return the angle used to test polygons disappearance. */ - virtual inline double getAngleDisappear() const { return angleDisappears; } - - /*! - Get the clipping used. - - \sa vpMbtPolygonClipping - - \return Clipping flags. - */ - virtual inline unsigned int getClipping() const { return clippingFlag; } - - /*! Return a reference to the faces structure. */ - inline vpMbHiddenFaces<vpMbtKltPolygon>& getFaces() { return faces;} - - /*! - Get the far distance for clipping. - - \return Far clipping value. - */ - virtual inline double getFarClippingDistance() const { return distFarClip; } - /*! Get the current list of KLT points. \return the list of KLT points through vpKltOpencv. */ - inline CvPoint2D32f* getKltPoints() {return tracker.getFeatures();} +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + inline std::vector<cv::Point2f> getKltPoints() {return tracker.getFeatures();} +#else + inline CvPoint2D32f* getKltPoints() {return tracker.getFeatures();} +#endif std::vector<vpImagePoint> getKltImagePoints() const; @@ -355,14 +339,7 @@ public: \return the number of features */ inline int getNbKltPoints() const {return tracker.getNbFeatures();} - - /*! - Get the near distance for clipping. - - \return Near clipping value. - */ - virtual inline double getNearClippingDistance() const { return distNearClip; } - + /*! Get the threshold for the acceptation of a point. @@ -370,44 +347,22 @@ public: */ inline double getThresholdAcceptation() const { return threshold_outlier;} + void reInitModel(const vpImage<unsigned char>& I, const std::string &cad_name, const vpHomogeneousMatrix& cMo_, + const bool verbose=false); + void reInitModel(const vpImage<unsigned char>& I, const char* cad_name, const vpHomogeneousMatrix& cMo, + const bool verbose=false); void resetTracker(); - - /*! - Set the angle used to test polygons appearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value lower than - this parameter, the polygon is considered as appearing. - The polygon will then be tracked. - \param a : new angle in radian. - */ - virtual inline void setAngleAppear(const double &a) { angleAppears = a; } - - /*! - Set the angle used to test polygons disappearance. - If the angle between the normal of the polygon and the line going - from the camera to the polygon center has a value greater than - this parameter, the polygon is considered as disappearing. - The tracking of the polygon will then be stopped. - - \param a : new angle in radian. - */ - virtual inline void setAngleDisappear(const double &a) { angleDisappears = a; } - void setCameraParameters(const vpCameraParameters& cam); - - virtual void setClipping(const unsigned int &flags); - - virtual void setFarClippingDistance(const double &dist); - + void setKltOpencv(const vpKltOpencv& t); /*! Set the value of the gain used to compute the control law. - \param lambda : the desired value for the gain. + \param gain : the desired value for the gain. */ - virtual inline void setLambda(const double lambda) {this->lambda = lambda;} + virtual inline void setLambda(const double gain) {this->lambda = gain;} /*! Set the erosion of the mask used on the Model faces. @@ -422,10 +377,20 @@ public: \param max : the desired number of iteration */ virtual inline void setMaxIter(const unsigned int max) {maxIter = max;} - - virtual void setNearClippingDistance(const double &dist); - - virtual void setOgreVisibilityTest(const bool &v); + + /*! + Use Ogre3D for visibility tests + + \warning This function has to be called before the initialization of the tracker. + + \param v : True to use it, False otherwise + */ + virtual void setOgreVisibilityTest(const bool &v){ + vpMbTracker::setOgreVisibilityTest(v); +#ifdef VISP_HAVE_OGRE + faces.getOgreContext()->setWindowName("MBT Klt"); +#endif + } virtual void setPose(const vpImage<unsigned char> &I, const vpHomogeneousMatrix& cdMo); @@ -442,9 +407,13 @@ public: protected: void computeVVS(const unsigned int &nbInfos, vpColVector &w); - virtual void initFaceFromCorners(const std::vector<vpPoint>& corners, const unsigned int indexFace = -1); - virtual void initCylinder(const vpPoint& , const vpPoint , const double , const unsigned int ){}; - + virtual void initFaceFromCorners(vpMbtPolygon &polygon); + virtual void initFaceFromLines(vpMbtPolygon &polygon); + virtual void initCircle(const vpPoint&, const vpPoint &, const vpPoint &, const double, const int, + const std::string &name=""); + virtual void initCylinder(const vpPoint&, const vpPoint &, const double, const int, + const std::string &name=""); + void preTracking(const vpImage<unsigned char>& I, unsigned int &nbInfos, unsigned int &nbFaceUsed); bool postTracking(const vpImage<unsigned char>& I, vpColVector &w); }; diff --git a/src/tracking/mbt/klt/vpMbtKltPolygon.cpp b/src/tracking/mbt/klt/vpMbtDistanceKltPoints.cpp similarity index 67% rename from src/tracking/mbt/klt/vpMbtKltPolygon.cpp rename to src/tracking/mbt/klt/vpMbtDistanceKltPoints.cpp index 9d02cabe2905b04f1c41add8af1361d1470d35f5..8d8a8cfc622197e49904a8d76ecf3e64fe2e0240 100644 --- a/src/tracking/mbt/klt/vpMbtKltPolygon.cpp +++ b/src/tracking/mbt/klt/vpMbtDistanceKltPoints.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtKltPolygon.cpp 4318 2013-07-17 09:47:51Z fspindle $ + * $Id: vpMbtDistanceKltPoints.cpp 4661 2014-02-10 19:34:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,7 +31,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: - * Generic model polygon, containing points of interest. + * Klt polygon, containing points of interest. * * Authors: * Romain Tallonneau @@ -39,34 +39,29 @@ * *****************************************************************************/ +#include <visp/vpMbtDistanceKltPoints.h> -#include <visp/vpMbtKltPolygon.h> - -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) /*! Basic constructor. */ -vpMbtKltPolygon::vpMbtKltPolygon() +vpMbtDistanceKltPoints::vpMbtDistanceKltPoints() + : H(), N(), N_cur(), invd0(1.), cRc0_0n(), initPoints(), curPoints(), curPointsInd(), + nbPointsCur(0), nbPointsInit(0), minNbPoint(4), enoughPoints(false), dt(1.), d0(1.), + cam(), polygon(NULL) { - minNbPoint = 4; - enoughPoints = false; - - nbPointsInit = 0; - nbPointsCur = 0; initPoints = std::map<int, vpImagePoint>(); curPoints = std::map<int, vpImagePoint>(); curPointsInd = std::map<int, int>(); - - isvisible = false; } /*! Basic destructor. */ -vpMbtKltPolygon::~vpMbtKltPolygon() +vpMbtDistanceKltPoints::~vpMbtDistanceKltPoints() {} /*! @@ -77,7 +72,7 @@ vpMbtKltPolygon::~vpMbtKltPolygon() \param _tracker : ViSP OpenCV KLT Tracker. */ void -vpMbtKltPolygon::init(const vpKltOpencv& _tracker) +vpMbtDistanceKltPoints::init(const vpKltOpencv& _tracker) { // extract ids of the points in the face nbPointsInit = 0; @@ -86,8 +81,8 @@ vpMbtKltPolygon::init(const vpKltOpencv& _tracker) curPoints = std::map<int, vpImagePoint>(); curPointsInd = std::map<int, int>(); std::vector<vpImagePoint> roi; - getRoiClipped(cam, roi); - + polygon->getRoiClipped(cam, roi); + for (unsigned int i = 0; i < static_cast<unsigned int>(_tracker.getNbFeatures()); i ++){ int id; float x_tmp, y_tmp; @@ -102,12 +97,15 @@ vpMbtKltPolygon::init(const vpKltOpencv& _tracker) } } + if(nbPointsCur >= minNbPoint) enoughPoints = true; + else enoughPoints = false; + // initialisation of the value for the computation in SE3 - vpPlane plan(p[0], p[1], p[2]); + vpPlane plan(polygon->getPoint(0), polygon->getPoint(1), polygon->getPoint(2)); d0 = plan.getD(); - N = plan.getNormal(); - + N = plan.getNormal(); + N.normalize(); N_cur = N; invd0 = 1.0 / d0; @@ -121,14 +119,14 @@ vpMbtKltPolygon::init(const vpKltOpencv& _tracker) \return the number of points that are tracked in this face and in this instanciation of the tracker */ unsigned int -vpMbtKltPolygon::computeNbDetectedCurrent(const vpKltOpencv& _tracker) +vpMbtDistanceKltPoints::computeNbDetectedCurrent(const vpKltOpencv& _tracker) { int id; float x, y; nbPointsCur = 0; curPoints = std::map<int, vpImagePoint>(); curPointsInd = std::map<int, int>(); - + for (unsigned int i = 0; i < static_cast<unsigned int>(_tracker.getNbFeatures()); i++){ _tracker.getFeature((int)i, id, x, y); if(isTrackedFeature(id)){ @@ -137,10 +135,10 @@ vpMbtKltPolygon::computeNbDetectedCurrent(const vpKltOpencv& _tracker) nbPointsCur++; } } - + if(nbPointsCur >= minNbPoint) enoughPoints = true; else enoughPoints = false; - + return nbPointsCur; } @@ -155,49 +153,49 @@ vpMbtKltPolygon::computeNbDetectedCurrent(const vpKltOpencv& _tracker) \param _J : the interaction matrix */ void -vpMbtKltPolygon::computeInteractionMatrixAndResidu(vpColVector& _R, vpMatrix& _J) +vpMbtDistanceKltPoints::computeInteractionMatrixAndResidu(vpColVector& _R, vpMatrix& _J) { - unsigned int index = 0; - + unsigned int index_ = 0; + std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin(); for( ; iter != curPoints.end(); iter++){ int id(iter->first); double i_cur(iter->second.get_i()), j_cur(iter->second.get_j()); - + double x_cur(0), y_cur(0); vpPixelMeterConversion::convertPoint(cam, j_cur, i_cur, x_cur, y_cur); - + vpImagePoint iP0 = initPoints[id]; double x0(0), y0(0); vpPixelMeterConversion::convertPoint(cam, iP0, x0, y0); - + double x0_transform, y0_transform ;// equivalent x and y in the first image (reference) computeP_mu_t(x0, y0, x0_transform, y0_transform, H ); double invZ = compute_1_over_Z(x_cur, y_cur); - - _J[2*index][0] = - invZ; - _J[2*index][1] = 0; - _J[2*index][2] = x_cur * invZ; - _J[2*index][3] = x_cur * y_cur; - _J[2*index][4] = -(1+x_cur*x_cur); - _J[2*index][5] = y_cur; - - _J[2*index+1][0] = 0; - _J[2*index+1][1] = - invZ; - _J[2*index+1][2] = y_cur * invZ; - _J[2*index+1][3] = (1+y_cur*y_cur); - _J[2*index+1][4] = - y_cur * x_cur; - _J[2*index+1][5] = - x_cur; - - _R[2*index] = (x0_transform - x_cur); - _R[2*index+1] = (y0_transform - y_cur); - index++; + + _J[2*index_][0] = - invZ; + _J[2*index_][1] = 0; + _J[2*index_][2] = x_cur * invZ; + _J[2*index_][3] = x_cur * y_cur; + _J[2*index_][4] = -(1+x_cur*x_cur); + _J[2*index_][5] = y_cur; + + _J[2*index_+1][0] = 0; + _J[2*index_+1][1] = - invZ; + _J[2*index_+1][2] = y_cur * invZ; + _J[2*index_+1][3] = (1+y_cur*y_cur); + _J[2*index_+1][4] = - y_cur * x_cur; + _J[2*index_+1][5] = - x_cur; + + _R[2*index_] = (x0_transform - x_cur); + _R[2*index_+1] = (y0_transform - y_cur); + index_++; } } double -vpMbtKltPolygon::compute_1_over_Z(const double x, const double y) +vpMbtDistanceKltPoints::compute_1_over_Z(const double x, const double y) { double num = cRc0_0n[0] * x + cRc0_0n[1] * y + cRc0_0n[2]; double den = -(d0 - dt); @@ -217,7 +215,7 @@ vpMbtKltPolygon::compute_1_over_Z(const double x, const double y) \param _cHc0 : the homography used to transfer the point */ inline void -vpMbtKltPolygon::computeP_mu_t(const double x_in, const double y_in, double& x_out, double& y_out, const vpMatrix& _cHc0) +vpMbtDistanceKltPoints::computeP_mu_t(const double x_in, const double y_in, double& x_out, double& y_out, const vpMatrix& _cHc0) { double p_mu_t_2 = x_in * _cHc0[2][0] + y_in * _cHc0[2][1] + _cHc0[2][2]; @@ -229,8 +227,8 @@ vpMbtKltPolygon::computeP_mu_t(const double x_in, const double y_in, double& x_o x_out = (x_in * _cHc0[0][0] + y_in * _cHc0[0][1] + _cHc0[0][2]) / p_mu_t_2; y_out = (x_in * _cHc0[1][0] + y_in * _cHc0[1][1] + _cHc0[1][2]) / p_mu_t_2; -} - +} + /*! compute the homography using a displacement matrix. @@ -244,33 +242,34 @@ vpMbtKltPolygon::computeP_mu_t(const double x_in, const double y_in, double& x_o \param _cHc0 : the homography of the plane */ void -vpMbtKltPolygon::computeHomography(const vpHomogeneousMatrix& _cTc0, vpHomography& _cHc0) +vpMbtDistanceKltPoints::computeHomography(const vpHomogeneousMatrix& _cTc0, vpHomography& _cHc0) { vpRotationMatrix cRc0; vpTranslationVector ctransc0; _cTc0.extract(cRc0); _cTc0.extract(ctransc0); - + vpMatrix cHc0(_cHc0); + // vpGEMM(cRc0, 1.0, invd0, cRc0, -1.0, _cHc0, VP_GEMM_A_T); - vpGEMM(ctransc0, N, -invd0, cRc0, 1.0, _cHc0, VP_GEMM_B_T); - _cHc0 /= _cHc0[2][2]; - - H = _cHc0; - + vpGEMM(ctransc0, N, -invd0, cRc0, 1.0, cHc0, VP_GEMM_B_T); + cHc0 /= cHc0[2][2]; + + H = cHc0; + // vpQuaternionVector NQuat(N[0], N[1], N[2], 0.0); // vpQuaternionVector RotQuat(cRc0); // vpQuaternionVector RotQuatConj(-RotQuat.x(), -RotQuat.y(), -RotQuat.z(), RotQuat.w()); // vpQuaternionVector partial = RotQuat * NQuat; // vpQuaternionVector resQuat = (partial * RotQuatConj); -// +// // cRc0_0n = vpColVector(3); // cRc0_0n[0] = resQuat.x(); // cRc0_0n[1] = resQuat.y(); // cRc0_0n[2] = resQuat.z(); - + cRc0_0n = cRc0*N; - + // vpPlane p(corners[0], corners[1], corners[2]); // vpColVector Ncur = p.getNormal(); // Ncur.normalize(); @@ -289,7 +288,7 @@ vpMbtKltPolygon::computeHomography(const vpHomogeneousMatrix& _cTc0, vpHomograph \return true if the id is in the list of tracked feature */ bool -vpMbtKltPolygon::isTrackedFeature(const int _id) +vpMbtDistanceKltPoints::isTrackedFeature(const int _id) { // std::map<int, vpImagePoint>::const_iterator iter = initPoints.begin(); // while(iter != initPoints.end()){ @@ -298,11 +297,11 @@ vpMbtKltPolygon::isTrackedFeature(const int _id) // } // iter++; // } - + std::map<int, vpImagePoint>::iterator iter = initPoints.find(_id); if(iter != initPoints.end()) return true; - + return false; } @@ -310,65 +309,98 @@ vpMbtKltPolygon::isTrackedFeature(const int _id) Modification of all the pixels that are in the roi to the value of _nb ( default is 255). - \param _mask : the mask to update (0, not in the object, _nb otherwise). - \param _nb : Optionnal value to set to the pixels included in the face. - \param _shiftBorder : Optionnal shift for the border in pixel (sort of built-in erosion) to avoid to consider pixels near the limits of the face. + \param mask : the mask to update (0, not in the object, _nb otherwise). + \param nb : Optionnal value to set to the pixels included in the face. + \param shiftBorder : Optionnal shift for the border in pixel (sort of built-in erosion) to avoid to consider pixels near the limits of the face. */ void -vpMbtKltPolygon::updateMask(IplImage* _mask, unsigned int _nb, unsigned int _shiftBorder) +vpMbtDistanceKltPoints::updateMask( +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat &mask, +#else + IplImage* mask, +#endif + unsigned char nb, unsigned int shiftBorder) { - int width = _mask->width; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + int width = mask.cols; + int height = mask.rows; +#else + int width = mask->width; + int height = mask->height; +#endif + int i_min, i_max, j_min, j_max; - cam.computeFov((unsigned)_mask->width, (unsigned)_mask->height); - computeRoiClipped(cam); std::vector<vpImagePoint> roi; - getRoiClipped(cam, roi); + polygon->getRoiClipped(cam, roi); vpMbtPolygon::getMinMaxRoi(roi, i_min, i_max, j_min,j_max); /* check image boundaries */ - if(i_min > _mask->height){ //underflow + if(i_min > height){ //underflow i_min = 0; } - if(i_max > _mask->height){ - i_max = _mask->height; + if(i_max > height){ + i_max = height; } - if(j_min > _mask->width){ //underflow + if(j_min > width){ //underflow j_min = 0; } - if(j_max > _mask->width){ - j_max = _mask->width; + if(j_max > width){ + j_max = width; + } + + double shiftBorder_d = (double) shiftBorder; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + for(int i=i_min; i< i_max; i++){ + double i_d = (double) i; + for(int j=j_min; j< j_max; j++){ + double j_d = (double) j; + if(shiftBorder != 0){ + if( vpMbtDistanceKltPoints::isInside(roi, i_d, j_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d+shiftBorder_d, j_d+shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d-shiftBorder_d, j_d+shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d+shiftBorder_d, j_d-shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d-shiftBorder_d, j_d-shiftBorder_d) ){ + mask.at<unsigned char>(i,j) = nb; + } + } + else{ + if(vpMbtDistanceKltPoints::isInside(roi, i, j)){ + mask.at<unsigned char>(i,j) = nb; + } + } + } } - - double shiftBorder_d = (double) _shiftBorder; - char* ptrData = _mask->imageData + i_min*width+j_min; +#else + unsigned char* ptrData = (unsigned char*)mask->imageData + i_min*mask->widthStep+j_min; for(int i=i_min; i< i_max; i++){ double i_d = (double) i; for(int j=j_min; j< j_max; j++){ double j_d = (double) j; - if(_shiftBorder != 0){ - if( vpMbtKltPolygon::isInside(roi, i_d, j_d) - && vpMbtKltPolygon::isInside(roi, i_d+shiftBorder_d, j_d+shiftBorder_d) - && vpMbtKltPolygon::isInside(roi, i_d-shiftBorder_d, j_d+shiftBorder_d) - && vpMbtKltPolygon::isInside(roi, i_d+shiftBorder_d, j_d-shiftBorder_d) - && vpMbtKltPolygon::isInside(roi, i_d-shiftBorder_d, j_d-shiftBorder_d) ){ - *(ptrData++) = _nb; + if(shiftBorder != 0){ + if( vpMbtDistanceKltPoints::isInside(roi, i_d, j_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d+shiftBorder_d, j_d+shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d-shiftBorder_d, j_d+shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d+shiftBorder_d, j_d-shiftBorder_d) + && vpMbtDistanceKltPoints::isInside(roi, i_d-shiftBorder_d, j_d-shiftBorder_d) ){ + *(ptrData++) = nb; } else{ ptrData++; } } else{ - if(vpMbtKltPolygon::isInside(roi, i, j)){ - *(ptrData++) = _nb; + if(vpMbtDistanceKltPoints::isInside(roi, i, j)){ + *(ptrData++) = nb; } else{ ptrData++; } } } - ptrData += width - j_max + j_min; + ptrData += mask->widthStep - j_max + j_min; } - +#endif } /*! @@ -379,13 +411,13 @@ vpMbtKltPolygon::updateMask(IplImage* _mask, unsigned int _nb, unsigned int _shi \param threshold_outlier : Threshold to specify wether or not a point has to be deleted. */ void -vpMbtKltPolygon::removeOutliers(const vpColVector& _w, const double &threshold_outlier) +vpMbtDistanceKltPoints::removeOutliers(const vpColVector& _w, const double &threshold_outlier) { std::map<int, vpImagePoint> tmp; std::map<int, int> tmp2; unsigned int nbSupp = 0; unsigned int k = 0; - + nbPointsCur = 0; std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin(); for( ; iter != curPoints.end(); iter++){ @@ -399,18 +431,18 @@ vpMbtKltPolygon::removeOutliers(const vpColVector& _w, const double &threshold_o nbSupp++; initPoints.erase(iter->first); } - + k+=2; } - + if(nbSupp != 0){ curPoints = std::map<int, vpImagePoint>(); curPointsInd = std::map<int, int>(); - + curPoints = tmp; curPointsInd = tmp2; if(nbPointsCur >= minNbPoint) enoughPoints = true; - else enoughPoints = false; + else enoughPoints = false; } } @@ -420,22 +452,22 @@ vpMbtKltPolygon::removeOutliers(const vpColVector& _w, const double &threshold_o \param _I : The image where to display. */ void -vpMbtKltPolygon::displayPrimitive(const vpImage<unsigned char>& _I) -{ +vpMbtDistanceKltPoints::displayPrimitive(const vpImage<unsigned char>& _I) +{ std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin(); for( ; iter != curPoints.end(); iter++){ int id(iter->first); vpImagePoint iP; iP.set_i(static_cast<double>(iter->second.get_i())); iP.set_j(static_cast<double>(iter->second.get_j())); - + vpDisplay::displayCross(_I, iP, 10, vpColor::red); - + iP.set_i( vpMath::round( iP.get_i() + 7 ) ); iP.set_j( vpMath::round( iP.get_j() + 7 ) ); char ide[10]; sprintf(ide, "%ld", static_cast<long int>(id)); - vpDisplay::displayCharString(_I, iP, ide, vpColor::red); + vpDisplay::displayText(_I, iP, ide, vpColor::red); } } @@ -445,22 +477,22 @@ vpMbtKltPolygon::displayPrimitive(const vpImage<unsigned char>& _I) \param _I : The image where to display. */ void -vpMbtKltPolygon::displayPrimitive(const vpImage<vpRGBa>& _I) -{ +vpMbtDistanceKltPoints::displayPrimitive(const vpImage<vpRGBa>& _I) +{ std::map<int, vpImagePoint>::const_iterator iter = curPoints.begin(); for( ; iter != curPoints.end(); iter++){ int id(iter->first); vpImagePoint iP; iP.set_i(static_cast<double>(iter->second.get_i())); iP.set_j(static_cast<double>(iter->second.get_j())); - + vpDisplay::displayCross(_I, iP, 10, vpColor::red); - + iP.set_i( vpMath::round( iP.get_i() + 7 ) ); iP.set_j( vpMath::round( iP.get_j() + 7 ) ); char ide[10]; sprintf(ide, "%ld", static_cast<long int>(id)); - vpDisplay::displayCharString(_I, iP, ide, vpColor::red); + vpDisplay::displayText(_I, iP, ide, vpColor::red); } } @@ -468,7 +500,8 @@ vpMbtKltPolygon::displayPrimitive(const vpImage<vpRGBa>& _I) // Static functions //################################### -bool vpMbtKltPolygon::intersect(const vpImagePoint& p1, const vpImagePoint& p2, const double i_test, const double j_test, const double i, const double j) +bool +vpMbtDistanceKltPoints::intersect(const vpImagePoint& p1, const vpImagePoint& p2, const double i_test, const double j_test, const double i, const double j) { double dx = p2.get_j() - p1.get_j(); double dy = p2.get_i() - p1.get_i(); @@ -477,7 +510,8 @@ bool vpMbtKltPolygon::intersect(const vpImagePoint& p1, const vpImagePoint& p2, double den = dx * ey - dy * ex; double t = 0, u = 0; - if(den != 0){ + //if(den != 0){ + if(std::fabs(den) > std::fabs(den)*std::numeric_limits<double>::epsilon()){ t = -( ey * ( p1.get_j() - j_test ) + ex * ( -p1.get_i() + i_test ) ) / den; u = -( dx * ( -p1.get_i() + i_test ) + dy * ( p1.get_j() - j_test ) ) / den; } @@ -487,7 +521,8 @@ bool vpMbtKltPolygon::intersect(const vpImagePoint& p1, const vpImagePoint& p2, return ( t >= std::numeric_limits<double>::epsilon() && t < 1.0 && u >= std::numeric_limits<double>::epsilon() && u < 1.0); } -bool vpMbtKltPolygon::isInside(const std::vector<vpImagePoint>& roi, const double i, const double j) +bool +vpMbtDistanceKltPoints::isInside(const std::vector<vpImagePoint>& roi, const double i, const double j) { double i_test = 100000.; double j_test = 100000.; @@ -498,7 +533,7 @@ bool vpMbtKltPolygon::isInside(const std::vector<vpImagePoint>& roi, const doubl computeAgain = false; for(unsigned int k=0; k< roi.size(); k++){ try{ - if(vpMbtKltPolygon::intersect(roi[k], roi[(k+1)%roi.size()], i, j, i_test, j_test)){ + if(vpMbtDistanceKltPoints::intersect(roi[k], roi[(k+1)%roi.size()], i, j, i_test, j_test)){ nbInter++; } } @@ -517,4 +552,4 @@ bool vpMbtKltPolygon::isInside(const std::vector<vpImagePoint>& roi, const doubl return ((nbInter%2) == 1); } -#endif // VISP_HAVE_OPENCV +#endif diff --git a/src/tracking/mbt/klt/vpMbtKltPolygon.h b/src/tracking/mbt/klt/vpMbtDistanceKltPoints.h similarity index 72% rename from src/tracking/mbt/klt/vpMbtKltPolygon.h rename to src/tracking/mbt/klt/vpMbtDistanceKltPoints.h index 7c1c868a1f3479bd53b2d558e4bf299b7ea4c5bf..5bcf43dfd3f7cb9352234c98a783c41fd90ae2b2 100644 --- a/src/tracking/mbt/klt/vpMbtKltPolygon.h +++ b/src/tracking/mbt/klt/vpMbtDistanceKltPoints.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtKltPolygon.h 4339 2013-07-23 15:24:42Z ayol $ + * $Id: vpMbtDistanceKltPoints.h 4661 2014-02-10 19:34:58Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,7 +31,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: - * Generic model polygon, containing points of interest. + * Klt polygon, containing points of interest. * * Authors: * Romain Tallonneau @@ -39,12 +39,14 @@ * *****************************************************************************/ -#ifndef vpMbtKltPolygon_h -#define vpMbtKltPolygon_h +#ifndef vpMbtDistanceKltPoints_h +#define vpMbtDistanceKltPoints_h #include <visp/vpConfig.h> -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100)) + +#include <map> #include <visp/vpMbtPolygon.h> #include <visp/vpKltOpencv.h> @@ -53,21 +55,22 @@ #include <visp/vpGEMM.h> #include <visp/vpHomography.h> #include <visp/vpPlot.h> +#include <visp/vpMbHiddenFaces.h> /*! - \class vpMbtKltPolygon - + \class vpMbtDistanceKltPoints + \brief Implementation of a polygon of the model containing points of interest. It is used by the model-based tracker KLT, and hybrid. - + \warning This class is only available if OpenCV is installed, and used. - + \ingroup ModelBasedTracking */ -class VISP_EXPORT vpMbtKltPolygon: public vpMbtPolygon +class VISP_EXPORT vpMbtDistanceKltPoints { private: //! the homography in meter - vpMatrix H; + vpMatrix H; //! normal to the initial plane vpColVector N; //! current normal @@ -80,7 +83,7 @@ private: std::map<int, vpImagePoint> initPoints; //! Current points and their ID std::map<int, vpImagePoint> curPoints; - //! Current points indexes and their ID + //! Current points ID and their indexes std::map<int, int> curPointsInd; //! number of points detected unsigned int nbPointsCur; @@ -96,72 +99,101 @@ private: double d0; //! Camera parameters vpCameraParameters cam; - + +public: + //! Pointer to the polygon that define a face + vpMbtPolygon *polygon; + private: - + double compute_1_over_Z(const double x, const double y); void computeP_mu_t(const double x_in, const double y_in, double& x_out, double& y_out, const vpMatrix& cHc0); bool isTrackedFeature(const int id); - + public: - vpMbtKltPolygon(); - virtual ~vpMbtKltPolygon(); - + vpMbtDistanceKltPoints(); + virtual ~vpMbtDistanceKltPoints(); + unsigned int computeNbDetectedCurrent(const vpKltOpencv& _tracker); void computeHomography(const vpHomogeneousMatrix& _cTc0, vpHomography& cHc0); void computeInteractionMatrixAndResidu(vpColVector& _R, vpMatrix& _J); - + void displayPrimitive(const vpImage<unsigned char>& _I); void displayPrimitive(const vpImage<vpRGBa>& _I); - + /*! Get the camera parameters of the face. \return cam : the camera parameters of the face. */ inline vpCameraParameters& getCameraParameters(){ return cam; } - + inline vpColVector getCurrentNormal() const {return N_cur; } - + inline std::map<int, vpImagePoint>& getCurrentPoints() {return curPoints; } - + inline std::map<int, int>& getCurrentPointsInd() {return curPointsInd; } - + /*! Get the number of point that was belonging to the face at the initialisation - \return the number of initial point + \return the number of initial point. + + \sa getCurrentNumberPoints() */ inline unsigned int getInitialNumberPoint() const { return nbPointsInit;} - /*! - get the number of points detected in the last image. + Get the number of points detected in the last image. - \warning to have the real number of points, the function computeNbDetectedCurrent + \warning To have the real number of points, the function computeNbDetectedCurrent() must be called first. - \return the number of points detected in the current image + \return the number of points detected in the current image. + + \sa getInitialNumberPoint() */ - inline unsigned int getNbPointsCur() const {return nbPointsCur;} - + inline unsigned int getCurrentNumberPoints() const {return nbPointsCur;} + inline bool hasEnoughPoints() const {return enoughPoints;} - + void init(const vpKltOpencv& _tracker); - + void removeOutliers(const vpColVector& weight, const double &threshold_outlier); - + /*! Set the camera parameters \param _cam : the new camera parameters */ - virtual inline void setCameraParameters(const vpCameraParameters& _cam){ cam = _cam; } - - void updateMask(IplImage* _mask, unsigned int _nb = 255, unsigned int _shiftBorder = 0); - -//################### -// Static Functions -//################### + virtual inline void setCameraParameters(const vpCameraParameters& _cam){ cam = _cam; } + +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + void updateMask(cv::Mat &mask, unsigned char _nb = 255, unsigned int _shiftBorder = 0); +#else + void updateMask(IplImage* mask, unsigned char _nb = 255, unsigned int _shiftBorder = 0); +#endif + + //################### + // Deprecated Functions + //################### +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS +public: + /*! + \deprecated Use rather getCurrentNumberPoints() that does the same. + + Get the number of points detected in the last image. + + \warning to have the real number of points, the function computeNbDetectedCurrent() + must be called first. + + \return the number of points detected in the current image + */ + vp_deprecated inline unsigned int getNbPointsCur() const {return nbPointsCur;} +#endif + + //################### + // Static Functions + //################### private: static bool isInside(const std::vector<vpImagePoint>& roi, const double i, const double j); static bool intersect(const vpImagePoint& p1, const vpImagePoint& p2, const double i, const double j, const double i_test, const double j_test); diff --git a/src/tracking/mbt/klt/vpMbtKltXmlParser.cpp b/src/tracking/mbt/klt/vpMbtKltXmlParser.cpp index 554ddd641bb41780df5d00d4e2df8a605ba27365..239389a74556daa1e4fbbcf809269bd2a6411495 100644 --- a/src/tracking/mbt/klt/vpMbtKltXmlParser.cpp +++ b/src/tracking/mbt/klt/vpMbtKltXmlParser.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtKltXmlParser.cpp 4320 2013-07-17 15:37:27Z ayol $ + * $Id: vpMbtKltXmlParser.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ * * * Description: - * Read MBT KLT Tracker information in an XML file + * Load XML parameters of the Model based tracker (using point features). * * Authors: * Aurelien Yol @@ -55,10 +55,9 @@ */ vpMbtKltXmlParser::vpMbtKltXmlParser() + : maskBorder(0), maxFeatures(0), winSize(0), qualityValue(0.), minDist(0.), + harrisParam(0.), blockSize(0), pyramidLevels(0) { - hasNearClipping = false; - hasFarClipping = false; - fovClipping = false; init(); } @@ -75,9 +74,8 @@ vpMbtKltXmlParser::~vpMbtKltXmlParser() void vpMbtKltXmlParser::init() { - setMainTag("conf"); + vpMbXmlParser::init(); - nodeMap["conf"] = conf; nodeMap["klt"] = klt; nodeMap["mask_border"] = mask_border; nodeMap["max_features"] = max_features; @@ -87,19 +85,6 @@ vpMbtKltXmlParser::init() nodeMap["harris"] = harris; nodeMap["size_block"] = size_block; nodeMap["pyramid_lvl"] = pyramid_lvl; - nodeMap["face"] = face; - nodeMap["angle_appear"] = angle_appear; - nodeMap["angle_disappear"] = angle_disappear; - nodeMap["near_clipping"] = near_clipping; - nodeMap["far_clipping"] = far_clipping; - nodeMap["fov_clipping"] = fov_clipping; - nodeMap["camera"] = camera; - nodeMap["height"] = height; - nodeMap["width"] = width; - nodeMap["u0"] = u0; - nodeMap["v0"] = v0; - nodeMap["px"] = px; - nodeMap["py"] = py; } /*! @@ -136,27 +121,27 @@ vpMbtKltXmlParser::writeMainClass(xmlNodePtr /*node*/) void vpMbtKltXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) { - bool klt_node = false; bool camera_node = false; bool face_node = false; + bool klt_node = false; for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { if(dataNode->type == XML_ELEMENT_NODE){ std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); if(iter_data != nodeMap.end()){ switch (iter_data->second){ - case klt:{ - this->read_klt(doc, dataNode); - klt_node = true; - }break; case camera:{ - this->read_camera(doc, dataNode); + this->read_camera (doc, dataNode); camera_node = true; }break; case face:{ this->read_face(doc, dataNode); face_node = true; }break; + case klt:{ + this->read_klt(doc, dataNode); + klt_node = true; + }break; default:{ // vpTRACE("unknown tag in read_sample : %d, %s", iter_data->second, (iter_data->first).c_str()); }break; @@ -165,95 +150,27 @@ vpMbtKltXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) } } - if(!klt_node) - std::cout << "WARNING: KLT Node not specified, default values used" << std::endl; - - if(!camera_node) - std::cout << "WARNING: CAMERA Node not specified, default values used" << std::endl; - - if(!face_node) - std::cout << "WARNING: FACE Node not specified, default values used" << std::endl; -} + if(!camera_node) { + std::cout << "camera : u0 : "<< this->cam.get_u0() << " (default)" <<std::endl; + std::cout << "camera : v0 : "<< this->cam.get_v0() << " (default)" <<std::endl; + std::cout << "camera : px : "<< this->cam.get_px() << " (default)" <<std::endl; + std::cout << "camera : py : "<< this->cam.get_py() << " (default)" <<std::endl; + } -/*! - Read face information. - - \throw vpException::fatalError if there was an unexpected number of data. - - \param doc : Pointer to the document. - \param node : Pointer to the node of the camera information. -*/ -void -vpMbtKltXmlParser::read_face(xmlDocPtr doc, xmlNodePtr node) -{ - bool angle_appear_node = false; - bool angle_disappear_node = false; - bool near_clipping_node = false; - bool far_clipping_node = false; - bool fov_clipping_node = false; - - for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { - if(dataNode->type == XML_ELEMENT_NODE){ - std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); - if(iter_data != nodeMap.end()){ - switch (iter_data->second){ - case angle_appear:{ - angleAppear = xmlReadDoubleChild(doc, dataNode); - angle_appear_node = true; - }break; - case angle_disappear:{ - angleDisappear = xmlReadDoubleChild(doc, dataNode); - angle_disappear_node = true; - }break; - case near_clipping:{ - nearClipping = xmlReadDoubleChild(doc, dataNode); - near_clipping_node = true; - hasNearClipping = true; - }break; - case far_clipping:{ - farClipping = xmlReadDoubleChild(doc, dataNode); - far_clipping_node = true; - hasFarClipping = true; - }break; - case fov_clipping:{ - fovClipping = (bool)xmlReadIntChild(doc, dataNode); - fov_clipping_node = true; - }break; - default:{ -// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); - }break; - } - } - } + if(!face_node) { + std::cout << "face : Angle Appear : "<< angleAppear <<" (default)" <<std::endl; + std::cout << "face : Angle Disappear : "<< angleDisappear <<" (default)" <<std::endl; } - - if(!angle_appear_node) - std::cout << "WARNING: In FACE Node, ANGLE_APPEAR Node not specified, default value used : " << angleAppear << std::endl; - else - std::cout << "face : Angle Appear "<< angleAppear <<std::endl; - - if(!angle_disappear_node) - std::cout << "WARNING: In FACE Node, ANGLE_DESAPPEAR Node not specified, default value used : " << angleDisappear << std::endl; - else - std::cout << "face : Angle Disappear : "<< angleDisappear <<std::endl; - - if(!near_clipping_node) - std::cout << "WARNING: In FACE Node, NEAR_CLIPPING Node not specified, no near clipping used" << std::endl; - else - std::cout << "face : Near Clipping : "<< nearClipping <<std::endl; - - if(!far_clipping_node) - std::cout << "WARNING: In FACE Node, FAR_CLIPPING Node not specified, no far clipping used" << std::endl; - else - std::cout << "face : Far Clipping : "<< farClipping <<std::endl; - - if(!fov_clipping_node) - std::cout << "WARNING: In FACE Node, FOV_CLIPPING Node not specified, no fov clipping used" << std::endl; - else{ - if(fovClipping) - std::cout << "face : Fov Clipping : True" <<std::endl; - else - std::cout << "face : Fov Clipping : False" <<std::endl; + + if(!klt_node) { + std::cout << "klt : Mask Border : "<< maskBorder <<" (default)" <<std::endl; + std::cout << "klt : Max Features : "<< maxFeatures <<" (default)" <<std::endl; + std::cout << "klt : Windows Size : "<< winSize <<" (default)" <<std::endl; + std::cout << "klt : Quality : "<< qualityValue <<" (default)" <<std::endl; + std::cout << "klt : Min Distance : "<< minDist <<" (default)" <<std::endl; + std::cout << "klt : Harris Parameter : "<< harrisParam <<" (default)" <<std::endl; + std::cout << "klt : Block Size : "<< blockSize <<" (default)" <<std::endl; + std::cout << "klt : Pyramid Levels : "<< pyramidLevels <<" (default)" <<std::endl; } } @@ -268,7 +185,7 @@ vpMbtKltXmlParser::read_face(xmlDocPtr doc, xmlNodePtr node) void vpMbtKltXmlParser::read_klt(xmlDocPtr doc, xmlNodePtr node) { - bool mask_border_node = false; + bool mask_border_node = false; bool max_features_node = false; bool window_size_node = false; bool quality_node = false; @@ -323,138 +240,45 @@ vpMbtKltXmlParser::read_klt(xmlDocPtr doc, xmlNodePtr node) } if(!mask_border_node) - std::cout << "WARNING: In KLT Node, MASK_BORDER Node not specified, default value used : " << maskBorder << std::endl; + std::cout << "klt : Mask Border : "<< maskBorder <<" (default)" <<std::endl; else std::cout << "klt : Mask Border : "<< maskBorder <<std::endl; if(!max_features_node) - std::cout << "WARNING: In KLT Node, MAX_FEATURES Node not specified, default value used : " << maxFeatures << std::endl; + std::cout << "klt : Max Features : "<< maxFeatures <<" (default)" <<std::endl; else std::cout << "klt : Max Features : "<< maxFeatures <<std::endl; if(!window_size_node) - std::cout << "WARNING: In KLT Node, WINDOW_SIZE Node not specified, default value used : " << winSize << std::endl; + std::cout << "klt : Windows Size : "<< winSize <<" (default)" <<std::endl; else std::cout << "klt : Windows Size : "<< winSize <<std::endl; if(!quality_node) - std::cout << "WARNING: In KLT Node, QUALITY Node not specified, default value used : " << qualityValue << std::endl; + std::cout << "klt : Quality : "<< qualityValue <<" (default)" <<std::endl; else std::cout << "klt : Quality : "<< qualityValue <<std::endl; if(!min_distance_node) - std::cout << "WARNING: In KLT Node, MIN_DISTANCE Node not specified, default value used : " << minDist << std::endl; + std::cout << "klt : Min Distance : "<< minDist <<" (default)" <<std::endl; else std::cout << "klt : Min Distance : "<< minDist <<std::endl; if(!harris_node) - std::cout << "WARNING: In KLT Node, HARRIS Node not specified, default value used : " << harrisParam << std::endl; + std::cout << "klt : Harris Parameter : "<< harrisParam <<" (default)" <<std::endl; else std::cout << "klt : Harris Parameter : "<< harrisParam <<std::endl; if(!size_block_node) - std::cout << "WARNING: In KLT Node, SIZE_BLOCK Node not specified, default value used : " << blockSize << std::endl; + std::cout << "klt : Block Size : "<< blockSize <<" (default)" <<std::endl; else std::cout << "klt : Block Size : "<< blockSize <<std::endl; if(!pyramid_lvl_node) - std::cout << "WARNING: In KLT Node, PYRAMID_LVL Node not specified, default value used : " << pyramidLevels << std::endl; + std::cout << "klt : Pyramid Levels : "<< pyramidLevels <<" (default)" <<std::endl; else std::cout << "klt : Pyramid Levels : "<< pyramidLevels <<std::endl; } -/*! - Read camera information. - - \throw vpException::fatalError if there was an unexpected number of data. - - \param doc : Pointer to the document. - \param node : Pointer to the node of the camera information. -*/ -void -vpMbtKltXmlParser::read_camera (xmlDocPtr doc, xmlNodePtr node) -{ - bool height_node = false; - bool width_node = false; - bool u0_node = false; - bool v0_node = false; - bool px_node = false; - bool py_node = false; - - // current data values. -// int d_height=0 ; -// int d_width= 0 ; - double d_u0 = this->cam.get_u0(); - double d_v0 = this->cam.get_v0(); - double d_px = this->cam.get_px(); - double d_py = this->cam.get_py(); - - for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { - if(dataNode->type == XML_ELEMENT_NODE){ - std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); - if(iter_data != nodeMap.end()){ - switch (iter_data->second){ - case height:{ - /* d_height = */ xmlReadIntChild(doc, dataNode); - height_node = true; - }break; - case width:{ - /* d_width = */ xmlReadIntChild(doc, dataNode); - width_node = true; - }break; - case u0:{ - d_u0 = xmlReadDoubleChild(doc, dataNode); - u0_node = true; - }break; - case v0:{ - d_v0 = xmlReadDoubleChild(doc, dataNode); - v0_node = true; - }break; - case px:{ - d_px = xmlReadDoubleChild(doc, dataNode); - px_node = true; - }break; - case py:{ - d_py = xmlReadDoubleChild(doc, dataNode); - py_node = true; - }break; - default:{ -// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); - }break; - } - } - } - } - - this->cam.initPersProjWithoutDistortion(d_px, d_py, d_u0, d_v0) ; - - if(!height_node) - std::cout << "WARNING: In CAMERA Node, HEIGHT Node not specified, default value used" << std::endl; - - if(!width_node) - std::cout << "WARNING: In CAMERA Node, WIDTH Node not specified, default value used" << std::endl; - - if(!u0_node) - std::cout << "WARNING: In CAMERA Node, u0 Node not specified, default value used : " << this->cam.get_u0() << std::endl; - else - std::cout << "camera : u0 "<< this->cam.get_u0() <<std::endl; - - if(!v0_node) - std::cout << "WARNING: In CAMERA Node, v0 Node not specified, default value used : " << this->cam.get_v0() << std::endl; - else - std::cout << "camera : v0 "<< this->cam.get_v0() <<std::endl; - - if(!px_node) - std::cout << "WARNING: In CAMERA Node, px Node not specified, default value used : " << this->cam.get_px() << std::endl; - else - std::cout << "camera : px "<< this->cam.get_px() <<std::endl; - - if(!py_node) - std::cout << "WARNING: In CAMERA Node, py Node not specified, default value used : " << this->cam.get_py() << std::endl; - else - std::cout << "camera : py "<< this->cam.get_py() <<std::endl; -} - - #endif diff --git a/src/tracking/mbt/klt/vpMbtKltXmlParser.h b/src/tracking/mbt/klt/vpMbtKltXmlParser.h index ee260f2f6cb802b72dfd3ebe9a41a0259d6256bc..0de55c7eb220e1f34365c6ba394c1a5bf387f61e 100644 --- a/src/tracking/mbt/klt/vpMbtKltXmlParser.h +++ b/src/tracking/mbt/klt/vpMbtKltXmlParser.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtKltXmlParser.h 4320 2013-07-17 15:37:27Z ayol $ + * $Id: vpMbtKltXmlParser.h 4582 2014-01-14 14:02:46Z ayol $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ * * * Description: - * Read MBT KLT Tracker information in an XML file + * Load XML parameters of the Model based tracker (using point features). * * Authors: * Aurelien Yol @@ -52,8 +52,7 @@ #include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ -#include <visp/vpXmlParser.h> -#include <visp/vpCameraParameters.h> +#include <visp/vpMbXmlParser.h> /*! \class vpMbtKltXmlParser @@ -63,7 +62,7 @@ Data parser for the KLT model based tracker. */ -class VISP_EXPORT vpMbtKltXmlParser: public vpXmlParser +class VISP_EXPORT vpMbtKltXmlParser: virtual public vpMbXmlParser { protected: //! Border of the mask used on Klt points @@ -82,26 +81,9 @@ protected: unsigned int blockSize; //! Number of pyramid levels unsigned int pyramidLevels; - //! Angle to determine if a face appeared - double angleAppear; - //! Angle to determine if a face disappeared - double angleDisappear; - //! Is near clipping distance specified? - bool hasNearClipping; - //! Near clipping distance - double nearClipping; - //! Is far clipping distance specified? - bool hasFarClipping; - //! Near clipping distance - double farClipping; - //! Fov Clipping - bool fovClipping; - //! Camera parameters. - vpCameraParameters cam; typedef enum{ - conf, - klt, + klt = vpMbXmlParser::last, mask_border, max_features, window_size, @@ -110,20 +92,8 @@ protected: harris, size_block, pyramid_lvl, - face, - angle_appear, - angle_disappear, - near_clipping, - far_clipping, - fov_clipping, - camera, - height, - width, - u0, - v0, - px, - py - } dataToParse; + last + } dataToParseMbKlt; public: @@ -131,20 +101,6 @@ public: vpMbtKltXmlParser(); virtual ~vpMbtKltXmlParser(); - /*! - Get the angle to determine if a face appeared. - - \return angleAppear - */ - inline double getAngleAppear() const {return angleAppear;} - - /*! - Get the angle to determine if a face disappeared. - - \return angleDisappear - */ - inline double getAngleDisappear() const {return angleDisappear;} - /*! Get the size of a block. @@ -152,27 +108,6 @@ public: */ inline unsigned int getBlockSize() const {return blockSize;} - /*! - Get the camera parameters. - - \return cam - */ - void getCameraParameters(vpCameraParameters& _cam) const { _cam = cam;} - - /*! - Get the far clipping distance. - - \return farClipping - */ - inline double getFarClippingDistance() const {return farClipping;} - - /*! - Use FOV clipping - - \return True if yes, False otherwise. - */ - inline bool getFovClipping() const {return fovClipping;} - /*! Get the Harris free parameter. @@ -201,13 +136,6 @@ public: */ inline double getMinDistance() const {return minDist;} - /*! - Get the near clipping distance. - - \return nearClipping - */ - inline double getNearClippingDistance() const {return nearClipping;} - /*! Get the number of pyramid levels @@ -229,40 +157,10 @@ public: */ inline unsigned int getWindowSize() const {return winSize;} - /*! - Has Far clipping been specified? - - \return True if yes, False otherwise. - */ - inline bool hasFarClippingDistance() const {return hasFarClipping;} - - /*! - Has Near clipping been specified? - - \return True if yes, False otherwise. - */ - inline bool hasNearClippingDistance() const {return hasNearClipping;} - void parse(const char * filename); - void readMainClass(xmlDocPtr doc, xmlNodePtr node); - void read_camera (xmlDocPtr doc, xmlNodePtr node); + virtual void readMainClass(xmlDocPtr doc, xmlNodePtr node); void read_klt(xmlDocPtr doc, xmlNodePtr node); - void read_face(xmlDocPtr doc, xmlNodePtr node); - - /*! - Set the angle to determine if a face appeared. - - \param aappear : New angleAppear - */ - inline void setAngleAppear(const double &aappear) {angleAppear = aappear;} - - /*! - Set the angle to determine if a face disappeared. - - \param adisappear : New angleDisappear - */ - inline void setAngleDisappear(const double &adisappear) {angleDisappear = adisappear;} /*! Set the size of a block. @@ -271,20 +169,6 @@ public: */ inline void setBlockSize(const unsigned int &bs) {blockSize = bs;} - /*! - Set the camera parameters. - - \param _cam : New cam - */ - void setCameraParameters(const vpCameraParameters& _cam) {cam = _cam;} - - /*! - Set the far clipping distance. - - \param fclip : New farClipping - */ - inline void setFarClippingDistance(const double &fclip) {farClipping = fclip;} - /*! Set the Harris free parameter. @@ -312,14 +196,7 @@ public: \param mD : New minDist */ inline void setMinDistance(const double &mD) {minDist = mD;} - - /*! - Set the near clipping distance. - \param nclip : New nearClipping - */ - inline void setNearClippingDistance(const double &nclip) {nearClipping = nclip;} - /*! Set the number of pyramid levels diff --git a/src/tracking/mbt/vpMbHiddenFaces.h b/src/tracking/mbt/vpMbHiddenFaces.h index 60b4e9138756b868051ab1607c04bc965bae3165..9bd1356db4f81289af4669507bdfde86fe0fd35c 100644 --- a/src/tracking/mbt/vpMbHiddenFaces.h +++ b/src/tracking/mbt/vpMbHiddenFaces.h @@ -3,7 +3,7 @@ * $Id: vpMbTracker.h 4004 2012-11-23 17:34:44Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,7 +36,7 @@ * * Authors: * Romain Tallonneau - * Aurélien Yol + * Aurelien Yol * *****************************************************************************/ #pragma once @@ -54,6 +54,7 @@ #endif #include <vector> +#include <limits> /*! \class vpMbHiddenFaces @@ -79,22 +80,27 @@ class vpMbHiddenFaces std::vector< Ogre::ManualObject* > lOgrePolygons; #endif - unsigned int setVisiblePrivate(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, + unsigned int setVisiblePrivate(const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed, bool useOgre = false, bool testRoi = false, - const vpImage<unsigned char> &_I = vpImage<unsigned char>(), - const vpCameraParameters &_cam = vpCameraParameters() - ) ; + const vpImage<unsigned char> &I = vpImage<unsigned char>(), + const vpCameraParameters &cam = vpCameraParameters()) ; - public : vpMbHiddenFaces() ; ~vpMbHiddenFaces() ; void addPolygon(PolygonType *p) ; + bool computeVisibility(const vpHomogeneousMatrix &cMo, + const double &angleAppears, const double &angleDisappears, + bool &changed, bool useOgre, bool testRoi, + const vpImage<unsigned char> &I, + const vpCameraParameters &cam, + const vpTranslationVector &cameraPos, + unsigned int index); #ifdef VISP_HAVE_OGRE - void displayOgre(const vpHomogeneousMatrix &_cMo); + void displayOgre(const vpHomogeneousMatrix &cMo); #endif /*! @@ -105,7 +111,7 @@ class vpMbHiddenFaces std::vector<PolygonType*>& getPolygon() {return Lpol;} #ifdef VISP_HAVE_OGRE - void initOgre(vpCameraParameters _cam = vpCameraParameters()); + void initOgre(const vpCameraParameters &cam = vpCameraParameters()); #endif /*! @@ -154,7 +160,7 @@ class vpMbHiddenFaces //! operator[] as reader. inline const PolygonType* operator[](const unsigned int i) const { return Lpol[i];} - void reset(); + void reset(); #ifdef VISP_HAVE_OGRE /*! @@ -166,16 +172,16 @@ class vpMbHiddenFaces \param h : Height of the background \param w : Width of the background */ - void setBackgroundSizeOgre(const unsigned int &h, const unsigned int &w) { ogreBackground.resize(h,w); } + void setBackgroundSizeOgre(const unsigned int &h, const unsigned int &w) { ogreBackground = vpImage<unsigned char>(h, w, 0); } #endif - unsigned int setVisible(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angle, bool &changed) ; - unsigned int setVisible(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; - unsigned int setVisible(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; + unsigned int setVisible(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angle, bool &changed) ; + unsigned int setVisible(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; + unsigned int setVisible(const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; #ifdef VISP_HAVE_OGRE - unsigned int setVisibleOgre(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; - unsigned int setVisibleOgre(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; + unsigned int setVisibleOgre(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; + unsigned int setVisibleOgre(const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) ; #endif /*! Get the number of polygons. @@ -207,7 +213,7 @@ class vpMbHiddenFaces \param d : New value. */ vp_deprecated void setDepthTest(const bool &d){depthTest = d;} - unsigned int setVisible(const vpHomogeneousMatrix &_cMo) ; + unsigned int setVisible(const vpHomogeneousMatrix &cMo) ; #endif } ; @@ -215,20 +221,21 @@ class vpMbHiddenFaces Basic constructor. */ template<class PolygonType> -vpMbHiddenFaces<PolygonType>::vpMbHiddenFaces(): nbVisiblePolygon(0) +vpMbHiddenFaces<PolygonType>::vpMbHiddenFaces() + : Lpol(), nbVisiblePolygon(0) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , depthTest(false) +#endif + { #ifdef VISP_HAVE_OGRE ogreInitialised = false; ogre = new vpAROgre(); ogre->setShowConfigDialog(false); - ogreBackground = vpImage<unsigned char>(480, 640); -#endif -#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - depthTest = false; + ogreBackground = vpImage<unsigned char>(480, 640, 0); #endif } - /*! Basic destructor. */ @@ -242,9 +249,22 @@ vpMbHiddenFaces<PolygonType>::~vpMbHiddenFaces() Lpol[i] = NULL ; } Lpol.resize(0); - + #ifdef VISP_HAVE_OGRE - delete ogre; + if(ogre != NULL){ + delete ogre; + ogre = NULL; + } + + // This is already done by calling "delete ogre" +// for(unsigned int i = 0 ; i < lOgrePolygons.size() ; i++){ +// if (lOgrePolygons[i]!=NULL){ +// delete lOgrePolygons[i] ; +// } +// lOgrePolygons[i] = NULL ; +// } + + lOgrePolygons.resize(0); #endif } @@ -261,6 +281,11 @@ vpMbHiddenFaces<PolygonType>::addPolygon(PolygonType *p) p_new->index = p->index; p_new->setNbPoint(p->nbpt); p_new->isvisible = p->isvisible; + p_new->useLod = p->useLod; + p_new->minLineLengthThresh = p->minLineLengthThresh; + p_new->minPolygonAreaThresh = p->minPolygonAreaThresh; + p_new->setName(p->name); + for(unsigned int i = 0; i < p->nbpt; i++) p_new->p[i]= p->p[i]; Lpol.push_back(p_new); @@ -281,29 +306,51 @@ vpMbHiddenFaces<PolygonType>::reset() Lpol[i] = NULL ; } Lpol.resize(0); + +#ifdef VISP_HAVE_OGRE + if(ogre != NULL){ + delete ogre; + ogre = NULL; + } + + // This is already done by calling "delete ogre" +// for(unsigned int i = 0 ; i < lOgrePolygons.size() ; i++){ +// if (lOgrePolygons[i]!=NULL){ +// delete lOgrePolygons[i] ; +// } +// lOgrePolygons[i] = NULL ; +// } + + lOgrePolygons.resize(0); + + ogreInitialised = false; + ogre = new vpAROgre(); + ogre->setShowConfigDialog(false); + ogreBackground = vpImage<unsigned char>(480, 640); +#endif } /*! Compute the number of visible polygons. - \param _cMo : The pose of the camera + \param cMo : The pose of the camera \param angleAppears : Angle used to test the appearance of a face \param angleDisappears : Angle used to test the disappearance of a face \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise \param useOgre : True if a Ogre is used to test the visibility, False otherwise \param testRoi : True if a face have to be entirely in the image False otherwise - \param _I : Image used to test if a face is entirely projected in the image. - \param _cam : Camera parameters. + \param I : Image used to test if a face is entirely projected in the image. + \param cam : Camera parameters. \return Return the number of visible polygons */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisiblePrivate(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, - bool &changed, bool useOgre, bool testRoi, - const vpImage<unsigned char> &_I, - const vpCameraParameters &_cam - ) +vpMbHiddenFaces<PolygonType>::setVisiblePrivate(const vpHomogeneousMatrix &cMo, + const double &angleAppears, const double &angleDisappears, + bool &changed, bool useOgre, bool testRoi, + const vpImage<unsigned char> &I, + const vpCameraParameters &cam) { nbVisiblePolygon = 0; changed = false; @@ -312,92 +359,137 @@ vpMbHiddenFaces<PolygonType>::setVisiblePrivate(const vpHomogeneousMatrix &_cMo, if(useOgre){ #ifdef VISP_HAVE_OGRE - _cMo.inverse().extract(cameraPos); - ogre->renderOneFrame(ogreBackground, _cMo); + cMo.inverse().extract(cameraPos); + ogre->renderOneFrame(ogreBackground, cMo); #else vpTRACE("ViSP doesn't have Ogre3D, simple visibility test used"); #endif } - for (unsigned int i = 0; i < Lpol.size(); i += 1){ - Lpol[i]->changeFrame(_cMo); - Lpol[i]->isappearing = false; - - if(Lpol[i]->isVisible()) - { - bool testDisappear = false; - unsigned int nbCornerInsidePrev = 0; - - if(testRoi){ - nbCornerInsidePrev = Lpol[i]->getNbCornerInsidePrevImage(); - if(Lpol[i]->getNbCornerInsideImage(_I, _cam) == 0) - testDisappear = true; - } - - if(!testDisappear){ - if(useOgre) + for (unsigned int i = 0; i < Lpol.size(); i++){ + if (computeVisibility(cMo, angleAppears, angleDisappears, changed, useOgre, testRoi, I, cam, cameraPos, i)) + nbVisiblePolygon ++; + } + return nbVisiblePolygon; +} + +/*! + Compute the visibility of a given face index. + + \param cMo : The pose of the camera + \param angleAppears : Angle used to test the appearance of a face + \param angleDisappears : Angle used to test the disappearance of a face + \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise + \param useOgre : True if a Ogre is used to test the visibility, False otherwise + \param testRoi : True if a face have to be entirely in the image False otherwise + \param I : Image used to test if a face is entirely projected in the image. + \param cam : Camera parameters. + \param cameraPos : Position of the camera. Used only when Ogre is used as 3rd party. + \param index : Index of the face to consider. + + \return Return true if the face is visible. +*/ +template<class PolygonType> +bool +vpMbHiddenFaces<PolygonType>::computeVisibility(const vpHomogeneousMatrix &cMo, + const double &angleAppears, const double &angleDisappears, + bool &changed, bool useOgre, bool /* testRoi */, + const vpImage<unsigned char> & I, + const vpCameraParameters & cam, + const vpTranslationVector & + #ifdef VISP_HAVE_OGRE + cameraPos + #endif + , + unsigned int index) +{ + unsigned int i = index; + Lpol[i]->changeFrame(cMo); + Lpol[i]->isappearing = false; + + //Commented because we need to compute visibility + // even when dealing with line in level of detail case + /*if(Lpol[i]->getNbPoint() <= 2) + { + Lpol[i]->isvisible = true; + } + else*/{ + if(Lpol[i]->isVisible()) + { + bool testDisappear = false; + unsigned int nbCornerInsidePrev = 0; + +// if(testRoi){ +// nbCornerInsidePrev = Lpol[i]->getNbCornerInsidePrevImage(); +// if(Lpol[i]->getNbCornerInsideImage(I, cam) == 0) +// testDisappear = true; +// } + + if(!testDisappear){ + if(useOgre) #ifdef VISP_HAVE_OGRE - testDisappear = ((!Lpol[i]->isVisible(_cMo, angleDisappears, true)) || !isVisibleOgre(cameraPos,i)); + testDisappear = ((!Lpol[i]->isVisible(cMo, angleDisappears, true, cam, I)) || !isVisibleOgre(cameraPos,i)); #else - testDisappear = (!Lpol[i]->isVisible(_cMo, angleDisappears)); + testDisappear = (!Lpol[i]->isVisible(cMo, angleDisappears, false, cam, I)); #endif - else - testDisappear = (!Lpol[i]->isVisible(_cMo, angleDisappears)); - } - - // test if the face is still visible - if(testDisappear){ -// std::cout << "Face " << i << " disappears" << std::endl; + else + testDisappear = (!Lpol[i]->isVisible(cMo, angleDisappears, false, cam, I)); + } + + // test if the face is still visible + if(testDisappear){ +// std::cout << "Face " << i << " disappears" << std::endl; + changed = true; + Lpol[i]->isvisible = false; + } + else { + //nbVisiblePolygon++; + Lpol[i]->isvisible = true; + + if(nbCornerInsidePrev > Lpol[i]->getNbCornerInsidePrevImage()) changed = true; - Lpol[i]->isvisible = false; - } - else { - nbVisiblePolygon++; - Lpol[i]->isvisible = true; - - if(nbCornerInsidePrev > Lpol[i]->getNbCornerInsidePrevImage()) - changed = true; - } } - else - { - bool testAppear = true; - - if(testRoi && Lpol[i]->getNbCornerInsideImage(_I, _cam) == 0) - testAppear = false; - - if(testAppear){ - if(useOgre) + } + else + { + bool testAppear = true; + +// if(testRoi && Lpol[i]->getNbCornerInsideImage(I, cam) == 0) +// testAppear = false; + + if(testAppear){ + if(useOgre) #ifdef VISP_HAVE_OGRE - testAppear = ((Lpol[i]->isVisible(_cMo, angleAppears, true)) && isVisibleOgre(cameraPos,i)); + testAppear = ((Lpol[i]->isVisible(cMo, angleAppears, true, cam, I)) && isVisibleOgre(cameraPos,i)); #else - testAppear = (Lpol[i]->isVisible(_cMo, angleAppears)); + testAppear = (Lpol[i]->isVisible(cMo, angleAppears, false, cam, I)); #endif - else - testAppear = (Lpol[i]->isVisible(_cMo, angleAppears)); - } - - if(testAppear){ -// std::cout << "Face " << i << " appears" << std::endl; - Lpol[i]->isvisible = true; - changed = true; - nbVisiblePolygon++; - } else - Lpol[i]->isvisible = false; + testAppear = (Lpol[i]->isVisible(cMo, angleAppears, false, cam, I)); + } + + if(testAppear){ +// std::cout << "Face " << i << " appears" << std::endl; + Lpol[i]->isvisible = true; + changed = true; + //nbVisiblePolygon++; + } + else{ +// std::cout << "Problem" << std::endl; + Lpol[i]->isvisible = false; } } - -// std::cout << "Nombre de polygones visibles: " << nbVisiblePolygon << std::endl; - return nbVisiblePolygon; + } + // std::cout << "Nombre de polygones visibles: " << nbVisiblePolygon << std::endl; + return Lpol[i]->isvisible; } /*! Compute the number of visible polygons. - \param _I : Image used to check if the region of interest is inside the image. - \param _cam : Camera parameters. - \param _cMo : The pose of the camera. + \param I : Image used to check if the region of interest is inside the image. + \param cam : Camera parameters. + \param cMo : The pose of the camera. \param angle : Angle used to test the appearance and disappearance of a face. \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise @@ -405,17 +497,17 @@ vpMbHiddenFaces<PolygonType>::setVisiblePrivate(const vpHomogeneousMatrix &_cMo, */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angle, bool &changed) +vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angle, bool &changed) { - return setVisible(_I, _cam, _cMo, angle, angle, changed); + return setVisible(I, cam, cMo, angle, angle, changed); } /*! Compute the number of visible polygons. - \param _I : Image used to check if the region of interest is inside the image. - \param _cam : Camera parameters. - \param _cMo : The pose of the camera + \param I : Image used to check if the region of interest is inside the image. + \param cam : Camera parameters. + \param cMo : The pose of the camera \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise \param angleAppears : Angle used to test the appearance of a face \param angleDisappears : Angle used to test the disappearance of a face @@ -424,15 +516,15 @@ vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& _I, const */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) +vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) { - return setVisiblePrivate(_cMo,angleAppears,angleDisappears,changed,false,true,_I,_cam); + return setVisiblePrivate(cMo,angleAppears,angleDisappears,changed,false,true,I,cam); } /*! Compute the number of visible polygons. - \param _cMo : The pose of the camera + \param cMo : The pose of the camera \param angleAppears : Angle used to test the appearance of a face \param angleDisappears : Angle used to test the disappearance of a face \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise @@ -441,23 +533,23 @@ vpMbHiddenFaces<PolygonType>::setVisible(const vpImage<unsigned char>& _I, const */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisible(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) +vpMbHiddenFaces<PolygonType>::setVisible(const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) { - return setVisiblePrivate(_cMo,angleAppears,angleDisappears,changed,false); + return setVisiblePrivate(cMo,angleAppears,angleDisappears,changed,false); } #ifdef VISP_HAVE_OGRE /*! Initialise the ogre context for face visibility tests. - \param _cam : Camera parameters. + \param cam : Camera parameters. */ template<class PolygonType> void -vpMbHiddenFaces<PolygonType>::initOgre(vpCameraParameters _cam) +vpMbHiddenFaces<PolygonType>::initOgre(const vpCameraParameters &cam) { ogreInitialised = true; - ogre->setCameraParameters(_cam); + ogre->setCameraParameters(cam); ogre->init(ogreBackground, false, true); for(unsigned int n = 0 ; n < Lpol.size(); n++){ @@ -482,11 +574,11 @@ vpMbHiddenFaces<PolygonType>::initOgre(vpCameraParameters _cam) /*! Update the display in Ogre Window. - \param _cMo : Pose used to display. + \param cMo : Pose used to display. */ template<class PolygonType> void -vpMbHiddenFaces<PolygonType>::displayOgre(const vpHomogeneousMatrix &_cMo) +vpMbHiddenFaces<PolygonType>::displayOgre(const vpHomogeneousMatrix &cMo) { if(ogreInitialised && !ogre->isWindowHidden()){ for(unsigned int i = 0 ; i < Lpol.size() ; i++){ @@ -496,16 +588,16 @@ vpMbHiddenFaces<PolygonType>::displayOgre(const vpHomogeneousMatrix &_cMo) else lOgrePolygons[i]->setVisible(false); } - ogre->display(ogreBackground, _cMo); + ogre->display(ogreBackground, cMo); } } /*! Compute the number of visible polygons through Ogre3D. - \param _I : Image used to check if the region of interest is inside the image. - \param _cam : Camera parameters. - \param _cMo : The pose of the camera + \param I : Image used to check if the region of interest is inside the image. + \param cam : Camera parameters. + \param cMo : The pose of the camera \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise \param angleAppears : Angle used to test the appearance of a face \param angleDisappears : Angle used to test the disappearance of a face @@ -514,15 +606,15 @@ vpMbHiddenFaces<PolygonType>::displayOgre(const vpHomogeneousMatrix &_cMo) */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisibleOgre(const vpImage<unsigned char>& _I, const vpCameraParameters &_cam, const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) +vpMbHiddenFaces<PolygonType>::setVisibleOgre(const vpImage<unsigned char>& I, const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) { - return setVisiblePrivate(_cMo,angleAppears,angleDisappears,changed,true,true,_I,_cam); + return setVisiblePrivate(cMo,angleAppears,angleDisappears,changed,true,true,I,cam); } /*! Compute the number of visible polygons through Ogre3D. - \param _cMo : The pose of the camera + \param cMo : The pose of the camera \param angleAppears : Angle used to test the appearance of a face \param angleDisappears : Angle used to test the disappearance of a face \param changed : True if a face appeared, disappeared or too many points have been lost. False otherwise @@ -531,9 +623,9 @@ vpMbHiddenFaces<PolygonType>::setVisibleOgre(const vpImage<unsigned char>& _I, c */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisibleOgre(const vpHomogeneousMatrix &_cMo, const double &angleAppears, const double &angleDisappears, bool &changed) +vpMbHiddenFaces<PolygonType>::setVisibleOgre(const vpHomogeneousMatrix &cMo, const double &angleAppears, const double &angleDisappears, bool &changed) { - return setVisiblePrivate(_cMo,angleAppears,angleDisappears,changed,true); + return setVisiblePrivate(cMo,angleAppears,angleDisappears,changed,true); } /*! @@ -594,7 +686,8 @@ vpMbHiddenFaces<PolygonType>::isVisibleOgre(const vpTranslationVector &cameraPos it++; while(!visible && it != result.end()){ distance = it->distance; - if(distance == distancePrev){ + //if(distance == distancePrev){ + if(std::fabs(distance - distancePrev) < distance * std::numeric_limits<double>::epsilon()){ if(it->movable->getName() == Ogre::StringConverter::toString(index)){ visible = true; break; @@ -630,18 +723,18 @@ vpMbHiddenFaces<PolygonType>::isVisibleOgre(const vpTranslationVector &cameraPos Compute the number of visible polygons. - \param _cMo : The pose of the camera + \param cMo : The pose of the camera \return Return the number of visible polygons */ template<class PolygonType> unsigned int -vpMbHiddenFaces<PolygonType>::setVisible(const vpHomogeneousMatrix &_cMo) +vpMbHiddenFaces<PolygonType>::setVisible(const vpHomogeneousMatrix &cMo) { nbVisiblePolygon = 0 ; for(unsigned int i = 0 ; i < Lpol.size() ; i++){ - if (Lpol[i]->isVisible(_cMo, depthTest)){ + if (Lpol[i]->isVisible(cMo, depthTest)){ nbVisiblePolygon++; } } diff --git a/src/tracking/mbt/vpMbTracker.cpp b/src/tracking/mbt/vpMbTracker.cpp index 44ce1855ad0f8eb4b4a6e9f802243f74ef506464..6b0760c196a935c9f0b35f5dc6ab49d3f65bdff1 100755 --- a/src/tracking/mbt/vpMbTracker.cpp +++ b/src/tracking/mbt/vpMbTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbTracker.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: vpMbTracker.cpp 5285 2015-02-09 14:32:54Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,6 +48,10 @@ \brief Generic model based tracker */ +#include <iostream> +#include <limits> +#include <algorithm> +#include <map> #include <visp/vpMatrix.h> #include <visp/vpMath.h> @@ -67,8 +71,7 @@ #include <visp/vpMbTracker.h> #include <visp/vpMatrixException.h> #include <visp/vpIoTools.h> -#include <iostream> -#include <limits> + #ifdef VISP_HAVE_COIN //Inventor includes #include <Inventor/nodes/SoSeparator.h> @@ -87,16 +90,58 @@ #endif + + +/*! + Structure to store info about segment in CAO model files. + */ +struct SegmentInfo { + SegmentInfo() : extremities(), name(), useLod(false), minLineLengthThresh(0.) {} + + std::vector<vpPoint> extremities; + std::string name; + bool useLod; + double minLineLengthThresh; +}; + +/*! + Structure to store info about a polygon face represented by a vpPolygon and by a list of vpPoint + representing the corners of the polygon face in 3D. + */ +struct PolygonFaceInfo { + PolygonFaceInfo(const double dist, const vpPolygon &poly, const std::vector<vpPoint> &corners) +: distanceToCamera(dist), polygon(poly), faceCorners(corners) {} + + bool operator<(const PolygonFaceInfo &pfi) const { + return distanceToCamera < pfi.distanceToCamera; + } + + double distanceToCamera; + vpPolygon polygon; + std::vector<vpPoint> faceCorners; +}; + /*! Basic constructor. Set default values. */ vpMbTracker::vpMbTracker() +: cam(), cMo(), oJo(6,6), isoJoIdentity(true), modelFileName(), modelInitialised(false), + poseSavingFilename(), computeCovariance(false), covarianceMatrix(), displayFeatures(false), + m_w(), m_error(), faces(), angleAppears( vpMath::rad(89) ), angleDisappears( vpMath::rad(89) ), + distNearClip(0.001), distFarClip(100), clippingFlag(vpMbtPolygon::NO_CLIPPING), useOgre(false), + nbPoints(0), nbLines(0), nbPolygonLines(0), nbPolygonPoints(0), nbCylinders(0), nbCircles(0), + useLodGeneral(false), applyLodSettingInConfig(false), minLineLengthThresholdGeneral(50.0), + minPolygonAreaThresholdGeneral(2500.0), mapOfParameterNames() { - modelInitialised = false; - computeCovariance = false; - displayFeatures = false; + oJo.setIdentity(); + //Map used to parse additional information in CAO model files, + //like name of faces or LOD setting + mapOfParameterNames["name"] = "string"; + mapOfParameterNames["minPolygonAreaThreshold"] = "number"; + mapOfParameterNames["minLineLengthThreshold"] = "number"; + mapOfParameterNames["useLod"] = "boolean"; } /*! @@ -106,7 +151,6 @@ vpMbTracker::~vpMbTracker() { } - /*! Initialise the tracking by clicking on the image points corresponding to the 3D points (object frame) in the file initFile. The structure of this file @@ -178,7 +222,7 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF std::cout << "No modification : left click " << std::endl; std::cout << "Modify initial pose : right click " << std::endl ; - vpDisplay::displayCharString(I, 15, 10, + vpDisplay::displayText(I, 15, 10, "left click to validate, right click to modify initial pose", vpColor::red); @@ -193,6 +237,8 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF } else { + vpDisplay *d_help = NULL; + vpDisplay::display(I) ; vpDisplay::flush(I) ; @@ -212,50 +258,84 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF else sprintf(s,"%s.init", initFile.c_str()); - std::cout << "filename " << s << std::endl ; + std::cout << "Load 3D points from: " << s << std::endl ; finit.open(s,std::ios::in) ; if (finit.fail()){ std::cout << "cannot read " << s << std::endl; throw vpException(vpException::ioError, "cannot read init file"); } - std::string dispF; - if( pos == initFile.size()-ext.size() && pos != 0) - dispF = initFile.substr(0,pos) + ".ppm"; - else - dispF = initFile + ".ppm"; - - sprintf(s, "%s", dispF.c_str()); - - vpImage<vpRGBa> Iref ; - //Display window creation and initialistation + //Display window creation and initialisation + try{ + if(displayHelp){ + std::string dispF; + if( pos == initFile.size()-ext.size() && pos != 0) + dispF = initFile.substr(0,pos) + ".ppm"; + else + dispF = initFile + ".ppm"; + + if (vpIoTools::checkFilename(dispF)) { + std::cout << "Load image to help initialization: " << dispF << std::endl; #if defined VISP_HAVE_X11 - vpDisplayX d; + d_help = new vpDisplayX ; #elif defined VISP_HAVE_GDI - vpDisplayGDI d; + d_help = new vpDisplayGDI; #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV d; + d_help = new vpDisplayOpenCV; #endif - try{ - if(displayHelp){ - vpImageIo::read(Iref,s) ; + + vpImage<vpRGBa> Iref ; + vpImageIo::read(Iref, dispF) ; #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) - d.init(Iref,10,500, "Where to initialize...") ; - vpDisplay::display(Iref) ; - vpDisplay::flush(Iref); + d_help->init(Iref, I.display->getWindowXPosition()+(int)I.getWidth()+80, I.display->getWindowYPosition(), + "Where to initialize...") ; + vpDisplay::display(Iref) ; + vpDisplay::flush(Iref); #endif - } + } + } } - catch(...){} + catch(...) { + if(d_help != NULL) { + delete d_help; + d_help = NULL; + } + } + + char c; + // skip lines starting with # as comment + finit.get(c); + while (!finit.fail() && (c == '#')) { + finit.ignore(256, '\n'); + finit.get(c); + } + finit.unget(); unsigned int n ; finit >> n ; - std::cout << "number of points " << n << std::endl ; + finit.ignore(256, '\n'); // skip the rest of the line + std::cout << "Number of 3D points " << n << std::endl ; + if (n > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of points."); + } + vpPoint *P = new vpPoint [n] ; for (unsigned int i=0 ; i < n ; i++){ + // skip lines starting with # as comment + finit.get(c); + while (!finit.fail() && (c == '#')) { + finit.ignore(256, '\n'); + finit.get(c); + } + finit.unget(); + finit >> X ; finit >> Y ; finit >> Z ; + finit.ignore(256, '\n'); // skip the rest of the line + + std::cout << "Point " << i+1 << " with 3D coordinates: " << X << " " << Y << " " << Z << std::endl; P[i].setWorldCoordinates(X,Y,Z) ; // (X,Y,Z) } @@ -266,23 +346,33 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF while(!isWellInit) { //////////////////////////////// + std::vector<vpImagePoint> mem_ip; for(unsigned int i=0 ; i< n ; i++) { - std::cout << "Click on point " << i+1 << std::endl ; + std::ostringstream text; + text << "Click on point " << i+1; + vpDisplay::display(I); + vpDisplay::displayText(I, 15, 10, text.str(), vpColor::red); + for (unsigned int k=0; k<mem_ip.size(); k++) { + vpDisplay::displayCross(I, mem_ip[k], 10, vpColor::green, 2) ; + } + vpDisplay::flush(I) ; + + std::cout << "Click on point " << i+1 << " "; double x=0,y=0; vpDisplay::getClick(I, ip) ; - vpDisplay::displayCross(I, ip, 5,vpColor::green) ; + mem_ip.push_back(ip); vpDisplay::flush(I) ; vpPixelMeterConversion::convertPoint(cam, ip, x, y); P[i].set_x(x); P[i].set_y(y); - std::cout << "click sur point " << ip << std::endl; + std::cout << "with 2D coordinates: " << ip << std::endl; - vpDisplay::displayPoint (I, ip, vpColor::green); //display target point pose.addPoint(P[i]) ; // and added to the pose computation point list } vpDisplay::flush(I) ; + vpDisplay::display(I) ; vpHomogeneousMatrix cMo1, cMo2; pose.computePose(vpPose::LAGRANGE, cMo1) ; @@ -299,13 +389,13 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF pose.computePose(vpPose::VIRTUAL_VS, cMo); display(I, cMo, cam, vpColor::green, 1, true); - vpDisplay::displayCharString(I, 15, 10, - "left click to validate, right click to re initialize object", - vpColor::red); + vpDisplay::displayText(I, 15, 10, + "left click to validate, right click to re initialize object", + vpColor::red); vpDisplay::flush(I) ; - vpMouseButton::vpMouseButtonType button = vpMouseButton::button1; + button = vpMouseButton::button1; while (!vpDisplay::getClick(I, ip, button)) ; @@ -330,6 +420,11 @@ vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::string& initF savePose(str_pose); else savePose(poseSavingFilename); + + if(d_help != NULL) { + delete d_help; + d_help = NULL; + } } std::cout <<"cMo : "<<std::endl << cMo <<std::endl; @@ -350,6 +445,7 @@ void vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::vector<v { vpDisplay::display(I) ; vpDisplay::flush(I) ; + vpDisplay *d_help = NULL; vpPose pose ; vpPoint *P = NULL; P = new vpPoint [points3D_list.size()] ; @@ -357,27 +453,33 @@ void vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::vector<v P[i].setWorldCoordinates(points3D_list[i].get_oX(),points3D_list[i].get_oY(),points3D_list[i].get_oZ()) ; vpImage<vpRGBa> Iref ; - //Display window creation and initialistation - #if defined VISP_HAVE_X11 - vpDisplayX d; - #elif defined VISP_HAVE_GDI - vpDisplayGDI d; - #elif defined VISP_HAVE_OPENCV - vpDisplayOpenCV d; - #endif - - if(displayFile != ""){ - try{ - std::cout << displayFile.c_str() << std::endl; - vpImageIo::read(Iref,displayFile.c_str()) ; - #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) - d.init(Iref,10,500, "Where to initialize...") ; - vpDisplay::display(Iref) ; - vpDisplay::flush(Iref); - #endif - } - catch(...){} - } + //Display window creation and initialisation + if(vpIoTools::checkFilename(displayFile)){ + try{ + std::cout << "Load image to help initialization: " << displayFile << std::endl; +#if defined VISP_HAVE_X11 + d_help = new vpDisplayX ; +#elif defined VISP_HAVE_GDI + d_help = new vpDisplayGDI; +#elif defined VISP_HAVE_OPENCV + d_help = new vpDisplayOpenCV; +#endif + + vpImageIo::read(Iref, displayFile) ; +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) + d_help->init(Iref, I.display->getWindowXPosition()+(int)I.getWidth()+80, I.display->getWindowYPosition(), + "Where to initialize...") ; + vpDisplay::display(Iref) ; + vpDisplay::flush(Iref); +#endif + } + catch(...) { + if(d_help != NULL) { + delete d_help; + d_help = NULL; + } + } + } vpImagePoint ip; bool isWellInit = false; @@ -416,7 +518,7 @@ void vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::vector<v pose.computePose(vpPose::VIRTUAL_VS, cMo); display(I, cMo, cam, vpColor::green, 1, true); - vpDisplay::displayCharString(I, 15, 10, + vpDisplay::displayText(I, 15, 10, "left click to validate, right click to re initialize object", vpColor::red); @@ -441,6 +543,10 @@ void vpMbTracker::initClick(const vpImage<unsigned char>& I, const std::vector<v vpDisplay::displayFrame(I, cMo, cam, 0.05, vpColor::red); delete [] P; + if(d_help != NULL) { + delete d_help; + d_help = NULL; + } init(I); } @@ -486,6 +592,12 @@ void vpMbTracker::initFromPoints( const vpImage<unsigned char>& I, const std::st double X, Y, Z; finit >> size ; std::cout << "number of points " << size << std::endl ; + + if (size > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of points."); + } + vpPoint *P = new vpPoint [size]; vpPose pose ; @@ -629,11 +741,11 @@ void vpMbTracker::initFromPose(const vpImage<unsigned char>& I, const std::strin Initialise the tracking thanks to the pose. \param I : Input image - \param cMo : Pose matrix. + \param cMo_ : Pose matrix. */ -void vpMbTracker::initFromPose(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo) +void vpMbTracker::initFromPose(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo_) { - this->cMo = cMo; + this->cMo = cMo_; init(I); } @@ -668,6 +780,171 @@ void vpMbTracker::savePose(const std::string &filename) finitpos.close(); } + +void vpMbTracker::addPolygon(const std::vector<vpPoint>& corners, const int idFace, const std::string &polygonName, + const bool useLod, const double minPolygonAreaThreshold, const double minLineLengthThreshold) +{ + std::vector<vpPoint> corners_without_duplicates; + corners_without_duplicates.push_back(corners[0]); + for (unsigned int i=0; i < corners.size()-1; i++) { + if (std::fabs(corners[i].get_oX() - corners[i+1].get_oX()) > std::fabs(corners[i].get_oX())*std::numeric_limits<double>::epsilon() + || std::fabs(corners[i].get_oY() - corners[i+1].get_oY()) > std::fabs(corners[i].get_oY())*std::numeric_limits<double>::epsilon() + || std::fabs(corners[i].get_oZ() - corners[i+1].get_oZ()) > std::fabs(corners[i].get_oZ())*std::numeric_limits<double>::epsilon()) { + corners_without_duplicates.push_back(corners[i+1]); + } + } + + vpMbtPolygon polygon; + polygon.setNbPoint((unsigned int)corners_without_duplicates.size()); + polygon.setIndex((int)idFace); + polygon.setName(polygonName); + polygon.setLod(useLod); + +// //if(minPolygonAreaThreshold != -1.0) { +// if(std::fabs(minPolygonAreaThreshold + 1.0) > std::fabs(minPolygonAreaThreshold)*std::numeric_limits<double>::epsilon()) { +// polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold); +// } +// +// //if(minLineLengthThreshold != -1.0) { +// if(std::fabs(minLineLengthThreshold + 1.0) > std::fabs(minLineLengthThreshold)*std::numeric_limits<double>::epsilon()) { +// polygon.setMinLineLengthThresh(minLineLengthThreshold); +// } + + polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold); + polygon.setMinLineLengthThresh(minLineLengthThreshold); + + for(unsigned int j = 0; j < corners_without_duplicates.size(); j++) { + polygon.addPoint(j, corners_without_duplicates[j]); + } + + faces.addPolygon(&polygon); + + if(clippingFlag != vpMbtPolygon::NO_CLIPPING) + faces.getPolygon().back()->setClipping(clippingFlag); + + if((clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) + faces.getPolygon().back()->setNearClippingDistance(distNearClip); + + if((clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) + faces.getPolygon().back()->setFarClippingDistance(distFarClip); +} + +void vpMbTracker::addPolygon(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int idFace, const std::string &polygonName, const bool useLod, const double minPolygonAreaThreshold) +{ + vpMbtPolygon polygon; + polygon.setNbPoint(4); + polygon.setName(polygonName); + polygon.setLod(useLod); + +// //if(minPolygonAreaThreshold != -1.0) { +// if(std::fabs(minPolygonAreaThreshold + 1.0) > std::fabs(minPolygonAreaThreshold)*std::numeric_limits<double>::epsilon()) { +// polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold); +// } + polygon.setMinPolygonAreaThresh(minPolygonAreaThreshold); + //Non sense to set minLineLengthThreshold for circle + //but used to be coherent when applying LOD settings for all polygons + polygon.setMinLineLengthThresh(minLineLengthThresholdGeneral); + + { + // Create the 4 points of the circle bounding box + vpPlane plane(p1, p2, p3, vpPlane::object_frame); + + // Matrice de passage entre world et circle frame + double norm_X = sqrt(vpMath::sqr(p2.get_oX()-p1.get_oX()) + + vpMath::sqr(p2.get_oY()-p1.get_oY()) + + vpMath::sqr(p2.get_oZ()-p1.get_oZ())); + double norm_Y = sqrt(vpMath::sqr(plane.getA()) + + vpMath::sqr(plane.getB()) + + vpMath::sqr(plane.getC())); + vpRotationMatrix wRc; + vpColVector x(3),y(3),z(3); + // X axis is P2-P1 + x[0] = (p2.get_oX()-p1.get_oX()) / norm_X; + x[1] = (p2.get_oY()-p1.get_oY()) / norm_X; + x[2] = (p2.get_oZ()-p1.get_oZ()) / norm_X; + // Y axis is the normal of the plane + y[0] = plane.getA() / norm_Y; + y[1] = plane.getB() / norm_Y; + y[2] = plane.getC() / norm_Y; + // Z axis = X ^ Y + z = vpColVector::crossProd(x, y); + for (unsigned int i=0; i< 3; i++) { + wRc[i][0] = x[i]; + wRc[i][1] = y[i]; + wRc[i][2] = z[i]; + } + + vpTranslationVector wtc(p1.get_oX(), p1.get_oY(), p1.get_oZ()); + vpHomogeneousMatrix wMc(wtc, wRc); + + vpColVector c_p(4); // A point in the circle frame that is on the bbox + c_p[0] = radius; + c_p[1] = 0; + c_p[2] = radius; + c_p[3] = 1; + + // Matrix to rotate a point by 90 deg around Y in the circle frame + for(unsigned int i=0; i<4; i++) { + vpColVector w_p(4); // A point in the word frame + vpHomogeneousMatrix cMc_90(vpTranslationVector(), vpRotationMatrix(0,vpMath::rad(90*i), 0)); + w_p = wMc * cMc_90 * c_p; + + vpPoint w_P; + w_P.setWorldCoordinates(w_p[0], w_p[1], w_p[2]); + + polygon.addPoint(i,w_P); + } + } + + polygon.setIndex(idFace); + faces.addPolygon(&polygon); + + if(clippingFlag != vpMbtPolygon::NO_CLIPPING) + faces.getPolygon().back()->setClipping(clippingFlag); + + if((clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) + faces.getPolygon().back()->setNearClippingDistance(distNearClip); + + if((clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) + faces.getPolygon().back()->setFarClippingDistance(distFarClip); +} + +void vpMbTracker::addPolygon(const vpPoint& p1, const vpPoint &p2, const int idFace, const std::string &polygonName, + const bool useLod, const double minLineLengthThreshold) +{ + // A polygon as a single line that corresponds to the revolution axis of the cylinder + vpMbtPolygon polygon; + polygon.setNbPoint(2); + + polygon.addPoint(0, p1); + polygon.addPoint(1, p2); + + polygon.setIndex(idFace) ; + polygon.setName(polygonName); + polygon.setLod(useLod); + +// //if(minLineLengthThreshold != -1.0) { +// if(std::fabs(minLineLengthThreshold + 1.0) > std::fabs(minLineLengthThreshold)*std::numeric_limits<double>::epsilon()) { +// polygon.setMinLineLengthThresh(minLineLengthThreshold); +// } + polygon.setMinLineLengthThresh(minLineLengthThreshold); + //Non sense to set minPolygonAreaThreshold for cylinder + //but used to be coherent when applying LOD settings for all polygons + polygon.setMinPolygonAreaThresh(minPolygonAreaThresholdGeneral); + + faces.addPolygon(&polygon) ; + + if(clippingFlag != vpMbtPolygon::NO_CLIPPING) + faces.getPolygon().back()->setClipping(clippingFlag); + + if((clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) + faces.getPolygon().back()->setNearClippingDistance(distNearClip); + + if((clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) + faces.getPolygon().back()->setFarClippingDistance(distFarClip); +} + /*! Load a 3D model from the file in parameter. This file must either be a vrml file (.wrl) or a CAO file (.cao). CAO format is described in the @@ -679,28 +956,69 @@ void vpMbTracker::savePose(const std::string &filename) int main() { ... -#ifdef VISP_HAVE_COIN +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) SoDB::finish(); #endif } \endcode - \throw vpException::ioError if the file cannot be open, or if its extension is not wrl or cao. - \param modelFile : the file containing the model. + \param modelFile : the file containing the the 3D model description. + The extension of this file is either .wrl or .cao. + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. */ void -vpMbTracker::loadModel(const std::string& modelFile) +vpMbTracker::loadModel(const char *modelFile, const bool verbose) +{ + loadModel( std::string(modelFile), verbose ); +} + +/*! + Load a 3D model from the file in parameter. This file must either be a vrml + file (.wrl) or a CAO file (.cao). CAO format is described in the + loadCAOModel() method. + + \warning When this class is called to load a vrml model, remember that you + have to call Call SoDD::finish() before ending the program. + \code +int main() +{ + ... +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + SoDB::finish(); +#endif +} + \endcode + + \throw vpException::ioError if the file cannot be open, or if its extension is + not wrl or cao. + + \param modelFile : the file containing the the 3D model description. + The extension of this file is either .wrl or .cao. + \param verbose : verbose option to print additional information when loading CAO model files which include other + CAO model files. +*/ +void +vpMbTracker::loadModel(const std::string& modelFile, const bool verbose) { std::string::const_iterator it; - if(vpIoTools::checkFilename(modelFile)){ + if(vpIoTools::checkFilename(modelFile)) { it = modelFile.end(); - if((*(it-1) == 'o' && *(it-2) == 'a' && *(it-3) == 'c' && *(it-4) == '.') || + if((*(it-1) == 'o' && *(it-2) == 'a' && *(it-3) == 'c' && *(it-4) == '.') || (*(it-1) == 'O' && *(it-2) == 'A' && *(it-3) == 'C' && *(it-4) == '.') ){ - loadCAOModel(modelFile); + std::vector<std::string> vectorOfModelFilename; + int startIdFace = 0; + nbPoints = 0; + nbLines = 0; + nbPolygonLines = 0; + nbPolygonPoints = 0; + nbCylinders = 0; + nbCircles = 0; + loadCAOModel(modelFile, vectorOfModelFilename, startIdFace, verbose, true); } else if((*(it-1) == 'l' && *(it-2) == 'r' && *(it-3) == 'w' && *(it-4) == '.') || (*(it-1) == 'L' && *(it-2) == 'R' && *(it-3) == 'W' && *(it-4) == '.') ){ @@ -729,7 +1047,7 @@ vpMbTracker::loadModel(const std::string& modelFile) int main() { ... -#ifdef VISP_HAVE_COIN +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) SoDB::finish(); #endif } @@ -753,7 +1071,7 @@ void vpMbTracker::loadVRMLModel(const std::string& modelFile) { #ifdef VISP_HAVE_COIN - SoDB::init(); // Call SoDD::finish() before ending the program. + SoDB::init(); // Call SoDB::finish() before ending the program. SoInput in; SbBool ok = in.openFile(modelFile.c_str()); @@ -788,7 +1106,7 @@ vpMbTracker::loadVRMLModel(const std::string& modelFile) in.closeFile(); vpHomogeneousMatrix transform; - unsigned int indexFace = 0; + int indexFace = 0; extractGroup(sceneGraphVRML2, transform, indexFace); sceneGraphVRML2->unref(); @@ -798,221 +1116,664 @@ vpMbTracker::loadVRMLModel(const std::string& modelFile) #endif } +void vpMbTracker::removeComment(std::ifstream& fileId) { + char c; + + fileId.get(c); + while (!fileId.fail() && (c == '#')) { + fileId.ignore(256, '\n'); + fileId.get(c); + } + fileId.unget(); +} + +std::map<std::string, std::string> vpMbTracker::parseParameters(std::string& endLine) { + std::map<std::string, std::string> mapOfParams; + + bool exit = false; + while (!endLine.empty() && !exit) { + exit = true; + + for (std::map<std::string, std::string>::const_iterator it = + mapOfParameterNames.begin(); it != mapOfParameterNames.end(); + ++it) { + endLine = trim(endLine); + // std::cout << "endLine=" << endLine << std::endl; + std::string param(it->first + "="); + + //Compare with a potential parameter + if (endLine.compare(0, param.size(), param) == 0) { + exit = false; + endLine = endLine.substr(param.size()); + + bool parseQuote = false; + if (it->second == "string") { + //Check if the string is between quotes + if (endLine.size() > 2 && endLine[0] == '"') { + parseQuote = true; + endLine = endLine.substr(1); + size_t pos = endLine.find_first_of('"'); + + if (pos != std::string::npos) { + mapOfParams[it->first] = endLine.substr(0, pos); + endLine = endLine.substr(pos + 1); + } else { + parseQuote = false; + } + } + } + + if (!parseQuote) { + //Deal with space or tabulation after parameter value to substring + // to the next sequence + size_t pos1 = endLine.find_first_of(' '); + size_t pos2 = endLine.find_first_of('\t'); + size_t pos = pos1 < pos2 ? pos1 : pos2; + + mapOfParams[it->first] = endLine.substr(0, pos); + endLine = endLine.substr(pos + 1); + } + } + } + } + +// for(std::map<std::string, std::string>::const_iterator it = mapOfParams.begin(); it != mapOfParams.end(); ++it) { +// std::cout << it->first << "=" << it->second << std::endl; +// } + + return mapOfParams; +} + /*! - Load a 3D model contained in a .cao file. + Load a 3D model contained in a *.cao file. - the structure of the file is (without the comments) : + Since ViSP 2.9.1, lines starting with # character are considered as comments. + It is also possible to add comment at the end of the lines. No specific character is requested before the comment. + In the following example we use "//" but it could be an other character. + + Since ViSP 2.9.1, circles are supported. + + The structure of the file is : \code V1 - 8 // Number of points describing the object - 0.01 0.01 0.01 // \ - ... // | coordinates of the points in the object frame (in m.) - 0.01 0.01 0.01 // / - 3 // Number of lines to track. - 0 2 // \ - 1 4 // | Index of the points representing the extremities of the lines - 1 5 // / - 0 // Number of polygon (face) to track using the line previously described - // Face described as follow : nbLine IndexLine1 indexLine2 ... indexLineN - 3 // Number of polygon (face) to track using the line previously described + # Number of points describing the object + 8 + 0.01 0.01 0.01 // point with index 0 \ + ... // ... | coordinates of the points in the object frame (in m.) + 0.01 0.01 0.01 // point with index 7 / + # Number of lines to track. + 3 + 0 2 // line with index 0 \ + 1 4 // | Index of the points representing the extremities of the lines + 1 5 // line with index 2 / + # Number of polygon (face) to track using the line previously described + 1 + 3 0 1 2 // Face described as follow : nbLine indexLine1 indexLine2 ... indexLineN + # Number of polygon (face) to track using the points previously described + 3 4 0 2 3 4 // Face described as follow : nbPoint IndexPoint1 IndexPoint2 ... IndexPointN 4 1 3 5 7 3 1 5 6 - 1 // Number of cylinder + # Number of cylinder + 1 6 7 0.05 // Index of the limits points on the axis (used to know the 'height' of the cylinder) and radius of the cyclinder (in m.) + # Number of circle + 1 + 0.5 0 1 2 // radius, index center point, index 2 other points on the plane containing the circle \endcode - \param modelFile : Full name of the .CAO file containing the model. + \param modelFile : Full name of the main *.cao file containing the model. + \param vectorOfModelFilename : A vector of *.cao files. + \param startIdFace : Current Id of the face. + \param verbose : If true, will print additional information with CAO model files which include other CAO model files. + \param parent : This parameter is set to true when parsing a parent CAO model file, and false when parsing an included + CAO model file. */ -void -vpMbTracker::loadCAOModel(const std::string& modelFile) -{ +void +vpMbTracker::loadCAOModel(const std::string& modelFile, + std::vector<std::string>& vectorOfModelFilename, int& startIdFace, + const bool verbose, const bool parent) { std::ifstream fileId; - fileId.exceptions ( std::ifstream::failbit | std::ifstream::eofbit ); - fileId.open (modelFile.c_str(), std::ifstream::in); - if(fileId.fail()) { - std::cout << "cannot read CAO model file: " << modelFile << std::endl; - throw vpException(vpException::ioError, "cannot read CAO model file"); + fileId.exceptions(std::ifstream::failbit | std::ifstream::eofbit); + fileId.open(modelFile.c_str(), std::ifstream::in); + if (fileId.fail()) { + std::cout << "cannot read CAO model file: " << modelFile << std::endl; + throw vpException(vpException::ioError, "cannot read CAO model file"); } - try{ - char c; - // Extraction of the version (remove empty line and commented ones (comment - // line begin with the #)). - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n'); - fileId.unget(); - - int caoVersion; - fileId.get(c); - if(c=='V'){ - fileId >> caoVersion; - } - else{ - std::cout <<"in vpMbEdgeTracker::loadCAOModel -> Bad parameter header file : use V0, V1, ..."; - throw vpException(vpException::badValue, - "in vpMbEdgeTracker::loadCAOModel -> Bad parameter header file : use V0, V1, ..."); - } + if(verbose) { + std::cout << "Model file : " << modelFile << std::endl; + } + vectorOfModelFilename.push_back(modelFile); + + try { + char c; + // Extraction of the version (remove empty line and commented ones (comment + // line begin with the #)). + //while ((fileId.get(c) != NULL) && (c == '#')) fileId.ignore(256, '\n'); + removeComment(fileId); + + //////////////////////////Read CAO Version (V1, V2, ...)////////////////////////// + int caoVersion; + fileId.get(c); + if (c == 'V') { + fileId >> caoVersion; + fileId.ignore(256, '\n'); // skip the rest of the line + } else { + std::cout + << "in vpMbTracker::loadCAOModel() -> Bad parameter header file : use V0, V1, ..."; + throw vpException(vpException::badValue, + "in vpMbTracker::loadCAOModel() -> Bad parameter header file : use V0, V1, ..."); + } + + removeComment(fileId); - while( (fileId.get(c)!=NULL)&&(c!='\n')) ; - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n') ; - fileId.unget(); - - //Read the points - unsigned int caoNbrPoint; - fileId >> caoNbrPoint; - std::cout << "> " << caoNbrPoint << " points" << std::endl; - vpPoint *caoPoints = NULL; - if (caoNbrPoint > 0) - caoPoints = new vpPoint[caoNbrPoint]; - - double x ; // 3D coordinates - double y ; - double z ; - - int i ; // image coordinate (used for matching) - int j ; - - - for(unsigned int k=0; k < caoNbrPoint; k++){ - fileId >> x ; - fileId >> y ; - fileId >> z ; - if (caoVersion == 2){ - fileId >> i ; - fileId >> j ; + + //////////////////////////Read the header part if present////////////////////////// + std::string line; + std::string prefix = "load"; + + fileId.get(c); + fileId.unget(); + bool header = false; + while(c == 'l' || c == 'L') { + header = true; + + getline(fileId, line); + if(!line.compare(0, prefix.size(), prefix)) { + + //Get the loaded model pathname + std::string headerPathname = line.substr(6); + size_t firstIndex = headerPathname.find_first_of("\")"); + headerPathname = headerPathname.substr(0, firstIndex); + + std::string headerPath = headerPathname; + if(!vpIoTools::isAbsolutePathname(headerPathname)) { + std::string parentDirectory = vpIoTools::getParent(modelFile); + headerPath = vpIoTools::createFilePath(parentDirectory, headerPathname); + } + + bool cyclic = false; + std::string headerModelFilename = vpIoTools::getName(headerPath); + if (!headerModelFilename.compare(vpIoTools::getName(modelFile))) { + cyclic = true; + } + + for (std::vector<std::string>::const_iterator it = + vectorOfModelFilename.begin(); + it != vectorOfModelFilename.end() - 1 && !cyclic; + ++it) { + std::string loadedModelFilename = vpIoTools::getName(*it); + if (!headerModelFilename.compare(loadedModelFilename)) { + cyclic = true; + } + } + + if (!cyclic) { + if (vpIoTools::checkFilename(modelFile)) { + loadCAOModel(headerPath, vectorOfModelFilename, startIdFace, verbose, false); + } else { + throw vpException(vpException::ioError, + "file cannot be open"); + } + } else { + std::cout << "WARNING Cyclic dependency detected with file " + << headerPath << " declared in " << modelFile << std::endl; + } + } else { + header = false; + } + + removeComment(fileId); + fileId.get(c); + fileId.unget(); } - if(k != caoNbrPoint-1){// the rest of the line is removed (not the last one due to the need to remove possible comments). - fileId.ignore(256,'\n'); + + //////////////////////////Read the point declaration part////////////////////////// + unsigned int caoNbrPoint; + fileId >> caoNbrPoint; + fileId.ignore(256, '\n'); // skip the rest of the line + + nbPoints += caoNbrPoint; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbrPoint << " points" << std::endl; } - caoPoints[k].setWorldCoordinates(x, y, z) ; - } - while( (fileId.get(c)!=NULL)&&(c!='\n')) ; - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n'); - fileId.unget(); + if (caoNbrPoint > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of points in the CAO model."); + } + + if (caoNbrPoint == 0 && !header) { + throw vpException(vpException::badValue, + "in vpMbTracker::loadCAOModel() -> no points are defined"); + } + vpPoint *caoPoints = new vpPoint[caoNbrPoint]; + + double x; // 3D coordinates + double y; + double z; - //Read the lines - unsigned int caoNbrLine; - fileId >> caoNbrLine; - unsigned int *caoLinePoints = NULL; - std::cout << "> " << caoNbrLine << " lines" << std::endl; - if (caoNbrLine > 0) - caoLinePoints = new unsigned int[2*caoNbrLine]; + int i; // image coordinate (used for matching) + int j; - unsigned int index1, index2; + for (unsigned int k = 0; k < caoNbrPoint; k++) { + removeComment(fileId); - for(unsigned int k=0; k < caoNbrLine ; k++){ - fileId >> index1 ; - fileId >> index2 ; + fileId >> x; + fileId >> y; + fileId >> z; + fileId.ignore(256, '\n'); // skip the rest of the line - caoLinePoints[2*k] = index1; - caoLinePoints[2*k+1] = index2; + if (caoVersion == 2) { + fileId >> i; + fileId >> j; + } - if(index1 < caoNbrPoint && index2 < caoNbrPoint){ - std::vector<vpPoint> extremities; - extremities.push_back(caoPoints[index1]); - extremities.push_back(caoPoints[index2]); - initFaceFromCorners(extremities, k); + caoPoints[k].setWorldCoordinates(x, y, z); } - else{ - vpTRACE(" line %d has wrong coordinates.", k); + + + removeComment(fileId); + + + //////////////////////////Read the segment declaration part////////////////////////// + //Store in a map the potential segments to add + std::map<std::pair<unsigned int, unsigned int>, SegmentInfo > segmentTemporaryMap; + unsigned int caoNbrLine; + fileId >> caoNbrLine; + fileId.ignore(256, '\n'); // skip the rest of the line + + nbLines += caoNbrLine; + unsigned int *caoLinePoints = NULL; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbrLine << " lines" << std::endl; } - if(k != caoNbrLine-1){// the rest of the line is removed (not the last one due to the need to remove possible comments). - fileId.ignore(256,'\n'); + if (caoNbrLine > 100000) { + delete[] caoPoints; + throw vpException(vpException::badValue, + "Exceed the max number of lines in the CAO model."); } - } - while( (fileId.get(c)!=NULL)&&(c!='\n')) ; - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n'); - fileId.unget(); - - - /* Load polygon from the lines extracted earlier - (the first point of the line is used)*/ - unsigned int caoNbrPolygonLine; - fileId >> caoNbrPolygonLine; - std::cout << "> " << caoNbrPolygonLine << " polygon line" << std::endl; - unsigned int index; - for(unsigned int k = 0;k < caoNbrPolygonLine; k++){ - unsigned int nbLinePol; - fileId >> nbLinePol; - std::vector<vpPoint> corners; - for(unsigned int i = 0; i < nbLinePol; i++){ - fileId >> index; - corners.push_back(caoPoints[caoLinePoints[2*index]]); + if (caoNbrLine > 0) + caoLinePoints = new unsigned int[2 * caoNbrLine]; + + unsigned int index1, index2; + //Initialization of idFace with startIdFace for dealing with recursive load in header + int idFace = startIdFace; + + for (unsigned int k = 0; k < caoNbrLine; k++) { + removeComment(fileId); + + fileId >> index1; + fileId >> index2; + + //////////////////////////Read the parameter value if present////////////////////////// + //Get the end of the line + char buffer[256]; + fileId.getline(buffer, 256); + std::string endLine(buffer); + std::map<std::string, std::string> mapOfParams = parseParameters(endLine); + +// fileId.ignore(256, '\n'); // skip the rest of the line + + std::string segmentName = ""; + double minLineLengthThresh = !applyLodSettingInConfig ? minLineLengthThresholdGeneral : 50.0; + bool useLod = !applyLodSettingInConfig ? useLodGeneral : false; + if(mapOfParams.find("name") != mapOfParams.end()) { + segmentName = mapOfParams["name"]; + } + if(mapOfParams.find("minLineLengthThreshold") != mapOfParams.end()) { + minLineLengthThresh = std::atof(mapOfParams["minLineLengthThreshold"].c_str()); + } + if(mapOfParams.find("useLod") != mapOfParams.end()) { + useLod = parseBoolean(mapOfParams["useLod"]); + } + + SegmentInfo segmentInfo; + segmentInfo.name = segmentName; + segmentInfo.useLod = useLod; + segmentInfo.minLineLengthThresh = minLineLengthThresh; + + caoLinePoints[2 * k] = index1; + caoLinePoints[2 * k + 1] = index2; + + if (index1 < caoNbrPoint && index2 < caoNbrPoint) { + std::vector<vpPoint> extremities; + extremities.push_back(caoPoints[index1]); + extremities.push_back(caoPoints[index2]); + segmentInfo.extremities = extremities; + + std::pair<unsigned int, unsigned int> key(index1, index2); + + segmentTemporaryMap[key] = segmentInfo; +// addPolygon(extremities, idFace++); +// initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added + } else { + vpTRACE(" line %d has wrong coordinates.", k); + } + } + + removeComment(fileId); + + + //////////////////////////Read the face segment declaration part////////////////////////// + /* Load polygon from the lines extracted earlier (the first point of the line is used)*/ + //Store in a vector the indexes of the segments added in the face segment case + std::vector<std::pair<unsigned int, unsigned int> > faceSegmentKeyVector; + unsigned int caoNbrPolygonLine; + fileId >> caoNbrPolygonLine; + fileId.ignore(256, '\n'); // skip the rest of the line + + nbPolygonLines += caoNbrPolygonLine; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbrPolygonLine << " polygon lines" << std::endl; } - if(k != caoNbrPolygonLine-1){// the rest of the line is removed (not the last one due to the need to remove possible comments). - fileId.ignore(256,'\n'); + + if (caoNbrPolygonLine > 100000) { + delete[] caoPoints; + delete[] caoLinePoints; + throw vpException(vpException::badValue, + "Exceed the max number of polygon lines."); } - initFaceFromCorners(corners, k); - } - while( (fileId.get(c)!=NULL)&&(c!='\n')) ; - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n'); - fileId.unget(); + unsigned int index; + for (unsigned int k = 0; k < caoNbrPolygonLine; k++) { + removeComment(fileId); + + unsigned int nbLinePol; + fileId >> nbLinePol; + std::vector<vpPoint> corners; + if (nbLinePol > 100000) { + throw vpException(vpException::badValue, "Exceed the max number of lines."); + } + + for (unsigned int n = 0; n < nbLinePol; n++) { + fileId >> index; +// if (2 * index > 2 * caoNbrLine - 1) { + if(index >= caoNbrLine) { + throw vpException(vpException::badValue, "Exceed the max number of lines."); + } + corners.push_back(caoPoints[caoLinePoints[2 * index]]); + corners.push_back(caoPoints[caoLinePoints[2 * index + 1]]); + + std::pair<unsigned int, unsigned int> key(caoLinePoints[2 * index], caoLinePoints[2 * index + 1]); + faceSegmentKeyVector.push_back(key); + } + + //////////////////////////Read the parameter value if present////////////////////////// + //Get the end of the line + char buffer[256]; + fileId.getline(buffer, 256); + std::string endLine(buffer); + std::map<std::string, std::string> mapOfParams = parseParameters(endLine); + +// fileId.ignore(256, '\n'); // skip the rest of the line + + std::string polygonName = ""; + bool useLod = !applyLodSettingInConfig ? useLodGeneral : false; + double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0; + if(mapOfParams.find("name") != mapOfParams.end()) { + polygonName = mapOfParams["name"]; + } + if(mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) { + minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str()); + } + if(mapOfParams.find("useLod") != mapOfParams.end()) { + useLod = parseBoolean(mapOfParams["useLod"]); + } + + addPolygon(corners, idFace++, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral); + // initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added + initFaceFromLines(*(faces.getPolygon().back())); // Init from the last polygon that was added + } + + //Add the segments which were not already added in the face segment case + for(std::map<std::pair<unsigned int, unsigned int>, SegmentInfo >::const_iterator it = + segmentTemporaryMap.begin(); it != segmentTemporaryMap.end(); ++it) { + if(std::find(faceSegmentKeyVector.begin(), faceSegmentKeyVector.end(), it->first) == faceSegmentKeyVector.end()) { + addPolygon(it->second.extremities, idFace++, it->second.name, it->second.useLod, minPolygonAreaThresholdGeneral, + it->second.minLineLengthThresh); + initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added + } + } + + removeComment(fileId); + + + //////////////////////////Read the face point declaration part////////////////////////// /* Extract the polygon using the point coordinates (top of the file) */ - unsigned int caoNbrPolygonPoint; - fileId >> caoNbrPolygonPoint; - std::cout << "> " << caoNbrPolygonPoint << " polygon point" << std::endl; - for(unsigned int k = 0;k < caoNbrPolygonPoint; k++){ - int nbPointPol; - fileId >> nbPointPol; - std::vector<vpPoint> corners; - for(int i = 0; i < nbPointPol; i++){ - fileId >> index; - corners.push_back(caoPoints[index]); + unsigned int caoNbrPolygonPoint; + fileId >> caoNbrPolygonPoint; + fileId.ignore(256, '\n'); // skip the rest of the line + + nbPolygonPoints += caoNbrPolygonPoint; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbrPolygonPoint << " polygon points" + << std::endl; } - if(k != caoNbrPolygonPoint-1){// the rest of the line is removed (not the last one due to the need to remove possible comments). - fileId.ignore(256,'\n'); + + if (caoNbrPolygonPoint > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of polygon point."); } - initFaceFromCorners(corners, k); - } - unsigned int caoNbCylinder; - try{ - while( (fileId.get(c)!=NULL)&&(c!='\n')) ; - while( (fileId.get(c)!=NULL)&&(c == '#')) fileId.ignore(256,'\n'); - fileId.unget(); + for (unsigned int k = 0; k < caoNbrPolygonPoint; k++) { + removeComment(fileId); + + unsigned int nbPointPol; + fileId >> nbPointPol; + if (nbPointPol > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of points."); + } + std::vector<vpPoint> corners; + for (unsigned int n = 0; n < nbPointPol; n++) { + fileId >> index; + if (index > caoNbrPoint - 1) { + throw vpException(vpException::badValue, + "Exceed the max number of points."); + } + corners.push_back(caoPoints[index]); + } + + + //////////////////////////Read the parameter value if present////////////////////////// + //Get the end of the line + char buffer[256]; + fileId.getline(buffer, 256); + std::string endLine(buffer); + std::map<std::string, std::string> mapOfParams = parseParameters(endLine); + +// fileId.ignore(256, '\n'); // skip the rest of the line - if(fileId.eof()){// check if not at the end of the file (for old style files) - delete[] caoPoints; - delete[] caoLinePoints; - return ; + std::string polygonName = ""; + bool useLod = !applyLodSettingInConfig ? useLodGeneral : false; + double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0; + if(mapOfParams.find("name") != mapOfParams.end()) { + polygonName = mapOfParams["name"]; + } + if(mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) { + minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str()); + } + if(mapOfParams.find("useLod") != mapOfParams.end()) { + useLod = parseBoolean(mapOfParams["useLod"]); + } + + + addPolygon(corners, idFace++, polygonName, useLod, minPolygonAreaThreshold, minLineLengthThresholdGeneral); + initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added } - /* Extract the cylinders */ + //////////////////////////Read the cylinder declaration part////////////////////////// + unsigned int caoNbCylinder; + try { + removeComment(fileId); + + if (fileId.eof()) { // check if not at the end of the file (for old style files) + delete[] caoPoints; + delete[] caoLinePoints; + return; + } + + /* Extract the cylinders */ + fileId >> caoNbCylinder; + fileId.ignore(256, '\n'); // skip the rest of the line + + nbCylinders += caoNbCylinder; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbCylinder << " cylinders" << std::endl; + } + if (caoNbCylinder > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of cylinders."); + } - fileId >> caoNbCylinder; - std::cout << "> " << caoNbCylinder << " cylinder" << std::endl; - for(unsigned int k=0; k<caoNbCylinder; ++k){ - double radius; - unsigned int indexP1, indexP2; - fileId >> indexP1; - fileId >> indexP2; - fileId >> radius; - initCylinder(caoPoints[indexP1], caoPoints[indexP2], radius); + for (unsigned int k = 0; k < caoNbCylinder; ++k) { + removeComment(fileId); + + double radius; + unsigned int indexP1, indexP2; + fileId >> indexP1; + fileId >> indexP2; + fileId >> radius; + + //////////////////////////Read the parameter value if present////////////////////////// + //Get the end of the line + char buffer[256]; + fileId.getline(buffer, 256); + std::string endLine(buffer); + std::map<std::string, std::string> mapOfParams = parseParameters(endLine); + +// fileId.ignore(256, '\n'); // skip the rest of the line + + std::string polygonName = ""; + bool useLod = !applyLodSettingInConfig ? useLodGeneral : false; + double minLineLengthThreshold = !applyLodSettingInConfig ? minLineLengthThresholdGeneral : 50.0; + if(mapOfParams.find("name") != mapOfParams.end()) { + polygonName = mapOfParams["name"]; + } + if(mapOfParams.find("minLineLengthThreshold") != mapOfParams.end()) { + minLineLengthThreshold = std::atof(mapOfParams["minLineLengthThreshold"].c_str()); + } + if(mapOfParams.find("useLod") != mapOfParams.end()) { + useLod = parseBoolean(mapOfParams["useLod"]); + } + + addPolygon(caoPoints[indexP1], caoPoints[indexP2], idFace, polygonName, useLod, minLineLengthThreshold); + initCylinder(caoPoints[indexP1], caoPoints[indexP2], radius, idFace++, polygonName); + } + + } catch (...) { + std::cerr + << "Cannot get the number of cylinders. Defaulting to zero." + << std::endl; + caoNbCylinder = 0; } - }catch(...){ - std::cerr << "Cannot get the number of cylinders. Defaulting to zero." << std::endl; - caoNbCylinder = 0; - } - - delete[] caoPoints; - delete[] caoLinePoints; - }catch(std::ifstream::failure e){ - std::cerr << "Cannot read line!" << std::endl; - throw vpException(vpException::ioError, "cannot read line"); - } -} + //////////////////////////Read the circle declaration part////////////////////////// + unsigned int caoNbCircle; + try { + removeComment(fileId); + if (fileId.eof()) { // check if not at the end of the file (for old style files) + delete[] caoPoints; + delete[] caoLinePoints; + return; + } + /* Extract the circles */ + fileId >> caoNbCircle; + fileId.ignore(256, '\n'); // skip the rest of the line + nbCircles += caoNbCircle; + if(verbose || vectorOfModelFilename.size() == 1) { + std::cout << "> " << caoNbCircle << " circles" << std::endl; + } + + if (caoNbCircle > 100000) { + throw vpException(vpException::badValue, + "Exceed the max number of cicles."); + } + + for (unsigned int k = 0; k < caoNbCircle; ++k) { + removeComment(fileId); + + double radius; + unsigned int indexP1, indexP2, indexP3; + fileId >> radius; + fileId >> indexP1; + fileId >> indexP2; + fileId >> indexP3; + + //////////////////////////Read the parameter value if present////////////////////////// + //Get the end of the line + char buffer[256]; + fileId.getline(buffer, 256); + std::string endLine(buffer); + std::map<std::string, std::string> mapOfParams = parseParameters(endLine); + +// fileId.ignore(256, '\n'); // skip the rest of the line + + std::string polygonName = ""; + bool useLod = !applyLodSettingInConfig ? useLodGeneral : false; + double minPolygonAreaThreshold = !applyLodSettingInConfig ? minPolygonAreaThresholdGeneral : 2500.0; + if(mapOfParams.find("name") != mapOfParams.end()) { + polygonName = mapOfParams["name"]; + } + if(mapOfParams.find("minPolygonAreaThreshold") != mapOfParams.end()) { + minPolygonAreaThreshold = std::atof(mapOfParams["minPolygonAreaThreshold"].c_str()); + } + if(mapOfParams.find("useLod") != mapOfParams.end()) { + useLod = parseBoolean(mapOfParams["useLod"]); + } + + addPolygon(caoPoints[indexP1], caoPoints[indexP2], + caoPoints[indexP3], radius, idFace, polygonName, useLod, minPolygonAreaThreshold); + + initCircle(caoPoints[indexP1], caoPoints[indexP2], + caoPoints[indexP3], radius, idFace++, polygonName); + } + + } catch (...) { + std::cerr << "Cannot get the number of circles. Defaulting to zero." + << std::endl; + caoNbCircle = 0; + } + + startIdFace = idFace; + + delete[] caoPoints; + delete[] caoLinePoints; + + if(vectorOfModelFilename.size() > 1 && parent) { + if(verbose) { + std::cout << "Global information for " << vpIoTools::getName(modelFile) << " :" << std::endl; + std::cout << "Total nb of points : " << nbPoints << std::endl; + std::cout << "Total nb of lines : " << nbLines << std::endl; + std::cout << "Total nb of polygon lines : " << nbPolygonLines << std::endl; + std::cout << "Total nb of polygon points : " << nbPolygonPoints << std::endl; + std::cout << "Total nb of cylinders : " << nbCylinders << std::endl; + std::cout << "Total nb of circles : " << nbCircles << std::endl; + } else { + std::cout << "> " << nbPoints << " points" << std::endl; + std::cout << "> " << nbLines << " lines" << std::endl; + std::cout << "> " << nbPolygonLines << " polygon lines" << std::endl; + std::cout << "> " << nbPolygonPoints << " polygon points" << std::endl; + std::cout << "> " << nbCylinders << " cylinders" << std::endl; + std::cout << "> " << nbCircles << " circles" << std::endl; + } + } + } catch (...) { + std::cerr << "Cannot read line!" << std::endl; + throw vpException(vpException::ioError, "cannot read line"); + } +} #ifdef VISP_HAVE_COIN /*! @@ -1020,10 +1781,10 @@ vpMbTracker::loadCAOModel(const std::string& modelFile) \param sceneGraphVRML2 : Current node (either Transform, or Group node). \param transform : Transformation matrix for this group. - \param indexFace : Index of the face. + \param idFace : Index of the face. */ void -vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &transform, unsigned int &indexFace) +vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &transform, int &idFace) { vpHomogeneousMatrix transformCur; SoVRMLTransform *sceneGraphVRML2Trasnform = dynamic_cast<SoVRMLTransform *>(sceneGraphVRML2); @@ -1069,11 +1830,11 @@ vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &tra child = sceneGraphVRML2->getChild(i); if (child->getTypeId() == SoVRMLGroup::getClassTypeId()){ - extractGroup((SoVRMLGroup*)child, transform_recursive, indexFace); + extractGroup((SoVRMLGroup*)child, transform_recursive, idFace); } if (child->getTypeId() == SoVRMLTransform::getClassTypeId()){ - extractGroup((SoVRMLTransform*)child, transform_recursive, indexFace); + extractGroup((SoVRMLTransform*)child, transform_recursive, idFace); } if (child->getTypeId() == SoVRMLShape::getClassTypeId()){ @@ -1085,16 +1846,16 @@ vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &tra SoVRMLIndexedFaceSet * face_set; face_set = (SoVRMLIndexedFaceSet*)child2list->get(j); if(!strncmp(face_set->getName().getString(),"cyl",3)){ - extractCylinders(face_set, transform); + extractCylinders(face_set, transform, idFace); }else{ - extractFaces(face_set, transform, indexFace); + extractFaces(face_set, transform, idFace); } } if (((SoNode*)child2list->get(j))->getTypeId() == SoVRMLIndexedLineSet::getClassTypeId()) { SoVRMLIndexedLineSet * line_set; line_set = (SoVRMLIndexedLineSet*)child2list->get(j); - extractLines(line_set); + extractLines(line_set, idFace); } } } @@ -1107,10 +1868,10 @@ vpMbTracker::extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &tra \param face_set : Pointer to the face in the vrml format. \param transform : Transformation matrix applied to the face. - \param indexFace : Face index. + \param idFace : Face id. */ void -vpMbTracker::extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, unsigned int &indexFace) +vpMbTracker::extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, int &idFace) { std::vector<vpPoint> corners; corners.resize(0); @@ -1129,8 +1890,8 @@ vpMbTracker::extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &t { if(corners.size() > 1) { - initFaceFromCorners(corners, indexFace); - indexFace++; + addPolygon(corners, idFace++); + initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added corners.resize(0); } } @@ -1162,9 +1923,10 @@ vpMbTracker::extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &t \param face_set : Pointer to the cylinder in the vrml format. \param transform : Transformation matrix applied to the cylinder. + \param idFace : Id of the face. */ void -vpMbTracker::extractCylinders(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform) +vpMbTracker::extractCylinders(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, int &idFace) { std::vector<vpPoint> corners_c1, corners_c2;//points belonging to the first circle and to the second one. SoVRMLCoordinate* coords = (SoVRMLCoordinate *)face_set->coord.getValue(); @@ -1217,10 +1979,56 @@ vpMbTracker::extractCylinders(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatri throw vpException(vpException::badValue, "Radius from the two circles of the cylinders are different."); } - initCylinder(p1, p2, radius_c1); + addPolygon(p1, p2, idFace); + initCylinder(p1, p2, radius_c1, idFace++); +} + +/*! + Extract a line of the object to track from the VMRL model. This method calls + the initFaceFromCorners() method implemented in the child class. + \param line_set : Pointer to the line in the vrml format. + \param idFace : Id of the face. +*/ +void +vpMbTracker::extractLines(SoVRMLIndexedLineSet* line_set, int &idFace) +{ + std::vector<vpPoint> corners; + corners.resize(0); + + int indexListSize = line_set->coordIndex.getNum(); + + SbVec3f point(0,0,0); + vpPoint pt; + SoVRMLCoordinate *coord; + + for (int i = 0; i < indexListSize; i++) + { + if (line_set->coordIndex[i] == -1) + { + if(corners.size() > 1) + { + addPolygon(corners, idFace++); + initFaceFromCorners(*(faces.getPolygon().back())); // Init from the last polygon that was added + corners.resize(0); + } + } + else + { + coord = (SoVRMLCoordinate *)(line_set->coord.getValue()); + int index = line_set->coordIndex[i]; + point[0]=coord->point[index].getValue()[0]; + point[1]=coord->point[index].getValue()[1]; + point[2]=coord->point[index].getValue()[2]; + + pt.setWorldCoordinates(point[0],point[1],point[2]); + corners.push_back(pt); + } + } } +#endif // VISP_HAVE_COIN + /*! Compute the center of gravity of a set of point. This is used in the cylinder extraction to find the center of the circles. @@ -1252,53 +2060,304 @@ vpMbTracker::getGravityCenter(const std::vector<vpPoint>& pts) return G; } +/*! + Get the list of polygons faces (a vpPolygon representing the projection of the face in the image and a list of face corners + in 3D), with the possibility to order by distance to the camera or to use the visibility check to consider if the polygon + face must be retrieved or not. + + \param orderPolygons : If true, the resulting list is ordered from the nearest polygon faces to the farther. + \param useVisibility : If true, only visible faces will be retrieved. + \return A pair object containing the list of vpPolygon and the list of face corners. + */ +std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > > +vpMbTracker::getPolygonFaces(const bool orderPolygons, const bool useVisibility) +{ + vpHomogeneousMatrix cMo; + vpCameraParameters cam; + getPose(cMo); + getCameraParameters(cam); + + //Temporary variable to permit to order polygons by distance + std::vector<vpPolygon> polygonsTmp; + std::vector<std::vector<vpPoint> > roisPtTmp; + + //Pair containing the list of vpPolygon and the list of face corners + std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > > pairOfPolygonFaces; + + for (unsigned int i = 0; i < getNbPolygon(); i++) { + std::vector<vpImagePoint> roi; + std::vector<vpPoint> roiPt; + //A face has at least three points + if (getPolygon(i)->nbpt >= 3) { + if((useVisibility && getPolygon(i)->isvisible) || !useVisibility) { + for (unsigned int j = 0; j < getPolygon(i)->nbpt; j++) { + vpPoint pt(getPolygon(i)->p[j]); + pt.project(cMo); + double u = 0, v = 0; + vpMeterPixelConversion::convertPoint(cam, pt.get_x(), pt.get_y(), u, v); + roi.push_back(vpImagePoint(v, u)); + roiPt.push_back(pt); + } + + polygonsTmp.push_back(vpPolygon(roi)); + roisPtTmp.push_back(roiPt); + } + } + } + + if(orderPolygons) { + //Order polygons by distance (near to far) + std::vector<PolygonFaceInfo> listOfPolygonFaces; + for(unsigned int i = 0; i < polygonsTmp.size(); i++) { + double x_centroid = 0.0, y_centroid = 0.0, z_centroid = 0.0; + for(unsigned int j = 0; j < roisPtTmp[i].size(); j++) { + x_centroid += roisPtTmp[i][j].get_X(); + y_centroid += roisPtTmp[i][j].get_Y(); + z_centroid += roisPtTmp[i][j].get_Z(); + } + + x_centroid /= roisPtTmp[i].size(); + y_centroid /= roisPtTmp[i].size(); + z_centroid /= roisPtTmp[i].size(); + + double squared_dist = x_centroid*x_centroid + y_centroid*y_centroid + z_centroid*z_centroid; + listOfPolygonFaces.push_back(PolygonFaceInfo(squared_dist, polygonsTmp[i], roisPtTmp[i])); + } + + //Sort the list of polygon faces + std::sort(listOfPolygonFaces.begin(), listOfPolygonFaces.end()); + + polygonsTmp.resize(listOfPolygonFaces.size()); + roisPtTmp.resize(listOfPolygonFaces.size()); + + size_t cpt = 0; + for(std::vector<PolygonFaceInfo>::const_iterator it = listOfPolygonFaces.begin(); it != listOfPolygonFaces.end(); + ++it, cpt++) { + polygonsTmp[cpt] = it->polygon; + roisPtTmp[cpt] = it->faceCorners; + } + + pairOfPolygonFaces.first = polygonsTmp; + pairOfPolygonFaces.second = roisPtTmp; + } else { + pairOfPolygonFaces.first = polygonsTmp; + pairOfPolygonFaces.second = roisPtTmp; + } + + return pairOfPolygonFaces; +} /*! - Extract a line of the object to track from the VMRL model. This method calls - the initFaceFromCorners() method implemented in the child class. + Use Ogre3D for visibility tests - \param line_set : Pointer to the line in the vrml format. + \warning This function has to be called before the initialization of the tracker. + + \param v : True to use it, False otherwise */ void -vpMbTracker::extractLines(SoVRMLIndexedLineSet* line_set) +vpMbTracker::setOgreVisibilityTest(const bool &v) { - std::vector<vpPoint> corners; - corners.resize(0); + useOgre = v; + if(useOgre){ +#ifndef VISP_HAVE_OGRE + useOgre = false; + std::cout << "WARNING: ViSP doesn't have Ogre3D, basic visibility test will be used. setOgreVisibilityTest() set to false." << std::endl; +#endif + } +} - int indexListSize = line_set->coordIndex.getNum(); +/*! + Set the far distance for clipping. - SbVec3f point(0,0,0); - vpPoint pt; - SoVRMLCoordinate *coord; - - unsigned int indexFace = 0; + \param dist : Far clipping value. +*/ +void +vpMbTracker::setFarClippingDistance(const double &dist) +{ + if( (clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING && dist <= distNearClip) + vpTRACE("Far clipping value cannot be inferior than near clipping value. Far clipping won't be considered."); + else if ( dist < 0 ) + vpTRACE("Far clipping value cannot be inferior than 0. Far clipping won't be considered."); + else{ + clippingFlag = (clippingFlag | vpMbtPolygon::FAR_CLIPPING); + distFarClip = dist; + for (unsigned int i = 0; i < faces.size(); i ++){ + faces[i]->setFarClippingDistance(distFarClip); + } +#ifdef VISP_HAVE_OGRE + faces.getOgreContext()->setFarClippingDistance(distFarClip); +#endif + } +} - for (int i = 0; i < indexListSize; i++) +/*! + Set the flag to consider if the level of detail (LOD) is used. + + \param useLod : true if the level of detail must be used, false otherwise. When true, + two parameters can be set, see setMinLineLengthThresh() and setMinPolygonAreaThresh(). + \param name : name of the face we want to modify the LOD parameter. + + \sa setMinLineLengthThresh(), setMinPolygonAreaThresh() + */ +void +vpMbTracker::setLod(const bool useLod, const std::string &name) +{ + for (unsigned int i = 0; i < faces.size(); i++) { - if (line_set->coordIndex[i] == -1) - { - if(corners.size() > 1) - { - initFaceFromCorners(corners, indexFace); - indexFace++; - corners.resize(0); - } + if(name.empty() || faces[i]->name == name) { + faces[i]->setLod(useLod); } - else - { - coord = (SoVRMLCoordinate *)(line_set->coord.getValue()); - int index = line_set->coordIndex[i]; - point[0]=coord->point[index].getValue()[0]; - point[1]=coord->point[index].getValue()[1]; - point[2]=coord->point[index].getValue()[2]; + } +} - pt.setWorldCoordinates(point[0],point[1],point[2]); - corners.push_back(pt); +/*! + Set the threshold for the minimum line length to be considered as visible in the LOD case. + + \param minLineLengthThresh : threshold for the minimum line length in pixel. + \param name : name of the face we want to modify the LOD threshold. + + \sa setLod(), setMinPolygonAreaThresh() + */ +void +vpMbTracker::setMinLineLengthThresh(const double minLineLengthThresh, const std::string &name) +{ + for (unsigned int i = 0; i < faces.size(); i++) + { + if(name.empty() || faces[i]->name == name) { + faces[i]->setMinLineLengthThresh(minLineLengthThresh); } } } +/*! + Set the minimum polygon area to be considered as visible in the LOD case. + + \param minPolygonAreaThresh : threshold for the minimum polygon area in pixel. + \param name : name of the face we want to modify the LOD threshold. + + \sa setLod(), setMinLineLengthThresh() + */ +void +vpMbTracker::setMinPolygonAreaThresh(const double minPolygonAreaThresh, const std::string &name) +{ + for (unsigned int i = 0; i < faces.size(); i++) + { + if(name.empty() || faces[i]->name == name) { + faces[i]->setMinPolygonAreaThresh(minPolygonAreaThresh); + } + } +} + +/*! + Set the near distance for clipping. + + \param dist : Near clipping value. +*/ +void +vpMbTracker::setNearClippingDistance(const double &dist) +{ + if( (clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING && dist >= distFarClip) + vpTRACE("Near clipping value cannot be superior than far clipping value. Near clipping won't be considered."); + else if ( dist < 0 ) + vpTRACE("Near clipping value cannot be inferior than 0. Near clipping won't be considered."); + else{ + clippingFlag = (clippingFlag | vpMbtPolygon::NEAR_CLIPPING); + distNearClip = dist; + for (unsigned int i = 0; i < faces.size(); i ++){ + faces[i]->setNearClippingDistance(distNearClip); + } +#ifdef VISP_HAVE_OGRE + faces.getOgreContext()->setNearClippingDistance(distNearClip); #endif + } +} + +/*! + Specify which clipping to use. + + \sa vpMbtPolygonClipping + + \param flags : New clipping flags. +*/ +void +vpMbTracker::setClipping(const unsigned int &flags) +{ + clippingFlag = flags; + for (unsigned int i = 0; i < faces.size(); i ++) + faces[i]->setClipping(clippingFlag); +} + + +void +vpMbTracker::computeCovarianceMatrix(const vpHomogeneousMatrix &cMoPrev, const vpColVector &deltaS, const vpMatrix &Ls, const vpMatrix &W) +{ + //building Lp + vpMatrix LpInv(6,6); + LpInv = 0; + LpInv[0][0] = -1.0; + LpInv[1][1] = -1.0; + LpInv[2][2] = -1.0; + + vpTranslationVector ctoInit; + + cMoPrev.extract(ctoInit); + vpMatrix ctoInitSkew = ctoInit.skew(); + + vpThetaUVector thetau; + cMoPrev.extract(thetau); + + vpColVector tu(3); + for(unsigned int i = 0 ; i < 3 ; i++) + tu[i] = thetau[i]; + + double theta = sqrt(tu.sumSquare()) ; + +// vpMatrix Lthetau(3,3); + vpMatrix LthetauInvAnalytic(3,3); + vpMatrix I3(3,3); + I3.setIdentity(); +// Lthetau = -I3; + LthetauInvAnalytic = -I3; + + if(theta / (2.0 * M_PI) > std::numeric_limits<double>::epsilon()) + { + // Computing [theta/2 u]_x + vpColVector theta2u(3) ; + for (unsigned int i=0 ; i < 3 ; i++) { + theta2u[i] = tu[i]/2.0 ; + } + vpMatrix theta2u_skew = vpColVector::skew(theta2u); + + vpColVector u(3) ; + for (unsigned int i=0 ; i < 3 ; i++) { + u[i] = tu[i]/theta ; + } + vpMatrix u_skew = vpColVector::skew(u); + +// Lthetau += (theta2u_skew - (1.0-vpMath::sinc(theta)/vpMath::sqr(vpMath::sinc(theta/2.0)))*u_skew*u_skew); + LthetauInvAnalytic += -(vpMath::sqr(vpMath::sinc(theta/2.0)) * theta2u_skew - (1.0-vpMath::sinc(theta))*u_skew*u_skew); + } + +// vpMatrix LthetauInv = Lthetau.inverseByLU(); + + ctoInitSkew = ctoInitSkew * LthetauInvAnalytic; + + for(unsigned int a = 0 ; a < 3 ; a++) + for(unsigned int b = 0 ; b < 3 ; b++) + LpInv[a][b+3] = ctoInitSkew[a][b]; + + for(unsigned int a = 0 ; a < 3 ; a++) + for(unsigned int b = 0 ; b < 3 ; b++) + LpInv[a+3][b+3] = LthetauInvAnalytic[a][b]; + + // Building Js + vpMatrix Js = Ls * LpInv; + + // building deltaP + vpColVector deltaP = (Js).pseudoInverse(Js.getRows()*std::numeric_limits<double>::epsilon()) * deltaS; + + covarianceMatrix = vpMatrix::computeCovarianceMatrix(Js,deltaP,deltaS,W); +} /*! Compute \f$ J^T R \f$, with J the interaction matrix and R the vector of @@ -1334,4 +2393,53 @@ vpMbTracker::computeJTR(const vpMatrix& interaction, const vpColVector& error, v } } +/*! + Get a 1x6 vpColVector representing the estimated degrees of freedom. + vpColVector[0] = 1 if translation on X is estimated, 0 otherwise; + vpColVector[1] = 1 if translation on Y is estimated, 0 otherwise; + vpColVector[2] = 1 if translation on Z is estimated, 0 otherwise; + vpColVector[3] = 1 if rotation on X is estimated, 0 otherwise; + vpColVector[4] = 1 if rotation on Y is estimated, 0 otherwise; + vpColVector[5] = 1 if rotation on Z is estimated, 0 otherwise; + + \return 1x6 vpColVector representing the estimated degrees of freedom. +*/ +vpColVector +vpMbTracker::getEstimatedDoF() +{ + vpColVector v(6); + for(unsigned int i = 0 ; i < 6 ; i++) + v[i] = oJo[i][i]; + return v; +} + +/*! + Set a 1x6 vpColVector representing the estimated degrees of freedom. The vector has to be this form: + vpColVector[0] = 1 if translation on X is estimated, 0 otherwise; + vpColVector[1] = 1 if translation on Y is estimated, 0 otherwise; + vpColVector[2] = 1 if translation on Z is estimated, 0 otherwise; + vpColVector[3] = 1 if rotation on X is estimated, 0 otherwise; + vpColVector[4] = 1 if rotation on Y is estimated, 0 otherwise; + vpColVector[5] = 1 if rotation on Z is estimated, 0 otherwise; + +*/ +void +vpMbTracker::setEstimatedDoF(const vpColVector& v) +{ + if(v.getRows() == 6) + { + isoJoIdentity = true; + for(unsigned int i = 0 ; i < 6 ; i++){ + // if(v[i] != 0){ + if(std::fabs(v[i]) > std::numeric_limits<double>::epsilon()){ + oJo[i][i] = 1.0; + } + else{ + oJo[i][i] = 0.0; + isoJoIdentity = false; + } + } + } +} + diff --git a/src/tracking/mbt/vpMbTracker.h b/src/tracking/mbt/vpMbTracker.h index e25d4b90dc81b3544085a9e7e7897f8dd03d383b..8c35e4763b60f80945e87df8bf8c322b28fff7b0 100755 --- a/src/tracking/mbt/vpMbTracker.h +++ b/src/tracking/mbt/vpMbTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbTracker.h 4317 2013-07-17 09:40:17Z fspindle $ + * $Id: vpMbTracker.h 5162 2015-01-14 19:09:23Z strinh $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,7 +36,7 @@ * * Authors: * Romain Tallonneau - * Aurélien Yol + * Aurelien Yol * *****************************************************************************/ @@ -47,6 +47,17 @@ #ifndef vpMbTracker_hh #define vpMbTracker_hh +#include <vector> +#include <string> +#include <sstream> +#include <fstream> +#include <map> +#include <algorithm> +#include <cctype> +#include <locale> +#include <functional> // std::not1 + + #include <visp/vpHomogeneousMatrix.h> #include <visp/vpImage.h> #include <visp/vpImagePoint.h> @@ -57,10 +68,11 @@ #include <visp/vpPoint.h> #include <visp/vpMbtPolygon.h> #include <visp/vpMbHiddenFaces.h> +#include <visp/vpPolygon.h> #ifdef VISP_HAVE_COIN //Work around to avoid type redefinition int8_t with Coin -// #if defined(WIN32) && defined(VISP_HAVE_OGRE) && (_MSC_VER >= 1600) // Visual Studio 2010 +// #if defined(_WIN32) && defined(VISP_HAVE_OGRE) && (_MSC_VER >= 1600) // Visual Studio 2010 // #define HAVE_INT8_T 1 // #endif @@ -70,9 +82,6 @@ # include <Inventor/VRMLnodes/SoVRMLIndexedLineSet.h> #endif -#include <vector> -#include <string> - /*! \class vpMbTracker \ingroup ModelBasedTracking @@ -87,17 +96,12 @@ realised by implementing the main functions: - init() : Initialisation of the tracker (it includes re-initialisation). This method is called at the end of the initClick() method. - - initFaceFromCorners() : Initialisation of a face using its corners. + - initFaceFromCorners() : Initialisation of the lines that has to be tracked. - track() : Tracking on the current image - testTracking() : Test the tracking. This method throws exception if the tracking failed. - display() : Display the model and eventually other information. - In addition, two flags are declared in this class and may have to be - initialised in the child class : - - modelInitialised : flag to ensure that the model has been loaded. - initialised (either by loading them from a configuration file or by setting - them with the setCameraParameters() method). */ class VISP_EXPORT vpMbTracker { @@ -106,6 +110,10 @@ protected: vpCameraParameters cam; //! The current pose. vpHomogeneousMatrix cMo; + //! The Degrees of Freedom to estimate + vpMatrix oJo; + //! Boolean to know if oJo is identity (for fast computation) + bool isoJoIdentity; //! The name of the file containing the model (it is used to create a file name.0.pos used to store the compute pose in the initClick method). std::string modelFileName; //! Flag used to ensure that the CAD model is loaded before the initialisation. @@ -118,6 +126,47 @@ protected: vpMatrix covarianceMatrix; //! If true, the features are displayed. bool displayFeatures; + //! Weights used in the robust scheme + vpColVector m_w; + //! Error s-s* + vpColVector m_error; + + //! Set of faces describing the object. + vpMbHiddenFaces<vpMbtPolygon> faces; + //! Angle used to detect a face appearance + double angleAppears; + //! Angle used to detect a face disappearance + double angleDisappears; + //! Distance for near clipping + double distNearClip; + //! Distance for near clipping + double distFarClip; + //! Flags specifying which clipping to used + unsigned int clippingFlag; + //! Use Ogre3d for visibility tests + bool useOgre; + //! Number of points in CAO model + unsigned int nbPoints; + //! Number of lines in CAO model + unsigned int nbLines; + //! Number of polygon lines in CAO model + unsigned int nbPolygonLines; + //! Number of polygon points in CAO model + unsigned int nbPolygonPoints; + //! Number of cylinders in CAO model + unsigned int nbCylinders; + //! Number of circles in CAO model + unsigned int nbCircles; + //! True if LOD mode is enabled + bool useLodGeneral; + //! True if the CAO model is loaded before the call to loadConfigFile, (deduced by the number of polygons) + bool applyLodSettingInConfig; + //! Minimum line length threshold for LOD mode (general setting) + double minLineLengthThresholdGeneral; + //! Minimum polygon area threshold for LOD mode (general setting) + double minPolygonAreaThresholdGeneral; + //! Map with [map.first]=parameter_names and [map.second]=type (string, number or boolean) + std::map<std::string, std::string> mapOfParameterNames; public: vpMbTracker(); @@ -132,7 +181,7 @@ public: \param cam : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). + \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). */ virtual void display(const vpImage<unsigned char>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false)=0; @@ -145,18 +194,31 @@ public: \param cam : The camera parameters. \param col : The desired color. \param thickness : The thickness of the lines. - \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). + \param displayFullModel : If true, the full model is displayed (even the non visible surfaces). */ virtual void display(const vpImage<vpRGBa>& I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor& col , const unsigned int thickness=1, const bool displayFullModel = false)=0; + /*! Return the angle used to test polygons appearance. */ + virtual inline double getAngleAppear() const { return angleAppears; } + + /*! Return the angle used to test polygons disappearance. */ + virtual inline double getAngleDisappear() const { return angleDisappears; } + /*! Get the camera parameters. - \param cam : copy of the camera parameters used by the tracker. + \param camera : copy of the camera parameters used by the tracker. */ - virtual void getCameraParameters(vpCameraParameters& cam) const { cam = this->cam;} + virtual void getCameraParameters(vpCameraParameters& camera) const { camera = this->cam;} + /*! + Get the clipping used and defined in vpMbtPolygon::vpMbtPolygonClippingType. + + \return Clipping flags. + */ + virtual inline unsigned int getClipping() const { return clippingFlag; } + /*! Get the covariance matrix. */ @@ -167,14 +229,103 @@ public: return covarianceMatrix; } + virtual vpColVector getEstimatedDoF(); + + /*! + Return the error vector \f$(s-s^*)\f$ reached after the virtual visual servoing process used to estimate the pose. + + The following example shows how to use this function to compute the norm of the residual and the norm of the residual + normalized by the number of features that are tracked: + \code + tracker.track(I); + std::cout << "Residual: " << sqrt( (tracker.getError()).sumSquare()) << std::endl; + std::cout << "Residual normalized: " << sqrt( (tracker.getError()).sumSquare())/tracker.getError().size() << std::endl; + \endcode + + \sa getRobustWeights() + */ + virtual vpColVector getError() { + return m_error; + } + + /*! Return a reference to the faces structure. */ + inline vpMbHiddenFaces<vpMbtPolygon>& getFaces() { return faces;} + + /*! + Get the far distance for clipping. + + \return Far clipping value. + */ + virtual inline double getFarClippingDistance() const { return distFarClip; } + + /*! + Return the weights vector \f$w_i\f$ computed by the robust scheme. + + The following example shows how to use this function to compute the norm of the weighted residual + and the norm of the weighted residual normalized by the sum of the weights associated to the + features that are tracked: + \code + tracker.track(I); + vpColVector w = tracker.getRobustWeights(); + vpColVector e = tracker.getError(); + vpColVector we(w.size()); + for(unsigned int i=0; i<w.size(); i++) + we[i] = w[i]*e[i]; + + std::cout << "Weighted residual: " << sqrt( (we).sumSquare() ) << std::endl; + std::cout << "Weighted residual normalized: " << sqrt( (we).sumSquare() ) / w.sum() << std::endl; + \endcode + + \sa getError() + */ + virtual vpColVector getRobustWeights() { + return m_w; + } + + /*! + Get the number of polygons (faces) representing the object to track. + + \return Number of polygons. + */ + virtual inline unsigned int getNbPolygon() const { + return static_cast<unsigned int>(faces.size()); + } + + /*! + Get the near distance for clipping. + + \return Near clipping value. + */ + virtual inline double getNearClippingDistance() const { return distNearClip; } + + /*! + Return the polygon (face) "index". + + \exception vpException::dimensionError if index does not represent a good + polygon. + + \param index : Index of the polygon to return. + \return Pointer to the polygon index. + */ + virtual inline vpMbtPolygon* getPolygon(const unsigned int index) { + if(index >= static_cast<unsigned int>(faces.size()) ){ + throw vpException(vpException::dimensionError, "index out of range"); + } + + return faces[index]; + } + + virtual std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > > getPolygonFaces(const bool orderPolygons=true, + const bool useVisibility=true); + /*! Get the current pose between the object and the camera. cMo is the matrix which can be used to express coordinates from the object frame to camera frame. - \param cMo : the pose + \param cMo_ : the pose */ - inline void getPose(vpHomogeneousMatrix& cMo) const {cMo = this->cMo;} + inline void getPose(vpHomogeneousMatrix& cMo_) const {cMo_ = this->cMo;} /*! Get the current pose between the object and the camera. @@ -212,11 +363,12 @@ public: Pure virtual method to adapt to each tracker. - \param configFile : the (xml) config file to parse + \param configFile : An xml config file to parse. */ virtual void loadConfigFile(const std::string& configFile)=0; - virtual void loadModel(const std::string& modelFile); + virtual void loadModel(const char *modelFile, const bool verbose=false); + virtual void loadModel(const std::string &modelFile, const bool verbose=false); /*! Reset the tracker. @@ -224,13 +376,37 @@ public: virtual void resetTracker() = 0; void savePose(const std::string &filename); + + /*! + Set the angle used to test polygons appearance. + If the angle between the normal of the polygon and the line going + from the camera to the polygon center has a value lower than + this parameter, the polygon is considered as appearing. + The polygon will then be tracked. + + \param a : new angle in radian. + */ + virtual inline void setAngleAppear(const double &a) { angleAppears = a; } + + /*! + Set the angle used to test polygons disappearance. + If the angle between the normal of the polygon and the line going + from the camera to the polygon center has a value greater than + this parameter, the polygon is considered as disappearing. + The tracking of the polygon will then be stopped. + + \param a : new angle in radian. + */ + virtual inline void setAngleDisappear(const double &a) { angleDisappears = a; } /*! Set the camera parameters. - \param cam : the new camera parameters + \param camera : the new camera parameters */ - virtual void setCameraParameters(const vpCameraParameters& cam) {this->cam = cam;} + virtual void setCameraParameters(const vpCameraParameters& camera) {this->cam = camera;} + + virtual void setClipping(const unsigned int &flags); /*! Set if the covaraince matrix has to be computed. @@ -239,12 +415,30 @@ public: */ virtual void setCovarianceComputation(const bool& flag) { computeCovariance = flag; } + virtual void setEstimatedDoF(const vpColVector& v); + /*! - Enable to display the features. - + Enable to display the features. By features, we meant the moving edges (ME) and the klt points if used. + + Note that if present, the moving edges can be displayed with different colors: + - If green : The ME is a good point. + - If blue : The ME is removed because of a contrast problem during the tracking phase. + - If purple : The ME is removed because of a threshold problem during the tracking phase. + - If red : The ME is removed because it is rejected by the robust approach in the virtual visual servoing scheme. + \param displayF : set it to true to display the features. */ void setDisplayFeatures(const bool displayF) {displayFeatures = displayF;} + + virtual void setFarClippingDistance(const double &dist); + + virtual void setLod(const bool useLod, const std::string &name=""); + + virtual void setMinLineLengthThresh(const double minLineLengthThresh, const std::string &name=""); + + virtual void setMinPolygonAreaThresh(const double minPolygonAreaThresh, const std::string &name=""); + + virtual void setNearClippingDistance(const double &dist); /*! Set the pose to be used in entry of the next call to the track() function. @@ -270,6 +464,8 @@ public: poseSavingFilename = filename; } + virtual void setOgreVisibilityTest(const bool &v); + /*! Test the quality of the tracking. @@ -285,17 +481,38 @@ public: virtual void track(const vpImage<unsigned char>& I)=0; protected: + void addPolygon(const std::vector<vpPoint>& corners, const int idFace=-1, const std::string &polygonName="", + const bool useLod=false, const double minPolygonAreaThreshold=2500.0, const double minLineLengthThreshold=50.0); + void addPolygon(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, const int idFace=-1, + const std::string &polygonName="", const bool useLod=false, const double minPolygonAreaThreshold=2500.0); + void addPolygon(const vpPoint& p1, const vpPoint &p2, const int idFace=-1, const std::string &polygonName="", + const bool useLod=false, const double minLineLengthThreshold=50); + + void computeCovarianceMatrix(const vpHomogeneousMatrix &cMoPrev, const vpColVector &deltaS, const vpMatrix &Ls, const vpMatrix &W); void computeJTR(const vpMatrix& J, const vpColVector& R, vpMatrix& JTR); #ifdef VISP_HAVE_COIN - virtual void extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &transform, unsigned int &indexFace); - virtual void extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, unsigned int &indexFace); - virtual void extractLines(SoVRMLIndexedLineSet* line_set); - virtual void extractCylinders(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform); + virtual void extractGroup(SoVRMLGroup *sceneGraphVRML2, vpHomogeneousMatrix &transform, int &idFace); + virtual void extractFaces(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, int &idFace); + virtual void extractLines(SoVRMLIndexedLineSet* line_set, int &idFace); + virtual void extractCylinders(SoVRMLIndexedFaceSet* face_set, vpHomogeneousMatrix &transform, int &idFace); #endif vpPoint getGravityCenter(const std::vector<vpPoint>& _pts); + /*! + Add a circle to track from its center, 3 points (including the center) defining the plane that contain + the circle and its radius. + + \param p1 : Center of the circle. + \param p2,p3 : Two points on the plane containing the circle. With the center of the circle we have 3 points + defining the plane that contains the circle. + \param radius : Radius of the circle. + \param idFace : Id of the face associated to the circle. + \param name : Name of the circle. + */ + virtual void initCircle(const vpPoint& p1, const vpPoint &p2, const vpPoint &p3, const double radius, + const int idFace=0, const std::string &name="")=0; /*! Add a cylinder to track from two points on the axis (defining the length of the cylinder) and its radius. @@ -303,22 +520,56 @@ protected: \param p1 : First point on the axis. \param p2 : Second point on the axis. \param radius : Radius of the cylinder. - \param indexCylinder : Index of the cylinder. + \param idFace : Id of the face associated to the cylinder. + \param name : Name of the cylinder. */ - virtual void initCylinder(const vpPoint& p1, const vpPoint p2, const double radius, const unsigned int indexCylinder=0)=0; + virtual void initCylinder(const vpPoint& p1, const vpPoint &p2, const double radius, const int idFace=0, + const std::string &name="")=0; /*! - Add a face to track from its corners (in the object frame). This method is - called from the loadModel() one to add a face of the object to track. - The initialisation of the face depends on the primitive to track. - - \param corners : The vector of corners representing the face. - \param indexFace : The index of the face. + Add the lines to track from the polygon description. If the polygon has only + two points, it defines a single line that is always visible. If it has three or + more corners, it defines a face. In that case the visibility of the face is computed + in order to track the corresponding lines only if the face is visible. + + The id of the polygon is supposed to be set prior calling this function. + + \param polygon : The polygon describing the set of lines that has to be tracked. */ - virtual void initFaceFromCorners(const std::vector<vpPoint>& corners, const unsigned int indexFace = -1)=0; - + virtual void initFaceFromCorners(vpMbtPolygon &polygon)=0; + virtual void initFaceFromLines(vpMbtPolygon &polygon)=0; + virtual void loadVRMLModel(const std::string& modelFile); - virtual void loadCAOModel(const std::string& modelFile); + virtual void loadCAOModel(const std::string& modelFile, std::vector<std::string>& vectorOfModelFilename, int& startIdFace, + const bool verbose=false, const bool parent=true); + + void removeComment(std::ifstream& fileId); + + inline bool parseBoolean(std::string &input) { + std::transform(input.begin(), input.end(), input.begin(), ::tolower); + std::istringstream is(input); + bool b; + //Parse string to boolean either in the textual representation (True/False) + //or in numeric representation (1/0) + is >> (input.size() > 1 ? std::boolalpha : std::noboolalpha) >> b; + return b; + } + + std::map<std::string, std::string> parseParameters(std::string& endLine); + + inline std::string <rim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace)))); + return s; + } + + inline std::string &rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); + return s; + } + + inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); + } }; diff --git a/src/tracking/mbt/vpMbXmlParser.cpp b/src/tracking/mbt/vpMbXmlParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84b450b9341d36caf5f9b0a14fb642a389c60b22 --- /dev/null +++ b/src/tracking/mbt/vpMbXmlParser.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** + * + * $Id: vpMbXmlParser.cpp 4577 2014-01-10 16:19:41Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Load XML Parameter for Model Based Tracker. + * + * Authors: + * Aurelien Yol + * + *****************************************************************************/ +#include <visp/vpConfig.h> + + +#ifdef VISP_HAVE_XML2 + +#include <iostream> +#include <map> + +#include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ + +#include <visp/vpMbXmlParser.h> + + +/*! + Default constructor. + +*/ +vpMbXmlParser::vpMbXmlParser() + : cam(), angleAppear(70), angleDisappear(80), + hasNearClipping(false), nearClipping(false), + hasFarClipping(false), farClipping(false), fovClipping(false) + +{ + init(); +} + +/*! + Default destructor. +*/ +vpMbXmlParser::~vpMbXmlParser() +{ +} + +/*! + Initialise internal variables (including the map). +*/ +void +vpMbXmlParser::init() +{ + setMainTag("conf"); + + nodeMap["conf"] = conf; + nodeMap["face"] = face; + nodeMap["angle_appear"] = angle_appear; + nodeMap["angle_disappear"] = angle_disappear; + nodeMap["near_clipping"] = near_clipping; + nodeMap["far_clipping"] = far_clipping; + nodeMap["fov_clipping"] = fov_clipping; + nodeMap["camera"] = camera; + nodeMap["height"] = height; + nodeMap["width"] = width; + nodeMap["u0"] = u0; + nodeMap["v0"] = v0; + nodeMap["px"] = px; + nodeMap["py"] = py; +} + +/*! + Parse the file in parameters. + This method is deprecated, use parse() instead. + + \param filename : File to parse. +*/ +void +vpMbXmlParser::parse(const char * filename) +{ + std::string file = filename; + vpXmlParser::parse(file); +} + +/*! + Write info to file. + + \warning Useless, so not yet implemented => Throw exception. +*/ +void +vpMbXmlParser::writeMainClass(xmlNodePtr /*node*/) +{ + throw vpException(vpException::notImplementedError, "Not yet implemented." ); +} + +/*! + Read the parameters of the class from the file given by its document pointer + and by its root node. + + \param doc : Document to parse. + \param node : Root node. +*/ +void +vpMbXmlParser::readMainClass(xmlDocPtr doc, xmlNodePtr node) +{ + bool camera_node = false; + bool face_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case camera:{ + this->read_camera (doc, dataNode); + camera_node = true; + }break; + case face:{ + this->read_face(doc, dataNode); + face_node = true; + }break; + default:{ +// vpTRACE("unknown tag in read_sample : %d, %s", iter_data->second, (iter_data->first).c_str()); + }break; + } + } + } + } + + if(!camera_node) { + std::cout << "camera : u0 : "<< this->cam.get_u0() << " (default)" <<std::endl; + std::cout << "camera : v0 : "<< this->cam.get_v0() << " (default)" <<std::endl; + std::cout << "camera : px : "<< this->cam.get_px() << " (default)" <<std::endl; + std::cout << "camera : py : "<< this->cam.get_py() << " (default)" <<std::endl; + } + + if(!face_node) { + std::cout << "face : Angle Appear : "<< angleAppear <<" (default)" <<std::endl; + std::cout << "face : Angle Disappear : "<< angleDisappear <<" (default)" <<std::endl; + } +} + +/*! + Read camera information. + + \throw vpException::fatalError if there was an unexpected number of data. + + \param doc : Pointer to the document. + \param node : Pointer to the node of the camera information. +*/ +void +vpMbXmlParser::read_camera (xmlDocPtr doc, xmlNodePtr node) +{ + bool u0_node = false; + bool v0_node = false; + bool px_node = false; + bool py_node = false; + + // current data values. + double d_u0 = this->cam.get_u0(); + double d_v0 = this->cam.get_v0(); + double d_px = this->cam.get_px(); + double d_py = this->cam.get_py(); + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case u0:{ + d_u0 = xmlReadDoubleChild(doc, dataNode); + u0_node = true; + }break; + case v0:{ + d_v0 = xmlReadDoubleChild(doc, dataNode); + v0_node = true; + }break; + case px:{ + d_px = xmlReadDoubleChild(doc, dataNode); + px_node = true; + }break; + case py:{ + d_py = xmlReadDoubleChild(doc, dataNode); + py_node = true; + }break; + default:{ +// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); + }break; + } + } + } + } + + this->cam.initPersProjWithoutDistortion(d_px, d_py, d_u0, d_v0) ; + + if(!u0_node) + std::cout << "camera : u0 : "<< this->cam.get_u0() << " (default)" <<std::endl; + else + std::cout << "camera : u0 : "<< this->cam.get_u0() <<std::endl; + + if(!v0_node) + std::cout << "camera : v0 : "<< this->cam.get_v0() << " (default)" <<std::endl; + else + std::cout << "camera : v0 : "<< this->cam.get_v0() <<std::endl; + + if(!px_node) + std::cout << "camera : px : "<< this->cam.get_px() << " (default)" <<std::endl; + else + std::cout << "camera : px : "<< this->cam.get_px() <<std::endl; + + if(!py_node) + std::cout << "camera : py : "<< this->cam.get_py() << " (default)" <<std::endl; + else + std::cout << "camera : py : "<< this->cam.get_py() <<std::endl; +} + +/*! + Read face information. + + \throw vpException::fatalError if there was an unexpected number of data. + + \param doc : Pointer to the document. + \param node : Pointer to the node of the camera information. +*/ +void +vpMbXmlParser::read_face(xmlDocPtr doc, xmlNodePtr node) +{ + bool angle_appear_node = false; + bool angle_disappear_node = false; + bool near_clipping_node = false; + bool far_clipping_node = false; + bool fov_clipping_node = false; + + for(xmlNodePtr dataNode = node->xmlChildrenNode; dataNode != NULL; dataNode = dataNode->next) { + if(dataNode->type == XML_ELEMENT_NODE){ + std::map<std::string, int>::iterator iter_data= this->nodeMap.find((char*)dataNode->name); + if(iter_data != nodeMap.end()){ + switch (iter_data->second){ + case angle_appear:{ + angleAppear = xmlReadDoubleChild(doc, dataNode); + angle_appear_node = true; + }break; + case angle_disappear:{ + angleDisappear = xmlReadDoubleChild(doc, dataNode); + angle_disappear_node = true; + }break; + case near_clipping:{ + nearClipping = xmlReadDoubleChild(doc, dataNode); + near_clipping_node = true; + hasNearClipping = true; + }break; + case far_clipping:{ + farClipping = xmlReadDoubleChild(doc, dataNode); + far_clipping_node = true; + hasFarClipping = true; + }break; + case fov_clipping:{ + if (xmlReadIntChild(doc, dataNode)) + fovClipping = true; + else + fovClipping = false; + fov_clipping_node = true; + }break; + default:{ +// vpTRACE("unknown tag in read_camera : %d, %s", iter_data->second, (iter_data->first).c_str()); + }break; + } + } + } + } + + if(!angle_appear_node) + std::cout << "face : Angle Appear : "<< angleAppear << " (default)" <<std::endl; + else + std::cout << "face : Angle Appear : "<< angleAppear <<std::endl; + + if(!angle_disappear_node) + std::cout << "face : Angle Disappear : "<< angleDisappear << " (default)" <<std::endl; + else + std::cout << "face : Angle Disappear : "<< angleDisappear <<std::endl; + + if(near_clipping_node) + std::cout << "face : Near Clipping : "<< nearClipping <<std::endl; + + if(far_clipping_node) + std::cout << "face : Far Clipping : "<< farClipping <<std::endl; + + if(fov_clipping_node) { + if(fovClipping) + std::cout << "face : Fov Clipping : True" <<std::endl; + else + std::cout << "face : Fov Clipping : False" <<std::endl; + } +} + +#endif + diff --git a/src/tracking/mbt/vpMbXmlParser.h b/src/tracking/mbt/vpMbXmlParser.h new file mode 100644 index 0000000000000000000000000000000000000000..198d70b2d13c1735b9ff609ebd81c0fc3dde279c --- /dev/null +++ b/src/tracking/mbt/vpMbXmlParser.h @@ -0,0 +1,210 @@ +/**************************************************************************** + * + * $Id: vpMbXmlParser.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Load XML Parameter for Model Based Tracker. + * + * Authors: + * Aurelien Yol + * + *****************************************************************************/ + +/*! + * \file vpMbXmlParser.h + * \brief Parse an Xml file to extract configuration parameters of a mbtConfig object. +*/ + +#ifndef vpMbXmlParser_HH +#define vpMbXmlParser_HH + +#include <visp/vpConfig.h> + +#ifdef VISP_HAVE_XML2 + +#include <libxml/xmlmemory.h> /* Fonctions de la lib XML. */ + +#include <visp/vpXmlParser.h> +#include <visp/vpCameraParameters.h> + +/*! + \class vpMbXmlParser + \brief Parse an Xml file to extract configuration parameters of a mbtConfig object. + \ingroup ModelBasedTracking + + Data parser for the model based tracker. + + */ +class VISP_EXPORT vpMbXmlParser: public vpXmlParser +{ +protected: + //! Camera parameters. + vpCameraParameters cam; + //! Angle to determine if a face appeared + double angleAppear; + //! Angle to determine if a face disappeared + double angleDisappear; + //! Is near clipping distance specified? + bool hasNearClipping; + //! Near clipping distance + double nearClipping; + //! Is far clipping distance specified? + bool hasFarClipping; + //! Near clipping distance + double farClipping; + //! Fov Clipping + bool fovClipping; + + typedef enum{ + conf, + face, + angle_appear, + angle_disappear, + near_clipping, + far_clipping, + fov_clipping, + camera, + height, + width, + u0, + v0, + px, + py, + last + } dataToParseMb; + + +public: + + vpMbXmlParser(); + virtual ~vpMbXmlParser(); + + /*! + Get the angle to determine if a face appeared. + + \return angleAppear + */ + inline double getAngleAppear() const {return angleAppear;} + + /*! + Get the angle to determine if a face disappeared. + + \return angleDisappear + */ + inline double getAngleDisappear() const {return angleDisappear;} + + void getCameraParameters(vpCameraParameters& _cam) const { _cam = this->cam;} + + /*! + Get the far clipping distance. + + \return farClipping + */ + inline double getFarClippingDistance() const {return farClipping;} + + /*! + Use FOV clipping + + \return True if yes, False otherwise. + */ + inline bool getFovClipping() const {return fovClipping;} + + /*! + Get the near clipping distance. + + \return nearClipping + */ + inline double getNearClippingDistance() const {return nearClipping;} + + /*! + Has Far clipping been specified? + + \return True if yes, False otherwise. + */ + inline bool hasFarClippingDistance() const {return hasFarClipping;} + + /*! + Has Near clipping been specified? + + \return True if yes, False otherwise. + */ + inline bool hasNearClippingDistance() const {return hasNearClipping;} + + void parse(const char * filename); + + virtual void readMainClass(xmlDocPtr doc, xmlNodePtr node); + void read_camera (xmlDocPtr doc, xmlNodePtr node); + void read_face(xmlDocPtr doc, xmlNodePtr node); + + /*! + Set the angle to determine if a face appeared. + + \param aappear : New angleAppear + */ + inline void setAngleAppear(const double &aappear) {angleAppear = aappear;} + + /*! + Set the angle to determine if a face disappeared. + + \param adisappear : New angleDisappear + */ + inline void setAngleDisappear(const double &adisappear) {angleDisappear = adisappear;} + + void setCameraParameters(const vpCameraParameters &_cam){ cam = _cam; } + + /*! + Set the far clipping distance. + + \param fclip : New farClipping + */ + inline void setFarClippingDistance(const double &fclip) {farClipping = fclip;} + + /*! + Set the near clipping distance. + + \param nclip : New nearClipping + */ + inline void setNearClippingDistance(const double &nclip) {nearClipping = nclip;} + + void writeMainClass(xmlNodePtr node); + +protected: + void init(); + +}; + +#endif + +#endif /* NMBXMLPARSER_H_ */ + + + diff --git a/src/tracking/mbt/edge/vpMbtPolygon.cpp b/src/tracking/mbt/vpMbtPolygon.cpp similarity index 50% rename from src/tracking/mbt/edge/vpMbtPolygon.cpp rename to src/tracking/mbt/vpMbtPolygon.cpp index d605fa8ce907b27e3fb3c430a5fdd13dcec975d6..fe779b3dcfbfbc8bfd53cc6b1abaf79f1c4ffdd6 100644 --- a/src/tracking/mbt/edge/vpMbtPolygon.cpp +++ b/src/tracking/mbt/vpMbtPolygon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtPolygon.cpp 4319 2013-07-17 10:04:09Z ayol $ + * $Id: vpMbtPolygon.cpp 4952 2014-11-12 14:04:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,22 +51,50 @@ */ #include <visp/vpMbtPolygon.h> +#include <visp/vpPolygon.h> /*! Basic constructor. */ vpMbtPolygon::vpMbtPolygon() + : index(-1), nbpt(0), nbCornersInsidePrev(0), isvisible(false), isappearing(false), + p(NULL), roiPointsClip(), clippingFlag(vpMbtPolygon::NO_CLIPPING), + distNearClip(0.001), distFarClip(100.), + useLod(false), minLineLengthThresh(50.0), minPolygonAreaThresh(2500.0), name("") { - nbpt = 0 ; - p = NULL ; - isappearing = false; - isvisible = false; - nbCornersInsidePrev = 0; - - distNearClip = 0.001; - distFarClip = 100.0; - - clippingFlag = vpMbtPolygon::NO_CLIPPING; +} + +vpMbtPolygon::vpMbtPolygon(const vpMbtPolygon& mbtp) + : index(-1), nbpt(0), nbCornersInsidePrev(0), isvisible(false), isappearing(false), + p(NULL), roiPointsClip(), clippingFlag(vpMbtPolygon::NO_CLIPPING), + distNearClip(0.001), distFarClip(100.), + useLod(false), minLineLengthThresh(50.0), minPolygonAreaThresh(2500.0), name("") +{ + *this = mbtp; +} + +vpMbtPolygon& vpMbtPolygon::operator=(const vpMbtPolygon& mbtp) +{ + index = mbtp.index; + nbpt = mbtp.nbpt; + nbCornersInsidePrev = mbtp.nbCornersInsidePrev; + isvisible = mbtp.isvisible; + isappearing = mbtp.isappearing; + roiPointsClip = mbtp.roiPointsClip; + clippingFlag = mbtp.clippingFlag; + distNearClip = mbtp.distNearClip; + distFarClip = mbtp.distFarClip; + useLod = mbtp.useLod; + minLineLengthThresh = mbtp.minLineLengthThresh; + minPolygonAreaThresh = mbtp.minPolygonAreaThresh; + name = mbtp.name; + + if (p) delete [] p; + p = new vpPoint [nbpt]; + for(unsigned int i = 0; i < nbpt; i++) + p[i] = mbtp.p[i]; + + return (*this); } /*! @@ -149,40 +177,68 @@ vpMbtPolygon::changeFrame(const vpHomogeneousMatrix &cMo) \param alpha : Maximum angle to detect if the face is visible (in rad). \param modulo : Indicates if the test should also consider faces that are not oriented counter clockwise. If true, the orientation of the face is without importance. + \param cam : Camera parameters (intrinsics parameters) + \param I : Image used to consider level of detail. \return Return true if the polygon is visible. */ bool -vpMbtPolygon::isVisible(const vpHomogeneousMatrix &cMo, const double alpha, const bool &modulo) +vpMbtPolygon::isVisible(const vpHomogeneousMatrix &cMo, const double alpha, const bool &modulo, + const vpCameraParameters &cam, const vpImage<unsigned char> &I) { // std::cout << "Computing angle from MBT Face (cMo, alpha)" << std::endl; - if(nbpt <= 2){ - /* a line is allways visible */ - isvisible = true; - isappearing = false; - return true ; - } changeFrame(cMo); - vpColVector e1(3) ; - vpColVector e2(3) ; - vpColVector facenormal(3) ; + if(nbpt <= 2) { + //Level of detail (LOD) + if(useLod) { + vpCameraParameters c = cam; + if(clippingFlag > 3) { // Contains at least one FOV constraint + c.computeFov(I.getWidth(), I.getHeight()); + } + computeRoiClipped(c); + std::vector<vpImagePoint> roiImagePoints; + getRoiClipped(c, roiImagePoints); + + if (roiImagePoints.size() == 2) { + double x1 = roiImagePoints[0].get_u(); + double y1 = roiImagePoints[0].get_v(); + double x2 = roiImagePoints[1].get_u(); + double y2 = roiImagePoints[1].get_v(); + double length = std::sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +// std::cout << "Index=" << index << " ; Line length=" << length << " ; clippingFlag=" << clippingFlag << std::endl; +// vpTRACE("index=%d lenght=%f minLineLengthThresh=%f", index, length, minLineLengthThresh); + + if (length < minLineLengthThresh) { + isvisible = false; + isappearing = false; + return false; + } + } + } - e1[0] = p[1].get_X() - p[0].get_X() ; - e1[1] = p[1].get_Y() - p[0].get_Y() ; - e1[2] = p[1].get_Z() - p[0].get_Z() ; + /* a line is always visible when LOD is not used */ + isvisible = true; + isappearing = false; + return true ; + } - e2[0] = p[2].get_X() - p[1].get_X() ; - e2[1] = p[2].get_Y() - p[1].get_Y() ; - e2[2] = p[2].get_Z() - p[1].get_Z() ; - - e1.normalize(); - e2.normalize(); + //Check visibility from normal + //Newell's Method for calculating the normal of an arbitrary 3D polygon + //https://www.opengl.org/wiki/Calculating_a_Surface_Normal + vpColVector faceNormal(3); + vpColVector currentVertex, nextVertex; + for(unsigned int i = 0; i<nbpt; i++) { + currentVertex = p[i].cP; + nextVertex = p[(i+1) % nbpt].cP; + + faceNormal[0] += (currentVertex[1] - nextVertex[1]) * (currentVertex[2] + nextVertex[2]); + faceNormal[1] += (currentVertex[2] - nextVertex[2]) * (currentVertex[0] + nextVertex[0]); + faceNormal[2] += (currentVertex[0] - nextVertex[0]) * (currentVertex[1] + nextVertex[1]); + } + faceNormal.normalize(); - facenormal = vpColVector::crossProd(e1,e2) ; - facenormal.normalize(); - vpColVector e4(3) ; vpPoint pt; for (unsigned int i = 0; i < nbpt; i += 1){ @@ -190,23 +246,39 @@ vpMbtPolygon::isVisible(const vpHomogeneousMatrix &cMo, const double alpha, cons pt.set_Y(pt.get_Y() + p[i].get_Y()); pt.set_Z(pt.get_Z() + p[i].get_Z()); } - e4[0] = -pt.get_X()/(double)nbpt; e4[1] = -pt.get_Y()/(double)nbpt; e4[2] = -pt.get_Z()/(double)nbpt; + e4[0] = -pt.get_X() / (double)nbpt; + e4[1] = -pt.get_Y() / (double)nbpt; + e4[2] = -pt.get_Z() / (double)nbpt; e4.normalize(); - - double cos_angle = vpColVector::dotProd (e4, facenormal); - double angle = acos(cos_angle); - -// std::cout << cos_angle << "/" << vpMath::deg(angle) << std::endl; - - if( angle < alpha ){ - isvisible = true; - isappearing = false; - return true; - } - if(modulo && (M_PI - angle) < alpha){ + double angle = acos(vpColVector::dotProd(e4, faceNormal)); + +// vpCTRACE << angle << "/" << vpMath::deg(angle) << "/" << vpMath::deg(alpha) << std::endl; + + if( angle < alpha || (modulo && (M_PI - angle) < alpha)) { isvisible = true; isappearing = false; + + if (useLod) { + vpCameraParameters c = cam; + if(clippingFlag > 3) { // Contains at least one FOV constraint + c.computeFov(I.getWidth(), I.getHeight()); + } + computeRoiClipped(c); + std::vector<vpImagePoint> roiImagePoints; + getRoiClipped(c, roiImagePoints); + + vpPolygon roiPolygon(roiImagePoints); + double area = roiPolygon.getArea(); +// std::cout << "After normal test ; Index=" << index << " ; area=" << area << " ; clippingFlag=" +// << clippingFlag << std::endl; + if (area < minPolygonAreaThresh) { + isappearing = false; + isvisible = false; + return false; + } + } + return true; } @@ -219,47 +291,106 @@ vpMbtPolygon::isVisible(const vpHomogeneousMatrix &cMo, const double alpha, cons else { isappearing = false; } + isvisible = false ; return false ; } /*! Compute the region of interest in the image according to the used clipping. + + \warning If the FOV clipping is used, camera normals have to be precomputed. \param cam : camera parameters used to compute the field of view. */ void vpMbtPolygon::computeRoiClipped(const vpCameraParameters &cam) -{ - roiPointsClip = std::vector<std::pair<vpPoint,unsigned int> >(); +{ + roiPointsClip.clear(); std::vector<vpColVector> fovNormals; - + std::vector<std::pair<vpPoint,unsigned int> > roiPointsClipTemp; + std::vector<std::pair<vpPoint,unsigned int> > roiPointsClipTemp2; + if(cam.isFovComputed() && clippingFlag > 3) fovNormals = cam.getFovNormals(); - - for (unsigned int i = 0; i < nbpt; i ++){ - vpPoint p1Clipped, p2Clipped; - unsigned int p1ClippedInfo, p2ClippedInfo; - vpImagePoint ip; - if(vpMbtPolygon::getClippedPoints(p[i], p[(i+1)%nbpt], p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, cam, fovNormals)){ - p1Clipped.projection(); - roiPointsClip.push_back(std::make_pair(p1Clipped, p1ClippedInfo)); - - if(p2ClippedInfo != vpMbtPolygon::NO_CLIPPING){ - p2Clipped.projection(); - roiPointsClip.push_back(std::make_pair(p2Clipped, p2ClippedInfo)); + + for(unsigned int i = 0 ; i < nbpt ; i++){ + p[i%nbpt].projection(); + roiPointsClipTemp.push_back(std::make_pair(p[i%nbpt],vpMbtPolygon::NO_CLIPPING)); + } + + if(clippingFlag != vpMbtPolygon::NO_CLIPPING) + for(unsigned int i = 1 ; i < 64 ; i=i*2) + { + if(((clippingFlag & i) == i) || ((clippingFlag > vpMbtPolygon::FAR_CLIPPING) && (i==1))) + { + for(unsigned int j = 0 ; j < roiPointsClipTemp.size() ; j++) + { + vpPoint p1Clipped = roiPointsClipTemp[j].first; + vpPoint p2Clipped = roiPointsClipTemp[(j+1)%roiPointsClipTemp.size()].first; + + unsigned int p2ClippedInfoBefore = roiPointsClipTemp[(j+1)%roiPointsClipTemp.size()].second; + unsigned int p1ClippedInfo = roiPointsClipTemp[j].second; + unsigned int p2ClippedInfo = roiPointsClipTemp[(j+1)%roiPointsClipTemp.size()].second; + + bool problem = true; + + switch(i){ + case 1: + problem = !(vpMbtPolygon::getClippedPointsDistance(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + i, distNearClip)); + break; + case 2: + problem = !(vpMbtPolygon::getClippedPointsDistance(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + i, distFarClip)); + break; + case 4: + problem = !(vpMbtPolygon::getClippedPointsFovGeneric(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + fovNormals[0], vpMbtPolygon::LEFT_CLIPPING)); + break; + case 8: + problem = !(vpMbtPolygon::getClippedPointsFovGeneric(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + fovNormals[1], vpMbtPolygon::RIGHT_CLIPPING)); + break; + case 16: + problem = !(vpMbtPolygon::getClippedPointsFovGeneric(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + fovNormals[2], vpMbtPolygon::UP_CLIPPING)); + break; + case 32: + problem = !(vpMbtPolygon::getClippedPointsFovGeneric(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, + fovNormals[3], vpMbtPolygon::DOWN_CLIPPING)); + break; + } + + if(!problem) + { + p1Clipped.projection(); + roiPointsClipTemp2.push_back(std::make_pair(p1Clipped, p1ClippedInfo)); + + if(p2ClippedInfo != p2ClippedInfoBefore) + { + p2Clipped.projection(); + roiPointsClipTemp2.push_back(std::make_pair(p2Clipped, p2ClippedInfo)); + } + + if(nbpt == 2){ + if(p2ClippedInfo == p2ClippedInfoBefore) + { + p2Clipped.projection(); + roiPointsClipTemp2.push_back(std::make_pair(p2Clipped, p2ClippedInfo)); + } + break; + } + } } - - if(nbpt == 2){ //Case of a line. - if(p2ClippedInfo == vpMbtPolygon::NO_CLIPPING){ - p2Clipped.projection(); - roiPointsClip.push_back(std::make_pair(p2Clipped, p2ClippedInfo)); - } - break; + + roiPointsClipTemp = roiPointsClipTemp2; + roiPointsClipTemp2.clear(); } - } } -} + + roiPointsClip = roiPointsClipTemp; +} /*! Get the clipped points according to a plane equation. @@ -280,7 +411,7 @@ vpMbtPolygon::computeRoiClipped(const vpCameraParameters &cam) \return True if the points have been clipped, False otherwise */ bool -vpMbtPolygon::getClippedPointsFov(const vpPoint &p1, const vpPoint &p2, +vpMbtPolygon::getClippedPointsFovGeneric(const vpPoint &p1, const vpPoint &p2, vpPoint &p1Clipped, vpPoint &p2Clipped, unsigned int &p1ClippedInfo, unsigned int &p2ClippedInfo, const vpColVector &normal, const unsigned int &flag) @@ -296,7 +427,10 @@ vpMbtPolygon::getClippedPointsFov(const vpPoint &p1, const vpPoint &p2, if((clippingFlag & flag) == flag){ double beta1 = acos( p1Vec * normal ); double beta2 = acos( p2Vec * normal ); - + +// std::cout << beta1 << " && " << beta2 << std::endl; + + // if(!(beta1 < M_PI / 2.0 && beta2 < M_PI / 2.0)) if(beta1 < M_PI / 2.0 && beta2 < M_PI / 2.0) return false; else if (beta1 < M_PI / 2.0 || beta2 < M_PI / 2.0){ @@ -322,119 +456,65 @@ vpMbtPolygon::getClippedPointsFov(const vpPoint &p1, const vpPoint &p2, return true; } -/*! - Get the clipped points. - - \param p1 : First extremity of the line. - \param p2 : Second extremity of the line. - \param p1Clipped : Resulting p1. - \param p2Clipped : Resulting p2. - \param p1ClippedInfo : Resulting clipping flag for p1. - \param p2ClippedInfo : Resulting clipping flag for p2. - \param cam : camera parameters. - - \return True if the points have been clipped, False otherwise -*/ -bool -vpMbtPolygon::getClippedPoints(const vpPoint &p1, const vpPoint &p2, - vpPoint &p1Clipped, vpPoint &p2Clipped, +bool +vpMbtPolygon::getClippedPointsDistance(const vpPoint &p1, const vpPoint &p2, + vpPoint &p1Clipped, vpPoint &p2Clipped, unsigned int &p1ClippedInfo, unsigned int &p2ClippedInfo, - const vpCameraParameters &cam, const std::vector<vpColVector> &fovNormals) + const unsigned int &flag, const double &distance) { - p1Clipped = p1; - p2Clipped = p2; - p1ClippedInfo = vpMbtPolygon::NO_CLIPPING; - p2ClippedInfo = vpMbtPolygon::NO_CLIPPING; - - if(clippingFlag != vpMbtPolygon::NO_CLIPPING){ - // Near Clipping - if( (clippingFlag & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING || - ((clippingFlag & vpMbtPolygon::LEFT_CLIPPING) == vpMbtPolygon::LEFT_CLIPPING && (clippingFlag & vpMbtPolygon::RIGHT_CLIPPING) == vpMbtPolygon::RIGHT_CLIPPING) || - ((clippingFlag & vpMbtPolygon::UP_CLIPPING) == vpMbtPolygon::UP_CLIPPING && (clippingFlag & vpMbtPolygon::DOWN_CLIPPING) == vpMbtPolygon::DOWN_CLIPPING)){ - if(p1Clipped.get_Z() < distNearClip && p2Clipped.get_Z() < distNearClip) //Not using getClippedPointsFov for efficiency - return false; - else if(p1Clipped.get_Z() < distNearClip || p2Clipped.get_Z() < distNearClip){ - vpPoint pClippedNear; - double t = (p2Clipped.get_Z() - p1Clipped.get_Z()); - t = (distNearClip - p1Clipped.get_Z()) / t; - - pClippedNear.set_X((p2Clipped.get_X() - p1Clipped.get_X())*t + p1Clipped.get_X()); - pClippedNear.set_Y((p2Clipped.get_Y() - p1Clipped.get_Y())*t + p1Clipped.get_Y()); - pClippedNear.set_Z(distNearClip); - - if(p1Clipped.get_Z() < distNearClip){ - p1Clipped = pClippedNear; - p1ClippedInfo = p1ClippedInfo | vpMbtPolygon::NEAR_CLIPPING; - } - else{ - p2Clipped = pClippedNear; - p2ClippedInfo = p2ClippedInfo | vpMbtPolygon::NEAR_CLIPPING; - } + // Since p1 and p1Clipped can be the same object as well as p2 and p2Clipped + // to avoid a valgrind "Source and destination overlap in memcpy" error, + // we introduce a two temporary points. + vpPoint p1Clipped_, p2Clipped_; + p1Clipped_ = p1; + p2Clipped_ = p2; + + p1Clipped = p1Clipped_; + p2Clipped = p2Clipped_; + + + bool test1 = (p1Clipped.get_Z() < distance && p2Clipped.get_Z() < distance); + if(flag == vpMbtPolygon::FAR_CLIPPING) + test1 = (p1Clipped.get_Z() > distance && p2Clipped.get_Z() > distance); + + bool test2 = (p1Clipped.get_Z() < distance || p2Clipped.get_Z() < distance); + if(flag == vpMbtPolygon::FAR_CLIPPING) + test2 = (p1Clipped.get_Z() > distance || p2Clipped.get_Z() > distance); + + bool test3 = (p1Clipped.get_Z() < distance); + if(flag == vpMbtPolygon::FAR_CLIPPING) + test3 = (p1Clipped.get_Z() > distance); + + if(test1) + return false; + + else if(test2){ + vpPoint pClippedNear; + double t; + t = (p2Clipped.get_Z() - p1Clipped.get_Z()); + t = (distance - p1Clipped.get_Z()) / t; + + pClippedNear.set_X((p2Clipped.get_X() - p1Clipped.get_X())*t + p1Clipped.get_X()); + pClippedNear.set_Y((p2Clipped.get_Y() - p1Clipped.get_Y())*t + p1Clipped.get_Y()); + pClippedNear.set_Z(distance); + + if(test3){ + p1Clipped = pClippedNear; + if(flag == vpMbtPolygon::FAR_CLIPPING) + p1ClippedInfo = p1ClippedInfo | vpMbtPolygon::FAR_CLIPPING; + else + p1ClippedInfo = p1ClippedInfo | vpMbtPolygon::NEAR_CLIPPING; } - } - - // Left Clipping - if((clippingFlag & vpMbtPolygon::LEFT_CLIPPING) == vpMbtPolygon::LEFT_CLIPPING){ - if(!cam.isFovComputed()) - vpTRACE("Field of view not computed, left clipping skipped."); - else if(!getClippedPointsFov(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, - fovNormals[0], vpMbtPolygon::LEFT_CLIPPING)) - return false; - } - - // Right Clipping - if((clippingFlag & vpMbtPolygon::RIGHT_CLIPPING) == vpMbtPolygon::RIGHT_CLIPPING ){ - if(!cam.isFovComputed()) - vpTRACE("Field of view not computed, right clipping skipped."); - else if(!getClippedPointsFov(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, - fovNormals[1], vpMbtPolygon::RIGHT_CLIPPING)) - return false; - } - - // Up Clipping - if((clippingFlag & vpMbtPolygon::UP_CLIPPING) == vpMbtPolygon::UP_CLIPPING){ - if(!cam.isFovComputed()) - vpTRACE("Field of view not computed, up clipping skipped."); - else if(!getClippedPointsFov(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, - fovNormals[2], vpMbtPolygon::UP_CLIPPING)) - return false; - } - - // Down Clipping - if((clippingFlag & vpMbtPolygon::DOWN_CLIPPING) == vpMbtPolygon::DOWN_CLIPPING ){ - if(!cam.isFovComputed()) - vpTRACE("Field of view not computed, down clipping skipped."); - else if(!getClippedPointsFov(p1Clipped, p2Clipped, p1Clipped, p2Clipped, p1ClippedInfo, p2ClippedInfo, - fovNormals[3], vpMbtPolygon::DOWN_CLIPPING)) - return false; - } - - // Far Clipping - if( (clippingFlag & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING ){ - if(p1Clipped.get_Z() > distFarClip && p2Clipped.get_Z() > distFarClip) //Not using getClippedPointsFov for efficiency - return false; - else if(p1Clipped.get_Z() > distFarClip || p2Clipped.get_Z() > distFarClip){ - vpPoint pClippedFar; - double t = (p2Clipped.get_Z() - p1Clipped.get_Z()); - t = (distFarClip - p1Clipped.get_Z()) / t; - - pClippedFar.set_X((p2Clipped.get_X() - p1Clipped.get_X())*t + p1Clipped.get_X()); - pClippedFar.set_Y((p2Clipped.get_Y() - p1Clipped.get_Y())*t + p1Clipped.get_Y()); - pClippedFar.set_Z(distFarClip); - - if(p1Clipped.get_Z() > distFarClip){ - p1Clipped = pClippedFar; - p1ClippedInfo = p1ClippedInfo | vpMbtPolygon::FAR_CLIPPING; - } - else{ - p2Clipped = pClippedFar; - p2ClippedInfo = p2ClippedInfo | vpMbtPolygon::FAR_CLIPPING; - } + else{ + p2Clipped = pClippedNear; + if(flag == vpMbtPolygon::FAR_CLIPPING) + p2ClippedInfo = p2ClippedInfo | vpMbtPolygon::FAR_CLIPPING; + else + p2ClippedInfo = p2ClippedInfo | vpMbtPolygon::NEAR_CLIPPING; } } - } - - return true; + + return true; } /*! @@ -474,6 +554,21 @@ vpMbtPolygon::getRoi(const vpCameraParameters &cam, const vpHomogeneousMatrix &c return getRoi(cam); } +/*! + Get the 3D points of the clipped region of interest. + + \warning Suppose that changeFrame() and computeRoiClipped() have already been called. + + \param points : resulting points. +*/ +void +vpMbtPolygon::getRoiClipped(std::vector<vpPoint> &points) +{ + for(unsigned int i = 0 ; i < roiPointsClip.size() ; i++){ + points.push_back(roiPointsClip[i].first); + } +} + /*! Get the region of interest clipped in the image. @@ -488,6 +583,7 @@ vpMbtPolygon::getRoiClipped(const vpCameraParameters &cam, std::vector<vpImagePo for(unsigned int i = 0 ; i < roiPointsClip.size() ; i++){ vpImagePoint ip; vpMeterPixelConversion::convertPoint(cam,roiPointsClip[i].first.get_x(),roiPointsClip[i].first.get_y(),ip); +// std::cout << "## " << ip.get_j() << " - " << ip.get_i() << std::endl; roi.push_back(ip); } } @@ -520,6 +616,7 @@ vpMbtPolygon::getRoiClipped(const vpCameraParameters &cam, std::vector<std::pair { for(unsigned int i = 0 ; i < roiPointsClip.size() ; i++){ vpImagePoint ip; + roiPointsClip[i].first.projection(); vpMeterPixelConversion::convertPoint(cam,roiPointsClip[i].first.get_x(),roiPointsClip[i].first.get_y(),ip); roi.push_back(std::make_pair(ip, roiPointsClip[i].second)); } @@ -570,6 +667,42 @@ vpMbtPolygon::getNbCornerInsideImage(const vpImage<unsigned char>& I, const vpCa // Static functions //################################### +/*! + Static method to compute the clipped points from a set of initial points. + + \warning When using FOV clipping and personnal camera parameters, camera normals have to be computed before (see vpCameraParameters::computeFov()) + + \param ptIn : Input points + \param ptOut : Output points (result of the clipping). + \param cMo : Pose considered for the clipping. + \param clippingFlags: Clipping flag (see vpMbtPolygon::vpMbtPolygonClippingType). + \param cam : Camera parameters (Only used if clipping flags contain FOV clipping). + \param znear : Near clipping distance value (Only used if clipping flags contain Near clipping). + \param zfar : Far clipping distance value (Only used if clipping flags contain Far clipping). +*/ +void +vpMbtPolygon::getClippedPolygon(const std::vector<vpPoint> &ptIn, std::vector<vpPoint> &ptOut, const vpHomogeneousMatrix &cMo, const unsigned int &clippingFlags, + const vpCameraParameters &cam, const double &znear, const double &zfar) +{ + ptOut.clear(); + vpMbtPolygon poly; + poly.setNbPoint((unsigned int)ptIn.size()); + poly.setClipping(clippingFlags); + + if((clippingFlags & vpMbtPolygon::NEAR_CLIPPING) == vpMbtPolygon::NEAR_CLIPPING) + poly.setNearClippingDistance(znear); + + if((clippingFlags & vpMbtPolygon::FAR_CLIPPING) == vpMbtPolygon::FAR_CLIPPING) + poly.setFarClippingDistance(zfar); + + for(unsigned int i = 0; i < ptIn.size(); i++) + poly.addPoint(i,ptIn[i]); + + poly.changeFrame(cMo); + poly.computeRoiClipped(cam); + poly.getRoiClipped(ptOut); +} + void vpMbtPolygon::getMinMaxRoi(const std::vector<vpImagePoint> &iroi, int & i_min, int &i_max, int &j_min, int &j_max) { @@ -628,6 +761,57 @@ vpMbtPolygon::roiInsideImage(const vpImage<unsigned char>& I, const std::vector< return true; } +/*! + Set the flag to consider if the level of detail (LOD) is used or not. + When activated, lines and faces of the 3D model are tracked if respectively their + projected lenght and area in the image are significative enough. By significative, we mean: + - if the lenght of the projected line in the image is greater that a threshold set by + setMinLineLengthThresh() + - if the area of the projected face in the image is greater that a threshold set by + setMinPolygonAreaThresh(). + + \param use_lod : true if level of detail must be used, false otherwise. + + The sample code below shows how to introduce this feature: + \code +#include <visp/vpMbEdgeTracker.h> +#include <visp/vpImageIo.h> + +int main() +{ +vpImage<unsigned char> I; + +// Acquire an image +vpImageIo::read(I, "my-image.pgm"); + +std::string object = "my-object"; +vpMbEdgeTracker tracker; +tracker.loadConfigFile( object+".xml" ); +tracker.loadModel( object+".cao" ); + +tracker.setLod(true); +tracker.setMinLineLengthThresh(20.); +tracker.setMinPolygonAreaThresh(20.*20.); + +tracker.initClick(I, object+".init" ); + +while (true) { + // tracking loop +} +vpXmlParser::cleanup(); + +return 0; +} + \endcode + + \sa setMinLineLengthThresh(), setMinPolygonAreaThresh() + */ +void +vpMbtPolygon::setLod(const bool use_lod) +{ + this->useLod = use_lod; +} + #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS /*! \deprecated This method is deprecated since it is no more used since ViSP 2.7.2. \n diff --git a/src/tracking/mbt/edge/vpMbtPolygon.h b/src/tracking/mbt/vpMbtPolygon.h similarity index 69% rename from src/tracking/mbt/edge/vpMbtPolygon.h rename to src/tracking/mbt/vpMbtPolygon.h index 1109ac6ef189716fb5a4027ce26b6b6e1baa5efc..3fde8b700540f55b72b62e36617886a2fcdcd0d5 100644 --- a/src/tracking/mbt/edge/vpMbtPolygon.h +++ b/src/tracking/mbt/vpMbtPolygon.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMbtPolygon.h 4331 2013-07-22 12:37:11Z ayol $ + * $Id: vpMbtPolygon.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -81,7 +81,7 @@ public: } vpMbtPolygonClippingType; public: - //! Index of the polygon. Cannot be unsigned int because deafult value is -1. + //! Index of the polygon. Cannot be unsigned int because default value is -1. int index; //! Number of points used to define the polygon. unsigned int nbpt; @@ -101,21 +101,30 @@ public: double distNearClip; //! Distance for near clipping double distFarClip; + //! Flag to specify if the visibility of the polygon depends also of the current level of detail (LOD) + bool useLod; + //! Threshold for minimum line length in pixel to consider if the line is visible or not in LOD case + double minLineLengthThresh; + //! Threshold for minimum polygon area in pixel to consider if the polygon is visible or not in LOD case + double minPolygonAreaThresh; + //! Name of the polygon + std::string name; private: - bool getClippedPointsFov(const vpPoint &p1, const vpPoint &p2, + bool getClippedPointsFovGeneric(const vpPoint &p1, const vpPoint &p2, vpPoint &p1Clipped, vpPoint &p2Clipped, unsigned int &p1ClippedInfo, unsigned int &p2ClippedInfo, const vpColVector &normal, const unsigned int &flag); - - bool getClippedPoints(const vpPoint &p1, const vpPoint &p2, - vpPoint &p1Clipped, vpPoint &p2Clipped, + + bool getClippedPointsDistance(const vpPoint &p1, const vpPoint &p2, + vpPoint &p1Clipped, vpPoint &p2Clipped, unsigned int &p1ClippedInfo, unsigned int &p2ClippedInfo, - const vpCameraParameters &cam, const std::vector<vpColVector> &fovNormals); + const unsigned int &flag, const double &distance); public: - vpMbtPolygon() ; + vpMbtPolygon() ; + vpMbtPolygon(const vpMbtPolygon& mbtp) ; virtual ~vpMbtPolygon() ; void addPoint(const unsigned int n, const vpPoint &P) ; @@ -146,6 +155,13 @@ public: \return index : the index of the face. */ inline int getIndex() const {return index ;} + + /*! + Get the name of the face. + + \return Name of the face. + */ + inline std::string getName() const {return name;} /*! Return the number of corners. @@ -176,6 +192,8 @@ public: std::vector<vpImagePoint> getRoi(const vpCameraParameters &cam, const vpHomogeneousMatrix &cMo); + void getRoiClipped(std::vector<vpPoint> &points); + void getRoiClipped(const vpCameraParameters &cam, std::vector<vpImagePoint>&roi); void getRoiClipped(const vpCameraParameters &cam, std::vector<vpImagePoint>&roi, const vpHomogeneousMatrix &cMo); @@ -185,9 +203,12 @@ public: void getRoiClipped(const vpCameraParameters &cam, std::vector<std::pair<vpImagePoint,unsigned int> > &roi, const vpHomogeneousMatrix &cMo); inline bool isAppearing() const {return isappearing;} - virtual bool isVisible(const vpHomogeneousMatrix &cMo, const double alpha, const bool &modulo = false) ; + virtual bool isVisible(const vpHomogeneousMatrix &cMo, const double alpha, const bool &modulo = false, + const vpCameraParameters &cam = vpCameraParameters(), const vpImage<unsigned char> &I = vpImage<unsigned char>()); bool isVisible() const {return isvisible;} - + + vpMbtPolygon& operator=(const vpMbtPolygon& mbtp) ; + /*! Specify which clipping to use. @@ -210,6 +231,45 @@ public: \param i : the new index of the face. */ virtual inline void setIndex(const int i ) { index = i ; } + + // Due to a doxygen warning include the sample code in the doc, we remove the inline and put the doc in the *.cpp file + void setLod(const bool use_lod); + /*! + Set the threshold for the minimum line length to be considered as visible in the LOD + (level of detail) case. This threshold is only used when setLoD() is turned on. + + \param min_line_length : threshold for the minimum line length in pixel. When a single line that doesn't + belong to a face is considered by the tracker, this line is tracked only if its lenght in pixel is + greater than \e min_line_length. + + \sa setLoD() + */ + inline void setMinLineLengthThresh(const double min_line_length) { + this->minLineLengthThresh = min_line_length; + } + /*! + Set the minimum polygon area to be considered as visible in the LOD (level of detail) + case. This threshold is only used when setLoD() is turned on. + + \param min_polygon_area : threshold for the minimum polygon area in pixel. When a face + is considered by the tracker, this face is tracked only if its area in pixel is + greater than \e min_polygon_area. + + \sa setLoD() + */ + inline void setMinPolygonAreaThresh(const double min_polygon_area) { + this->minPolygonAreaThresh = min_polygon_area; + } + + /*! + Set the name of the face. + + \param face_name : name of the face. + */ + inline void setName(const std::string &face_name) { + this->name = face_name; + } + virtual void setNbPoint(const unsigned int nb) ; /*! @@ -220,6 +280,9 @@ public: inline void setNearClippingDistance(const double &dist) { distNearClip = dist; clippingFlag = (clippingFlag | vpMbtPolygon::NEAR_CLIPPING);} public: + static void getClippedPolygon(const std::vector<vpPoint> &ptIn, std::vector<vpPoint> &ptOut, const vpHomogeneousMatrix &cMo, + const unsigned int &clippingFlags, const vpCameraParameters &cam = vpCameraParameters(), + const double &znear = 0.001, const double &zfar = 100 ); static void getMinMaxRoi(const std::vector<vpImagePoint> &roi, int & i_min, int &i_max, int &j_min, int &j_max); static bool roiInsideImage(const vpImage<unsigned char>& I, const std::vector<vpImagePoint>& corners); diff --git a/src/tracking/moments/vpMoment.cpp b/src/tracking/moments/vpMoment.cpp index e1c7a0ade932b401fb3aad4111c47ae6f8832202..df576f6d17bca6e5f4792d3bf3c5e3355bced1c4 100644 --- a/src/tracking/moments/vpMoment.cpp +++ b/src/tracking/moments/vpMoment.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMoment.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMoment.cpp 4709 2014-03-28 17:37:12Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,8 +52,7 @@ /*! Default constructor */ -vpMoment::vpMoment(): object(NULL),moments(NULL) { -} +vpMoment::vpMoment(): object(NULL), moments(NULL), values() {} /*! @@ -99,23 +98,27 @@ int main() \endcode - \param moments : database of moment primitives. + \param data_base : database of moment primitives. */ -void vpMoment::linkTo(vpMomentDatabase& moments){ - std::strcpy(_name,name()); - this->moments=&moments; +void vpMoment::linkTo(vpMomentDatabase& data_base){ + if (strlen( name() ) >= 255) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the moment name")); + } + std::strcpy(_name,name()); + this->moments=&data_base; - moments.add(*this,_name); + data_base.add(*this,_name); } /*! Updates the moment with the current object. This does not compute any values. - \param object : object descriptor of the current camera vision. + \param moment_object : object descriptor of the current camera vision. */ -void vpMoment::update(vpMomentObject& object){ - this->object=&object; +void vpMoment::update(vpMomentObject& moment_object){ + this->object=&moment_object; } /*! @@ -123,10 +126,18 @@ void vpMoment::update(vpMomentObject& object){ \param os : a std::stream. \param m : a moment instance. */ -std::ostream & operator<<(std::ostream & os, const vpMoment& m){ +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMoment& m){ for(std::vector<double>::const_iterator i = m.values.begin();i!=m.values.end();i++) os << *i << ","; return os; } +/*! +Prints values of all dependent moments required to calculate a specific vpMoment. +Not made pure to maintain compatibility +Recommended : Types inheriting from vpMoment should implement this function +*/ +void vpMoment::printDependencies(std::ostream& os) const{ + os << " WARNING : Falling back to base class version of printDependencies(). To prevent that, this has to be implemented in the derived classes!" << std::endl; +} diff --git a/src/tracking/moments/vpMoment.h b/src/tracking/moments/vpMoment.h index 02ec5b9a8f8d620eb549f7cf6558fbb151a46721..60bcf6e0f23c2a6f10545af5c1dfb5c7e2bc6548 100644 --- a/src/tracking/moments/vpMoment.h +++ b/src/tracking/moments/vpMoment.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMoment.h 4219 2013-04-17 10:00:56Z mbakthav $ + * $Id: vpMoment.h 4708 2014-03-28 17:36:46Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -87,19 +87,23 @@ class vpMomentObject; Some moments can be computed only if they are linked to a a database containing their dependencies. Linking to a database is done using the vpMoment::linkTo(...) method. - There are no constraints about format of the array returned by vpMoment::get: any implementation is fine. + There are no constraints about format of the array returned by vpMoment::get(); any implementation is fine. Each moment must have a string name by implementing the char* vpMoment::name() method which allows to identify the moment in the database. Each moment must also implement a compute method describing how to obtain its values from the object. - \attention Order of moment computation DOES matter: when you compute (vpMoment::compute call) a moment, all moment dependencies must be computed. - Moments pre-implementes dans ViSP: + \attention Order of moment computation DOES matter: when you compute a moment using vpMoment::compute(), + all moment dependencies must be computed. + We recall that implemented moments are: - vpMomentAlpha + - vpMomentArea + - vpMomentAreaNormalized - vpMomentBasic - vpMomentCentered - vpMomentCInvariant - - vpMomentSInvariant - - vpMomentAreaNormalized + - vpMomentGravityCenter + - vpMomentGravityCenterNormalized + */ class VISP_EXPORT vpMoment{ private: @@ -112,22 +116,21 @@ class VISP_EXPORT vpMoment{ Returns the linked moment database. \return the moment database */ - inline vpMomentDatabase& getMoments(){ return *moments; } - - + inline vpMomentDatabase& getMoments() const { return *moments; } public: - inline vpMomentObject& getObject() const { return *object;} + inline const vpMomentObject& getObject() const { return *object;} vpMoment(); /*! Returns all values computed by the moment. \return vector of values */ - std::vector<double>& get(){ return values;} + const std::vector<double>& get() const { return values;} void linkTo(vpMomentDatabase& moments); void update(vpMomentObject& object); virtual void compute()=0; - virtual const char* name() = 0; + virtual const char* name() const = 0; friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMoment& m); + virtual void printDependencies(std::ostream& os) const; /*! Virtual destructor. diff --git a/src/tracking/moments/vpMomentAlpha.cpp b/src/tracking/moments/vpMomentAlpha.cpp index 0a1b9ca283816685c8e0425183a8c43f516de446..8a180db4b378541f063c6559e29cd175d42e4171 100644 --- a/src/tracking/moments/vpMomentAlpha.cpp +++ b/src/tracking/moments/vpMomentAlpha.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentAlpha.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentAlpha.cpp 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,8 +48,8 @@ Empty constructor. Initializes alpha moment as a reference alpha. A default-constructed alpha moment may be used as a reference for other alphas. A reference alpha is class harbouring an alpha value computed for a \f$[-\pi/2..\pi/2]\f$ portion of the circle. Not setting a reference alpha will prevent you from doing more than 180deg rotation with moments. -*/ -vpMomentAlpha::vpMomentAlpha() : vpMoment(),isRef(true), symmetric(false){ + */ +vpMomentAlpha::vpMomentAlpha() : isRef(true), symmetric(false), ref(), alphaRef(0.) { values.resize(1); } @@ -57,102 +57,116 @@ vpMomentAlpha::vpMomentAlpha() : vpMoment(),isRef(true), symmetric(false){ Common constructor. Initializes alpha moment as a non-reference alpha. A default-constructed alpha moment must be used as a reference for other this alpha. A reference alpha is class harbouring an alpha value computed for a \f$[0..\pi]\f$ portion of the circle. Not having a reference alpha will prevent you from doing more than 180deg rotation with moments. - - \param ref : vector of 3rd order centered moments corresponding to the reference alpha in the following order: \f$\mu_{03},\mu_{12},\mu_{21},\mu_{30}\f$. - \param alphaRef : value of the reference alpha. + \param ref_ : vector of 3rd order centered moments corresponding to the reference alpha in the following order: \f$\mu_{03},\mu_{12},\mu_{21},\mu_{30}\f$. + \param alpha_ref : value of the reference alpha. */ -vpMomentAlpha::vpMomentAlpha(std::vector<double>& ref,double alphaRef) : vpMoment(),isRef(false),symmetric(false),ref(ref),alphaRef(alphaRef){ - for (std::vector<double>::iterator it = ref.begin(); it!=ref.end(); it++) - if (*it<=1e-4) - symmetric = true; - - values.resize(1); +vpMomentAlpha::vpMomentAlpha(std::vector<double>& ref_, double alpha_ref) + : vpMoment(),isRef(false),symmetric(false),ref(ref_),alphaRef(alpha_ref) +{ + for (std::vector<double>::iterator it = ref_.begin(); it!=ref_.end(); it++) + if (*it<=1e-4) + symmetric = true; + + values.resize(1); } /*! Compute the value of the alpha-moment. Depends on vpMomentCentered. -*/ + */ void vpMomentAlpha::compute(){ //symmetric = symmetric | this->getObject().isSymmetric(); - bool found_moment_centered; - - vpMomentCentered& momentCentered = (static_cast<vpMomentCentered&> (getMoments().get("vpMomentCentered", - found_moment_centered))); - - if (!found_moment_centered) - throw vpException(vpException::notInitialized, "vpMomentCentered not found"); - - double alpha = 0.5 * atan2(2.0 * momentCentered.get(1, 1), (momentCentered.get(2, 0) - momentCentered.get(0, 2))); - - unsigned int order = 4; - std::vector<double> rotMu(4); - std::vector<double> realMu(4); - - if (isRef) - { - alphaRef = alpha; - } - else - { - if (!symmetric) - { - double r11 = cos(alpha - alphaRef); - double r12 = sin(alpha - alphaRef); - double r21 = -sin(alpha - alphaRef); - double r22 = cos(alpha - alphaRef); - unsigned int idx = 0; - for (register unsigned int c = 0; c < (order) * (order); c++) - { - unsigned int i = c % order; - unsigned int j = c / order; - - if (i + j == 3) - { - double r11_k = 1.; - for (register unsigned int k = 0; k <= i; k++) - { - double r12_i_k = pow(r12, (int)(i - k)); - double comb_i_k = static_cast<double> (vpMath::comb(i, k)); - for (register unsigned int l = 0; l <= j; l++) - { - rotMu[idx] += static_cast<double> (comb_i_k * vpMath::comb(j, l) * r11_k * pow(r21, (int)l) * r12_i_k - * pow(r22, (int)(j - l)) * momentCentered.get(k + l, (unsigned int)(int)(i + j - k - l))); - } - r11_k *= r11; - } - realMu[idx] = momentCentered.get(i, j); - idx++; - } - } - - double sum = 0.; - bool signChange = true; - for (register unsigned int i = 0; i < 4; i++) - { - if (std::abs(rotMu[i]) > 1e10 * std::numeric_limits<double>::epsilon() && std::abs(ref[i]) > 1e10 - * std::numeric_limits<double>::epsilon() && rotMu[i] * ref[i] > 0) - signChange = false; - sum += std::abs(rotMu[i] * ref[i]); - } - - if (sum < 1e4 * std::numeric_limits<double>::epsilon()) - signChange = false; - if (signChange) - alpha = alpha + M_PI; - } - } - values[0] = alpha; + bool found_moment_centered; + + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&> (getMoments().get("vpMomentCentered", + found_moment_centered))); + + if (!found_moment_centered) + throw vpException(vpException::notInitialized, "vpMomentCentered not found"); + + double alpha = 0.5 * atan2(2.0 * momentCentered.get(1, 1), (momentCentered.get(2, 0) - momentCentered.get(0, 2))); + + unsigned int order = 4; + std::vector<double> rotMu(4); + std::vector<double> realMu(4); + + if (isRef) + { + alphaRef = alpha; + } + else + { + if (!symmetric) + { + double r11 = cos(alpha - alphaRef); + double r12 = sin(alpha - alphaRef); + double r21 = -sin(alpha - alphaRef); + double r22 = cos(alpha - alphaRef); + unsigned int idx = 0; + for (register unsigned int c = 0; c < (order) * (order); c++) + { + unsigned int i = c % order; + unsigned int j = c / order; + + if (i + j == 3) + { + double r11_k = 1.; + for (register unsigned int k = 0; k <= i; k++) + { + double r12_i_k = pow(r12, (int)(i - k)); + double comb_i_k = static_cast<double> (vpMath::comb(i, k)); + for (register unsigned int l = 0; l <= j; l++) + { + rotMu[idx] += static_cast<double> (comb_i_k * vpMath::comb(j, l) * r11_k * pow(r21, (int)l) * r12_i_k + * pow(r22, (int)(j - l)) * momentCentered.get(k + l, (unsigned int)(int)(i + j - k - l))); + } + r11_k *= r11; + } + realMu[idx] = momentCentered.get(i, j); + idx++; + } + } + + double sum = 0.; + bool signChange = true; + for (register unsigned int i = 0; i < 4; i++) + { + if (std::abs(rotMu[i]) > 1e10 * std::numeric_limits<double>::epsilon() && std::abs(ref[i]) > 1e10 + * std::numeric_limits<double>::epsilon() && rotMu[i] * ref[i] > 0) + signChange = false; + sum += std::abs(rotMu[i] * ref[i]); + } + + if (sum < 1e4 * std::numeric_limits<double>::epsilon()) + signChange = false; + if (signChange) + alpha = alpha + M_PI; + } + } + values[0] = alpha; } /*! - Prints the value of the orientation. -*/ -std::ostream & operator<<(std::ostream & os, const vpMomentAlpha& c){ - os << c.values[0] ; - return os; + Prints the value of the major-axis orientation in degrees and rad + */ +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentAlpha& c){ + os << (__FILE__) << std::endl; + os << "Alpha = " << c.values[0] << "rad = " << vpMath::deg(c.values[0]) << "deg " << std::endl; + return os; } - - - +/*! +Prints the dependencies of alpha, namely centered moments mu11, mu20 ad mu02 +*/ +void vpMomentAlpha::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + bool found_moment_centered; + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&> (getMoments().get("vpMomentCentered", + found_moment_centered))); + if (!found_moment_centered) + throw vpException(vpException::notInitialized, "vpMomentCentered not found"); + + os << "mu11 = " << momentCentered.get(1, 1) << "\t"; + os << "mu20 = " << momentCentered.get(2, 0) << "\t"; + os << "mu02 = " << momentCentered.get(0, 2) << std::endl; +} diff --git a/src/tracking/moments/vpMomentAlpha.h b/src/tracking/moments/vpMomentAlpha.h index eed0d6aa759dae7ea72e3df1bd80c31de014f3ca..4fcf3562a1b167c6294cfa9f0b20d53c89bcef10 100644 --- a/src/tracking/moments/vpMomentAlpha.h +++ b/src/tracking/moments/vpMomentAlpha.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentAlpha.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentAlpha.h 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -210,15 +210,30 @@ class VISP_EXPORT vpMomentAlpha : public vpMoment { /*! Retrieve the orientation of the object as a single double value. */ - double get(){ return values[0]; } + double get() const { return values[0]; } /*! Moment name. */ - const char* name(){return "vpMomentAlpha";} + const char* name() const {return "vpMomentAlpha";} + + inline bool is_ref() const + { + if (isRef) + return true; + else + return false; + } + + inline bool is_symmetric() const + { + if (symmetric) + return true; + else + return false; + } friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentAlpha& v); - - + void printDependencies(std::ostream& os) const; }; #endif diff --git a/src/tracking/moments/vpMomentArea.cpp b/src/tracking/moments/vpMomentArea.cpp index 5e23f6043cee55f59ddbdc879e70b4661a16e556..371362d8acee218a668b84623c059664c6c59e8c 100644 --- a/src/tracking/moments/vpMomentArea.cpp +++ b/src/tracking/moments/vpMomentArea.cpp @@ -3,7 +3,7 @@ * $Id: vpMomentArea.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,7 @@ void vpMomentArea::compute(){ /* getMoments() returns a reference to a vpMomentDatabase. It is a protected member of and is inherited from vpMoment * .get() is a member function of vpMomentDatabase that returns a specific moment which is linked to it */ - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); values[0] = momentCentered.get(2,0) + momentCentered.get(0,2); } @@ -74,7 +74,31 @@ vpMomentArea::vpMomentArea() : vpMoment(){ /*! Outputs the moment's values to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentArea& m){ - os << "Area a:" << m.values[0]; +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentArea& m){ + os << (__FILE__) << std::endl; + os << "a(m00) = " << m.values[0] << std::endl; return os; } + +/*! +If the vpMomentObject type is +1. DISCRETE(set of discrete points), uses mu20+mu02 +2. DENSE_FULL_OBJECT(from image) used mu00 +*/ +void vpMomentArea::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + + bool found_moment_centered; + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); + if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); + + if(getObject().getType()==vpMomentObject::DISCRETE) + { + os << "mu20 = " << momentCentered.get(2, 0) << "\t"; + os << "mu02 = " << momentCentered.get(0, 2) << std::endl; + } + else + { + os << "mu00 = " << momentCentered.get(0, 0) << std::endl; + } +} diff --git a/src/tracking/moments/vpMomentArea.h b/src/tracking/moments/vpMomentArea.h index f70783fea12c249501a86f5f6905846065622cac..70500c41914670ed8cd30f84321e0f09fb406002 100644 --- a/src/tracking/moments/vpMomentArea.h +++ b/src/tracking/moments/vpMomentArea.h @@ -3,7 +3,7 @@ * $Id: vpMomentArea.h 3530 2012-01-03 10:52:12Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,8 +61,9 @@ class VISP_EXPORT vpMomentArea : public vpMoment { /*! Moment name. */ - const char* name(){return "vpMomentArea";} + const char* name() const {return "vpMomentArea";} friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentArea& m); + void printDependencies(std::ostream& os) const; }; #endif diff --git a/src/tracking/moments/vpMomentAreaNormalized.cpp b/src/tracking/moments/vpMomentAreaNormalized.cpp index 12b8113320e1775aea50859f27132fe78cd16b34..4b2865b337b5700a3b6431fcb18dbf457b794a6a 100644 --- a/src/tracking/moments/vpMomentAreaNormalized.cpp +++ b/src/tracking/moments/vpMomentAreaNormalized.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentAreaNormalized.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentAreaNormalized.cpp 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ void vpMomentAreaNormalized::compute(){ /* getMoments() returns a reference to a vpMomentDatabase. (a protected member inherited from vpMoment) .get() is a member function of vpMomentDatabase that returns a specific moment which is linked to it*/ - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); @@ -71,18 +71,46 @@ void vpMomentAreaNormalized::compute(){ /*! Default constructor. - \param desiredSurface : desired area \e a* when the visual servoing converges. - \param desiredDepth : desired depth \e Z* when the visual servoing converges. + \param desired_surface : desired area \e a* when the visual servoing converges. + \param desired_depth : desired depth \e Z* when the visual servoing converges. */ -vpMomentAreaNormalized::vpMomentAreaNormalized(double desiredSurface, double desiredDepth) : vpMoment(),desiredSurface(desiredSurface),desiredDepth(desiredDepth){ +vpMomentAreaNormalized::vpMomentAreaNormalized(double desired_surface, double desired_depth) + : vpMoment(),desiredSurface(desired_surface),desiredDepth(desired_depth) +{ values.resize(1); } /*! Outputs the moment's values to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentAreaNormalized& m){ - os << "An:" << m.values[0] ; - +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentAreaNormalized& m){ + os << (__FILE__) << std::endl; + os << "An = " << m.values[0] << std::endl ; return os; } + +/*! +Prints dependencies namely, +1. Depth at desired pose Z* +2. Area moment at desired pose + m00* if DENSE moment object, (mu20* + mu02*) if DISCRETE moment object +3. Area moment at current pose + m00 if DENSE moment object, (mu20 + mu02) if DISCRETE moment object +*/ +void vpMomentAreaNormalized::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + os << "Desired depth Z* = " << desiredDepth << std::endl; + os << "Desired area m00* = " << desiredSurface << std::endl; + + bool found_moment_centered; + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered)); + if(!found_moment_centered) + throw vpException(vpException::notInitialized,"vpMomentCentered not found"); + + double a; + if(getObject().getType()==vpMomentObject::DISCRETE) + a = momentCentered.get(2,0)+momentCentered.get(0,2); + else + a = getObject().get(0,0); + os << "a = " << a << std::endl; +} diff --git a/src/tracking/moments/vpMomentAreaNormalized.h b/src/tracking/moments/vpMomentAreaNormalized.h index f6ac39f9640ff206753de00c750679dcace603ff..5c5e1c42711a1e0df3131c4f3b2ced039f2f6b75 100644 --- a/src/tracking/moments/vpMomentAreaNormalized.h +++ b/src/tracking/moments/vpMomentAreaNormalized.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentAreaNormalized.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpMomentAreaNormalized.h 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -139,17 +139,18 @@ class VISP_EXPORT vpMomentAreaNormalized : public vpMoment { /*! Retrieves the desired depth \e Z* as specified in the constructor. */ - double getDesiredDepth(){ return desiredDepth; } + double getDesiredDepth() const { return desiredDepth; } /*! Retrieves the desired surface \e a* as specified in the constructor. */ - double getDesiredSurface(){ return desiredSurface; } + double getDesiredSurface() const { return desiredSurface; } /*! Moment name. */ - const char* name(){return "vpMomentAreaNormalized";} + const char* name() const {return "vpMomentAreaNormalized";} friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentAreaNormalized& v); + void printDependencies(std::ostream& os) const; }; #endif diff --git a/src/tracking/moments/vpMomentBasic.cpp b/src/tracking/moments/vpMomentBasic.cpp index 37db4a4641141a1a9891dd5e17116e8682f0372f..9dfb71f905fb4702cbd7a59f443ddc3cc71ba745 100644 --- a/src/tracking/moments/vpMomentBasic.cpp +++ b/src/tracking/moments/vpMomentBasic.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentBasic.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentBasic.cpp 4711 2014-03-28 17:41:47Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ vpMomentBasic::vpMomentBasic() : vpMoment(){ Same behaviour as vpMomentObject. */ -std::vector<double>& vpMomentBasic::get(){ +const std::vector<double>& vpMomentBasic::get() const{ return getObject().get(); } @@ -66,7 +66,7 @@ std::vector<double>& vpMomentBasic::get(){ Same behaviour as vpMomentObject. */ -double vpMomentBasic::get(unsigned int i,unsigned int j){ +double vpMomentBasic::get(unsigned int i,unsigned int j) const{ return getObject().get(i,j); } @@ -81,9 +81,18 @@ void vpMomentBasic::compute(){ Outputs the moment's values to a stream. Same output as in vpMomentObject. */ -std::ostream & operator<<(std::ostream & os, vpMomentBasic& m){ - - os << m.getObject(); - +VISP_EXPORT std::ostream& operator<<(std::ostream & os, const vpMomentBasic& m){ + os << (__FILE__) << std::endl; + vpMomentObject::printWithIndices(m.getObject(), os); return os; } + +/*! +No dependencies on other vpMoments, since basic moments are computed in vpMomentObject +Just prints the basic moments in vpMomentObject with indices +*/ +void vpMomentBasic::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + os << "No dependencies on other vpMoments, since basic moments are computed in vpMomentObject" << std::endl; + vpMomentObject::printWithIndices(getObject(), os); +} diff --git a/src/tracking/moments/vpMomentBasic.h b/src/tracking/moments/vpMomentBasic.h index 26fd96c91966fa57c60fe527ab0283633dedb306..8dde8610ae622b45dae6cf1c9ad1c0d8889824a0 100644 --- a/src/tracking/moments/vpMomentBasic.h +++ b/src/tracking/moments/vpMomentBasic.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentBasic.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentBasic.h 4710 2014-03-28 17:39:33Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -76,13 +76,13 @@ class VISP_EXPORT vpMomentBasic : public vpMoment { vpMomentBasic(); void compute(); - std::vector<double>& get(); - double get(unsigned int i,unsigned int j); + const std::vector<double>& get() const; + double get (unsigned int i,unsigned int j) const; /*! Moment name. */ - const char* name(){ return "vpMomentBasic";} - + const char* name() const { return "vpMomentBasic";} + friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentBasic& v); + void printDependencies(std::ostream& os) const; }; -std::ostream & operator<<(std::ostream & os, vpMomentBasic& v); #endif diff --git a/src/tracking/moments/vpMomentCInvariant.cpp b/src/tracking/moments/vpMomentCInvariant.cpp index 4bda5b749a805d8b692482b4ee3c1ab2218b0a04..20230acb13202db68910ed3256bba797ce94f0c8 100644 --- a/src/tracking/moments/vpMomentCInvariant.cpp +++ b/src/tracking/moments/vpMomentCInvariant.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCInvariant.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentCInvariant.cpp 5304 2015-02-10 17:03:04Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,9 +45,12 @@ /*! Default constructor. + (option to use a different calculation mode for sx and sy) */ -vpMomentCInvariant::vpMomentCInvariant() : vpMoment(),I(16),II(4),c(4),s(4){ - values.resize(14); +vpMomentCInvariant::vpMomentCInvariant(bool flg_sxsynormalization) + : I(16),II(4),c(4),s(4), K(0.0), cn(4),sn(4), In1(0.0), flg_sxsynormalization_(flg_sxsynormalization) +{ + values.resize(14); } /*! @@ -55,9 +58,11 @@ vpMomentCInvariant::vpMomentCInvariant() : vpMoment(),I(16),II(4),c(4),s(4){ \param momentCentered : centered moments \param I : invariant output values */ -void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector<double>& I){ +void vpMomentCInvariant::computeI(const vpMomentCentered& momentCentered, std::vector<double>& I_val){ + double mu30 = momentCentered.get(3,0); double mu30_2 = mu30*mu30; + double mu30_3 = mu30_2*mu30; double mu03 = momentCentered.get(0,3); double mu03_2 = mu03*mu03; @@ -80,7 +85,6 @@ void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector< double mu12 = momentCentered.get(1,2); double mu11 = momentCentered.get(1,1); - double mu11_2 = mu11*mu11; double mu12_2 = mu12*mu12; double mu21_2 = mu21*mu21; @@ -94,8 +98,7 @@ void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector< double mu12_4 = mu12_3*mu12; double mu21_4 = mu21_2*mu21_2; - - double kappa = mu30_2+mu03_2-3*mu21_2+6*mu21*mu03; + //double kappa = mu30_2+mu03_2-3*mu21_2+6*mu21*mu03; //Used in I8 calculation but simplified with MAPLE and found it to be wrong double zeta = mu20-mu02; double zeta_2 = zeta * zeta; double omicron = (mu03_2+3*mu03*mu21+mu30*(mu30+3*mu12)); @@ -113,23 +116,23 @@ void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector< double delta_2 = delta*delta; double phi_2 = phi*phi; - I[1]=-mu20*mu02+mu11_2; - I[2]=zeta_2+4*mu11_2; - I[3]=(mu30-3*mu12)*(mu30-3*mu12)+(mu03-3*mu21)*(mu03-3*mu21); - I[4]=(mu30+mu12)*(mu30+mu12)+(mu21+mu03)*(mu21+mu03); - I[5]=-mu30_2*mu03_2+(-4*mu12_3+6*mu21*mu12*mu03)*mu30-4*mu21_3*mu03+3*mu21_2*mu12_2; - I[6]=3*mu12_4+2*mu30*mu12_3+(3*mu30_2-6*mu03*mu21)*mu12_2-6*mu30*mu21*(mu21+mu03)*mu12+2*mu30_2*mu03_2+2*mu21_3*mu03+3*mu21_2*mu03_2+3*mu21_4; - - I[7]=(3*mu21+2*mu03)*mu12_3+3*mu30*(mu03+2*mu21)*mu12_2-3*mu21*(mu30+mu03+mu21)*(-mu30+mu03+mu21)*mu12+mu30*(-mu30_2*mu03-2*mu21_3-3*mu03*mu21_2+mu03_3); - I[8]=3*mu21_4-3*mu21_3*mu03+(3*mu03_2+kappa-6*mu12_2)*mu21_2-mu03*(-15*mu12_2+kappa)*mu21-(-3*mu12_2*mu30+(2*kappa-3*mu03_2)*mu12+kappa*mu30)*mu12; - I[9]=omicron*omicron; - - I[10]=mu40*mu04-4*mu31*mu13+3*mu22_2; - I[11]=3*mu13_2+2*mu31*mu13+(-3*mu40-3*mu04)*mu22-2*mu40*mu04+3*mu31_2; - I[12]=3*mu04_2+(2*mu40+12*mu22)*mu04+3*mu40_2+12*mu40*mu22+16*mu31*mu13; - I[13]=omega_2+nu_2; - I[14]=ro_2+gamma_2; - I[15]=delta_2+phi_2; + I_val[1]=-mu20*mu02+mu11_2; + I_val[2]=zeta_2+4*mu11_2; + I_val[3]=(mu30-3*mu12)*(mu30-3*mu12)+(mu03-3*mu21)*(mu03-3*mu21); + I_val[4]=(mu30+mu12)*(mu30+mu12)+(mu21+mu03)*(mu21+mu03); + I_val[5]=-mu30_2*mu03_2+(-4*mu12_3+6*mu21*mu12*mu03)*mu30-4*mu21_3*mu03+3*mu21_2*mu12_2; + I_val[6]=3*mu12_4+2*mu30*mu12_3+(3*mu30_2-6*mu03*mu21)*mu12_2-6*mu30*mu21*(mu21+mu03)*mu12+2*mu30_2*mu03_2+2*mu21_3*mu03+3*mu21_2*mu03_2+3*mu21_4; + I_val[7]=(3*mu21+2*mu03)*mu12_3+3*mu30*(mu03+2*mu21)*mu12_2-3*mu21*(mu30+mu03+mu21)*(-mu30+mu03+mu21)*mu12+mu30*(-mu30_2*mu03-2*mu21_3-3*mu03*mu21_2+mu03_3); + //I_val[8]=3*mu21_4-3*mu21_3*mu03+(3*mu03_2+kappa-6*mu12_2)*mu21_2-mu03*(-15*mu12_2+kappa)*mu21-(-3*mu12_2*mu30+(2*kappa-3*mu03_2)*mu12+kappa*mu30)*mu12; + I_val[8] = 3*mu03*mu21_3-2*mu03_2*mu21_2+mu21_2*mu30_2+3*mu12_2*mu03*mu21-mu03*mu21*mu30_2-mu03_3*mu21+3*mu12_3*mu30-2*mu12_2*mu30_2+mu12_2*mu03_2-mu12*mu30_3-mu12*mu30*mu03_2+3*mu12*mu30*mu21_2-6*mu12*mu30*mu03*mu21; + I_val[9]=omicron*omicron; + + I_val[10]=mu40*mu04-4*mu31*mu13+3*mu22_2; + I_val[11]=3*mu13_2+2*mu31*mu13+(-3*mu40-3*mu04)*mu22-2*mu40*mu04+3*mu31_2; + I_val[12]=3*mu04_2+(2*mu40+12*mu22)*mu04+3*mu40_2+12*mu40*mu22+16*mu31*mu13; + I_val[13]=omega_2+nu_2; + I_val[14]=ro_2+gamma_2; + I_val[15]=delta_2+phi_2; double a; if(getObject().getType()==vpMomentObject::DISCRETE) @@ -148,10 +151,31 @@ void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector< II[2]=c[2]*c[2]+s[2]*s[2]; II[3]=momentCentered.get(2,0)+momentCentered.get(0,2); - K=(II[1]*(II[3]*sqrt(II[3])))/sqrt(a); + K=(II[1]*(II[3]*sqrt(std::abs(II[3]))))/sqrt(std::abs(a)); + + /* + * Intermediate quantities required for calculation of normalized version of Sx and Sy + * The pij doubles below are the respective centered moment values mu_ij scaled by mu20 + mu02 + */ + double p20 = momentCentered.get(2,0)/II[3]; // II[3] is the normalization factor for the 2nd order moments + double p11 = momentCentered.get(1,1)/II[3]; + double p02 = momentCentered.get(0,2)/II[3]; + double d = sqrt(std::abs(a))/(II[3]*sqrt(std::abs(II[3]))); // d is the normalization factor for 3rd order moments + double p30 = momentCentered.get(3,0)*d; + double p21 = momentCentered.get(2,1)*d; + double p12 = momentCentered.get(1,2)*d; + double p03 = momentCentered.get(0,3)*d; + cn[1] = p20 - p02; + sn[1] = 2.0*p11; + sn[2] = p30 - 3.0*p12; + cn[2] = p03 - 3.0*p21; + cn[3] = cn[1]*cn[1]-sn[1]*sn[1]; + sn[3] = 2.0*sn[1]*cn[1]; + + In1 = cn[1]*cn[1]+sn[1]*sn[1]; } /*! @@ -162,7 +186,7 @@ void vpMomentCInvariant::computeI(vpMomentCentered& momentCentered, std::vector< void vpMomentCInvariant::compute(){ if(getObject().getOrder()<5) throw vpException(vpException::notInitialized,"Order is not high enough for vpMomentCInvariant. Specify at least order 5."); bool found_moment_centered; - vpMomentCentered& momentCentered = (static_cast<vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered))); + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&>(getMoments().get("vpMomentCentered",found_moment_centered))); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); @@ -195,11 +219,30 @@ void vpMomentCInvariant::compute(){ values[9] = I[14]/I[15]; - values[10] = (c[2]*c[3]+s[2]*s[3])/K; - values[11] = (s[2]*c[3]-c[2]*s[3])/K; + if (flg_sxsynormalization_) + calcSxSyNormalized(values[10], values[11]); + else + calcSxSy(values[10], values[11]); + + values[12] = II[1]/(II3_2); // Px + values[13] = a*II[2]/(II3_3); // Py +} - values[12] = II[1]/(II3_2); - values[13] = a*II[2]/(II3_3); +/*! + Sx and Sy as it was inside compute() + */ +void vpMomentCInvariant::calcSxSy(double& sx, double& sy) const{ + sx = (c[2]*c[3]+s[2]*s[3])/K; + sy = (s[2]*c[3]-c[2]*s[3])/K; +} + +/*! + * Sx and Sy from normalized 2nd and 3rd order moments + * Numerically better (than in the usual Sx,Sy when K appears in the denominator) + */ +void vpMomentCInvariant::calcSxSyNormalized(double& sx, double& sy) const{ + sx = (cn[2]*cn[3] + sn[2]*sn[3]) / In1; + sy = (sn[2]*cn[3] - cn[2]*sn[3]) / In1; } /*! @@ -210,17 +253,26 @@ void vpMomentCInvariant::printI(unsigned int index){ std::cout << "I("<< index << ")=" << I[index] << std::endl; } +/*! + Print out all invariants that were computed + There are 15 of them, as in [Point-based and region based.ITRO05] + \cite Tahri05z + */ +void vpMomentCInvariant::printInvariants(std::ostream& os) const{ + for (unsigned int i = 1; i < I.size(); ++i){ // i = 1 since vector I has been indexed from 1 in vpMomentCinvariant + os << "I[" << i << "]=" << I[i] << std::endl; + } + os << std::endl; + +} + /*! Outputs the moment's values to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentCInvariant& c){ +VISP_EXPORT std::ostream& operator<<(std::ostream & os, const vpMomentCInvariant& c){ for(unsigned int i = 0;i<c.values.size();i++){ os << c.values[i] << "," << std::endl; } - return os; } - - - diff --git a/src/tracking/moments/vpMomentCInvariant.h b/src/tracking/moments/vpMomentCInvariant.h index 41898c5b79d48b300dcfdc78b31950a58e5fa171..c4652af9e3f9fcbdfde38d4a46d475a6877aeb0d 100644 --- a/src/tracking/moments/vpMomentCInvariant.h +++ b/src/tracking/moments/vpMomentCInvariant.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCInvariant.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpMomentCInvariant.h 5303 2015-02-10 17:01:28Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,10 +56,10 @@ class vpMomentBasic; \ingroup TrackingMoments - \brief This class defines several 2D (translation+rotation+scale) invariants for both symmetric and non-symmetric objects. + This class defines several 2D (translation+rotation+scale) invariants for both symmetric and non-symmetric objects. These moment-based invariants are described in the following papers \cite Chaumette04a, \cite Tahri05z. - The descriptions for the invariants \f$C_1\f$ to \f$C_{10}\f$ can be found in [1] and for invariants \f$P_x\f$,\f$P_y\f$,\f$S_x\f$,\f$S_y\f$ in [2]. + The descriptions for the invariants \f$C_1\f$ to \f$C_{10}\f$ can be found in \cite Chaumette04a and for invariants \f$P_x\f$,\f$P_y\f$,\f$S_x\f$,\f$S_y\f$ in \cite Tahri05z. These invariants are classicaly used in visual servoing to control the out-of-plane rotations. The C-type or P-type invariants are used for non-symmetric objects whereas the S-type invariants are used for symmetric objects. @@ -103,7 +103,7 @@ int main() db.updateAll(obj); // Update AND compute all moments //get C-invariant - vpMomentCInvariant& C = static_cast<vpMomentCInvariant&>(db.get("vpMomentCInvariant",success)); + const vpMomentCInvariant& C = static_cast<const vpMomentCInvariant&>(db.get("vpMomentCInvariant",success)); if(success) std::cout << C.get(1) << std:: endl; // print C2 invariant else @@ -121,89 +121,102 @@ class VISP_EXPORT vpMomentCInvariant : public vpMoment { std::vector<double> II; std::vector<double> c; std::vector<double> s; - double K; + double K; + void computeI(const vpMomentCentered& momentCentered, std::vector<double>& I); + + /* To calculate Sx and Sy from normalized moments */ + void calcSxSy(double& sx, double& sy) const; + void calcSxSyNormalized(double& sx, double& sy) const; + std::vector<double> cn; // same as s above but calculated from normalized moments + std::vector<double> sn; // same as c above but calculated from normalized moments + double In1; // same as I1 in Sx,Sy formulae but calculated from normalized moments + bool flg_sxsynormalization_; - void computeI(vpMomentCentered& momentCentered, std::vector<double>& I); public: - vpMomentCInvariant(); + vpMomentCInvariant(bool flg_sxsynormalization = false); /*! Shorcut for getting the value of \f$C_1\f$. */ - double C1(){ return values[0]; } + double C1() const { return values[0]; } /*! Shorcut for getting the value of \f$C_2\f$. */ - double C2(){ return values[1]; } + double C2() const { return values[1]; } /*! Shorcut for getting the value of \f$C_3\f$. */ - double C3(){ return values[2]; } + double C3() const { return values[2]; } /*! Shorcut for getting the value of \f$C_4\f$. */ - double C4(){ return values[3]; } + double C4() const { return values[3]; } /*! Shorcut for getting the value of \f$C_5\f$. */ - double C5(){ return values[4]; } + double C5() const { return values[4]; } /*! Shorcut for getting the value of \f$C_6\f$. */ - double C6(){ return values[5]; } + double C6() const { return values[5]; } /*! Shorcut for getting the value of \f$C_7\f$. */ - double C7(){ return values[6]; } + double C7() const { return values[6]; } /*! Shorcut for getting the value of \f$C_8\f$. */ - double C8(){ return values[7]; } + double C8() const { return values[7]; } /*! Shorcut for getting the value of \f$C_9\f$. */ - double C9(){ return values[8]; } + double C9() const { return values[8]; } /*! Shorcut for getting the value of \f$C_{10}\f$. */ - double C10(){ return values[9]; } + double C10() const { return values[9]; } - void compute(); + void compute(); /*! Gets the desired invariant. \param i given index. For invariants from C1 to C10 the corresponding index is from 0 to 9. For \f$S_x\f$,\f$S_y\f$ the indexes are 10,11 and for \f$P_x\f$,\f$P_y\f$ they are 12,13. */ - double get(unsigned int i){ return values[i]; } + double get(unsigned int i) const { return values[i]; } /*! Access to partial invariant c (see [2]). */ - double getC(unsigned int i){return c[i];} + double getC(unsigned int i) const {return c[i];} /*! Access to partial invariants. The index convention is the same as in [1]. */ - double getI(unsigned int index){return I[index];} + double getI(unsigned int index) const {return I[index];} + + /*! + Print the moment invariants used to obtain the actual visual features + */ + void printInvariants(std::ostream& os) const; /*! Access to partial invariant I (see [2]). */ - double getII(unsigned int i){return II[i];} + double getII(unsigned int i) const {return II[i];} /*! Access to partial invariant K (see [2]). */ - double getK(){return K;} + double getK() const {return K;} /*! Access to partial invariant S (see [2]). */ - double getS(unsigned int i){return s[i];} + double getS(unsigned int i) const {return s[i];} /*! Moment name. */ - const char* name(){return "vpMomentCInvariant";} + const char* name() const {return "vpMomentCInvariant";} /*! Print partial invariant. @@ -222,13 +235,40 @@ class VISP_EXPORT vpMomentCInvariant : public vpMoment { /*! Shorcut for getting the value of \f$S_x\f$. */ - double Sx(){ return values[10]; } + double Sx() const { return values[10]; } /*! Shorcut for getting the value of \f$S_y\f$. */ - double Sy(){ return values[11]; } + double Sy() const { return values[11]; } + + /*! + * Getters for I + * (calculated from normalized 2nd and 3ord order moments) + */ + double getIn1() const {return In1;} + + /*! + * Getter for c + * (calculated from normalized 2nd and 3ord order moments) + */ + double getCN(unsigned int i) const {return cn[i];} + + /*! + * Getter for s + * (calculated from normalized 2nd and 3ord order moments) + */ + double getSN(unsigned int i) const {return sn[i];} + + /*! + * To know if Sx and Sy were calculated from normalized moments or not + */ + bool isSxSyfromNormalizedMoments() const {return flg_sxsynormalization_;}; + + /*! + * To get all the invariant values as a whole. + */ + inline const std::vector<double>& getMomentVector() const { return values; } friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentCInvariant& v); }; - #endif diff --git a/src/tracking/moments/vpMomentCentered.cpp b/src/tracking/moments/vpMomentCentered.cpp index 6ba7b52dad2312a6f699732359722413bb157a8f..2e0a06693af33aa59e7b73179b9cdc0105a0beff 100644 --- a/src/tracking/moments/vpMomentCentered.cpp +++ b/src/tracking/moments/vpMomentCentered.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCentered.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentCentered.cpp 4712 2014-03-28 17:55:43Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,6 +45,19 @@ #include <exception> #include <cassert> +/*! + To set the values of centred moments. Required when normalizing the moment values. + @param i : first index of the 2D moment. + @param j : second index of the 2D moment. + @param value : value of the moment. +*/ +void vpMomentCentered::set(unsigned int i, unsigned int j, double value){ + vpMomentObject mobj = getObject(); + assert(i+j<=mobj.getOrder()); + if(i+j>mobj.getOrder()) throw vpException(vpException::badValue,"You cannot set that value."); + values[j*(mobj.getOrder()+1)+i] = value; +} + /*! Computes centered moments of all available orders. Depends on vpMomentGravityCenter. @@ -53,8 +66,9 @@ void vpMomentCentered::compute(){ bool found_moment_gravity; values.resize((getObject().getOrder()+1)*(getObject().getOrder()+1)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + unsigned int order = getObject().getOrder()+1; for(register unsigned int j=0;j<(order);j++){ for(register unsigned int i=0;i<order-j;i++){ @@ -87,11 +101,12 @@ vpMomentCentered::vpMomentCentered() : vpMoment(){ \param j : second index of the centered moment. \return \f$\mu_{ij}\f$ moment. */ -double vpMomentCentered::get(unsigned int i,unsigned int j){ - assert(i+j<=getObject().getOrder()); - if(i+j>getObject().getOrder()) throw vpException(vpException::badValue,"The requested value has not been computed, you should specify a higher order."); +double vpMomentCentered::get(unsigned int i,unsigned int j) const { + unsigned int order = getObject().getOrder(); + assert(i+j<=order); + if(i+j>order) throw vpException(vpException::badValue,"The requested value has not been computed, you should specify a higher order."); - return values[j*(getObject().getOrder()+1)+i]; + return values[j*(order+1)+i]; } /*! @@ -108,8 +123,9 @@ u02 u12 x x u30 x x x \endcode + This output will be followed by an output with indexes as produced by printWithIndices() function */ -std::ostream & operator<<(std::ostream & os, vpMomentCentered& m){ +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentCentered& m){ for(unsigned int i = 0;i<m.values.size();i++){ if(i%(m.getObject().getOrder()+1)==0) os << std::endl; @@ -121,6 +137,48 @@ std::ostream & operator<<(std::ostream & os, vpMomentCentered& m){ os << "\t"; } - + os << std::endl; + m.printWithIndices(os); return os; } + +/*! +Print in a readable form which looks better than output from << operator +*/ +void +vpMomentCentered::printWithIndices(std::ostream& os) const { + unsigned int orderp1 = getObject().getOrder()+1; + for(unsigned int k=0; k<orderp1; k++) { + for(unsigned int l=0; l<orderp1-k; l++) + { + os << "mu[" << k << "," << l << "] = " << this->get(k,l) << "\t"; + } + os << std::endl; + } + os << std::endl; +} + +/*! +Prints moments required for calculation of vpMomentCentered, +which are +1. Raw geometric moments (vpMomentObject) and +2. Centre of gravity (vpMomentGravityCentered) +*/ +void +vpMomentCentered::printDependencies(std::ostream& os) const { + os << (__FILE__) << std::endl; + /* + Retreive the raw moments + */ + const vpMomentObject objt = getObject(); + vpMomentObject::printWithIndices(objt, os); + + /* + Get xg,yg + */ + bool found_moment_gravity; + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); + if(!found_moment_gravity) + throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + os << "Xg = " << momentGravity.getXg() << "\t" << "Yg = " << momentGravity.getYg() << std::endl; +} diff --git a/src/tracking/moments/vpMomentCentered.h b/src/tracking/moments/vpMomentCentered.h index bed1e1af2b294aad26b66213f0b48f06ce7d4e67..0d00fb59b55ec9d4d709121f62ae6cce89214644 100644 --- a/src/tracking/moments/vpMomentCentered.h +++ b/src/tracking/moments/vpMomentCentered.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCentered.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentCentered.h 4712 2014-03-28 17:55:43Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -81,16 +81,23 @@ public: vpMomentCentered(); - void compute(); - double get(unsigned int i,unsigned int j); + void compute(); + double get(unsigned int i,unsigned int j) const; - inline std::vector<double>& get(); + inline const std::vector<double>& get() const; /*! Moment name. */ - inline const char* name(){return "vpMomentCentered";} + inline const char* name() const {return "vpMomentCentered";} + + friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentCentered& v); + void printWithIndices(std::ostream& os) const; + void printDependencies(std::ostream& os) const; + +protected: + + void set(unsigned int i, unsigned int j, double value); - friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, vpMomentCentered& v); }; /*! @@ -120,7 +127,7 @@ mu12 = mc.get()[2*(obj.getOrder()+1)+1]; // i=1 and j=2 mu12 = mc.get(1,2); // the same \endcode */ -inline std::vector<double>& vpMomentCentered::get() +inline const std::vector<double>& vpMomentCentered::get() const { return vpMoment::get(); } diff --git a/src/tracking/moments/vpMomentCommon.cpp b/src/tracking/moments/vpMomentCommon.cpp index 11f507a56766fa7fca1b1b264e40d03337fa84c6..9c67fcdf96f86d0cb211530bc221917d27d472f1 100644 --- a/src/tracking/moments/vpMomentCommon.cpp +++ b/src/tracking/moments/vpMomentCommon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCommon.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentCommon.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,13 +44,14 @@ /*! Default constructor. Initializes the common database with the following moments: - basic, gravity,centered,centered+normalized,normalized gravity,normalized surface, scale-plane-rotation-translation invariant,alpha, symmetric invariant. - \param dstSurface : destination surface. You may use vpMomentCommon::getSurface. - \param ref : reference 3rd order moments (see vpMomentAlpha). You may use vpMomentCommon::getMu3. - \param refAlpha : reference alpha (see vpMomentAlpha). You may use vpMomentCommon::getAlpha. + basic, gravity,centered,centered+normalized,normalized gravity,normalized surface, scale-plane-rotation-translation invariant, alpha, symmetric invariant. + \param dstSurface : destination surface. You may use vpMomentCommon::getSurface(). + \param ref : reference 3rd order moments (see vpMomentAlpha). You may use vpMomentCommon::getMu3(). + \param refAlpha : reference alpha (see vpMomentAlpha). You may use vpMomentCommon::getAlpha(). \param dstZ : destination depth. + \param flg_sxsyfromnormalized : flag to enable calculation of sx,sy from normalized moments. */ -vpMomentCommon::vpMomentCommon(double dstSurface,std::vector<double> ref,double refAlpha,double dstZ): +vpMomentCommon::vpMomentCommon(double dstSurface,std::vector<double> ref,double refAlpha,double dstZ, bool flg_sxsyfromnormalized): momentBasic(), momentGravity(), momentCentered(), @@ -60,12 +61,14 @@ vpMomentCommon::vpMomentCommon(double dstSurface,std::vector<double> ref,double momentAlpha(ref,refAlpha), momentArea() { + momentCInvariant = new vpMomentCInvariant(flg_sxsyfromnormalized); + momentBasic.linkTo(*this); momentGravity.linkTo(*this); momentCentered.linkTo(*this); momentGravityNormalized.linkTo(*this); momentSurfaceNormalized.linkTo(*this); - momentCInvariant.linkTo(*this); + momentCInvariant->linkTo(*this); momentAlpha.linkTo(*this); momentArea.linkTo(*this); } @@ -130,7 +133,7 @@ void vpMomentCommon::updateAll(vpMomentObject& object){ momentGravity.compute(); momentCentered.compute(); momentAlpha.compute(); - momentCInvariant.compute(); + momentCInvariant->compute(); momentSurfaceNormalized.compute(); momentGravityNormalized.compute(); @@ -140,14 +143,13 @@ void vpMomentCommon::updateAll(vpMomentObject& object){ std::cout << "exception:" << ex <<std::endl; } - } /*! Gets the surface of an object \param object : moment object */ -double vpMomentCommon::getSurface(vpMomentObject& object){ +double vpMomentCommon::getSurface(vpMomentObject& object) { vpMomentDatabase moments; vpMomentGravityCenter momentGravity;momentGravity.linkTo(moments); @@ -165,14 +167,13 @@ double vpMomentCommon::getSurface(vpMomentObject& object){ a = object.get(0,0); return a; - } /*! Gets a reference alpha of an object. \param object : Moment object. */ -double vpMomentCommon::getAlpha(vpMomentObject& object){ +double vpMomentCommon::getAlpha(vpMomentObject& object) { vpMomentDatabase moments; vpMomentGravityCenter momentGravity;momentGravity.linkTo(moments); @@ -191,7 +192,7 @@ double vpMomentCommon::getAlpha(vpMomentObject& object){ Gets the reference 3rd order moments of an object. \param object : Moment object. */ -std::vector<double> vpMomentCommon::getMu3(vpMomentObject& object){ +std::vector<double> vpMomentCommon::getMu3(vpMomentObject& object) { vpMomentDatabase moments; vpMomentGravityCenter momentGravity;momentGravity.linkTo(moments); @@ -204,13 +205,18 @@ std::vector<double> vpMomentCommon::getMu3(vpMomentObject& object){ std::vector<double> mu(4); unsigned int idx=0; - for (unsigned int i=0; i<4; i++) { - for (unsigned int j=0; j<4; j++) { + for (unsigned int j=0; j<4; j++) { + for (unsigned int i=0; i<4; i++) { if (i+j==3){ - mu[idx] = momentCentered.get(j,i); + mu[idx] = momentCentered.get(i,j); idx++; } } } return mu; } + +vpMomentCommon::~vpMomentCommon(){ + if (momentCInvariant) + delete momentCInvariant; +} diff --git a/src/tracking/moments/vpMomentCommon.h b/src/tracking/moments/vpMomentCommon.h index 5cce98c0056035edb2bdab014847bcb431b3e4d8..beb17c3bd6aa76b5d74d9163ccf561b746bfb615 100644 --- a/src/tracking/moments/vpMomentCommon.h +++ b/src/tracking/moments/vpMomentCommon.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentCommon.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpMomentCommon.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,14 +70,15 @@ class vpMomentObject; - vpMomentBasic - vpMomentGravityCenter - vpMomentCentered - - vpMomentCenteredNormalized + - vpMomentGravityCenterNormalized - vpMomentAreaNormalized - vpMomentCInvariant - vpMomentAlpha + - vpMomentArea There is no need to do the linkTo operations manually nor is it necessary to care about the order of moment computation. - This class carries an vpMomentCommon::updateAll method capable of updating AND computing moments from an object (see 4-step process in vpMoment). + This class carries an vpMomentCommon::updateAll() method capable of updating AND computing moments from an object (see 4-step process in vpMoment). The moments computed by this class are classical moments used in moment-based visual servoing. For more information see \cite Tahri05z. @@ -88,7 +89,8 @@ class vpMomentObject; - the surface of the destination object in the end of the visual servoing process. - the reference alpha: angular position of the object used to obtain the Mu3 set. - Shortcuts for each of these prerequisites are provided by this class except depth (methods vpMomentCommon::getMu3, vpMomentCommon::getSurface,vpMomentCommon::getAlpha). + Shortcuts for each of these prerequisites are provided by this class except depth (methods + vpMomentCommon::getMu3(), vpMomentCommon::getSurface(), vpMomentCommon::getAlpha()). \attention Make sure your object is at least of order 5 when using this pre-filled database. @@ -100,18 +102,17 @@ private: vpMomentCentered momentCentered; vpMomentGravityCenterNormalized momentGravityNormalized; vpMomentAreaNormalized momentSurfaceNormalized; - vpMomentCInvariant momentCInvariant; + vpMomentCInvariant* momentCInvariant; vpMomentAlpha momentAlpha; vpMomentArea momentArea; public: - vpMomentCommon(double dstSurface,std::vector<double> ref,double refAlpha,double dstZ=1.0); + vpMomentCommon(double dstSurface,std::vector<double> ref,double refAlpha,double dstZ=1.0, bool flg_sxsyfromnormalized=false); - static double getAlpha(vpMomentObject& objec); + static double getAlpha(vpMomentObject& object); static std::vector<double> getMu3(vpMomentObject& object); static double getSurface(vpMomentObject& object); void updateAll(vpMomentObject& object); + ~vpMomentCommon(); }; - - #endif // VPCOMMONMOMENTS_H diff --git a/src/tracking/moments/vpMomentDatabase.cpp b/src/tracking/moments/vpMomentDatabase.cpp index ee96c18ed3043835e9b69331ea0a92c659229cc6..f0c6f15713e1135a544b0eeaaab65f72cec488d9 100644 --- a/src/tracking/moments/vpMomentDatabase.cpp +++ b/src/tracking/moments/vpMomentDatabase.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentDatabase.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentDatabase.cpp 4620 2014-01-27 21:28:32Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,7 @@ void vpMomentDatabase::add(vpMoment& moment,const char* name){ \param found : true if the moment's type exists in the database, false otherwise. \return Moment corresponding to \e type. */ -vpMoment& vpMomentDatabase::get(const char* type, bool& found){ +const vpMoment& vpMomentDatabase::get(const char* type, bool& found) const { std::map<const char*,vpMoment*,vpMomentDatabase::cmp_str>::const_iterator it = moments.find(type); found = (it!=moments.end()); @@ -86,7 +86,7 @@ void vpMomentDatabase::updateAll(vpMomentObject& object){ /*! Outputs all the moments values in the database to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentDatabase& m){ +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentDatabase& m){ std::map<const char*,vpMoment*,vpMomentDatabase::cmp_str>::const_iterator itr; os << "{"; diff --git a/src/tracking/moments/vpMomentDatabase.h b/src/tracking/moments/vpMomentDatabase.h index efbd2938efdfa6a19363385d6ecb767b9ab1b67e..733574c777fcdbc343f07969b07b61622af66747 100644 --- a/src/tracking/moments/vpMomentDatabase.h +++ b/src/tracking/moments/vpMomentDatabase.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentDatabase.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentDatabase.h 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -136,9 +136,10 @@ class VISP_EXPORT vpMomentDatabase{ std::map<const char*,vpMoment*,cmp_str> moments; void add(vpMoment& moment, const char* name); public: + vpMomentDatabase() : moments() {} virtual ~vpMomentDatabase() {} - vpMoment& get(const char* type, bool& found); + const vpMoment& get(const char* type, bool& found) const; /*! Get the first element in the database. May be useful in case an unnamed object is present but is the only element in the database. diff --git a/src/tracking/moments/vpMomentGravityCenter.cpp b/src/tracking/moments/vpMomentGravityCenter.cpp index d782bbaeade2d1d65134f6fb6f389feae492d958..3d1e38fc37fbd39b43a0acf2bdda841fb5b01375 100644 --- a/src/tracking/moments/vpMomentGravityCenter.cpp +++ b/src/tracking/moments/vpMomentGravityCenter.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentGravityCenter.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentGravityCenter.cpp 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,15 +60,26 @@ vpMomentGravityCenter::vpMomentGravityCenter() : vpMoment(){ Returns a vector of the two gravity center coordinates. \return Coordinates in the following moment: \f$(x_g,y_g)\f$. */ -std::vector<double>& vpMomentGravityCenter::get(){ +const std::vector<double>& vpMomentGravityCenter::get() const{ return values; } /*! Outputs the moment's values to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenter& m){ - os << "Xg=" << m.values[0] << ", Yg=" << m.values[1]; - +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenter& m){ + os << (__FILE__) << std::endl; + os << "(Xg,Yg) = (" << m.values[0] << ", " << m.values[1] << ")" << std::endl; return os; } + +/*! +Prints its dependencies +Basic moments m10, m01 and m00 from vpMomentObject +*/ +void vpMomentGravityCenter::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + os << "m10 = " << getObject().get(1,0) << "\t"; + os << "m00 = " << getObject().get(0,1) << "\t"; + os << "m00 = " << getObject().get(0,0) << std::endl; +} diff --git a/src/tracking/moments/vpMomentGravityCenter.h b/src/tracking/moments/vpMomentGravityCenter.h index cf65cb72904672a9495b1e78cdf6db0e91caffb5..8efe3c31062ac7339f2595120922ccf3c205fcb4 100644 --- a/src/tracking/moments/vpMomentGravityCenter.h +++ b/src/tracking/moments/vpMomentGravityCenter.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentGravityCenter.h 4220 2013-04-17 10:03:36Z mbakthav $ + * $Id: vpMomentGravityCenter.h 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -115,7 +115,7 @@ class VISP_EXPORT vpMomentGravityCenter : public vpMoment { vpMomentGravityCenter(); void compute(); - std::vector<double>& get(); + const std::vector<double>& get() const; /*! Shortcut function to retrieve \f$x_g\f$. \return The first gravity center coordinate. @@ -129,8 +129,9 @@ class VISP_EXPORT vpMomentGravityCenter : public vpMoment { /*! The class's string name. */ - const char* name(){return "vpMomentGravityCenter";} + const char* name() const {return "vpMomentGravityCenter";} friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenter& v); + void printDependencies(std::ostream& os) const; }; #endif diff --git a/src/tracking/moments/vpMomentGravityCenterNormalized.cpp b/src/tracking/moments/vpMomentGravityCenterNormalized.cpp index d668b126db77b0f827e367d2a4165bcdfa1d0c7b..15da64662ade4a4ceef79255038be7129a6432d4 100644 --- a/src/tracking/moments/vpMomentGravityCenterNormalized.cpp +++ b/src/tracking/moments/vpMomentGravityCenterNormalized.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentGravityCenterNormalized.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentGravityCenterNormalized.cpp 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,8 +52,8 @@ void vpMomentGravityCenterNormalized::compute(){ bool found_moment_gravity; bool found_moment_surface_normalized; - vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<vpMomentAreaNormalized&>(getMoments().get("vpMomentAreaNormalized",found_moment_surface_normalized)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(getMoments().get("vpMomentAreaNormalized",found_moment_surface_normalized)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); if(!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); @@ -74,8 +74,27 @@ vpMomentGravityCenterNormalized::vpMomentGravityCenterNormalized() : vpMomentGra /*! Outputs the moment's values to a stream. */ -std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenterNormalized& m){ - os << "Xn:" << m.values[0] << ",Yn=" << m.values[1]; - +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenterNormalized& m){ + os << (__FILE__) << std::endl; + os << "(Xn,Yn) = (" << m.values[0] << ", " << m.values[1] << ")" << std::endl; return os; } + +/*! +Prints the dependent moments, +1. centre of gravity +2. normalized area moment +*/ +void vpMomentGravityCenterNormalized::printDependencies(std::ostream& os) const{ + os << (__FILE__) << std::endl; + bool found_moment_gravity; + bool found_moment_surface_normalized; + + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(getMoments().get("vpMomentAreaNormalized",found_moment_surface_normalized)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(getMoments().get("vpMomentGravityCenter",found_moment_gravity)); + + if(!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); + if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + os << "Xg = " << momentGravity.get()[0] << "\t" << "Yg = " << momentGravity.get()[1] << std::endl; + os << "An = " << momentSurfaceNormalized.get()[0] << std::endl; +} diff --git a/src/tracking/moments/vpMomentGravityCenterNormalized.h b/src/tracking/moments/vpMomentGravityCenterNormalized.h index 96488c21aabca5cf49239a4deb6ec211c5df43a9..b3a9caa7bd2772445fb5ac1c71abdd01cba494ec 100644 --- a/src/tracking/moments/vpMomentGravityCenterNormalized.h +++ b/src/tracking/moments/vpMomentGravityCenterNormalized.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentGravityCenterNormalized.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentGravityCenterNormalized.h 4713 2014-03-28 18:02:26Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,8 +68,9 @@ class VISP_EXPORT vpMomentGravityCenterNormalized : public vpMomentGravityCenter /*! Moment name. */ - const char* name(){return "vpMomentGravityCenterNormalized";} + const char* name() const {return "vpMomentGravityCenterNormalized";} friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentGravityCenterNormalized& v); + void printDependencies(std::ostream& os) const; }; #endif diff --git a/src/tracking/moments/vpMomentObject.cpp b/src/tracking/moments/vpMomentObject.cpp index 7b3d20f42a72de40161cb87819c3bd366dbdaabd..52b518d75c87fa0bc6165a951d642226a7bedb4c 100644 --- a/src/tracking/moments/vpMomentObject.cpp +++ b/src/tracking/moments/vpMomentObject.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentObject.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentObject.cpp 5301 2015-02-10 16:36:52Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,8 +44,11 @@ #include <visp/vpCameraParameters.h> #include <visp/vpPixelMeterConversion.h> #include <visp/vpConfig.h> +#include <stdexcept> + #include <cmath> #include <limits> + #ifdef VISP_HAVE_OPENMP #include <omp.h> #endif @@ -134,6 +137,32 @@ void vpMomentObject::cacheValues(std::vector<double>& cache,double x, double y){ } } +/*! + * Manikandan.B + * Need to cache intensity along with the coordinates for photometric moments + */ +void vpMomentObject::cacheValues(std::vector<double>& cache,double x, double y, double IntensityNormalized) { + + cache[0]=IntensityNormalized; + + double invIntensityNormalized = 0.; + if (std::fabs(IntensityNormalized)>=std::numeric_limits<double>::epsilon()) + invIntensityNormalized = 1.0/IntensityNormalized; + + for(register unsigned int i=1;i<order;i++) + cache[i]=cache[i-1]*x; + + for(register unsigned int j=order;j<order*order;j+=order) + cache[j]=cache[j-order]*y; + + for(register unsigned int j=1;j<order;j++){ + for(register unsigned int i=1;i<order-j;i++){ + cache[j*order+i] = cache[j*order]*cache[i]*invIntensityNormalized; + } + } +} + + /*! Computes basic moments from a vector of points. There are two cases: @@ -262,10 +291,10 @@ int main() */ void vpMomentObject::fromImage(const vpImage<unsigned char>& image, unsigned char threshold, const vpCameraParameters& cam){ -#ifdef VISP_HAVE_OPENMP +#ifdef VISP_HAVE_OPENMP #pragma omp parallel shared(threshold) - { - std::vector<double> curvals(order*order); + { + std::vector<double> curvals(order*order); curvals.assign(order*order,0.); unsigned int i_, j_; @@ -281,36 +310,35 @@ void vpMomentObject::fromImage(const vpImage<unsigned char>& image, unsigned cha double xval=1.; double yval=1.; - for(register unsigned int k=0;k<order;k++){ + for(register unsigned int k=0;k<order;k++){ xval=1.; for(register unsigned int l=0;l<order-k;l++){ curvals[(k*order+l)]+=(xval*yval); xval*=x; } yval*=y; - } + } } } - } - + } + #pragma omp master //only set this variable in master thread - { - values.assign(order*order, 0.); + { + values.assign(order*order, 0.); } #pragma omp barrier - - for(register unsigned int k=0;k<order;k++){ + + for(register unsigned int k=0;k<order;k++){ for(register unsigned int l=0;l<order-k;l++){ #pragma omp atomic values[k*order+l]+= curvals[k*order+l]; } } - - } - + + } #else - std::vector<double> cache(order*order,0.); + std::vector<double> cache(order*order,0.); values.assign(order*order,0); for(register unsigned int i=0;i<image.getCols();i++){ for(register unsigned int j=0;j<image.getRows();j++){ @@ -328,9 +356,128 @@ void vpMomentObject::fromImage(const vpImage<unsigned char>& image, unsigned cha } } #endif + + //Normalisation equivalent to sampling interval/pixel size delX x delY + double norm_factor = 1./(cam.get_px()*cam.get_py()); + for (std::vector<double>::iterator it = values.begin(); it!=values.end(); it++) { + *it = (*it) * norm_factor; + } +} + +/*! + * Manikandan. B + * Photometric moments v2 + * Intended to be used by 'vpMomentObject's of type DENSE_FULL_OBJECT + * @param image : Grayscale image + * @param cam : Camera parameters (to change to ) + * @param bg_type : White/Black background surrounding the image + * @param normalize_with_pix_size : This flag if SET, the moments, after calculation are normalized w.r.t pixel size + * available from camera parameters + */ +void vpMomentObject::fromImage(const vpImage<unsigned char>& image, const vpCameraParameters& cam, + vpCameraImgBckGrndType bg_type, bool normalize_with_pix_size) +{ + std::vector<double> cache(order*order,0.); + values.assign(order*order,0); + + // (x,y) - Pixel co-ordinates in metres + double x=0; + double y=0; + //for indexing into cache[] and values[] + unsigned int idx = 0; + unsigned int kidx = 0; + + double intensity = 0; + double intensity_white = 0; + + //double Imax = static_cast<double>(image.getMaxValue()); + double Imax = 255.; // To check the effect of gray level change. ISR Coimbra + + double iscale = 1.0; + if (flg_normalize_intensity) // This makes the image a probability density function + iscale = 1.0/Imax; + + if (bg_type == vpMomentObject::WHITE) { + /////////// WHITE BACKGROUND /////////// + for(register unsigned int j=0;j<image.getRows();j++){ + for(register unsigned int i=0;i<image.getCols();i++){ + x = 0; + y = 0; + intensity = (double)(image[j][i])*iscale; + intensity_white = 1. - intensity; + + vpPixelMeterConversion::convertPoint(cam,i,j,x,y); + cacheValues(cache,x,y, intensity_white); // Modify 'cache' which has x^p*y^q to x^p*y^q*(1 - I(x,y)) + + // Copy to "values" + for(register unsigned int k=0;k<order;k++){ + kidx = k*order; + for(register unsigned int l=0;l<order-k;l++){ + idx = kidx+l; + values[idx]+= cache[idx]; + } + } + } + } + } + else { + /////////// BLACK BACKGROUND /////////// + for(register unsigned int j=0;j<image.getRows();j++){ + for(register unsigned int i=0;i<image.getCols();i++){ + x = 0; + y = 0; + intensity = (double)(image[j][i])*iscale; + vpPixelMeterConversion::convertPoint(cam,i,j,x,y); + + // Cache values for fast moment calculation + cacheValues(cache,x,y, intensity); // Modify 'cache' which has x^p*y^q to x^p*y^q*I(x,y) + + // Copy to moments array 'values' + for(register unsigned int k=0;k<order;k++){ + kidx = k*order; + for(register unsigned int l=0;l<order-k;l++){ + idx = kidx+l; + values[idx]+= cache[idx]; + } + } + + } + } + } + + if (normalize_with_pix_size){ + // Normalisation equivalent to sampling interval/pixel size delX x delY + double norm_factor = 1./(cam.get_px()*cam.get_py()); + for (std::vector<double>::iterator it = values.begin(); it!=values.end(); it++) { + *it = (*it) * norm_factor; + } + } } +/*! + Does exactly the work of the default constructor as it existed in the very + first version of vpMomentObject + */ +void +vpMomentObject::init(unsigned int orderinp) { + order = orderinp + 1; + type = vpMomentObject::DENSE_FULL_OBJECT; + flg_normalize_intensity = true; // By default, the intensity values are normalized + values.resize((order+1)*(order+1)); + values.assign((order+1)*(order+1),0); +} +/*! + Helper to copy constructor + */ +void +vpMomentObject::init(const vpMomentObject& objin){ + order = objin.getOrder()+1; + type = objin.getType(); + flg_normalize_intensity = objin.flg_normalize_intensity; + values.resize(objin.values.size()); + values = objin.values; +} /*! Default constructor. @@ -338,14 +485,28 @@ void vpMomentObject::fromImage(const vpImage<unsigned char>& image, unsigned cha The parameter specified is the highest desired included order. All orders up to this values will be computed. In other words, a vpMomentObject will compute all \f$ m_{ij} \f$ moments with \f$ i+j \in [0..order] \f$. - \param order : Maximum reached order (i+j) to be used. All + \param max_order : Maximum reached order (i+j) to be used. All considered i+j will be of order smaller or equal than this parameter. For example if this parameter is 5, all moment values of order 0 to 5 included will be computed. + + Mani : outsourced the constructor work to void init (unsigned int orderinp); */ -vpMomentObject::vpMomentObject(unsigned int order) : order(order+1),type(DENSE_FULL_OBJECT){ - values.resize((order+1)*(order+1)*12); - values.assign((order+1)*(order+1)*12,0); +vpMomentObject::vpMomentObject(unsigned int max_order) + : flg_normalize_intensity(true), order(max_order+1), type(vpMomentObject::DENSE_FULL_OBJECT), + values() +{ + init(max_order); +} + +/*! + Copy constructor + */ +vpMomentObject::vpMomentObject(const vpMomentObject& srcobj) + : flg_normalize_intensity(true), order(1), type(vpMomentObject::DENSE_FULL_OBJECT), + values() +{ + init(srcobj); } /*! @@ -367,7 +528,7 @@ double m12; m12 = mij[2*(obj.getOrder()+1)+1]; // i=1 and j=2 \endcode */ -std::vector<double>& vpMomentObject::get() { +const std::vector<double>& vpMomentObject::get() const { return values; } @@ -386,7 +547,19 @@ double vpMomentObject::get(unsigned int i, unsigned int j) const { } /*! - Outputs the basic moment's values \f$m_{ij}\f$ to a stream presented as a matrix. + Sets the basic moment value \f$m_{ij}\f$ corresponding to i,j indexes + \param i : First moment index, with \f$i+j \leq order\f$. + \param j : Second moment index, with \f$i+j \leq order\f$. + \param value_ij : Moment value. +*/ +void vpMomentObject::set(unsigned int i, unsigned int j, const double& value_ij){ + assert(i+j<=getOrder()); + if(i+j>=order) throw vpException(vpException::badValue,"The requested value cannot be set, you should specify a higher order for the moment object."); + values[j*order+i] = value_ij; +} + +/*! + Outputs the basic moment's values \f$m_{ij}\f$ to a stream presented as a matrix. The first line corresponds to \f$m_{0[0:order]}\f$, the second one to \f$m_{1[0:order]}\f$ Values in table corresponding to a higher order are marked with an "x" and not computed. @@ -400,7 +573,7 @@ double vpMomentObject::get(unsigned int i, unsigned int j) const { \endcode */ -std::ostream & operator<<(std::ostream & os, const vpMomentObject& m){ +VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentObject& m){ for(unsigned int i = 0;i<m.values.size();i++){ if(i%(m.order)==0) @@ -417,3 +590,71 @@ std::ostream & operator<<(std::ostream & os, const vpMomentObject& m){ return os; } + +/*! + Outputs the raw moment values \f$m_{ij}\f$ in indexed form. + The moment values are same as provided by the operator << which outputs x for uncalculated moments. + */ +void +vpMomentObject::printWithIndices(const vpMomentObject& momobj, std::ostream& os) { + std::vector<double> moment = momobj.get(); + os << std::endl <<"Order of vpMomentObject: "<<momobj.getOrder()<<std::endl; + // Print out values. This is same as printing using operator << + for(unsigned int k=0; k<=momobj.getOrder(); k++) { + for(unsigned int l=0; l<(momobj.getOrder()+1)-k; l++){ + os << "m[" << l << "," << k << "] = " << moment[k*(momobj.getOrder()+1)+ l] << "\t"; + } + os << std::endl; + } + os <<std::endl; +} + +/*! + This function returns a vpMatrix of size (order+1, order+1). +\code + vpMomentObject obj(8); + obj.setType(vpMomentObject::DENSE_FULL_OBJECT); + obj.fromImageWeighted(I, cam, vpMomentObject::BLACK); // cam should have the camera parameters + vpMatrix Mpq = vpMomentObject::convertTovpMatrix(obj); +\endcode + Instead of accessing the moment m21 as obj.get(2,1), you can now do Mpq[2][1]. + This is useful when you want to use the functions available in vpMatrix. + One use case i see now is to copy the contents of the matrix to a file or std::cout. + For instance, like + \code + // Print to console + Mpq.maplePrint(std::cout); + // Or write to a file + std::ofstream fileMpq("Mpq.csv"); + Mpq.maplePrint(fileMpq); +\endcode + +The output can be copied and pasted to MAPLE as a matrix. + +\warning +The moments that are not calculated have zeros. For instance, for a vpMomentObject of order 8, +the moment m[7,2] is not calculated. It will have 0 by default. User discretion is advised. +*/ +vpMatrix +vpMomentObject::convertTovpMatrix(const vpMomentObject& momobj) { + std::vector<double> moment = momobj.get(); + unsigned int order = momobj.getOrder(); + vpMatrix M(order+1, order+1); + for(unsigned int k=0; k<=order; k++) { + for(unsigned int l=0; l<(order+1)-k; l++){ + M[l][k] = moment[k*(order+1)+ l]; + } + } + return M; +} + +/*! + Nothing to destruct. This will allow for a polymorphic usage + For instance, + \code + vpMomentObject* obj = new vpWeightedMomentObject(weightfunc,ORDER); where vpWeightedMomentObject is child class of vpMomentObject + \endcode + */ +vpMomentObject::~vpMomentObject(){ +// deliberate empty +} diff --git a/src/tracking/moments/vpMomentObject.h b/src/tracking/moments/vpMomentObject.h index cd6d348e6cdc83374adb42ec9e6cd16a38a6ab39..7b11b82d4c11fdaedb64a3d967fcb360e7e4c8a4 100644 --- a/src/tracking/moments/vpMomentObject.h +++ b/src/tracking/moments/vpMomentObject.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMomentObject.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMomentObject.h 5300 2015-02-10 16:26:32Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,7 +48,8 @@ #include <visp/vpImage.h> #include <visp/vpMoment.h> #include <visp/vpPoint.h> - +#include <visp/vpMath.h> +#include <cstdlib> #include <utility> class vpCameraParameters; @@ -58,7 +59,7 @@ class vpCameraParameters; \ingroup TrackingMoments - \brief Class for generic objects. + \brief Class for generic objects. It contains all basic moments often described by \f$m_{ij}\f$ of order \f$i+j\f$ going from \f$m_{00}\f$ to the order used as parameter in vpMomentObject() constructor. All other moments implemented in ViSP (gravity center, alpha orientation, centered moments...) use this moment object as a combination of its different values. @@ -133,11 +134,11 @@ int main() std::vector<double> moment = obj.get(); std::cout << std::endl << "Basic moment available (from vector of doubles) " << std::endl; for(unsigned int k=0; k<=obj.getOrder(); k++) { - for(unsigned int l=0; l<(obj.getOrder()+1)-k; l++){ - std::cout << "m" << l << k << "=" << moment[k*(momobj.getOrder()+1)+ l] << "\t"; - } - std::cout<<std::endl; - } + for(unsigned int l=0; l<(obj.getOrder()+1)-k; l++){ + std::cout << "m" << l << k << "=" << moment[k*(momobj.getOrder()+1)+ l] << "\t"; + } + std::cout<<std::endl; + } // 2. Print the contents of moment object directly std::cout << std::endl << "Basic moment available: "; @@ -171,7 +172,7 @@ int main() This example produces the following results: \code -Considered points: +Considered points: point 0: -0.2, 0.1 point 1: 0.3, 0.1 point 2: 0.2, -0.1 @@ -186,14 +187,14 @@ m04=0.00080625 m14=-7.125e-05 m05=-6.59375e-05 Basic moment available: -4 0.1 0.21 0.019 0.0129 0.00211 --0.05 0.02 0.003 0.0023 0.00057 x -0.0525 -0.0015 0.0026 9e-05 x x --0.002375 0.000575 -4.5e-05 x x x -0.00080625 -7.125e-05 x x x x --6.59375e-05 x x x x x - -Direct acces to some basic moments: +4 0.1 0.21 0.019 0.0129 0.00211 +-0.05 0.02 0.003 0.0023 0.00057 x +0.0525 -0.0015 0.0026 9e-05 x x +-0.002375 0.000575 -4.5e-05 x x x +0.00080625 -7.125e-05 x x x x +-6.59375e-05 x x x x x + +Direct acces to some basic moments: m00: 4 m10: 0.1 m01: -0.05 @@ -204,7 +205,7 @@ m02: 0.0525 Common moments computed using basic moments: Surface: 0.259375 Alpha: 0.133296 -Centered moments (mu03, mu12, mu21, mu30): 0.003375 0.0045625 -0.00228125 -0.000421875 +Centered moments (mu03, mu12, mu21, mu30): 0.003375 0.0045625 -0.00228125 -0.000421875 \endcode Note that in the continuous case, the moment object \f$m_{00}\f$ corresponds to the surface \f$a\f$ of the object. @@ -212,6 +213,7 @@ Centered moments (mu03, mu12, mu21, mu30): 0.003375 0.0045625 -0.00228125 -0.000 */ class VISP_EXPORT vpMomentObject{ public: + /*! Type of object that will be considered. */ @@ -220,32 +222,76 @@ public: DENSE_POLYGON = 1, /*!< A set of points (stored in clockwise order) describing a polygon. It will be treated as dense. */ DISCRETE = 2, /*!< A cloud of points. Treated as discrete. */ } vpObjectType; + + /*! + Type of camera image background. + */ + typedef enum{ + BLACK = 0, /*! Black background */ + WHITE = 1, /*! No functionality as of now */ + } vpCameraImgBckGrndType; + + bool flg_normalize_intensity; // To scale the intensity of each individual pixel in the image by the maximum intensity value present in it + + // Constructor helpers + void init(unsigned int orderinp); + void init(const vpMomentObject& objin); + // Constructors vpMomentObject(unsigned int order); - void fromImage(const vpImage<unsigned char>& image,unsigned char threshold, const vpCameraParameters& cam); + vpMomentObject(const vpMomentObject& srcobj); + /*! + Virtual destructor to allow polymorphic usage. + For instance, + \code + vpMomentObject* obj = new vpWeightedMomentObject(weightfunc,ORDER); where vpWeightedMomentObject is child class of vpMomentObject + \endcode + */ + virtual ~vpMomentObject(); + + void fromImage(const vpImage<unsigned char>& image,unsigned char threshold, const vpCameraParameters& cam); // Binary version + void fromImage(const vpImage<unsigned char>& image, const vpCameraParameters& cam, vpCameraImgBckGrndType bg_type, bool normalize_with_pix_size = true); // Photometric version + void fromVector(std::vector<vpPoint>& points); - std::vector<double>& get(); + const std::vector<double>& get() const; double get(unsigned int i,unsigned int j) const; + /*! \return The type of object that is considered. */ vpObjectType getType() const {return type;} + /*! \return The maximal order. The basic moments \f$m_{ij}\f$ that will be computed are for \f$i+j \in [0:\mbox{order}]\f$. */ unsigned int getOrder() const {return order-1;} + /*! Specifies the type of the input data. - \param type : An input type. + \param input_type : An input type. */ - void setType(vpObjectType type){this->type=type;} + void setType(vpObjectType input_type){this->type=input_type;} friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpMomentObject& v); + /*! + Outputs raw moments in indexed form like m[1,1] = value of moment m11 + \param momobj : A vpMomentObject + \param os : Output stream. + */ + static void printWithIndices(const vpMomentObject& momobj, std::ostream& os); + /*! + Converts the raw moments contained in vpMomentObject to a vpMatrix + \param momobj : A vpMomentObject + */ + static vpMatrix convertTovpMatrix(const vpMomentObject& momobj); -private: +protected: unsigned int order; vpObjectType type; std::vector<double> values; + void set(unsigned int i, unsigned int j, const double& value_ij); void cacheValues(std::vector<double>& cache,double x, double y); +private: + void cacheValues(std::vector<double>& cache,double x, double y, double IntensityNormalized); double calc_mom_polygon(unsigned int p, unsigned int q, const std::vector<vpPoint>& points); }; diff --git a/src/tracking/moving-edges/vpMe.cpp b/src/tracking/moving-edges/vpMe.cpp index 4b187885bb9514e8ffcc316f8143b8bbaeb94ae2..bdc02643c39ab8c68e36ba78174ba31048f919ef 100644 --- a/src/tracking/moving-edges/vpMe.cpp +++ b/src/tracking/moving-edges/vpMe.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMe.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMe.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -100,9 +100,9 @@ static point point_intersection(droite D1, droite D2) { point I; - double det; // déterminant des 2 vect.normaux + double det; // determinant des 2 vect.normaux - det = (D1.a*D2.b - D2.a*D1.b); // interdit D1,D2 parallèles + det = (D1.a*D2.b - D2.a*D1.b); // interdit D1,D2 paralleles I.x = (D2.c*D1.b - D1.c*D2.b)/det; I.y = (D1.c*D2.a - D2.c*D1.a)/det; @@ -114,7 +114,7 @@ recale(point & P, double Xmin, double Ymin, double Xmax, double Ymax) { if(vpMath::equal(P.x,Xmin)) - P.x=Xmin; // à peu près => exactement ! + P.x=Xmin; // a peu pres => exactement ! if(vpMath::equal(P.x,Xmax)) P.x=Xmax; @@ -143,7 +143,7 @@ permute(point &A, point &B) static bool clipping (point A, point B, double Xmin, double Ymin, double Xmax, double Ymax, - point & Ac , point & Bc )// résultat: A,B clippés + point & Ac , point & Bc )// resultat: A,B clippes { droite AB, D[4]; D[0].a = 1; D[0].b = 0; D[0].c = -Xmin; @@ -180,10 +180,10 @@ clipping (point A, point B, // 2 CAS OU L'ON PEUT CONCLURE => sortie // ===================================== - if((code_P[0] | code_P[1])==0000) // Aucun bit à 1 + if((code_P[0] | code_P[1])==0000) // Aucun bit a 1 /* NE TRIE PLUS LE RESULTAT ! S_relative() en tient compte { if(P[0].x < P[1].x) // Rend le couple de points - { Ac=P[0]; Bc=P[1]; } // clippés (ordonnés selon + { Ac=P[0]; Bc=P[1]; } // clippes (ordonnes selon else { Ac=P[1]; Bc=P[0]; } // leur abscisse x) */ { @@ -191,16 +191,16 @@ clipping (point A, point B, if(vpMath::equal(Ac.x,Bc.x) && vpMath::equal(Ac.y,Bc.y)) return(false); // AB = 1 point = invisible else - return(true); // Partie de AB clippée visible! + return(true); // Partie de AB clippee visible! } if((code_P[0] & code_P[1])!=0000) // au moins 1 bit commun { - return(false); // AB complètement invisible! + return(false); // AB completement invisible! } - // CAS GENERAL (on sait que code_P[0 ou 1] a au moins un bit à 1 + // CAS GENERAL (on sait que code_P[0 ou 1] a au moins un bit a 1 // - clippe le point P[n] qui sort de la fenêtre (coupe Droite i) // - reboucle avec le nouveau couple de points // ================================================================ @@ -215,7 +215,7 @@ clipping (point A, point B, for(i=0,bit_i=1; !(code_P[1] & bit_i); i++,bit_i<<=1){;} } - P[n] = point_intersection(AB,D[i]); // clippe le point concerné + P[n] = point_intersection(AB,D[i]); // clippe le point concerne // RECALE EXACTEMENT LE POINT (calcul flottant => arrondi) @@ -228,9 +228,9 @@ clipping (point A, point B, } -// calcule la surface relative des 2 portions définies -// par le segment PQ sur le carré Xmin,Ymin,Xmax,Ymax -// Rem : P,Q triés sur x, et donc seulement 6 cas +// calcule la surface relative des 2 portions definies +// par le segment PQ sur le carre Xmin,Ymin,Xmax,Ymax +// Rem : P,Q tries sur x, et donc seulement 6 cas static double S_relative(point P, point Q, double Xmin, double Ymin, double Xmax, double Ymax) @@ -288,11 +288,11 @@ S_relative(point P, point Q, static void -calcul_masques(vpColVector &angle, // définitions des angles theta +calcul_masques(vpColVector &angle, // definitions des angles theta unsigned int n, // taille masques (PAIRE ou IMPAIRE Ok) - vpMatrix *M) // résultat M[theta](n,n) + vpMatrix *M) // resultat M[theta](n,n) { - // Le coef |a| = |1/2n| n'est pas incorporé dans M(i,j) (=> que des int) + // Le coef |a| = |1/2n| n'est pas incorpore dans M(i,j) (=> que des int) unsigned int i_theta, // indice (boucle sur les masques) i,j; // indices de boucle sur M(i,j) @@ -338,7 +338,7 @@ calcul_masques(vpColVector &angle, // d // produit vectoriel dir_droite*(X,Y) sgn = vpMath::sign(cos_theta*Y - sin_theta*X); - // Résultat = P,Q + // Resultat = P,Q if( clipping(P1,Q1, X-0.5,Y-0.5,X+0.5,Y+0.5, P,Q) ) { // v dans [0,1] @@ -411,34 +411,28 @@ vpMe::print( ) } vpMe::vpMe() + : threshold(1500), mu1(0.5), mu2(0.5), min_samplestep(4), anglestep(1), mask_sign(0), + range(4), sample_step(10), ntotal_sample(0), points_to_track(500), mask_size(5), + n_mask(180), strip(2), mask(NULL) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , aberration(2), init_aberration(5.) +#endif { - mask = NULL ; - threshold = 1500 ; - mu1 = 0.5 ; - mu2 = 0.5 ; - sample_step = 10 ; - range = 4 ; - mask_size = 5 ; - n_mask = 180 ; - mask_sign = 0 ; - ntotal_sample = 0; // not sure that it is used - points_to_track = 500; // not sure that it is used + //ntotal_sample = 0; // not sure that it is used + //points_to_track = 500; // not sure that it is used anglestep = (180 / n_mask) ; - strip = 2 ; - min_samplestep = 4 ; - - #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - aberration = 2.0 ; - init_aberration = 5.0 ; - #endif - + initMask() ; } vpMe::vpMe(const vpMe &me) + : threshold(1500), mu1(0.5), mu2(0.5), min_samplestep(4), anglestep(1), mask_sign(0), + range(4), sample_step(10), ntotal_sample(0), points_to_track(500), mask_size(5), + n_mask(180), strip(2), mask(NULL) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , aberration(2), init_aberration(5.) +#endif { - mask = NULL ; - *this = me; } diff --git a/src/tracking/moving-edges/vpMe.h b/src/tracking/moving-edges/vpMe.h index c94d3170655b598a3cd1c1ab13c620e87a75aaff..d51f7f4116589721fd91a808fbe1cc83e1cdb748 100644 --- a/src/tracking/moving-edges/vpMe.h +++ b/src/tracking/moving-edges/vpMe.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMe.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMe.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -166,9 +166,9 @@ public: /*! Set the minimum image contrast allowed to detect a contour. - \param mu1 : new mu1. + \param mu_1 : new mu1. */ - void setMu1(const double &mu1) { this->mu1 = mu1 ; } + void setMu1(const double &mu_1) { this->mu1 = mu_1 ; } /*! Get the minimum image contrast allowed to detect a contour. @@ -180,9 +180,9 @@ public: /*! Set the maximum image contrast allowed to detect a contour. - \param mu2 : new mu2. + \param mu_2 : new mu2. */ - void setMu2(const double &mu2) { this->mu2 = mu2 ; } + void setMu2(const double &mu_2) { this->mu2 = mu_2 ; } /*! Get the maximum image contrast allowed to detect a contour. diff --git a/src/tracking/moving-edges/vpMeEllipse.cpp b/src/tracking/moving-edges/vpMeEllipse.cpp index 38ea8432038ea4ca2867877c08fd66fe6b571330..2ee89eabfa3326e1a90943cf54da3334babe9cf5 100644 --- a/src/tracking/moving-edges/vpMeEllipse.cpp +++ b/src/tracking/moving-edges/vpMeEllipse.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeEllipse.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpMeEllipse.cpp 4706 2014-03-28 07:52:00Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,6 +51,8 @@ #include <cmath> // std::fabs #include <limits> // numeric_limits + +void computeTheta(double &theta, vpColVector &K, vpImagePoint iP); /*! Computes the \f$ \theta \f$ angle which represents the angle between the tangente to the curve and the i axis. @@ -78,36 +80,36 @@ computeTheta(double &theta, vpColVector &K, vpImagePoint iP) /*! Basic constructor that calls the constructor of the class vpMeTracker. */ -vpMeEllipse::vpMeEllipse():vpMeTracker() +vpMeEllipse::vpMeEllipse() + : K(), iPc(), a(0.), b(0.), e(0.), iP1(), iP2(), alpha1(0), alpha2(2*M_PI), + ce(0.), se(0.), angle(), m00(0.), mu11(0.), mu20(0.), mu02(0.), + m10(0.), m01(0.), m11(0.), m02(0.), m20(0.), + thresholdWeight(0.2), expecteddensity(0.), circle(false) { vpCDEBUG(1) << "begin vpMeEllipse::vpMeEllipse() " << std::endl ; // redimensionnement du vecteur de parametre // i^2 + K0 j^2 + 2 K1 i j + 2 K2 i + 2 K3 j + K4 - circle = false ; K.resize(5) ; - alpha1 = 0 ; - alpha2 = 2*M_PI ; - //j1 = j2 = i1 = i2 = 0 ; iP1.set_i(0); iP1.set_j(0); iP2.set_i(0); iP2.set_j(0); - m00 = m01 = m10 = m11 = m20 = m02 = mu11 = mu20 = mu02 = 0; - - thresholdWeight = 0.2; - vpCDEBUG(1) << "end vpMeEllipse::vpMeEllipse() " << std::endl ; } /*! Copy constructor. */ -vpMeEllipse::vpMeEllipse(const vpMeEllipse &meellipse):vpMeTracker(meellipse) +vpMeEllipse::vpMeEllipse(const vpMeEllipse &meellipse) + : vpMeTracker(meellipse), K(), iPc(), a(0.), b(0.), e(0.), iP1(), iP2(), alpha1(0), alpha2(2*M_PI), + ce(0.), se(0.), angle(), m00(0.), mu11(0.), mu20(0.), mu02(0.), + m10(0.), m01(0.), m11(0.), m02(0.), m20(0.), + thresholdWeight(0.2), expecteddensity(0.), circle(false) { K = meellipse.K; iPc = meellipse.iPc; @@ -134,6 +136,9 @@ vpMeEllipse::vpMeEllipse(const vpMeEllipse &meellipse):vpMeTracker(meellipse) m02 = meellipse.m02; m20 = meellipse.m20; thresholdWeight = meellipse.thresholdWeight; + + circle = meellipse.circle; + expecteddensity = meellipse.expecteddensity; } /*! @@ -272,7 +277,7 @@ vpMeEllipse::reSample(const vpImage<unsigned char> &I) } unsigned int n = numberOfSignal() ; - double expecteddensity = (alpha2-alpha1) / vpMath::rad((double)me->getSampleStep()); + expecteddensity = (alpha2-alpha1) / vpMath::rad((double)me->getSampleStep()); if ((double)n<0.9*expecteddensity){ sample(I) ; } @@ -280,7 +285,7 @@ vpMeEllipse::reSample(const vpImage<unsigned char> &I) /*! - Computes the length of the semiminor axis \f$ a \f$, the length of the semimajor axis \f$ b \f$ and, + Computes the coordinates of the ellipse center, length of the semiminor axis \f$ a \f$, the length of the semimajor axis \f$ b \f$ and, \f$ e \f$ which is the angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$. All those computations are made thanks to the parameters \f$ K = {K_0, ..., K_4} \f$. @@ -296,7 +301,6 @@ vpMeEllipse::getParameters() double d = k[2]*k[2] - k[0]*k[1]; - iPc.set_i( (k[1] * k[3] - k[2] * k[4]) / d ); iPc.set_j( (k[0] * k[4] - k[2] * k[3]) / d ); @@ -394,13 +398,11 @@ vpMeEllipse::computeAngle(vpImagePoint pt1, vpImagePoint pt2) } //std::cout << "end vpMeEllipse::computeAngle(..)" << alpha1 << " " << alpha2 << std::endl ; - if (alpha2 <alpha1) - { -// double alphatmp = alpha2; -// alpha2 = alpha1; -// alpha1 = alphatmp; + if (alpha2 < alpha1) + alpha2 += 2 * M_PI; + //else if (alpha2 == alpha1) + else if (std::fabs(alpha2 - alpha1) < std::fabs(alpha1) * std::numeric_limits<double>::epsilon()) alpha2 += 2 * M_PI; - } //std::cout << "end vpMeEllipse::computeAngle(..)" << alpha1 << " " << alpha2 << std::endl ; @@ -416,16 +418,16 @@ vpMeEllipse::computeAngle(vpImagePoint pt1, vpImagePoint pt2) void vpMeEllipse::updateTheta() { - vpMeSite p; + vpMeSite p_me; double theta; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; + p_me = *it; vpImagePoint iP; - iP.set_i(p.ifloat); - iP.set_j(p.jfloat); + iP.set_i(p_me.ifloat); + iP.set_j(p_me.jfloat); computeTheta(theta, K, iP) ; - p.alpha = theta ; - *it = p; + p_me.alpha = theta ; + *it = p_me; } } @@ -629,10 +631,10 @@ vpMeEllipse::leastSquare() // A = (j^2 2ij 2i 2j 1) x = (K0 K1 K2 K3 K4)^T b = (-i^2 ) unsigned int i ; - vpMeSite p ; + vpMeSite p_me ; unsigned int iter =0 ; - vpColVector b(numberOfSignal()) ; + vpColVector b_(numberOfSignal()) ; vpRobust r(numberOfSignal()) ; r.setThreshold(2); r.setIteration(0) ; @@ -658,17 +660,16 @@ vpMeEllipse::leastSquare() unsigned int k =0 ; for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { - - A[k][0] = vpMath::sqr(p.jfloat) ; - A[k][1] = 2 * p.ifloat * p.jfloat ; - A[k][2] = 2 * p.ifloat ; - A[k][3] = 2 * p.jfloat ; + A[k][0] = vpMath::sqr(p_me.jfloat) ; + A[k][1] = 2 * p_me.ifloat * p_me.jfloat ; + A[k][2] = 2 * p_me.ifloat ; + A[k][3] = 2 * p_me.jfloat ; A[k][4] = 1 ; - b[k] = - vpMath::sqr(p.ifloat) ; + b_[k] = - vpMath::sqr(p_me.ifloat) ; k++ ; } } @@ -678,10 +679,10 @@ vpMeEllipse::leastSquare() DA = D*A ; vpMatrix DAp ; - x = DA.pseudoInverse(1e-26) *D*b ; + x = DA.pseudoInverse(1e-26) *D*b_ ; vpColVector residu(nos_1); - residu = b - A*x; + residu = b_ - A*x; r.setIteration(iter) ; r.MEstimator(vpRobust::TUKEY,residu,w) ; @@ -696,14 +697,14 @@ vpMeEllipse::leastSquare() k =0 ; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { if (w[k] < thresholdWeight) { - p.setState(vpMeSite::M_ESTIMATOR); + p_me.setState(vpMeSite::M_ESTIMATOR); - *it = p; + *it = p_me; } k++ ; } @@ -719,15 +720,14 @@ vpMeEllipse::leastSquare() unsigned int k =0 ; for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { - - A[k][0] = 2* p.ifloat ; - A[k][1] = 2 * p.jfloat ; + A[k][0] = 2* p_me.ifloat ; + A[k][1] = 2 * p_me.jfloat ; A[k][2] = 1 ; - b[k] = - vpMath::sqr(p.ifloat) - vpMath::sqr(p.jfloat) ; + b_[k] = - vpMath::sqr(p_me.ifloat) - vpMath::sqr(p_me.jfloat) ; k++ ; } } @@ -737,10 +737,10 @@ vpMeEllipse::leastSquare() DA = D*A ; vpMatrix DAp ; - x = DA.pseudoInverse(1e-26) *D*b ; + x = DA.pseudoInverse(1e-26) *D*b_ ; vpColVector residu(nos_1); - residu = b - A*x; + residu = b_ - A*x; r.setIteration(iter) ; r.MEstimator(vpRobust::TUKEY,residu,w) ; @@ -755,14 +755,14 @@ vpMeEllipse::leastSquare() k =0 ; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { if (w[k] < thresholdWeight) { - p.setState(vpMeSite::M_ESTIMATOR); + p_me.setState(vpMeSite::M_ESTIMATOR); - *it = p; + *it = p_me; } k++ ; } @@ -840,7 +840,7 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, if (circle==false) { vpMatrix A(n,5) ; - vpColVector b(n) ; + vpColVector b_(n) ; vpColVector x(5) ; // Construction du systeme Ax=b @@ -855,16 +855,16 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, A[k][3] = 2* iP[k].get_j() ; A[k][4] = 1 ; - b[k] = - vpMath::sqr(iP[k].get_i()) ; + b_[k] = - vpMath::sqr(iP[k].get_i()) ; } - K = A.pseudoInverse(1e-26)*b ; + K = A.pseudoInverse(1e-26)*b_ ; std::cout << K << std::endl; } else { vpMatrix A(n,3) ; - vpColVector b(n) ; + vpColVector b_(n) ; vpColVector x(3) ; vpColVector Kc(3) ; @@ -874,10 +874,10 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, A[k][1] = 2* iP[k].get_j() ; A[k][2] = 1 ; - b[k] = - vpMath::sqr(iP[k].get_i()) - vpMath::sqr(iP[k].get_j()) ; + b_[k] = - vpMath::sqr(iP[k].get_i()) - vpMath::sqr(iP[k].get_j()) ; } - Kc = A.pseudoInverse(1e-26)*b ; + Kc = A.pseudoInverse(1e-26)*b_ ; K[0] = 1 ; K[1] = 0 ; K[2] = Kc[0] ; @@ -891,9 +891,14 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, iP2 = iP[n-1]; getParameters() ; + std::cout << "vpMeEllipse::initTracking() ellipse avant: " << iPc << " " << a << " " << b << " " << vpMath::deg(e) << " alpha: " << alpha1 << " " << alpha2 << std::endl; + computeAngle(iP1, iP2) ; - display(I, vpColor::green) ; + std::cout << "vpMeEllipse::initTracking() ellipse apres: " << iPc << " " << a << " " << b << " " << vpMath::deg(e) << " alpha: " << alpha1 << " " << alpha2 << std::endl; + + expecteddensity = (alpha2-alpha1) / vpMath::rad((double)me->getSampleStep()); + display(I, vpColor::green) ; sample(I) ; // 2. On appelle ce qui n'est pas specifique @@ -901,7 +906,6 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, vpMeTracker::initTracking(I) ; } - try{ track(I) ; } @@ -915,6 +919,47 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, } +void +vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ic, double a_p, double b_p, double e_p, + double low_alpha, double high_alpha) +{ + iPc = ic; + a = a_p; + b = b_p; + e = e_p; + alpha1 = low_alpha; + alpha2 = high_alpha; + + if (alpha2 <alpha1) + alpha2 += 2 * M_PI; + + ce = cos(e); + se = sin(e); + +// vpDisplay::displayEllipse(I, iPc, a, b, e, 0, vpMath::rad(360), vpColor::red, 2); // TODO remove debug only + display(I, vpColor::green) ; +// vpDisplay::flush(I); // TODO remove debug only +// std::cout << "wait click" << std::endl; +// vpDisplay::getClick(I); // TODO remove debug only + sample(I) ; + + // 2. On appelle ce qui n'est pas specifique + { + vpMeTracker::initTracking(I) ; + } + + try{ + track(I) ; + } + catch(...) + { + vpERROR_TRACE("Error caught") ; + throw ; + } + vpMeTracker::display(I) ; + vpDisplay::flush(I) ; +} + /*! Track the ellipse in the image I. @@ -1060,7 +1105,7 @@ vpMeEllipse::computeMoments() */ void vpMeEllipse::computeAngle(int ip1, int jp1, double &_alpha1, - int ip2, int jp2, double &_alpha2) + int ip2, int jp2, double &_alpha2) { getParameters() ; @@ -1130,7 +1175,7 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, if (circle==false) { vpMatrix A(n,5) ; - vpColVector b(n) ; + vpColVector b_(n) ; vpColVector x(5) ; // Construction du systeme Ax=b @@ -1145,16 +1190,16 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, A[k][3] = 2* j[k] ; A[k][4] = 1 ; - b[k] = - vpMath::sqr(i[k]) ; + b_[k] = - vpMath::sqr(i[k]) ; } - K = A.pseudoInverse(1e-26)*b ; + K = A.pseudoInverse(1e-26)*b_ ; std::cout << K << std::endl; } else { vpMatrix A(n,3) ; - vpColVector b(n) ; + vpColVector b_(n) ; vpColVector x(3) ; vpColVector Kc(3) ; @@ -1164,10 +1209,10 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, A[k][1] = 2* j[k] ; A[k][2] = 1 ; - b[k] = - vpMath::sqr(i[k]) - vpMath::sqr(j[k]) ; + b_[k] = - vpMath::sqr(i[k]) - vpMath::sqr(j[k]) ; } - Kc = A.pseudoInverse(1e-26)*b ; + Kc = A.pseudoInverse(1e-26)*b_ ; K[0] = 1 ; K[1] = 0 ; K[2] = Kc[0] ; @@ -1192,7 +1237,6 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, vpMeTracker::initTracking(I) ; } - try{ track(I) ; } @@ -1221,16 +1265,18 @@ vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const unsigned int n, \param E : Angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$ - \param smallalpha : Smallest \f$ alpha \f$ angle. + \param smallalpha : Smallest \f$ alpha \f$ angle in rad. - \param highalpha : Highest \f$ alpha \f$ angle. + \param highalpha : Highest \f$ alpha \f$ angle in rad. \param color : Color used to display th lines. + + \param thickness : Thickness of the drawings. */ void vpMeEllipse::display(const vpImage<unsigned char>& I, const vpImagePoint ¢er, const double &A, const double &B, const double &E, const double & smallalpha, const double &highalpha, - vpColor color) + const vpColor &color, unsigned int thickness) { double j1, i1; vpImagePoint iP11; @@ -1240,7 +1286,7 @@ void vpMeEllipse::display(const vpImage<unsigned char>& I, const vpImagePoint &c double incr = vpMath::rad(2) ; // angle increment - vpDisplay::displayCross(I,center,20,vpColor::red) ; + vpDisplay::displayCross(I,center,20, vpColor::red, thickness) ; double k = smallalpha ; while (k+incr<highalpha) @@ -1260,7 +1306,7 @@ void vpMeEllipse::display(const vpImage<unsigned char>& I, const vpImagePoint &c iP22.set_j ( center.get_j() + cos(E) *j2 - sin(E) *i2 ); iP22.set_i ( center.get_i() -( sin(E) *j2 + cos(E) *i2) ); - vpDisplay::displayLine(I, iP11, iP22, color, 3) ; + vpDisplay::displayLine(I, iP11, iP22, color, thickness) ; k += incr ; } @@ -1280,8 +1326,8 @@ void vpMeEllipse::display(const vpImage<unsigned char>& I, const vpImagePoint &c iP22.set_j ( center.get_j() + cos(E) *j2 - sin(E) *i2 ); iP22.set_i ( center.get_i() -( sin(E) *j2 + cos(E) *i2) ); - vpDisplay::displayLine(I, center, iP11, vpColor::red, 3) ; - vpDisplay::displayLine(I, center, iP22, vpColor::blue, 3) ; + vpDisplay::displayLine(I, center, iP11, vpColor::red, thickness) ; + vpDisplay::displayLine(I, center, iP22, vpColor::blue, thickness) ; } /*! @@ -1298,16 +1344,18 @@ void vpMeEllipse::display(const vpImage<unsigned char>& I, const vpImagePoint &c \param E : Angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$ - \param smallalpha : Smallest \f$ alpha \f$ angle. + \param smallalpha : Smallest \f$ alpha \f$ angle in rad. - \param highalpha : Highest \f$ alpha \f$ angle. + \param highalpha : Highest \f$ alpha \f$ angle in rad. \param color : Color used to display th lines. + + \param thickness : Thickness of the drawings. */ void vpMeEllipse::display(const vpImage<vpRGBa>& I, const vpImagePoint ¢er, const double &A, const double &B, const double &E, const double & smallalpha, const double &highalpha, - vpColor color) + const vpColor &color, unsigned int thickness) { double j1, i1; vpImagePoint iP11; @@ -1317,7 +1365,7 @@ void vpMeEllipse::display(const vpImage<vpRGBa>& I, const vpImagePoint ¢er, double incr = vpMath::rad(2) ; // angle increment - vpDisplay::displayCross(I,center,20,vpColor::red) ; + vpDisplay::displayCross(I,center,20, vpColor::red, thickness) ; double k = smallalpha ; while (k+incr<highalpha) @@ -1337,7 +1385,7 @@ void vpMeEllipse::display(const vpImage<vpRGBa>& I, const vpImagePoint ¢er, iP22.set_j ( center.get_j() + cos(E) *j2 - sin(E) *i2 ); iP22.set_i ( center.get_i() -( sin(E) *j2 + cos(E) *i2) ); - vpDisplay::displayLine(I, iP11, iP22, color, 3) ; + vpDisplay::displayLine(I, iP11, iP22, color, thickness) ; k += incr ; } @@ -1357,6 +1405,6 @@ void vpMeEllipse::display(const vpImage<vpRGBa>& I, const vpImagePoint ¢er, iP22.set_j ( center.get_j() + cos(E) *j2 - sin(E) *i2 ); iP22.set_i ( center.get_i() -( sin(E) *j2 + cos(E) *i2) ); - vpDisplay::displayLine(I, center, iP11, vpColor::red, 3) ; - vpDisplay::displayLine(I, center, iP22, vpColor::blue, 3) ; + vpDisplay::displayLine(I, center, iP11, vpColor::red, thickness) ; + vpDisplay::displayLine(I, center, iP22, vpColor::blue, thickness) ; } diff --git a/src/tracking/moving-edges/vpMeEllipse.h b/src/tracking/moving-edges/vpMeEllipse.h index 8aa71961960743781d22a22201df898a572d90bc..fff27012ec61fb8b3277c3a23ab94313f5d73fc0 100644 --- a/src/tracking/moving-edges/vpMeEllipse.h +++ b/src/tracking/moving-edges/vpMeEllipse.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeEllipse.h 4231 2013-04-29 16:26:28Z fspindle $ + * $Id: vpMeEllipse.h 4705 2014-03-27 16:24:35Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -146,12 +146,17 @@ public: vpMeEllipse(const vpMeEllipse &meellipse) ; virtual ~vpMeEllipse() ; + /*! + \return Expected number of moving edges to track along the ellipse. + */ + int getExpectedDensity() {return (int)expecteddensity;}; void track(const vpImage<unsigned char>& Im); void initTracking(const vpImage<unsigned char> &I) ; - void initTracking(const vpImage<unsigned char> &I, const unsigned int n, - vpImagePoint* iP) ; + void initTracking(const vpImage<unsigned char> &I, const unsigned int n, vpImagePoint* iP) ; + void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ic, double a_p, double b_p, double e_p, double low_alpha, double high_alpha) ; void display(const vpImage<unsigned char>&I, vpColor col) ; + void display(const vpImage<unsigned char>& I) {vpMeTracker::display(I);} //Shouldn't be here since it's already in vpMeTracker void printParameters() ; #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS @@ -175,9 +180,9 @@ public: Compared to the classical equation of an ellipse, \f$ K_0 \f$ is equal to 1 and \f$ K_1 \f$ is equal to 0. - \param circle : Set to true if you want to track a circle. + \param is_circle : Set to true if you want to track a circle. */ - void setCircle(bool circle) { this->circle = circle ; } + void setCircle(bool is_circle) { this->circle = is_circle ; } /*! Gets the 0 order moment \f$ m_{00} \f$ which represents the area of the ellipse. @@ -315,7 +320,7 @@ protected: double b; //! \f$ e \f$ is the angle made by the major axis and the i axis of the image frame \f$ (i,j) \f$. double e; - + protected: //! The coordinates of the point corresponding to the smallest \f$ alpha \f$ angle. More things about the \f$ alpha \f$ are given at the beginning of the class description. vpImagePoint iP1; @@ -341,6 +346,8 @@ protected: double m11,m02,m20; //! Threshold for the robust least square. double thresholdWeight; + //! Expected number of me to track along the ellipse. + double expecteddensity; private: //! True if the ellipse to track is a circle @@ -373,11 +380,13 @@ public: static void display(const vpImage<unsigned char>& I, const vpImagePoint ¢er, const double &A, const double &B, const double &E, const double & smallalpha, const double &highalpha, - vpColor color = vpColor::green); + const vpColor &color = vpColor::green, + unsigned int thickness=1); static void display(const vpImage<vpRGBa>& I, const vpImagePoint ¢er, const double &A, const double &B, const double &E, const double & smallalpha, const double &highalpha, - vpColor color = vpColor::green); + const vpColor &color = vpColor::green, + unsigned int thickness=1); }; diff --git a/src/tracking/moving-edges/vpMeLine.cpp b/src/tracking/moving-edges/vpMeLine.cpp index fa562e93ef8f003df0500e82b70b853b0858a3d6..338fea347bb2e6cd15167165258a92496c705123 100644 --- a/src/tracking/moving-edges/vpMeLine.cpp +++ b/src/tracking/moving-edges/vpMeLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeLine.cpp 4140 2013-02-21 10:50:22Z fspindle $ + * $Id: vpMeLine.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,8 +58,12 @@ #include <stdlib.h> #include <cmath> // std::fabs #include <limits> // numeric_limits +#include <algorithm> // std::min + #define INCR_MIN 1 +void computeDelta(double &delta, int i1, int j1, int i2, int j2); + static void normalizeAngle(double &delta) { @@ -101,18 +105,21 @@ project(double a, double b, double c, Basic constructor that calls the constructor of the class vpMeTracker. */ -vpMeLine::vpMeLine():vpMeTracker() +vpMeLine::vpMeLine() + : rho(0.), theta(0.), delta(0.), delta_1(0.), angle(0.), angle_1(90), sign(1), + _useIntensityForRho(true), a(0.), b(0.), c(0.) { - sign = 1; - angle_1 = 90; - _useIntensityForRho = true; } /*! Copy constructor. */ -vpMeLine::vpMeLine(const vpMeLine &meline):vpMeTracker(meline) +vpMeLine::vpMeLine(const vpMeLine &meline) + : vpMeTracker(meline), + rho(0.), theta(0.), delta(0.), delta_1(0.), angle(0.), angle_1(90), sign(1), + _useIntensityForRho(true), a(0.), b(0.), c(0.) + { rho = meline.rho; theta = meline.theta; @@ -300,7 +307,7 @@ vpMeLine::leastSquare() vpColVector w(numberOfSignal()) ; vpColVector B(numberOfSignal()) ; w =1 ; - vpMeSite p ; + vpMeSite p_me ; unsigned int iter =0 ; unsigned int nos_1 = 0 ; double distance = 100; @@ -320,12 +327,12 @@ vpMeLine::leastSquare() nos_1 = numberOfSignal() ; unsigned int k =0 ; for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { - A[k][0] = p.ifloat ; + A[k][0] = p_me.ifloat ; A[k][1] = 1 ; - B[k] = -p.jfloat ; + B[k] = -p_me.jfloat ; k++ ; } } @@ -353,14 +360,14 @@ vpMeLine::leastSquare() k =0 ; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { if (w[k] < 0.2) { - p.setState(vpMeSite::M_ESTIMATOR); + p_me.setState(vpMeSite::M_ESTIMATOR); - *it = p; + *it = p_me; } k++ ; } @@ -385,12 +392,12 @@ vpMeLine::leastSquare() nos_1 = numberOfSignal() ; unsigned int k =0 ; for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { - A[k][0] = p.jfloat ; + A[k][0] = p_me.jfloat ; A[k][1] = 1 ; - B[k] = -p.ifloat ; + B[k] = -p_me.ifloat ; k++ ; } } @@ -416,17 +423,16 @@ vpMeLine::leastSquare() x_1 = x; } - k =0 ; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - if (p.getState() == vpMeSite::NO_SUPPRESSION) + p_me = *it; + if (p_me.getState() == vpMeSite::NO_SUPPRESSION) { if (w[k] < 0.2) { - p.setState(vpMeSite::M_ESTIMATOR); + p_me.setState(vpMeSite::M_ESTIMATOR); - *it = p; + *it = p_me; } k++ ; } @@ -482,9 +488,9 @@ vpMeLine::initTracking(const vpImage<unsigned char> &I, PExt[1].ifloat = (float)ip2.get_i() ; PExt[1].jfloat = (float)ip2.get_j() ; - double angle = atan2((double)(i1s-i2s),(double)(j1s-j2s)) ; - a = cos(angle) ; - b = sin(angle) ; + double angle_ = atan2((double)(i1s-i2s),(double)(j1s-j2s)) ; + a = cos(angle_) ; + b = sin(angle_) ; // Real values of a, b can have an other sign. So to get the good values // of a and b in order to initialise then c, we call track(I) just below @@ -634,7 +640,7 @@ vpMeLine::seekExtremities(const vpImage<unsigned char> &I) // number of samples along line_p n_sample = length_p/(double)me->getSampleStep(); - double sample = (double)me->getSampleStep(); + double sample_step = (double)me->getSampleStep(); vpMeSite P ; P.init((int) PExt[0].ifloat, (int)PExt[0].jfloat, delta_1, 0, sign) ; @@ -647,8 +653,8 @@ vpMeLine::seekExtremities(const vpImage<unsigned char> &I) for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat + di*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat + dj*sample ; P.j = (int)P.jfloat ; + P.ifloat = P.ifloat + di*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat + dj*sample_step ; P.j = (int)P.jfloat ; if(!outOfImage(P.i, P.j, 5, rows, cols)) { @@ -678,8 +684,8 @@ vpMeLine::seekExtremities(const vpImage<unsigned char> &I) P.setDisplay(selectDisplay) ; for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat - di*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat - dj*sample ; P.j = (int)P.jfloat ; + P.ifloat = P.ifloat - di*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat - dj*sample_step ; P.j = (int)P.jfloat ; if(!outOfImage(P.i, P.j, 5, rows, cols)) { @@ -767,34 +773,34 @@ vpMeLine::reSample(const vpImage<unsigned char> &I) void vpMeLine::updateDelta() { - vpMeSite p ; + vpMeSite p_me ; - double angle = delta + M_PI/2; + double angle_ = delta + M_PI/2; double diff = 0; - while (angle<0) angle += M_PI; - while (angle>M_PI) angle -= M_PI; + while (angle_<0) angle_ += M_PI; + while (angle_>M_PI) angle_ -= M_PI; - angle = vpMath::round(angle * 180 / M_PI) ; + angle_ = vpMath::round(angle_ * 180 / M_PI) ; - //if(fabs(angle) == 180 ) - if(std::fabs(std::fabs(angle) - 180) <= std::numeric_limits<double>::epsilon()) + //if(fabs(angle_) == 180 ) + if(std::fabs(std::fabs(angle_) - 180) <= std::numeric_limits<double>::epsilon()) { - angle= 0 ; + angle_= 0 ; } //std::cout << "angle theta : " << theta << std::endl ; - diff = fabs(angle - angle_1); + diff = fabs(angle_ - angle_1); if (diff > 90) sign *= -1; - angle_1 = angle; + angle_1 = angle_; for(std::list<vpMeSite>::iterator it=list.begin(); it!=list.end(); ++it){ - p = *it; - p.alpha = delta ; - p.mask_sign = sign; - *it = p; + p_me = *it; + p_me.alpha = delta ; + p_me.mask_sign = sign; + *it = p_me; } delta_1 = delta; } diff --git a/src/tracking/moving-edges/vpMeLine.h b/src/tracking/moving-edges/vpMeLine.h index 6f7897db63a470dda8dd3339d7761260c1954d73..3d51adda929a91c67e360cc3c650a3087c751042 100644 --- a/src/tracking/moving-edges/vpMeLine.h +++ b/src/tracking/moving-edges/vpMeLine.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeLine.h 4231 2013-04-29 16:26:28Z fspindle $ + * $Id: vpMeLine.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/tracking/moving-edges/vpMeNurbs.cpp b/src/tracking/moving-edges/vpMeNurbs.cpp index 389b91721d77f96f9d4685a056af9266930f0680..568561940b35712fd364b7efeb581b9de75686e8 100644 --- a/src/tracking/moving-edges/vpMeNurbs.cpp +++ b/src/tracking/moving-edges/vpMeNurbs.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeNurbs.cpp 4135 2013-02-13 16:48:19Z fspindle $ + * $Id: vpMeNurbs.cpp 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,6 +56,7 @@ #include <visp/vpRect.h> #include <visp/vpImageTools.h> #include <visp/vpImageConvert.h> +#include <visp/vpImageFilter.h> #include <stdlib.h> #include <cmath> // std::fabs #include <limits> // numeric_limits @@ -68,6 +69,15 @@ # endif #endif +double computeDelta(double deltai, double deltaj); +void findAngle(const vpImage<unsigned char> &I, const vpImagePoint &iP, + vpMe* me, double &angle, double &convlt); +vpImagePoint findFirstBorder(const vpImage<unsigned char>& Isub, const vpImagePoint &iP); +bool findCenterPoint(std::list<vpImagePoint> *ip_edges_list); +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS +vp_deprecated bool findCenterPoint(vpList<vpImagePoint> *ip_edges_list); +#endif + //Compute the angle delta = arctan(deltai/deltaj) //and normalize it between 0 and pi double @@ -220,20 +230,19 @@ bool findCenterPoint(std::list<vpImagePoint> *ip_edges_list) /*! Basic constructor that calls the constructor of the class vpMeTracker. */ -vpMeNurbs::vpMeNurbs():vpMeTracker() +vpMeNurbs::vpMeNurbs() + : nurbs(), dist(0.), nbControlPoints(20), beginPtFound(0), endPtFound(0), enableCannyDetection(false), + cannyTh1(100.), cannyTh2(200.) { - nbControlPoints = 20; - beginPtFound = 0; - endPtFound =0; - enableCannyDetection = false; - cannyTh1 = 100.0; - cannyTh2 = 200.0; } /*! Copy constructor. */ -vpMeNurbs::vpMeNurbs(const vpMeNurbs &menurbs):vpMeTracker(menurbs) +vpMeNurbs::vpMeNurbs(const vpMeNurbs &menurbs) + : vpMeTracker(menurbs), + nurbs(), dist(0.), nbControlPoints(20), beginPtFound(0), endPtFound(0), enableCannyDetection(false), + cannyTh1(100.), cannyTh2(200.) { nurbs = menurbs.nurbs; dist = menurbs.dist; @@ -459,7 +468,7 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) //Check if the two extremities are not to close to eachother. double d = vpImagePoint::distance(begin[0],end[0]); double threshold = 3*me->getSampleStep(); - double sample = me->getSampleStep(); + double sample_step = me->getSampleStep(); vpImagePoint pt; if ( d > threshold /*|| (list.firstValue()).mask_sign != (list.lastValue()).mask_sign*/) { @@ -483,8 +492,8 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) si = si * vpMath::sign(begin[1].get_i()); for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat - si*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat - co*sample ; P.j = (int)P.jfloat ; + P.ifloat = P.ifloat - si*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat - co*sample_step ; P.j = (int)P.jfloat ; pt.set_ij(P.ifloat,P.jfloat); if (vpImagePoint::distance(end[0],pt) < threshold) break; if(!outOfImage(P.i, P.j, 5, rows, cols)) @@ -501,9 +510,9 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) } } else { - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, pt, 10, vpColor::blue) ; - } + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, pt, 10, vpColor::blue) ; + } } } } @@ -521,8 +530,8 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) si = si * vpMath::sign(end[1].get_i()); for (int i=0 ; i < 3 ; i++) { - P.ifloat = P.ifloat + si*sample ; P.i = (int)P.ifloat ; - P.jfloat = P.jfloat + co*sample ; P.j = (int)P.jfloat ; + P.ifloat = P.ifloat + si*sample_step ; P.i = (int)P.ifloat ; + P.jfloat = P.jfloat + co*sample_step ; P.j = (int)P.jfloat ; pt.set_ij(P.ifloat,P.jfloat); if (vpImagePoint::distance(begin[0],pt) < threshold) break; if(!outOfImage(P.i, P.j, 5, rows, cols)) @@ -538,9 +547,9 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) } } else { - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, pt, 10, vpColor::blue) ; - } + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, pt, 10, vpColor::blue) ; + } } } } @@ -551,8 +560,8 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) { list.pop_front(); } - if(begin != NULL) delete[] begin; - if(end != NULL) delete[] end; + /*if(begin != NULL)*/ delete[] begin; + /*if(end != NULL) */ delete[] end; } @@ -567,7 +576,7 @@ vpMeNurbs::seekExtremities(const vpImage<unsigned char> &I) \param I : Image in which the edge appears. */ -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) void vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> &I) #else @@ -575,7 +584,7 @@ void vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) #endif { -#ifdef VISP_HAVE_OPENCV +#if (defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) vpMeSite pt = list.front(); vpImagePoint firstPoint(pt.ifloat,pt.jfloat); pt = list.back(); @@ -606,14 +615,17 @@ vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) if( u > 0) lastPtInSubIm = nurbs.computeCurvePoint(u); +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + vpImageFilter::canny(Isub, Isub, 3, cannyTh1, 3); +#else IplImage* Ip = NULL; vpImageConvert::convert(Isub, Ip); - IplImage* dst = cvCreateImage( cvSize((int)Isub.getWidth(), (int)Isub.getHeight()), 8, 1 ); cvCanny( Ip, dst, cannyTh1, cannyTh2, 3 ); vpImageConvert::convert(dst, Isub); +#endif vpImagePoint firstBorder(-1,-1); @@ -709,7 +721,7 @@ vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) me->setRange(memory_range); } - if (begin != NULL) delete[] begin; + /* if (begin != NULL) */ delete[] begin; beginPtFound = 0; } @@ -740,14 +752,17 @@ vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) if( u < 1.0) lastPtInSubIm = nurbs.computeCurvePoint(u); +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + vpImageFilter::canny(Isub, Isub, 3, cannyTh1, 3); +#else IplImage* Ip = NULL; vpImageConvert::convert(Isub, Ip); - IplImage* dst = cvCreateImage( cvSize((int)Isub.getWidth(), (int)Isub.getHeight()), 8, 1 ); cvCanny( Ip, dst, cannyTh1, cannyTh2, 3 ); - + vpImageConvert::convert(dst, Isub); +#endif vpImagePoint firstBorder(-1,-1); @@ -806,6 +821,7 @@ vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) } std::list<vpMeSite>::iterator itList = list.end(); + --itList; // Move on the last element double convlt; double delta; int nbr = 0; @@ -839,17 +855,18 @@ vpMeNurbs::seekExtremitiesCanny(const vpImage<unsigned char> & /* I */) unsigned int memory_range = me->getRange(); me->setRange(3); std::list<vpMeSite>::iterator itList2 = list.end(); + --itList2; // Move to the last element for (int j = 0; j < nbr; j++) { - vpMeSite s = *itList2; - s.track(I,me,false); - *itList2 = s; + vpMeSite me_s = *itList2; + me_s.track(I,me,false); + *itList2 = me_s; --itList2; } me->setRange(memory_range); } - if (end != NULL) delete[] end; + /* if (end != NULL) */ delete[] end; endPtFound = 0; } #else @@ -944,14 +961,14 @@ vpMeNurbs::localReSample(const vpImage<unsigned char> &I) //if(( u != 1.0 || uend != 1.0) if( (std::fabs(u-1.0) > std::fabs(vpMath::maximum(u, 1.0))*std::numeric_limits<double>::epsilon()) - || (std::fabs(uend-1.0) > std::fabs(vpMath::maximum(u, 1.0))*std::numeric_limits<double>::epsilon())) + || (std::fabs(uend-1.0) > std::fabs(vpMath::maximum(uend, 1.0))*std::numeric_limits<double>::epsilon())) { iP = nurbs.computeCurveDersPoint(u, 1); while (vpImagePoint::sqrDistance(iP[0],iPend) > vpMath::sqr(me->getSampleStep()) && u < uend) { u+=0.01; - if (iP!=NULL) { + /*if (iP!=NULL)*/ { delete[] iP; iP = NULL; } @@ -970,7 +987,7 @@ vpMeNurbs::localReSample(const vpImage<unsigned char> &I) } } } - if (iP!=NULL) { + /*if (iP!=NULL)*/ { delete[] iP; iP = NULL; } @@ -1054,7 +1071,7 @@ vpMeNurbs::track(const vpImage<unsigned char> &I) //Suppressions des points ejectes par le tracking suppressPoints(); - //Recalcule les param�tres + //Recalcule les parametres // nurbs.globalCurveInterp(list); nurbs.globalCurveApprox(list,nbControlPoints); diff --git a/src/tracking/moving-edges/vpMeNurbs.h b/src/tracking/moving-edges/vpMeNurbs.h index 737281298bd1fdac3333a6d08848e20b3bfee710..242a99e2ef5febc319e024722d2a8380ad361945 100644 --- a/src/tracking/moving-edges/vpMeNurbs.h +++ b/src/tracking/moving-edges/vpMeNurbs.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeNurbs.h 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpMeNurbs.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -160,16 +160,16 @@ class VISP_EXPORT vpMeNurbs : public vpMeTracker /*! Sets the number of control points used to compute the Nurbs. - \param nbControlPoints : The number of control points used to compute the Nurbs. + \param nb_point : The number of control points used to compute the Nurbs. */ - void setNbControlPoints(const unsigned int nbControlPoints) {this->nbControlPoints = nbControlPoints;} + void setNbControlPoints(const unsigned int nb_point) {this->nbControlPoints = nb_point;} /*! Enables or disables the canny detection used during the extremities search. - \param enableCannyDetection : if true it enables the canny detection. + \param enable_canny : if true it enables the canny detection. */ - void setEnableCannyDetection(const bool enableCannyDetection) {this->enableCannyDetection =enableCannyDetection;} + void setEnableCannyDetection(const bool enable_canny) {this->enableCannyDetection =enable_canny;} /*! Enables to set the two thresholds use by the canny detection. diff --git a/src/tracking/moving-edges/vpMeSite.cpp b/src/tracking/moving-edges/vpMeSite.cpp index 31a8af8823cbabe1e324f05b335850877185dca5..5b87a50f5894464924c69e1d0bdfdaf71c11c4da 100644 --- a/src/tracking/moving-edges/vpMeSite.cpp +++ b/src/tracking/moving-edges/vpMeSite.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeSite.cpp 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpMeSite.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -100,15 +100,21 @@ vpMeSite::init() } vpMeSite::vpMeSite() + : i(0), j(0), i_1(0), j_1(0), ifloat(0), jfloat(0), v(0), mask_sign(1), alpha(0.), + convlt(0.), normGradient(0), weight(1), selectDisplay(NONE), state(NO_SUPPRESSION) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , suppress(0) +#endif { - init() ; } vpMeSite::vpMeSite(double ip, double jp) + : i(0), j(0), i_1(0), j_1(0), ifloat(0), jfloat(0), v(0), mask_sign(1), alpha(0.), + convlt(0.), normGradient(0), weight(1), selectDisplay(NONE), state(NO_SUPPRESSION) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , suppress(0) +#endif { - init() ; - - selectDisplay = NONE ; i = vpMath::round(ip) ; j = vpMath::round(jp) ; ifloat = ip ; @@ -119,6 +125,11 @@ vpMeSite::vpMeSite(double ip, double jp) Copy constructor. */ vpMeSite::vpMeSite (const vpMeSite &mesite) + : i(0), j(0), i_1(0), j_1(0), ifloat(0), jfloat(0), v(0), mask_sign(1), alpha(0.), + convlt(0.), normGradient(0), weight(1), selectDisplay(NONE), state(NO_SUPPRESSION) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , suppress(0) +#endif { *this = mesite; } @@ -497,7 +508,7 @@ vpMeSite::track(const vpImage<unsigned char>& I, int max_rank =-1 ; // int max_rank1=-1 ; // int max_rank2 = -1; - double convolution = 0 ; + double convolution_ = 0 ; double max_convolution = 0 ; double max = 0 ; double contraste = 0; @@ -530,21 +541,21 @@ vpMeSite::track(const vpImage<unsigned char>& I, for(unsigned int n = 0 ; n < 2 * range + 1 ; n++) { // convolution results - convolution = list_query_pixels[n].convolution(I, me) ; + convolution_ = list_query_pixels[n].convolution(I, me) ; // luminance ratio of reference pixel to potential correspondent pixel // the luminance must be similar, hence the ratio value should // lay between, for instance, 0.5 and 1.5 (parameter tolerance) if( test_contraste ) { - likelihood[n] = fabs(convolution + convlt ); + likelihood[n] = fabs(convolution_ + convlt ); if (likelihood[n]> threshold) { - contraste = convolution / convlt; + contraste = convolution_ / convlt; if((contraste > contraste_min) && (contraste < contraste_max) && fabs(1-contraste) < diff) { diff = fabs(1-contraste); - max_convolution= convolution; + max_convolution= convolution_; max = likelihood[n] ; max_rank = (int)n ; // max_rank2 = max_rank1; @@ -555,10 +566,10 @@ vpMeSite::track(const vpImage<unsigned char>& I, else { - likelihood[n] = fabs(2*convolution) ; + likelihood[n] = fabs(2*convolution_) ; if (likelihood[n] > max && likelihood[n] > threshold) { - max_convolution= convolution; + max_convolution= convolution_; max = likelihood[n] ; max_rank = (int)n ; // max_rank2 = max_rank1; @@ -617,18 +628,18 @@ int vpMeSite::operator!=(const vpMeSite &m) } -std::ostream& operator<<(std::ostream& os, vpMeSite& vpMeS) +VISP_EXPORT std::ostream& operator<<(std::ostream& os, vpMeSite& vpMeS) { #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS return (os << "Alpha: " << vpMeS.alpha << " Convolution: " << vpMeS.convlt << " Flag: " << vpMeS.suppress << " Weight: " << vpMeS.weight ); -#endif - +#else return (os << "Alpha: " << vpMeS.alpha << " Convolution: " << vpMeS.convlt << " Weight: " << vpMeS.weight ); +#endif } void vpMeSite::display(const vpImage<unsigned char>& I) @@ -675,6 +686,7 @@ void vpMeSite::display(const vpImage<unsigned char>& I, const double &i, const d case TOO_NEAR: vpDisplay::displayCross(I,vpImagePoint(i,j),3,vpColor::cyan,1); + break; default: vpDisplay::displayCross(I,vpImagePoint(i,j),3,vpColor::yellow,1); @@ -718,6 +730,7 @@ void vpMeSite::display(const vpImage<vpRGBa>& I, const double &i, const double & case TOO_NEAR: vpDisplay::displayCross(I,vpImagePoint(i,j),3,vpColor::cyan,1); + break; default: vpDisplay::displayCross(I,vpImagePoint(i,j),3,vpColor::yellow,1); diff --git a/src/tracking/moving-edges/vpMeSite.h b/src/tracking/moving-edges/vpMeSite.h index d1c70b0ff78b6949fb87293b8a53fb76a8b93542..31c404893f944bdcbb4c3923d6c050d9bea19593 100644 --- a/src/tracking/moving-edges/vpMeSite.h +++ b/src/tracking/moving-edges/vpMeSite.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeSite.h 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpMeSite.h 5060 2014-12-12 18:31:03Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -215,7 +215,7 @@ public: */ inline double getWeight() const { return weight; } -//Opérators +//Operators vpMeSite &operator=(const vpMeSite &m) ; int operator!=(const vpMeSite &m) ; diff --git a/src/tracking/moving-edges/vpMeTracker.cpp b/src/tracking/moving-edges/vpMeTracker.cpp index 1d7c911abe918db69ed01225b5e9df19c60b3846..895f95f8556b3469b5302473e9af6d6797ca53fe 100644 --- a/src/tracking/moving-edges/vpMeTracker.cpp +++ b/src/tracking/moving-edges/vpMeTracker.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpMeTracker.cpp 4303 2013-07-04 14:14:00Z fspindle $ +* $Id: vpMeTracker.cpp 4797 2014-07-23 15:52:28Z fspindle $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,19 +66,20 @@ vpMeTracker::init() } vpMeTracker::vpMeTracker() + : list(), me(NULL), init_range(1), nGoodElement(0), selectDisplay(vpMeSite::NONE) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , query_range (0), display_point(false) +#endif { init(); - me = NULL ; - nGoodElement = 0; - init_range = 1; - - #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS - query_range = 0; - display_point = false ; - #endif } -vpMeTracker::vpMeTracker(const vpMeTracker& meTracker):vpTracker(meTracker) +vpMeTracker::vpMeTracker(const vpMeTracker& meTracker) + : vpTracker(meTracker), + list(), me(NULL), init_range(1), nGoodElement(0), selectDisplay(vpMeSite::NONE) +#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS + , query_range (0), display_point(false) +#endif { init(); @@ -94,17 +95,26 @@ vpMeTracker::vpMeTracker(const vpMeTracker& meTracker):vpTracker(meTracker) #endif } -vpMeTracker::~vpMeTracker() +/*! + Reset the tracker by removing all the moving edges. + */ +void vpMeTracker::reset() { + nGoodElement = 0; list.clear(); } +vpMeTracker::~vpMeTracker() +{ + reset(); +} + vpMeTracker& -vpMeTracker::operator = (vpMeTracker& p) +vpMeTracker::operator = (vpMeTracker& p_me) { - list = p.list; - me = p.me; - selectDisplay = p.selectDisplay ; + list = p_me.list; + me = p_me.me; + selectDisplay = p_me.selectDisplay ; return *this; } @@ -331,8 +341,8 @@ vpMeTracker::display(const vpImage<unsigned char>& I) } #endif for(std::list<vpMeSite>::const_iterator it=list.begin(); it!=list.end(); ++it){ - vpMeSite p = *it; - p.display(I); + vpMeSite p_me = *it; + p_me.display(I); } } diff --git a/src/tracking/moving-edges/vpMeTracker.h b/src/tracking/moving-edges/vpMeTracker.h index de23ef774c9fbabc4fe6d0f683508a9d625e8a6b..f82162207b47f1425720798f8c6e869ce60a4bc6 100644 --- a/src/tracking/moving-edges/vpMeTracker.h +++ b/src/tracking/moving-edges/vpMeTracker.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpMeTracker.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpMeTracker.h 4797 2014-07-23 15:52:28Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -114,6 +114,8 @@ public: int outOfImage( int i , int j , int half , int rows , int cols) ; int outOfImage( vpImagePoint iP , int half , int rows , int cols) ; + void reset(); + //!Sample pixels at a given interval virtual void sample(const vpImage<unsigned char> &image)=0; @@ -135,9 +137,9 @@ public: /*! Set the moving edges initialisation parameters - \param me : Moving Edges. + \param p_me : Moving Edges. */ - void setMe(vpMe *me) { this->me = me ; } + void setMe(vpMe *p_me) { this->me = p_me ; } /*! Return the moving edges initialisation parameters diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.cpp b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5fed1971f4148483f0df3ab6828e95c6bd6b88f --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSD.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ + +#include <visp/vpTemplateTrackerSSD.h> + +vpTemplateTrackerSSD::vpTemplateTrackerSSD(vpTemplateTrackerWarp *warp) + : vpTemplateTracker(warp), DI(), temp() +{ + dW.resize(2,nbParam); + G.resize(nbParam); + H.resize(nbParam,nbParam); + HLM.resize(nbParam,nbParam); + + temp.resize(nbParam); + + X1.resize(2); + X2.resize(2); + DI.resize(2); +} + +double vpTemplateTrackerSSD::getCost(const vpImage<unsigned char> &I,vpColVector &tp) +{ + double erreur=0; + double IW,Tij; + int i,j; + double i2,j2; + int Nbpoint=0; + + Warp->computeCoeff(tp); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,tp); + Warp->warpX(X1,X2,tp); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + //IW=getSubPixBspline4(I,i2,j2); + erreur+=((double)Tij-IW)*((double)Tij-IW); + Nbpoint++; + } + } + ratioPixelIn=(double)Nbpoint/(double)templateSize; + + if(Nbpoint==0)return 10e10; + return erreur/Nbpoint; +} + + +double vpTemplateTrackerSSD::getSSD(vpImage<unsigned char> &I,vpColVector &tp) +{ + double erreur=0; + double IW,Tij; + int i,j; + double i2,j2; + unsigned int Nbpoint=0; + + if(pyrInitialised) + { + templateSize=templateSizePyr[0]; + ptTemplate=ptTemplatePyr[0]; + } + + Warp->computeCoeff(tp); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,tp); + Warp->warpX(X1,X2,tp); + + j2=X2[0];i2=X2[1]; + if((j2<I.getWidth()-1)&&(i2<I.getHeight()-1)&&(i2>0)&&(j2>0)) + { + Tij=ptTemplate[point].val; + IW=I.getValue(i2,j2); + //IW=getSubPixBspline4(I,i2,j2); + erreur+=((double)Tij-IW)*((double)Tij-IW); + Nbpoint++; + } + } + if(Nbpoint==0)return 10e10; + return erreur/Nbpoint; +} diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.h b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.h new file mode 100644 index 0000000000000000000000000000000000000000..462066a4c76a0daebde6070b5fa0ecc57676bff0 --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSD.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSD.h 4956 2014-11-12 15:50:23Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerSSD.h + \brief +*/ + +#ifndef vpTemplateTrackerSSD_hh +#define vpTemplateTrackerSSD_hh + +#include <math.h> + +#include <visp/vpTemplateTracker.h> +#include <visp/vpImage.h> +#include <visp/vpDisplay.h> +#include <visp/vpImageTools.h> +#include <visp/vpImageIo.h> +#include <visp/vpIoTools.h> +#include <visp/vpImageTools.h> +#include <visp/vpImageFilter.h> +#include <visp/vpMath.h> +#include <visp/vpHomography.h> + +class VISP_EXPORT vpTemplateTrackerSSD: public vpTemplateTracker +{ + protected: + vpRowVector DI; + vpRowVector temp; + + protected: + double getCost(const vpImage<unsigned char> &I, vpColVector &tp); + double getCost(const vpImage<unsigned char> &I){ return getCost(I,p); } + virtual void initHessienDesired(const vpImage<unsigned char> &I) = 0; + virtual void trackNoPyr(const vpImage<unsigned char> &I) = 0; + + public: + vpTemplateTrackerSSD(vpTemplateTrackerWarp *warp); + + double getSSD(vpImage<unsigned char> &I,vpColVector &tp); + void setGain(double g){ gain=g; } +}; + +#endif + diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.cpp b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7865c9d88c6247b79dd1a76253bea110669f3b05 --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDESM.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerSSDESM.h> +#include <visp/vpImageFilter.h> + +vpTemplateTrackerSSDESM::vpTemplateTrackerSSDESM(vpTemplateTrackerWarp *warp) + : vpTemplateTrackerSSD(warp), compoInitialised(false), HDir(), HInv(), + HLMDir(), HLMInv(), GDir(), GInv() +{ + useCompositionnal=false; + useInverse=false; + + if(!Warp->isESMcompatible()) + std::cerr<<"The selected warp function is not appropriate for the ESM algorithm..."<<std::endl; + + HInv.resize(nbParam,nbParam); + HDir.resize(nbParam,nbParam); + HLMInv.resize(nbParam,nbParam); + HLMDir.resize(nbParam,nbParam); + GInv.resize(nbParam); + GDir.resize(nbParam); +} + +void vpTemplateTrackerSSDESM::initHessienDesired(const vpImage<unsigned char> &I) +{ + initCompInverse(I); +} + +void vpTemplateTrackerSSDESM::initCompInverse(const vpImage<unsigned char> &/*I*/) +{ + //std::cout<<"Initialise precomputed value of ESM with templateSize: "<< templateSize<<std::endl; + ptTemplateCompo=new vpTemplateTrackerPointCompo[templateSize]; + int i,j; + //direct + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + ptTemplateCompo[point].dW=new double[2*nbParam]; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + Warp->getdWdp0(i,j,ptTemplateCompo[point].dW); + + } + + //inverse + HInv=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + ptTemplate[point].dW=new double[nbParam]; + Warp->getdW0(i,j,ptTemplate[point].dy,ptTemplate[point].dx,ptTemplate[point].dW); + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + HInv[it][jt]+=ptTemplate[point].dW[it]*ptTemplate[point].dW[jt]; + } + vpMatrix::computeHLM(HInv,lambdaDep,HLMInv); + +compoInitialised=true; +} + +void vpTemplateTrackerSSDESM::trackNoPyr(const vpImage<unsigned char> &I) +{ + double erreur=0; + unsigned int Nbpoint=0; + + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + double IW,dIWx,dIWy; + double Tij; + unsigned int iteration=0; + int i,j; + double i2,j2; + double alpha=2.; + do + { + Nbpoint=0; + erreur=0; + dp=0; + HDir=0; + GDir=0; + GInv=0; + Warp->computeCoeff(p); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + //INVERSE + Tij=ptTemplate[point].val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + Nbpoint++; + double er=(Tij-IW); + for(unsigned int it=0;it<nbParam;it++) + GInv[it]+=er*ptTemplate[point].dW[it]; + + erreur+=er*er; + + //DIRECT + //dIWx=dIx.getValue(i2,j2); + //dIWy=dIy.getValue(i2,j2); + + dIWx=dIx.getValue(i2,j2)+ptTemplate[point].dx; + dIWy=dIy.getValue(i2,j2)+ptTemplate[point].dy; + + //Calcul du Hessien + //Warp->dWarp(X1,X2,p,dW); + Warp->dWarpCompo(X1,X2,p,ptTemplateCompo[point].dW,dW); + + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIWx+dW[1][it]*dIWy; + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + HDir[it][jt]+=tempt[it]*tempt[jt]; + + for(unsigned int it=0;it<nbParam;it++) + GDir[it]+=er*tempt[it]; + delete[] tempt; + } + + + } + if(Nbpoint==0) { + //std::cout<<"plus de point dans template suivi"<<std::endl; + throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No points in the template")); + } + + vpMatrix::computeHLM(HDir,lambdaDep,HLMDir); + + try + { + //dp=(HLMInv+HLMDir).inverseByLU()*(GInv+GDir); + //dp=HLMInv.inverseByLU()*GInv+HLMDir.inverseByLU()*GDir; + //dp=HLMInv.inverseByLU()*GInv; + dp=(HLMDir).inverseByLU()*(GDir); + } + catch(vpException &e) + { + //std::cout<<"probleme inversion"<<std::endl; + throw(e); + } + + dp=gain*dp; + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur/Nbpoint,dp,alpha); + dp=alpha*dp; + } + + //Warp->pRondp(p,dp,p); + p+=dp; + iteration++; + + } + while( (iteration < iterationMax)); + + nbIteration=iteration; +} + + +/*void vpTemplateTrackerSSDESM::InitCompInverse(vpImage<unsigned char> &I) +{ + ptTempateCompo=new vpTemplateTrackerPointCompo[taille_template]; + int i,j; + for(int point=0;point<taille_template;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + ptTempateCompo[point].dW=new double[2*nbParam]; + Warp->getdWdp0(i,j,ptTempateCompo[point].dW); + } + +} + +void vpTemplateTrackerSSDESM::track(vpImage<unsigned char> &I) +{ + double erreur=0,erreur_prec=1e38; + int Nbpoint=0; + + int taillefiltre=taille_filtre_dgaussien; + double *fg=new double[taillefiltre+1] ; + getGaussianDerivativeKernel(fg, taillefiltre) ; + getGradX(I, dIx,fg,taillefiltre); + getGradY(I, dIy,fg,taillefiltre); + delete[] fg; + + vpColVector dpinv(nbParam); + double lambda=lambdaDep; + double IW,dIWx,dIWy; + double Tij; + int iteration=0; + int i,j; + double i2,j2; + vpTemplateTrackerPoint *pt; + do + { + Nbpoint=0; + erreur_prec=erreur; + erreur=0; + dp=0; + HDir=0; + GDir=0; + GInv=0; + Warp->ComputeCoeff(p); + for(int point=0;point<taille_template;point++) + { + pt=&ptTemplate[point]; + i=pt->y; + j=pt->x; + X1[0]=j;X1[1]=i; + + Warp->ComputeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((j2<I.getWidth())&&(i2<I.getHeight())&&(i2>0)&&(j2>0)) + { + //INVERSE + Tij=pt->val; + IW=I.getPixelBI(j2,i2); + Nbpoint++; + double er=(Tij-IW); + for(int it=0;it<nbParam;it++) + GInv[it]+=er*ptTemplate[point].dW[it]; + + erreur+=er*er; + + //DIRECT COMPO + Tij=ptTemplate[point].val; + IW=I.getPixelBI(j2,i2); + dIWx=dIx.getPixelBI(j2,i2); + dIWy=dIy.getPixelBI(j2,i2); + Nbpoint++; + Warp->dWarpCompo(X1,X2,p,ptTempateCompo[point].dW,dW); + double *tempt=new double[nbParam]; + for(int it=0;it<nbParam;it++) + tempt[it] =dW[0][it]*dIWx+dW[1][it]*dIWy; + + for(int it=0;it<nbParam;it++) + for(int jt=0;jt<nbParam;jt++) + HDir[it][jt]+=tempt[it]*tempt[jt]; + + for(int it=0;it<nbParam;it++) + GDir[it]+=er*tempt[it]; + + delete[] tempt; + } + + + } + if(Nbpoint==0)std::cout<<"plus de point dans template suivi"<<std::endl; + try + { + dp=(HInv+HDir).inverseByLU()*(GInv+GDir); + } + catch(...) + { + std::cout<<"probleme inversion"<<std::endl; + break; + } + + if(Compare)write_infos(p,erreur/Nbpoint); + + p+=Gain*dp; + iteration++; + + } + while( (iteration < IterationMax)); + + NbIteration=iteration; +}*/ + diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.h b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.h new file mode 100644 index 0000000000000000000000000000000000000000..3ab98a1dddf8cc83327b3cb2375f4acb179f217f --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDESM.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDESM.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerSSDESM.h + \brief +*/ + +#ifndef vpTemplateTrackerSSDESM_hh +#define vpTemplateTrackerSSDESM_hh + +#include <visp/vpTemplateTrackerSSD.h> + +class VISP_EXPORT vpTemplateTrackerSSDESM: public vpTemplateTrackerSSD +{ + protected: + bool compoInitialised; + vpMatrix HDir; + vpMatrix HInv; + vpMatrix HLMDir; + vpMatrix HLMInv; + vpColVector GDir; + vpColVector GInv; + + protected: + void initHessienDesired(const vpImage<unsigned char> &I); + void initCompInverse(const vpImage<unsigned char> &I); + void trackNoPyr(const vpImage<unsigned char> &I); + + public: + vpTemplateTrackerSSDESM(vpTemplateTrackerWarp *warp); +}; +#endif diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.cpp b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90480f1119052715c7b0080852ef168480762289 --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDForwardAdditional.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ + +#include <limits> // numeric_limits + +#include <visp/vpTemplateTrackerSSDForwardAdditional.h> +#include <visp/vpImageTools.h> + +vpTemplateTrackerSSDForwardAdditional::vpTemplateTrackerSSDForwardAdditional(vpTemplateTrackerWarp *warp) + : vpTemplateTrackerSSD(warp), minimizationMethod(USE_NEWTON), p_prec(), G_prec(), KQuasiNewton() +{ + useCompositionnal=false; +} + +void vpTemplateTrackerSSDForwardAdditional::trackNoPyr(const vpImage<unsigned char> &I) +{ + double erreur=0; + unsigned int Nbpoint=0; + + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + dW=0; + + double lambda=lambdaDep; + double IW,dIWx,dIWy; + double Tij; + unsigned int iteration=0; + int i,j; + double i2,j2; + double alpha=2.; + do + { + Nbpoint=0; + erreur=0; + G=0; + H=0 ; + Warp->computeCoeff(p); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + + dIWx=dIx.getValue(i2,j2); + dIWy=dIy.getValue(i2,j2); + Nbpoint++; + //Calcul du Hessien + Warp->dWarp(X1,X2,p,dW); + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIWx+dW[1][it]*dIWy; + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + H[it][jt]+=tempt[it]*tempt[jt]; + + double er=(Tij-IW); + for(unsigned int it=0;it<nbParam;it++) + G[it]+=er*tempt[it]; + + erreur+=(er*er); + delete[] tempt; + } + + + } + if(Nbpoint==0) { + //std::cout<<"plus de point dans template suivi"<<std::endl; + throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No points in the template")); + } + + vpMatrix::computeHLM(H,lambda,HLM); + try + { + dp=1.*HLM.inverseByLU()*G; + } + catch(vpException &e) + { + throw(e); + } + + switch(minimizationMethod) + { + case vpTemplateTrackerSSDForwardAdditional::USE_LMA: + { + vpColVector p_test_LMA(nbParam); + p_test_LMA=p+1.*dp; + erreur=-getCost(I,p); + double erreur_LMA=-getCost(I,p_test_LMA); + if(erreur_LMA<erreur) + { + p=p_test_LMA; + lambda=(lambda/10.<1e-6)?lambda/10.:1e-6; + } + else + { + lambda=(lambda*10.<1e6)?1e6:lambda*10.; + } + } + break; + case vpTemplateTrackerSSDForwardAdditional::USE_GRADIENT: + { + dp=gain*0.000001*G; + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur,dp,alpha); + dp=alpha*dp; + } + p+=1.*dp; + break; + } + + case vpTemplateTrackerSSDForwardAdditional::USE_QUASINEWTON: + { + double s_scal_y; + if(iterationGlobale!=0) + { + vpColVector s_quasi=p-p_prec; + vpColVector y_quasi=G-G_prec; + s_scal_y=s_quasi.t()*y_quasi; + //if(s_scal_y!=0)//BFGS + // KQuasiNewton=KQuasiNewton-(s_quasi*y_quasi.t()*KQuasiNewton+KQuasiNewton*y_quasi*s_quasi.t())/s_scal_y+(1.+y_quasi.t()*(KQuasiNewton*y_quasi)/s_scal_y)*s_quasi*s_quasi.t()/s_scal_y; + //if(s_scal_y!=0.0)//DFP + if(std::fabs(s_scal_y) > std::numeric_limits<double>::epsilon()) //DFP + KQuasiNewton=KQuasiNewton+0.001*(s_quasi*s_quasi.t()/s_scal_y-KQuasiNewton*y_quasi*y_quasi.t()*KQuasiNewton/(y_quasi.t()*KQuasiNewton*y_quasi)); + } + dp=-KQuasiNewton*G; + p_prec=p; + G_prec=G; + p-=1.01*dp; + } + break; + + case vpTemplateTrackerSSDForwardAdditional::USE_NEWTON: + default: + { + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur,dp,alpha); + dp=alpha*dp; + } + + p+=1.*dp; + break; + } + } + + iteration++; + iterationGlobale++; + } + while( /*( erreur_prec-erreur<50) && */(iteration < iterationMax)); + + //std::cout<<"erreur "<<erreur<<std::endl; + nbIteration=iteration; +} + diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.h b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.h new file mode 100644 index 0000000000000000000000000000000000000000..4d57db07f1edb662695870af441cd981c81c3c3e --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardAdditional.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDForwardAdditional.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerSSDForwardAdditional.h + \brief +*/ + +#ifndef vpTemplateTrackerSSDForwardAdditional_hh +#define vpTemplateTrackerSSDForwardAdditional_hh + +#include <visp/vpTemplateTrackerSSD.h> + +/*! + The algorithm implemented in this class is described in \cite Baker04a. + */ +class VISP_EXPORT vpTemplateTrackerSSDForwardAdditional: public vpTemplateTrackerSSD +{ + public: + /*! Minimization method. */ + typedef enum { + USE_NEWTON, + USE_LMA, + USE_GRADIENT, + USE_QUASINEWTON + } vpMinimizationTypeSSDForwardAdditional; + + private: + vpMinimizationTypeSSDForwardAdditional minimizationMethod; + //valeur pour calculer Quasi_Newton + vpColVector p_prec; + vpColVector G_prec; + vpMatrix KQuasiNewton; + + protected: + void initHessienDesired(const vpImage<unsigned char> &/*I*/){} + void trackNoPyr(const vpImage<unsigned char> &I); + + public: + vpTemplateTrackerSSDForwardAdditional(vpTemplateTrackerWarp *warp); + + void setMinimizationMethod(vpMinimizationTypeSSDForwardAdditional method){minimizationMethod=method;} +}; +#endif + diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.cpp b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3e605ed068f26dd6c68007a1c95fbd2abb70658 --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDForwardCompositional.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerSSDForwardCompositional.h> +#include <visp/vpImageFilter.h> + +vpTemplateTrackerSSDForwardCompositional::vpTemplateTrackerSSDForwardCompositional(vpTemplateTrackerWarp *warp) + : vpTemplateTrackerSSD(warp), compoInitialised(false) +{ +} + +void vpTemplateTrackerSSDForwardCompositional::initCompo(const vpImage<unsigned char> &/*I*/) +{ + // std::cout<<"Initialise precomputed value of Compositionnal Direct"<<std::endl; + int i,j; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + ptTemplate[point].dW=new double[2*nbParam]; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + Warp->getdWdp0(i,j,ptTemplate[point].dW); + + } + compoInitialised=true; +} + +void vpTemplateTrackerSSDForwardCompositional::initHessienDesired(const vpImage<unsigned char> &I) +{ + initCompo(I); +} + +void vpTemplateTrackerSSDForwardCompositional::trackNoPyr(const vpImage<unsigned char> &I) +{ + if(!compoInitialised) + std::cout<<"Compositionnal tracking no initialised\nUse InitCompo(vpImage<unsigned char> &I) function"<<std::endl; + double erreur=0; + unsigned int Nbpoint=0; + + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + dW=0; + + double lambda=lambdaDep; + double IW,dIWx,dIWy; + double Tij; + unsigned int iteration=0; + int i,j; + double i2,j2; + double alpha=2.; + do + { + Nbpoint=0; + erreur=0; + G=0; + H=0 ; + Warp->computeCoeff(p); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + dIWx=dIx.getValue(i2,j2); + dIWy=dIy.getValue(i2,j2); + Nbpoint++; + //Calcul du Hessien + /*Warp->dWarp(X1,X2,p,dW); + double *tempt=new double[nbParam]; + for(int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIWx+dW[1][it]*dIWy;*/ + + Warp->dWarpCompo(X1,X2,p,ptTemplate[point].dW,dW); + + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it] =dW[0][it]*dIWx+dW[1][it]*dIWy; + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + H[it][jt]+=tempt[it]*tempt[jt]; + + double er=(Tij-IW); + for(unsigned int it=0;it<nbParam;it++) + G[it]+=er*tempt[it]; + + erreur+=(er*er); + delete[] tempt; + } + + + } + if(Nbpoint==0) { + //std::cout<<"plus de point dans template suivi"<<std::endl; + throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No points in the template")); + } + + vpMatrix::computeHLM(H,lambda,HLM); + + try + { + dp=1.*HLM.inverseByLU()*G; + } + catch(vpException &e) + { + //std::cout<<"probleme inversion"<<std::endl; + throw(e); + } + + dp=gain*dp; + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur/Nbpoint,dp,alpha); + dp=alpha*dp; + } + Warp->pRondp(p,dp,p); + //p+=Gain*dp; + iteration++; + } + while( /*( erreur_prec-erreur<50) &&*/ (iteration < iterationMax)); + + nbIteration=iteration; +} + diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.h b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.h new file mode 100644 index 0000000000000000000000000000000000000000..76134fe7ce8f1849abdef8d101915af7903cb66a --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDForwardCompositional.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDForwardCompositional.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerSSDForwardCompositional.h + \brief +*/ + +#ifndef vpTemplateTrackerSSDForwardCompositional_hh +#define vpTemplateTrackerSSDForwardCompositional_hh + +#include <visp/vpTemplateTrackerSSD.h> + +/*! + The algorithm implemented in this class is described in \cite Baker04a. + */ +class VISP_EXPORT vpTemplateTrackerSSDForwardCompositional: public vpTemplateTrackerSSD +{ + protected: + bool compoInitialised; + + protected: + void initHessienDesired(const vpImage<unsigned char> &I); + void initCompo(const vpImage<unsigned char> &I); + void trackNoPyr(const vpImage<unsigned char> &I); + + public: + vpTemplateTrackerSSDForwardCompositional(vpTemplateTrackerWarp *warp); +}; +#endif diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.cpp b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd955a52c2e90de3073657ba218401106140c3d1 --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDInverseCompositional.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerSSDInverseCompositional.h> +#include <visp/vpImageTools.h> + +vpTemplateTrackerSSDInverseCompositional::vpTemplateTrackerSSDInverseCompositional(vpTemplateTrackerWarp *warp) + : vpTemplateTrackerSSD(warp), compoInitialised(false), HInv(), HCompInverse(), useTemplateSelect(false), + evolRMS(0), x_pos(), y_pos(), threshold_RMS(1e-8) +{ + useInverse=true; + HInv.resize(nbParam,nbParam); + HCompInverse.resize(nbParam,nbParam); +} + +void vpTemplateTrackerSSDInverseCompositional::initCompInverse(const vpImage<unsigned char> &/*I*/) +{ + + H=0; + int i,j; + + for(unsigned int point=0;point<templateSize;point++) + { + if((!useTemplateSelect)||(ptTemplateSelect[point])) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + ptTemplate[point].dW=new double[nbParam]; + + Warp->getdW0(i,j,ptTemplate[point].dy,ptTemplate[point].dx,ptTemplate[point].dW); + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + H[it][jt]+=ptTemplate[point].dW[it]*ptTemplate[point].dW[jt]; + } + + } + HInv=H; + vpMatrix HLMtemp(nbParam,nbParam); + vpMatrix::computeHLM(H,lambdaDep,HLMtemp); + + HCompInverse.resize(nbParam,nbParam); + HCompInverse=HLMtemp.inverseByLU(); + //std::cout<<Hinverse<<std::endl; + vpColVector dWtemp(nbParam); + vpColVector HiGtemp(nbParam); + + for(unsigned int point=0;point<templateSize;point++) + { + if((!useTemplateSelect)||(ptTemplateSelect[point])) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + for(unsigned int it=0;it<nbParam;it++) + dWtemp[it]=ptTemplate[point].dW[it]; + + HiGtemp = -1.*HCompInverse*dWtemp; + ptTemplate[point].HiG=new double[nbParam]; + + for(unsigned int it=0;it<nbParam;it++) + ptTemplate[point].HiG[it]=HiGtemp[it]; + } + } + compoInitialised=true; +} + +void vpTemplateTrackerSSDInverseCompositional::initHessienDesired(const vpImage<unsigned char> &I) +{ + initCompInverse(I); +} + +void vpTemplateTrackerSSDInverseCompositional::trackNoPyr(const vpImage<unsigned char> &I) +{ + double erreur=0; + unsigned int Nbpoint=0; + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + + vpColVector dpinv(nbParam); + double IW; + double Tij; + unsigned int iteration=0; + int i,j; + double i2,j2; + double alpha=2.; + //vpTemplateTrackerPointtest *pt; + initPosEvalRMS(p); + + vpTemplateTrackerPoint *pt; + do + { + Nbpoint=0; + erreur=0; + dp=0; + Warp->computeCoeff(p); + for(unsigned int point=0;point<templateSize;point++) + { + if((!useTemplateSelect)||(ptTemplateSelect[point])) + { + //pt=&ptTemplatetest[point]; + pt=&ptTemplate[point]; + i=pt->y; + j=pt->x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + j2=X2[0];i2=X2[1]; + + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=pt->val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + Nbpoint++; + double er=(Tij-IW); + for(unsigned int it=0;it<nbParam;it++) + dp[it]+=er*pt->HiG[it]; + + erreur+=er*er; + } + } + } + //std::cout << "npoint: " << Nbpoint << std::endl; + if(Nbpoint==0) { + //std::cout<<"plus de point dans template suivi"<<std::endl; + deletePosEvalRMS(); + throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No points in the template")); + } + dp=gain*dp; + //std::cout<<erreur/Nbpoint<<","<<GetCost(I,p)<<std::endl; + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur/Nbpoint,dp,alpha); + dp=alpha*dp; + } + Warp->getParamInverse(dp,dpinv); + Warp->pRondp(p,dpinv,p); + iteration++; + + computeEvalRMS(p); + //std::cout << "iteration: " << iteration << " max: " << iterationMax << std::endl; + //std::cout << "evolRMS: " << evolRMS << " threshold: " << threshold_RMS << std::endl; + } + while(/*( erreur_prec-erreur<50) &&*/ (iteration < iterationMax)&&(evolRMS>threshold_RMS)); + + nbIteration=iteration; + deletePosEvalRMS(); +} + +void vpTemplateTrackerSSDInverseCompositional::initPosEvalRMS(vpColVector &p_) +{ + unsigned int nb_corners = zoneTracked->getNbTriangle() * 3; + x_pos.resize(nb_corners); + y_pos.resize(nb_corners); + + Warp->computeCoeff(p_); + vpTemplateTrackerTriangle triangle; + + for(unsigned int i=0;i<zoneTracked->getNbTriangle();i++) + { + zoneTracked->getTriangle(i, triangle); + for (unsigned int j=0; j<3; j++) { + triangle.getCorner(j, X1[0], X1[1]); + + Warp->computeDenom(X1,p_); + Warp->warpX(X1,X2,p_); + x_pos[i*3+j]=X2[0]; + y_pos[i*3+j]=X2[1]; + } + } +} + +void vpTemplateTrackerSSDInverseCompositional::computeEvalRMS(const vpColVector &p_) +{ + unsigned int nb_corners = zoneTracked->getNbTriangle() * 3; + + Warp->computeCoeff(p_); + evolRMS=0; + vpTemplateTrackerTriangle triangle; + + for(unsigned int i=0;i<zoneTracked->getNbTriangle();i++) + { + zoneTracked->getTriangle(i, triangle); + for (unsigned int j=0; j<3; j++) { + triangle.getCorner(j, X1[0], X1[1]); + + Warp->computeDenom(X1,p_); + Warp->warpX(X1,X2,p_); + evolRMS+=(x_pos[i*3+j]-X2[0])*(x_pos[i*3+j]-X2[0])+(y_pos[i*3+j]-X2[1])*(y_pos[i*3+j]-X2[1]); + x_pos[i*3+j]=X2[0]; + y_pos[i*3+j]=X2[1]; + } + } + evolRMS=evolRMS/nb_corners; +} + +void vpTemplateTrackerSSDInverseCompositional::deletePosEvalRMS() +{ +} diff --git a/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.h b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.h new file mode 100644 index 0000000000000000000000000000000000000000..c2c39e187a3c3bc12fdf10bac657a23d08b60d4c --- /dev/null +++ b/src/tracking/template-tracker/ssd/vpTemplateTrackerSSDInverseCompositional.h @@ -0,0 +1,85 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerSSDInverseCompositional.h 4672 2014-02-17 09:01:17Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerSSDInverseCompositional.h + \brief +*/ +#ifndef vpTemplateTrackerSSDInverseCompositional_hh +#define vpTemplateTrackerSSDInverseCompositional_hh + +#include <vector> + +#include <visp/vpTemplateTrackerSSD.h> + +/*! + The algorithm implemented in this class is described in \cite Baker04a. + */ +class VISP_EXPORT vpTemplateTrackerSSDInverseCompositional: public vpTemplateTrackerSSD +{ + protected: + bool compoInitialised; + vpMatrix HInv; + vpMatrix HCompInverse; + bool useTemplateSelect;//use only the strong gradient pixels to compute the Jabocian + //pour eval evolRMS + double evolRMS; + std::vector<double> x_pos; + std::vector<double> y_pos; + double threshold_RMS; + + protected: + void initHessienDesired(const vpImage<unsigned char> &I); + void initCompInverse(const vpImage<unsigned char> &I); + void trackNoPyr(const vpImage<unsigned char> &I); + void deletePosEvalRMS(); + void computeEvalRMS(const vpColVector &p); + void initPosEvalRMS(vpColVector &p); + + public: + vpTemplateTrackerSSDInverseCompositional(vpTemplateTrackerWarp *warp); + + /*! Use only the strong gradient pixels to compute the Jabobian. By default this feature is disabled. */ + void setUseTemplateSelect(bool b) {useTemplateSelect = b;} + void setThresholdRMS(double threshold){threshold_RMS=threshold;} +}; +#endif + diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.cpp b/src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e3c33643f29955b034cde4642632a2fcf5fe630 --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerBSpline.cpp 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerBSpline.h> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +double vpTemplateTrackerBSpline::getSubPixBspline4(const vpImage<double> &I, double r, double t) +{ + double res=0; + int cr=(int)(r); + int ct=(int)(t); + double er=(double)r-cr; + double et=(double)t-ct; + int height=(int)I.getHeight();//r + int width=(int)I.getWidth();//t + int tr,tt; + for(int ir=-1;ir<=2;ir++) + { + tr=ir+cr; + for(int it=-1;it<=2;it++) + { + tt=it+ct; + if(tr>=0 && tr <height && tt>=0 && tt <width) + res+=Bspline4((double)ir-er)*Bspline4((double)it-et)*I[tr][tt]; + } + } + return res; +} + +double vpTemplateTrackerBSpline::Bspline4(double diff) +{ + //double result; + double aDiff=vpMath::abs(diff); + if(aDiff<1.) + return (aDiff*aDiff*aDiff/2.-aDiff*aDiff+4./6.); + //return (0.5*(1.-aDiff)*(1.-aDiff)*(1.-aDiff)+0.5*(1.-aDiff)*(1.-aDiff)-0.5*(1.-aDiff)+1./6.); + else if(aDiff<2.) + return ((2.-aDiff)*(2.-aDiff)*(2.-aDiff)/6.); + else + return 0; +} + +#endif diff --git a/example/manual/image-manipulation/manGrab1394-1.cpp b/src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.h similarity index 51% rename from example/manual/image-manipulation/manGrab1394-1.cpp rename to src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.h index ae0f2615e02a843bdeb140ad521b79c6ff5b4474..7a372d30bab574dcc2e51718f6a35116855538fa 100644 --- a/example/manual/image-manipulation/manGrab1394-1.cpp +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerBSpline.h @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: manGrab1394-1.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpTemplateTrackerBSpline.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. @@ -12,11 +12,11 @@ * distribution for additional information about the GNU GPL. * * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional + * GPL, please contact INRIA about acquiring a ViSP Professional * Edition License. * * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * + * * This software was developed at: * INRIA Rennes - Bretagne Atlantique * Campus Universitaire de Beaulieu @@ -26,67 +26,39 @@ * * If you have questions regarding the use of this file, please contact * INRIA at visp@inria.fr - * + * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * * Description: - * Images grabbing example. + * Template tracker. * * Authors: - * Anthony Saunier + * Amaury Dame + * Aurelien Yol * Fabien Spindler * *****************************************************************************/ /*! - \file manGrab1394-1.cpp - - \brief Images grabbing example with the vp1394Grabber class. - - */ -/*! - \example manGrab1394-1.cpp + \file vpTemplateTrackerBSpline.h + \brief +*/ - \brief Images grabbing example with the vp1394Grabber class. - - */ +#ifndef vpTemplateTrackerBSpline_hh +#define vpTemplateTrackerBSpline_hh #include <visp/vpConfig.h> - -#include <stdlib.h> -#include <stdio.h> #include <visp/vpImage.h> -#include <visp/vp1394Grabber.h> +#include <visp/vpMath.h> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS -int main() +class VISP_EXPORT vpTemplateTrackerBSpline { -#ifdef VISP_HAVE_DC1394_1 - unsigned int ncameras; // Number of cameras on the bus - vp1394Grabber g; - g.getNumCameras(ncameras); - vpImage<unsigned char> *I = new vpImage<unsigned char> [ncameras]; - - // If the first camera supports MODE_640x480_YUV422 video mode - g.setCamera(0); - g.setFormat(FORMAT_VGA_NONCOMPRESSED); - g.setMode(MODE_640x480_YUV422); - - // If all cameras support 30 fps acquisition - for (unsigned int camera=0; camera < ncameras; camera ++) { - g.setCamera(camera); - g.setFramerate(FRAMERATE_30); - } - - for ( ; ; ) { - for (unsigned int camera=0; camera < ncameras; camera ++) { - // Acquire successively images from the different cameras - g.setCamera(camera); - g.acquire(I[camera]); - } - } - delete [] I; -#endif +public: + static double Bspline4(double diff); - return 0; -} + static double getSubPixBspline4(const vpImage<double> &I, double r, double t); +}; +#endif +#endif diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerHeader.h b/src/tracking/template-tracker/tools/vpTemplateTrackerHeader.h new file mode 100644 index 0000000000000000000000000000000000000000..339ff9260548399c038d294952aa1bf49120ddf2 --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerHeader.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerHeader.h 4632 2014-02-03 17:06:40Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerHeader.h + \brief +*/ + +#ifndef vpTemplateTrackerHeader_hh +#define vpTemplateTrackerHeader_hh + +#include <stdio.h> + +struct vpTemplateTrackerZPoint { + int x,y; + + vpTemplateTrackerZPoint() : x(0), y(0) {} +}; +struct vpTemplateTrackerDPoint { + double x,y; + + vpTemplateTrackerDPoint() : x(0), y(0) {} +}; +struct vpTemplateTrackerPoint { + int x,y; + double dx,dy; + double val; + double *dW; + double *HiG; + + vpTemplateTrackerPoint() : x(0), y(0), dx(0), dy(0), val(0), dW(NULL), HiG(NULL) {} +}; +struct vpTemplateTrackerPointCompo { + double *dW; + vpTemplateTrackerPointCompo() : dW(NULL) {} +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +struct vpTemplateTrackerPointSuppMIInv { + double et; + int ct; + double *BtInit; + double *Bt; + double *dBt; + double *d2W; + double *d2Wx; + double *d2Wy; + vpTemplateTrackerPointSuppMIInv() : et(0), ct(0), BtInit(NULL), Bt(NULL), dBt(NULL), + d2W(NULL), d2Wx(NULL), d2Wy(NULL) {} +}; +#endif // DOXYGEN_SHOULD_SKIP_THIS + +#endif diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.cpp b/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10c6f8f47da7b67147fb511e8a56e5c0980a1a9d --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerTriangle.cpp 5060 2014-12-12 18:31:03Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerTriangle.h> + +/*! + Default constructor. + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle() + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ +} + +/*! + Copy constructor. + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle(const vpTemplateTrackerTriangle& T) + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ + *this = T; +} + +/*! + Copy operator. + */ +vpTemplateTrackerTriangle & vpTemplateTrackerTriangle::operator=(const vpTemplateTrackerTriangle& T) +{ + minx_temp=T.minx_temp; + miny_temp=T.miny_temp; + + l_t=T.l_t; + h_t=T.h_t; + C1.x=T.C1.x; + C1.y=T.C1.y; + C2.x=T.C2.x; + C2.y=T.C2.y; + C3.x=T.C3.x; + C3.y=T.C3.y; + //uvinv.resize(2,2); + //uvinv=T.uvinv; + //p_ds_uv.resize(2); + //p_ds_uv=T.p_ds_uv; + //ptempo.resize(2); + //ptempo=T.ptempo; + not_good=T.not_good; + + uvinv00=T.uvinv00; + uvinv01=T.uvinv01; + uvinv10=T.uvinv10; + uvinv11=T.uvinv11; + + marge_triangle = T.marge_triangle; + area = T.area; + + return (*this); +} + +/*! + Create a triangle from 3 corners. + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + + The coordinates of the points are defined as a 2 dimension vector with coordinates (x,y). + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle(const vpColVector &c1, + const vpColVector &c2, + const vpColVector &c3) + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ + init(c1[0],c1[1],c2[0],c2[1],c3[0],c3[1]); +} +/*! + Return a triangle with coordinates that are down scaled by a factor 2. + */ +vpTemplateTrackerTriangle vpTemplateTrackerTriangle::getPyramidDown() const +{ + vpTemplateTrackerTriangle Ttemp; + Ttemp.init(C1.x/2.,C1.y/2.,C2.x/2.,C2.y/2.,C3.x/2.,C3.y/2.); + return Ttemp; +} + +/*! + Create a triangle from 3 corners with coordinates (x1,y1), (x2,y2), (x3,y3). + - x coordinate is along the columns + - y coordinate is along the rows. + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle(int x1,int y1, int x2,int y2, int x3,int y3) + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ + init(x1,y1,x2,y2,x3,y3); +} + +/*! + Create a triangle from 3 corners defined as image points. + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle(const vpImagePoint &c1, const vpImagePoint &c2, const vpImagePoint &c3) + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ + init(c1.get_u(), c1.get_v(), c2.get_u(), c2.get_v(), c3.get_u(), c3.get_v()); +} + +/*! + Create a triangle from 3 corners with coordinates (x1,y1), (x2,y2), (x3,y3). + - x coordinate is along the columns + - y coordinate is along the rows. + */ +vpTemplateTrackerTriangle::vpTemplateTrackerTriangle(double x1,double y1, double x2,double y2, double x3,double y3) + : minx_temp(0), miny_temp(0), C1(), C2(), C3(), l_t(0), h_t(0), not_good(false), + uvinv00(0.), uvinv01(0.), uvinv10(0.), uvinv11(0.), marge_triangle(0.00001), area(0) +{ + init(x1,y1,x2,y2,x3,y3); +} +/*! + Initializes a triangle from 3 corners. + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + + The coordinates of the points are defined as a 2 dimension vector with coordinates (x,y). + */ +void vpTemplateTrackerTriangle::init(const vpColVector &c1,const vpColVector &c2,const vpColVector &c3) +{ + init(c1[0],c1[1],c2[0],c2[1],c3[0],c3[1]); +} +/*! + Initializes a triangle from 3 corners defined as image points. + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + */ +void vpTemplateTrackerTriangle::init(const vpImagePoint &c1, const vpImagePoint &c2, const vpImagePoint &c3) +{ + init(c1.get_u(), c1.get_v(), c2.get_u(), c2.get_v(), c3.get_u(), c3.get_v()); +} + +/*! + Initializes a triangle from 3 corners with coordinates (x1,y1), (x2,y2), (x3,y3). + - x coordinate is along the columns + - y coordinate is along the rows. + */ +void vpTemplateTrackerTriangle::init(int x1, int y1, int x2,int y2, int x3, int y3) +{ + init((double)x1,(double)y1,(double)x2,(double)y2,(double)x3,(double)y3); +} + +/*! + Initializes a triangle from 3 corners with coordinates (x1,y1), (x2,y2), (x3,y3). + - x coordinate is along the columns + - y coordinate is along the rows. + */ +void vpTemplateTrackerTriangle::init(double x1, double y1, double x2,double y2, double x3, double y3) +{ + C1.x=x1;C1.y=y1; + C2.x=x2;C2.y=y2; + C3.x=x3;C3.y=y3; + + double minx,miny,maxx,maxy; + //calcul du rectangle minimal contenant le triangle seletionne + minx=(x1<x2)?x1:x2; + miny=(y1<y2)?y1:y2; + minx=(minx<x3)?minx:x3; + miny=(miny<y3)?miny:y3; + maxx=(x1>x2)?x1:x2; + maxy=(y1>y2)?y1:y2; + maxx=(maxx>x3)?maxx:x3; + maxy=(maxy>y3)?maxy:y3; + + vpColVector u; + vpColVector v; + u.resize(2); + v.resize(2); + vpMatrix uv(2,2); + vpMatrix uvinv(2,2); + + u[0]=C2.x-C1.x; + u[1]=C2.y-C1.y; + + v[0]=C3.x-C1.x; + v[1]=C3.y-C1.y; + + uv[0][0]=u[0];uv[1][0]=v[0]; + uv[0][1]=u[1];uv[1][1]=v[1]; + try + { + uvinv=uv.inverseByLU(); + not_good=false; + } + catch(...) + { + not_good=true; + std::cout<<"Triangle vide"<<std::endl; + + } + uvinv00=uvinv[0][0]; + uvinv01=uvinv[0][1]; + uvinv10=uvinv[1][0]; + uvinv11=uvinv[1][1]; + + l_t=maxx-minx; + h_t=maxy-miny; + minx_temp=minx; + miny_temp=miny; + + marge_triangle = 0.00001; + area = 0.5 * fabs(uv.det()); +} + +//marge ajoutee a zone pour que sommet soit pris en compte + +/*! + Indicates if a point with coordinates (i,j) is in the triangle. + \param i : Coordinate along the rows. + \param j : Coordinate along the columns. + */ +bool vpTemplateTrackerTriangle::inTriangle(const int &i, const int &j) const +{ + if(not_good) + return false; + + /*ptempo[0]=j-C1.x; + ptempo[1]=i-C1.y; + + p_ds_uv=ptempo*uvinv; + return (p_ds_uv[0]+p_ds_uv[1]<1. && p_ds_uv[0]>0 && p_ds_uv[1]>0);*/ + + double ptempo0=j-C1.x; + double ptempo1=i-C1.y; + double p_ds_uv0=ptempo0*uvinv00+ptempo1*uvinv10; + double p_ds_uv1=ptempo0*uvinv01+ptempo1*uvinv11; + return (p_ds_uv0+p_ds_uv1<1.+marge_triangle && p_ds_uv0>-marge_triangle && p_ds_uv1>-marge_triangle); +} + +/*! + Indicates if a point with coordinates (i,j) is in the triangle. + \param i : Coordinate along the rows. + \param j : Coordinate along the columns. + */ +bool vpTemplateTrackerTriangle::inTriangle(const double &i, const double &j) const +{ + if(not_good) + return false; + /*ptempo[0]=j-C1.x; + ptempo[1]=i-C1.y; + + p_ds_uv=ptempo*uvinv; + return (p_ds_uv[0]+p_ds_uv[1]<1. && p_ds_uv[0]>0 && p_ds_uv[1]>0);*/ + double ptempo0=j-C1.x; + double ptempo1=i-C1.y; + double p_ds_uv0=ptempo0*uvinv00+ptempo1*uvinv10; + double p_ds_uv1=ptempo0*uvinv01+ptempo1*uvinv11; + return (p_ds_uv0+p_ds_uv1<1.+marge_triangle && p_ds_uv0>-marge_triangle && p_ds_uv1>-marge_triangle); +} + +/*! + Indicates if an image point is in the triangle. + \param ip : Image point to consider. + */ +bool vpTemplateTrackerTriangle::inTriangle(const vpImagePoint &ip) const +{ + return inTriangle(ip.get_i(), ip.get_j()); +} +/*! + Returns the coordinates of the triangle corners as an image point. + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + */ +void vpTemplateTrackerTriangle::getCorners(vpImagePoint &c1, vpImagePoint &c2, vpImagePoint &c3) const +{ + c1.set_uv(C1.x, C1.y); + c2.set_uv(C2.x, C2.y); + c3.set_uv(C3.x, C3.y); +} + +/*! + Returns the coordinates of the triangle corners as a 3 dimension vector of image points. + \param c : 3 dimension vector of image points that correspond to the triangle corners. + */ +void vpTemplateTrackerTriangle::getCorners(std::vector<vpImagePoint> &c) const +{ + c.resize(3); + c[0].set_uv(C1.x, C1.y); + c[1].set_uv(C2.x, C2.y); + c[2].set_uv(C3.x, C3.y); +} + +/*! + Returns the coordinates of the triangle corners as a 2 dimension vector (x,y). + \param c1 : First corner. + \param c2 : Second corner. + \param c3 : Third corner. + */ +void vpTemplateTrackerTriangle::getCorners(vpColVector &c1,vpColVector &c2,vpColVector &c3) const +{ + c1=getCorner1(); + c2=getCorner2(); + c3=getCorner3(); +} + +/*! + Returns the coordinates of the triangle first corner. + \return A vector with dimension 2, that contains the coordinates (x,y) of the corner. + */ +vpColVector vpTemplateTrackerTriangle::getCorner1() const +{ + vpColVector c(2); + c[0]=C1.x; + c[1]=C1.y; + + return c; +} +/*! + Returns the coordinates of the triangle second corner. + \return A vector with dimension 2, that contains the coordinates (x,y) of the corner. + */ +vpColVector vpTemplateTrackerTriangle::getCorner2() const +{ + vpColVector c(2); + c[0]=C2.x; + c[1]=C2.y; + return c; +} + +/*! + Returns the coordinates of the triangle third corner. + \return A vector with dimension 2, that contains the coordinates (x,y) of the corner. + */ +vpColVector vpTemplateTrackerTriangle::getCorner3() const +{ + vpColVector c(2); + c[0]=C3.x; + c[1]=C3.y; + return c; +} + +/*! + Get the size of the triangle bounding box. + \param w : Bounding box width. + \param h : Bounding box height. + */ +void vpTemplateTrackerTriangle::getSize(double &w,double &h) const +{ + w=l_t; + h=h_t; +} +/*! + Get the size of the triangle bounding box. + \param w : Bounding box width. + \param h : Bounding box height. + */ +void vpTemplateTrackerTriangle::getSize(int &w,int &h) const +{ + w=(int)l_t+1; + h=(int)h_t+1; +} + +/*! + \return The minimal x coordinate (along the columns of the image) of the points that are in the triangle. + \sa getMaxx() + */ +double vpTemplateTrackerTriangle::getMinx() const +{ + return minx_temp-1; +} +/*! + \return The minimal y coordinate (along the rows of the image) of the points that are in the triangle. + \sa getMaxy() + */ +double vpTemplateTrackerTriangle::getMiny() const +{ + return miny_temp-1; +} +/*! + \return The maximal x coordinate (along the columns of the image) of the points that are in the triangle. + \sa getMinx() + */ +double vpTemplateTrackerTriangle::getMaxx() const +{ + return minx_temp+l_t+1; +} +/*! + \return The maximal y coordinate (along the rows of the image) of the points that are in the triangle. + \sa getMaxx() + */ +double vpTemplateTrackerTriangle::getMaxy() const +{ + return miny_temp+h_t+1; +} + + + diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.h b/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.h new file mode 100644 index 0000000000000000000000000000000000000000..e4324d5dfe1ee796f3da94d63332e319947d9bef --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerTriangle.h @@ -0,0 +1,165 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerTriangle.h 4781 2014-07-15 13:03:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerTriangle.h + \brief +*/ + +#ifndef vpTemplateTrackerTriangle_hh +#define vpTemplateTrackerTriangle_hh + +#include <assert.h> +#include <vector> + +#include <visp/vpMatrix.h> +#include <visp/vpMath.h> +#include <visp/vpColVector.h> +#include <visp/vpImagePoint.h> +#include <visp/vpTemplateTrackerHeader.h> + +class VISP_EXPORT vpTemplateTrackerTriangle +{ + protected: + double minx_temp; + double miny_temp; + vpTemplateTrackerDPoint C1; //! Corner 1 + vpTemplateTrackerDPoint C2; //! Corner 2 + vpTemplateTrackerDPoint C3; //! Corner 3 + + double l_t; + double h_t; + + bool not_good; + double uvinv00; + double uvinv01; + double uvinv10; + double uvinv11; + double marge_triangle; + double area; + + private: + vpColVector getCorner1() const; + vpColVector getCorner2() const; + vpColVector getCorner3() const; + + public: + vpTemplateTrackerTriangle(); + vpTemplateTrackerTriangle(const vpTemplateTrackerTriangle& T); + vpTemplateTrackerTriangle(const vpColVector &c1, const vpColVector &c2, const vpColVector &c3); + vpTemplateTrackerTriangle(const vpImagePoint &c1, const vpImagePoint &c2, const vpImagePoint &c3); + vpTemplateTrackerTriangle(int x1, int y1, int x2, int y2, int x3, int y3); + vpTemplateTrackerTriangle(double x1, double y1, double x2, double y2, double x3, double y3); + + /*! + Return the area of the triangle. + + \return The area of the triangle. + */ + inline double getArea() const{ + return this->area; + } + + vpTemplateTrackerTriangle getPyramidDown() const; + void getCorners(vpColVector &c1,vpColVector &c2,vpColVector &c3) const; + void getCorners(vpImagePoint &c1, vpImagePoint &c2, vpImagePoint &c3) const; + void getCorners(std::vector<vpImagePoint> &c) const; + + /*! + Returns the coordinates of a triangle corner. + \param i : Allowed values are 0, 1 or 2. + \return + - if i = 0, return corner 1 coordinates, + - if i = 1, return corner 2 coordinates, + - if i = 2, return corner 3 coordinates. + + The coordinates are returned as a 2 dimension vector (x,y). + */ + vpColVector getCorner(unsigned int i) const { + assert(i<3); + if(i==0) return getCorner1(); + else if(i==1) return getCorner2(); + else /*if(i==2)*/ return getCorner3(); + }; + /*! + Returns the coordinates of a triangle corner. + \param i : Allowed values are 0, 1 or 2. + - if i = 0, return corner 1 coordinates, + - if i = 1, return corner 2 coordinates, + - if i = 2, return corner 3 coordinates. + \param x,y : The coordinates of the corner. + */ + void getCorner(unsigned int i, double &x, double &y) const { + assert(i<3); + if(i==0) { + x = C1.x; + y = C1.y; + } + else if(i==1) { + x = C2.x; + y = C2.y; + } + else /*if(i==2)*/ { + x = C3.x; + y = C3.y; + } + }; + + double getMaxx() const; + double getMaxy() const; + double getMinx() const; + double getMiny() const; + + void getSize(double &w, double &h) const; + void getSize(int &w, int &h) const; + + void init(const vpColVector &c1,const vpColVector &c2,const vpColVector &c3); + void init(const vpImagePoint &c1, const vpImagePoint &c2, const vpImagePoint &c3); + void init(int x1,int y1, int x2,int y2, int x3,int y3); + void init(double x1,double y1, double x2,double y2, double x3,double y3); + bool inTriangle(const vpImagePoint &ip) const; + bool inTriangle(const int &i, const int &j) const; + bool inTriangle(const double &i,const double &j) const; + + vpTemplateTrackerTriangle & operator=(const vpTemplateTrackerTriangle& T); +}; +#endif + diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerZone.cpp b/src/tracking/template-tracker/tools/vpTemplateTrackerZone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..573786a84cbc7cb32b59a7d3bec24634ab5d7de7 --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerZone.cpp @@ -0,0 +1,619 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZone.cpp 5119 2015-01-05 10:02:46Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ + +#include <limits> // numeric_limits + +#include <visp/vpConfig.h> + +#if VISP_HAVE_OPENCV_VERSION >= 0x020300 +#include <opencv2/imgproc/imgproc.hpp> +#endif + +#include <visp/vpTemplateTrackerZone.h> + + +/*! + Default constructor. + */ +vpTemplateTrackerZone::vpTemplateTrackerZone() + : Zone(), min_x(-1), min_y(-1), max_x(-1), max_y(-1) +{ +} + +/*! + Copy constructor. + */ +vpTemplateTrackerZone::vpTemplateTrackerZone(const vpTemplateTrackerZone &z) + : Zone(), min_x(-1), min_y(-1), max_x(-1), max_y(-1) +{ + *this = z; +} + +/*! + Remove all the triangles that define the zone. + */ +void vpTemplateTrackerZone::clear() +{ + min_x=-1; + min_y=-1; + max_x=-1; + max_y=-1; + + Zone.clear(); +} + +/*! + Copy operator. + */ +vpTemplateTrackerZone & vpTemplateTrackerZone::operator=(const vpTemplateTrackerZone &z) +{ + clear(); + + this->copy(z); + return (*this); +} + +/*! + Initialize a zone in image \e I using mouse click. + + \param I : Image used to select the zone. + \param delaunay : Flag used to enable Delaunay triangulation. + - If true, from the image points selected by the user, a Delaunay triangulation is performed + to initialize the zone. + - A left click select a image point; + - A right click select the last image point and ends the initialisation stage. + In that case at least 3 points need to be selected by the user. + - If false, the user select directly points as successive triangle corners. + Three successive points define a triangle. It is not mandatory + that triangles have one edge in common; they can define a discontinued area. + - A left click select a triangle corner; + - A right click select the last triangle corner and ends the initialisation stage. + The number of points that are selected by the user should be a multiple of 3. + For example, to select a zone as two triangles, the user has to left click + five times and finish the selection on the sixth corner with a right click. + + */ +void vpTemplateTrackerZone::initClick(const vpImage<unsigned char> &I, bool delaunay) +{ + Zone.clear(); + + std::vector<vpImagePoint> vip; + + bool end = false; + + do { + vpImagePoint p; + vpMouseButton::vpMouseButtonType button; + if (vpDisplay::getClick(I, p, button, false) ) { + vip.push_back(p); + + vpDisplay::displayCross(I, p, 7, vpColor::red); + + if (vip.size() > 1) { + if (delaunay) { + // Draw a line between the 2 last points + vpDisplay::displayLine(I, p, vip[vip.size()-2], vpColor::blue, 3); + } + else { + if(vip.size() % 3 ==2) + // draw line between point 2-1 + vpDisplay::displayLine(I, p, vip[vip.size()-2], vpColor::blue, 3); + else if(vip.size() % 3 ==0) { + // draw line between point 3-2 + vpDisplay::displayLine(I, p, vip[vip.size()-2], vpColor::blue, 3); + // draw line between point 3-1 + vpDisplay::displayLine(I, p, vip[vip.size()-3], vpColor::blue, 3); + } + + } + } + + if (button == vpMouseButton::button3) + end = true; + } + + vpTime::wait(20); + vpDisplay::flush(I); + } while(!end); + + initFromPoints(I, vip, delaunay); +} + +/*! + + Initialize the zone using a vector of image points. + + \param I : Image to process. + \param vip : Vector of image points used as initialization. + \param delaunay : + - If true, a Delaunay triangulation is perfomed on the vector of image points. This functionality + is only available if ViSP is build with OpenCV >2.3 third-party. + - If false, the vector of image points describe triangles. Its size is then a multiple of 3. + */ +void vpTemplateTrackerZone::initFromPoints(const vpImage<unsigned char>& I, const std::vector< vpImagePoint > &vip, bool delaunay) +{ + if (delaunay) { + if(vip.size() == 3) { + initFromPoints(I, vip, false); + } + else if(vip.size() == 4) { + std::vector<vpImagePoint> vip_delaunay; + vip_delaunay.push_back(vip[0]); + vip_delaunay.push_back(vip[1]); + vip_delaunay.push_back(vip[2]); + vip_delaunay.push_back(vip[2]); + vip_delaunay.push_back(vip[3]); + vip_delaunay.push_back(vip[0]); + initFromPoints(I, vip_delaunay, false); + } + else { +#if VISP_HAVE_OPENCV_VERSION >= 0x020300 + // Init Delaunay + cv::Subdiv2D subdiv(cv::Rect(0, 0, (int)I.getWidth(), (int)I.getHeight())); + for(size_t i=0; i< vip.size(); i++) { + cv::Point2f fp((float)vip[i].get_u(), (float)vip[i].get_v()); + //std::cout << "Click point: " << vip[i] << std::endl; + subdiv.insert(fp); + } + + // Compute Delaunay triangulation + std::vector<cv::Vec6f> triangleList; + subdiv.getTriangleList(triangleList); + + // Keep only the Delaunay points that are inside the area + vpRect rect(0, 0, I.getWidth(), I.getHeight()); + + std::vector<vpImagePoint> vip_delaunay; + for( size_t i = 0; i < triangleList.size(); i++ ) { + cv::Vec6f t = triangleList[i]; + std::vector<vpImagePoint> p(3); + + p[0].set_uv(t[0], t[1]); + p[1].set_uv(t[2], t[3]); + p[2].set_uv(t[4], t[5]); + + if (p[0].inRectangle(rect) && p[1].inRectangle(rect) && p[2].inRectangle(rect)) { + vip_delaunay.push_back(p[0]); + vip_delaunay.push_back(p[1]); + vip_delaunay.push_back(p[2]); + } + } + + initFromPoints(I, vip_delaunay, false); +#else + throw vpException(vpException::functionNotImplementedError,"Delaunay triangulation is not available!"); +#endif + } + } + else { + Zone.clear(); + for(unsigned int i=0; i<vip.size(); i+=3) { + vpTemplateTrackerTriangle triangle(vip[i], vip[i+1], vip[i+2]); + add(triangle); + +// vpDisplay::displayLine(I, vip[i], vip[i+1], vpColor::green, 1); +// vpDisplay::displayLine(I, vip[i+1], vip[i+2], vpColor::green, 1); +// vpDisplay::displayLine(I, vip[i+2], vip[i], vpColor::green,1); +// vpDisplay::flush(I) ; + + // Update the bounding box + if((triangle.getMinx()<min_x)||(min_x==-1)) + min_x=(int)triangle.getMinx(); + if((triangle.getMaxx()>max_x)||(max_x==-1)) + max_x=(int)triangle.getMaxx(); + if((triangle.getMiny()<min_y)||(min_y==-1)) + min_y=(int)triangle.getMiny(); + if((triangle.getMaxy()>max_y)||(max_y==-1)) + max_y=(int)triangle.getMaxy(); + } + } +} + +/*! + Add a triangle to the zone and update the bounding box. + \param t : Triangle to introduce in the zone. + */ +void vpTemplateTrackerZone::add(const vpTemplateTrackerTriangle &t) +{ + Zone.push_back(t); + + // Update the bounding box + if((t.getMinx()<min_x)||(min_x==-1)) + min_x=(int)t.getMinx(); + if((t.getMaxx()>max_x)||(max_x==-1)) + max_x=(int)t.getMaxx(); + if((t.getMiny()<min_y)||(min_y==-1)) + min_y=(int)t.getMiny(); + if((t.getMaxy()>max_y)||(max_y==-1)) + max_y=(int)t.getMaxy(); +} + +/*! + Test if a pixel with coordinates (i,j) is in the zone.. + \param i, j : Coordinates of the pixel to test. + \return true if the pixel with coordinates (i,j) is in the zone defined by a set of triangles, false otherwise. + */ +bool vpTemplateTrackerZone::inZone(const int &i, const int &j) const +{ + std::vector<vpTemplateTrackerTriangle>::const_iterator Iterateurvecteur; + for(Iterateurvecteur=Zone.begin();Iterateurvecteur!=Zone.end();Iterateurvecteur++) + { + if(Iterateurvecteur->inTriangle(i,j)) + return true; + } + return false; +} + +/*! + Test if a pixel with coordinates (i,j) is in the zone.. + \param i, j : Coordinates of the pixel to test. + \return true if the pixel with coordinates (i,j) is in the zone defined by a set of triangles, false otherwise. + */ +bool vpTemplateTrackerZone::inZone(const double &i,const double &j) const +{ + std::vector<vpTemplateTrackerTriangle>::const_iterator Iterateurvecteur; + for(Iterateurvecteur=Zone.begin();Iterateurvecteur!=Zone.end();Iterateurvecteur++) + { + if(Iterateurvecteur->inTriangle(i,j)) + return true; + } + return false; +} + +/*! + Test if a pixel with coordinates (i,j) is in the zone and returns also the index of + the triangle that contains the pixel. + \param i, j : Coordinates of the pixel to test. + \param id_triangle : Index of the triangle that contains the pixel (i,j). + \return true if the pixel with coordinates (i,j) is in the zone defined by a set of triangles, false otherwise. + */ +bool vpTemplateTrackerZone::inZone(const int &i,const int &j, unsigned int &id_triangle) const +{ + unsigned int id=0; + std::vector<vpTemplateTrackerTriangle>::const_iterator Iterateurvecteur; + for(Iterateurvecteur=Zone.begin();Iterateurvecteur!=Zone.end();Iterateurvecteur++) + { + if(Iterateurvecteur->inTriangle(i,j)) + { + id_triangle=id; + return true; + } + id++; + } + return false; +} + +/*! + Test if a pixel with coordinates (i,j) is in the zone and returns also the index of + the triangle that contains the pixel. + \param i, j : Coordinates of the pixel to test. + \param id_triangle : Index of the triangle that contains the pixel (i,j). + \return true if the pixel with coordinates (i,j) is in the zone defined by a set of triangles, false otherwise. + */ +bool vpTemplateTrackerZone::inZone(const double &i,const double &j, unsigned int &id_triangle) const +{ + unsigned int id=0; + std::vector<vpTemplateTrackerTriangle>::const_iterator Iterateurvecteur; + for(Iterateurvecteur=Zone.begin();Iterateurvecteur!=Zone.end();Iterateurvecteur++) + { + if(Iterateurvecteur->inTriangle(i,j)) + { + id_triangle=id; + return true; + } + id++; + } + return false; +} + +/*! + A zone is defined by a set of triangles. This function returns the ith triangle. + \param i : Index of the triangle to return. + \param T : The triangle corresponding to index i. + \return true if the triangle with index i was found, false otherwise. + + The following sample code shows how to use this function: + \code + vpTemplateTrackerZone zone; + ... + for (unsigned int i=0; i < zone.getNbTriangle(); i++) { + vpTemplateTrackerTriangle triangle; + zone.getTriangle(i, triangle); + } + \endcode + */ +void vpTemplateTrackerZone::getTriangle(unsigned int i,vpTemplateTrackerTriangle &T) const +{ + if (i > getNbTriangle()-1) + throw(vpException(vpException::badValue, "Cannot get triangle with index %u", i)); + + T = Zone[i]; +} +/*! + A zone is defined by a set of triangles. This function returns the ith triangle. + \param i : Index of the triangle to return. + \return The triangle corresponding to index i. + + The following sample code shows how to use this function: + \code + vpTemplateTrackerZone zone; + ... + for (unsigned int i=0; i < zone.getNbTriangle(); i++) { + vpTemplateTrackerTriangle triangle = zone.getTriangle(i); + } + \endcode + */ +vpTemplateTrackerTriangle vpTemplateTrackerZone::getTriangle(unsigned int i) const +{ + if (i > getNbTriangle()-1) + throw(vpException(vpException::badValue, "Cannot get triangle with index %u", i)); + + return Zone[i]; +} +/*! + Return the position of the center of gravity of the zone. + \exception vpException::divideByZeroError: The size of the zone is null. + */ +vpImagePoint vpTemplateTrackerZone::getCenter() const +{ + double xc=0; + double yc=0; + int cpt=0; + for(int i=min_y;i<max_y;i++) + for(int j=min_x;j<max_x;j++) + if(inZone(i,j)) + { + xc+=j; + yc+=i; + cpt ++; + } + if(! cpt) { + throw(vpException(vpException::divideByZeroError, + "Cannot compute the zone center: size = 0")) ; + } + xc=xc/cpt; + yc=yc/cpt; + vpImagePoint ip; + ip.set_uv(xc, yc); + return ip; +} + +/*! + \return The maximal x coordinate (along the columns of the image) of the points that are in the zone. + \sa getMinx(), getBoundingBox() + */ +int vpTemplateTrackerZone::getMaxx() const +{ + return max_x; +} +/*! + \return The maximal y coordinate (along the rows of the image) of the points that are in the zone. + \sa getMiny(), getBoundingBox() + */ +int vpTemplateTrackerZone::getMaxy() const +{ + return max_y; +} +/*! + \return The minimal x coordinate (along the columns of the image) of the points that are in the zone. + \sa getMaxx(), getBoundingBox() + */ +int vpTemplateTrackerZone::getMinx() const +{ + return min_x; +} +/*! + \return The minimal y coordinate (along the rows of the image) of the points that are in the zone. + \sa getMaxy(), getBoundingBox() + */ +int vpTemplateTrackerZone::getMiny() const +{ + return min_y; +} + +/*! + Return a rectangle that defines the bounding box of the zone. + \sa getMinx(), getMiny(), getMaxx(), getMaxy() + */ +vpRect vpTemplateTrackerZone::getBoundingBox() const +{ + vpRect bbox; + bbox.setTopLeft(vpImagePoint(min_y, min_x)); + bbox.setBottomRight(vpImagePoint(max_y, max_x)); + return bbox; +} + +/*! + If a display device is associated to image \c I, display in overlay the triangles that define the zone. + \param I : Image. + \param col : Color used to display the triangles. + \param thickness : Thickness of the triangle lines. + */ +void vpTemplateTrackerZone::display(const vpImage<unsigned char> &I, const vpColor &col, const unsigned int thickness) +{ + std::vector<vpImagePoint> ip; + for (unsigned int i=0; i < Zone.size(); i++) { + vpTemplateTrackerTriangle triangle; + Zone[i].getCorners(ip); + vpDisplay::displayLine(I, ip[0], ip[1], col, thickness); + vpDisplay::displayLine(I, ip[1], ip[2], col, thickness); + vpDisplay::displayLine(I, ip[2], ip[0], col, thickness); + } +} + +/*! + If a display device is associated to image \c I, display in overlay the triangles that define the zone. + \param I : Image. + \param col : Color used to display the triangles. + \param thickness : Thickness of the triangle lines. + */ +void vpTemplateTrackerZone::display(const vpImage<vpRGBa> &I, const vpColor &col, const unsigned int thickness) +{ + std::vector<vpImagePoint> ip; + for (unsigned int i=0; i < Zone.size(); i++) { + vpTemplateTrackerTriangle triangle; + Zone[i].getCorners(ip); + vpDisplay::displayLine(I, ip[0], ip[1], col, thickness); + vpDisplay::displayLine(I, ip[1], ip[2], col, thickness); + vpDisplay::displayLine(I, ip[2], ip[0], col, thickness); + } +} + +/*! + Destructor. + */ +vpTemplateTrackerZone::~vpTemplateTrackerZone() +{ + clear(); +} + +/*! + Modify all the pixels inside a triangle with a given gray level. + \param I: Output image. + \param id: Triangle id. This value should be less than the number + of triangles used to define the zone and available using getNbTriangle(). + \param gray_level: Color used to fill the triangle with. + */ +void vpTemplateTrackerZone::fillTriangle(vpImage<unsigned char>& I, unsigned int id,unsigned char gray_level) +{ + assert(id < getNbTriangle()); + vpTemplateTrackerTriangle triangle; + getTriangle(id, triangle); + for (int i=0 ; i < (int) I.getHeight() ; i++) + { + for (int j=0 ; j < (int) I.getWidth() ; j++) + { + if(triangle.inTriangle(i,j)) + { + I[i][j]=gray_level; + } + } + } +} + +/*! + Return a zone with triangles that are down scaled by a factor 2. + */ +vpTemplateTrackerZone vpTemplateTrackerZone::getPyramidDown() const +{ + vpTemplateTrackerZone tempZone; + vpTemplateTrackerTriangle Ttemp; + vpTemplateTrackerTriangle TtempDown; + for(unsigned int i=0;i<getNbTriangle();i++) + { + getTriangle(i,Ttemp); + TtempDown=Ttemp.getPyramidDown(); + tempZone.add(TtempDown); + } + return tempZone; +} + +/*! + Copy all the triangles that define zone \c Z in the current zone (*this) and + update the zone bounding box. + \param z : Zone with a set of triangles provided as input. + */ +void vpTemplateTrackerZone::copy(const vpTemplateTrackerZone& z) +{ + vpTemplateTrackerTriangle triangle; + for(unsigned int i=0;i<z.getNbTriangle();i++) + { + z.getTriangle(i, triangle); + add(triangle); + // Update the bounding box + if((triangle.getMinx()<min_x)||(min_x==-1)) + min_x=(int)triangle.getMinx(); + if((triangle.getMaxx()>max_x)||(max_x==-1)) + max_x=(int)triangle.getMaxx(); + if((triangle.getMiny()<min_y)||(min_y==-1)) + min_y=(int)triangle.getMiny(); + if((triangle.getMaxy()>max_y)||(max_y==-1)) + max_y=(int)triangle.getMaxy(); + } +} + +/*! + Return the position of the center of gravity in a given area. + \param borne_x : Right coordinate of the area to consider. + \param borne_y : Bottom coordinate of the area to consider. + \exception vpException::divideByZeroError: The size of the zone is null. + */ + +vpImagePoint vpTemplateTrackerZone::getCenter(int borne_x, int borne_y) const +{ + int cpt_pt=0; + double x_center=0,y_center=0; + for(int j=0;j<borne_x;j++) + for(int i=0;i<borne_y;i++) + if(inZone(i,j)) + { + x_center+=j; + y_center+=i; + cpt_pt++; + } + + if(! cpt_pt) { + throw(vpException(vpException::divideByZeroError, + "Cannot compute the zone center: size = 0")) ; + } + + x_center=x_center/cpt_pt; + y_center=y_center/cpt_pt; + vpImagePoint center; + center.set_uv(x_center, y_center); + return center; +} + +/*! + Return the area of the template zone. + */ +double vpTemplateTrackerZone::getArea() const +{ + double area = 0; + vpTemplateTrackerTriangle triangle; + for (unsigned int i=0; i < getNbTriangle(); i++) { + getTriangle(i, triangle); + area += triangle.getArea(); + } + return area; +} + diff --git a/src/tracking/template-tracker/tools/vpTemplateTrackerZone.h b/src/tracking/template-tracker/tools/vpTemplateTrackerZone.h new file mode 100644 index 0000000000000000000000000000000000000000..324eb9353240265d6a1c31b9f70296d2e0934e8a --- /dev/null +++ b/src/tracking/template-tracker/tools/vpTemplateTrackerZone.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZone.h 4781 2014-07-15 13:03:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#ifndef vpTemplateTrackerZone_hh +#define vpTemplateTrackerZone_hh + +#include <vector> + +#include <visp/vpDisplay.h> +#include <visp/vpException.h> +#include <visp/vpImage.h> +#include <visp/vpTemplateTrackerTriangle.h> +#include <visp/vpTemplateTrackerHeader.h> +#include <visp/vpRect.h> + +/*! + A zone is defined by a set of triangles defined as vpTemplateTrackerTriangle. + + A zone can be initialized either by user interaction using mouse click in a display device + throw initClick(), or by a list of points throw initFromPoints(). + */ +class VISP_EXPORT vpTemplateTrackerZone +{ + protected: + std::vector<vpTemplateTrackerTriangle> Zone; //!< Vector of triangles that defines the zone. + int min_x; //!< Bounding box parameter + int min_y; //!< Bounding box parameter + int max_x; //!< Bounding box parameter + int max_y; //!< Bounding box parameter + + public: + vpTemplateTrackerZone(); + vpTemplateTrackerZone(const vpTemplateTrackerZone &z); + ~vpTemplateTrackerZone(); + + //add a triangle to the zone + void add(const vpTemplateTrackerTriangle &t); + void clear(); + void copy(const vpTemplateTrackerZone& z); + + //display the area on an image + void display(const vpImage<unsigned char> &I, const vpColor &col = vpColor::green, const unsigned int thickness=3); + void display(const vpImage<vpRGBa> &I, const vpColor &col = vpColor::green, const unsigned int thickness=3); + + //colorie le tieme triangle + void fillTriangle(vpImage<unsigned char>& I, unsigned int id, unsigned char gray_level); + + double getArea() const; + vpImagePoint getCenter() const; + vpImagePoint getCenter(int borne_x, int borne_y) const; + //get bounds of the area + int getMaxx() const; + int getMaxy() const; + int getMinx() const; + int getMiny() const; + vpRect getBoundingBox() const; + + /*! Return the number of triangles that define the zone. \sa getTriangle() */ + unsigned int getNbTriangle() const { return (unsigned int)Zone.size(); } + vpTemplateTrackerZone getPyramidDown() const; + //renvoie le ieme triangle de la zone + void getTriangle(unsigned int i, vpTemplateTrackerTriangle &T) const; + vpTemplateTrackerTriangle getTriangle(unsigned int i) const; + + //create an area by clicking on an image + void initClick(const vpImage<unsigned char>& I, bool delaunay = false); + //create an area with a pointer of integer that describes a series of triangles: + // *pt= t0.S1.x,t0.S1.y,t0.S2.x,t0.S2.y,t0.S3.x,t0.S3.y, t1.S1.x ... + void initFromPoints(const vpImage<unsigned char>& I, const std::vector< vpImagePoint > &ip, bool delaunay = false); + + //check if a point is in the area + bool inZone(const int &i,const int &j) const; + bool inZone(const double &i,const double &j) const; + //check if a point is in the area and return the corresponding triangle id_triangle where the point is. + bool inZone(const int &i,const int &j, unsigned int &id_triangle) const; + bool inZone(const double &i,const double &j, unsigned int &id_triangle) const; + + vpTemplateTrackerZone & operator=(const vpTemplateTrackerZone &z); +}; +#endif + diff --git a/src/tracking/template-tracker/vpTemplateTracker.cpp b/src/tracking/template-tracker/vpTemplateTracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e85adcd14e2c33d9bde48ae8ab14a00eff58e2f --- /dev/null +++ b/src/tracking/template-tracker/vpTemplateTracker.cpp @@ -0,0 +1,981 @@ +/**************************************************************************** + * + * $Id: vpTemplateTracker.cpp 5062 2014-12-15 13:43:33Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ + +#include <visp/vpTemplateTracker.h> +#include <visp/vpTemplateTrackerBSpline.h> + +vpTemplateTracker::vpTemplateTracker(vpTemplateTrackerWarp *_warp) + : nbLvlPyr(1), l0Pyr(0), pyrInitialised(false), ptTemplate(NULL), ptTemplatePyr(NULL), + ptTemplateInit(false), templateSize(0), templateSizePyr(NULL), + ptTemplateSelect(NULL), ptTemplateSelectPyr(NULL), ptTemplateSelectInit(false), + templateSelectSize(0), ptTemplateSupp(NULL), ptTemplateSuppPyr(NULL), + ptTemplateCompo(NULL), ptTemplateCompoPyr(NULL), zoneTracked(NULL), zoneTrackedPyr(NULL), + pyr_IDes(NULL), H(), Hdesire(), HdesirePyr(), HLM(), HLMdesire(), HLMdesirePyr(), + HLMdesireInverse(), HLMdesireInversePyr(), G(), gain(1.), thresholdGradient(40), + costFunctionVerification(false), blur(true), useBrent(false), nbIterBrent(3), + taillef(7), fgG(NULL), fgdG(NULL), ratioPixelIn(0), mod_i(1), mod_j(1), nbParam(0), + lambdaDep(0.001), iterationMax(30), iterationGlobale(0), diverge(false), nbIteration(0), + useCompositionnal(true), useInverse(false), Warp(_warp), p(0), dp(), X1(), X2(), + dW(), BI(), dIx(), dIy(), zoneRef_() +{ + nbParam = Warp->getNbParam() ; + p.resize(nbParam); + dp.resize(nbParam); + + fgG=new double[(taillef+1)/2] ; + vpImageFilter::getGaussianKernel(fgG, taillef) ; + + fgdG=new double[(taillef+1)/2] ; + vpImageFilter::getGaussianDerivativeKernel(fgdG, taillef) ; +} + +void vpTemplateTracker::setGaussianFilterSize(unsigned int new_taill) +{ + taillef=new_taill; + if (fgG) delete[] fgG; + fgG=new double[taillef] ; + vpImageFilter::getGaussianKernel(fgG, taillef) ; + + if (fgdG) delete[] fgdG; + fgdG=new double[taillef] ; + vpImageFilter::getGaussianDerivativeKernel(fgdG, taillef) ; +} + +void vpTemplateTracker::initTracking(const vpImage<unsigned char>& I, vpTemplateTrackerZone &zone) +{ + // std::cout<<"\tInitialise reference..."<<std::endl; + zoneTracked=&zone; + + int largeur_im=(int)I.getWidth(); + int hauteur_im=(int)I.getHeight(); + + unsigned int NbPointDsZone=0; + //double xtotal=0,ytotal=0; + int mod_fi,mod_fj; + mod_fi=mod_i;mod_fj=mod_i; + + for(int i=0;i<hauteur_im;i+=mod_fi) { + for(int j=0;j<largeur_im;j+=mod_fj) { + if(zone.inZone(i,j)) { + NbPointDsZone++; + //xtotal+=j; + //ytotal+=i; + } + } + } + + //Warp->setCentre((double)xtotal/NbPointDsZone,(double)ytotal/NbPointDsZone); + + templateSize=NbPointDsZone; + ptTemplate = new vpTemplateTrackerPoint[templateSize];ptTemplateInit=true; + ptTemplateSelect = new bool[templateSize];ptTemplateSelectInit=true; + + Hdesire.resize(nbParam,nbParam); + HLMdesire.resize(nbParam,nbParam); + + vpTemplateTrackerPoint pt; + //vpTemplateTrackerZPoint ptZ; + vpImage<double> GaussI ; + vpImageFilter::filter(I, GaussI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + unsigned int cpt_point=0; + templateSelectSize=0; + for(int i=0;i<hauteur_im;i+=mod_i) + { + //for(int j=minx_t;j<maxx_t;j++) + for(int j=0;j<largeur_im;j+=mod_j) + { +// if(i%mod_i ==0 && j%mod_j ==0) + if(zone.inZone(i,j)) + { + pt.x=j; + pt.y=i; + + pt.dx=dIx[i][j]; + pt.dy=dIy[i][j]; + + if(pt.dx*pt.dx+pt.dy*pt.dy>thresholdGradient) + { + ptTemplateSelect[cpt_point]=true; + templateSelectSize++; + } + else + ptTemplateSelect[cpt_point]=false; + //ptTemplate_select[cpt_point]=true; + + /*if(blur) + pt.val=GaussI[i][j]; + else + pt.val=I[i][j];*/ + pt.val=vpTemplateTrackerBSpline::getSubPixBspline4(GaussI,i,j); + //ptZone_pyr[NbLevelPyramid-cpt].push_back(pt); + + ptTemplate[cpt_point]=pt; + cpt_point++; + } + } + } + + // std::cout<<"\tNb pt template apres scale:"<<cpt_point<<std::endl; + // std::cout<<"utilisation de "<<taille_template_select<<"/"<<cpt_point<<" = "<<100.*taille_template_select/cpt_point<<"% pour calcul derivees"<<std::endl; + + templateSize=cpt_point; + GaussI.destroy(); + // std::cout<<"\tEnd of reference initialisation ..."<<std::endl; +} + +vpTemplateTracker::~vpTemplateTracker() +{ + // vpTRACE("destruction tracker"); + delete[] fgG; + delete[] fgdG; + + resetTracker(); +} + +/*! + Reset the tracker by freeing the memory allocated by the template tracker during the initialization. + */ +void vpTemplateTracker::resetTracker() +{ + // reset the tracker parameters + p = 0; + + // vpTRACE("resetTracking"); + if(pyrInitialised) + { + if(ptTemplatePyr){ + for(unsigned int i=0;i<nbLvlPyr;i++) + { + if(ptTemplatePyr[i]){ + for(unsigned int point=0;point<templateSizePyr[i];point++) + { + delete[] ptTemplatePyr[i][point].dW; + delete[] ptTemplatePyr[i][point].HiG; + } + delete[] ptTemplatePyr[i]; + } + } + delete[] ptTemplatePyr; + ptTemplatePyr = NULL; + } + + if (ptTemplateCompoPyr) { + for(unsigned int i=0;i<nbLvlPyr;i++) + { + if (ptTemplateCompoPyr[i]) { + for(unsigned int point=0;point<templateSizePyr[i];point++) { + delete[] ptTemplateCompoPyr[i][point].dW; + } + delete[] ptTemplateCompoPyr[i]; + } + } + delete[] ptTemplateCompoPyr; + ptTemplateCompoPyr = NULL; + } + + if (ptTemplateSuppPyr) { + for(unsigned int i=0;i<nbLvlPyr;i++) + { + if (ptTemplateSuppPyr[i]) { + for(unsigned int point=0;point<templateSizePyr[i];point++) { + delete[] ptTemplateSuppPyr[i][point].Bt; + delete[] ptTemplateSuppPyr[i][point].BtInit; + delete[] ptTemplateSuppPyr[i][point].dBt; + delete[] ptTemplateSuppPyr[i][point].d2W; + delete[] ptTemplateSuppPyr[i][point].d2Wx; + delete[] ptTemplateSuppPyr[i][point].d2Wy; + } + delete[] ptTemplateSuppPyr[i]; + } + } + delete[] ptTemplateSuppPyr; + ptTemplateSuppPyr = NULL; + } + + if(ptTemplateSelectPyr){ + for(unsigned int i=0;i<nbLvlPyr;i++){ + if(ptTemplateSelectPyr[i]) + delete[] ptTemplateSelectPyr[i]; + } + delete[] ptTemplateSelectPyr; + ptTemplateSelectPyr = NULL; + } + + if(templateSizePyr){ + delete[] templateSizePyr; + templateSizePyr = NULL; + } + + if(HdesirePyr) { + delete[] HdesirePyr; + HdesirePyr = NULL; + } + + if(HLMdesirePyr){ + delete[] HLMdesirePyr; + HLMdesirePyr = NULL; + } + + if(HLMdesireInversePyr){ + delete[] HLMdesireInversePyr; + HLMdesireInversePyr = NULL; + } + + if(zoneTrackedPyr){ + delete[] zoneTrackedPyr; + zoneTrackedPyr = NULL; + } + + if(pyr_IDes){ + delete[] pyr_IDes; + pyr_IDes = NULL; + } + } + else + { + if(ptTemplateInit) + { + for(unsigned int point=0;point<templateSize;point++) + { + delete[] ptTemplate[point].dW; + delete[] ptTemplate[point].HiG; + } + delete[] ptTemplate; + ptTemplate = NULL; + ptTemplateInit = false; + } + if (ptTemplateCompo) { + for(unsigned int point=0;point<templateSize;point++) + { + delete[] ptTemplateCompo[point].dW; + } + delete[] ptTemplateCompo; + ptTemplateCompo = NULL; + } + if (ptTemplateSupp) { + for(unsigned int point=0;point<templateSize;point++) + { + delete[] ptTemplateSupp[point].Bt; + delete[] ptTemplateSupp[point].BtInit; + delete[] ptTemplateSupp[point].dBt; + delete[] ptTemplateSupp[point].d2W; + delete[] ptTemplateSupp[point].d2Wx; + delete[] ptTemplateSupp[point].d2Wy; + } + delete[] ptTemplateSupp; + ptTemplateSupp = NULL; + } + if(ptTemplateSelectInit) { + if (ptTemplateSelect) { + delete[] ptTemplateSelect; ptTemplateSelect = NULL; + } + } + } +} + +/*! + Display the warped reference template in an image. + + \param I: Image in which the warped zone has to be displayed. + \param col: Color used to draw the triangle edges. + \param thickness: Thickness of the lines. + + The following code shows how to use display capabilities: + \code +#include <visp/vpTemplateTrackerSSDInverseCompositional.h> +#include <visp/vpTemplateTrackerWarpHomography.h> + +int main() +{ + vpImage<unsigned char> I; + vpTemplateTrackerWarpHomography warp; + vpTemplateTrackerSSDInverseCompositional tracker(&warp); + vpTemplateTrackerZone zoneRef, zoneWarped; + + // Display the warped zone + tracker.display(I, vpColor::red); + + // Display the reference zone + zoneRef = tracker.getZoneRef(); + zoneRef.display(I, vpColor::green); + + // Display the warped zone + vpColVector p = tracker.getp(); + warp.warpZone(zoneRef, p, zoneWarped); + zoneWarped.display(I, vpColor::blue); +} + \endcode + */ +void vpTemplateTracker::display(const vpImage<unsigned char> &I, const vpColor& col, const unsigned int thickness) +{ + if (I.display) { // Only if a display is associated to the image + vpTemplateTrackerZone zoneWarped; + Warp->warpZone(*zoneTracked, p, zoneWarped); + zoneWarped.display(I, col, thickness); + } +} + +/*! + Display the warped reference template in an image. + + \param I: Image in which the warped zone has to be displayed. + \param col: Color used to draw the triangle edges. + \param thickness: Thickness of the lines. + + The following code shows how to use display capabilities: + \code +#include <visp/vpTemplateTrackerSSDInverseCompositional.h> +#include <visp/vpTemplateTrackerWarpHomography.h> + +int main() +{ + vpImage<vpRGBa> I; + vpTemplateTrackerWarpHomography warp; + vpTemplateTrackerSSDInverseCompositional tracker(&warp); + vpTemplateTrackerZone zoneRef, zoneWarped; + + // Display the warped zone + tracker.display(I, vpColor::red); + + // Display the reference zone + zoneRef = tracker.getZoneRef(); + zoneRef.display(I, vpColor::green); + + // Display the warped zone + vpColVector p = tracker.getp(); + warp.warpZone(zoneRef, p, zoneWarped); + zoneWarped.display(I, vpColor::blue); +} + \endcode + */ +void vpTemplateTracker::display(const vpImage<vpRGBa> &I, const vpColor& col, const unsigned int thickness) +{ + if (I.display) { // Only if a display is associated to the image + vpTemplateTrackerZone zoneWarped; + Warp->warpZone(*zoneTracked, p, zoneWarped); + zoneWarped.display(I, col, thickness); + } +} + +void vpTemplateTracker::computeOptimalBrentGain(const vpImage<unsigned char> &I,vpColVector &tp,double tMI,vpColVector &direction,double &alpha) +{ + vpColVector **ptp; + ptp=new vpColVector*[4]; + vpColVector p0(nbParam); + p0=tp; + + //valeur necessaire si conditionnel + vpColVector dpt(Warp->getNbParam()); + vpColVector adpt(Warp->getNbParam()); + + vpColVector p1(nbParam); + if(useCompositionnal) + { + if(useInverse) + Warp->getParamInverse(direction,dpt); + else + dpt=direction; + Warp->pRondp(tp,dpt,p1); + } + else + { + p1=tp+direction; + } + + vpColVector p2(nbParam); + if(useCompositionnal) + { + adpt=alpha*direction; + if(useInverse) + Warp->getParamInverse(adpt,dpt); + else + dpt=adpt; + Warp->pRondp(tp,dpt,p2); + } + else + { + p2=tp+alpha*direction; + } + vpColVector p3(nbParam); + ptp[0]=&p0; + ptp[1]=&p1; + ptp[2]=&p2; + ptp[3]=&p3; + + + double *Cost=new double[4]; + //Cost[0]=getCost(I,p0); + Cost[0]=tMI; + Cost[1]=getCost(I,p1); + Cost[2]=getCost(I,p2); + + + double *talpha=new double[4]; + talpha[0]=0; + talpha[1]=1.; + talpha[2]=alpha; + + //for(int i=0;i<3;i++) + // std::cout<<"alpha["<<i<<"] = "<<talpha[i]<<" Cost["<<i<<"] = "<<Cost[i]<<std::endl; + + //Utilise trois estimᅵes de paraboles succesive ... + //A changer pour rendre adaptable + for(unsigned int opt=0;opt<nbIterBrent;opt++) + { + //double a=talpha[0]; + //double b=talpha[1]; + //double c=talpha[2]; + //double Cost0=Cost[0]; + //double Cost1=Cost[1]; + //double Cost2=Cost[2]; + + vpMatrix A(3,3); + for(unsigned int i=0;i<3;i++){A[i][0]=talpha[i]*talpha[i];A[i][1]=talpha[i];A[i][2]=1.;} + //std::cout<<"A="<<A<<std::endl; + vpColVector B(3);for(unsigned int i=0;i<3;i++)B[i]=Cost[i]; + vpColVector parabol(3);parabol=(A.t()*A).inverseByLU()*A.t()*B; + //vpColVector parabol(3);parabol=A.pseudoInverse()*B; + + //si convexe + if(parabol[0]>0) + { + talpha[3]=-0.5*parabol[1]/parabol[0]; + //std::cout<<"parabol = "<<parabol<<std::endl; + //std::cout<<"convexe talpha = "<<talpha[3]<<std::endl; + } + else//si concave + { + int tindic_x_min=0; + int tindic_x_max=0; + for(int i=1;i<3;i++) + { + if(talpha[i]<talpha[tindic_x_min]) + tindic_x_min=i; + if(talpha[i]>talpha[tindic_x_max]) + tindic_x_max=i; + } + + if(Cost[tindic_x_max]<Cost[tindic_x_min]) + { + //talpha[3]=talpha[tindic_x_max]+1.; + talpha[3]=talpha[tindic_x_max]+1.; + /*if(talpha[tindic_x_min]>talpha[tindic_x_max]) + talpha[3]=talpha[tindic_x_min]+1.; + else + talpha[3]=talpha[tindic_x_min]-1.;*/ + } + else + { + //talpha[3]=talpha[tindic_x_min]-1.; + talpha[3]=talpha[tindic_x_min]-1.; + /*if(talpha[tindic_x_min]<talpha[tindic_x_max]) + talpha[3]=talpha[tindic_x_max]+1.; + else + talpha[3]=talpha[tindic_x_max]-1.;*/ + } + //std::cout<<"concave talpha="<<talpha[3]<<std::endl; + } + //std::cout<<"talpha="<<talpha[3]<<std::endl; + int indic_x_min=0; + int indic_x_max=0; + for(int i=1;i<3;i++) + { + if(talpha[i]<talpha[indic_x_min]) + indic_x_min=i; + if(talpha[i]>talpha[indic_x_max]) + indic_x_max=i; + } + //std::cout<<"talpha = "<<talpha[3]<<std::endl; + if(talpha[3]>talpha[indic_x_max]) + if((talpha[3]-talpha[indic_x_max])>alpha)talpha[3]=talpha[indic_x_max]+4.; + if(talpha[3]<talpha[indic_x_min]) + if((talpha[indic_x_min]-talpha[3])>alpha)talpha[3]=talpha[indic_x_min]-4.; + + /*if(((b-a)*(Cost1-Cost2)-(b-c)*(Cost1-Cost0))==0) + {Cost[3]=1000;break;} + + //calcul du gain correspondant au minimum de la parabole estimᅵe + talpha[3]=b-0.5*((b-a)*(b-a)*(Cost1-Cost2)-(b-c)*(b-c)*(Cost1-Cost0))/((b-a)*(Cost1-Cost2)-(b-c)*(Cost1-Cost0)); + int indic_x_min=0; + int indic_x_max=0; + for(int i=1;i<3;i++) + { + if(talpha[i]<talpha[indic_x_min]) + indic_x_min=i; + if(talpha[i]>talpha[indic_x_max]) + indic_x_max=i; + } + std::cout<<"talpha = "<<talpha[3]<<std::endl; + if(talpha[3]>talpha[indic_x_max]) + if((talpha[3]-talpha[indic_x_max])>alpha)talpha[3]=talpha[indic_x_max]+alpha; + if(talpha[3]<talpha[indic_x_min]) + if((talpha[indic_x_min]-talpha[3])>alpha)talpha[3]=talpha[indic_x_min]-alpha;*/ + + //p3=tp-talpha[3]*direction; + if(useCompositionnal) + { + adpt=talpha[3]*direction; + if(useInverse) + Warp->getParamInverse(adpt,dpt); + else + dpt=adpt; + Warp->pRondp(tp,dpt,p3); + } + else + { + p3=tp+talpha[3]*direction; + } + + Cost[3]=getCost(I,p3); + //std::cout<<"new cost="<<Cost[3]<<std::endl; + + int indice_f_max=0; + for(int i=1;i<4;i++) + if(Cost[i]>Cost[indice_f_max]) + indice_f_max=i; + if(indice_f_max!=3) + { + *ptp[indice_f_max]=*ptp[3]; + Cost[indice_f_max]=Cost[3]; + talpha[indice_f_max]=talpha[3]; + } + else + break; + } + + int indice_f_min=0; + for(int i=0;i<4;i++) + if(Cost[i]<Cost[indice_f_min]) + indice_f_min=i; + + alpha=talpha[indice_f_min]; + + if(alpha<1)alpha=1.; + + delete[] ptp; + delete[] Cost; + delete[] talpha; +} + + +/*! + \param nbLvl : Number of levels in the pyramid. + \param l0 : Pyramid level where the tracking is stopped. The level with the highest resolution is 0. + */ +void vpTemplateTracker::initPyramidal(unsigned int nbLvl, unsigned int l0) +{ + // vpTRACE("init_pyramidal"); + nbLvlPyr=nbLvl; + l0Pyr=l0; + + zoneTrackedPyr=new vpTemplateTrackerZone[nbLvlPyr]; + pyr_IDes=new vpImage<unsigned char>[nbLvlPyr]; + ptTemplatePyr=new vpTemplateTrackerPoint*[nbLvlPyr]; + ptTemplateSelectPyr=new bool*[nbLvlPyr]; + ptTemplateSuppPyr=new vpTemplateTrackerPointSuppMIInv*[nbLvlPyr]; + ptTemplateCompoPyr=new vpTemplateTrackerPointCompo*[nbLvlPyr]; + for(unsigned int i=0; i< nbLvlPyr; i++) { + ptTemplatePyr[i] = NULL; + ptTemplateSuppPyr[i] = NULL; + ptTemplateSelectPyr[i] = NULL; + ptTemplateCompoPyr[i] = NULL; + } + templateSizePyr=new unsigned int[nbLvlPyr]; + HdesirePyr=new vpMatrix[nbLvlPyr]; + HLMdesirePyr=new vpMatrix[nbLvlPyr]; + HLMdesireInversePyr=new vpMatrix[nbLvlPyr]; + + pyrInitialised=true; + // vpTRACE("fin init_pyramidal"); + +} +void vpTemplateTracker::initTrackingPyr(const vpImage<unsigned char>& I,vpTemplateTrackerZone &zone) +{ + // vpTRACE("initTrackingPyr"); + zoneTrackedPyr[0].copy(zone); + //vpTRACE("fin copy zone"); + + pyr_IDes[0]=I; + initTracking(pyr_IDes[0],zoneTrackedPyr[0]); + ptTemplatePyr[0]=ptTemplate; + ptTemplateSelectPyr[0]=ptTemplateSelect; + templateSizePyr[0]=templateSize; + + //creationpyramide de zones et images desiree + if(nbLvlPyr>1) + { + for(unsigned int i=1;i<nbLvlPyr;i++) + { + zoneTrackedPyr[i]=zoneTrackedPyr[i-1].getPyramidDown(); + vpImageFilter::getGaussPyramidal(pyr_IDes[i-1],pyr_IDes[i]); + + initTracking(pyr_IDes[i],zoneTrackedPyr[i]); + ptTemplatePyr[i]=ptTemplate; + ptTemplateSelectPyr[i]=ptTemplateSelect; + templateSizePyr[i]=templateSize; + //reste probleme avec le Hessien + } + } + /*for(int i=0;i<nbLvlPyr;i++) + { + vpColVector ptemp(8);ptemp=0; + zoneTracked=&zoneTrackedPyr[i]; + init_pos_evalRMS(ptemp); + }*/ + zoneTracked=&zoneTrackedPyr[0]; + + // vpTRACE("fin initTrackingPyr"); + +} + +/*! + Select the reference template in image \e I using mouse click. + + \param I: Image containing the reference template. + \param delaunay: Flag used to enable Delaunay triangulation. + - If true, from the image points selected by the user, a Delaunay triangulation is performed + to initialize the reference template. + - A left click select a image point; + - A right click select the last image point and ends the initialisation stage. + - If false, the user select directly points as successive triangle corners. + The size of \e v_ip vector should be a multiple of 3. It is not mandatory + that triangles have one edge in common; they can define a discontinued area. + - A left click select a triangle corner; + - A right click select the last triangle corner and ends the initialisation stage. + For example, to select the reference template as two triangles, the user has to left click + five times and finish the selection on the sixth corner with a right click. + + */ +void vpTemplateTracker::initClick(const vpImage<unsigned char> &I, bool delaunay) +{ + zoneRef_.initClick(I, delaunay); + + if (nbLvlPyr > 1) { + initPyramidal(nbLvlPyr, l0Pyr); + initTrackingPyr(I, zoneRef_); + initHessienDesiredPyr(I); + } + else { + initTracking(I, zoneRef_); + initHessienDesired(I); +// trackNoPyr(I); + } +} + +/*! + Initialize the reference template from a vector of points. + + \param I: Image containing the reference template. + \param v_ip: Vector of image points defining the reference template. + \param delaunay: + - If true, from the image points defining the reference template enable Delaunay triangulation. + - If false, the vector of image points define the reference template as a list of triangles. + The size of \e v_ip vector should be a multiple of 3. + */ +void vpTemplateTracker::initFromPoints(const vpImage<unsigned char> &I, const std::vector< vpImagePoint > &v_ip, bool delaunay) +{ + zoneRef_.initFromPoints(I, v_ip, delaunay); + + if (nbLvlPyr > 1) { + initPyramidal(nbLvlPyr, l0Pyr); + initTrackingPyr(I, zoneRef_); + initHessienDesiredPyr(I); + } + else { + initTracking(I, zoneRef_); + initHessienDesired(I); + //trackNoPyr(I); + } +} + +/*! + Initialize the reference template from a vector of points. + + \param I: Image containing the reference template. + \param zone: The zone that describes the reference template. + */ +void vpTemplateTracker::initFromZone(const vpImage<unsigned char> &I, const vpTemplateTrackerZone& zone) +{ + zoneRef_ = zone; + + if (nbLvlPyr > 1) { + initPyramidal(nbLvlPyr, l0Pyr); + initTrackingPyr(I, zoneRef_); + initHessienDesiredPyr(I); + } + else { + initTracking(I, zoneRef_); + initHessienDesired(I); +// trackNoPyr(I); + } +} + +void vpTemplateTracker::initCompInversePyr(const vpImage<unsigned char> &I) +{ + vpTRACE("initCompInversePyr"); + templateSize=templateSizePyr[0]; + //ptTemplateSupp=ptTemplateSuppPyr[0]; + //ptTemplateCompo=ptTemplateCompoPyr[0]; + ptTemplate=ptTemplatePyr[0]; + ptTemplateSelect=ptTemplateSelectPyr[0]; + initCompInversePyr(I); + ptTemplateSuppPyr[0]=ptTemplateSupp; + ptTemplateCompoPyr[0]=ptTemplateCompo; + + + if(nbLvlPyr>1) + { + vpImage<unsigned char> Itemp=I; + for(unsigned int i=1;i<nbLvlPyr;i++) + { + vpImageFilter::getGaussPyramidal(Itemp,Itemp); + + templateSize=templateSizePyr[i]; + ptTemplate=ptTemplatePyr[i]; + ptTemplateSelect=ptTemplateSelectPyr[i]; + initCompInversePyr(Itemp); + ptTemplateSuppPyr[i]=ptTemplateSupp; + ptTemplateCompoPyr[i]=ptTemplateCompo; + } + } + // vpTRACE("fin initCompInversePyr"); + +} +void vpTemplateTracker::initHessienDesiredPyr(const vpImage<unsigned char> &I) +{ + // vpTRACE("initHessienDesiredPyr"); + + templateSize=templateSizePyr[0]; + //ptTemplateSupp=ptTemplateSuppPyr[0]; + //ptTemplateCompo=ptTemplateCompoPyr[0]; + ptTemplate=ptTemplatePyr[0]; + ptTemplateSelect=ptTemplateSelectPyr[0]; +// ptTemplateSupp=new vpTemplateTrackerPointSuppMIInv[templateSize]; + try{ + initHessienDesired(I); + ptTemplateSuppPyr[0]=ptTemplateSupp; + ptTemplateCompoPyr[0]=ptTemplateCompo; + HdesirePyr[0]=Hdesire; + HLMdesirePyr[0]=HLMdesire; + HLMdesireInversePyr[0]=HLMdesireInverse; + } + catch(vpException &e){ + ptTemplateSuppPyr[0]=ptTemplateSupp; + ptTemplateCompoPyr[0]=ptTemplateCompo; + HdesirePyr[0]=Hdesire; + HLMdesirePyr[0]=HLMdesire; + HLMdesireInversePyr[0]=HLMdesireInverse; + throw(e); + } + + if(nbLvlPyr>1) + { + vpImage<unsigned char> Itemp;Itemp=I; + for(unsigned int i=1;i<nbLvlPyr;i++) + { + vpImageFilter::getGaussPyramidal(Itemp,Itemp); + + templateSize=templateSizePyr[i]; + ptTemplate=ptTemplatePyr[i]; + ptTemplateSelect=ptTemplateSelectPyr[i]; + //ptTemplateSupp=ptTemplateSuppPyr[i]; + //ptTemplateCompo=ptTemplateCompoPyr[i]; + try{ + initHessienDesired(Itemp); + ptTemplateSuppPyr[i]=ptTemplateSupp; + ptTemplateCompoPyr[i]=ptTemplateCompo; + HdesirePyr[i]=Hdesire; + HLMdesirePyr[i]=HLMdesire; + HLMdesireInversePyr[i]=HLMdesireInverse; + } + catch(vpException &e){ + ptTemplateSuppPyr[i]=ptTemplateSupp; + ptTemplateCompoPyr[i]=ptTemplateCompo; + HdesirePyr[i]=Hdesire; + HLMdesirePyr[i]=HLMdesire; + HLMdesireInversePyr[i]=HLMdesireInverse; + throw(e); + } + } + } + // vpTRACE("fin initHessienDesiredPyr"); +} + +/*! + Track the template on image \e I. + \param I: Image to process. + */ +void vpTemplateTracker::track(const vpImage<unsigned char> &I) +{ + if (nbLvlPyr > 1) + trackPyr(I); + else + trackNoPyr(I); +} + +void vpTemplateTracker::trackPyr(const vpImage<unsigned char> &I) +{ + //vpTRACE("trackPyr"); + vpImage<unsigned char> *pyr_I; +// pyr_I=new vpImage<unsigned char>[nbLvlPyr+1]; // Why +1 ? + pyr_I=new vpImage<unsigned char>[nbLvlPyr]; // Why +1 ? + pyr_I[0]=I; + + try + { + vpColVector ptemp(nbParam); + if(nbLvlPyr>1) + { + // vpColVector *p_sauv=new vpColVector[nbLvlPyr]; + // for(unsigned int i=0;i<nbLvlPyr;i++)p_sauv[i].resize(nbParam); + + // p_sauv[0]=p; + for(unsigned int i=1;i<nbLvlPyr;i++) + { + vpImageFilter::getGaussPyramidal(pyr_I[i-1],pyr_I[i]); + //test getParamPyramidDown + /*vpColVector vX_test(2);vX_test[0]=15.;vX_test[1]=30.; + vpColVector vX_test2(2); + Warp->computeCoeff(p); + Warp->computeDenom(vX_test,p); + Warp->warpX(vX_test,vX_test2,p); + std::cout<<"p = "<<p.t()<<std::endl;*/ + //std::cout<<"get p down"<<std::endl; + Warp->getParamPyramidDown(p,ptemp); + p=ptemp; + zoneTracked=&zoneTrackedPyr[i]; + + // p_sauv[i]=p; + /*std::cout<<"p_down = "<<p.t()<<std::endl; + + vpColVector vX_testd(2);vX_testd[0]=15./2.;vX_testd[1]=30./2.; + vpColVector vX_testd2(2); + Warp->computeCoeff(p); + Warp->computeDenom(vX_testd,p); + Warp->warpX(vX_testd,vX_testd2,p); + std::cout<<2.*vX_testd2[0]<<","<<2.*vX_testd2[1]<<" <=> "<<vX_test2[0]<<","<<vX_test2[1]<<std::endl;*/ + + } + + for(int i=(int)nbLvlPyr-1;i>=0;i--) + { + if(i>=(int)l0Pyr) + { + templateSize=templateSizePyr[i]; + ptTemplate=ptTemplatePyr[i]; + ptTemplateSelect=ptTemplateSelectPyr[i]; + ptTemplateSupp=ptTemplateSuppPyr[i]; + ptTemplateCompo=ptTemplateCompoPyr[i]; + H=HdesirePyr[i]; + HLM=HLMdesirePyr[i]; + HLMdesireInverse=HLMdesireInversePyr[i]; + // zoneTracked=&zoneTrackedPyr[i]; + trackRobust(pyr_I[i]); + } + //std::cout<<"get p up"<<std::endl; + // ptemp=p_sauv[i-1]; + if (i > 0) { + Warp->getParamPyramidUp(p,ptemp); + p=ptemp; + zoneTracked=&zoneTrackedPyr[i-1]; + } + } + #if 0 + if(l0Pyr==0) + { + templateSize=templateSizePyr[0]; + ptTemplate=ptTemplatePyr[0]; + ptTemplateSelect=ptTemplateSelectPyr[0]; + ptTemplateSupp=ptTemplateSuppPyr[0]; + ptTemplateCompo=ptTemplateCompoPyr[0]; + H=HdesirePyr[0]; + HLM=HLMdesirePyr[0]; + HLMdesireInverse=HLMdesireInversePyr[0]; + zoneTracked=&zoneTrackedPyr[0]; + trackRobust(pyr_I[0]); + } + + if (l0Pyr > 0) { + // for (int l=(int)l0Pyr; l >=0; l--) { + // Warp->getParamPyramidUp(p,ptemp); + // p=ptemp; + // } + zoneTracked=&zoneTrackedPyr[0]; + } + #endif + // delete [] p_sauv; + } + else + { + //std::cout<<"reviens a tracker de base"<<std::endl; + trackRobust(I); + } + delete[] pyr_I; + } + catch(vpException &e){ + delete[] pyr_I; + throw(vpTrackingException(vpTrackingException::badValue, e.getMessage())); + } +} + +void vpTemplateTracker::trackRobust(const vpImage<unsigned char> &I) +{ + if(costFunctionVerification) + { + vpColVector p_pre_estimation;p_pre_estimation=p; + getGaussianBluredImage(I); + double pre_fcost=getCost(I,p); + + trackNoPyr(I); + + //std::cout<<"fct avant : "<<pre_fcost<<std::endl; + double post_fcost=getCost(I,p); + //std::cout<<"fct apres : "<<post_fcost<<std::endl<<std::endl; + if(pre_fcost<post_fcost) + p=p_pre_estimation; + } + else + trackNoPyr(I); +} diff --git a/src/tracking/template-tracker/vpTemplateTracker.h b/src/tracking/template-tracker/vpTemplateTracker.h new file mode 100644 index 0000000000000000000000000000000000000000..cac7730fb2b84b76f25449e4509cc077dfd823c1 --- /dev/null +++ b/src/tracking/template-tracker/vpTemplateTracker.h @@ -0,0 +1,234 @@ +/**************************************************************************** + * + * $Id: vpTemplateTracker.h 5113 2015-01-05 08:00:50Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTracker.h + \brief +*/ + +#ifndef vpTemplateTracker_hh +#define vpTemplateTracker_hh + +#include <math.h> + +#include <visp/vpTemplateTrackerHeader.h> +#include <visp/vpTemplateTrackerZone.h> +#include <visp/vpTemplateTrackerWarp.h> +#include <visp/vpImageFilter.h> + +class VISP_EXPORT vpTemplateTracker +{ + protected: + //traitement pyramidal + unsigned int nbLvlPyr; // If = 1, disable pyramidal usage + unsigned int l0Pyr; + bool pyrInitialised; + + vpTemplateTrackerPoint *ptTemplate; + vpTemplateTrackerPoint **ptTemplatePyr; + bool ptTemplateInit; + unsigned int templateSize; + unsigned int *templateSizePyr; + bool *ptTemplateSelect; + bool **ptTemplateSelectPyr; + bool ptTemplateSelectInit; + unsigned int templateSelectSize; + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + vpTemplateTrackerPointSuppMIInv *ptTemplateSupp; //pour inverse et compo + vpTemplateTrackerPointSuppMIInv **ptTemplateSuppPyr; //pour inverse et compo + #endif + + vpTemplateTrackerPointCompo *ptTemplateCompo; //pour ESM + vpTemplateTrackerPointCompo **ptTemplateCompoPyr; //pour ESM + vpTemplateTrackerZone *zoneTracked; + vpTemplateTrackerZone *zoneTrackedPyr; + + vpImage<unsigned char> *pyr_IDes; + + vpMatrix H; + vpMatrix Hdesire; + vpMatrix *HdesirePyr; + vpMatrix HLM; + vpMatrix HLMdesire; + vpMatrix *HLMdesirePyr; + vpMatrix HLMdesireInverse; + vpMatrix *HLMdesireInversePyr; + vpColVector G; + + double gain; + double thresholdGradient; + bool costFunctionVerification; + bool blur; + bool useBrent; + unsigned int nbIterBrent; + unsigned int taillef; + double *fgG; + double *fgdG; + double ratioPixelIn; + int mod_i; + int mod_j;//variable de sampling de zone de reference + unsigned int nbParam ; + double lambdaDep ; + unsigned int iterationMax ; + //pour BFGS + unsigned int iterationGlobale; + //diverge is set to true if there is no more point in the tracked area + bool diverge; + unsigned int nbIteration; + bool useCompositionnal; + bool useInverse; + + vpTemplateTrackerWarp *Warp; + //Parametre de deplacement + vpColVector p; + vpColVector dp; + + //temporary values for warping + vpColVector X1; + vpColVector X2; + //temporary derivative matrix + vpMatrix dW; + + vpImage<double> BI; + vpImage<double> dIx ; + vpImage<double> dIy ; + vpTemplateTrackerZone zoneRef_; // Reference zone + + public: + vpTemplateTracker(vpTemplateTrackerWarp *_warp); + virtual ~vpTemplateTracker(); + + void display(const vpImage<unsigned char> &I, const vpColor& col = vpColor::green, const unsigned int thickness=3); + void display(const vpImage<vpRGBa> &I, const vpColor& col = vpColor::green, const unsigned int thickness=3); + + bool getDiverge() const {return diverge;} + vpColVector getdp(){ return dp; } + vpColVector getG() const { return G; } + vpMatrix getH() const { return H; } + unsigned int getNbParam() const { return nbParam ; } + unsigned int getNbIteration() const { return nbIteration; } + vpColVector getp() const { return p;} + double getRatioPixelIn() const {return ratioPixelIn;} + + /*! + + \return The pointer to the warper. + */ + vpTemplateTrackerWarp *getWarp() const {return Warp;} + + /*! + Return the reference template zone. + */ + vpTemplateTrackerZone getZoneRef() const { return zoneRef_; } + + void initClick(const vpImage<unsigned char> &I, bool delaunay=false); + void initFromPoints(const vpImage<unsigned char> &I, const std::vector< vpImagePoint > &v_ip, bool delaunay=false); + void initFromZone(const vpImage<unsigned char> &I, const vpTemplateTrackerZone& zone); + + void resetTracker(); + + void setBlur(bool b){blur = b;} + void setCostFunctionVerification(bool b){costFunctionVerification = b;} + void setGain(double g){gain=g;} + void setGaussianFilterSize(unsigned int new_taill); + void setHDes(vpMatrix &tH){ Hdesire=tH; vpMatrix::computeHLM(Hdesire,lambdaDep,HLMdesire); HLMdesireInverse = HLMdesire.inverseByLU();} + /*! + Set the maximum number of iteration of the estimation scheme. + \param n : Maximum number of iterations to stop the estimation scheme. A typical value is arround 100. + */ + void setIterationMax(const unsigned int &n) { iterationMax = n ; } + /*! + Set the convergence gain used in the estimation scheme. + \param l : Gain. A typical value is 0.001. + */ + void setLambda(double l) { lambdaDep = l ; } + void setNbIterBrent(const unsigned int &b){nbIterBrent=b;} + void setp(const vpColVector &tp){ p=tp; diverge=false; iterationGlobale=0; } + /*! + Set the number of pyramid levels used in the multi-resolution scheme. + If \e nlevels > 1, the tracker uses a pyramidal approach. + + \param nlevels : Number of pyramid levels. Algorithm starts at level nlevels-1. + \param level_to_stop : Last level of the pyramid that will be considered. Lowest level is zero. + */ + void setPyramidal(unsigned int nlevels=2, unsigned int level_to_stop=1) { + nbLvlPyr = nlevels; + l0Pyr = level_to_stop; + if(l0Pyr >= nlevels){ + std::cout << "Warning: level_to_stop: " << level_to_stop << " higher than level_to_start: " << nlevels-1 << " (nlevels-1)" <<std::endl; + std::cout << "Level to stop put to: " << nlevels-1 << std::endl; + l0Pyr = nlevels-1; + } + } + /*! + Set the pixel sampling parameters along the rows and the columns. + \param sample_i : Sampling factor along the rows. + If 1 all the lines are considered. If 2, consider one line over two. + + \param sample_j : Sampling factor along the columns. + If 1 all the columns are considered. If 2, consider one column over two. + */ + void setSampling(int sample_i,int sample_j){mod_i=sample_i; mod_j=sample_j;} + void setThresholdGradient(double threshold){thresholdGradient=threshold;} + /*! By default Brent usage is disabled. */ + void setUseBrent(bool b){useBrent = b;} + + void track(const vpImage<unsigned char> &I); + void trackRobust(const vpImage<unsigned char> &I); + + protected: + + void computeOptimalBrentGain(const vpImage<unsigned char> &I,vpColVector &tp,double tMI,vpColVector &direction,double &alpha); + virtual double getCost(const vpImage<unsigned char> &I, vpColVector &tp) = 0; + void getGaussianBluredImage(const vpImage<unsigned char> &I){ vpImageFilter::filter(I, BI,fgG,taillef); } + void initCompInverse(const vpImage<unsigned char> &I); + virtual void initCompInversePyr(const vpImage<unsigned char> &I); + virtual void initHessienDesired(const vpImage<unsigned char> &I)=0; + virtual void initHessienDesiredPyr(const vpImage<unsigned char> &I); + virtual void initPyramidal(unsigned int nbLvl,unsigned int l0); + void initTracking(const vpImage<unsigned char>& I,vpTemplateTrackerZone &zone); + virtual void initTrackingPyr(const vpImage<unsigned char>& I,vpTemplateTrackerZone &zone); + virtual void trackNoPyr(const vpImage<unsigned char> &I) = 0; + virtual void trackPyr(const vpImage<unsigned char> &I); +}; +#endif + diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23333ef44bf5a03f3012aee974df6be6894d308e --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarp.cpp 4782 2014-07-15 13:04:19Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarp.h> + +void vpTemplateTrackerWarp::warpTriangle(const vpTemplateTrackerTriangle &in,const vpColVector &p, vpTemplateTrackerTriangle &out) +{ + if (p.size() < 2) { + vpCTRACE << "Bad template tracker warp parameters dimension. Should never occur. " << std::endl; + throw(vpException(vpException::dimensionError, "Bad template tracker warp parameters dimension")); + } + vpColVector S1(2),S2(2),S3(2); + vpColVector rS1(2),rS2(2),rS3(2); + in.getCorners(S1,S2,S3); + computeDenom(S1,p); + warpX(S1,rS1,p); + computeDenom(S2,p); + warpX(S2,rS2,p); + computeDenom(S3,p); + warpX(S3,rS3,p); + out.init(rS1,rS2,rS3); +} +void vpTemplateTrackerWarp::warpZone(const vpTemplateTrackerZone &in,const vpColVector &p, vpTemplateTrackerZone &out) +{ + vpTemplateTrackerTriangle TR,TT; + out.clear(); + for(unsigned int i=0;i<in.getNbTriangle();i++) + { + in.getTriangle(i,TR); + warpTriangle(TR,p,TT); + out.add(TT); + } +} + +double vpTemplateTrackerWarp::getDistanceBetweenZoneAndWarpedZone(const vpTemplateTrackerZone &Z, const vpColVector &p) +{ + unsigned int nb_corners = Z.getNbTriangle() * 3; + computeCoeff(p); + vpColVector X1(2),X2(2); + + double res=0; + vpTemplateTrackerTriangle triangle; + for(unsigned int i=0;i<Z.getNbTriangle();i++) + { + Z.getTriangle(i, triangle); + for (unsigned int j=0; j<3; j++) { + triangle.getCorner(j, X1[0], X1[1]); + + computeDenom(X1,p); + warpX(X1,X2,p); + res+=sqrt((X2[0]-X1[0])*(X2[0]-X1[0])+(X2[1]-X1[1])*(X2[1]-X1[1])); + } + } + + return res/nb_corners; +} + +void vpTemplateTrackerWarp::warp(const double *ut0,const double *vt0,int nb_pt,const vpColVector& p,double *u,double *v) +{ + computeCoeff(p); + vpColVector X1(2),X2(2); + for(int i=0;i<nb_pt;i++) + { + X1[0]=ut0[i]; + X1[1]=vt0[i]; + computeDenom(X1,p); + warpX(X1,X2,p); + u[i]=X2[0]; + v[i]=X2[1]; + //std::cout<<"warp "<<X2[0]<<","<<X2[1]<<std::endl; + } +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +void vpTemplateTrackerWarp::findWarp(const double *ut0,const double *vt0,const double *u,const double *v,int nb_pt,vpColVector& p) +{ + vpMatrix dW_(2,nbParam); + vpMatrix dX(2,1); + vpMatrix H(nbParam,nbParam), HLM(nbParam,nbParam); + vpMatrix G(nbParam,1); + + int cpt=0; + vpColVector X1(2); + vpColVector fX1(2); + vpColVector X2(2); + double erreur=0; + double erreur_prec; + double lambda=0.01; + do + { + erreur_prec=erreur; + H=0; + G=0; + erreur=0; + computeCoeff(p); + for(int i=0;i<nb_pt;i++) + { + X1[0]=ut0[i]; + X1[1]=vt0[i]; + computeDenom(X1,p); + warpX(X1,fX1,p); + dWarp(X1,fX1,p,dW_); + H+=dW_.AtA(); + + X2[0]=u[i]; + X2[1]=v[i]; + + dX=X2-fX1; + G+=dW_.t()*dX; + + erreur+=((u[i]-fX1[0])*(u[i]-fX1[0])+(v[i]-fX1[1])*(v[i]-fX1[1])); + + } + + vpMatrix::computeHLM(H, lambda, HLM); + try{ + p+=HLM.inverseByLU()*G; + } + catch(vpException &e) { + //std::cout<<"Cannot inverse the matrix by LU " << std::endl; + throw(e); + } + cpt++; + } + while((cpt<150)&&(sqrt((erreur_prec-erreur)*(erreur_prec-erreur))>1e-20)); + //std::cout<<"erreur apres transformation="<<erreur<<std::endl; +} +#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS + diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.h new file mode 100644 index 0000000000000000000000000000000000000000..74f527333effc53086fbd6602c4f2c287f0a4a7d --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarp.h @@ -0,0 +1,245 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarp.h 4632 2014-02-03 17:06:40Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarp.h + \brief +*/ + + +#ifndef vpTemplateTrackerWarp_hh +#define vpTemplateTrackerWarp_hh + +#include <visp/vpDisplay.h> +#include <visp/vpTemplateTrackerHeader.h> +#include <visp/vpTemplateTrackerTriangle.h> +#include <visp/vpTemplateTrackerZone.h> +#include <visp/vpTrackingException.h> + +class VISP_EXPORT vpTemplateTrackerWarp +{ + protected: + double denom; + vpMatrix dW; + unsigned int nbParam ; + + public: + //constructor; + vpTemplateTrackerWarp() : denom(1.), dW(), nbParam(0) {} + virtual ~vpTemplateTrackerWarp(){} + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + virtual void computeCoeff(const vpColVector &p)=0; + virtual void computeDenom(vpColVector &vX, const vpColVector &ParamM)=0; + #endif + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + virtual void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW) = 0; + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + virtual void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW) = 0; + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void findWarp(const double *ut0,const double *vt0,const double *u,const double *v,int nb_pt,vpColVector& p); + #endif + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis (along the columns) of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + virtual void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW) = 0; + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + virtual void getdWdp0(const int &i,const int &j,double *dIdW) = 0; + + /*! + Compute the distance between a zone and its associated warped zone. + + \param Z : Zone to consider. + \param p : Parameters of the warping function. + */ + double getDistanceBetweenZoneAndWarpedZone(const vpTemplateTrackerZone &Z,const vpColVector &p); + + /*! + Get the number of parameters of the warping function. + + \return Number of parameters. + */ + unsigned int getNbParam() const {return nbParam;} + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + virtual void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const = 0; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + virtual void getParamPyramidDown(const vpColVector &p,vpColVector &pdown) =0; + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + virtual void getParamPyramidUp(const vpColVector &p,vpColVector &pup) =0; + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + virtual bool isESMcompatible() const =0; + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + virtual void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const = 0; + + /*! + Set the number of parameters of the warping function. + + \param nb : New number of parameters. + */ + void setNbParam(unsigned int nb){nbParam=nb;dW.resize(2,nbParam);} + + /*! + Warp a list of points. + + \param ut0 : List of u coordinates of the points. + \param vt0 : List of v coordinates of the points. + \param nb_pt : Number of points to consider. + \param p : Parameters of the warp. + \param u : Resulting u coordinates. + \param v : resulting v coordinates. + */ + void warp(const double *ut0,const double *vt0,int nb_pt,const vpColVector& p,double *u,double *v); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the rows) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warp. + */ + virtual void warpX(const int &i, const int &j,double &i2,double &j2, const vpColVector &ParamM) = 0; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + virtual void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) = 0; + + /*! + Inverse Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + virtual void warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) = 0; + + /*! + Warp a triangle and store the result in a new zone. + + \param in : Triangle to warp. + \param p : Parameters of the warping function. These parameters are estimated by the template + tracker and returned using vpTemplateTracker::getp(). + \param out : Resulting triangle. + */ + void warpTriangle(const vpTemplateTrackerTriangle &in,const vpColVector &p, vpTemplateTrackerTriangle &out); + + /*! + Warp a zone and store the result in a new zone. + + \param in : Zone to warp. + \param p : Parameters of the warping function. These parameters are estimated by the template + tracker and returned using vpTemplateTracker::getp(). + \param out : Resulting zone. + */ + void warpZone(const vpTemplateTrackerZone &in,const vpColVector &p, vpTemplateTrackerZone &out); + +}; + +#endif + diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..078f13cf5450ad857909bd922e7b28feb0affaca --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpAffine.cpp 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarpAffine.h> + + +vpTemplateTrackerWarpAffine::vpTemplateTrackerWarpAffine() +{ + nbParam = 6 ; + dW.resize(2,nbParam); +} + +//get the parameter corresponding to the lower level of a gaussian pyramid +void vpTemplateTrackerWarpAffine::getParamPyramidDown(const vpColVector &p,vpColVector &pdown) +{ + pdown=p; + pdown[4]=p[4]/2.; + pdown[5]=p[5]/2.; +} + +void vpTemplateTrackerWarpAffine::getParamPyramidUp(const vpColVector &p,vpColVector &pup) +{ + pup=p; + pup[4]=p[4]*2.; + pup[5]=p[5]*2.; +} +/*calcul de di*dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpAffine::getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW) +{ + dIdW[0]=j*dx; + dIdW[1]=j*dy; + dIdW[2]=i*dx; + dIdW[3]=i*dy; + dIdW[4]=dx; + dIdW[5]=dy; +} +/*calcul de dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpAffine::getdWdp0(const int &i,const int &j,double *dIdW) +{ + dIdW[0]=j; + dIdW[1]=0; + dIdW[2]=i; + dIdW[3]=0; + dIdW[4]=1.; + dIdW[5]=0; + + dIdW[6]=0; + dIdW[7]=j; + dIdW[8]=0; + dIdW[9]=i; + dIdW[10]=0; + dIdW[11]=1.; +} + +void vpTemplateTrackerWarpAffine::warpX(const int &i, const int &j,double &i2,double &j2, const vpColVector &ParamM) +{ + j2=(1+ParamM[0])*j+ParamM[2]*i+ParamM[4]; + i2=ParamM[1]*j+(1+ParamM[3])*i+ParamM[5]; +} + + +void vpTemplateTrackerWarpAffine::warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + vXres[0]=(1.0+ParamM[0])*vX[0]+ParamM[2]*vX[1]+ParamM[4]; + vXres[1]=ParamM[1]*vX[0]+(1.0+ParamM[3])*vX[1]+ParamM[5]; +} + +void vpTemplateTrackerWarpAffine::dWarp(const vpColVector &X1,const vpColVector &/*X2*/,const vpColVector &/*ParamM*/,vpMatrix &dW_) +{ + double j=X1[0]; + double i=X1[1]; + dW_=0; + dW_[0][0]=j;dW_[0][2]=i;dW_[0][4]=1; + dW_[1][1]=j;dW_[1][3]=i;dW_[1][5]=1; +} + +/*compute dw=dw/dx*dw/dp +*/ +void vpTemplateTrackerWarpAffine::dWarpCompo(const vpColVector &/*X1*/,const vpColVector &/*X2*/,const vpColVector &ParamM, + const double *dwdp0, vpMatrix &dW_) +{ + for(unsigned int i=0;i<nbParam;i++) + { + dW_[0][i]=(1.+ParamM[0])*dwdp0[i]+ParamM[2]*dwdp0[i+nbParam]; + dW_[1][i]=ParamM[1]*dwdp0[i]+(1.+ParamM[3])*dwdp0[i+nbParam]; + } +} + +void vpTemplateTrackerWarpAffine::warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + vXres[0]=(1+ParamM[0])*vX[0]+ParamM[2]*vX[1]+ParamM[4]; + vXres[1]=ParamM[1]*vX[0]+(1+ParamM[3])*vX[1]+ParamM[5]; +} +void vpTemplateTrackerWarpAffine::getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const +{ + vpColVector Trans(2); + vpMatrix MWrap(2,2); + Trans[0]=ParamM[4];Trans[1]=ParamM[5]; + MWrap[0][0]=1+ParamM[0];MWrap[0][1]=ParamM[2]; + MWrap[1][0]=ParamM[1];MWrap[1][1]=1+ParamM[3]; + + vpMatrix MWrapInv(2,2);MWrapInv=MWrap.inverseByLU(); + vpColVector TransInv(2);TransInv=-1*MWrapInv*Trans; + + ParamMinv[0]=MWrapInv[0][0]-1;ParamMinv[2]=MWrapInv[0][1]; + ParamMinv[1]=MWrapInv[1][0];ParamMinv[3]=MWrapInv[1][1]-1; + ParamMinv[4]=TransInv[0];ParamMinv[5]=TransInv[1]; +} + +void vpTemplateTrackerWarpAffine::pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const +{ + vpColVector Trans1(2); + vpMatrix MWrap1(2,2); + Trans1[0]=p1[4];Trans1[1]=p1[5]; + MWrap1[0][0]=1+p1[0];MWrap1[0][1]=p1[2]; + MWrap1[1][0]=p1[1];MWrap1[1][1]=1+p1[3]; + + vpColVector Trans2(2); + vpMatrix MWrap2(2,2); + Trans2[0]=p2[4];Trans2[1]=p2[5]; + MWrap2[0][0]=1+p2[0];MWrap2[0][1]=p2[2]; + MWrap2[1][0]=p2[1];MWrap2[1][1]=1+p2[3]; + + vpColVector TransRes(2); + vpMatrix MWrapRes(2,2); + TransRes=MWrap1*Trans2+Trans1; + MWrapRes=MWrap1*MWrap2; + + pres[0]=MWrapRes[0][0]-1;pres[2]=MWrapRes[0][1]; + pres[1]=MWrapRes[1][0];pres[3]=MWrapRes[1][1]-1; + pres[4]=TransRes[0];pres[5]=TransRes[1]; +} diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.h new file mode 100644 index 0000000000000000000000000000000000000000..0c60a29fe2c3940df995be5ae6a2d8e79b82a2d6 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpAffine.h @@ -0,0 +1,175 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpAffine.h 4607 2014-01-21 16:02:11Z ayol $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarpAffine.h + \brief Affine warping function: w(X)=AX+b with: A=[[1+p0, p2], [p1, 1+p3]] and b= [p4, p5]] +*/ + + +#ifndef vpTemplateTrackerWarpAffine_hh +#define vpTemplateTrackerWarpAffine_hh + +#include <visp/vpTemplateTrackerWarp.h> + + +class VISP_EXPORT vpTemplateTrackerWarpAffine: public vpTemplateTrackerWarp +{ + public: + //constructor; + vpTemplateTrackerWarpAffine(); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void computeCoeff(const vpColVector &/*p*/){} + void computeDenom(vpColVector &/*vX*/, const vpColVector &/*ParamM*/){} + #endif + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW); + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW); + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const int &i,const int &j,double *dIdW); + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + void getParamPyramidDown(const vpColVector &p,vpColVector &pdown); + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + void getParamPyramidUp(const vpColVector &p,vpColVector &pup); + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + bool isESMcompatible() const {return false;} + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the columns) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM); + + /*! + Inverse Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); +}; +#endif diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46ad0f129a1e08276bbb2be49e924dca0be4a460 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpHomography.cpp 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarpHomography.h> +#include <visp/vpTrackingException.h> + +vpTemplateTrackerWarpHomography::vpTemplateTrackerWarpHomography() +{ + nbParam = 8 ; + dW.resize(2,nbParam); +} + +//get the parameter corresponding to the lower level of a gaussian pyramid +void vpTemplateTrackerWarpHomography::getParamPyramidDown(const vpColVector &p,vpColVector &pdown) +{ + pdown=p; + pdown[2]=p[2]*2.; + pdown[5]=p[5]*2.; + pdown[6]=p[6]/2.; + pdown[7]=p[7]/2.; +} + +void vpTemplateTrackerWarpHomography::getParamPyramidUp(const vpColVector &p,vpColVector &pup) +{ + pup=p; + pup[2]=p[2]/2.; + pup[5]=p[5]/2.; + pup[6]=p[6]*2.; + pup[7]=p[7]*2.; +} + +/*calcul de di*dw(x,p0)/dp */ +void vpTemplateTrackerWarpHomography::getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW) +{ + dIdW[0]=j*dx; + dIdW[1]=j*dy; + dIdW[2]=-j*j*dx-i*j*dy; + dIdW[3]=i*dx; + dIdW[4]=i*dy; + dIdW[5]=-i*j*dx-i*i*dy; + dIdW[6]=dx; + dIdW[7]=dy; +} +/*calcul de dw(x,p0)/dp */ +void vpTemplateTrackerWarpHomography::getdWdp0(const int &i,const int &j,double *dIdW) +{ + dIdW[0]=j; + dIdW[1]=0; + dIdW[2]=-j*j; + dIdW[3]=i; + dIdW[4]=0; + dIdW[5]=-i*j; + dIdW[6]=1.; + dIdW[7]=0; + + dIdW[8]=0; + dIdW[9]=j; + dIdW[10]=-i*j; + dIdW[11]=0; + dIdW[12]=i; + dIdW[13]=-i*i; + dIdW[14]=0; + dIdW[15]=1.; +} +void vpTemplateTrackerWarpHomography::computeDenom(vpColVector &vX, const vpColVector &ParamM) +{ + denom=(1./(ParamM[2]*vX[0]+ParamM[5]*vX[1]+1.)); +} + +void vpTemplateTrackerWarpHomography::warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM) +{ + j2=((1.+ParamM[0])*j+ParamM[3]*i+ParamM[6])*denom; + i2=(ParamM[1]*j+(1.+ParamM[4])*i+ParamM[7])*denom; +} + + +void vpTemplateTrackerWarpHomography::warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + //if((ParamM[2]*vX[0]+ParamM[5]*vX[1]+1)>0)//si dans le plan image reel + if((denom)>0)// FS optimisation + { + vXres[0]=((1+ParamM[0])*vX[0]+ParamM[3]*vX[1]+ParamM[6])*denom; + vXres[1]=(ParamM[1]*vX[0]+(1+ParamM[4])*vX[1]+ParamM[7])*denom; + } + else + throw(vpTrackingException(vpTrackingException::fatalError,"Division by zero in vpTemplateTrackerWarpHomography::warpX()")); +} + +void vpTemplateTrackerWarpHomography::dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &/*ParamM*/,vpMatrix &dW_) +{ + double j=X1[0]; + double i=X1[1]; + dW_=0; + dW_[0][0]=j*denom; + dW_[0][2]=-j*X2[0]*denom; + dW_[0][3]=i*denom; + dW_[0][5]=-i*X2[0]*denom; + dW_[0][6]=denom; + + dW_[1][1]=j*denom; + dW_[1][2]=-j*X2[1]*denom; + dW_[1][4]=i*denom; + dW_[1][5]=-i*X2[1]*denom; + dW_[1][7]=denom; +} + +/*compute dw=dw/dx*dw/dp */ +void vpTemplateTrackerWarpHomography::dWarpCompo(const vpColVector &/*X1*/,const vpColVector &X2,const vpColVector &ParamM, + const double *dwdp0,vpMatrix &dW_) +{ + double dwdx0,dwdx1; + double dwdy0,dwdy1; + + dwdx0=((1.+ParamM[0])-X2[0]*ParamM[2])*denom; + dwdx1=(ParamM[1]-X2[1]*ParamM[2])*denom; + dwdy0=(ParamM[3]-X2[0]*ParamM[5])*denom; + dwdy1=((1.+ParamM[4])-X2[1]*ParamM[5])*denom; + for(unsigned int i=0;i<nbParam;i++) + { + dW_[0][i]=dwdx0*dwdp0[i]+dwdy0*dwdp0[i+nbParam]; + dW_[1][i]=dwdx1*dwdp0[i]+dwdy1*dwdp0[i+nbParam]; + } +} + +void vpTemplateTrackerWarpHomography::warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + + if((ParamM[2]*vX[0]+ParamM[5]*vX[1]+1)<0)//si dans le plan image reel + { + vXres[0]=((1+ParamM[0])*vX[0]+ParamM[3]*vX[1]+ParamM[6])/(ParamM[2]*vX[0]+ParamM[5]*vX[1]+1); + vXres[1]=(ParamM[1]*vX[0]+(1+ParamM[4])*vX[1]+ParamM[7])/(ParamM[2]*vX[0]+ParamM[5]*vX[1]+1); + } + else + throw(vpTrackingException(vpTrackingException::fatalError,"Division by zero in vpTemplateTrackerWarpHomography::warpXSpecialInv()")) ; +} +void vpTemplateTrackerWarpHomography::getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const +{ + vpHomography H = getHomography(ParamM); + vpHomography Hinv = H.inverse(); + getParam(Hinv, ParamMinv); +} + + +vpHomography vpTemplateTrackerWarpHomography::getHomography(const vpColVector &ParamM) const +{ + vpHomography H; + for(unsigned int i=0;i<3;i++) + for(unsigned int j=0;j<3;j++) + { + if(i+3*j!=8) + { + H[i][j]=ParamM[i+3*j]; + if(i==j)H[i][j]++; + } + else + H[i][j]=1.; + } + + return H; +} +void vpTemplateTrackerWarpHomography::getParam(const vpHomography &H,vpColVector &par) const +{ + par=0; + for(unsigned int i=0;i<3;i++) + for(unsigned int j=0;j<3;j++) + { + if(i+3*j!=8) + { + par[i+3*j]=H[i][j]/H[2][2]; + if(i==j)par[i+3*j]--; + } + } +} + + + +void vpTemplateTrackerWarpHomography::pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const +{ + vpHomography H1 = getHomography(p1); + vpHomography H2 = getHomography(p2); + vpHomography H = H1*H2; + getParam(H,pres); +} diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.h new file mode 100644 index 0000000000000000000000000000000000000000..58201ab16a0c311a947ce93cabd121003e4f7fbf --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomography.h @@ -0,0 +1,200 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpHomography.h 4607 2014-01-21 16:02:11Z ayol $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarpHomography.h + \brief warping function of an homography: the homography is directly defined by the diplacement parameter: H=[[1+p0, p3, p6], [p1, 1+p4, p7], [p2, p5, 1]] +*/ + + +#ifndef vpTemplateTrackerWarpHomography_hh +#define vpTemplateTrackerWarpHomography_hh + +#include <visp/vpTemplateTrackerWarp.h> +#include <visp/vpHomography.h> + + +class VISP_EXPORT vpTemplateTrackerWarpHomography: public vpTemplateTrackerWarp +{ + public: + //constructor; + vpTemplateTrackerWarpHomography(); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void computeCoeff(const vpColVector &/*p*/){} + #endif + + /*! + Compute the projection denominator (Z) used in x = X/Z and y = Y/Z. + + \param vX : Point to consider + \param ParamM : parameters of the warping function. + */ + void computeDenom(vpColVector &vX, const vpColVector &ParamM); + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW); + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW); + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis (along the columns) of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const int &i,const int &j,double *dIdW); + + /*! + Return the homography defined by the given parameters + + \param ParamM : Parameters of the homography. + + \return An Homography via vpHomography. + */ + vpHomography getHomography(const vpColVector &ParamM) const; + + /*! + Compute the parameters of the homography warping function according to the given vpHomography + + \param H : Homography used to compute the parameters. + \param par : Resulting warping function parameters. + */ + void getParam(const vpHomography &H,vpColVector &par) const; + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + void getParamPyramidDown(const vpColVector &p,vpColVector &pdown); + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + void getParamPyramidUp(const vpColVector &p,vpColVector &pup); + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + bool isESMcompatible() const {return false;} + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const ; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the rows) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM); + + /*! + Inverse Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); +}; +#endif diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.cpp new file mode 100644 index 0000000000000000000000000000000000000000..507fec221a4be1bd1b72f23e3ec107669f7f0f3c --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.cpp @@ -0,0 +1,402 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpHomographySL3.cpp 4783 2014-07-15 13:07:52Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarpHomographySL3.h> + +//findWarp special a SL3 car methode additionnelle ne marche pas (la derivee n est calculable qu en p=0) +// => resout le probleme de maniere compositionnelle +void vpTemplateTrackerWarpHomographySL3::findWarp(const double *ut0,const double *vt0, + const double *u, const double *v,int nb_pt,vpColVector& p) +{ + //std::cout<<"findWarp OVERLOADE"<<std::endl; + vpColVector dp(nbParam); + vpMatrix dW_(2,nbParam); + vpMatrix dX(2,1); + vpMatrix H(nbParam,nbParam), HLM(nbParam,nbParam); + vpMatrix G_(nbParam,1); + + //vpMatrix *dW_ddp0=new vpMatrix[nb_pt]; + double **dW_ddp0=new double*[(unsigned int)nb_pt]; + for(int i=0;i<nb_pt;i++) + { + //dW_ddp0[i].resize(2,nbParam); + dW_ddp0[i]=new double[2*nbParam]; + //getdWdp0(vt0[i],ut0[i],dW_ddp0[i]); + //std::cout<<"findWarp"<<v[i]<<","<<u[i]<<std::endl; + getdWdp0(v[i],u[i],dW_ddp0[i]); + } + + int cpt=0; + vpColVector X1(2); + vpColVector fX1(2); + vpColVector X2(2); + double erreur=0; + double erreur_prec; + double lambda=0.00001; + do + { + erreur_prec=erreur; + H=0; + G_=0; + erreur=0; + computeCoeff(p); + for(int i=0;i<nb_pt;i++) + { + X1[0]=ut0[i]; + X1[1]=vt0[i]; + computeDenom(X1,p); + warpX(X1,fX1,p); + //dWarpCompo(X1,fX1,p,dW_ddp0[i],dW); + //dWarp(X1,fX1,p,dW); + for(unsigned int ip=0;ip<nbParam;ip++) + { + dW_[0][ip]=dW_ddp0[i][ip]; + dW_[1][ip]=dW_ddp0[i][ip+nbParam]; + } + + H+=dW_.AtA(); + + X2[0]=u[i]; + X2[1]=v[i]; + + dX=X2-fX1; + G_+=dW_.t()*dX; + + erreur+=((u[i]-fX1[0])*(u[i]-fX1[0])+(v[i]-fX1[1])*(v[i]-fX1[1])); + } + + vpMatrix::computeHLM(H, lambda, HLM); + try{ + dp=HLM.inverseByLU()*G_; + } + catch(vpException &e){ + //std::cout<<"Cannot inverse the matrix by LU "<<std::endl; + throw(e); + } + pRondp(p,dp,p); + + cpt++; + // std::cout<<"erreur ="<<erreur<<std::endl; + } + //while((cpt<1500)); + while((cpt<150)&&(sqrt((erreur_prec-erreur)*(erreur_prec-erreur))>1e-20)); + + //std::cout<<"erreur apres transformation="<<erreur<<std::endl; + for(int i=0;i<nb_pt;i++) + delete[] dW_ddp0[i]; + delete[] dW_ddp0; + +} + +vpTemplateTrackerWarpHomographySL3::vpTemplateTrackerWarpHomographySL3() + : G(), dGx(), A() +{ + nbParam = 8 ; + G.resize(3,3); + dGx.resize(3,nbParam); + + A.resize(8); + for(unsigned int i=0;i<8;i++) + { + A[i].resize(3,3); + A[i]=0; + } + A[0][0][2]=1; + A[1][1][2]=1; + A[2][0][1]=1; + A[3][1][0]=1; + A[4][0][0]=1; + A[4][1][1]=-1; + A[5][1][1]=-1; + A[5][2][2]=1; + A[6][2][0]=1; + A[7][2][1]=1; +} + +vpTemplateTrackerWarpHomographySL3::~vpTemplateTrackerWarpHomographySL3() +{ +} + +//get the parameter corresponding to the lower level of a gaussian pyramid +//a refaire de facon analytique +void vpTemplateTrackerWarpHomographySL3::getParamPyramidDown(const vpColVector &p,vpColVector &pdown) +{ + double *u,*v;u=new double[4];v=new double[4]; + //u[0]=0;v[0]=0;u[1]=640;v[1]=0;u[2]=640;v[2]=480;u[3]=0;v[3]=480; + u[0]=0;v[0]=0;u[1]=160;v[1]=0;u[2]=160;v[2]=120;u[3]=0;v[3]=120; + double *u2,*v2;u2=new double[4];v2=new double[4]; + warp(u,v,4,p,u2,v2); + //p=0;findWarp(u,v,u2,v2,4,p); + for(int i=0;i<4;i++) + { + u[i]=u[i]/2.; + v[i]=v[i]/2.; + u2[i]=u2[i]/2.; + v2[i]=v2[i]/2.; + //std::cout<<"recherche "<<u2[i]<<","<<v2[i]<<std::endl; + } + pdown=p; + findWarp(u,v,u2,v2,4,pdown); + delete[] u; + delete[] v; + delete[] u2; + delete[] v2; +} + +void vpTemplateTrackerWarpHomographySL3::getParamPyramidUp(const vpColVector &p,vpColVector &pup) +{ + double *u,*v;u=new double[4];v=new double[4]; + //u[0]=0;v[0]=0;u[1]=640;v[1]=0;u[2]=640;v[2]=480;u[3]=0;v[3]=480; + u[0]=0;v[0]=0;u[1]=160;v[1]=0;u[2]=160;v[2]=120;u[3]=0;v[3]=120; + //u[0]=40;v[0]=30;u[1]=160;v[1]=30;u[2]=160;v[2]=120;u[3]=40;v[3]=120; + double *u2,*v2;u2=new double[4];v2=new double[4]; + + //pup=p; + + /*vpColVector ptest=pup; + warp(u,v,4,ptest,u2,v2); + for(int i=0;i<4;i++) + std::cout<<"test "<<u2[i]<<","<<v2[i]<<std::endl;*/ + + warp(u,v,4,p,u2,v2); + //p=0;findWarp(u,v,u2,v2,4,p); + + + for(int i=0;i<4;i++) + { + u[i]=u[i]*2.; + v[i]=v[i]*2.; + u2[i]=u2[i]*2.; + v2[i]=v2[i]*2.; + /*std::cout<<"#########################################################################################"<<std::endl; + std::cout<<"#########################################################################################"<<std::endl; + std::cout<<"#########################################################################################"<<std::endl; + std::cout<<"recherche "<<u2[i]<<","<<v2[i]<<std::endl;*/ + } + findWarp(u,v,u2,v2,4,pup); + + delete[] u; + delete[] v; + delete[] u2; + delete[] v2; +} + +void vpTemplateTrackerWarpHomographySL3::computeDenom(vpColVector &vX, const vpColVector &/*ParamM*/) +{ + denom=vX[0]*G[2][0]+vX[1]*G[2][1]+G[2][2]; +} + +void vpTemplateTrackerWarpHomographySL3::computeCoeff(const vpColVector &p) +{ + vpMatrix pA(3,3); + pA[0][0]=p[4]; + pA[0][1]=p[2]; + pA[0][2]=p[0]; + + pA[1][0]=p[3]; + pA[1][1]=-p[4]-p[5]; + pA[1][2]=p[1]; + + pA[2][0]=p[6]; + pA[2][1]=p[7]; + pA[2][2]=p[5]; + + G=pA.expm(); +} + + +void vpTemplateTrackerWarpHomographySL3::warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &/*ParamM*/) +{ + double i=vX[1],j=vX[0]; + vXres[0]=(j*G[0][0]+i*G[0][1]+G[0][2])/denom; + vXres[1]=(j*G[1][0]+i*G[1][1]+G[1][2])/denom; +} +void vpTemplateTrackerWarpHomographySL3::warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &/*ParamM*/) +{ + j2=(j*G[0][0]+i*G[0][1]+G[0][2])/denom; + i2=(j*G[1][0]+i*G[1][1]+G[1][2])/denom; +} + +vpHomography vpTemplateTrackerWarpHomographySL3::getHomography() const +{ + vpHomography H; + for (unsigned int i=0; i<3; i++) + for (unsigned int j=0; j<3; j++) + H[i][j] = G[i][j]; + return H; +} + +void vpTemplateTrackerWarpHomographySL3::dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &/*ParamM*/,vpMatrix &dW_) +{ + vpMatrix dhdx(2,3); + dhdx=0; + dhdx[0][0]=1./denom;dhdx[1][1]=1./denom;dhdx[0][2]=-X2[0]/(denom);dhdx[1][2]=-X2[1]/(denom); + dGx=0; + for(unsigned int i=0;i<3;i++) + { + dGx[i][0]=G[i][0]; + dGx[i][1]=G[i][1]; + dGx[i][2]=G[i][0]*X1[1]; + dGx[i][3]=G[i][1]*X1[0]; + dGx[i][4]=G[i][0]*X1[0]-G[i][1]*X1[1]; + dGx[i][5]=G[i][2]-G[i][1]*X1[1]; + dGx[i][6]=G[i][2]*X1[0]; + dGx[i][7]=G[i][2]*X1[1]; + } + dW_=dhdx*dGx; + +} + +/*calcul de di*dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpHomographySL3::getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW) +{ + vpMatrix dhdx(1,3); + dhdx=0; + dhdx[0][0]=dx;dhdx[0][1]=dy;dhdx[0][2]=-j*dx-i*dy; + G.setIdentity(); + + dGx=0; + for(unsigned int par=0;par<3;par++) + { + dGx[par][0]=G[par][0]; + dGx[par][1]=G[par][1]; + dGx[par][2]=G[par][0]*i; + dGx[par][3]=G[par][1]*j; + dGx[par][4]=G[par][0]*j-G[par][1]*i; + dGx[par][5]=G[par][2]-G[par][1]*i; + dGx[par][6]=G[par][2]*j; + dGx[par][7]=G[par][2]*i; + } + + for(unsigned int par=0;par<nbParam;par++) + { + double res=0; + for(unsigned int par2=0;par2<3;par2++) + res+=dhdx[0][par2]*dGx[par2][par]; + dIdW[par]=res; + } + +} +/*calcul de dw(x,p0)/dp +*/ + +void vpTemplateTrackerWarpHomographySL3::getdWdp0(const int &i,const int &j,double *dIdW) +{ + vpMatrix dhdx(2,3); + dhdx=0; + dhdx[0][0]=1.;dhdx[1][1]=1.;dhdx[0][2]=-j;dhdx[1][2]=-i; + G.setIdentity(); + + dGx=0; + for(unsigned int par=0;par<3;par++) + { + dGx[par][0]=G[par][0]; + dGx[par][1]=G[par][1]; + dGx[par][2]=G[par][0]*i; + dGx[par][3]=G[par][1]*j; + dGx[par][4]=G[par][0]*j-G[par][1]*i; + dGx[par][5]=G[par][2]-G[par][1]*i; + dGx[par][6]=G[par][2]*j; + dGx[par][7]=G[par][2]*i; + } + vpMatrix dIdW_temp(2,nbParam); + dIdW_temp=dhdx*dGx; + + for(unsigned int par=0;par<nbParam;par++) + { + dIdW[par]=dIdW_temp[0][par]; + dIdW[par+nbParam]=dIdW_temp[1][par]; + } + +} +void vpTemplateTrackerWarpHomographySL3::getdWdp0(const double &i,const double &j,double *dIdW) +{ + vpMatrix dhdx(2,3); + dhdx=0; + dhdx[0][0]=1.;dhdx[1][1]=1.;dhdx[0][2]=-j;dhdx[1][2]=-i; + G.setIdentity(); + + dGx=0; + for(unsigned int par=0;par<3;par++) + { + dGx[par][0]=G[par][0]; + dGx[par][1]=G[par][1]; + dGx[par][2]=G[par][0]*i; + dGx[par][3]=G[par][1]*j; + dGx[par][4]=G[par][0]*j-G[par][1]*i; + dGx[par][5]=G[par][2]-G[par][1]*i; + dGx[par][6]=G[par][2]*j; + dGx[par][7]=G[par][2]*i; + } + vpMatrix dIdW_temp(2,nbParam); + dIdW_temp=dhdx*dGx; + + for(unsigned int par=0;par<nbParam;par++) + { + dIdW[par]=dIdW_temp[0][par]; + dIdW[par+nbParam]=dIdW_temp[1][par]; + } + +} +/*compute dw=dw/dx*dw/dp +*/ + +void vpTemplateTrackerWarpHomographySL3::dWarpCompo(const vpColVector &/*X1*/,const vpColVector &X2,const vpColVector &/*ParamM*/, + const double *dwdp0,vpMatrix &dW_) +{ + for(unsigned int i=0;i<nbParam;i++) + { + dW_[0][i]=denom*((G[0][0]-X2[0]*G[2][0])*dwdp0[i]+(G[0][1]-X2[0]*G[2][1])*dwdp0[i+nbParam]); + dW_[1][i]=denom*((G[1][0]-X2[1]*G[2][0])*dwdp0[i]+(G[1][1]-X2[1]*G[2][1])*dwdp0[i+nbParam]); + } +} + +void vpTemplateTrackerWarpHomographySL3::getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const +{ + ParamMinv=-ParamM; +} +void vpTemplateTrackerWarpHomographySL3::pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const +{ + //vrai que si commutatif ... + pres=p1+p2; +} + diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.h new file mode 100644 index 0000000000000000000000000000000000000000..43adff0e036df23a51d9b2f56ec02eab7e6e9453 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpHomographySL3.h @@ -0,0 +1,216 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpHomographySL3.h 4669 2014-02-16 16:25:57Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarpHomographySL3.h + \brief warping function of an homography: the homography is defined on the sl3 lie algebra H=exp(Sum(p[i]* A_i)) A_i is the basis of the SL3 Algebra +*/ + + +#ifndef vpTemplateTrackerWarpHomographySL3_hh +#define vpTemplateTrackerWarpHomographySL3_hh + +#include <vector> + +#include <visp/vpTemplateTrackerWarp.h> +#include <visp/vpHomography.h> + +class VISP_EXPORT vpTemplateTrackerWarpHomographySL3: public vpTemplateTrackerWarp +{ + protected: + vpMatrix G; + vpMatrix dGx; + std::vector<vpMatrix> A; + + public: + //constructor; + vpTemplateTrackerWarpHomographySL3(); + ~vpTemplateTrackerWarpHomographySL3(); + + /*! + Compute the exponential of the homography matrix defined by the given parameters + + \param p : Parameters of the SL3 homography warping function. + */ + void computeCoeff(const vpColVector &p); + + /*! + Compute the projection denominator (Z) used in x = X/Z and y = Y/Z. + + \param vX : Point to consider + \param ParamM : parameters of the warping function. + */ + void computeDenom(vpColVector &vX, const vpColVector &ParamM); + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW); + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW); + + /*! + Find the displacement/warping function parameters from a list of points. + + \param ut0 : Original u coordinates. + \param vt0 : Original v coordinates. + \param u : Warped u coordinates. + \param v : Warped v coordinates. + \param nb_pt : Number of points. + \param p : Resulting warping function parameters. + */ + void findWarp(const double *ut0,const double *vt0,const double *u,const double *v,int nb_pt,vpColVector& p); + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis (along the columns) of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const int &i,const int &j,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const double &i, const double &j,double *dIdW); + + /*! + Return the homography defined by the warping function + + \return An Homography via vpHomography. + */ + vpHomography getHomography() const; + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + void getParamPyramidDown(const vpColVector &p,vpColVector &pdown); + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + void getParamPyramidUp(const vpColVector &p,vpColVector &pup); + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + bool isESMcompatible() const {return true;} + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the rows) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void warpXInv(const vpColVector &/*vX*/,vpColVector &/*vXres*/,const vpColVector &/*ParamM*/) {} + #endif +}; +#endif diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6913eff1e994820747d9922b72c84afd8eb59104 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpSRT.cpp 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarpSRT.h> + + +vpTemplateTrackerWarpSRT::vpTemplateTrackerWarpSRT() +{ + nbParam = 4 ; + dW.resize(2,nbParam); +} + +//get the parameter corresponding to the lower level of a gaussian pyramid +void vpTemplateTrackerWarpSRT::getParamPyramidDown(const vpColVector &p,vpColVector &pdown) +{ + pdown=p; + pdown[2]=p[2]/2.; + pdown[3]=p[3]/2.; +} + +void vpTemplateTrackerWarpSRT::getParamPyramidUp(const vpColVector &p,vpColVector &pup) +{ + pup=p; + pup[2]=p[2]*2.; + pup[3]=p[3]*2.; +} +/*calcul de di*dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpSRT::getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW) +{ + // std::cout << "getdW0" << std::endl; + dIdW[0]=j*dx + i*dy; + dIdW[1]=-i*dx + j*dy; + dIdW[2]=dx; + dIdW[3]=dy; +} +/*calcul de dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpSRT::getdWdp0(const int &i,const int &j,double *dIdW) +{ + dIdW[0]=j; + dIdW[1]=-i; + dIdW[2]=1.; + dIdW[3]=0; + + dIdW[4]=i; + dIdW[5]=j; + dIdW[6]=0; + dIdW[7]=1.; +} + +void vpTemplateTrackerWarpSRT::warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM) +{ + j2=((1.0+ParamM[0])*cos(ParamM[1])*j) - ((1.0+ParamM[0])*sin(ParamM[1])*i) + ParamM[2]; + i2=((1.0+ParamM[0])*sin(ParamM[1])*j) + ((1.0+ParamM[0])*cos(ParamM[1])*i) + ParamM[3]; +} + + +void vpTemplateTrackerWarpSRT::warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + vXres[0]=((1.0+ParamM[0])*cos(ParamM[1])*vX[0]) - ((1.0+ParamM[0])*sin(ParamM[1])*vX[1]) + ParamM[2]; + vXres[1]=((1.0+ParamM[0])*sin(ParamM[1])*vX[0]) + ((1.0+ParamM[0])*cos(ParamM[1])*vX[1]) + ParamM[3]; +} + +void vpTemplateTrackerWarpSRT::dWarp(const vpColVector &X1,const vpColVector &/*X2*/,const vpColVector &ParamM,vpMatrix &dW_) +{ + double j=X1[0]; + double i=X1[1]; + dW_=0; + dW_[0][0]=cos(ParamM[1])*j - sin(ParamM[1])*i; + dW_[0][1]=(-(1.0+ParamM[0])*sin(ParamM[1])*j) - ((1.0+ParamM[0])*cos(ParamM[1])*i); + dW_[0][2]=1; + + dW_[1][0]=sin(ParamM[1])*j + cos(ParamM[1])*i; + dW_[1][1]=(1.0+ParamM[0])*cos(ParamM[1])*j - (1.0+ParamM[0])*sin(ParamM[1])*i; + dW_[1][3]=1; +} + +/*compute dw=dw/dx*dw/dp +*/ +void vpTemplateTrackerWarpSRT::dWarpCompo(const vpColVector &/*X1*/,const vpColVector &/*X2*/,const vpColVector &ParamM, + const double *dwdp0,vpMatrix &dW_) +{ + for(unsigned int i=0;i<nbParam;i++) + { + dW_[0][i]=((1.+ParamM[0])*cos(ParamM[1])*dwdp0[i]) - ((1.0+ParamM[0])*sin(ParamM[1])*dwdp0[i+nbParam]); + dW_[1][i]=((1.+ParamM[0])*sin(ParamM[1])*dwdp0[i]) + ((1.0+ParamM[0])*cos(ParamM[1])*dwdp0[i+nbParam]); + } +} + +void vpTemplateTrackerWarpSRT::warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + // std::cout << "warpXspe" << std::endl; + vXres[0]=((1.0+ParamM[0])*cos(ParamM[1])*vX[0]) - ((1.0+ParamM[0])*sin(ParamM[1])*vX[1]) + ParamM[2]; + vXres[1]=((1.0+ParamM[0])*sin(ParamM[1])*vX[0]) + ((1.0+ParamM[0])*cos(ParamM[1])*vX[1]) + ParamM[3]; +} + +void vpTemplateTrackerWarpSRT::getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const +{ + vpColVector Trans(2); + vpMatrix MWrap(2,2); + Trans[0]=ParamM[2]; + Trans[1]=ParamM[3]; + MWrap[0][0]=cos(ParamM[1]); + MWrap[0][1]=-sin(ParamM[1]); + MWrap[1][0]=sin(ParamM[1]); + MWrap[1][1]=cos(ParamM[1]); + + vpMatrix MWrapInv(2,2); + MWrapInv=MWrap.transpose(); + vpColVector TransInv(2); + TransInv=(-1.0/(1.0+ParamM[0]))*MWrapInv*Trans; + + ParamMinv[0]=1.0/(1.0+ParamM[0]) - 1.0; + ParamMinv[1]= atan2(MWrapInv[1][0],MWrapInv[1][1]); + ParamMinv[2]=TransInv[0]; + ParamMinv[3]=TransInv[1]; +} + +void vpTemplateTrackerWarpSRT::pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const +{ + vpColVector Trans1(2); + vpMatrix MWrap1(2,2); + Trans1[0]=p1[2];Trans1[1]=p1[3]; + + MWrap1[0][0]=cos(p1[1]); + MWrap1[0][1]=-sin(p1[1]); + MWrap1[1][0]=sin(p1[1]); + MWrap1[1][1]=cos(p1[1]); + + vpColVector Trans2(2); + vpMatrix MWrap2(2,2); + Trans2[0]=p2[2];Trans2[1]=p2[3]; + + MWrap2[0][0]=cos(p2[1]); + MWrap2[0][1]=-sin(p2[1]); + MWrap2[1][0]=sin(p2[1]); + MWrap2[1][1]=cos(p2[1]); + + vpColVector TransRes(2); + vpMatrix MWrapRes(2,2); + TransRes=(1.0+p1[0])*MWrap1*Trans2+Trans1; + MWrapRes=MWrap1*MWrap2; + + pres[0]=(1.0+p1[0])*(1.0+p2[0]) - 1.0; + pres[1]=atan2(MWrapRes[1][0],MWrapRes[1][1]); + + pres[2]=TransRes[0]; + pres[3]=TransRes[1]; +} diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.h new file mode 100644 index 0000000000000000000000000000000000000000..08f7d50e9008891583b92bdc30362133fac49b94 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpSRT.h @@ -0,0 +1,174 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpSRT.h 4607 2014-01-21 16:02:11Z ayol $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarpSRT.h + \brief sRt warping function +*/ + + +#ifndef vpTemplateTrackerWarpSRT_hh +#define vpTemplateTrackerWarpSRT_hh + +#include <visp/vpTemplateTrackerWarp.h> + +class VISP_EXPORT vpTemplateTrackerWarpSRT: public vpTemplateTrackerWarp +{ + public: + //constructor; + vpTemplateTrackerWarpSRT(); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void computeCoeff(const vpColVector &/*p*/){} + void computeDenom(vpColVector &/*vX*/, const vpColVector &/*ParamM*/){} + #endif + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW); + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW); + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis (along the columns) of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const int &i,const int &j,double *dIdW); + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + void getParamPyramidDown(const vpColVector &p,vpColVector &pdown); + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + void getParamPyramidUp(const vpColVector &p,vpColVector &pup); + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + bool isESMcompatible() const {return false;} + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the rows) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM); + + /*! + Inverse Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); +}; +#endif diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.cpp b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d61f8fc065cf516b8066b984d548d75603adf836 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpTranslation.cpp 4649 2014-02-07 14:57:11Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerWarpTranslation.h> + +vpTemplateTrackerWarpTranslation::vpTemplateTrackerWarpTranslation() +{ + nbParam = 2 ; + dW.resize(2,nbParam); +} + + +//get the parameter corresponding to the lower level of a gaussian pyramid +void vpTemplateTrackerWarpTranslation::getParamPyramidDown(const vpColVector &p,vpColVector &pdown) +{ + pdown[0]=p[0]/2.; + pdown[1]=p[1]/2.; +} + +void vpTemplateTrackerWarpTranslation::getParamPyramidUp(const vpColVector &p,vpColVector &pup) +{ + pup[0]=p[0]*2.; + pup[1]=p[1]*2.; +} + +/*calcul de di*dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpTranslation::getdW0(const int &/*i*/,const int &/*j*/,const double &dy,const double &dx,double *dIdW) +{ + dIdW[0]=dx; + dIdW[1]=dy; +} +/*calcul de dw(x,p0)/dp +*/ +void vpTemplateTrackerWarpTranslation::getdWdp0(const int &/*i*/,const int &/*j*/,double *dIdW) +{ + dIdW[0]=1.; + dIdW[1]=0; + + dIdW[2]=0; + dIdW[3]=1.; +} + +void vpTemplateTrackerWarpTranslation::warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM) +{ + j2=j+ParamM[0]; + i2=i+ParamM[1]; +} + + +void vpTemplateTrackerWarpTranslation::warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + vXres[0]=vX[0]+ParamM[0]; + vXres[1]=vX[1]+ParamM[1]; +} + +void vpTemplateTrackerWarpTranslation::dWarp(const vpColVector &/*X1*/,const vpColVector &/*X2*/,const vpColVector &/*ParamM*/, + vpMatrix &dW_) +{ + dW_[0][0]=1;dW_[0][1]=0; + dW_[1][0]=0;dW_[1][1]=1; +} + +/*compute dw=dw/dx*dw/dp +*/ +void vpTemplateTrackerWarpTranslation::dWarpCompo(const vpColVector &/*X1*/,const vpColVector &/*X2*/,const vpColVector &/*ParamM*/, + const double *dwdp0,vpMatrix &dW_) +{ + for(unsigned int i=0;i<nbParam;i++) + { + dW_[0][i]=dwdp0[i]; + dW_[1][i]=dwdp0[i+nbParam]; + } +} + +void vpTemplateTrackerWarpTranslation::warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM) +{ + vXres[0]=vX[0]+ParamM[0]; + vXres[1]=vX[1]+ParamM[1]; +} +void vpTemplateTrackerWarpTranslation::getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const +{ + ParamMinv[0]=-ParamM[0]; + ParamMinv[1]=-ParamM[1]; +} + +void vpTemplateTrackerWarpTranslation::pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const +{ + pres[0]=p1[0]+p2[0]; + pres[1]=p1[1]+p2[1]; +} diff --git a/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.h b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.h new file mode 100644 index 0000000000000000000000000000000000000000..61a705fd92d1618f4ba614aea2f5dcdc9abea9c7 --- /dev/null +++ b/src/tracking/template-tracker/warp/vpTemplateTrackerWarpTranslation.h @@ -0,0 +1,175 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerWarpTranslation.h 4607 2014-01-21 16:02:11Z ayol $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerWarpTranslation.h + \brief Translation warping function: w(X)=X+b with: b= [p0, p1]] +*/ + + +#ifndef vpTemplateTrackerWarpTranslation_hh +#define vpTemplateTrackerWarpTranslation_hh + +#include <visp/vpTemplateTrackerWarp.h> + + +class VISP_EXPORT vpTemplateTrackerWarpTranslation: public vpTemplateTrackerWarp +{ + public: + //constructor; + vpTemplateTrackerWarpTranslation(); + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + void computeCoeff(const vpColVector &/*p*/){} + void computeDenom(vpColVector &/*vX*/, const vpColVector &/*ParamM*/){} + #endif + + /*! + Compute the derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warping function. + \param dW : Resulting derivative matrix. + */ + void dWarp(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,vpMatrix &dW); + + /*! + Compute the compositionnal derivative of the warping function according to its parameters. + + \param X1 : Point to consider in the derivative computation. + \param X2 : Point to consider in the derivative computation. + \param ParamM : Parameters of the warp. + \param dwdp0 : Derivative matrix of the warping function according to the initial warping function parameters (p=0). + \param dW : Resulting compositionnal derivative matrix. + */ + void dWarpCompo(const vpColVector &X1,const vpColVector &X2,const vpColVector &ParamM,const double *dwdp0,vpMatrix &dW); + + /*! + Compute the derivative of the image with relation to the warping function parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dy : Derivative on the y-axis (along the rows) of the point (i,j). + \param dx : Derivative on the x-axis (along the columns) of the point (i,j). + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdW0(const int &i,const int &j,const double &dy,const double &dx,double *dIdW); + + /*! + Compute the derivative of the warping function according to the initial parameters. + + \param i : i coordinate (along the rows) of the point to consider in the image. + \param j : j coordinate (along the columns) of the point to consider in the image. + \param dIdW : Resulting derivative matrix (Image according to the warping function). + */ + void getdWdp0(const int &i,const int &j,double *dIdW); + + /*! + Get the inverse of the warping function parameters. + + \param ParamM : Parameters of the warping function. + \param ParamMinv : Inverse parameters. + */ + void getParamInverse(const vpColVector &ParamM,vpColVector &ParamMinv) const ; + + /*! + Get the parameters of the warping function one level down. + + \param p : Current parameters of the warping function. + \param pdown : Resulting parameters on level down. + */ + void getParamPyramidDown(const vpColVector &p,vpColVector &pdown); + + /*! + Get the parameters of the warping function one level up. + + \param p : Current parameters of the warping function. + \param pup : Resulting parameters one level up. + */ + void getParamPyramidUp(const vpColVector &p,vpColVector &pup); + + /*! + Tells if the warping function is ESM compatible. + + \return True if it is ESM compatible, False otherwise. + */ + bool isESMcompatible() const {return true;} + + /*! + Get the displacement resulting from the composition of two other displacements. + + \param p1 : First displacement. + \param p2 : Second displacement. + \param pres : Displacement resulting from the composition of p1 and p2. + */ + void pRondp(const vpColVector &p1, const vpColVector &p2,vpColVector &pres) const ; + + /*! + Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); + + /*! + Warp a point. + + \param i : i coordinate (along the rows) of the point to warp. + \param j : j coordinate (along the columns) of the point to warp. + \param i2 : i coordinate (along the rows) of the warped point. + \param j2 : j coordinate (along the columns) of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpX(const int &i,const int &j,double &i2,double &j2,const vpColVector &ParamM); + + /*! + Inverse Warp a point. + + \param vX : Coordinates of the point to warp. + \param vXres : Coordinates of the warped point. + \param ParamM : Parameters of the warping function. + */ + void warpXInv(const vpColVector &vX,vpColVector &vXres,const vpColVector &ParamM); +}; +#endif diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.cpp b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b14af265c20999e05003a9d1bb95e4a2089f4d4 --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCC.cpp 4682 2014-02-24 07:56:27Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerZNCC.h> + +vpTemplateTrackerZNCC::vpTemplateTrackerZNCC(vpTemplateTrackerWarp *warp) + : vpTemplateTracker(warp), DI(), temp() +{ + dW.resize(2,nbParam); + G.resize(nbParam); + H.resize(nbParam,nbParam); + HLM.resize(nbParam,nbParam); + + temp.resize(nbParam); + + X1.resize(2); + X2.resize(2); + DI.resize(2); +} + + +double vpTemplateTrackerZNCC::getCost(const vpImage<unsigned char> &I, vpColVector &tp) +{ + double IW,Tij; + int i,j; + double i2,j2; + int Nbpoint=0; + + Warp->computeCoeff(tp); + + double moyTij=0; + double moyIW=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,tp); + Warp->warpX(X1,X2,tp); + + j2=X2[0];i2=X2[1]; + if((j2<I.getWidth()-1)&&(i2<I.getHeight()-1)&&(i2>0)&&(j2>0)) + { + Tij=ptTemplate[point].val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + //IW=getSubPixBspline4(I,i2,j2); + moyTij+=Tij; + moyIW+=IW; + Nbpoint++; + } + } + ratioPixelIn=(double)Nbpoint/(double)templateSize; + if(! Nbpoint) { + throw(vpException(vpException::divideByZeroError, + "Cannot get cost: size = 0")) ; + } + + moyTij=moyTij/Nbpoint; + moyIW=moyIW/Nbpoint; + + double nom=0,denom=0; + double var1=0,var2=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,tp); + Warp->warpX(X1,X2,tp); + + j2=X2[0];i2=X2[1]; + if((j2<I.getWidth()-1)&&(i2<I.getHeight()-1)&&(i2>0)&&(j2>0)) + { + Tij=ptTemplate[point].val; + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + //IW=getSubPixBspline4(I,i2,j2); + nom+=(Tij-moyTij)*(IW-moyIW); + denom+=(Tij-moyTij)*(Tij-moyTij)*(IW-moyIW)*(IW-moyIW); + var1+=(IW-moyIW)*(IW-moyIW); + var2+=(Tij-moyTij)*(Tij-moyTij); + + Nbpoint++; + } + } + // if(Nbpoint==0)return 10e10; // cannot occur + //return -nom/sqrt(denom); + return -nom/sqrt(var1*var2); +} + + diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.h b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.h new file mode 100644 index 0000000000000000000000000000000000000000..39e45fa86a2f44eed4221eb5367d8b1ae8e4cb10 --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCC.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCC.h 4956 2014-11-12 15:50:23Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerZNCC.h + \brief +*/ + +#ifndef vpTemplateTrackerZNCC_hh +#define vpTemplateTrackerZNCC_hh + +#include <math.h> + +#include <visp/vpTemplateTracker.h> +#include <visp/vpImage.h> +#include <visp/vpDisplay.h> +#include <visp/vpImageTools.h> +#include <visp/vpIoTools.h> +#include <visp/vpImageTools.h> +#include <visp/vpImageFilter.h> +#include <visp/vpMath.h> +#include <visp/vpHomography.h> + +#define APPROX_NCC + +class VISP_EXPORT vpTemplateTrackerZNCC: public vpTemplateTracker +{ + protected: + vpRowVector DI; + vpRowVector temp; + + protected: + double getCost(const vpImage<unsigned char> &I, vpColVector &tp) ; + double getCost(const vpImage<unsigned char> &I) {vpColVector tp; return getCost(I,tp);} + virtual void initHessienDesired(const vpImage<unsigned char> &I)=0; + virtual void trackNoPyr(const vpImage<unsigned char> &I)=0; + + public: + vpTemplateTrackerZNCC(vpTemplateTrackerWarp *warp); + + void setGain(double _gain){gain=_gain;} +}; +#endif + diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.cpp b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98ab0ed8056a8a32a0160e4e3e7fa7c5f280c50a --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCCForwardAdditional.cpp 4783 2014-07-15 13:07:52Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <visp/vpTemplateTrackerZNCCForwardAdditional.h> +#include <visp/vpImageFilter.h> + +vpTemplateTrackerZNCCForwardAdditional::vpTemplateTrackerZNCCForwardAdditional(vpTemplateTrackerWarp *warp):vpTemplateTrackerZNCC(warp) +{ + useCompositionnal=false; +} + +void vpTemplateTrackerZNCCForwardAdditional::initHessienDesired(const vpImage<unsigned char> &I) +{ + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + vpImage<double> dIxx,dIxy,dIyx,dIyy; + vpImageFilter::getGradX(dIx, dIxx, fgdG,taillef); + vpImageFilter::getGradY(dIx, dIxy, fgdG,taillef); + + vpImageFilter::getGradX(dIy, dIyx, fgdG,taillef); + vpImageFilter::getGradY(dIy, dIyy, fgdG,taillef); + + Warp->computeCoeff(p); + double IW,dIWx,dIWy; + double Tij; + int i,j; + double i2,j2; + int Nbpoint=0; + + double moyTij=0; + double moyIW=0; + double denom=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + X2[0]=j;X2[1]=i; + + Warp->computeDenom(X1,p); + + j2=X2[0];i2=X2[1]; + + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + + Nbpoint++; + moyTij+=Tij; + moyIW+=IW; + } + } + moyTij=moyTij/Nbpoint; + moyIW=moyIW/Nbpoint; + Hdesire=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + X2[0]=j;X2[1]=i; + + Warp->computeDenom(X1,p); + + j2=X2[0];i2=X2[1]; + + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + + dIWx=dIx.getValue(i2,j2); + dIWy=dIy.getValue(i2,j2); + //Calcul du Hessien + Warp->dWarp(X1,X2,p,dW); + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIWx+dW[1][it]*dIWy; + + + double prod=(Tij-moyTij); + + double d_Ixx=dIxx.getValue(i2,j2); + double d_Iyy=dIyy.getValue(i2,j2); + double d_Ixy=dIxy.getValue(i2,j2); + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + Hdesire[it][jt] +=prod*(dW[0][it]*(dW[0][jt]*d_Ixx+dW[1][jt]*d_Ixy) + +dW[1][it]*(dW[0][jt]*d_Ixy+dW[1][jt]*d_Iyy)); + /*Hdesire[0][0]+=prod*d_Ixx; + Hdesire[1][0]+=prod*d_Ixy; + Hdesire[0][1]+=prod*d_Ixy; + Hdesire[1][1]+=prod*d_Iyy;*/ + + denom+=(Tij-moyTij)*(Tij-moyTij)*(IW-moyIW)*(IW-moyIW); + delete[] tempt; + } + + + } + + Hdesire=Hdesire/sqrt(denom); + vpMatrix::computeHLM(Hdesire,lambdaDep,HLMdesire); + HLMdesireInverse=HLMdesire.inverseByLU(); + //std::cout<<"Hdesire = "<<Hdesire<<std::endl; + +} + +void vpTemplateTrackerZNCCForwardAdditional::trackNoPyr(const vpImage<unsigned char> &I) +{ + double erreur=0; + int Nbpoint=0; + + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + /*vpImage<double> dIxx,dIxy,dIyx,dIyy; + getGradX(dIx, dIxx, fgdG,taillef); + getGradY(dIx, dIxy, fgdG,taillef); + + getGradX(dIy, dIyx, fgdG,taillef); + getGradY(dIy, dIyy, fgdG,taillef);*/ + + dW=0; + + //double lambda=lambdaDep; + double IW,dIWx,dIWy; + double Tij; + unsigned int iteration=0; + int i,j; + double i2,j2; + double alpha=2.; + do + { + Nbpoint=0; + erreur=0; + G=0; + H=0 ; + Warp->computeCoeff(p); + double moyTij=0; + double moyIW=0; + double denom=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + + Nbpoint++; + moyTij+=Tij; + moyIW+=IW; + } + } + + if(! Nbpoint) { + throw(vpException(vpException::divideByZeroError, + "Cannot track the template: no point")) ; + } + + moyTij=moyTij/Nbpoint; + moyIW=moyIW/Nbpoint; + //vpMatrix d2Wx(nbParam,nbParam); + //vpMatrix d2Wy(nbParam,nbParam); + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Tij=ptTemplate[point].val; + + if(!blur) + IW=I.getValue(i2,j2); + else + IW=BI.getValue(i2,j2); + + dIWx=dIx.getValue(i2,j2); + dIWy=dIy.getValue(i2,j2); + //Calcul du Hessien + Warp->dWarp(X1,X2,p,dW); + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIWx+dW[1][it]*dIWy; + + + double prod=(Tij-moyTij); + for(unsigned int it=0;it<nbParam;it++) + G[it]+=prod*tempt[it]; + + /* Warp->d2Warp(X1,X2,p,d2Wx,d2Wy); + for(int it=0;it<nbParam;it++) + for(int jt=0;jt<nbParam;jt++) + H[it][jt]+=prod*(d2Wx[it][jt]*dIWx+d2Wx[it][jt]*dIWy);*/ + /*double d_Ixx=dIxx.getValue(i2,j2); + double d_Iyy=dIyy.getValue(i2,j2); + double d_Ixy=dIxy.getValue(i2,j2); + + for(int it=0;it<nbParam;it++) + for(int jt=0;jt<nbParam;jt++) + H[it][jt] +=prod*(dW[0][it]*(dW[0][jt]*d_Ixx+dW[1][jt]*d_Ixy) + +dW[1][it]*(dW[0][jt]*d_Ixy+dW[1][jt]*d_Iyy));*/ + /*H[0][0]+=prod*d_Ixx; + H[1][0]+=prod*d_Ixy; + H[0][1]+=prod*d_Ixy; + H[1][1]+=prod*d_Iyy;*/ + + double er=(Tij-IW); + erreur+=(er*er); + denom+=(Tij-moyTij)*(Tij-moyTij)*(IW-moyIW)*(IW-moyIW); + delete[] tempt; + } + + + } + /*std::cout<<"G="<<G<<std::endl; + std::cout<<"H="<<H<<std::endl; + std::cout<<" denom="<<denom<<std::endl;*/ + G=G/sqrt(denom); + //std::cout<<G<<std::endl; + H=H/sqrt(denom); + + //if(Nbpoint==0)std::cout<<"plus de point dans template suivi"<<std::endl; // cannot occur + + try + { + //vpMatrix::computeHLM(H,lambda,HLM); + //dp=1.*HLM.inverseByLU()*G; + dp=1.*HLMdesireInverse*G; + } + catch(vpException &e) + { + //std::cout<<"probleme inversion"<<std::endl; + throw(e); + } + + dp=gain*dp; + if(useBrent) + { + alpha=2.; + computeOptimalBrentGain(I,p,erreur/Nbpoint,dp,alpha); + dp=alpha*dp; + } + p-=dp; + iteration++; + } + while( /*( erreur_prec-erreur<50) && */(iteration < iterationMax)); + + //std::cout<<"erreur "<<erreur<<std::endl; + nbIteration=iteration; +} + diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.h b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.h new file mode 100644 index 0000000000000000000000000000000000000000..5da1c7ec2fffc4cb6cf09625b4fcc3ec82764f25 --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCForwardAdditional.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCCForwardAdditional.h 4574 2014-01-09 08:48:51Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerZNCCForwardAdditional.h + \brief +*/ + +#ifndef vpTemplateTrackerZNCCForwardAdditional_hh +#define vpTemplateTrackerZNCCForwardAdditional_hh + + +#include <visp/vpTemplateTrackerZNCC.h> + +/*! + The algorithm implemented in this class is described in \cite Irani98a. + */ +class VISP_EXPORT vpTemplateTrackerZNCCForwardAdditional: public vpTemplateTrackerZNCC +{ + protected: + void initHessienDesired(const vpImage<unsigned char> &I); + void trackNoPyr(const vpImage<unsigned char> &I); + + public: + vpTemplateTrackerZNCCForwardAdditional(vpTemplateTrackerWarp *warp); +}; +#endif + diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.cpp b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d9cdcad9c7c7155fc048575e5b705ad06f67218 --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.cpp @@ -0,0 +1,439 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCCInverseCompositional.cpp 5264 2015-02-04 13:49:55Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +#include <limits> // numeric_limits + +#include <visp/vpTemplateTrackerZNCCInverseCompositional.h> +#include <visp/vpImageFilter.h> + +vpTemplateTrackerZNCCInverseCompositional::vpTemplateTrackerZNCCInverseCompositional(vpTemplateTrackerWarp *warp) + : vpTemplateTrackerZNCC(warp), compoInitialised(false), + evolRMS(0), x_pos(), y_pos(), threshold_RMS(1e-8), moydIrefdp() +{ + useInverse=true; +} + +void vpTemplateTrackerZNCCInverseCompositional::initCompInverse(const vpImage<unsigned char> &I) +{ + //std::cout<<"Initialise precomputed value of Compositionnal Inverse"<<std::endl; + int i,j; + + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + + X1[0]=j;X1[1]=i; + Warp->computeDenom(X1,p); + ptTemplate[point].dW=new double[nbParam]; + + double dx=ptTemplate[point].dx; + double dy=ptTemplate[point].dy; + //std::cout<<ptTemplate[point].dx<<","<<ptTemplate[point].dy<<std::endl; + + Warp->getdW0(i,j,dy,dx,ptTemplate[point].dW); + + } + //vpTRACE("fin Comp Inverse"); + compoInitialised=true; +} + +void vpTemplateTrackerZNCCInverseCompositional::initHessienDesired(const vpImage<unsigned char> &I) +{ + initCompInverse(I); + + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + vpImageFilter::getGradXGauss2D(I, dIx, fgG,fgdG,taillef); + vpImageFilter::getGradYGauss2D(I, dIy, fgG,fgdG,taillef); + + vpImage<double> dIxx,dIxy,dIyx,dIyy; + vpImageFilter::getGradX(dIx, dIxx, fgdG,taillef); + vpImageFilter::getGradY(dIx, dIxy, fgdG,taillef); + + vpImageFilter::getGradX(dIy, dIyx, fgdG,taillef); + vpImageFilter::getGradY(dIy, dIyy, fgdG,taillef); + + Warp->computeCoeff(p); + double Ic,dIcx=0.,dIcy=0.; + double Iref; + int i,j; + double i2,j2; + int Nbpoint=0; + + double moyIref=0; + double moyIc=0; + double denom=0; + moydIrefdp.resize(nbParam); moydIrefdp=0; + vpMatrix moyd2Iref(nbParam,nbParam);moyd2Iref=0; + + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + X2[0]=j;X2[1]=i; + + Warp->computeDenom(X1,p); + + j2=X2[0];i2=X2[1]; + + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Iref=ptTemplate[point].val; + + if(!blur) + Ic=I.getValue(i2,j2); + else + Ic=BI.getValue(i2,j2); + + Nbpoint++; + moyIref+=Iref; + moyIc+=Ic; + + for(unsigned int it=0;it<nbParam;it++) + moydIrefdp[it]+=ptTemplate[point].dW[it]; + + + Warp->dWarp(X1,X2,p,dW); + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIcx+dW[1][it]*dIcy; + double d_Ixx=dIxx.getValue(i2,j2); + double d_Iyy=dIyy.getValue(i2,j2); + double d_Ixy=dIxy.getValue(i2,j2); + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + { + moyd2Iref[it][jt] +=(dW[0][it]*(dW[0][jt]*d_Ixx+dW[1][jt]*d_Ixy) + +dW[1][it]*(dW[0][jt]*d_Ixy+dW[1][jt]*d_Iyy)); + } + + delete[] tempt; + + + } + } + + moyIref=moyIref/Nbpoint; + moydIrefdp=moydIrefdp/Nbpoint; + moyd2Iref=moyd2Iref/Nbpoint; + moyIc=moyIc/Nbpoint; + Hdesire=0; + double covarIref=0,covarIc=0; + double sIcIref=0; + vpColVector sIcdIref(nbParam);sIcdIref=0; + vpMatrix sIcd2Iref(nbParam,nbParam);sIcd2Iref=0; + vpMatrix sdIrefdIref(nbParam,nbParam);sdIrefdIref=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + X2[0]=j;X2[1]=i; + + Warp->computeDenom(X1,p); + + j2=X2[0];i2=X2[1]; + + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Iref=ptTemplate[point].val; + + if(!blur) + Ic=I.getValue(i2,j2); + else + Ic=BI.getValue(i2,j2); + + dIcx=dIx.getValue(i2,j2); + dIcy=dIy.getValue(i2,j2); + + Warp->dWarp(X1,X2,p,dW); + + double *tempt=new double[nbParam]; + for(unsigned int it=0;it<nbParam;it++) + tempt[it]=dW[0][it]*dIcx+dW[1][it]*dIcy; + + double prodIc=(Ic-moyIc); + + double d_Ixx=dIxx.getValue(i2,j2); + double d_Iyy=dIyy.getValue(i2,j2); + double d_Ixy=dIxy.getValue(i2,j2); + + for(unsigned int it=0;it<nbParam;it++) + for(unsigned int jt=0;jt<nbParam;jt++) + { + sIcd2Iref[it][jt] +=prodIc*(dW[0][it]*(dW[0][jt]*d_Ixx+dW[1][jt]*d_Ixy) + +dW[1][it]*(dW[0][jt]*d_Ixy+dW[1][jt]*d_Iyy)-moyd2Iref[it][jt]); + sdIrefdIref[it][jt] +=(ptTemplate[point].dW[it]-moydIrefdp[it])*(ptTemplate[point].dW[jt]-moydIrefdp[jt]); + } + + + delete[] tempt; + + for(unsigned int it=0;it<nbParam;it++) + sIcdIref[it]+=prodIc*(ptTemplate[point].dW[it]-moydIrefdp[it]); + + covarIref+=(Iref-moyIref)*(Iref-moyIref); + covarIc+=(Ic-moyIc)*(Ic-moyIc); + sIcIref+=(Iref-moyIref)*(Ic-moyIc); + } + + + } + covarIref=sqrt(covarIref); + covarIc=sqrt(covarIc); + + denom=covarIref*covarIc; + + double NCC=sIcIref/denom; + //std::cout<<"NCC = "<<NCC<<std::endl; + vpColVector dcovarIref(nbParam);dcovarIref=-sIcdIref/covarIref; + + vpColVector dNCC(nbParam);dNCC=(sIcdIref/denom-NCC*dcovarIref/covarIref); + vpMatrix d2covarIref(nbParam,nbParam); + d2covarIref=-(sIcd2Iref-sdIrefdIref+dcovarIref*dcovarIref.t())/covarIref; +#ifdef APPROX_NCC + Hdesire=sIcd2Iref/denom; +#else + Hdesire=(sIcd2Iref-sdIrefdIref+dcovarIref*dcovarIref.t())/denom; +#endif + vpMatrix::computeHLM(Hdesire,lambdaDep,HLMdesire); + HLMdesireInverse=HLMdesire.inverseByLU(); + //std::cout<<"Hdesire = "<<Hdesire<<std::endl; +} + +void vpTemplateTrackerZNCCInverseCompositional::trackNoPyr(const vpImage<unsigned char> &I) +{ + if(blur) + vpImageFilter::filter(I, BI,fgG,taillef); + + double erreur=0; + unsigned int Nbpoint=0; + vpColVector dpinv(nbParam); + double Ic; + double Iref; + unsigned int iteration=0; + int i,j; + double i2,j2; + initPosEvalRMS(p); + do + { + Nbpoint=0; + erreur=0; + G=0; + Warp->computeCoeff(p); + double moyIref=0; + double moyIc=0; + double denom=0; + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Iref=ptTemplate[point].val; + + if(!blur) + Ic=I.getValue(i2,j2); + else + Ic=BI.getValue(i2,j2); + + Nbpoint++; + moyIref+=Iref; + moyIc+=Ic; + } + + + } + if(Nbpoint > 0) + { + moyIref=moyIref/Nbpoint; + moyIc=moyIc/Nbpoint; + double sIcIref=0; + double covarIref=0,covarIc=0; + vpColVector sIcdIref(nbParam);sIcdIref=0; + vpColVector sIrefdIref(nbParam);sIrefdIref=0; + + + for(unsigned int point=0;point<templateSize;point++) + { + i=ptTemplate[point].y; + j=ptTemplate[point].x; + X1[0]=j;X1[1]=i; + + Warp->computeDenom(X1,p); + Warp->warpX(X1,X2,p); + + j2=X2[0];i2=X2[1]; + if((i2>=0)&&(j2>=0)&&(i2<I.getHeight()-1)&&(j2<I.getWidth()-1)) + { + Iref=ptTemplate[point].val; + + if(!blur) + Ic=I.getValue(i2,j2); + else + Ic=BI.getValue(i2,j2); + + + double prod=(Ic-moyIc); + for(unsigned int it=0;it<nbParam;it++) + sIcdIref[it]+=prod*(ptTemplate[point].dW[it]-moydIrefdp[it]); + for(unsigned int it=0;it<nbParam;it++) + sIrefdIref[it]+=(Iref-moyIref)*(ptTemplate[point].dW[it]-moydIrefdp[it]); + + double er=(Iref-Ic); + erreur+=(er*er); + //denom+=(Iref-moyIref)*(Iref-moyIref)*(Ic-moyIc)*(Ic-moyIc); + covarIref+=(Iref-moyIref)*(Iref-moyIref); + covarIc+=(Ic-moyIc)*(Ic-moyIc); + sIcIref+=(Iref-moyIref)*(Ic-moyIc); + } + + + } + covarIref=sqrt(covarIref); + covarIc=sqrt(covarIc); + denom=covarIref*covarIc; + + //if(denom==0.0) + if (std::fabs(denom) <= std::numeric_limits<double>::epsilon()) + { + diverge=true; + } + else + { + double NCC=sIcIref/denom; + vpColVector dcovarIref(nbParam);dcovarIref=sIrefdIref/covarIref; + G=1.*(sIcdIref/denom-NCC*dcovarIref/covarIref); + + + try + { + dp=-1.*HLMdesireInverse*G; + } + catch(...) + { + std::cout<<"probleme inversion"<<std::endl; + break; + } + + Warp->getParamInverse(dp,dpinv); + Warp->pRondp(p,dpinv,p); + + computeEvalRMS(p); + } + } + else + diverge=true; + + iteration++; + } + while( (!diverge &&(evolRMS>threshold_RMS) && (iteration < iterationMax))); + + //std::cout<<"erreur "<<erreur<<std::endl; + nbIteration=iteration; + + deletePosEvalRMS(); +} + +void vpTemplateTrackerZNCCInverseCompositional::initPosEvalRMS(vpColVector &p_) +{ + unsigned int nb_corners = zoneTracked->getNbTriangle() * 3; + x_pos.resize(nb_corners); + y_pos.resize(nb_corners); + + Warp->computeCoeff(p); + vpTemplateTrackerTriangle triangle; + + for(unsigned int i=0;i<zoneTracked->getNbTriangle();i++) + { + zoneTracked->getTriangle(i, triangle); + for (unsigned int j=0; j<3; j++) { + triangle.getCorner(j, X1[0], X1[1]); + + Warp->computeDenom(X1,p_); + Warp->warpX(X1,X2,p_); + x_pos[i*3+j]=X2[0]; + y_pos[i*3+j]=X2[1]; + } + } +} + +void vpTemplateTrackerZNCCInverseCompositional::computeEvalRMS(const vpColVector &p_) +{ + unsigned int nb_corners = zoneTracked->getNbTriangle() * 3; + + Warp->computeCoeff(p_); + evolRMS=0; + vpTemplateTrackerTriangle triangle; + + for(unsigned int i=0;i<zoneTracked->getNbTriangle();i++) + { + zoneTracked->getTriangle(i, triangle); + for (unsigned int j=0; j<3; j++) { + triangle.getCorner(j, X1[0], X1[1]); + + Warp->computeDenom(X1,p_); + Warp->warpX(X1,X2,p_); + evolRMS+=(x_pos[i*3+j]-X2[0])*(x_pos[i*3+j]-X2[0])+(y_pos[i*3+j]-X2[1])*(y_pos[i*3+j]-X2[1]); + x_pos[i*3+j]=X2[0]; + y_pos[i*3+j]=X2[1]; + } + } + evolRMS=evolRMS/nb_corners; + +} + +void vpTemplateTrackerZNCCInverseCompositional::deletePosEvalRMS() +{ +} diff --git a/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.h b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.h new file mode 100644 index 0000000000000000000000000000000000000000..814b2379475b15c49c161f2e80a2c3396fdd983d --- /dev/null +++ b/src/tracking/template-tracker/zncc/vpTemplateTrackerZNCCInverseCompositional.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * $Id: vpTemplateTrackerZNCCInverseCompositional.h 4669 2014-02-16 16:25:57Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Template tracker. + * + * Authors: + * Amaury Dame + * Aurelien Yol + * Fabien Spindler + * + *****************************************************************************/ +/*! + \file vpTemplateTrackerZNCCInverseCompositional.h + \brief +*/ + +#ifndef vpTemplateTrackerZNCCInverseCompositional_hh +#define vpTemplateTrackerZNCCInverseCompositional_hh + +#include <vector> + +#include <visp/vpTemplateTrackerZNCC.h> + +/*! + The algorithm implemented in this class is described in \cite Irani98a. + */ +class VISP_EXPORT vpTemplateTrackerZNCCInverseCompositional: public vpTemplateTrackerZNCC +{ + protected: + bool compoInitialised; + //pour eval evolRMS + double evolRMS; + std::vector<double> x_pos; + std::vector<double> y_pos; + double threshold_RMS; + vpColVector moydIrefdp; + + protected: + void initCompInverse(const vpImage<unsigned char> &I); + void initHessienDesired(const vpImage<unsigned char> &I); + void trackNoPyr(const vpImage<unsigned char> &I); + void deletePosEvalRMS(); + void computeEvalRMS(const vpColVector &p); + void initPosEvalRMS(vpColVector &p); + + public: + vpTemplateTrackerZNCCInverseCompositional(vpTemplateTrackerWarp *warp); + + void setThresholdRMS(double threshold){threshold_RMS=threshold;} +}; +#endif + diff --git a/src/video/vpFFMPEG.cpp b/src/video/vpFFMPEG.cpp index 85dc4cffe0a0146533c17e3d1fde3bd8753003e8..238238a30d6293899225b253b0bf3e1db25e98cd 100644 --- a/src/video/vpFFMPEG.cpp +++ b/src/video/vpFFMPEG.cpp @@ -3,7 +3,7 @@ * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,23 +65,15 @@ extern "C" Basic constructor. */ vpFFMPEG::vpFFMPEG() + : width(-1), height(-1), frameNumber(0), pFormatCtx(NULL), pCodecCtx(NULL), + pCodec(NULL), pFrame(NULL), pFrameRGB(NULL), pFrameGRAY(NULL), packet(NULL), + img_convert_ctx(NULL), videoStream(0), numBytes(0), buffer(NULL), index(), + streamWasOpen(false), streamWasInitialized(false), color_type(COLORED), + f(NULL), outbuf(NULL), picture_buf(NULL), outbuf_size(0), out_size(0), + bit_rate(500000), encoderWasOpened(false), + framerate_stream(-1), framerate_encoder(25) { - frameNumber = 0; - width = -1; - height = -1; - framerate_stream = -1; - framerate_encoder = 25; - buffer = NULL; - streamWasOpen = false; - streamWasInitialized = false; - bit_rate = 500000; - outbuf = NULL; - picture_buf = NULL; - f = NULL; - encoderWasOpened = false; packet = new AVPacket; - - pFormatCtx = NULL; } /*! @@ -99,14 +91,14 @@ vpFFMPEG::~vpFFMPEG() and the dimension of the images using getWidth() and getHeight(). \param filename : Path to the video which has to be read. - \param color_type : Desired color map used to open the video. + \param colortype : Desired color map used to open the video. The parameter can take two values : COLORED and GRAY_SCALED. \return It returns true if the paramters could be initialized. Else it returns false. */ -bool vpFFMPEG::openStream(const char *filename, vpFFMPEGColorType color_type) +bool vpFFMPEG::openStream(const char *filename, vpFFMPEGColorType colortype) { - this->color_type = color_type; + this->color_type = colortype; av_register_all(); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,0,0) // libavformat 52.84.0 @@ -141,9 +133,14 @@ bool vpFFMPEG::openStream(const char *filename, vpFFMPEGColorType color_type) #endif { videoStream = i; - std::cout << "rate: " << pFormatCtx->streams[i]->r_frame_rate.num << " " << pFormatCtx->streams[i]->r_frame_rate.den << std::endl; + //std::cout << "rate: " << pFormatCtx->streams[i]->r_frame_rate.num << " " << pFormatCtx->streams[i]->r_frame_rate.den << std::endl; +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(55,12,0) framerate_stream = pFormatCtx->streams[i]->r_frame_rate.num; framerate_stream /= pFormatCtx->streams[i]->r_frame_rate.den; +#else + framerate_stream = pFormatCtx->streams[i]->avg_frame_rate.num; + framerate_stream /= pFormatCtx->streams[i]->avg_frame_rate.den; +#endif found_codec= true; break; } @@ -170,11 +167,19 @@ bool vpFFMPEG::openStream(const char *filename, vpFFMPEGColorType color_type) return false; // Could not open codec } +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,34,0) pFrame = avcodec_alloc_frame(); - +#else + pFrame = av_frame_alloc(); // libavcodec 55.34.1 +#endif + if (color_type == vpFFMPEG::COLORED) { +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,34,0) pFrameRGB=avcodec_alloc_frame(); +#else + pFrameRGB=av_frame_alloc(); // libavcodec 55.34.1 +#endif if (pFrameRGB == NULL) return false; @@ -184,7 +189,11 @@ bool vpFFMPEG::openStream(const char *filename, vpFFMPEGColorType color_type) else if (color_type == vpFFMPEG::GRAY_SCALED) { +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,34,0) pFrameGRAY=avcodec_alloc_frame(); +#else + pFrameGRAY=av_frame_alloc(); // libavcodec 55.34.1 +#endif if (pFrameGRAY == NULL) return false; @@ -593,7 +602,10 @@ void vpFFMPEG::closeStream() { if (streamWasOpen) { - av_free(buffer); + if (buffer != NULL) { + free(buffer); + buffer = NULL; + } if (color_type == vpFFMPEG::COLORED) av_free(pFrameRGB); @@ -643,8 +655,7 @@ void vpFFMPEG::closeStream() Allocates and initializes the parameters depending on the video to write. \param filename : Path to the video which has to be writen. - \param width : Width of the image which will be saved. - \param height : Height of the image which will be saved. + \param w,h : Width and height of the image which will be saved. \param codec : Type of codec used to encode the video. By default codec is set to AV_CODEC_ID_MPEG1VIDEO. But if installed, you can use one of the @@ -656,9 +667,9 @@ void vpFFMPEG::closeStream() \return It returns true if the paramters could be initialized. Else it returns false. */ #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 -bool vpFFMPEG::openEncoder(const char *filename, unsigned int width, unsigned int height, CodecID codec) +bool vpFFMPEG::openEncoder(const char *filename, unsigned int w, unsigned int h, CodecID codec) #else -bool vpFFMPEG::openEncoder(const char *filename, unsigned int width, unsigned int height, AVCodecID codec) +bool vpFFMPEG::openEncoder(const char *filename, unsigned int w, unsigned int h, AVCodecID codec) #endif { av_register_all(); @@ -675,18 +686,25 @@ bool vpFFMPEG::openEncoder(const char *filename, unsigned int width, unsigned in #else pCodecCtx = avcodec_alloc_context3(NULL); #endif + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,34,0) pFrame = avcodec_alloc_frame(); pFrameRGB = avcodec_alloc_frame(); +#else + pFrame = av_frame_alloc(); // libavcodec 55.34.1 + pFrameRGB = av_frame_alloc(); // libavcodec 55.34.1 +#endif /* put sample parameters */ pCodecCtx->bit_rate = (int)bit_rate; /* resolution must be a multiple of two */ - pCodecCtx->width = (int)width; - pCodecCtx->height = (int)height; - this->width = (int)width; - this->height = (int)height; + pCodecCtx->width = (int)w; + pCodecCtx->height = (int)h; + this->width = (int)w; + this->height = (int)h; /* frames per second */ - pCodecCtx->time_base= (AVRational){1,framerate_encoder}; + pCodecCtx->time_base.num = 1; + pCodecCtx->time_base.den = framerate_encoder; pCodecCtx->gop_size = 10; /* emit one intra frame every ten frames */ pCodecCtx->max_b_frames=1; pCodecCtx->pix_fmt = PIX_FMT_YUV420P; diff --git a/src/video/vpFFMPEG.h b/src/video/vpFFMPEG.h index 5c6b586fb381d6f645f0ce50a5c97981748700bf..85b0986bbfc8b73aa42fd8b41fe6360791f9b6ce 100644 --- a/src/video/vpFFMPEG.h +++ b/src/video/vpFFMPEG.h @@ -3,7 +3,7 @@ * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -252,9 +252,9 @@ class VISP_EXPORT vpFFMPEG /*! Sets the bit rate of the video when encoding. - \param bit_rate : the expected bit rate. + \param rate : the expected bit rate. */ - inline void setBitRate(const unsigned int bit_rate) {this->bit_rate = bit_rate;} + inline void setBitRate(const unsigned int rate) {this->bit_rate = rate;} /*! Sets the framerate of the video when encoding. diff --git a/src/video/vpVideoReader.cpp b/src/video/vpVideoReader.cpp index f79518b26d934aae6c2a2aad8d147dfa634131b3..11aef7850b4a62e707e13e88f7fddfba116308e6 100644 --- a/src/video/vpVideoReader.cpp +++ b/src/video/vpVideoReader.cpp @@ -1,48 +1,48 @@ /**************************************************************************** - * - * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ - * - * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * ("GPL") version 2 as published by the Free Software Foundation. - * See the file LICENSE.txt at the root directory of this source - * distribution for additional information about the GNU GPL. - * - * For using ViSP with software that can not be combined with the GNU - * GPL, please contact INRIA about acquiring a ViSP Professional - * Edition License. - * - * See http://www.irisa.fr/lagadic/visp/visp.html for more information. - * - * This software was developed at: - * INRIA Rennes - Bretagne Atlantique - * Campus Universitaire de Beaulieu - * 35042 Rennes Cedex - * France - * http://www.irisa.fr/lagadic - * - * If you have questions regarding the use of this file, please contact - * INRIA at visp@inria.fr - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * - * Description: - * Read videos and image sequences. - * - * Authors: - * Nicolas Melchior - * Fabien Spindler - * - *****************************************************************************/ +* +* $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ +* +* This file is part of the ViSP software. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +* +* This software is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* ("GPL") version 2 as published by the Free Software Foundation. +* See the file LICENSE.txt at the root directory of this source +* distribution for additional information about the GNU GPL. +* +* For using ViSP with software that can not be combined with the GNU +* GPL, please contact INRIA about acquiring a ViSP Professional +* Edition License. +* +* See http://www.irisa.fr/lagadic/visp/visp.html for more information. +* +* This software was developed at: +* INRIA Rennes - Bretagne Atlantique +* Campus Universitaire de Beaulieu +* 35042 Rennes Cedex +* France +* http://www.irisa.fr/lagadic +* +* If you have questions regarding the use of this file, please contact +* INRIA at visp@inria.fr +* +* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Description: +* Read videos and image sequences. +* +* Authors: +* Nicolas Melchior +* Fabien Spindler +* +*****************************************************************************/ /*! - \file vpVideoReader.cpp - \brief Read videos and image sequences +\file vpVideoReader.cpp +\brief Read videos and image sequences */ #include <visp/vpDebug.h> @@ -50,511 +50,716 @@ #include <iostream> #include <fstream> +#include <limits> // numeric_limits /*! - Basic constructor. +Basic constructor. */ vpVideoReader::vpVideoReader() + : imSequence(NULL), +#ifdef VISP_HAVE_FFMPEG + ffmpeg(NULL), +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + capture(), frame(), +#endif + formatType(FORMAT_UNKNOWN), initFileName(false), isOpen(false), frameCount(0), + firstFrame(0), lastFrame(0), firstFrameIndexIsSet(false), lastFrameIndexIsSet(false) { - imSequence = NULL; - #ifdef VISP_HAVE_FFMPEG - ffmpeg = NULL; - #endif - initFileName = false; - isOpen = false; - firstFrame = 0; - frameCount = 0; - lastFrame = 0; - firstFrameIndexIsSet = false; - lastFrameIndexIsSet = false; } - /*! - Basic destructor. +Basic destructor. */ vpVideoReader::~vpVideoReader() { - if (imSequence != NULL) - { - delete imSequence; - } - #ifdef VISP_HAVE_FFMPEG - if (ffmpeg != NULL) - { - delete ffmpeg; - } - #endif + if (imSequence != NULL) + { + delete imSequence; + } +#ifdef VISP_HAVE_FFMPEG + if (ffmpeg != NULL) + { + delete ffmpeg; + } +#endif } /*! - It enables to set the path and the name of the file(s) which as/have to be read. - - If you want to read a video file, \f$ filename \f$ corresponds to the path to the file (example : /local/video.mpeg). - - If you want to read a sequence of images, \f$ filename \f$ corresponds to the path followed by the image name template. For exemple, if you want to read different images named image0001.jpeg, image0002.jpg, ... and located in the folder /local/image, \f$ filename \f$ will be "/local/image/image%04d.jpg". - - \param filename : Path to a video file or file name template of a image sequence. +It enables to set the path and the name of the file(s) which as/have to be read. + +If you want to read a video file, \f$ filename \f$ corresponds to the path to the file (example : /local/video.mpeg). + +If you want to read a sequence of images, \f$ filename \f$ corresponds to the path followed by the image name template. For exemple, if you want to read different images named image0001.jpeg, image0002.jpg, ... and located in the folder /local/image, \f$ filename \f$ will be "/local/image/image%04d.jpg". + +\param filename : Path to a video file or file name template of a image sequence. */ void vpVideoReader::setFileName(const char *filename) { - if (filename == '\0') - { - vpERROR_TRACE("filename empty ") ; - throw (vpImageException(vpImageException::noFileNameError,"filename empty ")) ; + if (!filename || *filename == '\0') + { + vpERROR_TRACE("filename empty ") ; + throw (vpImageException(vpImageException::noFileNameError,"filename empty ")) ; + } + + if (strlen( filename ) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the file name")); + } + + strcpy(this->fileName,filename); + + formatType = getFormat(fileName); + + if (formatType == FORMAT_UNKNOWN) { + throw(vpException(vpException::badValue, "Filename extension not supported")); } - - strcpy(this->fileName,filename); - - formatType = getFormat(fileName); - - initFileName = true; + + initFileName = true; } /*! - It enables to set the path and the name of the file(s) which as/have to be read. +It enables to set the path and the name of the file(s) which as/have to be read. - If you want to read a video file, \f$ filename \f$ corresponds to the path to the file (example : /local/video.mpeg). +If you want to read a video file, \f$ filename \f$ corresponds to the path to the file (example : /local/video.mpeg). - If you want to read a sequence of images, \f$ filename \f$ corresponds to the path followed by the image name template. For exemple, if you want to read different images named image0001.jpeg, image0002.jpg, ... and located in the folder /local/image, \f$ filename \f$ will be "/local/image/image%04d.jpg". +If you want to read a sequence of images, \f$ filename \f$ corresponds to the path followed by the image name template. For exemple, if you want to read different images named image0001.jpeg, image0002.jpg, ... and located in the folder /local/image, \f$ filename \f$ will be "/local/image/image%04d.jpg". - \param filename : Path to a video file or file name template of a image sequence. +\param filename : Path to a video file or file name template of a image sequence. */ void vpVideoReader::setFileName(const std::string &filename) { - setFileName(filename.c_str()); + setFileName(filename.c_str()); } /*! - Sets all the parameters needed to read the video or the image sequence. - - Grab the first frame and stores it in the image \f$ I \f$. - - \param I : The image where the frame is stored. +Sets all the parameters needed to read the video or the image sequence. + +Grab the first frame and stores it in the image \f$ I \f$. + +\param I : The image where the frame is stored. */ void vpVideoReader::open(vpImage< vpRGBa > &I) { - if (!initFileName) - { - vpERROR_TRACE("The generic filename has to be set"); - throw (vpImageException(vpImageException::noFileNameError,"filename empty")); - } - - if (formatType == FORMAT_PGM || - formatType == FORMAT_PPM || - formatType == FORMAT_JPEG || - formatType == FORMAT_PNG || - formatType == FORMAT_TIFF || - formatType == FORMAT_BMP || - formatType == FORMAT_DIB || - formatType == FORMAT_PBM || - formatType == FORMAT_RASTER || - formatType == FORMAT_JPEG2000) - { - imSequence = new vpDiskGrabber; - imSequence->setGenericName(fileName); - if (firstFrameIndexIsSet) - imSequence->setImageNumber(firstFrame); - } - #ifdef VISP_HAVE_FFMPEG - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV || - formatType == FORMAT_OGV) - { - ffmpeg = new vpFFMPEG; - if(!ffmpeg->openStream(fileName, vpFFMPEG::COLORED)) - throw (vpException(vpException::ioError ,"Could not open the video")); - ffmpeg->initStream(); - } - - #else - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV || - formatType == FORMAT_OGV) - { - vpERROR_TRACE("To read video files the FFmpeg library has to be installed"); - throw (vpException(vpException::fatalError ,"the FFmpeg library is required")); - } - #endif - else if (formatType == FORMAT_UNKNOWN) - { - vpERROR_TRACE("The format of the file does not correpsond to a readable format."); - throw (vpException(vpException::fatalError ,"The format of the file does not correpsond to a readable format.")); - } - - findFirstFrameIndex(); - frameCount = firstFrame; - if(!getFrame(I,firstFrame)) - { - vpERROR_TRACE("Could not read the first frame"); - throw (vpException(vpException::ioError ,"Could not read the first frame")); - } - height = I.getHeight(); - width = I.getWidth(); - - isOpen = true; - - findLastFrameIndex(); + if (!initFileName) + { + vpERROR_TRACE("The generic filename has to be set"); + throw (vpImageException(vpImageException::noFileNameError,"filename empty")); + } + + if (isImageExtensionSupported()) + { + imSequence = new vpDiskGrabber; + imSequence->setGenericName(fileName); + if (firstFrameIndexIsSet) + imSequence->setImageNumber(firstFrame); + } + else if (isVideoExtensionSupported()) + { +#ifdef VISP_HAVE_FFMPEG + ffmpeg = new vpFFMPEG; + if(!ffmpeg->openStream(fileName, vpFFMPEG::COLORED)) + throw (vpException(vpException::ioError ,"Could not open the video with ffmpeg")); + ffmpeg->initStream(); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + capture.open(fileName); + + if(!capture.isOpened()) + { + throw (vpException(vpException::ioError ,"Could not open the video with opencv")); + } +#else + //vpERROR_TRACE("To read video files ViSP should be build with ffmpeg or opencv 3rd party libraries."); + throw (vpException(vpException::fatalError ,"To read video files ViSP should be build with ffmpeg or opencv 3rd >= 2.1.0 party libraries.")); +#endif + } + else if (formatType == FORMAT_UNKNOWN) + { + //vpERROR_TRACE("The format of the file does not correspond to a readable format."); + throw (vpException(vpException::fatalError ,"The format of the file does not correspond to a readable format supported by ViSP.")); + } + + findFirstFrameIndex(); + frameCount = firstFrame; + if(!getFrame(I, firstFrame)) + { + //vpERROR_TRACE("Could not read the video first frame"); + throw (vpException(vpException::ioError ,"Could not read the video first frame")); + } + + height = I.getHeight(); + width = I.getWidth(); + + isOpen = true; + findLastFrameIndex(); } /*! - Sets all the parameters needed to read the video or the image sequence. - - Grab the first frame and stores it in the image \f$ I \f$. - - \param I : The image where the frame is stored. +Sets all the parameters needed to read the video or the image sequence. + +Grab the first frame and stores it in the image \f$ I \f$. + +\param I : The image where the frame is stored. */ void vpVideoReader::open(vpImage<unsigned char> &I) { - if (!initFileName) - { - vpERROR_TRACE("The generic filename has to be set"); - throw (vpImageException(vpImageException::noFileNameError,"filename empty")); - } - - if (formatType == FORMAT_PGM || - formatType == FORMAT_PPM || - formatType == FORMAT_JPEG || - formatType == FORMAT_PNG || - formatType == FORMAT_TIFF || - formatType == FORMAT_BMP || - formatType == FORMAT_DIB || - formatType == FORMAT_PBM || - formatType == FORMAT_RASTER || - formatType == FORMAT_JPEG2000) - { - imSequence = new vpDiskGrabber; - imSequence->setGenericName(fileName); - if (firstFrameIndexIsSet) - imSequence->setImageNumber(firstFrame); - } - #ifdef VISP_HAVE_FFMPEG - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV || - formatType == FORMAT_OGV) - { - ffmpeg = new vpFFMPEG; - if (!ffmpeg->openStream(fileName, vpFFMPEG::GRAY_SCALED)) - throw (vpException(vpException::ioError ,"Could not open the video")); - ffmpeg->initStream(); - } - #else - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV || - formatType == FORMAT_OGV) - { - vpERROR_TRACE("To read video files the FFmpeg library has to be installed"); - throw (vpException(vpException::fatalError ,"the FFmpeg library is required")); - } - #endif - else if (formatType == FORMAT_UNKNOWN) - { - vpERROR_TRACE("The format of the file does not correpsond to a readable format."); - throw (vpException(vpException::fatalError ,"The format of the file does not correpsond to a readable format.")); + if (!initFileName) + { + vpERROR_TRACE("The generic filename has to be set"); + throw (vpImageException(vpImageException::noFileNameError,"filename empty")); + } + + if (isImageExtensionSupported()) + { + imSequence = new vpDiskGrabber; + imSequence->setGenericName(fileName); + if (firstFrameIndexIsSet) + imSequence->setImageNumber(firstFrame); + } + else if (isVideoExtensionSupported()) + { +#ifdef VISP_HAVE_FFMPEG + ffmpeg = new vpFFMPEG; + if (!ffmpeg->openStream(fileName, vpFFMPEG::GRAY_SCALED)) + throw (vpException(vpException::ioError ,"Could not open the video with ffmpeg")); + ffmpeg->initStream(); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + capture.open(fileName); + + if(!capture.isOpened()) + { + throw (vpException(vpException::ioError ,"Could not open the video with opencv")); + } +#else + //vpERROR_TRACE("To read video files ViSP should be build with ffmpeg or opencv 3rd party libraries."); + throw (vpException(vpException::fatalError ,"To read video files ViSP should be build with ffmpeg or opencv >= 2.1.0 3rd party libraries.")); +#endif + } + else if (formatType == FORMAT_UNKNOWN) + { + //vpERROR_TRACE("The format of the file does not correspond to a readable format."); + throw (vpException(vpException::fatalError ,"The format of the file does not correspond to a readable format supported by ViSP.")); } - - findFirstFrameIndex(); - frameCount = firstFrame; - if(!getFrame(I,firstFrame)) - { - vpERROR_TRACE("Could not read the first frame"); - throw (vpException(vpException::ioError ,"Could not read the first frame")); + + findFirstFrameIndex(); + frameCount = firstFrame; + if(!getFrame(I,firstFrame)) + { + //vpERROR_TRACE("Could not read the video first frame"); + throw (vpException(vpException::ioError ,"Could not read the video first frame")); } - height = I.getHeight(); - width = I.getWidth(); - - isOpen = true; - - findLastFrameIndex(); + + height = I.getHeight(); + width = I.getWidth(); + + isOpen = true; + findLastFrameIndex(); } /*! - Grabs the current (k) image in the stack of frames and increments the frame counter - in order to grab the next image (k+1) during the next use of the method. If open() - was not called priviously, this method opens the video reader. - - This method enables to use the class as frame grabber. - - \param I : The image where the frame is stored. +Grabs the current (k) image in the stack of frames and increments the frame counter +in order to grab the next image (k+1) during the next use of the method. If open() +was not called previously, this method opens the video reader. + +This method enables to use the class as frame grabber. + +\param I : The image where the frame is stored. */ void vpVideoReader::acquire(vpImage< vpRGBa > &I) { - if (!isOpen) { - open(I); + if (!isOpen) { + open(I); + } + + //getFrame(I,frameCount); + if (imSequence != NULL) + { + imSequence->acquire(I); + frameCount++; // next index } - - //getFrame(I,frameCount); - if (imSequence != NULL) - imSequence->acquire(I); - #ifdef VISP_HAVE_FFMPEG - else if (ffmpeg !=NULL) - ffmpeg->acquire(I); - #endif - - frameCount++; +#ifdef VISP_HAVE_FFMPEG + else if (ffmpeg !=NULL) + { + ffmpeg->acquire(I); + frameCount++; // next index + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + else + { + capture >> frame; +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + frameCount = (long) capture.get(cv::CAP_PROP_POS_FRAMES); // next index +#else + frameCount = (long) capture.get(CV_CAP_PROP_POS_FRAMES); // next index +#endif + + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); + } +#endif } /*! - Grabs the kth image in the stack of frames and increments the frame counter in order to grab the next image (k+1) during the next use of the method. - - This method enables to use the class as frame grabber. - - \param I : The image where the frame is stored. +Grabs the kth image in the stack of frames and increments the frame counter in order to grab the next image (k+1) during the next use of the method. + +This method enables to use the class as frame grabber. + +\param I : The image where the frame is stored. */ void vpVideoReader::acquire(vpImage< unsigned char > &I) { - if (!isOpen) { - open(I); + if (!isOpen) { + open(I); + } + + if (imSequence != NULL) + { + imSequence->acquire(I); + frameCount++; // next index + } +#ifdef VISP_HAVE_FFMPEG + else if (ffmpeg != NULL) + { + ffmpeg->acquire(I); + frameCount++; // next index + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + else + { + capture >> frame; +#if VISP_HAVE_OPENCV_VERSION >= 0x030000 + frameCount = (long) capture.get(cv::CAP_PROP_POS_FRAMES); // next index +#else + frameCount = (long) capture.get(CV_CAP_PROP_POS_FRAMES); // next index +#endif + + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); } - - if (imSequence != NULL) - imSequence->acquire(I); - #ifdef VISP_HAVE_FFMPEG - else if (ffmpeg != NULL) - ffmpeg->acquire(I); - #endif - - frameCount++; +#endif } /*! - Gets the \f$ frame \f$ th frame and stores it in the image \f$ I \f$. - - \warning For the video files this method is not precise, and returns the nearest key frame from the expected frame. - But this method enables to position the reader where you want. Then, use the acquire method to grab the following images - one after one. - - \param I : The vpImage used to stored the frame. - \param frame : The index of the frame which has to be read. - - \return It returns true if the frame could be read. Else it returns false. +Gets the \f$ frame \f$ th frame and stores it in the image \f$ I \f$. + +\warning For the video files this method is not precise, and returns the nearest key frame from the expected frame. +But this method enables to position the reader where you want. Then, use the acquire method to grab the following images +one after one. + +\param I : The vpImage used to stored the frame. +\param frame_index : The index of the frame which has to be read. + +\return It returns true if the frame could be read. Else it returns false. */ -bool vpVideoReader::getFrame(vpImage<vpRGBa> &I, long frame) +bool vpVideoReader::getFrame(vpImage<vpRGBa> &I, long frame_index) { - if (imSequence != NULL) + if (imSequence != NULL) + { + try + { + imSequence->acquire(I, frame_index); + frameCount = frame_index + 1; // next index + } + catch(...) + { + vpERROR_TRACE("Couldn't find the %u th frame", frame_index) ; + return false; + } + } + else { - try +#ifdef VISP_HAVE_FFMPEG + if(!ffmpeg->getFrame(I, (unsigned int)frame_index)) { - imSequence->acquire(I, frame); + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index) ; + return false; } - catch(...) + frameCount = frame_index + 1; // next index +#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x030000) + if(!capture.set(cv::CAP_PROP_POS_FRAMES, frame_index)) { - vpERROR_TRACE("Couldn't find the %u th frame", frame) ; + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index) ; return false; } - } - #ifdef VISP_HAVE_FFMPEG - else - { - - if(!ffmpeg->getFrame(I, (unsigned int)frame)) + + capture >> frame; + frameCount = (long) capture.get(cv::CAP_PROP_POS_FRAMES); // next index + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); +#elif defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) + if(!capture.set(CV_CAP_PROP_POS_FRAMES, frame_index)) { - vpERROR_TRACE("Couldn't find the %ld th frame", frame) ; + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index) ; return false; } + + capture >> frame; + frameCount = (long) capture.get(CV_CAP_PROP_POS_FRAMES); // next index + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); +#endif } - #endif - return true; + + return true; } /*! - Gets the \f$ frame \f$ th frame and stores it in the image \f$ I \f$. - - \warning For the video files this method is not precise, and returns the nearest key frame from the expected frame. - But this method enables to position the reader where you want. Then, use the acquire method to grab the following images - one after one. - - \param I : The vpImage used to stored the frame. - \param frame : The index of the frame which has to be read. - - \return It returns true if the frame could be read. Else it returns false. +Gets the \f$ frame \f$ th frame and stores it in the image \f$ I \f$. + +\warning For the video files this method is not precise, and returns the nearest key frame from the expected frame. +But this method enables to position the reader where you want. Then, use the acquire method to grab the following images +one after one. + +\param I : The vpImage used to stored the frame. +\param frame_index : The index of the frame which has to be read. + +\return It returns true if the frame could be read. Else it returns false. */ -bool vpVideoReader::getFrame(vpImage<unsigned char> &I, long frame) +bool vpVideoReader::getFrame(vpImage<unsigned char> &I, long frame_index) { - if (imSequence != NULL) + if (imSequence != NULL) + { + try + { + imSequence->acquire(I, frame_index); + frameCount = frame_index + 1; + } + catch(...) + { + vpERROR_TRACE("Couldn't find the %u th frame", frame_index) ; + return false; + } + } + else { - try +#ifdef VISP_HAVE_FFMPEG + if(!ffmpeg->getFrame(I, (unsigned int)frame_index)) { - imSequence->acquire(I, frame); + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index) ; + return false; } - catch(...) + frameCount = frame_index + 1; // next index +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + if(!capture.set(cv::CAP_PROP_POS_FRAMES, frame_index)) { - vpERROR_TRACE("Couldn't find the %u th frame", frame) ; + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index) ; return false; } - } - #ifdef VISP_HAVE_FFMPEG - else - { - if(!ffmpeg->getFrame(I, (unsigned int)frame)) + capture >> frame; + frameCount = (long) capture.get(cv::CAP_PROP_POS_FRAMES); // next index + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + if(!capture.set(CV_CAP_PROP_POS_FRAMES, frame_index)) { - vpERROR_TRACE("Couldn't find the %ld th frame", frame) ; + vpERROR_TRACE("Couldn't find the %ld th frame", frame_index); // next index return false; } + capture >> frame; + frameCount = (long) capture.get(CV_CAP_PROP_POS_FRAMES); + if(frame.empty()) + setLastFrameIndex(frameCount-1); + else + vpImageConvert::convert(frame, I); +#endif } - #endif - return true; + + return true; } /*! - Gets the format of the file(s) which has/have to be read. - - \return Returns the format. +Gets the format of the file(s) which has/have to be read. + +\return Returns the format. */ vpVideoReader::vpVideoFormatType -vpVideoReader::getFormat(const char *filename) -{ - std::string sfilename(filename); - - std::string ext = vpVideoReader::getExtension(sfilename); - - if (ext.compare(".PGM") == 0) - return FORMAT_PGM; - else if (ext.compare(".pgm") == 0) - return FORMAT_PGM; - else if (ext.compare(".PPM") == 0) - return FORMAT_PPM; - else if (ext.compare(".ppm") == 0) - return FORMAT_PPM; - else if (ext.compare(".JPG") == 0) - return FORMAT_JPEG; - else if (ext.compare(".jpg") == 0) - return FORMAT_JPEG; - else if (ext.compare(".JPEG") == 0) - return FORMAT_JPEG; - else if (ext.compare(".jpeg") == 0) - return FORMAT_JPEG; - else if (ext.compare(".PNG") == 0) - return FORMAT_PNG; - else if (ext.compare(".png") == 0) - return FORMAT_PNG; + vpVideoReader::getFormat(const char *filename) +{ + std::string sfilename(filename); + + std::string ext = vpVideoReader::getExtension(sfilename); + + if (ext.compare(".PGM") == 0) + return FORMAT_PGM; + else if (ext.compare(".pgm") == 0) + return FORMAT_PGM; + else if (ext.compare(".PPM") == 0) + return FORMAT_PPM; + else if (ext.compare(".ppm") == 0) + return FORMAT_PPM; + else if (ext.compare(".JPG") == 0) + return FORMAT_JPEG; + else if (ext.compare(".jpg") == 0) + return FORMAT_JPEG; + else if (ext.compare(".JPEG") == 0) + return FORMAT_JPEG; + else if (ext.compare(".jpeg") == 0) + return FORMAT_JPEG; + else if (ext.compare(".PNG") == 0) + return FORMAT_PNG; + else if (ext.compare(".png") == 0) + return FORMAT_PNG; + else if (ext.compare(".TIFF") == 0) + return FORMAT_TIFF; + else if (ext.compare(".tiff") == 0) + return FORMAT_TIFF; + else if (ext.compare(".BMP") == 0) + return FORMAT_BMP; + else if (ext.compare(".bmp") == 0) + return FORMAT_BMP; + else if (ext.compare(".DIB") == 0) + return FORMAT_DIB; + else if (ext.compare(".dib") == 0) + return FORMAT_DIB; + else if (ext.compare(".PBM") == 0) + return FORMAT_PBM; + else if (ext.compare(".PBM") == 0) + return FORMAT_PBM; + else if (ext.compare(".SR") == 0) + return FORMAT_PBM; + else if (ext.compare(".sr") == 0) + return FORMAT_PBM; + else if (ext.compare(".RAS") == 0) + return FORMAT_RASTER; + else if (ext.compare(".ras") == 0) + return FORMAT_RASTER; + else if (ext.compare(".JP2") == 0) + return FORMAT_JPEG2000; + else if (ext.compare(".jp2") == 0) + return FORMAT_JPEG2000; else if (ext.compare(".AVI") == 0) - return FORMAT_AVI; - else if (ext.compare(".avi") == 0) - return FORMAT_AVI; - else if (ext.compare(".MPEG") == 0) - return FORMAT_MPEG; - else if (ext.compare(".mpeg") == 0) - return FORMAT_MPEG; - else if (ext.compare(".MPG") == 0) - return FORMAT_MPEG; - else if (ext.compare(".mpg") == 0) - return FORMAT_MPEG; - else if (ext.compare(".MOV") == 0) - return FORMAT_MOV; - else if (ext.compare(".mov") == 0) - return FORMAT_MOV; - else if (ext.compare(".OGV") == 0) - return FORMAT_OGV; - else if (ext.compare(".ogv") == 0) - return FORMAT_OGV; - else - return FORMAT_UNKNOWN; + return FORMAT_AVI; + else if (ext.compare(".avi") == 0) + return FORMAT_AVI; + else if (ext.compare(".MPEG") == 0) + return FORMAT_MPEG; + else if (ext.compare(".mpeg") == 0) + return FORMAT_MPEG; + else if (ext.compare(".MPG") == 0) + return FORMAT_MPEG; + else if (ext.compare(".mpg") == 0) + return FORMAT_MPEG; + else if (ext.compare(".MPEG4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".mpeg4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".MP4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".mp4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".MOV") == 0) + return FORMAT_MOV; + else if (ext.compare(".mov") == 0) + return FORMAT_MOV; + else if (ext.compare(".OGV") == 0) + return FORMAT_OGV; + else if (ext.compare(".ogv") == 0) + return FORMAT_OGV; + else if (ext.compare(".WMV") == 0) + return FORMAT_WMV; + else if (ext.compare(".wmv") == 0) + return FORMAT_WMV; + else if (ext.compare(".FLV") == 0) + return FORMAT_FLV; + else if (ext.compare(".flv") == 0) + return FORMAT_FLV; + else if (ext.compare(".MKV") == 0) + return FORMAT_MKV; + else if (ext.compare(".mkv") == 0) + return FORMAT_MKV; + else + return FORMAT_UNKNOWN; } // return the extension of the file including the dot std::string vpVideoReader::getExtension(const std::string &filename) { - // extract the extension - size_t dot = filename.find_last_of("."); - std::string ext = filename.substr(dot, filename.size()-1); - return ext; + // extract the extension + size_t dot = filename.find_last_of("."); + std::string ext = filename.substr(dot, filename.size()-1); + return ext; } /*! - Get the last frame index (update the lastFrame attribute). +Get the last frame index (update the lastFrame attribute). */ void -vpVideoReader::findLastFrameIndex() + vpVideoReader::findLastFrameIndex() { - if (!isOpen) + if (!isOpen) + { + vpERROR_TRACE("Use the open method before"); + throw (vpException(vpException::notInitialized,"file not yet opened")); + } + + if (imSequence != NULL) + { + if (! lastFrameIndexIsSet) { + char name[FILENAME_MAX]; + int image_number = firstFrame; + bool failed; + do + { + std::fstream file; + sprintf(name,fileName,image_number) ; + file.open(name, std::ios::in); + failed = file.fail(); + if (!failed) { + file.close(); + image_number++; + } + }while(!failed); + + lastFrame = image_number; + } + } + +#ifdef VISP_HAVE_FFMPEG + else if (ffmpeg != NULL) { + if (! lastFrameIndexIsSet) { + lastFrame = (long)(ffmpeg->getFrameNumber()); + } + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + else if (! lastFrameIndexIsSet) { - vpERROR_TRACE("Use the open method before"); - throw (vpException(vpException::notInitialized,"file not yet opened")); + lastFrame = (long) capture.get(cv::CAP_PROP_FRAME_COUNT); + if(lastFrame <= 2) // with tutorial/matching/video-postcard.mpeg it return 2 with OpenCV 3.0.0 + { + //std::cout << "Warning: Problem with cv::CAP_PROP_FRAME_COUNT. We set video last frame to an arbitrary value (1000)." << std::endl; + lastFrame = 100000; // Set lastFrame to an arbitrary value + } + lastFrame--; //Last frame index = total frame count - 1 } - - if (imSequence != NULL) +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + else if (! lastFrameIndexIsSet) { - char name[FILENAME_MAX]; - int image_number = firstFrame; - std::fstream file; - bool failed; - do + lastFrame = (long) capture.get(CV_CAP_PROP_FRAME_COUNT); + if(lastFrame <= 2) // with tutorial/matching/video-postcard.mpeg it return 2 with OpenCV 2.4.10 { - sprintf(name,fileName,image_number) ; - file.open(name, std::fstream::in); - failed = file.fail(); - if (!failed) file.close(); - image_number++; - }while(!failed); - - lastFrame = image_number - 2; + //std::cout << "Warning: Problem with CV_CAP_PROP_FRAME_COUNT. We set video last frame to an arbitrary value (1000)." << std::endl; + lastFrame = 100000; // Set lastFrame to an arbitrary value + } + lastFrame--; //Last frame index = total frame count - 1 } - - #ifdef VISP_HAVE_FFMPEG - else if (ffmpeg != NULL) - lastFrame = (long)(ffmpeg->getFrameNumber() - 1); - #endif +#endif } /*! - Get the first frame index (update the firstFrame attribute). +Get the first frame index (update the firstFrame attribute). */ void -vpVideoReader::findFirstFrameIndex() + vpVideoReader::findFirstFrameIndex() { - if (imSequence != NULL) - { - if (! firstFrameIndexIsSet) { - char name[FILENAME_MAX]; - int image_number = 0; - std::fstream file; - bool failed; - do { - sprintf(name, fileName, image_number) ; - file.open(name, std::fstream::in); - failed = file.fail(); - if (!failed) file.close(); - image_number++; - } while(failed); - - firstFrame = image_number - 1; - imSequence->setImageNumber(firstFrame); - } - } - - #ifdef VISP_HAVE_FFMPEG - else if (ffmpeg != NULL) { - if (! firstFrameIndexIsSet) { - firstFrame = (long)(0); - } - } - #endif + if (imSequence != NULL) + { + if (! firstFrameIndexIsSet) { + char name[FILENAME_MAX]; + int image_number = 0; + bool failed; + do { + std::fstream file; + sprintf(name, fileName, image_number) ; + file.open(name, std::ios::in); + failed = file.fail(); + if (!failed) file.close(); + image_number++; + } while(failed); + + firstFrame = image_number - 1; + imSequence->setImageNumber(firstFrame); + } + } +#ifdef VISP_HAVE_FFMPEG + else if (ffmpeg != NULL) { + if (! firstFrameIndexIsSet) { + firstFrame = (long)(0); + } + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + else if (! firstFrameIndexIsSet) + { + firstFrame = (long) (0); + } +#endif } /*! - Return the framerate in Hz used to encode the video stream. +Return the framerate in Hz used to encode the video stream. - If the video is a sequence of images, return -1. - */ -double vpVideoReader::getFramerate() const +If the video is a sequence of images, return -1. +*/ +double vpVideoReader::getFramerate() { - double framerate = -1.; + double framerate = -1.; #ifdef VISP_HAVE_FFMPEG - if (ffmpeg != NULL) - framerate = ffmpeg->getFramerate(); + if (ffmpeg != NULL) + { + framerate = ffmpeg->getFramerate(); + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + framerate = capture.get(cv::CAP_PROP_FPS); + // if(framerate == 0) + if(std::fabs(framerate) <= std::numeric_limits<double>::epsilon()) + { + vpERROR_TRACE("Problem with cv::CAP_PROP_FPS") ; + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + framerate = capture.get(CV_CAP_PROP_FPS); + // if(framerate == 0) + if(std::fabs(framerate) <= std::numeric_limits<double>::epsilon()) + { + vpERROR_TRACE("Problem with CV_CAP_PROP_FPS") ; + } #endif - return framerate; + return framerate; +} + +/*! +Return true if the image file extension is supported, false otherwise. +*/ +bool vpVideoReader::isImageExtensionSupported() +{ + return (formatType == FORMAT_PGM || + formatType == FORMAT_PPM || + formatType == FORMAT_JPEG || + formatType == FORMAT_PNG || + formatType == FORMAT_TIFF || + formatType == FORMAT_BMP || + formatType == FORMAT_DIB || + formatType == FORMAT_PBM || + formatType == FORMAT_RASTER || + formatType == FORMAT_JPEG2000); +} + +/*! +Return true if the video file extension is supported, false otherwise. +*/ +bool vpVideoReader::isVideoExtensionSupported() +{ + return (formatType == FORMAT_AVI || + formatType == FORMAT_MPEG || + formatType == FORMAT_MPEG4 || + formatType == FORMAT_MOV || + formatType == FORMAT_OGV || + formatType == FORMAT_WMV || + formatType == FORMAT_FLV || + formatType == FORMAT_MKV); } diff --git a/src/video/vpVideoReader.h b/src/video/vpVideoReader.h index 89790d9e66acd189c58265a7d1a1775b9b55de17..7bd511da7a072dfdaceebf8e3ce64d4ccf10ac5b 100644 --- a/src/video/vpVideoReader.h +++ b/src/video/vpVideoReader.h @@ -3,7 +3,7 @@ * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,6 +53,12 @@ #include <visp/vpDiskGrabber.h> #include <visp/vpFFMPEG.h> +#if VISP_HAVE_OPENCV_VERSION >= 0x020200 +#include "opencv2/highgui/highgui.hpp" +#elif VISP_HAVE_OPENCV_VERSION >= 0x020000 +#include "opencv/highgui.h" +#endif + /*! \class vpVideoReader @@ -62,9 +68,9 @@ images. As it inherits from the vpFrameGrabber Class, it can be used like an other frame grabber class. - The following example available in tutorial-grabber-video.cpp shows how this + The following example available in tutorial-video-reader.cpp shows how this class is really easy to use. It enables to read a video file named video.mpeg. - \include tutorial-grabber-video.cpp + \include tutorial-video-reader.cpp As shown in the next example, this class allows also to access to a specific frame. But be careful, for video files, the getFrame() method is not precise @@ -101,7 +107,10 @@ int main() The other following example explains how to use the class to read a sequence of images. The images are stored in the folder "./image" and are named "image0000.jpeg", "image0001.jpeg", "image0002.jpeg", ... As explained - in setFirstFrameIndex() it is also possible to set the first and last image numbers. + in setFirstFrameIndex() and setLastFrameIndex() it is also possible to set the + first and last image numbers to read a portion of the sequence. If these two + functions are not used, first and last image numbers are set automatically to + match the first and image images of the sequence. \code #include <visp/vpImage.h> @@ -116,6 +125,8 @@ int main() // Initialize the reader. reader.setFileName("./image/image%04d.jpeg"); + reader.setFirstFrameIndex(10); + reader.setLastFrameIndex(20); reader.open(I); while (! reader.end() ) @@ -157,6 +168,10 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber #ifdef VISP_HAVE_FFMPEG //!To read video files vpFFMPEG *ffmpeg; +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + //!To read video files with OpenCV + cv::VideoCapture capture; + cv::Mat frame; #endif //!Types of available formats typedef enum @@ -175,8 +190,12 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber // Video format FORMAT_AVI, FORMAT_MPEG, + FORMAT_MPEG4, FORMAT_MOV, FORMAT_OGV, + FORMAT_WMV, + FORMAT_FLV, + FORMAT_MKV, FORMAT_UNKNOWN } vpVideoFormatType; @@ -190,7 +209,7 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber //!Indicates if the video is "open". bool isOpen; //!Count the frame number when the class is used as a grabber. - long frameCount; + long frameCount; // Index of the next image //!The first frame index long firstFrame; //!The last frame index @@ -216,10 +235,10 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber } bool getFrame(vpImage<vpRGBa> &I, long frame); bool getFrame(vpImage<unsigned char> &I, long frame); - double getFramerate() const; + double getFramerate(); /*! - Get the current frame index. This index is updated at each call of the + Get the frame index of the next image. This index is updated at each call of the acquire method. It can be used to detect the end of a file (comparison with getLastFrameIndex()). @@ -227,6 +246,12 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber */ inline long getFrameIndex() const { return frameCount;} + /*! + Gets the first frame index. + + \return Returns the first frame index. + */ + inline long getFirstFrameIndex() const {return firstFrame;} /*! Gets the last frame index. @@ -250,24 +275,24 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber Enables to set the first frame index if you want to use the class like a grabber (ie with the acquire method). - \param firstFrame : The first frame index. + \param first_frame : The first frame index. \sa setLastFrameIndex() */ - inline void setFirstFrameIndex(const long firstFrame) { + inline void setFirstFrameIndex(const long first_frame) { this->firstFrameIndexIsSet = true; - this->firstFrame = firstFrame; + this->firstFrame = first_frame; } /*! Enables to set the last frame index. - \param lastFrame : The last frame index. + \param last_frame : The last frame index. \sa setFirstFrameIndex() */ - inline void setLastFrameIndex(const long lastFrame) { + inline void setLastFrameIndex(const long last_frame) { this->lastFrameIndexIsSet = true; - this->lastFrame = lastFrame; + this->lastFrame = last_frame; } private: @@ -275,6 +300,8 @@ class VISP_EXPORT vpVideoReader : public vpFrameGrabber static std::string getExtension(const std::string &filename); void findFirstFrameIndex(); void findLastFrameIndex(); + bool isImageExtensionSupported(); + bool isVideoExtensionSupported(); }; #endif diff --git a/src/video/vpVideoWriter.cpp b/src/video/vpVideoWriter.cpp index 0e9755588560638e1630b10753bf1c4e45c9f5d7..82720a16c610923e90c3bcb6761468204f377268 100755 --- a/src/video/vpVideoWriter.cpp +++ b/src/video/vpVideoWriter.cpp @@ -3,7 +3,7 @@ * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,25 +48,51 @@ #include <visp/vpDebug.h> #include <visp/vpVideoWriter.h> +#if VISP_HAVE_OPENCV_VERSION >= 0x020200 +# include <opencv2/imgproc/imgproc.hpp> +#endif + + /*! Basic constructor. */ -vpVideoWriter::vpVideoWriter() +vpVideoWriter::vpVideoWriter() : +#ifdef VISP_HAVE_FFMPEG + ffmpeg(NULL), +# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 + codec(CODEC_ID_MPEG1VIDEO), +# else + codec(AV_CODEC_ID_MPEG1VIDEO), +# endif + bit_rate(500000), + framerate(25), +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + writer(), fourcc(0), framerate(0.), +#endif + formatType(FORMAT_UNKNOWN), initFileName(false), isOpen(false), frameCount(0), + firstFrame(0), width(0), height(0) { initFileName = false; firstFrame = 0; frameCount = 0; - - #ifdef VISP_HAVE_FFMPEG + isOpen = false; + width = height = 0; +#ifdef VISP_HAVE_FFMPEG ffmpeg = NULL; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 +# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 codec = CODEC_ID_MPEG1VIDEO; -#else +# else codec = AV_CODEC_ID_MPEG1VIDEO; -#endif +# endif bit_rate = 500000; framerate = 25; - #endif +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + framerate = 25.0; + fourcc = cv::VideoWriter::fourcc('P','I','M','1'); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + framerate = 25.0; + fourcc = CV_FOURCC('P','I','M','1'); // default is a MPEG-1 codec +#endif } @@ -91,15 +117,24 @@ vpVideoWriter::~vpVideoWriter() */ void vpVideoWriter::setFileName(const char *filename) { - if (filename == '\0') + if (!filename || *filename == '\0') { vpERROR_TRACE("filename empty ") ; throw (vpImageException(vpImageException::noFileNameError,"filename empty ")) ; } - + + if (strlen( filename ) >= FILENAME_MAX) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the file name")); + } + strcpy(this->fileName,filename); formatType = getFormat(fileName); + + if (formatType == FORMAT_UNKNOWN) { + throw(vpException(vpException::badValue, "Filename extension not supported")); + } initFileName = true; } @@ -137,27 +172,31 @@ void vpVideoWriter::open(vpImage< vpRGBa > &I) width = I.getWidth(); height = I.getHeight(); } - #ifdef VISP_HAVE_FFMPEG else if (formatType == FORMAT_AVI || formatType == FORMAT_MPEG || + formatType == FORMAT_MPEG4 || formatType == FORMAT_MOV) { +#ifdef VISP_HAVE_FFMPEG ffmpeg = new vpFFMPEG; ffmpeg->setFramerate(framerate); ffmpeg->setBitRate(bit_rate); - if(!ffmpeg->openEncoder(fileName, I.getWidth(), I.getHeight(), codec)) - throw (vpException(vpException::ioError ,"Could not open the video")); - } - - #else - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV) - { - vpERROR_TRACE("To write video files the FFmpeg library has to be installed"); - throw (vpException(vpException::fatalError ,"the FFmpeg library is required")); + if(!ffmpeg->openEncoder(fileName, I.getWidth(), I.getHeight(), codec)) { + throw (vpException(vpException::ioError ,"Could not open encode the video with ffmpeg")); + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + writer = cv::VideoWriter(fileName, fourcc, framerate, cv::Size((int)I.getWidth(), (int)I.getHeight())); + + if(!writer.isOpened()) + { + //vpERROR_TRACE("Could not open encode the video with opencv"); + throw (vpException(vpException::fatalError , "Could not open encode the video with opencv")); + } +#else + //vpERROR_TRACE("To encode video files ViSP should be build with ffmpeg or opencv 3rd party libraries."); + throw (vpException(vpException::fatalError ,"To encode video files ViSP should be build with ffmpeg or opencv 3rd >= 2.1.0 party libraries.")); +#endif } - #endif frameCount = firstFrame; @@ -186,27 +225,31 @@ void vpVideoWriter::open(vpImage< unsigned char > &I) width = I.getWidth(); height = I.getHeight(); } - #ifdef VISP_HAVE_FFMPEG else if (formatType == FORMAT_AVI || formatType == FORMAT_MPEG || + formatType == FORMAT_MPEG4 || formatType == FORMAT_MOV) { +#ifdef VISP_HAVE_FFMPEG ffmpeg = new vpFFMPEG; ffmpeg->setFramerate(framerate); ffmpeg->setBitRate(bit_rate); - if(!ffmpeg->openEncoder(fileName, I.getWidth(), I.getHeight(), codec)) - throw (vpException(vpException::ioError ,"Could not open the video")); - } - - #else - else if (formatType == FORMAT_AVI || - formatType == FORMAT_MPEG || - formatType == FORMAT_MOV) - { - vpERROR_TRACE("To write video files the FFmpeg library has to be installed"); - throw (vpException(vpException::fatalError ,"the FFmpeg library is required")); + if(!ffmpeg->openEncoder(fileName, I.getWidth(), I.getHeight(), codec)) { + throw (vpException(vpException::ioError ,"Could not encode the video with ffmpeg")); + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + writer = cv::VideoWriter(fileName, fourcc, framerate, cv::Size((int)I.getWidth(), (int)I.getHeight())); + + if(!writer.isOpened()) + { + vpERROR_TRACE("Could not encode the video with opencv"); + throw (vpException(vpException::ioError ,"Could not encode the video with opencv")); + } +#else + //vpERROR_TRACE("To encode video files ViSP should be build with ffmpeg or opencv 3rd party libraries."); + throw (vpException(vpException::fatalError ,"To encode video files ViSP should be build with ffmpeg or opencv 3rd >= 2.1.0 party libraries.")); +#endif } - #endif frameCount = firstFrame; @@ -241,13 +284,16 @@ void vpVideoWriter::saveFrame (vpImage< vpRGBa > &I) vpImageIo::write(I, name); } - - #ifdef VISP_HAVE_FFMPEG else { +#ifdef VISP_HAVE_FFMPEG ffmpeg->saveFrame(I); +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + cv::Mat matFrame; + vpImageConvert::convert(I, matFrame); + writer << matFrame; +#endif } - #endif frameCount++; } @@ -279,13 +325,22 @@ void vpVideoWriter::saveFrame (vpImage< unsigned char > &I) vpImageIo::write(I, name); } - - #ifdef VISP_HAVE_FFMPEG else { +#ifdef VISP_HAVE_FFMPEG ffmpeg->saveFrame(I); +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + cv::Mat matFrame, rgbMatFrame; + vpImageConvert::convert(I, matFrame); + cv::cvtColor(matFrame, rgbMatFrame, cv::COLOR_GRAY2BGR); + writer << rgbMatFrame; +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + cv::Mat matFrame, rgbMatFrame; + vpImageConvert::convert(I, matFrame); + cv::cvtColor(matFrame, rgbMatFrame, CV_GRAY2BGR); + writer << rgbMatFrame; +#endif } - #endif frameCount++; } @@ -354,6 +409,14 @@ vpVideoWriter::getFormat(const char *filename) return FORMAT_MPEG; else if (ext.compare(".mpg") == 0) return FORMAT_MPEG; + else if (ext.compare(".MPEG4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".mpeg4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".MP4") == 0) + return FORMAT_MPEG4; + else if (ext.compare(".mp4") == 0) + return FORMAT_MPEG4; else if (ext.compare(".MOV") == 0) return FORMAT_MOV; else if (ext.compare(".mov") == 0) diff --git a/src/video/vpVideoWriter.h b/src/video/vpVideoWriter.h index 6a198c63f402c80bb80766a8bd7c636075dafed7..bac0aa2de49dd2de29b0692e528b68f645847ae8 100755 --- a/src/video/vpVideoWriter.h +++ b/src/video/vpVideoWriter.h @@ -3,7 +3,7 @@ * $Id: vpImagePoint.h 2359 2009-11-24 15:09:25Z nmelchio $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,6 +52,13 @@ #include <visp/vpImageIo.h> #include <visp/vpFFMPEG.h> +#if VISP_HAVE_OPENCV_VERSION >= 0x020200 +# include <opencv2/highgui/highgui.hpp> +#elif VISP_HAVE_OPENCV_VERSION >= 0x020000 +# include <opencv/highgui.h> +#endif + + /*! \class vpVideoWriter @@ -59,7 +66,12 @@ \brief Class that enables to write easily a video file or a sequence of images. - The following example shows how this class is really easy to use. It enable to write an image sequence. The images are stored in the folder "./image" and are named "image0000.jpeg", "image0001.jpeg", "image0002.jpeg", ... + The following example available in tutorial-video-recorder.cpp shows how this + class can be used to record a video from a camera by default in an mpeg file. + \include tutorial-video-recorder.cpp + + The following example shows also how this class can be used to write an image sequence. + The images are stored in the folder "./image" and are named "image0000.jpeg", "image0001.jpeg", "image0002.jpeg", ... \code #include <visp/vpConfig.h> @@ -97,13 +109,14 @@ int main() { -#ifdef VISP_HAVE_FFMPEG vpImage<vpRGBa> I; vpVideoWriter writer; // Set up the framerate to 30Hz. Default is 25Hz. writer.setFramerate(30); + +#ifdef VISP_HAVE_FFMPEG // Set up the bit rate writer.setBitRate(1000000); // Set up the codec to use @@ -111,6 +124,9 @@ int main() writer.setCodec(CODEC_ID_MPEG2VIDEO); #else writer.setCodec(AV_CODEC_ID_MPEG2VIDEO); +#endif +#elif defined VISP_HAVE_OPENCV + writer.setCodec( CV_FOURCC('P','I','M','1') ); #endif writer.setFileName("./test.mpeg"); @@ -127,7 +143,6 @@ int main() writer.close(); return 0; -#endif } \endcode */ @@ -147,6 +162,10 @@ class VISP_EXPORT vpVideoWriter //!The bite rate unsigned int bit_rate; int framerate; +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + cv::VideoWriter writer; + int fourcc; + double framerate; #endif //!Types of available formats typedef enum @@ -157,6 +176,7 @@ class VISP_EXPORT vpVideoWriter FORMAT_PNG, FORMAT_AVI, FORMAT_MPEG, + FORMAT_MPEG4, FORMAT_MOV, FORMAT_UNKNOWN } vpVideoFormatType; @@ -212,16 +232,16 @@ class VISP_EXPORT vpVideoWriter /*! Sets the bit rate of the video when encoding. - \param bit_rate : the expected bit rate. + \param bitrate : the expected bit rate. By default the bit rate is set to 500 000. */ - inline void setBitRate(const unsigned int bit_rate) {this->bit_rate = bit_rate;} + inline void setBitRate(const unsigned int bitrate) {this->bit_rate = bitrate;} /*! Sets the codec used to encode the video. - \param codec : the expected codec. + \param codec_id : the expected codec. By default codec is set to AV_CODEC_ID_MPEG1VIDEO. But if installed, you can use one of the AVCodecID proposed by ffmpeg such as : AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_MPEG2VIDEO_XVMC, @@ -230,10 +250,12 @@ class VISP_EXPORT vpVideoWriter Of course to use the codec it must be installed on your computer. */ #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 - inline void setCodec(const CodecID codec) {this->codec = codec;} + inline void setCodec(const CodecID codec_id) {this->codec = codec_id;} #else - inline void setCodec(const AVCodecID codec) {this->codec = codec;} + inline void setCodec(const AVCodecID codec_id) {this->codec = codec_id;} #endif +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + inline void setCodec(const int fourcc_codec) {this->fourcc = fourcc_codec;} #endif void setFileName(const char *filename); @@ -241,19 +263,30 @@ class VISP_EXPORT vpVideoWriter /*! Enables to set the first frame index. - \param firstFrame : The first frame index. + \param first_frame : The first frame index. */ - inline void setFirstFrameIndex(const unsigned int firstFrame) {this->firstFrame = firstFrame;} + inline void setFirstFrameIndex(const unsigned int first_frame) {this->firstFrame = first_frame;} #ifdef VISP_HAVE_FFMPEG /*! Sets the framerate in Hz of the video when encoding. - \param framerate : the expected framerate. + \param frame_rate : the expected framerate. + + By default the framerate is set to 25Hz. + */ + inline void setFramerate(const int frame_rate) { + this->framerate = frame_rate; + } +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + /*! + Sets the framerate in Hz of the video when encoding. + + \param frame_rate : the expected framerate. By default the framerate is set to 25Hz. */ - inline void setFramerate(const int framerate) { - this->framerate = framerate; + inline void setFramerate(const double frame_rate) { + this->framerate = frame_rate; } #endif diff --git a/src/visual-feature/vpBasicFeature.cpp b/src/visual-feature/vpBasicFeature.cpp index 2b228bbe1b1842efc0cda97a01bfd702897d9230..4029b5fa606d89b6f59ed0c6df8899ce0b41c433 100644 --- a/src/visual-feature/vpBasicFeature.cpp +++ b/src/visual-feature/vpBasicFeature.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBasicFeature.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBasicFeature.cpp 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,20 +69,50 @@ const unsigned int vpBasicFeature::FEATURE_ALL = 0xffff; \file vpBasicFeature.cpp \brief Class that defines what is a visual feature. */ - +/*! + Default constructor. +*/ vpBasicFeature::vpBasicFeature() + : s(), dim_s(0), flags(NULL), nbParameters(0), deallocate(vpBasicFeature::user) +{ +} + +/*! + Destructor that free allocated memory. +*/ +vpBasicFeature::~vpBasicFeature() { -// featureLine[0] = 0x1 ; -// featureLine[1] = 0x2 ; -// featureLine[2] = 0x4 ; -// featureLine[3] = 0x8 ; -// featureLine[4] = 0x10 ; -// featureLine[5] = 0x20 ; -// featureLine[6] = 0x40 ; -// featureLine[7] = 0x80 ; - //vpTRACE("0x%x", this); - deallocate = vpBasicFeature::user ; + if (flags != NULL) { + delete [] flags; flags = NULL; + } +} + +/*! + Copy constructor. +*/ +vpBasicFeature::vpBasicFeature(const vpBasicFeature &f) + : s(), dim_s(0), flags(NULL), nbParameters(0), deallocate(vpBasicFeature::user) +{ + *this = f; +} + +/*! + Copy operator. +*/ +vpBasicFeature &vpBasicFeature::operator=(const vpBasicFeature &f) +{ + s = f.s; + dim_s = f.dim_s; + nbParameters = f.nbParameters; + deallocate = f.deallocate; + if (flags) + delete [] flags; + flags = new bool [nbParameters]; + for (unsigned int i = 0; i < nbParameters; i++) + flags[i] = f.flags[i]; + + return (*this); } //! Get the feature vector dimension. diff --git a/src/visual-feature/vpBasicFeature.h b/src/visual-feature/vpBasicFeature.h index 23258fd407c4036715d361b04f313043970b64d1..4f4d0550394b50f18ca00652c66ddbd6724ed82c 100644 --- a/src/visual-feature/vpBasicFeature.h +++ b/src/visual-feature/vpBasicFeature.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpBasicFeature.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpBasicFeature.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -106,7 +106,9 @@ public: virtual void init() = 0 ; vpBasicFeature() ; - virtual ~vpBasicFeature() { /*vpTRACE("0x%x", this)*/; } + vpBasicFeature(const vpBasicFeature &f) ; + vpBasicFeature &operator=(const vpBasicFeature &f) ; + virtual ~vpBasicFeature(); //! Return element \e i in the state vector (usage : x = s[i] ) virtual inline double operator[](const unsigned int i) const { return s[i]; } diff --git a/src/visual-feature/vpFeatureDepth.cpp b/src/visual-feature/vpFeatureDepth.cpp index 385f543dd6b74b4f7ba9476701566e5e12302034..e8bc72c90370d29b91f050a2e469b8175f51d0ca 100644 --- a/src/visual-feature/vpFeatureDepth.cpp +++ b/src/visual-feature/vpFeatureDepth.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureDepth.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureDepth.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -92,13 +92,15 @@ vpFeatureDepth::init() if (flags == NULL) flags = new bool[nbParameters]; for (unsigned int i = 0; i < nbParameters; i++) flags[i] = false; + x = y = 0.; + Z = 1.; } /*! Default constructor that build a visual feature. */ -vpFeatureDepth::vpFeatureDepth() : vpBasicFeature() +vpFeatureDepth::vpFeatureDepth() : x(0), y(0), Z(1.) { init() ; } @@ -131,12 +133,12 @@ vpFeatureDepth::get_LogZoverZstar() const /*! Set the value of \f$ x \f$ which represents the x coordinate of the point in the camera frame. - \param x : \f$ x \f$ value to set. + \param x_ : \f$ x \f$ value to set. */ void -vpFeatureDepth::set_x(const double x) +vpFeatureDepth::set_x(const double x_) { - this->x = x ; + this->x = x_ ; flags[0] = true; } @@ -156,12 +158,12 @@ vpFeatureDepth::get_x() const /*! Set the value of \f$ y \f$ which represents the y coordinate of the point in the camera frame. - \param y : \f$ y \f$ value to set. + \param y_ : \f$ y \f$ value to set. */ void -vpFeatureDepth::set_y(const double y) +vpFeatureDepth::set_y(const double y_) { - this->y = y ; + this->y = y_ ; flags[1] = true; } @@ -180,12 +182,12 @@ vpFeatureDepth::get_y() const /*! Set the value of \f$ Z \f$ which represents the depth in the 3D camera frame. - \param Z : \f$ Z \f$ value to set. + \param Z_ : \f$ Z \f$ value to set. */ void -vpFeatureDepth::set_Z(const double Z) +vpFeatureDepth::set_Z(const double Z_) { - this->Z = Z ; + this->Z = Z_ ; flags[2] = true; } @@ -205,20 +207,20 @@ vpFeatureDepth::get_Z() const /*! Set the value of \f$ x \f$, \f$ y \f$, \f$ Z \f$ and \f$ log(\frac{Z}{Z^*}) \f$. \f$ x \f$ and \f$ y \f$ represent the coordinates of the point in the camera frame. \f$ Z \f$ is the 3D coordinate representing the depth. \f$ log(\frac{Z}{Z^*}) \f$ represents the logarithm of the current depth relative to the desired depth. - \param x : \f$ x \f$ value to set. - \param y : \f$ y \f$ value to set. - \param Z : \f$ Z \f$ value to set. + \param x_ : \f$ x \f$ value to set. + \param y_ : \f$ y \f$ value to set. + \param Z_ : \f$ Z \f$ value to set. \param LogZoverZstar : \f$ log(\frac{Z}{Z^*}) \f$ value to set. */ void -vpFeatureDepth::set_xyZLogZoverZstar(const double x, - const double y, - const double Z, +vpFeatureDepth::set_xyZLogZoverZstar(const double x_, + const double y_, + const double Z_, const double LogZoverZstar) { - set_x(x) ; - set_y(y) ; - set_Z(Z) ; + set_x(x_) ; + set_y(y_) ; + set_Z(Z_) ; set_LogZoverZstar(LogZoverZstar) ; for( unsigned int i = 0; i < nbParameters; i++) flags[i] = true; } @@ -276,23 +278,23 @@ vpFeatureDepth::interaction(const unsigned int select) L.resize(1,6) ; - double x = get_x(); - double y = get_y(); - double Z = get_Z(); + double x_ = get_x(); + double y_ = get_y(); + double Z_ = get_Z(); - if (Z < 0) + if (Z_ < 0) { vpERROR_TRACE("Point is behind the camera ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point is behind the camera ")) ; } - if (fabs(Z) < 1e-6) + if (fabs(Z_) < 1e-6) { vpERROR_TRACE("Point Z coordinates is null ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point Z coordinates is null")) ; @@ -303,9 +305,9 @@ vpFeatureDepth::interaction(const unsigned int select) L = 0; L[0][0] = 0; L[0][1] = 0; - L[0][2] = -1/Z; - L[0][3] = -y; - L[0][4] = x; + L[0][2] = -1/Z_; + L[0][3] = -y_; + L[0][4] = x_; L[0][5] = 0; } @@ -403,20 +405,20 @@ vpFeatureDepth::print(const unsigned int select ) const /*! Build a 3D depth visual feature from the point coordinates \f$ x \f$ and \f$ y \f$ given in the camera frame, \f$ Z \f$ which describes the depth and \f$ log(\frac{Z}{Z^*}) \f$ which represents the logarithm of the current depth relative to the desired depth. - \param x : The \f$ x \f$ parameter. - \param y : The \f$ y \f$ parameter. - \param Z : The \f$ Z \f$ parameter. + \param x_ : The \f$ x \f$ parameter. + \param y_ : The \f$ y \f$ parameter. + \param Z_ : The \f$ Z \f$ parameter. \param LogZoverZstar : The \f$ log(\frac{Z}{Z^*}) \f$ parameter. */ void -vpFeatureDepth::buildFrom(const double x, const double y, const double Z, const double LogZoverZstar) +vpFeatureDepth::buildFrom(const double x_, const double y_, const double Z_, const double LogZoverZstar) { s[0] = LogZoverZstar; - this->x = x ; - this->y = y ; - this->Z = Z ; + this->x = x_ ; + this->y = y_ ; + this->Z = Z_ ; if (Z < 0) { diff --git a/src/visual-feature/vpFeatureDepth.h b/src/visual-feature/vpFeatureDepth.h index d6cadd223d3753c36ba53490bc7575f3afee7e66..7528ea08dd38423af204b660d5d346233bd14619 100644 --- a/src/visual-feature/vpFeatureDepth.h +++ b/src/visual-feature/vpFeatureDepth.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureDepth.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureDepth.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -175,13 +175,10 @@ private: double Z; public: - void init() ; - vpFeatureDepth() ; - //! destructor - virtual ~vpFeatureDepth() { if (flags != NULL) delete [] flags; } - + //! Destructor. + virtual ~vpFeatureDepth() {} /* section Set coordinates diff --git a/src/visual-feature/vpFeatureDisplay.cpp b/src/visual-feature/vpFeatureDisplay.cpp index e97aa9abf2a78d5c1d4ccd2816198b5e6c3f22ef..83fdd19801fe58d43a9b36d86528c51f0c3caf0f 100644 --- a/src/visual-feature/vpFeatureDisplay.cpp +++ b/src/visual-feature/vpFeatureDisplay.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureDisplay.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureDisplay.cpp 4773 2014-07-10 17:09:23Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -165,11 +165,16 @@ void vpFeatureDisplay::displayCylinder(double rho1,double theta1, } } /*! - \param x, y, mu20, mu11, mu02 : Ellipse parameters. + \param x, y, mu20, mu11, mu02 : Ellipse parameters where: + - \f$(x,y)\f$ are the normalized coordinates of the ellipse center, respectively + along the horizontal and vertical axis in the image. + - \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ are the centered moments. \param cam : Camera intrinsic parameters. \param I : Image. \param color : Color to use to display the feature \param thickness : Thickness of the feature representation. + + \sa vpDisplay::displayEllipse() */ void vpFeatureDisplay::displayEllipse(double x,double y, double mu20, double mu11, double mu02, @@ -178,64 +183,62 @@ void vpFeatureDisplay::displayEllipse(double x,double y, const vpColor &color, unsigned int thickness) { + try { + unsigned int number_of_points = 45 ; + const double incr = 2 * M_PI/(double)number_of_points ; // angle increment + unsigned int i = 0 ; + + double s = sqrt(vpMath::sqr(mu20-mu02)+4*mu11*mu11) ; + double a, b, e ; + + //if (fabs(mu11)<1e-6) e =0 ; + if (fabs(mu11) < std::numeric_limits<double>::epsilon()) { + e = 0 ; + a = sqrt(mu20); + b = sqrt(mu02); + } + else { + e = (mu02-mu20+s)/(2*mu11) ; + a = sqrt( (mu02+mu20+s)/2.0) ; + b = sqrt( (mu02+mu20-s)/2.0) ; + } + double e1 = atan(e) ; - try{ - { - unsigned int number_of_points = 45 ; - const double incr = 2 * M_PI/(double)number_of_points ; // angle increment - unsigned int i = 0 ; - - // std::cout << s.t() ; - double s = sqrt(vpMath::sqr(mu20-mu02)+4*mu11*mu11) ; - double e ; - - if (fabs(mu11)<1e-6) e =0 ; - else e = (mu02-mu20+s)/(2*mu11) ; - double a =sqrt( (mu02+mu20+s)/2.0) ; - double b =sqrt( (mu02+mu20-s)/2.0) ; - - // vpTRACE("%f %f %f", a,b,e) ; - - double e1 = atan(e) ; - - double k = 0.0 ; + double k = 0.0 ; - double ce = cos(e1) ; - double se = sin(e1) ; + double ce = cos(e1) ; + double se = sin(e1) ; - double x2 = 0; - double y2 =0; - vpImagePoint ip1, ip2; + double x2 = 0; + double y2 =0; + vpImagePoint ip1, ip2; - for( i = 0; i < number_of_points+2 ; i++) - { - double x1 = a *cos(k) ; // equation of an ellipse - double y1 = b *sin(k) ; // equation of an ellipse - double x11 = x + ce *x1 - se *y1 ; - double y11 = y + se *x1 + ce *y1 ; + for( i = 0; i < number_of_points+2 ; i++) + { + double x1 = a *cos(k) ; // equation of an ellipse + double y1 = b *sin(k) ; // equation of an ellipse + double x11 = x + ce *x1 - se *y1 ; + double y11 = y + se *x1 + ce *y1 ; - vpMeterPixelConversion::convertPoint(cam, x11, y11, ip1); + vpMeterPixelConversion::convertPoint(cam, x11, y11, ip1); - if (i > 1) { - ip2.set_u( x2 ); - ip2.set_v( y2 ); + if (i > 1) { + ip2.set_u( x2 ); + ip2.set_v( y2 ); - vpDisplay::displayLine(I, ip1, ip2, color, thickness) ; - } + vpDisplay::displayLine(I, ip1, ip2, color, thickness) ; + } - ip2 = ip1; - y2 = y1 ; - x2 = x1 ; - k += incr ; - } // end for loop - } - // vpDisplay::getClick(I) ; + ip2 = ip1; + y2 = y1 ; + x2 = x1 ; + k += incr ; + } // end for loop } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e); } } @@ -348,11 +351,16 @@ void vpFeatureDisplay::displayCylinder(double rho1, double theta1, } /*! - \param x, y, mu20, mu11, mu02 : Ellipse parameters. + \param x, y, mu20, mu11, mu02 : Ellipse parameters where: + - \f$(x,y)\f$ are the normalized coordinates of the ellipse center, respectively + along the horizontal and vertical axis in the image. + - \f$\mu_{20}, \mu_{11}, \mu_{02}\f$ are the centered moments. \param cam : Camera intrinsic parameters. \param I : Image. \param color : Color to use to display the feature \param thickness : Thickness of the feature representation. + + \sa vpDisplay::displayEllipse() */ void vpFeatureDisplay::displayEllipse(double x, double y, double mu20, double mu11, double mu02, @@ -361,67 +369,61 @@ void vpFeatureDisplay::displayEllipse(double x, double y, const vpColor &color, unsigned int thickness) { + try { + unsigned int number_of_points = 45 ; + const double incr = 2 * M_PI/(double)number_of_points ; // angle increment + unsigned int i = 0 ; + + double s = sqrt(vpMath::sqr(mu20-mu02)+4*mu11*mu11) ; + double a, b, e ; + + //if (fabs(mu11)<1e-6) e =0 ; + if (fabs(mu11) < std::numeric_limits<double>::epsilon()) { + e = 0 ; + a = sqrt(mu20); + b = sqrt(mu02); + } + else { + e = (mu02-mu20+s)/(2*mu11) ; + a = sqrt( (mu02+mu20+s)/2.0) ; + b = sqrt( (mu02+mu20-s)/2.0) ; + } + double e1 = atan(e) ; - try{ - { - unsigned int number_of_points = 45 ; - const double incr = 2 * M_PI/(double)number_of_points ; // angle increment - unsigned int i = 0 ; - - // std::cout << s.t() ; - double s = sqrt(vpMath::sqr(mu20-mu02)+4*mu11*mu11) ; - double e ; - - if (fabs(mu11)<1e-6) e =0 ; - else e = (mu02-mu20+s)/(2*mu11) ; - double a =sqrt( (mu02+mu20+s)/2.0) ; - double b =sqrt( (mu02+mu20-s)/2.0) ; - - // vpTRACE("%f %f %f", a,b,e) ; - - double e1 = atan(e) ; + double k = 0.0 ; - double k = 0.0 ; + double ce = cos(e1) ; + double se = sin(e1) ; - double ce = cos(e1) ; - double se = sin(e1) ; - double x2 = 0; - double y2 =0; - vpImagePoint ip1, ip2; + double x2 = 0; + double y2 =0; + vpImagePoint ip1, ip2; - for( i = 0; i < number_of_points+2 ; i++) - { - double x1 = a *cos(k) ; // equation of an ellipse - double y1 = b *sin(k) ; // equation of an ellipse - double x11 = x + ce *x1 - se *y1 ; - double y11 = y + se *x1 + ce *y1 ; + for( i = 0; i < number_of_points+2 ; i++) + { + double x1 = a *cos(k) ; // equation of an ellipse + double y1 = b *sin(k) ; // equation of an ellipse + double x11 = x + ce *x1 - se *y1 ; + double y11 = y + se *x1 + ce *y1 ; - vpMeterPixelConversion::convertPoint(cam, x11, y11, ip1); + vpMeterPixelConversion::convertPoint(cam, x11, y11, ip1); - if (i > 1) { - ip2.set_u( x2 ); - ip2.set_v( y2 ); + if (i > 1) { + ip2.set_u( x2 ); + ip2.set_v( y2 ); - vpDisplay::displayLine(I, ip1, ip2, color, thickness) ; - } + vpDisplay::displayLine(I, ip1, ip2, color, thickness) ; + } - ip2 = ip1; - k += incr ; - } // end for loop - } - // vpDisplay::getClick(I) ; + ip2 = ip1; + y2 = y1 ; + x2 = x1 ; + k += incr ; + } // end for loop } - catch(...) + catch(vpException &e) { - vpERROR_TRACE("Error caught") ; - throw ; + throw(e); } } - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/src/visual-feature/vpFeatureDisplay.h b/src/visual-feature/vpFeatureDisplay.h index bb39b212839be1df446390e68e90c3f0bc9f72f1..9547c4df945b568a24746a5e1644cd6485c8a371 100644 --- a/src/visual-feature/vpFeatureDisplay.h +++ b/src/visual-feature/vpFeatureDisplay.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureDisplay.h 4062 2013-01-09 10:30:06Z fspindle $ + * $Id: vpFeatureDisplay.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/visual-feature/vpFeatureEllipse.cpp b/src/visual-feature/vpFeatureEllipse.cpp index ff30ae64f8e031016ef51f6704c144b4a635501d..8c864e16ef8721e8b5a261115bc47ad6542c8670 100644 --- a/src/visual-feature/vpFeatureEllipse.cpp +++ b/src/visual-feature/vpFeatureEllipse.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureEllipse.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureEllipse.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -97,7 +97,7 @@ vpFeatureEllipse::init() } -vpFeatureEllipse::vpFeatureEllipse() : vpBasicFeature() +vpFeatureEllipse::vpFeatureEllipse() : A(0), B(0), C(0) { init() ; } @@ -343,7 +343,7 @@ void vpFeatureEllipse::buildFrom(const double x, const double y, const double mu20, const double mu11, const double mu02, - const double A, const double B, const double C) + const double a, const double b, const double c) { s[0] = x ; @@ -352,9 +352,9 @@ vpFeatureEllipse::buildFrom(const double x, const double y, s[3] = mu11 ; s[4] = mu02 ; - this->A = A ; - this->B = B ; - this->C = C ; + this->A = a ; + this->B = b ; + this->C = c ; for( unsigned int i = 0; i < nbParameters; i++) flags[i] = true; } @@ -382,11 +382,11 @@ vpFeatureEllipse::set_xy(const double x,const double y) } void -vpFeatureEllipse::setABC(const double A, const double B, const double C) +vpFeatureEllipse::setABC(const double a, const double b, const double c) { - this->A = A ; - this->B = B ; - this->C = C ; + this->A = a ; + this->B = b ; + this->C = c ; for( unsigned int i = 5; i < nbParameters; i++) flags[i] = true; } diff --git a/src/visual-feature/vpFeatureEllipse.h b/src/visual-feature/vpFeatureEllipse.h index dd99810a95bb8962e419a117252dfc2b283143ec..aea4547494e4a9f858ac7d526c5aee55711fcd85 100644 --- a/src/visual-feature/vpFeatureEllipse.h +++ b/src/visual-feature/vpFeatureEllipse.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureEllipse.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureEllipse.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,12 +74,12 @@ private: public: - //! basic construction + //! Default initialization. void init() ; - //! basic constructor + //! Default constructor. vpFeatureEllipse() ; - //! destructor - virtual ~vpFeatureEllipse() { if (flags != NULL) delete [] flags; } + //! Destructor. + virtual ~vpFeatureEllipse() { } public: /*! diff --git a/src/visual-feature/vpFeatureException.h b/src/visual-feature/vpFeatureException.h index 61cfcc6e981c7f8d73625ce688da5b8bdab407e3..f89311d14eaf446ac301243e2af301f1063b0ff0 100644 --- a/src/visual-feature/vpFeatureException.h +++ b/src/visual-feature/vpFeatureException.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureException.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureException.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -70,12 +70,12 @@ */ class VISP_EXPORT vpFeatureException : public vpException { -public: - /*! + public: + /*! \brief Lists the possible error than can be emmited while calling vpFeature member */ - enum errorFeatureCodeEnum + enum errorFeatureCodeEnum { //! feature list or desired feature list is empty badErrorVectorError, @@ -84,25 +84,20 @@ public: badInitializationError } ; -public: - vpFeatureException (const int code, const char * msg) - : vpException(code, msg){ ; } - vpFeatureException (const int code, const std::string & msg) - : vpException(code, msg){ ; } - vpFeatureException (const int code) - : vpException(code){ ; } + public: + vpFeatureException (const int id, const char* format, ...) + { + this->code = id; + va_list args; + va_start(args, format); + setMessage(format, args); + va_end (args); + } + vpFeatureException (const int id, const std::string & msg) + : vpException(id, msg){ ; } + vpFeatureException (const int id) + : vpException(id){ ; } }; - - - - -#endif /* #ifndef __vpFeatureException_H */ - - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ +#endif diff --git a/src/visual-feature/vpFeatureLine.cpp b/src/visual-feature/vpFeatureLine.cpp index b04c8525411b5a119192d70b89094a7dc43b1273..77d348d7276784f1ec4b3e6f9a882743948edcb8 100644 --- a/src/visual-feature/vpFeatureLine.cpp +++ b/src/visual-feature/vpFeatureLine.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureLine.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureLine.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -113,7 +113,7 @@ vpFeatureLine::init() /*! Default constructor that build a visual feature. */ -vpFeatureLine::vpFeatureLine() : vpBasicFeature() +vpFeatureLine::vpFeatureLine() : A(0), B(0), C(0), D(0) { init() ; } @@ -139,19 +139,19 @@ vpFeatureLine::setRhoTheta(const double rho, const double theta) \f[ AX + BY + CZ + D = 0 \f] Those parameters are needed to compute the interaction matrix associated to a visual feature. Normally, two plans are needed to describe a line (the intersection of those two plans). But to compute the interaction matrix only one plan equation is required. The only one restrictions is that the value of D must not be equal to zero ! - \param A : A value to set. - \param B : B value to set. - \param C : C value to set. - \param D : D value to set. + \param A_ : A value to set. + \param B_ : B value to set. + \param C_ : C value to set. + \param D_ : D value to set. */ void -vpFeatureLine::setABCD(const double A, const double B, - const double C, const double D) +vpFeatureLine::setABCD(const double A_, const double B_, + const double C_, const double D_) { - this->A = A ; - this->B = B ; - this->C = C ; - this->D = D ; + this->A = A_ ; + this->B = B_ ; + this->C = C_ ; + this->D = D_ ; for(unsigned int i = 2; i < nbParameters; i++) flags[i] = true; } @@ -457,22 +457,22 @@ vpFeatureLine::buildFrom(const double rho, const double theta) \param rho : The \f$ \rho \f$ parameter. \param theta : The \f$ \theta \f$ parameter. - \param A : A parameter of the plan equation. - \param B : B parameter of the plan equation. - \param C : C parameter of the plan equation. - \param D : D parameter of the plan equation. + \param A_ : A parameter of the plan equation. + \param B_ : B parameter of the plan equation. + \param C_ : C parameter of the plan equation. + \param D_ : D parameter of the plan equation. */ void vpFeatureLine::buildFrom(const double rho, const double theta, - const double A, const double B, - const double C, const double D) + const double A_, const double B_, + const double C_, const double D_) { s[0] = rho ; s[1] = theta ; - this->A = A ; - this->B = B ; - this->C = C ; - this->D = D ; + this->A = A_ ; + this->B = B_ ; + this->C = C_ ; + this->D = D_ ; for(unsigned int i = 0; i < nbParameters; i++) flags[i] = true; } diff --git a/src/visual-feature/vpFeatureLine.h b/src/visual-feature/vpFeatureLine.h index 0d946351fcc3abed4a0f22a0e2f0effff6d4dbc2..5bb47d27451d897bceed4ac676ae15b8174dcc2b 100644 --- a/src/visual-feature/vpFeatureLine.h +++ b/src/visual-feature/vpFeatureLine.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureLine.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureLine.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -210,10 +210,10 @@ private: double A,B,C,D ; public: - void init() ; vpFeatureLine() ; - virtual ~vpFeatureLine() { if (flags != NULL) delete [] flags; } + //! Destructor. + virtual ~vpFeatureLine() {} public: diff --git a/src/visual-feature/vpFeatureLuminance.cpp b/src/visual-feature/vpFeatureLuminance.cpp index 63a7d86a39ede789fd6b3b7d48a5c0d5e1c4e13f..b40dbe57590acc55422f56b2854cc6dd218e8dd0 100755 --- a/src/visual-feature/vpFeatureLuminance.cpp +++ b/src/visual-feature/vpFeatureLuminance.cpp @@ -15,11 +15,7 @@ \file vpFeatureLuminance.cpp \brief Class that defines the image luminance visual feature - for more details see - C. Collewet, E. Marchand, F. Chaumette. Visual - servoing set free from image processing. In IEEE Int. Conf. on - Robotics and Automation, ICRA'08, Pages 81-86, Pasadena, Californie, - Mai 2008. + For more details see \cite Collewet08c. */ @@ -39,6 +35,7 @@ vpFeatureLuminance::init() firstTimeIn =0 ; + nbr = nbc = 0; } @@ -70,37 +67,61 @@ vpFeatureLuminance::init(unsigned int _nbr, unsigned int _nbc, double _Z) /*! Default constructor that build a visual feature. */ -vpFeatureLuminance::vpFeatureLuminance() : vpBasicFeature() +vpFeatureLuminance::vpFeatureLuminance() + : Z(1), nbr(0), nbc(0), bord(10), pixInfo(NULL), firstTimeIn(0), cam() { nbParameters = 1; dim_s = 0 ; - bord = 10 ; flags = NULL; - pixInfo = NULL; init() ; } +/*! + Copy constructor. + */ +vpFeatureLuminance::vpFeatureLuminance(const vpFeatureLuminance& f) + : vpBasicFeature(f), Z(1), nbr(0), nbc(0), bord(10), pixInfo(NULL), firstTimeIn(0), cam() +{ + *this = f; +} + +/*! + Copy operator. + */ +vpFeatureLuminance &vpFeatureLuminance::operator=(const vpFeatureLuminance& f) +{ + Z = f.Z; + nbr = f.nbr; + nbc = f.nbc; + bord = f.bord; + firstTimeIn = f.firstTimeIn; + cam = f.cam; + if (pixInfo) + delete [] pixInfo; + pixInfo = new vpLuminance[dim_s] ; + for(unsigned int i=0; i< dim_s; i++) + pixInfo[i] = f.pixInfo[i]; + return (*this); +} + /*! - Default destructor. + Destructor that free allocated memory. */ vpFeatureLuminance::~vpFeatureLuminance() { if (pixInfo != NULL) delete [] pixInfo ; - if (flags != NULL) delete [] flags; } - - /*! Set the value of \f$ Z \f$ which represents the depth in the 3D camera frame. - \param Z : \f$ Z \f$ value to set. + \param Z_ : \f$ Z \f$ value to set. */ void -vpFeatureLuminance::set_Z(const double Z) +vpFeatureLuminance::set_Z(const double Z_) { - this->Z = Z ; + this->Z = Z_ ; flags[0] = true; } diff --git a/src/visual-feature/vpFeatureLuminance.h b/src/visual-feature/vpFeatureLuminance.h index fe68cc76ecce434d7ba1355a93f5a223aa6395c0..e5958ab1192b5c91fbd3359151fdcab50a990367 100755 --- a/src/visual-feature/vpFeatureLuminance.h +++ b/src/visual-feature/vpFeatureLuminance.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureLuminance.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureLuminance.h 5098 2014-12-20 16:32:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,11 +51,7 @@ \file vpFeatureLuminance.h \brief Class that defines the image luminance visual feature - for more details see - C. Collewet, E. Marchand, F. Chaumette. Visual - servoing set free from image processing. In IEEE Int. Conf. on - Robotics and Automation, ICRA'08, Pages 81-86, Pasadena, Californie, - Mai 2008. + For more details see \cite Collewet08c. */ @@ -82,11 +78,7 @@ class VISP_EXPORT vpLuminance \class vpFeatureLuminance \brief Class that defines the image luminance visual feature - For more details see - C. Collewet, E. Marchand, F. Chaumette. Visual - servoing set free from image processing. In IEEE Int. Conf. on - Robotics and Automation, ICRA'08, Pages 81-86, Pasadena, Californie, - Mai 2008. + For more details see \cite Collewet08c. */ class VISP_EXPORT vpFeatureLuminance : public vpBasicFeature @@ -116,7 +108,9 @@ public: void init(unsigned int _nbr, unsigned int _nbc, double _Z) ; vpFeatureLuminance() ; - + vpFeatureLuminance(const vpFeatureLuminance& f) ; + vpFeatureLuminance &operator=(const vpFeatureLuminance& f) ; + //! Destructor. virtual ~vpFeatureLuminance() ; diff --git a/src/visual-feature/vpFeatureMoment.cpp b/src/visual-feature/vpFeatureMoment.cpp index ae3cd40b102377a7dfa7cf5d687db490a5010cb9..9b92dee27f2949687a6440f0cc55fe2060d13972 100644 --- a/src/visual-feature/vpFeatureMoment.cpp +++ b/src/visual-feature/vpFeatureMoment.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureMoment.cpp 4303 2013-07-04 14:14:00Z fspindle $ + * $Id: vpFeatureMoment.cpp 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -142,20 +142,19 @@ void vpFeatureMoment::display (const vpCameraParameters &cam, const vpImage< vpR \attention The behaviour of this method is not the same as vpMoment::update which only acknowledges the new object. This method also computes the interaction matrices. - \param A : A coefficient of the plane. - \param B : B coefficient of the plane. - \param C : C coefficient of the plane. + \param A_ : A coefficient of the plane. + \param B_ : B coefficient of the plane. + \param C_ : C coefficient of the plane. */ -void vpFeatureMoment::update (double A, double B, double C){ - this->A = A; - this->B = B; - this->C = C; +void vpFeatureMoment::update (double A_, double B_, double C_){ + this->A = A_; + this->B = B_; + this->C = C_; if(moment==NULL){ bool found; this->moment = &(moments.get(momentName(),found)); - if(!found) throw ("Moment not found for feature"); - + if(!found) throw vpException(vpException::notInitialized,"Moment not found for feature"); } nbParameters = 1; if(this->moment!=NULL){ @@ -235,10 +234,15 @@ vpBasicFeature* vpFeatureMoment::duplicate () const */ void vpFeatureMoment::linkTo(vpFeatureMomentDatabase& featureMoments){ - std::strcpy(_name,name()); - this->featureMomentsDataBase=&featureMoments; + if (strlen( name() ) >= 255) { + throw(vpException(vpException::memoryAllocationError, + "Not enough memory to intialize the moment name")); + } + + std::strcpy(_name,name()); + this->featureMomentsDataBase=&featureMoments; - featureMoments.add(*this,_name); + featureMoments.add(*this,_name); } @@ -247,5 +251,26 @@ void vpFeatureMoment::compute_interaction (){ } vpFeatureMoment::~vpFeatureMoment (){ - delete[] flags; +} + +VISP_EXPORT std::ostream& operator<<(std::ostream & os, const vpFeatureMoment& featM) { + /* + A const_cast is forced here since interaction() defined in vpBasicFeature() is not const + But introducing const in vpBasicFeature() can break a lot of client code + */ + vpMatrix Lcomplete((unsigned int)featM.getDimension(), 6); // 6 corresponds to 6velocities in standard interaction matrix + Lcomplete = const_cast<vpFeatureMoment&>(featM).interaction(vpBasicFeature::FEATURE_ALL); + Lcomplete.matlabPrint(os); + return os; +} + +/*! +Interface function to display the moments and other interaction matrices +on which a particular vpFeatureMoment is dependent upon +Not made pure to maintain compatibility +Recommended : Types inheriting from vpFeatureMoment should implement this function +*/ +void +vpFeatureMoment::printDependencies(std::ostream& os) const{ + os << " WARNING : Falling back to base class version of printDependencies() in vpFeatureMoment. To prevent that, this has to be implemented in the derived classes!" << std::endl; } diff --git a/src/visual-feature/vpFeatureMoment.h b/src/visual-feature/vpFeatureMoment.h index 85ec24427772af1e7ac1ce622f1500554f24da96..fcf9bbef7f1859e23aaa637abbd61533f01b66ad 100644 --- a/src/visual-feature/vpFeatureMoment.h +++ b/src/visual-feature/vpFeatureMoment.h @@ -1,9 +1,9 @@ /**************************************************************************** * -* $Id: vpFeatureMoment.h 4276 2013-06-25 12:36:48Z fspindle $ +* $Id: vpFeatureMoment.h 4714 2014-03-28 18:16:13Z mbakthav $ * * This file is part of the ViSP software. -* Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +* Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -145,8 +145,8 @@ return 0; */ class VISP_EXPORT vpFeatureMoment : public vpBasicFeature{ protected: - vpMoment* moment; - vpMoment& getMoment(){return *moment;} + const vpMoment* moment; + const vpMoment& getMoment() const {return *moment;} vpMomentDatabase& moments; vpFeatureMomentDatabase* featureMomentsDataBase; std::vector<vpMatrix> interaction_matrices; @@ -155,22 +155,24 @@ protected: double B; double C; char _name[255]; + public: /*! Initializes the feature with information about the database of moment primitives, the object plane, feature database and matrix size. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database \param nbmatrices : If you want to create a new vpFeatureMoment implementation, your feature will often have a matrix size of n lines. You can specify the number of lines by this parameter. */ - vpFeatureMoment(vpMomentDatabase& moments,double A=0.0, double B=0.0, double C=0.0,vpFeatureMomentDatabase* featureMoments=NULL,unsigned int nbmatrices=1) : - moment(NULL), - moments(moments), - featureMomentsDataBase(featureMoments), - interaction_matrices(nbmatrices), - A(A),B(B),C(C) {} + vpFeatureMoment(vpMomentDatabase& data_base,double A_=0.0, double B_=0.0, double C_=0.0, + vpFeatureMomentDatabase* featureMoments=NULL,unsigned int nbmatrices=1) + : moment(NULL), + moments(data_base), + featureMomentsDataBase(featureMoments), + interaction_matrices(nbmatrices), + A(A_),B(B_),C(C_) {} virtual ~vpFeatureMoment(); virtual void compute_interaction (void); @@ -182,28 +184,30 @@ public: int getDimension (unsigned int select=FEATURE_ALL) const; void init (void); - vpMatrix interaction (const unsigned int select=FEATURE_ALL); + vpMatrix interaction (const unsigned int select=FEATURE_ALL) ; void linkTo(vpFeatureMomentDatabase& featureMoments); /*! Name of the moment corresponding to the feature. This allows to locate the moment associated with the feature in the provided database. */ - virtual const char* momentName() = 0; + virtual const char* momentName() const = 0; /*! Name of the feature used to locate it in the database of features. */ - virtual const char* name() = 0; + virtual const char* name() const = 0; void print (const unsigned int select=FEATURE_ALL) const ; void update (double A, double B, double C); #ifndef DOXYGEN_SHOULD_SKIP_THIS - void operator=(const vpFeatureMoment &){ + vpFeatureMoment& operator=(const vpFeatureMoment &){ throw vpException(vpException::functionNotImplementedError,"Not implemented!"); } #endif + friend VISP_EXPORT std::ostream& operator<<(std::ostream & os, const vpFeatureMoment& featM); + virtual void printDependencies(std::ostream& os) const; }; /*! @@ -222,15 +226,17 @@ Duplication is mostly used internally in ViSP. */ class VISP_EXPORT vpMomentGenericFeature : public vpFeatureMoment{ public: - vpMomentGenericFeature(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments, vpMoment* moment) : vpFeatureMoment(moments,A,B,C,featureMoments){this->moment = moment;} + vpMomentGenericFeature(vpMomentDatabase& data_base,double A_, double B_, double C_, + vpFeatureMomentDatabase* featureMoments, const vpMoment* p_moment) + : vpFeatureMoment(data_base,A_,B_,C_,featureMoments){this->moment = p_moment;} /*! No specific moment name. */ - const char* momentName() { return NULL;} + const char* momentName() const { return NULL;} /*! No specific feature name. */ - virtual const char* name() { return NULL;} + virtual const char* name() const { return NULL;} }; #endif diff --git a/src/visual-feature/vpFeatureMomentAlpha.cpp b/src/visual-feature/vpFeatureMomentAlpha.cpp index 26ae79e18cd3cdccf9105354ae5a8a9c076f0be2..9645e49e1fcc24321e618fbdf327f5c7b0611975 100644 --- a/src/visual-feature/vpFeatureMomentAlpha.cpp +++ b/src/visual-feature/vpFeatureMomentAlpha.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,8 +62,8 @@ void vpFeatureMomentAlpha::compute_interaction(){ bool found_moment_centered; bool found_FeatureMoment_centered; - vpMomentCentered& momentCentered = (static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); - vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMoments->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); + vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMomentsDataBase->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); if(!found_FeatureMoment_centered) throw vpException(vpException::notInitialized,"vpFeatureMomentCentered not found"); @@ -87,9 +87,9 @@ void vpFeatureMomentAlpha::compute_interaction(){ bool found_moment_centered; bool found_moment_gravity; - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpMomentObject& momentObject = moment->getObject(); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentObject& momentObject = moment->getObject(); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); diff --git a/src/visual-feature/vpFeatureMomentAlpha.h b/src/visual-feature/vpFeatureMomentAlpha.h index 1f03148004267f75569adb4fef3d5ef73fd1c53d..40faae3afd619c9552dbaae005d146eec8774a26 100644 --- a/src/visual-feature/vpFeatureMomentAlpha.h +++ b/src/visual-feature/vpFeatureMomentAlpha.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -91,11 +91,11 @@ class VISP_EXPORT vpFeatureMomentAlpha : public vpFeatureMoment{ /*! associated moment name */ - const char* momentName(){ return "vpMomentAlpha";} + const char* momentName() const { return "vpMomentAlpha";} /*! feature name */ - const char* name(){ return "vpFeatureMomentAlpha";} + const char* name() const { return "vpFeatureMomentAlpha";} }; #else class vpMomentDatabase; @@ -149,26 +149,26 @@ class VISP_EXPORT vpFeatureMomentAlpha : public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentAlpha(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,1) + vpFeatureMomentAlpha(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments,1) {} void compute_interaction(); /*! Associated moment name. */ - const char* momentName(){ return "vpMomentAlpha";} + const char* momentName() const { return "vpMomentAlpha";} /*! Feature name. */ - const char* name(){ return "vpFeatureMomentAlpha";} + const char* name() const { return "vpFeatureMomentAlpha";} vpColVector error (const vpBasicFeature &s_star, const unsigned int select=FEATURE_ALL); }; diff --git a/src/visual-feature/vpFeatureMomentArea.cpp b/src/visual-feature/vpFeatureMomentArea.cpp index a26646497b80a062333c2fa9205caa887a22f19c..175f9227b8ab32a64a791d694d7df57b4563e094 100644 --- a/src/visual-feature/vpFeatureMomentArea.cpp +++ b/src/visual-feature/vpFeatureMomentArea.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentArea.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,21 +78,21 @@ void vpFeatureMomentArea::compute_interaction(){ interaction_matrices[0][0][5] = 0.; } else { - // Get Xg and Yg + // Get Xg and Yg bool found_xgyg; - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_xgyg)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_xgyg)); if (!found_xgyg) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); bool found_m00; - vpMomentArea& areamoment = static_cast<vpMomentArea&>(moments.get("vpMomentArea", found_m00)); + const vpMomentArea& areamoment = static_cast<const vpMomentArea&>(moments.get("vpMomentArea", found_m00)); if (!found_m00) throw vpException(vpException::notInitialized,"vpMomentArea not found"); double Xg = momentGravity.getXg(); double Yg = momentGravity.getYg(); double a = areamoment.get()[0]; // Area scalar - //assert(a==mobj.get(0,0)); - assert(std::fabs(a-mobj.get(0,0)) < a*std::numeric_limits<double>::epsilon()); + + assert(std::fabs(a-mobj.get(0,0)) < a*std::numeric_limits<double>::epsilon()); interaction_matrices[0][0][0] = -a*A; interaction_matrices[0][0][1] = -a*B; diff --git a/src/visual-feature/vpFeatureMomentArea.h b/src/visual-feature/vpFeatureMomentArea.h index 42045ef6a0e848126c2d9461a441f98ebe5ddf6a..1d95aac849c680f27100f3b04289e671f06ae4b0 100644 --- a/src/visual-feature/vpFeatureMomentArea.h +++ b/src/visual-feature/vpFeatureMomentArea.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentArea.h 3317 2011-09-06 14:14:47Z mbakthav $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,25 +61,25 @@ class VISP_EXPORT vpFeatureMomentArea : public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentArea(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,1){} + vpFeatureMomentArea(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments,1){} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentArea";} + const char* momentName() const { return "vpMomentArea";} /*! feature name */ - const char* name(){ return "vpFeatureMomentArea";} + const char* name() const { return "vpFeatureMomentArea";} }; #endif diff --git a/src/visual-feature/vpFeatureMomentAreaNormalized.cpp b/src/visual-feature/vpFeatureMomentAreaNormalized.cpp index 30142620c570104602062d4fa0c63e19f23f0177..5150af6d8d99243ba9977c26404ad55799d89e64 100644 --- a/src/visual-feature/vpFeatureMomentAreaNormalized.cpp +++ b/src/visual-feature/vpFeatureMomentAreaNormalized.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,14 +68,12 @@ void vpFeatureMomentAreaNormalized::compute_interaction(){ bool found_FeatureMoment_centered; bool found_featuremoment_basic; - vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMoments->get("vpFeatureMomentBasic",found_featuremoment_basic))); + vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMomentsDataBase->get("vpFeatureMomentBasic",found_featuremoment_basic))); - - - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); - vpMomentObject& momentObject = moment->getObject(); - vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); - vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMoments->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); + const vpMomentObject& momentObject = moment->getObject(); + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); + vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMomentsDataBase->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); if(!found_FeatureMoment_centered) throw vpException(vpException::notInitialized, "vpFeatureMomentCentered not found"); if(!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); @@ -125,10 +123,10 @@ void vpFeatureMomentAreaNormalized::compute_interaction(){ bool found_moment_surface_normalized; bool found_moment_gravity; - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpMomentObject& momentObject = moment->getObject(); - vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentObject& momentObject = moment->getObject(); + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); if (!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); if (!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); diff --git a/src/visual-feature/vpFeatureMomentAreaNormalized.h b/src/visual-feature/vpFeatureMomentAreaNormalized.h index b43e2ea26a793bb0237e775da6e563aaceda5ab9..7004fe33a6ea1e4b1f9c4c2b19642fab8cdc0cb3 100644 --- a/src/visual-feature/vpFeatureMomentAreaNormalized.h +++ b/src/visual-feature/vpFeatureMomentAreaNormalized.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,24 +77,24 @@ class VISP_EXPORT vpFeatureMomentAreaNormalized : public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param database : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentAreaNormalized(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,1){} + vpFeatureMomentAreaNormalized(vpMomentDatabase& database,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) + : vpFeatureMoment(database,A_,B_,C_,featureMoments,1){} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentAreaNormalized";} + const char* momentName() const { return "vpMomentAreaNormalized";} /*! feature name */ - const char* name(){ return "vpFeatureMomentAreaNormalized";} + const char* name() const { return "vpFeatureMomentAreaNormalized";} }; @@ -160,24 +160,24 @@ class VISP_EXPORT vpFeatureMomentAreaNormalized : public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentAreaNormalized(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,1){} + vpFeatureMomentAreaNormalized(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments,1){} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentAreaNormalized";} + const char* momentName() const { return "vpMomentAreaNormalized";} /*! feature name */ - const char* name(){ return "vpFeatureMomentAreaNormalized";} + const char* name() const { return "vpFeatureMomentAreaNormalized";} }; #endif diff --git a/src/visual-feature/vpFeatureMomentBasic.cpp b/src/visual-feature/vpFeatureMomentBasic.cpp index 102ce4a76d37d0cd765bd4f9785f0cfc45b85c83..08ef40ebf6732ac23cc9b15c4703f0aba228dc8a 100644 --- a/src/visual-feature/vpFeatureMomentBasic.cpp +++ b/src/visual-feature/vpFeatureMomentBasic.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -48,14 +48,14 @@ #include <limits> /*! Default constructor. - \param moments : Database of moment primitives. - \param A : First plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param B : Second plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param C : Third plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param data_base : Database of moment primitives. + \param A_ : First plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param B_ : Second plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param C_ : Third plane coefficient for a plane equation of the following type Ax+By+C=1/Z. \param featureMoments : Database of features. */ -vpFeatureMomentBasic::vpFeatureMomentBasic(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments) : - vpFeatureMoment(moments,A,B,C,featureMoments) +vpFeatureMomentBasic::vpFeatureMomentBasic(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments), order(0) { } @@ -65,12 +65,12 @@ vpFeatureMomentBasic::vpFeatureMomentBasic(vpMomentDatabase& moments,double A, d */ void vpFeatureMomentBasic::compute_interaction(){ int delta; - vpMomentObject& momentObject = moment->getObject(); - order = momentObject.getOrder()+1; + const vpMomentObject& m = moment->getObject(); + order = m.getOrder()+1; interaction_matrices.resize(order*order); for(std::vector< vpMatrix >::iterator i=interaction_matrices.begin();i!=interaction_matrices.end();i++) i->resize(1,6); - if (momentObject.getType()==vpMomentObject::DISCRETE){ + if (m.getType()==vpMomentObject::DISCRETE){ delta=0; } else { delta=1; @@ -84,12 +84,12 @@ void vpFeatureMomentBasic::compute_interaction(){ int WZ = 5; //i=0;j=0 - interaction_matrices[0][0][VX] = -delta*A*momentObject.get(0, 0); - interaction_matrices[0][0][VY] = -delta*B*momentObject.get(0, 0); - interaction_matrices[0][0][VZ] = 3*delta*(A*momentObject.get(1, 0)+B*momentObject.get(0, 1)+C*momentObject.get(0, 0))-delta*C*momentObject.get(0, 0); + interaction_matrices[0][0][VX] = -delta*A*m.get(0, 0); + interaction_matrices[0][0][VY] = -delta*B*m.get(0, 0); + interaction_matrices[0][0][VZ] = 3*delta*(A*m.get(1, 0)+B*m.get(0, 1)+C*m.get(0, 0))-delta*C*m.get(0, 0); - interaction_matrices[0][0][WX] = 3*delta*momentObject.get(0, 1); - interaction_matrices[0][0][WY] = -3*delta*momentObject.get(1, 0); + interaction_matrices[0][0][WX] = 3*delta*m.get(0, 1); + interaction_matrices[0][0][WY] = -3*delta*m.get(1, 0); interaction_matrices[0][0][WZ] = 0; // int i=0; @@ -98,13 +98,13 @@ void vpFeatureMomentBasic::compute_interaction(){ unsigned int jm1_ = j_ - 1; unsigned int jp1_ = j_ + 1; - interaction_matrices[j_*order][0][VX] = -delta*A*momentObject.get(0, j_); - interaction_matrices[j_*order][0][VY] = -j*(A*momentObject.get(1,jm1_)+B*momentObject.get(0,j_)+C*momentObject.get(0,jm1_))-delta*B*momentObject.get(0,j_); - interaction_matrices[j_*order][0][VZ] = (j+3*delta)*(A*momentObject.get(1,j_)+B*momentObject.get(0,jp1_)+C*momentObject.get(0,j_))-delta*C*momentObject.get(0,j_); + interaction_matrices[j_*order][0][VX] = -delta*A*m.get(0, j_); + interaction_matrices[j_*order][0][VY] = -j*(A*m.get(1,jm1_)+B*m.get(0,j_)+C*m.get(0,jm1_))-delta*B*m.get(0,j_); + interaction_matrices[j_*order][0][VZ] = (j+3*delta)*(A*m.get(1,j_)+B*m.get(0,jp1_)+C*m.get(0,j_))-delta*C*m.get(0,j_); - interaction_matrices[j_*order][0][WX] = (j+3*delta)*momentObject.get(0,jp1_)+j*momentObject.get(0,jm1_); - interaction_matrices[j_*order][0][WY] = -(j+3*delta)*momentObject.get(1,j_); - interaction_matrices[j_*order][0][WZ] = -j*momentObject.get(1,jm1_); + interaction_matrices[j_*order][0][WX] = (j+3*delta)*m.get(0,jp1_)+j*m.get(0,jm1_); + interaction_matrices[j_*order][0][WY] = -(j+3*delta)*m.get(1,j_); + interaction_matrices[j_*order][0][WZ] = -j*m.get(1,jm1_); } //int j=0; @@ -113,13 +113,13 @@ void vpFeatureMomentBasic::compute_interaction(){ unsigned int im1_ = i_ - 1; unsigned int ip1_ = i_ + 1; - interaction_matrices[i_][0][VX] = -i*(A*momentObject.get(i_, 0)+B*momentObject.get(im1_, 1)+C*momentObject.get(im1_, 0))-delta*A*momentObject.get(i_, 0); - interaction_matrices[i_][0][VY] = -delta*B*momentObject.get(i_, 0); - interaction_matrices[i_][0][VZ] = (i+3*delta)*(A*momentObject.get(ip1_, 0)+B*momentObject.get(i_, 1)+C*momentObject.get(i_, 0))-delta*C*momentObject.get(i_, 0); + interaction_matrices[i_][0][VX] = -i*(A*m.get(i_, 0)+B*m.get(im1_, 1)+C*m.get(im1_, 0))-delta*A*m.get(i_, 0); + interaction_matrices[i_][0][VY] = -delta*B*m.get(i_, 0); + interaction_matrices[i_][0][VZ] = (i+3*delta)*(A*m.get(ip1_, 0)+B*m.get(i_, 1)+C*m.get(i_, 0))-delta*C*m.get(i_, 0); - interaction_matrices[i_][0][WX] = (i+3*delta)*momentObject.get(i_, 1); - interaction_matrices[i_][0][WY] = -(i+3*delta)*momentObject.get(ip1_, 0)-i*momentObject.get(im1_, 0); - interaction_matrices[i_][0][WZ] = i*momentObject.get(im1_, 1); + interaction_matrices[i_][0][WX] = (i+3*delta)*m.get(i_, 1); + interaction_matrices[i_][0][WY] = -(i+3*delta)*m.get(ip1_, 0)-i*m.get(im1_, 0); + interaction_matrices[i_][0][WZ] = i*m.get(im1_, 1); } for(int j=1; j<(int)order-1; j++){ @@ -132,13 +132,13 @@ void vpFeatureMomentBasic::compute_interaction(){ unsigned int im1_ = i_ - 1; unsigned int ip1_ = i_ + 1; - interaction_matrices[j_*order+i_][0][VX] = -i*(A*momentObject.get(i_, j_)+B*momentObject.get(im1_, jp1_)+C*momentObject.get(im1_,j_))-delta*A*momentObject.get(i_, j_); - interaction_matrices[j_*order+i_][0][VY] = -j*(A*momentObject.get(ip1_, jm1_)+B*momentObject.get(i_, j_)+C*momentObject.get(i_,jm1_))-delta*B*momentObject.get(i_, j_); - interaction_matrices[j_*order+i_][0][VZ] = (i+j+3*delta)*(A*momentObject.get(ip1_, j_)+B*momentObject.get(i_,jp1_)+C*momentObject.get(i_, j_))-delta*C*momentObject.get(i_,j_); + interaction_matrices[j_*order+i_][0][VX] = -i*(A*m.get(i_, j_)+B*m.get(im1_, jp1_)+C*m.get(im1_,j_))-delta*A*m.get(i_, j_); + interaction_matrices[j_*order+i_][0][VY] = -j*(A*m.get(ip1_, jm1_)+B*m.get(i_, j_)+C*m.get(i_,jm1_))-delta*B*m.get(i_, j_); + interaction_matrices[j_*order+i_][0][VZ] = (i+j+3*delta)*(A*m.get(ip1_, j_)+B*m.get(i_,jp1_)+C*m.get(i_, j_))-delta*C*m.get(i_,j_); - interaction_matrices[j_*order+i_][0][WX] = (i+j+3*delta)*momentObject.get(i_, jp1_)+j*momentObject.get(i_, jm1_); - interaction_matrices[j_*order+i_][0][WY] = -(i+j+3*delta)*momentObject.get(ip1_, j_)-i*momentObject.get(im1_, j_); - interaction_matrices[j_*order+i_][0][WZ] = i*momentObject.get(im1_,jp1_)-j*momentObject.get(ip1_, jm1_); + interaction_matrices[j_*order+i_][0][WX] = (i+j+3*delta)*m.get(i_, jp1_)+j*m.get(i_, jm1_); + interaction_matrices[j_*order+i_][0][WY] = -(i+j+3*delta)*m.get(ip1_, j_)-i*m.get(im1_, j_); + interaction_matrices[j_*order+i_][0][WZ] = i*m.get(im1_,jp1_)-j*m.get(ip1_, jm1_); } } } @@ -149,7 +149,7 @@ Interaction matrix corresponding to \f$ m_{ij} \f$ moment. \param select_two : second index (j). \return Interaction matrix \f$ L_{m_{ij}} \f$ corresponding to the moment. */ -vpMatrix vpFeatureMomentBasic::interaction (unsigned int select_one,unsigned int select_two){ +vpMatrix vpFeatureMomentBasic::interaction (unsigned int select_one,unsigned int select_two) const { if(select_one+select_two>moment->getObject().getOrder()) throw vpException(vpException::badValue,"The requested value has not been computed, you should specify a higher order."); return interaction_matrices[select_two*order+select_one]; diff --git a/src/visual-feature/vpFeatureMomentBasic.h b/src/visual-feature/vpFeatureMomentBasic.h index 90d889a338852d91b560d221d419e15dcee76792..64df62c7161fa3254321a6ce9e7f8955c54b394d 100644 --- a/src/visual-feature/vpFeatureMomentBasic.h +++ b/src/visual-feature/vpFeatureMomentBasic.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,7 +71,7 @@ class vpMomentDatabase; */ class VISP_EXPORT vpFeatureMomentBasic : public vpFeatureMoment{ -private: +protected: unsigned int order; public: vpFeatureMomentBasic(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL); @@ -84,15 +84,15 @@ private: } #endif - vpMatrix interaction (unsigned int select_one,unsigned int select_two); + vpMatrix interaction (unsigned int select_one,unsigned int select_two) const; /*! Associated moment name. */ - const char* momentName(){ return "vpMomentBasic";} + const char* momentName() const { return "vpMomentBasic";} /*! Feature name. */ - const char* name(){ return "vpFeatureMomentBasic";} + const char* name() const { return "vpFeatureMomentBasic";} }; #endif diff --git a/src/visual-feature/vpFeatureMomentCInvariant.cpp b/src/visual-feature/vpFeatureMomentCInvariant.cpp index d00d1bd40f330ce86364131e728d140975c9ac2c..10aa1ec9fa8ae0d40bad9936eb97ae44803a3e1b 100644 --- a/src/visual-feature/vpFeatureMomentCInvariant.cpp +++ b/src/visual-feature/vpFeatureMomentCInvariant.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,12 +68,12 @@ void vpFeatureMomentCInvariant::compute_interaction(){ bool found_FeatureMoment_centered; bool found_featuremoment_basic; - vpMomentObject& momentObject = moment->getObject(); - vpMomentCentered& momentCentered = (static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); - vpMomentCInvariant& momentCInvariant = (static_cast<vpMomentCInvariant&>(moments.get("vpMomentCInvariant",found_moment_cinvariant))); - vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMoments->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); + const vpMomentObject& momentObject = moment->getObject(); + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); + const vpMomentCInvariant& momentCInvariant = (static_cast<const vpMomentCInvariant&>(moments.get("vpMomentCInvariant",found_moment_cinvariant))); + vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMomentsDataBase->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); - vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMoments->get("vpFeatureMomentBasic",found_featuremoment_basic))); + vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMomentsDataBase->get("vpFeatureMomentBasic",found_featuremoment_basic))); if(!found_featuremoment_basic) throw vpException(vpException::notInitialized,"vpFeatureMomentBasic not found"); @@ -194,7 +194,7 @@ void vpFeatureMomentCInvariant::compute_interaction(){ #include <vector> #include <iostream> #include <limits> - +#include <cmath> /*! Computes interaction matrix for space-scale-rotation invariants. Called internally. @@ -207,23 +207,23 @@ void vpFeatureMomentCInvariant::compute_interaction(){ */ void vpFeatureMomentCInvariant::compute_interaction(){ - std::vector<vpMatrix> LI(16); + //std::vector<vpMatrix> LI(16); + LI.resize(16); // LI made class member bool found_moment_centered; bool found_moment_cinvariant; bool found_FeatureMoment_centered; bool found_featuremoment_basic; - vpMomentObject& momentObject = moment->getObject(); - vpMomentCentered& momentCentered = (static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); - vpMomentCInvariant& momentCInvariant = (static_cast<vpMomentCInvariant&>(moments.get("vpMomentCInvariant",found_moment_cinvariant))); + const vpMomentObject& momentObject = moment->getObject(); + const vpMomentCentered& momentCentered = (static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); + const vpMomentCInvariant& momentCInvariant = (static_cast<const vpMomentCInvariant&>(moments.get("vpMomentCInvariant",found_moment_cinvariant))); + vpFeatureMomentCentered& featureMomentCentered = (static_cast<vpFeatureMomentCentered&>(featureMomentsDataBase->get("vpFeatureMomentCentered",found_FeatureMoment_centered))); vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMomentsDataBase->get("vpFeatureMomentBasic",found_featuremoment_basic))); if(!found_featuremoment_basic) throw vpException(vpException::notInitialized,"vpFeatureMomentBasic not found"); - - if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); if(!found_moment_cinvariant) throw vpException(vpException::notInitialized,"vpMomentCInvariant not found"); if(!found_FeatureMoment_centered) throw vpException(vpException::notInitialized,"vpFeatureMomentCentered not found"); @@ -256,8 +256,6 @@ void vpFeatureMomentCInvariant::compute_interaction(){ double mu12 = momentCentered.get(1,2); double mu11 = momentCentered.get(1,1); - - double mu12_2 = mu12*mu12; double mu21_2 = mu21*mu21; double mu21_3 = mu21*mu21_2; @@ -282,7 +280,6 @@ void vpFeatureMomentCInvariant::compute_interaction(){ vpMatrix Lmu41 = featureMomentCentered.interaction(4,1); vpMatrix Lmu50 = featureMomentCentered.interaction(5,0); - LI[1]= -Lmu20*mu02-mu20*Lmu02+2*mu11*Lmu11; LI[2]= (-2*mu20+2*mu02)*Lmu02+8*mu11*Lmu11+(2*mu20-2*mu02)*Lmu20; @@ -317,6 +314,7 @@ void vpFeatureMomentCInvariant::compute_interaction(){ double s2 = momentCInvariant.getS(2); double c3 = momentCInvariant.getC(3); double c2 = momentCInvariant.getC(2); + double I1 = momentCInvariant.getII(1); double I2 = momentCInvariant.getII(2); double I3 = momentCInvariant.getII(3); @@ -331,7 +329,6 @@ void vpFeatureMomentCInvariant::compute_interaction(){ vpMatrix LI2 = 2 * (mu03 - 3 * mu21) * (Lc2) + 2 * (mu30 - 3 * mu12) * (Ls2); vpMatrix LI3 = Lmu20 + Lmu02; - vpMatrix La(1,6); double a; if(momentObject.getType()==vpMomentObject::DISCRETE){ @@ -341,10 +338,16 @@ void vpFeatureMomentCInvariant::compute_interaction(){ a = momentObject.get(0,0); La = featureMomentBasic.interaction(0,0); } + interaction_matrices.resize(14); + /* + momentCInvariant.printInvariants(std::cout); + printLsofInvariants(std::cout); + */ interaction_matrices[0] = (1./(momentCInvariant.getI(2)*momentCInvariant.getI(2)))*(momentCInvariant.getI(2)*LI[1]-momentCInvariant.getI(1)*LI[2]); + interaction_matrices[1] = (1./(momentCInvariant.getI(4)*momentCInvariant.getI(4)))*(momentCInvariant.getI(4)*LI[3]-momentCInvariant.getI(3)*LI[4]); interaction_matrices[2] = (1./(momentCInvariant.getI(6)*momentCInvariant.getI(6)))*(momentCInvariant.getI(6)*LI[5]-momentCInvariant.getI(5)*LI[6]); @@ -355,7 +358,6 @@ void vpFeatureMomentCInvariant::compute_interaction(){ interaction_matrices[5] = (1./(momentCInvariant.getI(6)*momentCInvariant.getI(6)))*(momentCInvariant.getI(6)*LI[9]-momentCInvariant.getI(9)*LI[6]); - interaction_matrices[6] = (1./(momentCInvariant.getI(10)*momentCInvariant.getI(10)))*(momentCInvariant.getI(10)*LI[11]-momentCInvariant.getI(11)*LI[10]); interaction_matrices[7] = (1./(momentCInvariant.getI(10)*momentCInvariant.getI(10)))*(momentCInvariant.getI(10)*LI[12]-momentCInvariant.getI(12)*LI[10]); @@ -364,12 +366,59 @@ void vpFeatureMomentCInvariant::compute_interaction(){ interaction_matrices[9] = (1./(momentCInvariant.getI(15)*momentCInvariant.getI(15)))*(momentCInvariant.getI(15)*LI[14]-momentCInvariant.getI(14)*LI[15]); - interaction_matrices[10] = (Lc2 * c3 + c2 * Lc3 + Ls2 * s3 + s2 * Ls3) * sqrt(a) / I1 * pow(I3, -0.3e1 / 0.2e1) + (c2 * c3 + s2 * s3) * pow(a, -0.1e1 / 0.2e1) / I1 * pow(I3, -0.3e1 / 0.2e1) * La / 0.2e1 - (c2 * c3 + s2 * s3) * sqrt(a) * pow(I1, -0.2e1) * pow(I3, -0.3e1 / 0.2e1) * LI1 - 0.3e1 / 0.2e1 * (c2 * c3 + s2 * s3) * sqrt(a) / I1 * pow(I3, -0.5e1 / 0.2e1) * LI3; interaction_matrices[11] = (Ls2 * c3 + s2 * Lc3 - Lc2 * s3 - c2 * Ls3) * sqrt(a) / I1 * pow(I3, -0.3e1 / 0.2e1) + (s2 * c3 - c2 * s3) * pow(a, -0.1e1 / 0.2e1) / I1 * pow(I3, -0.3e1 / 0.2e1) * La / 0.2e1 - (s2 * c3 - c2 * s3) * sqrt(a) * pow(I1, -0.2e1) * pow(I3, -0.3e1 / 0.2e1) * LI1 - 0.3e1 / 0.2e1 * (s2 * c3 - c2 * s3) * sqrt(a) / I1 * pow(I3, -0.5e1 / 0.2e1) * LI3; interaction_matrices[12] = (1/(I3*I3))*LI1-(2*I1/(I3*I3*I3))*LI3; + interaction_matrices[13] = (I2/(I3*I3*I3))*La+(a/(I3*I3*I3))*LI2-(3*a*I2/(I3*I3*I3*I3))*LI3; + + /* + std::cout << (*this); + vpTRACE("Done."); + std::exit(-1); + */ +} + +/*! + Print out all invariants that were computed + There are 15 of them, as in [Point-based and region based.ITRO05] + \cite Tahri05z + */ +void vpFeatureMomentCInvariant::printLsofInvariants(std::ostream& os) const{ + for (unsigned int i = 1; i < 15; ++i){ + os << "LI[" << i << "] = "; + LI[i].matlabPrint(os); + os << std::endl; + } +} + +VISP_EXPORT std::ostream& operator<<(std::ostream & os, const vpFeatureMomentCInvariant& featcinv) +{ + //Print L for c1 .. c10 + for (unsigned int i = 0; i < 10; ++i){ + os << "L_c[" << i << "] = "; + featcinv.interaction_matrices[i].matlabPrint(os); + os << std::endl; + } + + // sx, sy + os << "L_sx = "; + featcinv.interaction_matrices[10].matlabPrint(os); + os << std::endl; + os << "L_sy = "; + featcinv.interaction_matrices[11].matlabPrint(os); + os << std::endl; + // Px,Py + os << "L_Px = "; + featcinv.interaction_matrices[12].matlabPrint(os); + os << std::endl; + os << "L_Py = "; + featcinv.interaction_matrices[13].matlabPrint(os); + os << std::endl; + + return os; + } #endif diff --git a/src/visual-feature/vpFeatureMomentCInvariant.h b/src/visual-feature/vpFeatureMomentCInvariant.h index b6218c45dc0cd5c79887a1005dff11a2e2602615..a9039cd52c6182ef1d2546a609d8b3114adaa58e 100644 --- a/src/visual-feature/vpFeatureMomentCInvariant.h +++ b/src/visual-feature/vpFeatureMomentCInvariant.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,11 +96,11 @@ class VISP_EXPORT vpFeatureMomentCInvariant : public vpFeatureMoment{ /*! associated moment name */ - const char* momentName(){ return "vpMomentCInvariant";} + const char* momentName() const { return "vpMomentCInvariant";} /*! feature name */ - const char* name(){ return "vpFeatureMomentCInvariant";} + const char* name() const { return "vpFeatureMomentCInvariant";} /*! Shortcut selector for \f$C_1\f$. @@ -198,27 +198,29 @@ class vpMomentDatabase; */ class VISP_EXPORT vpFeatureMomentCInvariant : public vpFeatureMoment{ + private: + std::vector<vpMatrix> LI; public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentCInvariant(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,16){} + vpFeatureMomentCInvariant(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments,16){LI.resize(16);} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentCInvariant";} + const char* momentName() const { return "vpMomentCInvariant";} /*! feature name */ - const char* name(){ return "vpFeatureMomentCInvariant";} + const char* name() const { return "vpFeatureMomentCInvariant";} /*! Shortcut selector for \f$C_1\f$. @@ -277,6 +279,16 @@ class VISP_EXPORT vpFeatureMomentCInvariant : public vpFeatureMoment{ */ static unsigned int selectPy(){ return 1 << 13; } + /*! + Print all the interaction matrices of the moment invariants + */ + void printLsofInvariants(std::ostream& os) const; + + /*! + Print all the interaction matrices of visual features + */ + friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpFeatureMomentCInvariant& featcinv); + }; #endif #endif diff --git a/src/visual-feature/vpFeatureMomentCentered.cpp b/src/visual-feature/vpFeatureMomentCentered.cpp index 246230aa0241744c041d91029f4418fed6b1d7f9..e518fe27c158579a557ec40bbe7e9d74a5eeb7c5 100644 --- a/src/visual-feature/vpFeatureMomentCentered.cpp +++ b/src/visual-feature/vpFeatureMomentCentered.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,35 +36,35 @@ * * Authors: * Filip Novotny - * + * Manikandan Bakthavatchalam *****************************************************************************/ #include <visp/vpConfig.h> -#ifdef VISP_MOMENTS_COMBINE_MATRICES #include <vector> #include <limits> +#include <visp/vpMomentCentered.h> +#include <visp/vpMomentGravityCenter.h> #include <visp/vpMomentObject.h> -#include <visp/vpFeatureMomentCentered.h> #include <visp/vpFeatureMomentBasic.h> -#include <visp/vpFeatureMomentGravityCenter.h> -#include <visp/vpMomentGravityCenter.h> +#include <visp/vpFeatureMomentCentered.h> #include <visp/vpFeatureMomentDatabase.h> +#include <visp/vpFeatureMomentGravityCenter.h> + /*! Default constructor - \param moments : Database of moment primitives. - \param A : First plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param B : Second plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param C : Third plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param moments_ : Database of moment primitives. + \param A_ : First plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param B_ : Second plane coefficient for a plane equation of the following type Ax+By+C=1/Z. + \param C_ : Third plane coefficient for a plane equation of the following type Ax+By+C=1/Z. \param featureMoments : Database of features. */ -vpFeatureMomentCentered::vpFeatureMomentCentered(vpMomentDatabase& moments, - double A, double B, double C, - vpFeatureMomentDatabase* featureMoments) : - vpFeatureMoment(moments,A,B,C,featureMoments) - +vpFeatureMomentCentered::vpFeatureMomentCentered(vpMomentDatabase& moments_, + double A_, double B_, double C_, + vpFeatureMomentDatabase* featureMoments) + : vpFeatureMoment(moments_, A_, B_, C_, featureMoments), order(0) { } @@ -74,229 +74,241 @@ Interaction matrix corresponding to \f$ \mu_{ij} \f$ moment \param select_two : second index (j) \return Interaction matrix corresponding to the moment */ -vpMatrix vpFeatureMomentCentered::interaction (unsigned int select_one,unsigned int select_two){ +vpMatrix vpFeatureMomentCentered::interaction (unsigned int select_one,unsigned int select_two) const { if(select_one+select_two>moment->getObject().getOrder()) throw vpException(vpException::badValue, "The requested value has not been computed, you should specify a higher order."); return interaction_matrices[select_two*order+select_one]; } +/*! + * Core function for the interaction matrix computation for moment m_pq + * Given its dependent moment and interaction matrices, computes the interaction matrix of centred moments + */ +vpMatrix +vpFeatureMomentCentered::compute_Lmu_pq(const unsigned int& p, const unsigned int& q, const double& xg, const double& yg, + const vpMatrix& L_xg, const vpMatrix& L_yg, + const vpMomentBasic& m, const vpFeatureMomentBasic& feature_moment_m) const +{ + // term1, term2 and Lterm3 (matrix) will be repeatedly computed inside the innermost loop + double term1 = 0.0; + double term2 = 0.0; + vpMatrix Lterm3(1,6); + + double pcombk = 0.0; + double qcombl = 0.0; + double pcombkqcombl = 0.0; + + double mkl = 0.0; + vpMatrix L_mkl; + + int pmk = 0; // p-k + int qml = 0; // q-l + double minus1pow = 0.; // (-1)^(p+q-k-l) + double pintom = 0.; + + for (unsigned int k = 0; k <=p; ++k) + { + pmk = (int)p-(int)k; + pcombk = static_cast<double>(vpMath::comb(p,k)); + for (unsigned int l = 0; l <= q; ++l) + { + qml = (int)q - (int)l; + qcombl = static_cast<double>(vpMath::comb(q,l)); + minus1pow = pow((double)-1, (double)(pmk + qml)); + pcombkqcombl = pcombk * qcombl; + mkl = m.get(k, l); + pintom = pcombkqcombl * mkl; + L_mkl = feature_moment_m.interaction(k, l); + if(pmk>0) + term1 += pintom * pmk * pow(xg, pmk-1) * pow(yg, qml) * minus1pow; + if(qml>0) + term2 += pintom * qml * pow(xg, pmk) * pow(yg, qml-1) * minus1pow; + Lterm3 += pcombkqcombl * pow(xg, pmk) * pow(yg, qml) * L_mkl * minus1pow; + } + } + + // L_xg and L_yg stay constant with respect to the above loops. Lterm3 is summed over + vpMatrix L_mupq = L_xg*term1 + L_yg*term2 + Lterm3; + return L_mupq; +} /*! - Computes interaction matrix for centered moment. Called internally. - The moment primitives must be computed before calling this. - This feature depends on: + Interface to the interaction matrix computation for centered moments. Called internally. + Calls compute_Lmu_pq() for main computation moments (upto order-1) +Dependencies: + Moment classes + - vpMomentBasic + Interaction matrix classes + - vpMomentGravityCenter - vpFeatureMomentBasic - vpFeatureMomentGravityCenter - - vpMomentGravityCenter */ -void vpFeatureMomentCentered::compute_interaction(){ - bool found_featuremoment_basic; - bool found_feature_gravity_center; - bool found_moment_gravity; - - vpMomentObject& momentObject = moment->getObject(); +void vpFeatureMomentCentered::compute_interaction() { +#ifdef VISP_MOMENTS_COMBINE_MATRICES + const vpMomentObject& momentObject = moment->getObject(); order = momentObject.getOrder()+1; interaction_matrices.resize(order*order); for(std::vector< vpMatrix >::iterator i=interaction_matrices.begin();i!=interaction_matrices.end(); ++i) i->resize(1,6); - vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMoments->get("vpFeatureMomentBasic",found_featuremoment_basic))); - vpFeatureMomentGravityCenter& featureMomentGravityCenter= (static_cast<vpFeatureMomentGravityCenter&>(featureMoments->get("vpFeatureMomentGravityCenter",found_feature_gravity_center))); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpMatrix zeros(1,6); - for(int i=0;i<6;i++) zeros[0][i]=0; - - if(!found_featuremoment_basic) throw vpException(vpException::notInitialized,"vpFeatureMomentBasic not found"); - if(!found_feature_gravity_center) throw vpException(vpException::notInitialized,"vpFeatureMomentGravityCenter not found"); + bool found_moment_gravity; + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + double xg = momentGravity.get()[0]; + double yg = momentGravity.get()[1]; - vpMatrix LXg = featureMomentGravityCenter.interaction(1<<0); - vpMatrix LYg = featureMomentGravityCenter.interaction(1<<1); - - //compute centered moment features as a combination of basic features - for(int i=0;i<(int)order;i++){ - for(int j=0;j<(int)order-i;j++){ - interaction_matrices[(unsigned int)j*order+(unsigned int)i] = zeros; - vpMatrix mat1 = zeros; - vpMatrix mat2 = zeros; - vpMatrix mat3 = zeros; - for(int k=0;k<=i;k++){ - for(int l=0;l<=j;l++){ - mat1+= std::abs(momentGravity.get()[0])<std::numeric_limits<double>::epsilon()?zeros:vpMath::comb((unsigned int)i, (unsigned int)k) * vpMath::comb((unsigned int)j, (unsigned int)l) * pow(-momentGravity.get()[0], i - k) * (i - k) * LXg / momentGravity.get()[0] * pow(-momentGravity.get()[1], j - l) * momentObject.get((unsigned int)k, (unsigned int)l); - mat2+= std::abs(momentGravity.get()[1])<std::numeric_limits<double>::epsilon()?zeros:vpMath::comb((unsigned int)i, (unsigned int)k) * vpMath::comb((unsigned int)j, (unsigned int)l) * pow(-momentGravity.get()[0], i - k) * pow(-momentGravity.get()[1], j - l) * (j - l) * LYg / momentGravity.get()[1] * momentObject.get((unsigned int)k, (unsigned int)l); - mat3+= vpMath::comb((unsigned int)i, (unsigned int)k) * vpMath::comb((unsigned int)j, (unsigned int)l) * pow(-momentGravity.get()[0], i - k) * pow(-momentGravity.get()[1], j - l) * featureMomentBasic.interaction((unsigned int)k, (unsigned int)l); - } - } - interaction_matrices[(unsigned int)j*order+(unsigned int)i]=(mat1+mat2+mat3); - } - } -} - -#else - -#include <vector> -#include <limits> - -#include <visp/vpMomentObject.h> -#include <visp/vpMomentGravityCenter.h> -#include <visp/vpMomentCentered.h> -#include <visp/vpFeatureMomentCentered.h> -#include <visp/vpFeatureMomentDatabase.h> - - -/*! - Default constructor - \param moments : Database of moment primitives. - \param A : First plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param B : Second plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param C : Third plane coefficient for a plane equation of the following type Ax+By+C=1/Z. - \param featureMoments : Database of features. -*/ -vpFeatureMomentCentered::vpFeatureMomentCentered(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments) : - vpFeatureMoment(moments,A,B,C,featureMoments) + bool found_feature_gravity_center; + vpFeatureMomentGravityCenter& featureMomentGravityCenter= (static_cast<vpFeatureMomentGravityCenter&>(featureMomentsDataBase->get("vpFeatureMomentGravityCenter",found_feature_gravity_center))); + if(!found_feature_gravity_center) throw vpException(vpException::notInitialized,"vpFeatureMomentGravityCenter not found"); + vpMatrix Lxg = featureMomentGravityCenter.interaction(1<<0); + vpMatrix Lyg = featureMomentGravityCenter.interaction(1<<1); -{ -} + bool found_moment_basic; + const vpMomentBasic& momentbasic = static_cast<const vpMomentBasic&>(moments.get("vpMomentBasic",found_moment_basic)); + if(!found_moment_basic) throw vpException(vpException::notInitialized,"vpMomentBasic not found"); -/*! -Interaction matrix corresponding to \f$ \mu_{ij} \f$ moment -\param select_one : first index (i) -\param select_two : second index (j) -\return Interaction matrix corresponding to the moment -*/ -vpMatrix vpFeatureMomentCentered::interaction (unsigned int select_one,unsigned int select_two){ - if(select_one+select_two>moment->getObject().getOrder()) - throw vpException(vpException::badValue,"The requested value has not been computed, you should specify a higher order."); - return interaction_matrices[select_two*order+select_one]; -} + bool found_featuremoment_basic; + vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMomentsDataBase->get("vpFeatureMomentBasic",found_featuremoment_basic))); + if(!found_featuremoment_basic) throw vpException(vpException::notInitialized,"vpFeatureMomentBasic not found"); + // Calls the main compute_Lmu_pq function for moments upto order-1 + for(int i=0;i<(int)order-1;i++){ + for(int j=0;j<(int)order-1-i;j++){ + interaction_matrices[(unsigned int)j*order+(unsigned int)i] = compute_Lmu_pq(i, j, xg, yg, Lxg, Lyg, momentbasic, featureMomentBasic); + } + } +#else // #ifdef VISP_MOMENTS_COMBINE_MATRICES + bool found_moment_centered; + bool found_moment_gravity; + + const vpMomentCentered& momentCentered= (static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + + if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); + if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + + int delta; + int epsilon; + const vpMomentObject& momentObject = moment->getObject(); + order = momentObject.getOrder()+1; + interaction_matrices.resize(order*order); + for (std::vector< vpMatrix >::iterator i=interaction_matrices.begin(); i!=interaction_matrices.end(); ++i) + i->resize(1,6); + if (momentObject.getType()==vpMomentObject::DISCRETE) { + delta=0; + epsilon=1; + } else { + delta=1; + epsilon=4; + } + double n11 = momentCentered.get(1,1)/momentObject.get(0,0); + double n20 = momentCentered.get(2,0)/momentObject.get(0,0); + double n02 = momentCentered.get(0,2)/momentObject.get(0,0); + double Xg = momentGravity.getXg(); + double Yg = momentGravity.getYg(); + double mu00 = momentCentered.get(0,0); + + unsigned int VX = 0; + unsigned int VY = 1; + unsigned int VZ = 2; + unsigned int WX = 3; + unsigned int WY = 4; + unsigned int WZ = 5; + + interaction_matrices[0][0][VX] = -(delta)*A*mu00; + interaction_matrices[0][0][VY] = -(delta)*B*mu00; + + // Since mu10=0 and mu01=0 + // interaction_matrices[0][0][WX] = (3*delta)*MU(0,1)+(3*delta)*Yg*mu00; + // interaction_matrices[0][0][WY] = -(3*delta)*MU(1,0)-(3*delta)*Xg*mu00; + // we get the simplification: + interaction_matrices[0][0][WX] = (3*delta)*Yg*mu00; + interaction_matrices[0][0][WY] = -(3*delta)*Xg*mu00; + interaction_matrices[0][0][VZ] = -A*interaction_matrices[0][0][WY]+B*interaction_matrices[0][0][WX]+(2*delta)*C*mu00; + interaction_matrices[0][0][WZ] = 0.; + + for (int i=1; i<(int)order-1; i++){ + unsigned int i_ = (unsigned int) i; + unsigned int im1_ = i_ - 1; + unsigned int ip1_ = i_ + 1; + + double mu_im10 = momentCentered.get(im1_,0); + double mu_ip10 = momentCentered.get(ip1_,0); + double mu_im11 = momentCentered.get(im1_,1); + double mu_i0 = momentCentered.get(i_,0); + double mu_i1 = momentCentered.get(i_,1); + + interaction_matrices[i_][0][VX] = -(i+delta)*A*mu_i0-(i*B*mu_im11); + interaction_matrices[i_][0][VY] = -(delta)*B*mu_i0; + + interaction_matrices[i_][0][WX] = (i+3*delta)*mu_i1+(i+3*delta)*Yg*mu_i0+i*Xg*mu_im11-i*epsilon*n11*mu_im10; + interaction_matrices[i_][0][WY] = -(i+3*delta)*mu_ip10-(2*i+3*delta)*Xg*mu_i0+i*epsilon*n20*mu_im10; + interaction_matrices[i_][0][VZ] = -A*interaction_matrices[i_][0][WY]+B*interaction_matrices[i_][0][WX]+(i+2*delta)*C*mu_i0; + interaction_matrices[i_][0][WZ] = i*mu_im11; + } -/*! - Computes interaction matrix for centered moment. Called internally. - The moment primitives must be computed before calling this. - This feature depends on: - - vpMomentGravityCenter - - vpMomentCentered -*/ -void vpFeatureMomentCentered::compute_interaction(){ + for(int j=1;j<(int)order-1;j++){ + unsigned int j_ = (unsigned int) j; + unsigned int jm1_ = j_ - 1; + unsigned int jp1_ = j_ + 1; - bool found_moment_centered; - bool found_moment_gravity; + double mu_0jm1 = momentCentered.get(0,jm1_); + double mu_0jp1 = momentCentered.get(0,jp1_); + double mu_1jm1 = momentCentered.get(1,jm1_); + double mu_0j = momentCentered.get(0,j_); + double mu_1j = momentCentered.get(1,j_); - vpMomentCentered& momentCentered= (static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + interaction_matrices[j_*order][0][VX] = -(delta)*A*mu_0j; + interaction_matrices[j_*order][0][VY] = -j*A*mu_1jm1-(j+delta)*B*mu_0j; - if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); - if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); + interaction_matrices[j_*order][0][WX] = (j+3*delta)*mu_0jp1+(2*j+3*delta)*Yg*mu_0j-j*epsilon*n02*mu_0jm1; + interaction_matrices[j_*order][0][WY] = -(j+3*delta)*mu_1j-(j+3*delta)*Xg*mu_0j-j*Yg*mu_1jm1+j*epsilon*n11*mu_0jm1; + interaction_matrices[j_*order][0][VZ] = -A*interaction_matrices[j_*order][0][WY]+B*interaction_matrices[j_*order][0][WX]+(j+2*delta)*C*mu_0j; + interaction_matrices[j_*order][0][WZ] = -j*mu_1jm1; + } - int delta; - int epsilon; - vpMomentObject& momentObject = moment->getObject(); - order = momentObject.getOrder()+1; - interaction_matrices.resize(order*order); - for (std::vector< vpMatrix >::iterator i=interaction_matrices.begin(); i!=interaction_matrices.end(); ++i) - i->resize(1,6); - if (momentObject.getType()==vpMomentObject::DISCRETE) { - delta=0; - epsilon=1; - } else { - delta=1; - epsilon=4; - } - double n11 = momentCentered.get(1,1)/momentObject.get(0,0); - double n20 = momentCentered.get(2,0)/momentObject.get(0,0); - double n02 = momentCentered.get(0,2)/momentObject.get(0,0); - double Xg = momentGravity.getXg(); - double Yg = momentGravity.getYg(); - double mu00 = momentCentered.get(0,0); - - unsigned int VX = 0; - unsigned int VY = 1; - unsigned int VZ = 2; - unsigned int WX = 3; - unsigned int WY = 4; - unsigned int WZ = 5; - - interaction_matrices[0][0][VX] = -(delta)*A*mu00; - interaction_matrices[0][0][VY] = -(delta)*B*mu00; - - // Since mu10=0 and mu01=0 - // interaction_matrices[0][0][WX] = (3*delta)*MU(0,1)+(3*delta)*Yg*mu00; - // interaction_matrices[0][0][WY] = -(3*delta)*MU(1,0)-(3*delta)*Xg*mu00; - // we get the simplification: - interaction_matrices[0][0][WX] = (3*delta)*Yg*mu00; - interaction_matrices[0][0][WY] = -(3*delta)*Xg*mu00; - interaction_matrices[0][0][VZ] = -A*interaction_matrices[0][0][WY]+B*interaction_matrices[0][0][WX]+(2*delta)*C*mu00; - interaction_matrices[0][0][WZ] = 0.; - - for (int i=1; i<(int)order-1; i++){ + for(int j=1; j<(int)order-1; j++) { + unsigned int j_ = (unsigned int) j; + unsigned int jm1_ = j_ - 1; + unsigned int jp1_ = j_ + 1; + for(int i=1; i<(int)order-j-1; i++) { unsigned int i_ = (unsigned int) i; unsigned int im1_ = i_ - 1; unsigned int ip1_ = i_ + 1; - double mu_im10 = momentCentered.get(im1_,0); - double mu_ip10 = momentCentered.get(ip1_,0); - double mu_im11 = momentCentered.get(im1_,1); - double mu_i0 = momentCentered.get(i_,0); - double mu_i1 = momentCentered.get(i_,1); - - interaction_matrices[i_][0][VX] = -(i+delta)*A*mu_i0-(i*B*mu_im11); - interaction_matrices[i_][0][VY] = -(delta)*B*mu_i0; - - interaction_matrices[i_][0][WX] = (i+3*delta)*mu_i1+(i+3*delta)*Yg*mu_i0+i*Xg*mu_im11-i*epsilon*n11*mu_im10; - interaction_matrices[i_][0][WY] = -(i+3*delta)*mu_ip10-(2*i+3*delta)*Xg*mu_i0+i*epsilon*n20*mu_im10; - interaction_matrices[i_][0][VZ] = -A*interaction_matrices[i_][0][WY]+B*interaction_matrices[i_][0][WX]+(i+2*delta)*C*mu_i0; - interaction_matrices[i_][0][WZ] = i*mu_im11; + double mu_ijm1 = momentCentered.get(i_,jm1_); + double mu_ij = momentCentered.get(i_,j_); + double mu_ijp1 = momentCentered.get(i_,jp1_); + double mu_im1j = momentCentered.get(im1_,j_); + double mu_im1jp1 = momentCentered.get(im1_,jp1_); + double mu_ip1jm1 = momentCentered.get(ip1_,jm1_); + double mu_ip1j = momentCentered.get(ip1_,j_); + + interaction_matrices[j_*order+i_][0][VX] = -(i+delta)*A*mu_ij-i*B*mu_im1jp1; + interaction_matrices[j_*order+i_][0][VY] = -j*A*mu_ip1jm1-(j+delta)*B*mu_ij; + + interaction_matrices[j_*order+i_][0][WX] = (i+j+3*delta)*mu_ijp1+(i+2*j+3*delta)*Yg*mu_ij + +i*Xg*mu_im1jp1-i*epsilon*n11*mu_im1j-j*epsilon*n02*mu_ijm1; + interaction_matrices[j_*order+i_][0][WY] = -(i+j+3*delta)*mu_ip1j-(2*i+j+3*delta)*Xg*mu_ij + -j*Yg*mu_ip1jm1+i*epsilon*n20*mu_im1j+j*epsilon*n11*mu_ijm1; + interaction_matrices[j_*order+i_][0][VZ] = -A*interaction_matrices[j_*order+i_][0][WY]+B*interaction_matrices[j_*order+i_][0][WX]+(i+j+2*delta)*C*mu_ij; + interaction_matrices[j_*order+i_][0][WZ] = i*mu_im1jp1-j*mu_ip1jm1; } + } +#endif // #ifdef VISP_MOMENTS_COMBINE_MATRICES +} - for(int j=1;j<(int)order-1;j++){ - unsigned int j_ = (unsigned int) j; - unsigned int jm1_ = j_ - 1; - unsigned int jp1_ = j_ + 1; - - double mu_0jm1 = momentCentered.get(0,jm1_); - double mu_0jp1 = momentCentered.get(0,jp1_); - double mu_1jm1 = momentCentered.get(1,jm1_); - double mu_0j = momentCentered.get(0,j_); - double mu_1j = momentCentered.get(1,j_); - - interaction_matrices[j_*order][0][VX] = -(delta)*A*mu_0j; - interaction_matrices[j_*order][0][VY] = -j*A*mu_1jm1-(j+delta)*B*mu_0j; - - interaction_matrices[j_*order][0][WX] = (j+3*delta)*mu_0jp1+(2*j+3*delta)*Yg*mu_0j-j*epsilon*n02*mu_0jm1; - interaction_matrices[j_*order][0][WY] = -(j+3*delta)*mu_1j-(j+3*delta)*Xg*mu_0j-j*Yg*mu_1jm1+j*epsilon*n11*mu_0jm1; - interaction_matrices[j_*order][0][VZ] = -A*interaction_matrices[j_*order][0][WY]+B*interaction_matrices[j_*order][0][WX]+(j+2*delta)*C*mu_0j; - interaction_matrices[j_*order][0][WZ] = -j*mu_1jm1; - } - for(int j=1; j<(int)order-1; j++) { - unsigned int j_ = (unsigned int) j; - unsigned int jm1_ = j_ - 1; - unsigned int jp1_ = j_ + 1; - for(int i=1; i<(int)order-j-1; i++) { - unsigned int i_ = (unsigned int) i; - unsigned int im1_ = i_ - 1; - unsigned int ip1_ = i_ + 1; - - double mu_ijm1 = momentCentered.get(i_,jm1_); - double mu_ij = momentCentered.get(i_,j_); - double mu_ijp1 = momentCentered.get(i_,jp1_); - double mu_im1j = momentCentered.get(im1_,j_); - double mu_im1jp1 = momentCentered.get(im1_,jp1_); - double mu_ip1jm1 = momentCentered.get(ip1_,jm1_); - double mu_ip1j = momentCentered.get(ip1_,j_); - - interaction_matrices[j_*order+i_][0][VX] = -(i+delta)*A*mu_ij-i*B*mu_im1jp1; - interaction_matrices[j_*order+i_][0][VY] = -j*A*mu_ip1jm1-(j+delta)*B*mu_ij; - - interaction_matrices[j_*order+i_][0][WX] = (i+j+3*delta)*mu_ijp1+(i+2*j+3*delta)*Yg*mu_ij - +i*Xg*mu_im1jp1-i*epsilon*n11*mu_im1j-j*epsilon*n02*mu_ijm1; - interaction_matrices[j_*order+i_][0][WY] = -(i+j+3*delta)*mu_ip1j-(2*i+j+3*delta)*Xg*mu_ij - -j*Yg*mu_ip1jm1+i*epsilon*n20*mu_im1j+j*epsilon*n11*mu_ijm1; - interaction_matrices[j_*order+i_][0][VZ] = -A*interaction_matrices[j_*order+i_][0][WY]+B*interaction_matrices[j_*order+i_][0][WX]+(i+j+2*delta)*C*mu_ij; - interaction_matrices[j_*order+i_][0][WZ] = i*mu_im1jp1-j*mu_ip1jm1; +std::ostream& operator<<(std::ostream & os, const vpFeatureMomentCentered& mu){ + vpTRACE(" << Ls - CENTRED MOMENTS >>"); + unsigned int order_m_1 = (unsigned int)(mu.order - 1); + for(unsigned int i=0; i<order_m_1; i++){ + for(unsigned int j=0; j<order_m_1-i; j++){ + os << "L_mu[" << i << "," << j << "] = "; + mu.interaction(i,j).matlabPrint(os); } - } } -#endif + return os; +} + diff --git a/src/visual-feature/vpFeatureMomentCentered.h b/src/visual-feature/vpFeatureMomentCentered.h index 80e9100cefc521b768b215a1eea085c0c4af81ac..bfb13abda7c9490d8e67536eb056938deee94c72 100644 --- a/src/visual-feature/vpFeatureMomentCentered.h +++ b/src/visual-feature/vpFeatureMomentCentered.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,7 +36,7 @@ * * Authors: * Filip Novotny - * + * Manikandan Bakthavatchalam *****************************************************************************/ /*! \file vpFeatureMomentCentered.h @@ -45,7 +45,7 @@ #ifndef __FEATUREMOMENTCENTERED_H__ #define __FEATUREMOMENTCENTERED_H__ #include <visp/vpFeatureMoment.h> -#ifdef VISP_MOMENTS_COMBINE_MATRICES +#include <visp/vpFeatureMomentBasic.h> class vpMomentDatabase; /*! \class vpFeatureMomentCentered @@ -69,56 +69,18 @@ class vpMomentDatabase; - vpMomentGravityCenter */ class VISP_EXPORT vpFeatureMomentCentered : public vpFeatureMoment{ -private: - unsigned int order; - public: - vpFeatureMomentCentered(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL); - void compute_interaction(); - /*! - Interaction matrix corresponding to \f$ \mu_{ij} \f$ moment - \param select_one : first index (i) - \param select_two : second index (j) - \return Interaction matrix corresponding to the moment - */ - vpMatrix interaction (unsigned int select_one,unsigned int select_two); - - /*! - associated moment name - */ - const char* momentName(){ return "vpMomentCentered";} - /*! - feature name - */ - const char* name(){ return "vpFeatureMomentCentered";} - -}; - -#else -class vpMomentDatabase; -/*! - \class vpFeatureMomentCentered - - \ingroup VsFeature2 - - \brief Functionality computation for centered moment feature. Computes the interaction matrix associated with vpMomentCentered. - - The interaction matrix for the feature is defined in \cite Tahri05z, equation (17). - This vpFeatureMoment, as well as it's corresponding moment primitive is double-indexed. - The interaction matrix \f$ L_{\mu_{ij}} \f$ is obtained by calling vpFeatureMomentBasic::interaction (i,j) and is associated to \f$ \mu_{ij} \f$ obtained by vpFeatureMomentCentered::get (i,j). - - vpFeatureMomentCentered computes interaction matrices all interaction matrices up to vpMomentObject::getOrder()-1. - \attention The maximum order reached by vpFeatureMomentBasic is NOT the maximum order of the vpMomentObject, it is one unit smaller. - For example if you define your vpMomentObject up to order n then vpFeatureMomentBasic will be able to compute interaction matrices up to order n-1 that is - \f$ L_{m_{ij}} \f$ with \f$ i+j<=n-1 \f$. - - This feature depends on: - - vpMomentGravityCenter - - vpMomentCentered -*/ -class VISP_EXPORT vpFeatureMomentCentered : public vpFeatureMoment{ -private: +protected: + unsigned int order; + /*! + Core computation of interaction matrix for moment m_pq + */ + vpMatrix + compute_Lmu_pq(const unsigned int& p, const unsigned int& q, const double& xg, const double& yg, + const vpMatrix& L_xg, const vpMatrix& L_yg, + const vpMomentBasic& m, const vpFeatureMomentBasic& feature_moment_m) const; + public: vpFeatureMomentCentered(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL); void compute_interaction(); @@ -129,20 +91,24 @@ private: throw vpException(vpException::functionNotImplementedError,"Not implemented!"); } #endif - - vpMatrix interaction (unsigned int select_one,unsigned int select_two); + /*! + Interaction matrix corresponding to \f$ \mu_{ij} \f$ moment + \param select_one : first index (i) + \param select_two : second index (j) + \return Interaction matrix corresponding to the moment + */ + vpMatrix interaction (unsigned int select_one,unsigned int select_two) const; /*! associated moment name */ - const char* momentName(){ return "vpMomentCentered";} + const char* momentName() const { return "vpMomentCentered";} /*! feature name */ - const char* name(){ return "vpFeatureMomentCentered";} - + const char* name() const { return "vpFeatureMomentCentered";} +friend VISP_EXPORT std::ostream & operator<<(std::ostream & os, const vpFeatureMomentCentered& v); }; #endif -#endif diff --git a/src/visual-feature/vpFeatureMomentCommon.cpp b/src/visual-feature/vpFeatureMomentCommon.cpp index 40a6e253df349e8be27dfcabc61729f4ed3e6328..7b4bf31653b49f55a721894db6543e592994153c 100644 --- a/src/visual-feature/vpFeatureMomentCommon.cpp +++ b/src/visual-feature/vpFeatureMomentCommon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureMomentCommon.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureMomentCommon.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,7 +57,7 @@ vpFeatureMomentCommon::vpFeatureMomentCommon(vpMomentDatabase& moments,double A, featureAlpha(moments,A,B,C), featureCentered(moments,A,B,C), featureMomentBasic(moments,A,B,C), - feature_moment_area(moments,A,B,C) + feature_moment_area(moments,A,B,C) { featureGravity.linkTo(*this); diff --git a/src/visual-feature/vpFeatureMomentCommon.h b/src/visual-feature/vpFeatureMomentCommon.h index 911e9cb1ccdc3d73af57bafa07960d4a01a9a2c2..4bb96ae9c8f86136038c3392110ebec44758f7a8 100644 --- a/src/visual-feature/vpFeatureMomentCommon.h +++ b/src/visual-feature/vpFeatureMomentCommon.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureMomentCommon.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpFeatureMomentCommon.h 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/visual-feature/vpFeatureMomentDatabase.cpp b/src/visual-feature/vpFeatureMomentDatabase.cpp index 908a8749bd1cb352a819dcced409233c9d31a07d..be3943425084e7e0086befbd2929a7932057f681 100644 --- a/src/visual-feature/vpFeatureMomentDatabase.cpp +++ b/src/visual-feature/vpFeatureMomentDatabase.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureMomentDatabase.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureMomentDatabase.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/visual-feature/vpFeatureMomentDatabase.h b/src/visual-feature/vpFeatureMomentDatabase.h index 747b08e6103465fb69575589c6a0a61772b4972b..f7457c57d1f98144266ef8f302b331b9224c9fe2 100644 --- a/src/visual-feature/vpFeatureMomentDatabase.h +++ b/src/visual-feature/vpFeatureMomentDatabase.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureMomentDatabase.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureMomentDatabase.h 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -162,14 +162,18 @@ class VISP_EXPORT vpFeatureMomentDatabase{ void add(vpFeatureMoment& featureMoment,char* name); public: /*! - virtual destructor. + Default constructor. + */ + vpFeatureMomentDatabase() : featureMomentsDataBase() {} + /*! + Virtual destructor that does nothing. */ virtual ~vpFeatureMomentDatabase() {} virtual void updateAll(double A=0.0, double B=0.0, double C=1.0); vpFeatureMoment& get(const char* type, bool& found); - //friend std::ostream & operator<<(ostream & os, const vpFeatureMomentDatabase& v); + friend VISP_EXPORT std::ostream & operator<<(std::ostream& os, const vpFeatureMomentDatabase& m); friend class vpFeatureMoment; }; diff --git a/src/visual-feature/vpFeatureMomentGravityCenter.cpp b/src/visual-feature/vpFeatureMomentGravityCenter.cpp index 8a8f032b53014c1c9469161abcdf744b04f6e106..425d757d60da20660ef89eaae6f016a6bdf098ef 100644 --- a/src/visual-feature/vpFeatureMomentGravityCenter.cpp +++ b/src/visual-feature/vpFeatureMomentGravityCenter.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,8 +63,8 @@ void vpFeatureMomentGravityCenter::compute_interaction(){ bool found_featuremoment_basic; - vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMoments->get("vpFeatureMomentBasic",found_featuremoment_basic))); - vpMomentObject& momentObject = moment->getObject(); + vpFeatureMomentBasic& featureMomentBasic= (static_cast<vpFeatureMomentBasic&>(featureMomentsDataBase->get("vpFeatureMomentBasic",found_featuremoment_basic))); + const vpMomentObject& momentObject = moment->getObject(); if(!found_featuremoment_basic) throw vpException(vpException::notInitialized,"vpFeatureMomentBasic not found"); @@ -104,11 +104,11 @@ void vpFeatureMomentGravityCenter::compute_interaction(){ bool found_moment_centered; bool found_moment_gravity; - vpMomentCentered& momentCentered= (static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentCentered& momentCentered= (static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered))); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpMomentObject& momentObject = moment->getObject(); + const vpMomentObject& momentObject = moment->getObject(); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); diff --git a/src/visual-feature/vpFeatureMomentGravityCenter.h b/src/visual-feature/vpFeatureMomentGravityCenter.h index 6cdb1da1387b58932fcd067d218e0a1e1c711e25..e79af85c957a01e99e9e558bb721a45f1f8c28e0 100644 --- a/src/visual-feature/vpFeatureMomentGravityCenter.h +++ b/src/visual-feature/vpFeatureMomentGravityCenter.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -147,24 +147,24 @@ class VISP_EXPORT vpFeatureMomentGravityCenter: public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param database : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentGravityCenter(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,2) + vpFeatureMomentGravityCenter(vpMomentDatabase& database,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) + : vpFeatureMoment(database,A_,B_,C_,featureMoments,2) {} void compute_interaction(); /*! Associated moment name. */ - const char* momentName(){ return "vpMomentGravityCenter";} + const char* momentName() const { return "vpMomentGravityCenter";} /*! Feature name. */ - const char* name(){ return "vpFeatureMomentGravityCenter";} + const char* name() const { return "vpFeatureMomentGravityCenter";} /*! Shortcut selector for \f$x_g\f$. @@ -208,24 +208,24 @@ class VISP_EXPORT vpFeatureMomentGravityCenter: public vpFeatureMoment{ public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentGravityCenter(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,2) + vpFeatureMomentGravityCenter(vpMomentDatabase& data_base,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) : + vpFeatureMoment(data_base,A_,B_,C_,featureMoments,2) {} void compute_interaction(); /*! Associated moment name. */ - const char* momentName(){ return "vpMomentGravityCenter";} + const char* momentName() const { return "vpMomentGravityCenter";} /*! Feature name. */ - const char* name(){ return "vpFeatureMomentGravityCenter";} + const char* name() const { return "vpFeatureMomentGravityCenter";} /*! Shortcut selector for \f$x_g\f$. diff --git a/src/visual-feature/vpFeatureMomentGravityCenterNormalized.cpp b/src/visual-feature/vpFeatureMomentGravityCenterNormalized.cpp index b048413fb23519698ffb37803e60737fa1acabf8..88b590576726bd64f8ac59b1842fe2eb7b43e08d 100644 --- a/src/visual-feature/vpFeatureMomentGravityCenterNormalized.cpp +++ b/src/visual-feature/vpFeatureMomentGravityCenterNormalized.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.cpp 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,10 +71,10 @@ void vpFeatureMomentGravityCenterNormalized::compute_interaction(){ bool found_featuremoment_gravity; bool found_featuremoment_surfacenormalized; - vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpFeatureMomentGravityCenter& featureMomentGravity = (static_cast<vpFeatureMomentGravityCenter&>(featureMoments->get("vpFeatureMomentGravityCenter",found_featuremoment_gravity))); - vpFeatureMomentAreaNormalized featureMomentAreaNormalized = (static_cast<vpFeatureMomentAreaNormalized&>(featureMoments->get("vpFeatureMomentAreaNormalized",found_featuremoment_surfacenormalized))); + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + vpFeatureMomentGravityCenter& featureMomentGravity = (static_cast<vpFeatureMomentGravityCenter&>(featureMomentsDataBase->get("vpFeatureMomentGravityCenter",found_featuremoment_gravity))); + vpFeatureMomentAreaNormalized featureMomentAreaNormalized = (static_cast<vpFeatureMomentAreaNormalized&>(featureMomentsDataBase->get("vpFeatureMomentAreaNormalized",found_featuremoment_surfacenormalized))); if(!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); @@ -117,15 +117,15 @@ void vpFeatureMomentGravityCenterNormalized::compute_interaction(){ bool found_moment_gravity; bool found_moment_centered; - vpMomentCentered& momentCentered = static_cast<vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); - vpMomentGravityCenter& momentGravity = static_cast<vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); - vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); + const vpMomentCentered& momentCentered = static_cast<const vpMomentCentered&>(moments.get("vpMomentCentered",found_moment_centered)); + const vpMomentGravityCenter& momentGravity = static_cast<const vpMomentGravityCenter&>(moments.get("vpMomentGravityCenter",found_moment_gravity)); + const vpMomentAreaNormalized& momentSurfaceNormalized = static_cast<const vpMomentAreaNormalized&>(moments.get("vpMomentAreaNormalized",found_moment_surface_normalized)); if(!found_moment_surface_normalized) throw vpException(vpException::notInitialized,"vpMomentAreaNormalized not found"); if(!found_moment_gravity) throw vpException(vpException::notInitialized,"vpMomentGravityCenter not found"); if(!found_moment_centered) throw vpException(vpException::notInitialized,"vpMomentCentered not found"); - vpMomentObject& momentObject = moment->getObject(); + const vpMomentObject& momentObject = moment->getObject(); interaction_matrices[0].resize(1,6); interaction_matrices[1].resize(1,6); diff --git a/src/visual-feature/vpFeatureMomentGravityCenterNormalized.h b/src/visual-feature/vpFeatureMomentGravityCenterNormalized.h index e66685e221bb1d798b4b31c861780a287db00dea..f6a657e0670d1d75f7c463c6162a0bd3d191c1d2 100644 --- a/src/visual-feature/vpFeatureMomentGravityCenterNormalized.h +++ b/src/visual-feature/vpFeatureMomentGravityCenterNormalized.h @@ -3,7 +3,7 @@ * $Id: vpFeatureMomentImpl.h 3317 2011-09-06 14:14:47Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -83,25 +83,25 @@ class VISP_EXPORT vpFeatureMomentGravityCenterNormalized : public vpFeatureMomen public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param database : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentGravityCenterNormalized(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,2) + vpFeatureMomentGravityCenterNormalized(vpMomentDatabase& database,double A_, double B_, double C_,vpFeatureMomentDatabase* featureMoments=NULL) + : vpFeatureMoment(database,A_,B_,C_,featureMoments,2) {} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentGravityCenterNormalized";} + const char* momentName() const { return "vpMomentGravityCenterNormalized";} /*! feature name */ - const char* name(){ return "vpFeatureMomentGravityCenterNormalized";} + const char* name() const { return "vpFeatureMomentGravityCenterNormalized";} /*! Shortcut selector for \f$x_n\f$. @@ -219,25 +219,26 @@ class VISP_EXPORT vpFeatureMomentGravityCenterNormalized : public vpFeatureMomen public: /*! Initializes the feature with information about the database of moment primitives, the object plane and feature database. - \param moments : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. - \param A : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param B : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. - \param C : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param data_base : Moment database. The database of moment primitives (first parameter) is mandatory. It is used to access different moment values later used to compute the final matrix. + \param A_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param B_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. + \param C_ : Plane coefficient in a \f$ A \times x+B \times y + C = \frac{1}{Z} \f$ plane. \param featureMoments : Feature database. */ - vpFeatureMomentGravityCenterNormalized(vpMomentDatabase& moments,double A, double B, double C,vpFeatureMomentDatabase* featureMoments=NULL) : - vpFeatureMoment(moments,A,B,C,featureMoments,2) + vpFeatureMomentGravityCenterNormalized(vpMomentDatabase& data_base,double A_, double B_, double C_, + vpFeatureMomentDatabase* featureMoments=NULL) + : vpFeatureMoment(data_base,A_,B_,C_,featureMoments,2) {} void compute_interaction(); /*! associated moment name */ - const char* momentName(){ return "vpMomentGravityCenterNormalized";} + const char* momentName() const { return "vpMomentGravityCenterNormalized";} /*! feature name */ - const char* name(){ return "vpFeatureMomentGravityCenterNormalized";} + const char* name() const { return "vpFeatureMomentGravityCenterNormalized";} /*! Shortcut selector for \f$x_n\f$. diff --git a/src/visual-feature/vpFeaturePoint.cpp b/src/visual-feature/vpFeaturePoint.cpp index c2b590b16f7de7555650c031823dc00b59e3d5f0..0e216dac340e2c8808bf39f3851403c81f970eb0 100644 --- a/src/visual-feature/vpFeaturePoint.cpp +++ b/src/visual-feature/vpFeaturePoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeaturePoint.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -89,13 +89,12 @@ vpFeaturePoint::init() //default value Z (1 meters) Z = 1; - } /*! Default constructor that build a visual feature. */ -vpFeaturePoint::vpFeaturePoint() : vpBasicFeature() +vpFeaturePoint::vpFeaturePoint() : Z(1.) { init() ; } @@ -104,12 +103,12 @@ vpFeaturePoint::vpFeaturePoint() : vpBasicFeature() /*! Set the value of \f$ Z \f$ which represents the depth in the 3D camera frame. - \param Z : \f$ Z \f$ value to set. + \param Z_ : \f$ Z \f$ value to set. */ void -vpFeaturePoint::set_Z(const double Z) +vpFeaturePoint::set_Z(const double Z_) { - this->Z = Z ; + this->Z = Z_ ; flags[2] = true; } @@ -185,18 +184,18 @@ vpFeaturePoint::get_y() const parameters of the visual feature \f$ s \f$. \f$ Z \f$ is the 3D coordinate in the camera frame representing the depth. - \param x : \f$ x \f$ value to set. - \param y : \f$ y \f$ value to set. - \param Z : \f$ Z \f$ value to set. + \param x_ : \f$ x \f$ value to set. + \param y_ : \f$ y \f$ value to set. + \param Z_ : \f$ Z \f$ value to set. */ void -vpFeaturePoint::set_xyZ(const double x, - const double y, - const double Z) +vpFeaturePoint::set_xyZ(const double x_, + const double y_, + const double Z_) { - set_x(x) ; - set_y(y) ; - set_Z(Z) ; + set_x(x_) ; + set_y(y_) ; + set_Z(Z_) ; for(unsigned int i = 0; i < nbParameters; i++) flags[i] = true; } @@ -275,23 +274,23 @@ vpFeaturePoint::interaction(const unsigned int select) resetFlags(); } - double x = get_x() ; - double y = get_y() ; - double Z = get_Z() ; + double x_ = get_x() ; + double y_ = get_y() ; + double Z_ = get_Z() ; - if (Z < 0) + if (Z_ < 0) { vpERROR_TRACE("Point is behind the camera ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point is behind the camera ")) ; } - if (fabs(Z) < 1e-6) + if (fabs(Z_) < 1e-6) { vpERROR_TRACE("Point Z coordinates is null ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point Z coordinates is null")) ; @@ -301,12 +300,12 @@ vpFeaturePoint::interaction(const unsigned int select) { vpMatrix Lx(1,6) ; Lx = 0; - Lx[0][0] = -1/Z ; + Lx[0][0] = -1/Z_ ; Lx[0][1] = 0 ; - Lx[0][2] = x/Z ; - Lx[0][3] = x*y ; - Lx[0][4] = -(1+x*x) ; - Lx[0][5] = y ; + Lx[0][2] = x_/Z_ ; + Lx[0][3] = x_*y_ ; + Lx[0][4] = -(1+x_*x_) ; + Lx[0][5] = y_ ; L = vpMatrix::stackMatrices(L,Lx) ; } @@ -316,11 +315,11 @@ vpFeaturePoint::interaction(const unsigned int select) vpMatrix Ly(1,6) ; Ly = 0; Ly[0][0] = 0 ; - Ly[0][1] = -1/Z ; - Ly[0][2] = y/Z ; - Ly[0][3] = 1+y*y ; - Ly[0][4] = -x*y ; - Ly[0][5] = -x ; + Ly[0][1] = -1/Z_ ; + Ly[0][2] = y_/Z ; + Ly[0][3] = 1+y_*y_ ; + Ly[0][4] = -x_*y_ ; + Ly[0][5] = -x_ ; L = vpMatrix::stackMatrices(L,Ly) ; } @@ -435,32 +434,32 @@ vpFeaturePoint::print(const unsigned int select ) const See the vpFeaturePoint class description for more details about \f$ x \f$ and \f$ y \f$. - \param x : The \f$ x \f$ parameter. - \param y : The \f$ y \f$ parameter. - \param Z : The \f$ Z \f$ parameter. + \param x_ : The \f$ x \f$ parameter. + \param y_ : The \f$ y \f$ parameter. + \param Z_ : The \f$ Z \f$ parameter. */ void -vpFeaturePoint::buildFrom(const double x, const double y, const double Z) +vpFeaturePoint::buildFrom(const double x_, const double y_, const double Z_) { - s[0] = x ; - s[1] = y ; + s[0] = x_ ; + s[1] = y_ ; - this->Z = Z ; + this->Z = Z_ ; - if (Z < 0) + if (Z_ < 0) { vpERROR_TRACE("Point is behind the camera ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point is behind the camera ")) ; } - if (fabs(Z) < 1e-6) + if (fabs(Z_) < 1e-6) { vpERROR_TRACE("Point Z coordinates is null ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point Z coordinates is null")) ; diff --git a/src/visual-feature/vpFeaturePoint.h b/src/visual-feature/vpFeaturePoint.h index 4e56541fd549fd4863943c4f7c7964d3aeeda581..932f08a80382fbd3d95342c7d0cff01610cd4811 100644 --- a/src/visual-feature/vpFeaturePoint.h +++ b/src/visual-feature/vpFeaturePoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePoint.h 4233 2013-05-02 13:46:42Z fspindle $ + * $Id: vpFeaturePoint.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -197,7 +197,7 @@ public: vpFeaturePoint() ; //! Destructor. - virtual ~vpFeaturePoint() { if (flags != NULL) delete [] flags; } + virtual ~vpFeaturePoint() {} /* diff --git a/src/visual-feature/vpFeaturePoint3D.cpp b/src/visual-feature/vpFeaturePoint3D.cpp index 72073ded50139931ac7baed93c20049b835fef52..3cea622347c2b44623289840797b226c226bec42 100644 --- a/src/visual-feature/vpFeaturePoint3D.cpp +++ b/src/visual-feature/vpFeaturePoint3D.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePoint3D.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeaturePoint3D.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,7 +96,7 @@ vpFeaturePoint3D::init() initialize it to \f${\bf X} = (0, 0, 1)\f$. */ -vpFeaturePoint3D::vpFeaturePoint3D() : vpBasicFeature() +vpFeaturePoint3D::vpFeaturePoint3D() { init() ; } diff --git a/src/visual-feature/vpFeaturePoint3D.h b/src/visual-feature/vpFeaturePoint3D.h index 0fbb526ad4d61afaa551e199191fa3d615423398..b398d3767f2c56767d1b7f86176cd3864081cfc1 100644 --- a/src/visual-feature/vpFeaturePoint3D.h +++ b/src/visual-feature/vpFeaturePoint3D.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePoint3D.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeaturePoint3D.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -223,7 +223,7 @@ public: // basic constructor vpFeaturePoint3D() ; //! Destructor. Does nothing. - virtual ~vpFeaturePoint3D() { if (flags != NULL) delete [] flags; } + virtual ~vpFeaturePoint3D() {} /* /section Set coordinates diff --git a/src/visual-feature/vpFeaturePointPolar.cpp b/src/visual-feature/vpFeaturePointPolar.cpp index f792b11943fcb6e447272524b9e4802f1d33a60f..d6775f9839f82f456f403629eb4b0cf6b8d4d92f 100644 --- a/src/visual-feature/vpFeaturePointPolar.cpp +++ b/src/visual-feature/vpFeaturePointPolar.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePointPolar.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeaturePointPolar.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -108,7 +108,7 @@ vpFeaturePointPolar::init() matrix (see interaction()) is initialized to \f$Z=1\f$. */ -vpFeaturePointPolar::vpFeaturePointPolar() : vpBasicFeature() +vpFeaturePointPolar::vpFeaturePointPolar() : Z(1.) { init() ; } @@ -142,9 +142,9 @@ vpFeaturePointPolar::set_theta(const double theta) */ void -vpFeaturePointPolar::set_Z(const double Z) +vpFeaturePointPolar::set_Z(const double Z_) { - this->Z = Z ; + this->Z = Z_ ; flags[2] = true; } @@ -154,18 +154,18 @@ vpFeaturePointPolar::set_Z(const double Z) \param rho, theta : Polar coordinates \f$(\rho,\theta)\f$ of the image point. - \param Z : 3D depth of the point in the camera frame. + \param Z_ : 3D depth of the point in the camera frame. \sa set_rho(), set_theta(), set_Z() */ void vpFeaturePointPolar::set_rhoThetaZ(const double rho, const double theta, - const double Z) + const double Z_) { set_rho(rho) ; set_theta(theta) ; - set_Z(Z) ; + set_Z(Z_) ; for(unsigned int i = 0; i < nbParameters; i++) flags[i] = true; } @@ -311,10 +311,10 @@ vpFeaturePointPolar::interaction(const unsigned int select) double rho = get_rho() ; double theta = get_theta() ; - double Z = get_Z() ; + double Z_ = get_Z() ; - double c = cos(theta); - double s = sin(theta); + double c_ = cos(theta); + double s_ = sin(theta); double rho2 = rho*rho; @@ -326,20 +326,19 @@ vpFeaturePointPolar::interaction(const unsigned int select) "rho polar coordinate of the point is null")) ; } - - if (Z < 0) + if (Z_ < 0) { vpERROR_TRACE("Point is behind the camera ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point is behind the camera ")) ; } - if (fabs(Z) < 1e-6) + if (fabs(Z_) < 1e-6) { vpERROR_TRACE("Point Z coordinates is null ") ; - std::cout <<"Z = " << Z << std::endl ; + std::cout <<"Z = " << Z_ << std::endl ; throw(vpFeatureException(vpFeatureException::badInitializationError, "Point Z coordinates is null")) ; @@ -349,11 +348,11 @@ vpFeaturePointPolar::interaction(const unsigned int select) { vpMatrix Lrho(1,6) ; Lrho = 0; - Lrho[0][0] = -c/Z ; - Lrho[0][1] = -s/Z ; - Lrho[0][2] = rho/Z ; - Lrho[0][3] = (1+rho2)*s ; - Lrho[0][4] = -(1+rho2)*c ; + Lrho[0][0] = -c_/Z_ ; + Lrho[0][1] = -s_/Z_ ; + Lrho[0][2] = rho/Z_ ; + Lrho[0][3] = (1+rho2)*s_ ; + Lrho[0][4] = -(1+rho2)*c_ ; Lrho[0][5] = 0 ; // printf("Lrho: rho %f theta %f Z %f\n", rho, theta, Z); @@ -366,11 +365,11 @@ vpFeaturePointPolar::interaction(const unsigned int select) { vpMatrix Ltheta(1,6) ; Ltheta = 0; - Ltheta[0][0] = s/(rho*Z) ; - Ltheta[0][1] = -c/(rho*Z) ; + Ltheta[0][0] = s_/(rho*Z_) ; + Ltheta[0][1] = -c_/(rho*Z_) ; Ltheta[0][2] = 0 ; - Ltheta[0][3] = c/rho ; - Ltheta[0][4] = s/rho ; + Ltheta[0][3] = c_/rho ; + Ltheta[0][4] = s_/rho ; Ltheta[0][5] = -1 ; // printf("Ltheta: rho %f theta %f Z %f\n", rho, theta, Z); @@ -513,7 +512,7 @@ vpFeaturePointPolar::print(const unsigned int select ) const \param rho, theta : Polar coordinates \f$(\rho,\theta)\f$ of the image point. - \param Z : 3D depth of the point in the camera frame. + \param Z_ : 3D depth of the point in the camera frame. \exception vpFeatureException::badInitializationError: If the depth (\f$Z\f$ coordinate) is negative. That means that the 3D point is @@ -525,13 +524,13 @@ vpFeaturePointPolar::print(const unsigned int select ) const */ void vpFeaturePointPolar::buildFrom(const double rho, const double theta, - const double Z) + const double Z_) { s[0] = rho ; s[1] = theta ; - this->Z = Z ; + this->Z = Z_ ; if (Z < 0) { diff --git a/src/visual-feature/vpFeaturePointPolar.h b/src/visual-feature/vpFeaturePointPolar.h index 0be5c89151689d71088d73f515ed98feeb1c2342..dc3e8118a1a0f4d551f03065ece8ad4fef06ec86 100644 --- a/src/visual-feature/vpFeaturePointPolar.h +++ b/src/visual-feature/vpFeaturePointPolar.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeaturePointPolar.h 4276 2013-06-25 12:36:48Z fspindle $ + * $Id: vpFeaturePointPolar.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -276,7 +276,7 @@ public: // basic constructor vpFeaturePointPolar() ; //! Destructor. Does nothing. - virtual ~vpFeaturePointPolar() { if (flags != NULL) delete [] flags; } + virtual ~vpFeaturePointPolar() { } void buildFrom(const double rho, const double theta, const double Z) ; diff --git a/src/visual-feature/vpFeatureSegment.cpp b/src/visual-feature/vpFeatureSegment.cpp index 2892577089f5e52cbb7e2207d7ef127a569ba2ac..7ba11dd8222d9e7c6e5e038a720be67d0cb9716e 100644 --- a/src/visual-feature/vpFeatureSegment.cpp +++ b/src/visual-feature/vpFeatureSegment.cpp @@ -3,7 +3,7 @@ * $Id: vpFeatureThetaU.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,7 +62,6 @@ \brief class that defines the vpFeatureSegment visual feature */ - /*! Initialise the memory space requested for segment visual @@ -89,11 +88,9 @@ vpFeatureSegment::init() \param normalized : If true, use normalized features \f${\bf s} = (x_n, y_n, l_n, \alpha)\f$. If false, use non normalized features \f${\bf s} = (x_c, y_c, l_c, \alpha)\f$. */ -vpFeatureSegment::vpFeatureSegment(bool normalized): - vpBasicFeature() +vpFeatureSegment::vpFeatureSegment(bool normalized) + : xc_(0), yc_(0), l_(0), alpha_(0), Z1_(0), Z2_(0), cos_a_(0), sin_a_(0), normalized_(normalized) { - this->normalized_ = normalized; - init(); } diff --git a/src/visual-feature/vpFeatureSegment.h b/src/visual-feature/vpFeatureSegment.h index fc6d931bb63578054a7695e82050784a34b4124a..5463d2aeac4c3ae35ed539e7bace87691af7e22a 100644 --- a/src/visual-feature/vpFeatureSegment.h +++ b/src/visual-feature/vpFeatureSegment.h @@ -3,7 +3,7 @@ * $Id: vpFeatureThetaU.h 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -80,7 +80,7 @@ public: vpFeatureSegment(bool normalized=false); //! Destructor. Does nothing. - ~vpFeatureSegment() { if (flags != NULL) delete [] flags; } + ~vpFeatureSegment() {} // change values of the segment void buildFrom(const double x1, const double y1, const double Z1, const double x2, const double y2, const double Z2); diff --git a/src/visual-feature/vpFeatureThetaU.cpp b/src/visual-feature/vpFeatureThetaU.cpp index 728948ac362034eda25fa915668e9e266766f726..cfee61e418b6779bdc8c7ccfb0909e084c58bb9b 100644 --- a/src/visual-feature/vpFeatureThetaU.cpp +++ b/src/visual-feature/vpFeatureThetaU.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureThetaU.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureThetaU.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -91,8 +91,8 @@ vpFeatureThetaU::init() visual feature. */ -vpFeatureThetaU::vpFeatureThetaU(vpFeatureThetaURotationRepresentationType r): - vpBasicFeature() +vpFeatureThetaU::vpFeatureThetaU(vpFeatureThetaURotationRepresentationType r) + : rotation(r) { //vpTRACE("0x%x", this); init() ; @@ -121,15 +121,12 @@ vpFeatureThetaU::vpFeatureThetaU(vpFeatureThetaURotationRepresentationType r): */ vpFeatureThetaU::vpFeatureThetaU(vpThetaUVector &tu, - vpFeatureThetaURotationRepresentationType r): - vpBasicFeature() + vpFeatureThetaURotationRepresentationType r) + : rotation(r) { init() ; buildFrom(tu) ; - - // kind of rotation representation - rotation = r; } /*! @@ -150,16 +147,13 @@ vpFeatureThetaU::vpFeatureThetaU(vpThetaUVector &tu, */ vpFeatureThetaU::vpFeatureThetaU(vpRotationMatrix &R, - vpFeatureThetaURotationRepresentationType r): - vpBasicFeature() + vpFeatureThetaURotationRepresentationType r) + : rotation(r) { init() ; vpThetaUVector tu(R) ; buildFrom(tu) ; - - // kind of rotation representation - rotation = r; } /*! @@ -182,17 +176,14 @@ vpFeatureThetaU::vpFeatureThetaU(vpRotationMatrix &R, */ vpFeatureThetaU::vpFeatureThetaU(vpHomogeneousMatrix &M, - vpFeatureThetaURotationRepresentationType r): - vpBasicFeature() + vpFeatureThetaURotationRepresentationType r) + : rotation(r) { init() ; vpRotationMatrix R ; M.extract(R) ; vpThetaUVector tu(R) ; buildFrom(tu) ; - - // kind of rotation representation - rotation = r; } /*! diff --git a/src/visual-feature/vpFeatureThetaU.h b/src/visual-feature/vpFeatureThetaU.h index 97c11da0875cd2e008d1e1892fc3ed8170746d74..aa4a8b93269a76852ee9425642f5fdf2739ee339 100644 --- a/src/visual-feature/vpFeatureThetaU.h +++ b/src/visual-feature/vpFeatureThetaU.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureThetaU.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureThetaU.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -271,9 +271,8 @@ public: // build from an homogeneous matrix void buildFrom(const vpHomogeneousMatrix &M) ; - //! Destructor. Does nothing. - virtual ~vpFeatureThetaU() { if (flags != NULL) delete [] flags; /*vpTRACE("0x%x", this)*/ ;} + virtual ~vpFeatureThetaU() {} public: diff --git a/src/visual-feature/vpFeatureTranslation.cpp b/src/visual-feature/vpFeatureTranslation.cpp index 616e7f7f0b0ee363e4bee3afa42c15a5840c7767..7b7031b284fd309194ace4cacfea177878a5574d 100644 --- a/src/visual-feature/vpFeatureTranslation.cpp +++ b/src/visual-feature/vpFeatureTranslation.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureTranslation.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureTranslation.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -92,11 +92,10 @@ vpFeatureTranslation::init() \param r : Type of considered 3D translation feature. */ -vpFeatureTranslation::vpFeatureTranslation(vpFeatureTranslationRepresentationType r) : vpBasicFeature() +vpFeatureTranslation::vpFeatureTranslation(vpFeatureTranslationRepresentationType r) + : f2Mf1(), translation(r) { init() ; - - translation = r; } @@ -105,31 +104,30 @@ vpFeatureTranslation::vpFeatureTranslation(vpFeatureTranslationRepresentationTyp Constructor that build a 3D visual feature from an homogeneous matrix \f$ ^{{\cal{F}}_2}M_{{\cal{F}}_1} \f$ that represent the 3D transformation between two frames \f${\cal{F}}_1\f$ and \f${\cal{F}}_2\f$. - \param f2Mf1 [in] : 3D displacement that the camera has to achieve to + \param f2Mf1_ [in] : 3D displacement that the camera has to achieve to move from the frame \f${\cal{F}}_2\f$ to the frame \f${\cal{F}}_1\f$ (\f$ ^{{\cal{F}}_2}M_{{\cal{F}}_1} \f$). \param r : type of feature. It can be vpFeature::cdMc or vpFeature::cMo. */ -vpFeatureTranslation::vpFeatureTranslation(vpHomogeneousMatrix &f2Mf1, vpFeatureTranslationRepresentationType r) : vpBasicFeature() +vpFeatureTranslation::vpFeatureTranslation(vpHomogeneousMatrix &f2Mf1_, vpFeatureTranslationRepresentationType r) + : f2Mf1(), translation(r) { init() ; - translation = r; - - buildFrom(f2Mf1) ; + buildFrom(f2Mf1_) ; } /*! Build a 3D translation visual feature from an homogeneous matrix \f$ ^{{\cal{F}}_2}M_{{\cal{F}}_1} \f$ that represent the 3D transformation between two frames \f${\cal{F}}_1\f$ and \f${\cal{F}}_2\f$. - \param f2Mf1 [in] : 3D displacement that the camera has to achieve to + \param f2Mf1_ [in] : 3D displacement that the camera has to achieve to move from the frame \f${\cal{F}}_2\f$ to the frame \f${\cal{F}}_1\f$ (\f$ ^{{\cal{F}}_2}M_{{\cal{F}}_1} \f$). */ void -vpFeatureTranslation::buildFrom(const vpHomogeneousMatrix &f2Mf1) +vpFeatureTranslation::buildFrom(const vpHomogeneousMatrix &f2Mf1_) { - this->f2Mf1 = f2Mf1 ; + this->f2Mf1 = f2Mf1_ ; s[0] = f2Mf1[0][3] ; s[1] = f2Mf1[1][3] ; s[2] = f2Mf1[2][3] ; diff --git a/src/visual-feature/vpFeatureTranslation.h b/src/visual-feature/vpFeatureTranslation.h index 9a636cb27553bb3550d1019311188eb110763d8d..4ecacd4250116d7f3b05261059da4f336db24cc0 100644 --- a/src/visual-feature/vpFeatureTranslation.h +++ b/src/visual-feature/vpFeatureTranslation.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureTranslation.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureTranslation.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -306,7 +306,7 @@ public: // cdMc is the displacement that the camera has to realize vpFeatureTranslation(vpHomogeneousMatrix &f2Mf1, vpFeatureTranslationRepresentationType r) ; //! Destructor. Does nothing. - virtual ~vpFeatureTranslation() { if (flags != NULL) delete [] flags; } + virtual ~vpFeatureTranslation() {} // build from an homogeneous matrix // cdMc is the displacement that the camera has to realize diff --git a/src/visual-feature/vpFeatureVanishingPoint.cpp b/src/visual-feature/vpFeatureVanishingPoint.cpp index 287980279c101c2120862140df16891636e6bc00..c3cd4c4a5829d348046e57bb24361c60fb48d970 100644 --- a/src/visual-feature/vpFeatureVanishingPoint.cpp +++ b/src/visual-feature/vpFeatureVanishingPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureVanishingPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureVanishingPoint.cpp 4632 2014-02-03 17:06:40Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,7 +77,7 @@ vpFeatureVanishingPoint::init() //set_Z(1) ; } -vpFeatureVanishingPoint::vpFeatureVanishingPoint() : vpBasicFeature() +vpFeatureVanishingPoint::vpFeatureVanishingPoint() { init() ; } diff --git a/src/visual-feature/vpFeatureVanishingPoint.h b/src/visual-feature/vpFeatureVanishingPoint.h index a8467bcf0e6800b36eb61002689d67b458e79af5..d3c0169f749ef34fcfaed8ce2584311d514b6a77 100644 --- a/src/visual-feature/vpFeatureVanishingPoint.h +++ b/src/visual-feature/vpFeatureVanishingPoint.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpFeatureVanishingPoint.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpFeatureVanishingPoint.h 5237 2015-01-30 13:52:04Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -82,12 +82,12 @@ public: //no Z required public: - //! basic construction + //! Default initialization. void init() ; - //! basic constructor + //! Default constructor. vpFeatureVanishingPoint() ; - //! destructor - virtual ~vpFeatureVanishingPoint() { if (flags != NULL) delete [] flags; } + //! Destructor. + virtual ~vpFeatureVanishingPoint() {} public: diff --git a/src/visual-feature/vpGenericFeature.cpp b/src/visual-feature/vpGenericFeature.cpp index f4d2a3186d1c29b631d3eee01a8552be620ba099..0144fb5f016fd1c5f4adeae35b7cd0742cc8fe04 100644 --- a/src/visual-feature/vpGenericFeature.cpp +++ b/src/visual-feature/vpGenericFeature.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpGenericFeature.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpGenericFeature.cpp 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -81,6 +81,7 @@ void vpGenericFeature::init() */ vpGenericFeature::vpGenericFeature() + : L(), err(), errorStatus(errorNotInitalized) { /* vpERROR_TRACE("You are not allow to use this constructor ") ; @@ -96,28 +97,28 @@ vpGenericFeature::vpGenericFeature() /*! Constructor of the class you have to use. The feature table is initilialized with the good dimension. - \param dim_s : Dimension of the feature. It corresponds to the number of features you want to create. + \param dimension_gen_s : Dimension of the generic feature. It corresponds to the number of features you want to create. */ -vpGenericFeature::vpGenericFeature(unsigned int dim_s) +vpGenericFeature::vpGenericFeature(unsigned int dimension_gen_s) + : L(), err(), errorStatus(errorNotInitalized) { - this->dim_s = dim_s ; - s.resize(dim_s) ; - errorStatus = errorNotInitalized ; + this->dim_s = dimension_gen_s ; + s.resize(dimension_gen_s) ; } /*! Set the error vector \f$(s-s*)\f$. - \param error : Error vector \f$(s-s*)\f$. + \param error_vector : Error vector \f$(s-s*)\f$. \exception vpFeatureException::sizeMismatchError : If the size of the error vector is bad. */ void -vpGenericFeature::setError(vpColVector &error) +vpGenericFeature::setError(const vpColVector &error_vector) { - if (error.getRows() != dim_s) + if (error_vector.getRows() != dim_s) { vpERROR_TRACE("size mismatch between error dimension" "and feature dimension"); @@ -127,7 +128,7 @@ vpGenericFeature::setError(vpColVector &error) } errorStatus = errorInitialized ; - err = error ; + err = error_vector ; } @@ -448,80 +449,74 @@ vpGenericFeature::interaction(const unsigned int select) /*! \brief set the value of the interaction matrix. - \param L : The matrix corresponding to the interaction matrix you computed. + \param L_ : The matrix corresponding to the interaction matrix you computed. \exception an exception is thrown if the number of row of the interaction matrix is different from the dimension of the visual feature as specified in the constructor */ void -vpGenericFeature::setInteractionMatrix(const vpMatrix &L) +vpGenericFeature::setInteractionMatrix(const vpMatrix &L_) { - - if (L.getRows() != dim_s) + if (L_.getRows() != dim_s) { - std::cout << L.getRows() <<" " << dim_s << std::endl ;; + std::cout << L_.getRows() <<" " << dim_s << std::endl ;; vpERROR_TRACE("size mismatch between interaction matrix size " "and feature dimension"); throw(vpFeatureException(vpFeatureException::sizeMismatchError, "size mismatch between interaction matrix size " "and feature dimension")); - } - this->L = L ; - + this->L = L_ ; } /*! \brief set the value of all the features. - \param s : It is a vector containing the value of the visual features. + \param s_vector : It is a vector containing the value of the visual features. \exception an exception is thrown if the number of row of the vector s is different from the dimension of the visual feature as specified in the constructor */ void -vpGenericFeature::set_s(const vpColVector &s) +vpGenericFeature::set_s(const vpColVector &s_vector) { - if (s.getRows() != dim_s) + if (s_vector.getRows() != dim_s) { vpERROR_TRACE("size mismatch between s dimension" "and feature dimension"); throw(vpFeatureException(vpFeatureException::sizeMismatchError, "size mismatch between s dimension" "and feature dimension")); - } - this->s = s ; + this->s = s_vector ; } /*! \brief get the value of all the features. - \param s : It is a vector which will contain the value of the visual features. + \param s_vector : It is a vector which will contain the value of the visual features. \exception an exception is thrown if the number of row of the vector s is different from the dimension of the visual feature as specified in the constructor */ void -vpGenericFeature::get_s(vpColVector &s) const +vpGenericFeature::get_s(vpColVector &s_vector) const { - - if (s.getRows() != dim_s) + if (s_vector.getRows() != dim_s) { vpERROR_TRACE("size mismatch between s dimension" "and feature dimension"); throw(vpFeatureException(vpFeatureException::sizeMismatchError, "size mismatch between s dimension" "and feature dimension")); - } - s = this->s ; + s_vector = this->s ; } diff --git a/src/visual-feature/vpGenericFeature.h b/src/visual-feature/vpGenericFeature.h index 656519490c0aea29c7465cc49a7c86063e9fc04e..ffbccadcf24a89c4ab7fdf9a87a68ea9c355b2b0 100644 --- a/src/visual-feature/vpGenericFeature.h +++ b/src/visual-feature/vpGenericFeature.h @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: vpGenericFeature.h 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: vpGenericFeature.h 4649 2014-02-07 14:57:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -209,7 +209,7 @@ private: public: void setInteractionMatrix(const vpMatrix &L) ; vpMatrix getInteractionMatrix() const { return L ; } - void setError(vpColVector &error) ; + void setError(const vpColVector &error_vector) ; void set_s(const vpColVector &s) ; void set_s(const double s0) ; void set_s(const double s0, const double s1) ; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 87750a1ae0f8af0b8db0487700771b7f836a9653..a20846074244be4c62eb79483df54f473c75a24e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4574 2014-01-09 08:48:51Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/test/camera/CMakeLists.txt b/test/camera/CMakeLists.txt index 5216126fe78750fbbd0875ddc15b8c61be1cc109..3c36476c6b26900c2f6e2f6708651e4c6fcf1182 100644 --- a/test/camera/CMakeLists.txt +++ b/test/camera/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testCameraParametersConversion.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/camera/testCameraParametersConversion.cpp b/test/camera/testCameraParametersConversion.cpp index 2d6ea7029e7ca5b54564ae6717dda5153ed09f7c..f8a70f9f77cfffe22ffa827638967bb1b5c9b52a 100644 --- a/test/camera/testCameraParametersConversion.cpp +++ b/test/camera/testCameraParametersConversion.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testCameraParametersConversion.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testCameraParametersConversion.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,9 @@ #include <stdlib.h> #include <stdio.h> +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -93,15 +96,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -110,7 +113,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -121,56 +124,62 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpCameraParameters cam; - double px,py,u0,v0; - px = 1657.429131; - py = 1658.818598; - u0 = 322.2437833; - v0 = 230.8012737; - vpCameraParameters camDist; - double px_dist,py_dist,u0_dist,v0_dist,kud_dist,kdu_dist; - px_dist = 1624.824731; - py_dist = 1625.263641; - u0_dist = 324.0923411; - v0_dist = 245.2421388; - kud_dist = -0.1741532338; - kdu_dist = 0.1771165148; - - cam.initPersProjWithoutDistortion(px,py,u0,v0); - camDist.initPersProjWithDistortion(px_dist,py_dist,u0_dist,v0_dist, - kud_dist, kdu_dist); - - double u1 = 320; - double v1 = 240; - double x1 = 0, y1 = 0; - double u2 = 0, v2 = 0; - vpPixelMeterConversion::convertPoint(cam,u1,v1,x1,y1); - vpMeterPixelConversion::convertPoint(cam,x1,y1,u2,v2); - if(!vpMath::equal(u1,u2) || !vpMath::equal(v1,v2)){ - vpTRACE("Error in convertPoint without distortion:\n" - "u1 = %f, u2 = %f\n" - "v1 = %f, v2 = %f\n",u1,u2,v1,v2); - return -1; + vpCameraParameters cam; + double px,py,u0,v0; + px = 1657.429131; + py = 1658.818598; + u0 = 322.2437833; + v0 = 230.8012737; + vpCameraParameters camDist; + double px_dist,py_dist,u0_dist,v0_dist,kud_dist,kdu_dist; + px_dist = 1624.824731; + py_dist = 1625.263641; + u0_dist = 324.0923411; + v0_dist = 245.2421388; + kud_dist = -0.1741532338; + kdu_dist = 0.1771165148; + + cam.initPersProjWithoutDistortion(px,py,u0,v0); + camDist.initPersProjWithDistortion(px_dist,py_dist,u0_dist,v0_dist, + kud_dist, kdu_dist); + + double u1 = 320; + double v1 = 240; + double x1 = 0, y1 = 0; + double u2 = 0, v2 = 0; + vpPixelMeterConversion::convertPoint(cam,u1,v1,x1,y1); + vpMeterPixelConversion::convertPoint(cam,x1,y1,u2,v2); + if(!vpMath::equal(u1,u2) || !vpMath::equal(v1,v2)){ + vpTRACE("Error in convertPoint without distortion:\n" + "u1 = %f, u2 = %f\n" + "v1 = %f, v2 = %f\n",u1,u2,v1,v2); + return -1; + } + vpTRACE("convertPoint without distortion :\n" + "u1 - u2 = %.20f\n" + "v1 - v2 = %.20f\n",u1 - u2,v1 - v2); + + vpPixelMeterConversion::convertPoint(camDist,u1,v1,x1,y1); + vpMeterPixelConversion::convertPoint(camDist,x1,y1,u2,v2); + if(!vpMath::equal(u1,u2) || !vpMath::equal(v1,v2)){ + vpTRACE("Error in convertPoint with distortion :\n" + "u1 = %f, u2 = %f\n" + "v1 = %f, v2 = %f\n",u1,u2,v1,v2); + return -1; + } + vpTRACE("convertPoint with distortion :\n" + "u1 - u2 = %.20f\n" + "v1 - v2 = %.20f\n",u1 - u2,v1 - v2); + return 0; } - vpTRACE("convertPoint without distortion :\n" - "u1 - u2 = %.20f\n" - "v1 - v2 = %.20f\n",u1 - u2,v1 - v2); - - vpPixelMeterConversion::convertPoint(camDist,u1,v1,x1,y1); - vpMeterPixelConversion::convertPoint(camDist,x1,y1,u2,v2); - if(!vpMath::equal(u1,u2) || !vpMath::equal(v1,v2)){ - vpTRACE("Error in convertPoint with distortion :\n" - "u1 = %f, u2 = %f\n" - "v1 = %f, v2 = %f\n",u1,u2,v1,v2); - return -1; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - vpTRACE("convertPoint with distortion :\n" - "u1 - u2 = %.20f\n" - "v1 - v2 = %.20f\n",u1 - u2,v1 - v2); - return 0; } diff --git a/test/device/CMakeLists.txt b/test/device/CMakeLists.txt index ee860dcab3c523bf2712ea1019ee2acf46045a8d..f61ebd21f80e10feee8cb9ac3d90af1cd0b6ddc0 100644 --- a/test/device/CMakeLists.txt +++ b/test/device/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 3057 2011-02-11 13:17:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -49,8 +49,3 @@ SET (SRC_SUBDIRS # Build process propagation in the sub directories SUBDIRS(${SRC_SUBDIRS}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/test/device/display/CMakeLists.txt b/test/device/display/CMakeLists.txt index 7a7a9c84203abe057070686a6814539e20fbbc18..abb24d27f4c4b6b4c4bd0e5479e182a38e6d0274 100644 --- a/test/device/display/CMakeLists.txt +++ b/test/device/display/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,8 +43,9 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testClick.cpp + testDisplayRoi.cpp testDisplays.cpp testMouseEvent.cpp testVideoDevice.cpp @@ -52,15 +53,19 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH @@ -68,13 +73,9 @@ ENDFOREACH(source) # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(testClick testClick -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(testDisplays testDisplays -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(testMouseEvent testMouseEvent -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(testVideoDevice testVideoDevice -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(testVideoDeviceDual testVideoDeviceDual -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(testClick testClick -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(testDisplayRoi testDisplayRoi -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(testDisplays testDisplays -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(testMouseEvent testMouseEvent -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(testVideoDevice testVideoDevice -c ${OPTION_TO_DESACTIVE_DISPLAY}) +add_test(testVideoDeviceDual testVideoDeviceDual -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/test/device/display/testClick.cpp b/test/device/display/testClick.cpp index 7b4648fef6e0362f93b9f4a20ceca642130f4305..ed3d5c1e695db006282130372f2500bb149015fb 100644 --- a/test/device/display/testClick.cpp +++ b/test/device/display/testClick.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testClick.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testClick.cpp 5230 2015-01-30 06:55:59Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,6 +77,11 @@ typedef enum { vpCV } vpDisplayType; +void usage(const char *name, const char *badparam, std::string ipath, vpDisplayType &dtype); +bool getOptions(int argc, const char **argv, + std::string &ipath, vpDisplayType &dtype, bool &list, + bool &click_allowed, bool &display ); + /*! Print the program options. @@ -163,17 +168,17 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, std::string &ipath, vpDisplayType &dtype, bool &list, - bool &click_allowed, bool &display ) + bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; std::string sDisplayType; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'l': list = true; break; - case 't': sDisplayType = optarg; + case 't': sDisplayType = optarg_; // Parse the display type option if (sDisplayType.compare("X11") == 0) { dtype = vpX11; @@ -197,7 +202,7 @@ bool getOptions(int argc, const char **argv, case 'd': display = false; break; default: - usage(argv[0], optarg, ipath, dtype); return false; break; + usage(argv[0], optarg_, ipath, dtype); return false; break; } } @@ -206,7 +211,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, dtype); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -239,10 +244,8 @@ main(int argc, const char ** argv) opt_dtype = vpCV; #endif - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); // Set the default input path if (! env_ipath.empty()) @@ -317,7 +320,7 @@ main(int argc, const char ** argv) vpImage<unsigned char> I ; // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); vpCTRACE << "Load " << filename << std::endl; vpImageIo::read(I, filename) ; @@ -371,7 +374,7 @@ main(int argc, const char ** argv) break; case vpCV: std::cout << "Requested OpenCV display functionnalities..." << std::endl; -#if defined VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) display = new vpDisplayOpenCV; #else std::cout << " Sorry, OpenCV video device is not available.\n"; @@ -406,6 +409,7 @@ main(int argc, const char ** argv) case vpMouseButton::button1: std::cout << "with left button.\n"; break; case vpMouseButton::button2: std::cout << "with middle button.\n"; break; case vpMouseButton::button3: std::cout << "with right button.\n"; break; + case vpMouseButton::none: break; } vpDisplay::getClickUp(I, ip, button); std::cout << " You click up on pixel (" << ip <<") "; @@ -413,6 +417,7 @@ main(int argc, const char ** argv) case vpMouseButton::button1: std::cout << "with left button.\n"; break; case vpMouseButton::button2: std::cout << "with middle button.\n"; break; case vpMouseButton::button3: std::cout << "with right button.\n"; break; + case vpMouseButton::none: break; } vpDisplay::getPointerPosition(I,ip); std::cout << " Pointer poisition : " << ip << std::endl; @@ -426,7 +431,6 @@ main(int argc, const char ** argv) vpERROR_TRACE("Error while displaying the image") ; exit(-1); } - } #else diff --git a/test/device/display/testDisplayRoi.cpp b/test/device/display/testDisplayRoi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e2ec1993a968d3365b7410557c4ca93f13a7e6a --- /dev/null +++ b/test/device/display/testDisplayRoi.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** + * + * $Id: testDisplayRoi.cpp 4776 2014-07-11 10:11:02Z fspindle $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Test for image roi display. + * + * Authors: + * Fabien Spindler + * + *****************************************************************************/ + +/*! + \example testDisplayRoi.cpp + + Test display of an image roi. + +*/ + +#include <stdlib.h> + +#include <visp/vpImage.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDisplayGTK.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayD3D.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpParseArgv.h> +#include <visp/vpRect.h> + +// List of allowed command line options +#define GETOPTARGS "cdh" + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display); + +/*! + + Print the program options. + + \param name : Program name. + \param badparam : Bad parameter name. + + */ +void usage(const char *name, const char *badparam) +{ + fprintf(stdout, "\n\ +Read an image on the disk, display it using X11, display some\n\ +features (line, circle, caracters) in overlay and finaly write \n\ +the image and the overlayed features in an image on the disk.\n\ +\n\ +SYNOPSIS\n\ + %s [-c] [-d] [-h]\n", name); + + fprintf(stdout, "\n\ +OPTIONS: Default\n\ + -c\n\ + Disable the mouse click. Useful to automate the \n\ + execution of this program without humain intervention.\n\ +\n\ + -d \n\ + Disable the image display. This can be useful \n\ + for automatic tests using crontab under Unix or \n\ + using the task manager under Windows.\n\ +\n\ + -h\n\ + Print the help.\n\n"); + + if (badparam) { + fprintf(stderr, "ERROR: \n" ); + fprintf(stderr, "\nBad parameter [%s]\n", badparam); + } + +} + +/*! + + Set the program options. + + \param argc : Command line number of parameters. + \param argv : Array of command line parameters. + \param click_allowed : Enable/disable mouse click. + \param display : Set as true, activates the image display. This is + the default configuration. When set to false, the display is + disabled. This can be useful for automatic tests using crontab + under Unix or using the task manager under Windows. + + \return false if the program has to be stopped, true otherwise. + +*/ +bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display) +{ + const char *optarg_; + int c; + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { + + switch (c) { + case 'c': click_allowed = false; break; + case 'd': display = false; break; + case 'h': usage(argv[0], NULL); return false; break; + + default: + usage(argv[0], optarg_); return false; break; + } + } + + if ((c == 1) || (c == -1)) { + // standalone param or error + usage(argv[0], NULL); + std::cerr << "ERROR: " << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; + return false; + } + + return true; +} + +int main(int argc, const char ** argv) +{ +#ifdef VISP_HAVE_DISPLAY + bool opt_click_allowed = true; + bool opt_display = true; + + // Read the command line options + if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) { + exit (-1); + } + + if (opt_display) { + + vpImage<unsigned char> I(480,640,255); + +#if defined(VISP_HAVE_X11) + vpDisplayX d; +#elif defined(VISP_HAVE_GTK) + vpDisplayGTK d; +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d; +#elif defined(VISP_HAVE_D3D9) + vpDisplayD3D d; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d; +#endif + d.init(I); + vpDisplay::display(I); + vpDisplay::flush(I); + + I = 0; + + vpRect roi(I.getWidth()/4, I.getHeight()/4, I.getWidth()/2, I.getHeight()/2); + vpDisplay::displayROI(I, roi); + vpDisplay::flush(I); + if (opt_click_allowed) { + std::cout << "A click in the image to continue..." << std::endl; + vpDisplay::getClick(I); + } + vpDisplay::close(I); + + vpImage<vpRGBa> C(480,640,vpRGBa(255,0,0,0)); + + //vpDisplayX d; + d.init(C); + vpDisplay::display(C); + vpDisplay::flush(C); + + C = vpRGBa(0,255,0,0); + + vpDisplay::displayROI(C, roi); + vpDisplay::flush(C); + if (opt_click_allowed) { + std::cout << "A click in the image to exit..." << std::endl; + vpDisplay::getClick(C); + } + } +#else + (void)argc; + (void)argv; +#endif + return 0; +} diff --git a/test/device/display/testDisplays.cpp b/test/device/display/testDisplays.cpp index 753a60c3f26cb39db24a596c2a02e4eebb522eb1..8d904cbba23c140f8c75a7c27c76033fdb077a27 100644 --- a/test/device/display/testDisplays.cpp +++ b/test/device/display/testDisplays.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testDisplays.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testDisplays.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -68,7 +68,11 @@ */ // List of allowed command line options -#define GETOPTARGS "hl:dc" +#define GETOPTARGS "hldc" + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool &list, bool &click_allowed, bool &display); +void draw(vpImage<vpRGBa> &I); /*! @@ -120,13 +124,12 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, bool &list, - bool &click_allowed, bool &display ) +bool getOptions(int argc, const char **argv, bool &list, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; std::string sDisplayType; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'l': list = true; break; @@ -135,16 +138,15 @@ bool getOptions(int argc, const char **argv, bool &list, case 'd': display = false; break; default: - usage(argv[0], optarg); return false; break; + usage(argv[0], optarg_); return false; break; } } - if ((c == 1) || (c == -1)) { // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -165,7 +167,7 @@ void draw(vpImage<vpRGBa> &I) iP1.set_i(20); iP1.set_j(60); - vpDisplay::displayCharString (I, iP1, "Test...", vpColor::black); + vpDisplay::displayText (I, iP1, "Test...", vpColor::black); iP1.set_i(80); iP1.set_j(220); @@ -234,8 +236,6 @@ void draw(vpImage<vpRGBa> &I) iP1.set_i(380); iP1.set_j(400); vpDisplay::displayRectangle (I, iP1, 45, w, h, vpColor::green, 3); - - } int @@ -258,23 +258,23 @@ main(int argc, const char ** argv) unsigned nbDevices = 0; std::cout << "List of video-devices available: \n"; #if defined VISP_HAVE_GTK - std::cout << " GTK (use \"-t GTK\" option to use it)\n"; + std::cout << " GTK\n"; nbDevices ++; #endif #if defined VISP_HAVE_X11 - std::cout << " X11 (use \"-t X11\" option to use it)\n"; + std::cout << " X11\n"; nbDevices ++; #endif #if defined VISP_HAVE_GDI - std::cout << " GDI (use \"-t GDI\" option to use it)\n"; + std::cout << " GDI\n"; nbDevices ++; #endif #if defined VISP_HAVE_D3D9 - std::cout << " D3D (use \"-t D3D\" option to use it)\n"; + std::cout << " D3D\n"; nbDevices ++; #endif #if defined VISP_HAVE_OPENCV - std::cout << " CV (use \"-t CV\" option to use it)\n"; + std::cout << " OpenCV\n"; nbDevices ++; #endif if (!nbDevices) { @@ -310,7 +310,7 @@ main(int argc, const char ** argv) } #endif -#if defined VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) vpDisplayOpenCV *displayCv = NULL; displayCv = new vpDisplayOpenCV; Icv.init(480, 640, 255); @@ -380,7 +380,7 @@ main(int argc, const char ** argv) delete displayGtk; #endif -#if defined VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) delete displayCv; #endif diff --git a/test/device/display/testMouseEvent.cpp b/test/device/display/testMouseEvent.cpp index c0f1796862a1d309ceb2a23375d7b6acbbf44054..52c428268e14c4a7fdf36ac53b630adc9a903b89 100644 --- a/test/device/display/testMouseEvent.cpp +++ b/test/device/display/testMouseEvent.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testMouseEvent.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testMouseEvent.cpp 5230 2015-01-30 06:55:59Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -91,6 +91,12 @@ typedef enum { vpD3D, } vpDisplayType; +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, + unsigned first, unsigned nimages, unsigned step, vpDisplayType &dtype); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, + unsigned &first, unsigned &nimages, unsigned &step, + vpDisplayType &dtype, bool &list, bool &display, bool &click, bool &wait); + /*! Print the program options. @@ -106,8 +112,7 @@ typedef enum { */ void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, - unsigned first, unsigned nimages, unsigned step, - vpDisplayType &dtype) + unsigned first, unsigned nimages, unsigned step, vpDisplayType &dtype) { fprintf(stdout, "\n\ Read an image sequence from the disk and display it.\n\ @@ -217,15 +222,15 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp unsigned &first, unsigned &nimages, unsigned &step, vpDisplayType &dtype, bool &list, bool &display, bool &click, bool &wait) { - const char *optarg; + const char *optarg_; int c; std::string sDisplayType; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click = false; break; case 'd': display = false; break; - case 't': sDisplayType = optarg; + case 't': sDisplayType = optarg_; // Parse the display type option if (sDisplayType.compare("X11") == 0) { dtype = vpX11; @@ -241,18 +246,18 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp } break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'l': list = true; break; - case 'p': ppath = optarg; break; - case 'f': first = (unsigned) atoi(optarg); break; - case 'n': nimages = (unsigned) atoi(optarg); break; - case 's': step = (unsigned) atoi(optarg); break; + case 'p': ppath = optarg_; break; + case 'f': first = (unsigned) atoi(optarg_); break; + case 'n': nimages = (unsigned) atoi(optarg_); break; + case 's': step = (unsigned) atoi(optarg_); break; case 'w': wait = true; break; case 'h': usage(argv[0], NULL, ipath, ppath, first, nimages, step, dtype); return false; break; default: - usage(argv[0], optarg, ipath, ppath, first, nimages, step, dtype); + usage(argv[0], optarg_, ipath, ppath, first, nimages, step, dtype); return false; break; } } @@ -261,7 +266,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp // standalone param or error usage(argv[0], NULL, ipath, ppath, first, nimages, step, dtype); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -281,7 +286,7 @@ main(int argc, const char ** argv) unsigned opt_nimages = 10; unsigned opt_step = 1; vpDisplayType opt_dtype; // Type of display to use - bool opt_list = false; // To print the list of video devices + bool opt_list = false; // To print the list of video devices bool opt_display = true; bool opt_click = true; bool opt_click_blocking = false; @@ -297,10 +302,8 @@ main(int argc, const char ** argv) opt_dtype = vpD3D; #endif - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); // Set the default input path if (! env_ipath.empty()) @@ -308,11 +311,11 @@ main(int argc, const char ** argv) // Read the command line options if (getOptions(argc, argv, opt_ipath, opt_ppath,opt_first, opt_nimages, - opt_step, opt_dtype, opt_list, opt_display, opt_click, - opt_click_blocking) == false) { + opt_step, opt_dtype, opt_list, opt_display, opt_click, + opt_click_blocking) == false) { exit (-1); } - // Print the list of video-devices available + // Print the list of video-devices available if (opt_list) { unsigned nbDevices = 0; std::cout << "List of video-devices available: \n"; @@ -350,10 +353,10 @@ main(int argc, const char ** argv) if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) { if (ipath != env_ipath) { std::cout << std::endl - << "WARNING: " << std::endl; + << "WARNING: " << std::endl; std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; } } @@ -361,14 +364,14 @@ main(int argc, const char ** argv) if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty() ){ usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step,opt_dtype); std::cerr << std::endl - << "ERROR:" << std::endl; + << "ERROR:" << std::endl; std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl - << " Use -p <personal image path> option if you want to "<<std::endl - << " use personal images." << std::endl - << std::endl; + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl + << " Use -p <personal image path> option if you want to "<<std::endl + << " use personal images." << std::endl + << std::endl; exit(-1); } @@ -383,8 +386,8 @@ main(int argc, const char ** argv) char cfilename[FILENAME_MAX]; if (opt_ppath.empty()){ - - + + // Warning : // the image sequence is not provided with the ViSP package // therefore the program will return you an error : @@ -396,15 +399,15 @@ main(int argc, const char ** argv) // The sequence is available on the visp www site // http://www.irisa.fr/lagadic/visp/visp.html // in the download section. It is named "ViSP-images.tar.gz" - + // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); - + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); + // Build the name of the image file - + s.setf(std::ios::right, std::ios::adjustfield); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { @@ -427,62 +430,62 @@ main(int argc, const char ** argv) // Note that another error message has been printed from readPGM // to give more information about the error std::cerr << std::endl - << "ERROR:" << std::endl; + << "ERROR:" << std::endl; std::cerr << " Cannot read " << filename << std::endl; std::cerr << " Check your -i " << ipath << " option, " << std::endl - << " or your -p " << opt_ppath << " option " <<std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable" - << std::endl; + << " or your -p " << opt_ppath << " option " <<std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable" + << std::endl; exit(-1); } - // Create a display for the image + // Create a display for the image vpDisplay *display = NULL; switch(opt_dtype) { - case vpX11: - std::cout << "Requested X11 display functionnalities..." << std::endl; + case vpX11: + std::cout << "Requested X11 display functionnalities..." << std::endl; #if defined VISP_HAVE_X11 - display = new vpDisplayX; + display = new vpDisplayX; #else - std::cout << " Sorry, X11 video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, X11 video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpGTK: - std::cout << "Requested GTK display functionnalities..." << std::endl; + break; + case vpGTK: + std::cout << "Requested GTK display functionnalities..." << std::endl; #if defined VISP_HAVE_GTK - display = new vpDisplayGTK; + display = new vpDisplayGTK; #else - std::cout << " Sorry, GTK video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, GTK video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpGDI: - std::cout << "Requested GDI display functionnalities..." << std::endl; + break; + case vpGDI: + std::cout << "Requested GDI display functionnalities..." << std::endl; #if defined VISP_HAVE_GDI - display = new vpDisplayGDI; + display = new vpDisplayGDI; #else - std::cout << " Sorry, GDI video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, GDI video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpD3D: - std::cout << "Requested D3D display functionnalities..." << std::endl; + break; + case vpD3D: + std::cout << "Requested D3D display functionnalities..." << std::endl; #if defined VISP_HAVE_D3D9 - display = new vpDisplayD3D; + display = new vpDisplayD3D; #else - std::cout << " Sorry, D3D video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, D3D video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; + break; } if (opt_display) { @@ -501,12 +504,12 @@ main(int argc, const char ** argv) } catch(...) { vpERROR_TRACE("Error while displaying the image") ; - delete display; + delete display; exit(-1); } } -// double tms_1 = vpTime::measureTimeMs() ; + // double tms_1 = vpTime::measureTimeMs() ; unsigned niter=0 ; // this is the loop over the image sequence while (iter < opt_first + opt_nimages*opt_step) { @@ -518,7 +521,7 @@ main(int argc, const char ** argv) if (opt_ppath.empty()){ s.str(""); s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm"; - filename = dirname + s.str(); + filename = vpIoTools::createFilePath(dirname, s.str()); } else { sprintf(cfilename, opt_ppath.c_str(), iter) ; @@ -534,31 +537,32 @@ main(int argc, const char ** argv) //Flush the display vpDisplay::flush(I) ; - if (opt_click_blocking) { - std::cout << "A click in the image to continue..." << std::endl; - } - vpImagePoint ip; - vpMouseButton::vpMouseButtonType button; - - if (opt_click) { - bool pressed = vpDisplay::getClick(I, ip, button, opt_click_blocking); - if (pressed) { - switch (button) { - case vpMouseButton::button1: - std::cout << "Left button was pressed." << std::endl; - break; - case vpMouseButton::button2: - std::cout << "Middle button was pressed." << std::endl; - break; - case vpMouseButton::button3: - std::cout << "Right button was pressed. Bye. " << std::endl; - delete display; - return 0; break; - } - } - } - - vpTime::wait(tms, 1000); + if (opt_click_blocking) { + std::cout << "A click in the image to continue..." << std::endl; + } + vpImagePoint ip; + vpMouseButton::vpMouseButtonType button; + + if (opt_click) { + bool pressed = vpDisplay::getClick(I, ip, button, opt_click_blocking); + if (pressed) { + switch (button) { + case vpMouseButton::button1: + std::cout << "Left button was pressed." << std::endl; + break; + case vpMouseButton::button2: + std::cout << "Middle button was pressed." << std::endl; + break; + case vpMouseButton::button3: + std::cout << "Right button was pressed. Bye. " << std::endl; + delete display; + return 0; break; + case vpMouseButton::none: break; + } + } + } + + vpTime::wait(tms, 1000); } else { @@ -568,15 +572,15 @@ main(int argc, const char ** argv) niter++ ; } catch(...) { - delete display; + delete display; exit(-1) ; } iter += opt_step ; } - delete display; -// double tms_2 = vpTime::measureTimeMs() ; -// double tms_total = tms_2 - tms_1 ; -// std::cout << "Total Time : "<< tms_total<<std::endl; + delete display; + // double tms_2 = vpTime::measureTimeMs() ; + // double tms_total = tms_2 - tms_1 ; + // std::cout << "Total Time : "<< tms_total<<std::endl; } #else @@ -587,9 +591,3 @@ main() } #endif - -/* - * Local variables: - * c-basic-offset: 2 - * End: - */ diff --git a/test/device/display/testVideoDevice.cpp b/test/device/display/testVideoDevice.cpp index 320ee3e255f308d9a223d5c070575a6d9cb2d9da..badda35468321ffc499812606d15695bc5dbfb26 100644 --- a/test/device/display/testVideoDevice.cpp +++ b/test/device/display/testVideoDevice.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testVideoDevice.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testVideoDevice.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,6 +77,11 @@ typedef enum { vpCV } vpDisplayType; +void usage(const char *name, const char *badparam, std::string ipath, vpDisplayType &dtype); +bool getOptions(int argc, const char **argv, + std::string &ipath, vpDisplayType &dtype, bool &list, + bool &click_allowed, bool &display ); + /*! Print the program options. @@ -161,15 +166,15 @@ bool getOptions(int argc, const char **argv, std::string &ipath, vpDisplayType &dtype, bool &list, bool &click_allowed, bool &display ) { - const char *optarg; + const char *optarg_; int c; std::string sDisplayType; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'l': list = true; break; - case 't': sDisplayType = optarg; + case 't': sDisplayType = optarg_; // Parse the display type option if (sDisplayType.compare("X11") == 0) { dtype = vpX11; @@ -193,7 +198,7 @@ bool getOptions(int argc, const char **argv, case 'd': display = false; break; default: - usage(argv[0], optarg, ipath,dtype); return false; break; + usage(argv[0], optarg_, ipath,dtype); return false; break; } } @@ -202,7 +207,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, dtype); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -235,10 +240,8 @@ main(int argc, const char ** argv) opt_dtype = vpCV; #endif - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); // Set the default input path if (! env_ipath.empty()) @@ -280,7 +283,6 @@ main(int argc, const char ** argv) return (0); } - // Get the option values if (!opt_ipath.empty()) ipath = opt_ipath; @@ -315,12 +317,12 @@ main(int argc, const char ** argv) vpImage<vpRGBa> Irgba ; // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); vpCTRACE << "Load " << filename << std::endl; vpImageIo::read(I, filename) ; // Load a color image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); vpCTRACE << "Load " << filename << std::endl; vpImageIo::read(Irgba, filename) ; @@ -375,7 +377,7 @@ main(int argc, const char ** argv) break; case vpCV: std::cout << "Requested OpenCV display functionnalities..." << std::endl; -#if defined VISP_HAVE_OPENCV +#if defined(VISP_HAVE_OPENCV) display = new vpDisplayOpenCV; #else std::cout << " Sorry, OpenCV video device is not available.\n"; diff --git a/test/device/display/testVideoDeviceDual.cpp b/test/device/display/testVideoDeviceDual.cpp index 6e4b1ab505ff1128a62e0f2cd2b854284bb3fa3e..b0c765c722fe574826b407ad8fc5ce23c77074a2 100644 --- a/test/device/display/testVideoDeviceDual.cpp +++ b/test/device/display/testVideoDeviceDual.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testVideoDeviceDual.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testVideoDeviceDual.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,7 @@ */ + // List of allowed command line options #define GETOPTARGS "hlt:dc" @@ -71,6 +72,9 @@ typedef enum { vpCV } vpDisplayType; +void usage(const char *name, const char *badparam, vpDisplayType &dtype); +bool getOptions(int argc, const char **argv, vpDisplayType &dtype, bool &list, bool &click_allowed, bool &display); + /*! Print the program options. @@ -142,16 +146,16 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, vpDisplayType &dtype, bool &list, - bool &click_allowed, bool &display ) + bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; std::string sDisplayType; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'l': list = true; break; - case 't': sDisplayType = optarg; + case 't': sDisplayType = optarg_; // Parse the display type option if (sDisplayType.compare("X11") == 0) { dtype = vpX11; @@ -175,7 +179,7 @@ bool getOptions(int argc, const char **argv, case 'd': display = false; break; default: - usage(argv[0], optarg, dtype); return false; break; + usage(argv[0], optarg_, dtype); return false; break; } } @@ -183,7 +187,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, dtype); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -192,152 +196,158 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - bool opt_list = false; // To print the list of video devices - vpDisplayType opt_dtype; // Type of display to use - bool opt_click_allowed = true; - bool opt_display = true; + try { + bool opt_list = false; // To print the list of video devices + vpDisplayType opt_dtype; // Type of display to use + bool opt_click_allowed = true; + bool opt_display = true; - // Default display is one available + // Default display is one available #if defined VISP_HAVE_GTK - opt_dtype = vpGTK; + opt_dtype = vpGTK; #elif defined VISP_HAVE_X11 - opt_dtype = vpX11; + opt_dtype = vpX11; #elif defined VISP_HAVE_GDI - opt_dtype = vpGDI; + opt_dtype = vpGDI; #elif defined VISP_HAVE_D3D9 - opt_dtype = vpD3D; + opt_dtype = vpD3D; #elif defined VISP_HAVE_OPENCV - opt_dtype = vpCV; + opt_dtype = vpCV; #endif - // Read the command line options - if (getOptions(argc, argv, opt_dtype, opt_list, - opt_click_allowed, opt_display) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_dtype, opt_list, + opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Print the list of video-devices available - if (opt_list) { - unsigned nbDevices = 0; - std::cout << "List of video-devices available: \n"; + // Print the list of video-devices available + if (opt_list) { + unsigned nbDevices = 0; + std::cout << "List of video-devices available: \n"; #if defined VISP_HAVE_GTK - std::cout << " GTK (use \"-t GTK\" option to use it)\n"; - nbDevices ++; + std::cout << " GTK (use \"-t GTK\" option to use it)\n"; + nbDevices ++; #endif #if defined VISP_HAVE_X11 - std::cout << " X11 (use \"-t X11\" option to use it)\n"; - nbDevices ++; + std::cout << " X11 (use \"-t X11\" option to use it)\n"; + nbDevices ++; #endif #if defined VISP_HAVE_GDI - std::cout << " GDI (use \"-t GDI\" option to use it)\n"; - nbDevices ++; + std::cout << " GDI (use \"-t GDI\" option to use it)\n"; + nbDevices ++; #endif #if defined VISP_HAVE_D3D9 - std::cout << " D3D (use \"-t D3D\" option to use it)\n"; - nbDevices ++; + std::cout << " D3D (use \"-t D3D\" option to use it)\n"; + nbDevices ++; #endif #if defined VISP_HAVE_OPENCV - std::cout << " CV (use \"-t CV\" option to use it)\n"; - nbDevices ++; + std::cout << " CV (use \"-t CV\" option to use it)\n"; + nbDevices ++; #endif - if (!nbDevices) { - std::cout << " No display is available\n"; + if (!nbDevices) { + std::cout << " No display is available\n"; + } + return (0); } - return (0); - } - // Create 2 images - vpImage<unsigned char> I1(240, 320), I2(240, 320); - I1 = 128; - I2 = 255; + // Create 2 images + vpImage<unsigned char> I1(240, 320), I2(240, 320); + I1 = 128; + I2 = 255; - // Create 2 display - vpDisplay *d1 = NULL, *d2 = NULL; + // Create 2 display + vpDisplay *d1 = NULL, *d2 = NULL; - // Initialize the displays - switch(opt_dtype) { - case vpX11: - std::cout << "Requested X11 display functionnalities..." << std::endl; + // Initialize the displays + switch(opt_dtype) { + case vpX11: + std::cout << "Requested X11 display functionnalities..." << std::endl; #if defined VISP_HAVE_X11 - d1 = new vpDisplayX; - d2 = new vpDisplayX; + d1 = new vpDisplayX; + d2 = new vpDisplayX; #else - std::cout << " Sorry, X11 video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, X11 video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpGTK: - std::cout << "Requested GTK display functionnalities..." << std::endl; + break; + case vpGTK: + std::cout << "Requested GTK display functionnalities..." << std::endl; #if defined VISP_HAVE_GTK - d1 = new vpDisplayGTK; - d2 = new vpDisplayGTK; + d1 = new vpDisplayGTK; + d2 = new vpDisplayGTK; #else - std::cout << " Sorry, GTK video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, GTK video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpGDI: - std::cout << "Requested GDI display functionnalities..." << std::endl; + break; + case vpGDI: + std::cout << "Requested GDI display functionnalities..." << std::endl; #if defined VISP_HAVE_GDI - d1 = new vpDisplayGDI; - d2 = new vpDisplayGDI; + d1 = new vpDisplayGDI; + d2 = new vpDisplayGDI; #else - std::cout << " Sorry, GDI video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, GDI video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpD3D: - std::cout << "Requested D3D display functionnalities..." << std::endl; + break; + case vpD3D: + std::cout << "Requested D3D display functionnalities..." << std::endl; #if defined VISP_HAVE_D3D9 - d1 = new vpDisplayD3D; - d2 = new vpDisplayD3D; + d1 = new vpDisplayD3D; + d2 = new vpDisplayD3D; #else - std::cout << " Sorry, D3D video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, D3D video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - case vpCV: - std::cout << "Requested OpenCV display functionnalities..." << std::endl; -#if defined VISP_HAVE_OPENCV - d1 = new vpDisplayOpenCV; - d2 = new vpDisplayOpenCV; + break; + case vpCV: + std::cout << "Requested OpenCV display functionnalities..." << std::endl; +#if defined(VISP_HAVE_OPENCV) + d1 = new vpDisplayOpenCV; + d2 = new vpDisplayOpenCV; #else - std::cout << " Sorry, OpenCV video device is not available.\n"; - std::cout << "Use \"" << argv[0] - << " -l\" to print the list of available devices.\n"; - return 0; + std::cout << " Sorry, OpenCV video device is not available.\n"; + std::cout << "Use \"" << argv[0] + << " -l\" to print the list of available devices.\n"; + return 0; #endif - break; - } + break; + } - if (opt_display){ - int winx1 = 100, winy1 = 200; - d1->init(I1, winx1, winy1, "Display 1"); + if (opt_display){ + int winx1 = 100, winy1 = 200; + d1->init(I1, winx1, winy1, "Display 1"); - int winx2 = winx1+10+(int)I1.getWidth(), winy2 = winy1; - d2->init(I2, winx2, winy2, "Display 2"); + int winx2 = winx1+10+(int)I1.getWidth(), winy2 = winy1; + d2->init(I2, winx2, winy2, "Display 2"); - vpDisplay::display(I1); - vpDisplay::display(I2); + vpDisplay::display(I1); + vpDisplay::display(I2); - vpDisplay::flush(I1); - vpDisplay::flush(I2); - } + vpDisplay::flush(I1); + vpDisplay::flush(I2); + } - std::cout << "A click in display 1 to exit..." << std::endl; - if ( opt_click_allowed ) - vpDisplay::getClick(I1); + std::cout << "A click in display 1 to exit..." << std::endl; + if ( opt_click_allowed ) + vpDisplay::getClick(I1); - delete d1; - delete d2; + delete d1; + delete d2; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/test/device/framegrabber/CMakeLists.txt b/test/device/framegrabber/CMakeLists.txt index a3cede79590ec1a742148040fb5841059a1b42d5..9a7a6db0ee3a81fdb2a9f66c7595f6b05ec183c9 100644 --- a/test/device/framegrabber/CMakeLists.txt +++ b/test/device/framegrabber/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE test1394TwoResetBus.cpp test1394TwoGrabber.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test - #ADD_TEST(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/device/framegrabber/test1394TwoGrabber.cpp b/test/device/framegrabber/test1394TwoGrabber.cpp index 9a5dc5df60145fb83cff1f4c854a4a6b9479e860..77ef541b3124e953aee931222ac8d8ead257dff0 100644 --- a/test/device/framegrabber/test1394TwoGrabber.cpp +++ b/test/device/framegrabber/test1394TwoGrabber.cpp @@ -1,10 +1,10 @@ /**************************************************************************** * - * $Id: test1394TwoGrabber.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: test1394TwoGrabber.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/test/device/framegrabber/test1394TwoResetBus.cpp b/test/device/framegrabber/test1394TwoResetBus.cpp index 029102904094298aa7df4822f86adb9247641375..14839c0f62c8743b369ae03e238947ac4b10cf28 100644 --- a/test/device/framegrabber/test1394TwoResetBus.cpp +++ b/test/device/framegrabber/test1394TwoResetBus.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: test1394TwoResetBus.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: test1394TwoResetBus.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/test/feature/CMakeLists.txt b/test/feature/CMakeLists.txt index e9629e672ea829f7ad6819b1ca11c0842522bc8f..556fde323bf0cc104d0e00add841a784abfad155 100644 --- a/test/feature/CMakeLists.txt +++ b/test/feature/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testPoint.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/feature/testPoint.cpp b/test/feature/testPoint.cpp index 39407acc34d0f0da15fafe69466ea65ff18b9eea..a16f39cdb45418986a75c8f744407cd180792b4b 100644 --- a/test/feature/testPoint.cpp +++ b/test/feature/testPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testPoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testPoint.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,6 +61,10 @@ #include <stdlib.h> #include <stdio.h> + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -91,15 +95,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -108,7 +112,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -119,114 +123,119 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - vpHomogeneousMatrix cMo ; - cMo[0][3] = 0.1 ; - cMo[1][3] = 0.2 ; - cMo[2][3] = 2 ; - - vpPoint point ; - vpTRACE("set point coordinates in the world frame ") ; - point.setWorldCoordinates(0,0,0) ; - - - std::cout <<"------------------------------------------------------"<<std::endl ; - vpTRACE("test the projection ") ; - point.track(cMo) ; - - vpTRACE("coordinates in the world frame ") ; - std::cout << point.oP.t() << std::endl ; - vpTRACE("coordinates in the camera frame ") ; - std::cout << point.cP.t() << std::endl ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpTRACE("2D coordinates ") ; - std::cout<< point.get_x() << " " << point.get_y() << std::endl ; + vpHomogeneousMatrix cMo ; + cMo[0][3] = 0.1 ; + cMo[1][3] = 0.2 ; + cMo[2][3] = 2 ; - std::cout <<"------------------------------------------------------"<<std::endl ; - vpTRACE("test the interaction matrix ") ; + vpPoint point ; + vpTRACE("set point coordinates in the world frame ") ; + point.setWorldCoordinates(0,0,0) ; - vpFeaturePoint p ; - vpFeatureBuilder::create(p,point) ; + std::cout <<"------------------------------------------------------"<<std::endl ; + vpTRACE("test the projection ") ; + point.track(cMo) ; - vpMatrix L ; - L = p.interaction() ; - std::cout << L << std::endl ; + vpTRACE("coordinates in the world frame ") ; + std::cout << point.oP.t() << std::endl ; + vpTRACE("coordinates in the camera frame ") ; + std::cout << point.cP.t() << std::endl ; - vpTRACE("test the interaction matrix select") ; - vpTRACE("\t only X") ; - L = p.interaction(vpFeaturePoint::selectX()) ; - std::cout << L << std::endl ; + vpTRACE("2D coordinates ") ; + std::cout<< point.get_x() << " " << point.get_y() << std::endl ; - vpTRACE("\t only Y") ; - L = p.interaction(vpFeaturePoint::selectY()) ; - std::cout << L << std::endl ; + std::cout <<"------------------------------------------------------"<<std::endl ; + vpTRACE("test the interaction matrix ") ; - vpTRACE("\t X & Y") ; - L = p.interaction(vpFeaturePoint::selectX() | - vpFeaturePoint::selectY()) ; - std::cout << L << std::endl ; + vpFeaturePoint p ; + vpFeatureBuilder::create(p,point) ; - vpTRACE("\t selectAll") ; - L = p.interaction(vpFeaturePoint::selectAll() ) ; - std::cout << L << std::endl ; + vpMatrix L ; + L = p.interaction() ; + std::cout << L << std::endl ; - std::cout <<"------------------------------------------------------"<<std::endl ; - vpTRACE("test the error ") ; + vpTRACE("test the interaction matrix select") ; + vpTRACE("\t only X") ; + L = p.interaction(vpFeaturePoint::selectX()) ; + std::cout << L << std::endl ; - try{ - vpFeaturePoint pd ; - pd.set_x(0) ; - pd.set_y(0) ; + vpTRACE("\t only Y") ; + L = p.interaction(vpFeaturePoint::selectY()) ; + std::cout << L << std::endl ; - pd.print() ; std::cout << std::endl ; - vpColVector e ; - e = p.error(pd) ; - std::cout << e << std::endl ; + vpTRACE("\t X & Y") ; + L = p.interaction(vpFeaturePoint::selectX() | + vpFeaturePoint::selectY()) ; + std::cout << L << std::endl ; - vpTRACE("test the interaction matrix select") ; + vpTRACE("\t selectAll") ; + L = p.interaction(vpFeaturePoint::selectAll() ) ; + std::cout << L << std::endl ; + + std::cout <<"------------------------------------------------------"<<std::endl ; + vpTRACE("test the error ") ; + + try{ + vpFeaturePoint pd ; + pd.set_x(0) ; + pd.set_y(0) ; + + pd.print() ; std::cout << std::endl ; + vpColVector e ; + e = p.error(pd) ; + std::cout << e << std::endl ; + + vpTRACE("test the interaction matrix select") ; + vpTRACE("\t only X") ; + e = p.error(pd,vpFeaturePoint::selectX()) ; + std::cout << e << std::endl ; + + vpTRACE("\t only Y") ; + e = p.error(pd,vpFeaturePoint::selectY()) ; + std::cout << e << std::endl ; + + vpTRACE("\t X & Y") ; + e = p.error(pd,vpFeaturePoint::selectX() | vpFeaturePoint::selectY()) ; + std::cout << e << std::endl ; + + vpTRACE("\t selectAll") ; + e = p.error(pd,vpFeaturePoint::selectAll() ) ; + std::cout << e << std::endl ; + } + catch(vpFeatureException me){ std::cout << me << std::endl ; } + catch(vpException me){ std::cout << me << std::endl ; } + std::cout <<"------------------------------------------------------"<<std::endl ; + vpTRACE("test the dimension") ; + unsigned int dim ; + dim = p.getDimension() ; + std::cout << "Dimension = " << dim << std::endl ; + + vpTRACE("test the dimension with select") ; vpTRACE("\t only X") ; - e = p.error(pd,vpFeaturePoint::selectX()) ; - std::cout << e << std::endl ; + dim = p.getDimension(vpFeaturePoint::selectX()) ; + std::cout << "Dimension = " << dim << std::endl ; vpTRACE("\t only Y") ; - e = p.error(pd,vpFeaturePoint::selectY()) ; - std::cout << e << std::endl ; + dim = p.getDimension(vpFeaturePoint::selectY()) ; + std::cout << "Dimension = " << dim << std::endl ; vpTRACE("\t X & Y") ; - e = p.error(pd,vpFeaturePoint::selectX() | vpFeaturePoint::selectY()) ; - std::cout << e << std::endl ; + dim = p.getDimension(vpFeaturePoint::selectX() | vpFeaturePoint::selectY()) ; + std::cout << "Dimension = " << dim << std::endl ; vpTRACE("\t selectAll") ; - e = p.error(pd,vpFeaturePoint::selectAll() ) ; - std::cout << e << std::endl ; + dim = p.getDimension(vpFeaturePoint::selectAll() ) ; + std::cout << "Dimension = " << dim << std::endl ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - catch(vpFeatureException me){ std::cout << me << std::endl ; } - catch(vpException me){ std::cout << me << std::endl ; } - std::cout <<"------------------------------------------------------"<<std::endl ; - vpTRACE("test the dimension") ; - unsigned int dim ; - dim = p.getDimension() ; - std::cout << "Dimension = " << dim << std::endl ; - - vpTRACE("test the dimension with select") ; - vpTRACE("\t only X") ; - dim = p.getDimension(vpFeaturePoint::selectX()) ; - std::cout << "Dimension = " << dim << std::endl ; - - vpTRACE("\t only Y") ; - dim = p.getDimension(vpFeaturePoint::selectY()) ; - std::cout << "Dimension = " << dim << std::endl ; - - vpTRACE("\t X & Y") ; - dim = p.getDimension(vpFeaturePoint::selectX() | vpFeaturePoint::selectY()) ; - std::cout << "Dimension = " << dim << std::endl ; - - vpTRACE("\t selectAll") ; - dim = p.getDimension(vpFeaturePoint::selectAll() ) ; - std::cout << "Dimension = " << dim << std::endl ; - } diff --git a/test/homography/CMakeLists.txt b/test/homography/CMakeLists.txt index 42054ab176f9a6e4c692841af9869d54623d9895..7382083497657e8c49dd0b8065d1e6d6ac006716 100644 --- a/test/homography/CMakeLists.txt +++ b/test/homography/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testDisplacement.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/homography/testDisplacement.cpp b/test/homography/testDisplacement.cpp index c0d9eb2718679ac106458690d5987e66e7bc457f..1f9c5abe10f944b7dcfe58f9ab568aed984d65e6 100644 --- a/test/homography/testDisplacement.cpp +++ b/test/homography/testDisplacement.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testDisplacement.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testDisplacement.cpp 4833 2014-08-28 14:21:46Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,6 +63,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -93,15 +96,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -110,7 +113,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -121,131 +124,114 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - { - vpThetaUVector tu(vpMath::rad(90), vpMath::rad(120), vpMath::rad(45)) ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - std::cout << "Initialization " <<std::endl ; - // std::cout << tu << std::endl ; + { + vpThetaUVector tu(vpMath::rad(90), vpMath::rad(120), vpMath::rad(45)) ; + std::cout << "Initialization " <<std::endl ; + // std::cout << tu << std::endl ; - std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; - vpRotationMatrix R(tu) ; + std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; + vpRotationMatrix R(tu) ; + // pure rotation + vpHomogeneousMatrix M ; + M.insert(R) ; - // pure rotation - vpHomogeneousMatrix M ; - M.insert(R) ; + std::cout << "M" <<std::endl <<M << std::endl ; + vpPlane p(0,0,1,1) ; + vpHomography H(M,p) ; + std::cout << "H" <<std::endl <<H << std::endl ; - std::cout << "M" <<std::endl <<M << std::endl ; - vpPlane p(0,0,1,1) ; + vpColVector n ; + vpTranslationVector T ; - vpHomography H(M,p) ; + H.computeDisplacement(R,T,n) ; - vpTRACE(" ") ; - std::cout << "H" <<std::endl <<H << std::endl ; + std::cout << "R" <<std::endl << R ; + std::cout << "T" <<std::endl << T.t() << std::endl; + std::cout << "n" <<std::endl << n.t() << std::endl; + } + std::cout <<"------------------------------------------------------" << std::endl ; + { + vpThetaUVector tu(vpMath::rad(90), vpMath::rad(120), vpMath::rad(45)) ; - vpTRACE(" ") ; + std::cout << "Initialization " << std::endl ; + // std::cout << tu << std::endl ; - vpColVector n ; - vpTranslationVector T ; + std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; + vpRotationMatrix R(tu) ; - H.computeDisplacement(R,T,n) ; + // pure rotation + vpHomogeneousMatrix M ; + M.insert(R) ; - std::cout << "R" <<std::endl << R ; - std::cout << "T" <<std::endl << T.t() ; - std::cout << "n" <<std::endl << n.t() ; - vpTRACE(" ") ; - vpTRACE(" ") ; - } - std::cout <<"------------------------------------------------------" << std::endl ; + M[0][3] = 0.21 ; + M[1][3] = 0.31 ; + M[2][3] = 0.5 ; - { - vpThetaUVector tu(vpMath::rad(90), vpMath::rad(120), vpMath::rad(45)) ; + std::cout << "M" << std::endl << M << std::endl ; + vpPlane p(0,0,1,1) ; - std::cout << "Initialization " <<std::endl ; - // std::cout << tu << std::endl ; + vpHomography H(M,p) ; + std::cout << "H" << std::endl << H << std::endl ; - std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; - vpRotationMatrix R(tu) ; + vpColVector n ; + vpTranslationVector T ; + H.computeDisplacement(R,T,n) ; - // pure rotation - vpHomogeneousMatrix M ; - M.insert(R) ; - + std::cout << "R" <<std::endl << R ; + std::cout << "T" <<std::endl << T.t() << std::endl; + std::cout << "n" <<std::endl << n.t() << std::endl; + } - M[0][3] = 0.21 ; - M[1][3] = 0.31 ; - M[2][3] = 0.5 ; + std::cout <<"------------------------------------------------------" << std::endl ; + { + vpThetaUVector tu(vpMath::rad(-190), vpMath::rad(12), vpMath::rad(-45)) ; + vpRotationMatrix R(tu) ; - std::cout << "M" <<std::endl <<M << std::endl ; - vpPlane p(0,0,1,1) ; + // pure rotation + vpHomogeneousMatrix M ; + M.insert(R) ; - vpHomography H(M,p) ; + M[0][3] = 0.21 ; + M[1][3] = -0.31 ; + M[2][3] = 0.5 ; - vpTRACE(" ") ; - std::cout << "H" <<std::endl <<H << std::endl ; + std::cout << "M" << std::endl << M << std::endl ; + vpPlane p(0.4,-0.5,0.5,1) ; + vpHomography H(M,p) ; - vpTRACE(" ") ; + std::cout << "H" << std::endl << H << std::endl ; - vpColVector n ; - vpTranslationVector T ; + vpColVector n ; + vpTranslationVector T ; + H.computeDisplacement(R,T,n) ; - H.computeDisplacement(R,T,n) ; + std::cout << "R" <<std::endl << R ; + std::cout << "T" <<std::endl << T.t() << std::endl; + std::cout << "n" <<std::endl << n.t() << std::endl; - std::cout << "R" <<std::endl << R ; - std::cout << "T" <<std::endl << T.t() ; - std::cout << "n" <<std::endl << n.t() ; - vpTRACE(" ") ; - vpTRACE(" ") ; + vpPlane p1(n[0],n[1],n[2],1.0) ; + H.buildFrom(R,T,p1) ; + std::cout << "H" << std::endl << H << std::endl ; + } + return 0; } - - std::cout <<"------------------------------------------------------" << std::endl ; - { - vpThetaUVector tu(vpMath::rad(-190), vpMath::rad(12), vpMath::rad(-45)) ; - - vpRotationMatrix R(tu) ; - - - // pure rotation - vpHomogeneousMatrix M ; - M.insert(R) ; - - M[0][3] = 0.21 ; - M[1][3] =- 0.31 ; - M[2][3] = 0.5 ; - - std::cout << "M" <<std::endl <<M << std::endl ; - vpPlane p(0.4,-0.5,0.5,1) ; - - vpHomography H(M,p) ; - - vpTRACE(" ") ; - std::cout << "H" <<std::endl <<H << std::endl ; - - vpTRACE(" ") ; - vpColVector n ; - vpTranslationVector T ; - H.computeDisplacement(R,T,n) ; - - std::cout << "R" <<std::endl << R ; - std::cout << "T" <<std::endl << T.t() ; - std::cout << "n" <<std::endl << n.t() ; - - vpPlane p1(n[0],n[1],n[2],1.0) ; - H.buildFrom(R,T,p1) ; - std::cout << "H" <<std::endl <<H << std::endl ; - + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/image/CMakeLists.txt b/test/image/CMakeLists.txt index 90fcb462f1db6ca29793b31af171848ba6f3c63a..3ac6c1069df7902d0f2e1e56c3706035550cde31 100644 --- a/test/image/CMakeLists.txt +++ b/test/image/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testConversion.cpp testCreateSubImage.cpp testImagePoint.cpp @@ -54,29 +54,28 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() # Add test # To run some of these tests don't forget to set VISP_INPUT_IMAGE_PATH # environment variable to the ViSP test sequences location. # To get these sequence download ViSP-images.tar.gz from # http://www.irisa.fr/lagadic/visp/visp.html -ADD_TEST(testConversion testConversion) -ADD_TEST(testCreateSubImage testCreateSubImage) -ADD_TEST(testImagePoint testImagePoint) -ADD_TEST(testIoPGM testIoPGM) -ADD_TEST(testIoPPM testIoPPM) -ADD_TEST(testReadImage testReadImage) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(testConversion testConversion) +add_test(testCreateSubImage testCreateSubImage) +add_test(testImagePoint testImagePoint) +add_test(testIoPGM testIoPGM) +add_test(testIoPPM testIoPPM) +add_test(testReadImage testReadImage) diff --git a/test/image/testConversion.cpp b/test/image/testConversion.cpp index aec83bcf7cecbae80e1e20d59fe9ceaed798533d..4fe6cfd2fbd24874b438b80babc4bda958811a82 100644 --- a/test/image/testConversion.cpp +++ b/test/image/testConversion.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testConversion.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testConversion.cpp 5023 2014-12-03 16:07:48Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,6 +61,9 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); + /* Print the program options. @@ -72,8 +75,7 @@ \param user : Username. */ -void usage(const char *name, const char *badparam, std::string ipath, - std::string opath, std::string user) +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user) { fprintf(stdout, "\n\ Test image conversions.\n\ @@ -120,20 +122,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -141,7 +142,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -151,410 +152,413 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef WIN32 - opt_opath = "C:/temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if defined(_WIN32) + opt_opath = "C:/temp"; #else - opt_opath = "/tmp"; + opt_opath = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + usage(argv[0], NULL, ipath, opt_opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (opt_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opt_opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (opt_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - // - // Here starts really the test - // - - vpImage<unsigned char> Ig ; // Grey image - vpImage<vpRGBa> Ic ; // Color image - - //-------------------- .pgm -> .ppm - vpTRACE("Convert a grey image (.pgm) to a color image (.ppm)"); - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ig, filename) ; - // Create a color image from the grey - vpImageConvert::convert(Ig, Ic); - filename = opath + vpIoTools::path("/Klimt_color.ppm"); - vpCTRACE << "Write " << filename << std::endl; - vpImageIo::write(Ic, filename) ; - - //-------------------- .ppm -> .pgm - vpTRACE("Convert a color image (.ppm) to a grey image (.pgm)"); - // Load a color image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ic, filename) ; - // Create a grey image from the color - vpImageConvert::convert(Ic, Ig); - filename = opath + vpIoTools::path("/Klimt_grey.pgm"); - vpCTRACE << "Write " << filename << std::endl; - vpImageIo::write(Ig, filename) ; - - //-------------------- YUV -> RGB - unsigned char y=187, u=10, v=30; - unsigned char r, g, b; - - // Convert a YUV pixel value to a RGB value - vpImageConvert::YUVToRGB(y, u, v, r, g, b); - vpTRACE("y(%d) u(%d) v(%d) = r(%d) g(%d) b(%d)", y, u, v, r, g, b); + // + // Here starts really the test + // + + vpImage<unsigned char> Ig ; // Grey image + vpImage<vpRGBa> Ic ; // Color image + + //-------------------- .pgm -> .ppm + vpTRACE("Convert a grey image (.pgm) to a color image (.ppm)"); + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ig, filename) ; + // Create a color image from the grey + vpImageConvert::convert(Ig, Ic); + filename = vpIoTools::createFilePath(opath, "Klimt_color.ppm"); + vpCTRACE << "Write " << filename << std::endl; + vpImageIo::write(Ic, filename) ; + + //-------------------- .ppm -> .pgm + vpTRACE("Convert a color image (.ppm) to a grey image (.pgm)"); + // Load a color image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ic, filename) ; + // Create a grey image from the color + vpImageConvert::convert(Ic, Ig); + filename = vpIoTools::createFilePath(opath, "Klimt_grey.pgm"); + vpCTRACE << "Write " << filename << std::endl; + vpImageIo::write(Ig, filename) ; + + //-------------------- YUV -> RGB + unsigned char y=187, u=10, v=30; + unsigned char r, g, b; + + // Convert a YUV pixel value to a RGB value + vpImageConvert::YUVToRGB(y, u, v, r, g, b); + vpTRACE("y(%d) u(%d) v(%d) = r(%d) g(%d) b(%d)", y, u, v, r, g, b); #ifdef VISP_HAVE_OPENCV - double t0 = vpTime::measureTimeMs(); - ///////////////////////// - // Convert a IplImage to a vpImage<vpRGBa> - //////////////////////// - IplImage* image = NULL; /*!< The image read / acquired */ - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - vpCTRACE << "Reading the color image with opencv: "<< std::endl - << filename << std::endl; - if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR)) == NULL) { - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return (-1); - } - vpImageConvert::convert(image, Ic); - filename = opath + vpIoTools::path("/Klimt_color_cv.ppm"); - /* Save the the current image */ - vpImageIo::write(Ic, filename) ; +#if VISP_HAVE_OPENCV_VERSION < 0x020408 + double t0 = vpTime::measureTimeMs(); + ///////////////////////// + // Convert a IplImage to a vpImage<vpRGBa> + //////////////////////// + IplImage* image = NULL; /*!< The image read / acquired */ + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + + /* Read the color image */ + + vpCTRACE << "Reading the color image with opencv: "<< std::endl + << filename << std::endl; + if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR)) == NULL) { + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return (-1); + } + vpImageConvert::convert(image, Ic); + filename = vpIoTools::createFilePath(opath, "Klimt_color_cv.ppm"); + /* Save the the current image */ + vpImageIo::write(Ic, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); - /* Read the pgm image */ + /* Read the pgm image */ - vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl - << filename << std::endl; - if(image!=NULL) cvReleaseImage( &image ); - if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE)) == NULL) { - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return (-1); - } - vpImageConvert::convert(image, Ic); - filename = opath + vpIoTools::path("/Klimt_grey_cv.ppm"); - /* Save the the current image */ - vpImageIo::write(Ic, filename) ; - - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - /////////////////////////// - // Convert a IplImage to a vpImage<unsigned char> - //////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - vpCTRACE << "Reading the color image with opencv: "<< std::endl - << filename << std::endl; - if(image!=NULL) cvReleaseImage( &image ); - if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR)) == NULL) { - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return (-1); - } - vpImageConvert::convert(image, Ig); - filename = opath + vpIoTools::path("/Klimt_color_cv.pgm"); - /* Save the the current image */ - vpImageIo::write(Ig, filename) ; + vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl + << filename << std::endl; + if(image!=NULL) cvReleaseImage( &image ); + if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE)) == NULL) { + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return (-1); + } + vpImageConvert::convert(image, Ic); + filename = vpIoTools::createFilePath(opath, "Klimt_grey_cv.ppm"); + /* Save the the current image */ + vpImageIo::write(Ic, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + /////////////////////////// + // Convert a IplImage to a vpImage<unsigned char> + //////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); - /* Read the pgm image */ + /* Read the color image */ - vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl - << filename << std::endl; - if(image!=NULL) cvReleaseImage( &image ); - if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE)) == NULL) { - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - - return (-1); - } - vpImageConvert::convert(image, Ig); - filename = opath + vpIoTools::path("/Klimt_grey_cv.pgm"); - /* Save the the current image */ - vpImageIo::write(Ig, filename) ; - - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - //////////////////////////////////// - // Convert a vpImage<vpRGBa> to a IplImage - //////////////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - // Load a color image from the disk - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ic, filename) ; - vpImageConvert::convert(Ic, image); - filename = opath + vpIoTools::path("/Klimt_ipl_color_cv.ppm"); - /* Save the the current image */ - vpCTRACE << "Write " << filename << std::endl; - if((cvSaveImage(filename.c_str(), image)) == 0) { - vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + vpCTRACE << "Reading the color image with opencv: "<< std::endl + << filename << std::endl; if(image!=NULL) cvReleaseImage( &image ); - return (-1); - } - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR)) == NULL) { + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return (-1); + } + vpImageConvert::convert(image, Ig); + filename = vpIoTools::createFilePath(opath, "Klimt_color_cv.pgm"); + /* Save the the current image */ + vpImageIo::write(Ig, filename) ; - //////////////////////////////////////// - // Convert a IplImage to a vpImage<unsigned char> - //////////////////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - /* Read the grey image */ + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); - // Load a color image from the disk - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ig, filename) ; - vpImageConvert::convert(Ig, image); - filename = opath + vpIoTools::path("/Klimt_ipl_grey_cv.pgm"); - /* Save the the current image */ + /* Read the pgm image */ - vpCTRACE << "Write " << filename << std::endl; - if((cvSaveImage(filename.c_str(), image)) == 0) { - vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl + << filename << std::endl; if(image!=NULL) cvReleaseImage( &image ); - return (-1); - } - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + if((image = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE)) == NULL) { + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - if(image!=NULL) cvReleaseImage( &image ); - double t1 = vpTime::measureTimeMs(); - std::cout << "Conversion c interface : " << t1 - t0 << " ms" << std::endl; - - /* ------------------------------------------------------------------------ */ - /* conversion for the new c++ interface */ - /* ------------------------------------------------------------------------ */ + return (-1); + } + vpImageConvert::convert(image, Ig); + filename = vpIoTools::createFilePath(opath, "Klimt_grey_cv.pgm"); + /* Save the the current image */ + vpImageIo::write(Ig, filename) ; + + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + //////////////////////////////////// + // Convert a vpImage<vpRGBa> to a IplImage + //////////////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + + /* Read the color image */ + + // Load a color image from the disk + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ic, filename) ; + vpImageConvert::convert(Ic, image); + filename = vpIoTools::createFilePath(opath, "Klimt_ipl_color_cv.ppm"); + /* Save the the current image */ + vpCTRACE << "Write " << filename << std::endl; + if((cvSaveImage(filename.c_str(), image)) == 0) { + vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + if(image!=NULL) cvReleaseImage( &image ); + return (-1); + } + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + //////////////////////////////////////// + // Convert a IplImage to a vpImage<unsigned char> + //////////////////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + + /* Read the grey image */ + + // Load a color image from the disk + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ig, filename) ; + vpImageConvert::convert(Ig, image); + filename = vpIoTools::createFilePath(opath, "Klimt_ipl_grey_cv.pgm"); + /* Save the the current image */ + + vpCTRACE << "Write " << filename << std::endl; + if((cvSaveImage(filename.c_str(), image)) == 0) { + vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + if(image!=NULL) cvReleaseImage( &image ); + return (-1); + } + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 - double t2 = vpTime::measureTimeMs(); - ///////////////////////// - // Convert a cv::Mat to a vpImage<vpRGBa> - //////////////////////// - cv::Mat imageMat; - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpCTRACE << "Reading the color image with c++ interface of opencv: "<< std::endl - << filename << std::endl; - imageMat = cv::imread(filename, 1);// force to a three channel color image. - if(imageMat.data == NULL){ - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return -1; - } - vpImageConvert::convert(imageMat, Ic); - filename = opath + vpIoTools::path("/Klimt_color_cvMat.ppm"); - /* Save the the current image */ - vpImageIo::write(Ic, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - /* Read the pgm image */ - - vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl - << filename << std::endl; - imageMat = cv::imread(filename, 0);// forced to grayscale. - if(imageMat.data == NULL) { - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return (-1); - } - vpImageConvert::convert(imageMat, Ic); - filename = opath + vpIoTools::path("/Klimt_grey_cvMat.ppm"); - /* Save the the current image */ - vpImageIo::write(Ic, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - /////////////////////////// - // Convert a cv::Mat to a vpImage<unsigned char> - //////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - vpCTRACE << "Reading the color image with opencv: "<< std::endl - << filename << std::endl; - imageMat = cv::imread(filename, 1);// force to a three channel color image. - if(imageMat.data == NULL){ - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return -1; - } - vpImageConvert::convert(imageMat, Ig); - filename = opath + vpIoTools::path("/Klimt_color_cvMat.pgm"); - /* Save the the current image */ - vpImageIo::write(Ig, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - - /* Read the pgm image */ - - vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl - << filename << std::endl; - imageMat = cv::imread(filename, 0); - if(imageMat.data == NULL){ - vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; - return (-1); - } - vpImageConvert::convert(imageMat, Ig); - filename = opath + vpIoTools::path("/Klimt_grey_cvMat.pgm"); - /* Save the the current image */ - vpImageIo::write(Ig, filename) ; - - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - //////////////////////////////////// - // Convert a vpImage<vpRGBa> to a cv::Mat - //////////////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - // Load a color image from the disk - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ic, filename) ; - vpImageConvert::convert(Ic, imageMat); - filename = opath + vpIoTools::path("/Klimt_ipl_color_cvMat.ppm"); - /* Save the the current image */ - vpCTRACE << "Write " << filename << std::endl; - if(!cv::imwrite(filename, imageMat)){ - vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; if(image!=NULL) cvReleaseImage( &image ); - return (-1); - } - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - //////////////////////////////////////// - // Convert a IplImage to a vpImage<unsigned char> - //////////////////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - - /* Read the grey image */ + double t1 = vpTime::measureTimeMs(); + std::cout << "Conversion c interface : " << t1 - t0 << " ms" << std::endl; +#endif - // Load a color image from the disk - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ig, filename); - vpImageConvert::convert(Ig, imageMat); - filename = opath + vpIoTools::path("/Klimt_ipl_grey_cvMat.pgm"); - /* Save the the current image */ + /* ------------------------------------------------------------------------ */ + /* conversion for the new c++ interface */ + /* ------------------------------------------------------------------------ */ - vpCTRACE << "Write " << filename << std::endl; - if(!cv::imwrite(filename, imageMat)){ - vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; - if(image!=NULL) cvReleaseImage( &image ); - return (-1); - } - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - double t3 = vpTime::measureTimeMs(); - std::cout << "Conversion c++ interface : " << t3 - t2 << " ms" << std::endl; +#if VISP_HAVE_OPENCV_VERSION >= 0x020100 + double t2 = vpTime::measureTimeMs(); + ///////////////////////// + // Convert a cv::Mat to a vpImage<vpRGBa> + //////////////////////// + cv::Mat imageMat; + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpCTRACE << "Reading the color image with c++ interface of opencv: "<< std::endl + << filename << std::endl; + imageMat = cv::imread(filename, 1);// force to a three channel color image. + if(imageMat.data == NULL){ + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return -1; + } + vpImageConvert::convert(imageMat, Ic); + filename = vpIoTools::createFilePath(opath, "Klimt_color_cvMat.ppm"); + /* Save the the current image */ + vpImageIo::write(Ic, filename) ; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + /* Read the pgm image */ + + vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl + << filename << std::endl; + imageMat = cv::imread(filename, 0);// forced to grayscale. + if(imageMat.data == NULL) { + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return (-1); + } + vpImageConvert::convert(imageMat, Ic); + filename = vpIoTools::createFilePath(opath, "Klimt_grey_cvMat.ppm"); + /* Save the the current image */ + vpImageIo::write(Ic, filename) ; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + /////////////////////////// + // Convert a cv::Mat to a vpImage<unsigned char> + //////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + + /* Read the color image */ + + vpCTRACE << "Reading the color image with opencv: "<< std::endl + << filename << std::endl; + imageMat = cv::imread(filename, 1);// force to a three channel color image. + if(imageMat.data == NULL){ + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return -1; + } + vpImageConvert::convert(imageMat, Ig); + filename = vpIoTools::createFilePath(opath, "Klimt_color_cvMat.pgm"); + /* Save the the current image */ + vpImageIo::write(Ig, filename) ; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + + /* Read the pgm image */ + + vpCTRACE << "Reading the greyscale image with opencv: "<< std::endl + << filename << std::endl; + imageMat = cv::imread(filename, 0); + if(imageMat.data == NULL){ + vpCTRACE<<"Cannot read image: "<< std::endl << filename << std::endl; + return (-1); + } + vpImageConvert::convert(imageMat, Ig); + filename = vpIoTools::createFilePath(opath, "Klimt_grey_cvMat.pgm"); + /* Save the the current image */ + vpImageIo::write(Ig, filename) ; + + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + //////////////////////////////////// + // Convert a vpImage<vpRGBa> to a cv::Mat + //////////////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + + /* Read the color image */ + + // Load a color image from the disk + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ic, filename) ; + vpImageConvert::convert(Ic, imageMat); + filename = vpIoTools::createFilePath(opath, "Klimt_ipl_color_cvMat.ppm"); + /* Save the the current image */ + vpCTRACE << "Write " << filename << std::endl; + if(!cv::imwrite(filename, imageMat)){ + vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + return (-1); + } + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + //////////////////////////////////////// + // Convert a IplImage to a vpImage<unsigned char> + //////////////////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + + /* Read the grey image */ + + // Load a color image from the disk + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ig, filename); + vpImageConvert::convert(Ig, imageMat); + filename = vpIoTools::createFilePath(opath, "Klimt_ipl_grey_cvMat.pgm"); + /* Save the the current image */ + + vpCTRACE << "Write " << filename << std::endl; + if(!cv::imwrite(filename, imageMat)){ + vpCTRACE<<"Cannot write image: "<< std::endl << filename << std::endl; + return (-1); + } + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + double t3 = vpTime::measureTimeMs(); + std::cout << "Conversion c++ interface : " << t3 - t2 << " ms" << std::endl; #endif #endif - - //////////////////////////////////// - // Split a vpImage<vpRGBa> to vpImage<unsigned char> - //////////////////////////////////// - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - - /* Read the color image */ - - // Load a color image from the disk - vpCTRACE << "Load " << filename << std::endl; - vpImageIo::read(Ic, filename) ; - vpImage<unsigned char> R,G,B,a; - vpImageConvert::split(Ic, &R,NULL,&B); - double begintime = vpTime::measureTimeMs(); - for(int i=0; i<1000;i++){ + + //////////////////////////////////// + // Split a vpImage<vpRGBa> to vpImage<unsigned char> + //////////////////////////////////// + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + + /* Read the color image */ + + // Load a color image from the disk + vpCTRACE << "Load " << filename << std::endl; + vpImageIo::read(Ic, filename) ; + vpImage<unsigned char> R,G,B,a; vpImageConvert::split(Ic, &R,NULL,&B); + double begintime = vpTime::measureTimeMs(); + for(int i=0; i<1000;i++){ + vpImageConvert::split(Ic, &R,NULL,&B); + } + double endtime = vpTime::measureTimeMs(); + + std::cout<<"Time for 1000 split (ms): "<< endtime - begintime <<std::endl; + + filename = vpIoTools::createFilePath(opath, "Klimt_RChannel.pgm"); + /* Save the the current image */ + vpCTRACE << "Write " << filename << std::endl; + vpImageIo::write(R, filename) ; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + + filename = vpIoTools::createFilePath(opath, "Klimt_BChannel.pgm"); + /* Save the the current image */ + vpCTRACE << "Write " << filename << std::endl; + vpImageIo::write(B, filename) ; + vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - double endtime = vpTime::measureTimeMs(); - - std::cout<<"Time for 1000 split (ms): "<< endtime - begintime <<std::endl; - - filename = opath + vpIoTools::path("/Klimt_RChannel.pgm"); - /* Save the the current image */ - vpCTRACE << "Write " << filename << std::endl; - vpImageIo::write(R, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - - filename = opath + vpIoTools::path("/Klimt_BChannel.pgm"); - /* Save the the current image */ - vpCTRACE << "Write " << filename << std::endl; - vpImageIo::write(B, filename) ; - vpCTRACE<< "Convert result in "<<std::endl<< filename << std::endl; - } diff --git a/test/image/testCreateSubImage.cpp b/test/image/testCreateSubImage.cpp index 44105425a67d657265f9a749edac2e87847d5b25..9f33448d36de4cd0bc78bc2e0a6618864a839dd6 100644 --- a/test/image/testCreateSubImage.cpp +++ b/test/image/testCreateSubImage.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testCreateSubImage.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testCreateSubImage.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,9 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); + /* Print the program options. @@ -120,20 +123,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -141,7 +143,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -151,112 +153,115 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef WIN32 - opt_opath = "C:/temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if defined(_WIN32) + opt_opath = "C:/temp"; #else - opt_opath = "/tmp"; + opt_opath = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + usage(argv[0], NULL, ipath, opt_opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (opt_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opt_opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (opt_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } + // + // Here starts really the test + // + vpImage<unsigned char> I; // Input image + vpImage<unsigned char> C; // Cropped output image + + // Read the input grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(I, filename) ; + + // Specify the cropping area + vpRect crop; + crop.setLeft(-10.2); + crop.setTop(10.0); + crop.setWidth(I.getWidth()); + crop.setHeight(20.0); + + // Create the cropped image + vpImageTools::createSubImage(I, crop, C); + + // Write the cropped image on the disk + filename = vpIoTools::createFilePath(opath, "Klimt_crop.pgm"); + std::cout << "Write cropped image: " << filename << std::endl; + vpImageIo::write(C, filename) ; + return 0; } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - // - // Here starts really the test - // - vpImage<unsigned char> I; // Input image - vpImage<unsigned char> C; // Cropped output image - - - // Read the input grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename) ; - - // Specify the cropping area - vpRect crop; - crop.setLeft(-10.2); - crop.setTop(10.0); - crop.setWidth(I.getWidth()); - crop.setHeight(20.0); - - // Create the cropped image - vpImageTools::createSubImage(I, crop, C); - - // Write the cropped image on the disk - filename = opath + vpIoTools::path("/Klimt_crop.pgm"); - std::cout << "Write cropped image: " << filename << std::endl; - vpImageIo::write(C, filename) ; - } diff --git a/test/image/testImagePoint.cpp b/test/image/testImagePoint.cpp index 87cb0b8fe2a49b5b89dfecf21e32127b118ee55a..d21b6404a148548fcf5fdcdf2cd50ca50ab1d397 100644 --- a/test/image/testImagePoint.cpp +++ b/test/image/testImagePoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testImagePoint.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testImagePoint.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/test/image/testIoPGM.cpp b/test/image/testIoPGM.cpp index d094fc6de71d1c4d9de5b8815bf9a8f873cde98e..74a920511d55e1456cb43726a202f088a70a605e 100644 --- a/test/image/testIoPGM.cpp +++ b/test/image/testIoPGM.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testIoPGM.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testIoPGM.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,6 +58,8 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); /* @@ -116,20 +118,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -137,7 +138,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -147,128 +148,126 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef WIN32 - opt_opath = "C:/temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if defined(_WIN32) + opt_opath = "C:/temp"; #else - opt_opath = "/tmp"; + opt_opath = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + usage(argv[0], NULL, ipath, opt_opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opt_opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // + // Here starts really the test + // - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Create a grey level image + vpImage<unsigned char> I ; - // - // Here starts really the test - // - - // Create a grey level image - vpImage<unsigned char> I ; - - // Load a grey image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename); - // Write the content of the image on the disk - filename = opath + vpIoTools::path("/Klimt_grey.pgm"); - std::cout << "Write image: " << filename << std::endl; - vpImageIo::write(I, filename) ; - - // Try to load a non existing image (test for exceptions) - try - { - // Load a non existing grey image - filename = ipath + vpIoTools::path("/ViSP-images/image-that-does-not-exist.pgm"); + // Load a grey image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } - - // Try to write an image to a non existing directory - try - { - filename = opath + vpIoTools::path("/directory-that-does-not-exist/Klimt.pgm"); + vpImageIo::read(I, filename); + // Write the content of the image on the disk + filename = vpIoTools::createFilePath(opath, "Klimt_grey.pgm"); std::cout << "Write image: " << filename << std::endl; vpImageIo::write(I, filename) ; + + try { + // Try to load a non existing image (test for exceptions) + // Load a non existing grey image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/image-that-does-not-exist.pgm"); + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(I, filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception due to a non existing file: " << e << std::endl; + } + + try { + // Try to write an image to a non existing directory + filename = vpIoTools::createFilePath(opath, "directory-that-does-not-exist/Klimt.pgm"); + std::cout << "Write image: " << filename << std::endl; + vpImageIo::write(I, filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception due to a non existing file: " << e << std::endl; + } + return 0; } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/image/testIoPPM.cpp b/test/image/testIoPPM.cpp index 3327e51a54942d6342ed86505ff7eb8805554fcd..35ea8bedd5f134200842e1ad013fe4e5976aa47e 100644 --- a/test/image/testIoPPM.cpp +++ b/test/image/testIoPPM.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testIoPPM.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testIoPPM.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,6 +59,8 @@ // List of allowed command line options #define GETOPTARGS "i:o:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); /* @@ -118,20 +120,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -139,7 +140,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -149,170 +150,165 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef WIN32 - opt_opath = "C:/temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if defined(_WIN32) + opt_opath = "C:/temp"; #else - opt_opath = "/tmp"; + opt_opath = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + usage(argv[0], NULL, ipath, opt_opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opt_opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } + // + // Here starts really the test + // - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + ///////////////////////////////////////////////////////////////////// + // Create a grey level image + vpImage<unsigned char> I ; - // - // Here starts really the test - // - - ///////////////////////////////////////////////////////////////////// - // Create a grey level image - vpImage<unsigned char> I ; - - // Load a color image from the disk and convert it to a grey level one - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename); - // Write the content of the image on the disk - filename = opath + vpIoTools::path("/Klimt_grey.ppm"); - std::cout << "Write image: " << filename << std::endl; - vpImageIo::write(I, filename) ; - - // Try to load a non existing image (test for exceptions) - try - { - // Load a non existing grey image - filename = ipath + vpIoTools::path("/ViSP-images/image-that-does-not-exist.ppm"); + // Load a color image from the disk and convert it to a grey level one + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } - - // Try to write an image to a non existing directory - try - { - filename = opath + vpIoTools::path("/directory-that-does-not-exist/Klimt.ppm"); + vpImageIo::read(I, filename); + // Write the content of the image on the disk + filename = vpIoTools::createFilePath(opath, "Klimt_grey.ppm"); std::cout << "Write image: " << filename << std::endl; vpImageIo::write(I, filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } - ///////////////////////////////////////////////////////////////////// - // Create a color image - vpImage<vpRGBa> Irgba ; - - // Load a color image from the disk - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(Irgba, filename); - // Write the content of the color image on the disk - filename = opath + vpIoTools::path("/Klimt_color.ppm"); - std::cout << "Write image: " << filename << std::endl; - vpImageIo::write(Irgba, filename) ; - - // Try to load a non existing image (test for exceptions) - try - { - // Load a non existing color image - filename = ipath + vpIoTools::path("/ViSP-images/image-that-does-not-exist.ppm"); - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(Irgba, filename) ; - } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; - } + // Try to load a non existing image (test for exceptions) + try + { + // Load a non existing grey image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/image-that-does-not-exist.ppm"); + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(I, filename) ; + } + catch(vpImageException e) + { + vpERROR_TRACE("at main level"); + std::cout << e << std::endl ; + } - // Try to write a color image to a non existing directory - try - { - filename = opath + vpIoTools::path("/directory-that-does-not-exist/Klimt.ppm"); + // Try to write an image to a non existing directory + try { + filename = vpIoTools::createFilePath(opath, "directory-that-does-not-exist/Klimt.ppm"); + std::cout << "Write image: " << filename << std::endl; + vpImageIo::write(I, filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception due to a non existing file: " << e << std::endl; + } + + ///////////////////////////////////////////////////////////////////// + // Create a color image + vpImage<vpRGBa> Irgba ; + + // Load a color image from the disk + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(Irgba, filename); + // Write the content of the color image on the disk + filename = vpIoTools::createFilePath(opath, "Klimt_color.ppm"); std::cout << "Write image: " << filename << std::endl; vpImageIo::write(Irgba, filename) ; + + try { + // Try to load a non existing image (test for exceptions) + // Load a non existing color image + filename = vpIoTools::createFilePath(ipath, "ViSP-images/image-that-does-not-exist.ppm"); + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(Irgba, filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception due to a non existing file: " << e << std::endl; + } + + try { + // Try to write a color image to a non existing directory + filename = vpIoTools::createFilePath(opath, "directory-that-does-not-exist/Klimt.ppm"); + std::cout << "Write image: " << filename << std::endl; + vpImageIo::write(Irgba, filename) ; + } + catch(vpException e) { + std::cout << "Catch an exception due to a non existing file: " << e << std::endl; + } + return 0; } - catch(vpImageException e) - { - vpERROR_TRACE("at main level"); - std::cout << e << std::endl ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/image/testReadImage.cpp b/test/image/testReadImage.cpp index 59eb8c4570b0a75f8604745df4e26354e40bbd40..68cae29d2947f17b3e217bd37f8f16d835f8bfe0 100644 --- a/test/image/testReadImage.cpp +++ b/test/image/testReadImage.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testReadImage.cpp 4314 2013-07-16 17:41:21Z fspindle $ + * $Id: testReadImage.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,6 +57,8 @@ // List of allowed command line options #define GETOPTARGS "i:p:h" +void usage(const char *name, const char *badparam, std::string ipath, std::string ppath); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath); /* @@ -112,20 +114,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &ppath) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'p': ppath = optarg; break; + case 'i': ipath = optarg_; break; + case 'p': ppath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, ppath); return false; break; default: - usage(argv[0], optarg, ipath, ppath); return false; break; + usage(argv[0], optarg_, ipath, ppath); return false; break; } } @@ -133,7 +134,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, ppath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -143,102 +144,98 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - - std::string env_ipath; - std::string opt_ipath; - std::string opt_ppath; - std::string ipath; - std::string ppath; - std::string filename; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_ppath) == false) { - exit (-1); - } - - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_ppath.empty()) - ppath = opt_ppath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_ppath; + std::string ipath; + std::string ppath; + std::string filename; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_ppath) == false) { + exit (-1); } - } + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_ppath.empty()) + ppath = opt_ppath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } + } - // - // Here starts really the test - // - ///////////////////////////////////////////////////////////////////// - // Create a grey level image - //vpImage<vpRGBa> I; - vpImage<unsigned char> I; - vpImage<vpRGBa> Irgb; - - try { - if (opt_ppath.empty()) - { - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpImageIo::read(I,filename); - printf("Read ppm ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(I,filename); - printf("Read pgm ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.jpeg"); - vpImageIo::read(I,filename); - printf("Read jpeg ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/mire/mire.jpg"); - vpImageIo::read(I,filename); - printf("Read jpg ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.png"); - vpImageIo::read(I,filename); - printf("Read png ok\n"); - - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); - vpImageIo::read(Irgb,filename); - printf("Read ppm ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); - vpImageIo::read(Irgb,filename); - printf("Read pgm ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.jpeg"); - vpImageIo::read(Irgb,filename); - printf("Read jpeg ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/mire/mire.jpg"); - vpImageIo::read(Irgb,filename); - printf("Read jpg ok\n"); - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.png"); - vpImageIo::read(Irgb,filename); - printf("Read png ok\n"); - } - else - { - filename = opt_ppath; - vpImageIo::read(I,filename); - vpTRACE("image read without problem"); - } + // + // Here starts really the test + // + + ///////////////////////////////////////////////////////////////////// + // Create a grey level image + //vpImage<vpRGBa> I; + vpImage<unsigned char> I; + vpImage<vpRGBa> Irgb; + + if (opt_ppath.empty()) + { + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(I,filename); + printf("Read ppm ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(I,filename); + printf("Read pgm ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.jpeg"); + vpImageIo::read(I,filename); + printf("Read jpeg ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.jpg"); + vpImageIo::read(I,filename); + printf("Read jpg ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.png"); + vpImageIo::read(I,filename); + printf("Read png ok\n"); + + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); + vpImageIo::read(Irgb,filename); + printf("Read ppm ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm"); + vpImageIo::read(Irgb,filename); + printf("Read pgm ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.jpeg"); + vpImageIo::read(Irgb,filename); + printf("Read jpeg ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.jpg"); + vpImageIo::read(Irgb,filename); + printf("Read jpg ok\n"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.png"); + vpImageIo::read(Irgb,filename); + printf("Read png ok\n"); + } + else + { + filename = opt_ppath; + vpImageIo::read(I,filename); + vpTRACE("image read without problem"); + } } - catch(...) { - std::cout << "Unsupported image format" << std::endl; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - return 0; } diff --git a/test/image/testUndistortImage.cpp b/test/image/testUndistortImage.cpp index 6c8001ded1a9ad7a5e91f3fd72f5279d56110f4b..a29ce28c28a315d0068e4c90abb3331546381a04 100644 --- a/test/image/testUndistortImage.cpp +++ b/test/image/testUndistortImage.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testUndistortImage.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testUndistortImage.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,9 @@ //#define COLOR #define BW +void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user); +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user); + /* Print the program options. @@ -125,20 +128,19 @@ OPTIONS: Default\n\ \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &ipath, std::string &opath, std::string user) +bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'i': ipath = optarg; break; - case 'o': opath = optarg; break; + case 'i': ipath = optarg_; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, ipath, opath, user); return false; break; default: - usage(argv[0], optarg, ipath, opath, user); return false; break; + usage(argv[0], optarg_, ipath, opath, user); return false; break; } } @@ -146,7 +148,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, ipath, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -155,130 +157,135 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string opt_opath; - std::string ipath; - std::string opath; - std::string filename; - std::string username; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string env_ipath; + std::string opt_ipath; + std::string opt_opath; + std::string ipath; + std::string opath; + std::string filename; + std::string username; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { - exit (-1); - } + // Get the user login name + vpIoTools::getUserName(username); - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - if (!opt_opath.empty()) - opath = opt_opath; + // Read the command line options + if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) { + exit (-1); + } - // Append to the output path string, the login name of the user - opath += vpIoTools::path("/") + username; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + if (!opt_opath.empty()) + opath = opt_opath; + + // Append to the output path string, the login name of the user + opath = vpIoTools::createFilePath(opath, username); + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(opath) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(opath); + } + catch (...) { + usage(argv[0], NULL, ipath, opt_opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << opath << std::endl; + std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + exit(-1); + } + } - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(opath) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(opath); + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (opt_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - catch (...) { + + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ usage(argv[0], NULL, ipath, opt_opath, username); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << opath << std::endl; - std::cerr << " Check your -o " << opt_opath << " option " << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; exit(-1); } - } - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (opt_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; - } - } - - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath, opt_opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } - // - // Here starts really the test - // + // + // Here starts really the test + // #if defined BW - vpImage<unsigned char> I; // Input image - vpImage<unsigned char> U; // undistorted output image + vpImage<unsigned char> I; // Input image + vpImage<unsigned char> U; // undistorted output image #elif defined COLOR - vpImage<vpRGBa> I; // Input image - vpImage<vpRGBa> U; // undistorted output image + vpImage<vpRGBa> I; // Input image + vpImage<vpRGBa> U; // undistorted output image #endif - vpCameraParameters cam; - cam.initPersProjWithDistortion(600,600,192,144,-0.17,0.17); - // Read the input grey image from the disk + vpCameraParameters cam; + cam.initPersProjWithDistortion(600,600,192,144,-0.17,0.17); + // Read the input grey image from the disk #if defined BW - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.pgm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.pgm") ; #elif defined COLOR - filename = ipath + vpIoTools::path("/ViSP-images/Klimt/Klimt.ppm"); + filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm"); #endif - std::cout << "Read image: " << filename << std::endl; - vpImageIo::read(I, filename) ; + std::cout << "Read image: " << filename << std::endl; + vpImageIo::read(I, filename) ; - std::cout << "Undistortion in process... " << std::endl; - vpImageTools::undistort(I, cam, U); + std::cout << "Undistortion in process... " << std::endl; + vpImageTools::undistort(I, cam, U); - double begintime = vpTime::measureTimeMs(); + double begintime = vpTime::measureTimeMs(); - // For the test, to have a significant time measure we repeat the - // undistortion 100 times - for(unsigned int i=0;i<100;i++) - // Create the undistorted image - vpImageTools::undistort(I, cam, U); + // For the test, to have a significant time measure we repeat the + // undistortion 100 times + for(unsigned int i=0;i<100;i++) + // Create the undistorted image + vpImageTools::undistort(I, cam, U); - double endtime = vpTime::measureTimeMs(); + double endtime = vpTime::measureTimeMs(); - std::cout<<"Time for 100 undistortion (ms): "<< endtime - begintime - << std::endl; + std::cout<<"Time for 100 undistortion (ms): "<< endtime - begintime + << std::endl; - // Write the undistorted image on the disk + // Write the undistorted image on the disk #if defined BW - filename = opath + vpIoTools::path("/Klimt_undistorted.pgm"); + filename = vpIoTools::path( vpIoTools::createFilePath(opath, "Klimt_undistorted.pgm") ); #elif defined COLOR - filename = opath + vpIoTools::path("/Klimt_undistorted.ppm"); + filename = vpIoTools::path( vpIoTools::createFilePath(opath, "Klimt_undistorted.ppm") ); #endif - std::cout << "Write undistorted image: " << filename << std::endl; - vpImageIo::write(U, filename) ; + std::cout << "Write undistorted image: " << filename << std::endl; + vpImageIo::write(U, filename) ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/key-point/CMakeLists.txt b/test/key-point/CMakeLists.txt index 8d743b3c452e8c34c2ea953d930722312dd00246..6f8bf19ac797e965362f7f5491148b1d17379a93 100644 --- a/test/key-point/CMakeLists.txt +++ b/test/key-point/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testSurfKeyPoint.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - -ENDFOREACH(source) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() # Add test -ADD_TEST(testSurfKeyPoint testSurfKeyPoint -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +add_test(testSurfKeyPoint testSurfKeyPoint -c ${OPTION_TO_DESACTIVE_DISPLAY}) diff --git a/test/key-point/testSurfKeyPoint.cpp b/test/key-point/testSurfKeyPoint.cpp index 06d3318375808b50764aa5705a60bd055a61086c..502112a457f9262deb0410da01811e6de985cf2b 100644 --- a/test/key-point/testSurfKeyPoint.cpp +++ b/test/key-point/testSurfKeyPoint.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testSurfKeyPoint.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testSurfKeyPoint.cpp 5202 2015-01-24 09:29:06Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,7 +47,7 @@ #include <stdio.h> #include <sstream> #include <iomanip> -#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION >= 0x010100)) // Require opencv >= 1.1.0 +#if ((defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000)) // Require opencv >= 1.1.0 < 3.0.0 #include <visp/vpKeyPointSurf.h> #include <visp/vpImage.h> @@ -71,6 +71,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +void usage(const char *name, const char *badparam, std::string ipath); +bool getOptions(int argc, const char **argv, std::string &ipath, + bool &click_allowed, bool &display); + /*! Print the program options. @@ -128,18 +132,18 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -148,7 +152,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -159,114 +163,112 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filenameRef; - std::string filenameCur; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, - opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filenameRef; + std::string filenameCur; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, + opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> Iref ; - vpImage<unsigned char> Icur ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> Iref ; + vpImage<unsigned char> Icur ; - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/cube/"); + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/cube"); - // Build the name of the image file - filenameRef = dirname + "image.0000.pgm"; - filenameCur = dirname + "image.0079.pgm"; + // Build the name of the image file + filenameRef = vpIoTools::createFilePath(dirname, "image.0000.pgm"); + filenameCur = vpIoTools::createFilePath(dirname, "image.0079.pgm"); - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filenameRef << std::endl; + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filenameRef << std::endl; - vpImageIo::read(Iref, filenameRef) ; + vpImageIo::read(Iref, filenameRef) ; - vpCTRACE << "Load: " << filenameCur << std::endl; + vpCTRACE << "Load: " << filenameCur << std::endl; - vpImageIo::read(Icur, filenameCur) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filenameRef << "or" << filenameCur <<std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + vpImageIo::read(Icur, filenameCur) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filenameRef << "or" << filenameCur <<std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display[2]; + vpDisplayX display[2]; #elif defined VISP_HAVE_GTK - vpDisplayGTK display[2]; + vpDisplayGTK display[2]; #elif defined VISP_HAVE_GDI - vpDisplayGDI display[2]; + vpDisplayGDI display[2]; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display[0].init(Iref, 100, 100, "Reference image") ; // Display the image @@ -278,99 +280,97 @@ main(int argc, const char ** argv) //Flush the display vpDisplay::flush(Iref) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpKeyPointSurf surf; - unsigned int nbrRef; + vpKeyPointSurf surf; + unsigned int nbrRef; - if (opt_click_allowed && opt_display) - { - std::cout << "Select a part of the image where the reference points will be computed. This part is a rectangle." << std::endl; - std::cout << "Click first on the top left corner and then on the bottom right corner." << std::endl; - vpImagePoint corners[2]; - for (int i=0 ; i < 2 ; i++) + if (opt_click_allowed && opt_display) { - vpDisplay::getClick(Iref, corners[i]); + std::cout << "Select a part of the image where the reference points will be computed. This part is a rectangle." << std::endl; + std::cout << "Click first on the top left corner and then on the bottom right corner." << std::endl; + vpImagePoint corners[2]; + for (int i=0 ; i < 2 ; i++) + { + vpDisplay::getClick(Iref, corners[i]); + } + + vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::red); + vpDisplay::flush(Iref); + unsigned int height, width; + height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); + width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); + + //Computes the reference points + nbrRef = surf.buildReference(Iref, corners[0], height, width); } - vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::red); - vpDisplay::flush(Iref); - unsigned int height, width; - height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); - width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); - - //Computes the reference points - nbrRef = surf.buildReference(Iref, corners[0], height, width); - } - - else - { - nbrRef = surf.buildReference(Iref); - } + else + { + nbrRef = surf.buildReference(Iref); + } - if(nbrRef < 1) - { - vpTRACE("No reference point"); - exit(-1); - } + if(nbrRef < 1) + { + vpTRACE("No reference point"); + exit(-1); + } - unsigned int nbrPair; - if (opt_display) { - display[1].init(Icur, (int)(100+Iref.getWidth()), 100, "Current image") ; - // display variable. - vpDisplay::display(Icur) ; - //Flush the display - vpDisplay::flush(Icur) ; - } + unsigned int nbrPair; + if (opt_display) { + display[1].init(Icur, (int)(100+Iref.getWidth()), 100, "Current image") ; + // display variable. + vpDisplay::display(Icur) ; + //Flush the display + vpDisplay::flush(Icur) ; + } - if (opt_click_allowed && opt_display) - { - std::cout << "Select a part of the current image where the reference will be search. This part is a rectangle." << std::endl; - std::cout << "Click first on the top left corner and then on the bottom right corner." << std::endl; - vpImagePoint corners[2]; - for (int i=0 ; i < 2 ; i++) + if (opt_click_allowed && opt_display) { - vpDisplay::getClick(Icur, corners[i]); + std::cout << "Select a part of the current image where the reference will be search. This part is a rectangle." << std::endl; + std::cout << "Click first on the top left corner and then on the bottom right corner." << std::endl; + vpImagePoint corners[2]; + for (int i=0 ; i < 2 ; i++) + { + vpDisplay::getClick(Icur, corners[i]); + } + vpDisplay::displayRectangle(Icur, corners[0], corners[1], vpColor::green); + vpDisplay::flush(Icur); + unsigned int height, width; + height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); + width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); + + //Computes the reference points + nbrPair = surf.matchPoint(Icur, corners[0], height, width); } - vpDisplay::displayRectangle(Icur, corners[0], corners[1], vpColor::green); - vpDisplay::flush(Icur); - unsigned int height, width; - height = (unsigned int)(corners[1].get_i() - corners[0].get_i()); - width = (unsigned int)(corners[1].get_j() - corners[0].get_j()); - - //Computes the reference points - nbrPair = surf.matchPoint(Icur, corners[0], height, width); - } - else - { - nbrPair = surf.matchPoint(Icur); - } + else + { + nbrPair = surf.matchPoint(Icur); + } - if(nbrPair < 1) - { - vpTRACE("No point matched"); - } + if(nbrPair < 1) + { + vpTRACE("No point matched"); + } - if (opt_display) - { - surf.display(Iref, Icur, 7); - vpDisplay::flush(Iref) ; - vpDisplay::flush(Icur) ; - if (opt_click_allowed) + if (opt_display) { - std::cout << "A click on the reference image to exit..." << std::endl; - vpDisplay::getClick(Iref); + surf.display(Iref, Icur, 7); + vpDisplay::flush(Iref) ; + vpDisplay::flush(Icur) ; + if (opt_click_allowed) + { + std::cout << "A click on the reference image to exit..." << std::endl; + vpDisplay::getClick(Iref); + } } + return (0); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return (1); } - - return (0); } #else int @@ -379,7 +379,7 @@ main() #if ( ! (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) ) vpERROR_TRACE("You do not have X11, GTK or GDI display functionalities..."); #else - vpERROR_TRACE("You do not have OpenCV-1.1.0 or a more recent release that contains opencv_nonfree component..."); + vpERROR_TRACE("You do not have 1.1.0 <= OpenCV < 2.4.8 that contains opencv_nonfree component..."); #endif } diff --git a/test/math/CMakeLists.txt b/test/math/CMakeLists.txt index 506d8e6ba4a5240d6373628a2cfff056a373b3bc..0ad8f8421935d507bd1bc36c06a8d399b5c04974 100644 --- a/test/math/CMakeLists.txt +++ b/test/math/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testColvector.cpp testKalmanAcceleration.cpp testKalmanVelocity.cpp @@ -57,20 +57,19 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/math/testColvector.cpp b/test/math/testColvector.cpp index 972acefaf364d35bdbfd98bf993ddc89ede0487d..64ccecfcdb3a8a41ba683cb3bec6470629e0c5bb 100644 --- a/test/math/testColvector.cpp +++ b/test/math/testColvector.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testColvector.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testColvector.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,6 +55,8 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); /*! @@ -86,15 +88,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -103,7 +105,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -114,24 +116,30 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - vpColVector V(4) ; - V = 1.0; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpTRACE("------------------------"); - vpTRACE("call std::cout << V;"); - std::cout << V << std::endl; + vpColVector V(4) ; + V = 1.0; - vpTRACE("------------------------"); - vpTRACE("call V.normalize();"); - V.normalize(); + vpTRACE("------------------------"); + vpTRACE("call std::cout << V;"); + std::cout << V << std::endl; - vpTRACE("------------------------"); - vpTRACE("call std::cout << V;"); - std::cout << V << std::endl; + vpTRACE("------------------------"); + vpTRACE("call V.normalize();"); + V.normalize(); + vpTRACE("------------------------"); + vpTRACE("call std::cout << V;"); + std::cout << V << std::endl; + return (0); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return (1); + } } diff --git a/test/math/testKalmanAcceleration.cpp b/test/math/testKalmanAcceleration.cpp index 02afce193a44c541b495500abedd63745806599c..e3ed84c08e2a6a1c1ced0d29989bb3747c76f50c 100644 --- a/test/math/testKalmanAcceleration.cpp +++ b/test/math/testKalmanAcceleration.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testKalmanAcceleration.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testKalmanAcceleration.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,64 +53,70 @@ int main() { - unsigned int nsignal = 1; // Number of signal to filter - unsigned int niter = 100; + try { + unsigned int nsignal = 1; // Number of signal to filter + unsigned int niter = 100; - std::string filename = "/tmp/log.dat"; - std::ofstream flog(filename.c_str()); + std::string filename = "/tmp/log.dat"; + std::ofstream flog(filename.c_str()); - vpLinearKalmanFilterInstantiation kalman; + vpLinearKalmanFilterInstantiation kalman; - vpLinearKalmanFilterInstantiation::vpStateModel model; - model = vpLinearKalmanFilterInstantiation::stateConstAccWithColoredNoise_MeasureVel; - kalman.setStateModel(model); + vpLinearKalmanFilterInstantiation::vpStateModel model; + model = vpLinearKalmanFilterInstantiation::stateConstAccWithColoredNoise_MeasureVel; + kalman.setStateModel(model); - unsigned int size_state_vector = kalman.getStateSize()*nsignal; - unsigned int size_measure_vector = kalman.getMeasureSize()*nsignal; + unsigned int size_state_vector = kalman.getStateSize()*nsignal; + unsigned int size_measure_vector = kalman.getMeasureSize()*nsignal; - vpColVector sigma_measure(size_measure_vector); - for (unsigned int signal=0; signal < nsignal; signal ++) - sigma_measure = 0.0001; - vpColVector sigma_state(size_state_vector); - for (unsigned int signal=0; signal < nsignal; signal ++) { - sigma_state[3*signal] = 0.; // not used - sigma_state[3*signal+1] = 0.000001; - sigma_state[3*signal+2] = 0.000001; - } - - vpColVector velocity_measure(size_measure_vector); + vpColVector sigma_measure(size_measure_vector); + for (unsigned int signal=0; signal < nsignal; signal ++) + sigma_measure = 0.0001; + vpColVector sigma_state(size_state_vector); + for (unsigned int signal=0; signal < nsignal; signal ++) { + sigma_state[3*signal] = 0.; // not used + sigma_state[3*signal+1] = 0.000001; + sigma_state[3*signal+2] = 0.000001; + } - double rho = 0.9; // correlation - double dt = 0.2; // sampling period + vpColVector velocity_measure(size_measure_vector); - for (unsigned int signal=0; signal < nsignal; signal ++) - velocity_measure[signal] = 3+2*signal; + double rho = 0.9; // correlation + double dt = 0.2; // sampling period - kalman.verbose(false); - kalman.initFilter(nsignal, sigma_state, sigma_measure, rho, dt); + for (unsigned int signal=0; signal < nsignal; signal ++) + velocity_measure[signal] = 3+2*signal; + kalman.verbose(false); + kalman.initFilter(nsignal, sigma_state, sigma_measure, rho, dt); - for (unsigned int iter=0; iter <= niter; iter++) { - std::cout << "-------- iter " << iter << " ------------" << std::endl; - for (unsigned int signal=0; signal < nsignal; signal ++) { - velocity_measure[signal] = 3+2*signal - + 0.3*sin(vpMath::rad(360./niter*iter)); - } - std::cout << "measure : " << velocity_measure.t() << std::endl; - flog << velocity_measure.t(); + for (unsigned int iter=0; iter <= niter; iter++) { + std::cout << "-------- iter " << iter << " ------------" << std::endl; + for (unsigned int signal=0; signal < nsignal; signal ++) { + velocity_measure[signal] = 3+2*signal + + 0.3*sin(vpMath::rad(360./niter*iter)); + } + std::cout << "measure : " << velocity_measure.t() << std::endl; - // kalman.prediction(); - kalman.filter(velocity_measure); - flog << kalman.Xest.t(); - flog << kalman.Xpre.t(); + flog << velocity_measure.t(); - std::cout << "Xest: " << kalman.Xest.t() << std::endl; - std::cout << "Xpre: " << kalman.Xpre.t() << std::endl; + // kalman.prediction(); + kalman.filter(velocity_measure); + flog << kalman.Xest.t(); + flog << kalman.Xpre.t(); - flog << std::endl; - } + std::cout << "Xest: " << kalman.Xest.t() << std::endl; + std::cout << "Xpre: " << kalman.Xpre.t() << std::endl; + + flog << std::endl; + } - flog.close(); - return 0; + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 0; + } } diff --git a/test/math/testKalmanVelocity.cpp b/test/math/testKalmanVelocity.cpp index e5f3f7700ab12250d8ab8686a834e5225ad9672d..25f5b7c02a299ce1a6e99a9a8292f03728715ae5 100644 --- a/test/math/testKalmanVelocity.cpp +++ b/test/math/testKalmanVelocity.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testKalmanVelocity.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testKalmanVelocity.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,79 +58,85 @@ typedef enum { int main() { - unsigned int nsignal = 2; // Number of signal to filter - unsigned int niter = 200; - unsigned int size_state_vector = 2*nsignal; - unsigned int size_measure_vector = 1*nsignal; - //vpMeasureType measure_t = Velocity; - vpMeasureType measure_t = Position; - - std::string filename = "/tmp/log.dat"; - std::ofstream flog(filename.c_str()); - - vpLinearKalmanFilterInstantiation kalman; + try { + unsigned int nsignal = 2; // Number of signal to filter + unsigned int niter = 200; + unsigned int size_state_vector = 2*nsignal; + unsigned int size_measure_vector = 1*nsignal; + //vpMeasureType measure_t = Velocity; + vpMeasureType measure_t = Position; + + std::string filename = "/tmp/log.dat"; + std::ofstream flog(filename.c_str()); + + vpLinearKalmanFilterInstantiation kalman; + + vpColVector sigma_measure(size_measure_vector); + for (unsigned int signal=0; signal < nsignal; signal ++) + sigma_measure = 0.000001; + vpColVector sigma_state(size_state_vector); + + switch (measure_t) { + case Velocity: + for (unsigned int signal=0; signal < nsignal; signal ++) { + sigma_state[2*signal] = 0.; // not used + sigma_state[2*signal+1] = 0.000001; + } + break; + case Position: + for (unsigned int signal=0; signal < nsignal; signal ++) { + sigma_state[2*signal] = 0.000001; + sigma_state[2*signal+1] = 0; // not used + } + break; + } - vpColVector sigma_measure(size_measure_vector); - for (unsigned int signal=0; signal < nsignal; signal ++) - sigma_measure = 0.000001; - vpColVector sigma_state(size_state_vector); + vpColVector measure(size_measure_vector); - switch (measure_t) { - case Velocity: for (unsigned int signal=0; signal < nsignal; signal ++) { - sigma_state[2*signal] = 0.; // not used - sigma_state[2*signal+1] = 0.000001; + measure[signal] = 3+2*signal; } - break; - case Position: - for (unsigned int signal=0; signal < nsignal; signal ++) { - sigma_state[2*signal] = 0.000001; - sigma_state[2*signal+1] = 0; // not used - } - break; - } - - vpColVector measure(size_measure_vector); - for (unsigned int signal=0; signal < nsignal; signal ++) { - measure[signal] = 3+2*signal; - } + kalman.verbose(true); + + vpLinearKalmanFilterInstantiation::vpStateModel model; + double dt = 0.04; // Sampling period + double rho = 0.5; + double dummy = 0; // non used parameter + switch (measure_t) { + case Velocity: + model = vpLinearKalmanFilterInstantiation::stateConstVelWithColoredNoise_MeasureVel; + kalman.setStateModel(model); + kalman.initFilter(nsignal, sigma_state, sigma_measure, rho, dummy); + break; + case Position: + model = vpLinearKalmanFilterInstantiation::stateConstVel_MeasurePos; + kalman.setStateModel(model); + kalman.initFilter(nsignal, sigma_state, sigma_measure, dummy, dt); + break; + } - kalman.verbose(true); - - vpLinearKalmanFilterInstantiation::vpStateModel model; - double dt = 0.04; // Sampling period - double rho = 0.5; - double dummy = 0; // non used parameter - switch (measure_t) { - case Velocity: - model = vpLinearKalmanFilterInstantiation::stateConstVelWithColoredNoise_MeasureVel; - kalman.setStateModel(model); - kalman.initFilter(nsignal, sigma_state, sigma_measure, rho, dummy); - break; - case Position: - model = vpLinearKalmanFilterInstantiation::stateConstVel_MeasurePos; - kalman.setStateModel(model); - kalman.initFilter(nsignal, sigma_state, sigma_measure, dummy, dt); - break; - } + for (unsigned int iter=0; iter <= niter; iter++) { + std::cout << "-------- iter " << iter << " ------------" << std::endl; + for (unsigned int signal=0; signal < nsignal; signal ++) { + measure[signal] = 3+2*signal + 0.3*sin(vpMath::rad(360./niter*iter)); + } + std::cout << "measure : " << measure.t() << std::endl; - for (unsigned int iter=0; iter <= niter; iter++) { - std::cout << "-------- iter " << iter << " ------------" << std::endl; - for (unsigned int signal=0; signal < nsignal; signal ++) { - measure[signal] = 3+2*signal + 0.3*sin(vpMath::rad(360./niter*iter)); - } - std::cout << "measure : " << measure.t() << std::endl; + flog << measure.t(); - flog << measure.t(); + // kalman.prediction(); + kalman.filter(measure); + flog << kalman.Xest.t() << std::endl; - // kalman.prediction(); - kalman.filter(measure); - flog << kalman.Xest.t() << std::endl; + std::cout << "Xest: " << kalman.Xest.t() << std::endl; + } - std::cout << "Xest: " << kalman.Xest.t() << std::endl; + flog.close(); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - flog.close(); - return 0; } diff --git a/test/math/testMatrix.cpp b/test/math/testMatrix.cpp index e3c392afeafba6b149a1120947a8931eb17d5917..02aad08306bce368a47e6bcab18b39a189dad302 100644 --- a/test/math/testMatrix.cpp +++ b/test/math/testMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testMatrix.cpp 5242 2015-01-30 18:22:41Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -90,15 +93,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -107,7 +110,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -118,137 +121,166 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + { + vpMatrix M(4,5); + int val = 0; + for(unsigned int i=0; i<M.getRows(); i++) { + for(unsigned int j=0; j<M.getCols(); j++) { + M[i][j] = val++; + } + } + std::cout <<"M "; + M.print (std::cout, 4); + + vpMatrix N; + N.init(M, 0, 1, 2, 3); + std::cout <<"N "; + N.print (std::cout, 4); + } + { + + std::cout << "------------------------" << std::endl; + std::cout << "--- TEST PRETTY PRINT---" << std::endl; + std::cout << "------------------------" << std::endl; + vpMatrix M ; + M.eye(4); + + std::cout << "call std::cout << M;" << std::endl; + std::cout << M << std::endl; + + std::cout << "call M.print (std::cout, 4);" << std::endl; + M.print (std::cout, 4); + + std::cout << "------------------------" << std::endl; + M.resize(3,3) ; + M.eye(3); + M[1][0]=1.235; + M[1][1]=12.345; + M[1][2]=.12345; + std::cout << "call std::cout << M;" << std::endl; + std::cout << M; + std::cout << "call M.print (std::cout, 6);" << std::endl; + M.print (std::cout, 6); + std::cout << std::endl; + + std::cout << "------------------------" << std::endl; + M[0][0]=-1.235; + M[1][0]=-12.235; + + std::cout << "call std::cout << M;" << std::endl; + std::cout << M << std::endl; + + std::cout << "call M.print (std::cout, 10);" << std::endl; + M.print (std::cout, 10); + std::cout << std::endl; + + std::cout << "call M.print (std::cout, 2);" << std::endl; + M.print (std::cout, 2); + std::cout << std::endl; + + std::cout << "------------------------" << std::endl; + M.resize(3,3) ; + M.eye(3); + M[0][2]=-0.0000000876; + std::cout << "call std::cout << M;" << std::endl; + std::cout << M << std::endl; + + std::cout << "call M.print (std::cout, 4);" << std::endl; + M.print (std::cout, 4); + std::cout << std::endl; + std::cout << "call M.print (std::cout, 10, \"M\");" << std::endl; + M.print (std::cout, 10, "M"); + std::cout << std::endl; + std::cout << "call M.print (std::cout, 20, \"M\");" << std::endl; + M.print (std::cout, 20, "M"); + std::cout << std::endl; + + + std::cout << "------------------------" << std::endl; + std::cout << "--- TEST RESIZE --------" << std::endl; + std::cout << "------------------------" << std::endl; + std::cout << "5x5" << std::endl; + M.resize(5,5,false); + std::cout << M << std::endl; + std::cout << "3x2" << std::endl; + M.resize(3,2,false); + std::cout << M << std::endl; + std::cout << "2x2" << std::endl; + M.resize(2,2,false); + std::cout << M << std::endl; + std::cout << "------------------------" << std::endl; + + vpVelocityTwistMatrix vMe; + vpMatrix A(1,6),B; + + A=1.0; + //vMe=1.0; + B=A*vMe; + + std::cout << "------------------------" << std::endl; + std::cout << "--- TEST vpRowVector * vpColVector" << std::endl; + std::cout << "------------------------" << std::endl; + vpRowVector r(3); + r[0] = 2; + r[1] = 3; + r[2] = 4; + + vpColVector c(3); + c[0] = 1; + c[1] = 2; + c[2] = -1; + + double rc = r * c; + + r.print(std::cout, 2, "r"); + c.print(std::cout, 2, "c"); + std::cout << "r * c = " << rc << std::endl; + + std::cout << "------------------------" << std::endl; + std::cout << "--- TEST vpRowVector * vpMatrix" << std::endl; + std::cout << "------------------------" << std::endl; + M.resize(3,3) ; + M.eye(3); + + M[1][0] = 1.5; + M[2][0] = 2.3; + + vpRowVector rM = r * M; + + r.print(std::cout, 2, "r"); + M.print(std::cout, 10, "M"); + std::cout << "r * M = " << rM << std::endl; + + std::cout << "------------------------" << std::endl; + std::cout << "--- TEST vpGEMM " << std::endl; + std::cout << "------------------------" << std::endl; + M.resize(3,3) ; + M.eye(3); + vpMatrix N(3, 3); + N[0][0] = 2; + N[1][0] = 1.2; + N[1][2] = 0.6; + N[2][2] = 0.25; + + vpMatrix C(3, 3); + C.eye(3); + + vpMatrix D; + + //realise the operation D = 2 * M^T * N + 3 C + vpGEMM(M, N, 2, C, 3, D, VP_GEMM_A_T); + std::cout << D << std::endl; + return 0; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - vpTRACE("------------------------"); - vpTRACE("--- TEST PRETTY PRINT---"); - vpTRACE("------------------------"); - vpMatrix M ; - M.eye(4); - - vpTRACE("call std::cout << M;"); - std::cout << M << std::endl; - - vpTRACE("call M.print (std::cout, 4);"); - M.print (std::cout, 4); - - vpTRACE("------------------------"); - M.resize(3,3) ; - M.eye(3); - M[1][0]=1.235; - M[1][1]=12.345; - M[1][2]=.12345; - vpTRACE("call std::cout << M;"); - std::cout << M; - vpTRACE("call M.print (std::cout, 6);"); - M.print (std::cout, 6); - - vpTRACE("------------------------"); - M[0][0]=-1.235; - M[1][0]=-12.235; - - vpTRACE("call std::cout << M;"); - std::cout << M; - - vpTRACE("call M.print (std::cout, 10);"); - M.print (std::cout, 10); - - vpTRACE("call M.print (std::cout, 2);"); - M.print (std::cout, 2); - - vpTRACE("------------------------"); - M.resize(3,3) ; - M.eye(3); - M[0][2]=-0.0000000876; - vpTRACE("call std::cout << M;"); - std::cout << M; - - vpTRACE("call M.print (std::cout, 4);"); - M.print (std::cout, 4); - vpTRACE("call M.print (std::cout, 10, \"M\");"); - M.print (std::cout, 10, "M"); - vpTRACE("call M.print (std::cout, 20, \"M\");"); - M.print (std::cout, 20, "M"); - - - vpTRACE("------------------------"); - vpTRACE("--- TEST RESIZE --------"); - vpTRACE("------------------------"); - vpCTRACE << "5x5" << std::endl; - M.resize(5,5,false); - vpCTRACE << std::endl<< M; - vpCTRACE << "3x2" << std::endl; - M.resize(3,2,false); - vpCTRACE <<std::endl<< M; - vpCTRACE << "2x2" << std::endl; - M.resize(2,2,false); - vpCTRACE << std::endl<<M; - vpTRACE("------------------------"); - - - vpVelocityTwistMatrix vMe; - vpMatrix A(1,6),B; - - A=1.0; - //vMe=1.0; - B=A*vMe; - - vpTRACE("------------------------"); - vpTRACE("--- TEST vpRowVector * vpColVector"); - vpTRACE("------------------------"); - vpRowVector r(3); - r[0] = 2; - r[1] = 3; - r[2] = 4; - - vpColVector c(3); - c[0] = 1; - c[1] = 2; - c[2] = -1; - - double rc = r * c; - - r.print(std::cout, 2, "r"); - c.print(std::cout, 2, "c"); - std::cout << "r * c = " << rc << std::endl; - - vpTRACE("------------------------"); - vpTRACE("--- TEST vpRowVector * vpMatrix"); - vpTRACE("------------------------"); - M.resize(3,3) ; - M.eye(3); - - M[1][0] = 1.5; - M[2][0] = 2.3; - - vpRowVector rM = r * M; - - r.print(std::cout, 2, "r"); - M.print(std::cout, 10, "M"); - std::cout << "r * M = " << rM << std::endl; - - vpTRACE("------------------------"); - vpTRACE("--- TEST vpGEMM "); - vpTRACE("------------------------"); - M.resize(3,3) ; - M.eye(3); - vpMatrix N(3, 3); - N[0][0] = 2; - N[1][0] = 1.2; - N[1][2] = 0.6; - N[2][2] = 0.25; - - vpMatrix C(3, 3); - C.eye(3); - - vpMatrix D; - - //realise the operation D = 2 * M^T * N + 3 C - vpGEMM(M, N, 2, C, 3, D, VP_GEMM_A_T); - std::cout << D << std::endl; - } diff --git a/test/math/testMatrixException.cpp b/test/math/testMatrixException.cpp index fb81e5b277a8f7baf4f6c2c97b1f62753046cb20..d9b8b88db1011e606bdcd88362a12fd7cf74c5e4 100644 --- a/test/math/testMatrixException.cpp +++ b/test/math/testMatrixException.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testMatrixException.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testMatrixException.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,6 +58,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -88,15 +91,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -105,7 +108,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -116,39 +119,42 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpMatrix M ; - vpMatrix M1(2,3) ; - vpMatrix M2(3,3) ; - vpMatrix M3(2,2) ; + vpMatrix M ; + vpMatrix M1(2,3) ; + vpMatrix M2(3,3) ; + vpMatrix M3(2,2) ; - vpTRACE("test matrix size in multiply") ; + vpTRACE("test matrix size in multiply") ; - try - { - M = M1*M3 ; - } - catch (vpMatrixException me) - { - vpCTRACE ; - std::cout << me << std::endl ; - } + try + { + M = M1*M3 ; + } + catch (vpMatrixException me) + { + std::cout << me << std::endl ; + } - vpTRACE("test matrix size in addition") ; + vpTRACE("test matrix size in addition") ; - try - { - M = M1+M3 ; + try + { + M = M1+M3 ; + } + catch (vpMatrixException me) + { + std::cout << me << std::endl ; + } } - catch (vpMatrixException me) - { - vpCTRACE ; - std::cout << me << std::endl ; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - } diff --git a/test/math/testMatrixInverse.cpp b/test/math/testMatrixInverse.cpp index 708025a9f1eb8f64c8e6c8e14acef96859e581d0..6051083539767d48694fc0925a16d1fa18832944 100644 --- a/test/math/testMatrixInverse.cpp +++ b/test/math/testMatrixInverse.cpp @@ -3,7 +3,7 @@ * $Id: testSvd.cpp 3857 2012-07-25 11:47:30Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,6 +61,14 @@ // List of allowed command line options #define GETOPTARGS "n:i:pf:r:c:vh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, + unsigned int& nb_matrices, unsigned int& nb_iterations, + bool& use_plot_file, std::string& plotfile, + unsigned int& nbrows, unsigned int& nbcols, bool& verbose); +void writeTime(const char *name, double time); +vpMatrix makeRandomMatrix(unsigned int nbrows, unsigned int nbcols); + /*! Print the program options. @@ -129,36 +137,36 @@ bool getOptions(int argc, const char **argv, bool& use_plot_file, std::string& plotfile, unsigned int& nbrows, unsigned int& nbcols, bool& verbose) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; case 'n': - nb_matrices = (unsigned int)atoi(optarg); + nb_matrices = (unsigned int)atoi(optarg_); break; case 'i': - nb_iterations = (unsigned int)atoi(optarg); + nb_iterations = (unsigned int)atoi(optarg_); break; case 'f': - plotfile = optarg; + plotfile = optarg_; use_plot_file = true; break; case 'p': use_plot_file = true; break; case 'r': - nbrows = (unsigned int)atoi(optarg); + nbrows = (unsigned int)atoi(optarg_); break; case 'c': - nbcols = (unsigned int)atoi(optarg); + nbcols = (unsigned int)atoi(optarg_); break; case 'v': verbose = true; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -167,20 +175,20 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } return true; } -void writeTime(const char *name, double time){ +void writeTime(const char *name, double time) +{ std::cout << name << " time=" << time << " ms." << std::endl; } - - -vpMatrix makeRandomMatrix(unsigned int nbrows, unsigned int nbcols){ +vpMatrix makeRandomMatrix(unsigned int nbrows, unsigned int nbcols) +{ vpMatrix A; A.resize(nbrows,nbcols); @@ -195,113 +203,121 @@ int main(int argc, const char ** argv) { #ifdef VISP_HAVE_LAPACK - unsigned int nb_matrices=1000; - unsigned int nb_iterations=10; - unsigned int nb_rows = 6; - unsigned int nb_cols = 6; - bool verbose = false; - std::string plotfile("plot.txt"); - bool use_plot_file=false; - std::ofstream of; - - double t, qr_time, lu_time,pi_time,chol_time; - // Read the command line options - if (getOptions(argc, argv, nb_matrices,nb_iterations,use_plot_file,plotfile,nb_rows,nb_cols,verbose) == false) { - exit (-1); - } - - if(use_plot_file){ - of.open(plotfile.c_str()); - } - - for(unsigned int iter=0;iter<nb_iterations;iter++){ - std::vector<vpMatrix> benchQR; - std::vector<vpMatrix> benchLU; - std::vector<vpMatrix> benchCholesky; - std::vector<vpMatrix> benchPseudoInverse; - if(verbose) - std::cout << "********* generating matrices for iteration " << iter << "." << std::endl; - for(unsigned int i=0;i<nb_matrices;i++){ - vpMatrix cur; - double det=0.; - //don't put singular matrices in the benchmark - for(cur=makeRandomMatrix(nb_rows,nb_cols);std::abs(det=cur.AtA().det())<.01;cur = makeRandomMatrix(nb_rows,nb_cols)) - if(verbose){ - std::cout << "Generated random matrix A*tA=" << std::endl << cur.AtA() << std::endl; - std::cout << "generated random matrix not invertibleL: det="<<det<< ". Retrying..." << std::endl; - } - benchCholesky.push_back(cur); - benchQR.push_back(cur); - benchLU.push_back(cur); - benchPseudoInverse.push_back(cur); + try { + unsigned int nb_matrices=1000; + unsigned int nb_iterations=10; + unsigned int nb_rows = 6; + unsigned int nb_cols = 6; + bool verbose = false; + std::string plotfile("plot.txt"); + bool use_plot_file=false; + std::ofstream of; + + double t, qr_time, lu_time,pi_time,chol_time; + // Read the command line options + if (getOptions(argc, argv, nb_matrices,nb_iterations,use_plot_file,plotfile,nb_rows,nb_cols,verbose) == false) { + exit (-1); } - if(verbose) - std::cout << "\t Inverting " << benchCholesky[0].AtA().getRows() << "x" << benchCholesky[0].AtA().getCols() << " matrix using cholesky decomposition." << std::endl; - t = vpTime::measureTimeMs() ; - for(unsigned int i=0;i<nb_matrices;i++){ - benchCholesky[i]=benchCholesky[i].AtA().inverseByCholesky()*benchCholesky[i].transpose(); - } - chol_time = vpTime::measureTimeMs() - t ; - - if(verbose) - std::cout << "\t Inverting " << benchLU[0].AtA().getRows() << "x" << benchLU[0].AtA().getCols() << " matrix using LU decomposition." << std::endl; - t = vpTime::measureTimeMs() ; - for(unsigned int i=0;i<nb_matrices;i++) - benchLU[i] = benchLU[i].AtA().inverseByLU()*benchLU[i].transpose(); - lu_time = vpTime::measureTimeMs() -t ; - - if(verbose) - std::cout << "\t Inverting " << benchQR[0].AtA().getRows() << "x" << benchQR[0].AtA().getCols() << " matrix using QR decomposition." << std::endl; - t = vpTime::measureTimeMs() ; - for(unsigned int i=0;i<nb_matrices;i++){ - benchQR[i]=benchQR[i].AtA().inverseByQR()*benchQR[i].transpose(); - } - qr_time = vpTime::measureTimeMs() - t ; - - if(verbose) - std::cout << "\t Inverting " << benchPseudoInverse[0].AtA().getRows() << "x" << benchPseudoInverse[0].AtA().getCols() << " matrix while computing pseudo-inverse." << std::endl; - t = vpTime::measureTimeMs() ; - for(unsigned int i=0;i<nb_matrices;i++){ - benchPseudoInverse[i]=benchPseudoInverse[i].pseudoInverse(); - } - pi_time = vpTime::measureTimeMs() - t ; - - double avg_err_lu_qr=0.; - double avg_err_lu_pi=0.; - double avg_err_lu_chol=0.; - double avg_err_qr_pi=0.; - double avg_err_qr_chol=0.; - double avg_err_pi_chol=0.; - - for(unsigned int i=0;i<nb_matrices;i++){ - avg_err_lu_qr+= (benchQR[i]-benchLU[i]).euclideanNorm(); - avg_err_lu_pi+= (benchPseudoInverse[i]-benchLU[i]).euclideanNorm(); - avg_err_qr_pi+= (benchPseudoInverse[i]-benchQR[i]).euclideanNorm(); - avg_err_qr_chol+= (benchCholesky[i]-benchQR[i]).euclideanNorm(); - avg_err_lu_chol+= (benchCholesky[i]-benchLU[i]).euclideanNorm(); - avg_err_pi_chol+= (benchCholesky[i]-benchPseudoInverse[i]).euclideanNorm(); - } - - avg_err_lu_qr/=nb_matrices; - avg_err_lu_pi/=nb_matrices; - avg_err_qr_pi/=nb_matrices; - if(use_plot_file){ - of << iter << "\t" << lu_time << "\t" << qr_time << "\t" << pi_time << "\t" << chol_time << "\t" << avg_err_lu_qr << "\t" << avg_err_qr_pi << "\t" << avg_err_lu_pi << "\t" << avg_err_qr_chol << "\t" << avg_err_lu_chol << "\t" << avg_err_pi_chol << std::endl; + of.open(plotfile.c_str()); } - if(verbose || !use_plot_file){ - writeTime("LU",lu_time); - writeTime("QR",qr_time); - writeTime("Pseudo-inverse",pi_time); - writeTime("Cholesky",chol_time); + + for(unsigned int iter=0;iter<nb_iterations;iter++){ + std::vector<vpMatrix> benchQR; + std::vector<vpMatrix> benchLU; + std::vector<vpMatrix> benchCholesky; + std::vector<vpMatrix> benchPseudoInverse; + if(verbose) + std::cout << "********* generating matrices for iteration " << iter << "." << std::endl; + for(unsigned int i=0;i<nb_matrices;i++){ + vpMatrix cur; + double det=0.; + //don't put singular matrices in the benchmark + for(cur=makeRandomMatrix(nb_rows,nb_cols);std::abs(det=cur.AtA().det())<.01;cur = makeRandomMatrix(nb_rows,nb_cols)) + if(verbose){ + std::cout << "Generated random matrix A*tA=" << std::endl << cur.AtA() << std::endl; + std::cout << "generated random matrix not invertibleL: det="<<det<< ". Retrying..." << std::endl; + } + benchCholesky.push_back(cur); + benchQR.push_back(cur); + benchLU.push_back(cur); + benchPseudoInverse.push_back(cur); + } + + if(verbose) + std::cout << "\t Inverting " << benchCholesky[0].AtA().getRows() << "x" << benchCholesky[0].AtA().getCols() << " matrix using cholesky decomposition." << std::endl; + t = vpTime::measureTimeMs() ; + for(unsigned int i=0;i<nb_matrices;i++){ + benchCholesky[i]=benchCholesky[i].AtA().inverseByCholesky()*benchCholesky[i].transpose(); + } + chol_time = vpTime::measureTimeMs() - t ; + + if(verbose) + std::cout << "\t Inverting " << benchLU[0].AtA().getRows() << "x" << benchLU[0].AtA().getCols() << " matrix using LU decomposition." << std::endl; + t = vpTime::measureTimeMs() ; + for(unsigned int i=0;i<nb_matrices;i++) + benchLU[i] = benchLU[i].AtA().inverseByLU()*benchLU[i].transpose(); + lu_time = vpTime::measureTimeMs() -t ; + + if(verbose) + std::cout << "\t Inverting " << benchQR[0].AtA().getRows() << "x" << benchQR[0].AtA().getCols() << " matrix using QR decomposition." << std::endl; + t = vpTime::measureTimeMs() ; + for(unsigned int i=0;i<nb_matrices;i++){ + benchQR[i]=benchQR[i].AtA().inverseByQR()*benchQR[i].transpose(); + } + qr_time = vpTime::measureTimeMs() - t ; + + if(verbose) + std::cout << "\t Inverting " << benchPseudoInverse[0].AtA().getRows() << "x" << benchPseudoInverse[0].AtA().getCols() << " matrix while computing pseudo-inverse." << std::endl; + t = vpTime::measureTimeMs() ; + for(unsigned int i=0;i<nb_matrices;i++){ + benchPseudoInverse[i]=benchPseudoInverse[i].pseudoInverse(); + } + pi_time = vpTime::measureTimeMs() - t ; + + double avg_err_lu_qr=0.; + double avg_err_lu_pi=0.; + double avg_err_lu_chol=0.; + double avg_err_qr_pi=0.; + double avg_err_qr_chol=0.; + double avg_err_pi_chol=0.; + + for(unsigned int i=0;i<nb_matrices;i++){ + avg_err_lu_qr+= (benchQR[i]-benchLU[i]).euclideanNorm(); + avg_err_lu_pi+= (benchPseudoInverse[i]-benchLU[i]).euclideanNorm(); + avg_err_qr_pi+= (benchPseudoInverse[i]-benchQR[i]).euclideanNorm(); + avg_err_qr_chol+= (benchCholesky[i]-benchQR[i]).euclideanNorm(); + avg_err_lu_chol+= (benchCholesky[i]-benchLU[i]).euclideanNorm(); + avg_err_pi_chol+= (benchCholesky[i]-benchPseudoInverse[i]).euclideanNorm(); + } + + avg_err_lu_qr/=nb_matrices; + avg_err_lu_pi/=nb_matrices; + avg_err_qr_pi/=nb_matrices; + + if(use_plot_file){ + of << iter << "\t" << lu_time << "\t" << qr_time << "\t" << pi_time << "\t" << chol_time << "\t" << avg_err_lu_qr << "\t" << avg_err_qr_pi << "\t" << avg_err_lu_pi << "\t" << avg_err_qr_chol << "\t" << avg_err_lu_chol << "\t" << avg_err_pi_chol << std::endl; + } + if(verbose || !use_plot_file){ + writeTime("LU",lu_time); + writeTime("QR",qr_time); + writeTime("Pseudo-inverse",pi_time); + writeTime("Cholesky",chol_time); + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } + #else (void)argc; (void)argv; std::cout << "You don't have lapack installed" << std::endl; -#endif return 0; +#endif } diff --git a/test/math/testRobust.cpp b/test/math/testRobust.cpp index 6c3370f971c973f8f8a72e0affc15e8af43c8bf3..5bf3cd4d90a5c86e702f66d16200439883324579 100644 --- a/test/math/testRobust.cpp +++ b/test/math/testRobust.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobust.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobust.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,6 +57,9 @@ // List of allowed command line options #define GETOPTARGS "ho:" +void usage(const char *name, const char *badparam, std::string ofilename); +bool getOptions(int argc, const char **argv, std::string &ofilename); + /*! Print the program options. @@ -107,16 +110,16 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv, std::string &ofilename) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'o': ofilename = optarg; break; + case 'o': ofilename = optarg_; break; case 'h': usage(argv[0], NULL, ofilename); return false; break; default: - usage(argv[0], optarg, ofilename); + usage(argv[0], optarg_, ofilename); return false; break; } } @@ -125,7 +128,7 @@ bool getOptions(int argc, const char **argv, std::string &ofilename) // standalone param or error usage(argv[0], NULL, ofilename); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -137,74 +140,81 @@ bool getOptions(int argc, const char **argv, std::string &ofilename) int main(int argc, const char ** argv) { - std::string ofilename; - std::string username; + try { + std::string ofilename; + std::string username; - // Set the default output filename -#ifdef WIN32 - ofilename = "C:/temp"; + // Set the default output filename +#if defined(_WIN32) + ofilename = "C:/temp"; #else - ofilename = "/tmp"; + ofilename = "/tmp"; #endif - // Get the user login name - vpIoTools::getUserName(username); + // Get the user login name + vpIoTools::getUserName(username); + + // Append to the output filename string, the login name of the user + ofilename = ofilename + "/" + username; + + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(ofilename) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(ofilename); + } + catch (...) { + usage(argv[0], NULL, ofilename); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << ofilename << std::endl; + std::cerr << " Check your -o " << ofilename << " option " << std::endl; + exit(-1); + } + } - // Append to the output filename string, the login name of the user - ofilename = ofilename + "/" + username; + // Append to the output filename string, the name of the file + ofilename = ofilename + "/w.dat"; - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(ofilename) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(ofilename); + // Read the command line options + if (getOptions(argc, argv, ofilename) == false) { + exit (-1); } - catch (...) { + + double sig = 1 ; + + double w ; + std::ofstream f; + std::cout << "Create file: " << ofilename << std::endl; + f.open(ofilename.c_str()); + if (f.fail()) { usage(argv[0], NULL, ofilename); std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot create " << ofilename << std::endl; + std::cerr << " Cannot create the file: " << ofilename << std::endl; std::cerr << " Check your -o " << ofilename << " option " << std::endl; exit(-1); - } - } - // Append to the output filename string, the name of the file - ofilename = ofilename + "/w.dat"; - - // Read the command line options - if (getOptions(argc, argv, ofilename) == false) { - exit (-1); - } - - double sig = 1 ; - - double w ; - std::ofstream f; - std::cout << "Create file: " << ofilename << std::endl; - f.open(ofilename.c_str()); - if (f == NULL) { - usage(argv[0], NULL, ofilename); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create the file: " << ofilename << std::endl; - std::cerr << " Check your -o " << ofilename << " option " << std::endl; - exit(-1); - - } - double x = -10 ; - while (x<10) - { - if (fabs(x/sig)<=(4.6851)) - { - w = vpMath::sqr(1-vpMath::sqr(x/(sig*4.6851))); } - else + double x = -10 ; + while (x<10) { - w = 0; + if (fabs(x/sig)<=(4.6851)) + { + w = vpMath::sqr(1-vpMath::sqr(x/(sig*4.6851))); + } + else + { + w = 0; + } + f << x <<" "<<w <<std::endl ; + x+= 0.01 ; } - f << x <<" "<<w <<std::endl ; - x+= 0.01 ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/math/testRotation.cpp b/test/math/testRotation.cpp index 9dec934feabe7030f585ae8ecdbc1c6d73c3e078..d87369fda53c734f2b66afff9176f1e5f4d31a4e 100644 --- a/test/math/testRotation.cpp +++ b/test/math/testRotation.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRotation.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRotation.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -59,6 +59,9 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); + /*! Print the program options. @@ -89,15 +92,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -106,7 +109,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -117,90 +120,97 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - vpRotationMatrix R; - for(int i=-10;i<10;i++){ - for(int j=-10;j<10;j++){ - vpThetaUVector tu(vpMath::rad(90+i), vpMath::rad(170+j), vpMath::rad(45)) ; - tu.buildFrom(vpRotationMatrix(tu)); //put some coherence into rotation convention - - std::cout << "Initialization " <<std::endl ; - - double theta; - vpColVector u; - tu.extract(theta, u); - - std::cout << "theta=" << vpMath::deg(theta) << std::endl ; - std::cout << "u=" << u << std::endl ; - - std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; - R.buildFrom(tu) ; - - std::cout << "Matrix R" ; - if (R.isARotationMatrix()==1) std::cout <<" is a rotation matrix " << std::endl ; - else std::cout <<" is not a rotation matrix " << std::endl ; + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } + vpRotationMatrix R; + for(int i=-10;i<10;i++){ + for(int j=-10;j<10;j++){ + vpThetaUVector tu(vpMath::rad(90+i), vpMath::rad(170+j), vpMath::rad(45)) ; + tu.buildFrom(vpRotationMatrix(tu)); //put some coherence into rotation convention - std::cout << R << std::endl ; + std::cout << "Initialization " <<std::endl ; + + double theta; + vpColVector u; + tu.extract(theta, u); + + std::cout << "theta=" << vpMath::deg(theta) << std::endl ; + std::cout << "u=" << u << std::endl ; + + std::cout << "From vpThetaUVector to vpRotationMatrix " << std::endl ; + R.buildFrom(tu) ; + + std::cout << "Matrix R" ; + if (R.isARotationMatrix()==1) std::cout <<" is a rotation matrix " << std::endl ; + else std::cout <<" is not a rotation matrix " << std::endl ; + + std::cout << R << std::endl ; + + std::cout << "From vpRotationMatrix to vpQuaternionVector " << std::endl ; + vpQuaternionVector q(R); + std::cout << q <<std::endl ; + + R.buildFrom(q); + std::cout << "From vpQuaternionVector to vpRotationMatrix " << std::endl ; + + std::cout << "From vpRotationMatrix to vpRxyzVector " << std::endl ; + vpRxyzVector RxyzBuildFromR(R) ; + std::cout << RxyzBuildFromR <<std::endl ; - std::cout << "From vpRotationMatrix to vpQuaternionVector " << std::endl ; - vpQuaternionVector q(R); - std::cout << q <<std::endl ; - - R.buildFrom(q); - std::cout << "From vpQuaternionVector to vpRotationMatrix " << std::endl ; - - std::cout << "From vpRotationMatrix to vpRxyzVector " << std::endl ; - vpRxyzVector RxyzBuildFromR(R) ; - std::cout << RxyzBuildFromR <<std::endl ; - - - std::cout << "From vpRxyzVector to vpThetaUVector " << std::endl ; - std::cout << " use From vpRxyzVector to vpRotationMatrix " << std::endl ; - std::cout << " use From vpRotationMatrix to vpThetaUVector " << std::endl ; - - - vpThetaUVector tuBuildFromEu ; - tuBuildFromEu.buildFrom(R) ; - - std::cout << std::endl ; - std::cout << "result : should equivalent to the first one " << std::endl ; - - - double theta2; - vpColVector u2; - - tuBuildFromEu.extract(theta2, u2); - std::cout << "theta=" << vpMath::deg(theta2) << std::endl ; - std::cout << "u=" << u2 << std::endl ; - - assert(vpMath::abs(theta2-theta)<std::numeric_limits<double>::epsilon()*1e10); - assert(vpMath::abs(u[0]-u2[0])<std::numeric_limits<double>::epsilon()*1e10); - assert(vpMath::abs(u[1]-u2[1])<std::numeric_limits<double>::epsilon()*1e10); - assert(vpMath::abs(u[2]-u2[2])<std::numeric_limits<double>::epsilon()*1e10); + + std::cout << "From vpRxyzVector to vpThetaUVector " << std::endl ; + std::cout << " use From vpRxyzVector to vpRotationMatrix " << std::endl ; + std::cout << " use From vpRotationMatrix to vpThetaUVector " << std::endl ; + + + vpThetaUVector tuBuildFromEu ; + tuBuildFromEu.buildFrom(R) ; + + std::cout << std::endl ; + std::cout << "result : should equivalent to the first one " << std::endl ; + + + double theta2; + vpColVector u2; + + tuBuildFromEu.extract(theta2, u2); + std::cout << "theta=" << vpMath::deg(theta2) << std::endl ; + std::cout << "u=" << u2 << std::endl ; + + assert(vpMath::abs(theta2-theta)<std::numeric_limits<double>::epsilon()*1e10); + assert(vpMath::abs(u[0]-u2[0])<std::numeric_limits<double>::epsilon()*1e10); + assert(vpMath::abs(u[1]-u2[1])<std::numeric_limits<double>::epsilon()*1e10); + assert(vpMath::abs(u[2]-u2[2])<std::numeric_limits<double>::epsilon()*1e10); + } + vpRzyzVector rzyz(vpMath::rad(180), vpMath::rad(120), vpMath::rad(45)) ; + std::cout << "Initialization vpRzyzVector " <<std::endl ; + std::cout << rzyz << std::endl ; + std::cout << "From vpRzyzVector to vpRotationMatrix " << std::endl ; + R.buildFrom(rzyz) ; + std::cout << "From vpRotationMatrix to vpRzyzVector " << std::endl ; + vpRzyzVector rzyz_final ; + rzyz_final.buildFrom(R) ; + std::cout << rzyz_final << std::endl ; + + + vpRzyxVector rzyx(vpMath::rad(180), vpMath::rad(120), vpMath::rad(45)) ; + std::cout << "Initialization vpRzyxVector " <<std::endl ; + std::cout << rzyx << std::endl ; + std::cout << "From vpRzyxVector to vpRotationMatrix " << std::endl ; + R.buildFrom(rzyx) ; + std::cout << R << std::endl ; + std::cout << "From vpRotationMatrix to vpRzyxVector " << std::endl ; + vpRzyxVector rzyx_final ; + rzyx_final.buildFrom(R) ; + std::cout << rzyx_final << std::endl ; } - vpRzyzVector rzyz(vpMath::rad(180), vpMath::rad(120), vpMath::rad(45)) ; - std::cout << "Initialization vpRzyzVector " <<std::endl ; - std::cout << rzyz << std::endl ; - std::cout << "From vpRzyzVector to vpRotationMatrix " << std::endl ; - R.buildFrom(rzyz) ; - std::cout << "From vpRotationMatrix to vpRzyzVector " << std::endl ; - vpRzyzVector rzyz_final ; - rzyz_final.buildFrom(R) ; - std::cout << rzyz_final << std::endl ; - - - vpRzyxVector rzyx(vpMath::rad(180), vpMath::rad(120), vpMath::rad(45)) ; - std::cout << "Initialization vpRzyxVector " <<std::endl ; - std::cout << rzyx << std::endl ; - std::cout << "From vpRzyxVector to vpRotationMatrix " << std::endl ; - R.buildFrom(rzyx) ; - std::cout << R << std::endl ; - std::cout << "From vpRotationMatrix to vpRzyxVector " << std::endl ; - vpRzyxVector rzyx_final ; - rzyx_final.buildFrom(R) ; - std::cout << rzyx_final << std::endl ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/math/testSvd.cpp b/test/math/testSvd.cpp index c37bc71943549a3952ca8f4a53e4c2881a0d32c6..5963321ef4b1a8e2d7eadb5bf2b9cd66b3bba349 100644 --- a/test/math/testSvd.cpp +++ b/test/math/testSvd.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testSvd.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testSvd.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,13 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); +bool testSvdOpenCvGSLCoherence(double epsilon); +#ifdef VISP_HAVE_GSL +bool testRandom(double epsilon); +#endif + /*! Print the program options. @@ -90,15 +97,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -107,7 +114,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -116,7 +123,6 @@ bool getOptions(int argc, const char **argv) #define abs(x) ((x) < 0 ? - (x) : (x)) #ifdef VISP_HAVE_GSL - bool testRandom(double epsilon) { vpMatrix L0(6,6); @@ -126,8 +132,6 @@ bool testRandom(double epsilon) for (unsigned int j=0 ; j < L0.getCols() ; j++) L1[i][j] = L0[i][j] = (double)rand()/(double)RAND_MAX; - - vpColVector W0(L0.getCols()) ; vpMatrix V0(L0.getCols(), L0.getCols()) ; vpColVector W1(L1.getCols()) ; @@ -151,7 +155,8 @@ bool testRandom(double epsilon) #endif -bool testSvdOpenCvGSLCoherence(double epsilon){ +bool testSvdOpenCvGSLCoherence(double epsilon) +{ #if (VISP_HAVE_OPENCV_VERSION >= 0x020101) && defined (VISP_HAVE_GSL) // Require opencv >= 2.1.1 vpMatrix A; vpMatrix vA; @@ -188,72 +193,78 @@ bool testSvdOpenCvGSLCoherence(double epsilon){ int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - unsigned int i,j ; - vpMatrix L(60000,6), Ls ; - for (i=0 ; i < L.getRows() ; i++) - for (j=0 ; j < L.getCols() ; j++) - L[i][j] = 2*i+j + cos((double)(i+j))+((double)(i)) ; - // std::cout << L << std::endl ; - Ls = L ; - std::cout << "--------------------------------------"<<std::endl ; + vpMatrix L(60000,6), Ls ; + for (unsigned int i=0 ; i < L.getRows() ; i++) + for (unsigned int j=0 ; j < L.getCols() ; j++) + L[i][j] = 2*i+j + cos((double)(i+j))+((double)(i)) ; + // std::cout << L << std::endl ; + Ls = L ; + std::cout << "--------------------------------------"<<std::endl ; - vpColVector W(L.getCols()) ; - vpMatrix V(L.getCols(), L.getCols()) ; + vpColVector W(L.getCols()) ; + vpMatrix V(L.getCols(), L.getCols()) ; - double t = vpTime::measureTimeMs() ; - L.svdNr(W,V) ; - t = vpTime::measureTimeMs() -t ; + double t = vpTime::measureTimeMs() ; + L.svdNr(W,V) ; + t = vpTime::measureTimeMs() -t ; - std::cout <<"svdNr Numerical recipes \n time " <<t << std::endl; - std::cout << W.t() ; - std::cout << "--------------------------------------"<<std::endl ; + std::cout <<"svdNr Numerical recipes \n time " <<t << std::endl; + std::cout << W.t() ; + std::cout << "--------------------------------------"<<std::endl ; #ifdef VISP_HAVE_GSL - L = Ls ; - t = vpTime::measureTimeMs() ; - L.svdGsl(W,V) ; - t = vpTime::measureTimeMs() -t ; - std::cout <<"svdGsl_mod \n time " <<t << std::endl; - std::cout << W.t() ; - - std::cout << "--------------------------------------"<<std::endl ; - std::cout << "TESTING RANDOM MATRICES:" ; - - bool ret = true; - for(int i=0;i<2000;i++) - ret = ret & testRandom(0.00001); - if(ret) - std:: cout << "Success"<< std:: endl; - else - std:: cout << "Fail"<< std:: endl; - - std::cout << "--------------------------------------"<<std::endl ; + L = Ls ; + t = vpTime::measureTimeMs() ; + L.svdGsl(W,V) ; + t = vpTime::measureTimeMs() -t ; + std::cout <<"svdGsl_mod \n time " <<t << std::endl; + std::cout << W.t() ; + + std::cout << "--------------------------------------"<<std::endl ; + std::cout << "TESTING RANDOM MATRICES:" ; + + bool ret = true; + for(unsigned int i=0;i<2000;i++) + ret = ret & testRandom(0.00001); + if(ret) + std:: cout << "Success"<< std:: endl; + else + std:: cout << "Fail"<< std:: endl; + + std::cout << "--------------------------------------"<<std::endl ; #endif - std::cout << "--------------------------------------"<<std::endl ; - std::cout << "TESTING OPENCV-GSL coherence:" ; - - bool ret2 = true; - for(int i=0;i<1;i++) - ret2 = ret2 & testSvdOpenCvGSLCoherence(0.00001); - if(ret2) - std:: cout << "Success"<< std:: endl; - else - std:: cout << "Fail"<< std:: endl; - - std::cout << "--------------------------------------"<<std::endl ; - - L = Ls ; - t = vpTime::measureTimeMs() ; - L.svdFlake(W,V) ; - t = vpTime::measureTimeMs() -t ; - std::cout <<"svdFlake\n time " <<t << std::endl; - std::cout << W.t() ; + std::cout << "--------------------------------------"<<std::endl ; + std::cout << "TESTING OPENCV-GSL coherence:" ; + + bool ret2 = true; + for(unsigned int i=0;i<1;i++) + ret2 = ret2 & testSvdOpenCvGSLCoherence(0.00001); + if(ret2) + std:: cout << "Success"<< std:: endl; + else + std:: cout << "Fail"<< std:: endl; + + std::cout << "--------------------------------------"<<std::endl ; + + L = Ls ; + t = vpTime::measureTimeMs() ; + L.svdFlake(W,V) ; + t = vpTime::measureTimeMs() -t ; + std::cout <<"svdFlake\n time " <<t << std::endl; + std::cout << W.t() ; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/math/testTwistMatrix.cpp b/test/math/testTwistMatrix.cpp index c061d764880b318f5bbe833abececf30dee70536..f0cd2dba11d93198ad70994de8a881ca50e67861 100644 --- a/test/math/testTwistMatrix.cpp +++ b/test/math/testTwistMatrix.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testTwistMatrix.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testTwistMatrix.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,6 +64,8 @@ // List of allowed command line options #define GETOPTARGS "h" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); /*! @@ -95,15 +97,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -112,7 +114,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -123,56 +125,62 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } - - vpTRACE("--------------------------"); - vpTRACE("--- TEST vpVelocityTwistMatrix ---"); - vpTRACE("--------------------------"); + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - // Set the translation - vpTranslationVector cte; - cte[0] = 1.; - cte[1] = 0.5; - cte[2] = -1.; + vpTRACE("--------------------------"); + vpTRACE("--- TEST vpVelocityTwistMatrix ---"); + vpTRACE("--------------------------"); - // Set the rotation - vpRxyzVector cre; - cre[0] = M_PI/2.; - cre[1] = -M_PI/2.; - cre[2] = -M_PI/4.; + // Set the translation + vpTranslationVector cte; + cte[0] = 1.; + cte[1] = 0.5; + cte[2] = -1.; - // Build rotation matrix - vpRotationMatrix cRe(cre); + // Set the rotation + vpRxyzVector cre; + cre[0] = M_PI/2.; + cre[1] = -M_PI/2.; + cre[2] = -M_PI/4.; - // Build the twist matrix - vpVelocityTwistMatrix cVe(cte, cRe); + // Build rotation matrix + vpRotationMatrix cRe(cre); - vpTRACE("cVe twist matrix:"); - cVe.print (std::cout, 6); + // Build the twist matrix + vpVelocityTwistMatrix cVe(cte, cRe); + vpTRACE("cVe twist matrix:"); + cVe.print (std::cout, 6); - // Set a speed skew - vpColVector ev(6); - ev[0] = 1.; - ev[1] = 0.1; - ev[2] = -0.5; - ev[3] = M_PI/180.; - ev[4] = M_PI/18.; - ev[5] = M_PI/10.; + // Set a speed skew + vpColVector ev(6); - vpTRACE("ev colvector:"); - ev.print (std::cout, 6); + ev[0] = 1.; + ev[1] = 0.1; + ev[2] = -0.5; + ev[3] = M_PI/180.; + ev[4] = M_PI/18.; + ev[5] = M_PI/10.; - // Set a speed skew - vpColVector cv; + vpTRACE("ev colvector:"); + ev.print (std::cout, 6); - cv = cVe * ev; + // Set a speed skew + vpColVector cv; - vpTRACE("cv = cVe * ev:"); - cv.print (std::cout, 6); + cv = cVe * ev; + vpTRACE("cv = cVe * ev:"); + cv.print (std::cout, 6); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/network/CMakeLists.txt b/test/network/CMakeLists.txt index 40b5481fbfacc218f12bb7581591bd2422adae1d..26f45ab28b38772b75b4c4dc46ed756c53421e1f 100644 --- a/test/network/CMakeLists.txt +++ b/test/network/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,18 +43,22 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testClient.cpp testServer.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/network/testClient.cpp b/test/network/testClient.cpp index 0e5a9e92640f76ca90c132bcd29d318bd8913252..bdf7eb6516dc100cbf8fc9f1d5b61432dfb5fb66 100644 --- a/test/network/testClient.cpp +++ b/test/network/testClient.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testClient.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testClient.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,27 +50,32 @@ int main() { - std::string servername = "localhost"; - unsigned int port = 35000; + try { + std::string servername = "localhost"; + unsigned int port = 35000; - vpClient client; - client.connectToHostname(servername, port); - //client.connectToIP("127.0.0.1",port); + vpClient client; + client.connectToHostname(servername, port); + //client.connectToIP("127.0.0.1",port); - int val = 0; + int val = 0; - while(1) - { - if(client.send(&val) != sizeof(int)) //Sending the new value to the first client - std::cout << "Error while sending" << std::endl; - else - std::cout << "Sending : " << val << std::endl; - - if(client.receive(&val) != sizeof(int)) //Receiving a value from the first client + while(1) + { + if(client.send(&val) != sizeof(int)) //Sending the new value to the first client + std::cout << "Error while sending" << std::endl; + else + std::cout << "Sending : " << val << std::endl; + + if(client.receive(&val) != sizeof(int)) //Receiving a value from the first client std::cout << "Error while receiving" << std::endl; - else - std::cout << "Received : " << val << std::endl; + else + std::cout << "Received : " << val << std::endl; + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } diff --git a/test/network/testServer.cpp b/test/network/testServer.cpp index 1bb222f25b361c3d19504443252d341eb00b3b7d..e71f13b64524f01a7e9e822014af0deee7411014 100644 --- a/test/network/testServer.cpp +++ b/test/network/testServer.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testServer.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testServer.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,30 +50,35 @@ int main() { - int port = 35000; - vpServer serv(port); //Launch the server on localhost - serv.start(); - - bool run = true; - int val; - - while(run){ - serv.checkForConnections(); - - if(serv.getNumberOfClients() > 0) - { - if(serv.receive(&val) != sizeof(int)) //Receiving a value from the first client - std::cout << "Error while receiving" << std::endl; - else - std::cout << "Received : " << val << std::endl; - - val = val+1; - if(serv.send(&val) != sizeof(int)) //Sending the new value to the first client - std::cout << "Error while sending" << std::endl; - else - std::cout << "Sending : " << val << std::endl; + try { + int port = 35000; + vpServer serv(port); //Launch the server on localhost + serv.start(); + + bool run = true; + int val; + + while(run){ + serv.checkForConnections(); + + if(serv.getNumberOfClients() > 0) + { + if(serv.receive(&val) != sizeof(int)) //Receiving a value from the first client + std::cout << "Error while receiving" << std::endl; + else + std::cout << "Received : " << val << std::endl; + + val = val+1; + if(serv.send(&val) != sizeof(int)) //Sending the new value to the first client + std::cout << "Error while sending" << std::endl; + else + std::cout << "Sending : " << val << std::endl; + } } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - return 0; } diff --git a/test/pose/CMakeLists.txt b/test/pose/CMakeLists.txt index 1343a5565635e0bfad53633e553991e0fdc0589e..6456016e1899b93179406d15bb48f77f6b57af3c 100644 --- a/test/pose/CMakeLists.txt +++ b/test/pose/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testPose.cpp testPoseRansac.cpp testFindMatch.cpp @@ -51,20 +51,19 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/pose/testFindMatch.cpp b/test/pose/testFindMatch.cpp index ea11e5dd6c43bf50f113d3c1c9a9f66646d63a3d..62223fb3846d66909081e4ebb57779d302cff051 100644 --- a/test/pose/testFindMatch.cpp +++ b/test/pose/testFindMatch.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testFindMatch.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testFindMatch.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,60 +62,66 @@ int main() { + try { + std::cout << "Find Matches using Ransac" << std::endl; + unsigned int nb3D = 5; + unsigned int nb2D = 5; + std::vector<vpPoint> P(nb3D); + std::vector<vpPoint> p(nb2D); + + P[0].setWorldCoordinates(-L,-L, 0 ) ; + P[1].setWorldCoordinates(L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-L,L, 0 ) ; + P[4].setWorldCoordinates(-0,L/2., L ) ; + + vpHomogeneousMatrix cMo_ref(0, 0.2, 1, vpMath::rad(3), vpMath::rad(-2), vpMath::rad(10)) ; + + for(unsigned int i=0 ; i < nb3D ; i++) + { + vpPoint pt = P[i]; + pt.project(cMo_ref); + p[i].set_x(pt.get_x()); + p[i].set_y(pt.get_y()); + } + + unsigned int ninliers ; + std::vector<vpPoint> inliers; + double threshold = 1e-6; + unsigned int nbInlierToReachConsensus = nb3D; + + vpHomogeneousMatrix cMo ; + + vpPose::findMatch(p,P,nbInlierToReachConsensus,threshold,ninliers,inliers,cMo); + + std::cout << "Inliers: " << std::endl; + for (unsigned int i = 0; i < inliers.size() ; i++) + { + inliers[i].print() ; + std::cout << std::endl; + } + + std::cout << "cMo :\n" << vpPoseVector(cMo).t() << std::endl << std::endl; + + vpPoseVector pose_ref = vpPoseVector(cMo_ref); + vpPoseVector pose_est = vpPoseVector(cMo); - std::cout << "Find Matches using Ransac" << std::endl; - unsigned int nb3D = 5; - unsigned int nb2D = 5; - std::vector<vpPoint> P(nb3D); - std::vector<vpPoint> p(nb2D); - - P[0].setWorldCoordinates(-L,-L, 0 ) ; - P[1].setWorldCoordinates(L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-L,L, 0 ) ; - P[4].setWorldCoordinates(-0,L/2., L ) ; - - vpHomogeneousMatrix cMo_ref(0, 0.2, 1, vpMath::rad(3), vpMath::rad(-2), vpMath::rad(10)) ; - - for(unsigned int i=0 ; i < nb3D ; i++) - { - vpPoint pt = P[i]; - pt.project(cMo_ref); - p[i].set_x(pt.get_x()); - p[i].set_y(pt.get_y()); - } - - unsigned int ninliers ; - std::vector<vpPoint> inliers; - double threshold = 1e-6; - unsigned int nbInlierToReachConsensus = nb3D; - - vpHomogeneousMatrix cMo ; - - vpPose::findMatch(p,P,nbInlierToReachConsensus,threshold,ninliers,inliers,cMo); - - std::cout << "Inliers: " << std::endl; - for (unsigned int i = 0; i < inliers.size() ; i++) - { - inliers[i].print() ; std::cout << std::endl; - } - - std::cout << "cMo :\n" << vpPoseVector(cMo).t() << std::endl << std::endl; + std::cout << "reference cMo :\n" << pose_ref.t() << std::endl << std::endl; + std::cout << "estimated cMo :\n" << pose_est.t() << std::endl << std::endl; - vpPoseVector pose_ref = vpPoseVector(cMo_ref); - vpPoseVector pose_est = vpPoseVector(cMo); + int test_fail = 0; + for(unsigned int i=0; i<6; i++) { + if (std::fabs(pose_ref[i]-pose_est[i]) > 0.001) + test_fail = 1; + } - std::cout << std::endl; - std::cout << "reference cMo :\n" << pose_ref.t() << std::endl << std::endl; - std::cout << "estimated cMo :\n" << pose_est.t() << std::endl << std::endl; + std::cout << "Matching is " << (test_fail ? "badly" : "well") << " performed" << std::endl; - int test_fail = 0; - for(unsigned int i=0; i<6; i++) { - if (std::fabs(pose_ref[i]-pose_est[i]) > 0.001) - test_fail = 1; + return test_fail; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - std::cout << "Matching is " << (test_fail ? "badly" : "well") << " performed" << std::endl; - return test_fail; } diff --git a/test/pose/testPose.cpp b/test/pose/testPose.cpp index 57b1fa8d2acb06337f8af374c86cc880b10e8526..00b39226ca19ae6c0a80f90b5c79d481d7085f7b 100644 --- a/test/pose/testPose.cpp +++ b/test/pose/testPose.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testPose.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testPose.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -66,6 +66,14 @@ Non-Linear approach. */ + +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); +void print_pose(const vpHomogeneousMatrix &cMo, const std::string &legend); +int compare_pose(const vpPose &pose, const vpHomogeneousMatrix &cMo_ref, const vpHomogeneousMatrix &cMo_est, + const std::string &legend); + + /*! Print the program options. @@ -97,15 +105,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -114,7 +122,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -137,7 +145,8 @@ void print_pose(const vpHomogeneousMatrix &cMo, const std::string &legend) } // test if pose is well estimated -int compare_pose(const vpPose &pose, const vpHomogeneousMatrix &cMo_ref, const vpHomogeneousMatrix &cMo_est, const std::string &legend) +int compare_pose(const vpPose &pose, const vpHomogeneousMatrix &cMo_ref, const vpHomogeneousMatrix &cMo_est, + const std::string &legend) { vpPoseVector pose_ref = vpPoseVector(cMo_ref); vpPoseVector pose_est = vpPoseVector(cMo_est); @@ -149,10 +158,16 @@ int compare_pose(const vpPose &pose, const vpHomogeneousMatrix &cMo_ref, const v if (std::fabs(pose_ref[i]-pose_est[i]) > 0.001) fail = 1; } + std::cout << "Based on 3D parameters " << legend << " is " << (fail ? "badly" : "well") << " estimated" << std::endl; // Test done on the residual double r = pose.computeResidual(cMo_est); + if (pose.listP.size() < 4) { + fail = 1; + std::cout << "Not enough point" << std::endl; + return fail; + } r = sqrt(r)/pose.listP.size(); //std::cout << "Residual on each point (meter): " << r << std::endl; fail = (r > 0.1) ? 1 : 0; @@ -163,97 +178,103 @@ int compare_pose(const vpPose &pose, const vpHomogeneousMatrix &cMo_ref, const v int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - vpPoint P[5] ; // Point to be tracked - vpPose pose ; - pose.clearPoint() ; - - P[0].setWorldCoordinates(-L,-L, 0 ) ; - P[1].setWorldCoordinates(L,-L, 0 ) ; - P[2].setWorldCoordinates(L,L, 0 ) ; - P[3].setWorldCoordinates(-2*L, 3*L, 0 ) ; - P[4].setWorldCoordinates(-L,L, 0.01 ) ; - //P[3].setWorldCoordinates(-L,L, 0 ) ; - - int test_fail = 0, fail = 0; - vpPoseVector cpo_ref = vpPoseVector(0.01, 0.02, 0.25, vpMath::rad(5), 0,vpMath::rad(10)); - vpHomogeneousMatrix cMo_ref(cpo_ref) ; - vpHomogeneousMatrix cMo ; // will contain the estimated pose - - for(int i=0 ; i < 5 ; i++) { - P[i].project(cMo_ref) ; - //P[i].print(); - pose.addPoint(P[i]) ; // and added to the pose computation class - } + vpPoint P[5] ; // Point to be tracked + vpPose pose ; + pose.clearPoint() ; + + P[0].setWorldCoordinates(-L,-L, 0 ) ; + P[1].setWorldCoordinates(L,-L, 0 ) ; + P[2].setWorldCoordinates(L,L, 0 ) ; + P[3].setWorldCoordinates(-2*L, 3*L, 0 ) ; + P[4].setWorldCoordinates(-L,L, 0.01 ) ; + //P[3].setWorldCoordinates(-L,L, 0 ) ; + + int test_fail = 0, fail = 0; + vpPoseVector cpo_ref = vpPoseVector(0.01, 0.02, 0.25, vpMath::rad(5), 0,vpMath::rad(10)); + vpHomogeneousMatrix cMo_ref(cpo_ref) ; + vpHomogeneousMatrix cMo ; // will contain the estimated pose + + for(int i=0 ; i < 5 ; i++) { + P[i].project(cMo_ref) ; + //P[i].print(); + pose.addPoint(P[i]) ; // and added to the pose computation class + } - // Let's go ... - print_pose(cMo_ref, std::string("Reference pose")); // print the reference pose + // Let's go ... + print_pose(cMo_ref, std::string("Reference pose")); // print the reference pose - std::cout <<"-------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::LAGRANGE, cMo) ; + std::cout <<"-------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::LAGRANGE, cMo) ; - print_pose(cMo, std::string("Pose estimated by Lagrange")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Lagrange")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange"); + test_fail |= fail; - std::cout <<"--------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::DEMENTHON, cMo) ; + std::cout <<"--------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::DEMENTHON, cMo) ; - print_pose(cMo, std::string("Pose estimated by Dementhon")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Dementhon")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon"); + test_fail |= fail; - std::cout <<"--------------------------------------------------"<<std::endl ; - pose.setRansacNbInliersToReachConsensus(4); - pose.setRansacThreshold(0.01); - pose.computePose(vpPose::RANSAC, cMo) ; + std::cout <<"--------------------------------------------------"<<std::endl ; + pose.setRansacNbInliersToReachConsensus(4); + pose.setRansacThreshold(0.01); + pose.computePose(vpPose::RANSAC, cMo) ; - print_pose(cMo, std::string("Pose estimated by Ransac")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Ransac"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Ransac")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Ransac"); + test_fail |= fail; - std::cout <<"--------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::LAGRANGE_LOWE, cMo) ; + std::cout <<"--------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::LAGRANGE_LOWE, cMo) ; - print_pose(cMo, std::string("Pose estimated by Lagrange than Lowe")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange than Lowe"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Lagrange than Lowe")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange than Lowe"); + test_fail |= fail; - std::cout <<"--------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::DEMENTHON_LOWE, cMo) ; + std::cout <<"--------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::DEMENTHON_LOWE, cMo) ; - print_pose(cMo, std::string("Pose estimated by Dementhon than Lowe")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon than Lowe"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Dementhon than Lowe")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon than Lowe"); + test_fail |= fail; - // Now Virtual Visual servoing + // Now Virtual Visual servoing - std::cout <<"--------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::VIRTUAL_VS, cMo) ; + std::cout <<"--------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::VIRTUAL_VS, cMo) ; - print_pose(cMo, std::string("Pose estimated by VVS")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by VVS"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by VVS")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by VVS"); + test_fail |= fail; - std::cout <<"-------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::DEMENTHON_VIRTUAL_VS, cMo) ; + std::cout <<"-------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::DEMENTHON_VIRTUAL_VS, cMo) ; - print_pose(cMo, std::string("Pose estimated by Dementhon than by VVS")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon than by VVS"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Dementhon than by VVS")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Dementhon than by VVS"); + test_fail |= fail; - std::cout <<"-------------------------------------------------"<<std::endl ; - pose.computePose(vpPose::LAGRANGE_VIRTUAL_VS, cMo) ; + std::cout <<"-------------------------------------------------"<<std::endl ; + pose.computePose(vpPose::LAGRANGE_VIRTUAL_VS, cMo) ; - print_pose(cMo, std::string("Pose estimated by Lagrange than by VVS")); - fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange than by VVS"); - test_fail |= fail; + print_pose(cMo, std::string("Pose estimated by Lagrange than by VVS")); + fail = compare_pose(pose, cMo_ref, cMo, "pose by Lagrange than by VVS"); + test_fail |= fail; - std::cout << "\nGlobal pose estimation test " << (test_fail ? "fail" : "is ok") << std::endl; + std::cout << "\nGlobal pose estimation test " << (test_fail ? "fail" : "is ok") << std::endl; - return test_fail; + return test_fail; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/pose/testPoseFeatures.cpp b/test/pose/testPoseFeatures.cpp index 1004757d5bcb2efc0e70329bff3c0c9c749e8e3e..19d2a598ae999a5b2e6fd5fdb5761450bec79f72 100644 --- a/test/pose/testPoseFeatures.cpp +++ b/test/pose/testPoseFeatures.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testPoseFeatures.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testPoseFeatures.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -87,127 +87,130 @@ void vp_createLine(vpFeatureLine &fp,const vpLine &v){ int main() { - vpImage<unsigned char> I(600,600); - - vpHomogeneousMatrix cMo_ref(0., 0., 1., vpMath::rad(0), vpMath::rad(0), vpMath::rad(60)); - vpPoseVector pose_ref = vpPoseVector(cMo_ref); + try { + vpImage<unsigned char> I(600,600); - std::cout << "Reference pose used to create the visual features : " << std::endl; - std::cout << pose_ref.t() << std::endl; - - vpPoseFeatures pose; - - vpPoint pts[6]; + vpHomogeneousMatrix cMo_ref(0., 0., 1., vpMath::rad(0), vpMath::rad(0), vpMath::rad(60)); + vpPoseVector pose_ref = vpPoseVector(cMo_ref); + + std::cout << "Reference pose used to create the visual features : " << std::endl; + std::cout << pose_ref.t() << std::endl; + + vpPoseFeatures pose; + + vpPoint pts[6]; - double val = 0.25; - double val2 = 0.0; - - //2D Point Feature - pts[0].setWorldCoordinates(0.0,-val,val2); - pts[1].setWorldCoordinates(0.0,val,val2); - pts[2].setWorldCoordinates(-val,val,val2); - - //Segment Feature - pts[3].setWorldCoordinates(-val,-val/2.0,val2); - pts[4].setWorldCoordinates(val,val/2.0,val2); - - //3D point Feature - pts[5].setWorldCoordinates(0.0,0.0,-1.5); - - //Line Feature - vpLine line; - line.setWorldCoordinates(0.0,1.0,0.0,.0, - 0.0,0.0,1.0,0.0); - - //Vanishing Point Feature - vpLine l1; - l1.setWorldCoordinates(0.0,1.0,0.2,0.0, - 1.0,0.0,0.0,-0.25); - - vpLine l2; - l2.setWorldCoordinates(0.0,1.0,0.2,0.0, + double val = 0.25; + double val2 = 0.0; + + //2D Point Feature + pts[0].setWorldCoordinates(0.0,-val,val2); + pts[1].setWorldCoordinates(0.0,val,val2); + pts[2].setWorldCoordinates(-val,val,val2); + + //Segment Feature + pts[3].setWorldCoordinates(-val,-val/2.0,val2); + pts[4].setWorldCoordinates(val,val/2.0,val2); + + //3D point Feature + pts[5].setWorldCoordinates(0.0,0.0,-1.5); + + //Line Feature + vpLine line; + line.setWorldCoordinates(0.0,1.0,0.0,.0, + 0.0,0.0,1.0,0.0); + + //Vanishing Point Feature + vpLine l1; + l1.setWorldCoordinates(0.0,1.0,0.2,0.0, + 1.0,0.0,0.0,-0.25); + + vpLine l2; + l2.setWorldCoordinates(0.0,1.0,0.2,0.0, -1.0,0.0,0.0,-0.25); - - //Ellipse Feature - vpCircle circle; - circle.setWorldCoordinates(0.0, 0.0, 1.0 , 0.0, 0.0, 0.0, 0.25); - - pts[0].project(cMo_ref); - pts[1].project(cMo_ref); - pts[2].project(cMo_ref); - - pts[3].project(cMo_ref); - pts[4].project(cMo_ref); - - pts[5].project(cMo_ref); - - line.project(cMo_ref); - - l1.project(cMo_ref); - l2.project(cMo_ref); - - circle.project(cMo_ref); - - pose.addFeaturePoint(pts[0]); -// pose.addFeaturePoint(pts[1]); - pose.addFeaturePoint(pts[2]); - - pose.addFeaturePoint3D(pts[5]); - - pose.addFeatureVanishingPoint(l1,l2); - -// pose.addFeatureSegment(pts[3],pts[4]); -// -// pose.addFeatureLine(line); - - pose.addFeatureEllipse(circle); + + //Ellipse Feature + vpCircle circle; + circle.setWorldCoordinates(0.0, 0.0, 1.0 , 0.0, 0.0, 0.0, 0.25); + + pts[0].project(cMo_ref); + pts[1].project(cMo_ref); + pts[2].project(cMo_ref); + + pts[3].project(cMo_ref); + pts[4].project(cMo_ref); + + pts[5].project(cMo_ref); + + line.project(cMo_ref); + + l1.project(cMo_ref); + l2.project(cMo_ref); + + circle.project(cMo_ref); + + pose.addFeaturePoint(pts[0]); + // pose.addFeaturePoint(pts[1]); + pose.addFeaturePoint(pts[2]); + + pose.addFeaturePoint3D(pts[5]); + + pose.addFeatureVanishingPoint(l1,l2); + + // pose.addFeatureSegment(pts[3],pts[4]); + // + // pose.addFeatureLine(line); + + pose.addFeatureEllipse(circle); #ifdef VISP_HAVE_CPP11_COMPATIBILITY - vpFeaturePoint fp; - vpFeatureLine fl; - vpFeatureSegment fs; - void (*ptr)(vpFeatureSegment&, vpPoint&, vpPoint&) = &vpFeatureBuilder::create; - vp_createPointClass cpClass; - int (vp_createPointClass::*ptrClass)(vpFeaturePoint&, const vpPoint&) = &vp_createPointClass::vp_createPoint; - pose.addSpecificFeature(&cpClass, ptrClass, fp, pts[1]); - pose.addSpecificFeature(&vp_createLine, fl, line); - pose.addSpecificFeature(ptr, fs, pts[3], pts[4]); + vpFeaturePoint fp; + vpFeatureLine fl; + vpFeatureSegment fs; + void (*ptr)(vpFeatureSegment&, vpPoint&, vpPoint&) = &vpFeatureBuilder::create; + vp_createPointClass cpClass; + int (vp_createPointClass::*ptrClass)(vpFeaturePoint&, const vpPoint&) = &vp_createPointClass::vp_createPoint; + pose.addSpecificFeature(&cpClass, ptrClass, fp, pts[1]); + pose.addSpecificFeature(&vp_createLine, fl, line); + pose.addSpecificFeature(ptr, fs, pts[3], pts[4]); #endif - - pose.setVerbose(true); - pose.setLambda(0.6); - pose.setVVSIterMax(200); - pose.setCovarianceComputation(true); - - vpHomogeneousMatrix cMo_est(0.4, 0.3, 1.5, vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - vpPoseVector pose_est = vpPoseVector(cMo_est); - std::cout << "\nPose used as initialisation of the pose computation : " << std::endl; - std::cout << pose_est.t() << std::endl; - - pose.computePose(cMo_est); -// pose.computePose(cMo_est, vpPoseFeatures::ROBUST_VIRTUAL_VS); - - std::cout << "\nEstimated pose from visual features : " << std::endl; - pose_est.buildFrom(cMo_est); - std::cout << pose_est.t() << std::endl; - - std::cout << "\nResulting covariance (Diag): " << std::endl; - vpMatrix covariance = pose.getCovarianceMatrix(); - std::cout << covariance[0][0] << " " - << covariance[1][1] << " " - << covariance[2][2] << " " - << covariance[3][3] << " " - << covariance[4][4] << " " - << covariance[5][5] << " " << std::endl; - - int test_fail = 0; - for(unsigned int i=0; i<6; i++) { - if (std::fabs(pose_ref[i]-pose_est[i]) > 0.001) - test_fail = 1; - } + pose.setVerbose(true); + pose.setLambda(0.6); + pose.setVVSIterMax(200); + pose.setCovarianceComputation(true); + + vpHomogeneousMatrix cMo_est(0.4, 0.3, 1.5, vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + vpPoseVector pose_est = vpPoseVector(cMo_est); + std::cout << "\nPose used as initialisation of the pose computation : " << std::endl; + std::cout << pose_est.t() << std::endl; - std::cout << "\nPose is " << (test_fail ? "badly" : "well") << " estimated" << std::endl; + pose.computePose(cMo_est); + // pose.computePose(cMo_est, vpPoseFeatures::ROBUST_VIRTUAL_VS); - return test_fail; + + std::cout << "\nEstimated pose from visual features : " << std::endl; + pose_est.buildFrom(cMo_est); + std::cout << pose_est.t() << std::endl; + + std::cout << "\nResulting covariance (Diag): " << std::endl; + vpMatrix covariance = pose.getCovarianceMatrix(); + std::cout << covariance[0][0] << " " << covariance[1][1] << " " + << covariance[2][2] << " " << covariance[3][3] << " " + << covariance[4][4] << " " << covariance[5][5] << " " << std::endl; + + int test_fail = 0; + for(unsigned int i=0; i<6; i++) { + if (std::fabs(pose_ref[i]-pose_est[i]) > 0.001) + test_fail = 1; + } + + std::cout << "\nPose is " << (test_fail ? "badly" : "well") << " estimated" << std::endl; + + return test_fail; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/pose/testPoseRansac.cpp b/test/pose/testPoseRansac.cpp index 0a8892b3a4320dd52419e05fe25bca7309e30692..70e8acca73cd532fa747da22f3dbc5585e6413fa 100644 --- a/test/pose/testPoseRansac.cpp +++ b/test/pose/testPoseRansac.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testPoseRansac.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testPoseRansac.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,7 @@ int main() { + try { std::cout << "Pose computation with matched points" << std::endl; int size = 8; vpPoint *P = new vpPoint [size] ; // Point to be tracked @@ -81,7 +82,7 @@ main() vpHomogeneousMatrix cMo_ref(0, 0.2, 1, 0, 0, 0) ; for(int i=0 ; i < size ; i++) - { + { P[i].project(cMo_ref) ; P[i].print() ; std::cout << std::endl; @@ -131,4 +132,9 @@ main() std::cout << "Pose is " << (test_fail ? "badly" : "well") << " estimated" << std::endl; delete [] P; return test_fail; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/servo-afma4/CMakeLists.txt b/test/servo-afma4/CMakeLists.txt index ac0fe331bad77acca8e62fbae6c702f662099553..abcac6cc6763eee3938c46977fd18755baee017c 100644 --- a/test/servo-afma4/CMakeLists.txt +++ b/test/servo-afma4/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,25 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testAfma4.cpp testRobotAfma4.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/servo-afma4/testAfma4.cpp b/test/servo-afma4/testAfma4.cpp index 1df849ba4796a90cfdd46a58d8e724570e5b3c69..03583ed57f7368725e91d9de5618e533dcf72fc4 100644 --- a/test/servo-afma4/testAfma4.cpp +++ b/test/servo-afma4/testAfma4.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testAfma4.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testAfma4.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,10 +67,9 @@ int main() return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - } diff --git a/test/servo-afma4/testRobotAfma4.cpp b/test/servo-afma4/testRobotAfma4.cpp index 60f1919e16629cd67f3fa02cd6e0811aff5fedc5..96a26fffff9d52de60366c60d2785173e7148583 100644 --- a/test/servo-afma4/testRobotAfma4.cpp +++ b/test/servo-afma4/testRobotAfma4.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobotAfma4.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobotAfma4.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,14 +64,11 @@ int main() vpRobotAfma4 afma4; std::cout << afma4 << std::endl; - - return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - + return 0; } #else int main() diff --git a/test/servo-afma6/CMakeLists.txt b/test/servo-afma6/CMakeLists.txt index 0ccf349fc1e1556a7c45b64d28c74a30de751dab..d0a19dc6ed0046f5748e80561ccaac161c92a0e1 100644 --- a/test/servo-afma6/CMakeLists.txt +++ b/test/servo-afma6/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,32 +43,31 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testAfma6.cpp testRobotAfma6.cpp testRobotAfma6Pose.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() # Add test -ADD_TEST(testAfma6 testAfma6) -ADD_TEST(testRobotAfma6 testRobotAfma6) +add_test(testAfma6 testAfma6) +add_test(testRobotAfma6 testRobotAfma6) # testRobotAfma6Pose cannot be run automaticlly, since human interaction is # required to click on the dots for the pose computation -#ADD_TEST(testRobotAfma6Pose testRobotAfma6Pose) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +#add_test(testRobotAfma6Pose testRobotAfma6Pose) diff --git a/test/servo-afma6/testAfma6.cpp b/test/servo-afma6/testAfma6.cpp index 0c11f6d80997d6409008dd752d1c253db75296e4..06657867cf707ae478dd3f80d33db897e90f337f 100644 --- a/test/servo-afma6/testAfma6.cpp +++ b/test/servo-afma6/testAfma6.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testAfma6.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testAfma6.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -104,10 +104,9 @@ int main() return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - } diff --git a/test/servo-afma6/testRobotAfma6.cpp b/test/servo-afma6/testRobotAfma6.cpp index 62cd515677e3a1cd5ff5fa1db64dae18c9b37792..9ac60a2789e63f20ee803377281fc41d91524b84 100644 --- a/test/servo-afma6/testRobotAfma6.cpp +++ b/test/servo-afma6/testRobotAfma6.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobotAfma6.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobotAfma6.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -101,14 +101,11 @@ int main() std::cout << afma6 << std::endl; afma6.getCameraParameters(cam, 640, 480); std::cout << cam << std::endl; - - return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - + return 0; } #else int main() diff --git a/test/servo-afma6/testRobotAfma6Pose.cpp b/test/servo-afma6/testRobotAfma6Pose.cpp index 2d223720c184d6bf31e9c5d523d9f3113a8c7f44..038212ae7ad1d753340cb0d499b6c9c11fabd6da 100644 --- a/test/servo-afma6/testRobotAfma6Pose.cpp +++ b/test/servo-afma6/testRobotAfma6Pose.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobotAfma6Pose.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobotAfma6Pose.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -223,12 +223,11 @@ int main() << vpMath::deg(r[1]) << " " << vpMath::deg(r[2]) << " deg" << std::endl << std::endl; - return 0; } - catch(...) { - std::cout << "Test failed" << std::endl; - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } + return 0; } #else int main() diff --git a/test/servo-viper/CMakeLists.txt b/test/servo-viper/CMakeLists.txt index 8d859e099e14e26564b05c8f3ca6d93cd9a018fe..13f8ba559c9bf4cc2274a6d178debfefd54858ae 100644 --- a/test/servo-viper/CMakeLists.txt +++ b/test/servo-viper/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -50,25 +50,24 @@ SET (SOURCE ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() # Add test -ADD_TEST(testViper850 testViper850) -ADD_TEST(testRobotViper850 testRobotViper850) +add_test(testViper850 testViper850) +add_test(testRobotViper850 testRobotViper850) # testRobotViper850Pose cannot be run automaticlly, since human interaction is # required to click on the dots for the pose computation -#ADD_TEST(testRobotViper850Pose testRobotViper850Pose.cpp) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) +#add_test(testRobotViper850Pose testRobotViper850Pose.cpp) diff --git a/test/servo-viper/testRobotViper850.cpp b/test/servo-viper/testRobotViper850.cpp index 48105bcc9f112fe0b33d319596de727affb43cb6..cad8251d1683a6ffd1cc858cbb5786ccd1cfb381 100644 --- a/test/servo-viper/testRobotViper850.cpp +++ b/test/servo-viper/testRobotViper850.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobotViper850.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobotViper850.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -110,13 +110,11 @@ int main() << " " << vpMath::deg(rzyz[1]) << " " << vpMath::deg(rzyz[2]) << std::endl; - return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - + return 0; } #else int main() diff --git a/test/servo-viper/testRobotViper850Pose.cpp b/test/servo-viper/testRobotViper850Pose.cpp index 223026b2f90a016a70c2aa218746c35363afc790..9748bb893907b3eb52cc140baecc54c1beba511b 100644 --- a/test/servo-viper/testRobotViper850Pose.cpp +++ b/test/servo-viper/testRobotViper850Pose.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testRobotViper850Pose.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testRobotViper850Pose.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -226,12 +226,11 @@ int main() << vpMath::deg(r[1]) << " " << vpMath::deg(r[2]) << " deg" << std::endl << std::endl; - return 0; } - catch(...) { - std::cout << "Test failed" << std::endl; - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } + return 0; } #else int main() diff --git a/test/servo-viper/testViper850.cpp b/test/servo-viper/testViper850.cpp index 9a84e287b0fdda717a7eb3a358be406064b57df6..20c35abbe37ed5fb5ad9f28021af295ddb7938ee 100644 --- a/test/servo-viper/testViper850.cpp +++ b/test/servo-viper/testViper850.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testViper850.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testViper850.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -120,9 +120,9 @@ int main() return 0; } - catch(...) { - vpERROR_TRACE(" Test failed"); - return 0; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index d309b045a556031654e9204db7c54230a63a19ab..b1437dfea560519f173589e9ddf558ee06c9f819 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 5255 2015-02-03 14:52:43Z strinh $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,6 +43,8 @@ # # If you add/remove a directory, modify here SET (SRC_SUBDIRS + convert + io geometry time xml @@ -50,8 +52,3 @@ SET (SRC_SUBDIRS # Build process propagation in the sub directories SUBDIRS(${SRC_SUBDIRS}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) diff --git a/example/servo-cycab/CMakeLists.txt b/test/tools/convert/CMakeLists.txt similarity index 73% rename from example/servo-cycab/CMakeLists.txt rename to test/tools/convert/CMakeLists.txt index 184a92adabcad28b20d6c1d54bfb7ab580ebe2c3..4a27cab28ecee3a93aebd914852d925b20766e08 100644 --- a/example/servo-cycab/CMakeLists.txt +++ b/test/tools/convert/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,7 +31,7 @@ # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # Description: -# ViSP configuration file. +# ViSP overall configuration file. # # Authors: # Fabien Spindler @@ -43,18 +43,26 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE - servoCycab.cpp - servoCycabBasic.cpp +set(SOURCE + testConvert.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) -ENDFOREACH(source) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + # Add test + add_test(${binary} ${binary}) + + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() + diff --git a/test/tools/convert/testConvert.cpp b/test/tools/convert/testConvert.cpp new file mode 100644 index 0000000000000000000000000000000000000000..92d8b85b9d0bad10d7427f3aa0cf66f54b80d829 --- /dev/null +++ b/test/tools/convert/testConvert.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** + * + * $Id: testIoTools.cpp 5210 2015-01-26 10:51:11Z strinh $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Test functions in vpIoTools. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ + +/*! + + \example testConvert.cpp + + \brief Test functions in Convert. + +*/ + +#include <iostream> // std::cout +#include <limits> // std::numeric_limits +#include <visp/vpConfig.h> +#include <visp/vpConvert.h> + + +bool areSame(double a, double b) { + return fabs(a - b) < std::numeric_limits<double>::epsilon(); +} + +void testConvertFromImagePointToPoint2f() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + vpImagePoint imPt1(12.5f, .85f); + vpImagePoint imPt2(-44.26f, 125.11f); + vpImagePoint imPt3(0.0f, -1.756e-10f); + + cv::Point2f pt1, pt2, pt3; + vpConvert::convertToOpenCV(imPt1, pt1); + vpConvert::convertToOpenCV(imPt2, pt2); + vpConvert::convertToOpenCV(imPt3, pt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(imPt1.get_u(), pt1.x) && areSame(imPt1.get_v(), pt1.y)) nbOk++; else nbNOk++; + if(areSame(imPt2.get_u(), pt2.x) && areSame(imPt2.get_v(), pt2.y)) nbOk++; else nbNOk++; + if(areSame(imPt3.get_u(), pt3.x) && areSame(imPt3.get_v(), pt3.y)) nbOk++; else nbNOk++; + + std::vector<vpImagePoint> listOfImPts(3); + listOfImPts[0] = imPt1; + listOfImPts[1] = imPt2; + listOfImPts[2] = imPt3; + + std::vector<cv::Point2f> listOfPts; + vpConvert::convertToOpenCV(listOfImPts, listOfPts); + + if(listOfImPts.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfImPts[i].get_u(), listOfPts[i].x) && areSame(listOfImPts[i].get_v(), listOfPts[i].y)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromImagePointToPoint2f=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromPoint2fToImagePoint() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + vpImagePoint imPt1, imPt2, imPt3; + + cv::Point2f pt1(12.5f, .85f), pt2(-44.26f, 125.11f), pt3(0.0f, -1.756e-10f); + vpConvert::convertFromOpenCV(pt1, imPt1); + vpConvert::convertFromOpenCV(pt2, imPt2); + vpConvert::convertFromOpenCV(pt3, imPt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(imPt1.get_u(), pt1.x) && areSame(imPt1.get_v(), pt1.y)) nbOk++; else nbNOk++; + if(areSame(imPt2.get_u(), pt2.x) && areSame(imPt2.get_v(), pt2.y)) nbOk++; else nbNOk++; + if(areSame(imPt3.get_u(), pt3.x) && areSame(imPt3.get_v(), pt3.y)) nbOk++; else nbNOk++; + + std::vector<vpImagePoint> listOfImPts; + + std::vector<cv::Point2f> listOfPts(3); + listOfPts[0] = pt1; + listOfPts[1] = pt2; + listOfPts[2] = pt3; + + vpConvert::convertFromOpenCV(listOfPts, listOfImPts); + + if(listOfImPts.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfImPts[i].get_u(), listOfPts[i].x) && areSame(listOfImPts[i].get_v(), listOfPts[i].y)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromPoint2fToImagePoint=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromImagePointToPoint2d() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + vpImagePoint imPt1(12.5, .85); + vpImagePoint imPt2(-44.26, 125.11); + vpImagePoint imPt3(0, -1.756e-10); + + cv::Point2d pt1, pt2, pt3; + vpConvert::convertToOpenCV(imPt1, pt1); + vpConvert::convertToOpenCV(imPt2, pt2); + vpConvert::convertToOpenCV(imPt3, pt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(imPt1.get_u(), pt1.x) && areSame(imPt1.get_v(), pt1.y)) nbOk++; else nbNOk++; + if(areSame(imPt2.get_u(), pt2.x) && areSame(imPt2.get_v(), pt2.y)) nbOk++; else nbNOk++; + if(areSame(imPt3.get_u(), pt3.x) && areSame(imPt3.get_v(), pt3.y)) nbOk++; else nbNOk++; + + std::vector<vpImagePoint> listOfImPts(3); + listOfImPts[0] = imPt1; + listOfImPts[1] = imPt2; + listOfImPts[2] = imPt3; + + std::vector<cv::Point2d> listOfPts; + vpConvert::convertToOpenCV(listOfImPts, listOfPts); + + if(listOfImPts.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfImPts[i].get_u(), listOfPts[i].x) && areSame(listOfImPts[i].get_v(), listOfPts[i].y)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromImagePointToPoint2d=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromPoint2dToImagePoint() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + vpImagePoint imPt1, imPt2, imPt3; + + cv::Point2d pt1(12.5, .85), pt2(-44.26, 125.11), pt3(0, -1.756e-10); + vpConvert::convertFromOpenCV(pt1, imPt1); + vpConvert::convertFromOpenCV(pt2, imPt2); + vpConvert::convertFromOpenCV(pt3, imPt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(imPt1.get_u(), pt1.x) && areSame(imPt1.get_v(), pt1.y)) nbOk++; else nbNOk++; + if(areSame(imPt2.get_u(), pt2.x) && areSame(imPt2.get_v(), pt2.y)) nbOk++; else nbNOk++; + if(areSame(imPt3.get_u(), pt3.x) && areSame(imPt3.get_v(), pt3.y)) nbOk++; else nbNOk++; + + std::vector<vpImagePoint> listOfImPts; + + std::vector<cv::Point2d> listOfPts(3); + listOfPts[0] = pt1; + listOfPts[1] = pt2; + listOfPts[2] = pt3; + + vpConvert::convertFromOpenCV(listOfPts, listOfImPts); + + if(listOfImPts.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfImPts[i].get_u(), listOfPts[i].x) && areSame(listOfImPts[i].get_v(), listOfPts[i].y)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromPoint2dToImagePoint=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromKeyPointToImagePoint() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + cv::KeyPoint kp1(12.5f, .85f, 0), kp2(-44.26f, 125.11f, 0), kp3(0.0f, -1.756e-10f, 0); + vpImagePoint imPt1, imPt2, imPt3; + + vpConvert::convertFromOpenCV(kp1, imPt1); + vpConvert::convertFromOpenCV(kp2, imPt2); + vpConvert::convertFromOpenCV(kp3, imPt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(imPt1.get_u(), kp1.pt.x) && areSame(imPt1.get_v(), kp1.pt.y)) nbOk++; else nbNOk++; + if(areSame(imPt2.get_u(), kp2.pt.x) && areSame(imPt2.get_v(), kp2.pt.y)) nbOk++; else nbNOk++; + if(areSame(imPt3.get_u(), kp3.pt.x) && areSame(imPt3.get_v(), kp3.pt.y)) nbOk++; else nbNOk++; + + std::vector<cv::KeyPoint> listOfKeyPoints(3); + listOfKeyPoints[0] = kp1; + listOfKeyPoints[1] = kp2; + listOfKeyPoints[2] = kp3; + + std::vector<vpImagePoint> listOfImPts; + vpConvert::convertFromOpenCV(listOfKeyPoints, listOfImPts); + + if(listOfImPts.size() == listOfKeyPoints.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfImPts[i].get_u(), listOfKeyPoints[i].pt.x) && areSame(listOfImPts[i].get_v(), listOfKeyPoints[i].pt.y)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromKeyPointToImagePoint=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromPoint3fToPoint() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + cv::Point3f pt1(12.5f, .85f, 110.0f), pt2(-44.26f, 125.11f, -98e2f), pt3(0.0f, -1.756e-10f, 0.00015f); + vpPoint point1, point2, point3; + + vpConvert::convertFromOpenCV(pt1, point1); + vpConvert::convertFromOpenCV(pt2, point2); + vpConvert::convertFromOpenCV(pt3, point3); + + int nbOk = 0, nbNOk = 0; + if(areSame(pt1.x, point1.get_oX()) && areSame(pt1.y, point1.get_oY()) && areSame(pt1.z, point1.get_oZ())) nbOk++; else nbNOk++; + if(areSame(pt2.x, point2.get_oX()) && areSame(pt2.y, point2.get_oY()) && areSame(pt2.z, point2.get_oZ())) nbOk++; else nbNOk++; + if(areSame(pt3.x, point3.get_oX()) && areSame(pt3.y, point3.get_oY()) && areSame(pt3.z, point3.get_oZ())) nbOk++; else nbNOk++; + + std::vector<cv::Point3f> listOfPoints3f(3); + listOfPoints3f[0] = pt1; + listOfPoints3f[1] = pt2; + listOfPoints3f[2] = pt3; + + std::vector<vpPoint> listOfPts; + vpConvert::convertFromOpenCV(listOfPoints3f, listOfPts); + + if(listOfPoints3f.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfPts[i].get_oX(), listOfPoints3f[i].x) && areSame(listOfPts[i].get_oY(), listOfPoints3f[i].y) && areSame(listOfPts[i].get_oZ(), listOfPoints3f[i].z)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromPoint3fToPoint=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +void testConvertFromPointToPoint3f() { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + cv::Point3f pt1, pt2, pt3; + vpPoint point1, point2, point3; + point1.set_oX(12.5f); + point1.set_oY(.85f); + point1.set_oZ(110.0f); + + point2.set_oX(-44.26f); + point2.set_oY(125.11f); + point2.set_oZ(-98e2f); + + point3.set_oX(0.0f); + point3.set_oY(-1.756e-10f); + point3.set_oZ(0.00015f); + + vpConvert::convertToOpenCV(point1, pt1); + vpConvert::convertToOpenCV(point2, pt2); + vpConvert::convertToOpenCV(point3, pt3); + + int nbOk = 0, nbNOk = 0; + if(areSame(pt1.x, point1.get_oX()) && areSame(pt1.y, point1.get_oY()) && areSame(pt1.z, point1.get_oZ())) nbOk++; else nbNOk++; + if(areSame(pt2.x, point2.get_oX()) && areSame(pt2.y, point2.get_oY()) && areSame(pt2.z, point2.get_oZ())) nbOk++; else nbNOk++; + if(areSame(pt3.x, point3.get_oX()) && areSame(pt3.y, point3.get_oY()) && areSame(pt3.z, point3.get_oZ())) nbOk++; else nbNOk++; + + std::vector<cv::Point3f> listOfPoints3f; + std::vector<vpPoint> listOfPts(3); + listOfPts[0] = point1; + listOfPts[1] = point2; + listOfPts[2] = point3; + + vpConvert::convertToOpenCV(listOfPts, listOfPoints3f); + + if(listOfPoints3f.size() == listOfPts.size()) { + for(size_t i = 0; i < 3; i++) { + if(areSame(listOfPts[i].get_oX(), listOfPoints3f[i].x) && areSame(listOfPts[i].get_oY(), listOfPoints3f[i].y) && areSame(listOfPts[i].get_oZ(), listOfPoints3f[i].z)) nbOk++; else nbNOk++; + } + } else { + nbNOk += 3; + } + + std::cout << "testConvertFromPointToPoint3f=" << nbOk << "/" << (nbOk + nbNOk) << std::endl; +#endif +} + +int main() { + testConvertFromImagePointToPoint2f(); + testConvertFromPoint2fToImagePoint(); + testConvertFromImagePointToPoint2d(); + testConvertFromPoint2dToImagePoint(); + + testConvertFromKeyPointToImagePoint(); + testConvertFromPoint3fToPoint(); + testConvertFromPointToPoint3f(); + return 0; +} diff --git a/test/tools/geometry/CMakeLists.txt b/test/tools/geometry/CMakeLists.txt index ebcd2a82d48fb05482f842c21eb699ee351a78a4..bc22f008d61da8aef6f04385650b5453133e867a 100644 --- a/test/tools/geometry/CMakeLists.txt +++ b/test/tools/geometry/CMakeLists.txt @@ -3,7 +3,7 @@ # $Id: CMakeLists.txt 2897 2010-11-04 11:17:58Z rtallonn $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testPolygon.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary} -c ${OPTION_TO_DESACTIVE_DISPLAY}) + add_test(${binary} ${binary} -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/tools/geometry/testPolygon.cpp b/test/tools/geometry/testPolygon.cpp index a56236fa714a2d0e097f9d074bd60b71932b29c0..3a9f7c74fbf502bea4ba47aa7eac46823c4ddba8 100755 --- a/test/tools/geometry/testPolygon.cpp +++ b/test/tools/geometry/testPolygon.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testPolygon.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testPolygon.cpp 5004 2014-11-24 08:24:18Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,9 @@ //! List of allowed command line options #define GETOPTARGS "cdh" +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv, bool& opt_display, bool& opt_click); + /*! Print the program options. @@ -103,12 +106,11 @@ OPTIONS: \n\ \param opt_click : activates the mouse click. \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - bool& opt_display, bool& opt_click) +bool getOptions(int argc, const char **argv, bool& opt_display, bool& opt_click) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': opt_click = false; break; @@ -116,7 +118,7 @@ bool getOptions(int argc, const char **argv, case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); return false; break; + usage(argv[0], optarg_); return false; break; } } @@ -124,7 +126,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -140,111 +142,114 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char** argv) { - bool opt_display = true; - bool opt_click = true; - vpImage<unsigned char> I(480, 640, 255); + try { + bool opt_display = true; + bool opt_click = true; + vpImage<unsigned char> I(480, 640, 255); + + // Read the command line options + if (getOptions(argc, argv, opt_display, opt_click) == false) { + return (-1); + } - // Read the command line options - if (getOptions(argc, argv, opt_display, opt_click) == false) { - return (-1); - } + std::vector <vpImagePoint> vec1; + vec1.push_back(vpImagePoint(200, 200)); + vec1.push_back(vpImagePoint(200, 400)); + vec1.push_back(vpImagePoint(320, 400)); + vec1.push_back(vpImagePoint(380, 300)); + vec1.push_back(vpImagePoint(280, 280)); + vpPolygon p1; + p1.buildFrom(vec1); + + std::vector <vpImagePoint> vec2; + vec2.push_back(vpImagePoint(20, 20)); + vec2.push_back(vpImagePoint(100, 20)); + vec2.push_back(vpImagePoint(100, 100)); + vec2.push_back(vpImagePoint(20, 100)); + vpPolygon p2(vec2); - std::vector <vpImagePoint> vec1; - vec1.push_back(vpImagePoint(200, 200)); - vec1.push_back(vpImagePoint(200, 400)); - vec1.push_back(vpImagePoint(320, 400)); - vec1.push_back(vpImagePoint(380, 300)); - vec1.push_back(vpImagePoint(280, 280)); - vpPolygon p1; - p1.buildFrom(vec1); - - std::vector <vpImagePoint> vec2; - vec2.push_back(vpImagePoint(20, 20)); - vec2.push_back(vpImagePoint(100, 20)); - vec2.push_back(vpImagePoint(100, 100)); - vec2.push_back(vpImagePoint(20, 100)); - vpPolygon p2(vec2); - - - std::vector <vpImagePoint> vec3; - vec2.push_back(vpImagePoint(250, 250)); - vec2.push_back(vpImagePoint(150, 250)); - vec2.push_back(vpImagePoint(250, 250)); - vpPolygon p3(vec3); + + std::vector <vpImagePoint> vec3; + vpPolygon p3(vec3); #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #else - opt_display = false; + opt_display = false; #endif - std::cout << " Polygon 1 : " << std::endl; - std::cout << " area : " << p1.getArea() << std::endl; - std::cout << " center : " << p1.getCenter() << std::endl << std::endl; + std::cout << " Polygon 1 : " << std::endl; + std::cout << " area : " << p1.getArea() << std::endl; + std::cout << " center : " << p1.getCenter() << std::endl << std::endl; - std::cout << " Polygon 2 : " << std::endl; - std::cout << " area : " << p2.getArea() << std::endl; - std::cout << " center : " << p2.getCenter() << std::endl << std::endl; + std::cout << " Polygon 2 : " << std::endl; + std::cout << " area : " << p2.getArea() << std::endl; + std::cout << " center : " << p2.getCenter() << std::endl << std::endl; - std::cout << " Polygon 3 : " << std::endl; - std::cout << " area : " << p3.getArea() << std::endl; - std::cout << " center : " << p3.getCenter() << std::endl; + std::cout << " Polygon 3 : " << std::endl; + std::cout << " area : " << p3.getArea() << std::endl; + std::cout << " center : " << p3.getCenter() << std::endl; - if(opt_display){ + if(opt_display){ #if (defined VISP_HAVE_X11) || (defined VISP_HAVE_GTK) || (defined VISP_HAVE_GDI) - display.init(I, 10, 10, "Test vpPolygon"); + display.init(I, 10, 10, "Test vpPolygon"); #endif - vpDisplay::display(I); - p1.display(I, vpColor::green, 1); - vpDisplay::displayCross(I, p1.getCenter(), 5, vpColor::green); - p2.display(I, vpColor::red, 1); - vpDisplay::displayCross(I, p2.getCenter(), 5, vpColor::red); - p3.display(I, vpColor::blue, 1); - vpDisplay::displayCross(I, p3.getCenter(), 5, vpColor::lightBlue); - vpDisplay::displayCharString(I, vpImagePoint(10, 10), "Click to finish", vpColor::red); - vpDisplay::flush(I); - - if (opt_click) - vpDisplay::getClick(I); - - - vpDisplay::display(I); - vpDisplay::displayCharString(I, vpImagePoint(10, 10), "Left click to add a point", vpColor::red); - vpDisplay::displayCharString(I, vpImagePoint(20, 10), "Right click to build the polygon", vpColor::red); - vpDisplay::flush(I); - if (opt_click) { - vpPolygon p4; - p4.initClick(I); - p4.display(I, vpColor::green, 1); - std::cout << std::endl; - std::cout << " Polygon 4 : " << std::endl; - std::cout << " area : " << p4.getArea() << std::endl; - std::cout << " center : " << p4.getCenter() << std::endl; - std::cout << "Click to continue." << std::endl; + vpDisplay::display(I); + p1.display(I, vpColor::green, 1); + vpDisplay::displayCross(I, p1.getCenter(), 5, vpColor::green); + p2.display(I, vpColor::red, 1); + vpDisplay::displayCross(I, p2.getCenter(), 5, vpColor::red); + p3.display(I, vpColor::blue, 1); + vpDisplay::displayCross(I, p3.getCenter(), 5, vpColor::lightBlue); + vpDisplay::displayText(I, vpImagePoint(10, 10), "Click to finish", vpColor::red); vpDisplay::flush(I); - vpDisplay::getClick(I); - vpRect bbox = p4.getBoundingBox(); - for(unsigned int i= (unsigned int)floor(bbox.getTop()); i<(unsigned int)ceil(bbox.getBottom()); ++i){ - for(unsigned int j=(unsigned int)floor(bbox.getLeft()); j<(unsigned int)ceil(bbox.getRight()); ++j){ - if(p4.isInside(vpImagePoint(i, j))){ - vpDisplay::displayPoint(I, vpImagePoint(i, j), vpColor::orange); + if (opt_click) + vpDisplay::getClick(I); + + + vpDisplay::display(I); + vpDisplay::displayText(I, vpImagePoint(10, 10), "Left click to add a point", vpColor::red); + vpDisplay::displayText(I, vpImagePoint(20, 10), "Right click to build the polygon", vpColor::red); + vpDisplay::flush(I); + if (opt_click) { + vpPolygon p4; + p4.initClick(I); + p4.display(I, vpColor::green, 1); + std::cout << std::endl; + std::cout << " Polygon 4 : " << std::endl; + std::cout << " area : " << p4.getArea() << std::endl; + std::cout << " center : " << p4.getCenter() << std::endl; + std::cout << "Click to continue." << std::endl; + vpDisplay::flush(I); + vpDisplay::getClick(I); + + vpRect bbox = p4.getBoundingBox(); + for(unsigned int i= (unsigned int)floor(bbox.getTop()); i<(unsigned int)ceil(bbox.getBottom()); ++i){ + for(unsigned int j=(unsigned int)floor(bbox.getLeft()); j<(unsigned int)ceil(bbox.getRight()); ++j){ + if(p4.isInside(vpImagePoint(i, j))){ + vpDisplay::displayPoint(I, vpImagePoint(i, j), vpColor::orange); + } } } - } - vpDisplay::flush(I); - std::cout << "Click to finish." << std::endl; + vpDisplay::flush(I); + std::cout << "Click to finish." << std::endl; - vpDisplay::getClick(I); + vpDisplay::getClick(I); + } } - } - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/tools/io/CMakeLists.txt b/test/tools/io/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d3680715447ae3fbd848dc8d8bacffc390c57b8 --- /dev/null +++ b/test/tools/io/CMakeLists.txt @@ -0,0 +1,68 @@ +############################################################################# +# +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ +# +# This file is part of the ViSP software. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. +# +# This software is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# ("GPL") version 2 as published by the Free Software Foundation. +# See the file LICENSE.txt at the root directory of this source +# distribution for additional information about the GNU GPL. +# +# For using ViSP with software that can not be combined with the GNU +# GPL, please contact INRIA about acquiring a ViSP Professional +# Edition License. +# +# See http://www.irisa.fr/lagadic/visp/visp.html for more information. +# +# This software was developed at: +# INRIA Rennes - Bretagne Atlantique +# Campus Universitaire de Beaulieu +# 35042 Rennes Cedex +# France +# http://www.irisa.fr/lagadic +# +# If you have questions regarding the use of this file, please contact +# INRIA at visp@inria.fr +# +# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# Description: +# ViSP overall configuration file. +# +# Authors: +# Fabien Spindler +# +############################################################################# + +# SOURCE variable corresponds to the list of all the sources to build binaries. +# The generate binary comes by removing the .cpp extension to +# the source name. +# +# If you want to add/remove a source, modify here +set(SOURCE + testIoTools.cpp +) + +# rule for binary build +foreach(source ${SOURCE}) + # Compute the name of the binary to create + get_filename_component(binary ${source} NAME_WE) + + # From source compile the binary and add link rules + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + + # Add test + add_test(${binary} ${binary}) + + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() + + diff --git a/test/tools/io/testIoTools.cpp b/test/tools/io/testIoTools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..871127ca3eae2af09f0300e283c5106252cbda02 --- /dev/null +++ b/test/tools/io/testIoTools.cpp @@ -0,0 +1,446 @@ +/**************************************************************************** + * + * $Id: testIoTools.cpp 5210 2015-01-26 10:51:11Z strinh $ + * + * This file is part of the ViSP software. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * ("GPL") version 2 as published by the Free Software Foundation. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact INRIA about acquiring a ViSP Professional + * Edition License. + * + * See http://www.irisa.fr/lagadic/visp/visp.html for more information. + * + * This software was developed at: + * INRIA Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * http://www.irisa.fr/lagadic + * + * If you have questions regarding the use of this file, please contact + * INRIA at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * + * Description: + * Test functions in vpIoTools. + * + * Authors: + * Souriya Trinh + * + *****************************************************************************/ + +/*! + + \example testIoTools.cpp + + \brief Test functions in IoTools. + +*/ + +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <visp/vpIoTools.h> + + +int +main(int argc, const char ** argv) +{ + const char c = vpIoTools::separator; + if(c == '\\') + { + std::cout << "The directory separator character is '" << c << "' (Windows platform)." << std::endl; + } + else { + std::cout << "The directory separator character is '" << c << "' (Unix like platform)." << std::endl; + } + + + std::string pathname = ""; +#if defined(_WIN32) + pathname = "C:\\Program Files (x86)\\Java\\jre7"; +#else + pathname = "/usr/bin/java"; +#endif + + std::cout << "Parent of " << pathname << " is " << vpIoTools::getParent(pathname) << std::endl; + std::cout << "Name of " << pathname << " is " << vpIoTools::getName(pathname) << std::endl; + + + if(argc == 3 && std::string(argv[1]) == std::string("-i")) + { + std::cout << "Parent of " << argv[2] << " is " << vpIoTools::getParent(argv[2]) << std::endl; + std::cout << "Name of " << argv[2] << " is " << vpIoTools::getName(argv[2]) << std::endl; + } + + std::string windowsPathnameStyle = "\\usr\\bin\\java"; + std::cout << "Parent of " << windowsPathnameStyle << " is " << vpIoTools::getParent(windowsPathnameStyle) << std::endl; + std::cout << "Name of " << windowsPathnameStyle << " is " << vpIoTools::getName(windowsPathnameStyle) << std::endl; + + std::string parent = "/usr/toto/", child = "\\blabla\\java"; + std::cout << "parent=" << vpIoTools::path(parent) << " ; child=" << vpIoTools::path(child) << std::endl; + std::cout << "Create file path from parent=" << parent << " and child=" << child << " is " + << vpIoTools::createFilePath(parent, child) << std::endl; + + std::string expandPath = "~/Documents/fictional directory/fictional file"; + std::cout << "Path for " << expandPath << " is " << vpIoTools::path(expandPath) << std::endl; + + std::cout << "Test get name with an empty pathname=" << vpIoTools::getName("") << std::endl; + std::cout << "Get parent with an empty pathname=" << vpIoTools::getParent("") << std::endl; + std::cout << "Get parent with a filename=" << vpIoTools::getParent("my_file.txt") << std::endl; + expandPath = "~/Documents/fictional dir/fictional file.txt"; + std::cout << "Get name with a unix expand pathname " << expandPath << "=" << vpIoTools::getName(expandPath) << std::endl; + std::cout << "Get parent with a unix expand pathname " << expandPath << "=" << vpIoTools::getParent(expandPath) << std::endl; + + + pathname = "c:/dir"; + std::cout << "pathname=" << vpIoTools::splitDrive(pathname).first << " ; " << vpIoTools::splitDrive(pathname).second << std::endl; + + std::cout << "isAbsolutePath of " << pathname << "=" << vpIoTools::isAbsolutePathname(pathname) << std::endl; + + pathname = "c:/dir/fictional directory/fictional file.txt"; + std::cout << "isAbsolutePath of " << pathname << "=" << vpIoTools::isAbsolutePathname(pathname) << std::endl; + + pathname = "/home/user/Documents/fictional directory/fictional file.txt"; + std::cout << "isAbsolutePath of " << pathname << "=" << vpIoTools::isAbsolutePathname(pathname) << std::endl; + + pathname = "~/Documents/fictional directory/fictional file.txt"; + std::cout << "isAbsolutePath of " << pathname << "=" << vpIoTools::isAbsolutePathname(pathname) << std::endl; + + pathname = "fictional directory/fictional file.txt"; + std::cout << "isAbsolutePath of " << pathname << "=" << vpIoTools::isAbsolutePathname(pathname) << std::endl; + + + //Test vpIoTools::splitDrive + unsigned int nbFail = 0, nbOk = 0; +#if defined(_WIN32) + if(strcmp(vpIoTools::splitDrive("c:\\foo\\bar").first.c_str(), "c:") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("c:\\foo\\bar").first << " should be=c:" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("c:\\foo\\bar").second.c_str(), "\\foo\\bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("c:\\foo\\bar").second << " should be=\\foo\\bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("c:/foo/bar").first.c_str(), "c:") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("c:/foo/bar").first << " should be=c:" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("c:/foo/bar").second.c_str(), "/foo/bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("c:/foo/bar").second << " should be=/foo/bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("\\\\conky\\mountpoint\\foo\\bar").first.c_str(), "\\\\conky\\mountpoint") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\conky\\mountpoint\\foo\\bar").first << " should be=\\\\conky\\mountpoint" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("\\\\conky\\mountpoint\\foo\\bar").second.c_str(), "\\foo\\bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\conky\\mountpoint\\foo\\bar").second << " should be=\\foo\\bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("//conky/mountpoint/foo/bar").first.c_str(), "//conky/mountpoint") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("//conky/mountpoint/foo/bar").first << " should be=//conky/mountpoint" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("//conky/mountpoint/foo/bar").second.c_str(), "/foo/bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("//conky/mountpoint/foo/bar").second << " should be=/foo/bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("\\\\\\conky\\mountpoint\\foo\\bar").first.c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\\\conky\\mountpoint\\foo\\bar").first << " should be=" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("\\\\\\conky\\mountpoint\\foo\\bar").second.c_str(), + "\\\\\\conky\\mountpoint\\foo\\bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\\\conky\\mountpoint\\foo\\bar").second << " should be=\\\\\\conky\\mountpoint\\foo\\bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("///conky/mountpoint/foo/bar").first.c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("///conky/mountpoint/foo/bar").first << " should be=" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("///conky/mountpoint/foo/bar").second.c_str(), "///conky/mountpoint/foo/bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("///conky/mountpoint/foo/bar").second << " should be=///conky/mountpoint/foo/bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("\\\\conky\\\\mountpoint\\foo\\bar").first.c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\conky\\\\mountpoint\\foo\\bar").first << " should be=" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("\\\\conky\\\\mountpoint\\foo\\bar").second.c_str(), + "\\\\conky\\\\mountpoint\\foo\\bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("\\\\conky\\\\mountpoint\\foo\\bar").second << " should be=\\\\conky\\\\mountpoint\\foo\\bar" << std::endl; + } + + if(strcmp(vpIoTools::splitDrive("//conky//mountpoint/foo/bar").first.c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("//conky//mountpoint/foo/bar").first << " should be=" << std::endl; + } + if(strcmp(vpIoTools::splitDrive("//conky//mountpoint/foo/bar").second.c_str(), "//conky//mountpoint/foo/bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::splitDrive("//conky//mountpoint/foo/bar").second << " should be=//conky//mountpoint/foo/bar" << std::endl; + } + + std::cout << "Test vpIoTools::splitDrive (Win32) - passed: " << nbOk << "/" << (nbOk+nbFail) << std::endl; +#endif + + + //Test vpIoTools::getFileExtension +#if defined(_WIN32) + nbFail = 0; + nbOk = 0; + + if(strcmp(vpIoTools::getFileExtension("foo.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("/foo/foo.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("/foo/foo.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension(".ext").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension(".ext") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("\\foo.ext\\foo").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("\\foo.ext\\foo") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("foo.ext\\").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.ext\\") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("foo.bar.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.bar.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("xx/foo.bar.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("xx/foo.bar.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("xx\\foo.bar.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("xx\\foo.bar.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("c:a/b\\c.d").c_str(), ".d") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("c:a/b\\c.d") << " should be=.d" << std::endl; + } + + std::cout << "Test vpIoTools::getFileExtension (WIN32 platform) - passed: " << nbOk << "/" << (nbOk+nbFail) << std::endl; +#else + nbFail = 0; + nbOk = 0; + + if(strcmp(vpIoTools::getFileExtension("foo.bar").c_str(), ".bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.bar") << " should be=.bar" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("foo.boo.bar").c_str(), ".bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.boo.bar") << " should be=.bar" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("foo.boo.biff.bar").c_str(), ".bar") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("foo.boo.biff.bar") << " should be=.bar" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension(".csh.rc").c_str(), ".rc") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension(".csh.rc") << " should be=.rc" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("nodots").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("nodots") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension(".cshrc").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension(".cshrc") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("...manydots").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("...manydots") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("...manydots.ext").c_str(), ".ext") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("...manydots.ext") << " should be=.ext" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension(".").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension(".") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("..").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("..") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("........").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("........") << " should be=" << std::endl; + } + + if(strcmp(vpIoTools::getFileExtension("").c_str(), "") == 0) { + nbOk++; + } + else { + nbFail++; + std::cout << "Fail=" << vpIoTools::getFileExtension("") << " should be=" << std::endl; + } + + std::cout << "Test vpIoTools::getFileExtension (Unix-like platform) - passed: " << nbOk << "/" << (nbOk+nbFail) << std::endl; +#endif + + + std::cout << std::endl << "End" << std::endl; + + return 0; +} diff --git a/test/tools/time/CMakeLists.txt b/test/tools/time/CMakeLists.txt index 189f0140d23a01ae49fcaf2050b66ac422c6c848..380fb6976ead677bf70182c2d047743fff772c32 100644 --- a/test/tools/time/CMakeLists.txt +++ b/test/tools/time/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testTime.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/tools/time/testTime.cpp b/test/tools/time/testTime.cpp index b9665a1fe8b62ae9bde1885123e9d4cb969337e6..259d9e602343d202f0cbca25732466deefd42428 100644 --- a/test/tools/time/testTime.cpp +++ b/test/tools/time/testTime.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testTime.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testTime.cpp 4658 2014-02-09 09:50:14Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,9 +47,9 @@ */ #include <visp/vpConfig.h> -#if defined UNIX +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX # include <unistd.h> -#elif defined WIN32 +#elif defined(_WIN32) # include <windows.h> # include <mmsystem.h> # include <winbase.h> @@ -61,7 +61,8 @@ #include <visp/vpTime.h> #include <visp/vpParseArgv.h> - +void usage(const char *name, const char *badparam); +bool getOptions(int argc, const char **argv); // List of allowed command line options #define GETOPTARGS "h" @@ -96,15 +97,15 @@ OPTIONS: Default\n\ */ bool getOptions(int argc, const char **argv) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'h': usage(argv[0], NULL); return false; break; default: - usage(argv[0], optarg); + usage(argv[0], optarg_); return false; break; } } @@ -113,7 +114,7 @@ bool getOptions(int argc, const char **argv) // standalone param or error usage(argv[0], NULL); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -124,65 +125,71 @@ bool getOptions(int argc, const char **argv) int main(int argc, const char ** argv) { - // Read the command line options - if (getOptions(argc, argv) == false) { - exit (-1); - } + try { + // Read the command line options + if (getOptions(argc, argv) == false) { + exit (-1); + } - double v = 0; + double v = 0; - double t0 = vpTime::measureTimeMs(); - for (int i =0 ; i < 100000; i ++) - for (int j =0 ; j < 100; j ++) - v = i * 2 / 3. + j; - std::cout << "Computed dummy value: " << v << std::endl; + double t0 = vpTime::measureTimeMs(); + for (int i =0 ; i < 100000; i ++) + for (int j =0 ; j < 100; j ++) + v = i * 2 / 3. + j; + std::cout << "Computed dummy value: " << v << std::endl; - double t1 = vpTime::measureTimeMs(); - vpTime::wait(t1, 40); + double t1 = vpTime::measureTimeMs(); + vpTime::wait(t1, 40); - double t2 = vpTime::measureTimeMs(); + double t2 = vpTime::measureTimeMs(); - // Sleep 10ms -#if defined UNIX - usleep(10*1000); -#elif defined WIN32 - Sleep(10); + // Sleep 10ms +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + usleep(10*1000); +#elif defined(_WIN32) + Sleep(10); #endif - double t3 = vpTime::measureTimeMs(); + double t3 = vpTime::measureTimeMs(); - // Sleep 2ms -#if defined UNIX - usleep(2*1000); -#elif defined WIN32 - Sleep(2); + // Sleep 2ms +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + usleep(2*1000); +#elif defined(_WIN32) + Sleep(2); #endif - double t4 = vpTime::measureTimeMs(); + double t4 = vpTime::measureTimeMs(); - vpTime::wait(t4, 19); + vpTime::wait(t4, 19); - double t5 = vpTime::measureTimeMs(); + double t5 = vpTime::measureTimeMs(); - vpTime::wait(5); + vpTime::wait(5); - double t6 = vpTime::measureTimeMs(); + double t6 = vpTime::measureTimeMs(); - vpTime::wait(21); + vpTime::wait(21); - double t7 = vpTime::measureTimeMs(); + double t7 = vpTime::measureTimeMs(); - vpTime::wait(2); + vpTime::wait(2); - double t8 = vpTime::measureTimeMs(); + double t8 = vpTime::measureTimeMs(); - std::cout << "t1-t0: computation time: " << t1 - t0 << std::endl; - std::cout << "t2-t1: wait(t1, 40 ms): " << t2 - t1 << std::endl; - std::cout << "t3-t2: sleep(10 ms): " << t3 - t2 << std::endl; - std::cout << "t4-t3: sleep(2 ms): " << t4 - t3 << std::endl; - std::cout << "t5-t4: wait(t, 19 ms): " << t5 - t4 << std::endl; - std::cout << "t6-t5: wait(5 ms): " << t6 - t5 << std::endl; - std::cout << "t7-t6: wait(21 ms): " << t7 - t6 << std::endl; - std::cout << "t8-t7: wait(2 ms): " << t8 - t7 << std::endl; + std::cout << "t1-t0: computation time: " << t1 - t0 << std::endl; + std::cout << "t2-t1: wait(t1, 40 ms): " << t2 - t1 << std::endl; + std::cout << "t3-t2: sleep(10 ms): " << t3 - t2 << std::endl; + std::cout << "t4-t3: sleep(2 ms): " << t4 - t3 << std::endl; + std::cout << "t5-t4: wait(t, 19 ms): " << t5 - t4 << std::endl; + std::cout << "t6-t5: wait(5 ms): " << t6 - t5 << std::endl; + std::cout << "t7-t6: wait(21 ms): " << t7 - t6 << std::endl; + std::cout << "t8-t7: wait(2 ms): " << t8 - t7 << std::endl; - return 0; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/test/tools/xml/CMakeLists.txt b/test/tools/xml/CMakeLists.txt index 4a1b57aa8a92fa1b3efa3741b45bae595a07a651..1738304f178eb273127440ed1ed25915439d67af 100644 --- a/test/tools/xml/CMakeLists.txt +++ b/test/tools/xml/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,25 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testXmlParser.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) # Add test - ADD_TEST(${binary} ${binary}) + add_test(${binary} ${binary}) -ENDFOREACH(source) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/tools/xml/testXmlParser.cpp b/test/tools/xml/testXmlParser.cpp index 1d725605e6adccaf65f7116ed66ff12597af167c..d0f074010a88445ba3ca7a1c602c516cabcc4300 100755 --- a/test/tools/xml/testXmlParser.cpp +++ b/test/tools/xml/testXmlParser.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testXmlParser.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testXmlParser.cpp 5126 2015-01-05 22:07:11Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -127,17 +127,13 @@ protected: */ vpExampleDataParser::vpExampleDataParser() + : m_range(0.), m_step(0), m_size_filter(0), m_name("") { nodeMap["config"] = config; nodeMap["range"] = range; nodeMap["step"] = step; nodeMap["size_filter"] = size_filter; nodeMap["name"] = name; - - m_range = 0.0; - m_step = 0; - m_size_filter = 0; - m_name = ""; } /*! @@ -212,6 +208,9 @@ vpExampleDataParser::writeMainClass(xmlNodePtr node) // List of allowed command line options #define GETOPTARGS "o:h" +void usage(const char *name, const char *badparam, const std::string& opath, const std::string& user); +bool getOptions(int argc, const char **argv, std::string &opath, const std::string& user); + /*! Print the program options. @@ -225,25 +224,23 @@ Print the program options. void usage(const char *name, const char *badparam, const std::string& opath, const std::string& user) { fprintf(stdout, "\n\ - Write and read data in a xml file.\n\ +Write and read data in a xml file.\n\ \n\ - SYNOPSIS\n\ - %s [-o <output image path>] [-h]\n \ - ", name); - - fprintf(stdout, "\n\ - OPTIONS: Default\n\ - -o <output data path> %s\n\ - Set data output path.\n\ - From this directory, creates the \"%s\"\n\ - subdirectory depending on the username, where \n\ - dataTestXml.xml file is written.\n\ +SYNOPSIS\n\ + %s [-o <output image path>] [-h]\n", name); + + fprintf(stdout, "\n\ +OPTIONS: Default\n\ + -o <output data path> %s\n\ + Set data output path.\n\ + From this directory, creates the \"%s\"\n\ + subdirectory depending on the username, where \n\ + dataTestXml.xml file is written.\n\ \n\ - -h\n\ - Print the help.\n\n", - opath.c_str(), user.c_str()); + -h\n\ + Print the help.\n\n", opath.c_str(), user.c_str()); - if (badparam) { + if (badparam) { fprintf(stderr, "ERROR: \n" ); fprintf(stderr, "\nBad parameter [%s]\n", badparam); } @@ -258,19 +255,18 @@ void usage(const char *name, const char *badparam, const std::string& opath, con \param user : Username. \return false if the program has to be stopped, true otherwise. */ -bool getOptions(int argc, const char **argv, - std::string &opath, const std::string& user) +bool getOptions(int argc, const char **argv, std::string &opath, const std::string& user) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { - case 'o': opath = optarg; break; + case 'o': opath = optarg_; break; case 'h': usage(argv[0], NULL, opath, user); return false; break; default: - usage(argv[0], optarg, opath, user); return false; break; + usage(argv[0], optarg_, opath, user); return false; break; } } @@ -278,7 +274,7 @@ bool getOptions(int argc, const char **argv, // standalone param or error usage(argv[0], NULL, opath, user); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -293,88 +289,93 @@ bool getOptions(int argc, const char **argv, int main(int argc, const char** argv) { - std::string opt_opath; - std::string opath; - std::string filename; - std::string username; - - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << " testXmlParser.cpp" <<std::endl << std::endl ; - std::cout << " writing and readind data using a xml parser" << std::endl ; - std::cout << "-------------------------------------------------------" << std::endl ; - std::cout << std::endl ; - - // Set the default output path -#ifdef UNIX - opt_opath = "/tmp"; -#elif WIN32 - opt_opath = "C:\\temp"; + try { + std::string opt_opath; + std::string opath; + std::string filename; + std::string username; + + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << " testXmlParser.cpp" <<std::endl << std::endl ; + std::cout << " writing and readind data using a xml parser" << std::endl ; + std::cout << "-------------------------------------------------------" << std::endl ; + std::cout << std::endl ; + + // Set the default output path +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + opt_opath = "/tmp"; +#elif defined(_WIN32) + opt_opath = "C:\\temp"; #endif - // Get the user login name - vpIoTools::getUserName(username); + // Get the user login name + vpIoTools::getUserName(username); - // Read the command line options - if (getOptions(argc, argv, opt_opath, username) == false) { - exit (-1); - } + // Read the command line options + if (getOptions(argc, argv, opt_opath, username) == false) { + exit (-1); + } - // Get the option values - if (!opt_opath.empty()) - opath = opt_opath; + // Get the option values + if (!opt_opath.empty()) + opath = opt_opath; - // Append to the output path string, the login name of the user - std::string dirname = opath + vpIoTools::path("/") + username; + // Append to the output path string, the login name of the user + std::string dirname = vpIoTools::createFilePath(opath, username); - // Test if the output path exist. If no try to create it - if (vpIoTools::checkDirectory(dirname) == false) { - try { - // Create the dirname - vpIoTools::makeDirectory(dirname); + // Test if the output path exist. If no try to create it + if (vpIoTools::checkDirectory(dirname) == false) { + try { + // Create the dirname + vpIoTools::makeDirectory(dirname); + } + catch (...) { + usage(argv[0], NULL, opath, username); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot create " << dirname << std::endl; + std::cerr << " Check your -o " << opath << " option " << std::endl; + exit(-1); + } } - catch (...) { - usage(argv[0], NULL, opath, username); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot create " << dirname << std::endl; - std::cerr << " Check your -o " << opath << " option " << std::endl; - exit(-1); + + filename = dirname + vpIoTools::path("/") + "dataTestXml.xml"; + + // Write data using a parser. + { + vpExampleDataParser parser1; + + // Acquire data from measurments or tests. + parser1.setRange(3.5); + parser1.setStep(2); + parser1.setSizeFilter(5); + parser1.setName("cube"); + + std::cout << "Write data to " << filename << std::endl; + parser1.save(filename); } - } - filename = dirname + vpIoTools::path("/") + "dataTestXml.xml"; - - // Write data using a parser. - { - vpExampleDataParser parser1; - - // Acquire data from measurments or tests. - parser1.setRange(3.5); - parser1.setStep(2); - parser1.setSizeFilter(5); - parser1.setName("cube"); - - std::cout << "Write data to " << filename << std::endl; - parser1.save(filename); - } - - // Read data using another parser. - { - vpExampleDataParser parser2; - - parser2.parse(filename); - - std::cout << "Read from " << filename << std::endl ; - std::cout << "Range : " << parser2.getRange() << std::endl; - std::cout << "Step : " << parser2.getStep() << std::endl; - std::cout << "Filter size : " << parser2.getSizeFilter() << std::endl; - std::cout << "name : " << parser2.getName() << std::endl; - } + // Read data using another parser. + { + vpExampleDataParser parser2; + + parser2.parse(filename); - // Clean up memory allocated by the xml library - vpXmlParser::cleanup(); + std::cout << "Read from " << filename << std::endl ; + std::cout << "Range : " << parser2.getRange() << std::endl; + std::cout << "Step : " << parser2.getStep() << std::endl; + std::cout << "Filter size : " << parser2.getSizeFilter() << std::endl; + std::cout << "name : " << parser2.getName() << std::endl; + } - return 0; + // Clean up memory allocated by the xml library + vpXmlParser::cleanup(); + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } #else diff --git a/test/tracking/CMakeLists.txt b/test/tracking/CMakeLists.txt index 43a83546ea981e97418e5e21dfcd55f2943340a4..9d1bec4e87443bd23855c30a21913ef9db9a6627 100644 --- a/test/tracking/CMakeLists.txt +++ b/test/tracking/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,24 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testTrackDot.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + # Add test + add_test(${binary} ${binary} -c ${OPTION_TO_DESACTIVE_DISPLAY}) -ENDFOREACH(source) - -# Add test -ADD_TEST(testTrackDot testTrackDot -c ${OPTION_TO_DESACTIVE_DISPLAY}) - -# customize clean target -SET_DIRECTORY_PROPERTIES(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES "core*;*~;gmon.out;DartTestfile.txt" -) + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() diff --git a/test/tracking/testTrackDot.cpp b/test/tracking/testTrackDot.cpp index 1f527e5ed66d858c9093b54d00e6aa5ca2bde507..2af158619368349b08f8754777c0e74fef237975 100644 --- a/test/tracking/testTrackDot.cpp +++ b/test/tracking/testTrackDot.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testTrackDot.cpp 4323 2013-07-18 09:24:01Z fspindle $ + * $Id: testTrackDot.cpp 4814 2014-07-31 11:38:39Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,7 @@ #include <visp/vpDisplayX.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayGDI.h> -#include <visp/vpDot.h> +#include <visp/vpDot2.h> #include <visp/vpFeatureEllipse.h> #include <visp/vpCameraParameters.h> #include <visp/vpFeatureBuilder.h> @@ -71,6 +71,10 @@ // List of allowed command line options #define GETOPTARGS "cdi:h" +bool getOptions(int argc, const char **argv, std::string &ipath, + bool &click_allowed, bool &display); + +void usage(const char *name, const char *badparam, std::string ipath); /*! Print the program options. @@ -128,18 +132,18 @@ OPTIONS: Default\n\ bool getOptions(int argc, const char **argv, std::string &ipath, bool &click_allowed, bool &display) { - const char *optarg; + const char *optarg_; int c; - while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg)) > 1) { + while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) { switch (c) { case 'c': click_allowed = false; break; case 'd': display = false; break; - case 'i': ipath = optarg; break; + case 'i': ipath = optarg_; break; case 'h': usage(argv[0], NULL, ipath); return false; break; default: - usage(argv[0], optarg, ipath); + usage(argv[0], optarg_, ipath); return false; break; } } @@ -148,7 +152,7 @@ bool getOptions(int argc, const char **argv, std::string &ipath, // standalone param or error usage(argv[0], NULL, ipath); std::cerr << "ERROR: " << std::endl; - std::cerr << " Bad argument " << optarg << std::endl << std::endl; + std::cerr << " Bad argument " << optarg_ << std::endl << std::endl; return false; } @@ -159,107 +163,105 @@ bool getOptions(int argc, const char **argv, std::string &ipath, int main(int argc, const char ** argv) { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string dirname; - std::string filename; - bool opt_click_allowed = true; - bool opt_display = true; - - // Get the VISP_IMAGE_PATH environment variable value - char *ptenv = getenv("VISP_INPUT_IMAGE_PATH"); - if (ptenv != NULL) - env_ipath = ptenv; - - // Set the default input path - if (! env_ipath.empty()) - ipath = env_ipath; - - - // Read the command line options - if (getOptions(argc, argv, opt_ipath, - opt_click_allowed, opt_display) == false) { - exit (-1); - } + try { + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string dirname; + std::string filename; + bool opt_click_allowed = true; + bool opt_display = true; + + // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value + env_ipath = vpIoTools::getViSPImagesDataPath(); + + // Set the default input path + if (! env_ipath.empty()) + ipath = env_ipath; + + + // Read the command line options + if (getOptions(argc, argv, opt_ipath, + opt_click_allowed, opt_display) == false) { + exit (-1); + } - // Get the option values - if (!opt_ipath.empty()) - ipath = opt_ipath; - - // Compare ipath and env_ipath. If they differ, we take into account - // the input path comming from the command line option - if (!opt_ipath.empty() && !env_ipath.empty()) { - if (ipath != env_ipath) { - std::cout << std::endl - << "WARNING: " << std::endl; - std::cout << " Since -i <visp image path=" << ipath << "> " - << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl - << " we skip the environment variable." << std::endl; + // Get the option values + if (!opt_ipath.empty()) + ipath = opt_ipath; + + // Compare ipath and env_ipath. If they differ, we take into account + // the input path comming from the command line option + if (!opt_ipath.empty() && !env_ipath.empty()) { + if (ipath != env_ipath) { + std::cout << std::endl + << "WARNING: " << std::endl; + std::cout << " Since -i <visp image path=" << ipath << "> " + << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl + << " we skip the environment variable." << std::endl; + } } - } - // Test if an input path is set - if (opt_ipath.empty() && env_ipath.empty()){ - usage(argv[0], NULL, ipath); - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " - << std::endl - << " environment variable to specify the location of the " << std::endl - << " image path where test images are located." << std::endl << std::endl; - exit(-1); - } + // Test if an input path is set + if (opt_ipath.empty() && env_ipath.empty()){ + usage(argv[0], NULL, ipath); + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " + << std::endl + << " environment variable to specify the location of the " << std::endl + << " image path where test images are located." << std::endl << std::endl; + exit(-1); + } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage<unsigned char> I ; + // Declare an image, this is a gray level image (unsigned char) + // it size is not defined yet, it will be defined when the image will + // read on the disk + vpImage<unsigned char> I ; - // Set the path location of the image sequence - dirname = ipath + vpIoTools::path("/ViSP-images/ellipse/"); + // Set the path location of the image sequence + dirname = vpIoTools::createFilePath(ipath, "ViSP-images/ellipse"); - // Build the name of the image file - filename = dirname + "ellipse.pgm"; + // Build the name of the image file + filename = vpIoTools::createFilePath(dirname, "ellipse.pgm"); - // Read the PGM image named "filename" on the disk, and put the - // bitmap into the image structure I. I is initialized to the - // correct size - // - // exception readPGM may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try{ - vpCTRACE << "Load: " << filename << std::endl; + // Read the PGM image named "filename" on the disk, and put the + // bitmap into the image structure I. I is initialized to the + // correct size + // + // exception readPGM may throw various exception if, for example, + // the file does not exist, or if the memory cannot be allocated + try{ + vpCTRACE << "Load: " << filename << std::endl; - vpImageIo::read(I, filename) ; - } - catch(...) - { - // an exception is throwned if an exception from readPGM has been catched - // here this will result in the end of the program - // Note that another error message has been printed from readPGM - // to give more information about the error - std::cerr << std::endl - << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." - << std::endl; - exit(-1); - } + vpImageIo::read(I, filename) ; + } + catch(...) + { + // an exception is throwned if an exception from readPGM has been catched + // here this will result in the end of the program + // Note that another error message has been printed from readPGM + // to give more information about the error + std::cerr << std::endl + << "ERROR:" << std::endl; + std::cerr << " Cannot read " << filename << std::endl; + std::cerr << " Check your -i " << ipath << " option " << std::endl + << " or VISP_INPUT_IMAGE_PATH environment variable." + << std::endl; + exit(-1); + } - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK or GDI. #if defined VISP_HAVE_X11 - vpDisplayX display; + vpDisplayX display; #elif defined VISP_HAVE_GTK - vpDisplayGTK display; + vpDisplayGTK display; #elif defined VISP_HAVE_GDI - vpDisplayGDI display; + vpDisplayGDI display; #endif - if (opt_display) { - try{ + if (opt_display) { // Display size is automatically defined by the image (I) size display.init(I, 100, 100,"Display...") ; // Display the image @@ -271,38 +273,40 @@ main(int argc, const char ** argv) //Flush the display vpDisplay::flush(I) ; } - catch(...) - { - vpERROR_TRACE("Error while displaying the image") ; - exit(-1); - } - } - vpDot dot ; - dot.setMaxDotSize(0.50); // dot max size = 50% of the image size - vpImagePoint ip; - ip.set_i( 140 ); - ip.set_j( 140 ); - dot.initTracking(I, ip) ; - if (opt_display) { - dot.setGraphics(true) ; - } - else { - dot.setGraphics(false) ; - } - dot.setComputeMoments(true) ; - dot.track(I) ; - - vpFeatureEllipse e ; - - vpCameraParameters cam ; - vpFeatureBuilder::create(e,cam,dot) ; - if (opt_display) { - e.display(cam, I, vpColor::red) ; - if (opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; - vpDisplay::getClick(I) ; + vpDot2 dot ; + std::cout << "debut 1\n"; + //dot.setMaxDotSize(0.50); // dot max size = 50% of the image size + vpImagePoint ip; + ip.set_i( 140 ); + ip.set_j( 140 ); + dot.initTracking(I, ip); + if (opt_display) { + dot.setGraphics(true) ; } + else { + dot.setGraphics(false) ; + } + dot.setComputeMoments(true); + dot.track(I) ; + + vpFeatureEllipse e ; + + vpCameraParameters cam ; + vpFeatureBuilder::create(e,cam,dot) ; + if (opt_display) { + e.display(cam, I, vpColor::red) ; + vpDisplay::flush(I); + if (opt_click_allowed) { + std::cout << "A click to exit..." << std::endl; + vpDisplay::getClick(I) ; + } + } + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } #else diff --git a/test/visual-feature/CMakeLists.txt b/test/visual-feature/CMakeLists.txt index 5b1c83e7319c6eafb0e5bbd4c59181723fa76b14..203793e139061b0e5c3f4d0b974fd34bdbcea792 100755 --- a/test/visual-feature/CMakeLists.txt +++ b/test/visual-feature/CMakeLists.txt @@ -1,9 +1,9 @@ ############################################################################# # -# $Id: CMakeLists.txt 4056 2013-01-05 13:04:42Z fspindle $ +# $Id: CMakeLists.txt 4961 2014-11-14 13:06:26Z fspindle $ # # This file is part of the ViSP software. -# Copyright (C) 2005 - 2013 by INRIA. All rights reserved. +# Copyright (C) 2005 - 2014 by INRIA. All rights reserved. # # This software is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,26 +43,28 @@ # the source name. # # If you want to add/remove a source, modify here -SET (SOURCE +set(SOURCE testFeature.cpp testFeatureMoment.cpp testFeatureSegment.cpp ) # rule for binary build -FOREACH(source ${SOURCE}) +foreach(source ${SOURCE}) # Compute the name of the binary to create - GET_FILENAME_COMPONENT(binary ${source} NAME_WE) + get_filename_component(binary ${source} NAME_WE) # From source compile the binary and add link rules - ADD_EXECUTABLE(${binary} ${source}) - TARGET_LINK_LIBRARIES(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) + add_executable(${binary} ${source}) + target_link_libraries(${binary} ${VISP_INTERN_LIBRARY} ${VISP_EXTERN_LIBRARIES}) - # Add test + add_dependencies(visp_tests ${binary}) + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${binary} PROPERTIES FOLDER "tests") + endif() +endforeach() -ENDFOREACH(source) - -ADD_TEST(testFeature testFeature ${OPTION_TO_DESACTIVE_DISPLAY}) -ADD_TEST(testFeatureMoment testFeatureMoment) -ADD_TEST(testFeatureSegment testFeatureSegment ${OPTION_TO_DESACTIVE_DISPLAY} -normalized 0) -ADD_TEST(testFeatureSegment-norm testFeatureSegment ${OPTION_TO_DESACTIVE_DISPLAY} -normalized 1) +add_test(testFeature testFeature) +add_test(testFeatureMoment testFeatureMoment) +add_test(testFeatureSegment testFeatureSegment ${OPTION_TO_DESACTIVE_DISPLAY} -normalized 0) +add_test(testFeatureSegment-norm testFeatureSegment ${OPTION_TO_DESACTIVE_DISPLAY} -normalized 1) diff --git a/test/visual-feature/testFeature.cpp b/test/visual-feature/testFeature.cpp index c6fe49d0f624308e85b8ca9ef3d44970a3853a50..cd8a2cc8877fa99b69f51f174fbc2f8c28a5ab3f 100644 --- a/test/visual-feature/testFeature.cpp +++ b/test/visual-feature/testFeature.cpp @@ -1,9 +1,9 @@ /**************************************************************************** * - * $Id: testFeature.cpp 4056 2013-01-05 13:04:42Z fspindle $ + * $Id: testFeature.cpp 4574 2014-01-09 08:48:51Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,8 +96,8 @@ int main() } return 0; } - catch(vpServoException e) { - std::cout << e << std::endl; - return -1; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } } diff --git a/test/visual-feature/testFeatureMoment.cpp b/test/visual-feature/testFeatureMoment.cpp index fd5f8b52dfff6a968aa1b8f911f37f41956d65d9..cfd2ada0b59199af3e3f87842068fbcfcfe7208a 100644 --- a/test/visual-feature/testFeatureMoment.cpp +++ b/test/visual-feature/testFeatureMoment.cpp @@ -3,7 +3,7 @@ * $Id: testFeatureMoment.cpp 3323 2011-09-13 15:23:56Z fnovotny $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,43 +51,36 @@ #include <iostream> //initialize scene in the interface -void initScene(); +void initScene(const vpHomogeneousMatrix& cMo, const vpHomogeneousMatrix& cdMo, + vpMomentObject &src, vpMomentObject &dst); -void init(vpHomogeneousMatrix& cMo, vpHomogeneousMatrix& cdMo); -vpMatrix execute(); //launch the test -void _planeToABC(vpPlane& pl, double& A,double& B, double& C); +vpMatrix execute(const vpHomogeneousMatrix& cMo, const vpHomogeneousMatrix& cdMo, + vpMomentObject &src, vpMomentObject &dst); //launch the test +void planeToABC(const vpPlane& pl, double& A,double& B, double& C); int test(double x,double y,double z,double alpha); -//start and destination positioning matrices -vpHomogeneousMatrix cMo; -vpHomogeneousMatrix cdMo; - -vpServo::vpServoIteractionMatrixType interaction_type; //current or desired - -//source and destination objects for moment manipulation -vpMomentObject src(6); -vpMomentObject dst(6); - -using namespace std; - //Compute a set of parallel positions and check if the matrix is in the right form; int main() { - int sum=0; - for(double i=-0.2;i<0.2;i+=0.1){ - for(double j=-0.2;j<0.2;j+=0.1){ - for(double k=-vpMath::rad(30);k<vpMath::rad(30);k+=vpMath::rad(10)){ - for(double l=0.5;l<1.5;l+=0.1){ - sum+=test(i,j,l,k); + try { + int sum=0; + for(double i=-0.2;i<0.2;i+=0.1){ + for(double j=-0.2;j<0.2;j+=0.1){ + for(double k=-vpMath::rad(30);k<vpMath::rad(30);k+=vpMath::rad(10)){ + for(double l=0.5;l<1.5;l+=0.1){ + sum+=test(i,j,l,k); + } } - } + } } + if(sum<0) return -1; + else return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; } - - - if(sum<0) return -1; - else return 0; } int test(double x,double y,double z,double alpha){ @@ -96,48 +89,51 @@ int test(double x,double y,double z,double alpha){ //Desired pose vpHomogeneousMatrix cdMo(vpHomogeneousMatrix(0.0,0.0,1.0,vpMath::rad(0),vpMath::rad(0),-vpMath::rad(0))); + //source and destination objects for moment manipulation + vpMomentObject src(6); + vpMomentObject dst(6); + //init and run the simulation - init(cMo,cdMo); - vpMatrix mat = execute(); + initScene(cMo, cdMo, src, dst); //initialize graphical scene (for interface) + + vpMatrix mat = execute(cMo, cdMo, src, dst); - if(fabs(mat[0][0]-(-1)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[0][1]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[0][2]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[0][0]-(-1)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[0][1]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[0][2]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[1][0]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[1][1]-(-1)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[1][2]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[1][0]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[1][1]-(-1)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[1][2]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[2][0]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[2][1]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[2][2]-(-1)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[2][5]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[2][0]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[2][1]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[2][2]-(-1)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[2][5]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[3][0]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[3][1]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[3][2]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[3][5]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[3][0]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[3][1]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[3][2]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[3][5]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[4][0]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[4][1]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[4][2]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[4][5]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[4][0]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[4][1]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[4][2]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[4][5]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[5][0]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[5][1]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[5][2]-(0)) > numeric_limits<double>::epsilon()*1e10) return -1; - if(fabs(mat[5][5]-(-1)) > numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[5][0]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[5][1]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[5][2]-(0)) > std::numeric_limits<double>::epsilon()*1e10) return -1; + if(fabs(mat[5][5]-(-1)) > std::numeric_limits<double>::epsilon()*1e10) return -1; return 0; } - - - - -void initScene(){ - vector<vpPoint> src_pts; - vector<vpPoint> dst_pts; +void initScene(const vpHomogeneousMatrix& cMo, const vpHomogeneousMatrix& cdMo, + vpMomentObject &src, vpMomentObject &dst) +{ + std::vector<vpPoint> src_pts; + std::vector<vpPoint> dst_pts; double x[5] = { 0.2, 0.2,-0.2,-0.2, 0.2 }; double y[5] = {-0.1, 0.1, 0.1,-0.1,-0.1 }; @@ -160,21 +156,14 @@ void initScene(){ } dst.setType(vpMomentObject::DENSE_POLYGON); dst.fromVector(dst_pts); - } -void init(vpHomogeneousMatrix& _cMo, vpHomogeneousMatrix& _cdMo) +vpMatrix execute(const vpHomogeneousMatrix& cMo, const vpHomogeneousMatrix& cdMo, + vpMomentObject &src, vpMomentObject &dst) { - cMo = _cMo; - cdMo = _cdMo; - interaction_type = vpServo::CURRENT; + vpServo::vpServoIteractionMatrixType interaction_type = vpServo::CURRENT; ; //current or desired - - initScene(); //initialize graphical scene (for interface) -} - -vpMatrix execute(){ vpServo task; task.setServo(vpServo::EYEINHAND_CAMERA); //A,B,C parameters of source and destination plane @@ -186,11 +175,11 @@ vpMatrix execute(){ vpPlane pl; pl.setABCD(0,0,1.0,0); pl.changeFrame(cMo); - _planeToABC(pl,A,B,C); + planeToABC(pl,A,B,C); pl.setABCD(0,0,1.0,0); pl.changeFrame(cdMo); - _planeToABC(pl,Ad,Bd,Cd); + planeToABC(pl,Ad,Bd,Cd); //extracting initial position (actually we only care about Zdst) vpTranslationVector vec; @@ -228,8 +217,8 @@ vpMatrix execute(){ } -void _planeToABC(vpPlane& pl, double& A,double& B, double& C){ - +void planeToABC(const vpPlane& pl, double& A,double& B, double& C) +{ A=-pl.getA()/pl.getD(); B=-pl.getB()/pl.getD(); C=-pl.getC()/pl.getD(); diff --git a/test/visual-feature/testFeatureSegment.cpp b/test/visual-feature/testFeatureSegment.cpp index aef937bf031bb6408b226d7390bc1e27959b7318..855035acd587688110b8f1af23d375cebb89972f 100644 --- a/test/visual-feature/testFeatureSegment.cpp +++ b/test/visual-feature/testFeatureSegment.cpp @@ -3,7 +3,7 @@ * $Id: testFeature.cpp 3530 2012-01-03 10:52:12Z fspindle $ * * This file is part of the ViSP software. - * Copyright (C) 2005 - 2013 by INRIA. All rights reserved. + * Copyright (C) 2005 - 2014 by INRIA. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,19 +69,20 @@ */ int main(int argc, const char **argv) { + try { #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - int opt_display = 1; - int opt_curves = 1; + int opt_no_display = 0; + int opt_curves = 1; #endif - int opt_normalized = 1; + int opt_normalized = 1; - // Parse the command line to set the variables - vpParseArgv::vpArgvInfo argTable[] = + // Parse the command line to set the variables + vpParseArgv::vpArgvInfo argTable[] = { - #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - {"-d", vpParseArgv::ARGV_CONSTANT, 0, (char *) &opt_display, + #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) + {"-d", vpParseArgv::ARGV_CONSTANT, 0, (char *) &opt_no_display, "Disable display and graphics viewer."}, - #endif + #endif {"-normalized", vpParseArgv::ARGV_INT, (char*) NULL, (char *) &opt_normalized, "1 to use normalized features, 0 for non normalized."}, {"-h", vpParseArgv::ARGV_HELP, (char*) NULL, (char *) NULL, @@ -89,131 +90,89 @@ int main(int argc, const char **argv) {(char*) NULL, vpParseArgv::ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL} } ; - // Read the command line options - if(vpParseArgv::parse(&argc, argv, argTable, - vpParseArgv::ARGV_NO_LEFTOVERS | - vpParseArgv::ARGV_NO_ABBREV | - vpParseArgv::ARGV_NO_DEFAULTS)) { - return (false); - } + // Read the command line options + if(vpParseArgv::parse(&argc, argv, argTable, + vpParseArgv::ARGV_NO_LEFTOVERS | + vpParseArgv::ARGV_NO_ABBREV | + vpParseArgv::ARGV_NO_DEFAULTS)) { + return (false); + } - std::cout << "Used options: " << std::endl; + std::cout << "Used options: " << std::endl; #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - opt_curves = opt_display; - std::cout << " - display : " << opt_display << std::endl; - std::cout << " - curves : " << opt_curves << std::endl; + opt_curves = (opt_no_display == 0) ? 1 : 0; + std::cout << " - no display: " << opt_no_display << std::endl; + std::cout << " - curves : " << opt_curves << std::endl; #endif - std::cout << " - normalized: " << opt_normalized << std::endl; + std::cout << " - normalized: " << opt_normalized << std::endl; - vpCameraParameters cam(640.,480.,320.,240.); + vpCameraParameters cam(640.,480.,320.,240.); #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) - vpDisplay *display = NULL; - if (opt_display) { + vpDisplay *display = NULL; + if (!opt_no_display) { #if defined(VISP_HAVE_X11) - display = new vpDisplayX; + display = new vpDisplayX; #elif defined VISP_HAVE_GDI - display = new vpDisplayGDI; + display = new vpDisplayGDI; #endif - } + } #endif - vpImage<unsigned char> I(480,640,0); + vpImage<unsigned char> I(480,640,0); #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - if (opt_display) - display->init(I); + if (!opt_no_display) + display->init(I); #endif - vpHomogeneousMatrix cMo (-0.5, 0.5, 4., vpMath::rad(10), vpMath::rad(20), vpMath::rad(90)); - vpHomogeneousMatrix cdMo(0., 0., 1., vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); + vpHomogeneousMatrix cMo (-0.5, 0.5, 4., vpMath::rad(10), vpMath::rad(20), vpMath::rad(90)); + vpHomogeneousMatrix cdMo(0., 0., 1., vpMath::rad(0), vpMath::rad(0), vpMath::rad(0)); - vpPoint P[4]; // 4 points in the object frame - P[0].setWorldCoordinates( .1, .1, 0.); - P[1].setWorldCoordinates(-.1, .1, 0.); - P[2].setWorldCoordinates(-.1, -.1, 0.); - P[3].setWorldCoordinates( .1, -.1, 0.); + vpPoint P[4]; // 4 points in the object frame + P[0].setWorldCoordinates( .1, .1, 0.); + P[1].setWorldCoordinates(-.1, .1, 0.); + P[2].setWorldCoordinates(-.1, -.1, 0.); + P[3].setWorldCoordinates( .1, -.1, 0.); - vpPoint Pd[4]; // 4 points in the desired camera frame - for (int i=0; i<4; i++) { - Pd[i] = P[i]; - Pd[i].project(cdMo); - } - vpPoint Pc[4]; // 4 points in the current camera frame - for (int i=0; i<4; i++) { - Pc[i] = P[i]; - Pc[i].project(cMo); - } - - vpFeatureSegment seg_cur[2], seg_des[2]; // Current and desired features - for (int i=0; i <2; i++) - { - if (opt_normalized) { - seg_cur[i].setNormalized(true); - seg_des[i].setNormalized(true); + vpPoint Pd[4]; // 4 points in the desired camera frame + for (int i=0; i<4; i++) { + Pd[i] = P[i]; + Pd[i].project(cdMo); } - else { - seg_cur[i].setNormalized(false); - seg_des[i].setNormalized(false); + vpPoint Pc[4]; // 4 points in the current camera frame + for (int i=0; i<4; i++) { + Pc[i] = P[i]; + Pc[i].project(cMo); } - vpFeatureBuilder::create(seg_cur[i], Pc[i*2], Pc[i*2+1]); - vpFeatureBuilder::create(seg_des[i], Pd[i*2], Pd[i*2+1]); - seg_cur[i].print(); - seg_des[i].print(); - } - - //define visual servoing task - vpServo task; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(1) ; - - for (int i=0; i <2; i++) - task.addFeature(seg_cur[i], seg_des[i]); - -#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) - if (opt_display) { - vpDisplay::display(I); - for (int i=0; i <2; i++) { - seg_cur[i].display(cam, I, vpColor::red); - seg_des[i].display(cam, I, vpColor::green); - vpDisplay::flush(I); + + vpFeatureSegment seg_cur[2], seg_des[2]; // Current and desired features + for (int i=0; i <2; i++) + { + if (opt_normalized) { + seg_cur[i].setNormalized(true); + seg_des[i].setNormalized(true); + } + else { + seg_cur[i].setNormalized(false); + seg_des[i].setNormalized(false); + } + vpFeatureBuilder::create(seg_cur[i], Pc[i*2], Pc[i*2+1]); + vpFeatureBuilder::create(seg_des[i], Pd[i*2], Pd[i*2+1]); + seg_cur[i].print(); + seg_des[i].print(); } - } -#endif - -#if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - vpPlot *graph = NULL; - if (opt_curves) - { - //Create a window (700 by 700) at position (100, 200) with two graphics - graph = new vpPlot(2, 500, 500, 700, 10, "Curves..."); - - //The first graphic contains 3 curve and the second graphic contains 3 curves - graph->initGraph(0,6); - graph->initGraph(1,8); -// graph->setTitle(0, "Velocities"); -// graph->setTitle(1, "Error s-s*"); - } -#endif - //param robot - vpRobotCamera robot ; - float sampling_time = 0.010f ; // Sampling period in seconds - robot.setSamplingTime(sampling_time) ; - robot.setPosition(cMo) ; - int iter=0; - - do{ - double t = vpTime::measureTimeMs(); - robot.getPosition(cMo); - for (int i=0; i <4; i++) - Pc[i].project(cMo); + //define visual servoing task + vpServo task; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(1) ; for (int i=0; i <2; i++) - vpFeatureBuilder::create(seg_cur[i], Pc[i*2], Pc[i*2+1]); + task.addFeature(seg_cur[i], seg_des[i]); #if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) - if (opt_display) { + if (!opt_no_display) { vpDisplay::display(I); for (int i=0; i <2; i++) { seg_cur[i].display(cam, I, vpColor::red); @@ -223,34 +182,82 @@ int main(int argc, const char **argv) } #endif - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; - #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) + vpPlot *graph = NULL; if (opt_curves) { - graph->plot(0, iter, v); // plot velocities applied to the robot - graph->plot(1, iter, task.getError()); // plot error vector + //Create a window (700 by 700) at position (100, 200) with two graphics + graph = new vpPlot(2, 500, 500, 700, 10, "Curves..."); + + //The first graphic contains 3 curve and the second graphic contains 3 curves + graph->initGraph(0,6); + graph->initGraph(1,8); + // graph->setTitle(0, "Velocities"); + // graph->setTitle(1, "Error s-s*"); } #endif - vpTime::wait(t, sampling_time * 1000); // Wait 10 ms - iter ++; - - } while(( task.getError() ).sumSquare() > 0.0005); - - // A call to kill() is requested here to destroy properly the current - // and desired feature lists. - task.kill(); + //param robot + vpRobotCamera robot ; + float sampling_time = 0.010f ; // Sampling period in seconds + robot.setSamplingTime(sampling_time) ; + robot.setPosition(cMo) ; + int iter=0; + + do{ + double t = vpTime::measureTimeMs(); + robot.getPosition(cMo); + for (int i=0; i <4; i++) + Pc[i].project(cMo); + + for (int i=0; i <2; i++) + vpFeatureBuilder::create(seg_cur[i], Pc[i*2], Pc[i*2+1]); + +#if (defined (VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) + if (!opt_no_display) { + vpDisplay::display(I); + for (int i=0; i <2; i++) { + seg_cur[i].display(cam, I, vpColor::red); + seg_des[i].display(cam, I, vpColor::green); + vpDisplay::flush(I); + } + } +#endif + + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v) ; #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - if (graph != NULL) - delete graph; + if (opt_curves) + { + graph->plot(0, iter, v); // plot velocities applied to the robot + graph->plot(1, iter, task.getError()); // plot error vector + } #endif + + vpTime::wait(t, sampling_time * 1000); // Wait 10 ms + iter ++; + + } while(( task.getError() ).sumSquare() > 0.0005); + + // A call to kill() is requested here to destroy properly the current + // and desired feature lists. + task.kill(); + #if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) - if (opt_display && display != NULL) - delete display; + if (graph != NULL) + delete graph; +#endif +#if (defined (VISP_HAVE_X11) || defined (VISP_HAVE_GDI)) + if (!opt_no_display && display != NULL) + delete display; #endif - std::cout << "final error=" << ( task.getError() ).sumSquare() << std::endl; + std::cout << "final error=" << ( task.getError() ).sumSquare() << std::endl; + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } } diff --git a/tutorial/CMakeLists.txt b/tutorial/CMakeLists.txt index d713a785ab369f2d697f51f23ebadfa8746d5721..cf3e0ce07e29bf945fb0d4366c6e086f69854cb9 100644 --- a/tutorial/CMakeLists.txt +++ b/tutorial/CMakeLists.txt @@ -2,15 +2,43 @@ project(ViSP-tutorial) cmake_minimum_required(VERSION 2.6) -subdirs(computer-vision) -subdirs(grabber) -subdirs(image) -subdirs(robot/pioneer) -subdirs(simulator/image) -subdirs(tracking/blob) -subdirs(tracking/keypoint) -subdirs(tracking/model-based/edges) -subdirs(tracking/model-based/hybrid) -subdirs(tracking/model-based/keypoint) -subdirs(tracking/moving-edges) -subdirs(visual-servo/ibvs) +if(MSVC) + if(NOT VISP_SHARED) + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif() + if(${flag_var} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:msvcrtd.lib") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt.lib") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libcmtd.lib") + endif() +endif() + +add_subdirectory(computer-vision) +add_subdirectory(grabber) +add_subdirectory(detection/barcode) +add_subdirectory(detection/face) +add_subdirectory(detection/matching) +add_subdirectory(detection/object) +add_subdirectory(image) +add_subdirectory(robot/pioneer) +add_subdirectory(simulator/image) +add_subdirectory(trace) +#add_subdirectory(tracking/barcode) +add_subdirectory(tracking/blob) +add_subdirectory(tracking/keypoint) +add_subdirectory(tracking/model-based/edges) +add_subdirectory(tracking/model-based/hybrid) +add_subdirectory(tracking/model-based/keypoint) +add_subdirectory(tracking/template-tracker) +add_subdirectory(tracking/moving-edges) +add_subdirectory(visual-servo/ibvs) diff --git a/tutorial/computer-vision/CMakeLists.txt b/tutorial/computer-vision/CMakeLists.txt index 83f990ddf34d465fb4b79f85260d25ba790a6246..a8aed1f17a1ef4dcb37eb8be9d85d403312b707b 100644 --- a/tutorial/computer-vision/CMakeLists.txt +++ b/tutorial/computer-vision/CMakeLists.txt @@ -3,20 +3,22 @@ project(tutorial-computer-vision) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-pose-from-points-image tutorial-pose-from-points-image.cpp) -add_executable(tutorial-pose-from-points-tracking tutorial-pose-from-points-tracking.cpp) +set(tutorial_cpp + tutorial-homography-from-points.cpp + tutorial-pose-from-points-image.cpp + tutorial-pose-from-points-tracking.cpp) -# copy the data -get_target_property(target_location tutorial-pose-from-points-image LOCATION) -get_filename_component(target_location "${target_location}" PATH) -set(data "${CMAKE_CURRENT_SOURCE_DIR}/square.pgm" ) -add_custom_command( - TARGET tutorial-pose-from-points-image - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" -) +set(tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/square.pgm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-pose-from-points-image.cpp ${data}) +endforeach() diff --git a/tutorial/computer-vision/tutorial-homography-from-points.cpp b/tutorial/computer-vision/tutorial-homography-from-points.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e4aae515bcd047b8119ff921f2a75d4571139d30 --- /dev/null +++ b/tutorial/computer-vision/tutorial-homography-from-points.cpp @@ -0,0 +1,82 @@ +//! \example tutorial-homography-from-points.cpp + +//! [Include] +#include <visp/vpHomography.h> +//! [Include] +#include <visp/vpMeterPixelConversion.h> + +int main() +{ + //! [Set 3D points] + double L = 0.1; + std::vector<vpPoint> oP(4); + oP[0].setWorldCoordinates( -L,-L, 0); + oP[1].setWorldCoordinates(2*L,-L, 0); + oP[2].setWorldCoordinates( L, 3*L, 0); + oP[3].setWorldCoordinates( -L, 4*L, 0); + //! [Set 3D points] + + //! [Simulation] + vpHomogeneousMatrix bMo(0.1, 0, 1, 0, vpMath::rad(15), 0); + vpHomogeneousMatrix aMb(0.2, -0.1, 0.1, vpMath::rad(-3), vpMath::rad(20), vpMath::rad(5)); + vpHomogeneousMatrix aMo = aMb*bMo; + //! [Simulation] + + //! [Image plane coordinates] + std::vector<vpPoint> aP(4), bP(4); + std::vector<double> xa(4), ya(4), xb(4), yb(4); + for(unsigned int i=0 ; i < 4; i++) + { + oP[i].project(aMo); + xa[i] = oP[i].get_x(); + ya[i] = oP[i].get_y(); + oP[i].project(bMo); + xb[i] = oP[i].get_x(); + yb[i] = oP[i].get_y(); + } + //! [Image plane coordinates] + + //! [Compute homography] + vpHomography aHb ; + vpHomography::DLT(xb, yb, xa, ya, aHb, true); + std::cout << "Estimated homography using DLT:\n" << aHb/aHb[2][2] << std::endl; + + vpHomography::HLM(xb, yb, xa, ya, true, aHb); + std::cout << "Estimated homography using HLM:\n" << aHb/aHb[2][2] << std::endl; + //! [Compute homography] + + //! [Get transformation] + vpRotationMatrix aRb; + vpTranslationVector atb; + vpColVector n; + aHb.computeDisplacement(aRb, atb, n); + //! [Get transformation] + + //! [Print results] + std::cout << "\nEstimated displacement:" << std::endl; + std::cout << " atb: " << atb.t() << std::endl; + vpThetaUVector atub; + atub.buildFrom(aRb); + std::cout << " athetaub: "; + for(unsigned int i=0; i<3; i++) + std::cout << vpMath::deg(atub[i]) << " "; + std::cout << std::endl; + std::cout << " n: " << n.t() << std::endl; + //! [Print results] + + //! [Get pixel coordinates] + vpImagePoint iPa, iPb; + vpCameraParameters cam; + vpMeterPixelConversion::convertPoint(cam, xb[3], yb[3], iPb); + vpMeterPixelConversion::convertPoint(cam, xa[3], ya[3], iPa); + + std::cout << "Ground truth: Point 3 in pixels in frame b: " << iPb << std::endl; + std::cout << "Ground truth: Point 3 in pixels in frame a: " << iPa << std::endl; + //! [Get pixel coordinates] + + //! [Project] + // Project the position in pixel of point 3 from the homography + std::cout << "Estimation from homography: Point 3 in pixels in frame a: " + << vpHomography::project(cam, aHb, iPb) << std::endl; + //! [Project] +} diff --git a/tutorial/computer-vision/tutorial-pose-from-points-image.cpp b/tutorial/computer-vision/tutorial-pose-from-points-image.cpp index b310f50062f3363b01a08700562840101f323ec1..a4140871059de0de316c6a00e1b36cb6285bad1a 100644 --- a/tutorial/computer-vision/tutorial-pose-from-points-image.cpp +++ b/tutorial/computer-vision/tutorial-pose-from-points-image.cpp @@ -1,11 +1,15 @@ /*! \example tutorial-pose-from-points-image.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpDot2.h> #include <visp/vpImageIo.h> #include <visp/vpPixelMeterConversion.h> #include <visp/vpPose.h> +void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, + const vpCameraParameters &cam, bool init, vpHomogeneousMatrix &cMo); + void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, const vpCameraParameters &cam, bool init, vpHomogeneousMatrix &cMo) { @@ -17,50 +21,68 @@ void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, pose.addPoint(point[i]); } - if (init == true) pose.computePose(vpPose::DEMENTHON_VIRTUAL_VS, cMo); - else pose.computePose(vpPose::VIRTUAL_VS, cMo) ; + if (init == true) { + vpHomogeneousMatrix cMo_dem; + vpHomogeneousMatrix cMo_lag; + pose.computePose(vpPose::DEMENTHON, cMo_dem); + pose.computePose(vpPose::LAGRANGE, cMo_lag); + double residual_dem = pose.computeResidual(cMo_dem); + double residual_lag = pose.computeResidual(cMo_lag); + if (residual_dem < residual_lag) + cMo = cMo_dem; + else + cMo = cMo_lag; + } + pose.computePose(vpPose::VIRTUAL_VS, cMo); } int main() { - vpImage<unsigned char> I; - vpImageIo::read(I, "square.pgm"); + try { + vpImage<unsigned char> I; + vpImageIo::read(I, "square.pgm"); #if defined(VISP_HAVE_X11) - vpDisplayX d(I); + vpDisplayX d(I); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I); + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); #endif - vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); - std::vector<vpDot2> dot(4); - dot[0].initTracking(I, vpImagePoint(193, 157)); - dot[1].initTracking(I, vpImagePoint(203, 366)); - dot[2].initTracking(I, vpImagePoint(313, 402)); - dot[3].initTracking(I, vpImagePoint(304, 133)); - std::vector<vpPoint> point(4); - point[0].setWorldCoordinates(-0.06, -0.06, 0); - point[1].setWorldCoordinates( 0.06, -0.06, 0); - point[2].setWorldCoordinates( 0.06, 0.06, 0); - point[3].setWorldCoordinates(-0.06, 0.06, 0); - vpHomogeneousMatrix cMo; - bool init = true; + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + std::vector<vpDot2> dot(4); + dot[0].initTracking(I, vpImagePoint(193, 157)); + dot[1].initTracking(I, vpImagePoint(203, 366)); + dot[2].initTracking(I, vpImagePoint(313, 402)); + dot[3].initTracking(I, vpImagePoint(304, 133)); + std::vector<vpPoint> point(4); + point[0].setWorldCoordinates(-0.06, -0.06, 0); + point[1].setWorldCoordinates( 0.06, -0.06, 0); + point[2].setWorldCoordinates( 0.06, 0.06, 0); + point[3].setWorldCoordinates(-0.06, 0.06, 0); + vpHomogeneousMatrix cMo; + bool init = true; - while(1){ - vpImageIo::read(I, "square.pgm"); - vpDisplay::display(I); - for (unsigned int i=0; i < dot.size(); i ++) { - dot[i].setGraphics(true); - dot[i].track(I); - } - computePose(point, dot, cam, init, cMo); - vpDisplay::displayFrame(I, cMo, cam, 0.05, vpColor::none); - vpDisplay::flush(I); + while(1){ + vpImageIo::read(I, "square.pgm"); + vpDisplay::display(I); + for (unsigned int i=0; i < dot.size(); i ++) { + dot[i].setGraphics(true); + dot[i].track(I); + } + computePose(point, dot, cam, init, cMo); + vpDisplay::displayFrame(I, cMo, cam, 0.05, vpColor::none); + vpDisplay::flush(I); - if (init) init = false; // turn off pose initialisation + if (init) init = false; // turn off pose initialisation - if (vpDisplay::getClick(I, false)) break; + if (vpDisplay::getClick(I, false)) break; - vpTime::wait(40); + vpTime::wait(40); + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } } diff --git a/tutorial/computer-vision/tutorial-pose-from-points-tracking.cpp b/tutorial/computer-vision/tutorial-pose-from-points-tracking.cpp index aa000b87aece23a002be1e3b1499a923da0eb3e5..a9af0cf034e084704aa183445c70379032d71de0 100644 --- a/tutorial/computer-vision/tutorial-pose-from-points-tracking.cpp +++ b/tutorial/computer-vision/tutorial-pose-from-points-tracking.cpp @@ -2,11 +2,18 @@ #include <visp/vp1394CMUGrabber.h> #include <visp/vp1394TwoGrabber.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpDot2.h> #include <visp/vpPixelMeterConversion.h> #include <visp/vpPose.h> +void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, + const vpCameraParameters &cam, bool init, vpHomogeneousMatrix &cMo); +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) +void track(vpImage<unsigned char> &I, std::vector<vpDot2> &dot, bool init); +#endif + void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, const vpCameraParameters &cam, bool init, vpHomogeneousMatrix &cMo) { @@ -18,11 +25,22 @@ void computePose(std::vector<vpPoint> &point, const std::vector<vpDot2> &dot, pose.addPoint(point[i]); } - if (init == true) pose.computePose(vpPose::DEMENTHON_VIRTUAL_VS, cMo); - else pose.computePose(vpPose::VIRTUAL_VS, cMo) ; + if (init == true) { + vpHomogeneousMatrix cMo_dem; + vpHomogeneousMatrix cMo_lag; + pose.computePose(vpPose::DEMENTHON, cMo_dem); + pose.computePose(vpPose::LAGRANGE, cMo_lag); + double residual_dem = pose.computeResidual(cMo_dem); + double residual_lag = pose.computeResidual(cMo_lag); + if (residual_dem < residual_lag) + cMo = cMo_dem; + else + cMo = cMo_lag; + } + pose.computePose(vpPose::VIRTUAL_VS, cMo); } -#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) void track(vpImage<unsigned char> &I, std::vector<vpDot2> &dot, bool init) { if (init) { @@ -44,49 +62,71 @@ void track(vpImage<unsigned char> &I, std::vector<vpDot2> &dot, bool init) int main() { -#if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) && (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394)) - vpImage<unsigned char> I; +#if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) && (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) || (VISP_HAVE_OPENCV_VERSION >= 0x020100)) + try { vpImage<unsigned char> I; #if defined(VISP_HAVE_DC1394_2) - vp1394TwoGrabber g; + vp1394TwoGrabber g; + g.open(I); #elif defined(VISP_HAVE_CMU1394) - vp1394CMUGrabber g; + vp1394CMUGrabber g; + g.open(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture g(0); // open the default camera + if(!g.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); #endif - g.open(I); - // Parameters of our camera - vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + // Parameters of our camera + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); - // The pose container - vpHomogeneousMatrix cMo; + // The pose container + vpHomogeneousMatrix cMo; - std::vector<vpDot2> dot(4); - std::vector<vpPoint> point(4); - double L = 0.06; - point[0].setWorldCoordinates(-L, -L, 0); - point[1].setWorldCoordinates( L, -L, 0); - point[2].setWorldCoordinates( L, L, 0); - point[3].setWorldCoordinates(-L, L, 0); + std::vector<vpDot2> dot(4); + std::vector<vpPoint> point(4); + double L = 0.06; + point[0].setWorldCoordinates(-L, -L, 0); + point[1].setWorldCoordinates( L, -L, 0); + point[2].setWorldCoordinates( L, L, 0); + point[3].setWorldCoordinates(-L, L, 0); - bool init = true; + bool init = true; #if defined(VISP_HAVE_X11) - vpDisplayX d(I); + vpDisplayX d(I); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I); + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); #endif - while(1){ - // Image Acquisition - g.acquire(I); - vpDisplay::display(I); - track(I, dot, init); - computePose(point, dot, cam, init, cMo); - vpDisplay::displayFrame(I, cMo, cam, 0.05, vpColor::none, 3); - vpDisplay::flush(I); - if (init) init = false; // turn off the initialisation specific stuff + while(1){ + // Image Acquisition +#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + g >> frame; + vpImageConvert::convert(frame, I); +#endif + + vpDisplay::display(I); + track(I, dot, init); + computePose(point, dot, cam, init, cMo); + vpDisplay::displayFrame(I, cMo, cam, 0.05, vpColor::none, 3); + vpDisplay::flush(I); + if (init) init = false; // turn off the initialisation specific stuff - if (vpDisplay::getClick(I, false)) + if (vpDisplay::getClick(I, false)) break; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/detection/barcode/CMakeLists.txt b/tutorial/detection/barcode/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..add4cc6f4621b8e8be592116d819022e856c6732 --- /dev/null +++ b/tutorial/detection/barcode/CMakeLists.txt @@ -0,0 +1,24 @@ +project(tutorial-barcode) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +set(tutorial_cpp + tutorial-barcode-detector.cpp + tutorial-barcode-detector-live.cpp) + +set(tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/bar-code.pgm") + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-barcode-detector.cpp ${data}) +endforeach() + diff --git a/tutorial/detection/barcode/bar-code.pgm b/tutorial/detection/barcode/bar-code.pgm new file mode 100644 index 0000000000000000000000000000000000000000..f8623e1f61a93836dc953978f100b7b2fc31c1bc --- /dev/null +++ b/tutorial/detection/barcode/bar-code.pgm @@ -0,0 +1,215 @@ +P5 +640 480 +255 +wwttsutw{vuuvxwyy{zxyz{|||vxwywy{yyzxxyywyy|~|||||{}{}||{}{|{|xz{{yy||zy{{{xx{}zyzzzz{{{{{z{xxzywywzzxx{}zyzxv~z|z~{||{|{{xzyyvxwvvvvywvyywwxwzvwywxwxxvzvzuwwvvv{zzw|yyzzzy{zz{zzvxxxxyzzwzzywwzvwxvwuxwzxuyryuzwzzz}zzzywwyxzvywvuvwtvxtxvyyyxx|vyy|yywyyx{y}yz{y|{{yy|ywy{yyxwwwyyywwww{yzxyyyyywvuzv{{zzzw|wyyyy{v{ywx{wzw{v{vy{zv{yyywyzy|v{z}x|{|yzz{x|{vxywwyyywyywvvvxvxwwwvvwwwyzwyywwwvwyvvvyuzw|xxxyzxxxyyyywvwyyxxxyyxxzzwwyyyzuyyvvvxxywxzyyyy{zwzu{vuvzuuvvvvwvxwvzuxvvvwvwxwvwyvwxywwyyyyxxxwxyvxxyyyxwywwwvwvwvxwvwuwxwxwwwwuvzztwyzvvzxyyxzwwwv{zzv{zyzzz{{z{z|{}{{z{|}|{{{~y|}{{~~~€€ƒ‚€„„„…†††‡‡……„ˆ…Ї‹‹‹Œ‹ŽŽŽŽŒŒŠŒŠ‹Œ‰‰‰ˆ‰Š‰…ˆ‡‡‡ˆŠˆ‹vvuuvtuyzvvzyyxxyz|xzwzyxwwwywxy{yzxyy{yy|z{~{|~{{|{|||{|{{y||y|}|{{}|z{z{zz{~y|}~{~~~}~}yyz{xxyxzzyy~z~}||~y}~x}{{}w{yxywxwwvxzv{xvy{{zzwzyxzwxwwx{zw{vuvvvv{wwwvzvywzyzz{||{{{{yzyxzyyyz{zzwvzvyvwww{xzyzyztzzzzz{zuyyxxxxyxwxvtuuwwxuzzzzzvwyyyyw|{{{|x}z{zxzz{zz{y{z{{{yywwvyv{v|w{z{wyyyvzxw{{{{z|{{w{w{y{{{v{zzt{w{z{|{{{{zz{{{{wyyv|{|x~}|y||{z{{{zwxyxwywzxwyyywwvvvywwwwwwxxwzvywwwxyzw|||w{x|zwwyvyyyxyyyyywywywwzwswyyywuwvywzzwwyzyuyvywvyywzx{|{zzyyyzzzyxwwvzz{{yu{{xw{yvvvvw|xx{wyxyyzyyww{vux{x|zxzyyxwwxwxwxyxxywywyvwxxwzxvwyvuxzz{z{uzyxuzzz{|z{yyzzw{x}}||{{}z}~|}{~~|}}{{}~€}„€~}}ƒ€ƒ………ƒ†‡‡‡‰ˆˆŒ‹‹‹‹‹‹ŠŒŒŒŒ‹Œˆ‰‰ˆ‰‰‡‡†ˆˆ‰Œvvrusuuxwvvzz{zz|{{zvxwxs€yyxzyxyy|x~z~|||{||{|}~~~{|{|||{}||{{{~|}|yyz|}{{yy}€|z~{}~}}y|{{yyzz{zyzy~}}{€x~|yy}}|}|{{yvvwwtvvz{|www|{xwvzzzzwwwxv{yxvvvu{xuzzxvvvwwwvx~|y{{zyzzyxyy{y{{zuvvwzzzyxwyvwwzzyyzuwwxwzuzvyyvxzyyy|ztuxwwxwvwvxzxwyy|y{x{u~|zxzz{{{{{~w||{{vxywyvvwuvvvvywvyzxwx{vyyzy{xyyzy{wvwwyvwwwz{yvwwvv|{yw{vwv{zzz{zvu|xzxxzxxz{|zz{yxvwwvvyzzwyvvxuyxzvwtwwywwxwy|wvyzxvywwvv{y|yxxwyyzyzxxwyvyxwvwvwwzyuzu~ywvvyyzxyyywvvuywwuvxvvwxwwxvwvtt{qxuzwwv{{vxzu{zvx{uwvvwwz{xvv{yw{vywzz||w{{||vzy|wzzw|ww{xxwxyxwwvvxw|{txsyxyyysy{ywwwywvzzxx{zyxxyzyzx||{z}{~~{x~}|~|||||{€€~}€ƒ€|…………‡…ˆ‡†ˆ†‡ŠŒŠŒ†ŒŒ‰‹ŒŒŽ‰‘މŒŠŽŽŽŒ‹ˆŠŠŠ‡‹vrsusvuuvvvuvy{zwz|zwwwxq~xywwwxyw~~€y~|||||~{|}~€~{||~y{|}|||{{z||||{{z{y|y{{{|~{~~~~|}}}{{yz{z{~{yz}z~{}yy{}}|{y{{wyxwwvxvuywxwxx{{wv{{{yxvuvw|zwwvzxzxw{zwz{zwywwx~zyzzz{{zyzxx{zzzwvvvvzwzxywyyvvzwvyzxwwyvyzzwwvxyyxyx|vtvxwzxxwxvxwvzzx{vzv|v}zzzzz{yxy{{wy|}zvywwwvuwuxv{uywwvzw}{|zzzwy{z{y{|{vzyywwvww{zywww{w|yxx{uwv{y{zzwvu{xxwwy{zz{|{{zwu{uwvvvwzwvxvyzywztxvwvwwywwv|vyyzxvvvv|y{w{wwyyxyyyxxwywwvwwywywwwyzzo~yyyywwzyxyyyxzzyvyzxvuvuvzywwywuvzyyyzwwv{zvvzzzyv{{uwxwvwx|ywv{{yyvuxwzz|}{y||wyy{xxz||vwzwyzzxwwwxvxx|wvssrv{yxuyzwyyyyvw{zxw||{zyyzzzz{|zz|z|}}w|}}~|~||{|~}}~€€€€€~€€€ƒ…………„†…†„„†‹‹‡‡‡ŠŒŒŒŒŒŒŽŒŒ‹Š‹Œ‹‹‹‹‹ŒŒŽŒŠ‰ˆˆ‡ˆvwttuuuuuuvyvxxxwyyzyzxy|}|zyxyzxw~zxw|||x|}}||~|||{~€}~{zy{{{~{{z|||{}|zz|||{||€}}€}€~~}|}~z{{}{}{{z{||zzy{x}{yxyw{{wyzwvvvvwxwuwwuz}vvwwvvywuzz|zyvvvvzwxzzvxzxvxvwv}ywyyzxxzyzzzyz{z{y{uuvwwxvwwwvwwywyyyyyywvwxzvvvxxywwutsusuyzxrxwwwwwwvwy{w|xvw{{xvvvyz{yyxyzywwvvvvvvzxywywyxzzzwvztz{{{xyzzz{{xwwuwvvvwvuuvuw|ywxvv{vvwvvxz{xuwvwwwwzz{zvwxvvvwuqv{vvvzvvuvwxvxvvvwuvvwwwvstvwwyyvysvwyyvwxwyzwvxvwwxywvwvwxwyywuyyws{xyxxwwwwxyyywwwwuyzzyyyvxxvwxuyvvyyyyywvvvwwyvwy{zzvuvvwwwxy{wwz|{xyzzyxx{{zy{~x{y|yxy|xux|zyy{wyxwwzvxuus~uuuxyzyyzwyyyyvywwxyzz{uyyzvy{z{{|z|zxvvyz}z||}|~~}€€€~~€€ƒƒ„‚‚ƒ„‚„ˆ‰…ˆ†ˆ‡‡Ž‹Œ‹‹‹ŒŒ‹‰ˆ‰Œ‹ŒŒ‰‰ˆ‰‹‹‰uvttuuuvuuuyvuwy|xwwzzu{y{~zzxxxwz~~x|}}{~}}~}~|x|{~~~|zzx{{~~~zzy{}|{|{z{{|z{{€}{}{~€€{|{{}yzyxwxz{{|yy{y|yyxzxx{zyvwwxu|zwuyx||{yvvwvvy{wwwzzvxwwwwzxwxzzyxwuxy{v}yyvuzyxzxz{zzwv{zyzusrvywvwzxuwxwwwwutwyuvwvwzywwvvzvvtvvzzyyyvyyyvwvvwwv||}wwz{zyw|v{z|{{zzxyywvwvvx|zzzww{zwy{xwv|{{{zwwwwwww{wwvvuwtvvwvvvvv{{wwwx{w{vywvy{wvvwvvyz{zz{z{vvu{wuvwuwvuzwxwwwvwxvvuwyvwvvxywwwvvywyxsvywwvwyywwvywwuvxzvwwzyzwzyzwzwyy|yyxwwxvvyzwzwwwvvyyzzyvyyyyzyywywvvzyyyzxwwwwvuzyuzvvzu†z|xw{wwx||z{z}z{yzz{y{|yxz{|wy|xvwwwxw{x{wwwzvxxvtuwvwxu|zvzzywwxvy{{zzzz{tzz|zz}z{{|zz|zwvy{~x|}{z~~||~ƒ|}~€‚€€ƒ}€€„‚†……†…‡‡‡‹‹Œ‰Œ‹‰‹‰Š‰‹ˆ‰‰ŠˆˆŠ‰ˆˆuvuuvvwywuuyvzxzyxwwwyw~zyxzyyyzw||}|zxvz~|||{{|y|{}}}~|{xx|{}|yz{{yz|{}zyyy|~~~~|}}~z~}~zz{}~~yzyy{yxxy{z~w{yywx{{{zvwxxx{ywxyxvtuwxxwuwvvuwvxvwuuwwwuuyvvxswuywuu{yyzz{yzxzzyxywxyxvyvuuuvtuwwwruwuwywwttutxwvuttvyywxwvuwwxusuxxxwwxwywyvs|xvyyx{vvzzu{{{|{zvxwvuvvwvz{{{wuu{zxw{xvv{wzvwwvvwuwvvvwvvtwrwvvuvvvvv{zvyz{wuvwyvywxvuvuu{{{ywvsuvvvvvvwwryuszz|wyyrvxuvvvyvuvwwvvwywuvywwvwyywyvuwwvvwwvvuyzwwywyyzzzzvyyyy{kxywyxwvxyywwwyvxyvvuwwzywwwvxzwvwvy{vuy{wvwzuuzvwzywwwwzz{z{wxwz{{{zzyyyz{||zx{}xw|}||}}yxyzz{z{yyv{wwxxvvwyxyxxvuwyyswvwyx{y|yy{v{x{|z}}||}zzz{{zw|}y}€}~y~~~~~}€~~€€}~€‚……„ƒ…ˆ†‡‡ŠŠ‹‹‹‹Œ‹‹‰ŒŒŠ‹ŒŠ‰‰‹Š‰‹ŽˆŒtssuxvyzzuxvu{yyvxzxvvxz{xy{~|yxwy~}|zyxyy|z|{{|x|{}|}}~}|{}|{{{|{z{|}}z}€€z€~~}|||}|yzz€~z{}{z|~|wvwx}|}xx||{wx~zw{|x}z|v|{|y|zwxwvwvxxxwwwxx{wvvvvwwuwwwwvvwwvwwzwuwzwvwz{zzxwzywwwwywwywyyqvsuuwuuuwuvvuuxxzzzxwvvuwwywwvvwwwyzyvwwvvyvvyxxwruyv|{|uywwy{{{yvwwwwv||vvy|||w{{zyvw{w{zzz{w|vwwyyyvvuwuwuwsvvwuwuvvwwvv{z|yuuwwvvzzwuwwvtv{vvvvvvwvwuvuuzurx{zvy{uwvuwyyyvuv{uvvwwyvvwvvwvyy|xvuywvvwvvwyw{wywyyyyywzwzyzz|zyyxwyzwywwwwzwwvzxzyzz{z{wwxvwuwvyyvwvzwzxyxuwzy{{yw|zvxz{{{wwx{{{||{w{}||||{y||wz}||z|{{|yzyyxx{{xxyxzwxwwwwxwwvusxys{yyvx}{wxz{xz||{{||{xy{yyz||v{|{}}||||€ƒ{€}}~€€„~€€‚€‚ƒ‚ƒ€†‡‡‡……ˆˆ‹‹ŒŠŽ‹Š‹Žˆ‰ˆ‰Šˆ‰‰ˆ‡Š‹ˆˆyyuuuvxwvuw|||yxwwz{u{wzy}|}}~wywywx{}xzzy|zz|{~~y{}|}}~{{{}|}{zy|}{yz|~~z|€€€€z{|~z|{}{{zzxwy{}|yxxx|zxxz}||vw{yw|{vvwxxz|zxutwvuuwuwxwvwvvvvwvwuvvvwzwvvwwvvyvvvzsvxsuuvyywzyvvvttuuuxxvxvptsusrtstwtrtrrsruxxrwvvvvrvvwwuruvvwvwvvwvxswxxxvrsvuuwvvwwwwxxxwwv}v{vvvvuvvy{{{xz{zyvwzwy{z{ywvvuwxvvuuvvuuvwxuxvuuuuwvvvvwxz{zzvwyzyyvwytvvuzvwvvwvwwuruuuuvxz{{vvz{vvuvuyyyyvuuuvyvuwwvxvuuvwx|wvwyvuvwuwwvwwxwwwwyyyyzzxzwz{yyrywwyyywzzzuvsuvwvuuuuuyywyxvuyw{vvvuwwxyzzvzzuwx{xwvwxxzz|wzv||{z{{zz}{zz{{|{{}~{x||{y{~w{yzxz{{vyyxvuwvuwxwwvwwwywvvz{zyyvxvyz|{}|{z}xyww{|yxy{z|y~{||{}{|~~}||~}~~~€€€‚€†‡„„„†ˆŽ‰ˆˆ‰ŠŒŠŒ‡Š‡ˆˆˆ‡‡ˆ‰‰‹‹‹uuuuttzwvvwxyyvuuv{yx{xvyxxwx|xwyw|yywy|}||{z{{}~{|}||}|yz{|z{z{||{{}|||}}~}zz|~~}{z{}{{zyz~zyyxx{{~z{ww{|x|xwuv|vx{xwz|{xwwwwvwwwswvwwuuywwtvvvx{wvwvusswuuvwvwuuuzwvwuuwsuuvzzz{{wvvwuwxwyyzwvwuuvsusurwwurxruutvvrwvruwvwvwwvrrutvwuutusvswuyxvrvtvuvuwvvxxuxvvuvux{wxwrzwvvz|{uwz{vvx{yvuwwwvwuvwxtwwvtwtuuwvuuxwwuvyvuwv{ytvxwvv{yvtvuztvvwvvvvtquvwutuvzwvuvwzyvz{wuvwuywwyyvruvvvuwvvwwvyvwv|v|wwvwuyxywywvvvuwvyyzyzwwyyyzyyswwyzyyvuvsuuuuttvvyvywyzzyzwvwyxuwyyyvww{xvy{uwxzyvwxwxvv{vwwvwxzyx{{||zxv{~wxw|{|||}xy{yy{zwyy|wzurttxz|zxwwwuwzyxwuuv{wvuxvx{{r}zyyvwxww{xxzx{{xyy{{~}}yyy|~|{z~€~|{{€ƒ~€~€„€…………„…ˆˆˆˆ‰ˆ‹ŠŒŒˆ‹‰Š†‰ˆ‡‰‰‡…ˆŠtzxzxvzzz|xuwvwzwvwwxzyyywxyxxwyww{}x~€|x{z|~||||}{|{{{{y{|zy{|€~}}{{{{|}z~~z{~~~~{|||{yyzz{z}|xvyzywxww|wwwwvu|{{z{xw{|wwwxvwwwvtuwvuuwywuuvvuuvvvvuvuuuuuvvvwvvuuyuwvzwusvw{yyyzyyvxuxvywwyuvvtpqorppqtuvtuvsuqqusssuvysrtruwsuvvvupuqvpvrsqwvtruqvuvrvvvqxvwvvuvxwyz{wvvwwwvwuvuwwvv{{zwvuvwruuvuvvuuvwuuvvvvvuuuvwvxuvwwvvvwuvvvuuvuuuxvvuvwwwvvuuuwuuuu{ywyuqywvzxvurwvuvvnwwv{v{wvuuvvuvvvvs{vvyvustwxwwvvwyvxwwvyzwywwzwwxwytwwwvyzvvwxtzvuuwvuvvzxywuuwzuwzzwyy{yuw{zzxvuuwvwyyyxxz{zzwxx{vvy{||{{{ywxz|xwv{{}|}{y{{|z{zz{|xxz{y{x{z{yyxyv€yxwvutuxvvtyxxyz{zz{y{www}{wy{wz{xy}z}~{}}{|}{}}~€~z~€€€‚‚‚€€‚………„……‰ˆŠ‰ˆ‰‹Œ‹‹‹Šˆ‹‹ŠŒ‹utu{yxzzxz{vwuzzwvuysx{xywywyx|{yxyyy}{y|{||{{{{}}}{{y{~z||{{yz{|zz}{zz{{~|z~~~~{{~{zy~w~||z~zywyxwwwxzvxwwww|zzvyzz{zzy|zxwxwwttwrvwvyvvuzwvxxxvtsuyvzwvuvwvwvvuuvuux{{zxxvyyzyyyyvtvvvxwvywuup{orqvqtuyrwvuuvvvusuvvyvvtqsvuvwwvvvvpvpvqvuvspqvwvvwwvvvuwu{vvwxyxxzxyywy|wvuvvzuvyvyvwwtvuvuvuvvvtwvwyyswuvuuuzuvuwwyvxwzz{|{uzuvvutvuwwvvwvwwxwwvvuvuzyzvuvur{vvuxvuqwt{uvwxvwuuyzv{vwv{w{vurzvwwxuvwwxwvwwzyywwuwwywzvzzywwuz{zwzwz{zzzwuwyxxvzvwvzxz{zyyyvu{wyyyyzyzyuvzv|{zzwwz{{z{|{zzw{xvtzz}|z|{{|y{{zxy}{y}}||x~~~|€|~{zw~}|{z{xx{w{|xvzxvwxx{yxuttyxzwvu{|{{yxyy{z|ywy{x|~|z|}€€}|~~~}{|~€zz~}~}€€€€€‚~‚€€€„††ˆ‹‡ˆˆˆˆ‰Œ‹ŠŽ‰‹‰‰‰‰Š‹‹Š‹vx{zwx{xw|xwwuzzzvv{sxxxy}y}yx|z{xzz~|y{€z|~xx~|}~}|~y{{{{{|~~w~€}{|y}|||}zy{{z{x{yyxxwxwvwy{{{xyyyuwxz{{wwwvxz}wyxzxxxxz{yzwrvuuttxvvvvvwwxxvwvuvuvuvzzyyvwwutuwyvyuuwyzuwxwuyzyzwwwuryvtvrvqpourvqprutrvtruwsswvtwsrvwuvuxpvutuvuurqprrqrrtvrupvurrruvvpuwvuuuvxy{zxzzy{z{{wvvuuvww{vyyuuuvuuutuwwvuuuuuuuwuuvutuvvtuuvvuuxvuuuuzuvvuuvvyuuuvuuvvurrvvuwxuuxwvtvuuvvvtvsvu{svuuxvvsyywvwuvwwuuuryvwxv{vywwwxwwy{vvvvyzyyzwvwxyxxxwwwvxvvwwvsuvwuxvuxwyx{yyvyyzuuywyzyzwvyyxwvwwvxzuvxzyvw{yvzwywww{}{{||||{zxvy{v{|{z|{|{z{}|}|z||yw|z}yyyvxyyy{yxxzyyw{{uwtustuzwsyxuzzw{yx{z|xyyyxww{z{||}yy{||}}~~||€~}|~ƒ€€€„ƒ…ƒˆ‡‹‡‰Žˆ‹‡Œ‹ˆˆ‰ˆŒŠŠŠ‰‰ŠŒ‹yy{{||{wy{wwytywxuyxuxxxy}y{yw~}xxxwy|{y{|~}|{|~‚}|xwy{yy|v{z||{z~€}z{{}~{{wy{y{|ywyy|{yxv{xx{|{z{x|}|x}||{|{xvvvywvzwwwxww}xwvvrxwxsuwtuuvuwvwzwxwvvvvuwzwvywvuuuuyvuwtuvvxvuwwwyyzwzyxutvyywswuuqwqurrruturtrvtruvststrwqvxtrprsrxvrrrqurvqsqrvrtvtussquuwtvuwuwyzu{{{{zy{{{z{vvuvrvwwvvxysuuuttvvtwvwvvqzuzywuuuvu{wxwvuuuuvwuuqyy{twtutut{rwvwwzxvvvvvtvvwvvwwvuuwwvvwvvuvv{zyvvtrrrwuuvvvuvv{wwvvwyxuuuxwwyx|zy{vwyvvz{yzzwvvwxxzx|wzyvvwvyyyxwwwuvvyyywvwxwuzvvzyzyuvuvywzvyxz{yyuwxxzwvyz|zy{wxv{{www{|{~{}|xxy{{{|yz~yxy~||}}|{yx|x~yxwwzwwz||{wvyyyxzzxuuwyxzvtwwtzwvwywzy|}{xwvxy|{{z|}|}…~|}zz|}{}~€~~€~~~~„|‚€ƒ‚€‚„ƒ„ŒˆŒ‹‰‹‹‡ŒŒŒ‰‰Œ‰‰‰‰ˆŠ‡‰zzy{{{ywxyxxwuxwwuvwszxx{~x|yx|{w|{|z{y{{}~~{v|~~~|~{zyyyyxzz{z~~|y{x~~€}{w{zu{xy}yx{{yxy~wzy{yy{{wv|zy}}|{{wxvuz|{|wxwzxwvwwwwrwwvuuvuvuwwvxwwwxwvuuvvrywvxuuutuwvvtvuxttvxwuwvyvzz{srtuswyvssussqqurqtuuusrsuursvqvwrpwqvxuquxuqtvqqppvvqrruvurrpqqsrsrqvvtrwwvvvvvwwwvvwvvvvuuvuvuvvvvvzvuruturruvwutrpzvuvuvsuvvuvvwvuuvtqqrurrrutsuuuuwyruuuvvvvvvvvuvuvwwwuuuuuvuwvwvwwvuvvwuqqwvwuquwuvvvwyvuvxxxwwwxwwywzzw}uzyvvyyyyyvywxxwzxwyzyuvwuzwwvvvvtvvvwvwvyxyyztyy{yywwvvwxwvyvvvwzwzvvwvvzzzy{xwvw||w}|||||{|}||{|{|{z{z|z|y|}}yxy|w}{zzyzyyyyxzyyxyyuxywxxzzzxzuuvvwzwwwwy{|{wwwwvv{{y{}~||zw|~z|~||~€~~{~€}€€ƒ…€„ƒ‚ƒ‚„‹‡Œ‰‹ˆŒŒŽ‹ˆˆŠŠŽ‡ŠˆŠ‰‹zzyxywwyzyzwwxyvwwyxstvw{~yyy{~zyz{{}yx{{|}|||~~~|{y{|zy{{{{}xy|z|yyy|z~|z~{x~{z{wxxzz{yywzwxy{zxyxwuv|yz{|{ywuvyw|||{wvzz{wxwvvwsuuusuuvvvuvuzwxvvuuvvvzxxvvutvvruvuutvtvvvwwywyxzvzx{twvwtvtrssswsrrurqrrrurrquvsqvpvzsowqwwvuwvwrxtsqppvrqqquqvstvrrrrqyvuuvuvvzxvxzwvvwvvvvuzqvvuuuuwuuuztusvttpqtvuuuvwzu‚qvuwtvtwvzvuuqqvvvuutwquuutvwvvvuvvuv{uyuwvvuuuvuuuvvvuyqvwvuxv{vvuwvrqquzvvvvuvvvvuuwwyw{ywwwuwwywwvwvv{yvvvyz{vw{vwww{wywyyvwzzyw}{zvxsvuuwvzvvxxwyuwwwzzvwuwyvvvzwvvuxwyuuvwvyzz{xvy{w|{z|{|{{|{{zy|{{{{|{|||zw{}|{{vzw}}|{||~z~~yyw|}xxyu|yxz{yyyw{wuuuxxzvwwyw{{wuwy|||{|{|}|z~~€{~}t~}~|{}{|€}ƒƒ‚€‚ƒ„‚ƒ…‡ˆ‡‰‰ˆ†††‡ˆ‰‰‰‰Š‰‡ˆˆˆˆ†zzxyyxwywz||xvuuwxwzvvwxz|xy{~|ywyz~|{{|x|}}zx|{{|{|{{y}||{|{xy|||{{y}~{y}{{|z|xy{x}{zwxy‚xxxxy{yywwwywwwywxwvyx{||zwxyzzvwxwuvvuuutuuvuvvussuwwvvuvxutttuuvryuuruuusuuusuuuuuuuuvsusrrurtxusuuuutrtprtsprrrrrqsssupuvspvqqtssrrprqqrsopprrrrtuuqutrquupuuursvvwywuwwvuuxwuwwuvruvuwusturqwquputtpsuuuuvuuttuvuuutuuuvvvuurquuuvtuwtvvuvvwvuuuvvuwvvvxvvvwuwvyuuuvvvyvwwvrwttwvutuuussrruuvyvuuxtvvvuwwyvvwuvxvwwzv{{{vwuttuzwy|ywwyzvwwuz{zzyxxvusuvussuvwyvuyuwzzwzuyzyyuxyzvwuuvuuuv{vuvwxzzwvxvz{xwxyz||yw{|{{z~~|{{z{{||}{{|z||yzyy}|{yyy{{{{yyxz||{yw{zy~yyxxx{wwuyvzvww{zwzxvwwzzxz|~|{|y{|~}|{|{~~|€|||~~|~~~€€€…ƒ„ƒŠ‡‡‡†††‡‡ˆ…†‡‰ˆˆ†‰‡ˆ‡Ž{{zxzxxywzzyxvvwywwyvwxyywyx|{|yxxy~|{x|xy|{x{|{{{|{{xzz|{{}yx{yy{|zwzz{{~}||}{}xzxxz{xwwwwwwvvzz|xywwxwwxy|}|wyx||}zyvvvzwxwwvwuvxusuuuuvvuuqsvwuwuuuuutuwwuusruvuuuyxyxtuuquywuxwvswrvuwuwyywvvrttrusrqqppqrruqstvqsqrpvqqpprurtrrqqpprrqrputrssvptrvvqvwvqqsvvytvuvvvvvuwvvuuuuuwwuqrqqpvtvsusuqvsuuwuvuvtzywwvuwwuqutvtutpsutttvuywvwwuvtuuwutuzxvvwuvuvvwuwuuvzxyvyvvtvvwtwutvvtuvws~uvtututvvuuwwyyyyyvwuwwuuvuvvwwwtuzx||~w{yywxvvvvvyvxxvyyswvuuvs{wyuzzyxwwzzyzzxvyyxsvuuwvzwyxvwwvzz{vwzvx{{{z{{{zzz{||y{{w{||}{y~x{y{}z~{||{yy{|~~zyx{yzz{}~{yvzzxwyxxy{zzvwvuvwx{{xz|zzxvwx{{{~z{||y{~|{{~~€}|||~{€~~~€€€€€€€ƒ„ˆŠ‹‡†‡‡†ˆ‡Š‰‰‰ˆ‡‡Šˆ‡……|yzzxzxzwzw~yxw~xzwyuxwxx}xy|||~v~|}{{{||~||{z||zzz{|{x{|zz|zy{{yz{zz{|~{{}zyzz{wwxwywxv€xyywwwvyyxwwwwvxxyyywyzz{{z|ywvvwvvxwvvvvvuurvvuvuvvutuwvuuvuwvzvwvsuvuvutuvwvvxuusrvywvuurrrtssvwvwwwvurtqvuurrpwqqqpqpprrsrupqqqqrqrpqtqqrrwpvurqrqprqqutvwwwvvsrqpqrqruuvurqruvvvvwtuuuwvrppqqptwvquvspvvttvvuvvtvmuqrtuuttssttuuuttruvwxwuuyuwuuvvwwvzywvwvzzwwwwvuvvwvvvwwwuuuvvvuvvwvtv{wqtvuuu{vvvwvwwwwvwvvwwwwyvwtyvyvvwtywvxz{zxyywxwvuuvwwywwyxytvvwvvsvwyuvvvuwywvuvuvvzyvtwuuwywvvvtxu{zyvwx|ux{{|zzwxz{{zz|{y|w{{|{z{}w|zx|x}|~|z„yzz{zzz||xzwzzy}{|wwz{yxyz~{z}zywvxyw{|{zzy{{{xxwwxzz|{||}wxxx|{|~}€z€|~||||€€~€~…~€~Š‚„‡ˆ‰ˆˆ…ˆ‡†ˆ…‡‡ˆ‰‡ˆ†Š‰ˆˆ‹|xwwwwxwwyz|~zx}{{yxxxyyxwyxxz}x{{z{{|||~~|~xy|z}{z~{{{zyz{y||zyz|xx{yw{wy}|zzzzxwuywuwtuxxyxyvvwwwyxwwvyuy|wyxww{xv|yxvvyxvxxuuuvuvuuvuuqrqvvwtvyywwuwwvuvssuuwuutuuuwuvwvywywxvuwwquuvvwvxwwwvuwutuusrrrtsqqprqwwwrrqqqqqrrrsrvuprpquouvurqrrrqqrttutuuvvuuvqrpwwvtrquttrrvpuuuwtxuqozqtqtsutqxvqswtswtzuzvvvuuvtvqqtuuttvtwuuruuwuvxtsuwuuvtvwvwwuzzuvvxzxwvvuzvtuwwtrqusuvtuvzwztvxzyvuuvvvvu{vxvuu{xywvvuvyuvuuwwwtvvvwvwvwwwwwyxxyvztxytuuvxxzwwvyxwvvvyvuuuuxzyvyuuuzwzvuvvwxvwvwvvsvzvuvwz|vx{x{yzyvzvvx{{|vwwuw|wz}yx{{y|{~}}zy|xx}z}x|{|z|xyx{{{zyz|zz{z{|y{{zywzzxz|{{y{xx{zxywvzxwvz||yy}w||}|xxz}€w{x}€~„€~€…‚€€€€ƒ†‡ˆˆ‡‡…‰††‡„ˆˆ†ˆˆ‰…‰‡ˆ„…{}zzyyy|wzyz~x}|~~x}}}ywwywxx|~|{yx{{||||}~xy|x|{{}|{xx{{x{zzy{{|zz{yxzz~zwxzwyutuxwywzw†wvwwvww{wyz|v{vyvxxxwwy{z{xxuvxzvywvsuvvvzuvvwwwwwvusvyuuuuuuuuwvvuurruuuvuwwuvwutvxywvxzsutvvuuxxvvvwtuuvuuutuupqpqprrsrqqpoporrqpuuvurqutuvvuvurrsqqrrqqquuvuutuurpuvrrrqrqutqqupuvtvuuuuprtssrouzvwvttvvwvtxuyzz{ttvtvurtuvuvutvvvuutvuw{utuvuvutwxu{vtzzz|wzyxvwvvvvvwwwvvuutwuqtuyzxwvuy{vuuwuwvuzvvwuuuuvwv{vwuuuywwwyuswqwyxyvywvvxyzyvvvywwvwwwxwyyywvvvvvvwuvvxzuzxuvttvtyzsuutuxx{wyvwvwvzvz{|v{{xwzy{zzz{vzz|v{{|{|{{|yz|{z||}~}}{zz|zwvw|zxzwx{~x}~~|{z{wyz|wyzzyyyxxzx{zyyzz{yz{zz{z{{|{{zzxw{}|yz~x‚€}||}|}€ƒ€„€ƒ~‚‚}€€„…‡‡ˆ‡ˆ…‹…‰†……‡†‡†‰ˆˆˆ‰ŠŠy{{xyz|zzz|y€y}||z~x||{zyxx{vwz|z}zyy|{||{€z|zyz|{{{|~xy|{xxzz{yyyzzz{~z{|yxxzwxvvtwxxwvvuvxyxxuxxwuwwvvwwzxwvyvw{wvxvwvuwwvwuusrqqwuuuvsywwvurtwvvuusrrrruuurvvvvuuuvxwutuvxvyzrvyywuxwyrxwvtwwtrtuvvsuusqpqpvqwqtpqppoqrsupupqsqqsuvuprquprqqqqqstrsusvvuuuqpuuuurrqpqvsqpqrwttrqswour}rvqtuttututusvtzywuvuuuvtvtuuuuzuvqwuvuvtvwzwvuuvuvyuwvuuvuzzz{uvwvzxzvuwwxstvvtuvuvuuuzywvuuuxwuwwuvvu{vvuutwvwvwuvvvvvvrqryvwwwxwzxyzwuyyywwvvu|wwvwv{tsyzyuvyzzxwuuwyy{zzvwwvuvuzyuuutussxwxuuwvwwvzvvwz{vwyzzzz{xvzz{wz{z|||y|v|}~~{zy}y||{}|xyz{zz|xwzy|{|zyzyy{|zwzyzzzyy|xxwzzyzzy{{|{{{x{{yxy{}xz|zyw{||||zyzz}{€~}€~„€~€€€€€}€‰†…†ˆ……ˆ†…„ƒ‡ˆ††‡ƒ…‰ƒ||||yxxxyzyy~~~z}z~|}||yzz~zywxxw{|yxwvw{{{yzz|yyyyy{|€xx{{x~z{yzyxxy{~yz|xwwyyxxywvwvxxvvyzwxwvwxw{wwvvvuwwwvv|v{uwxxwywvwwvvusrtuwr{vwxwxvwwszuvurrurrrrrrtuvvuxvuuvvwwruxwuuvvrvvvvvvvrrsuwvvwwqtrsswrrruqpqrvpopoopppsuvuurrrqquupmmlmsnqsrutuusuuuruvuuuqputuuuuvrqqqpqqqrurrpourvtwpuospvuuuuuuuutrtuvvtuvttttsuuvuuusuvuttuuuvvv}rtuuwxvzuuuvwzz{uvyzzwyzwvvyz{vuuvvuvuuwwwuvvvvvvvvvuuvvvwvuuuwuvxwvuruvuuuuqvyquywnywvyvvvxyvwwwxxzwmxwzvvyvwwwxutuzxxyvxyzvtwxuuvy{yuvtuuytvuvtvwvtuuyvvwvzvwyzzzzxww{{|vvwwwyyyxwy|}|z|y}{}|||yz{y}x|xvy|y|zxyxwzv}~{uzy|zz{|yxw{yzxzz|{{|yy}xy{wxx|{yzzxzwy}|ywxz€y~|~}|}~~€~ƒ~€€~€ˆ‡‹…‡‡‡‡‡††…†††………†‡‰‰‡yyy}zvyyyxy|}~|z{y|~}|ywyy}|z}{yyy{yx|wwy{yyy€yy|z{z}{xx{z{{zzx{zxyx{{xzxxxxwxwxxyvxxzwuvxxwvwwwwvyxwywxwvvxwwwwwwyzwxzustrtrstvvuwtwwwvuuuvuuurrvssuqootrruuvuvxwqvuwwzxwtsusvzwxwwuuvwsuruvvsvuuqqqtqsusrsrrquqqpqnqpqqvrrtrtqpqvvsqmrprorqqtvvvtvuuttuttsqqoqtvtusuqrqqrqpptutpququstuvuttvtutuvvvvqssvvvqut}tutuswuututvuur}tuuwttqpstqwwwuuuuvwvzyvvzyzyuzzuvyuwvtwuvvvtuvuvvvwvttttuvwuvuuuwuvxwuwvrturuuutvtsuxwvwyvwvwvvwwyyyxxyyv{yzzwvwvtuyvxywyxtvvtuuvutzvuszyvvyvzuussvwvvuwuyuvvwvvvyy{zzyvyxw|wwwwvwwxwxxwvwvwvwxy{|{{y}x|{wwx{yw{{yz{wzww{wvvuuxyz{|{{xxyxxxyyxx{{y{yyyy{{||~{{{|x|yw{|}}}}zyx||~|}~~{~€€ƒ„ƒ…‰ˆ……‡„…††…ƒ†ƒ‚†ˆ†ˆ‡‡x{y|wwyxyzy~{yzz|||}|ywy|}|~wyz|yyz{{{xxzxxwyy{{yyy{zz{ww|{zzzz||||zwwy{yxwxwyywwwxutvwxzvwwwuvuwwuwvyxwysvxwvvwyvt€uuxxvwwztwvvuuuvwwwwwvuwuurvwvrqrsuurrquuvurvtuytrruwsqpuuuurttsrstsuvutrturstqqopoppqsssrrsrrqrrppppqprqqrusrrrqtvvpqqrqqqpqquvvwrqtuvvrrrrpqrttuqpruprrtpvqurvuvurtstqqqqqpqvruusrttuqutrtvsststvutvvwuututttuuvvuvuuvuuvvutuwuzwux|wwy{z{{{zuwywvvuxuuwvvvwuvvvwxvvuvvwyutuuzuuvxvvvvrruvuruwvwtruwwwwywvwstvvvvxvvwywrywyssuuurutwwwwwvsrruutwvxwxwvsuvtsvuvuwrwvwtrvutuuvvxvvwzyzyvwzwvstuvvzwvvvuwvvvywvvwwvvzyyvwz|wwxywvvxxuvvwyyzyxwwwyyzxxx~y{yw}}}x}||}}y{||{{}|||{|x|{{|z||z{|yx|||~~|~}{{}€…€~€……ƒ…„ˆˆ††…ƒ„€‡‡††„ƒƒ„†ˆˆˆvxyzxwxxzxy~xxy{~{wy|y}x~xv|vzv{yz{{|xwywxxwyy{y{yyyyxzxwwyz{zzzxxvy{zwy{{{zzy{|zxzxxwxzzwwvwwwuvwvwwwxuywvwvvwvwvvuuvxxxvvtvvwwvuuuvvswxuvwvssuwwzyyrwvuusruvussvyvrrrrvruttrqrqsuuwswsvusvwvvvvrrswrqqqqsuvquvwqrqsqqqqqrquoqrpmpsutvrppqovsrrrrurturqsuupqqvvuuvtuquruupruspqqtuwoqutuquuuvvqutvqututtuvruquuxsuuutusuvurvuttvtuvuvurruuvuuuwyxzxvwwtx|zvvxz{zz{tyzyxzzyz{vwvvvvvwv{wztvuwyzuvuwwwwwvvwvuwuvtutuuvu{vrtuvyyvvyvwvwvyzwvvuvwvrƒsvsxttuuvxwywywwvtvuuwwz|ysvtvuuvuvuusszuywuvwvvuwstvvzyxyxwwwwwwvvy{|{w|x{uvx{w{vywvxwxwxxvwxwvvxwpw|vquvussrutss|zuwy}|{|{y~{{xx{z|{{|yy||}~|||}w}|~{y}|{|}~}||~||€xy}}~u€~~‚€€†…‡‡ˆˆ‡†„…†‡ˆƒ‚‚†…ˆˆˆˆuxwyyxw{yyy~wyyyxxwyyyxvxyz|wzv{{|{wuvy{yyzwyzyy{{y~yxy{vxyzzxwxwyz{yyvz{zzzyyxxxxvttzxwuvwuwxwwwwxyvvusvzwwuvuvuwuyvquvvvvuvvtvtvvvuuuwuuwvuusuvqsvymvvvprsuruvrtuuqurqqrrrqrrrpssusuusrtvvxruvursuwsqqpqqpuqssqqqrssqrrrqrqqqrrzrrrvvqqrqquvvuswvrturuuvvrrpqtuptputvtuuuvtppqqvvvtuuuutuutqrrvvutvvvvuvvwvuuuutuvusqsrtuuvutuvuuyvwuuuuuuwuwwvtz{vuvuy{yzyyxyxyzuyzyyxyzywwwwuvutvvvvuutuwwvvvzwyutvwy|vvuuuvuvtutuvxurvvvxvwyvwvuuxywvwwvwvrrrrtywvwsywyyvyyyyuxwvwyxtssuuruvuuuuussrvuttvvuuwtvuzy{vvwxwwvzxxuwwuwrvwwwvvvwwwvvvwvzzzyvuyx|€ƒŒŠƒ~}y|vqomigfledLK_tw{}}z{z{{{yyzz{~}|{{}}}}{{{zyxxzw|||yw|}~|~}~‡w}€€€z€€~}€€…‡‡†ƒ…‡†„ƒ„……ƒ„„„†…†…vsw{zywywzzxyxzyy{~wyy~vzz}zvyy{||{wxw|xwx{x{yxz{zyyyzzywyzxzzzyyzxxv{wzxzw{zxxxxxttttwwwuvuwtrwrwvvwvssvzrvvvuxuvuyusuuwvwwvuusrvquuwuwxuwuvwvuuuvuvuvwwotutsuuvssrqrrqrqrrqopoqrrusursrtxwxqrrrrsqvqrrtqvqvqsqrqqssrqputqqrqqqpqqoqsuurprrutvsruvpvtrpqqwrroqtvtuquuwpvtuusqvqrpqvrtpqusvuusvsuuutvvvuuvuuvvtuutywuqzuvuuuutvuvuvwuuuuwvuvywvtvxzwuwzvvxyyyyyxyy{{zv{yxxz{{{zvvuutvuvvvvwuvtvuvwvxvwvwzyvuuuutuuuu{vwuvuvtuyvvyvwvwwwwwvwyswvvvvstxwvtuuxwxzzywvwwwwwyxuvsuuwsuuytvvyvsusstuusrsxwvuvvuuvuvvvuvvwwxzwpusqppovlonus{posus|umpcVX[J@>;<:532.-)!7Hzwz~|zz||}~~}~z|~{{|||yy{yyy{{wuvyx|}}|z||||€y€|~€y€~€‚}|†††………†…‚‚‚‚€‚„‚€‚……tywwyvyyyxxxyxzx}zwwy}xvxwx{|w{{||yxww|wwyyy{y}{xy|yywxyzxxy{zzvyyywzw{zyw{yxwxxwuttwwwvxvxtssvwwuuwsrrtxqvvvuzuwvxuusrvwuwvwuuuvvvusuwxxwwwvvusvurvyussrruroussssuqrqrrqqrpvppqquurtqssivutprvrrrqrtrrrsqsqqrvrrpqqprqwvpvpqqqqrqqqqsuurqsssuuqrsrrrqrrsvqqqquuvuquqrrtvvuttvqrusvvwututuurvutuuuuwvsuuvvyvvuuuuuuttzuuuuuuuuuuuuuuuuwvuwuyvuuvzyuvyxyyzz{yzyzyxyxyvwvvvvuzwvvvuuuuvwwvvuvvwwvvvyyyxusyxv{ywvuuvuvututvvvruyvvvvwwtxuvuvvxsvvwvxsvuvwusvvssssrwtuwwuuvsrvutvssuvvvuustuvuusssvusssrpqrrqw{{ƒxtswvwuvvpv~sspl_XSZD>993/#(1ywz~|||{|~|{y~{{}|y}{{zz{{xyy{||{zz}}|{|||~‚z~~~€€€€ƒ„†ˆ†…‚„€€‚€€€€…txzxvvwxzyyxyxyyxxxwwx~xxxvwxwy|}{|xwvwvwv{{{{yzyvyyyxzx{zzxzx{{yzyyyxwyuxx{zxyxxwwvuvxvurrvruruwvvswrsqqqswwuwvvwxxxuuuwuxuvwuvutvuvvvvvvutuuusuuutwvuusuuurququrrsrqqqsrrqqrurqpuvwsrruowurpuvsqrprrspuqrqvrrrrrttuqpqvspuqprqrqqqrqututrsvputqquqvrqrqtvttqutstvounvpvrvsuuuqqvutvvvuuuvptvurvuvvvtuuuuuvvrruvw{vwuzuuuywuuuvztvtzsuvvtvsyw{yywyuzyzwzyzzzxzz{yyy|xvuuuvu{{zyzvvuzuwvvuzwvw{v{uzxwvvtzvvvwvvvwuvtwvwvvuwswvyvwxuurqtvwxursvwurqsttuvrswwwsqurxusuwvuruvurvvuusuytxsvuvvstvyunqowusy‚€~|ygncPTawuxutw]@*)vww{z|}||||zzy€€~}}y|}~|y}||}{zz{}{{~|~}}|}ƒ|}~€€~€‚€€€‚‡…„„‡„€€‚€„ƒ€€€€wvzywwvzxzzyyxzyywyww|ywyt{yxuzwx|ywwvvuvzwy}yzxvyyy{zyyzzzzzx{wxvvyxxxxytsvyxwwwwsuqwwstsssssvvvvtqrqsqrrvrtuuvvvwwvvwvvuuwzsussrsrrvvwwusrussrrrtuuuvuuvtrrsrqqqrrrronorrsspnpqrowsrvuourqqrrstppqpoprqoppqqqsvvuppqurrqvvvrrprqqqpvuturquruuruspuutsuuuqtuuvvuvqqqwrprvvutuuvvvwvuuutxuquvvuvwuvuuuvuutvvyuvuutvvutuwyvuuuuxytuuwuvvuuuuzuvuxvvuzv{uyx{vyvzuwzyyyzzuvuuuzwvvwyuuuuuzvvvzzzyvwvwwuuuwvvvuuvuuuwuswwwvvssswrwwxyvvrsswvvuruvwqtutrrqrsvvuvsrrtt{yywvusuwuuuvwxvutwurvtrurwjmf_Y_SG?82.:Nruvuv†I ��"|vv|{xz~|}{}|w}|€||{}|‚~|{|}}|}||||z~~‚€€}}}~}‚z~|}~~}|~€~€‚€„…†…~‚„„ƒ€€€€€‚xxyyyzzxyx{yvwwv{xx{yw|xywvvywvuwy|yzuuuwx{ywxyxyyyyxyyx{yyz{zzxzyvvywwxwyvvwxsxwwwuvsvxrrstvuvvvuvvrrqpqrssrruvvxsu{wvvvsuuwuusssvsvruuruuvuvruvsuvtswuvusrrrqrsrqrrrsnrqrqqqqpqqrnvqpqwuvstrrrsuqrqrsqrqqpqqvrrtrrrtsqqprsuuurvvrqurvsuvrqupuqurrqutvtuuvuuutvusuprwwvvvvxuuvutwwuuywuwutuuzvvvuuztytuuvvuyvwxvyvvrvuzwutuvuuytvtxuyvvwyx{uvvyyzyzvyryx|vyyywvuvwz{|vvv{zzx|vuvvvvvvyvvuuuvwuuuvuuuwvvvwtzuutuvvuvvvuvusuwtvvvupqrssrvuvqwxvvvvuqrruuursrtrrqokkkjgjhmvususuyuuvuuruuuvv{5��DmvvwvŽC ��'€wv{{€€€~€~{||yx|~€}}||{}~~z{}|†|}{zz{~||{}}y|{||€~~~ƒ‚„………€€€„‚€‚€~€‚xxw{xv{zxyxyvww{{|vzxyyvwxuuwwuxz{zz{vuuw{z{y{yyxxxvwzxxww{{z|zxzwuyxvwxwxxyxxwxwwwwwtvwvwtwvvtrvvuvuwqqqvrsrqswwwvvuuwwuusuuxwusuvvusrssrvuuysssusotruuuwsrprrrqrqrrrqnqrplqsomprooutqquvvrrsrrrrquuurqqrrpptvvrqsqqurqqqpotqtuuvtttrsuuwutsptpsqrrrvuusvutuvuuuuttuvuvtuuuvuuuuuutuuwzxxuyutsssuuuvwwvvzvzyywywvutrtttwvwxwyuuyvwyxvwyvyvuvuwyxyywvvyszyzvyzyzwyutz{zuvyzzyzwuvxxzvvvyuwutuvvvuusuuuuvvtvuzuuwvuvuwywvuwvuuvvqsruutuurqpopppopppomptrzy€†„…†€€wqdk_Q>Bbrsutuvvutrsuwutuxr|1A:wuuwv„G �‚vv{|z||yww{|{~}{|€€}}||~|~}||}}|}~~€y}}}}}||€~}€||~|~ƒ‚}~€ƒ……†‚…†€„€€€€‚zxyyxwywxxxwwxx|x}vwxwywwxxxxwtwvvwxzxvvwwwxyywu|yxwvuvxvv{yz{zz{vxxvxuyyxxwussvxvquvvrrssssssvqtuvvwspqrrrsvqqsvvxvwuzvvwvwvuwwvwwvwusuwurruwusssrrtuurusssrpqqpnopqqommqqpqrmnmprpsuspqtvvwswrrqqqrrrqpqqqppquvrsrpurquuvpurutwuuuurruvtuuutttuqtpqwuuttuuvuvuusvtvvvvytuuuvuuusvsytuuxwuvywvruuuuuuvvxyvvzxvvvvvtztttyuyxxvxyyyzzyxywvzuuywvxyxyzyvztvxzuyyxxzyys{yzuvyyyvuvw|wuvww{xtuzrvtwvuuuuuruvvrvuzywwvuwvyuwwvwuqtuurpppmkknlxz‚{{~ur~y}of^ZE=71( ++)[psuuvvvwrsuvvvuuws€I �1yvuvwŠK + # (%+*30-8<ADLkO �ƒzxz|xxxy{yz}~~~~|{~|€}€|~~~}{}~}~}z}~}|}}}}~}~~€~|}€€~}€€€…„€€ƒ„~~~|}€wyxxxzyzzyw†wx{}w|vyw|w}wxwwwxuwu{yuz{yyxvwzwvxvzzy|{|wxuuxzz|ywvwwvvwxyyxxxusrvvssruussttvwrssrrrrssqpvrrrqmkqqrrqrvvvwvwwwvuvxwvusvvuuuvuvuyusvorsrvurruuuuurrmuoqqrorsqomprssppsrrrrrrvvpurrqqssqoqqrqqprqpuuvqruvvurpqvutqvtuvvvvtuuvutttttuvqsuuvstuvvvuuuuvvwttuttzwvuuutuuuvuuttuxwuwvxtttuuyuuuywzvtytuvtutvuvvvuuyyyuyywvzuwywvvzuvyyyyyxyzwwvwxxxvyuwyzvyuuvvuwxwywvvywuuuvtzvvuyquvvuvwvusquqvtvwwvvvuvuuvtuuupx†…„ˆ†ƒxupuf]B;41/+����Pvqurxwuuuuuuuuwvvm~J��/…uvtv†W 1379?BINUORX[^npvs~‚ŠŒ‚x†h(�ƒuxz|{|yyz|z~|€|}~|€€€€{€‚|{z||}||}{|ƒ€~|€€€‚„……€€€€ƒ€‚€vz|zxzxzzxwxy{{ywuuuw{yyxy|yxwvxwy{y{yvx{vwzvuwv|xzyzyxwwwyxzywvyxyurvxzxxuvxvtrqvsswrsssssrrrsrqsrqvqqqrrroqqvvuprrvvwwwxzyzxwvswwvvvssuuvuuuwuuuvrruwpuuttvsrspuqprqrssqmprqqqrppprrrtuwvqvussrqwrqqrtwsrrrqvuvtvvuuvsrputuqwtuuwuvtusvwutttuuvuvuuusqtvvuuuuuuuxvuuttytutttvtvuvuuqxwyvvuvuttutuuuvvwzutsyyywzruutuyyyyxyyzxvyyzuvyxxzxvvxvuvuuzzzwyvywxvzzyzyyzvvw|vzwzxyuvwvwzyvs{wyuzquuvsuuvu}svvvvvuutvutvurwvvvug>'.1+ J}ssvvwusuutuuusru]{S�B$ + + +�(€rrrvƒXQ_ehw~{x~q|„‚zw|~€~zwwvwwxxyvl~)‚uxwz||ywywz}~~}~}€€€€~‚‚„ƒ‚ƒ~||~}}}}}}}€€€}~€€€ƒƒ…‚€‚‚€~~‚€€€€€€w{vv{{yywxvxw{yxxxwww|y{z||}yxyywuz|yxvwyyxzxwuxwyxxy{xwuyxvwywuzxvwwwyzwxuuvwvruwuusssruvqsrrswtsrqqrqrquppqqrdrqquuvwyy{xwyywwsxvwvvuussssutvssuurrsrtrnuxxwvuutqqppuimmorrrpqssrrrruvwwursruuvsrruvquuvsrsrroulvruuvwrquwuqwtvusturvuuquuuuutvtqquvuuuvvuuuuvvuwytutvtrtrruttvywtqqtuvyvuuuuutytuvyuvuutttuutytuuutvywvwzxxyuvxzwvzyxyuvxxvwuuuyyzwuvvuyvyzzvwyyvuvvvuvvuuuwvvvzywuzyuuuuttuvtuvvuvtuvvuuuuuvuutuuvuuwe+Osssssssrrsqppysr‡„U� + %!%��'‚qwuvzkh]u|sxuuvuvuwvvwwywwvxwxwwuuxozxr”,�ˆszyy{|z|xˆ{~}}~~€~€€€€}€„€€~}€€~~€€€}}~€€‚€€~~~…}„€„€€€€€€vy|vx{xwwvxwzyx|vwwyyyx|zw{{ywwwy|xwwwvxyzzwvyz{yxx{zwusuywwyxwwvusssuwwxwttrrruvpvsswuuurrsrrrrrrqvrqppqpqsqprqsuqquxwzzuwzyvwusssvuyuvuuutvvvuuuuwvxsrpyxyywsvsssspwusrqprsrqrqrrrsttxuuvvwwuxrvqsvrruuvtsrrpuvvpuuvrrqvutuzuvuvutqwvuvutvtutvrzyvuuuuvvutuvuvvvuutttvttqxyytvyytsrutsvytuutttuutxvuuuuuuvuyyyyyuttuvxvyzyxztuvzwuyuwyyxvyxxvyyxx{uzyzvyyyvzxzvxvyvyvxyywvvyyxxyxzyxxvvvvvtzvutvutvttvuvvvuuuuquuuvvu|i�B{uuwuspsxvww|vngc^TIS_acknr‚€…L%��� qvvuzhfoosuvwxxwwtqqtuvuursurwxwwwxy|xz’3xxyw}{|{xz|||€|~}€€}€ƒ€ƒ|y~€|~~~€~|‚€}|€€€€}{~~€€{€‚……„€€|‚€€€yytuwyywvvuww|wxvvy{yyxxwyx{{}www|zxy{wwwy{zyywuwywwwyzsuuwwwwuttvxs}tvwutttwrsvvuvrswvsurwqppuvvrrqqquqsqvqrqrqqpqquvvw{uwyvvywvw{vvruvvvvwuvtuustuuusqqxxywvusuuuutxtvvvwusssrwrxusrrwvvxuuuvwrsrtvqqwvwvvvrqrqqpsquoppuvwvzuuvvotrrruvttttttutuvutuuvvuuuuvvutuvwytuuuttwrstuxvsruttqryuutsttttuwyutvyvuvyxtuutttxwuvvxyyyzttxxyyyvuyywyzuzyzvuvwvvuyvxyyywzzxuywvvyvvwxwvwzxxyyyzyzwvvvvvtuvzvvvvtutuuuvvuuuwqvvwvuuq ��?‰swuuv[CD:3) ;Švs~…yzuq•f*�‚qvwvvv�H‰ryv{uwyvvvtuuuvvvyyzwvwwwwwxxyx5}xzxxxx||||{€|€€~€€€€€€}}}~‚|}~~€‚}~€„ƒ„~€€~€€ƒ‚€…€€€€€wvwwwwywxwvwxvwvwy{{xxyyyxwzwyyxvx{||ywvvvwxyyvwstuuzxvuuuuvzwxutuwwsssvztwrvwvsussrrrusquqwpqrupusrrpqppsrqrqqnpoqqsrqqruwxzywvzwx{wvvuxvwuvuuuuuuustusrrxyzvuuwwvusuwtuvwwvwxsrrsvwsytvwwwvuvuvturvtspvuvvvuruvrpputunutttvwzvvuuuuvxquuzuwvvuvrvryuuuuvuuuuxvuuutttuuytusvvytuvvsuqtrqqyuvquturyswyutyxyyzxzszuzuuxzu{yywyzztvuyxxxyyxxyzzr{|{vzyzuvsyvzxyy{w{xzxvtuuvtyxzwuwyvyyzyzyzwyuyvvwvuvvztttztuuvuvsuutuwuvtqu��� + -154;Ng><‡svpr€N9�/)‹`uwutttvvtrz-{quzww�:‡uwvwux{vuuvuwuvvvurpqptxspryyywAn|yxxyz{|~}|~|~~|€€~€ƒƒ„}}|~€€~‚‚|~|}€€~~€~~~€~„ƒƒ}‚ƒ…ƒ€€€€€‚€wuxywwyzw‚xxwww{{|||wxy{z|w{w{x{vwwuwzvwvvvxxyxyy{vwyysuuyuuuvtwstuuuutszuuuswstsurrrsrrrwwwqsrruwqqspppqsrrqqrqpqpomppqruuyxwwvwzyywvvuuwuuuvuurussruvwsuqpquuuvwutuuwystwwusuruyvussxvvwv|uutuvvvpvrqpuuusuusuuuuuttupuuttvuuvsrvvuusqwvuvvvutttuuyttttvuutuvvtututtuxuuutvxyuutuurtuqruuuussusrrswyvvyxxxyyytzvuvwyvvwxyxuuvvvuzxvvvyxxvuuvqrzvvwvvuuuwwyuz{zyxyvvutvuuytyzvvvxyzy{zuyvwuyvyyvutyzsuvzvuuvpvtuvuuvvuquw�� + +*)76@EKRdivx{x}ypvƒt��8€swuuƒJ&%krtwwzzzvvm…,~pxwwy€�4†vvuuusuzt…}|{~|{{sur`SP\rwyywŽDluxwvwy{|yy|~~}~|{}~||~€€€€€€~€€ƒƒƒ}~~}…~}~€€€€†ƒ€~~€€€ƒ€€~€ywyxxvywwwxv{yy{{z|ywxzzwywyvxyywvutuuutwvvwxx{yxwwywvvvwvutttvwvvvwwutrzvutsrrrussssrrqrvqprsrrrrqpspwuwvuqrqnpmprmpppoqvruuussuuvwyuuvuvusyrtvtrururrqrruuuvwvruuusuuvuuwxwotvwwyyyqxvvvvuvu‚swvvqwpqoqtvuqsuqqtuutqutvy}qvuvrusws~ruqzvuuvuuuttuuytuvuuuuzuuuuuxvytuvywyyywyuvuttttvqsuuuuuvp}svsyyzxyyywxyyzzuwwyvtuuvxxutuuvuzywvuwzzutzv}tzuyvxxywutuwywzyzuyuwtyvyuyy{zyuvyyvyzyvzxvuyyyxyvuuxtzzyxzuvvzqtvvvvvuu}{�3;Gbemu€ƒ…†…†…‡ˆ€ƒŠ‡„}yvwurrrnit�4„svvs‹C,€prrqqrrusme“:�& !0qpzvsy}2„wwwyzfTLHA?;974$" +*;E…wyywŠG�izxyx}vyzz|~}z{}}{~|{|~}€|~~€~~~€||€€€}€ƒ€~~}€‚~}€€€€}~€€…€€€~yyyywwwwwxyxywx|zxuvwywxvww{vvxzvuuuuuuvwvvuxxzwxzzzxvtuwwuysuwxvwrvvttuwwstttqsssssrqrrqurqqrqqqqqqppquppqrrropoqqpppmopprrrssuruvvtuuwtxwsuopotprqrrrrrtuvuwvvsvuvuussuwwwvrrwwwwvvrvruvvvvwrqywvvvrrpsuuppqrqrvrrtpututrrvvvuursrstuuuvuuutvvtvuxyzttuuuuuvuuuuuuuvssvxwzvusuuvsrtutttutrrxvpxuutwyuwxyvvxuwvytutxuuwtuuxyzvutuwzxyvuwytuuustyzvwzyyyuututuzyzuztutyutuvy{uyvvywuvzvvyxyyvvvtvuuuvuzuuuuuuvtqsvuwvuuer†,Ÿ”…zxt|}yyyvuutuvvvvvuvvvvvuuurp„-†ruwrC…|opztsn{xrŠ™:�:Fpsyury‚ 0ƒwxy{„<E†vwxw‹If}xwywxxyy~}}~€z{|€~|}{}{|~€}€‚|€~~€~€~€}~~€~~~€ƒ„~|}€~€ywwwvw}yyyyxyvw{|vuvywwvxwuyuvyyvwywwvuvwwvvvwzz{zzxwutuxvvuvxwyusvvttsvwwvssrrrssrsrqsssrrrqqqorqrqrqqpqqpqpmkppppppmropqpsosrsuruuvuussrvsuuvtuuutrqtutrwuuufvsuuurqrqwtrvvurvruwvwrvvvvvvwvvr{vywursquuupvvrrvrqqtuuuvvuvvvvuusvrvuvuuttturytuwvwyutqtuvuvuttytvqur}rtqssuuuuutssuurtttttuxyxxxyqyxz|zvuuyzwvxtuuyuywuttxzzyyyvwyyywtuuutuv{xyzwwzvyxutzuvuzz{uyvuwytzuuy{tywv{uuuwvvyxyyvvvsys{{ytzuztvtzvus~vvtwrwoq†�#“Wvwwwuvrnpuuttuuvvvuuqqtvvwxwu}ƒ0…quwsŽH4 ZwyltsmfVUC=Svzn_jpvvvs€‡ %‚wvw|‹H/�8‡twwy„PVxwzy{xyy}|~{{|z{}w{€{}~y||„ƒ{~€~}€ƒ}~|ƒ~~€‚‚~|ƒ„€|~~‚€€€~|€€€€wwxxwwwzyyyxwywxwwvwuvuuvxtywvvwuyzzxuwxvvwyxyyy{zyyvxvuuuuuvyzzsywvttwxwxssrrqrrrrrqqrrrrrsqqpuqrppruprsqqrnpqqoqqpporqrrpsqsqsssuvuwwuuuussuurrururruqrqpqsuussusrpqourqqrsutuuvurrssrrruqwvuxwuuvuvuuuvxxvvrrrrswtsuuwrvvuwvuuvvruuuuutuuuttuuyuututvqxwyvvuuyruuuwutuvuttttututtuuuurtttrxusxjxrxtxuuvvyyyuuuutuwutuuxxxwuy{ywyywyvuuyvztxxxyywyyxxxvtzuuuztrtyywzwuvvwyvuwxyywyyyszyzxxwvuuyuyxyvvvvvuszvtvuuvqqpvsr„��$’lvvvvuuuvuuvuvuvuuvvuvuuqptuuvu…+„quwt‡C3#%,38@DNL^P;+%��Kއstystuwvv|†„uvr{ˆO���2‡svvy…T$�Y„xxyx}zz}~}~€||z{|~y~~€~{€}€€{~~}|€„ƒ…ƒ‚}‚€‚ƒ„ƒ‚ƒ„€„„„}€|„€€‚€€~€€€€xwvwxvvuuyvwyzwwvwvuvvutvwtwvwvwuw{zusrvvuvuwvwwz{zvvwvuuuutxvsssuvussvvstsrrrsrsssruqqrqqqrpqqqqquqrqqppprppqmmprqnpqrruqqqqqqrtrsssrsuuuvurrurrtrvsrurtqqrrpqqrrqrusppqqvpsurqrtsqursorqupwtutwvutvwvvvvtuttvvuuuqruuszrttvuuswuylvuutuuutuuutuuututrrrxutvvuuyrvuuuusyzyrwrtuuuwwvtutttuuuttuyyyxywvwyuytvwusssttyvuuuxwwtuxuuuuvuuwwwxutuxww{wxyzyxxytzuvuzyzsvvzwvuvv|zvuzyz{yyzwrzzxxxyvvuxsywyuvuzvuuzvuvvuvwyyxuvˆ�1uvvwuvvtvvuuuuutrpqpqptwskruwtrŠ-zqvxt~eW\z€…‡„yt€‘1/T{iwuvtrqsww|‡ ˆvvvz„R��������$ŠruuxT Owzzy{xz{yx|}||}vz~€x€€{{€~{~}}‚€€ƒ~„}€„ƒ„€„„€€€|~~€€€€€~ƒƒ~~€€xyvwwyxxwxxywywwvwtz{|vvvwuuvwuuuvxywusvvtuuvvwvyzvvvvvwuxussurusuuurtuwrtrttsqurrrrssqsrqqqpqrsqrrrqppppurmmqnmrrmnnrrrppqqpqrspoqvrsrtuvturrrrrttuuurprtqrsmqtprrrrtpopqurqrrqqqpppqqqqrqsqqukvvuorrqqruuqqtuvuuuvuvvvzuuutuuwuuvrutuuuuuutuutwzvruttutxtsuutuvtstvxuuuurtvsruutyuutuvuuuxuuryuwwsuuuxvuutvxuyruuuvwuuvxwwvxvxu{uuuuwyyyuuyywyvwxyyyvvuurustzywtuuzuwyvu{zvvwyzwwvyywxyxvyyzvvuuytxtvssvvyyvvzvvvuvwvuu‘urrvwvqpsosrz}…ŠˆŽ‰ˆuTUe|utrrŽ�„qvvrvv|xtsrrrtwwpcƒ)'�K€out{xwy{vvx‡ ‡uvwz„W������‹ruwy|_&H‚wwzz|w~}~~{{yz}{||}|€€}€€}‚|‚€€€€‚€€€ƒ„‚„„€€~ƒ€|€€€€~~ƒ€yvvuwzxvyxywxywwwwwtvwxuvvwwwvuvvuuyvwwttuyuvuuvywwuvwwvsxvryuswwwvsrqvursrrsrqsrqvrsqqpppppqqrqrrqqropqrqqppqrrsqqpnpspopsrrrrqpquvssrrutqqrqrrprvuurrqpqwusrrsqqrrsqppqqsrqpqqqprqqpqqqqpruuvvvturqppoppqppoqppy~qtutsytuuututuuuuutuuututstutyvyuuuutuuuruvuutturwvvuuuvvvvutyyyxyuuuuuvzvuttuxxyyyxyxxxwvwuxtuuvvvyyywvuywuywwyxywyxuuwxxxxxuuxxwuuuuvyrvrzywt{yzzyzzuzuvvzzzyzxxvyv{zywzzvvwwynywzwvwyxyxvyvuvt{vvtv‡%� utrqumvolaYX[B;51#S|rwtrŒ �ƒtvvtwxwyytwvuvvvwm‚"�C€uvtywwuwyvv„€tvwy„W����rwzyxj�Bwx{z{wuw}||}|~yy|€z~|||}~{€€€~€|€€~|€€‚‚‚€‚~|ƒ~~€|~€€…€€€€€€€ƒ~€€wuvvwywxwxwvuywxwwwwuvvvuvwwvvvxuuv{rtttuvuvvvxxwuuuvwwwrwuttuwwvvrrrssrsrrrrsrvrrrsrqppprppprrrrsqprpprpoprqqqrqqqsmorpmspooqqtorupporqruuuuuqqrsrqurprqpqsrssnqrrqrrpqpmnnmmllnqqqmlllklkkllkkllnvuy|……‡†ˆ†ƒ|sfPIbwuuuttutrttsuvptuttuuturtttutpuusuuuuttuuuuttutvvuuutuyuvuqwyxuuuvtvxuyuwvuuywwyuyxyxxuuvwytytvvuuwvzvuuxvwuuuvvrvuvuttwuvyvuttuuvyuzvttuz{wuzyyyzvtvuuuvzyyyyzywvvzyyxyywzvzuuvvzywyzvyzyyuvwvyzupw„/�tuuu†4��������CKqwwq …rvwwutsuyvuqsuutws„0�����?‰swtzz{vywvu††twwyY���ˆrvyw}c'�<~wz{y}x{z~}|||{y|€}|}|||€ƒ€€}‚}~€€‚€€€€€……„……~~~€€Šƒ~€€€€€€yvvvwyyywxuwusvw|vywwwwutvwwvvxw{wttrrvvutsvxusxruvuwwutswusrrwxssssssqrvrwusrqqqqrspqqqprpqqqpnqqppspqqporpqonnpqssnmqqlmmpporrpqroqpupssssrqrrmqrrssrrpnssssrssqrsrqqpqpqqnlmkkjjjqoqx|t}|ywqtqj]VF@;941 +-?Bvvvuutttrusuqvsutuuuuprrtttutup|quuutvuuwuuwuutwvustvvuuuutwyxvvuvvvvuuuvyvuxuuztzxxyyuuvxyutuvvvvvuuwvuuwuuvwwvvvxxuzuwvzxzuyrvuvwvuutuwywyu{ywv|vuvzyzv{wzyuyyvvuzwyzyyzxtwyyyy{vxwyyyyzwvv{uyzus4�ŒuvvtŠH>�@„svvqŠ…ruvrtsyyxsuprsuvwr‚0��:Šrstwxwwyuuv’"�†sxvw…_���Šrvzvwl�:‚xyxz{{yz|~}}|~~|y{€}€~€{{}~}‚~„~||€}€€€€€„€€„ƒ€€~|‚„‚€‚~€ƒ}yzvuw|yywxvxvuvvvvw{z{vtsuwxxvvvy{twt}tusuuvvwtywwvwvrrsswsxttuxsvvxusrswsuwrqrsqqsrqrqrnrnrqpnqpqnqrqqporqnmnmmmpppnmqsmpmmmlqrmoloopqpnmpqoqrvrpppnmmmmmmmoqppmlprrqqpljq|pqowuupndWE<4/')��1Evttuqtouqrrsusuruuvsutrrtsvrsvpptuuuutxyyttuusurrssstuytruuyxyuwxyxuuuuvvyvvxyozuywvxwvvuxyuwvvvtuvvvwwvvutuxvyvvvuxvzuvuvuxuytrruzvwwvvyyzyvuuwvvxyzzzzvzxwxwyxxyzwxvxwuzvvvwywyzuvuuuttuyvutuzzuuv|7�Žqvwt†O��?ƒtuurŒˆqrrrrswxvtvpqsvwur~3����3„rvruwwvzuvuŒ +…uwvv}k������„yvzwxq7|yxxyx{~{zz{xw}~|}|{{|€‚~||‡}~}}}€|€€€€€€ƒ€ƒ€‚ƒ……€ƒƒƒ„‚‚‚„…~€€zxwuvxyvxvvwvwwvwvwvwwvuuvvwyxwvvzvuuuvwwuuvsussvvwsxsssttttsrssrtvxrvwwwussqqsrsrrqqrqqppprqnrpqqlnnpqqqpoppmpnklmmppommnpmmnommmokpnmnpmmlkglpqomppmoqrlont{{uvlhmqqplfR@92+&��Asstvputvrvqsuytuuuustuuursqsrtottustutxmxuuutuuvvwxxwvuuuvtwxztyyyyzyzzzxyvvxyyxxyvvwxyzzxvvvyvxtqvyvvvwwrsvtuyyuyuzuyzwtwxztwwwuuuvwywwvyxzvwzwvzzzzywyyyuvvxxvyywywxvyzyvywvuvyzy{tvuvtvvtuuu{vutv‚:� +„uuzw†^����:‰uvsu‰$�€qqwvvrvuvuxquuwvur‡2��1‹ruusttvzvvuˆ/€ywtx}l�������ƒuvxyu~!4zywywy{{|||y€~}~€}z}€~||}}}}|z€}€€}~€‚‚ƒ€€€„„~~€€ƒ‚€„……€€€…†€wwxywvuvuuvvwxvwy{zywwuttvuvwxwvuyvvuyyzuuswvuuuvwwsrrrrsusurvtrrtvvvwwwvwqsqrsqqprrrrqppqqrqppqqqmomqlpppolojlmnppqqqoollmoopoommkhgiiukjiuttqhkoopmgVKEA@L92+#%AIlpqlk]:����<xsqqsuvuuustuvuuxuuttsruqsuruqoutusruuyrxutuuuuuuvtuwvuvwvuuvutyutuvwvvvvvwvvvvwyvvuuvxyywzvtwvuurxvwvvwutwtswxyvvuyrvwvuwxzuvyyzuuuuvywwwxvtwywxyzzxyzyzwuuyy{yyyyyyxxy{zvy{wuxxwvvvuutvvttuuutuuuuƒ?� +tuvv„g�����:ˆtupuƒ0|prrssruvutsquvuvvs‡3���+ŠqsuxsxyyuvrŒ1�€uwszxq ��������‚uwvvv#3y|{}wv{}}}z|}~x~{}~~~}|~|}~€}ƒ|€~~~}€‚‚„€€…€‚ƒƒ…‚ƒ„„„ƒƒ…€†ƒ‚€…~€ƒ…„ƒwyyzwvxwyvwvvwvwvzwvwvttwvutuwvvuxvvwvvyutuvyvyyyxxvuvvustsrwvstwwvvuvsrrwqrrrsqqqsrrqsnqplllqqpmmpmmnmmimojpqpjjkkljjjgjfhhhiiomljda]UU\cXBA?BOallmke;2+@pqokgY-�;{ttutvruuwuuvururutytvruuutuuuuutuqtttrutuuuuuuuvwwzxwuwwvtuuwwyqttuvyywvxusvurtxyvvuuuywywurwtssuvuwyyxttuvxzuyyzuyzvvvwzyzwvxuuwwxvuuwwwvvvzzzw{yzvzyywvuwyzyyzzzy|{zyyy{zyy{wxxyyzvututuuywvuvwuC zwtvuƒh����8Žpvru4�~musuwqtuvutttvsvsq‰5��-‚puuuszyvuut€3xxwsy~s�������‚uwvyx†&‚ww|vv{{~}}|{~|~~}|€€~}{z{~€€|ƒ}€€€€€€€€ƒ‚…€€…€‚„„ƒ€‚€„…‚~ƒ~}~„vzw|vwxxxywwvvvxz|wwwxvvwwvxusuvuztxvvwzuvwxwuwxxwwxwxuurttrussuvvurwxrrrwsqqwruqsqsqqqmnpkqppopmlkhjljlhhhgglruqvu{ovtsjcc\QWenllj`A8=Wvnmhe1& 6toqkjf��-yqtuuuvuvvtuvuurytrruuruuuytuuruuuqtrtqstuututyttwvuuuvvvvuuryurrstuvutuutsrvvutxxyuwvryutrttttuvuuwywvuvvtvzwtvuuuxzvuvvvwyyvxuswyxwvvuywwvwxyyuwzywzyvvvvvxwxy{{zyy{|zzyy{yxxwxy{yyvuutvwuuyuvxvwv~I�|quuur���3ˆqwvt5�lstvvsssuvtruwttss†8��†gsuuvyvutuu‹5�wqwrv~z �����~xwzwz€2�"ƒvw|zz{}{|€{z{}{}~{€}}|{€~‚€|‚}‚€€‚‚‚~€~‚„„ƒ‚„‚ƒ…€…†…‚‚„„„„‡ƒ„ƒƒ€„~„‚…wywuuuwxwvwwwuvvz{xwuvwwuvyvttuuwvuvvvywwuuwyvwvvutxrrsrsrssuswvsssqvwrqsrsrrrssrqqppnmlkkkkjhkkhgpcmmwutm|plYDDQFM80+))&.3J^ljklj;",QspnhY:)uopmkv ��� (06:?K]M>�*|sttrutvvutvuuttttttuuruquusuurxququruqzvuvyuuuurvxxtuuuvwuurvuur}tqrttvuttrvvuutuuuuzryrurtttqwvuuyyxwvxyxzyvvvuyyyyuuwvzuywywvtvxywyuuuuttwywyvxyyy{wywzyzy{y{y{w~y|{zyzzzxyzyyywwvuvvryvvvxuwyvuvzR~qtst|x����!Šrwvv|8slrvvvtrqrtvtuxvsss‡9�z\fpyvprwuvw’:�suvswy{����x|v{xy€4�€wy{|}|}y~€{yz}|}}{~}|{z~€~}~‚|€~€€~ƒƒƒ€„„€€€…ƒ€€€‚„‚€…‡…ƒ„ƒ‚‚xyvwwxwyw|xxwvwx{|xvvuuwuvvwuuuuuvvwtusuttrxvuuuuyswtvrssrstssrtuurruxrrqrrrrsrqpqqpmoryrurprpmpikb_SG<83/%! +GU}kkkh3 �Rtekfg<�,spqkkn) �!.49?NGNZgnyx~€|~zv„ŠS$„sttuusuvutuuuusvqstvutusrtuuutuuutsusqwuttywwwttutuuutrrvquruvurrtrqqrqrsurrrrqrttutvsrrvvtuqsuuxxvxvtutwyyzyyvvuyyzzzvywuwzyuvtrtutvxuvsvvywzyxxtyyyxvyxxzzyyz{yxy{|yyzzzxvwzyywuuvuxvttuuyvvvxwuv…Yxruttz��)Œruvuˆ<rlwtsutrqqqpqtvurss‰=�†qaL?Yqytuv‹? ��spvuyu����iuv|wx‡0�www{||~y€€€{„z}{{}|}€€€€ƒ€€~}€}€€~ƒ}€‚~„„„………†€…~ƒ„…€………‚…„€†„„„†€ƒwxwwvxvwwzvxvvwwq{zwvutuvuvvwwvvuuuvtsttuusrutuusswvusssvvvtrrsrsrrsssrqrpqrrqppqome^XTIG@<93-(% ���� 6MvmikZ) ++LyXa`bQ< ���/doqkkq1 +jfigjqwyxz‚~{xuvyvnnonoooookdwU„tqruuvuvututyutstttuuttuuryvuttrrrrturvsywzvuutrurwuur|svvvvuusqwvxprrqrrutqwqrrutvqyvvrvutrruvqqpruttuvvvwyuwxuvtuwvvwyvvyuyywuutvszyxwsuvywwyxxxzxyx}vvxzxzz{|yxyxzxyywzstuxyvvuyvuxyvvtuuuuyxyuuw`nvusx��‹rvtu{@�owvrquop}z{kouutrrŠB����:Kyuuwv‡Cusuwxv��izv{wz,†wxx|}|~zy{€zz{~~||z}{z~}y{ƒ|€€~}}€z€€|€€€„~„ƒ„…ƒ„€…~~~…‚†ƒ‚ƒ„ƒƒ„‚€‚‚‚vxwxxywwwzuyu||{{{vwvzz{zzvvvuvtvvvxvutssuuuvwuyuvs|rsrutuuwsrqvuvuvvwrrsrqrqsnpplnb@82O€nlkd&' :ŽŽs`QY+*!�%jorhko6Szcjzrsopoooolnoooooooppporoohjastrvuutvuustyutywyuuutrrqsuwtttrrtuuutuwyrrtuurtsuvrrrsuvuupuwsqqprtrrssquqqrrrortrqvutrrsrrrtutu|rtttvwtruwtvvxyywvuvv{wuyuuywzvuvtutuvttwywwvxyzyyzyzvvvuuztwyyzxyyxxyy{xyyxwvvvwzuxvuuuuvuuvuuvu}b�jxuutvz������ ‚pqqrzE `wvtruq\L@9:5MzruorS�C~rrwuE�oyvvvv€ +R|u|y{Š9��…vw{|~{~y||z|€||z{|~~~€~|}~ƒ~€€~}|}~‚y‚}|€‚ƒ„…†‚„…„…ƒƒ‚€€†€ƒ…††‡…~…€„…„€ƒystyvxvxxzwwwz{{u{wzvuvvwyvvvtvvvsxzwvytvtvvwuvyzwsqrtsvxwsqssrrsqssswrqrrsrqsmqqokZ>(-PsomkkA,-*$%-4868898899:A^~qjmphsqglg9�]fkooppolllooqqqljnopnopooooqoin�qrutvpuvuuvvyuuuuuzturvuxsrrrtuqtuuuuruwyvur}rsttsuuvrvuuuututsqvvttwsurrrqqurrrrtsqvutruqrrvotuvqquwtvttrustvutvuzxwuvxyxytusstuuvtwvstvszuuv{vvvvvyxzx~yzuzwvwzxzzzzzyzzzy{vwxvuurtxwwyvvwyvvvuuv„n^vtvtw…qqrqvL�`{uvtr‡*<‚ustr‡P��7ˆrs{wŠKg|vuwww$� +'2Qxv{zy5 +�…zs|}zz|y{|z|€z{~yz}€€|~~~}{}~|{|{|~}}~}}€€€„„ƒƒ„ƒ€€€‚……†……‚‚€€ƒ……yyxxwxtxy||}ywx{zzz{wwwwxywvuvvwutwzuuuvwuswvwwyxwwwuvsvtsrrstssssssswtqrssuqrqqqpi`;� ���1PsqqmfVPRcepwtlozŒB>zsibYr]rrjlg?�Tnnopnnoolkopopmklooomlopmmpqoin�spuupqprtstuxvututttutttrturrrtuqrttuutuwuusssruzutruuuvutvuttsqsuuuvttusrsrqrrrqruusqrrruqpqprusvsqtqswqrsvquywwvuyywuvtxvxssuutvtttswusuuuuvxvwvvyyyxyy|xwwz{yxwwyuxzzyyyzxyuwzuzzzyzxxvyuutwvxuvu{o�Uztvtx€„|quq|N��i{ttuq‘H %…tutuƒP�2…su{w†Lk|utzsA(��&)Ga}v{zy‹< +� €vu{|†||{}|}|~}}{zz|{z~y~}€|€}€€„€€‚~ƒ~€‚‚€…‚„…†„„‚‚ˆ…„ˆ„ƒƒ€€ƒƒ„…y||xoyuwyz|{wxy|vwxxwv{z{{ww{wyvuwyxtuwwyvtuwvyxuxwxwvrvsttstsrqsrsrstvrrrqqrrqqehhi: +#('1499=JgME�1OmooqnmoolrmnnnjczT--?skngbkZzqkkiF�A€pnoopplooppollkjhhkomvojlpopgsbtuvuvpspuuutuqtuysrtutttrqstrtuqsrttvuwuuutturvytuwxwuvuvuyttstrttssttvusoqqrrrrusuuuuvqtqvquvvvvsttwtyqrrvtwvxvuvzyvuvtyvzuqtwuvwvtswvuyzzyyvtwzyyyyyzyz{{zzzyxvvywyyzzyvyxzyyvtwzy{{zxzwurywwuvtvqt�Y†uvux��� +ƒqrtruYUƒqwqqŽ!„qtuvG1‡rvuv|N_zuwvv€@0CAGOVR^elamtvsrolruy}{z‹<}xvz|||{{}}}}{}}€{z|}|y|~}€€|€€€{~€|€€€}„~€€‚€ƒ‚„……„†‚„ƒ†„„„ˆƒƒ…„…ƒ‚„††…ƒxwzzyztz||{yvxy{xyyywvyzz{tvuuwvuxxyszwywvuttvwxvzuvuuuwrttvvvqwssrwsssrrrrqrspqqpga<!,0:<BGNY[U]eqoq|xtjhMA)LqonqqpqopopnmmkjiX>{glfu] S~pkliQ�<upoppos…ƒƒ€~wyslcXI@4Vkmooiznntusrqqotwutsrtustuvuusrstrutrsqrtrrqruututuutvyrtqtuquuutvsssrqsquvtosvqrqrsqqrrtqqurvtrsurroqpsttttuttstrsvuvyvwwyvvutwuvuuqvrttuvwxuuurzywyyyzzvxyzzywwwzzzyxwwwwvwzzwyyzywv{rwzyyxzzyyutvyutsqv{w�a‚vuvu}� +~oqqrv`�Y~qupqˆ!‰qwsw{U���/†qryu|Y�Zvwyvryƒƒ‰„…}ƒ„…~ˆ„Љ…‚ƒyvyzzz‡?rvw{|zz}}}}|}zzz{~}~}~}€~}z€|~~€€€„€€€ƒ‚„€…‚‚………ƒˆ€ƒƒ††‰ƒˆƒ…„ƒ‚„…†……„ƒ„ywwwwwvxwwwx|yx{vxzxwwzwvvuvvtvvvvuwrxxwwxzuwuuyvvssvvvvrrssrussrrsxsrrqrqrqsqqqpliaJ ,<O]Y[cjonusomjpmhrpwspnpomic[7�Jfpmmlmmmjlpvkplld^ 9habbZX!L€nlgo^�:wooooj;9?6/" /7xopnj|lntrqqp|xyvurwttruuwvrurqttuuuotqrtrruruuuuvvvvvvuttttttqqsvuvtrpsputsqrqrsuuuuuqssvossvrtuurqrrnqttrtuyutrtqyuvvwvwy{vutzxvq|pywvptvvvvuurzywwyyyyxvyxzyywyyzxtytzwwyyyxwxzyyuuuuu|vzxyvuywtusuvytyq�Q‰ptuuƒ~oqturj��R}tvqr‰+}qwrv‚^�(+245Dl�#‹qsvuy^Ovvvvuvwzz{zzxwwvvvrruww{zwzzywyŽ?ryw|~}{yyz|}}zyy€~}}||~€{{|z|€}|€€~€ƒ‚€€€€€„…ƒ‚‚€‚‡ˆ‡ƒ‚……„…‡…„‡……ƒvvwwvvxxxwvz{{|vwxwwxwwwwtvwxvwvwwxrxvvvwvssvwxvvvxwvuuvwtsrwvvuvswrsqrrrrprrrrmhi\G + +Lastnovwoppoooopnonmljjlmmllgi?� + HinmmmlnmmllkkollfX8 +X}zjl\Mvoldkd�7knnnkk+����!2ƒnjmkwdvuqrpopttuutrstqttwutuurrrrrrqpqrtrttrvuttuvvuutsurqqtrrrstuuuurrrtpuvsqrqurrtrrrrrrstuutstsprqoqotqrvtussstywvuvtwvvuuszusqqqvvvuvvvsuuvtwzyyuvxxywxxvxvvwyyyyzz{vvxxy{wyyvywvywwuuyyuuvyvtv{vzwuyu�Ouuvu…�qquvor��G}uuqs,†qwstƒcB‹Œ†™‚$!‡qtuv~eP~uuztwuywuwzxwvvuvwvxzzzyyvuwzzw|<vvvxyyy|}~yz}~~xxyyy}}|||€~{€€{€~|~~€}‚~€ƒ€ƒ€€‚€ƒ……„………†‚†ƒ„„„ƒ„…†……†……„…†…|wvwwwvx{y{wxywwwz{xww|xxutuyvwwuuywsrvvvwxxzyxwvvwwwwwwruvvsuwvssssrrsssssqrqmkmihZV +Obcoooooooooppqrrrrrqnmmmmllr`MJjnlmlkfpsvhrkrnlk`NB;9?OE820%Cypohng�1ooooqh5 ++nlol{ +�`vtoprorswqurrruqttyuwuwrur|rtqrrutrtvwvuuqrqwvurvrvtsqvrrqvsstwqtttuvuvusquprrrqvuuustvuttvrqqupsrtutqtuxwzyxxyuvvxvvuwuyyyqvvvuvvwuvquuvtvxzxxyyyxxxxywwwwuzvzyyyyxzzzyxyyyxxyyyxyywvwwyyxxxvwyyvyv€�Buytv|Fƒqqtvox�A†uuqr3 „pvrq‡eUs[pqxo+ˆqsuw{p�@qruuwvwwvvvwvwxvwxvxxvywvyyz~qp:wuwvwyw{}z||||}{zzz|}~{{{z|}€~~€€|…€€€}€€€€€€€‚€ƒ€€ƒ…„‡†…ƒ„…„ƒ…ƒ‡……†‡††„ˆ‡„ƒwx{|xxy{zvvwxxwywzwwwwwwv{vvwvwwsvvysurwvswwxtwuuvwvvwwwvuvvusvvrsrsrouvrrqsrpqrljh`Y� +!CtioopneonpqpomoooopopllmmlooaKCroolk`QJKB3Vfppqqkgflq~uAxqmlki�0rno_kc/���!‚opomz_ttttporouttrrsttrqttuvvrrrrsttrrrqqtrrrrrtrptuutrqtvsqqrrssruuuorquuurttsqqrrtrqtotstsrsqttuqtuqrruvutuvvtqqpquuumwwvtuuwuusuvuvwuwvvrrrvuvxxswtyzxxxoyvuuvvzyvvzxyz{{yzzzzzyyyywyvwwzzyuuxyyuvvywvv†@†tttwI ��8rorvqoz>„tuuq3�…orrq„qIyhrrv~3Šrrxvwu�<vjuvvxyyy|wuturqrsr{t†}|{‚w{z‚mly7lys{w}w|{z|||€~€{z|~~~|{|~~~€€}}}€€€€€€€}€€€€€„„€€ƒ……‚„„„‚„†………†…‰…„†‡†††„„…|xuvy{{{{yuvwxwwxwuv|w|vuxvw{wwwvwwwuuswwsuuzxxsuvwwtuuvvuwxwssrrrrrrswwrrrqrqrqlljaV + +!Appppoooommljhhhporqkstwtlnll`[�@qomlmL?/ 2;blnoopqpkfew%�B|qmor^ .llonqc.����-ypmom|!Mwrlkooqtwssqrrtqupuutuur|tssstuqqovrrpuqrrutttuturutsqqqrtwqvvuo|ourtqsqsqsrurssutrqtttsusstrtvtrsvuvvuutswququuvvxwwuvuyutrutwwzrwvxxywwwyyyyyuzyxyz|xvxvyz{xvy{wzz{zwwywz{{yzwxvyww{zyyyxxyxxxyxyuˆ>Štvuvn7&/8:>BCPXpvw~…„‚…yprvrqtz�;}srvr‰7�ƒqnvs€sC|ruuy’4rqwww~�7kg|ynqvpvy|w~‚‡€{nocX_cNF@<93,& rxr{yzw|z{~}{|~}~~~~|z}~~~~|~}~~ƒ€|~|ƒ€€€€€€~ƒƒ…„…†„„ƒ‚ƒ‡‡…‰‡…††„…ƒ„…††ƒ€|zxxw{{|zyywvwwvwwvwuuwxuzwz{zxvuvvwwxwywuvwwyvtrrwwusuvuuvvtsrvrvsqrwuvrvqvuuqrlmibS +!@pmooojfdolsqrigQEDC<:43VqmnlbX;ummlgQ1$5pllpprrpmkin0"Bqrmohi ++eqnnlr3����€pmmo‚.�Bzsmppostuspprqsovtupruuutsttttrvrpqqrturqrusutvvttuussqrrtusssspurrtrtqqqqrturuvuuqoutrqtustrtuutxvvvvvvstrsurutxwuvwwwuuutttsvyvtsrwwyxyovtrtutywxyyzwuuuvyxywwzv{{{yxywvzwvvvvwyyzvywtzyyyyxyvutyt† ?‰twuwpy„‚Š‹‰†Œ„†ytqqqtuurquvtqqt|�9qtvrŽ8��‚unqv}}:rrvy9‹xrwwt~��>|mksd^ROH=:92* ��b{sxz}|~|~}|{z|z|~}}~~~~~||€€~|€€~~‚ƒ‚€€€‚€ƒƒ„„…„„…„…„‰ˆ‰ˆ†‰ˆ…ˆ…††…„‡ˆƒƒ‰|zxwwuvwwuzzxvwx{zyvwuwxuvwxuzwvvvuuwwwxwvvwxxxutuxxysvvqsrstrrrrrursrrrrqquqrpqlmjcX +<nmnmg`K=5%&%1DqmllbV�:xmomgo',Xoorqppqqqkk9�>6mponic*^uonlk9��zxlmmƒ2A}qppqoustsqqpotpvtwuysvruqvvttrqtrtqqsvuwuttrpvvyvqrsstqwrvqrrtpuuwwvtvqvqttvqwvurqovqqqsqqqrquutsuwtuvusrtsuruuxxxxyyxqsuwrtqvvvuvwwuxxxxyrzyywxwyxyzzvvvyyy{zx{z|{}yxxxw{{}||w{|yxwwyyz{{xxxyywsyuƒ8‰uxxvtwvuttsuuuuuuuuutuutuwttqpt~�4quvs‹;��}qpqvx8…srvv9ƒruwwr†� +awvx|z||~}|{zz~~~|}{{}}}~€}~€€~|€}}}€€€€|z|~€ƒ€€ƒ„„ƒ………†Š‰ˆ…ˆ„…‡……†……‡‰ˆ…†wywxtwwwuuuwwvvxvvyuvuvwvwwwryxzrutvvvvzwvvvvyxvuwyoxsuvuurwrurvrqussrqrrrruqsqqjokfW"�:qlnjbO3'@ummld\ �8woqkfs�Q…gkloppppmeA��;moopla�"Q}nolr<�����msmll|5�9€rqoqoptpptqopqrppvwuqvqpposrrrtqsqsqssstvuvptvvvwrrrsqqsrrtrqqqqrqpqttqutttsrqqrttqqqqurrrqqrwrqvsvrvvussstusqusuvvwtrsuuuusqutvvvvuuvvwwyuvvvwvwvyyzzxxzy{yzyyxyyzyywyvx{uvyzzwvwxvvxxuuvuzvuxvuwt‰)7Štvqtyuxvyyyvvuttqrtuvuttuvutttsl4‡otur‹=�~uvvxs€6…srvvŠ=‚rsvvt� +Pzwy{}||{|||||{}~~~|{}}}~~~€~~€€ƒ€€€€€€ƒƒ€…}~€‚„ƒ„†ˆˆ‰ˆ‰‰†……ˆ†ˆ„†ƒ…ˆ„„ˆxwwwqwywwuvvwvuwvt{uutyv{vwvtuvuvuuvwvuuwwwwswxxwxzyxrrtwsrwsssrssuurqrqvwwsqrrqlkkdX�9imohaH&<nqnldb6brrlgo�!LwqnqlpppkgU5?F@BDQP[hiktpomok"�LxokloI�gnnly: 7ƒornsoopqtuuupttttttuuutsssoqrstqqwrsrqppooqtsvvsvqorptttqtqqprqsqrqrrtpvtturqqqttwsuttswrqsvrvuqqrvqvprrturvurruuwvuuuvuuuuvtwqwuvtyuvvvuyyxuvvvvvvyzzv{yyzz{}wvvzwvww{{{zvxuttsvy{wzwywzu{yyvuuwuŠ2twvttuttvvvvuttvtvuvvustttqpmh|b0Šowrsz?vquuvry3„urvw|A…ruvvr„ ""Lzxx|}}|{{|{}}z~~~}~|}{|~}~~~~~~~~~}|z{|€€‚‚‚€€‚‚~„ƒ€ƒ…‡‡ˆ‡‰Šˆ‡……†……„…ˆˆ‡……wwwysxvvvz{|vwuwvtzvuvuvvvwxtwwwvvswssvuttsusuwvwyvwwsvwwsrvvwsvruwwwurrrrqqqsqqpoke^'�7lmqhkE:mmmle_�0krtmjl&SVCCB@`oqoponqtqrwyw~{xqqpppjko,��IqjmiU���ixponvC4}ormrtpqrrrtrrrtuptquvvsvtttttrqrqwpqqqqrtqttutuuustrqtvtrturqrvusqsrttrtuuvutuuttttuvvqtttvutsuurrxuutruutquvuvvvvvuuuwvrvwwtstttqtuwvvvuuvuuvvuustzywvyyxyzzyzxxyvwywvyyzvvyy{zyxxwyxxzwvuqrwtuxwu‡01Œotvwwvvuuuurrrtqqptsru}ˆ‡‹ˆŠ›Z$ŒppquzAvpuuxt2„uttwzE~rutuu€?Puvyyx||yyy{|||~z|}{~|€~~~~€|{€|€€‚€€€~~Š‚„‚ƒ‚ƒ……‹††‡‡‡‡†…†‰‰‰…І††‰yvzyuwwwyxxwuvuwxr{uvvuvvusvuxyvvvvwvsxusuuttruuvvvwstvuvtsssuswsvwwwvwvvsqqusqpppkca,9grmhpE9msmme`-ourokm)#;Upppppmlnqqqqqoqppqtqrnlg5Hploj[���]nmnxL0ƒorrppqqpqtqrrqtvosptpvtwqrtssqqtqwqqpvpqtsqqqssvuurvutuvqutvtquvsrsuutrsuuuvtttwwwwxvwuuuwvutuvurqvxvqruuuttvvuvtssuuuuxxwvwvwsvuvutuwxxuwwzvvvvwyu{xyxxwyx{{zzywyv}|zyyyyvvxz|zvzw{zzwzxzzwxwvwxxuˆ2,™ppwtwrv~}|‡…„„€wulijOA;92$pupvvCvpuswy1}xrsvz@~suuvv,�$! &238:=>ACP[fvvyzy|{z{y|}~~~~~~~€z{~}~{|€~}|y~€€€€€~€~€€‚…„ƒƒ†Š‡‰‰‰Šˆ‡†……‡†‡†……†…wwxxwwvwwzuvvwutstyutzuvvvsuuzwxrsswursvtvtxuxvvvvvwsvwxvwvuuwsvrsrtsquuvvprrrppopjcb-�4[rlirL1lpmlh`,hqslkf."Kppopoqprqqqpqrpprtrqrpli7D~pplig��_xmmlsZ-zopqrtrpotqqrqptvoporstpprqsqvrrrpqpqoqsqtrrrrtstutrwytrvuurstrstttttuvxuurqtuttwwvvuuuyxuvyuuttuppvtvutvwtruvvttuuvuuwrwwvvvuwrywwwvwwyuuvxyutuwywwxtwwyuzzyyyzwwwvyvyyzyyzvxwywyyxxyyyyxwxvzwwyyxx…4%€šˆpf`cIA<82/†pqqt|Lvsurvt|��&„lsutvE[~vyvvu@�.-79;@DJVP\`lqtzy‚‰‹Œ‘Œy{z||{||||x}|~~~~}z{}~~|{}}~{€~}}€ƒ}}||}€€‚}~ˆ†€€‚‚‡‰Š‹‹ˆˆ‰…ˆ†…††ˆ……‡‰‰‡wvyywwvwwvuwwvvsvtzuuzvvvuvvuwyxrtttvsrswutuwwwvvwssrrsvxvwwwvvtsrssrqsuuuqpqqpokkkci03ZvlkjU��7lymmhc/^mrlig3 +Orkoooplpsqlqqqqooppqponk:�B|pplfj���T|ljlsW,xoqqrpqoopqrtsqqvppqtprrrqpppquurpvprrurrtvvvrttqqssyutqwutrrsuuvvwwuuvvuuuuuuwvuwwvuuwwywwxutwvwpqtruwsrvttuurvyyzvuuzwwvuuwuuwxvvuvvvyvuwxvuzwwvwwyyxtxxz|{wvxxzzxzvyzvuuxwx{yyzyvyxywywwwwwvvyyyuŠ5ˆqqruuS�ksurtr#‚fmts‰N:(TvwywyoS?6(6;>BJNKVcfhtvz€{syqx~€~{†p|zuzwvwwwwvwxvxz|}x{}{||}|y}|~~~~~}~~}~~~~~}}~|z~€~||{€€}{}~ƒ‚ƒ~ƒ……ˆ‰ˆˆ‡‰ˆ„‡‡‰ˆ†…†ˆ‰‰†…wvyyvwwxwvvzwtuuuutttztvuuvvuuwyruvvvwruuttvwwvuxwrwsssxywvtssrrrtrrqsrrrsqrqqqpoplee30WymjjR&��.esmpid%dqrlkg3�`pbipoplpqpppozy|suorqqmn=�<zqpjgo��PynknuY�+mtrqptqumqqqputqqqqpurqrqqqtqqtusqpqrrpruttutsututtuvtsrvuutrtuqppuuvvvwutuuvvuuurrrsuvvwyxxtuttvqqqquuqrvuttttuwvuuuttuvvuuwvutuyvswyuyuwuwwwwyuuvyyuvuxyytvzvwyzyyvvyyvvvzyzyzwyyxxxwwwxywvxvzyyxvˆ9„rstuvU��qrurrq‚E +n…ƒs}ƒA!-Aevwxxxwy{††Š‰†ˆ„€„~€}‡y{{{{{€vxyzyzz{z{zzuyz{{|xwx{w}{|||z|{|}w~~|}|€|~}~~}€}}|~|‚z~||ƒ€€~|}~ƒ|~‚…}„~€€„ƒƒ‚…ƒ…‡ˆŠ‡‰ˆ‡ˆˆˆŠ„‰ƒŠˆŠ†…vvvuvvwxwwwvwtuuvuuutttvyuyz{wswwtxwssuvvsvsuvyuxvrtturuwtssvrrrrrvrsuwqsrwrrqqooplfb5/Uymje_&���,[rpphf(cpookn2���>Piumwslooplk]SQLKSboqppgH9uwokgr��C}lkor_�jtrqqprqprrpsqttspsousvvuqsrqotuvrsssrvtwsvvttvtvvqtytqsvuuturtuvrrsttwwurvuyyywwtqqwuuuuuvtuuvtuqyvuttssrsuyxyxxvwvutrwwwwwvtyuuyupwyvuuuuvywvvyyzyxvwxyyztvxwyzxwzyvyxvwyzyxx{vwzyyyyxwyzyyxw|yvxx„:O}pvuvv_��eztqqqr8)??BFTB8/1:¡šŠ†…†yvwwwuxxtrvwxwy{xwzzzyyyywwusvvwwxyzzxwuuvwwyzz{wwyxwxw{{zxxx{x}y~xx{{z}|}~~~~z~~€}~€~~{}~||~~}€€~€€~„€~~„ƒ‚„„ŠŠŠˆˆ‰‡‡ˆ‡†„ˆ„ˆ‰‰…†wwtvvvwxwxvzwvuvxxvwwyuxwvwxxxwxvswywuvxvsvwtsrtvuvxvttutsrvvsrurqrurvusqrqrqrpqmpkhc8"Tvqgc]-�������.kmrohi/&]ompko8���B\SDA >lpprmf+5 +UwppoqK 4xppmju�C}nnorc�bxqpoorpooqqqruwrrttuttqrtsqrvquvsrqttutuvtquwttuwwwvutvwvsrstssturrrrvwurqquxyvwwtruvqrruvqvvutrrtvvvrrprrurrvvustvuwuyutvvwuyvwyuqvwvwuvywwxvwzvvwvvwwwxvvvxwzzzyzyvywyyxzxyy{vzzzyzzzyzwwvyz{wzxy€> „puuwxe��cwvvwqp{‹“†ƒ„ '?™epwvwvxvwwxwxxyvyyzwwvuw{yywwvxyzwvuvvtsrvvvzz{zzywvwzyz{y}{|{{{x{z|~~w~}|}|~|~~|}|~~|~€€}„€|€„}„……‚€„„‰‰‰‰ˆ‰‰‡‹‡ˆ‰ŠˆŠ‰‰‰ˆxutuwwvvuvutvwuwwvuvzwuvvuvvyyutuuwxywwuuuwxsrtswvwwwyxwttruvrwsrqrrrqsqqrqrwqnqlpmgc8Qyqjec.�������$amqqhk/Qtpmjq9���$7/lpkqki4 +6L}pppjW5{pooit Aƒnlmob\{rqqrqppkqptrroqtsstttortsstrrturrpvswutvvtqvutuwpqttuwwuuuutvsquutrruuuturpwzuttwtuuurrrwwuvuuvuutwvuuwusrrrttuvquwypruvvuwtzvuuvywuwzuuzwwzvuzzyyyuwvyzwwvwz|yyzzywxwz|zvyyzyyzz{yyyxvvwvvxyywzyz‰D ���� +.39:;@IIZSQm‡quuwpm�^zuvussvvvtnzx*8mszwvvyyzvvwvzywutrrrrrvutwyyyzuvxxvuvvwuvuurtuvywyskutr€|iovw|||y{~}~|x}~}}}||~~z}~~}~~~~{}~{|~€€}€~€‚}}‚‚‚†‡ˆˆˆ‰ˆŠŠ‡‰†Š‡‡‰Š‰‰ˆˆwyvwvwvvx{w{vuvwwxwxxxwvuuuvwyvsryuuyxvrvwvwvwurvwvwwwxxuuvsusvrqsrssrqqqrpqsupplomj_B�Qopied1����glqohg2Ttqnkq?�7lomrkl1K}pqokY+soqpkz�C{ooojrQ…qrrrqqqpqosrqrputuutrruvrruvutsusrqsvvvvustutvvwqutxuustuvwtqruvwwuuvquuuutwywtqutuqtuqruuuvuwwwurvwwwwxsrrssutupsuyuvuwuuuuyuvxysvwvvuuyyyywzxoyvyuvuyywzvwyzy|yyyzyx{wvuyyz{yxw{y{yxvzyyvxyzz{zz‚Q, 038?DQSckzxx~…‰‹ˆŠ‰ŽŒ„†‚yutuuw{v��]ysuqsvvuurqu 5ruwxxxxxxvxwwvvyzuwvuuuuvvyzzz{wu{†‡Š„Œ‰Œƒ„xwzrxmjg_OMA?AGO{ty{||z{}|||{|||}||{|z}}€~~~|€ƒ|{€„|}€€€€ƒƒ‚„€ƒ‚„…‡†Š‰Š‰ŒŠ‰Š‰Š‰ˆˆ‰‡„‰†z|tvwxvvvzwywwwwsywyuzvvxuuuwwyurrrs{wxwywvvvvwwwxsstuvvxwuuvrurwtssqrwrrppqpprplpmg]@�Qtpiee2��alqmgv3XtpmjtH�7copllf+�Hqpomb�"msnml{ 7 #4;HXvpppjuWzpqrqqpppqqqqqpruquvsrrtqrsvvrtsstqrsvssqsqutuuvvtvuuvtturvwrrtvvswxvwrutrsuvru}quvwouurtutuvuxwuqpwvvvwurqvsrxupqquyuuuuwuywvvrxxvutruvtvwyvwxwuzxzzzyywvwxw{zyyyyxvvy{|{v{zyzzwyxy{zvvzyyvxyyxx{yy\49:>NVbeyw€€ƒ‚…‡…ˆ†…†ƒƒqqqqppnpqtqtuvvutsssswuz��\zovputrtutqp„9‹uxwvwxz{xwwy||‚…y{}€~‰€wvzyzz|cTMG?;72&."'$.8…rw|}~~||}~}||x}{}{|{~~}z}z}~|~y~~~~~}|€~||}}~€€€€€€…„‡ˆŠ‰‰‰‰ˆ‡ˆˆ‡‹ˆ‡„ˆ‰„‰…z}uuuwuuvyvvw{vwrywxuyuyvvuyvuwytustzzzxyvuvusrtvyswsttuuwwwuvuvwwrtqruqqqrsqppqqqmj^PPspgfe2��Rornin5LsplkoK9hnokln0Bqqojj�trnplu6;<DDLXimtpx{~|kjlooojs$ J~rqrssqptrpqrrqqvrrssrtrrruvvutrrqvuuwsqrrrssrsuusuuuuttuuutrruuvtvvrpqusqruvrrrrsrrqquruuvuxsuuuqqtxwrvrrrsuuutqsvsxtvwwutwwwzrvvvtuuvuuwywxyzswwnywyywvxvxwvwyyyzyvw|yyvu{{zzwwwywwzvvzy{vxwwvxyzwu‹‡„Љ‘‚…|ytpqxyƒvvvuuuuuuvttuuuuvvprrttuussupqqtqvldkkqrqrqruypj +@}uwvvywwvxwwwgfpcYLMA<32�RovyzzS -$„rz|}~€~~}}}~y~|}y}|}~y~z€€~€y}}{|z€}ƒ}€ƒ€€ƒ„€„ƒ„…†ˆŒ‰ˆˆŒˆŒ‰‰ˆ‹ˆ‰„…‰‡…ˆ{{uwtsttwz{|z{vwsrtswxutttuvutxxutsvyxswyxywrrsswtttsssrsuwvrwqvwvssrrsprtsrrrmmmmojiR#Orpjgc4 Qsplhp9�Ivpmiu?��1-toqlou6?poohp�mlopoojjmomyumjkklmnnppquulrmkz.G|tpururprrptqtsrtvtrqvsutsuuuvrrqruvuwwwvwvsrqqqqruwuuutuutrrutuuyturunurrqrussuqutqpppqtuuuxstutqqqtwuuruvwuusstuutuuuvvuswvyusuuuuuuuxuvtuvvvrxwwzwywxyzyywyyywwvvwvy{zxxyz{{}wwwzyyuy{yzvwzu€v{yxvywxvwutssuxuutrtutuvuuuuywutttutttrrurqruttqqqqppplfkA`hstsuutxu|•¬<-:Vtvwwwwwwwvvw€UAutwwyˆP* †r{|z{xw}~|yyxy}~x{z|~}|zz~zyzzzz}{|€€€€~€€{{|€‚€‚…‰Œ‹ˆ‡Œ‰Š‹Šˆ…‰Œˆ‡‰‰‹Šzzwwsyu{uyz{xwxwwwwwwzutwyxwwwwxwytvvxrwxruxvvtrwvvtrrrstsvvrwuvvwwwrsqqqqstprnpmrojeR�#Olphhi;*Rrokhk;�IupoktG(�%/9*7foqpos88zqpoju���llpooqqppnopqpomnoppqqqrrpooog{/@~tqpqqtttrqrruvssuutrqrrtuxvqqrrvuutuwvsstuurrsuwvuvvtvuuvwuuvxwuurqqqqprrsrusrqruutrrqsuvyuxuwtrsvrrqprsuvurqrrutuuuuuyxvuvvuuvuvxvuvvttvsvuvxwxuutuxvyzywwwzyzwvvvwvyxyvxy{zzyyywyyyuyyy{wxzwwyvxxyyywuuuuvvyyzxyvuvuvztutuvuuyvutuuurqlemuuvrrrrrx{†mcj�;og`m_XCD:80eˆƒˆŒ}swurxwvwwwwy†K/,}swvwƒQ��������#‡r{||{|{|}zwvw|}}|~~~~z~|~€}{|€€€€ƒ„……€€‚ƒ‡‰Œ‰‹‹ŠŒ‹Š‰‰ŠŒ‹ˆ‰Š‹Šz||utytzwvvvwwyzzwmwwwtwxtsv{xwxy|tvywswwxtsvwvrwwurxtsuvtsvsxuvvwrssqrqurqqppqonqpjdN�&Lpmiid@��&<MrlmimA��D|mqlzZ92@F^A +rjov{rooqqofB7yqpqkw +�itopnqopolmklomlnnokmoopppmmoew3>tuturtutrrqrtvutqtsuqurtuxwyqwtutttutwvuuvvyrutuwqqruuwuvwuxuvvwttuxvwvwuutwvstsruuuqvvuuwwwyvwurvtrrssruvvsrrqquuuuuuuyyyuwtruvvvvwwywuuwwvuuuywwwwqzzzxyzzw{zzvwwxvwxvyvvxy{zyw}wyyxuzw{|zy{|zwxv{yyyzyyxwvvrqtyxwuyxyytvzyuwuqqqomnqyw‡‡€ntuorupe`n`ZQ;gsqzuqrrxvwyw{xzwwwŽDsxxxs†U +���� Awv|}}w{|yx{|z}|}||{~~}}~~~}~€€~|}€€~}|€€ƒ„„‚€€~~„ƒ‡‰‡ˆ‹ŠŠŠ‰ŒŒŠ‡‰ˆ‹Œ‰z{vvv{txvuxzwwvwxwxzvvvywuvxzzz|xxyyxvsyxxuuvxvswvwttuuvuxswruuvvxpsvuqquqqvssrrmnpjdF�LpnhimJ1 + ���,PsllhtR�Awmokpku…vt}["p^gprrppqrlkE�.vpppkq� +kfmnpnkkjllknnnoonnooknkjkkiiŒ2:€sprurrrrrtruusqrrqtttuuuvwvqyttwrsuuwwvuvwuqvwwwrusuvvvwvwvvuuutvvxiunvuuuuuuwuutuuvuuuqyuvxvwurursrruuxvwuuvywwvwvvuvxvuuwuvwwwutuwyyuyyywwuuvvwvvszvvvyyywwzzzwxy|zwwzvvvxyyxw|yxxvuyyzyy{zyvuyyxxxwvvwyuvvvwwvrwuwyvvszyyy|Šƒ†‚wqj]KC@G]qutvuyJ)xoquvwwuwvwuvzxwyvxŽBzpmha*�����Uvx}||zxwxxyz{|}~{{~~~}||~~€}}}~€ƒ~Œ|€~„ƒ€€~„„„…‡†‰‰‰‹‹Š‹‰Šˆ‹‰ˆŠ‹‹‹wzxww{vuuuzyzuuuwvzzvvwxuvyywyuwwwwxxwsrruvvwwwsvvvvttuuvwrrrrrvvxruvvutvrqquqqqmpqkaN�#LonhmrQ9##026;?CG]Z[TUcilmklhtM>xlppqqpomi`a)�XokpoopoqroiZ�'upmqlt(l`hooomkkljjiiimlqtw~…‡†ƒ€uujd?,�6trrsqrvsustuwwwuutvvvsutxtvqzuuurrsuwuwwwvuqzwwvtuwttuutuuuuxuutuuyyywwvuuvrruwtstyvrrvwzuywwyzsvwwryxvxvvwvvvvwvtruuuyvuwwwxvyyvuvwuxuyywwwywwwvuuvyxwxyyzwwwzzwyxz{zyxywvxwy{{{vyxxtzz{zxxzz|zzzyxƒv‚{}„‡‰…‰oyvyxxvtzyxy}-OwsuvvA�wuwrruvrvvxxvxwvvwwŽ9.���^{‚†’P(#��€sdeypvz|wz}{wz}yy}|}€~||~}|~}}||}€€|€{{~}€}~€ƒ€‚€€€…‚}~ƒˆ‡‡ˆ‰ŒŒ‘’Ž‹Žˆ‹‹ŠŒŒŒvxu{w~w{uvyyzvvzwww|wyyzuyyyvxywuwwwxxsuuvtxwwvtrsvvvvvuwwwvvuuvuwrxuqsvrtuuqqppmplmd\ Shnhmi]]qhrspvxqsurwxtqommllktD�<xlprrrqqolae*�&Qyenolpppuofa�|ooqmv*coitƒ‚‡‚€}}wypif^XU?730*}trqsqtrtrrqtuuvuuuuvuuutqswrvutrqruvvwyyvvvuwrvusvwwxztwuwwvvvvttwvvuuvsrvvsuwvrsuuurqwwuuvxvyurvvtqvwwxvwwwuvwwsuuuuuwxuwyywvuuuwuuwxyywyvzywwwuzttynvvyyyxwwwwv{xyyyywxvvxw{zzwxxyyuywvvvvzyyzyyznf__\GB:7('Qqyvvxxxxyxuw†G >Juuyvˆ=nqvsyvvvvvwvvwxvwyy~F- ��'*<AYYXRKAEUjlzƒ~N��€‡‰‚{|||z{|~|{|~~~}|}}|€|~}€€~}~~…~€„€€‚€€€€„‚‡…†ˆ‹ŽŽ’“ˆŽ‹Œ‹‹‰Œvwwwwvxzwvyx{wuuuruyvwy{vtvyvyyvuwwwwxuuwwssuwwxwwxxvwwuwwrqsvrsvvrsvqssvsvrppppppkmea EekjiikijlllkllmmpnmlmlllmlljtA;xnooqqqqplfu0!)4R{Zipppoqrnfd�!qpqqlx- ?laC740-'����(|xqqsrqprurqoqqurrttruttrupwqvsuruquuvvqpqvxuwvusvuxvuuvvwtuuvuwtttuuyturrturvuwwvuruvuuwvuvwwwupwvuuuuutvvwwwwvupsuuwwwxxxxywuvvvvxxwwxyywwuuwwvuvvwyvyxyzzyywyyxyy{zzzvyvywy|xxyxxxzvwvyvzzyy{zzwxv=vwvuvyztwyvxŠ?# G‚uuyvx69WtusvvuvwvvwvvvwzswydRNRbw|y‡w|u];6`ptplmdh‡guwvwwwy{{{|{{}||{|€}|{|}}}~{}}|{||~}|~~€~€ƒ€‚„€|}{€‚€€…€„…‡ŠŠŽ‹‹Š‹ˆŒ‰‹Œvvxxxzzzxwyxvvw€v}ryuvxxz|{|vywwwxuuxxvxwwxxwwwvwxurvrxvuwrutvrqvvrrqqrrrurpqqqpppmme_ Diijtihilloqrqsqpornmlllnmlkg]^ ;hpnmmqmlhf€0A‹Žsuhlqunlg�tpqplt4�������{rqosrrppqrqqpvussusurwtrrquptrwuuqstturutuwwwvuusqvvvwuuuuuuvuuuuuttuuuusrrruuwwuvwwvvtquuuvwyurquuuuuuvtwvvuuuuuuvvurruvxxxvvuyuvvwxywvyuuurywwwwyyywwrwywyyz{|xxyzwyzuxxyyx{ywxyyyywuvyuxxvxyyxwxŒY$‚uwvwwyyywvtŒ7A…uuzuo1*��:(R{vwxvvvwwywwvvu„swuy„Œ‡„‚zz|vrm€ 1wjwxpx[`ƒrz{{|{xw||{{{~{{|}y€|||~||}~€||€~}}~~~~~~~ƒƒ‚{€€~~€‚€€€ƒ‡„€€…ˆˆ‰ŒŽ‘‹‰Œ‹’uvutvxzy{{ywwww{ttuvtvxwz{wwvxyztywxwwwwwxuxwxvwuwsswvzuswruuuvuvssrrqvqpvqqsqnpqpmke[!�>ngklklmlmjhjlllkjmllkgghhhh`S`:aoqmlj`_hYVB;8?CRD<5.+10Bqotoko�ixrplv8 {rtrqtqqprrrqqrtutuwvrrstututtrwvuqwvurrtuuwvwxxvrrutwvuuutuvxuuutttuwvuuqrqsvvuupuwvuuusuuuuxvwquvvuvsrvvvwvuuusvvwvvrztwvuwvtuwutuuvyvtzxvtrrutxxyxyvwwvvxuu{uyxvwzwyzvwvyyzyxvyzxyyzwxxvxxw{zxyyu‹ +tvwvvvyyxxoˆ>�;‰tuxvnB."�5]pr{xqvvwwwwwwwyvvvwrttsuvvvvwxzxxwvur‡&‡nxxuz\�'[ƒwz|{z{{|}||{z~~|y|r}||}}}}|}~|€~|~~}}~|~„€€z€€€~|€‚„‚€€†††‰ˆˆˆˆŒ‘ŒŒŽŽŽ‹‰‹‹Œ‰Šuwuuuvxwy{zxvyv{rytvwxxwxzxwwwwxwwvxwvvxxxwwwwwwvwtvvuvwwwqvuyuurrrvsqvrpvqvqqpqpqljfa:Y`glmollkknmiqmlttnqu|sw}qstZ�7\tqljdB5S†ƒu„‡A$<!pntmkl�ewtolw;G}rrrrrqqrqqrsrqsruuvxuqrvrqurtsqpttwvurruuvuwvvwwrxtqqruvwvuxvvtuttuvuruuuvuvvwtuvuustuvuwxxvvvvruvvqqruwwvvvuuuuuvvxuuyuvwuxuutvuutuuxuuvwuuusprruvwvvvxwwwwy{wyxtw{vwwxywvwzwv{yzvywzyzvvyzz{vxxyu‰3yŽquwvuttqq†‘I +�8‹tsvvn`W[u€”y GŽŠruwwwvvxwwyvywyvvvvvxtŒrxwywwvyzxxvyƒz.Šmrupsi +�?Xvw{z|{{z|{|{|z~{{y}|}|||}|~}}~~~‚€~~~~~~~ƒ€€€„ƒ†~‚}„€…„†„ƒ†‡Š‘‹ŽŽŽŽŠ“utttuuyvwx{wuvvyrrttvvxvuyvxwwwwxxxzuuuxuwzxxwssrrstvvsxnwrqqvvurrruvvvqrqrrqpppqqikgZ$-OWjhnmnhjhjgd]ab]L\UD;811.2^sqmjc?&RxffdqS/xptokt�Oqlky;�� .jrqtssqsqqhutusrtuvwvwsquuquotqqqrswvutwtuvuvwvwvtuqsvwuxwwvuuuuuwtwvurruuuvuxvusuuvsuuvuwyutuuvvvvvrvuuuuuuuvvtxquwvuuwyusuuuvvwwuutuuvwqwutxxywvvvvvvyzwwxvz{zvxtzyvwwwzwquzvxxyxvvvwwwwvyyzyxvxywŠ^”‰„‹‡ˆ‰‡xhcW�/‘rywzx€}wqnm“"�?ˆpuwyzwvwwy{{wwwvwxwyvxssozvvwvvuuywuspv&��”knqkap 08>Uyv{{}||{{||{z{|~|{z||||}}~}||~~~ƒƒ‚€ƒ}|||~}ƒ|€€‚€…€€}€ƒ€ƒ€„ƒ†ƒ„‡‰Ž‘ŽŒŽŽŽŽ‘‘ŒŠŠuvvvuwwwvwwwwxxxwvuzvvvvswxwwsquuvwxwwwxrwwwxvwwuvsuwyrwvxqsuvvwsrqquvqpmpqprqoupqjlg_'� +$[bXYNJB931*����1Vvpmji9 + HsjlhmQ6soumlq&�I}qmmrE(������ ,148:BQS^_ov|z}klrqqtrqtrqnusrqqttqppturutqqqrppqtuuuuuutuvuwvxvvuttvvquxuuvvtuvuttuvuurvuuutvwuvvvusuuutuxuuvvtttuusuxuytuvvvvuvuuvvvvvywwvuuvuwvuuvvvvywxuuvvuuuuwvvwyzwxvwwyyuutyzxvvxwwwywvww{zuxvvvwvxwyz{yzyzv‡�"(358<>>>74,) +3ŽtyzzwvyzztoŒ.:‡uvwzz|{{{{xvvyz{v|{|vww{{zvuwz}€……‹sc"�z“†ˆƒqA�^ofj{yztv|||}{{|||~{~~}{z}{||~}}|{{}~ƒ‚‚~}~ƒ|~€}~„}ƒ€ƒ„…€}ƒƒ„ƒ€„€†††…‹Š‘ŽŽŽŽ“ŽŽˆwuxxwvuuuvzyyvsxyvssvuzwssswxxxwurvwwxxxsvuvwvrqrrtuuusyswrrsupvvvvrruoptqsqqpolpqklh],� �.Snplj`8�IynopeY%>aoqoks(�L{pilnU2!.038?KWTebhoxv~||ƒ|}lwuuurnplmloqqqqpqtrqtpqozqqpqqrqqorrtstutqrpqrqqqqrtruurruqquwwuuuuuuuvvuurvuuutvvwvqtqusuuuutwuutuuvtvvuuvvvtqsutmxvvvvwvruuuwvtwuuvuyzvuwurvutuuuxvvvxzuwvuuvwwtuvxxwzwxwvuvwtz{zwzxzzzynvzw||wwzwwzzzyw}{y{zxw_`_x…§����)uywxvvwyuuuŠ67Švv{xwwuvvvvssw^ww{y{vwvyst{t|rzrkec`U^n�� ��Kbfzuvy{|u{||{}{}}~|}~|{{z||{{|}{|}}||‚~~~ƒƒ}€~~€~€€ƒ€‚€‚„†ƒ†„Šˆ’ŽŽŽ”ŽŽŒŽŒ‘vvwxuttuuuzzvuuyvvvuvxvwwwvwwyzyswwvwvvwxvvwytsustsuvvryqvrvrvuvwwvvvusrrprtpmmlmqlmgY0�3Oroljk:�C}opnjR8 5xlqljl.�JzqpombZhkqvyz€‚ƒ…ƒvxtqnkjkkklommoonooolllnlnlkoqqrrrtsqqrqtnqrptrppqpppputtqsrqtuuuutprurwwvvvuruywuvuuvsuvuuuuvuvvuutwtuvttsvwuuuuuuuuuvuuuwwvvwvssvyuuvwwwwuvuuuttvxuwytwuurrruuuuttuxvvvuuwuvvuuutuuvxyyzwyvxvvvwzwxxyyywyzyzyyxyyyyzwzzzywvww{zyw‚>œ’pY$$…svvwyuuwz{xŠ?$ +6‡wxvvwˆ‡—’Іuwvvx{yyvwyyrdH?( +�����<tnvz|~{zv{||{||~||{}{||‹|{}|}~~||||ƒ|~~€€~€ƒ€€€€‚…~€€~„…~€‚‚„„…„†„ˆˆ–ŽŽŽŽ”‹ŽŽ‹’vuwxttttvvzwutwy{uruxxuv{xvwxxsssxxxsvwxxvvwxvstwvwvvvsqrussvuuutrtuottqrqppqppmqpmkhW7�� +)8IiojkgF?{ooosYACCEHGxjlmhq2�Byppqoijlmmomopollmompqrrqorppppqrqqqqlrlnntqponlorptuqspqquorrqrupqppouvutypxrruutuvyxuurqqsuuyyyxxvvuuuuvvuwwxxvuvuuuwuuuuuuuxxywwuuquuvvuuwvuuwvvvvvvuuwxwywvvuvwvywvy{{wv{zxxxuuuuuvvwwvuvwzwyvxtwwwwywyzyywxywy{zyxxxvzvz|{{wxyy}y}zywyw~{{ww{zyxˆ�&‘rzyq'�:‡ptvutuuvvyxC6‡vvuu~a:+/7AOfvvvvv|xxxzz}W <ruyzzzzv{|{|{|{{{}{z|}||||||}}|€€}}|}}~}~~~~~€ƒ€ƒ~€…„‚ƒ…„„„‚……ˆˆŒŽŽŽ‘‘ŽŽŽŒ‹Œ’wxvwvxuuwyyvvuuussruuwvvwxvwwwvyvwyztvuvwwvvvwtvruvvuws{ruvwsuuuttrtuvrrsusroqqnmmnje^;��� �A[loljjI:zopoplqx}{†w{mkmks5��Axpoqpoopooooopoooomopqmotbrooonopmmommlmlnrpopprpqqupmsrqsqpqoprprqpppuusurpwvsvuvuryvtvtvuvtqvvwwuuuvuuuvwyxxuttuutuuuuuuuuurprssutvtuvvvvwutrtvwuuvwuzvwwwyuuvvww{z{{{yywwyxvvuuuuvxuvuuuxuwwvvvuuuuwxwvywvxxxvxyyxyxyvyywwzz{yywwwyyzyywz{zzzw|yzyŠ�8‰vzxy'�†…{††Œ‚vttyyrLDHVdy}2ŒvvvuyP)l}vwvvzvv|wy}iExpuzwvx|z{{{zz{||}|}}}~~|†|{}||||~}‚~~€|~€€„|}€‚‚~„€€ƒ€……„………†‚„„ƒ†ˆŠ’‘‘ŽŽ‘’‹’““vuutuwvswwztxtuttrqqswvsswwwvsuyywstttrrvvuuxxvvruvuuvsqrstrsvrtqqqppsrrrppnpqqmqnnibi@3 #.2239=FFSYeloqsk`blpolieC6voprppopooooopmmk{9�=„ooqqpppppppoqpppppppponrsqnqnllmmoqqpoqroqpuoqrrsrnpstrrsqppnqrutwpportqqrtwvrqruuqyuuuutwvwvvvyyuttvwuuvwwxvyzyuyuuuuuuuvutrwruuuuwtvvwvwvsuvtuxzwuuvuuuvuyxvtvvwvuuuuyvwxywzu{vwvvuvwuwutvvwwyvuuxxyxwxzwuyuuwwywwwuxxyzwywyzzx}yzzzzzw~{yzywwvz{{‰-)?…zyyxŠD ��045DLTC@@A25{vtyyu~ˆ~‰n^y+†vuxwwN*rqvvvvzwwvyyyn��6ejrvvvyxvzvy{z{{{{w|xxx{}|||||{~|€~~ƒ}~€}~}€~ƒƒ…„||~„€€„…€€€‡‚‚…„‡ˆŠŽ‰‹Ž‘ŽŒŒ‹Œ‹Œtutuvvvuwwyvuwwvvxpxvyvtswvuttuvuzxtttszvvusvwz{tvvwvwuwr‚usrwrvrssrruruqrrqpommmpmmheD7 "!',,/38:@DLS_foluwstwxvyursoolmwstprmljqO�� (tmppopmqpqqppopmkx;�9kjmpmlmoopoqqtpqnpmmmsmsqponpklwy„}zklnommqomoooquqpppquuspppnorrtrqstqvtrrvyyvvruuqwtuwuturwxxnyyurvwvvvvvvwwvuvvwxuxwyutvutqvutuvvutvwwwwvvwvvuyw{zywwuutswwvvuvvvvvvywwwyyyvvvwvvvvvwwzyywwuuvvvvvwxwwvyywwvtwwwzxzuxyxzyvwwy{|zzy}zywyz{{}zyw|yyz”�3ˆyzxyŠ% �ŒŒ}xƒ‚a ++ƒtuwyvvvvuul $…tuwvxUmuw|zvwzzywzy_�-c”|orrrvv||{z{z{|||||}zŠ{~~}}||{~~~‚~€}}~~€}‚z‹……€‚€€€€†……ƒ€‹ƒ‡ƒ„ˆˆŽ‹‹‘‘’‘utttttttvyyvvwywtwrvywutvvwtuvyxuxyxutwzwvuuwtrrsvvuuwrwvuusssrqrrsrrrrqrsqpqpqqmolnleZOGJVS_]dkrtzswtnjkhnmvlllllllmmmmmlkklkkklnlkjgR&nlklrppnlllnnpmplyC1aojmlnmiqptmlnprnnmlnpopqqtkqlfQA8&/'cnmoprluqrpqqsqqqruuqporovqqusquuquurrrrwwvruy|yruuruurwxyxxwusvwvuvwvvxvvuxvwxvvuvwwwuvvvvvwwwwtuusuvvwvqtwxzxuvvuuuttwwwwwvvuvvvvvwvvwwvuvvvvvwvtyz{{zuwvuuvvvvxvwwyxvvwvyxuxyzxyyyzy|~|y|{z{w||v~{z{zzz{zyzzz‘$78<8>|z{yutx}€ƒŠ£Im^muret9…rtuxyyywvvq‰�"tvuvteqzv{yvx{uvvvy^@- �+9FCC^e*$Aw{||zy{zzyx{|}||{|~|{}|~}||}~ƒ€€€€~€~€~}}€€}{z{…€€„‚€€€€‚ƒ†ˆŒŽŠ‹‹ŽˆŒŠ‰‰‘‹Œtwsxuuvyyzwvwyywrysvwswxvwvvtwxyxxxztswzwxwvv~rsrvwxwuwwuurqsvrsrrrqqtswsunqqqqqmpopmopvzvvtypmlrlmlmmolmmmlmoppqomkkmpkqqqppkqqqkjglK %._lqqqprpnrrpqnmmmrJ�+S”‚}zqibSNAD`xpsmponpromqrpnokl/9roopqmorqpqqppuqrqptrqpqpstuuurprqtttrsswuvsuuuwrvuuvuruvxyvtttuwwvvuvywwwywwwxwyuvwwvvuvwywwwuuuuutuuttttsuxwwvvvvuuvvuuvwwwv{u|wwvvvwvvvvuvvvuwvvvvy{wwwvvwvvwuwuuvuwvxwvyuvyyyxywxyyz|{||z{}|}zy|zz{{zxz|yw}|„‡†…†Žzz{ysquutppt�ciuuukŠ-‰lquwyzyzuyhŒ"tvwwqzVTv{z{xyuwwrvg==81<BENZfh…z?�uŽ“zb‡ +24ˆv{{|~z|z€x{z|||{{|~||~~~}}}|~€ƒ€~~ƒ~~~}ˆ}|„„„~€€ƒƒ€€…‚„‡ˆ‹‹‰‹ŽŽŽŽŽ‹‰ˆˆŠ‘‘’usttuuuuuwwxzxwwvwtuvrrwwxywtsuyzyvwuuvxsyrvwsrvwtstwtvuvvrsvsrqrsvvouussvpqqmmnooqppnljlkkklkllmlmmmlkmnnmmrppnoomlmllkkkllpllkkkje`tL1 ,<\ltkolfhmfZlloppmxQ� -9Oooronrnfooltrpmpkz dworruurrrppppoqruptruoqpqtqquqwqpurruqrqqrrrvvvvwuuuuqvvvxwzvuwvwvyvvwwwzvwwyxzwvwxuvvwvuvwwxyyuwvuuuttsvpvwsuuuvvvvwttrtuywwwvwyvxuwuuwzzzzzuvvvvvtyzwz{wwywuywwuwx{vvwyvyxvuuxywwxyyz{|||z{|yzzz|z{z{yzz|zzz|zzz{zzyyzz{{ttsvwxqqw�WƒwrwxŠ�&‹eqtuuttutkV’ttvvzo +)@KWuw{vxwxvvwuwpxy……€}€|y~to:rhwrl| D†wz{zw~||{z{{z}|{|~}|}€~~€}}}~~~~€~}}€€€~€vƒ€{ƒ€€€‚ƒ€…„„‡ˆŒ‰‹Ž‹‹‰‰Œ‹Š‹ˆ‰‰uvyztwvuvuxxw|wxvvtwuuvwvuwwsyyzyyvstywwwwruwvvvvtswxwwuvwuvuusvrvuvvxrvuvrqooprpomppqmoookjlmlkkmmljnllnplmmlkjjjgfgnrqt{yy}{}~yq}†I?/;@FM`mympmH#&Dxqmpqb�.I„lqooopppmosrrmmlz�^zpoqplqtrqqqpqqrqqqqqqtusuqqqqrspurpvutuuvrsuvvustttwruuvxuywwwwvywvvywwwwvwwvwvvxwyvvwwtywwvvstqsuuutrttsuuuuuutuvvwutuvyvvwwvywuuuvuuywvvwwwvvvwu{z{vzzywwwwvwvuwu{ywxw{xvvuvvwzvxwyxx{}||zzy|zz|{yy||zy{{zz{}}}||{xyywwtuuuuvwrr‰-�WwvrxrŒ�$”‘~ŒŽŒ†œ«�€twvvvp}…ƒx~v{{yz{ywwxvyyxyzyzzyxuo„B{sqvv}y*@pww{v||||||}y€||z|~|}€}€~€~|€~~}€„}ƒ„…€†„‚€€~}€…†…‡ˆŠ‰ˆˆ‡Šˆˆˆ‡‹‹‰Šww{xtuuuvuwvuvvyusutuuwvuuwxvyuzwxwvxwuvyyruvsrtrsswvuvuvwuvrrrrromumvuvpuurqorrroqoonmlkklllkllljkhhhkklmoookg`mkvirliXTGC::8521"3Yyx{uonqronr>. +0G~qkmr`Grmpppnlilnpupqpolx# .Ryoqqqjqtqpppqqrqqptruvututqququrppqpwwwvvusvuuvvzvuuytvvxxxvvwuwwuwvvxvvwyywxvwwwwuyyuwxuuwvyvvvwwuuwtzuyuvtyuvvuuutyxvuwvuwwtvvwvuuutwzvuuuvw{wwtwzwwvzwvyvwwwwyvyu{{wwwyzuuuxuz{wvvvywz}zz{xy{zz{zz{|||}|zz{|z|{{zyzzuuvvuvvtuurt-"Mxvrxr‰� Fg[TC@;41+ +ƒtwvvvuvuutuuuzvzxx{v{xxwwvyyyxsuwvwwytX�}mvwtr†,7;’`rvwxy{{||}|{{|z{~|~~|}}||€}~~~~~ƒƒ~|}}~~€~€‚~ƒ€„~‚………€ƒƒ†††ˆŠ‹‹Œ‰ˆ‡ˆŠ‰ˆ‡ˆˆŠˆˆ‰Œ‹wvvvtvwwuvuuvxyzwxtvwwuuuxuxuyzzwswxuusrwxuvusuvwvvvvvvvvwtvuvssssqwvusupssrrqsuqpqsmmigkrvyxxwusvzqhfpilmhlhX8) †ojmpoqqoonqA DypqpnaBomllmmopqpnnnorpnu>&Kuopprlrtqsppqtqmqqttuqpqqqqrqpqqppqquuuquvuxutvrvuvxutuqywwwvxwwwxvtwxyyvww{vvvvvvuuwxuvwvvxwvwwv{vuutuvvvuvwyvxvwvvwuvwuwutrsvwvvwwuvvyvwuuuxzxwyw{uwvwwuvwuwvwwvwv{zvxxyz{wwwvwywwvvutz}ywwyxzz}zzyyyzywz{||{zyxyywvuurrrsttvvttrƒ9*;H[rzsxr‡���†qvwzwvwvyzz|uwvvwvxvuwvzv|y{yyvvwyvywqƒU�‚du}l[˜<¨›Š†„‚rsvx{||||z|{{~}}|{}}}€|€‚„||}€€}€~‚ƒ„„~…€‚€‚…†‡€‡‡ˆ…‰Š‰ŠŒ‰’‡Š‰‰ˆ‰ˆˆˆ‰‰Š‰‹uvuuwwwuvwssvsuyxwuuuvuxuvuxuxu{xxwwsrrsvwxvtwvuvuvvvusvswtvvvvsqqrprwrqruourqpnppqooka^[JB<<;:41,( '.rmjlfK1 ����mkmllpqnmoo{B@oqqoS(AdnftoqquttoqmpponoX>;?8K`tprprlptqpqpppqqrquvuqqpqpurpppqqqrtuvvtutuxutyvvuvxxvzsxxwtvvwvvzvuyyywwwwwvuwvuuuuuwyxwxyxxxzxuwzxutzuvxvv{zyxuwvxwuwuvwwvwwwwwyzvwwvyvtvuvy{zzzzxwyvvyvvvwywuywww{zwwwz{zywyvwvvvyvwywuxuwzxyzwyyxvzzzxyxw{zy{„…y„‚}{€„€lsvrtqxFRcjty€‡Š‰„~ptwuvu…#ˆ|uuzwtvxviuuvvvwxvvvvvvuuvywwwwvvuvvu|[�zgfz‡¡�'29BRGC9;*)17\|wyy{{|}{~|z||||~{~€}|{z|}}}~}€‚}~~|}}€€ƒ€…†‡ƒ†ƒˆ…‰ˆ‰‰ˆ‡‹‰‰ˆ‰‡ˆˆ‡‰‰……‰‰vvtzwxvvwvvvvwtyvvuxwvuuuxvxsxxyxwwxxxvxyxvwvvvwvvvwuwvwrtvvvuvvqqqurvvuuurrrvmspppqmh^-��?#sllljK#YnpppqooooooG-@yppplg +.����-6K`XTQKW<7:;:bnoqqmoyzz}yqpqpppqqtsqqprqqrrsturrqrsrrurqqqpqtuttwruruuuvvxwvvxwvwrvswuuuwwwwuuuvwywwy|w}{{vvvuy{ywwxyyy{{|zzwwvvvvvvvvzxyxwyyzwwy{z{wwwvzyuuutzzyzv{vwvxz|zzzzwywvyzyxwwwyyuwxzwwwwzzyvwvuvvwuyvv|yuuvwxwvzyy{yyyywxxyvywwdIGD<941) ?xvttutyƒ€{wwtu{uxuuuvvvu†!Šjuwzywyzwwvvvvwxxxvvv{vywv|yƒ‚†’‹†‹Œ€wxg�([L:3)l£‰†™—b +M‚zz~~{€~||{{||ƒ}}|||~}}|}ƒ~€~…~€‚…€€‚…†ˆ‡‡‡ˆ„†ˆŒ‡ŽŒ‰‰‡‹‰‰‰Šˆ‰ˆ‰‰‰vwvwvuuxywsvuututuzywxssuxwxswuwxxswsxwwswwwrqsvvuuuswwvrswuvwurqspvrsvvlurrqpprqmmolff*$ 5pklldZ'9RsnorlllppmkO9?Tnmppoi99ABQP[anjnvun~vsrZ9$9Uuprrqomnpqqovwvpqqrqrqqrqqrrqqqtqruuuqpuuqppqqutrtywvvwwwvuuyxwwzvvtvtwvwvvvvv{{|{{{zu{|xzzy{{yz|{zzz{{zzzu{{zuzzwwwywwwzzvyywwzyxzyvuwzvvzzwwwy{{{wwwwx{xxy{ywwwz|w}}{yvx{zzx{u{yzwzzvu|wzuvwvwyvwxyuwywxxw{{yyyyyyyxvwwuvƒD:':2Štrqttqprsuturqqutsrrvuwv)Š]jpquwqopsxz}…ƒsŒ‡{syzvrs~PPJA@:73/ +�j‚osm~�:A‰z{}|||}{||{z|~{{|~~~||~|}}~|}~€|€|~€€€€†„………†…ˆ‡‡ˆˆˆ‡‡‡ˆ‰‡‰‰ˆ‰ˆˆ‡Švwwwwwwzxwuxvuuwwxxzyyuurvvwswuvxwrxtwsyuuswwvvwuuvwrwwwursuuxuvqsuwrusuuursprqqqpoojgc-,pkmkfU0��'Pooprpplpomj[Vgfn_fmkoponrxxyuvwtoolllkkigdv�!Oorrsrqqqoqpqvqqsrstpqnprputtqqqqtuuursuuutsutrrrssruuuurwwvtvuwwvwvtwwvvwywwwvvwz|zzyyy{xwx}{vy{{{yzzwyyy||{{|{||{xuuwvwyxxz{|wz{{{{vzy{zxz{zxwwvvvvvvy|zxwwz{vwuz{zyz{xv{xxzuzvxzyxwwvwvvvwwywyyuwyxwwvvxuyyzyzyyyywxwwuqv„I*�3Œussquvwrrusutuuuuurrwrwtz7��Ÿž‰††~wzqnkTG@85,Lfywv~r…<}txs‚n +>~{z|{|}~€~~~}}~}|}~~~~|}}~~|~€}ƒ~~~~~~}ƒ€€€€„…„„„ƒ……ˆ‚ˆ‡‰‡‹‡ˆ††‡†…‡ˆˆˆ‡†Šˆ‹xxuwxv{wvvvwuqsvwwswvvvvsxyxsxwuwvvwwxswwstwsvwvwvvurwvvwstuqqusrrvsuurrrrospppplopolfc+)rknlh^22!EhebYbhlllmollqomlkmlrmoooopppoonpqppooprsrrqplkgrKxrrrrquutvvuvtusrsroqrpqqqrtrquqrtwtqruuvvvuurvvvvutrrrtwwwwxyzzwvwwywvwywyyyyywwwwuyyzz{vu|zyzyzz{yyx{zzzzzu{{||{{z|wzuwx{zz|wz}}|ywyzz|zyzzy}wywww|z{|{yz{yzzwwyyy{zzyvvvuzy{wxzwx{w{wvvwwwwuvvvyyyyyvuuvwyxxxxuuxwxrpqtxwQ�5ˆxsrrqrqprttvvvvupuwusrvut8 + %./0"7\rvvzr‹,%#uy{y„j'>y{{~||~z|~{~~~~||~~}{|~}~}}}{|}~|}€~~€€€ƒ……€„€„ƒ†ˆˆ‡ˆˆˆ†…ˆˆˆ‡†††ˆ†‰‡‡xxuvvvvvuyvwvuuvvwuxtvuwsyvvvxwwutvwwyxvvsuwvwwwwwvvvxywurswvwuutsuptuurrupsqrppkqqplfi.����������,pkmikaC;JYecov|lkb%fmaejmpopklmqpqpoloppqtqqqtrqppqssrqmqjpoopooollqi +Oxtrqrqrrpqqrqqqtplkrt}zwosturrrqqrqruutuwuqqrrwuvuuxruuvvuvvwywwvuwwuvwyzzwvyyz{zxzywzwwwvy{y|{w{wvxvv|xvwzz{zy|{z|xxw{wyzyz{{vz{yzxwyz{{{wyzyzz{|w|{z{zz{{|yv{yz{wwwwy{wvwxwyyyy|wywywvvvwwvwuxvvvuuvvvuxvvw{xyxzzywxuqsuv{U�š[ksrsqqpopox|z}€…}pqttuvnW,7>fL�kt”œ¨B����]yvv|u’#9m|xzy}gB +*F‚z||||{z|~~~~~}}||~|€€{}~ƒ|~~}}|ƒ}~€}€€€„~„†……†„…††ˆˆˆ††……†ˆ‡‰„†‡‰…‡‡wwuvxvtsutuvwwuvruvvvvuvvsrrrqsstrwwvvws{ustvxruwvsvwwvwrvruvtsvuuvquuqqpqprpqpnmpkolfi.+bnihjfeqnjgnokjg_V+hahmonnpolhonkmmnpppqtsrppoqqsmsqpppqrpopomonplgfi�&=TrsoqqpndonwvpqhsrgZLCCEH[vutrtrtwtprruuuwuqrstwtvuuyuvvvwywuyzz{{zzxwu{zzwzwyw{{{zwwzzzxwy|zw|zz|{yvwx|w|xzzz{v|{{{{zy|{zz{{|ywwwy{wwzzy{wy{xwz~zxxwy|y{|vxyy{{}|ywxwwyxxzy|{ywyvwzzxwu{wyvwvwvvxvuwwwuvvwuvyvvyxyxuwwruvuy\P�� „…Œ‡‰Š‡„}wqjdXFCAC[orwxyq|‰…{ž {iowL†NT†vw|wŽ�28rwvyyxjVC, ]xxzx{{{||z{{||~||yy{||~}{|~}||~}}~}{}€€€~~€€€‚€€„‡„„‚ƒ„†‰…‡‡‡†††…………„„ˆ„‡‡ˆuxwvxyuyyyrvwwuvrvuwuxwxxxutuustsrvxssstxtrrvvvuvxwvwxvvrtswuutvuuuvtupsusnpoommmomolf[1�������cmgllklllkkkllljf^4�Ybmmqqqoomkpnmloqqqppqpooopoomlmpomlmptly}v}|~nh`�$)2:CK]oqqqoN&6$(vqrrpi<7$Kyttutsttuvvttutuvwvquvvuvuywwvvv{vvzuyv{{zxvvwy{wwvwyyzz{{{w{zwwzzyy|zw{|zxwxwwvvzz{{|}zxwwwy{{z{{wwwwvwzz{z{{ywz||{{{|zxz{wxxy{|yxw{{xxzxwwywwxyy{}ywxz{z|wxwuywwvuvuvwutuvwwuvvvvvyvzwyywuuuuvxur^�>U93, +����3„rqryttuttla“|{uui|G�"Pƒxv{u–P��14:AJLSnyyyyzvps~‡‡}wzz|x~{|z~||{||~{}}}||}|‚‚}|€}~}~}ƒ~~|~€€~…€~‚‚ƒ……………ƒˆ†‰‡††††ˆ…‹†ˆ…ˆ…‰ˆˆ‡Švw{xyxwzoxutvtvvrtvvuvxxwxutuutrsrrxsrswxywtvuuuvxruwvvyurvvwuvvvurrrqpqrrpqpqkmqnmolgb5']rhmoppokpollmmjfqGEkfonnpjmqqolllklllonrqtmmpplmkjp|rxvkdi^]VOF?=<, Qg|ˆxorqqqvV8 +F!}oqnphC%Gsuqwruutuwvquuuuuvvquvwzuwzvvvuvwwwyyyz|{{w{yww{xyu|yxyy{|zyz{zzz{zzzzz{{zzzw~}}{|zy}}{{xzv|{{{{z{wzy|y|zzzzy{|{z|{y{|yw}|{wzy|{|zz{z|{z|v{zzz{yz{}uyyyyz{vxwuxv{vwvvyzwxuuyvvwwwvvuvyvzywxvvvvuuuhk‹‰†œ¨�-‰spsyvssuvpn‹rssrzH����:T€xuwu€+234:;BDRYegxx€‚„‚|†ŠŠ†~louywxy{ywyz{z{z|||{|{z|{~{{|{|}|}||||}|}}~}}~~}||}~~€|{~€„€„‚€€‚€………„„‡ˆ‡‰…ˆˆ„…‡†ˆ‡…ˆ‰ˆ†‰vw{{vuuzz{uywxvvruvytuvxwwvwwsvvwxy{rwvwvtwywsvwswvwxwvvuwuwutuvuvussqqtrsqqprmnmnnpkic<�[rlkjkkjkllkllic\rX4„]ezszpmollkp{prpmfdXJGO`opomjV2������bƒglqpporqqmluqorolAAzuqpquvtvvvuuuuvvuuvvuvvvwwwuvuvzw|wyz|{{{w{{wvzyyv{xyzww|zzzz|zy|zy{zzz{zzzv|zz|{z{}}y{xxyzz{}{z{wwyzwzz|xxxzzzzz|{z{|zw|y|xzzzz{|||z{||{xz{{{y{{{{vzyzzz{wxwvvw{yxvwvuvvxyywywvvwvwuxvwwwwxuvvquxtqqrjX %�%„rqttyyzuyrs…•alputK .2cuwvvxml~„ƒƒŠˆŠ’ŒŒ‹ƒ}y{}zw|y}xxyywwxzy{zzzzwz{}{zzux{{{||||y}}|}||{~||}|}|†|||{|}~}~}€€„€†ƒ„†„‚ƒƒ€…ƒ€‚ƒ……†„…‡ˆ‡†ˆˆ‚ˆ…†‡ˆ‡‡ˆ‰…‰wwv|wxuwwwwvvsvvvssssrvyswwxwwuxwvxyrzwssvyxwvvwrwvxwsrruuutrqquuuwrurusrspmmnqpmmlolfi>Pullglroggiqusjzn]1!uhZIKWfopolm-# 0<KopnplH Qpqtttuqsqol��'ppmspoDAqzruqutqruuyvuuvuwvqvwtvxzwywwwwvwwzz{{{yz{|{z{{yyzzyzywu|zz{||}||yzx{{z{{y|}zxzzzyzxzz{z|wz{z{||{z|z{{{{~x~xwxzzwzz|{z{{{|}x{zz{{{}{{{}|zwzz|{z|{z{z{zzzz{|zzuwu|xyxzuwxvyyvywwwwvwwuvwwywvxuvwvyr{€|zzkŠ2‹jrsstutvvvqˆP*œŠƒxpnM��UuzŒ‡~rwzvuzywvvvvvwvvvvuwxxzzwz{{zzz}yzxwwxwzwuwzyzwvw{{{ywxzyy{{{{z|~~{{}~|{}|||||~|{{||}~~€||~}~„€€€‚ƒƒ…„…‚…€€„……†‡‡‡††ˆˆ„ƒ„‡†‡‰ˆˆ‰ˆ‡w{u{vysvuvuvsxrurwvrssuxswwxwwwwxvxzxvvtrxvuvwxwqwwwsrruuvx}qwuvwwvrrurrrrrsnqmsmompjglT����Qklljf`X_^UEF;72- ��� Banspjt+�-HolmtNAknoonqqrqrnf!=erqroqJ5$9vvuusqptuvuuuvuttuvwuuuvvuvwxuwwxwwwy||z{{xzyx{zvzyzyzzyyzzz{zyyzvwxvz{{{yw{zzz{{zzzz{|||{x{|z{z{{zz{{{z{|y{xxzyzv{y|{z{wzwxy{zyzz{{{{}zy{xvvzzzz{{z{z{{|wz{xwywvwxwxwvwwuvvxwvuvwxvwvvwywwuyuwvuxuuxxxup‚< +’`j‰†‚qrvttt�%-?JTHB@ACd€†y|w3 Ÿqtxvrs|vwvvvxvvuuvwwxvwvuvvzyyyyyxy{twzyzvwzyyyz{y{zzyzyyvvzy{}||z€~~y||}{}|€€|~~~{}||}}~~€ƒ|€~ƒƒ„„…ƒ„†ƒ€‚€…‡†††††‡‡‡ˆ‚†ƒ‡††‡ˆˆˆ‡‰uvwztxuvuwrssuvusvwsussxtxsruqrxzxrzxussrrvwvtvvrqrtwvwvvsrrquvwuuusqtqqrsssnnpnppjphelF� #*1BQ�JnlkgfS;"���),insoiq2Iroomf];gfnpppuqqrql81JerusrkQ=@7:/={vuuswruvuuvvutwuuvtuvvvutwvtvuvzwy{zzxxyyyzxy{z|zz{y|zzx|z{z{yz|{z||{y|z}{~{{y{z}}|{|{{{zz|{zyz{|{z{{z{{||{wwyvzvxzz|||{zw}y{w|xzzwz|{yw{zzy{z{z|zz{zz|zy||z|z|uywwxwywtvvxwwvxvvvwvvvvwwwwwvvvwyxyyyxxuŠ<&�����‚rmVQP]qvuuvp|Œ†‹”d?ilticF��‹quvwwsr{vvwxzxwuvwvusvqvwwyz{{zyyywwztywtttsvuuuy~ywtswrprxqq|||wz{{{{}z}{{|{}}}|||{{|~||||}}{~|€}}|€€~~€€€~…„……‚€€‚„‡‡ˆ‡‡‡‡……„„‡„‚‚‡„ˆˆˆwxvxuwuuvyyyvttvvvuussrxsxstuvwyy{svwxwvuutvuwvwt}ruvwwrsrqrrrstruvwqtqrrprsssrqmsqnhahL�`vfakfLRmlkgs@$eqoofx,�OjgolbR'<rnr€rnlpsrpkYLKTV_ksrurmpz|€|xuuutrvuvvvywwuuvvvvuuuwwxrvvuuww{z{zxxyw{xxxxx|{{z{z{{{{{zz{{|zy~{{z}|z{z{{zz{{y|~~|||{{{~~|zzzz{{{zzzyzz}zzzyzzzvwwv|{zzzvzzzwy{y~y{{{{|wyxyxyzzzzw{yz|zz{{||y{uvwwwvwvuuuyvuuwvwwvxuxvxywwywwvvwyxyyxwvzA +��Kuruuvwxuuqn~‰@�5yopqppI‡uwuzzzz{{||}{|{zwxwvuvwww|xwvvvusxru~‡ˆ‹ŽŽŒŠ„‚€}|zvxfPITmxw{zy{~~~~{}{~|}|~}}|}{{z~z|{||~€€}{}}€~€€€€ƒ„†€ƒ†ƒ‚‚ƒ†‡‡‰ˆ‡ˆ„…ˆ…‡ƒ‹‚…‡‰†ˆwwsvvvwvvsssvvwwywrvvsuuvwusyxxzvxwuvxuuvvwwwuqwuzruuwwrsrsrvtpuuqqssqnuvqqqqqompoolhfjXHQ[baUZDulkgv<hlhm`a,`Y`fh\O5 -13G]0!))AQgptqrnroxlmpqqqqrqrpqqrttuuuuuvvytvwyywuwwuvvvuuw{ssvvuuyz{{{wwyvzwxywxz{{{x{{yz{zwy|{|yz}zy{{||{{{{{{{zz|~}|||{~~}||zw{||{}zzzyyy}|zzzy{vyz|z{yzzzxzzzzzyzxzvw{z{wwxx{z{zwxyyyy||zw{{zyzvww{vzwwuyxyvvvzx{yyxvuuwwwwyxwwv{xyyzxytqfYWrg�� 9E€rrruvuquuqo† ��0usuu…Cˆvvwyuzw|r{{|wvus|qswz~€}xwx‚‡‚|yrg]`JB:8422,!." ;<wvuw‡w{{zzz|{{~|||{{{~|{|||x||~|yy}~}||~~~€~€€€…‚‚€}„†…„‡†††„…‚€…ƒƒ‚†ƒƒ‡‡uwvuuuwwvwsstwvywwvvsuuwstuuwxwyxwwwuyvywwvvuvuvuzs‚russsrstvurursqvrssusuquqrprpolmkkg`AWnfeXX��;kojfiC + ���Ougw€f/� .rrcYJHHO^[kqvv|z{}ƒ‚ƒodn^�qrrrpsqqqtrqrsqrquuvuurrtuuuuuvwwwvvuvuzyxwvvtutuz{{vutvwwy{zwvvv{w{xzv|{{{zzzz{zzzzzzyz{{|~zz{{z{{{{~{{zz|{|zz~{z{{{z{{}{|z{x{y~~~y{yzz|yyz{zzzzzzz{{|{{{|zz{|{{zzy{z|{{yyz{|wvv{ywy{wvvwwwwuwwvwyywvuuvuwvvuvuxwzvyzzwxzyxxxuvwyv}…vq†ž�@‹qrrtuusyyyq†1†uvuuH' ‚vvvvwyv}|||~|utrmg`PB<740- ����7€utsuu|zz|x||{}|{}|}|}}~~~z‰x}~€}}}|}|}€€~~…‚…†‡…††…………††‡‡ˆ‡ˆˆˆˆˆuvwttruvvsrsvvstvrwvsrswstvvrywyswwwvuvvvwuxruvuvwrruustrrvvpqrppsqmoqpuvqqqpqmqmmllmjcT5ofgfeY5ebfelI $)4@Ws];9'/Yquxvrooopmlmmmnh]d�myppquuuurrqttrprpppqqrruuuuyvuwyzwuvuuxtyxwutvvttwvxvuzyuyzyuvuwzwyzzwvyy{xvz{y~{{|{{{{|{||||{{{{zy|{|{|z{}|xz|zzz{{|{|||{{{z{{~~{zzzzzz{{||{z{{{{||{y{wxzzz{{wyzyyxw{wy{{|~zwt|zwy{zwv{uywwwvvvwxvvutuvwvuuyuvyxwwxwyz{xyyyvzzwwwwwrnŠ 7xrsutttuxyzuˆ"�2†tvut‚G@#uvuvwyvwvuv€@!'�‚vruvwzzy{{||||{{}~z~||{{|}|{zxy€|~||}€~{€~z€~~~€€€…ˆ††€…ƒ…ƒ„‚ˆ€ƒ„„‡†uvsssuvwvvvyvuwvvxuustuwsuwwwzywwxvsvvvwvwvvvvvwuvpuwysvuuswwvsrqvquutrupppuprrrmsmlkkc\��0w[\_za�.yY_{ƒU(17:?@CJVY`huw{yu–6���"}oikopqrtqolllnlobj `zkyuuuuuqsuuuuuuuusqrpqruvuvwvwwwvuuvvxy{wwvxuttuvzzzuwvxyywxvywxvzx|vzzz|zzzz||{{zzz|{zzz|~|z{{z{zzzz{|{}|y}{{|z}zz{{}~{||{|z{|zzzz{~z|{{{z{|zz||zzyzvww{zz{ywzzwwyzz|{{{yz{zyz{zzz{xvwwwvwwvvwwwywvvuuwvwuwtvwxuwxvyzyxxvuvvvvwvvzyp„*��1j‚vrzztvwwyv‰ /ƒuyzyu_NPcs‡œ"}yvuwvyu{x{u3?†twzzz{|{{{|{|z|{{||z{}|‚||||~‚{}}}}}}~~~€~€}‡‚€ƒ€‚„ˆˆˆ†…†‡†…ˆ†€‡…€€†wvvtvvvvwvvvvvrvvvvsqtvwuvxwwustvwrsxtwvwwwuvvrxvvrrquuvuuuurwsrsqqusqrrpqpsorronqmkiic^ 'lfl`i+�kXJ*@RZnx~€ƒ{xzsslwmlllg^„>�7sllooortqppnorppmjdY{doqqrswquvrxwlsvy€€„{xomsuyxxvwvvwyvyywwywwytuyvyz{xxwwvvuvxyxwuw{zywzzyy{{{z||{zy~~{zz|||{}z{z|zyz{}|{||{{{{{z~|||~|{{|z~~{|{{{|||{{z~z|zz|zz|{zxzyzx|z|z{wzuwwzy{{}{|wwvwxwz{zwxzuwwwwyvuuyxywwvyvvwywvttuywvxyy{yuzzwzyxwxyywwyu9�$4\pz…cUbƒuwyvƒ"%‚uuyyyzoƒoe…1~vvuuwvvwwwwL%‹vu{zzz|||{||{zz{z|}|||||||||{||zz{{{}~}|~~‚}€z~~~|}~‚‚ƒ‚„‡ˆ‡††€ƒˆ€€‚€…‡†ƒ†wwy{vxwvwyvtvvvtuvvxrvuuuwuuvvsstursvwuwvvwwvxxwuurwrvuvuvvvwwvusqpssrsuprrrntpnlrkmhgbd�!���=fjgkkljkklloopqqppoollfiA snjmqptqnoqorookg„f@™•‹…‚zqqps}aRO[MD>=7,9AIcruwvuvyuvuwvvxvvvvvywvvvwwutywuuuuv|wwvww{zzwwwwz{|}{|{{zzyz}|{{{{{{z{z{~|{|y~~~{|{{z{yz{z~|z{~{{{z{{{z{zz{|{{||{{~{|{y{zy|{zzyzzzz{z{{{z|zw{{zyzzzyxxwzyzz|zxyuv|vwwxuuwwxxwvvvvxuvwwuvvwvwwyyywywwxyyyxzzzwyu{+���� +*@’uyzu†)#„uvyzzyzzxwq‹3ƒtvuwuvzwwv|N‚„w|z|{|z|{}{|z|z||~}~~|{{}}|||~{„z}|~|~{€}}{€„~~€‚~€€…€ƒƒ†ˆˆ…†€‡†ˆ€„…†„ƒ„‡‚‡ww{xuuvuxxsswwvuvuxwssrvuwrqrsrrsrsrsvvvvvswwvxvrvrsswuuuqpppvuwssrstuuupqrvmomlmlkjijbZ�6TgjmomlkkklllmpppopololjA�_{kklqoqqoqojhv|…G +1$15-#AxusuvQ?2 +Txuwvvy{uvwywvuuuuuvvywxwywzvyzz{{zwvzz|vwwwyzz{z{xz{|{{{z{z{|{w{z{{{|{~|}}~|z}~~z{{{{zyz|z||{||{{{zzz{{{{{zzz||{z||{zy{{{zwy|{{z{zz{zz{zz|{{xzywxyy{{{{v{{zytwzwywtvvvvzzwuwyyx{yzyyw{xwuwuwywuw|{}…„wrzwvzx;EVQ[p€��0<uz{u€/�xtzytxyzzsu‰:jvuvvuuwvwvw‚\� + "$126::;Abr€{wwv{v|z{{zzxz{{€||~~~}{{}|}||||zy{}~~}|~€~~~€z€€€€€€€~„„€€‚…†ƒ„€€ƒ‡€„„‡…ƒƒƒƒ†vwttswwxwxwuvwvxwxyxrwqwswvwvuswsssssuwwvvsxvvvwxwstqvvwwzyyvvvvvwqrsssvuuttrspol{lvimeX&�3YhklmoopoomnllllklmolpkhE/���Hv{mbV]qrmqmeI=7">~€~�F*†uuuv[5Mvuuuvzzyvvvxuvw{vvwvvxwwwyzzwvz{zzyuw{zywwvw{{z{zyy{||z|y|w~zzy||{{{{|~|zyzz{~~{{||{}z{{|{z{|~}|{{||{{{{{{|||{z{{z|{z{{z{zz}|{{}|y~y{}~zzzzzxxwyyy{{z{|}}z{ywwyzzzxvxyyzxvyvyzzyyzxvxwxvwyzwwwxrXGD?= 2|zuuysy€ƒ~{Ž��Duvzv‡8"ltvyxyvuvvlwOh”nuyzzvwxxxƒY� '$028<BOWXmu{|‡‡ˆŠ‡”“•ycƒ,�€yv{z{{|z{zz~z|y|||}€{|}||~{|}z~||ƒ|}~€}€€€‚~‚~€„ƒƒ‚†„ƒ…†ƒ„„ƒˆ†„‡„‡€ƒ„‡uvvssuwvwxvtsvwssuyvswsussurusrurrvvwswxsuutttuuxvusrsuvqwuvvuuxpvqsqqrurruqpoopmlmmmmfN0&vkdkmlooooomljko|ukompniN>9. Ce_bjhkqw€—A<Ozuopmx3ˆllh^ƒ.5~wvu}oI€vvvwzzvxuvvvxzzzzwuww{xzy{z{{|zwy{zwv{~{wzz|{zz{x{zz~zzw}r}wzz{~|zz{{|{~|{{||~}z|{z{{{{{{{|{~~}}||{{~||~{{|{{y{|~|z{|{w|~}~{{{~{z|z|}~|{|{zzzyzzzy{z{{|{{{{{|w{z{zwvyvzxwyxy{zzz{zwxyxwy{{wuywzG0…xxwvxyxyupvm����>tuzv„=�!“[ntuuttrrmfkmJžŒŒ†~pwvxx]��BŠƒ€‚|†€ˆ…~{xzrxvuquuusuusuwxumu6�ƒuu{z{{~{||~|~z{zz}|~||}}w{€{{~|||}|}|€}~~|}~€z|~€~~~€ƒ‚€„……‚€ƒ…€ƒ„…†‡†€…ˆvwuustvvxyutrvvvsrwvvvwustsssrsxrrvwwssxsuttuxsvutvvuvwwwwvwwwuyrurrsttuquurrtpomqqoppgL7aƒ{}vtonmkkja]QIAG[wmqnoighvjg`�pzotvrrooh`@3Dztpomqnlprk|7� ;ywyr}p1J{wwxwyzzywvvy{{{zwwwvwz{{xvuuvwwvzz~wvz{{zz{zzzzzz{~~~|{|~|||{z|{{z{||{}}z{zz{{{{{{}||z{{|{|{{~y}{|~}{{z{~{z{{{zz{||z}{{zz{{|~{}|zz{|}|zzzz{|y|zz{}{z{}zz{{z|wzyzwy|zyyxwvwz{{wt{zwzwyvz{{wwxu{A! *6Œyyyvxyyyyvs€ <Œsvzu‹/� ˜ŠŽŽ•••‘‹qB&-&--}vvvx€[�l‹ipzwxxywyyxz|||{zzzzyzzvvwyvzvv:��‚ux{{|{~{}|}{~{{{|{||||}|}~~€}}€{„}}~„„ƒ~€€€€€€…„‚ƒ…„…ƒˆ‡†…„„„†ˆƒ‡ƒ‡vtuuuuywxysrruvuwuxsuwwwqsvsvsssrqrvrsrwswvuvwruwtwuuwuurxuvvssssrrururrrsurusrmooqlophXB.(.-359:::;8:;AAXlokkt5+&+7>|pnonooolgaj�hforrqqtqolpE?{rrorh&ctruq{@�@uyxpy}MPyvz{ywzzzwxvzywxzwwy{vyz{xywwvvwvwzxwy{yz{w{zzzz{z{|}~|}~~z~|~{zz{z|z~}~~z{{{{{z{~||{{|||{~{~}}}z~{{{z{{zz~{{|{{{{~}zz{}|{zy~}~||{zz||{{z{}}~|z}~z{z{|~xzy{xy|{zywywywy{yt|wxxwww{{{yyyu‚8&ŽtvvvvzxzzzvŒ�� :ŒuwvuƒAB@;;=??=:7.'���uww{}i�Xrpvz{zyxwvwwywwvw{{zyyywz{zyyxuxŽ?�ˆqxxz}{~|||}|{{{{z||€}}~}}{}{~}|}}|~{~}}|}€~}||~€~~~€€€„‚€ƒ‚ƒ…„ƒ„‚€…†„‚„†ˆ€ƒ‚vwyzyywvvyuxsssvuvwsrtvwrsuussrssvvwrrruvxwvwwvuwvuwuvvuuvwwwvsssrrsuussrtrqrrspnmllmsiaN9::AW�H[t{qsxzzt‚Œ5�<MloljqB- ,0‡nkpnoollkie�ulqqoqqrqosbX�5utrmre 9Y}stqqD4#8*yvzu{q% %48>COO_uywvwwwxwzzxz{z{{{wy{xwwz|yuvyvwvywvwz{|{|{{yzzwz}{~{||}|zzzz|{{z{zzyz{|{||{}}||{{{~~|{|{{|{{~|~zz{~~|{~|{{|{z{zz|{|{{{{||z{z|{zyz{||zz{z{{~}|{~|}}|{~{{zzyx}z{|zzz|z{vxy{zyy{xuzuvww{||{zvvwu†3hpvvvwyz|zv‰ ��!9‰vtyuuy‚†Šˆ” "‹vvww|pWwvvuw{zy{|zzwxvtzzyyyywwwvwzw{uy‚Aƒqzzz|y~}{|{€||{|}~}|}~~~}}{€~||}~z}{{||~~€€||~~~ƒ„‚‚„†ƒ‰‡…ƒ…‡ƒ„„‡€ƒ…stuwyvvtwvvwsrswruwstrrvrrrusrrssqsvwvsvvsrvvvvurvuxvwvvwwwvvuvsrtqmprsqtvprsssssqoooqmffkkZUU:cWchlllklg[r;� ,JjokjgH>xprqprqtmqm{�gqrpqqqpog]n�.|trmx]>) 3>RusrtkQ89425:;?AMS[uw{uxljˆ{|‰ƒ‚„ƒuwz|vwvyxw{zy{yyy{{zzzwwyzz{zwvvxxwwtwz{{{{{y{z{zzy{{{zzz{{{z{zzz||€~{„|}{||~||}~}~}||||{|~~{€z|}}|{z|{zzz{|||{|y~~€€}{z{y|{{z{z{|{z{z||z|~}}{|{}|}y~}{||y{||{yyz{z{{|{xx{yzyxxz{{xwxwu‹5¥…}‹‰~zz|zu{R39Ac;�jr|‡¤�8‹vsstuwuutoq}/‰vqxw}t�M‡vwvvwwvvtrrrtvwyuurusru‡zxvwxxx‰Cƒrzz{{{}y|{||{{{|||{}~{{|~}{€~}|{}~{{}{~~|~~~}~~|€€~€€€€‚…„…ˆ€€€€ƒƒ‚‚ƒ‚……vuwwussvuuvxwwvwsvusvvwururvuuvwvtvwvvvxvruvvvuuuvswussssuuursvvturuqqssrrrrsrsrrsmqlomkknjinQLQsgjjlkkjkbi9GtokkmH@|prpqrqolpl{* ^wpooqt€‰|w„y�+zwqmoeTCFOQWersstqrrrvƒ{olruvwuxxvvvvvvuvvuvvwwvuyywwxv{zy|zyyz{zy{wy{zzzzvywwvvvvyzy{z{z|yxw{zv|}{{z|||{{{|{{{{~|zzz|{~}z}{{|zz{|{{{{|{~z||~|{z{{|{{{{z€~|{{~|}{|{|~}{{z{{{{z{zz{~zz|~}|{~~z||||z{zyy{|||z{zx{zxyz{{zz{w{yzwvyz{ywxvyvu„=�H]HE/auz}wup|Šˆ}ŠŽ�^Ž‹~l\–0‹vsxtvyxuurq!}ws{v€l�A†uwwxur€ˆ‹ŒŽŠ‰††€~{vdOG_swuxxŠGzsyv{{{{{{{|||z|{{||}}|||€€€€‚~~~}||{{~}{|{|}|~~€€~~„}ˆ†€ƒƒ‚‡€ƒ†„…‚ƒƒvzxvvtuusvwwssuustvsuuvusrsusrrwuvxvvsrqrryuuwuuwwuuvuwvrrsrurusqsrusruqvprurrsqsqomoomlllkjeUWQelnpomlklf{;/ (GuolljM7'(Brqponoqqnpm|'�P~pprs^>>81+�wpqpompwwpxurquuutuv{wvvwvvw{wwvuuwvuuwwwvutrvwxv{{wvw{zzxwzyz{yywyzzu{z{{zz{zvwzz{vzv{z{zzz{zwvwyzv|{{{{}~y{{{{{y~~}~{|zz{||}}}}|{|||||~{|{~{{|~{{{}|zz{{|||{}}}{{{{z{|~y{zz{~{~{||{z~~~}~~|~||||{zyz{}|~{||yzzz{|xzwywxyzxxy{wzzvxwv€>� .#Ptw|yusuvywqfVŠqtrp1rutuvyvuttt‹��Ntvywxx>…qwxw€\LIA865-+8HvyyyxHy|zy~|zzzxzz||{|{{|{}{|}|{|{}~€}|~}||{}~}{~z€€|||€|~~}€~„~€€€†€€~€€‚€„vuwwsuvvvvuxrrsusttuutsvuvvuuvrvvvvvvvqqqrrutwuvvvuuvuuwrwssuvuuturtssrsvqrrruruqppooomvmhhhb]SXZgjklkkjjz…>:;solkmT8%0Rnpnptpqqopnt1�Hpnsw?!ttqooqqqruvxuruvvwuuuusotrrrrqvvvwvvuwvutstwwwwyz{ywz{z{wxw{zxz{y{wywyzzz{zzzyz{zwxwzzzzz{zzzzwwy~ww{wz{{{{{zz{|z{}{{|}|{{{{z{{}~~}|{~|~~|}~}|{~}~|{{{{{}~~~{~~€|{~|}~{{z{{|{|{{y|{{}|{|{~~}}~}|{|||~|{{{{{z{yvx{yy{{|{yy}{ywz{{{|zyyzwzx{z{ww†@Gv|wzuyyxwxkx T‚ls~t%Žcuvvuuwvwus‰.�/{vvwwytAŠvvxx‚F'.„vwzyˆK�q{z{|y~~~}||}z{}}|}{||||~€}||}}}|~y„|~}||z|€~~}€|~{€€€„~€€…†………†ƒ„ƒƒusuxsuturwusrrvusrsstsrusvvvvxsrsuwuvqsuurstwuuuxvvwrvswssuuvurqvsqrtsusvqpoqrrupppppmplkjhf_RaSQamƒ{nrvjiP;;BBJP]momllahx~‚zwsrotrqooojo{9G€pmpvG! sqrqtqqpopsuvvvtsqrqqpqqptututtuuuuuutqpqrqsuuvuwuwzzxwwwuyyzxyy{zz{zyz{{|w{zyzyzyzz{zzzw{{zz|{z{}wyzz|{~{z{{y|{{||~~~|zz{{{|~}~~~{||}~}||{{|{}~}}}~~|~|~|}~{~z{|{||~|€||zzz{{~|{~|~~}}|{}~|||~||{|{{zzw||{|~|{yzzwz{z{{{{|{wxxz{yyv‚CF‚wxwwuyxvwyrŠ H‹^pqkw� {z…ƒƒyvuvus„,I£Š…ƒˆxwzwuvyz :‰uy{y„H=7{uzzzŽMjuy|{zyy|{|}||||~|~|{||~}|}~~~}|||{|{zz{}}y|}zz}yy}}}||€|{€~„‚ƒ…‚€€€ƒ„€ƒƒstxyrxutswsrrwuvsrrwttsttuvwwwswrvvsursvvurrvwuvuuuvstsysvvvusruvwuuttsuutnuuursnsmppsnlhpnkgdN3_A1+)9Їxvyslpqoonlknorqqpqrmppmpoor9LzqnpvQh~qqqqqqptuvuurrruvwuuutssutw{{y|{‰‰‡Š‰‰ˆ„„Š}xoqtyy|wywvwwzzzz{{wzzzy|zy|yzyyz{z|{zz{{|zzyzyz{wz{|{{|{|{{{{~|}{{~y}|z~{{{|~|~||~||}}|{|~}~~~~~~~}}}~|}|{|}{~}|}~~~~|{{z{{~{|{~~~|~~~}|{€~||~}z{|zvzzzw||{|{z{xwyz{z{{|||||{|zzwxzvƒG�Nyqwyzyyzxv€£G–˜‹vit� h~obQH`xuwur‡1gjpvvwwzw|r|v8ˆuw{zŽC��3yvzwx…O�dyy~}~y|}~{||||~||‹{€|}}~}}}}}|€€z…y|}€}€~~{Š|yz{~~~~|~€„ƒ…ƒ€‡€ƒ‰„„„‡†ƒvusstwvutwtrsvrssruwvttttswvvvrtsvvuuqruvuusuwvtuuxxwwuzuvwwwvvrqusqtvrssvpupprppppmmlki^I7+����79ˆZoppqppoonjlpoqptqpqrknmmmmjj=Czqql€Hatutrqqouuux{z~{~€„…€}ƒ~{ppncWUTHBD>>:412+)?Pgwyxwwvvwuyz{z{wvzzz{z{{zyzwzw{zz{{z|z{{|zzz{zzzwzzz||{{{{{{}~}|{€~{|{||~|{~~~~|z~{|~{~|~~~€|~~}}~}€|{}{}}|{~~~|}~|~}|~~||||}~}~{|{~}}}}|{xz|zw|||{||{{{zzywvwz{{|{|{ywwz{t‰E'�[rbjy‚€‚†…|†›b%,<IKVDCCA +&;<‰tuvqŒ+�U~tvwvwzwv|ry|8‹uwyzˆB�����2xvywyŠO�csy{s}z{{{{|{z~|||zz}{{|~||}~|{||€q„}~|}}~|z{|}y~}}t~|}~|€‚ƒ€ƒ‚€€ƒ€€€„‚„„€uvvwvywvtwuusuwxxvvwsxrsrsvvxvsssvvtvwpvvvuuuvvuvwuuwwwyxyvxwustrusrrtssrtsrsspqpmpqmmkhS7��7{ckqkpoppppnoonpqqxpkmnomomh`~U3FrqrlxM3�U{rsrqjcajeeVUJLD@:840*' +���� +>^‚ywvxwywwzzyyzwy{z{zzzxw|zxvvy{w{{zyw{x~zz{|{||{{zzz|zy~{z{{}~||{zz~~~~|||{|{|~{}||{{||{~|{{~{€{}}|~}|}}{~{{z{{z|}~~|~||€~~~~}|{{{~|{~|{||}~z~~}|z{z{zy{|{|{v|r{y{xvuwy{{{{{|zzw{zu~I#����:}ƒyeUGB@;40mœ‘†™¡O��-2‡ttvs8Tsuwxvwwwvzsx€�8‹wvuy@ 1}v{v|T�X{y~~~{{|}{|{||~}|}€||}}}~}€}|{}|‚‚„~|||~~ƒ||}~y€ƒƒ~}|€€~€‚€€€€€ƒ€vvvuuxxvwxwwsssxxwwxtssuvsuvvvvuwvwuuwrvuuuuvvwuuuwvuuvwrtvvruvsrrqrsruunqvquvoqtjoomqkfW*/hmpqpooooopqqmotkhmmllqpsvtxE=Voqspx\4=T3QvtsqnR:6�� ]}xx{zzuwy{wyvzz{z{z{zzzvtwy|{|{wz|{zw|z{{z{{{zzzw{|||}~|z{{}|}}{|{ƒ~~}~{|||{|€~|z{~|~~{€~~€{{{}|~~}~}~{{z|~{|~~~|{||}|€~€|}||{{{}{}|{{{~{}~~~~{{zyz{{y|{{{{}}||y|{{z~{|{yz~||zzxzz~bEHRn[ tpoh‰Y�8<ˆuuwsƒ=��Kppwvvuqvuxxu3Švvyz~B0}vww{T��S{x{{|z{zz||{}~{z}~|{|}||||~|€~€~}{{}~~}}{|{{{~~|}€€€|‚€€„‚ƒ€€€~€€~€‚yzuyvwxyxwwxsuwxwxwwrwwwuusvwxussrwwvwwwvvuwxxwvruwusuvwvvuvuvuvrsnvrrruutttrtqoslopppkg_ƒsjkyzy}€€€onopsyjjkektmkdg\TB=Kfljcgmpqtqiw‡}t‰ �H}uspoQ3 W€y{yyzzwywxxv{z{wz{wwyzwwyz}y|{wzzzwwz{{z{|z{{yz{z|{~z{~|z|{|{}||{~~}|z~{{|z{||~~|||{z}}|~€}~{~~~}zz|~}{{~|{{{~~~||~}€{{{z|~~}€{}{|~~}~|}|{~o}~{|{w{{z{zz{z||{||y||{z{||{y{{z|yz{zzwy†‰…sƒ—�…ss|p€b�1‚vvvt€B8 L„elŠŠƒuvtzwvƒ,‰wv{zE%uuuz€^��Qy{z}}||~~}|{~}|}}||||€~|‚~€|}}~}~€~~~~}‡~}|€‚ƒƒ‚zƒ~€€ƒ€~~€€‚……yyuuvxxxwxwxswuxwvvxtyyxwvvwsxwwwrwvvuvvvuuwwwusuuuwswvvwvrtruutrrqwrqruruuurqpoqppprpmh\'��� +"7T‚pdg\YVGGGKeoppmgR7" 7“{pnopnmmstrrrqnY‚-Dvqppk_Sƒyzwzz{{wxwwyz{}u{yvtvvzxzz}{|{{}{yww{{{y|||z~}~}}}z}||{{z{{{{{|z{|€|||~|}}|{{|}}~|{||{{~~€}|~}{~~~}{|~|z{~}||z€~{{{}{}||{~~~|{~~}€~{~|{{{z|{{{{{|z~|{{||}{|z{zyx{|yy|{}|zwwwuuvulŠ wuu{xzc&ywuu~B +;F[[_SHQgzuyzvˆ“wwzz†J�)txs|ya +�Py{yz|{{{{{}z{{|€|z{|||}}}{yz‚~}}}}|{~~{|}}~~{€~€z~~~~}~€€ƒ€}„€~~€ƒ‚ƒwzvuwzxvvwvxwwxxwwvwwxvwxuwvuvwywsvxwvvywwwwuuvvvvsvvvvuuusruvvuruszrutuussssomnqmopqqjg]. +%%"xybsƒ^: ��-coqqkq@>6‰flssrrtrqqrtqqpfx3 Dzqpqll����J…wwv{zxvwwyy|{||v{zywwz{ww{|r{{{|{|yzzzzz{~z|}}|}}}~~{|{{z~z{||{{z{{|{~{{{{|~}~~~{~{|{}~|~~|{~~yz}~z|}~{|~€}{{~~}~{{{|~€€}}}~||z}~|{{z{{|{{{zzyzz{{||}zzz{{z{{yzzyyz|wyz{zwwy{{zuwwq†3 �u~vzu[7�� 7S{yxuxtG;QDBdo£š€|~G ixvzyw‡%ŠwwzxO +‡s{w|~h�C~wz{|zz{x|~|z€z€~|€zz~~~~~~~{}~~{~~~|}z‚~€€€~~~€€}ˆ€€}€ƒ„wvvvwxvwwwvtuvwxxwwxvruvyxwwxxxyvwwwwvwwywywwuvxuvuwwuutursrwuussuwwsvrusstsspooppnkmmkfi1�Ajlsjl kZbffy3 Xvpqmy>.zhqrnpqprqqpqqssms=#Czqqrmu� + +"%136310Hywv{yuzw{u{{{{}x|zzz{{||{z{z{{|z{zyyzyzz{zz{~|{{|||||{{z|z{|{zzy{~|z|{{{}~|||}~~|{}~}{~}|~}{|~}~}}~~y{~~{z{{{{€~~~~~|{~{|~€€~}}‚€}}||}~}~|{{|{{{|{z{{{z~{|{zzzzzu{{{yy{wzywzzzxzz{uywwƒ<�'�uuwurxaJ6028;AEX_fjb^iuxvxuu~„y†t^o™MqtqtN?unyzxvŠ!‹wwvv„U����…uzxy‚h�I}yz|{zyyz{}~~{}~z}€~|{{{|~|{~{{|~{~~|}x€~}~~~€€€}~€|~€€}~~€wzv{wwuvuuv|{~vvvwvwswwwwwvtwxw|twwuvxuvwvvxvvvvvvrvvwvusurxvwvvvvvusvwysssvsrprqrmmmmjgf2�llea\$fmeecp1!YvpqppC�3ƒgntusqqsrqrqtrpmx96!9)prplw�� +&28:@EKNU`fmtvz{z„†„Œ‡ˆ…†Ž,�Cvxvzvxvvuvyzz{{z|wzwzyzz{uww{{zzxyy|wwzz{|yzzzz{|{zzz{zz{|||{{{{{{||z~{{{{{y{~}~|||{~~}}~~||{{|}~~~~}~z}}{z{z{{~~|}||~||z{{|||{€~€€|~~~~~}}}€{|~~}~~{~zv|{z{{}{zz{{{{{{|{yz||}{}}~w{zzz{{z{z{ywww†.��������oxyztvpnz„Š‹ˆ‰‡‹Š‡€‚ƒ„}~vuvwuuwyyywvnz%‚hqvvlhf{u{vv‡,#Šwv{v„Z���„uzwyzm +�D€v{z|yx{{z{€y}~{z{€|~|}}|€{~~~€‚€ƒ~}~ƒ|€~~€~|€€€}‚€€€{€€€…utw{ywxwwxtyvvwzxzuwvvvvwxxyvvutvvvrsvwwvvvvuwvrsvswwwwvusryvuvuwwvuruqqurssurpqmrpnmoggp1chjhfu(\}hu^r.QwooorD�6‡}lsyvorprqtpqrnouV:@?BATxqqqlyG”‹…„{yzus~wxypqqrqqrqpoptpa~1 ?wvvtvzwvwvvuzz|x|v{{{z{z{xvy{ywwxxz{wwzyw{zz{z{||zwvwyzxwxxz{{{yzzz|z{|{z{zz}}||zy|{~}}zzzz{{zz}}zz{{z{{{}z{{|{{zzzz}||{||~{{|}|{}€~|~|{}~}€z|~~~|{{|}}{|~{yzz{z{{wy|{zz|{{zwvwz|{{{}|{{|{ywwƒ=DKIOU`lsw{‚™I��styzxwvsttutvwvutqtrttvvvvyxvwuvzuuuuutƒ$!yyuuu{e�du{tv‡'Švwzw…^�‚rwvzƒs�Bzvyvyx{{{z{~x{|~}||}}{{ƒ}}~|}||}}}}|||wƒ}}}|~}‚~|z~}~|€€€€|~|}€zzz{€€€uv{|zzwws~sww|vwxyvvwxuswxvsxzzxy}vvvvuvuvwvv{uztvuvvvvztwtxyyvuvvuwruqssrqsrvqrotqonqjhd6a^nhj]/�Ttc^X,�*PppponX��.1?KNJOKDgrotuuprqtprr€~ƒ~€tsrqplx�j„jquuwuwvvusssuuuvwywxtrtvuum~:9‡vwuxwxwxuuuvzx{y{w{|zw|{zzvwvwwwwyyzvvw{{{{{{{{{z{{{{{ywwzzzz{{zyzz{zzz{{|zy~yyyy~~~}yzzzzyyy}}z{z|z{{z||zzz|z{z{{|{~€{|}{|}{~{~~~€|||~~~~}{{{|~~~zzzzzyz{ywzzz|{z{{zywx|}{||{{|||ywwx{„„…‚ƒ‚‚~ˆtj€h� mwwzwywxyyyzzzvy{{zztvwyutuwwxvvuutvuwuŒ)|pwrnlcƒv|uv„1Œvw{v„d�‰qzvyvy =xuyz{yy{{|}}y||~|{|}€}ƒ‚‚|}~~{||}}€}|‡ƒƒ{€€€|€€ƒ€~€ƒ„}‡€€€€}}€y€€ƒƒƒ„…€ƒwwxzwwxwwvvwvvvwxvvsrrvrvwtswusuwvvyuvruuuuuvvvwvwvvvutwvwvwuxyusuuvqsrrtsurutssrrmomllhi9�cXajaP1�\\p|j`!�#).)?`momnsZ��qxz|ƒR<24^pprovrqruopqpqrqqqqtrrl~$�[opoqqstuvvvvvtrqututqpprtuwvu~F�,€uvuvvvuuvtww{xzzzy{{zu~zwuwwvvvuxywz{wuwuzuzz{zz{{z{yuw{zzzz{{zzyzuww{zzy~w~}~|zyzz~}{}z{zzzzyy}|{zz|{y{{z{yxwyzy{z{{{{~~||}{~~€|{}~~}~}~~{{{~{~€|~~~|zy{}~|{{zy{xwvwy~{{|{zzy{{||}}}|||{zzzzzyyzzyvxvvwvpoqppm`��przwyyyywwtwzywvvsqrttttvtuxuuuvuuuuuuu…3\k„‡˜I\xvzvw…*�Šuvuw|f����…rwuzvu<|uyzz{xz|{{|{{{||~~|~€}}|}{{}{{~}|{{~~~}|||zyz~|{|~~€~~€ƒ~~~}‚€~€}{€{€}ƒƒ~‚€vuw{twxxwywxwxvyxwvsrvsswwvwvwvwwyu{xwvvsvvvwwu{vwvuuvuuwwvtwwvvquutsuqvrrsqutuupqonmvkkaC�f}xlmt[V`V<3(e}qsƒvsookmpb�'}^dioz+Tvnsnvqtrtnrqsrrrqrurrrn|'�_zvuuuwvuuuurpqpquvusruuvvuuvr}P�*ƒrprvwyvvvuwwvyz|z{zvyyzzwwvuuvwwy{zzzyxyy{{{{{zyyzzzwwuvy{z}zzyzyzzzvv{{{vux}~zyzz}}}}~}}{vy{yyyz{zz{z}zz{{y{zzzzyyy{{{}~~|~~}}~||~}{~}{{zz}z~€|~~}}zzz{z{yz|zzxvwwvy{z{zzzy{{{|{||y||{{z}wywxwz{{zyvswxwwv€hV frywvvywwyyyyzwuvvvuvutvqwxuxyyuwvzvuu‰0gngN>86��R…uvvv‚9�…vwvv|lˆryyzz{:‚v{yzz{{{zyyxy||}€|||}}{|}}{||~z€~~|~y~|~~‚~€~‚~€~{|€€€wvwzvvwxwxxwxzuywuwurrssvvwvvwvxvww{owwttvvuwwuuvwxwuusuvusuvvvurtuusurrsurrusqroppnmmmlbI� "����RYetoponmimqi�*rhllj{( Nrouivqppqqqpqrqqqtvprrl~-Zzuuqwtrrqqqpsnvyyy†…‰„Šrutvqy]�-qqtywxzxtuwuuzyzzz{xz||{{yuwy{zz{zz{ywyxwxwvw{zxz{wz~|zxwz{{{zyy|zyxwy{{|{{x}€wz{{|}}}~~~~~{{x{|{{{{{}{|}{z|~||}|y~|{}~€}~}}||{€{~{~|y{|}}|{|€|{{~|}{zzz{||{||{vzzvw{z|z{|~y|{y|{z{|}zy{{|wwvvvzwxvtwvyzzzwvgX# L_|ywxxyvtuvvwuyˆ‹ˆ‡‰‚€|wWR{uxwvvuwxyuu‡6���Q†vxzvŒ9�…vwvwwm�zuzyz€q�9|u{zz~}~{{{{{{x{|{z|{{{|€|z{~|}}{|~||}yxy||~€~~}~€z~~‚~„‚~{|€~€€~wwvxvyvxzzwwyzsyuywxsurvwvvvwxwvuxuwwxxwusvvwwuwyzvwuwsvvzzyvysuuuuurtrsstsvqqqqmmprqqlmaU���?droprqqrllfj+]vmsbu2Kslrrttqpqppqqpsrqrwpsrnv5Oyuuru~‡‡†‚xtsq`YNQA@?9)"cxvvpv`��"€purvvzztuvvzz{z{ywwyyz{{zzxyz{zz{zz{zyy{zzz{{|~~}|zzzz{}{zz|zzwzz{{{{{zzz{zz}~~|z~€~~{zz~€z{{z{~z~~~{}}{z}}~|~|}{~{}~{}€|}~~€€}|{{{{{z||{€||~~~~z{{{{{yzyw{|{{{z||zzyz|}}}z{{|{|||||||{{{|wzvwwzzyvzulxW ]~vvyywxtyyyx_PG=71+8&yuyvtvzwzwxuŠ7�LŠvzxv‰;� „wv{vxt ,ru{vzww4uz{y{~|{|€{z{{|||||z€€x|~|||}}}|z€{|}€~|~~~ƒ|„~€‚€~€€~xxvswvwzsxwvvstttrssvvsvswxwnwmuuvvwwxutwvwwwvuuusuvvwsuutvsqqsuvuvurtoqsrrrsqmmmmorpllmeQ=…qmnnlllmjhk_ajgY†4/#Gtcgsvy|€…‚uqprtrrrvqrslx;Hwsuuqj-+Sƒvvusq��"~quuvw{ztuuvuvz{yvvvzzxyyyyyyvtvz{z{{{y{wwz{{z{}~}|yv{|zz{zzzyyyy{{yzxyzy{{z}}}{z{{{z}~z{|~}{}}x}}z{}~{}}|{}z}~~~{zz|}~{{{}{}}~~~}}{{}}|z{}{~€~}}|}|{{}zzz{~~|~w{{{~{zz||z{{{zzy{{{|||{{{{|wwyxwxyzqf{vd_RGHJNTefktrvuxuqtvtvyR���� $2vvvyyyyw{wuvˆ8�Jv{zxŠ;~uvzwv€M�����)qtytz| 2ƒu|vywww|ywwx{xs}~|{|zz{z{{y~swy~|||||~~~|€~}~~~{~~|~~~€~{~~{z~€€}}€wzuzsxxysxuzswwxvvvxuvwwxyzywwwwvvvwxxuuurvxwvwr~szuytuusrrrvsuuuvuqvurrusxsqppmqqqpmlleJ?|qmplllomjxh�]fuˆ–œ8 .>Ne_VFB?:8<:TlqusrrvqrrpqCAztuvpiK J{vtuot��rvvuv{yvuvuwy{zxvwvwyzzzzzxyz{zz{|{{{z{xv|z{{|}}}}yz|{z|z|z~wz{{zzz~w{z|zz|{}}~}}~{|}{z~~€z|z|}zz}z~{{z~~~~{zzz„z{}}{~}~~}~~|}€||~||}~~~€~~}~}||{~{vz|}}{{|{z{z{{{}€zz|{z|z}y{|}|y{{|~|{w{{{xw~� +D|yu†vuuvwxyyyzz{uyxV= %|xruywyz{vvp‚?�D†u|wz‚@�uyvzv}q���� +.169@GNZstyzzv‚ +%„rvz||v‚wz{}||~}}{}||~€{~|~}~~}}|~€}~|‚€~}€~}}~€€€€€„€€~|}}||~vzuuuxruwvusswuxvwwxuuvwzxyxmvwvwwwvwvvvvsrwquuwszususvxvvvsuvsuuvvusruruvsrsqqqmlmmomlgqN���;vqmlmh^V_lk=�$0."41:S{xy\C" *9eqrtrrwsqrqoF=}uwsom H~vuvp}��uxuvuvywxwuvz{{{xwu{wwzzyzzwzzy|y{{|{{zzvw{|~z{|~~}~w{{z{|{{zzyyy~zz{z{y}zzzy}{~~~{{}~€{|}{zxz~{{{{{{{z~{{zzzzzz|~{}{{}||}~{~{{€~||~}~~~}€|||}~~~|{||}{yvy~~{y{{|{zz|||{zz{{|{}|||y{|}|||zy{|zwyzxxw‚E"8ysttvzzzzwyvwzyywztd + ƒrttrqquvwwiWB†u{uy‚B�yywzxz|`QWv~}†‡†Š…†{z{ywqrtyx{w‡$zt}vzyyx{uzvyz|{}{|||{~~|}||~||~|€|{}~~~}}}}|~~~~}~~€}…ƒ~}y~€€~z{zz€~|}~u|txsxvwuvttsxwxvzwwsrvxwwwvvwwvvwvqvuvyvvpwsvvxrxywuzuwvusttvuuvvwwsurrrtrtrsqqqsmmolkknZ������8yqpmke-2��<Šmkpa&tqrrtuvsrqqqZ9€tvtpy +B€vxvq€ ��i}vuuuvvyxvv{z{yxvv{wy{z|zzyzzzyx{{z{||yyyz{~z~~~~{z|z{|~{}{}zz~{zzz|{}{{y{{{~{||}}z~{z{}}|~{zz||z~z|}zzy{{{z{{~z||~~€~}~|}}}|}{~~~€|€}|{~~€~~||{{{~~|{|}z{{zzz{||}{|y{z{{||~|{zy{|{u|zz|{zz{{zww|' -€{tuuuwzz{wwwyyzwztj7†Šruzzyy~|~–O��@€u{vy‚A�t}v{w{{‰…}yyvvx}‚‚}yyyywywwwxx}y‹ �(€szz|y~y{z|~ƒzz|~€~||}|~||{}}‹|}}|}~~}|~~|}|€~|~}}~ƒ…€ƒƒƒˆy||~€€||}€€~vzwvuuuvxwvvswwxuywwvvvuvvvvsvxwvvuuvuwvuvrwvxtxvxvvuzuvsurtttvtssuzuusrqsntuqpqqqmoqlkkjbQ\_dlE3|pqmjh7<€kmlij�otpsqtrstsos]�5€uuvr~ ?{txwq„�`uuwttvwzuvxzyvvww{w{{{{|{|{z{{y|{}|{u{yu{{z}}}}|zy~|{}||~|zz}{{z{|~~zy{|zx~~{{}~}€|}{y~€~{~{{z|}z~~}}{{zy}|{{zzzy~{}|}|€~~{~}€|||z|~~|||z{{z~{{{{yz|}z{{||zyzyzyz{{{|zzz{|{y|x{{{z|yzzxw}4�� Žuttxwwuwwxxwvwyxzqg��e‘Œ†‰ƒzpd_J,�>¢ŽŠ“ b@€v{zx‚C��sxvvw{oruvvvvvwzwxvvyyyyzz{{{{z{|Š�"†uuy}zzzx|}yz{{~€~||}|~{~}}}{{|}}~}|}|{~|}}~}~€z}€ƒ€}ƒ€€€€ƒx|~~~~~||o€y~||yywyusvxxztyvvwvwwvvuvvwvvwvvvsswxwvvwvyrwwwswvwuuvywztwuwsvsuttruszrusuusqvunqrqqlsommkge-tddmog-kpomik/�@|otolt� msoqsqrrurpnc�4yuttr„9‡tyup �Tƒusuuyvwvxwwwwz{{{zw{{{z{||{{zzz{zyz{|zwz|y}}}}}z{z{|}z~{{|{{{zzyzz{~}~~{y|~z{}~|z~{}}}}}}{{z}|{zyz}{}zz{}~~{zz{zz~z~}{~|~|{~}‚||}}~€|~}{z{{{z||{|{zz~|{zy{{z{|~z{||zzzy{zz{zv{z{{{yyyz|wyxD nt{x|€~}…„†vwywzrv�&ZTJSnf>3%����m‹kpr‚�;ƒu{yyƒF�r€uxx{ywu|wywyv{v{{|y{wyywz{z~zwmŠ�syw|yzz|~x|||z{||}€|~y|€€€‚€€|}}}}~|~~‚|~€}ƒƒ„„‚ƒ„€~€€„€z~~€‚~€y~|}}}usuutuwxzwuwxwwwvwvswwwvvvvvwvstwwwtwvwzuwwvuwwwwwvxvwvvrussrprtrusxrusvopoturqpqqmlmmmmgg 7_fgi`c3opomjn&4Fvqpqm~O�kxoqpqqqurpjo-|tutuu,1…uwvq‚"�R‡uuuvvwvvwzwvvzz{ywx{|{z}{{|{yzz~{zyzw{{zzzyz~|{|}{|{|{~{{}{{zzzzz{}}}~|}{{z{{{|~|z{z~€}~~}}}|z~}|{{zzzzz{z{z{{zz{{{}}~|~{}{€~}}}€€~|{{|~}~|{{{zz{{|{{z|{{z{{{{|zzyz}|z{zzw|zy}zyzz{{xz{~||{zzyy{J9¤›ƒzmkWTI8qwyw{rt�vXvv~ˆ—7irtvxo‡ +9‡vz{y…I��xnvwwzuvwyxywvxzwvwyywywwwywvyyo[Œ €rxyx{y{|zz{{z{|~z{}}~{||}|{z€||~|||}}}}|{~}€€~}€ƒ…„„x}~z~€zz{|}}}}wysvvvx}vzzyxxwxxwvswxvtvxwvvvrwwvuuvzwxuwvwxxwwwyvvuzxvvvrussstsvsuvvrtqrrruqptqqpqlqomgf7[yjlcc <Zvmolp+���Iypnrq| d{qquuuttqljr {vutu|1&‡uwvt}.��[‚vuvuyyxwwvwvw{|{xyzz~z|~|zzz~zz|}zy{{{{{z}zz|{{}|{{}}~}}}z{~{zz{{|{~}}}}~|zy{~{~|~|~}{{z}}~}}|}{{zzz||~z{{{z}z~|}}z|}~~~~}}€€€~}€~}|z€z|{z||{~|{||}€y|{zz{z}}yz{{zz{||{{zzz{zyzy}{{|wyzzqXXuw{{b1� + 7ouyw{r| f`„vqk†7#csyyzq‡ 6‰uyzzN�zdrvvvwwww…„ˆŒŒŽŒ”•—š•˜“˜“‘Ž‹”¡„sxwz|~xz{{}{}|}{|{z|~~€€€€{~€{||{|ˆ}„}{|~|~|~~ƒ€„…„~}z~~€y||€||z~yzyxwy{{uzwvvsuxxvvvwvwwywwvvtsvrvvuvvvuvwuuxxxwvvvuvwssrruurtusrvssuussrtootrrtqqsomopogh(;grolfe�1:Ompoln1��1FVGJ`]ntqqsur€ƒrr �W|tssqvtpqkjl�|tuqu2�‰uxuu„0�Vwuvzxvvwvvvwv{z{xzyzz{|~z~|z|z{{{{z{{|z~|z{zz|y}|zy{{{}z}~zzyzz{~~}~{{z}{{~~}}~}}zyz{}}}}{}{z{|}{{{zzzz~}z{}zzyz~~~~~~~~~€~}}}}€€}~~~}~~~~|{|~{{{|~||||||{||w}t~{z{{|}~}{|{|{zz{{|}{yx{|{y}{z|yz||wuxwpo~k��-luxwysw�Fqvyvs†7P}yz{uŠ1uwyz}U�ss„…‡‰ˆ‡„{tikecSOGD>:750.€sxy{wzzxx~{z}{{{{y{|z{{{}{{{{€|}||}}|‚~€€}||~}}~€€ƒ€ƒ‚~‚…„|€z€}{|~x}~|{{{yywwuzzzz{t~sxvvvvwywwuxxvvvvwvwwwvwwvuuvwv{xxwvwwuuuutvuuuuuvwurvrssuswrurqrtrtomnmoopohq/Deonkgn���/47@CD^xnolt3�"—‘vszqpqttssrwvka+Tvqqrstqpokw�p|tpt€8„txux|6�K{wwzywuvvwuvwwz{zx{y{~{|y{{||||{{{z}~{y|~z}zyzzzzz{{|z}|~~{}{z{}}~{|}}€~{zz}}|}z{|~~z{z~}}}}}~|z|zz~{{zz|}}~}{z~|~~~~}}}~}~~~~}}ƒ~~}€~{~}}~|||}}~~}|}}{{~~~|z||z|~|z|}{z{{y{||{{}{{||{{z}{||{z{zwt|%_wzv{tvGvywy‚8�AP{{{zuŒ+‰uuvyƒZ�$/57.+����€rxz~z{{xx{{zy|~}{|z{{|}|†{~~€{~|||ƒ~‚‚~|~~€~ƒ‚€…€„„…€„|x€€~~{€||z{xvuwvyvyvxusuwvvsvyxxwwwvvsrrqrsvxrywvwxvwvyvuwuuvuwuuvsuuuuuuvuuusvutuvqrsrqrupqntmppqnlc2+&/BRepolgk%�ux{y|~vyqrrmpmplv6�-‡]nrrqpguqqqqqpmhv/ +F|spqqotoqql{n}vvvy>��~uvux};G‚vzy{uzwvsvwwwwwwv|z{{|z{{w|{{~~}z|~{zzyz{{xzzyzzyz|{~‚~~~{zzz~|~{{|}}}}~~|}}}|{{{y{{zz{}~}~}}}~~~{{zzyzz~z{~}}}{zz}{€€~~}}~|ƒ~~}~~~~}€}{|{~~}|~~}{z}~~}{{{|{|zz{zz{{{~{{{{zz}{{||{x{{|{||y{|{||zzvuu‚Uzw{un <M}z{zy~8 +`txtxuŠ /‰vwyyX����� ‚uyzz||n~yy{zz{~~~~||{{||}|||||{|{||}|}}|~}€‚~~€~~ƒ‚ƒ…„„‚|yyy{y{{~|{x}}}{{xwwxwwxxxxvvxxvvwwvzv|wyuvvvvyvvvwuzuwvuvxsvuvvysvswwqwwvvvvtuwvuuututuvuwrsqruqqormpqpooknw{wuulopkhl)�uVckllmppqpqppmplt:� 0€koqrroprqrssqummu7"�%A~mpusqrpqokyg€uvuvE€twvyv>�B†vwzzwwwwwvzv{||wyzzvwwwzzz{{{~~}~{}{yzvz}z{z}zyy}}€}}|{z{{{zzzx~{~~||~~}}{|}~~~}}}|~~}}|{z{z{zz|z{~~{|z{}~‚}}|~~~}~}~~~~~|~€z€~}€~|{z||{||y~{|z{zz{{z{{|z|zzz|~z|{{{{{z{zz{|z{|{z|||}|{{{u„ �<P{zw{ua3 Wyzzwy~:7ddmqt~‹‰”Šqrvvyv�(‰vyzyf,}uzz~z|}~z||||~~~~~~}{||{…{€|}~†€{}||}}|€}€~}|~€~€€€|†ƒ€€€|{|{|{€{~}…z}~y|xuwvuwuwwxxxxxwvwxuuvuwxvuvuvwwvsuvuvvxuwyvwuwvvsssuxwwwrsrusvuvuutrrsvtvuorqruurpqqrqqrqmkkkkkklpojjn,�cchlpopmlkjqlmlloqC +%}vpoorrqqrrorpqnpz26@‡ros{yrrqnmw*dyutuxZ�uxvwy€C>…wvyywwwwwwvwx{{w{|{wv{w{{{{|z~}|||~{||~{zx{~{z{{zyz~€€€~~|~~~}{{}}~z~~‚}}}~~~€~~~}}{}~~~}~}~{zzzz}}}{{~z{~~~~~~‚‚~ƒ~~ƒ€~}€€}}|z{|{{~z}{||y{||~{z{{{|||z{zz{zz|}zw}{twzzzz{{zz|{|{zz|yv…cuywzyl@<N_pokvywyx…= |os{}zvtvywuuyxyxx"�(‰yzvwZ+pv{zxyy||zxxz~~||~{|~|{{{{|}|~|}€|{~|‡~€}}~}€€}|||}€€~}„{z{{|zy{€{{wyuwtwquuuxwwxxwxwvwwzwyywsuuvxxstuwwxwuvwwwvusutxuvxuwwvwwwrxuvutrsrrrtrrrtssrrsrqpqqprpmkpopllmqolhl3� VshpoqoqqqppoooootF'Iurpprrrprtuqsrqpn{0$#@r‰tYONmrqpn~/X|urtyS�w}vwz}D�;}vvyxzwwvwwxwvzzz{{|wvuwzyz{z|{{|||}|{{|zzzzz}{{z{z{z}~~~~~~{z{{}}}{y|~~~}~|€‚}~~{|}~}~~~}~~}}}}}{z~~}|}}}}}~}~~€~€~~}~~~~€~~{{|{{}~x{{z{{z||z}{z|z{}z{z{z~}|{{{{}ƒ†Š“~yyy|{{|{{{{v†!„‡ˆŽ‹†uvwyuw…Œ‡…}zyzyxxw…>Šdzxxywyvu{{{{zz{zv‰*(†xzwub#������/>pv|yzy|zzy}|||~|€~}~€~~|~~~|ƒ|~{|~}~„ƒ„~€|†}}~€ƒ€€…€€~‡y€|}{„y|~{~ywwwrwsutuxyzywvvsvwxzvwozusvsrsvvswxwusvvxwvvwvvxvvwwwwwwqvsuvvvrrrrsqtoqrumqrqrqpopopplmmokpllmoolja? 5\xfkmmononlorpponqG8+%4qrpqrqotrtrqroprmtg|†~†•E99nrpoo{0Vvrtru`��j|tuw~H6€twxvzvuvuuxww{{zzzwwwwwzz{z{{|||}}}}|{{zzyz{||{|z{{{||}~z~{{|}|{{{{{}}}‚~}~|~‚}}|}}~~}{|}{y{~~{{}}|z}}~‚~}{}~~|}~‚~}{~~~}~~~}~€€~}~||}{|~z{|{{{zx|||{{{{z|{{yzzz{|{zz|{{wi]e[Q@<v|||z{{zy{zy.(†bmzvxwvvuuxyzyxxxwyy{{zuB‚mwvyztx|zz{yyzy{{u‡&#(xyzzw„U7����1.689<ACU]cjuqmjiuz|zzy}zyy}}|{||~€€~|}}ƒ}|~}}|}}ƒ~~}||~€ƒ„|}€€€€ƒ€€€€€€€{zz~|z{zz{€~{{wxyyuxuuuuwyyzusrsvwwwwzwzuuusruvxuvwxutvuvquwvuvwvvwwv{uvuvvvuuvsrutuquqrruprqurqppppqrpqmpopqoooookl@3 Dކy{nnptqqqpqmom^@JWeqstpmvmgnrntrqtttsqrtppqmpoomh\‘Z14h{opn|3H}ruqz\�!b{uuvzT4{wwxy{wwwwyzwvz{z{ywx|{{yzzz{{|~|~}}{}}y{{z}{{{~z|z}z|zz~}{z{{z~~}}|{z{z}|~}}~}}}‚}}|}~~~€‚‚~~{z}{}~}z~}}}}}}}~}}€~}}~~€~~}}|}~~~€€€~~~{|{z~z}{}z|{{yyyz{{z{z{{y{{{|{{{|{w}Y;€{{{zz|zz{{x†."€rrvyyywwwzwyyyyzz{wxzyzu†F~rwtw{u|||{|{|{|{zvGF‰v{zy}Q'�!(*7:AKSZbixu{}‡‚‡ˆŠˆ‘‹ƒŽŠ‡€~z{wz|~~~z}zy}|{{{}}|€~~z||€||~ƒ{|}ƒ€~{ˆ~€~ƒ{}€ƒ€€€€„~€~€{~||||~€{€wxrrwvvtrrtuustttrvwwvwvxvswwururvwwwxwuvvxvvvzyvvvwwwwvvuvwxvuwusvurrrsrqsrrqrsrqqoptrpopmrpopppoklmjTHB@@@?8244(;xpqpqqpppolopypvlrooopprrsnqrrrorrqorrrrrrrrrqolm +�cprn}9Grvq|Z$7V}uuuW$„|wvzyzwzzzywxwyzzzz{|}z~z|yzy}}}~}}~y~wz}}{zzzz{{{zzy~}~|{{{{}}}}{{zz{}~~~}~~‚€~}~~~}}~}€€{~~€‚|{|z~€{~~~}~€~}~}}~{}€}}~}~}ƒ~€}}}|zz{~{{|{{{{{{~~~||z{}{y{{{zz{|z{|zvƒQG##q~z{z{|yzw{xˆ+.‚y|zzvxwyywwxzzz{|y|zzy{v‚9.Q~tytvvvyzy{{~|~||{y‡"$1|z{{{}gXXu€…‡ˆŒ‹’”ŽŠ‹}st€x~vvxwwyyxwyzzzywwzzzy{}{{}}{|{zyz}z}}|{{||~€}z|{{|~||||~}z€~}ƒ€~‚~~~~|~~€|~€~~~~€…„„„ƒ~~~~|z}}}}|~||wyvvvvvzsvuwwwtytsswwvvvvvwyvuqvrvvwwxwxvvuuvvvzvuuutvwyuvvuuwsuuuvuswruuuuusqqrqqpmqspoppmqrqoppoooopkoyx…ŠD&>ˆpqotpppomqrtutrrssqsrqqrrppppqqsqqmrrqqoportpnld!T{qrl< @}qwqz^;��Kvxuuzl}wvvzxwyzzywvzxv{|{||{|zyzyz{yyy}}|{{{zz|v{}~~{{z{zz{zzz{~~~z}}}{}}~|{{}}~~~}}|~~}€~~~}~~~€}}{~‚~~~|~}~{~|~}|‚~‚~~~~}~~~}}|z~~~~~~~€ƒ|{{{zz|}||z}|{{|~}}z{|{|z{z}~|~z{|zu…N�!ty{{{z|w{{{vŒ9„z|{|||wzz~||{|yzzyyzywzx}F#.stwtwyv|yyz|||z{{{z}}‰‡Š‰”ˆvz{{{{z„Œ‚}ƒz{{{yyzzz{yxz{zzzz{{{{{|{}{z||||wzyyxz{z|}|{||z~~~~~||||€|||||}{€€€€€‚~}{‚~~€~~€€‚‚ƒƒ€|‡}ƒ~€„„…|~}}z|||€}€|rwuvvuutswv{xvvttruuvxxwruvwsurqruuwux{vvvuuwvwyyurvvwvytwzvuvtuvuuvuurrqrqrttrqqpqproooppmmlqkoqooppoolmiblY� )<‚qpqroppnmllmpnprooppqpqqrpppotprpnooppqqsrusqnnZ9 +9PsrrlxK<rurrfS%2427;@COLZgd[lltyzuo€tvu{xvvwxzyuxyx{z{|zy{{|{~yzy{{~{{||yyz{|}{z|}z{z{|{z~{z~}|{}~}}|{}}~}{}~~~~}~~}~~}}}€€~~}}~}}~}‚~~€€€}{}}}{~}‚‚‚€~~~}}}~€‚€~‚~~~~{|{zz{|}{|{||~~{{{}~|||{{z}{z|~||~{uwN + +!„w|||y{w{{|y5'�j{|zy|~zz{zzzz|yyyyzzywyvveTTipzmqwvvyzvy{|zzzz|{zz|zzw{yyyyv{{{|}{{zzz{z{{{|zzvyxxwvvvwzz{||{}y{vzzz||~|zzzxwwz{~~~~|zy|€}|{{}|~|€€z{|{{~|||~|{~~€€€‚ƒƒ€€€€‚€~~€~ƒ€ƒ…€ƒ„{~~{~{{||}}||}|~}yyvwvuusrvxyvuwwwvvutvuvvvvwuurvuvvxuvvusuuwvxwuwwvuvvuyyywyyyvvwuusrsrrrrrtqvrrppqmrsppoqmrmrmppqoooollllfcc�2€noqqpqqplpppoooqqtqsttqrrrqqoqqqqqmx‚‚‚~xrrqpllI ^|rrlyX7…rqquqn‰ƒ„……}zvw~{~|}yvwxzuwuuuwwww|z{yyxyyyz{|{z{|{{{zyzzz}{{{~z|wv{{zzzz}zz{|~{z||}}|}~}}}}}}~~}}}zyy}}}}|~~~~~~}}}}~~}}}~€~~}€~~~~~}}€‚‚~‚‚}}~~~€||}}~}~}~€{zz{{{|{{|{|~{{}{{{{{|{z{{{z~{{||u{O6 Ov}z~y}x|z}ytTB[v{}zy|}{z{{w~z|vzwyzzwvyyww…„xyvuvyvywww}{~z{{~{{{{{|{|{{{{yzz{{~}|||{|{|{|||{||~x{zxz~x{y|z{y}z}x|y~{z~~yzy}~y}|||}|{|}~}}{||‚|{{}~|‹|€}|z|~~‚ƒƒ€~|~~‚„~€€~~~~~€€€…{~~~€€{|}ƒ|tvvxuuusrwwxvvvvwwwsrtvtrrsuvvsuwwwvvrqvxuswuvtvuuxxwwuwvuvxtruuyvusqqrqqqwqrrsrrqroqupqlqlmmmljioooplkmlllef�1|mlqqpljjllrrvyyuvnpqqoqqtrqqqqqqqqp_FKNIIVlsrrnhe~n‚€turrlv_�1‡qpuuuuustvvuuuvtssttvuuuuwzu|~}tvxuxzyv{zz{zyx{z|zvy|||{{z{{zzzz}yz|yvzz{{}}~z}{{~|z}}}~}}~|}}‚~}~}}~~~}~~}€~~~}{|~}~~€‚~€}{}‚|}}~}}‚‚}‚€‚€~~}}}€{|}€~~}€~~~|{|{{{||~{{~~~~}}{{{{|}{{{{|{|z||z{wrQ:1 hy|xxyzzy{|ytz}spƒ|sy{{zz|}|zyzwww|wuwyzzzyyyzyvuywz|zwvxxvy~{zzz{y|z|{z{{zz{||z{{z{|~||{~|{{|z{{|||{wz||||x{{|zyywyzzxy|||~|yz{|z{z{}~{{{{}}|}}€~{}€‚|||||{{}||}~|}}}€}‚€||{|}~~ƒƒƒ€€€„„~ƒ€€€~ƒ~z|}€€€€svuxssuxrxvvvxuuuuvttxsutuuuvurvvwwxvvuvwusvtusvuvvvuvuvwwuzzzuuvvsrrtqrrturqssrrprrrtrslqoqpqpoonnoputqkkkeg�%mlppjmw|{xupd_YNTaqrrrppqroopsssqrrT.=hxrsttrqurpqstprlsb�)Šqqssuuttuvtptvuuuuuxyyvvvwyvl€ztuxuyyz{zz{zyy{z{zzz{{|{{{z{{z{y{zzzzyyz|{{||||{{z~}}}~~~|}~~}}}}}}~{zz}}~~}|~|‚}~}~~}}}z~~}}}}~€~~€~|}~~~~}}~‚‚ƒ‚~‚ƒ|~~~}}}|~~~~€~~~~€~|~|{z{zzzz|~}}}z}~{z}{~{|{{zzzz||y{zwsyx…‹~t|}xz{{x~|~zxwwv{xz{|zwy{z~{zyzw}{|~}vywxzz{{{zwvyyzzzyyzxzy{z}zz{|{{{|||||{|||{|~|{{}{{{|{||~{~|~zzww{||}w}{}x}~~|}z~z|{|||x~}~~{|{}|~|{|}{~|{}~~~{~~}|†|€{|€}‚|~~€|„ƒ~|{€€|€|~€€‚€€~ƒ€€ƒ€~~|…€||€‚€€‚rsrvuuusssusuxwuttsrsvruuuuuvusvvvtxvuuuusssrruuvwvvvwvvwuuvuuvvuussrrquwrqsrsrqqpqrlkllmqljgipjrqsrlbdillngj��"ypmoogS2, U{qqqolonppoopqrqwW1 4_yropomplpqnptqomnj� €qrrtutrrvuqtvtvvuuuuyzzxvyvpf†txwwvu{vw{zz{z{{z{|z{zwz|{{z{{zz{}{}~xzzz|{{{|{{{{{}{~~}‚|ƒ€|}}}}~~~}}}~~}€‚~|}}}|{|{}z}{~~~}ƒ|}}‚~}~}~~ƒ‚‚€}}~~}‚}}~}~~}|}z}~}~|{|~z{{{|}{||{{{{{{|{|{{z{z{zzz{z{z|{||{{|{zy{|zxwwzwwwyzzz|{zz|{z{{wyy{}|||}wxzxyzzz{{zyyyzzx{yzz{zyz}}~z|y{yy}|{{~||~}~~}}{{{{||{|{z|||{ywvwzxxwz{|y|{}y~{||}||||yt~~|}z{z|~{|{z|~||zz}~~|~~|}|}~||~}~||~|~~}||€w‚|~ƒƒƒ~}}~ƒ€~{~~€€€€‚wxswusrrrwwwvwwystr|tvwwwvwvuvvwvwrsuuvvuuuvuztzvwsvuvwvvvsttwsrqrssrruuurqussqpopprlnllmqgTHG@:75.!1@Khmqnkl{monol@ +GIoppooooqpnrooqrmn�\ymtrrppotqvutolopl�h~prrqqqqrtuyuvyyx||€~†……ŽŒˆ—› hwyxvv{vyzzz{yz|z{{z{}yyzzz{{{z~~~z}{yzzz|zz}{|zz|}~}‚{~€~}{~~~~|~~~{{{{~{~~~€‚€~}~}~~~}}~~~~~~~~~~€~}}‚~~~~~~}~}}~~}€~}~~~€~~}~~~|~~}|}~~~|z{zzzz{|{{{zz{|~z{||||{|{}zz|{{|||{|||{{w}}~|{{{|}}}{|||{yzy{|z{|zz|}}~~z|w}|}yyyxzy{{zzyyx{z|y}{}zz{~|z{~~~~~~}{€}~||}}|{{{{|}~z|{{{zy|~|z|y|{|z~~|~~}}|€||{|z}|{~|~zƒ{}}{}{{~{zx~€€~~~€„}~|€|~~€€‚„ƒ|~~{€‚ƒ}~}~€~€€„„€~€€€€€€wwrtususrwvuuxvtsssrsrruxvvuuuvwvuusvwvwvvvuvuuvvwuuuvvwvtttsruuusrrrsurrsrruuuqrrrrlmmklpbC0(<eoqnoiulokom9 E€qqqmppnpopqnqqonx +Zjhpprqpqrrssrpnoro +hŽ}v{~ˆ‡‡†…{€{znpohXUJFCB;4)W{zz{w{|zxuyzyvyvy{z{z{z{{zzzx{|{~z|{y{zzz{zy{{~~}}~~|{{~{ƒ€~~{~~~~|}|z~~|}}~}~ƒƒƒ€ƒ}}~~~~}}}ƒ~‚~}ƒ€€‚~}~ƒ€}~}~~~~ƒ~~~}}}~~|{zzz{~{{{{{|{{z{~|{|||{{{|{||{{zz{}~z}}{zzzy||z{|w|{{||~||{zyw}{z|~|z{}|z~wz|{zyyy|{zzzzywy{zzy{{zzyz}|}~}~~{|{~€{€{||}{}{|}{|{{{z{{{{{||{{{{~|z|~{|}y}}}}||||{|{|}~|zx{zz||}|}~|z|~~€{}~‚|{~}|}€ƒ|€~~|{}‚~~„‚€€~€€~‚~ƒ„ƒ…„‚€€€ƒ€uwsrstuwrvsuuxvtsvwvsrswwwwwvswvvwuyuvutuvwuuvuvvvtvuwtutvvrrtrssttusssrsusuqrqqrrrrkrnqkr^; &gkpkjbepomlq2 N‰df\gnosstttqrplo$ c^|†~rmpqrqrqoopsqgUTKC@;85-%%( �O‚vy{wz{y{ywyzzzy{|{{|z~}~~y{{||y}{yz}}~z~yzz~~~}~}}}}|~~{{{}~~}~~}}}{zz}~|}‚~€‚~}‚‚~ƒ~ƒ}‚‚~|{~~~}}~‚~€‚}ƒ€€€}}}~ƒ~}~~|}}~€~~~~~|z|~z|||{{|w{z{||}|{|||zz{z||{{{z{{{{zz||{{z|{zz|||{{{{||{{z{w}{|{}}}zy|}~|{z{z{{{{|~z{zzwyyywy{|}z~}}|}|~|€~{~||{{|{}~}}}}{|{||}{}||{{y~|||}{z{}|{|}}}|}{}{}{|y‚ƒƒ|~~~||{}~~~~€y}~€||~€|‚|ƒ€~|~~|~}‚€ƒ~ƒ€‚€ƒ„ƒ~………~‚„ƒ…ƒ„……|‚vxtsuurrrrrrwwwtsquursvvwvsvvuvuuwuywwusstwuvuruvxsyyuusvuvqtvvsttsurstrsrusrrrqrrqrmmlllln3 _iljfi01Utmnir1$F•uzz„~yyƒ}ywpprmo~#LR]NINXlrrqpqqpqqp|��K…wy{vzvvz{{{{{{{{{z|zz~{~z|yz{||}y|~z{{}}{{z{zy{|~}}~}}}~~|{{{}}}}}{~}{~{}}}}}~}‚ƒƒ~}€‚~~€~‚~}}}}€€€‚ƒƒƒƒƒ‚€‚‚‚~}~}~ƒ|}}~~~~€}{|}|}~}}~~{{{{~z{{|z}~~|||||z||{{|{|{|}|{~|||}{|yzz{yzy{{|{{yxuw{v{zx||}z}}~z{{|xyz|{zwyzy}z|zzy{{wwz{yz{{||{|{{||~~~}{~|{{{}|y|y}}~~{{|{||}|}|||{{}{|~{{|{|}{y|}}~|||~}}}|{}}}}}|}€~|{{~~~€{zy~|{{}{z~{€~~€€~|{||‚€€yƒ~„‚‚‚„~€ƒ~„€ƒ„ƒ€€€„€„€ƒ€vxsuuvuutustvyxsuvuvsswvvwswvvvruxvwvwvuuuruvuvvvutuyvutvprtvyuurttutsssrqqtssqqtsoplqkmkki1�4]}gdaw8 *QonomqE��7V^XaLJB?<>'Cbnpqm€1��F~luozpqrrqqz��'L„syzwuuuzzwzzz|z{zzzzz|z~z{yz{~}}z{{{z{|}{zz{zyz}}{{z{}}~‚~yz{{z}}}}}}~~~{{z}}~€~~‚€ƒ~~~}}|‚~}}~€‚‚}ƒ‚‚‚~‚~€‚€~~€~~~ƒ~~€‚|~}}~~zzz{~{z{{~{{zz{z|z{}}z||{z}||zz{{{{{y{{}z|}||{{{~{yz}z{{|{{z}{{{|zy{|zzyx|z|zzyzw{z{zy{{{y}z{wzwyyyz{y~{{{||{|~}~~||~}~|€{~~||y||~|{~~~|}{{~~~||y~}}||{|{|||||{|}}|€€}|}|€|}~|}~~~|}~~~~z€~‚{||{~~€|~~‚}|Œ||ƒƒƒ‚…„…€‚…~‚€~ƒ€„……ƒ…€‚€‚€†wvtsruxwsrrsuzxwwvruwvwuswtwrwwvvwvuurrvussssuuvvussyzyyvursuuvuuuvstsussrquvsrprqpmlplkkls4�I†…xea>96@aoqpojYE[wp}A06Qonpl{7�.@€`upqqppprqw@M„pxzzvuz{vy{zz|z{zzzy{|zzzyzz{{{yz{~{zz|~{z{{z~}~zzzy{}~~~~{z{zz}|~}}~}{~}}}}~}‚|~~€€~~ƒ€~~|~}}}}~{|~‚‚ƒ‚€€€}~ƒ„ƒ„‚ƒ€‚€ƒ€€}|}|~~{{zz|{|{zz|{|{y{z{z{{{{|||||}}{{{||||zz{{}{{}{{{{{{|z{{zzyzzz|{{z{|{z{{z{zy}x|{{{|w|{z||{zyw{yyvvzzzyz{yvzz~~}|{~~}{}|}€}{}|z{~~{zz||~}{||{{{{z~}~~||{{}|z}|||z|~y}y~}}~~}~~~{|z{~{~~z~~~€~~z~~{|||{|z~}||||~|{|||||}}~‚€‚}}~€€„€‚||€ƒ€„††„…‚€€„€€wwtsvuvyrvvwuwwwvwrwwwwwsyyxwwvvuuwvu~qvquuuuvsvuwruswwutsstsyvusvvwtsrrsspvuurqopommmlqjkj8DcgU[[8' +R}ot~{{otqqowwve`vi*Rzppmx:�;„lvpqrwquqqw2�������������M‚vyy{z{{{{{{{{}yzzzz{z~z{z|y}{yyzz{{~z}z{zzzzzyzzzzzzz{{~|{{}zzy}|{yzz{{{z{{z~}|}}|}}z}}~~~~€ƒ~~~}~~~|}~}}~~€€‚}‚ƒ~‚ƒ}‚ƒ}~ƒ~~~~~~~‚}~}}{|z|}~~~~|z~z|}{{{{~|z{{||||}~~}{||{}zz{||||}|}{{|~|{{}{~y|y{|{{{{|z}zzy}yzzzwyz|zxzz|}y{zzwyvxv}{zy}z}x~z{{}}}z~}||~}|}}~{|€€|~}~}}|||~y|{|||{{z}}}}|~||{||}}~}~}z{}~~}~||{}~}|}}~~~{}{}~~~||||||{|}}~€~~}†|}}„„…„…€|}~€ƒƒƒƒƒ€‚ƒ…†‡†„„‚€‰€†„„…ˆwxxvuuwwrwrsuvvtrvsrsrsvvuvxwvvvuuvuvzqqqruuwwuuuvrruvvuurtvssvussuwvuqqrrmmorrqqqmmmllplgj9�NR[mv„9 +LvikmmmnoqppoomkhfZ�XzqnlpB�9oruuusqoqqv52������ *3289?AFIPWTU_tyxzyvvwzz{vuvwyyz{{{{zzzvwxz|{yx~{zxzy{|}||zzy|{}}~yxzzz{{{zzyzy{|}|}|{{}z~|~}~~~~}}~~}‚~}}~€ƒ‚ƒ|~~}}~}{~}|€€‚‚€‚€}~ƒ~~‚„~}‚~}}~~}~}|}}~|~{{|z{{}{zz{}~|zz||}}|~}}}{|{z|zz{{z{||}|{yz}{|{{{|zzyz{z{{|{zzzyyyzz{{|xxxyz{yxyyx{zxy|zy{{{zyvyz~{{{~{{{~{}€~€|~{|||}|}|{z{|||zzz{{}{{{||}{|}}~z{~{}~z{}{|}~yzw{~~~|}~z|}{|}~~~~|z}€€€~}}~}||||~~}{|€||~}}ƒ~|}ƒ€…€~ƒ…€‚}‚…‡„‚€€€„†……€vwuuuvy{rvwvuxwwxwvvvyxyv{uws{uvwwwyxywwuvuvvvvuuvvvuvvxs}rvutt}strwwwwwqtrusrssssnrlpopljkJ�jiigev= NoklppompqurpoqqpbQ0OzqpkiD16}orruutqrtqn5 !(1257?@BF\Z`gnjqwyz…ƒ„†„…‚‰‹ˆ…†‚†~xuuuzyz{zzz{yvzyz{z}zzzzz{vz{yyzz|y~yzyx{}{{zz|~~~|zz{z}}~z{zzz}~~}}}}~{|}|}~~}}~~~~}}‚ƒ}‚ƒ‚}~~}}}}~~{}‚ƒ‚‚~ƒƒ~ƒ~~~€~~~~~~~~€~~~}}~}}}||||z{}z|||}~~}}~||||~||zz{{|{{}|||z||{€z|z||~~|{}y|{{|}|{z~y~xywyz||}||{yx}}z{||}yyz}y{|~|{{}}}z}~~}}z€{€€|~|~}}|}|{{{|}||x|||{{z|{}}}}}||||||{~~|{~~|z~|||z~}~~~{z~~~~|~}~|~~}~~€€~||{‚‚ƒ}~‚€}€~ƒ‚‚€ƒ€~~~€€€‚ƒ„ƒƒ‚‚„†€†‚~„€‚„…‡…„„‡vwwuvwvxtwuuvxwwuwsvvvvvvuvwqzuuvuuwxwuvvwvvvvwvwvutuvuvstuuvuusrrsvroryqtprsqjvssmpmllnkhdT�Ypokfs< JtoqquttqqqqopptpeQ$����);M{rrooI(( +!%.3orpptrrquuuwnxjq€…|ƒƒ|z€|~{xtqrwwxuzuuzuruwwvzyzwvvvvvvwvzz{{u{{{z|wz{z|{z{z{{{zzvz{{{{zyx|xyywyz{{zz~~zz{zz}}~{{z{{|}~z{z}}~~~}|~}}}|~}‚~~„}‚}}~~~~|~‚‚~‚ƒ„~€„~€ƒ€ƒ€‚‚}ƒ~}ƒ}}~~~||||{|~||{~€€~|{|z{{{||{}|}{|{{{z||z|{{{{||{|||zz||}}{|y|zz}{y|{zzyyz|y~|zy{{{zwyzzwzzzzyzyyy{~yw||{{z}~~~z{||~}€|~€~~|{{|~~~{yz|z|{|{{{||{|~||~}}||{||{z|~}|}|{yx}{z|}z~~|z~||}}}{{{||€|}||~~|~}}|{~€}~~|||}}|~~€|‚|ƒ‚€€†ƒƒzƒ„ƒ„‡€…„…€€ƒ…†††…„„wywvvwvxsvurwyyvvwrwvwvvvxxr}swvvrxvvuvwwuwvvwwvxyztvvvuztyturrrurustruvvvwsrsuuulpmqpljgfH $Jrtolk@"Glpqtrqoppopturrrh^4-&,/14:@EN_qrupusshy€‹†‡…ƒ|„squqptpuouvuuuu||yvvuvvuvwwxuwwyzzzzwwvvvvvvwzz{z|yxvvwwwwvvwyzzzz{z{|w{yz|{{{z{z{zyyzz}}}z~w}yyy|zzz{zz|~~~z}z{z}~~{}~~z{{}{}y}|~}~~}~~}~~~}~}|z}~~‚}}~|‚}}‚€}‚}}|~ƒƒƒ‚‚~‚„ƒ~ƒ~~€‚€~~}~~~~}~}~~~€‚‚|{{~|}{|}||z~~€}|}~~}}|}}}{z{|{|||zw{}|||~{~{|z~||{||||{{z{{zz|{{z{{{~yyz|{||||}y}~}}~z~z{{~~€}~{€|}|}|}}€~||€€}{~€|z{~{||~||{||~|~|~|~||{||}{z|~x~}}}~}~~~~{~z~}~}~~~}}~{}|}{{||~~€z|€‚|€}|}}„‚‚€€€‚|„ƒ„€ƒ€ƒ|„‚„„…„„…†††„…„~†„†…†……„ˆuvvwywwwswwrvwnwrwrvuvssuusrrrsrrrrwvvuvruuspquvvutursvvvwuwsusrssrrqssrrtqusqurqslplpqkmjiM1$:FstpjoK94Rkpptpponpmqpooqrje]Tfetywruwxtuxyw{v{sqsplolkmonnjnmlppqtqqqppruvqquvsowvvvwpvvwtwqtvvwvttuwwwwuvwwz{xvzzvwxwvztvxwy{ywy{{|{z{wy{z{{zzz|zyzyzz{}zzzzxvzz{{zzzz{~{{{z{zzy{|{{{~z|{z{yz}}{~}~{~~~}~}}~~~~~~~{|~~}~}‚~ƒ~~~‚~‚‚‚}‚ƒ€‚€~‚}‚ƒ}~~}}ƒ}~}~z{}~~|||||{{{{yz{z{z}|{z{|~||{}|{{{zy{|z{{{z{{{|}}~~xz}|z||{{||z{z{{yxzz||}{||zyzvv{wyxy|{z~wxywwz|{{{zzz~}{€}}{}{~|~~||{z{||}|{}~||~~~{z{~}|||z{|~|xwvz||}}|~y{y~y{z}|~~~€z€|}|}}~}{~~{{~||{|y†|~{{}|{~|}}}}~|{~ƒ}}~ƒ„~ƒ|~€ƒ„„†……†„€}„€…€„ƒuvxxxyxwsvwuwwwwxxrwuwvwwvvvrvvvwxruuwvvuvryxyvwwwvurvvvtwwwvxrvqurvuwruuussrrqqprppoollmlhZ02.-Bmuogl]QO_ibeltttttppqtpqpqqqppszvzwsosopopqqqqpptuqrpylronppopprrqstuvpvuvusuurvuvvuvwuvuvwxwyz|twvxv|{{ywvww{vz{zzzzzuwvwyuuwvzyxyyyyz{{}yzyz||yzy{zyy|yyz~z|zzyyz}}~z~}~}}|{{zzzzz{{z}}~}~{~{~}}|}}~~~}~}|||~~~~~}€|~€‚ƒƒ‚ƒƒƒƒ„‚ƒ‚‚‚~‚ƒ‚„„€‚€‚‚‚€€‚€~}}}~}~{|z‚|}~~}}}{{}~€{|z{|{{{}}~€~}z}{{{|z{{{}~{{||~€y||~yz{{{z|{|}{}wzzzz~{y||yy}vzyyyy{{{}vzzzx|zz{{z}z~~~|{†{€}}~}|{‚}}{€~|||€{|{}{|}~|{{|||||~y’v}{||~~~~y}}~{~}}}}|{~ƒ€|~~|~~€{|{€}}y„|}~~|||~|~€€€|ƒ~„„„‚ƒ~€€€……„…††ƒ†‡†…€…‡ˆ‚„‡uvuvxxuusvxwwuwwvxsuwvwvwuvuswuvwxrqswuuuurtwvwvsuvvvsrxvvsutrrussssrrrrsvuqrqpqmqpprppollkghsstznpronomkjpmllmqoptrqpppqnprollnonokeolomplooqkrrprtotqqoqomonqrqrrrusturtutrssssvuxwwuvuvvuzv{{yztwzzuvutywzzzxwvu{{zz{vvwwzyzwv|yxzyww{{~{wzz{{{{{zz{zzyzxz}{zzyyyz}xz{{{}{}}zy{yyxz~z|~|}{{~{{}{|}{€~}~€‚|~~‚}~~}~€~€ƒƒ‚‚‚„ƒƒ„~‚‚‚‚}€ƒƒ‚„ƒƒ‚„‚‚ƒƒ€€€„~}„~}|}~~€}~~}~}}~{{|||{z{|{{|~}}~~}|{z{|{ywz~|zz{z{zyux|{|{{{y|zyzz}yw|{{zz{}yy{yzw}yv|}{y|yz{vzzzzzzz}{{yz~}||‚{~}}{|~~~|||~|{|€z{{||z||{|z{||||{|~{~x}}z}}}||yx{zz{}}}|}zz}|ƒ{{{}{~}}~||~~~|{{{}~|{{yz|}}{|~}~}}~€}~|}ƒ€~~~}|{‚„„…‡ˆ„‡„…„†‡†€†ƒ‡…„uyuywwsusuwywyuvuxsvwwwxvvuusvvvwxwwwwxxrwswwywvrxwzwuu{vwrvuvuuwusxs|wzssrvrrqtrqotrtsqlqlllppppopqoloqppnmprprptuupppqqtoropppopqqqrppoooopronqtqrqvqpuvuvrvqtppquuttvrruttursuuuwwvwyv|wvyyyzwwwyw{vvw{z|ywvwxz{{{|y|vzwvzzyzvzvyy{yzzz{{z{zzy{{{zzyyyy{}}}|}}}|}z}~~}}{}~}}}zxv}z~~~||}{{z}}{}}~~}~~}|}}~~}}~~~~€‚ƒƒƒ…ƒ‚‚ƒƒ„ƒ„ƒ‚‚€~€€ƒƒ‚‚ƒ‚‚„‚†‚‚€~€‚~‚€~€‚€~~~||}}~€~~}|}{}~€~|}{|||||z~~~}{~}~~~~~~€wzzz|}z{{{}}}}~{ywyz|}z}wyz{z|zxyzxyy|wy{xvwwyy}v|wzx~~~}{}}€€{~~~€~~‚~}€|‚‚‚€~€{‚~~}{}|{{~{}|||~~~}}}~|~~€z~||}}|}z~~~|~}~|€€}ƒ~}}€}|z€€€€€{y~|‡{{|~~ƒ|€z€~~‚„„„ƒ…€…‡‡‡ˆ†…†……†ˆˆ‡ˆ‚……†uyuysssvurrzxwvvvwsuwwv{vvxuwwvwvwwuwvwxqsuvvvvuuwvwuvwwvurtuwswussssrruuqtvrqqpqqprlllppslmonpnkppoqoolpopppnrqpnppqqqqrtpppqrrrqqqtqqprnopqrqoutrqrpqqtttuurrqrpqqrrwusquuvvwwvuwvwuvww|wuyzzu{zvvvyytzyvwwuwwxy{y|{z}vxwxzxyxwvwwwyzz{{{{}{yzzyz{yyyy{z~~}|}}}{zzzz{|}}{{}|zyzz{zy|z}~~z|}|{{{}~€}}}}}}}}}~|€~}ƒ€~~~‚€~~„ƒƒ‚„†„ƒ‚‚„ƒƒ‚~~‚~‚‚ƒ‚ƒ‚‚ƒ‚‚€€€}ƒ~~‚„€~~~~~z|~|{z{|~}}~{}{~|{|||{zz{z|}~~{{{|||||{{~|x{{y{}{zz|||{z||{zywz||||}y{{zz}~yz{}|{y|}wy|x€vzzw|wwwwww{}}{|~}{|}}~|~~~}|||€€€€||||~~€|{{‚}{|||~y{||~~{|{x~z~}|{|{|z||}~z{z{}}~|~~{{|~~{~~}||yx{}}z{€|}{{{{|}~‚~}~}{€~„ƒ€~‚€€…ƒ‡†ƒˆ†…‡†…††††ƒ†††„„zzxzsyrvvƒrwwwvrvwvvwwwyvwvvwwuyvxwvvxx{zztwv{vtuyvuvzxvvursvxuusussssrssvqurrrqpqrqopproqqrokoqqpqqqqmqptmqooprmtrsrsruqtoooopppqrqoooorspoprttqrooqrqurtuuutrrrqqqqqqrsrrwrpquuuuuwuwyv|uvvxwwzzzzwwwvuvvwuvvwyzyw|{z{wyvzzzyvuww{vwzv{y{{{|zzz~yyyzyzyz}z{~}zzzxzz{|}|}||{|z{zyz{z{{}}z}|{z{z~}~~~~‚~}|}~}€~€~~‚€ƒ‚‚„~ƒ„‚‚„‚‚‚‚~~„ƒ‚‚ƒ€€€„ƒ‚ƒƒ‚‚‚‚‚„~~€~~~~~z{{{{z{z}~|}}z~{{z{||{|{z||~€zz}~~~~}}~~}zzxy{}z{z|||||{y{~w{w}z{{}||z||||z|{y{zyz|zwvvy|{|wzzzz{y~|}{z~~|{||~}~~~}|}€€€ƒ~{}~~|~€‚ƒ‚„|}{|||{}}|||~|||}||~~}~{}|{{~|~~y~{{}~||{~~~|~~~~{yz†||}{z€€€{‹{‚~~}~~‚|~~€}ƒ‚ƒ€€€ƒ~ƒ…‚…„„……†…†……†††…‡„„……†vuuxryrstsswsxxuuvvvwvxxxvxwwwwwuyxxvzx{vvvvvwwwvxywywtuuusruuvsuvvrssstrrrqsssqqqmopoopooltqmqqqqpppopooqoppqppmrqpomopqropunqqprsrrrqopnopptqrsoopsrqqrrusuuvvvttqqqqrsqrqrsrtuuuuwwvvuywuwvzywtwxzzz{zuwwvvzw{vwx|y{zvwwuuxwyzzzyvyzy{{zy{|{z{zxyyuzy{}{{{~|{zyy{|~{z~}|y}zz~{zzz{}{zz{}{{}|{~~~~~~}}}}}~~~~~}~~ƒ‚‚ƒƒ‚ƒ„‚‚‚‚‚ƒƒƒƒ‚‚‚ƒƒ‚‚‚~~~~„‚‚}‚|€~€}}ƒ~~€{{{|{y|{}}}|~~z}||{{zz{{{{|{~|zz~|~}~z||{zywz}{{{{{z{||y||y{wzyz|{zzzy~{{zzxyz{{|||{yvyvyyz|vzzz{xz~}|{|||{|}~|||{~~|}~}}|€€}€|||}|~{z{~||}|{z|~}||{{|}}}||}}}~}~~~€z{}z}{{|}€}{||||zz|€||{||{}|}~}||{‚}€„|€~~~}~€€€€‚ƒ„…†ˆˆ†……„…„…†……†ƒƒuwyyvwuŒsvrwwww|wwvwuyxwxxuwwwwwsxxww{wzwwuxvuuzvuvwxysutttuuwwwwwuusvutrxrusssrqporrqopoqnymqrrqrqoqpoqppoutusrnqrpoooppppqqoqtuurttrqpopqoqsuvppqqrtqurtusrvuuuuuqpqruvuruvusruuuxwywvuwvvwv{{zwwyw{{zzzvxwwxwvwvwvw|zywvwvxvyyzzzy{wyy{zzz|zzz}zywyz{{{{z{}~|zz{|~~{z~|{{}yz|}z{{~}z|~}{}}z||}~~‚~||}}~~‚€‚~~|}~~~~€‚ƒ„„ƒ…ƒ‚‚„ƒ„ƒ‚ƒ‚‚‚ƒ‚€€‚„‚€€~‚~ƒ‚ƒ‚ƒ‚‚~~~~~~~‚{}|{{v{|{}€}}||{|{~|{{|||{{~€|~z}|}}~~€€|{z{z{}~||z||}{|{|{{{|z||z{{{z{||{z{|||{zzzzyyz}zyyzyzyyyzz{|~~~~}~|{~|~}|~{|~€~}}€|€~~~}‚~|~|~|~z~|}{{z{||||}~~|{€|~~~~~}}~~z}}}~~||}~~~||||z|z~~~}z€~€{{~|‚‚|ƒ}€|€}€€„‚ƒ…‡…ˆ‡‡ˆ‰„…†ˆ††…†€…ƒ…vtuutttssxrrvwvywzwwvxuuutvwwvxvuwzyvwxwvvxxywvuvvtyzxuuuvxwwwwxyuuvuufwtyrqrrrqrqqqrpqpqrntlqpmpprpqoooooppppurpoqolnppopqqtoptqurtorqppnqpropnnrqqqtqrrrusrtutvvvspprvwwquuustuuvutxvwywvwwv|{{wvyvwvzvvvvwyzvwwwwzy|x{wvtvvvyuywyxwvzyz{yy{zzz}zyvwzz{{{{zz|}~~{z~~~y{yy{}~zz{~{zz|}z}~}{{~{~~}}{€{~~~~|z}}~~}}}€ƒ‚ƒ„„…„ƒƒƒƒ„„‚‚ƒƒƒ‚‚‚ƒ‚€ƒƒ„‚~‚€‚}ƒ‚~ƒƒƒ‚‚~ƒƒ~}}}}|~{x{~~~~|{{|{|~~}~}|z|||}{||{{~|}}t€|z~~}~}~~~}|{{|{~}{{zz|z{z{z{z{{|z{{||{zyzyxyy{zywzzvz{{|~~}}{{}~}{}}}}}~}~~€||{|||{}‚|€€€€|{{{}{||}{}~}}{{{}}€{~}|z~}~~~||~{{~}~|{|~~}zy€~}y|€}||{|}}}|{}~}}||}€ƒ€}}}}}~~~}~ƒ€€„………„…„„ƒ†……‰t‡…ˆ‚ƒ‚ttzzz{vwvxs||xzx{vywxuvuuvwvxwwuwz|vwwwvtwyyyuzxysyy{uwwzwzvyxvuuuvvvvywwvvsvprrspmrqpoqumrrrpvqoquqppoqunoopqtqopqrutrotpnooptrtrspuqqqoqrrrssmtrrptrrrtqrrusvuxqqqwuvuvvutuuutwuvvxz|zwvywz{{zxvzuvwwwxv{zzyxvwyzz{zvywv{vvwww{yxxzv~}~{~z|xzz}z{|}y~z{|~{z{~}}{z|~|z|~xz{~z}z~{z{}}{~}z~|z|~}~}~}}~~}}~}~}|‚~}}}€„„ƒƒƒƒ„„„„„„„ƒ‚‚‚‚‚‚‡ƒ„‚†ƒ‚ƒ„‚€ƒ„ƒ‚ƒƒ‚}‚„‚‚€€~~~‚~{v{}{x|~~~~€€}|{{~|{|~{{{|{~||{|{~{{{}w‚{{{}|~~€{|}zz~}|~~|{z{{||~y|zz{}|||}{}}yzz~}wzy|y{xz{}}~|{|~}}}~{{{}~~~~|€€~€~|‚€~€‚}}|}{}~}|}}{{~||}ƒ||~}}|}}~~}~~~}~|}{zw|~€~|{}~~~€€y||}€~{†|‡|€€€|‡ƒ„ƒ€‚~€ƒ}ƒ„€„„„„‡…‰…ˆƒ„„ˆˆ‡‡ˆ…Šˆ‡‡ˆyx{{w{uvvxuvyxwwwvvvvwwuwvwvvxvwvwzxwxvwwwwwywvzuxuzy{tvywvvvyywssuvrvwsrqrrrqpqrqqqstroqqlooqqqqqrqqqpproooppopqmmoonnmoooqqssrrqstqpqqttsqpoonqnppqqqqpourtrsvqrqrrruuquwsruuttuuuvvzx{{zywy{ywxvzuvvwwww{uzwy{zzzzyzzzyvvwvyxzxzyyywws~{{~|zzzzy{ywz{zy~}~zz~}|}~~~{z{zzzz}{}~{~{}}€~|{z{z{|~}}|zzz|~}|}~~||~€‚‚~‚‚„ƒ„„„„„ƒ‚~€„ƒ„ƒ„„„ƒ‚„ƒƒ„‚‚‚‚€€‚ƒ‚ƒ€ƒ€‚€‚|{}}{|}}}}~~~|}}~|~€|z{}|}~{{~~{||{z{{{{wxw|{||{{}||}{z}||{z{~{||zyz}y{{|{}||}zz|yzyyz{{~w|zz{{w|{{{vzzz|{}{{|}~~|||~~€€€~ƒ~~{€€€}{~~}{|}z~{{}{{|||}|~~~||}|z{z{~}}|~|z|yyxy~}{{}~~~{~ƒ|~|}|{|€|…|€€‚ƒ€~ƒ‚€€‚‚ƒƒ„ƒ€„~ƒ…‡…‡…ˆ……„‡‡………†‡‡……ˆzyyy{{uuxxvwuuuxwxwvvvvuvvvwuvwzzxvwvxwwvuuvxuvzyzuzz{xwyvuzuwwxrtuwwxuzsvs|prprqrrpoprpopppoqpvpsssrvorpopprtttpooooootmrqsqqqopqtuqprrqpqqptqrqqopqrprprrqrurvurrrstqvrqvqruuququwwwvvy|{xwxy{wwwzv{uyzzw{z{yyywzwvuzvzzuuwvzyvwzyyyy{|}{yywyyy{{{yyzzzx~z}{y}|{}~}}{{}}}{z}~~~~~zzz~}}}}{{zzz{~{{~y{zz~}}y~}~||~}€~~€ƒ‚‚‚ƒƒ‚‚ƒ‚‚ƒƒ„~„„…‡ƒ‡„†‚ƒ‚ƒƒƒƒƒ‚ƒ‚ƒ€€€‚~~€‚€‚ƒ~~€‚}z}|z{{}~}~~}~~||zzz{|{{{~}|{~|{|~||zz{zz{|}|}{|z|}}~{|{{|{|{{||{z{}w{z}{}zz{||}|}w~{{z}z}zy|zw}wzzzz}~}~~{~~~}~~~€€€}~~~~€ƒ~‚}~‚~€|||~|}{}}~z{{~{|||~}||}|}~||}~~~|}}~~~€z~{~~|{}~€‚€€~~~|‚‚‚‚ƒ{ƒ~ƒ~ƒ„‚|‚‚„ƒ€…ƒ„……ƒ…„„‚„„„††…ˆ……ˆˆ‡‡ˆˆˆ‰†„†ˆzz{{zzxwwwvvtuuyxyyyyuwvvwwyvuxzzwvwwwwvvuuwwxyzywwzv{zxyvvvwwxxruwwvwwyrusqrqpqqqrqqqrponpprqpoppqqqpommpqpommopooomqqqpnoooprrqtuttporrqqpqoqqpqqqqqqoqprrusrvvvqqstquuuuuvuuqvvuuwwvuzzzzwuwwxuwzvvuvvzwz{{zwwwzzvv{wzuvvvvyxwxzxwzzz{{zzyy{zzxz{{zvxzz~y}|z||}~{{}|~}{{x{}~}~}z€{{z{{{{{z~|{|z}z}}}}~~|~{~‚‚~ƒ‚~€ƒƒƒƒ‚„‚~‚‚„ƒ…„„…†„„…„‚ƒ‚„„ƒ€‚~~ƒ€‚‚‚~~~~~~~}~~||}}~}|{{{{~|{z{|{{{|~~~~}{|y~}{{z{{{{}{{~|y{z}|{{~|z{|}zxz}v}z{y~{}y{x{|||{{{{z{zz|vx{zz~~}}z{{}~~~~~~~|€‚~~~~‚||}}|€‚€|}|}||{{||z|{}|{|~}}{{|{~~}~||{~~|}~~€z}}|}€~{{}z€{~ƒ{{~~~~~|{{}||ƒ‚„„ƒ€‚||~‚€€ƒ„‚ƒ‚„ƒƒ„€ƒ‚ƒ…‰……„†‡ˆ‰‡…†††…zzzz{{xuwyy{swwwvxx{wwwwxywwwvwzxwvuwwuzz|uzvvvvuwuzzzzzvvtzwwxxsvvwvwvwwtswurrwqsqrqqqqororrrqpquqrpuusruppppqppomqlrpnonospmqrqtpoqmmqpoqqqrrqpqrpqqrpqrrrrutvuwuxruuvvvutrqrrvvvvvvvvvwy|vwvywvwxyzxxwwwy{{ywxyyzyyvuzuvvvxvxywvvx{||zzz}y|zyyzyzzzvzyz}}|}}}}}|~||||}}|{zzz}}~}y}{~}}}}~~{{zz~zzz{}}{|}~|zz~}~}~}€‚‚ƒƒƒ€ƒ…ƒ‚‚…€~€‚ƒ‚ƒ‚„ƒ‡„ƒƒ‚‚‚‚ƒ‚‚‚ƒ„„€‚‚~ƒ~€€‚‚ƒ~€~€‚~{~z~~}}}||z~}}}~|~€z~~|||€{~}|~~|z{||}~||~|{||}||{z|~~}{{|}|||}{|}}v~z}}~~}z}yzyz{{{{zz{zz{zzz}~~~~~~~~~€~~~~~€}~„‚€}‚|~€ƒ|}}}{{{|}z}{~~~~~~}}~|~}}~~{~}~}}~~|{~|~}~~|~~~~}}ƒ~~~~€ƒ€…ƒ‚ƒ„„„„…„‚}„ƒ‚…†„„„…„‡ˆˆ…ˆ„…†‡ˆ‰‡„„…†‡z{wxxxxyyzssrrsvwxwvwwywsxwwxyw|vywvwwturuuwvxtqrxuwywuwuvuuxuuwrvxvtsutvvuuvuuuqvsqqoooqposurspqrpmonpppppoqpnppoppmrompoqqtmqqoooosrqpnosqrqqotrqnoqrqsqroqsuuwuqqrpqvrpquxsuuuuvvvvxvvvxvwvwwwz{xxxxv{ywv{zzwyyzz{yvwzuvsuuxwzzzz{{wvvyyyuzyy}{{zy{zzzz}}}}{}}||}}}}}~|{||~}}z}~}~}~}z~}zz}}}|}}z~~~~}x~‚…„‚‚‚ƒƒ‚}‚‚€‚ƒ‚ƒ„„ˆ†„‚‚‚‚‚ƒƒƒ‚‚‚€„‚‚~€ƒ‚~‚‚~€~z~}}~}|zzz{{{{|}€{~||~|~€€z~|~}{|{{~€|~€~~{wz|}}z{|}{||y||y||zwy{|{{zxxxzyzzzzzzzz}zyz{|z|zsz{{€€€~~~~~|~€~~}~‚€€€€}}}ƒ|~€„{|~~~|}|||{}|~|z{{{{}|~|~|}~~|~~}~~}~~~~~}~~}|}€~~~}z~€€|~~~{}~…‚|€}„„€…€‚‚ƒ€€~€…ˆ†††„…ˆ‡‡w†„ˆ‡‡ˆ‡†‡xyvwvwxwxyvwyzwwwyvvvyvxwwuvw{v|w|uwxxs~tvuxxztyvvvuwwwwvvuuvwuvuvwusuutuvrssrrrpvqqqqprquuurrssqrlxuvoprynoppprppqmppprppnlmpnqpqorrsqonpooqqqttqroprrqsrropqprrrruqqpwqruvuvuvuqtvvywxvxvvvywywyzzz|zzwwwwyz{wvwvv{{vzyvvvvwwyxxy{{{|yzzyvxyzy{{}zyyyz{{}~}}}{z{}|}}~{~€}~~~€~{{{~}}‚}~}}}{}~z{|~~~}}~{~~~}‚„~‚‚‚‚~‚‚‚…}€ƒ‚€ƒ†………„„„ƒ„ƒ„ƒƒ‚„ƒƒ€„€‚ƒ€€~‚‚~|{}}}{|{{z{z~|{{{~~{}}}~||~||{z|{||||{{||}}z|€€~|}|}{{|y{}|z|||{zz|{y{z|}€y|{yz~{zz{{|yzzzzzzzzzz|}~~}}€~€€~}€€}ƒ~ˆ‚€€€|‚|ƒ}€€€~{}{|~|{~z~|}}}~~~€€~~z|€€|€€~€€€|~‚~€‚€€{€ƒƒ}€‚}€€€…„‚ƒ„ƒ„…‚…††………„…†‡‡ˆ†‡‡ˆˆ††‡xxyxxxwwxzzzzwwwvvwxw{yvwvtuvuvxuyuvrxuvwvwvvzvzxwzvuvvvyuwtuwwuutwsrssssvrusqrrrrrrppqppoptrqrqqsmllqqpronppooploooolkppouppnlloooooqplsoqopoooopqooqrpqqqpvpprusqqqrrrsrvuvuutuwvwwvuwvuuuvuwwyy{z{zzz|wwwwy|vz|yz{wzzwwxyuwvvvw{wz{{zyzzyvvzz{{}~|z|}zzy{|~|{z{}{{}}€€~|}|~|z}~}~~~~‚}}}~~~}|zz{~}}‚‚~‚€~ƒ€ƒ‚‚‚ƒ}ƒ‚ƒ€~~}~~~}~ƒƒƒ‚„„ƒƒ„ƒƒ‚ƒ„„„„‚ƒ‚ƒƒƒƒ~}~~{{|~|~}}{|~~{||||}}~~~}{|}}~{~~~||}{}€}}{~~~|{|~~|{||~~}{|{|||{{|}{{z|{|{z{{z}~z||}}z}}}|{{zxy||zzzzxz{}~~~€~~}~€|ƒ~~€€€{~~~|‚}}~|}}}}x|~}}~}}yx~}{~€}}~{|zz{}|{}|z}~€€|~|€~{~}|~{{|||~€€ƒ€~~€ƒ~€‚}ƒ€€„…†„‡…„„…‡ƒ……‡…‰†‡‰ˆwwxxxxuzxzxwvxxxxyy{v|z|uwtƒvvvwuyzxwwuuwswuuyyzyywystusswwwsxvwssrsstvrswuussqrrvvvqrrqpunrpqqrrvpopppopoqupqprooltlmlqppopoqqqoqpqqpoooprpotqrpqoopqqqqqqpruuwrsrqqrrysrrssuqquwuvwwvvvvv{vxwywz{{zwuvvwwwvz{wyzzzyz{{zzwvvuvwyzz|vyzzz{zvyzvuz{|~~~{yy{z}{zyyz}|~||}}~~z|~~}}~{}}~}~}}|z|~~}}}ƒƒƒ‚‚‚‚‚„‚‚‚‚‚ƒ€€~~‚‚‚~~~~~~‚~€‚ƒ‚‚ƒƒ„„„‚‚€‚ƒ„„„„„ƒ„€‚‚~€‚‚}}}}||||}{|}~~~}}~€{~~}||z|{~~~~|}}}}{{{|€||~~}}}~{z|}||z~|{{z{}}|||z{|}{}|}{z{}zz{zzyy|~zyz~y~||~€}}€~~~~~~~~~€|„€~€€€€~€ƒ|„„ƒ€€}‚€€~~}~~|‚|~}z|‚~}‚{€€~~|}}}ˆ~~~~~~~~~}~~~€€|ˆ}~~ƒ€€}€‚‚„ƒƒ„‚‚…„‚‚„†‡‡ˆ‰…„ˆ‡ˆ‡ˆˆˆwvuywyuyxwwwwyvwswwwwxzywvsrvvwxuwoxvwuwvrzvvxvwwvuuvvvuuvusttuutrstuurqsvvtrrrsurpuqsrqnqooppqqqrroomomqqqrppqqqnouklkrqpoommlmopoqooorplmqosrrrponqrrpqqtprsttrqrrrrrwsrrrrqqpuuvvwwyw{uyzwwwwwx{zywzuvwwxwzzzwvwyzyzzwxzzwuwwwvvvwyzzz{zyzzywyy~|€~{{{{z{{{}~|}}~~~}z{|~~}~}z{~}}~{€~~~~~z}}~~}‚~~€‚ƒƒ€~‚„ƒ‚€€~~~~‚~}~ƒƒƒƒƒ‚„„‚ƒƒƒ‚ƒ„ƒƒƒ‚€ƒƒƒ€ƒƒƒ€‚ƒƒ€‚ƒ€|~|~€{zzz|}|{}~~~}}{{{~~|z{{~~}|}{~|}||{{|{{~}|{||~~}|{|~~||}~{{||{|z|yyz}}y|y{y{|zz{|||yzz{{{zzyz~}|~€~}€}€€~}|ƒ~€|~~~|~}ƒ}||‚€€|}}~~}}~~|{{‚}}|x~€}||{}|~z|~}}~}~~}~~~~}{€|{~}{}€}}}|~~~|€|‚~€~€ƒƒ„uƒƒ„€ƒƒ……ƒ„„…ƒˆ…ƒ„‚€‡†‰svwwwyxvvwwxvwvwwwwvvvwxv{uvuvuvvvvvvwvwuuzuvxvvvvwyyyvvuztztvuxuvwwrussrtvutwvussssqrpppqpqqrqvqrqrqqpnptqtqroppnotlnmxppoooplooroqopotlompptsssrqorusqqtsruvrsturrswwwqursrrrrsuuxvwzwzyy{zxwxxzwzzwzwwzvxx{zzywvwyyz|zzzwvvuwv{vwyzzyz{z{yvvvz{}}}{z{zzz{zyz{z~~~~}~{zzy}~}~|z}~}{~}~€~}}}{|}ƒ~~~~~~~~€ƒƒƒƒƒ~~€~~ƒƒƒ‚ƒ€}‚}ƒƒƒƒ‚„„ƒ€€„„‚‚ƒ„‚€€ƒ„„‚„€‚‚‚€‚€~}{z|€|{{~~~{}€|€~~~}~~}€ƒ€~~~}}~}~~}}|{~€{~€{|}€|€|||}~}{||y{|}{|{|{{||||||zzw|{zz{z{z}|{||zz|y~~||€||€~~~€~}|‚|€}€}|{}~€|„|€€€€~}~~€€‚€‚|{}}|~~ƒ~~}€~€|ƒ~‚~€€~~ƒ€~~~~~|}†|‚€€~}‚ƒƒ}€€€„‚ƒ„……„†…†…†„„„„ƒ…„…ˆ……†……ƒ‡…ˆuuusssrvwwwxrsvwtvwvvwvtwzxxuuvuvuwxqwtvvvzvvuuuuvwvvuvuvutzvwuuwvrrsvssvuvqvvsuqrqoppqoqoqpqnoroqqplopqqqopppoooloqqqpmnooooloqppploqtqmmpprpqqqqttruqpqtrtutpuurrrsuwvrrrrrqpqquuwww{yyyxv{{wwywvzxxzvyvvvvvvyzwwxwywxwwzuvvvwv{wwzzzwz{{{zuyzz{~{}{z{}{}|{|}{{z{~€~~}{zz{~}}{}~}{~~~‚}~€‚}}~}}‚ƒ€~~~}‚‚ƒ‚€‚€~€}‚}ƒ„ƒƒƒƒƒƒƒƒƒƒƒ„‚ƒƒ„‚~ƒƒ‚ƒƒƒƒ‚‚ƒ€~~{|{|~~{z~~€}}||~|~€}€€~{~|~{}~||~}{|}|{||{}}}|||{~yxz~{{~||{}|z||y}{yyz{||zz||||}||{zzz}~}}z€{€€}~€€~‚|€}~~|~}}{€{€~€~‚‚‚~~{|~~~~}}}|{{||||~~~€v~}}~~}~|~}~€~~~~~~~~‚}{~~~}~€}~ƒ}|}{||~€}|{|€}ƒ€„„{„‚„……†„…ˆˆ„€‡‚„…†„††„††vusssuvwwww{rtvxqsrvvwuvuzvvvvvusuuywwuwwvuuvutvvxyuvutvuzzzswsuuww{twqqvvtussrwqsppppqppnqpqppqprqpprqppuqqpoqqoomopmmmmxlnnpmuqoprqsqtrsppmoqooqqqrvqqrtttrtuvursuuuurpqpprusquuuuwyy{zyyyz|{zwyyxvvvwvwz{{zz{{|wxvyzzwuywwyvwv{zxyvzyy{{{zvyy{zzz{}}}{{|}}}}zyz|~~~~z~~|zz|}}}~~|}|}}~}}}}|~~‚‚‚~‚~}}~€€~~~€„€‚€~}€~}„‚ƒ„ƒ‚€‚‚„ƒƒƒ„ƒ„ƒƒƒƒƒ‚‚ƒ„‚~‚ƒ~~~{~z{~|z}}}|}|~}}~€€€~€~€~}~~~~~}~~}|{~€~z|~{{|}€{|||||{}{|zz~}{{zz|}zz{zyz{|{y|~~~||{}{€z}~}{|†|€€€~}~€|~~}‚€~~}€}|}~~~‚„~€y‚}~|€ƒ~~{~~~|~~~}~~}}|‚|~~}}~~}~~~{‚~ƒ~€~}€ƒ~~}}ƒ€€€~}}~€€€|~€{‚‚ƒ‚€„……‚‚…†…„„‡†„†ˆ„†„„„……†…ˆuuuuuruwtvxyssswrswusuvtwyuuvuuuuuuzmwvvtwuuvvtvzwzvvvuwwytuuwruuuvytsrvvurssqrwqpspqpropooopqqpqqqpppqnotrmpppppmqpsprrmmnnpqonrqlsqoqrusqpqpqqrrrrtttrsqtsouuuursuuuusrqrpuuqpvuwuzzw|{xxywzzy{z|w|u|{|z{{wz|{{|wwwvwx{uzvvxwww{zzyyzx|{zzzz{{{z|z||{}{z}~}}{zy~{|~~~z~{z{{~~}~~~}~~~~~}}}~}}‚‚€~€‚‚}}}~|}~‚‚~‚ƒ~~~}ƒ‚„€‚‚ƒ„„„„ƒ…ƒ„ƒƒ„‚ƒƒ„ƒƒ„„ƒ‚€‚€‚‚‚‚€{|{}{|~}|y~{{zz}{}}~{~€~~{~~}}~~||{{{}€|||{{{}~{|}z{~|{{|{{||{{|~{~z|{y{z{|{zy|{{z}|yz~z~}z{~€~{|~}~}}~{}}|€~~€€z~}€‚…‚{„~~€}ƒ~||}||{}~~~~~~~~}|||}~€€~}~~~~~~~{~€~}~~~~~‚{€{|€{€|}}~~~€€~€~~€€}~€~}~ƒ€„…€„‚ƒ†‡……„†}††……ƒ„……ƒ…ˆuusvuuvvrywywsrwqvvsrrvuvwvvwvvvuvuzqvvxuvsvvvtzwuvvwzyyvxwwxxtuuuwxtywwwuqsrsrvoqsqpoqqromopprupqooppppmqpopqppmopppqptmnnomttrqqqrqpqurqqttspttqrrrurrqqrtrtuvtruutywwsqsrssrrstwtz{||||{{wywyyywwvw{z{|{|zzzz{{{{||wvzuuuuwvxxz{|vyz{{{zzz|zz{y}~z}}}|{|~}}}{{|}~~~}~~y}zz{{|}|}}}~~~~~}}}~}~~~€‚}€~|}}~~~‚ƒ„€~~‚„„}~~}‚}ƒƒ€‚‚„‚†ƒ‡ƒ…†ƒ€†ƒ„‚€‚‚ƒ„€ƒ…ƒƒ€‚€€‚‚ƒ‚ƒ~~{}z~}}{~}|€{~{|}~~€}~~~~~€~~|{}~}~~~~€|~{~|{||}z{{z||{{}|{|~||{{{}y{||yzzzz{|~w{}}|{y~€‚‚‚{‡|€€|}}|}€{€|€}|}}}~~€…Ž{‚€€~‚‚~{|{||~~~~}~~~}|~~~~~€}~|~}~€€}‚ƒƒ~ƒ‚z}‚{€€€‚€~}~€€‚‚‚„~€ƒƒƒ„„„€ˆ„‰…ˆ†ˆˆˆ‡‡†‡…„……‡†ˆusssurwvsvvwwwuvrrrrvvvuwvwwvuvsuvwyqsuwwwswvvwwwvwvvvvuuuvwvxvvvuuwuwwwvrrqrtropoppqonnlmpoppqroqoloooommmprppnmmpoqrqpqpppmpooopqsusrpqpqssqqqpopsqqqrrqvvvtpvttusrrqqqrusurvvuvwwzz{zx|yyyywy|uzz~z{|w{z{{zzyvuw|{{wzzzwwwuxyzxw{vxyyz{zz{z~{{v}{{{zzz}}}{{z|}z}~~~|~{}z{}}{~}}}|}|~}}}ƒ~}}}}~€€‚}~€‚~„€ƒ€~~€}~~~}}~~‚‚ƒƒ‚†ƒƒ„„†„„ƒ‚‚ƒƒ‚„„ƒ‚ƒ„ƒ‚‚ƒ‚‚ƒ‚ƒ‚~}~|{{{~z~}~}~|{~zz|~{}|}|~|€~|~~~~|€{{z{€{~~~~{|z|~~{€||~}{~~~||zv|||zwz}||zy|{z|}~y{{{|{|}~€~€{‚{~}~~{{|~~€}}~~€|~~|~~€}}{€||~~‚€€~|y{{z}~~~{~~€}}~~}~||{~~~~€{}~~~|~~~‚{~~~~~{€~}€~|}~~ƒ~€‚‚ƒ|€„‚‚„ƒƒƒ„„………†ƒ…†‡„„………„wxvvruuvsuvwvwutrvqssvvzwwuuvwruvuvvuvuvwysuuytxvuxxvzyzxxwyvxuvvvvwwwwzstrsvsrrpuprrqmqlrqpoooqqrlpotsulqmqqpooppptqprsrpqqpoprrprutvrrrutturqqppptqvrtttrtrtuuvwtutwrstussrqqtwvwwyyzw{{{wzwxwvzz|y{|w{z{{{z}y~~}}{{|wuuyzzwxxvyzz{zzz|z{{z{{x|~~~|{}}}z}~~}~~}~~~~}~}}~~||}}|{}~~}}~~~~}~~~~‚~‚‚ƒƒ…€‚‚ƒ‚‚‚~~}}}~~~ƒ‚‚„ƒ…ƒƒƒ………„…ƒƒ‚ƒ‚ƒ‚‚‚…ƒ€‚ƒ€ƒ‚‚ƒ‚‚„~~~|z{~}~}}~~~~~~{{||~€|~€}}€€~{~|}~}||}||{{z{|~}€}||}||~€~~{{}|}{zz||{|~|~~}~||{~zz{}{|~{‚{~|}}}€€}|~~€€€~~~}|€~‚‚‚~~~|ƒ~€~}‚|~{|||}~~~|ƒ~~|‚~~}~||~~€}€|}~~|~~~‚€€}}}~~ƒƒƒ€~ƒ~|‚|€~‚…~‚}ƒƒ„~‚…ƒ„„„ƒƒƒ„…‰„……„……ƒˆ†…†…†………†ytsvuuusstvvwxstssrrrrrtvvpuwusuvrvvuxrvwvtrtvuvwuwuvzyzuzywuuttxwwvuruusswwvqsqqqqprqmllorqqppopqloqootorooqqmkqpppumoopprorptqspqouuttutvturqrnppqrrrrurqutpvuswuuqqrtuttuwvvvttvw{yywy{{|vyywxy{{|y{zxz{z{{{yz}z{||{xvuvyzzzxzzzz{z{zz{z{~~|{z{}||{|{}}~{~|}~~‚}~~~~}}~~~}}}‚€‚€~}~~~~~}}~}~~€‚‚„ƒ‚‚‚‚€ƒ„„„„ƒƒ€‚€~€~}~|~}‚‚ƒƒ„„ƒƒƒ„††……ƒƒ‚……„ƒ‚ƒ‚‚ƒ„ƒƒƒƒ‚‚‚‚€€€~|}|}}~~€~}}z{|}~~~~|}~~~||~~€~z~}{~|{zz~|{|{{}{}}|}{|{~~}{{{|}{{{|{||z||z{|}~~}~z}||z}z{yz~}}~}~{~~~~}}~€~€|y{{{|~~~€€~|~|~~€|}|{~~|~}}~}|€|~|{‚~~~~}~~~}~{€~|€}}~~{ƒ}~€~~~€€l~€‚‚€~~}|||€€{~ƒ‚€‚„„„„„…ƒ‚‚„ƒ…„„†„„†„……|…ƒ…†wvuvvwrrrsvuuyrxruvwvƒruuvuwsrsuvtvwwuuwwwsxsvvvwxuuvzy{vzwwuyrtuvsstzvtuuvssruvrqpqqrmrqqqsrqooprmxopqumrmqprltuuqnqoottrpopppqqprrsstttturoqqtqsqsrsrssuvwrqsutvwwvvrurvvvutuurtuwyxy{zzz|wzvyz{{|{zzxyzz{{{zzz~y{|||xvwwwy{yzwwzyzz}~|zz~~~}z{{{}}}{{}~z|~}z|}}}}}€~€~~|~~~}~}~‚{~|}{~}}{}}z{€‚‚ƒ‚€€ƒ€‚„‚‚ƒ‚‚ƒ‚ƒ‚‚‚‚€~~~~‚‚ƒƒ‚ƒƒƒƒƒ„†…ƒ„†„ƒƒ†„‡ƒ„‚‚ƒ†ƒƒƒ‚‚‚ƒ‚„~‚€~~}{||}|z~~}~~~}~€}~~~}~}}€|}||z{~~~€€{~}}||~~{{|~{{z{|~}{|}{{|y|}{{}}}}{z|}|||zyz|~}}{|w~y}|}}~{~~~}~{ƒ~{{z}}}~€}||||{||‚€€~~|~}|€€€}€|‚|~~~~€}ƒ~~}‚|||~~~~~|€€~|~€~‚ƒ€~ƒ„€ƒx€‚„€‚€~~|~€~~ƒƒ‚ƒ„ƒƒ„ƒƒ…„†‡……ƒƒƒƒ„†„„„……………„‰wvuvwvrrssrqssswruwvwsrxutwuqrtuvuvwwvwwwwryusrrwvvuyywzvvuvuvvsrvwrtwwrvvuusrvuurpqqqoqrqrqrqpopppoopqtopppqoounotttpqpoptppppppouqooqpqprropooqrrqqqssttuwqsuvqqrvrqruqsuwptwvvuuwz{{{|{zzz{yww{y{{{zzzyz|||{z|{{{|{{xwwvvz{z{vxzzzz{zz{~}|}|{{}z}}}~}}{{}|{~~}z}~}~}~}‚€‚~{~‚|}}{~~~‚‚€ƒƒƒƒ€}‚ƒ‚‚ƒ‚ƒ„‚ƒ€€€~~ƒ„~‚„ƒ„„†ƒ…†ƒ‚ƒƒ„ƒ†…„‚ƒƒƒ‚‚ƒ‚„ƒ‚‚‚‚ƒ‚€~|}}{|zz}~}}~~€~|~|{~€|}‚|~{{{{~€{{z{~|}~{|{}|}|||{|{||}|}~}{{|z}{{|{}|z{~|{}}}zyzz|}|}||wzzz{|~}~~}}~~~|}~}|~~~~|{|{{|{{~|}|}{||~|}}}~~€~}€~}}}}~~}}‚~}‚||}~}}~~~~|€|~~~‚{€ƒ~~€}~~{~}‚ƒ~~~|…ƒ€€€ƒ‚‚ƒ…„€„‡‡…„…„…„„„„‚€„…„…†vvtrvurwswsuuvuwutwxvvvwuuuwvvsuvvuuvsuvvwryuvsvvwwvyvwzuywwswvwuvvususurrswuuvvwwqqqropqqqrrqpqrupsqrptuvppprptqoqnqqrsoqqrrrotqoqqquqrqrqsqvpqrrrtrqquqquwvvvwvvrwpuuuqvuwwvwwwzwyyy{{{zw}z|zxz|{zz~|z{{z|{{{{{|{{{z{{zzvwzz{||}yvz{zzyz{|{}}}~z|}}z}{}}~}}}zy}}}~~~~~~z‚{|}}~}}}~~~€~|~}}~}~}~‚‚ƒ~‚‚‚~€~~}}~ƒƒ‚‚ƒ…‚ƒ€~}~~~‚‚€ƒƒ„‚ƒƒ„……ƒ„‚‚‚„††„„ƒƒƒ„ƒ„ƒƒƒ‚ƒƒ„ƒ€€ƒ~‚~~~~}}~~|{{z|~~~~~~{|~~|}ƒ|{|{z~~}|~~z{}~|{z}}}{}|~|||||||||€{|{|{|{|{}{{zz~~}~|}|}{y|~{~~}y|y~{z|~|}{€}~~€~}}~€€€{|||{€|‚|~{|||€}€€}~|}~€€~‡||~~}ƒ~~‚{|}~}~~~~}~€{~}€~‚‚‚ƒƒƒƒ€€‚ƒ€€ƒ}}€„€‚ƒ…ƒ€…‚„‚‚ƒƒƒ„†……„…„…„ƒƒ„„ƒ……„†~„„„ƒˆvvtuvstsswusrvvtwvwvwuuuwwsvvvusuvvurutxvwrxwvuvwxz{yvusuwvvrsstvwwvuuwvsrsqtuwtppqqpqpolqptrovqrvpoqqptoupppoprrqsrrrsoqpqrpmopuqpoptoorrqqrqqpppststqtruvvrturrrrqptuuququuuvvutuwzz{z{zzz{{|zzzzzzyz{zz{zzy|{w{{z|{z{{zwyzy{{{zzzz{~{{y{|{}}}~~}}}z}|}}|z{}{}}~}}}}z}}}}}~~}„}~}}~}z}~{ƒ‚ƒ‚€ƒƒ‚~~~~}ƒ‚ƒ‚‚ƒ‚‚ƒ†ƒ‚€~€„ƒƒ†„‚‚„„„„„‚ƒ‚ƒ‚‡…ƒƒƒ‚ƒƒƒƒ„„ƒƒ‚ƒ†€ƒ‚~~}}|~~}|{z{{~}}|{|{{~||}ƒ~~|{{{|~|~||zz|~~{{{€}|{~{{||{{}~|||{}y|yz}||{y{~}|||{||~~|{}}{~y}{|z}||~}||~~~|}~}|~€~~{z{{|~{{{|~{|{y|||~}|€}}|}~{|~~{}~~||~~€~|€|€}}|~~|}€€~~~~~|}€}ƒ€ƒ{}ƒ‚z|||ƒ‚~€€}~‚„€€ƒ‚„…€ƒ„ƒ„‚………ƒ„„„€ƒ„„„‚€‚„„„ƒvvuwvssvuwsssutrvvvwwxwyuvrvvvvwvvvuuwvwwzrwvwvzxvvvwxvuuvvvvxruuyuuvywuuvrtstuvwxsrqppoqqorppqpouppouqvnumpprrrppqtoqqqqqqsrsotqtppqrmqqqpnqqqvopqvrtqtqvttrqruuururrsuvuttuuttuuvwwzyyz|{|{{{{z~|||{{|z{||z~{{z|z}|{zz{{z{ywzyz~zzyyz{{{{}{}|}{{zz~{}~}}~~zz}z}|~}}}}~}}|}~}}~‚~~}~~~~~}‚ƒƒ‚ƒ‚ƒ‚ƒ‚ƒ‚ƒ~‚}}~‚ƒƒ‚€€‚‚ƒƒ„ƒƒ‚~ƒƒ‚‚„ƒƒƒƒ„†ƒ„ƒ‚ƒ„‚ƒ††ƒ‚ƒƒ„…‚ƒƒ„„„€‚‚„ƒ‚~~|}}{{{{{~~~}}}}}~}{z}||||ƒ}|{{||{~~|{||}~}}|z}}|}}}}}|{{|~w||~{z{yz{z||}z|{{}}~~|~|||~z~{|~y~€|€~€~€|~€|~€~~|€€€~~||}~~~{{€|~|€~}ƒ~~||}~}‚|||~~~~~~~~|~|~}~}~~€€€~|}||z|€€€‚~„ƒƒ€~}|‚‚ƒ‚€€}}„„€ƒ‚„……„…‚„†€„„†ƒ…‚ƒ…€………„ƒ…††‚ƒ†wvuxyvuuusssttwrvuvvwyqqrrsvqvqvtvvuuvwsuxvvvuuurrrttvyvwuvyyxuuuusuyywqvvrsruvwwuqqpppoqoqppqqmnoqporppmmooporqpnmlmoprrqqprupoopqprqompqqoqpqpqqtwrrrrqprturrrwussvupvwupqtuutqvwwxywzyy||~}}|zzzzz||||{{|{}yz||z|{{{||zzzzzx{{zxzzzyyz~{}~}~{{{{~~}|~~€{z}{~}}~}}~~}}~~„~}}~}}~€~~}‚‚ƒƒƒƒƒ€‚~}~‚„ƒ‚‚‚ƒ‚‚„€€€~~ƒ„ƒƒƒ„†„„„„„ƒƒƒƒ„‚ƒ„‡…ƒ‚ƒ„„…ƒ‚„‚ƒƒƒ‚‚ƒƒ„€~~~}}}{zz}{}~~~|||€z|~z~}~|{{|{{z|}|z{{{||~~z~}{~xz{}}~}{~}|~||{y||{|{{{}{zzzzz}{{{~~|z{{z}|{zz|~~~}€~|~}~~~}{}~~}~|{|~||||~{z|~}|||}~|{|€~|}}~~}|~~}}{|||}€|z|}}~|~~~|}}~~|€|~€~~„~€€{€~}€€„ƒƒ„}||ƒƒ€~„„‚„ƒ„ƒ…€ƒ€ƒ‚‚€€~‚„€€ƒuvxwvvvwutrttttswwvvy|wvqwwwwwuwvvuuswuurvuuuzwvuuwyyyvuvyvuvzuuttswwwvtvvvvvvvvvwprqqopptuvqrpyproqpuprrrooptpqrrppoprqqqrrqtprttrqrrqspuoopnppptrvrsrrqsrursqrturqtvwvvvurtuttvwtyyzwzyx|yz{{}|{wyz||zz{{|{}z{{{z~{{{www{{z{zyzz{{zzzzzzz|}}}~{~}|~{}{~~~{{|}~{{{~}}~~~}}~~~~~€~~~ƒ~}}~~}ƒƒ‚ƒ‚‚ƒ€~€‚‚‚‚‚‚ƒƒƒ€‚€‚ƒ€‚‚ƒƒ„~‚„ƒƒ†††ƒƒ„„„„ƒ‚ƒƒ„ƒ„„ƒƒƒ‚ƒ‚„‚…ƒ‚ƒ„‚‚‚‚ƒƒƒ~‚}}}}{~|}|}}~~~}|}~|{z{}{||€||{{z{|{{||€|}|||~}}|{{||{z}|{|}{~z||||{{{|z}~~z|{{z}{}}}zzzyy~|}|{y{{{~€~|~~}|~~~~~}}}{€}€€|~}|~}|~||€€€}|€€€}|}}~~~{}~|~|~}~}}||}~~~~}}}~€|~~€€„€‚}ƒƒ„€„‚‚€€€€‚ƒ‚€……„…ƒ…„ƒƒƒ‚‚ƒ~„€‚„……ƒ‚wtuwxwrvxvvsrrsuvusvnyyvqyuwsywvvwxuuvuwwvsvwwvvuwvysyuuywxz{vuvsstwwwvvwvvvuvwuwwqqpoqpqpppqtpqsqooqrqppqootupqtrqsrquuvqrqqtpqtsrpmurstrqoqttrttrqururptrtutusqtwpuvqrruututuuvvtzyzz{|z|{{z{zwwzz{{{{z{zzz{|{zz{{{{{y{yzyzz|{zx|}|{zwwy~|{}}}~|{~{~~~|}}}|}~~|~~}~}€€€}}|~€}~~ƒ„„ƒ‚ƒ‚‚‚ƒ€}‚‚ƒ„„ƒ„ƒ‚‚„‚€„€‚‚ƒ‚„„„ƒ†ƒƒƒƒ„„…€ƒƒƒ„†…†ƒƒ„†‚ƒƒƒ…‚„ƒƒƒƒ‚ƒ†…†‚ƒ‚‚~||z}~|}}~~~}~}||~€}{{~{|{~{{z{z||~~||{{}~|y}{}|{|~~}||~{{|}|{z{z{|{z{{~{z||yywwy{zzz~}|{}{{~~{{||~~~~|z{{{{{|{~~~€~~|~|~~ƒ}{~|€€~~}~€€~}|}}~}~{{}|~}}}€}€}{x{{z~{~~~€~|~~€€€~~|~~€ƒ€~|€ƒƒƒ‚~ƒ‚}€„ƒ‚ƒ……‚ƒ„€~€||~~€„~~xyz{wxvuxvutrtqvvwvvwyvvqyuzz{xwvwuvwvsvvzzzzywvvzyyyyyzvxwzzuvxsutyywvustvwswvxwwurrrppqrquuvqrqqpqquprrqppqurvqpqqqrqtutpprtpuqpopqurorqruqtpprtsuurtpptttutrvutrqtuvvuuuvvsuwvwv{vxx||{{|{{{zvz||z{{|||{~|}|{z{zz{|{{zzzzy{zz{{{|z{z}{{zz{~}~}}}}{~~~~{{{{}}}z}}}~}}}}}}~~~‚ƒ‚ƒ~„~ƒ~~}}~‚‚ƒ}‚„ƒ„‚ƒƒ„~~ƒ„„„ƒ„ƒƒƒƒƒ„‚~‚‚ƒ~„………††‡ƒ†ƒƒƒƒ‚‚‚ƒƒ„ƒƒ„„„„„ƒƒ…ƒ„ƒƒƒ„„„‚ƒƒ„ƒ‚‚ƒ‚{~{†~€~}}}}}~}{|~}~|}}|}}{zz{{{{~|{~|{~}{{~}}~{~}}}€{}€z{z}{€{}{~{|zzyz|{z}}}x{yy{}{z{~~~~~~~{~|~|~~~~~~}~~{€~~~~~€||}}€{~‚€z€|~~~}}|~~|||~}}}~€{‚}||~zz|{~|~€~}}~~~~€„ˆ~€~~~€ƒ‚€€€€€€€€ƒƒƒ…ƒ‚ƒƒ„ƒ„…~€€€|€}€€~„„‚xyxxxxxxytstwvrrsrrvstuvssvwnvvxvxtvsuuvvrpoqsrswvvwuxwwxzyxxywwtrtyssusssuvuuwxvrprrrqppppnpvqrqqqoqtpprrrottqtuwvqqqrrrtrpqqrppnpqpoqoturuqqprqtruuptsrrususruvurtuuvxyvwwutrvrxwvvvw{wwy|y{zxwzvwzzzz{{{yzy|z{zzz{|{{{{{zyxzz{{zzzwz{z{vwvwz}}}~~|~‚|z~}~}|}~~~}}|{~€~‚‚‚‚}€~‚ƒƒ‚‚…€‚€~„‚‚ƒ„„„„„ƒƒƒ~~ƒƒƒ€‚…ƒ†……ƒƒ„‚‡„ƒ…†‚ƒƒƒƒ†„„‚„ƒ„††…ƒ„ƒ‚„ƒƒƒƒ‚„ƒƒ‚ƒ{{{~€}}}~}}|{|z}~}~~~~~||{{|||z|~{|}}|}|||}{{{|{~}~|{yz{{||||{|{z{zywzz{{|~xy|~||~|yzzxz~~}z{{~}|{~~}€|~~~~{|~€~|}||||}}~~~||€{}{€}€|~}}~}}}}~}}~|~}|{}}}€}}}}}~~|~~~~~|€}~~|~}~}||{|€€|~}}€|€€„~||}€…ƒ~~€€„ƒ…„…„ƒ…„~~||{~~~€||||yzwxvwxxvttsuwruuvuvvvwvvxuxwvvxwxtuuvuvuuvvvvvwuutsvxutsszuwzvvvvuxstsytssvwvwwusrrrpqqpvouqutttwvvptsrsurrrustuurtrpppturqqprqqspusprsvwrvqsqrrsssrtrrsttttrrvuvuyxxyywxuuuuuvsxu{w|z||~w{{{zzy{zz{{zzzzz{||z~z{z~~|{zy{z|x{yv{}yzzzyzzzxyz{|}{|}~|~~~~}~{}~w||~~~}{|~€~~€‚~~~~~~}}‚~}‚ƒƒ‚‚~‚‚ƒ†……ƒ‚€‚ƒ~ƒ‚‚ƒ‚ƒ„‚~ƒ‚‚ƒƒ„ƒƒ†„‚ƒƒƒ„†ƒƒƒƒ„„„„„„ƒƒƒƒ„†††…‚‚„ƒƒ‡ƒ„„†‚„„ƒ‚‚}~~|~}~~}}}{||}~~~}~~~|{z}||{~|||}|}||}|{|{}}€€{{~{{zzz}|{{}{{{{{||{{}z|{|||{|||{{|~}}~~~~~{y~~|}}‚€~~~~|{|}ƒ~}}~~~~€€}~~~}}}~|‚}~~}‚~~}€~|~}~}}~~~€~~€~z~~}€~€€~~‚„|‡~„}€‚ƒƒ€ˆ{€~€}ƒ…ƒ…„……ƒƒ…„|~~~~y€~}€}€}v{vvwxxvrvwuswuuuusvwvwuuvtvuvwwxxvvvuqrsvwwsvwxuvyyyxuuvsyutzwvsuxxssruwusvvurwsrrurpqpptoqprrupppqrprtvruqtsuuupqqtuuoutqqpnsqqqstrrrpovttrqtsutvrsuuoruurrquuwuusuyzzttvuuuruuuuzx{y{{}wzzzzx|y{z{zz{{z|{|{{zz|{z{z{{z{{{zwwyzyzyz{zzzy}y~{}|{|}~}~~}~€~}‚€}}}}}~}~~}~€}‚~}€}}|‚ƒ‚ƒƒ‚‚„ƒƒ„††††…‚‚€~€ƒ‚ƒ„ƒ‚‚„…€~„~~‚†ƒƒ†…ƒƒƒƒƒ…„„†‡…ƒˆ„†ƒƒ„……††‡†††…„„„ƒƒ„…‡†‚ƒƒ‚}}~}~€}}|~~€|}}{~~|€~~~~}|{{|~|{~|{|{{{}€~~}~}|||{{{{|z{||}{{|y{{{{}y|}}y|~~||}zyzzz}|}z{€z~~~{{~~}}|}~~}{}€€|{~€‚€€€‚‚€€€||}}~€~}}~|||~~~}{}}}|}}{~~}}}~~}~|{~€€}{|€~€€€ƒ€‚€ƒ€|{€|‚ƒƒ‚|‚~ƒ„}€ƒ‚„ƒ‚‚……††~~y~~y‚~€|||~}|||wzwwwyrvvwwxuuvwwwwvuyxvvwvwvwuvuvxuuvvwvwwwrytuvwvrssrsuvtwvxrstywxvvvvruvwwwsupwssrtsttutrqvutrrruuwsutruuttsusqqutoptqqprrqowtuuuttrrqsusruutrquvutrtrrrtutvyvuuyyzwuuyvvvvuvv{v{w|z}z{zzz{{|{{zz|{zz{z{{{z~{|{{{~||z{{xyzzyz{zz{|{yyyy{{{{}~~~~~~}}}}}~~}}~}}}}}}|{~~~~~ƒ~~}~}}{|~}}‚ƒ€‚~€~ƒ†††††„‚ƒƒƒƒ‚~‚‚ƒ„„„‚~}~~ƒƒ†‚ˆ†‡…‡…†…†…†††„ƒ„„„†ƒƒ…‚†ƒƒ‚ƒ„ƒƒ„„ƒƒƒ„…‡ƒƒƒƒ~}~~}}~~}}}}}{|}}}}{~€}}}{z}}z}{||€|z{~~}}|€||}z{{{{y}{{{{{zz|}~~~~|{~{~~}|{~}}}}|~yz{}~~|€~}~}~}~~{†}~~~~~}~~~}|}~€~€~€~‚€}‚€ƒ~~‚|†~ƒ~}~~‚€‚ƒƒ{~~~€~~~~€~~€€€€€€€€€|{|~€‚€„~}„ƒ~‚„„}‚ƒ„„‡…‡†……………€€~~y‹ƒ||}vxxxyxvwuuv{wxwvruvwwywxyw{zzzzxvwvvxvwwmwvuuvu{vstxuutrvvttuvswtvswvuuruvsvvvvxvvpurrqpqnppqqqsvvvsqtpwrqtruvuquutttppooopoosrrpvttqpqqqoosuqrvqsrquuturpwvruruuwwvuwtyyvvzvwvuuuwzvwx|zyzzzzzz{z{z{{|{{z|{{zzz{z{z{{{{|{zz|{yxyx{{zy||~zzyzz{z}}~~}}{~}}~~~~~}}}}}}}|}}€~|~€}zz{|~}~}~~‚‚~~ƒ†„„ƒ‚„ƒ€€€}‚…„„„„~}„}‚………‡ˆ‡…ƒˆˆ‡†…„ƒ„„ƒƒƒƒ†ƒˆƒ„ƒ„‚ƒ„„ƒƒ€…„„„ƒ‚ƒ~~~~{{~}~~}~~}||{{|~~}||}|~}{{~{~|}~€||~~}€|}|}}|}}}{z~{{{}||{{{~y{z|}z{||}{~z{~~zz{~||zyy{||~}}}~}~~~~€|}{‚|{|~€€€€ƒ~~€€€~~~}€‚€€‚~~€}€}|~||{{~}}€~|~€€~}~~~~~}€~~‚€~ƒ{€€~€~~€‚€~}€„€€„„ƒ‚……………~€}~zzz{{}}~xwuvwwuwwxxyyzwwqwyywvvwvxxxvvx|svvvuuvwwwuwvvyzuyyyvwwwwzvwtyywrvvwvuswvwvxvuwxsvvvqsqvvvqrqqqrpqprqvuurxutuuvuuuusqrqqqustttsuqwstttrtuutrrtrvutrsvurtstuvuuuwuxyxuwxywvvxvwvvwwuxw|w|{{z{z|w{y~zz{|{{z{||||yyyz{~{{z}z{{{{|z{yz{{zy|zyzzyzzz}}{}~~z~~~~}}}~|{{|||||}~}{~~~~~}}}{z~~~€€~~ƒƒ„ƒƒƒ„€‚ƒ„„„„€~€‚‡†ƒ€€‚‚„€‚ƒƒƒ…†‡ˆ‡‡†‡ƒƒƒ……„ƒƒƒƒƒƒƒ‚ƒ„ƒ„……†ƒ‚ƒ†ƒƒ‚‚‚‚~}}}|~|}}}}~}{~~{|}{~~}€|}~||{}~|~}~~|}~}}€{~|}|‚}|~€z}|{|}|}{{{{~yz{{}}~{zzz{|y}~}|~}~}}{~}}~{}~~~‚~~}~~}~€€~~~€ƒƒ‚}|{~€~~~ˆ~€€}~€}€‚€€‚€~~}}~‡}ƒ~|‹|~~}~~€}~~~|€€~€€ƒ‚„€€~€|€‚„‚‚€€~€€ƒƒ„~ƒƒ€€„~‡„†„ƒ„…‚ƒ€}~|€|†€~~€€€wxxxwxvwuvwuy~wxryzywvwvwvxwwvvvvuvvtvvvwvuwuuwuvvvwutywszwwtxuxuuuvsquwwuvwqtwvqqppqqqosrrtvtvsrsqpoulqruttuvutuquqpqtsqsprovppqqpqrrtrqvvquuuvtsuuvvvrstuuturrsqrvwwvwxxvwyvvvyyxwywyz{z{{{{wzw}zyz{wz{|||~{zz||{||zzyz{|zw}{|{y~{~|{zz{{{~z{|z{{|z}{~}}~|}}~~{z{|}|||}}‚‚}~€~|~}‚~{‚‚€~ƒƒ…‡…†‚‚‚‚}ƒ„ƒ‚„†ƒ‚€‚‚„‚„ƒƒ€ƒƒƒƒƒƒ„‡†‡†„‚‡††††„ƒƒƒ‚ƒ‚†…„ƒ…„ƒ„ƒ‚‚‚~‚‚‚}~}}|~~~~~~~}}|||}~}~~~~|~~~~{{|}}}~~~|{|{z|{|€€{~||{{||}{{|{y|z{|z{{z|{}||}zzzz}~}{|}}~~zz{z{zz{}~~€}€€}~~}~}}{{}||€€€€||~~|}‚~€€€€€€~ƒƒ}~„}~~~v‚~~€~ƒ‚~~}}~~€€€€€€€€}~€€€€„€€€„„€}ƒ‚‚ƒ~„€ƒ„„…ƒ…‚„†ƒ~€~€~~~~{zz{wwyxxwwwwzw{{{{{{zzyuxwxvvvwwvuwuzuwuvtuwwvvuuuvvvuvuuvwrxvwxxvxuxswsywwvuvyyzwvurrroypsrrruuuuvrrrrnuuvsuqsuvuurtusoquprttusupzptuvrrtuvwvvwwvvutruvvusruuuuvwxruswvvuvwyuzvvwwxxvyxzzy{{{{z{{{{||zz|w{z~||~{z{{{{|zz{zzzzy{zzyzz{z{{|~~z||zz}z|z{||~}}||}}}{{z†~~}}}ƒ‚‚ƒƒ~€~~~~|}}}€„}}}~~€€‚‚€†„ƒƒ‚ƒ‚ƒƒ‚‚‚‚‚‚‚…‚„„„ƒƒƒ„€~‚ƒƒ‡„„ƒƒƒ„‚‚ƒƒ…†ƒƒ„„„„ƒƒ…†ƒ‚†„„„ƒ„ƒƒ‚‚‚„~‚~}~}|~}ƒ}~~|{z{€|{~}|}~}|}|~~~~z}~}{~~~~||}|€{|{€{}{}|||}{{zz~~z|{~}||}y}||z~}|}zzy|y}{…€‚~~|ƒ~~~~~~~~|€€|ˆ|{|}‚~~ˆ~€€€€€€}€€€€€‚ƒ„€~ƒ}€€€ƒ~~~ƒ€€ƒ~~~~~{}~~~€€~~€~|ƒƒƒ€ƒ€€‚‚ƒ„~€€„€„„‚„ƒ„„„„„………†~€~}~~|~~€z~||€ywuyuzxvwyxxyvsuwzkxwwuyvwxwxzuxvyvwwwtvvsvuvvvussrqsvvzuxuzyzuwvusstvvvywrvxxxvwvqpqoqqrrsqqtuvsppqqppprtusptopoputqouruvturtpoprtqqqutvvqruurrrtuvurqruvrtvvrqrvstuuquqvuxvvwxwzvwxx{|{{{z|{|{~}w{{~wz{|z}|}||{{~{{zzzzv|{yzz{z|y~{y}}{{{}|zz{{{|~}~~~}}}{{|}{zz~~}}‚‚~~‚‚~€~~}~}}‚ƒƒ€~}}}€~ƒƒ„~„ƒ~}ƒƒ„„‚ƒƒƒ‚‚‚‚‚ƒ}ƒ„ƒƒ‚‚‚‚‚ƒƒƒ„†„„„…†…ƒƒƒ„‡„ƒ„„‡„‚„…„†‚……„„ƒ‚‚‚‚‚ƒƒ‚‚}}}}}||~~~}{}}~}€|{~|~|€~|~}~}}~|z}}{~~~€z}|{{{|{{|||||||z~}}}~~{{|{||zz|{zz}{z{zwy{{€}{{€~{}€~|€}}}}|{ƒ~€||}||€ƒ~€|~€}||~€€~~ƒ‚ƒ€}}~~~€€~€€€„~€€~{€~~}}~~~}~|~{€~}|€€…|~€ƒƒ€~~…ƒƒ…ƒ…„ƒ„…„„‡~€~{€|~~||z€|~wvzzxywwwwyzvwwwxzwywvtwwxuyv{rwu{{yyztywusvuxyyswsuwyvyuyuxw|vxvwuyswwvyxyxwvwxwxwxprrqqrrwrrqvruuvrtqrovvtttuutqpqqqrttorurtnuurrppqruuuutuvstrtrurwuvsvvwvwtruvsrsxwxvuuvuvvwuyuvyz{}{y|{z|{{{}v}}~{{{~y~{}}|{|{|{{{{yzzz{zzzz{zyyzy}~€{~z{{{{~~}z~{}~~}‚z{|}}‚~ƒ‚ƒ}}}‚|‚~~}‚~„~~~~€ƒ~€ƒ‚†~‚‚‚‚~‚„„€‚„„‚ƒ‚‚€ƒ„ƒƒ‚ƒ€ƒ‚ƒƒ„…„ƒ†…‡……‡‡„ˆƒ…‡‡„††……ƒ…‚ƒ…‚ƒƒ†„ƒ„†„ƒ€‚‚}}~~}~~}~~‚€€€~~}|~~~|~~}|~~}||z~~~~}}{{||{}€{{z||{{|{|||~}{y|}||{zyyy{z|zz{ƒx}z{~z‚ƒ}~„€|}~~€€€{„€‚ƒ{Œ|€ƒ…}‚|€‚€}}€„~€€€‚ƒƒƒ~‡ƒ€„€ƒ€€€‚ƒ„€€~~€}~€ƒ€€~„‚ƒ~}„„‚ƒƒ…ƒ€ƒ~„…„„„†‡…††„……ˆ‚…|ƒz}}z…{~|z{z€xxywwxuuwysrswvwvvwvwuuwwwwwwwuxv{uwuuwzzzzwvuvuttrrutuuvvvuxztsswvvuvvvxwvwqpqupwrtmssrqqrqsrrvqtptqqpppuuqutuuurqqoqqqrsssrqoqqsoqpqrvuurtttqrrrqusttvuuvwyyvqwvwrsxwwxvvuvwuvuuvvyyw{|w{{yyz{{w|w}{{zyzz|{{{z|{{~{||{{{{z{{z|zzyzz{{~|{zzyz~zy{|~~~}||~~~~~{|{}}}~~~~~~}}}||~|}}}}~~~}}~ƒ€‚‚‚„€~}~„€‚~‚‚„„ƒ‚‚‚€„ƒ„‚‚‚„ƒ……„†„†„„‡†„†ƒ†‡†††„„†††ƒ„„ƒƒƒ„……„„€‚„€~~~}}~~}}~€€}~{~~{}}~~€€~|||~}{zzz}}}}||{~||{|{|}|{{~{{{|||{}}{z|z{{}}z{yvxvz|~|{{z}zz~{z{|}~~}~}~~~|{|€||~€‚|||€{„~{~€|}€€‚‚€‚~€~~~~~€‚‚„ƒ„„€~}~€~||€€~~€€‚~„x„€€„x‚~ƒ…~€…†……†ˆ„~€{€|z|||z{{~€|z€xxwxxyxyzzvwwwxwwwwwvzz{||xvvyvxwzuwuvv{wvvwv{u{ttryxyvvvwvuxyrtrvvxuvwxvvvwvvrvuvqqqsqwrrrvrqrvqsquqrqtqvtvvuqpprpuuurtptrsrropqtvurqtvvvsuqtsvrqqvsvrvuvuvvvvsuvwsuxyywvuuvvuwwvwyxxyy|yz{z{zzz{||w~|zz~|zyzz}}}z{{{zz{{zzz}z|{{z|yyz{{|z{{{|}}~zyzz|~}}}}~}‚~||~|~}~~}‚~~~€~~~~~€~~~~ƒ‚‚ƒ~‚‚‚ƒ‚‚ƒ~ƒ~‰€‚„€‚ƒƒ€†‡‡ƒƒ‚ƒƒ‚ƒ„ƒ‚‚‚„…‡ˆƒ……‚†††††††‡††„„‡†„„„ƒ„‡‚‚††ƒ‚ƒ‚€‚„ƒ~~~€~~~|}}{{}}~€{~~~}}~€~{|||}}zz~€}}{€~~|{z~~}}{|{||{|z€z|||{{|}z{|}||}{{||}||yzzz†{~~~~€~}~€~~~€„ƒ‚€‚|ˆ~€€€€€€€€€€€ƒ‚ƒƒ€}„~€ƒ…ƒƒ‚ƒ…€ƒ…ƒƒƒ€ƒ„………†~€~}‚|€€€~ƒ‚ƒ€€„‚‚‚„ƒƒ„‚ƒ„„€„„„„…„ƒ€‚ƒƒ…€†…‡†………x~|€€y}~€||z~vxxxvyqzywwvwvxuwwwvszxxxxxvuuwvwwvuwxwvwwzvutvyvsvzy{vsvzvwxurrrqswsvutvtuvsurvvuqpqtptqrrussrvruqoqqqpquqtvttrtqoqqrurporqppppqruuuruquvuururrsqttsruruuvutvutvuwuvvvvvvvtwwvvywwwvy|z{zz{|{|{|{~{w}|zz}~{~{z}y}{~{{{{}}{{{{{{{zzzzy{~~{z{|||~}}}~{{~~}~}~~}}}z~}}}}~}}‚‚‚€‚‚€z}~}~‚‚‚€‚„ƒ†ƒ…††€‚~~}„ƒƒƒƒƒƒ„ƒ„ƒ~‚ƒƒ‚‚„ƒ‚ƒ‚€ƒ………„…†ˆ‡‡‡†ƒ†„†‡‡‡‚‡†ƒ„ƒƒ‡„‚‚‡„ƒƒƒƒ€~~}~~~~~~|{~~~~}|€~~~~~}~|€}||||~~{}}|{|}~~{{z{||{~}}{xywxx{{}}}z|||{{||{|{zzy}z|zwz~z|{|~}}}€~~}~~€‚{}~}|€€€€€}€{|{€~|}}~}}~„~}~~}„‚‚‚‚€ƒ€€…€~ƒƒ‚…„„ƒƒ‚ƒƒƒ~€…|‚€~„€€y„|„€„z…ƒ‚€€€‚ƒ„„„…„……ƒ€ƒ………„„‚€~z~|}|}|{}|€~uwxwxz{{vvyzxxwvvxuvs{wxxyxywwvvvuvwv{vxwwuuw{vwutsyyyutsyvxxvuuvwwwwwutvqutsvuvvvrsruuvurqrquuvrvqqpqruvuvuuuvuqrsusrurrtppqtoprttttqtwrvsuuurttrqrstswssuuuwtvvuvussuuvxuqyuvwwxvww|{{{{z|||z|z~{yz{{{z{{{~}{}y}~}|{{{{{{{{|~}{zz{y{{|||}|~}}}||{|}}‚}‚~~|~~~}~~~}||}}|~}€‚€€~}ƒ‚‚‚‚~~€‚ƒ‚ƒ‚ƒ…‚‚‚‚„ƒƒ‚‚‚‚‚ƒ„†…†‚ƒ‚‚€‚ƒ„ƒƒƒ†ƒƒƒƒƒ€ƒƒ†„„…†††††ˆƒˆ„‡…„‚‚††…„‚……ƒƒ†…‚ƒƒƒ€‚~}}|~~~~~{}~|z|}~|€}}}~~~|}{~~}€~~~|{|~~€~{|~||}~}{{|zy€|{|}~|{||~~~||{~|z}z|y~z~z}}~~~~~~}~~{€€}„~||{€‚€€€€~~~€}‚|‚}~}~‚~€}‚~~ƒ€~‡„„„ƒ„…„‡€‚‚ƒ„…„†€~ƒ‚„„„‚„„‚€€ƒ‚ƒƒ€ƒ€ƒ€„€†„ƒƒ‚ƒƒƒƒƒ…††…„……„…„…„„…†…‡†~~~€|~„‚~|~~rvuwwxyyvwvxyyvwvxvvswvxvwzwqwvwwxvxutuzwwyxvyywzvwzvyyvsvrvvvvrustsrwwutrvssvvuqrsqqtuuuqqqqrvwrturqqusqttuuurqrrttottruutpqupqrturrrstsqsuqvuttrrrtrrrssttwuuvvtwwrssuyxvryvvuvxwx|zwwz{}|y|{||z{||{{{{y{z~|z}zzzz{{{z|}||||~~{}}{{{yzy~z{}}~~~~zz||~{{~€~~}}{~|{{}|‚‚‚€~~ƒ„‚}€}~€€‚‚‚ƒ‚‚ƒƒ†€‚ƒ‚‚€‚„…„ƒ„ƒ‚‚ƒ„‚‚ƒ‚ƒƒ‚ƒƒ„„„…†††…††‡‡„‚„†„‡†„ƒ††‡„ƒ……†‡„ƒƒ„ƒƒƒƒ‚~ƒ~~~~{|ƒ|ƒ~€€~{}|~}|€|~{|}}}|}~~~~~~~~~}{~|€~}{~}}|~~{|}|{{z|}z{~{z||~}~||}~}}|zzywxz{|{|{{~}|~~~||„~{||€€€€~}}~|ƒ€€€~~~~|~}}}~„„„€‡ƒƒ‚†‚~ƒ„‡ƒ…€€€€‚‚†€…~€}€€~~~€ƒ€‚‚€€„‡ƒ‚ƒ„~„ƒƒ†{†{„{†ƒ„ƒƒƒ„‚‚‚…€|€~~~~rwzzzyvyywwwwywxwwuxvwvxvvwwt{wyvzwxw|uxwwyzvzyxyvw{syyvuuuxuurxstsusyutuwusrvvvtvuuqvsqqqqsquvvsturpptuqvsttvrrrtqsqtttrsqpptqrquuxyzquuussrvsprrrvussuuvutuutvvuwwwysvvxvuuxvuvxwxxzz||}|}|~{|{~||||z|||{{z{|~z{z~y|{|}{~}{{{|{{y|}z{}~€~~zz|~|{~~~€}}}~}}~}~|}~|‚‚‚‚‚‚‚€€„€~‚‚‚€‚€~€ƒ‚‚‚ƒƒ„„……†€ƒ‚‚ƒ†„…„……„„ˆ€‚‚…††ƒƒƒ†‡„„ƒ…†…ˆ‚……‡ƒ†„€ƒ„ƒƒ„ƒ‚…†‡…„……„‚‚ƒƒ„ƒ€~{}z~}}|}~~||}~{~~~~|€|~~|~}}|{||~{}~{y€}}~~|~~~~||{€~~|z~~y}}~~~|~}|z|~zzz{{}z|~~~{~}~~}{ƒ~~|„}|~€~~~‚„€€|€ƒ€€~‚}ƒƒ€€~€~|~‚‚‚‚‚ƒ‡†‡†…„ƒ‚ˆ„„…‡…„€€}€„ƒ„ƒ††€„‚ƒƒƒŠ…ƒ€€‚‹€†‡‡€ƒƒ„„„„ƒƒ„„„„ƒ……„………„…………†…†€†ƒ~|€|{}|{~~sxsyuuttvxwwwysrssuyzvssrtywvuwvvvuww|tyuyzzwvuyyvvuuwywxuzwwurqrrsssvtwpprstvqrquvsqrspqqrqsrvuuuuuuswuqrruurqqututqrpoqrpoprqtqowvsqppptsrrpqqppruvrttuuwtuuuuvuvwlxrwwwuuxxyttutvxy|zwvz}}}||{||||{{|||}~~~~zzzyzzz}|z|~z{{|{z~~{yz}zz{{}}{~}}{{z{~~~~~€~~~}}}}}}}|}~~ƒ‚‚‚‚‚‚€€~~~~ƒ‚ƒ„‚ƒƒƒƒƒ…„„„ƒ‚„„ƒ‚ƒ„~…ƒ…ƒ‚‚‚„„ƒƒƒƒƒ„†„„„†…„„ƒƒƒƒƒ„ƒ€ƒ„‚ƒ†…†‚„…„†‚ƒ‚‚‚„€}~}~€{||~}~~~~~}~}|~‚~€||{||~~~|||}~|}}{~zzz~|~~|}~{}|{|~~|||{z~}z|{~}}||~}yz|~yyzz|zz€~}}{|~}|{||{~~€{}}}}}~€~~€~€€}€€‚‚€~|„€€€€}||~€€ƒƒ‚€ƒ„ƒ€{…„‚„„†‡„„„ƒ„…€€~„€~€~|~~€„ƒ€€ƒ‚€†€~„„ƒ‚ƒƒ~~„€…zƒ…„„€ƒƒ…€ƒ~~~~}}|||~wxuyuvtssxxwvwy{r~uvuxywvwwxvwwzvwu{{|szrxwyvtzyzuwyywywvxxwvtwwyqvrvvwrtrruvuuvvtrrrrsqqrpqruuuvuutvuupuquuqpuutuusrqrqrruqsptpqutuursuqtqqqvvvptrturqrrssuuruuuvvywwvxyxxuvzz{{{{zyy{{z}{|z|{}~~{{{{{~}}}|zzz{z}z|{|zz{{{z{|~z{|~}|}}}}{|{|~~~~}‚€}}~~}~}}}}}~~~€‚ƒ‚€~€}ƒ~ƒ~}~~€ƒ‚‚‚ƒ‚ƒ„‚„„ƒƒƒƒ€„ƒ„ƒ‚€…†‡‚ƒ‚ƒ„†„„ƒ‡ƒ†ƒ‡‡ˆ‡†…„‚†‡†ƒ‚ƒ‡€ƒƒƒ…††‡ƒ„„„„‚ƒƒ‚‚€~€~~~~€}‚}~~~~~€~€}~~~||{~€€€~|||~€|~}€z~}}|€|~|||}~€~}}}}~|}||{|z}|}||zzw||~z}|{{{~~~~~€|€‚ƒƒ„~€}€~~€€~‚€€~€~ƒ‚€ƒ‚‚„‚ƒ‚‚‚ƒ‚‚…ƒ‚……†ƒ‡‚…„ƒƒ‡‚††‡…†„ƒ…‡…ƒ„…~‚€‚„„€€‚€‚…„€…„„„…„„ƒ„„ƒ€†………„„„„………ƒ‚€„…†ƒ†€…ƒ}}~€~…{‚ƒƒ€€wuuxwvssvwwysww{svxxvxywzzuwvzyzwxwuu{uytsv{utvvvzuvwvuw{vuwxwvuusrrtvxwtsssttuurrruqpqqqqrqqqruvvuqqqrqqqquurrtvupouttrosuqpvopttuuwtuuurrqqppoouqrrrutuuytruvtuusvvxvwvuxxwwvuuuvuzxzy{z{|{}{z||{~{~||~z}{~}}{{{{zzzzzzzyz|||||{zyz{~{{}}~{~~}}{{{{{~~~€~~~~~~{|~}~€~~€}~~}‚€}‚ƒ‚~‚€~~ƒ~~~ƒƒ‚ƒ„„ƒ‚„ƒ‚„ƒ‚ƒ„ƒƒ„ƒƒ„‚‚„„…†„ƒƒƒ…ƒ‡ƒˆƒ‡‡„ƒˆ†„„„„„ƒƒƒ„‡…ƒƒ„„„„‚‚„„ƒ„ƒ‚~€€‚‚‚}~€~}‚~}~|~}~~~}~€{~}~~|}~}{}~~||z{{}|}}~}€~€~~~||{~z}y|||z~}}~|z{z|z{~~}zz~~}~{€~}~|€}}~|~~€€~~€|~}~|€‚€~|||~‚‚€‚ƒ€€‚~ƒ„€‚€€‚ƒ…ƒƒƒ„„„„†††ƒ„€€ƒ€ƒ„€~€~{…€‚„„~€ƒƒ„…„„ƒ„„„†€ƒƒƒƒ†ƒƒ~€}~„~~|€€wvvvwyyzuvw{yxyzyxwxtxvxxwwvvzvwxzz{yxw{u||sxv{uzxvtvvvwwwwvxutuzsurvwxs}surruwrsrvpvrvqvqurrquuvuurwrvuutttvstuxw}tutuvvusrtrttsqsutuuuws|qsrtrtttrustttytrruxuurvuxwwvzxtu{vvvz{{wy{z{|{|z}{{{zz}|||}}~z|~{z{|{{{{{~yz{|}~~{}y|z}|{z|}}{z{~y{{}y~}}}}~~~~|{|~~~~‚}‚€~~~~~‚ƒƒ~~€~~}€~‚|€~†ƒ‚„„„ƒ‚‚‚ƒ€‚€‚~‚€ƒ€€‚ƒƒ‚‚‚„„„ƒ‚„…‡‡†‡ƒ‡‡…†‡„„„†††…‡ƒƒƒ†…„„ƒ„…€„†ƒ…„„~~‚€~€€~‚‚‚‚€|~}}}~}~€}€~€€|€€~}||~€}~~z~}|€~}~~~~{{{}z{|{|}{}|||y|z|z~}}}~z}}~~~}~}}€‚~~~|~ƒ~|ƒ€€€€‚~€ƒƒ~„€€€‚ƒ…ˆ|€„„‚ƒ„‚‚„ƒ‚‚‚„‡€„„„„„ƒ……‡‚ƒ…†ƒƒ€†ƒƒ„„€‡‚‚‚‚„„ƒƒƒ„…†…‡†‡…„„…‚‚…ƒˆ……€„„…ƒ†…†„ƒ†€…€‚€€~€…ƒŠ€€~vxusuuuzwywxwwsruvwtrtvxzyyvvvwwxywxvzuyuvuzuswvvxzzzwuttwzvxzxxwytrrvvvsssusstwwrrqqrrrrqppqprtrsrttrruutotturtuupruupuruqstptupstsstustrrrrrutstttuusuvvwruuttvrsvutuuvwyxuvwvwwvzyy{y~}||{{|z|||{~~}{{|}zz|||{{{{{|}|{z|utxy{~{zzyyyy{z|{}{zz{zz{|z}}{{zz~}}}}z{~{‚~~~~}~~}}~‚~~~}€~‚~~}~…‚‚„‚€‚€‚€€‚ƒ„‚‚€}‚‚€‚ƒ…‚‚‚ƒ‚ƒƒ„…†‡‡‡„…††ƒ„ƒ‚ƒƒ†…ƒ‚ƒƒƒ…‡„„ƒ„†‚†„ƒƒ„„~~‚~~{~~~‚~~~|~}}~~€~~~|~}|{~~yx}|||{{}~{{~~|{{zz}~|w~}~{zz|z}{zz|{~|~zwz{{}~~}~}€~{{|~}{~€‚‚‚‚~~€€€€}€ƒƒ~~‚ƒƒ}~~ƒƒ‚€ƒ‚€‚„ƒƒ…‡ƒƒ„„ƒƒ„„‚…„„€~‚„„€€ƒƒ†…€„‚€€€€ƒ€ƒ††‡„ƒƒ~ƒ„†„„…†‚‚„ƒƒ…„‚ƒ…†…€‚€€„€€€€~wyuxwxs{wxwvvwwwvvvyxxvyv{xvwwvwvvvwy|wwwvvyyzyyvxw{zzz|t„wywuvywztyyyuwwvuuuvvyuvrsqssvqrrrpuqrptrruttutuuuuuutuvvyuuruptqsrutttttqqtttsursqquuruuuutpvtrrtsxuuussvvxuuuuvyzzyz{|v{zywy{||}~}z~{{{{{|}~zz|~z{zy||{{{|{zz{zzz{zz{~~~{|w{z|z|z~z{z{{{z{||}~}}}~}~~~|zƒ~ƒ€‚~~~~~~~~~‚~~~}}‚~‚}‚ƒƒƒƒ…„‚€‚~ƒ‚‚ƒ~~~ƒ‚‚ƒƒ‚„‚‚ƒƒ…„ƒ„‡„ƒ…‡‚„‚‚ƒ„„‡„ƒ…‡‚„ƒƒ„†‡‡‚ƒ‚‚…ƒ„}ƒ~~~}~~~‚€~~~~}|~~ƒ~~|~‚‚~~~}||~{{{}~~~~}}~}~~~}|zyvx{~||z|z~~w€zzz{|}{zy|||}~y|y{z~~}€}~~ƒ€‡|ƒ€€|~~€€€€‚‚‚~~~~~€€‚€€‚ƒ‚€ƒ†„„~ˆ„„„„„„‚ƒ…ƒ…ˆ„………„‡‚……†„„„ƒ€„…‡€…ƒ†„‚„€ƒ„€…„‚ƒ„„„„…„„ƒ„………ƒ‚„…ƒˆƒ……‡ƒ………†††ˆ€…~€€€…€€€ƒ~€wwtvyxv{zxwwuvzu{vuvvwxxyzzwwvvxxvvxv|vzuvvwzzxxwyzyz{zwvuuwzvuytuumryvvwwuvvwmwssqrrtquqomrqtqrpsoruuuuqruvuuusuuvquvruqqrqtvtrrstqpuurrrsruvvstuurrupurqrtvuxuurrrruywvxwvvvwvuvwy{{|{||}}||||{z{|z||{{|{{z{z|z{zy{zzz{zzzz{{{}zz|{y|z{{{{~{}}|~{{|}|~~~}{|~|}~~|{~}ƒ€€}~€~~~}~~ƒ‚}‚€€‚~€~„ƒƒƒ‚‚…‚‚‚‚ƒ~€ƒƒƒ‚‚‚ƒƒ‚ƒ‚ƒ„€‚‚€ƒƒƒ‚ƒ‚ƒ„‚ƒƒƒ‡‡„„…ƒƒƒƒ„‡†††‚€‚„†ƒ„ƒ……ƒ…„‚ƒ~ƒ€~‚~~€‚~~~|}~}~~~ƒ|€~{}{}{~}{z~€|{{}~~~|}{|}{{|z|{zzzzy{~zzzz|{zyzzz|y{z}zzyzz{|~}‚{~~~~|ƒ{||~~€~„‚€€€~€‚‚ƒ€‚‚€€‚„„ƒƒ}~~„ƒƒ€…„„„ƒ…„ƒ„†‚…‚…„……„ƒ„€„ƒ„‚ƒƒ€…„„„ƒ€ƒ‚…ƒ…„‚„„„„„ƒ€ƒz„€‚ƒƒ‡†„‡„†„„…„†‡‡†…‡~~„~~€|€wwz{xwv{xwwzwvyvuuvyt|zxxwvxwxz{v|vvu{u{vzvttuvyyzzzw{wwwvwwwzxxywvwyzxvwyuvvwwzrrqqqspqqrqrquprntuurquvquuquutruruvuuuusqqqqtrtuuttqsutrvstuuttuttysvqusvruwxvvuttvttvzzzwvvvuuwyyyz}{{|x|||~{{~~~{z||{{{z{|z{{{z|{|zzzzyzy{{{{}}~~~}}{}|{{~~}|}{{{}|~}{}~~{~|}}}ƒ~ƒ~~}‚‚‚„~€~€ƒ‚ƒƒ~}~‚ƒ‚~}~‚‚„…†…‚†„…‚~~‚ƒ„‚‚ƒƒ‚„„„„„„ƒ‚ƒƒƒ€…„‚‚ƒƒ„„„ƒƒ„ƒ……††…†ƒƒƒ„ƒ„†‡‚…‡‡ƒˆƒƒ„„ƒ‡€ƒ€‚‚€~~}~~~‚~~€~|~~{~~}}‚~€~~|~€}||{||}|}}}}|~~{}|||||}~~|{z|{}|~{z|}{xyzz}w|{}}}}~}~}~|ƒz~}ƒ|~~~|ƒ€€€€€€€€~‚€ƒ‚‚‚ƒ€ƒ€‚€„ƒƒ„…„€„ƒƒƒ…„ƒ„…„…„‡‡…ƒŒ…„„„„„†€„…†ƒƒƒ„ƒ„‡„„…††…€†„……ƒƒƒ„ƒ…„…………„ƒ„„„„…„ˆ‚…ƒ‡…ˆ„ˆ‰ˆˆ‡…~~€‚€€€wxyyusvvuvwxwvzxwuvxvxswtxvuuvwvv{xwuvw|vzwwwvwvvwwyy{ywxzyxzzruxvyzwzuvvvuvrqqyurrqpqqqqpqqqvppqtlwtrtvrttswvuruqqusuturrtqqqrruvusqpqqtvsrvsqvtrtrsruruurvyxvuxyyvutuxzzvtwvvvuuyzw{wz{y}|}~|{{zzzzz}{||{z{|{zz||{{zzz{{yw|{z|z|€|z{}{|}}~|~}}|{|{z~€{~}{~~}|~}~~}‚}~~‚‚‚}~}‚‚€ƒƒƒ‚ƒ‚„„„„‚€~~ƒ„‡…ƒ„„‚ƒƒƒ€~~‚ƒƒ„ƒ‚„…„ƒ„†€†‡…ƒƒ…‡ƒ‚„ƒ„ƒƒ†…†„„ƒƒƒƒ†ƒƒ„‚„…†„ƒ††„‚†ƒƒƒƒƒƒƒ„€‚~~~}‚~~}~}}~~~~€{{{~€~{|}~€€~|}}{€}|}}}|{|||||}}}}~|||||}}|~~{|~{|~}||}yz{|}y{|{{yy{|~~ƒ|~}|{€~~~}‚~€€~„‚~~€€ƒ„€‚‚€€€€€€€ƒƒƒ~}~ƒƒ€ƒƒ€€ƒ„‚ƒ‡‡‡„††„…„„……€‚ƒ€€ƒ„ƒ„„…†€‚„……‡ƒ„ƒ„„…‚†„„„„€…„‚„……††„„†‚‚…ˆ„…†……„„†„€~€…†€€ƒ…€wxxxxxwywxyuvzxxyu{vvuxuyz{vzvwvxwxyyu|vzvuuxyzywwyz{{yzzy{yyuxwxwwvzwvvwtxtsrwsrrrqqpvnqprqvvuvvwwvvstrvqtvputrqrtutstrutttusytrtuqvvyuuttuvwvuvuvsyuuvuvvxuuuxxxyyyvvwxvuvwwxwww{z{{{{~|{z{z{{|{{{z}w{z{|{|{|y||{zzzzywvv{{y{{y{{||{}{{|}|}~}zzz{{z}}~}‚~~‚ƒ}~}}~~~~}~~€~~ƒ}„ƒ‚ƒ‚‚‚„ƒ€‚}ƒ‚„‚„„ƒƒ‚ƒˆƒƒ‚‚ƒ„„ƒƒ„„ƒƒƒ…ƒƒ‚‚†††‡…„„ƒ‚ƒ…†„„„„‡‡‡‡††‡‡‡‡…………†„…†…†……ƒ„ƒ†ƒ„„„„„ƒ€~~~~~}}~‚~~~€~€|‚‚€{}ƒ~€~€€€€}€|{}{||€~}~{~{{~~|}}||~|{||}~zz|{z~{z|||{|}z{}~}~‚~ƒ}~~ƒƒƒƒ„„„ƒ€€…~…€‚„ƒ‚€€€ƒ„ƒ€€€€ƒ‚‚‚„€„ƒ„ƒ‚€ƒ„‚‚ƒƒ„ƒƒ„……†‡ˆ„……††„‡…„„ƒƒ€…„…„ˆ…†…‡‡…‡ƒ…€„„„„…„ˆ„„„ˆ€„„„……„„„„„‡……ˆ††‡‡„‡ˆ†„€~Š€€€€……‚vwzyzyxwwwxwww{zvwvys{xxw{xwwxxwwyxwwwu|vzvuvxyzzvyzwuvvxxzzyyuvyywtuvwwwxtwtssssussrqrvlnpopuruptuuquqtqqqtutwutqrtvrrrrurttrsstruurvutututspovvvuvrxvwsrwvxxwvywqwxvzvyuzxvuyvwuvw{{{{{{{{|{{{z|||wy{z|{{zz{{|{z{zzzxv|z{zyz{z{z||{|{{{}|z{{y}{z~}~€~€~|{~~~}~|~~~~~‚}~€ƒ~}~~€„„ƒ‚„ƒ~€ƒ‚ƒ„ƒƒ„ƒ„‚‚‚‚‚€‚„ƒƒ„ƒ„„„ƒƒ„†…†‡†„ƒ‚ƒ€ƒ†ƒƒ„†‡‡‡†††††…ƒ„†ƒ††„„…†‡„ƒ„……ƒ„‚„ƒ‚ƒ~~~}~€€}~€‚ƒ€~~€€|€€€{€{€~~€}|€}|~~{|}||}||{~||{{}}|}}}}~{|{~|zz|z~~||{}~~z{||~€~}~€€|z€…„ƒ€€‚€~„„‚€€€€‚€€ƒƒƒ‚€„‚ƒƒ‚‚‚€‚‚~€„€„‚„…{…„†…ƒƒ‡………„‚‚…„…†…††……€…‚……‡…„ƒƒ„‚‚‚………‚‚„…ƒ…„……„‚„„€†…†„†‡ŠŠ‰‡…„„€€€~~ƒ…„€‚wwxxz{zwvxw|wv{{vvwyu|xz{{z{|{uwwzvvuwt{uyvwwy{|wvw{wyvzyzwywwwyxywwwyvvwxsvrussrwqurqqwqrquourrqvwvqvttsuputuutsttttrtuuuqtr|srusturxrtttttrspxvusxrvvwsrtuvvvuuxtxxwxvwwvwvvvvuutvxwy{yy{|{|{{{|}}|x|}zy|x{|zzzz{|{{zzwxwwz{z~zz{|||{{{{z{~{zzzz{~{z|~~~‚~~~{zz~}‚~~~}‚‚‚}~~‚}}€ƒ€ƒƒ‚ƒƒƒ€€„ƒ‚ƒ„ƒƒ„ƒ‚ƒƒ‚‚ƒ‚ƒ„ƒƒƒ„‚ƒ~~‚„ƒƒ„†…„€ƒƒ‚ƒ†„…„……†‡‡‡‡‡ˆ…ƒƒƒ„†„ƒ„†ƒ„ƒƒ…„ƒ„…†ƒ„€ƒƒ‚‚ƒ~~~~}~€~}‚ƒ~€~€€€~{~~~}||~~~~€‚€{~{|}~|||}}|||{z}~}}}|}||z|zyz{z}}||{~~~|z{~}‚ƒƒ~„€€€€ƒ€€€~„€„€€‚…}ƒ‚ƒ€ƒƒƒ†‚ƒƒƒ€ƒƒ‚‚€€„~‚ƒ‚„…†…†…„ƒ„„„ƒ„…†„ƒƒ‚‚„…€†…†ƒˆ†‡††………‡…‚ƒ„‚€€„‚ƒ‚ƒƒˆ„…„„„ƒ„ˆ……„…‚‰…††‡††‹‰‰†ƒ€‹‚€~„ƒ…ƒ€‚vuuwz|xyrvwvwxz{wwvvwzz{yvutv|wxwwwwwxwwuuwwyvw|yvsuuwvvstvvwvwwywtsrrrrrvuttusqswqupopnprrqqprqqrsrqppsuuqtuvoipqquutsuuurqpqtuusstuvrqqtwvtuquvusvtwuuutsuuvvtqwsvuwxyxuzwuvvvvvyywwxxyzz{{{|{~|}}~{{{zx}}{{{y||z~{z{zwxwwzzz~{zz{{{yxzzz{|{yyyzz{z{~~~„||zz~}}|}}}}|}}~‚~€~‚€‚ƒƒ€ƒ‚ƒƒ„„„ƒƒƒ‚ƒ‚ƒƒ‚ƒƒƒ‚€‚ƒƒƒƒ‚ƒ‚ƒ‚‚‚‚ƒ„„„„…„‚~‚ƒ„„ƒ„††‡†„‡…†…‡ƒ„…„ƒ‡†ƒ„‚ƒƒ„…†††††„ƒ„ƒ„ƒ‚}‚~~}}}}~}„€€~~}ƒ~{ƒz}€~{~~€~~|~~{}}}~~~}||{{~{{{}{|}}}}{{z{{||~||{}|||~{||~}z||}‚}}}||{€}ƒ‚€~€€€€„€€ƒ‚ƒ€ƒ€€‚€~}}~~„€€‚€„‚…ƒ€~ƒ€‚ƒ‚ƒ‚ƒ€‚„…†„„„ƒ…„„†ƒ„„„……„‚†ƒ†‚†…ˆƒˆˆ†„†…„ƒ‚‚ƒƒ„ˆ…ƒ‚‚…„…†„„„ƒƒ‡…†…‡ƒˆ‹ˆˆ†…€~~ƒx„~‚„wyuvx{w{txvxyzz{v|vvvz{{{|uxv{vwuyy{wwvvuxxzywx{x{wuvxxyxxzzxwwywxtywwwwwwvxwwrrsxrsqrqrrrq{qpqurqrqptstuuqvuvuvuttvtuttuvrvutuusurwwwsuruuuvvvtuwuyuuvuuusvwwwzyzrvvxxvxuvwvwwwuxyyvwvyz||}{{{{{}~}{{zyz|zzzzz||{||z{z{{z{}z~}|{zz{|~zzz{z||z{zzz{z€~}}}|}||~~~~~|}}}~€~‚‚‚~€‚ƒ~}ƒ„ƒƒ€€ƒ„€‚ƒƒ‚ƒƒƒ„‚‚€ƒƒƒ‚‚~~~‚ƒ‚ƒ„‚ƒ„ƒ…‚‚ƒ„ƒ„††‡‡†‡†‡††‡‡…ƒ„‡„„„„ƒ„ƒ††‡…‚ƒƒƒ„‚„ƒ‚‚‚~}‚~~ƒ}}}~~~~~€ƒ€}|{z~~€|~||€{{|~~{}}{|~}}{y|{zzz{}{}||~{|~|}y}{}}}}{{z‚~„€€€ƒ‚ƒ„ƒ„€€€€„ƒƒ€€‚ƒ‚‚‚…ƒ€„…„€‚…ƒƒƒƒƒƒƒ‚„…„„ƒ‚ƒ~†€†„†„„…„„††‡„„…„„ƒ†ƒƒ„…‡†‡ƒ‡‡‡‡‡††ƒ‡„‰‡„ƒ„„ƒ†„„…‰……„„„„…ˆ……„ˆƒ†…‹‹‰ˆ‰‹Œ‹Š„€…ˆ€‚v„€„{sswwwwytzvwzwsxwyvuwuwvvvwxvuvvwwwzzwwxuvwzzzy{y{ywx{vwwyzwvywvwzuywwvuvwrquuusrrrrrqqprppoqprorrqpqoptpqqvuuuuvsvtuuutrttttuusststwurqrtuuuuuttrtvvuwwvvvuvvtrrrsvyxytzvwvuuuwuuvwxuwy{z|{||{{{{{z|~{zyz{|y||zz|yyyzyzy{{|{zzw||yz{|{|yyyz|~|{yzzz~~‚€}ƒ~}~€~}}~~|‚€~~~}ƒ€ƒ~~~ƒƒ‚ƒƒ‚ƒ‚‚„ƒ‚„ƒƒ„ƒ€ƒƒ…ƒ„„„ƒƒƒ„ƒ‚‚‚€ƒ€‚„„ƒƒ€„‡…„ƒƒ…‚ƒ‚†„‡ˆˆ††††‡††‡‡†ƒ††ƒƒƒ‚‚ƒ…„„„„„ƒƒ„}€}~€~~~€ƒ~}}}~}~~|ƒ~|~~€|{}{{||~~{{{||{|||}{~~}{}~~{{~y|}|||{}}y{}~~}~zyx{|}~~z||{z~€€„}~€ƒƒ„ƒ„„ƒ€‚~~‚‚€€~~€„„‚ƒƒ‚‚‚€€ƒ‚ƒƒ„ƒƒ€€€ƒ„‚‚…„„„…†…†ƒ†…„€……„€†„†…†ƒ………ƒ…‡†„‡„ƒ„ˆ……ƒ„ƒ…………„„…‚……††…„…ˆƒ‡ˆ‰‡‰†‰ˆŠŒŒˆ…€„€€‚w„€‚†„{xxxwwwwwxvywvwwxyyywyxyv|vyx|vzwwvwzzyxtvvwwyz{zxx{zzz{x|xwwzuvvzyywvwvvwvvvusvuusrqppqtzmqpoqvqrpppqqtruqvqvuuvusrtvuurtuwsurvstsrrtrrrttusurts|quuuvvuuuvwztxs}rvwxxtwvttuuvyuuvywvwyy}{{{||~{{}z{z{zzzzzz{{||{{yzzzxwz{zzy}zzz}{zzzz|}}~~}~{{z}z~€~‚€€~€€~}}~€~~}‚~~~‚€‚ƒƒƒ€‚ƒ‚€‚‚ƒƒ€€‚ƒƒƒƒ„ƒƒ‚‚„„„ƒƒƒ„€‚€„‚ƒ‚‚‚€‚„…††„€ƒ„ƒƒ†ƒ†„†ƒ††††„‡††ƒƒ†…„„ƒ‡‚ˆ„……ƒ…‚ƒ†€ƒƒ~~~~‚€~‚{~€‚‚|€~~~€|}~~€~~~~~|~}|zz~€~~|…{|}|}|||}||||}€z~}}{x|{~{€€€~~~w}w{||}zz|y|}~~‚‚ƒƒ„„„€ƒ€ƒƒ‚€€€€€€‚~‚„…ƒ‚‚‚‚‚„…„€……‡‚ƒ…ƒ‚ƒ„ƒƒ€}‚„€€„ƒ„‚„…†‚„…ˆƒ„…„…„†………………„………†‡‡‡ƒ‡ƒ‰ƒ‡†‡„ƒ…‰„„…††…„ˆ‡ˆ‡ˆˆ‡…ŠŠ‰ˆ‰‰Š‰Šˆ‘‡€…†€€„…‡€‚‚zvuyy|vxwww{wuvuvtuuvvwxv{vyw|w{wwuwzzzxvyvuvvu{qxy{yw{{xzwwuztutruutuwuwvvwqtsvqrqqqqmlmmlponqvrqqppppqutpuqpotvrqquuuuusutrttxuttuyusrrurqrrrrsrrrutuwvvtttstuuxrvwxxuvvuuywvvxvwzwvwvw|yz||z}{z~y{{z{y{z{{zzzyz|zyyy{xwy|}z{zz{{zz{zz{}|y~~}|zz{{{}‚‚‚‚€‚‚‚}€‚}}~~}}‚~€~~‚€ƒƒ„‚ƒƒ„„‚ƒƒ„‚ƒ‚ƒ„†„‡ƒƒ‚„„‚ƒƒƒ„„‚‚‚ƒ}~‚~ƒ…ƒ„„„„ƒƒƒƒƒƒ††††‡†‡††…‡‡‡ˆ‡†…„ƒ„„ƒƒ†„†‡†‡„„ƒƒ„€~~‚€ƒ„‚‚€€~~€‚~}€}€}~€}}{~|~~}}~|~~‚~~|||~}{||z|}{|}z|z{}|{{z}{{{{{|{||{}zyxyzz{{{z{zzz{zz{{|~~~~{o{ƒ‚€€€~}~~€€€€€~€‚‚ƒ€„‚€€‚„ƒ„„~€ƒ€}ƒ€‚„ƒ„„„„ƒƒ„„„„„ƒ„„…‡|‡‡††……„…†‚ƒ†…†‡ƒ„„ƒ„…‡………„„„„…ƒ…ˆ…„„„„ˆ‡‡‡ˆŠˆ‰‰‰‰ˆ‡„……„„„€~€€„ƒ„xyz{zzywwzvwyuuywvvwuwvxwwv|v{w{vwuvzzy{zzwyvvwzzyxvuvzzw{z{u{vzuxvwv{vuwwuwvvuuuuuwqrpvtrqrpppuqrqrpututttvsssuvuqptuusrssuuuvwrttvytstttrrqvvurtruvwuwuvuttvryuvwwvwwwuuvwuyrtuuuyyyz{w{z|{{{|z|||y{{w{z|{z|z{||y{zzyzz{zz{~~zwz|{{z|z|z{|~|}~|{|z{{~~}‚‚}ƒ‚ƒƒ‚‚‚}~€‚~€}€~‚€€~‚ƒ‚‚‚ƒƒ†€†ƒƒ€ƒƒ‚€ƒ†ƒƒ„‚ƒ‚ƒ„ƒ†ƒ„ƒ„ƒƒ‚ƒ}‚„„ƒƒ‚†ƒƒ‚„‚„„„„…„„…………„…‡‡†„…†„ƒ„ƒƒ‰‡†…„ƒ„…†„„‚…ƒ‚~‚z‚‚~~~~€}~~~~€€€~~~~~€~~~}}}~~~~~‚|}~~|{{{}{{{|}|yyz~z{{{{zzz|{|{{||{||~}}|||}}~~‚‚‚}~~}ŠŽ‘Ž‘ˆŒ”“‘—šœ˜šŸ¡ Ÿ”–‘‰‹Šz|~…€…Š„‚ƒ†„ƒ€ƒƒ„‚„‚„|„‡‡€…„„„„…‚ƒƒ‡„†„ˆˆ‡ˆ†‡…‰†‡†ˆ†‡†‡‡‰„„…”…††…„„„‡ƒ„„‡„……„ƒˆ…ˆˆ‰‰‹ˆ‰‡††††……„„ƒ„„†ƒ„„ƒƒƒƒ…xxyvvvxyuvyvyzvyvvvwvuvvwxwyw|wywvtwzxyyzxszvvwvvz{xyyzzyztuuzxzvzwuutvwytswvtruvtvrqrrurrqqqqpqqpqsqopopqurutqtputsruurrrrqqppqttuvyssvurrqqsutrpqrrvutuuuvvusuvuuuwzuuuuwwvxquwvvyyy{{wzzxwy{||{z{{z{|{z}{|}|||{zy|}wz{{wy{~{{{zz{}zzyzyy{~}||}z{|z}}}‚€‚~|}~~}‚}~‚{€‚~~~~}~‚€‚ƒƒ‚„ƒƒƒƒƒƒ‚„ƒ‚ƒ„„„ƒƒƒ„„„†‚…ƒ‚€~‚}ƒ‚‚‚‚ƒ‚ƒ‚„„„„ƒ„†…††…‡„…‡…‚ƒƒ„„………‡†„…ƒ„„„„‚‚‚€~~}~€|{~ƒ~€‚€~~{{€€~~~}~~€~|}z{{|~|~|}||{|}}|~||{||||}||{{z{{||~~€z~~~}vyy}……ˆƒƒƒŒŒ”“’‘“’‰ƒ‡Š†{zpssl`mh\QPPKCB>?8$+$$%&A}}„€†ƒ}‚‚„„€ƒ„€~}„‚…ƒ‚ƒ„€…„ƒ„ˆ€€†…†€‡‡†††………†‡…††ƒ†‡‡„…„ƒƒ‡ˆˆ‡……‡……„„„…ƒ‚ƒˆ…„…‡„„…Ї…†‡‡‹†„ƒ„…„ƒ…ƒ€…ƒƒ‚y{}}uzyyuvvwxzwxwwuwvvvvuxvwu|uxvuvvzwvxvvy{vytvvzxwwxwywyyzwzz{vzyxwyxyvvsvvxrvwrrpozqvqspvrrsrrrqrs{qrqqqtuutuqussrutqtuttrrqtsvtvuurvuustquttuvstswuvruuvuusuuusuuytvuwwwwwtwwwvyzzzzw{zxwyz|~zz{{|z|zz{{{~|{{{{{{}wxz{wyz}{|{|{w{{zzzyz}{}~~{{{}z}}~~ƒ~ƒ~€|~€}‚|€~‚‚‚ƒƒ‚ƒ‚ƒƒ€ƒƒƒ€}€€ƒƒƒ„„€‚„ƒ„ƒ„ƒƒ„ƒ‚ƒ‚ƒƒ†‚‡…‚ƒ~‚~~€ƒƒ†ƒƒƒƒ„†…†ƒ††‡„‡ƒ‡…„„‡ƒƒ‚†…†„Ž„†‡†††…†††‚„€‚€}€~}‚‚‚€€€~}}}~~~~}~}}~~~~~€~}}}||||zzz|~‹ŠŠ‰ˆ‡„„‰‰ˆ‡Œ‹’•”“•‘Œˆ†‡Š†‚ƒƒ€vpoha^QPRMKGGB?:50.#! + +?‚{ƒ†„…€„Ї‚„…„ƒƒƒ„††‡‚ƒ€†„ƒ„††„‡…‰ˆ†€†‡†€ˆ†„……„‡†‡†‡…††‡ˆ‡‡‰„„…ˆˆ†‡…„‡……„…‚ˆƒ‰‰Š…‡ƒˆ…‡…‰‡ˆ†ˆ‡‹‡††………ƒƒ„…………‡ƒƒ†yvvvvuvwuvvwxxxvwwxvvvvuvwxwwzusuwwx{xwwvvz{vvwwvzwxxwyyuvwwxyu{wsuywvwxwyruxvruxqvprqrrsqoqqtsqqqqqqqqoqqqtuuvuqrtrrutuuvrqqqtrsvtuuuqquvvqquvvxwvusvutsuvurruuuurvussvqtwxxwuuvvwyzzvzwwzzxvzz}|{{{{{z{z{z|{{|{~|zz{ywywyzzz{{{zzzzzzyzz{{~}{{{zzzz~ƒ~~‚€€~}€}‚~‚~~‚ƒ€‚‚‚‚‚~~€‚€ƒƒƒ‚ƒ„„ƒ„ƒ„ƒ„ƒ€ƒ„ƒ‚‚‚‚‚ƒ~}‚~ƒ„ƒƒƒƒƒ„ƒ‚††„„…„†„…‡†„„ƒ‚€‡‚†……ƒƒ‚‚‚‚€‚…ƒ„‚‚ƒ€€~ƒƒƒ~‚ƒ€}|}}|€„„‚‚…„„‰‰ƒ‡Š‰Œ‹ŽŠ”ŒŽŒ„„~|vzsqfichbdPTSICCB?<8)2,* ##}{‚€€‚€~†ƒ„ƒ„„„ƒ{†ƒƒƒƒƒ„„„‚„…ƒ„„ƒˆw…€…ƒ†€‡†…„…ƒ€ƒ…†‡ƒ…ƒ‡‡†‡………‡ˆ‡†‡†„‡†…„€ˆƒ‰……†ˆƒƒ…ˆ„ˆ‡‡‡ˆ†‰…„€„ƒ„„…„„…ƒƒ…xyxxuysƒs„{zwwwvuyvzwyw{uvvxu{{zxyvvwxuvuzz|v}u{zyuxvyz{wwwzxxuyuzuvwvwwvvvwvrtvwrqur{rsqqpvsuqrqroutuprrrqtutuuqqqqquvvtstrsuuvrvtutrquuwurrtutttutrwrvssttrvrrtvvwvwqwtruvuqruv{v{yzw|zzz|xxyy}|{}{{z{|}€{zz{}}||zzzywwvzz{{|{zzzz{y|zz{}{|{{~z~y~‚~‚‚~‚ƒ~}}}~‚}‚~~~~‚~‚ƒ„‚„ƒ‚~~€ƒƒ~}~~€‚ƒ„„ƒ‚…ƒ„…‚ƒƒƒ‚ƒ‚ƒ‚„‚~€ƒ†…„ƒƒƒ‚ƒ„„‡ƒƒƒ‡ƒƒ…††„ƒƒ‚††‡‚‡†ˆƒ‚„„ƒƒƒ‡‚„ƒ„‚‚‚‚€~ƒ~‚‚‚ƒƒƒ„€~„ƒŠ‰‰~wqjnhaXa[PLJDA@=43(+)"! � ��~{‚ƒ„ƒƒ‚‡‚…‡„„„„……„‡‡†„‚‚ƒ‚„ƒ†„‚…†…†„ˆ‡ˆ†‡…†‡‡………†…††‡‡‡‡‡ƒ‡…‡„‡‡†„†‡ˆˆ‰‡‡ˆˆˆ‰‰Ž‰ƒˆƒˆˆ‰‰ˆ„ˆˆ‡†…†‹…ˆ…„…„‡ƒ„ƒ„††…„††…xz{xvyussruuvvvvvxwwvww{vwxxvzvuwwwwwxuvxwvuuzuruuwvvwxwwuuzwvvzuvvuzwvvvuvuuvytvtprrptrqqooolpqpoppopporrpsptttqqqprspnoqttsrrrsrusqqrtuuwqqqrrrqqtuwrrtttqrtrnuupxvwqquuuuuxxvuxvyvzx|{yz{zxx{|yy{|{{zyy{|||~|}{z|{z|{{xwz{{|}|zzyzz{{y{zz||||~{{z}~~‚~}~~}}}}~~~~~~‚ƒƒƒƒ~‚‚ƒ‚€‚€}ƒ‚‚ƒ~~‚ƒ‚„ƒƒƒƒƒƒ‚‚‚‚„ƒ‚‚‚ƒ„ƒ€ƒ€‚ƒƒ„‚‡‚‚„„‚€‚ƒƒƒƒƒƒ…‡ƒƒƒƒƒƒƒƒ‚‚ƒƒ€†„ƒ‚ƒ…„ƒƒƒƒƒƒ‚‚~}~}}}~„‚‚ƒ€€~uW7& + + +�����"w{€ƒ‚„‚€ƒ‚ƒƒ„…€„„„ƒ„‚‚ƒ…„ƒƒ……„……„„„„‡ƒ†………†…„…††††‡„‚ƒ†‡‡„…„„„…‡†…„ƒ„„„…………ƒ„ƒ‚„„…‰ˆˆ…ˆ‡‡…‡‡‰„ˆ††„ƒ„‡ƒ†…€€…ƒ„vyxzuyuwxywwwvvwvxwwwzx{wxxvuxvwvxvxvxvvv{vyxyuvvywyyyz{xwvwvvvywwwvvwvtvvwxxwusuwywqsrrqrmrrqqqppqqpqqrpvutruutqqsqrrqtptuvutrtttttuuqwttsrsuusuustuxqtsvqqqqqqtuqxxyyzuvvuvxyytxvvv{z|zywz{zy{{{ww{{z{w‡y{{~{{{{{|{{||zzyzyz{|{{}~y{zzy{|{|€{|~~}~z~}~}‚€~~~|}~~}}~€~}}ƒ„„ƒ‚‚‚‚ƒ~€‚‚‚ƒ„ƒ„€~‚~~€€ƒƒ„ƒ„‚‚†€ƒƒƒƒƒƒ„‚‚ƒ‚…ƒƒ„ƒ‚‚ƒ‚ˆƒ‚ƒ‡„ƒ…ˆ„ƒƒƒƒ…ƒƒ‚‚„…„†€ƒ„ƒƒƒˆƒƒ‚ƒ‚‚‚}~~~€~€~~~‚ƒ~~~tB(���<v{€ƒ„ƒ„ƒƒ„……„€‡‚‡„ˆ„„„ƒ„„ƒ„…„ƒ†„ƒƒ…„ƒƒ…„ˆ„‡………††‡ˆ‡‡†…†…†‡ˆ„†ˆ†††ƒ’ƒ…†‡‡‡……ƒˆ‚…„ˆ„‰„ˆ„‰ˆ‡‡ˆˆ‡…‡†‰‰‰ˆ‰†…„„‡€……†€„„ƒ…‚…uzusttuxxxwvvvxvwwwwwxwwxxwwvxvvuywwxxxwxzwyvvvuvvvxyzv{xywwvwvvvvyzywwvwwszqurrrqqrrsrqlrqqpprrppqpqqttqtpqpmptqqtrrqrqqstpouruuquvqsqptqtqtuvtruvutuvvsqqpqqttturumvnyusrtutuuvyvywtw|zxwzwzzyy{yww{{|xww{y|z{{{{z|||{{zvzzzzzy{}}{|zzz{}|~~z|~~{zz{}~‚€~~}~~~~~ƒ}~~„ƒ‚~~‚ƒƒ€€ƒ€ƒ„„‚ƒ„ƒ€„„‚ƒ„…„„‚ƒƒ„„„ƒƒ†ƒ„†ƒ‚„„„~ƒ†ƒƒ…†ƒ†ˆ„ƒƒ„†„‚ƒ†…„…†„‚ƒ…‚ƒ‚‚ƒƒ„„‚‚‚‚}~ƒ~€~€‚K ������m|€ƒƒƒ‚‚ƒ„…‡…ƒ†‚…„ƒ‚‚ƒ‚„……†ƒ‚ƒ‡„ƒ………ƒƒ……†…†††…‡†…‡‡†…††…††…ˆ‡„…ƒ‚‚„„„‡„ƒƒ„„‚‚‚„‡„ˆ„†„€„ˆˆˆ‡‡‡‡‡‡‡†‡Šˆ…„ƒ€€……„ƒ„wwxytuxxxwxvxwwvxwvwxyyxxwxwxwvuzxzuywwvzw{vuuyvzvwwwuzuyvyzywzvxyyxxxwvwsysvrwrurqrsrqqrqqoqqutvqqptuuvvurqrpvptuupvstutrvuvssqvt|quuuutrruwvyuxttuuuxswuwvvursuuuvvxyyyuruwwwvyu{vvvzz{yzyy{|{{z||}{|v~}|z}|z{{z~z|zyzzz|z}~~zz}}|||{{~~z}}|~~~~€~€‚€~€‚~}~}~~‚ƒ}}‚~‚ƒ‚‚€€‚ƒ€}ƒƒ‚ƒ‚ƒ‚ƒ~‚ƒ€‚„‚†‚ƒ‚‚„€†„ˆƒ…„ƒ„„‚‚‚ƒƒƒƒ†‡‡…ƒ‚ƒƒ„‚‚†„ƒ„„††‡„‡‡‡†……………††„ƒƒ‚ƒ†‚ƒ‚‚‚†‡‡‚ƒ€}‚~ƒ~€€~ƒ~O���� +')+-:@HVk~ƒƒ†…ƒƒ…„†…‡ƒ‡ƒƒ‡ƒ‚ƒ‡…ˆ‡ˆ‡ˆˆ‡‡……ƒ„……………„‡…ˆ‡‡………‡‡ˆ„†…††ˆˆŠ‡‡…„…‡†ˆ„„ƒ‡„„‚‡‚‡‡ˆ„‰…„†ˆ‡ˆ‰‰ˆŠ‡‹ˆ‘…‹‡‰„ˆ€Š€………€†……ƒxvvvvvuvwvvwwxvxvvstvwvwxwwxwwwwwwwuvuvvvzwxxsuxvvvvvuvvvvvw{xwuy{uuvyzussssrvsqrrrrrqpqrqqrppqpqtppqrqoptvtqrpqpsorprrsrqovuuupqrurqqqqqtttuuuuutsruurrrqvuqtvvwuutuuuywxvquuxvutuuvvvxzxz{zzz||{{|xz{zv}z|||y~z{}{{{y{yvy{{{||{~~}|{|||~~~~}z~}}~}€~‚€‚€~~~}~}~‚~}~ƒ~}}~‚‚„ƒ„„ƒƒ€‚‚€ƒ‚‚ƒ†ƒ‚ƒƒƒ†ƒƒƒ‚„„„‚ƒƒƒƒƒƒ„‡‡…ƒ‚†€‚…†………ƒƒ††ƒ„†‡†ƒƒ„„„„†…†„„ƒƒƒ„„„ƒ‚„„„‚‚€~~~~~}~~~{€N� ,347:9<AAH`k::“‹€{ƒ…Š”’Ž‹€}}„ƒ„…†‡†‡‡‚ƒ…ƒ€ƒ„ƒƒƒ„‡…‡w‡„ƒ„ˆ†…„…„‡††…‡††„ƒƒ…„‡†………„……„ƒ‚‡‡‡ƒƒ‡‡‡‡ƒ‡…ƒƒƒ……„ˆ‚……„ƒ…ˆ…ƒ…„…‰†‰‡ˆ†‡†††‡…†ƒ€…€††„……„‚ƒyzuvvxwvwrswvvwvvtzyywwvxwwvwuxv{wxwvwwvzuvw{vwvxwvuvwxxxxxzzyzwyuvwyvuuyxxrwrusrtvrwopppouorqqqvqrqqrrrqrvquprotouqpprruwyqrqrssrrrrrtrtuvvsuuqqprpuuwqtusqqrttturuuuwuuusuutwuqtwyyvsw{{|{|z{{|z~{{{|{|{|{z~||~{|{|{zz{z}z{~~~{z{{~|{{||~}~~}}}~}~~~‚~€|~}~‚~ƒ€~€ƒ‚‚~€‚‚‚„‚„‚‚‚ƒ‚ƒ€€‚~€€ƒ†…†ƒƒƒƒ~…‚„ƒˆƒ‚„„ƒƒƒ„ƒƒƒ†ƒ‚ƒ‚…€~…„…ˆ…„ƒˆ…†…ƒ………††‡„‡†ˆ‡‡ƒƒƒƒƒƒ…†ƒ‚ƒ„ƒ‚€‚€ƒ~~~€ƒ€~|~†Z���# %011*�j™‘’”–”—ƒrwskŠpv„‚~|}‚‚ƒƒ~‡……ƒƒ„‡†††…„…„ƒƒƒˆ„‡„……„…‹~…„……†‡†„†ˆŠ…†ƒƒ†„„†„‚………†…‡†ˆ……ƒ‚…ˆ‡‰ˆ‡ƒ‚‚ƒ„‡‰„……ˆ„…„‰‡‡„ˆ‡‡ˆ‹…ˆ†Š‰‡‡ˆˆ‰‡‡††‡‡‡†††ˆ†vvuwvxwwvtsuwwvxwwszrvwvqwwuwvwvwwxwxvvvvyvxw|vruyyvvwxxzzzzzzvxwzxwvuuuttussruvvrqwsrrpvrpqprrqrrqprqrqrpurqqqoornproopttsrrqrsuoorssrttuuutrutuuponpoqtttrrrrqstuttttuwuvvwvtuusttvuuuvvututu{~{zyzz}||}||||{z~}|~{z~{wz|{{{{z{{z|~||~||{~|}~{}}}}}~}~}~~~~~~„„„‚ƒ€ƒ‚€~~~‚€‚€‚‚€‚ƒƒ€‚~}}ƒ‚„„‡ƒ„‚‚‚†ƒ„ƒ„‚‚ƒƒ„ƒ„ƒ‚ƒƒƒ„ƒ„„ƒƒ†„„†……„†……‡ƒ…ƒ…„……„ˆ‡…ƒ„†‚††††‡ƒƒƒƒ…„€€€~~}‚€€€ƒ€~€ƒ~€|ƒ\-#-7;;>CCDDReg:4‘“€ƒ……ƒ„„…ƒttt‚ypuyy|||||yzujvUxy}~€€‚‚ƒ€€‚ƒƒ„„‚‚ƒ‡†„„‚ƒƒ„„„„†€……„{„„††„†„„…„„ƒ…‚…„…„„„†ƒƒ…†‡‡‡ƒ‚ƒƒ„‡†……ˆƒƒ„ƒ‚ƒ„…ƒ…†††…‡ˆˆ…ˆ†…‡‹‡ˆ…‰ˆˆ………‰‡†…„……|†„††„€…wusxvvwxz{rzyxxxwwsytvwvwwwvvww{vxvuvwvzvxwyv{wvuwwvwywywvzzyzzzzzzztutwxxxxrswvuqwrqoruuprqsmpqqpoqrqpppqrpqptrpnorqqpqtpqsursqoqrrtutrtuuuuuurrrqrtstsuusqqqpq|{„ƒ‚‚‚†ŠŠŽ†…„‰€‚wpttomlo{}wx||z|}~|zz{|{~{{~{zzzz{|zz{{{|{{{|{~~zz{{{|~~}}~~~}~}}}}~~‚ƒƒƒ‚‚‚‚ƒ‚‚~~}€~‚‚‚ƒ‚‚€€ƒƒ„ƒƒƒƒ€„„…„„ƒƒ‚ˆ‚„ƒ„‚„‚ƒ‚ƒ‚€ƒ„ƒ„„ƒƒ€‚ƒƒ„ƒ†‡‡ƒƒƒ…„ƒ…ƒƒƒ‚ƒƒˆ…†…††‚†ˆ…ƒ‡ƒƒƒ„€€€~~~~~€~}~^ :œ–ŽŠ”‘’~ktw�e‚lr|~z{|€{zrbo � +qyvƒ‚ƒ‚„‚€€~€pgI‚~}‚‚‚€ƒ‚~ƒ„„……„…†„†…†‡ˆ…„†‡„‚‚…„ˆ„„„†…„ƒƒ„…†ˆ‡…………†ƒ‰ƒ…„€€ƒƒ††…„†‡„…‡„‡ƒˆ„†…„„‰„……ƒ†‚Š„‡ƒ…„…„‡ˆˆ„‰†ˆˆˆ‰Š‹‹Š‹…††‰……‡‡„†‡‡††…€…zvvxuvvxxzjzxxwsswwzsrsuvwxxwxv|wwxxvuwwvwuuvyyz{xwwyzosttzzzy{xuxtyssstuuusrruutsrqpnoqqoqppmlqpopnpoppppqqrpqptqpppnonpopppqsrploppponnlqqyy}|v}€zyyƒ}€ƒƒ‚‡†‚}kkupeb[KIGC?;;:7,533&""$W‡|yz|{{|~}{z{z{{||{{~|{zw{zzz{{{{{{|~zz~~|||{~|{{~}}~}~|}~~~ƒ}~‚‚‚„ƒƒ‚‚‚‚€~…~~~}~‚ƒƒƒ‚‚€‚„ƒƒ„ƒ€ƒ„†„„ƒƒ‚‚ƒƒ‚ƒƒƒ‚ƒ‚‚€‚‚ƒ„„ƒƒ‚‚ƒƒ‚ƒƒ„‡‡†ƒƒ…ƒƒ†‡ˆ„„„„„††‡‡‡‚††‡…†…„ƒ„€‡…ƒ~ƒ€€}~~€€~~„`J~hu|||||{{zwqgs Rsuy{~|{zy{~}wpc g‰{|~~~}~~~xj + +J†|~€€€‚‚„„……‚ƒ~€€ƒ„†‚…†‡‡„……„„„‚ƒ€ƒˆ…„€‚ƒ…„„„…„…†„‚‚„……†…ƒ‚ƒ„ƒ„†…‡„………ƒ…„„ƒ„‚‚‚„†ˆ„ƒ‚…‰…„……„ƒƒ‡ˆˆ…ˆ‡‡Šˆ…‰‰…†‹…‡‡‰…„‡‡…‡ƒ‡€…††…†tsuvswvwwxzzxzw|zzuxs~svvwxxwxx{vwuyyyz{wyz|uwzywzwzzzz{suwyyyzyvxuyrwuususwsutsqruwwzurqqpoopoppooooonooslmrvwzv~‚†…ŠŒ‹†ˆ‡†„Љ‡„~}tsqzmqe_NIMLD@<95520# +� ��"8‚|zx|}{{{{zz{z{z{{{{}~z{z{{z{|zz{|{{{||~~}}}}~~~|~~~~~~}|}~~~~~ƒ„„ƒ‚ƒ…ƒ…€‚‚„€}}}~~ƒƒƒƒ„ƒ~ƒ‚‚‚€‚ƒƒƒ‡…„„ƒ‚ƒ„†…„ƒ„‚…ƒƒ‚‚€ƒƒ‚‚€‚‚‚€‚ƒ„„€ƒƒ€ƒƒƒƒ„‡„„„ƒ„„………‡„„ˆƒƒ††…ƒ…ƒ…ˆ…‚‚‚‚ƒƒ€ƒ~‚~~~‚ƒƒƒ~~€}€e�G€u|{z|}}~}}~{ni Cv}~}||||~~}w^� a„{€~~{y{{tn +Etz~~€€‚€…ˆ„‡…‡†„„…‚ƒƒ„„ˆ†ˆ…ƒ………†„„‚„……„†„†„………†……€€„„„‡„ƒƒ†„……„………‡‚…†ˆ…„„ƒˆ…‰‰‰‰‰ƒˆ…†ƒˆˆ‡‰‰ˆ‡ˆ‡‰‹ˆ‡…‰…††……‡ƒ‡……„ƒ„ˆwuvxuwwwwxuzxxvxvvvvuvsuxwrxwxsuuuvwwwx|wwwwtuywwzoxvystuwvwwxwwuxxzvxwuwxsusuqo{qljlpnrruzxwqtvuztr||~yszussdga_NGFFCA<:7424(!��>|{x{||{|||{{||y|{{z{z{{|z|~€|{}||{|~~}€~~}}~~‚~~~}~~‚~ƒ‚‚††ƒ‚‚‚ƒ‚‚ƒ€„„ƒƒƒ„‚„‚ƒƒƒƒƒ†††„ƒƒ‚„†„ƒƒ‚ƒƒ„ƒƒ‚ƒ„„ƒ‚ƒƒ„„‚ƒ‚ƒ‚‚ƒ‚‚„†‡‚‡‡†„†„„„‡…ƒ„ƒ„‡‡ƒƒƒƒ†ˆ„ƒ„…‡†„ƒƒ}‚ƒƒ„~~€€€€ƒ€}}g�B…y}||}{{|}{{z|m� At|}yz||~}}v{mZ‡||~~~z{}x_>‰ŠˆŒ‹Ž’’’’Љ||‚ƒ€†…„„…†ƒƒ„†…ƒ†‡„…†……„„…„„ƒ………‡„„…………„…‡€€…‚ƒ…‡†ƒ€‡…„…†„„„††ƒƒ…†ƒ…………‰…ˆ„ƒ†„ƒ„…†‡Š‹‰‡‰ˆˆ‡‰„‰‡††‡…†…‡††ƒ†…wzwysxwwvwzxxvvwwwvv€uwuvxxvwrxsvv{xww{z{v{tvvxwyzzwytzrvuuwwwwwzvzvxwwx{rurvcOUehle[YNKOGE@?;7331."&! ���� + �8|z{|~|{||z{{{zz|{{~}||||}~}}|}~}||~~~~~{}}}ƒƒ…~‚~~~ƒ€~~‚ƒ†‚‚„†…€‚„ƒ…‚ƒ„ƒ„……„†…„‚‚‚„„†…ƒ„††‡…„ƒ†…†„„„‚ƒ„…†„„ƒ…„†ƒƒƒ†ƒ‚ƒƒƒ‚ƒ„…†„ƒƒ‡‡ˆ…ƒ†‡ƒ‚………ƒ‡†„ƒ„…‡ˆ„ƒƒ††‰…„‚ƒ……ƒ‚ƒ‚ƒ‚‚ƒƒƒƒƒƒƒƒ„~‚€€~}n?}r{€€€|v€n: +8@fw~€€€€„~‚xl�������"RŠ{z~„†‰“‡…~b4"%-:AJ]X]QOLHDB@>EKOdz‚‚‚ƒ„ƒƒ„‡„ƒƒ†„‡ƒ„„‡ƒ„††‡‡ƒ„‚…‚ˆ„‡…‡ƒ…ƒ„„……„ƒ…††…‚…‡‡‡ƒ„…‡‡‡†††‡†…„„ƒ„…ˆ…ˆ„„……‡ˆ‰„‰‚„‡ƒˆ‰Š‰‹‰Š‡…ˆ‹‡ˆŠ‰…†…†…‡†‡†‡ƒˆˆˆxvsrssuwwxxyxwxwxwwvxwvyvwwvwtsqsvvzvxututwysyuuwuuwuwtzuuxuwwvvzywytvywyvrrrkS@2 +��2…{yz|z|{~|{z{z|z~~~~~}|zzz{~~€€{|~}~‚ƒ}~}‚~€~‚ƒƒ€‚ƒ‚‚ƒ€‚‚‚ƒ‚ƒ„†ƒ„„ƒ„……‚„†…„ˆ†ƒ†€„…††‡…†„ƒ„†„„‡„…†‡‡„‡„ˆ„‡ˆ„„„„„…†‡‡†‡†ƒƒ„……†„ƒƒ‡ˆ‡ƒ„†„ƒ„……†„‡†‡‡‡††‡‚ƒƒ„†…ƒƒ†ƒ‡„„‚„„~ƒƒ‚€ƒ‚~~€€~~y :Axlt|{~~~|zyw}ƒ[<Ÿ–€‹‹ŠŠ‰‰…~}}V'342+ �Q†{}€~ˆ8%41, <iˆ…u…™™J +T„€~€€ƒƒ‡…„ƒ~‡„ƒ‚ƒ„ƒ„„ƒ„€„…†‡„„………„„ƒƒ…†€ƒƒ…„‚†‡‚†€…‡‡ƒ‡„ƒw‡†…„„„‡…ˆ„„†……‡†…‰€…„„…†‰‹ŠŠ‰‡†††‡‡†Šƒƒ‡†……††„„„ƒ„‡wyu~zzyxwywxwzwwwzwwuyzywwwwvwqwrwuzz|uyyzyzy{u~t{tzuxvxvywuwwwwzzvyuvy{xwutqgH0� +&}||}{|||{||{|{{|~|||€{~zz{}€€€€€}€~~~}€€ƒ~„~ˆ€‚‚~ƒƒ~‚„ƒ}‚ƒƒ€‚‚‚ƒ‚ƒƒƒ„„„ƒƒ‚‚„‡†‡…ƒƒ‡†ƒƒ‚„†‡‡………††ˆ‡‡ƒˆ……††…††††‡†††ˆ…ƒ†ˆ…‡ˆ‡…†‡†„†ƒ„‡††‡†‡„‡ƒƒ„‡„ƒ……†„„……ƒƒ‡‚„„„††„…††…†††‚‚‚ƒ‚ƒƒƒ„„€‚‚ƒ‚€~€~z{��@[€‡ŠŠ‰„…‡€thZSD:68>@A@CGDA@@=;91,9@G\q{~†‹“‘—šI�Q„z}}~†D&"rx~xp‘d0<T‡†„…„†‡‡……~†„„ƒƒ„„‰‰‰€„ƒƒ„‡‡ˆ‡‡‡…ƒ†„„†‡…‡Œ‚†‡„‰‡…‡‡†ƒ†‡‡„…‡ˆ‰„‡…‡„„……„„„…„Žƒ…†Š…ˆˆˆ†‡Š‹ˆˆ†Š‡ˆˆˆ‡ˆˆ…„††……‡ƒ‰ˆˆyywuvxxwxwuvxztvwwrwuzjxuvwwwvssrxwvvsuzvxoxw{utvtuxv{vxyyyzzwywzywxvvyzwuutuiJ�����>Voz|{{{{z{|{}|{z~|z{z|{z|{}€~~~€€€~ƒ}€ƒ‚ƒ~‚ƒ„ƒƒ„‚~„…ƒ‚ƒ‚ƒƒ…ƒƒƒ„…„…‚‚‚‚‚ƒ‚ƒ††‡„„„ƒƒƒƒ……‡†……„„ƒ†„„…‡††ƒƒ……††……†……‡ˆˆ‡‡…„ƒ„…‡†ˆ„„„†††††‡†ƒ†ƒ‚‡†„„†††…ƒ††……†„…„…†…ƒ„†……ˆ‡…‚‚‚€€€€ƒ~~€|~z|-9;>EIQW\cefsTE@<8*"A[p}}ˆ‰›V +h{t|yxvy{zyzun‚^�[€{z}~~ƒ=$x€v{{x{b��H„ƒ‚‚~„~~}…„„~~€†ƒ€‚‡„~{‡††„ˆ……ƒ„ƒ…ƒ~€ƒƒƒ‚„‡‡„ƒ„‡…„…†„………„„ƒ‚„„…†…†ˆ†ƒ„ˆ†„…„…„„†„…†‹‡ˆˆ‰Š‰‡ˆ††ˆ…†…‡…ƒ……‡„†ˆƒ‚„‡wyzxwwwwwwxzxxrwwwvy{zwwuwwyvwwvsvwwt{wvvvxxyywxvwuz{zuzyyvyzxwyvxyytwwxvtuqrjY��������*>XIOTU]afmabaju{z|z{{{{||{{|||z{|||{~||{|~€~~€€„ƒ‚€~€ƒ‚€„‚‚‚€„‚ƒƒ‚€„„ƒ„„‚ƒƒƒƒ„„„…†ƒ„ƒ†„†…ƒƒ‚†„……†ˆƒƒƒ‚†‡††‡‡†‡†‡‡‡†‡„†…„……„†††…‡…ƒ†‡†‡„ƒ‚ƒ…†…Šˆ‡‡†„…†‡……ƒ…„ƒ†‡ƒ…„……†ƒ‡†ˆƒ†ƒ‡„ƒƒ†ƒƒ„…‡‡„ƒ‚‚ƒ„€ƒƒ‚€€€€€€}z„#9r{{Ї‰Š‡ƒ‡Šh�pq‚wqf1(jˆwy|}}zw{~}|urX��O„z~~~~‡<��(gŽv~z|~e +A‚€„…„„ƒ€‚€€„ƒ„ƒ†‡„†ƒ…††Š‡‰ˆˆ‡‡…†€ƒ„…„…ƒ…‚…ƒ†…††………„‰„…††ƒ„„„ƒ„„ƒ„„†„…‡‡…ƒˆ„„‰„„ƒ†„ˆˆ‡ˆ‰‡ˆ‡‡…†‡Š…‡ˆ‡†‹…‡………††ˆ‡ˆ‡‡wyxyzywxuxxxxwtwvzzyxyuuvvwxwwxurwvvuvvwwxwxuv{wxxwwyzuwzyvwyyyvwyvxusuuurtrqi`��������teZ\d`nluvwƒ\F–ƒ†Ž€€†~|uvwzz{{zz|{~|{z|{{|{~z}~|||~~}€€~‚€‚‚‚€~€„†‡€‚‚ƒƒ‚ƒƒƒ€‚…‚„„„ƒƒ„ƒƒƒ‚‚ƒ„†„„††ƒƒƒ„‡ƒ†„†‡ƒƒƒ……„†‡ƒ†‡††‡††‡‡††ƒƒ„†‡…ˆ††„ƒƒ„‡†‡„ƒˆ†„„…‡‡†…†††„‡†…‡††ƒ††ƒ‡…„ˆ…ƒ…†‡ƒ‡„…‡ƒƒ…„„†ˆ‡‡„†‡‚ƒ‚„„€€ƒ„€ƒ~~‚{€)7kqz||}}}}{t‚ypxvz|{tj!b€{z{|{z{|~~|{a�M}{}~}|†2 +bŽl|y„g +:~€€ƒ~ƒ‚„…‚ƒ‡†„ˆƒ…ƒ†ƒ†|‡ƒ‚„‡…„…†€€„ƒƒ„ƒ…………„€†‚‚‚€„ƒ€„‚‚‚…„„…‡‡‡ˆˆˆ‡‡„„†ˆ…„ƒ…‡‰‡~‚ƒƒ„††††…………ˆ……‡ˆ…‡……†„…………xyxzxxwwwwwwwxsy{|z|xzyzy{wywwvywvvwvwwwuvvzuvwwxywuvxx{ywxyxzzvvvwwuvwzuuusqjb���� +'.258?EQRRN\aP�†]evzzxƒxsmjt†RŽ^pvwwwvwyy{{|{{z}}}|z}}z|{|||zy|~|{}}}~}~~~€„ƒƒ~~€„ƒ€€‚ƒƒ„€‚„ƒƒƒ€‚„†‚ƒƒ„ƒƒƒ†ƒƒƒ‚ƒ†ƒƒ„†ƒƒ„‡ƒ‚……††ƒ„…‡…„†‡†‡‡‡‡‡†…‡„…†……††‡„‡†„†‡ˆ‡‡†‡…ƒ‡‡„ƒ††‡„ƒ††‡‡ˆ…†††„ƒ††ƒ†††„…ƒ„†ˆ„‡…ƒƒ„ƒƒƒƒ…††ˆƒ„„…„„‚„‚€ƒƒƒ‚€ƒ~~}&:n}}~}||||{}i]‹x}}}tr b|{|ƒ‚~}yrhKx{{zuˆ9"0\y~|q � !)†{~€€ƒ‚ƒ„ƒ…~†ƒ„†ˆ‡‡ƒ†ƒ‡‡‡‡…‚…„†††……ƒ„‚„……†‡€…ƒ……†„‚‹„…ˆ€ƒ„„……‚Œ‚‡‡‡†‡†………„…ˆ…„†ˆ„ˆ…„‡‹Š‰ƒ‰„…‡‰…‰‡‰‰ˆ…†‡‹…ˆˆˆ…ˆˆ††ˆxyxxvuuxwxvsrrrvyxvuwvxxyxxwvvwxwvvvvuuuuuvvuvwvwzwvzzzwvxzyvuzzvvuxvu{zusurqkh���8ThUW]ddeowop^-�?€ƒƒ{}zzzz~“^�-uhrturqqqrpogzIywxxvvyvy{yw|{{{{|~{{~|||{||{{z{||~~}~~~~~€€€~€ƒ~„‚€‚„ƒ‚„‚ƒƒƒ„†„‚‚„ƒƒ„ƒ…„ƒƒƒƒ„„ƒƒ…ƒ‚‡…„„„„††ƒ…†„†††‡……†…‡ˆˆ‡‡„…ƒ„„†††‡Š…†…††‡††‡ˆˆ‡†……†…†„‡††††ˆƒ†……ƒ„‡„ƒ„††‡‡……‡‡„„†…†ƒƒ„†„„‡…†ƒ‚ƒ……‚„ƒ€€‚€€‚ƒ|‚~‚~€‚z59„}~~~|}}‚{g-brz{{|u `ƒ|}}}~~~yt„u�, =K}nu…uoC.7(_ˆ~~€}o[y}~~~~„‚‚„…ˆ…ƒ‚ƒƒ……‚„„ƒ„„„‚„„†ƒ€†€ƒ„„…„€€€ƒ†…„…„„„‚‚…‡…ˆ……‡……ƒ‡…„…„…‡ˆ†‡‡……ˆ„‡…†‡‡†……†ˆˆ†‡‡…†ˆ………‡‡…†…wyzzwvwwwzuvwyxvvwwvw{ywwzv{uwwxwww{vwwwtzz|uvuyvzvzzwzxuzzyvuvyxyxxvwz|swrqql`���W]m{v€s|yykmjT1�^‡ejpooooookc€^��3{rqutouwpurrj| �Frwxywyz{z{zxw||z{||}y|}||€}|||}{||}}~}{~€~€€|‚‚ƒƒ„~ƒƒƒ‚€‚€}}€€„„„„ƒˆ…ƒ‚ƒ„„ƒ‡ƒ‡…ƒƒ„ƒƒƒ††‡…†……ƒ†…„„…†‡†‡†…††…†ƒƒƒ„ƒ†‡„„‡…„„ƒ†‹†„†‡‰‹††‡†‡‡‡‡…„ƒƒ……………†‡‰„„„†ƒ…‡„ƒ…†††„„……‡ƒƒƒˆ…„‚‡………††‡‚„ƒ‚…ƒ„ƒ‚~~‚‚‚‚ƒƒƒ~}„69€~~~~~~~}yow3$_m…„zvu“ ^~}|}|…“Š•˜g?d}yx|zaI37>#0l~}€|yu� w–‹ˆ‹‡}w€‚ƒ‚ƒ‚ƒƒ…~ƒ‚ƒƒ„ƒ‚‚ƒ„…ƒƒ„„„†…‰„„„„„†…†…€……ˆ‡…††……ƒ„ƒ…„ƒ‚‚…†‡††‚………€‡ƒ‡†……†…‡‚……‡†ˆ…‹…†…ŠŠŠˆ‡†‡‡‹…‡‡ˆ†…†…†‰…‰†…‡…†Šyywzvwwvvxxxxwxwuvxwwuvywusrvxrruwwvwwwvutvwvvwvvuvzyxxvwzzxvwwyyvxyuw{ztrrqrmb%����Shdkoooopolkjc8�Drloolopppopkri.Šurqtrwvqsutoƒ + Cyovvwyzzzzywwyxv{{yyz|||~|zz|}}}|}z}~|||}~~~~ƒ|~~ƒ‚ƒ€„€€€€}ƒ„„„ƒƒƒƒ„†„„„„„ƒƒ…„……‚ƒƒ„ƒƒƒ„†††„„„†……„„„………†‡‡…„‚„‡ƒƒ„…‡…‡ˆˆƒ……††…‡‹„ƒ„††‡††‡‡‡†„ƒ„……„‡‡„„„„ƒƒƒƒ„‡…„ƒ‚††„„…‡„„‚‚†‚„‚†††ƒ‡ƒƒƒƒƒ‚„„‚~€€~ƒ€€ƒ~}~}~~€:6„|}|}|{||„y…)���� N‚v|‹…Z@�S‚}|{}|t\2.81-9Gcrk{ˆhC:&Qo|‰€x‰‰{~~|z{!%nty{||z{z|ƒ€ƒƒƒƒ„~}}ƒƒ„ƒ„„‡‡„„‚ƒ‚„ƒ…ˆ…„„‚„†ƒ†ƒƒ~~„€…ƒ„„‚‚…„ƒ…‚„‚…‡†„‚„ƒ„„‚…„†‡…ƒ…ˆˆŒ„ˆ‰‰†ˆˆ‹††…ˆ†………†††…†ˆ‡††‰yyxzz{txxyzzwvwzuvx|{zzzuwxwuwvvuwvyz|uyyzu{vwuzzyyzyywuwywvvwxzzwvxtvx{uwsrqmk-��M|iqmooopqoojv=�@poqolopupuopfu +,‚usqsuuuuvros‡&8T}lr‚zz}€€…ˆ’…ƒwx{|z€{}|}|{{}~~{|z~~}|||~€ƒ~ƒ~€‚ƒ€ƒƒ„~~„ƒ„ƒƒ„„„„……‡„‡„…ƒ†„…ƒƒƒƒ……„…‡††„„ƒ…†ˆƒ„ƒ„„„‡†‡‡ƒƒ…ˆƒ‡…ƒ‡ˆ‡ˆ†„†‹†ˆˆŠ‡†‡‡…†‡‡…‡‡„ƒƒƒ…„††ˆ‡‡„ƒ‚ƒ„‡„…††ƒƒ„‡…ƒ„†„„……„……ˆ€†„……‡ƒƒƒˆƒƒ„ƒ‚ƒ~€~€€~ƒƒƒ„„~~~:0ƒ|€||wztwimUD?::?ADKSUV[aTQ==0%#/9IQ—ˆ‚{t{rS}}{}|zwN":Z~wu€„X� +L‚†|}‚}|‚ƒ„~~z{ƒuz}}{ƒ‚ƒƒ„„„ƒƒ‚ƒƒ…‡†€„„ˆ‡†„ƒ†ƒ„€‡„ˆ……‚€ƒ†„†…†€„†‡€ƒƒ‡„ˆƒ†…‡…‡‚…„ƒ„ˆ‚ˆˆ‡……ƒ………„…„‰ƒ…†…†‡ˆ‰‰†Š„†‰Š‹‹‹‹†Š‰ˆ………†††………‰ˆˆ„ˆyyvzxyvyxyoyxzszvwwwxxzztxywxvvxwuuvwxvvwvwvwwvuwz{yzzuxyuww{zzzzvwwuwwyuvurrmn1����H}oqkmpolnpqri? Atkmmkkopppon`Š�{tsvvtzssv}{vS3JWm}v}th`^QOMP]kv|||{|}}||zz~{||{z|{|~~}~}~}„€€€€€ƒ€€~„„„ƒ„„ƒ„„ƒƒ‚€…ƒ„„ƒ„„‚‚ƒƒƒƒƒ‚„ƒ„†‡…„‡…‚‚„„……ƒ‡„‡†‡……„„„†ˆ„…‡„‹…‡ˆ†‡‡††ˆˆ†‹Š‡‡……‡†‡ˆ‡ƒ‡‡‡‡„ƒ„†„…„‚ˆ†……„‡ƒ‡…„„…ƒ…†…„‚†…††…‡‚ƒ„‡…„ƒƒƒ‚‚ƒƒ‚ƒ€€€~ƒƒ‚€€€€€~‚<$†}~||‚a +`‘ŠŠŒ‰…Œˆ„ƒ„ttuC(WŠovzw…c +T€}||{zv?! 8gpw||uk"J…v{{||||ƒ€~~{x&{~}€‚ƒƒ‚ƒƒ„ƒƒ€~‚„‚„€…ƒƒ„„…„…„€†„ƒ‡…„††„ƒ„„……„ƒ…†‡„…†…ƒƒƒƒƒƒ…„‚ƒ„……„„„…„„ˆ†…‡ƒ„ƒƒ„‰ƒƒ……†‡ˆ‡†‰†„†‡ˆˆˆ†ˆŠ†…†…………„…ƒ…„ƒ††ƒ‡yzxzwwwywxvywxwxwwwxw{zz{zwxxxwvu{uvwwvzwwvywvuxyyyzxzzzwvy{|{zzzuxywyzzuusuqlp4������Eglorxqqqqmjg\OLyfkvwzwwvwlŠ˜��������&wrqvwtrH?F@<:>CXjuv}|L�[y|~|~~|zz{}}}€|{|€{~~~~~~~~~€€„€‚yƒ€ƒ€‚„ƒ€‚‚€€€‚ƒ„ƒƒ„ƒƒ„‚ƒƒ‚ƒƒ‚„ƒƒƒ‚ƒ„‚ƒ…‚†…ƒ‚†ƒ„„ƒƒ„†‡††„„„ˆ†‡…ƒ‡†…ƒ†‡†‡‡ˆ†‡‡‡ˆ‹…‡††‡ƒƒ…‡„‡‡‡ˆ„…‡‡‡ˆƒƒ‡††††‡ƒ‡…‡„‡‚„ƒƒƒƒ†‚…†…†„ƒ„ˆ…„ƒ‡ƒƒ‚ƒƒƒ€~€‚ƒ‚ƒ‚„€€~„B'z{||{yUH0{Šqw{{}{{||zxyiD.Zqu}{wuf L~~||zzv=.qtx}~x†d Hx~„„†~~€|{))tz€ƒ‚ƒ‚‚ƒ„€†ˆ…ƒ…‡„ƒƒ„„ˆ„…ˆ„…€‡„„„†„†…………ƒ…„‡††…†ƒ†‡ƒ…‡ƒ„ˆ„ƒ‚ƒ…„…„…………ƒ„……‡‡‡†„ƒŠ‚…‚Š…‡‡†‡‰ˆ‰†‡ˆ‹ˆˆ†‹†‰‰ˆ‡ˆ†ƒ…‡ƒ‡ˆˆ‡ˆzzzzzzzzuuv{uvxxwxvxwvwwxwxwzwxwwvvuvwxzwxxxvvwxzyyzyxyyywzxxwzzywxwwwwzwuusqm{7R`^kvrvqy|~{v\O(?WexvtgpkodcXTA:@MFCQXYb_rmc]Xuuntup}28Ohdhlpy^�<Y|||{|}{~|{yz}{}~||}||~~~~~~~~~€€ƒ€‚ƒƒ„„„€„ƒ€ƒƒ€€€€€‚ƒ„„„ƒ€ƒƒƒƒƒ‚„„‚ƒƒ…ƒ‚‚ƒ†ƒƒƒƒƒƒƒ„ƒƒ††‡‡‡…‚…††…†‡…„………‡††‰‡††‡‡‡„ˆ††ˆ„„…‡„†††‡‡†‡†‡ˆ†…‡††„ƒ„ƒƒ„………„‚‡„ƒ„†ƒ„„†„ƒƒ‡†„„…ƒ„ƒƒ„ƒ€~€}ƒƒƒ‚„€}ƒ€€~Eƒz~~z}V s}w|{|}~|{z{wwrE+^}||||siH†~{|yz<�-m„y~~zf��Cvx|€€„|~~|}7uv|~€€€€€€ƒ‡‚ƒƒƒ‚ƒ„„†„ƒ€………‚††„„ƒ…€‡‚„ƒ†‡…‡‡„~†ˆ„ƒƒ‚„†„†„„„„ƒ‚‚„…‡………„…‰ƒ…‡†ƒ‡ˆ‰…ˆŠ†„†Š‡……†…‰„ƒ„†…‚‚‚€ƒˆ††…‡xzvvxzvzuxuvxwyvuwyy{wwwz{{xywwuz{{vwx{wuw|wyyzz|yzzzyzyzwxvwz{wwxxwywzwzssrmw:�������,pl]XMHJBA:7894D^_bwuSSˆ‚}ƒzvsxsvƒ‹_ jupvvqu9 GsnsojoeQ{||}||{~{~{}{}|}{~~€~~~€~}~~~~ƒƒ€ƒ~~€ƒ€‚ƒ€‚€€‚ƒƒ‚„ƒƒ…ƒƒ„„ƒƒƒ„ƒƒƒ…„„ƒ…††ƒƒƒ‚ƒ„ƒƒ„†ƒƒ‡…„…ƒ†††………†…‡††‡ˆ†‹…‡‡‡‡‡ƒˆ††ƒƒƒ‚…„†„…‡‡‡†„†ˆƒŠ‡†„„…‡…„„‡„ƒ„ƒ‡„ƒƒƒƒƒ†‚ƒƒ…†††„„‚‚ƒ„„„€~€ƒ€„€€‚€~}†Iwy}}|xj� +c~|}~}~~}|zwJ��&T†}z}ts Irv|~yu{? w|v~~|€gEj|~~„~|„}|€€}† f‘•Ž“€~€ƒƒƒˆ…„„…‚„ƒ„ƒ€€ƒ„………„‚ƒ„…†€†‡……†……‡…………‡‡‡†„„‡‡‡„††‡‡‚…ƒ…†‡ˆ‰„ˆ„………„ˆƒ„„„…І‡ˆ‡†††‡…‰…‡††‡‹ƒ†……ƒ‡‚……‡‡…††‡uyvvwxwyswtzwvuvvvwyswwwxvwwwwyxv|yz{{z|vuw{z{z{y|yyzzyxzxvuyzzzzzyxxwwwxyvvsmxA�JZ_Z\do5"5idckjj` XyckooonqrokhwY hysuvuv0Furrrrwm�P|z~|}|{z{z|{{|~~{}~~~~€~}}~€ƒƒ€€ƒƒƒ€ƒƒ‚„ƒƒƒ‚„„†ƒƒƒƒƒƒ‚†„ƒ€„‚„„…†…„ƒƒ‚ƒ„†……†……††„††„ƒƒƒ……††…†††‡‡ˆ„ˆ‡††‡„ƒ†ˆƒƒ„„††ˆ…†…‡‡†„…†ƒ‰‡‰ƒ„‡‡ˆˆ‡†„ƒ‚ƒ‡…ƒ‚ƒƒƒƒ†…„„‡…„…„ƒƒƒ…„„ƒ€€…ƒ€€€ƒ‚ƒ~}|ƒL!uy€€~}yf�Y‚{€€€ƒ€}|y‚SU||~x|tm@mnxwr‰B +}zz~~‚i 3ar{‰…Šƒ‚‚€ƒ€4.38;IU]YVSUbr~‚‚„ƒƒ‡†………ƒƒƒ„„€ƒ…ˆˆƒ†„„‚†„†…‡……„ƒƒ„ƒ‡†…‡††ˆƒ‡„…†……†ƒ†‡…‡‚„„‡‡††ˆ„††††…„‡……ƒ€„ˆˆ„…‡……†ˆ†ˆ…†‡‡ˆ†„……„…‡ƒƒ„††„††††tyxywwvyuwuzvwvwxyw}sxxxvwvzwxx{w{wwxzzyvvxz{{y|wzyzyzxyyxwzyzxxwxyyx}xxxyxvqlpH4[x}ifjn�"dlkkhfp +Qjtqqrtttrrmnb[€qtuqp/ G{qwrrou K†{y|||||~||||{{{~{~}€~}~~€€€€~~€€€ƒ€€~€ƒƒƒ„‚‚ƒƒƒ„ƒƒƒƒ„„ƒ‚€‚ƒ„ƒ…„ƒƒƒƒ„ƒ„„‡ƒ„ƒ„„…†‡††…†††…ˆ„„ƒ„…††‡††††„†‡†…‡‡…†ˆ„‹Š‹„††††‡…ƒ…‡†††„ƒ†‚ˆ‡ˆƒ„„‡……‚ƒ„ƒƒ……†ƒ„ƒƒ‚†„†‡‡…†„„„…‡‡‡‡ƒ„ƒƒ„„‚€„~‚€€ƒ|S�$f‚~€~~vv�W}{€€~~z„Q��]{{~z}ux!6jox†€upC)0<Sp{}€~‚j'09?@B:#`{‚€~€{€“Œ†„€_< s}‚ƒ„†„ƒ„†„ƒ‚…„…„ƒ„‰‡ˆ……‚€€€…†……„……‡‡ˆ‡†ƒ‡††ˆˆŠ‹ƒ…„ƒ„††‡‡‡…ƒƒ‡ƒ„„ˆ‡††‡……†ˆ††„‡„†ˆŠƒ†‡ˆˆ‰ˆ‰†‹…‰‰‹‡‹†ˆ„†„…ƒˆ‰‡………††‡††vyyxzxuuuvwzw{zxxvw‚uwxy{vwxwxywxyvxuzu{zxz{y|zxwz{zzyzzzyvwzzyzzwuyxxwxzwquqnqT4vgtmk`d�jzmpofrQ‡quttsqqqrrrju%Wnopnko2$Fvqwrtpw!A‚{|{{|{}||~z{{||{{~€~~~~€€€€€„‚‚€„ƒ‚„†„ƒ„†‚„ƒƒ„„„ƒƒ„„„‚ƒƒƒ†„ƒƒƒ†…‡ˆƒ†‡†††††ƒ†‡ˆˆ†„‡‚‚ƒ†……‡†††‡‡‡††‡ˆ‡†…†ˆ‡‡…†‡‰‡……‚„„ƒƒƒ†ƒ…ƒ…„„ƒƒƒ……†…‡‡„‚ƒ†„††ˆ„†…„ƒƒ‡†„‡ƒ„€‚‚ƒ„ƒ…€€ƒ‚ƒ‚~}^��"h{~~vr�QŠ|~|yR��R{}~{}v�",44*7C>k|‚|}qy~~€}}m- *4Aƒ€€~~|{un‚>. 4v|‚ƒƒ…‚„†„„‚ƒƒ†‡‚…‡ˆˆˆ…ƒ‚…„…†ˆ‰…„ˆ~‡…„„„„‰ˆƒˆ‚„†„„‚„…„……‚…………„„…‡„‡††…‡‰‡…„„†‡ˆ„†‡…„‡……†…„‰‰Š…Šˆ††‡„„†„€…††††xxyzxtzyxxyyzyzywx|uxyzyz{|zyx|vwvxw{{|zyzz{{zyyzz{y{yzyyyxv{x}}}{zxxvwzxvtrpj[1‚glmldY�{jjp|f|G‚qvuvqqrtttkdt*Ooy~ke€70A'†qwruo€[z{|~~||{|{~{|{|||€~~€€}|€}~€€ƒ~‚ƒƒ€€ƒ„€ƒƒ~„‚ƒ‚‚‚‚ƒ„‚‚€ƒƒƒ…†……ƒƒƒƒƒ…ƒ†…†ƒƒƒƒƒ†‚‡‡ˆ…‡‡†††…†††‡‡††ƒ……„‡‡†††‡ˆ‹‹‹†††ƒ†„„††‡‡ˆ‡†…ƒ†ˆƒˆ‰ˆƒ†‡ˆ‚’ƒ†ƒƒ…ƒ„……†„„ƒ†ƒ††ˆ‡ˆ„‡……„ƒ…‡„„‚‚„€€~ƒ€ƒ‚ƒ~}\��ez€~ytN„|~~~yQ�P|}{}v€ D„nv€}}}~}}~~}~t0(A‚€‚‚€~}v‰=q€…‚‡‡‡…„„ƒ…‡††ƒ‡‡‡†‰…‡„ƒƒƒƒƒ…†‡‡†……‚‰„†„……†‡‡†‡‡†…‡„‡…„…„‚ƒƒ„„„„…„‡‰ˆ‰‰ˆ‰‡ˆ‰ˆ‡ˆ††‡‡†……‰ˆ‡…˜ƒˆ‰‹‹‹ˆ†…†„ˆ‡ˆ„…†…†………‡yxywx{szwzyvvvuvwwzyvx|wvww{zwxxyzvtv{vw{zzzvwyzwzvzyvwyv{{yw{{zyyyxxvzvzwwvmojY4mnljmY�*lelp{M€G€puppqqprtx]X'H„yth_6+0…qrrtp|3’‡povru||~{{{{{{{|{~}{|€~|||~~€~~~~€‚ƒ€ƒƒ€‚ƒ}€ƒ……ƒƒ‚‚„ƒ€„€ƒ‚‚……ƒƒƒƒƒƒ…„ƒƒ‚ƒƒ‡††…ƒƒƒ…†‡„…†………„††ˆ……„†‡„††‡ˆ†…ƒ…ƒƒ††‡ƒ†…ƒƒ„‡†‡‡…‡ˆˆ†ƒˆ‡†††‡‡„‚‚ˆ…„……‡‡ƒ„„ƒƒƒƒ„‡‡‡†„„†„„‚ƒ†„ƒ~‚~„…ƒ€}‚~……ƒ€‚yadz‚~~~r�N|~}€€~|{{`�N‚{yz{x}% Czwz‚~~~~~}~{u1@Ž~€~‚ƒ€}?�f…ƒ…„‚€ƒ…ƒ‡ˆ‚ƒ‡„ƒ………‡‡‚„ƒƒƒ„†…†……€‚ƒ‡‡‡‡…††ƒ……†‚†……‡…ƒ‚„„‚‚ƒ‚…„…†‡‡„„……„‡ˆˆ…ˆ‡‡………„…‡ˆ†…„…Љˆˆ‡ˆ……ƒƒ€‡€†…†††…‚ƒww{zz|szwxys~tvvvwywzzy{}xvxv{|zz{|wzoŽwzz{xyz{wzzzyzvzu|||{{{zvxwvwvuzwuuuqhe%tmmgdv`��ccwtxŒ›- >ˆqursrhZPQVT^EFZQNV[ZB =zv{‹ˆ‰|ypvuup&iŒkq~vvwy{{zyyy|{~€|||||~~{~|~€|€}||~€~‚~~ƒ€‚ƒ€€~ƒƒƒ„‚‚ƒƒƒ„€ƒ‚„†„„ƒ„ƒƒƒƒƒ„ƒƒƒƒ„‚…‚…‡„„ƒ„„ˆƒƒ……††…††ˆ‡ˆ„††„††‡ƒƒ…††„ˆ„ƒƒ„†‡ƒƒƒˆ„„…†………†‡‹†‡…‡…†…†‚‹‡†…‡…‡ƒ‚ƒƒƒ‚„………„ƒ„‚†……ƒ„…ƒƒƒ„‚€‚ƒ‚‚~|€ƒ‚z^�^~~ƒ|~zxJƒ~}€€€‚€€~{{uR��J|{€€€yy-@v…{yz{{~~~|}€{q3-Žx~~€~yŽC�!i‚†„ƒ‡€„„„…ˆ„„€ˆ…†…††‡…ƒ„…„„„………„……………„‡‡‡††…†ƒ…††„‡…ƒ……„„…‡…„ƒ‡‚„‚„†‡‡‡…Іˆ‡ˆˆ‰ˆ‰ˆ‰„†…‡…Š…˜„‹Š‰ˆ‡……„††……~‡…†…†€†‚„www|wzvx{yyyrqswwwyww{zzwvvxxw{xw|yxwvtvv{wy{zuyvzw{wsuvwuvzzzvyywywuvvvyvurrrhfb[jtk]Q +�� &1;EONH@QB8?XX[go*5ruttplgxxzhftF�.<rqturuvquwqo(^zvuwvw{z||yz||}~~~|}~~{||{~||}~}~~~~~~€„‚ƒ‚€‚‚„ƒ„„„€‚‚ƒƒ„ƒƒƒƒ„ƒ„‚‚ƒ„ƒƒƒƒƒƒƒ„‚†ƒ‡†……‡‚ƒ„ƒƒ…………†…‡‡†ˆƒ…‡‡†ƒ†…ƒ…ˆ…ƒ‡…„„„†…„ƒƒˆƒ„„„†„„…‡‹†…††‡‡ƒ„ƒ‡‡††‡…ƒƒƒ‚ƒƒ†…„„…†…‚†††…„…„ƒ€ƒƒƒ~‚‚ƒ€€‚~€aR€|‚{}{~%�F}~{€}‚}}~||qU�Fz|}}}}y5:v~{{}~€~€€z{3&“j}~ƒ‚‚ƒ‚€|g“I�5Tz€ƒ„…ƒ‚†{„„„‚‡„ƒƒˆ„‚‚‡ˆ†……††…„„ƒ„…„ƒ€„ƒ†…„€…‡†‡ˆ…ˆƒƒ†††……†ƒ„……„ƒ„‚ƒƒ‚‚„‡‡††…††‰ˆ†ˆˆ…„ƒ………ˆ…‰…†…ˆ†‡ˆ†††ƒ„€„ƒ†~„ˆ‰‚……„…yz{{wyuwyzxvvwwxwxvwy{{|rvvwy{xxx{wwwwyz{|wvyzxyyzv{vyy{{zzzyzyyvvzzzwvyvuutrrhh��_UBON;9[rlr€||ƒ~w€q?CˆŠ}har`6quurooE:xyjmqnqH 7…oqrrqtuutvpp1Lzvywx{z{||y}{~~}}}~}z}}~~|{|{|}€}}~}~€€~~~~‚ƒƒ~ƒƒ„„„ƒ€€‚ƒƒ†ƒ„„…„„„……†ƒ„„ƒƒ„ƒƒƒƒ‡„…††…†‚‚ƒƒƒ„……………‡†‡‡‡‡††…‚‡‡„ƒ†…„†……ƒƒƒ‚…‡ˆ‰„ˆ„ƒ††…Šˆ‹†‡‡‡‡†„ƒ‚…††…‡„„ƒ†‚ƒ†……ƒƒ‚ƒƒ„…†††„…ƒ€‚ƒƒ~‚€€€|€€~{~|h�O‰yz~w.�!Amz|~ƒ‚‚ƒƒ€|ubFwx{|yqr0;8”bu}wnv}~{~~{|59 ”ˆŠŒ”™—™”Ž™œN��f}„………†………†ƒˆ‡‡„ˆ‡‡‡‰ˆ†………†…‡†‡††………†††…††‡‡‡‡‡ˆ„‚„†…†……ƒ…„‡ƒ„…ˆˆˆ„ˆ…„ˆ‡„‡††ˆˆˆ†‡‰†‡…ˆ…‰…‰…ЄЋŒŒ‹……„ƒƒƒ…………‰…„†††yxwvwxwx{yswywsuwusvy{wvuuvuyxwwxwzwwwvw{{wwyv{zzzv{uvyyzyyzzzzywxzzzywywsuttpgp_[ekjnmkllkjjt; 8P}_mmg_d� +0‚rvtqoj ��ZynsqqqK� 8‡ruwxvwrtrwtpt6� F‚ryyyywwyyy|{}€}{~~}{}}~{~€|{|~|{€{}~~~~~~€}~z€‚ƒ‚€ƒ‚ƒƒ€ƒƒ‚‚…†„ƒƒ„…‚ƒƒ…†„†ƒ„„ˆ„‚„‡‚†ƒƒ„…ƒ„…ˆ†‡‡‡‡‡‡†‡„…†ƒ†††ƒ†……„ƒ…ˆ†‡‡‡…„„„„„„‡ƒ‡…ˆ‰‡‡†…‡†‡„ƒƒ††††ƒƒƒ‚ƒƒ„…„ƒ„ƒƒƒ‚ƒ‚…††…„ƒƒ€ƒ~ƒ€‚‚~|~ƒ~~o Sˆu~y{mŠ4'>jv{~~}}}{~x|L<Muku…qi‡"�����8¤”ŽŽw~~€~{}6�8APd^^Xa[TOIGJKKMTgmnoq{yo|„„~€„ƒ~…ƒ‡‡……‡}…ˆ†…††„……†‡†…†……†ˆ†„…†‡ƒ††………„ˆ†„„‚„……†„„„‡………ˆ‡‡…ˆ…ƒˆ……‡††ˆ…ƒˆˆ†…„„……††…ƒ„‡Œ‹Š‰‰†…„€€ƒ„€„„‡€…„„€z{tvxwvwwwxwwywvvsryw}wxvwxwwyvwxxxyu{vw|{yz{vyzyzzz{|yywvvywwzzvzzwxwwzwwutuugu�`hkptuutvtpliq9Bƒlwomc^�*„ptrrk}QwurrrwV6zptvwwussruwt|<<C“du||zwz|{{{|}~~}}|}}|}}{z}~€|{~}}}~~ƒ~~~€€€„ƒƒƒ€ƒ‚‚ƒƒ‚€ƒ„ƒ‚‚†ƒƒ„‡‡‡…ƒƒƒ‚ƒƒ„„„†……„ƒƒƒƒƒƒ…ƒ„„„‡‡†‡ˆ„‡†„‡„ƒ„…‡‡†ƒ…‡ˆ‰„†……‡‡…„ƒ‡„„ƒ†…„……‡ˆ‡†„……†…‡…‚‡‡…‡ˆˆƒ‡„„ƒ„‚‡„…„ƒƒƒƒ„„ƒ…„ƒ„ƒ„ƒƒ€„ƒ‚~€~€}~~l�C›~{yŽž9 Mr‰}ˆŠŒ‰{~€€€~zw +AY{xy|‚Š'�&29=<<=@9>>CFI=by~|~~}v:’ˆ†™ž[��R”ˆ†‚~ƒ‡„„„ƒ€…‡ˆ†ƒ††…†††…‰ˆˆ…ˆ…ˆ„†…ˆ†ˆ…‰ˆ‰‡‡‡ˆ‡‡……††…†ˆ‰…‹„†ƒˆˆˆ„ˆˆˆ†‡ˆˆˆˆ†…ƒƒ„‡…„………‡‰‰…Ž‚†…†…†…ˆˆˆˆˆ‡‡‚„‚€‡…„…††„„„‡{{yxxw{xxvvxwwxvssvxwuuwvwxxwywvwvvvvwwx{{{zzvwuvwvyy{zzuxzzyxzzwzzztmrzvrqurujk��Pzpppotppqpplw;�B„tsopof"xoqprg„%Q€trttoa 'ufptuuvvwtpvtnL0> ’„€„}qs{}{{|{{||~}|{|~€}||{{|~€{~{}‚€|~}~}€~€‚„ƒ€ƒ€€€‚„ƒ„„ƒ‚ƒ‚ƒ‚‚‚~ƒƒ„„ƒ„†€‚€ƒ‚„ƒƒƒ„ˆ‚ƒƒ‚†ƒ„†‡……†ˆˆˆ‡‡‡†‡†„„„††‡†‡ˆ„„ƒƒƒ…‡ˆƒ‚†…„ƒ„„„…††‡†‡†ƒ„†ƒ‡†…††…„†††ˆ…ƒƒ‚ƒˆ„„ƒ„€€€€„…ƒƒ€€‚€~€ƒ‚„ƒ~}~}~ƒs/2r†{zh\RH508;7?BDJKINOJj†‚~{€nxvneaK;ŒŽ{t~‰“\+APw|z{~}w> }‡zwp„u�9NŒu|€€€€~‚‚‚~…‚„€ƒ‚ƒ‚ƒ„‡…„†„†……ƒˆ……„„„†…ˆ‡ˆ„‰~‡…‡…‡ƒ†…‚………††…„…„‚€€…ˆyˆ‡ˆ„„ƒƒ……†…„„†…ƒ„…‡…„………„„„††‡……‡ˆ‡……„…€€€€€~†…zyxyxu{zz{{{xxvwy}{|wwxxv{yzwzwyx}||zyx|ywy{yxwzy|wzyyyyyzzzwwzzuzyyyyxyuwvvpujs)��!KjqponuqspppopA $?ssrqfk#myqom~Š2+M€tsttub#bru†y{wvwyyvp^A2>AEID@>::3Svw}{~{|{}{}~|}{|~€{|||{{}€~}}€~}~‚}~~~~~~€~‚ƒ€ƒ€€ƒƒƒ€ƒ„„ƒ‚‚ƒ…„„„†ƒ„…„…„†…†€ƒƒ„ƒƒƒ„…ƒƒƒ‚‚ƒƒƒ‡…ƒ„†‡„†††„‡‡…ƒƒ‡‡†‡„…‡‡‡‡‡……†ˆƒƒƒƒƒ……‡…„†…†…†††ˆ†„…‡…†……ƒƒ…‡†‡‡†……†‡„„ƒ„……„…‡‡„„ƒƒ‚„„ƒ~…ƒ€ƒƒ‚~€}~v0 DQ¡‘Šˆ{` �2)X‹}€€}~~‚z„„b�gv„xs|4%/Oy}y…~‚zCg|z~yvi+ + EŠz€€ƒƒƒƒƒƒ…„‚ƒ‚ƒ‡†ƒ„†ƒˆ………ˆ‡†ˆˆ‡‡…‡…†…‰ƒ‡‡‡‡ˆ‡‡„‡‡‡††…Š„ˆƒƒ…„‡ˆ‡‡ˆƒŽ„ˆ‡†„‡ƒ‰‰ˆ……„ˆ‚”…‹…†…ˆ‰Š‰ˆ‡ˆˆ‡…†…†€Š‡ˆ€‹€„…†……„…zzz{zwzyxxz{urvwwwxzwuvvvtvywzzwwz|{zzx{vwwx{ywxzxx{zw{zuzzyyzzzvyyvx{jwuyutptkq/�Obipppprtvqqoy@-!2>ytpppnl Kkw‚}ƒW5-/[urtqrrj� huvuyyrqq€€€Š“P*Fsu}|}{|{{|}~}~~~|~||}|}||€~~~}}~{~~€~€€€€‚ƒ‚ƒ„€ƒ~ƒ„„„„„„„„…~~„ƒ‚‚ƒ„‚ƒƒƒ„„ƒƒƒƒ„„††‡††††…†ƒ„…‡†‡†‡‰ˆ†‡ˆ‡†††ƒƒƒ„‡‡‡„†…ˆ†……‡†………ƒ„††ƒ„‡…ƒƒ………‡ˆ……„………‡…‡…‡‚……„„‚„„€€‚ƒ„ƒ€€€~€~~}|.�� *U‘gvwqlu�2R‹}€‚}|}zz|}yr~f�gsy~}ws} Hz{zy}~zD�bƒ}z{‚v*BŽ{~~}}ƒ~‚}‚~‚ƒ~ƒ…ƒ€…„„…„„€ˆxˆ‡ˆˆ‡}‡…‡ˆ†„‚ƒ‡‡†ƒƒ‚ˆ„†‡‡„‡…………„‚…ˆ…„„‰„‰‚ˆ‚……„ˆ„……†…‡„…†‡‡…„ƒ„ƒ…ˆˆ†ˆˆ†„ƒ‚†ƒ„€zywwwxwxxxxzr~wwyyz|yvx{|~uyrzz|z{zz{}uyz|vwwvuxyyzzzz{|szy{{{yzzzw}{zyxwwwxsukr2������ Pkw‡|zvrvwqqpG9(&7>Zqtqqqkq@d†Šˆznqrtqqxl�MU{urxyrwyyytd…f; *yv~€{|z}~}~~~~|||}||~~}{}~}~|}~~~~~€z€}ƒ}€~„~~€€‚„ƒƒ‚ƒƒ„ƒƒƒ„ƒ„ƒƒ‚‚ƒ„ƒ……„ƒ†ƒ„„„‚ƒ‡‚ƒƒ„ƒƒƒ…ƒ„ƒƒ…†…‡„†ƒƒ‚‚„„„………‡ˆ‡†„††††‡††„…‡†‡ƒ†‡‡‡‡‡†ˆ†‡…ˆƒ…„„…ˆ„ƒ‚„„ƒ„‡„„„…†‡…„„…„„ƒ…„„‚€ƒ„…~‚…€„€~}|4T„w~~|tu "M„~‚~~{z~~wi)]‡{{}to��B~|{|~wN$nq†zƒxn%:Šu|‚€‚€‚‚‚‚ƒ~ƒ‚‚ƒƒ††…‚‚„„„†……ˆˆ‰…ˆ‡‰‡†„‰ˆ‡„Š„……†„Šƒ†‡‡ˆˆƒ‡ˆ‹„‡†‡‡‡‡ˆ„…‚‡‡ˆ‡ˆ‚„…Š„ˆ…………„ˆˆˆˆ‰††…‡‡‡…Šˆ‰ˆˆˆˆ€„€…€‹€†€‡‡ˆ……„xwuuuuvxvxtzsywwyxusuuwvwxxwryvyw|w{xwvyzzvwxwvuv|zxvzwzuzy|zyzywyyututvvuvyurmq7� +,00154:>@AC<;:75@5Akvxppqnlmqmopmqurrnvr�Rndkuqtsrtusstt��Xyvsvuwxwxwvuti�sx~{{|{{{|~~~|||||{~~~~~€}||{|~|{{}€|{||{||}~~€€ƒ‚€‚ƒ~~€‚ƒ„€€‚„ƒ‚‚ƒ‚‚‚ƒƒƒƒ„‚~…ƒƒƒƒ†„ƒƒƒƒƒ‚‡…„ˆ„ƒƒƒƒƒ„„ƒ„ƒ„†‡„†‚ƒ„‡†††…†ˆƒ‡‡…‹ˆˆˆ„‡ƒ†‡‡„„…ƒ„ƒ‰ˆ„ƒƒƒƒƒƒ„„†‡ƒ‡…„……ƒƒ‚ƒƒƒ€€€…‚€€ƒ€€ƒ€€ƒ€€€€~}|3�Q~||~}{i%�G…|€{}yz}}|qh.(]|~z~ys 5‚|}|{|‚Kqksyzu{y+$�.„kv…~~|‚{||ƒ~‚~€ƒ€‚„€€€€ƒ„‡†€„…†‡}‡ˆ†ƒˆ}„…†‡…‚†„…„††…„ƒ„†‚‰„††…ƒ„„†‚‚„‡†„ƒ„„„„„…„ƒ„…„„‡…„…†…„…††‰…‰†ˆ‡…†‡„…€†€ƒ‚„„yyvwvxxzzxz|sxywwxwww|vyvvxvvxw{}|uxxvvwxz{{wwv{z{yyyyvz{{z{zzyywyyzzyvwxwvwurmm<�awhp‚€|}†€pmrH"&;+~rwvvvtrwwwruuttuskrx Iunuvyxxxywuqsw\svswuwxxz€v}lx�mw€z||}||{{|{~~}{||~~}~|z{|}z}|}~~}€€€€~~€‚‚ƒ€€€‚ƒ€‚€€„ƒ„‚€ƒ‚ƒƒ„„„ƒƒ„„‚ƒ†ƒ„ƒ„ƒ„„„ƒ‚‚‚‚ƒ„‡…‡ƒƒ„„„‡…‡„†…†„…„…‡ˆ………††ˆƒ†…†‡ƒ…†‡ˆ„††‡…„„ˆ†ƒ‡‡‡ˆƒ„„ƒƒ†ƒˆ‡„†„…†„„ƒƒƒƒ‚€„…ƒƒƒ‚€ƒƒ~~~~7�Rwv{|z‚g @u|~~~}}}}{lp4"(V}}{~{‹P! +!„{{~|{}RI dwu“„ŠA&އnhxŒ}ƒ„ƒ€‚‚ƒ€„ƒ€„……„†………ˆ„†…„††…‡„ˆˆŠ„‡††………†ƒ…‡…†‡‡‡ƒŽ„ˆ††…„†ˆƒ„…„†„…„„………ˆƒ„…†…‡…†…‡††…‡†ˆ…Š…ˆ‡Š……ƒ„…Ž€ƒ€††…‡„ƒxwxwxwxxxyx{usvwuywxxwuxuwwwwwwuu{vwuvuvwwxzvxuwwuvxxzvzzxyyzyzwyyyzwwwwzvvuwomkGHYempppqpqpmi]O*=|rvvutrroqqrtuqtvtpry �Hwyvuuvvttrrqp{ �Yrrsstuusuuvyi{�)\€y{|||||z{z~~|||}€€}€|~z}{{~~~}€~~~~ƒ~~~€‚ƒ‚‚‚‚~€ƒƒƒ‚‚„„ƒ‚€€€€€‚„„„„ƒƒƒ‚ƒ„ƒƒƒ„ƒƒ„„‚ƒ…ƒƒ„„„„ƒ„‚……ƒˆƒƒ„ƒ„„‡ˆ‡‡ˆ‡‡…ƒƒ‚…††‡„„ƒƒ‡…ƒƒ…‡ƒ„‡ˆ‡ˆˆ†…„†…†‡†„ˆ‡‡‡‡‡†…„ƒ‚„ƒ„ƒ€ƒ„€‚‚ƒƒ€€€~ƒ€€~€9W€iy|{“… +<Œsxƒˆƒƒ†‚…yq}x.�� Yw}}~~}‹ � +?Zt|~€{~zn)+0MPW]QQPNMMMXX^[S% /.B‰~„‚€ƒ‚ƒ€€€ƒƒƒ‚ƒ‡…††„„…„„……………„„ˆƒˆ†ˆ}‡‡†„ƒ„…ˆ…y‰ƒˆƒˆ„‡‡‡‚††ƒ„ƒ„„……ƒƒ…ƒ…†„„……ˆ…„†‡††††‡‡‡†…†‹†…„~~}€„€€€‡„„vyw{xyz{xyz|xyvwuywxxxuywwuxwxwxvzv|vwvwuvwzwwvvv{{{{{wzxyyz|~wywyyzuxxwyvysvpnpE� +@mlpunoqsrqnmaU8|qwuvrvvuvuszuwuusujq�Ipvuvuwurrxrq{� 5Vkrwwzƒzƒ}u…€ �� 2aw|z{|}{y{|{z{~}|{|{|{~}~~}}~~~~~~€~}~€~~}~~€‚€„~€ƒ‚„ƒ‚€~ƒ„€‚ƒ…‚ƒ€ƒƒ„ƒ‚†ƒ„…„ƒ„„‚ƒ„†„„„„ƒƒƒƒ‚…„ƒ‡ƒ„„†ƒ„„‡„ƒ„ƒ……„†„…†‡‡‡„……‡„†…„‡„ƒ††‡‡„…„„…„‡‡††ˆ…„„„„‡„…ƒ…„…‚„‚ƒƒ‚‚ƒƒƒ…€€}~~~~}?:–œ‰‡‰Ž",k‹xxorqmmnqlaWOI@;<?;1�W|}}~€}„‚І„urpz~}€|…•›—’‹|H)_z„Ž–˜^A*‡~ƒ€…„ƒ„…€„‚„ƒƒƒ†ƒ‹~†„„„‡„……†„†………†„ЄЉ‰‡ˆˆˆ‡‡†‡‡‡ˆ‹Šˆ‡†‡ˆ…€ƒ„………„†ƒ†„„„…†‡‡‡†††ˆ…‡‡ˆ‡‡†ˆ‡‡‡Š‹Œ‡‡†…~€€ƒ€ˆ~ƒ†……zyuvxyuzvtszzzvwvvxwuuvxwvswxwvvvvxywxxwvwwzzwvwv|vzuwzzvxvtvuuywuyzustwyw{xwrpeV�:…oqpmqrssrrpkb��0}swutuuutvvvxsqsspth€8F‰_ouuuuvuvvtq€�DœŠ€ƒ‚€wwpol`JA;96?3Hjxz||||{z{{y{{{|{||{{}}~}~|||z|€}€~|~|~|~~~~~ƒ€€€}~€€€€ƒ€ƒ~ƒƒƒ‚€„…€ƒ€}‚ƒ‚…‚‚‚‚„‚ƒ‚†…„„…ƒ…ƒ‚‚‡„ƒƒƒƒ„„ƒ‚ƒƒ„„„‚†„„„†††…ƒ…„…„…„†‡†‡†„ƒ„…‡…‡…ƒƒ„„„††‡„…„ƒ„…„†„„„…€„ƒƒ‚‚‚€„~€€~~~{B&+3-25% +@W…„Œ~uk��\|}~~}~€~y|{zvy~~~}~€}{{z{yyvqG.fjwzzj‰^?~€‚„~ƒ~~‚…ƒ{…ƒ‚€€†„††‡‡†………†€ƒ„‡…‰ƒˆ‡ˆƒ€‚‚ƒˆ‡‡ƒ‰ˆ†„Ї‰ˆ†‡†ƒ€ƒƒ‚‚‚„ƒ„„„„‡‰…ƒ„……„‡ˆ„†‰‡Šˆ†„‡†††‡†„„€€€}}€„ƒzyuuvyvzt}szxxxxuwxwxyxxwvrxywv{{}wwy{xwy}w{zzwwxzyzxyyyy{zz{~wyv{xywwuvzssrrvqgZ:qjnqqqqrsvsphc,!„txvvvuuxvzuxt€ƒ‡~vˆ™���A›™„|ƒ|mqtvvtq'�4mm[]^`@%) + +s†‹‡Žƒuyz{{|}{z{|y|||||{}|}}~{}z||~}~~~~}~€|~€~~}~~~€ƒ€~~~€€~‚ƒ‚„€‚~€ƒ~~}‚ƒ„†€ƒ€‚‚‚‚‚ƒ„„†ƒ†„ƒƒ„‚ƒ…„‚…„ƒƒ„ƒ„ƒƒƒƒƒ„ƒ„ƒƒ‡‡ƒ„…†„„ƒ„†…†‡‡‡…‰…„‡…ƒƒƒ…„„„‡‡‡„„ƒ…ƒ„†‡††…†€…„ƒ„~‚‚ƒƒ„ƒ~€~‚‚€~„~D�����!oˆpuwsdf� P{{}}~~{|~~~}}}~~~€‡|~zyyH%iq{{~wƒc3}ƒƒ…„ƒƒ…}ƒƒ„…†„‡€ƒ…‡‡ˆ‡†††„„„‡‡‰„‡„‰„††‡…‡†‡ƒ‹…‡ˆ‹ˆˆ†‡‡‹‡ˆ‡„„†‹ƒƒ‚„……‚‡‡‡‚‡‡‰„†…………†‡††‡‰ˆˆ†Š‹ŽˆŠ……€‚€‰ƒ€€ƒ„zyutuuvytssyxwxwuvxwuxxvwvsxwvvvwvvvwvww{zvvwwxxz{zzvxyyzzyvy~ywv|wxxvxvzxvruwrik�9idpspooqrusqkU9%8B}uvwtvwtuwuuxwixtffiC'6K_T`OG:'*Sjqwuwq‚,�bhcgvA (yqwyxxx{}|{x}|{{yz}xxw{{|z}||}||||~~||z}{z~~{|||{€€€}~~~~€~}€€€€€~ƒƒƒ€€ƒ~~~€ƒ|ƒ€‚ƒƒ„ƒƒƒ„„„ƒ€„„‚ƒ„ƒ‚„ƒ„ƒƒƒƒ‚ƒƒ‡ƒƒƒ„ƒ‡†„ƒƒ‚ƒƒƒ‡…„„ƒ……„ƒ…‡…„‚ƒ…„„„…‡„…ƒ„„ƒƒ€ƒ„……€„„ƒƒ‚~‚ƒƒ‚€€€€€€‚‚H gx{z{}mdNƒ{}~}{|~~{{~~~~{|~}|}~{…HZ|||~~|l* -Š}ƒ‚‚ƒ„~„ƒ„ƒ…„…~€„††‡€„‚…ƒ…†„…„…†‚ˆ………†…†‡‡…‰…‡‡††‡ˆˆ‰ˆ‡‡‡……„ƒ†††…„†x‰„…‡†……†„‡ˆ…„‡†…ˆ†‡‡Š‹Šˆ………€€€€€€‚ƒwywwuwwxswwzwwxxvvuxvwvuxw{wwvutwu{wwwxwxxz{zxvw{yzwzyyz{v}zzy{{zvxyuvxy{vxyxpjn�1bhfjnvjmrrrqk]GDCDEH]qtuutvzuvvuuuy +j}hju{‹\��Aarwtus{/��_juto_~H ysqvwzxyvw{{w||{{|{}wzz{|||}|||z|z}}~}||{|||{{|zz}~~~~}}~|~~}~€€€~€€~~€„ƒ€|ƒ~€~‚€~~{ƒ}~€}~~~ƒƒ‚ƒ‚‚ƒƒ„ƒƒƒ€‚„‚„ƒ‚„„ƒƒƒ‚ƒ€ƒƒƒƒƒƒ…ƒ‚ƒ„ƒƒƒƒ„„„ƒƒƒ„„‚„ƒ„ƒ„ƒ„ƒƒ€€ƒƒƒƒ€„„„ƒ„„ƒ„„„‚…ƒ„~‚€€€€€‚~€‚~~~U�ar{}}v„]�� K…u}~~|}~~|}|}~~~€ƒ‚‚}~€{x|Kdƒvz~|s‡3 4Œ{€ƒ…„…„„…€‡€ƒ‰„†„‡……„††††‡†††‡†ˆ†††ˆˆˆ†††Š†ˆ‡‡‡‰‰‡‡‡†‡‡‰„…€†ƒ„‡„„…‰ˆ‰„†……‡†…‡…†……†ˆ†……‡…‹Šˆ†‡‡†…ƒ€ƒ€„†……ywwxvwwxuwuxuxxwywtxusssvxxxxwwyyxvvwwvzvv{zv{wwwwvuwuuwtzv{z{zzvxxwxxyyzyvzyvqiq %*urqwsmlmjtusw|tvtttuvvrvvvo{C�Zbjxoinb�1\rwuur|1�Hvuutp{Rj‡luwyvywv{ywww{zyvxvu{{y{v{z||{|{{}|{{{|{z{}}{|~}}{{{|}~~~|{~||~€~~~ƒ~€€„€}ƒ€€}~~~~‚||}‚€~~‚€€€‚„‚ƒƒƒ„‚„„„ƒ€‚‚„‚ƒ‚‚ƒ€~{ƒ€ƒ„„ƒ†ƒ‚‚‚ƒ…ƒƒ„„…ƒ€‚€…‚„ƒƒ‚„ƒ„„ƒ~ƒ„ƒƒ„€„~€€€€€‚€€‚€~}|W�lqxz~uŠYY‰_~z|~~~}~€|~~|{}{{zvŽUX}v’“:‰|€ƒ€‚€€ƒƒƒ‡„†……„††‡‡‡†††‡†…†‡††…†‡…†‡…‡‡„‚„ˆ„ˆ‡ˆˆ†‡ˆ„ƒ„„„„„……ƒ……„‡„ƒ„„†‡…†ƒ„„†…‡ˆˆ††„Š…„…†ˆˆ€ƒƒ€€„„„„yywyvxwzyxxxxwxwxxxwwwywv|||xxuvxwvxwy{|uuxzz}vvwwwvvxyyxzz{z~z{xvvxw{zwxyuyyxrls�?tqsuvutrqruuvvvuyyzurqquvsq~�Luprtpoo)\wwvury7�D„qzutkS +gwosuuvwwyywv|wzzzwyyx||{|{|z{{}|}{~|{z{{|||}}{|{{}|z~||}€|~~z~~~€{‚€~~~~€‚~~}~~~~€‚ƒƒ€}€€‚€ƒ‚„ƒ€€‚€‚ƒ‚„€‚‚ƒ„€~€ƒ‚ƒ„ƒ‚‚~ƒƒƒ„„„ƒƒ€€€~~~ƒ‚„„~‚ƒƒƒ€ƒƒ~…~ƒ~€ƒ€€€€€€{‡c{tx†’k�B“–ˆ‰’‰|~~€~~~~}ƒƒ„~z…‘“”‚swQGp…stm^OICB?ABCDHN_[YQUfw~‚„Їˆˆˆ…†„…„ˆ‚…„…„……††‡†…„…„…„„…†…‡†…‡ˆ„‡†…†‡ˆ‹‰ˆ‡ˆ††ˆˆ…‰ƒ„††……„„…ˆƒ…„†…„„ˆ‡†„†‡…‰‡‰…†‡‰ƒ‰‰‡ˆ…€Š…ƒƒ†„ˆ€Šƒ„…‡yxyyvwyz{{xywwwxyxuvsxwwxyzzxxvuyvvxxuvwvvvzw{vvwxwvuxzyvz{z{zzzzwvyyyywvuuyurumu�!)kqvsvprrrvvuuuuuspzuvupuvpq�G†s}ttmj3kwwvuuy=D„eyvqdN[zlu€{wwxxrwvuwzuzwv{y}{|||{{{{{}|{||{~yyzz|~|z{{{~{|{{{}{}}|}}||~~~~~{ƒ~~}|~€ƒ~~}~~|€~~~~~}€‚~„~}~~„‚~}€ƒƒ‚ƒƒ‚€ƒ„‚‚ƒ‚„}ƒƒ€…„€‚„~ƒ€€€€~ƒ€„€~~~€„‚ƒ€}}ƒ€~„€~€€~€€ƒ}ƒ_������RЇ€vpxz?Wla^VW[k}~~~~}}~„~}|s-+&'5JM^elqpmeC(N‘‡~€‹‡Š‹ˆŒ‚‚|‚‚„ƒ{…ƒˆ„…„…„ƒƒ†„„ƒ€……†„ƒ„‚ƒ„……‚€€‡„ƒƒ††…†…‡‡‡ˆ†‡…ˆˆˆ…ˆ…„ƒ‚„‚„„…ƒ……………‚‚€„†„…†…†Š‡……„‡†„ˆ‹‡ˆ„„ƒ„‚ƒ„ƒ€ƒƒyxyyxyyzyzyzwxwwwzswwxxxxwvxz{{{{{{}vzwxuzvww|vvywvwvyxyzzxyz~y{zzyzwwwyzzy{vvrlm'�%xlvqwqwruuvuuvvuwq{uvurrwqqy&%G‚rstrpj >\|vyvurN�8B‘swkW�I™‡…nelsyuvuuwyz{zyyzxz||{|zz{v{~{||{z{zzxy~{zzz{~z{{{}}}}{€|}||}}~~{€€€€~{~~~~~|z€~~€ƒ~~~ƒ}~~€}‚~…~~~ƒ}|}ƒ€‚‚ƒƒƒƒƒ‚~ƒ€~~„~€„„ƒ‚€€€€ƒƒ„€~~€€~€ƒ~€€~„€€~‚€ƒ€‚}k"./268<<@@AAE?:^zutuvdR +�Wƒ}}~|}~€„~{y<!'J€tv…vp|U,*e‰vzƒ€‚‚€€€‚€‚„„~†„†„‡ƒ†ƒ„ƒ„€ˆƒˆ‰Šƒ„…„„†„Šˆ‡„‡‚……†………†……‡†††…‡‡‰†ˆ…Ї‡ˆ‡„…ƒ†††„„……†…„…††‡…ƒ„……††‡ˆŠ‡ŠŠŒŠŒ‡ˆ‡ˆ†‡„†„„ƒƒ‚…†ˆ‡ˆ‡ˆ‡†zwxyyzvzuzwzy{{xutvwxwzzwwsrruwvu|yzxyxxuzwzw{wwyzzwvxzzzxzvyzyyyzzzzwvywyoywyrln()jmqqoqprqpppprqrnrxuuvqqprpm-"�@8‰usqtri%/�2HL}vvvtrS7@JkmdXUGA??=@ar %CZqzvuutwy{{{zwyyy{{{{zz{z{||yy{{{|z|y{{{u{|{{{{{|}}}|{|{|}}||~~|€|€}|€~~}}€~~}€~}€~€~~~~~‚€€‚€~€ƒ}„ƒ~ƒ‚€ƒ€‚~€}„}~ƒƒ„€}ƒ„„ƒ„~ƒ‚ƒ€€ƒ|€‚ƒ„‚€ƒ€ƒƒƒ~€€‚€~~€€€~m +J˜†ˆ„…††‚~†pim7€fk{„”m��9 N}~~|€}~€‚|{p(@zx|~{vzWdu{|||}}€€ƒƒ~ƒ~ƒ‚‡…‡ƒ„„ƒ„€……„…‰…………†ƒ…†ƒ„ƒƒ…„„„…‡………‡†……‡‡‡ˆ†‡ˆ‡ˆ‰‡†ˆ††…„……ƒƒ………†…ƒ„ˆˆ†„„‰ˆ„‡Š‰‡…ˆˆ‡Šˆ‰„……††ƒ†„……ƒ……„ƒˆ…„ƒ†{{yyx{{{uxwzwwwyyzyxwxy}wxwwvxyvw}xxvzywy|wzz{uuyxxzwwy|{vywzzyyy{{{zzzzvxwvvwrol-��evjp€|ƒ‡ƒ‡yrxrvuuuqrurqo8 >vururm91BDKOWezxzwupozz{ŒšD8wz…p^$ Htyuyyywxy{zxwwvwyzz|w{zzzzyyz|zzz{z{z{{|zzyw{|zz}~~}|}||~~~|{|||z~{~||}~|€€€€€~}~€€ƒ}}~„ƒ„ƒ€‚ƒ}~~€€~~~„ƒƒ~‚‚ƒ~‚‚€}ƒ‚„€€‚ƒ„ƒ‚€€€€ƒ€‚„ƒƒƒ€€€‚ƒƒ„‚ƒƒ€€€~€€…nbcu{zz{{{|{}rK�rnx|wn‡|�G†||{{‚~~€€{n*>w}|€||tV`z}ƒƒ‰ƒ‚€€„ƒ‚‚€„ƒ„„ƒƒ…‚ˆ…ˆ„‰„†‹ƒ„…‡Š„ƒ‚†‡………‡‰‰Š„ƒ……†…‡‡††ˆ…ˆ†‡†ˆˆ‡ˆ‡‡ˆˆ‡ˆˆ‡ˆ…„…†ƒ‡†……ˆ………‰…ƒ…‡ˆ‰…‡††…‡„‡Š‡‹…‡‡†„†‡ˆ‡ˆ‡†‡ˆˆ‡‡‰ƒ‡zzzzz{zyvwyyxxxyxwxvyxzwwwwwvyyvywwvuwvwwxvvv{vwvyuzvww|zzyz{{{wwwvuvxzyvxyurvrlp/ +Iehzwvkfed[YVApqwruvvrqurutrZIK``prsfv{|vwquvuuutrv€}y|uuwvyywvutwrjY‰M:&knrpkp+�8|wvwyyywvwwwwuwyyz{wy{{z{z{{{yu{z{~z|||{{zvy{{|{|zyz{~}|}€|{{{zy{|||{}~~|||~~€|~{{|}~€~ƒ€|€~€ƒ‚ƒ~{{|~~‚‚€~~}„‚~€€~€€€€‚€€ƒ„ƒ€€~€‚€‚€ƒ€€‚ƒ…€€€ƒ€€ƒ}~~~ƒƒ}„€~€€€€~‚€€€‚f +[yy{}€~€~zn^�mt|~~{iA‚}|ƒ‚€€}h$:…‚|{||€\�U‰}~€€€€€‚~€‚‚ƒ‚„ƒ„„„ƒ„ƒ…€€„ƒ„}‰…†…††€ˆƒƒ‚‚‚„†…‚…†ˆ‡………‡†‡ˆ„‡…‡ˆ‡‡†‡†‡‡‡††‡„†††…†…„…„‰…‰††…†ƒ‡ˆ………‰‡†……‡…†‡…†……†ƒƒˆ††€„…zzxxz{xwwywxxzxzxzxwwzyww|zwvvv{wvuywwwwu|vww|vzvzy{z{vyyywvwv{zyyuwvxwyuyvuuwrkr1�� #(7kqxrvturrsuuusz‚z{t}z~~~|xuuxwvuuvuuvvuvxuwvyvvvvvuutqt^ +sqsvtwv4� uuwywywyvwyzvvuwwxzuy|{{zxy|zyz}}}}{{|y~{|z{{z{{{{z{z||~||}||{{z~||||~~{~~~~€}|~€~}}€~€€|‚‚~}}}~}~~}}€~‚}~ƒ„~}€~€~~€ƒƒ„ƒ€ƒƒƒ€€‚ƒƒ€€€~~~€„~€~~€}„}€~€‚}}q Q||}||{{{ˆY�� g€|}~~{g�;†|~~~~~~{Œk6…|‚~}yaA@a„zŽ€€~~~„‚‚„‡†ˆƒ…ˆ„†ƒŒ€„…„†ƒˆ€‹‚…„‡……€‡‡Š‚„‡†…†††‡…‡‡ˆˆˆ‡‰…‡†‡‡‡†‡††ˆ‰……‡ˆ‡‡‡ˆ„‡…„…†††…†„…†‡††…ˆˆ†…І‰‡†„††ˆˆ‡†ˆ†Šˆ†‚ˆ‡ˆ‰yywwyxzyyxwxzyxxxxwwvvvvtrswwvu{vvwxwwvxwwvxvwwzvzvxvxvuwzvwzyzz{wwywxuvvtsuwtvly3���� 7Wqwsuuuvqrtuquutpqqtuurpidioquxxvttvvuuwurvvuwvvvvuurrttkj� +{{uutt9 + /zvywvuvvwyywvuvu{vxz{{zzyxxwyz|yy{~}|zyy{{{z|{{|~{{{z{~||{€|{|{{|~||{{{z||~}~|~~}}}€}~€~ƒ}~„€~~€~~€‚€€~~€€‚€}}~€€€€ƒƒ‚‚~}}€€€„€€‚ƒ~ƒ‚€~ƒ~~‚~ƒ|~~~€~€|~|€€~€‚nJƒ}}}}~~€~~}†R*�)\…~}~}b3�;Š{}~~}}€€€‚|~‹|(ˆ{|‚~}ZY‚~€~~‚†‘Œ}yz~ƒ€~‡~……„„…††€‡„„„ƒ‚…€†……„ƒ‚ƒ„………‚‚†‡…„…‡ˆ‰†„„„„††‡‡‡…‡………ˆ…‡ˆ……†„……ƒ…ƒ„……ˆˆ……‡‰‡„…†ˆ‡…ˆ‰††ˆ‡……†ˆ…††††ˆˆˆ„„‚€†‡yx||zzywxzwyzyxyyzzzyzw…uxxwwyu{vwwwvwuvvvvxx|vzwyxwwxv|vwvxyxvwzvtvwxxxwvutstulq9���Ypysvvqvuwzuvuuuvvyvvuuw|wyyyyyw{zxxxy{yywwyzxwvwwxyzvupkubnuvqk?.>uvwwwwvwwyywwyyxzxxz{{{|xzxwvwyywyyyyzz|zzzz||}}}~||{{|~{}{{{{{||}~~}€~}€~|}}|}~~}~~€~}~~}~€~}}„€~€€€ƒ~~ƒ~~~€‚ƒ€€€€‚ƒ„ƒ€€€„€~~~‚„~~‚‚ƒ‚~ƒ~}ƒ€~~„~~€„}|€~}‚~~qJƒ}{~{~€€~|zW<BVŠ}~}}}^/������������9‰z~||zus}ƒvs{†#&†{{‚~~hVKGJKP[jz€€ƒ}„H&7+$4CAp~ƒ€‹€„ƒ…ƒ„„ƒ†ˆ‡ˆ‚‡……………„†„…„…„„„…„††ˆ†……††„„ˆƒ‰…†„†‡‰…‡†‡„…ƒˆ„ˆ†…„……†‡„ƒ‰……„‰…„‡ˆˆˆ…………†Œˆˆ††††‡†‡…†‡†ˆ††‡ˆ‚’ƒ‰†Šzzzxuvwx{xww{xxxwxx{wzwvwyzwyxw|vwwtwzyxywwyzwvywx{xvww|vvvxywuv{zyyxw{wxwuuwrulm@�asvvtvvuuvvuuupuwvuuvuvutuxvxvvuyzyzxyyuyvwuvwvvwvvyvvrlsŒ�4Vwovuw=�<Ppvvuyyyyzzywzyzyzzzy|zw|xxzz||}|{||{|z{|||~||{~||~}~}~||~|||{{|{|~|}~}~}~{|}~}€~}}}€~~~}„‚€€€€~~€„~}€‚‚€€„ƒ„ƒƒ‚€€‚ƒ„‚„~€ƒ„€~~~‚€~~ƒ€~~~‚ƒ€~€‚€€€€~€~~„w'F…|{ƒ‚~|}{e??+&'Lƒ~~}}|hD.996)4ehea`U=�6}z{||„QF=1#($…{|€€€€}}ˆ…u}|€~€~<A,*Be€‚ƒ……„…ƒ‚ƒ„†‡„„„†……†…€„„…†…ƒ„ƒ€„†‡†††…„ˆ‡„„ƒ„„†……‡‡†„…†‡„„…‰„ˆ…‡‰ˆ‡‡‡„…‰†‡‡…‡…†††‡‡†…„†‹‹ˆ‡‡†…ˆˆˆ†…‡ˆ‡…†‡ˆ„ƒ„ˆ‡‰vxvvvyyzyxwxwvwwwzx}xzxwvvwyyzx|wxxxvzw|vwvzzxwyvvvxtzz{wvxywzxvwzyuxxyvuwvvwtrneK�W|wsvvvwwuvvuuutzw{uvvvvvvxvxxxy}xxvqyzyywxwwvzyyvu}‡ŠŠŠ’}��>Iƒ{nmz^?:<:9;?GY^`bZ^eqxvvvwzzw{zywyz{{yxxy{{{{{z{{{{|{{{{{|||||{|{{}z{|}~}|~}}||||||z{|~}|~~~}€~~|}~~}}~€~}€~~~~~~€}ƒ€~~~~€€‚‚‚‚„~€~„‚‚‚‚€€~ƒƒƒ~~‚„~€~€ƒ‚„}€€€€~~~~‚ƒ~€‚€~~~~}{xC„||€~€€}rlz|}x‚u||€~}}y†Š€‚~m{}sx‡‘˜n�0‡z~{{…O +ƒ|}€€‚‚€€€€‚€ŒM/)dƒ‚‚………„†„„‡„†ƒ„………„……„†…†…†„‚‚ˆƒ……†…‡„„„„†‡ˆ‰……‡†‡†ˆŠ‚††‡…‡ˆ‰ƒ‰…ˆ‡‡……†‹Š‰‡‹‡‡……………‡‡ˆ…Љ‰‰‡‡ˆ‡†ˆ†…‡……ˆŠ‡†ˆ‡„‰†‡‡‹uwuvwxy{uxx{wwwwwxy~zzuxxz|wvzywxzvvxxv{www|{xuz{xwwtuwwxyzvvvzyyyzxxxywvvuvwuqpc\��8O|vswvtvtwq{wuvwvtuvuuuwwut~€†€wttouvvvwwuvuyvuvlE3;:9:=A^mx‹Y9>šv||yvtvrsz|uywywvy{ywx{uzzz{{z{yw{{{ywywy|{zyz}{||}||{~|{{zzz|{{{|~}|}}{{{z{~||}~~}ƒ~|~|}~~}~|€€}|~~~~}}}~~ƒ€~€~„€€~~ƒƒ~‚€€„‚ƒ€€ƒ€‚€€ƒ€€~€~~}‚€~~~~ƒƒ€|€~‚‚‚~v#B‰{~€~}~~~}~yyyvxvy{|||~}|||||{qcoygryzmw€%4|z|}{~S�€~€€€~€}|€€‘Nb†‚‚‚„…€ƒ„ƒ„„……„„††„‚…†††…„†„…„†„ƒ„ƒ„„……‡‡†…‡†…†‚„„„………„„„ƒ‚†…†††…‡‡……‡‡‡†…†Šˆ‡……†ˆ‹…‰‰ˆ‡‡‡‡ˆˆ†‡„††‰ˆ‡………‰ˆ…‰ˆyy{{z|x|tz||vxwwxyy}{|uywzywx|xyw{v€wwv{wvw|zzyzzwvwyzxvxruuw{wyyzxxvyw{uuuwyxqsed��� #Hzuvvuqjcii`ZXVqvvvtvvwwvwxvkT& 5=8byuu{vxuzuwvutt9 £oopxƒ69;doywwwxvxyyvwwxvyyzwvy{zxzzz{zzyy{{{wz{y{z||{{{{{~{|}{|{{{{{}{|zy{{|~|{||{~€{{z|||{|~~}}{~~~}{}~}€}~‚~~}ƒ~~}|}~}€€€~€€~~‚~~ƒ‚„„~‚€€ƒ…‚ƒ‚ƒƒ€ƒ„‚ƒƒ€€€€€€€~€€~€‚~€€€€€€‚‚~€€€‚~}€‚€~€}€&>ˆ}}}~€|~€{}}|y}}}|}z€~}}~|z{}|xpl^o€~w{s.�.y}}{~R +t‚|‚~€€‚€€~}‚~~„LT„~…€„€„„ƒ„„„ƒƒ……………††„……„…„†‚†††„†††„†ƒƒ…‡†„‡ˆ‡†…‡ƒˆˆˆˆˆ‰Š„‡ƒ††„ˆ‡ˆ‰ŠŠ‹…‰†‹…‰ˆ‰ˆ‡…Ї‹…‹‰ˆˆˆ‡Š‡ˆˆ‰ˆ†ƒ‰ˆˆ†‰ˆ‰ˆ‰‰Šuvvwx{zzvzx|wvwzvwzwuvxxx|zwwvyzwwvvwxxywxx{xwwxzxvwz{{yzywvwwyyzzyyywxyuvzxuwrsk[��+/4?IL$�Ehd`\omvvvvsE+/'AL{vutvuvvuvuzun? '?[|vvzyzuywwyvta!‹ltvsk‚/ +8qpqpoqrxwzyywv{zyyxywz{wyyyyz{wuwz{z{x{yyz|{w{}}}||z||z{{€{{|{zy€z|{~€{|||~~|{{||{|~~{|~~}~€}~}~€}ƒ~{}€€|||{|}~~€ƒ~ƒ„~€~~~~~}€„€€~ƒ€ƒ„ƒƒƒ€€€€€€~€€~€~~~~~€€€€~ƒ~€ƒ€€}€€€‚}€{|}38…}}}€ƒ€~}}~~}~z}zz{|ƒ€}~{ˆg�Q„~}€{‚q))y}}{~{Y�$mu|}{ƒ}}€€‚}zz{|y}QQŒ~€„ƒƒ„„†ƒ…ƒƒ†…‚…‡†‚„‚‚‚„…„‚††„†„„‡…‡†ƒ†††……ˆ‡ˆˆ„…„ƒƒƒ………‰ˆ‡„ˆ‰‰†‰…‰…ˆ…‡ˆ‡†‡†‰‡ˆ‡ˆ‡ˆ‰‰‹‹„…ƒƒ‡„ˆˆ‰ˆ†‰‰ˆˆzz{{||{yyyyzwzy|vww~wxyzz|wzwyyy{~xyuyz{z|yzwwx|uzzzy{zzzyxvx{zzzzyyuwxwy{rwqrgb��.lˆ‚moyt��)qnrtpuuwvrq‚I7H}ut~tvuvuvvyrs10R~yu{zzwvvwywxq!uqxuqp†/8{vuyvvwyyzywwvyyyyvww{{xwwyzy{{}|{{xwx{yz{z{|{|zyyxz|~zz{|{{{|{|z{}€|}~{{{|{|}~}~~|{|{|{{||€}~€€€~€}}~€~€€~~}}‚€€~‚‚ƒƒƒƒ„~‚€‚ƒ€~ƒƒ‚‚„„‚€ƒƒ„€€~~~~~}~€~‚‚„€ƒ€€€~€€‚}|}56y|{~}}‚‚€ƒ‚ƒ}}~~}}~}}}~~~}~€~|yvy!(O~€yu;!�‚v~|~~|\!�in}}~‚‚€€…„„…‰ƒŒŒŠ}s{_$BW‚€…„„€€…ƒ†„„„ƒ„†ƒƒ†„‚……†…†‚‚„‡‡ˆ‡‡„ƒ†‚ƒ……‡…‡†‡………‡‡ˆˆ‰ƒ‰‚ƒƒ‰‡‡ˆˆˆ‰ˆˆ…‰„‰„ˆ„‹Œ‹‹‹‡ˆ‡‹‹‹‡‰‰ŒŠ‹†ˆ„‰‡…†ˆ‡‰ˆ‡ˆ‰‰ˆvz{}vuvwwzxwxyx{wxxxx{yvyvwzxxwxzw}|w{x{wx{|{{vwvzwz{yvwvywx{wvyzzuzzzvx{xvwusrnu\�R_amkg[l4qgrqprtuvwuqoK!Azwurtuvvvvwyt{"�L‚zuuuwzvzyxu{y +l|xssu€/ 8†uwwwwyxy{yxyxz{zyvvxx{zxvtz{{x{{zzyyw|z{zwzzy|w{z~}|z|zz}{{||{~~~~}~{|~~~|~~}}|}||~€}€ƒ}|{€ƒ€~~€€€‚ƒ‚ƒ~€~‚‚~€ƒƒ‚ƒ„„~ƒ‚€€€‚ƒ„ƒ}~‚…ƒƒƒƒ€‚„ƒ€€~€€~€€~ƒ~~€€~~ƒƒ„ƒ„€€~~‚€€}~~€ƒ€|z80k~x}‚‚‚ƒ€|{~{z|~|~|}|}|}}|wq}7@Ž~{{x†t-7{u|{~}U��f‚zwŽ…‹‘€„‰ƒ|{{utk\PF* '.l|€~…‚‚‚€€€€€€€ƒ…„ƒ†‡„ˆ„„…††€ƒ„…‚ƒ†…‚……€‡ƒ„…††‡…ƒƒ„…„„„…ˆˆ‚‚„„ƒƒ‚„‰‡…‰…ˆˆˆ‡‡„ˆ…‡…„„…‡†ˆˆˆ‡†…‡‹‰‹Œˆ‡„…‹ˆ‡‰†ˆ‰ˆ†ˆŠˆˆ{{~ƒtz{|xzzzx}z{wxwxxzy}|{{||{w}x{{{xyv}wxz{{{v{{zzz{{z{vxyzxwwz{zxzyzzzyzuxsvqlvU�9{glmpan+‚kxqwwzuvvuptY8uyvvuuvzxzvys-%K…yyzyyzxxyysgzY€xzzvz2'tw|wzxxy€‡‡yzy|wzyywwz{|}|}y|zzzzyy{z{|{{zyyyy{yz{|zz{|{~}}{{~{}}|||~~}}}|~€~}}||{||~~~}}~~ƒƒ„|~€ƒƒ„~€€ƒƒ~}}~ƒ€‚‚„„„‚…€ƒƒ„ƒ„ƒ~€‚ƒ„„ƒƒƒ‚€€ƒ~€€€~~€ƒ}~~‚ƒ‚„‚ƒƒ€€}~~€…zw: )p•Ž…‚~€€‚}}‡Š‘ŒˆŠŠ‰„„ƒ‡‡‡†ŠŠ…ƒzf4 ++G~~|y‚{6)-%Ewv|~€~ƒ_ "3FFEHHB7A??=@<721-0%-"!* $23N„†Œ…‹‡}‚…„…‚~~„ƒ…ƒ„ƒƒƒ„ƒ„„ƒ†€ƒ„…‡……„‚„‚‚„„ˆ…„†…„ˆ†ˆˆ‰‡‡ƒˆ‡‡………Šƒ…ƒŒƒ‚‰ˆ…‰…‡ˆ‰Š‰…‡†††††‹‡ˆˆˆ‰‰‰‰‡Œˆ‰‰‡ˆ‰‰‡Šˆˆ†‡ˆˆˆŠˆ‰z|ywuyzzzzxyxx{zxxwwxywxwvwxz{ywwzw{yww|vyx|||uxy|zzz{zyx{|zutuuv{zyyzwwyxurssrls\�!{qrqrga)xqrquvuruvurzd-tlvxzvvuuxutup5.5‰xxvzxxwxxxj^| X€xyyvvG1!>Vtwxxxx{xxjc`UVc|yzyywywtnz{yuvyvxzwwyw{wz|zzywzyyyzz~}|yy}{|{{|{~~||}~||{~||{|}}||||}~z|~~}}}~€}~~|}~~~ƒƒƒ€~}€~~ƒƒ…„‚€ƒ‚€€€€€ƒ€~‚„…„€„€‚€ƒ€€€€€€€€€~~€„…„‚€€€„~€€€€€~~A|ub^\brxˆ€€€|ucTRZTQOOLLLLJHFFBCCDH[SXcsgeWZjxz}}}zntgyu}|yx|~~ve�kllŠ€€`F$! HŠrz}}z{~‚ƒ~ƒ‚~ƒƒƒ…„ƒƒ€ƒƒ…„†ƒ€€‚‚ƒ……„ƒ‚„ƒƒ„ƒ„„††ƒ…‡ˆƒ‰‡‡……„…‚ƒ†ˆ„……‚„ˆ„…‡†………‡†‡†……†ˆ‡ˆ…‡ˆ‡‰ˆˆŠˆˆˆ‡Œ‡‰‰‡ˆ‡…ˆˆˆŠ††ƒ‡†Šˆ‹|}z{{{{{zyz{wzy{yyxxyzwywvwxy|yyw{wzv}}}vyv{zzuxu{|{zzz|x{y{{{|}uyxxxzwxxyuvsxqrlh*|rsqqjT 2xxprutwvvvupzb +_jwuuuvwyutwt$5!Axxuywt}vƒ} \xyyxr_PPbdfbkwzxyywz T‹zzyyyyw}xvwxyyxxy{wwww{yy{{zzyz{yzy}}{zz{|€|{{~z||||{{||~|~{|~~||}}|}€~{~~‚~~‚€‚€~‚ƒ€~€€€€‚‚‚„„€‚‚ƒƒ‚ƒ€€€€ƒ„€€ƒ‚€‚ƒ€€„~~€€ƒ~€€€‚„„ƒ€€‚€„‚„}~~ƒ€€|E�#00!B‚‚‚}y ~~|ƒ„„ƒ†z{}{}{|ƒ~ƒz{{{}|~|c'�Vd|yzyt@$ +Awsz~~}{‚~ƒ‚‚~„ƒƒ‚„ƒƒ„ƒƒ…„„„†……‡……‡„†…………„…„†„ƒ…††„„†‡ˆ…‰…Šˆ‰…‡ˆˆƒ…„††‡‰‹ˆ‡†‰…ˆ…‡„Žƒ‰‰Šˆ‡‡‰‰Œ‰ŠˆŠˆˆ†ˆˆ‰ˆŒˆ†‡ˆˆ‰ˆ‡‡ˆ‰Œ‰Šwzwzvzw{{zzyyxy{wzwwxxwxwxyxxxzxxxyvv{|zz|v{{zusv|wwwwz|zzwyzxz{uyvyyzyyxypzuvermm�$zrrlpoS7]x|‚sqvvurxi#|kyvq`elqvvuvmGEHBKC?atvzzyvjSB2�\€xxzxtpnqnprxuwyyzyvJ:Nyzzyyyx}yvvuuuvvuuywwy{w}{|{zzwyyyz}{~|}~|~{{{zzzzyz{|{‚||{|{|}~~{~}}}~}~~~}~€€€ƒ}€€~‚~~€}„ƒƒ€ƒ€€€‚‚‚ƒƒ„‚ƒ‚€€‚ƒƒƒ€ƒ‚€€ƒƒ€‚ƒ€€€‚‚€}‚~~~~~‚€ƒ}……ƒ~€ƒ€ƒƒ~€‚ƒ~€€}~}…H$99x~‚…}{)0�;ƒsu{}}}}{~}}{{}{{{zyz||~|~{uh�@{u{{{{‚@:ˆ}w|~~~~€ƒ„‚„€€„…€…{…ƒ„‡„€€‚‚‚ƒ…„ƒ„†„†ƒ„„„‡„„„„„‡‡…ƒ„„ƒ„………………ˆˆˆ†ˆ‰‡…„ƒƒƒ……†‡‡†‹‹ˆ‡‡ˆˆˆˆ‹ˆ†‡‡‡ˆ‰ˆˆ‡‰ˆˆ‡‡‰‰ˆˆˆzzuyv|{zyz||z{zzzzzywyxxw{zzxwvxw~y{uzz|{~x{{{z|zzxwwxvw{zwvw{z|tyzzw{wzvzxxwwvtlt(rtrpqm^GB857;CMOIIJ=Bktuuqsk�.7$[uvvuurr}†…‡{wvvxyvtm@���[…xz|wzyxyyxwwywyyzvu.HŠzzzzyzz{zwuuvuuyvwvwv{{wxw{{w|~}{{{}{{~|zz}{|||}||{{{|}~|{{€|~~~z‚~~~~{}€~~|‚~|ƒ€€‚~€„€~}~~~ƒ‚ƒ€ƒ‚ƒ„„ƒ„€€€€~ƒ€€€„ƒ„ƒƒ€~€~~€€ƒ~€ƒƒƒƒ€ƒ‚‚„€€}‚„ƒƒ~‚~~€€|ˆJ'C}}}~„}w=p€y}}~~€}~~}}‚~~~{z{~|{|€l#?”w}{{’C8q{x„…………„‡‡ˆƒ„ƒƒ‚‚„…„‚ƒ†‚‡……€†„………†‡‡‡††ƒ……‚‚†„‡ƒ‡………‡ˆ‰„„††‚‚ƒ‡„„„Š…ˆ††‰‰‰ˆ‡‰‰Šˆˆ‚†„‡Š†‡‡‰ˆˆˆ‡‡‡ˆŠ‰†‘‡ˆ‰‰‰ˆˆ‡ˆŒ‹‹Š‰ˆ‹wyvwyyyyuzwvv{wzwzzywyy{w{{xwwvvwwwxwyyz{|yx{zz{{zzzz|vw{ywwx|utsuvxyzyzvzvvxwtqlp,�$fmqromjghwmthY8;8xrvuqoq���$;Xzvuvvuutsrttuuuvyvvrp,�TŽzyyvyuxyyyyxvyy{{uvu!D…zyxxyz{z|xzwxvyzvwzwwz|{|{{zyz{|~{{{}|~|||{{|{}||~~|}|z|~|~~~{{}~|}~z|}||}~~€€€~€€~ƒƒƒ€€|ƒƒƒ€€€€ƒƒ‚ƒƒ}€€ƒƒƒ€€„‚ƒƒ€€€ƒƒ‚€‚‚€~€€€€~ƒƒƒ~‚„ƒƒ‚ƒ‚‚€‚€„…„€}‚€|…Q�?||}ƒ}<l…||ƒ~€~~~|~||}{{|}€{|l0 +#=Ž{||„}’H 2npv{~~€ƒƒƒ€€„„‡†€ƒ……ƒ„ƒ€ƒƒ‡ƒ…€ƒ‡……………„‚ƒ‚„…ƒ†„……„„ˆ‡‡†‡‡…‚ƒ…„„„…‰‡‡‰‰ˆˆˆˆ‡„ˆ‰ˆ‡…‡„ˆ„†ˆˆ…‡ˆ‰‡‡ˆ‰Š‰ˆ‹‰ˆŠ†††‰ˆˆˆŒ†Š‹‹‡‹zz{{{zxzxyz{z{yzxyyxyyy|z{w|zvw}w}w|wwzzz{yz{{{z{|}{{|xxwzz{vyz{t|wzxzxzuywvwurrlm0�hmsssqppopplhq$ ;aruupom�(Zuwuvvvuuwzyuwyyyxuu{{Rƒyzzyzwvzzxxyzv|z{uu{0 <zyxxzyxxvwyyxxyzy{zzyyz}{||z{{zz{{{{{~~}z|{{z{|{}}|}}}}~~}{||~~zz||~|~~|{{{|}|}}~~€€~~ƒƒ€€€€{ƒ€‚‚€‚ƒ~~€€€‚‚€€‚‚‚„„„ƒ„ƒ€‚‚„ƒƒƒ~€€ƒƒ„€ƒƒ€ƒ€€ƒ‚€€‚‚‚‚~„€‚‚„„„„€€}}|‰T� !8|‚|~}‚@�e„|}ƒ~€€~~€~}~~~~}|wtw|{~~€z~eEE'Š{{‚z“J(oƒ—‘™Ž~€‚„„ƒ„„„„†‡ˆ€…„…„……‰‡‡……………„…„„…†††ƒƒ†††‚†………„„†…†……†ƒƒƒ………„…‰‡…‡ˆ†…‡‰ˆ‰‡‡ˆ‰‰ˆˆˆˆˆˆ…‡‡Œ‰ˆ‡‹‰ˆ‡‹ˆˆˆ‡ˆˆ‰ˆ‡ˆŒ‰‰‰ˆ‡Šyxxxzzu{zzqzzzpwwxyxvyy|x{xxyxzyxxwxyx|{xwyz}zw|{zwxwwxvwx{xwzz|vxy|vwwxvvuyxuuqmj7� bwutsrqlnpnki|!4fvssqp{��Rƒvuuuuuuzzyyyyvxyvqj}I€ryyyyywyywzyzyvyxwuz<-��"Žyvxzzzxwy{ywyzyy}yvyzzwy{||z|{}{}}|{{||~{{}{z~~}|}{|}{~}~|z{~||z}|||~€}ƒ€€€~€‚ƒ€€~ƒ„„„ƒƒƒ‚€~€€~„ƒƒ„ƒƒƒƒ‚ƒ„ƒ‚‚‚ƒ€€‚}ƒ€~ƒ‚€‚‚€€~~€€ƒ„ƒ€ƒƒ‚„„€„††‚†ƒ€‚‚~|‚W7u|||Cj„}ƒƒ}}‚~}z}ˆŠ“’•“Œz|}~|||mY7$"C{{‚~{K��/897@0(]‡€…„……………„ƒˆ„ƒƒƒƒ†‡††‡€„€ƒ…‡‡‡†„‚‚€„‚‡‡„„…†…‚„„„†„…‡†„„„„…„……ˆ‰††………‡„†……„‡…†‡‡‡‡‰‰‡‡‡‡Š†‡‰‰ˆˆ‡ˆˆ‰ˆˆˆˆ‹…„‡‰‹Š††‰ˆxzxxzz{{{|{{zzzzwxxyx{y{w~xzywwxwzwwxxvxuvwy{{z{xwy{{zw}uvx{zzy{zzy}u…vzzyyxvwsmop6�_krrrsqqttrklk10bxorown( +Q†uuvvuuvyyvuvyxxvttY”!AJ—hpxuxstxzzxxxyvuvywwGU‡yvx{||xxxzwyyxxy}{{}||zyz}~|z{z{|}||{{{|}~~~€~~{||}|{||}}}{{||~~{{{||~}~~~z}~~~}~~€€€€~ƒ€~€~‚ƒ„€€~ƒ‚‚ƒ„ƒƒ€€‚ƒ~€‚ƒ€ƒƒƒ„€~~ƒ€€€„„ƒ‚ƒƒƒƒ€€€€€€ƒ‚„~~~~~„‚‚‚„}ƒ‚ƒƒ„…ƒ‚€€~~|ˆX � &q|~}wF��^~}~~€~}ƒ~}{eICGGHMUf{~}~woebm€y…qu{„|}‹Q�$-Ž…………„„ƒ„ƒˆˆˆˆˆˆˆ‡‰‰ˆˆˆ‡†„ˆ†…†‡††„„€…‡‡‡……ƒ‡…„„„„……‡‡ˆ‡‡………„………‰…‡‚ˆ…‰ƒŠ††‡‡„†‡ˆˆˆ‡‡‡‹‡Š‹ˆˆ‰ˆ‰‰ˆˆ‰ŠŒŒŒŽ‡‡Šˆ‰ˆ‡Œyzuxvzzzxyzzwwyxwwwxxz{wv{y{wxzxyxwwwwywww|||}z{wwwxzzvvwwvxzzzyyz{xwwvx{yyxvwuqqj:�Tlmsrsqqtlklhf3bukqphf,�3M…uzvywwvzyuuqqt„y}ŒŸ,D›xww[]qzzzxywxvwuz{x`T_kcvywwyyzzzzyyywy{{y{y{{|||{zyyzz|{||{|{~||z{}}|zy{~||~{}}~~|~|~|{}{{{zzy|}||{||~|€ƒ}~~~‚}€ƒ~€€„‚ƒ‚ƒ„ƒ€€„„ƒƒ€€„ƒƒ„„„€ƒƒ„€€ƒ~€‚~~‚„ƒ‚€„„„~€ƒ„„„}‚„‚‚‚„ƒ€~‚€|ˆdF`megSVeu}}€ƒK���� aƒ}}‚}~~€}‰YC, Sˆ€€}zz|y{{~~ƒ~€~ŒT��,’„ƒ……„ƒ†„…‡„…‡ˆƒ‡‡‰…†…ˆ‚…‡†……†††‡‡‚ƒ„†ˆ†ƒ‡†„ƒ‚‚ƒ€€…‡…††ƒˆ…„…„……ƒƒ……‚†……ƒ„‡†††…ƒ††††ˆ‡‰ˆ‡ˆ‹‹Œˆˆˆ‰Šˆˆˆˆ‰ŒˆŒ‰ˆˆ‰Šˆ‹‹ˆ‹z{vxxzz{w~yyvxwx{xzwxyzwy{|x{{|{|xwwwwyx{{{{|z{z|v{zyxwy{yz{|zzy|zwwywyz{wwvwusmjBewgmrqppuolfbu5+eŠ~bey"��.!Jyuvvw{vyxyuutl^ROJGF?>BYU\bnekhw|ƒ€H +=vyzxxvxy{wwyyvv|{qsuvwyyzzz{yy{zzx}zz{{{|}z{{z~z{z|z|{{||{{{{}||{||}}~~|}~~}|€~~{{|{}||{z~}|{{{€~|~~€‚~~~~€€ƒ€€}„€ƒƒ‚‚ƒƒƒƒƒƒƒ„‚€ƒ‚ƒ€€€€ƒ‚„€€ƒ~€€~~€‚ƒ€€ƒ„‚€ƒ~€„‚‚‚„ƒ~}€‚€€€ƒ€~~€{„bZ™|w†„ƒ|~~}~M���P‡}~~}~~„}€}yV*$GD~€~~~~}~}}~€ƒ~€~ˆT���6†ƒ…„ˆ„…‡‡„†„†…‰‡†…‰†„†ˆ††‡ˆ††„…†ˆ‡…………„„††ˆ‚„ƒ„…†‡‡‡……†‡ˆ…‰„‡††ƒ„……‚ˆ„†…„†‡…ƒ„…„Šˆ†ŒŒŒ†‡‡ˆˆ‰‡Šˆ‡ˆˆˆ‰ˆŒ‹‹ˆŠŒ‰‰‰Ž‹zvvxxwzzz~zxzz{zwxy{yzvxwxvuvuwxywxw{{{yw|xw{{xzvxwxvzzy|y|{{zwzzyyxxwvyzwuuzwtrpmE�F`n„}w{zs|rS7-8?IJSLCIY:O[LSSVdxuvuyvvxyyvwvj2J’|zwstptqrtˆNAuyzwyxvzyvwzyz}{yz{zwyzyz}yzzyy{yz{zyz{yv}w{{y}z{||||{y}{{{|~|{|{~|||{|}||}~~~{€~~||}}|}}|}}}{||||||~~}€€~€}€}ƒ‚‚€€€€‚ƒ€€„„„ƒ~~ƒ„ƒƒƒƒ‚„‚ƒ€€€€ƒ‚ƒƒƒƒ€ƒ‚‚‚€€€€„„~„‚€€€€~‚„„ƒ„‚}~~~~€€€€‚„}|…f�[hx|{|~|z€‚‚~„L���M„}}~~~|y[ AŠ~}„„~„‚~€€€€€~‚~V���†€ƒ„†ƒ‡‡……„„„„††„…††††ƒ†…„†ˆ…„‡‡ˆ‡‡‰ˆƒ‡‡…†„„„„……„†‰ˆ„ˆ‡†…„…†††„„„‚………………‡†…„…„…†ˆ‡ˆ‹†…‡ˆ‡‡‡‡ˆ‹Š‰ˆˆ‹‰‰‰ˆˆŽˆŒ‰ˆˆ‡ŠŒx{xxwvzz{z{z{zzywx|v|wyyyvwv€wyzzyxy{{yw{vwy{y{u{{{v{zzz{yz{{zzwyyyyzzzyzvvztvspnH'-*$=|usw‚‹?LŒŠvt{uuvvuuwvwvyyyxtu +0 :Y’_putxwyxxuoz_*zxyy{zxxyvxyzywvwyzxyyzy{yyyxyy{yy|z{zzwwvw|||}zzz|{{{||~~|{{y{{{|{|~}||{|}}{{~~}}||||}|}~~~{€}|~~~~‚|}€€~}~~}€€~€ƒ‚ƒ„‚„‚€€€‚ƒ‚€‚„‚ƒ‚ƒƒ€~~~€„„„€€„€ƒ‚€‚€‚‚€~ˆ~€€~~~€€€„€~‚€€„€}|Šc�Pˆw~}~~~~‚€W�� I†|~~~}~~ˆ~~~zfDv}~}~}~€€€€€€wW���.€ƒƒƒ„~ƒ…ˆ††…†……„†‡‡………„ƒ…„„…‰ˆ‡†ˆˆˆ†‡†ƒ‡ˆ…‡‡‡‡‡ƒ†‚…ˆˆ‚‰†„††…‡ˆ‰ˆˆ„…„……ˆƒ……„„…ƒŠ‹‡Œ‰‡ˆˆ‡ˆˆˆˆŒˆ‰‹‰‰‹ˆŠ‰‹Œˆ‹‹ŒŒ‹xyyw~|{{zzz||uvwvx{xvxwyxwvxzxxwvvuv||{y{vxvwwwvz{zwwvywy{{zzzzvwy{uxw{vuwvxuvspfT�=yhkgb{?5I‚cmuuuuwvuvuvwuyyywtrRƒ‚{{yxxxvuuuz_txyz{{yyxyyyzyw|}}{{z{zyzzy{x~z|yywz{||zyzy~{z}|yƒzzx{|~}{{{{|{|~}{{zzz|€z~|‚~€~~|~}~~|~~|~|€‚‚{~~‚€~€~€ƒ€€ƒ‚ƒƒ€€€€€€€€€€ƒ}€‚ƒƒƒƒ„€€€ƒƒ„„„‚€€€~€~„„‚‚„‚‚‚‚€‚€€€€€~~~}~€€‚€€ƒ€~‚ƒ‚}‚i# �MŒ{}}~~x~ƒ€~‹Z��L}{|~|}~~€€~\Invz|~}||||||||€€€†[’[����I!‡~„ƒ„…€ƒ†††††„ƒ„‡………‡„†‚††………†‡„‚‚‡„…‡„„‡€‚„†…†‚„‡„ƒˆ„„‡‡ˆ‡„‰‡‰„„„†‡…ƒƒƒƒ………‰‰‡‡ŠŒŒ‹ˆ‡‡ˆˆ‰‡‹ˆŠŠŠ‹‹‹Œ‰ˆˆˆŒ‰Š‰‹‹‹‹‰xwwvz{zzz~yy{{v€{{{yzzz{y{y{wz{|~}|}|}zyw|vwwww|x|||{|{{{|{|wvy{z{x|z{vzzzuvxzstpg`� $Avmllgg>�C‹lquvw{vvvvuzvuwzvvq}`xuwyvvvwvvwvp_�1xv{z{z{zyyzyzyxvyyyvvy}y|ywyzyyyy{|zzz|zyxyz|||zzzyyzz{|zyz|z|{z}}}||{{||||{~~‚‚„‚}~€€€|}‚}}€€|~€‚€„{~ƒ~ƒƒ€ƒ~ƒ€€€€€ƒ€‚‚€„„ƒ„€€€€‚ƒƒƒ‚‚ƒ„‚‚€‚„~‚‚†€‚€‚ƒ„€€€€„ƒ}„€‚€€€€€€~ƒ€‚€~„q"�\kw{}€€€~}ƒ~~~\�Hn}€‚ƒƒ„„~}}^/;QozŒ†ŠŽ…‰‘‘‘‘‘•›§e�"ƒ„„……„ƒ„†‡†…†„‡„‡‡‡†‡„‡†‡†……‡†‡‡†…‡†††††‡€‚…ˆˆˆ‡…‡‡†………ˆˆ‡ˆ‰ˆ‰ŠŠˆ‰††…‡ˆˆ„‰‰‹‰ˆ†‹‡‡ˆŠ‹Œ‹Œ‰‰ˆ‰Š‰ŒŽŽˆŒŒ‹‰vz{zzxzzzzyy{{xvwvuspx|{{zx{y}wxvywzx{{wy{yw{yxxz|w|{yxxyxywvxwxzzzzzxwyvwvuvwsrqfc��>…qqmmh@��A}rsutmuvvwvvuutuuuuo†!fuquuutuwxvvqgR?duz{{ywzy{xy{zyƒ…ˆ„‡ˆƒyzzzzywwzvyv||yzyzzyxy{}{{|}}|{{{|~~}|~y|}}}}~|z{}{|}~}€~}~~~€ƒ~~~~}|}€~€~‚€€}‚ƒƒ€€~~~~~ƒƒƒ€€ƒ€€~‚€€„„……~‚…€€€€‚„ƒ€ƒ…‚~~‚„‡‚ƒ‚‚€€„€€‚€~ƒ€€€~‚}€€€‚€t$�]uku‡…~€|~~X�/Zw†‡Š~~€~||iO)O]ksrsssrttzpidd_c]XWVXU�“‚rz|z…y}‚„„†„„€„‚€…„…„ƒƒ………„……†‡†‡‡‡††‡‡†„ˆ…†„‡ƒƒˆ‡ƒ…†‡ˆƒ††‡‡‡ˆˆ…‰‡††‡‡……†ƒƒ…‡‡Š‰ˆ…„ƒ„…‰…†…„…‰ˆŠ‰‡ŒŽˆ‡ˆ‡ˆŠˆˆˆŠŠŒŠzz||zƒyzzzz€z|zxwyu~~z{y}zz{~wxwywzzxx|yzywxzxyz{x}{vy|{{}|z|wwyyy{z~|zzyxzwvvrrij�<sopmghB +"Drqvwzz{zwvvvvvvvvrq‚ˆ)Fhvq{‹‹Š~ƒ†yuzU ��'Fcvzzyyyxyy}{zzl[QOTNLZrz|zy{}|zxxz}~zy{|}{zz{yywy{y|yz{||~~}~~~~~}~}}}{}}|~}{}||~~~€~|~€€}|~ƒ~~~€‚…~~|‚€€€€~~~€€‚€~„‚ƒ‚‚~€‚……„‚„ƒƒ€ƒ‚ƒ„€ƒ„‚„€€€„…ƒ„~€‚ƒ~ƒ‚€€„€€‰‚‚‚u1�)Š|nlpgs~‹~~ui�,8<@G)=m‚€}~ux~ƒƒ~†O(|ˆ†|~~ƒ†………†„ƒ„†ˆ†ˆ‡†„„……„†„‡‡…‡ˆ‡……†…†„‰†‡‡ˆˆˆƒ‡†‡ƒƒƒ„…††ˆˆ‰†‡‡‡‡Š…ˆˆ‰†…†ˆ„‡‡‰ˆ‡ˆ‰‰…†„‹ŠŠ„”‡‹ŠŒ‹Œˆˆ‹Œ‹‹ˆ’‡ˆ‡‹‰ˆ‹zzyzyxxzzzz{zzyw{{uyyzyyxw{|xyzz|yx{{zx}yvwzzwxy|{x|{{{{{{|z{{wzzyvzzyyzzxv{ywurukl�:kdljbwL�$����!Grmlz{zz~}}x€„ˆ‡€„„…zW8TPbed_a_S[QJGUJGHWMQXcnxy|wzzxyxvuxxo +xy||}}z{xxy}}~{}}}}}||{{{{xzz~z{x}|}}y}}}|}{|~yz~}||}}|{~{€~€{~}~~‚€}}}}€€€ƒ…~~‚}€€}ƒ‚€ƒ€‚„€ƒƒ€€€ƒ€‚€€‚…„€‚‚ƒ€‚„ƒ‚„ƒ‚„„„ƒƒ‚…„€‚ƒƒƒ€€€€„€‚€„€€€~~~‚„‚‚‚~€ƒ€~‚€€€‚‚‚ƒ‚}r1(*@2S‰}wk%2[ƒ€ƒ€{yzyyvug ���_‚y€€‚‚€‚‚ƒ…„†„„††}‡„†|‡…††‡††‡ƒˆ†‡‡†‡ˆ‡ƒ…„„††‡„„…‡ƒ††……„„…‡‡„ˆˆˆˆˆ„ˆ……‡‡††…„„……ƒ…ˆˆˆ‰ˆ…‡†„‡‡ˆˆ…‡ˆ‹Š‰ˆ‡ˆ‡Œ‹ŽŒŒ‰‰‡†‰‰Š‹‰ŽŒyyˆw{zzxy{{zzzywyz{{}zzw}|}{|{{zxyx||wy{|{|{~y}{{{{{{xvzzyzz|{{zzz|xxyzzzzzzzsttli���:hop?0HPY\QLOJHGGGA@6:75*!,>A\u}€‹„E-#�z‚€†ƒ|uxyy|zyzyxxywwzA7rx{||zxzzz}{{z|}}|z{z|zzzz|z{}y|z||{~z{{||}{€}|{{}}~~{||}||~|~}{~€~~}}}~}}€~‚‚}€€€„}{‚€~}~€~‚€‚„„€ƒ‚~‚€€€ƒ„„„ƒ„€‚‚‚‚‚„€ƒ„‚‚€„„€€‚ƒ‚‚€‚€‚€€„„€€€€~€‚„„„‚‚€~|5�?QŠ}‚~€vq )U…~„€€~ysmb{~ƒ„‚ƒ€†ƒ…ƒ†‡‡‡‡ƒ†„‡ƒ„„††‡…‰‡‡†‡‡ˆ…ˆˆ„„ˆˆˆ†‡‡ˆ‡‡†‡†„…ˆ…†‡‰ƒˆ‰Š‰†…‡‡‡ˆ†††ƒŽ…‹ŠŒ†Š‰‰ˆ††‡…Œ…†‡‡ˆˆˆ‹‰ˆŽ‰‰‰‰‡ˆ‹Žˆ‹ˆˆˆ‰Š‹‰Œzvvwu{zzy|wzyy{ywyz|{z}yw}{|x||zxz|z{{xyv{xxz}yww|{z|zxxzzwz{{y|||xwwz{zz{wvvyvuvlkPD:;H@K�#*10248^‚vukkqxjyzg_Q0 &Luhlqp|G��@€vpvzzyyzx}wzy{yz{vw~z +]€{|zxx}zyz{{|{zz{||z|wyxz{{y|w{z|{||z||{{||}|zz{{~}}{~||~|}z€€~~€~€€~€‚‚€~}z}‚€€€€~|{{~€€ƒ‚ƒƒz€~‚ƒ„ƒ‚~ƒ€€~~~€~ƒƒ€€€‚ƒ‚„‚‚€€€‚ƒƒƒ„€…‚€~ƒƒ€~~‚€ƒ€ƒ~„ƒ„„ƒ‚€|€ƒƒ…ƒƒ„€~ƒ~~5,U‹|}~ws$YŒ}|„~~€}~yukr€€‚€ƒ„…„‡…„„…†ƒ‡………ƒƒ„†‡…†„ƒ………‡‡‡†…†‡…ƒ„…‡‡…ˆ‡„…‡ˆ„ˆ„‡‡…††…‰ˆ‡‡†‚‚…„‡††ˆˆˆ‡‡‰ˆ‡‡‡‡ˆˆˆˆ‰‰ˆŒˆ‰‰ˆ‰ˆ‹ˆˆˆ‹ˆ‰ˆˆ‰‰ˆ‹{{z{||yywxyzwvzxxzwx{zz{{|y|zz{{xyz||{yyw|zx{{{|{|{v|zz{z{z{{|{{z{wwwzz|y{wxv{utuml'j]p|wa1†xjlwrxxxssxsWRrrvrnuP�pxwzvxyx{{zzyy{yyywvqn� + `ƒz|yyyz{{zyzvxzzxyyyzzy{y}zzzywy{|z|{{|~|||{y}}|{~€|||~}||~~{€y~z}€}~}€~‚|~}~~~€~€€}€„ƒ|ƒƒ„‚‚‚€‚~€ƒ~€€ƒ‚€‚ƒ„„ƒ€‚‚‚€‚‚€~}}~‚~~ƒ€€}€€€€€€€€€~€~€‚‚ƒ„€8� �P…}~}}zu \Šv|€€~~|{~sx +"fm{~~€€ƒƒƒ‚ƒƒ……„‡€…„…„†„„‡…ƒ„„ƒ…ˆ…‡„………†‡‡†…†……‡‰ˆˆ†‡‡……„„†ˆ‰ƒ………ƒ…†ˆƒˆ‰‹†„‡ƒ‰…ˆ†‡‰‰‡……‡ˆ‡‡…‡‰‡‰‹‡‰‰‡ˆ‹ŠŠŠ‡ˆ‰‰‡ŽŒzz{}{zyzwuyxy{{y{z‚w~|x}~}x{zyzzzzw}|xzxxz}||z{|~}{{|||yxxwwx|{|{yvvvvw{zzxxvvuuukj* +ZgfjfiW �$1|kpqollqplmopfN{wvyuxR�tszzzzzx{||{{|zyxyxulˆ#WŒ{|~}wx{~{zzzv{{zzzy~|y{{zyz{z}zywzwy{|~~}}}}}||€€{~}{~}}}||€~}|€€€‚|€~~€ƒ~}~€~}~€€€€~ƒ€€€‚€„€ƒƒ€€€„€~ƒ€€~€„„€€€ƒƒ„ƒ„ƒ…€‚€…ƒ„ƒ‚‚€€€ƒ„}€‚‚‚€~‚€€€‚ƒ€€€€‚€€€~€ƒ€~€†„ƒ‚‚‚…‚<�Ko}~}}{w&b‡e~}~€~xoˆ"������������� +Yzpz•Œ~~„„„ƒ……€ƒ……„„…………‚…†…††‡†„…ƒ…ˆ‡……††‡†ˆˆ‡……†‡………‰‡‡ˆ„…‡††…‡†……‡Š‡Š……„ˆ…‡ˆˆ‰‡‡‡…ˆ†ŒˆˆŒˆŠŒˆŠŽ‰ˆ‡‡‡ŠˆŒˆ‹ˆ‰‡Š‰‰ˆˆ‰‰ŠŠ{|yz|zwy{{{|z{zyz{xy|~{{~~{|{||}yzw{zxyywxy|||y{{|y{{||~wz{{x{{}z{wvvz{zy{wxvvuvrkg0(Rvmvjj`� '|oqrsqqruuvpvo��HŽvwvvvZvlwyxzxzywvxwvyzyxuqާ >Rƒyyyyyyyz{z|zzzzz}zz||z~z}z{{{~{z{y}{|€€€{z~{~~}}~~€~||~|||}}}|€}}~|€||}}€}~}€}~~{~~€}}€€ƒ~ƒƒ€„€€€€~}}}~€€‚‚€€€‚€€„„ƒ„‚‚„„ƒ„„„ƒ‚‚‚„€ƒ„ƒ€‚€‚€€€€ƒ€‚€€€‚€‚€‚€ƒƒ€?�GŠ|~}{y%�����������������Z—”„Š“““ŠŠŠŒ€ƒŒ…-����� (%010277@@KOQQQIOn€ƒƒƒ…†‹…†„‡†………††ƒ†‡††…ˆ‡…‡‹‰‹‡‡†‡ˆˆ‡……„„…‡…‡…‡„‰…ˆ………††‡…Œ‹‹……‡‰…†ŠŠ‰Š‰‰…Іއˆ‡‡ˆˆŠˆ‰ˆ‰‰ŠŒŽˆ‰‹…‹Š‹‹‹‰‰‰Šz{ywuuxzyyzz|{zzz{zxw{|z~|zzxxx{w{y{yyyxwww{||yz{{{}||||yz{{zy{{|zyxw{|{yzwyvtvusmn4-Wnrtkgm *{rrruwvvuuvqjsF‰wwvtwN-t`}‚’†…‰†Œ‰ŒƒˆŠŠ…†sh= c€vyxyz~w|z~|{zxyx}zz||z{y{|z|{~|{zw~~|~~}~{{~}}~~}}~€}|}}}{~~|||}~}}€€€€}}~~€€€€|€€~‚€‚~}„ƒ„€~€~ƒ€€€ƒ‚‚€€€‚€„ƒ…„€‚‚ƒ„„„„‚ƒ‚„€„ƒ„€€‚€}ƒƒ€€~€‚€‚‚„€ƒ‚‚}€ƒƒ€€‚ƒŠ?D„}~~~{€2 '#($+!7Od^^UQRZZ_SQQUKRJLVJJ[\X\gh|~~ K‡€vmsu{rxz{|„…€{|y‚L&,<J]ƒ…„ƒ„€€…†††……„…††ˆˆ‡†‡†ˆ‡‡ˆ†…‡‰ˆ‡†‡ˆˆ‰‡…………ƒƒ„††………†ˆ…„„†‡‡…„‚‚‚„‡ˆ…†Œˆ…„…††‰ˆ…‡…†ˆˆˆ‰‰Š‰‰‰ŒŒŠˆ‹‰‰†‡‰‰ˆ‰‰‰‰ˆz{y|x}{{w‚xy|~vyyzxxx~||{{yy{~}}}~}~wxxx|}{{||{|{y{|z{z|{{{|y{y|z{{{{{zzxzwywuvyrmk9�-Xcxula{''%qqquwvwvwvvqjq7����BŽwyvvvU+�7f[MJLIC>;9004)'&()0H †„ˆ‡~zwz}yv{xyyz|||{{y~}}{|z{z{|}z}|}{v}~~~}~~~~~€€|}}€€|{|~|‚ƒ€|}}}~}€}€‚~~~~~€ƒ}€€€€~‚€€ƒƒ„}‚ƒ„€€€€€‚ƒ‚€‚ƒ‚‚€~€€€€ƒ„‚„‚€€‚ƒ„‡€~‚€€€€‚ƒ€‚‚€€‚‚‚‚‚‚‚€€‚ƒ‚‚€„……~€‚}‚C @}}€}}0�.l‡ƒ€ƒ‚ƒƒ|{‰o�$~‡”‹Š}}~~og„7Œ~rv‚ƒ~„€€}}~~~~{|sJ/YŠ}„……ƒ„„„ƒˆ…‡…‰„‰‡ˆ‡†‡ˆ†‰‹Š†‹†‡‡‰‡ˆ‡‡‡ˆ‡‡ˆ‰†………„„……„……†‡‡‡ˆ‡‡ƒ……†…‰…ˆƒˆˆˆˆŒ…ˆ‡‹Œ†‡‡‹ˆŽ‰Œ‡‰ˆŒˆŠŠ‰‰ˆŒŒ‹ŒŽ‰‰‡ˆ‰‰‡‹{zzzwwwywwy{z|zyzzxzz}{}{{~}}|y~wuw}wxz{}||~xzx|{{{y{||{{z{{zxzzzzw{z{z{yzvxzuvwumq8�"\Xbeqw†3���(jqswwywvwvqqnfO9.#-+"@wvuvvj`TQdrs…� +� p‚nuyyz{{{z{zy{zyxxxyz|{zyxy|z~~{}€~z{|z|}x{|{€}z}{|~€€}€~{€}~~~}€€€~}||||}€€}~~~}~{‚~}}|~€~€€€‚€‚€€ƒ„‚€€‚€‚‚„€ƒ„€‚€ƒ€„„€€‚ƒ~‚‚€€ƒƒ€‚‚‚‚€€€ƒ€€€€‚‚‚€~ƒ~}~€ƒ€ƒ€€€~ƒ|€ƒI<†}‚‚z4/–vu}zzzyyzyvtpŠ��>…~uz~~~}~}~}svS{qz}~}|}‚~~~~~}y‚L �j„~„„„€€ƒ†…‡…†‡Š†‡†‡‡ˆˆˆ…†‡ˆ‡ˆ†‡‡ˆ†…ƒˆ‡††‡‡„†‡ˆ†……‡‡„…ƒ†‚‡ˆˆ„„……†ˆ……„‰‡†ˆ‡†ˆˆ†…‡ˆ‡†‹‰‰ˆ‹ˆ‡‰‰‰‰‰ˆ‰ˆ‰Šˆ‰‡‡ŠŠ‰ˆ‰Šˆ‹{{yywvv{wy|~z{x‚zzwz{}z~zwzy{}{}{|x~w{z{|{|{||zyz|z|y}|}y{{|ywz}{{{{zzzzyywywvvzuoo<�9ddUOB:;<=BOQX"rqvxvvxwyuyppkfjrz~ƒ…Š‹‹}qe-�?wyyyvuxƒ„ˆj`Š�ruwzyyzzxyz{zzxxz|x{yx}{y{zzzy}}}}||yz|{}}z{|{z{~ƒ}€}}}€||}{}~€{~|}€€€~}~~~|}€€~~~|z}~~‚€}€€€‚ƒ„ƒ‚ƒƒ€~€€‚€€€€ƒ€€€~€ƒ€‚~„‚ƒ„‚‚‚‚‚€}~}ƒ€€‚€€~„ƒ|„‚ƒƒ€‚‚ƒ‚ƒƒƒ|€€M8…{‚€‚}6/…ux}~~~~~~~|uŠ&w€x~}}€‚€€{x_l~~}€~~|{‚}‡‚‚‚ƒ}‚€|•Q��Uƒ~‚ƒƒ‚ƒ†ˆƒ…„‡†‡…‰ˆ‰…Їˆ‡ˆ‡ˆ‡ˆ†……ˆ†‡‡‡‡††Œˆ……„„‚ˆˆˆˆˆ‰…†††ƒ‡ˆŠ‹‹‰ˆˆ‰ŠŠƒˆˆŠ…‰ŠŠ‡Š„†‰‰‹Ž‡‡‹‰‡ˆ‰ˆŽˆˆˆˆ‡†‡†‰ˆ‰‰‰‰‰ˆŒzz{xwwwwvuw{{{vwx|xwx|z|{{yxwyy}w|ywwxy|||}zzz||{zy|y{|{x||zwwzzzz|{{zzzzv{zwvuwuovB�@…{rb\]#%gqvxurwwuuxuqrpppqpqrtqqqqge@?‡wwvxzwuvvvvj„+ +j€yyzy{y~|}{zyzz~zz{y{~yxy~{zz}~~{z|}{zy~~}{|{~}€}}|z{~‚~}}}€{}~~{|}}~~}|~€€|€~}||||}~€€}~‚€ƒ€€‚€€„ƒƒƒƒ€~„€…€ƒ€‚‚‚€~‚€‚€ƒ‚€„€‚ƒ‚‚€~€€€‚|„€ƒ‚€‚€‚‚€‚}„€„ƒ€‚~‚„ƒ}~€S7„|‚€~80|~~~}~|~~~|ƒ4hŠ||‚€~€}QcŒ~~||}|‚€|{~~€‚€”U"T‡„€‚„†€„†‡‡‡‡Š‡†††ˆ‡‡‡‡ˆ‡…††‡‡‡‡‡‡‡†ˆˆ‰…ˆ………ˆ‡ˆˆ†„‡‡…………‰‰‡‰ˆˆ…ˆˆ‡„‡ˆ‡……†‡ˆ‡††ŠŠŒŠ‡††…‡‰‰ˆ‹Œˆ‡ˆŠ‰‡ˆ‡‡†‡†ˆˆŠŠˆŽzz{wwxwwvvx{z{xxz|xyxzz{{~yyxxy{xxz{zv}{{{|y|||zzz|y{{{w{zzzzz{{~yzy{yyzyyzwyvtrqrI +;R€`gliZ+jqxwwwzvvvyusuvuwwxyywvwvton<;…vvwxyywvvyyv}0m|w{yyyzyz{zz|yzy{z{zyyxxyy{{}|}zyzz{}{{||{||}{€}z}}||~~~|{{}|~~}|}}{}~~~{{~~}~|~}}|{€||{~~€}}‚€€€€€€€ƒ‚‚€€€€€€€‚€}|||{€€€€€€„‚€~~~„€~~}€€ƒ~€€ƒ‚ƒƒ‚‚€€~‚€€€€€€€}}}c6|w~}}{„91{€€€ƒ~}ƒ€8iy{€~|}€‚‚}ˆS,=Z•ky€~~~~~~~~€~~‚€C::M‡~€‚ƒ……†€…†‡„Šˆ‹‡Š†‡‡‹‡‡ˆŠ‡‡‡ˆˆˆ‡‡‡ˆ‡ˆ†…‡„ˆ……‡ˆ…†ˆƒ………‚„†…‡‹‡‰…†ˆˆ†‹…‡…‡ˆ‹ˆ‡††ˆˆˆ‹ˆ†‰„Œ‹‹‰‰ˆˆŒˆ‹†‡‡ˆ†‰ˆˆ‡‹‹‹‹{{zyvvwvwwywxzwxzzzwyxxxxzyxz{ywz{{}}yy~zywzz||zy{zyy{wxw{zzzz{zxyxz{{vz{yvvwxztsqoUGyprrhj4 _twwvvwvuuvqqwvutryvvwyjywr€@6…zwyxyy{vvy{z~9A{~py}{}|{z~yzzzz||{{|y|}|y~}}}|z|{|y}}{z{}{z}||}z}~}{|€}||‚{}}~}~~~}|€‚€}~{}€~€€~€€{}||}€€€€}‚€~~€€€€€‚„‚€~€€‚ƒ€„€€€ƒƒ‚‚‚€€€‚€€‚‚€‚„‚‚€„ƒ‚ƒ‚„~€ƒ‚~‚‚‚ƒ€ƒƒ€€€€€‚~}„b#,Šqz|w„;+|~~~~~‚ƒ‡<jzu|†‚‚~|€ƒ…[F™Ž‚„„„‰ŒŒŒ‘Œ…„~€€ŽZL5 0/H€€…ƒ‡…„†€ƒ„††ƒˆˆ†††…†‡‰‡ˆˆ‰‡‡ˆ‡ƒ‡‰†…‡‡††„‡‡‡………‡…ƒ†„ƒƒƒ……†……ЇЅ…‡…ˆ††……†‡†‰ˆ‡Œ†‰ˆˆŒ‰‰†…ˆŠŠ‰‰Š‰‰‰††ˆ‰Š‡‰ˆ‡‡‡ˆŠ‰Š{{{{wwwxy{{{wyvzzzxyyz{{{yyxyv}|~y~}x{}|||{{|{|zzyyz{{||~w{{|{zwz{zzzz|xywzyyyxrpg]Fopqrew:Wxvvsvyvyvvuvvtuwsvuvuzz{vurL3‹wyyzyzzvy{zyxG �����������Z—–††…yyz}zz|{z{}{{zx{||z}}|y}}}z{{}}}}{~{}|~~~|||}}~~}{{{}~{~€}~|€~~||~~~|}€€€€~|||€~|€}€}~€~€‚€~€€€€€‚€‚€‚‚€„€‚€€€€„†€~‚‚ƒ„ƒ‚€€ƒƒ}{}€„}€…‚‚‚‚‚‚€‚€€€€€€€ƒ~€i�¡’rsx…I~{~~}~‚€~„~‚‚€…B +�>’Šxvu~}~€€jTJ@847<DR[XYZ_URPPNRRUc~~s[\rnotrt~€€…„…†…†…„„†„…ƒ………„†…‡†ˆˆ‰‡‰‡ˆˆ‡‰‰‡ˆ‡†ˆ…‡…‡‡‡‡……‚„ƒ…‡‡‡‰††…ˆ‰‰‰Š…‡„††‰†‘„ˆˆ‡‡†…‹ˆ‹Š‰ˆˆ‰‹‹‰‰ˆˆ‡’‰‹†‰‰‰‡ˆ‡ˆˆ‰‰‹‹{wwxww{x}{{{zzv{{z{yyy{{|{{|xww}y|y|yw{vy|}yxz{||wy{{zzwyw{{{{z{{{|xz{z{{{{zzyvpqg\�'JnmoqbdPS|uvvvuwtuuvwvurtsuvuvyxwvvsX�1‹wwzxzzzz{zyxpT7 +'")12378>>A@?@>68+ Yƒ{~|z|~z|}z{|zyz|~|y}~}}}}}|{~~}}{}~}}~~|~~}}|~|||{ƒ~~~|~}}€}€}}}|}~€~}|}|~€}}€}‚~|}~€~{‚~‚€~~€€€~€€ƒ‚€€‚€€€€‚‚€€‚ƒƒƒ€‚ƒ‚€~‚€€€€‚ƒ€‚„€€‚€€„~‚‚ƒ‚€~ƒƒ‚€€€ƒ„€}b*.231@btiimykM|z~z}~ƒ‚€€~ƒ€…BEtugrygD�&$Yp€ƒ€yus…~„§J (*"m€…~ƒ€„ƒ}}yzz~€€€‡…„„„…††…„„‚ƒ„†‡„†‡ˆ‡‡ˆ†††‡‡Šˆ‡ˆˆˆˆ‡ˆˆ†…‰ˆ‰ˆ‡†…ƒƒ†‡…†‰ˆ‡ˆˆ‡†…ˆ…†…„†ˆ†‡‡Š‰‰‡†ˆ…‡ˆ‰Š‰‰ˆŠŠ‹‹‰ˆˆ…ˆˆ‡…ˆ†ˆ‡…ˆ‰‰‹‰‰‰‰xyxywv{xyzxzzz{}{{yzzzzzxxy{wyz{{{||{zvz{x{zy{{{~~~{}{|||||||{|{}|}|zzz{{zzyxxwstgf��&Pzfh{tTaP{vvpvrqx~‚‚Š}vwvvuvu{vvvvog�!‰vwvuzww{zyz{tgXQivpxL$aczŠ…„†Ž‡ˆƒƒ”œD" Š{z||~{}|z|{zz{z{{z}}{xwzy{||}}|~~~}|||~~~{}|||}|}€}~}}{{~~‚€~||~~}}}|{}~|€}{}}||}~~~~~}€}€€~~~€€€„‚€„€~€‚€€„€ƒ€€€€€€‚ƒ‚„€€€€‚‚€‚‚€ƒ€‚‚‚ƒ€ƒƒ€‚ƒƒ€~ƒƒ~~g� +w‚rx…Š–v �u~~~~~~~€ƒ€€€€ƒI*‰‹tr€šS Je‡€ƒ„€ƒyg›M*3s~ƒƒƒ…€~~~€ƒ…€„†„†ƒ‡††‡‡„ƒ„†‡‡‡‡†ˆ‡‡‡‡†‹‡…‰ˆ‡ˆˆ‰‡‡‡ˆˆ‡‡ˆˆ‰†‡ˆ‰…‰ˆŠ‰‰‡ˆ‡‡…††Š…‡…ˆ…‰‡ŠŒŒ’…ŠˆŠ‰‡ˆ‰Œˆ‡‹Œ‹‹‹ŒŽˆ‹…†ˆˆ†‰ˆˆˆ‰‰ˆ‰‰y{{yxy{wyxvuwzzyyyyzxyyxxxyz{}{{zzzyzyxyw{|xxy{z~||~||x|{z{|xz{|z|zy{zzxvvwwwwwwurj_ "(8IP^heZPD LtuwuunbQJGA2Aarwvvwwuvvvuvvj‡vwvv{zvzyy{{utwsrkcan+—potuuwwuuyqYŽH> !Šyz|~~{~{}}{xzz}zz{~{|{{||{~{}{{}~~}{||~}|{|}~~~~}€|}}{|}~€€~€}|~€}~}}|€||€}{~|}||}|}}~~€}~€ƒ~„~€€€ƒ„€}~€€‚€‚ƒ„ƒ€‚ƒƒ€~ƒ€~…}€ƒ‚‚‚€ƒ€ƒ€~~~~‚‚€€~€‚€‚ƒƒ€„‚~€}n�Ybowyts!s}}}€~€€‚€}O2ƒnv{yt‡Y3a…‚ƒƒƒ€}~€€~{€Q#€„†€~}~€„„ƒƒ„…†„††ˆ‡†……††‡†……‚ƒ…†‡‡ˆˆ…‰ˆ‡‰„‡‡‡‡‡‡‰ˆˆˆˆˆˆ„ƒ„†ˆŠ†ˆ…Љ‡„„‰ˆ‡‡…‡†‹ˆ‹…Œ‡‡†‹ˆˆˆˆˆ‰ˆˆ‹ŒˆˆˆŒŽŒ‡…†ˆ„†ˆŠ†‡ˆ‰ŒŠ‰‰wxxz{{yxx|wyz{{zyzxz{|ywxzxzxxwxwyz}z|xxw}{yzw{{z{|~|||}z{y{{|{}{~yzz|xvu{{{wyyywukd s~pmv}OJwtuvvjO: .Swvvvwyuwuzvwrfƒvz{z{|{|{{zzw{zxvuqkg� ‚ruyzz{zz{zvp€H'„y{}~~}~~~~~{|{x{|zy{|{{|}}}}z{|{{|~|{{}~~~}}~~}~~}€~}~}}~€~~}}|}~~~|{}}}{|~€~|~}|~~|~~}€}~~~~}}€€~ƒ‚€~~€€€€‚„ƒ‚~‚ƒ€€€€‚€ƒƒ~ƒƒƒ…‡‚€~~€ƒ„~€‚€€€€„‚€‚…~€ƒ€€~‚‚ƒƒ‚‚ƒ€€€~sVpy{~‚x…5�i‚{}~~ƒƒ}€ƒ‚}„K/qt{|{xŠT�f‚†‚€ƒ†€Š€ƒ}ƒZ|z~‚ƒ…„†„ƒƒƒ…††……„„†………ˆ†‡…†…ƒ‡†…ˆ‡ˆƒŽ…‡‡‡†‡‡ˆ…ˆ‡ˆˆˆ‡‰ˆ‡ˆ‰ˆ‡ˆ‰‰‹ƒ‡‰Š‡ˆˆ‰ˆˆ†……†…†‡ˆ†Š‹‹„Œ…†ˆ‹‡‡ˆŠ‰Šˆ‰‹‹‰ˆ‡ŽŽ‹‹‰ˆ‰‰ˆ…‰‡ˆˆ‰‰‹‰‹wxwvxwwwwvvyz|{zzyxxy~{wxyx}yxy|yy{zzyxzxwwz}zzz{w~|||~}||y{|{{}{zyzwvwyuuxwxzxzvsm[�p[fmkoZ +H|ruvvgL(+Gwuvw{wvwvrvxs_‚v{r{z{{{wz{yz|yxvuuuf�"vwyz{{{{{zxyzK�2‰yz|}}zzzz{~|}}}{z}zzwz|{|~|x}{zz{}|z{|y}~~~}~~~~~}|}~~~}}~}|}€~‚}~|~€~||}|}}}~~|€~}‚€~‚€€}€‚‚„ƒƒ~~‚~€„ƒ„ƒ‚‚€‚ƒ€‚€€€€ƒ‚€‚ƒ‚„ƒ‚ƒ„ƒ€ƒ‚‚‚€~€‚…ƒ‚„„„ƒ„~„„‚‚‚‚ƒ€‚}€…‚€€{v"X‹~~~{ƒ8oz|}~ƒ‚€}–W!p‚|}~‰X gt€ƒ‚„ƒ‚ƒ~‚…[x|~€~~ƒ…„‚………‡…ƒ…„„††…„‡‡‡„ƒƒƒ‡…†††…ƒ„†‡‡‡‡ˆ‡†„……†…ˆˆ†‰†‡‡…‡ˆŠ‡‡……‡‰‰‰Š‡…„‡ƒ„…†‹‹‰Š†Š…Ї‡‡‰‡ˆ‡‹ˆŠˆˆ‰‹Š‹ˆˆŒ‰‹ˆ‹‰††…ŠŠ‰‡‰‰‰ˆ‰xwuvuyyyyzzzz|zzz{zzw{yzyzz{yyw{wxyywwwzyxx{yvwx|x|y|||||||}z{{|y{{~xvw||z|{yzz{utlf'#hfopnj\ AzutvqkN Eqvvvvwxvvwyyri {r{zzzz{yzzyz{~yyzzvye0}}wzzz{|z{zzv‚K��<~{zyz}}~~~}}{{|{{{{|z}|{{}zyz{{{z}~{}{|{{|}}}~}}~~~€|}}~|}~}|‚~|}}}}~~€~~}}{~|}~~{€|€~€€‚‚‚|€‚~€€„ƒƒ€€€€‚€ƒƒ„€€€€‚€‚€‚‚€‚„‚ƒ‚€€€€ƒƒƒ€‚€ƒƒ„‚‚ƒ„„ƒ‚‚‚‚†‚‚‚‚}‚‚ƒƒƒ€„‚|y C}}€}z{>i}w}€€yrUs~‚~…[5 +an{ƒƒ„„‡‚„‚~aU‰w~‰‹ˆ‚ƒ’„Œ‡~‚†„ƒƒ‡…„„…„‡…‡……„……„„†„„…†…ˆ‡‡‡ˆ‡ˆ‡ˆ…‰‡‡‡‡ˆˆ„…„‡‡‰„‹†‡…Љˆˆˆ…†ˆ‰‰ˆ…ˆŒˆŒ…‰†‹ˆ‡ˆ‰†‹‡ŒˆŠ‹‹†ˆ‡ˆ‰‰ˆˆ‰Šˆ‡ˆ‰‡‡ˆŠ‰‰ˆŠ‰Œ‰‰wzyvuuwwuyzyvy{zw{zxww{{z{zyxwwvwvwwxz}{}yy{xxy{|x|{||z{v{|xxz{~|||{yuyzzy{|zzzzrql_*� +,dxrorpg ;…quusdf� Byorvvvwvvyprrqzpuxywyzy{vzz||yyyywul"}}vz{z{|{|z{w‰Q=7A{y{y{|}}zy}|~|{~~|||||~{~x{z{}~}}}||||||}{€}~{~}{~€}}€}|‚~|~€|€|~|}}}}}~}‚~~~~~~~{ƒ€ƒ~€€~~~}€€‚„}€„…„„„ƒ€€€ƒ~€€€€€€€‚‚ƒ€~‚‚„~‚†…€‚ƒ‚„€~„€‚ƒ‚€ƒ‚„‚ƒ‚~€…‚‚‚‚‚‚ƒ‚€€ƒƒ}€" +<ut~}u~A%�-^‘mx}~€€€ƒ‡…yfem~‚€€~c48 &]|zŒ’‹Šƒƒƒ‚„€k + W’ˆxuvqvuxttokep}„…‚‡€‚…„…†‡…††………‡…„ƒ„„†‰ˆ‰ˆ†‡ˆ‡‡…„„‰ˆ‡‡‰„‡…ˆˆ‰…††…†ˆ‰‰‰‰††‰‰ŠŠ†ˆ„‰‡‡†‰ˆ‹ˆˆ……†Œˆˆˆˆ‰ˆ†ˆˆ‰‰ˆˆˆ‰‹ˆ‡ŠŠ‡†ˆ‹ˆˆ…‡ˆ‡ˆˆˆˆxyvvuvw{tzyxxxyzw{zywxxzxyyzw‚wzx}{zxy~}zxy|xz{{{|{w}|z{|zyyxw{|{{zyyvwwxz|zyxzzusmb1 +,mutrtlg9kpsyaf=B†gnwvvvvvwrn…‹€‹oryzwxzyvzzy{y{yyvwt|}wy{}z~~y|w€a=CJOSuzz}{{z}{||z}}~~||~|}~}}|x}}|}||~~}~}}~}||||||€~~€€€}~~|{}}}}}~~|}}}~~€~}}~}~€~€~|~~€€}‚€ƒ‚‚€€‚~€‚‚„~€‚ƒƒ€€€‚‚€‚€ƒ‚‚‚‚…€ƒ…„…€‚€‚‚‚ƒ‚‚€‚ƒ€€€‚‚ƒ€…‚‚ƒ‚‚„‚‚‚ƒ€‚‚ƒ}Š(4i{w’xu…H(#G ”„‰…………„~}pk„~e}‚‚‚„o6!>:;8::>?A>AHHGIJPPQBWyƒ„†„€rkjfhl‚o@,%%%Yy‚†…‡ƒ„„„††‡‡…†ˆ‡‡ˆˆ‡‡‡‰†ˆ‡‡…‡‡‡‡†‚ˆˆ‰‰‹‡‰‰Š…‡‡ˆˆˆ…‹‡„…††††ˆˆ‡ˆŠ†Š†ˆ‡‰Š‰‰‹ŒŒˆŠ‹ŒŒ‡‰ˆŽ‰ŠŠ‰ˆ‹ˆ‡‰‰ˆ‡ˆ‰ˆŠ„‹ˆ‡‡ˆ‰‰z{xwvvwvvxzyyvwzzz{zwxwzxyywyxy||zzz|{}|zw{|y{||yzzx€||{{xuz{z{zxzzz{{zz{{w{wzvxxspe6 +%gpsrqrj0egwsfdL Ewy€€„„ƒ€raH�^—‰‚{kfnzyyyyyxzxxxyty� vyz{z{yyzz}yxx‰€~wzy|zz{|zz|z|~~}{~}}~~€‚|~}{~{ƒ~~}|}z~}}}}~z}}€{€€€}€~}~‚}}|~{‚{{|}{~}€‚€~€ƒ~~~~€ƒ‚‚‚‚~}}~€‚€}„‚‚‚~„}‚€‚ƒ€~„€‚‚ƒ†………†…„„ƒ}ƒ‚…ƒ‚ƒ€ƒƒ€€€‚„…ƒƒ„‚€€‚€€€„~ƒ~ƒ~~€ƒƒ~‚„}~3"5FSJGJONWUc`iee`e`WeqUG95+5+.+/*-141)V‚‚€‚ƒ~xys|€ˆˆ„ŠŽcH)**@+x‚„…‡ƒ‚~z~‚€ƒ£D Iƒ€‚„‡…ƒ‚ƒ‡†ˆ…………‡ƒ†‡†††‡Š†„†‡‰†……ˆ†…„………ˆˆˆ………†‡ˆˆˆ†ˆ…‚„…‰…‡‰‰„‡‡ˆˆ‡†‡‡†ˆ‹‰ˆ‡‡‡ˆˆˆ‰‡†‡ˆˆˆ‹‰‡ˆ‰‰‰‹‰‰‰‡‡‡‡…†…Šˆ‰ˆˆ†‡z|zyxvyxxyz{xuvzzz{|yyyxz{zzyyw{z{yz{|}~~}xzy}||||xyyz{{{yv|||{zz{zz{{y|{zw|xzwuxumg8Zstrrok��0@k^YWI@;87:@CIHHFDA?>>:::90! [yz{z{{|zzxyxxs#k€x{z{{zy|z~xyzyxyxzzzzzz{{~~}{~}~~~~}}|y}}~|€}{~}}}}}}z|}~~|}~}}€}}}}}~€}}€~|~|{~z‚}|~~{|}~€€‚€~~~‚}~~}}€€€~}~}~~€}~€€ƒƒ€~€~€„€~ƒƒ€€€‚„…ƒƒ„€‚ƒ€€€ƒ…ƒƒ€€‚€€‚…€ƒƒƒƒ‚‚~…‚‚‚„ƒƒ€€~|}€„„„~0!U{lr‡………„‰„ƒ‘ G�T„‚€€ƒƒ~}}~ƒ~€}{~? +#7fƒƒƒ„…„‚‚{s™g<‡~„‡‡ˆƒ„…††††…†…†‡†…†ˆˆ‡„†ˆˆ‡…„‡‰‡‡„‡‡ˆ‡‰‚„ƒ…„……‰…„††…ˆ‡‹‡Šˆ…ˆ‡…‰ˆˆ…‡††ŠŒ‡ˆ‡‹‹Œ‡‰Š‹‹ˆˆˆ‰‡‰ˆŒ‡‰ˆ‡‡‰†ˆ…‰ˆˆ‰‰‡†{zw|{zzwwzzyxxvy{{ywxyww|zzzzxwzxxzzw~z{wwyz{~||~|yx|yzy|{v|y{|ywvwww{zy{yz{uvvvyvng9�T|tusod'���7[‚|w~‰V���1Nzz{z{v|zz{zwxymzx{z~{|zwyy{zzyyyxy}|~v}~}}}~z|{x{}}}z}|}}~|}}}||~~~|z|~}}{~~~{€|~}}~}}~~~~€}€~}{}|‚‚€}~€~}{|~€‚ƒ€~}€{~~€}}~}~~ƒ€~~€€€€‚~„„ƒ‚ƒ‚€€„€ƒ€€ƒƒ„ƒ„„‚ƒ‚„€ƒƒ‚ƒƒ‚€‚‚‚‚„‚„„‚†ƒ€€€„…ƒ€€€€ƒ€€€~…€0 #Qvw{~}~}}}~yoJ�]„€€€€~~}|~}{z|}~~‚}‹C/^Šƒ„„‚‚ƒƒ‚…ƒƒƒŠX �~}†…„ƒ…††††„‚†„‡†„„‚„‰ˆˆ†…‡‡ˆˆˆ‡ˆ„‡ˆ‡†……ƒ‚„‚‚…†ˆ‡†ˆ‡ˆ‰‹†‰ˆ‰Šˆ†ˆˆ‰……‡‡Š‰‡‡ˆ‡‡‰†‰‰Šˆ‡‰Š‰†ˆ‹‹‹‹ˆŒ‡‰ˆ†…ˆ‰‰„‰‰‰‰ˆ‰ˆyy{{zxxyyyxx{}vy{zwww{zyzzzyzyw~y{xxwˆy|vxy~y~~}|||wwzyx{{v|uxy{|}{zv|{{zzyzxwvyxuqk<�Q{tqqko+� +|vlhfb8FŽy{zzz{zz{|vu{�]ˆy{zz~z|zzzxzzyy|}}}{y}zz}}}{{}|~}{{}}}}{~~}}}||}~~}}}}~|{|{|~}}{||€}~}€|}}€~~|€|{{~~|}|}~~|}€}}}~~}}€~~}‚~€€€€~€€€}}€€€~€€~€„€…€ƒ€€€‚ƒ€€ƒ€‚‚€‚‚ƒƒ„„„€€€€€‚ƒ€€‚‚€‚€€€€‚„ƒ†‚‚‚ƒ€€†‚‚‚€~}~€€ƒƒ‚„„‡€…†1 +Oy}~€~€ƒ€~x‡K + ]ƒ€€€…‚ƒƒ„€ƒ‚~‚‚JeŽ‚„„‚€„„††‡ƒ‚„W�,‚x‚…„„ƒ†……††…†…‡„††ˆ‰‚ƒ‡Š‰Œ„††‡ˆˆ‡ˆ‡ˆ†‡……‚……„‡†……‡ˆ‰Š‹ˆŒ‹Š‹†ˆ‰Š‹ˆ‰…‰‰‰ˆ‡†‡‡‡„ˆ‡ˆˆ‹ŠŒ‰ˆˆŠŠ‰‰ˆˆˆŒ‡‰‰ˆ„ˆ‰Š‡‰Š‹ˆˆˆˆ|vwzwvwwxwwy{zwv{xtuwwxyzyz{vxxxxyyyyxwvxywzwzxvxz}yy{z{{zv{vvvw||xyvuvxzz{zwxxuwutmI�#Rwutolr.��xnqsmks@‡vzvzy|{z{zuzx*[v{zz{{zzzzzzw||z{}z{|{|~}€}~|z}}{~}‚~}}~~}}}~|}}}|}~~z|~|z~~|€~€||{{|~~}}~~{‚~~€€‚}}‚}€}~|~~~~|~}}{€~~~~€€}~~‚€€€~~~€‚€€‚€~€€€ƒ~‚„ƒ‚€‚ƒ€€€ƒ„„ƒ‚ƒ„ƒƒƒ…€ƒ€‚‚‚‚€€‚„…ƒƒ€„€€€‚‚ƒƒ‚ƒ„„ƒ€„‚ƒƒƒ‚€}…‡9 Kˆ|~~‚‚€t„s€}†Q�Qˆ€„€€€„‚‚‚‚‚€‚ƒƒƒ‚‚‚ŒP mˆ‚„…ƒƒƒ„ƒ„„ƒ|g)�!5…s‚„„…„……‡††……„†„ƒ…ˆ†ˆ‡‚ƒ‡†‡†ˆ‡‡ˆˆ†„ˆ……„„„ƒƒ‚……‡‡‡……‡…ˆ‰Šˆ‰‰ˆŠˆ…„…‡‡„‡‹…‰‡‡ˆ‡ˆˆ„‡ˆˆˆˆŠ‰‰ˆ‡‡‰ˆ‡‰ˆˆˆ‰ˆˆˆ‡…‰‰‰‰‰‡‡ˆˆˆˆvvvxuww{xwv|{yxy{wtyyxyxzz{|zxz}|~}}ywvwwzw~{zyyxyxywwy|{{{|zz{y{|zz{|{zy|z{{zwuuurjR�Touurjx2,snrrph}�@…qz{{{{z{zyww& ++Wlwyyyz{yyyzzzz{{{zyz|zy|{~~€|}}}}}~}}}}}}}}~}}}}|{|}{}}~~~‚€}|}}}~ƒ~|‚~~~~~€‚€€~}~}}~{|}|}}~}€€~‚~€‚€‚€}~‚€€‚€€€€€‚€€€€‚~~€ƒ‚€ƒ‚€ƒ€…€€€ƒ„ƒ‚€„‚‚ƒƒ…ƒƒ„€†‚ƒƒ~ƒ„„ƒ‚‚ƒ€ƒƒ€…ƒ€‚‚€€ƒ‡‚ƒ~€€?$UŽwz‚‚ƒ„ƒ„‚ƒˆX�M‹ƒ€€‚ƒ„ƒ„‚ƒ‚‚„………„€‰R�b‡„„„………„Œ|x’X�: Œwƒ……‡ˆ‡‡††††…‡„…„ˆˆ…†ˆ†„†‡‡††„…ˆˆˆ‡ˆ‡ˆ…‡…ˆ„ƒ„ˆ††‡‰†ˆ…ˆŠ‹ˆ‡‡‡ˆŒ…Šˆ‡†Š‰‰‡ˆ‡‡‡‡ˆ‰ˆˆˆˆ‡ˆˆˆ‡‰Š‰ˆŠ‰Š‰Šˆˆ‡Š‰‰ˆ‰‰Šˆ‰ˆ‡‡ˆˆ‰|wxxvvurvvvutyzyzwvxyytyzzvvyz{{}{zyyzwwvvyz{vx|~z{||wyxz{y|{z|~|{{zwzzzz{{{{zuvztrghNwrsrit8�~lqrpgk AC‘ctzzzy{}{y{yy$D!Mpk|”‘””—–”‹“–‹}Š€„xuv{}€~~~}~~‚‚€}}~~~~~}€~|}|}}z~~‚~~~‚‚€‚€~~~|‚}~~‚‚‚~€ƒ‚‚}|}~}|}~€€~~‚‚~€‚ƒ„ƒ€€‚€ƒ‚€€‚€ƒ‚€€€€‚~€ƒƒ‚€…ƒ‚€€€‚‚€„„‚€€ƒƒ‚ƒ„††„‚ƒƒƒƒ‚€€~€€‚„††‚‚‚ƒ„ƒƒƒƒƒ„‚‚‚ƒ‚ƒ€„ƒƒ‚ƒƒƒ€‚ƒDC”ƒ‚ˆ…‚ƒ„ƒ‚€_J‰‚€„‚…‰ˆŠ””™’‚ƒ…‚€„Z +a‡ƒƒ„‚„€w|€yv~…PI9++2()#�2rkSKROIƒ„ƒƒ†‡ˆ†ƒ†††‡†‚ƒ††ˆ…‡ˆˆ†‡ˆ‡††„…„…‰‰‡‡‡‚††‡…ƒ†„†‰†…†ˆ…„„†Šˆˆ‰‰‰„…ˆ……„…‰‡‡ˆˆ‰‡†‡ˆ‡‡ˆ†ˆ‰‡……†Š‰‡ˆˆˆ‡‡ˆ‡ˆ‰ˆ‰‰ˆŠ‰ˆ‰ˆ‰Šˆ†wwxxtwuvv‰sxvzz{zz{{z{wxxz|~~}zz{}zzxzyxw†vyww{||}|~{y{vy}x}zzz}||z{yƒxzz{{{{{xvwvrlU U„hjnpt? +��!ˆ{mnpuj���������C™œŽ„†{w{{{zyx, 56):@GQQNHGDB<>14/&),'&"8e|z~}}~~€~€~~{z}~~}~}}€}}}}~~}}}~~„€}}}}|„}}|€€~~~~€‚~€€€}~~}{z€z~}~€~€}€€‚€ƒ‚„€€€‚€€€€‚€€€€‚‚€€ƒƒ…„„†ƒ€€€‚‚ƒ€€‚‚‚„†„…ƒ‚€‚ƒ€‚ƒ…†ƒ€‚‚€€ƒƒƒ„ƒ€€€€‚ƒ„ƒƒ‚€‚‚‚ƒ„ƒƒ‚„H+,)4:AUspe`imv€…€~€b!�&4<?978,M€€„D;NCD7(Xy‚„„ƒŠWZ‹€ƒ†‡€†L"""G‚‹‹‘€us4z†uƒŠ‰”Ї„……„……†…††ˆ†ƒ‚ƒ………†‡‡††…‡†‡†‚„…„‰‰††…ƒ††‡†ƒ††…‰„……ˆ‡†…†‡‰‡Š‡…ƒ…………†…‡ˆ‡‡‡‡ˆ…‡†…ˆ‰…ˆ‡‡‰Š‡ˆˆ‰‡‰‰ŽŽˆ‰ˆ‡‡‹‡‰Š‰‰‰Š‹Š‹wy|xu|vwwutvvwuyxyuuvzywwyz||{y{y{xyyyyzwyvvwz~{z}zyxx|||zxyzz{ywwy|zzy{||{w{zxvuuj_ ;\hxwjQD2 +2T…{qoD"22155899;<9@ABNYV^XX:09HRXy}}{zyxƒzxcH!) � + +>,Xƒ{~y|~{‚€~€~}}~~~~€~~{€~|~~~~~~~}€€~~~€‚~€}ƒ}|€€€€~~~~}~}~~~}~~€€€€€ƒ|€{~‚~‚‚‚€€…‚€‚‚‚€~„ƒƒ€}‚~~€€‚ƒ~€‚ƒ‚‚…ƒ€ƒ‚€‚‚ƒ‚‚€ƒƒ‚€…„ƒƒ…€ƒ€ƒ€ƒƒƒ„‚‚‚€ƒƒ„ƒ„‚ƒ€€‚ƒ‚…ƒ€‚ƒ‚€€‚€N�‰vklry{d*%.')i~ƒ~€ƒ‚h�l}otƒz‘nG„€~€„‹?A0#!k‰ƒ‚„…aUŠ~„‡†~“$" ’y{vwB�rjv€~ƒ‚‚ƒ‡……„ƒ†ƒ†‡ƒ„…‚ƒˆˆ~†‡†…ˆ†ˆˆ„‡„†ˆƒ‡ˆ…„……‡‰„‡†……„„„…‡‡‡…„„‡†„„„„†ˆˆ…†ˆˆ‡…„†‰‡…ˆ…‰ˆ‡ˆ†‡‰‰ˆ†‰‰ˆˆˆŒ‰‰ˆˆ‰‡‡‡ˆˆˆˆ‰‰‹Š‰‹wxzxx{{{yuutxvzzzwuvyyyyywxyzwxyx~|zy}{zwxww}|{|{|||{{|xyxz||~w{zz{€xz{wzzxyzzwrtj]-8UA7:;AIaeXca@�Lp~t|‡sB'%k€okwy„x}|}ƒ€~yxv…s[…{{z{{{vvvvx|C� O‹{~{}ƒ}~}~~~}~~}~y~~}~~}}~}~~}}„~~€}|~~ƒ~|~‚‚}}}‚~}~}~‚‚‚}~~€€‚ƒƒƒ|~}‚€}ƒ~}ƒ€…‚€…‚€€ƒ}„€‚‚‚ƒƒ‚ƒ…~‚‚‚‚‚€€€€€‚‚€€~ƒ‚€~…‚ƒƒ€€‚‚€…„„ƒƒ‚€‚ƒƒ‚~~~~}ƒ€ƒ…‚~‚€‚€‚‡„‚†„ƒƒ‚†P�…Ž}~ws|{ + C +_…ƒ€€„j�Xgy~{y‹h'AŠ€€€ƒ„J #qƒ‚‡‚bX‰€‡‡„•9$ ˆ{~~}}Ybv€€€‚††‚†„‚…†„…„†…„‡†„„ƒƒ†‡†‡††…†…‰‡ƒˆˆ…‰‡‰„…„……†ˆ…„…„ˆˆˆ†ˆ…‡†ˆ……‡ˆ……‡‰ˆ‰‡‡†ˆ‡‡…‹…‡„Š„…†‡†ˆ‰ˆ‡ˆ‰ˆŒ‹ŠˆŒ‹‹ŠŠ‰‰…†ˆˆ…ˆ‰‰‰ˆˆ‹xz||{zvvwuwvvvvyvzzxwxwvvwxxxzxz|zyzwyyzz{zy|{{|x}}z{|wz{{|zx|{|wy|{{zxwz{zz{xzzzvtjY%,7UaemhRL��Fpdijk|?��fak}vvvvxuptutpuvvvop|"f…zyzvwyyzyuvCQŒ}}{}~€„}}~}}~~}}~~~~~~~~~~~}}~~}}~~€~~~}€~€}~~€€€‚‚€~}€}}ƒ‚‚}~~~‚‚‚€ƒ}‚…€€ƒ~~‚„€€‚‚‚ƒ…ƒƒ„„‚‚„‚ƒ‚ƒ„ƒƒ‚‚‚‚€…ƒ†€ƒ€€|†„€†€€‚ƒƒ~ƒ€„…€‚„„‚…ƒ„…„ƒ„ƒ„ƒ‚ƒ‚ƒƒ‚‚‚‚‡†ƒƒ‚ƒ„„‚‚ƒ[sx€zul Zˆ‚ƒˆl�Yx{~{u0� ?Š€ƒ‚ƒFm€‚‚€‡`V‰ƒ‡ˆ‚‚–>��‹„ƒ„ƒ“R�\Œƒ„…‚€†„†‡†‡ƒƒ„……††‡„………„‚ˆ‡‡‡‡………ˆ‡‡‰……ˆ‚‡„†………‡ˆ…‰‡ˆ‡‰‰‡†…ˆ†ˆˆ…ˆ††…‡‰ˆ…‡‡‡‡‡†‡†‡‰†‡„„ˆ‡‡†ˆˆ‰†‰ŠˆŒ†‰‰‰‰ˆˆˆˆ‡ˆ‹Šˆ‡‰ˆˆ‹‰‰xy{|y}wwvyz{wxzyzzz}xxyzxx{{zzz|{~|zyzwwzxxyxw{|{}|{|||}{|{|~|{|v{|{w‚v{x}||xvwzxxrp\(1slmgdpY�Hyjrpkx= [nqvvvuvwy{zz{}|{z{tp|#[‰{{yzzzz{{ytxINŠ}€}|}z€ƒ{~~~~~~~~~~~‚~}}}}~|~~~}~~~‚„‚€}~~€~~€€€€~€~~~~}~~~}€€ƒ€€~ƒ‚„~€‚€‚€~€‚ƒƒƒ‚‚ƒ€€‚ƒ‚}‚ƒ‚€€ƒ‚‚€€„‚€„ƒ‚€€‚ƒ„…€‚‚‚~ƒƒ‚‚„€€‚ƒ„ƒƒƒƒ‚ƒ‚€€€‚‚ƒ„„†‡‡ƒ…ƒƒƒƒƒƒƒ€ƒ~‚‚‚ƒ‚‚ƒƒƒƒƒ‡‚‚‚„]�o}ƒ‚xs!Zƒƒƒ|‰f*J“{‚}ˆ6� � +>Ž€€€Izƒ‚„c! Q‰ƒ‡‡ƒƒ–F#‚€…„„…U!Uƒ€……ƒ†€‡‡†…‡†„ƒ…††…†…ƒ†……‡…‡‡ˆˆˆƒ‰†‰‡‡…„„ˆˆˆˆˆ‰‡…†……†‡‡…‰Š‡‡‡‡…†…†††††„І……‡ˆ‡‡†ˆ‰ˆˆ†††‰†•†‰‡ŽŠŽ†ŠˆŒŽˆ‰‰‰‰‹Šˆˆ‹‡ˆˆ‹Š‹yz|yx{ywuuv{w{zzuzw{zzyxvxwzu{zyu{{x{y|wzy{|yux{}{||y|x{zz||{xz|vxw|s~xxxy{xwwv{wurre�"1wpqmnvU�J…qtumx<Ovvvxywy{ywyzyzz{zwrz Wˆ{{}~v{zz|ztz]>NŠ~‚~}}}}}~~|{~~~‚~€}||}}}~~€~€€~‚€€~}}}‚}‚€~€‚~~~~€~~~€€€€‚€‚ƒ…‚ƒ€‚ƒ€€€ƒƒƒ„~‚‚„€‚ƒƒ€‚€€€€‚ƒ„ƒ‚ƒƒ‚‚ƒ„†ƒ‚€ƒ€ƒ€ƒƒƒ~€‚ƒ‚‚€†€€ƒ‚‚ƒ„„ƒƒ†‡„„ƒƒƒƒ€ƒƒƒ‚‚…‚ƒ‚†…„ƒ‚‚ƒ‚a t~ƒ‚ƒ„x�[†‚ƒ…w†r4 +LŠ‚€ƒ}?!7‚‚€…M f…ƒƒƒf%7Mˆƒ„„…†N63€†††„‘U; �7Rˆ……‚‚ƒ„‡††ˆ…‚‚‚………„ƒ„…†‡…€„‡}‡„„…‰‡†„ƒ†ˆˆŠˆ†ˆ‰„†……„†ˆ‡ˆ‰†…‡‡‡††……„‡†††‡……‡†ˆˆˆ‡‰…‡‡ˆ‡‰…„…ˆ†…ˆˆŠ‹ˆŠˆ‰ˆŽ‰Œ‰‰‰Šˆ‰‰ˆˆŠ„ˆˆˆ‰Œz|zyw|xy|{z{{{zzzzzzx|zyxyzz{~zwy{z{zyyy{zy|y}}|{}z|y}||z{{|{{z{wxvz{}{}vzxwxxw|vvus`)�!/|mqpocb� J€lqtmo@JŠyywz{vz{|zz{{zzzzus~�#T‹z|{}{{zzy|†ŒV���Q‹{€~~{}|{}€‚‚ƒ‚‚~|}}~~~€€ƒ‚„{‚~~}}~‚€€~‚€‚€ƒ‚ƒ~}~ƒ„}‚}€‚}‚€€~~~€„~‚‚€€ƒ‚‚‚„‚ƒƒ€~€}€ƒ€~ƒ~‚‚€~€‚‚…ƒ€€€€‚€ƒƒ€€ƒ‚‚„ƒƒ„…‚‚ƒ€~‚„ƒ‚„€€‚ƒ‚€€‚‚ƒ‚…„„ƒ‚ƒ‡„‡††„†€€€€ƒƒ„„ƒƒ„€€‚„ƒ‚‚‚‡Z2 7_{ƒƒƒ„~€BEU…ƒƒ†~ŒkB ;8“ƒ„„Š!F/-Ž‚€‚‚ƒO/ Ra„‹~Œg +N…ƒˆ‡‡…‡Rh‹……„ƒ‹T*7#E‚€„†…†…‚„‡‡‡…‚„‚‚‚„ˆ‚†„ˆ‡‡†‡††…‡…†„ˆ‡‡………ˆˆŠ‰ˆ„‰…††‡…„…‡ˆ‰ˆŠ‡‡‡‡…†…†…†…ˆ‡‡…Ї‹‡ˆˆˆ…‡ˆ‡…ˆƒŽ„‡…ŠŠŠ‹‹‰‹‹‹ˆ‹ˆ‹‹‹Š‡ˆˆ‹ˆŒ‰‰‰‰ˆ{{|zwwxx{zxz{zz|vxwyz{zy{yyzz}xxwzw|ywz{|}|}{||{w~z|}{|y|wy||wwyxwz|~z|v{wxzwv{uuvmf.�$ˆgppkaX9LugougnCGŠyy{{wwvyvwxzwwvwwrtP…z{zzz„…xpcC >^KMVQWeu}€~~}{~}ƒ{‚~€~~}}~~~~~~}€|~~~~„‚‚€‚ƒ‚‚ƒ‚€‚}~€}~€€~ƒ‚‚~€~|~€‚€€‚€€ƒ€€‚‚~ƒ}€ƒ€‚€€ƒ€€„ƒ‚‚ƒ€ƒ€ƒ‚‚‚€ƒƒ€€…ƒƒ€ƒ€€‚€€€‚‚ƒ€€ƒ‚‚„ƒƒ„ƒ€€€„„‚€ƒƒ‚ƒ‚ƒ€„€€ƒƒ€‚}}‚ƒ€†`/745`z„ƒ„ƒ~~ + Y~ƒƒƒƒƒqL?��"&.‚‚ƒ„…‚„-% 4€„‚ƒƒ{a f‚‚‡t†rN$68HP\†„…‚‡ƒ„fUIGHFLXm‚……ƒ…ƒr_ZassZ[m„ƒ„……†‚„†Š††††…„‚„†„†„†„…„„„‡}†…„…‡„…‡ˆˆˆˆ‰„„ƒƒƒ„‡…†„ƒ„‡‡‰†……†‡†…†ˆ†„†ˆˆ…‡‹†…‡…††……‰‡…„„„†††‡ˆ‹ˆ…‡Œˆˆ‰‡‹‹‹‹Š‰ˆ‹Œ‰ŠŒ…†‰‰{}yxy{{{zzz|vzz{{}xyz|x{}~{yxyx|xyxyy}z~}}}~x‚x|y~{|x|||zywvw{tz}~~~tz{{zww{vstnm2�� Žxw}{_I†sq|O��C‡yv{xwvjmwq][r{vwuuM„{|zzyM8.$<hŠ†Žš¤‹Œw{‡}€yz~~~}{}~~}„€~„~}~~€~~}}}}}~~~~~~‚„ƒ„‚‚€‚ƒƒƒ€€‚ƒ€~€~}~€€ƒ‚~‚‚€€€€€‚‚‚‚€‚€~~€‚€€~}~€‚†ƒƒƒƒƒ„‚‚ƒ†‚ƒ„‚‚†‚€‚€„ƒ„‚ƒƒƒƒ‚‚ƒ„€ƒ„€„€€„ƒ€~ƒ‚ƒƒ€€‚ƒ‚„‚‚ƒƒƒƒƒ„‚ƒƒƒ„‚‚‚ƒ„€€€€‹x@6HHJGCl}‚ƒƒˆ€|gYSQOPWh{„ƒƒ‚ƒ{_Y^dj_\h|ƒ„„„ƒ~~wozsyid{„ƒ‚†€‰‡ƒ€~uƒ€†…‚~Š’–Š‹…‰}‚…‡‡ˆ„‡‰Œ‘‹•Œ‡……„†„‡‡ˆˆ€…ˆˆ€„…ˆ……ƒ‚…ˆ‡ˆ†‡‡ˆ‡ˆ…‡‡†…‡„‡†„„ˆ‡‡‡‡‡ˆƒ„‡ˆˆˆˆ‹„‰ƒ………†ˆ‡†‡‡†††…„‰…‡…‡„‰…†‡ˆ‡‹Š‹†ˆ‡ˆˆ‡†‡‡ˆˆ‰‰ˆ…„…‡‡‡‰‡‰Š‹‰‰‰‹ŒŒˆ‹‡‹‰‰Š‹Ž‡‹‹‹|yz||{{x{yw{zzxwwvvvxyxxx{y|w||{yxyyyxwwyw{|~€z|wwwwyz{{x|yxwy|yyxw{x}wxwvww{xvvwwuqo1 BA:;BGGGIC@?=6<@ACTNQJC;:<<BEU]am\M�=†vvzwrXA8Nvzwwvt€L„||xyƒE?');ƒjrylQ’ �uiu|}}~~~}}|ƒ~~‚~}}ƒ~€~‚‚}~~~~~~|~„€€‚„ƒ„„ƒ~~~€€ƒƒ„‚€€~~€ƒ€ƒ‚€‚~€~~ƒ‚€€ƒ‚ƒ‚ƒƒƒ‚ƒ€ƒ†„ƒ‚„ƒ‚ƒƒ„†‚‚†ƒƒ„„€ƒƒƒ‚„‚ƒ‚ƒƒƒƒ…‚†„ƒ‚€}†‚‚ƒƒƒ~„„„€‚„‚ƒƒƒ‚€…‚‚€‚‚„ƒ„„‚‚„€‚Š„tlvsƒy{ƒƒƒƒ„‚~}ƒ‚ƒ}{|ƒƒƒ‚‚€ƒ‚‚}}}|€€ƒ„ƒ…„„~{yz{„}€€„„„ƒ‚€~{y|~€ƒƒ„ƒ„‚‚‚‚ƒ‚„„‡†ˆ‡‡†ƒƒƒƒƒ‚ƒ‚„ƒ†…„†„ƒ„„„†…‚ƒ…†‚ˆ‚‚‡‚‡……‡ˆ‡‡‡„‡…‡‡‡†……†ˆ†‡ˆ…‡ƒ‡†……ˆˆˆ„‡…„ƒƒ…ƒ‡†‡‡ˆ„‰‡†††††‰………†…ˆ…†…„††‡‡‰ˆ‡†„…‡ˆ‰‰‰ˆˆˆˆˆˆŠˆˆ‡Š‡ŠˆŒ‹ŠŠ‰‰ŠˆŠˆ‰‰‰‰Š†ŠˆŒ‰‹|{z{z}ywz~wy{|wzvwwvzyyzyzxzwzz|{xxywyyyy|zz{|}}|~||}|}}~w{zvzz|u{||{{||zzyxxzwvvpo2 +X^s…q„tB <duq|~i #?xffzkT_;‰uvxws[;?qwwwwv)F‹||xvxN& 0€svvuiŠ + +fvzzy{~~|{~€~{{}}~‚‚~~~~~~~~‚‚~~~~~~|~~€}ƒƒƒƒ€€€€}€~~€€ƒ~}~ƒ€€„~‚„}‚€€„ƒ„€€„}ƒƒ‚€€€~€€€€€„†ƒ‚€€…†…†‡ˆƒ„‚ƒ‚‚‚‚‚ƒ€~‚ƒƒ€‚„„‚†„ƒ„ƒƒ…€ƒ†€…€€€ƒ„‚}~ƒ‚ƒƒ€€ƒƒ€€€„†ƒ~‚€‚ƒ„ƒ€‚„ƒ€ƒ‚~~~~~€ƒ„€„†„„‚€€‚ƒƒƒ„„„ƒ„ƒ‚‚‚€‚€‚‚‚€€€‚„…†ƒ€€€€ƒ„„„…††…ƒ€‚€€€‡‡‡„„ƒ„„„„„ƒ„„†‡ˆ‡‡††‡‡†ƒƒ‡††………‡†…††„…„†‡„‚„„††‡ˆƒ…‡…‡††‡‰„ˆ…ˆ‡ˆ†‰‡†‡„‡ˆ‡‡†‡†……ЇŒ‰ˆ„‡ƒ†„ƒ‚‚‡‡‡ˆ†‡„ƒ‡‡†‰…†„‡…‰††„”…‡‡‡ˆˆ†‡‡‡ˆˆ‡ˆ‰…‰‰ˆˆ‡‡‡ˆ‡Ž‰Ž‡‹ˆ‹Š‰‰‹‰‰‰Š‰Š‹Œ†Š‹‹‰‹|||z{{xz{}xvvvwwzxwvxxyyyxvzzy{zx{||}yw{{z{yy}|x~~||~}}~xx}z|~}|vz||{|wyzy{{zy{uvpl6chgkjlj7# 6dkjlfhz�/ =ekpng`d5ƒvwwwrr% 2uuywwu†+B†||{x{M0ƒyx|vu€*�`Œ{|z|€~€€}‡~€~€ƒƒ}€~ƒƒƒƒ„ƒ~€€€ƒ~~~~‚~~}€‚ƒ~„€„~ƒ‚€~}~€ƒ€€}ƒ‚ƒ‚„ƒ‚ƒ‚~€‚‚‚ƒƒ„~ƒ„„ƒ†Š€…}„ƒƒ€…€€ƒ‚‚ƒ…ƒŠ‡………†ƒƒ‚‚ƒƒƒƒƒ‚ƒƒ€~ƒƒ„€€‚†„†ƒƒƒ„†…€„„€ƒ…€€€‚‚‚ƒƒƒƒ‚€€‚€„„€ƒƒ„€€‚‚‚ƒƒƒƒ„ƒƒ‚ƒƒƒƒ…„………ƒƒ‚„ƒ…ƒ„‚‚‚€ƒƒ‚‚ƒƒƒ„„ƒƒƒ„‚…‚ƒƒ‚€‚‚„„†‚‚„ƒ„„‚€„…„„„‚‡‡€‚ƒ€€€ƒ†………†ƒ„ƒ‡……„†ˆ‰„ƒƒ††…††„„……„†…††…‡†…‡…††„„ƒ„………„ˆ†…………ˆ……†…‡„‚ƒ‡‡‡‡†ˆ‡‡‡‡ˆ‡‡„„ˆ‡ˆˆˆ‡…‡„„…„„ƒˆ‰……ˆ„‰ƒ‡…‡ˆ……„‡†††ˆ‡‚„‡†…††‡‡‡‡ˆ‡‡…ˆ‰‰‰ˆ‰ˆ†ˆˆˆ‹ˆ‡ˆˆ‰Šˆˆ‰‰‰‰‰ŠŠ‹ŒŒ‡‰‰ˆˆˆ}z{{{|||{}yxxxxw{{yyxwwxwxy{y{z|{zyywwwxyx|xx|||||||}~z}zxz{|}~{xyy{z{xwzzzyvwvuxre?�(R}mqmii) &_xqpmf|;doqqlt]9|wwwvqomvwxwv…& C†|{{yxN ;…yz|xwv7 X‰|{{{{~€€||ƒ~|~‚‚~€€ƒƒƒ‚ƒ€‚~~‚ƒ€~~~€~~}|}ƒ~~~~~~~~~|}~~‚‚ƒƒ€€€‚ƒ~~€€„†„{…ƒ~ƒ„ƒ€€ƒ…‚‚€‚ƒ‚€„„€‚…ƒ‚‚ƒ„„…‚€€€ƒ‚€ƒ‚ƒ†ƒƒ€€„„ƒƒ‚€€€ƒ‚ƒ†„„„ƒ€††‚€ƒƒ‚ƒ}ƒƒ‚€‚€‚€‚‚‚ƒ~€ƒƒƒ‚~„ƒ‚ƒƒ€„‚ƒ„ƒƒ„„‚€…„„„ƒƒ‚…€…„ƒ…ƒƒƒ„„†ƒƒƒ€ƒ‚ƒ„ƒƒ‚€‚‚‚ƒ„…ƒ…ƒ‚ƒ„ƒƒƒ‚…‡€†„€ƒ‡…ƒ…††‡‡„„„ƒ„„„‡‡†„„ƒ†††„ˆ†‰ƒ†„†‡ˆ‡†…………††„†‚‚‚……‡…††††…„ƒ†ˆƒ†…†………‡†‡…ƒ†‡‡………‡‡‡‡…„†……†‡ˆƒ„„„…„‡Š„…„…ˆƒ„†‡ˆ„‰††ˆˆ…‹†††ˆ„Š……‡ˆˆˆ‡‡†…‰‡…ˆ†‰ˆ‰ˆˆˆŠ‰‰‰‰Š‰ˆŠ‰ˆˆŠˆŠ‰‰Š‹‡ˆ‡Š‰Ž|z{{v|{{||yx{x|y{zxywwvwxxyyzzzy{}{yywwy|||zyz}{zywxy}~{zyzxz{|}~{yzz{{|w||{wxxxwxsq@�-Pzvqpjr4.durpiu:xprrroY;ruwvwpq! +2i{xwu‰: +(I†}{{yƒO$>z{z~zzvF $:U{|{{{€||~€€€~ƒ~€~€|}‚~€€€€~‚‚‚~‚~|‚€‚ƒ‚„ƒ€~~~€~~ƒ€€€~ƒ‚‚ƒ„€~€‚„„†„††…‚ƒ‚‚‚„‰„~Š‚€†ƒƒ‚‚†‚ƒƒ‚€~~~€€ƒ‚‚‚ƒ€„ƒ‚„ƒƒ„ƒ~ƒƒ„‚ƒ…„ƒƒ‚†‚ƒƒ‚‚ƒƒ„ƒƒ‚ƒ€„‚ƒ€‚}ƒƒƒƒƒƒ„ƒƒ‚ƒ€„ƒ…ƒƒƒ„‚…ƒ„„„€„‚…ƒ„‚ƒ‚ƒ„€…ƒ„…ƒ„ƒ„„…„‚„†‚…„ƒ‚‚ƒ€„ƒ†ƒ‚ƒ……„‚………„„†……†„ƒ„………„‡†‡„„…†„„„…ƒ„…‡‡……†„„††‡…„…„„„…‡‡…†…„„…………„…‚ƒ„†…„„†††‚†‡ƒƒ‚‚‚……‡‡‡‡„…†„…†‡†ˆ‡ˆˆˆˆ†…„ƒ„‚„‰„ƒ‚ƒ‡‡‡ˆ„‡‰…‡†‡ˆ……„††ˆ‡…‰ˆ‡†„…†…‡‡‡†ˆˆ…ˆ…‰‡†……Šˆ‰ˆˆ‰‰Šˆ‡ˆˆˆ‰‰‰ˆŠ‰†Š†ˆ‹ŠŠˆ‰‰Šx{{|z{|†zzzz{}|{{{{{xvvywxxxyyyzy}{{y{y{yy}zy~{{{}|y}}{z}y~z|zzz{||wx|{zy{|zxwwwwvujK4Owwqplh9#9Wzusplj &Amqtrpl`-Bkwvvupm5CXŒyxxv{I$ 9-“{{{{}S +*#9.„zz{|{w;;�*J†}}{}}}}~}ƒ‚€|‡~~ƒ~€~€€€‚€€~~ƒƒ‚‚}‚}‚„‚‚ƒƒ‚~~‚}~‚€~€€€ƒƒ€ƒƒ~‚€ƒ‚€€€‚‚ƒƒ‚ƒ…€€„ƒ‚€‚ƒƒƒƒ‚‚ƒƒƒ€€€„‚~‚ˆ‚€ƒ……ƒƒ‚‚‚~~€„†‚†ƒƒƒ‚„„ƒ‚‚€~~‚ƒ€„ƒ‚ƒ€€ƒ€‚‚ƒ…‚ƒ‚„„ƒ„‚ƒ„‚ƒƒƒƒ‚€„ƒ„„…€€ƒ……ƒ‚‚ƒƒ…†€‚ƒƒƒ„„‚„‚‚†‡…„†„‡‚€…„„…„…„„‚…‡„„…ˆ††…†ƒ‚ƒ‚ƒƒƒƒ„‡†……………ƒ„„ˆ…„†…ˆ…†††…†…‰…„ƒˆˆˆ……„„…………„†‡‡…„„……‡…ˆ…†ƒ…„ƒ‚‚‚…ƒ……ˆ‡ˆ„„†‡††„„ƒ„†ˆ…„………ˆ…Šˆˆ„…ƒ„†‡‡‰‡‰†„………‰…†„‡†††ˆ‡‡‡ˆˆ‡‡‡†‰‡ˆˆˆ‡‡‡…ˆŠ†‹‹Š‰‰Š‡Š‡ˆˆŠ‰‹‡‰‰‰ˆ‰ˆŠ…‡‰‰ˆ‰ˆŠ‰Œyywzz{{{zz|}yxzz{ywzywvvwxxxzzxyz{xzzzxyz{~y{x{~}z|zx}{z{y||{{|||~}{z{zyxwz{yxxvvwrnK0'9Mywsspl830=Tturplp8 48oprqqpb// 5Knvwwvsn6483Fƒvw{v{W� 107‚{zx{wd@;BDLPZw}{{}{uVZhlmnqq€z~{||~||}~„~|{{~‚~ƒ„ƒ„~€‚‚€€~„ƒƒ‚ƒ~‚‚‚€€€}‚€€}‚ƒƒƒƒ‚€€„ƒ~€ƒ€€€‚„€~€‚†ƒƒ‡‚€ƒƒ~‚‚‰€‡‚ƒƒ‚‚ƒ„ƒ‚…ƒ€‚ƒ‚ƒƒ„„‚‚…€ƒƒƒƒƒ€„€„‡€„ƒ€‚ƒ‚ƒƒƒ‚‚„‡‚€~~ƒƒ‚‚€€‚‚€ƒ‚‚‚ƒƒƒ‚‚‚ƒ‚€‚ƒƒƒ€€€„ƒ…„€ƒ…‡ƒ„ƒƒ„ƒ…€€€€„ƒ„ƒ…„„‚‚„…ƒƒ„„†€€„„†„„…„„††………†ƒ††‡†…ƒ‚„†‡†„„‡ƒ„„„ƒƒƒ„ƒ„‚„………†……ˆ†…‡………ƒˆ‡†ƒ……ƒ„„„†††…†……†…†…††…†…†…„ƒ‚‚‚ƒ††…|†……†‡†…„„…„‡…„ƒƒ‚‡††„…„†ƒƒ…ƒ‡‡Š‡††„………ˆ„…………„…†††…‡ˆ‡†‡‡‰…‡‡†……†‡ŠŠ†‹ˆŠˆ‡ˆ‡‡‡‡‡ˆ‡ˆ‹ˆ‰†‡ˆˆ‡†††ˆ‰…ˆ‰Š‹|}w{||{~zw{€z|z€{{w{yzyvwyyzx{z|y{z}z{|~yx||zy{~}~|{{}||zxz}z{z{{}~{y|{{{{{|ywuvwvsO9/,9F]wsvupgJ964*C^vruqoj9#:<@2DkrrsrqgLBIGHPZo{vywvoTRfedlur{xxz{xo„‡…||z|zz{x{‡‡ˆ†w}}{{z{wyz€xyz{{{~~~~~}~€}~~|}~€ƒ~„€€~}~~~ƒƒƒ~ƒƒ€~~ƒ~€‚ƒ~‚‚~‚€€€€€€‚€~~€‚‚‚ƒ‚ƒ‚‚‚€€ƒ~€‚‚ƒƒ€€ƒƒ„ƒ„ƒ„‚€~„‚ƒ„‚ƒ‚€„„„†ƒ„„……„†‚‚‚}ƒ†„ƒƒƒ€ƒ€ƒ„„ƒ„„„ƒ„ƒƒ„„„‡†‡„„ƒ‚„~‚}‚„‚‚‚‚ƒ€‚‚ƒ„‚ƒƒƒƒƒ€ƒ„ƒ€‚ƒ€€€„„ƒ„‚……„„„ƒ†…„ƒƒ„„ƒ…ƒ…„‚†„„„ƒ„…ƒ…„‚„‚…†„‚ƒ†‡†…†ƒ‚ƒ„„ƒ…††‡……†‡…‚‚†‡ˆƒ‡„„ƒ…„„ƒ„……„ƒ„‰†……ˆƒ‰‡†„††ˆ…ˆ†ˆ†ƒ„†…†„†…ƒ„‡‚„…‚„‡…†…………††……‚‡ƒ„…„‡…ƒ„‡…†„†ˆ‡†‡……„…„„……„ˆ…ˆ…‡…‡†„…‡‡‰†‡…ˆ…ˆ…ˆ††…„‚…†…‡…‡ˆˆˆ‡‡‡ˆˆˆˆ‡…„ˆ‰Š…ˆ…Œ‡‰ˆ‰†‰‡‰‡ˆˆ‹‹‹‡‡†‡‡‡††‡‰ƒŒ‰Š‰Œ{yxx||zz{{{zz{zyzywyzzzz{{yyzzyx|z~yz{zz|xx}{zz{|}||~|{{yyxyxzzy|{|~}y{{{w{x|zxvwvtuiqjlibrrquvtmeqpuuxnpssuuplqquzcuvqrsrurplhprnlpwzuzwvvpmotostu|xy{{{wvuuuvwz{}{{zzzzwvxzy}~}{}z{{{{}|{~€~~{|~|~€~}€~~~€~€€€~~€}ƒ‚~~„€~€„ƒ‚ƒ‚ƒƒƒ„ƒƒ€€‚‚‚‚ƒ‚ƒ‚€~‚ƒ€‚~€ƒ~„ƒƒ„ƒ~‚ƒƒ„€ƒ„„„~ƒƒ…‚‡ƒ€€ƒ€~€ƒƒ~€‚ƒˆƒ…„‚„ƒ‚‚„ƒ€‚„ƒ€€€ƒ†‚„ƒ†…†ƒ‡ƒ…‚ƒ…ƒ…„ƒ†‚†ƒ„ƒƒƒ‚„„„ƒ‚~‚ƒ€€‚ƒ„ƒ‚ƒ‚€ƒƒ„ƒ‚ƒ€~„€€„„ƒ~ƒ‚„„ƒƒ€„„‡ƒ‡……ƒ€‚„„ƒƒ‚…„‚‚…„ƒƒ„€…„†…„„„ƒ‚‚‚„………„ƒƒ…„…„ƒ…ˆ„„„‚†‡„ƒƒ„„„†„……„‡„†ƒ„‚……„…„…‡ˆ…‡††ƒ‡…ˆ„„…ˆ†„††ˆ†………„††„„„…ƒ†‡…†…‡„ƒ„ƒ†„‚‚ƒƒ………„„‡‡†‡‡…………„…ˆ…………†‡†‡ˆ‡…†„„„††…………†††„„„‡††…‡†††‡†…ˆˆ†……‰Šˆ‡…ˆ‰‰†‰‡‹‰‰†‰„‰…Šˆˆˆ‰‰‰‡†‚ˆˆ‡†…………Œ‰‰ˆˆ|}xxz{|||}}|{zzyzz{zxy{z{|zz{{~{z€z{}zywx|}}x}~}~~{{yy}{{{|{}|~{{{}v{{|wwwvwtwuuur|sswwuwvvvssxrqqrvtswrtrrqrqtrrtvuxwxvtsssuuuxzz{vyxwwwvxz{{|w}{w{{{|{|||{}}}|{{{z}~{|~~|~|~}{{|{{{|{~€€~~€~|€€€€€ƒ}~‚}~ƒ„ƒ~}~~~€‚€~‚„‚~~‚‚ƒ‚‚ƒ‚„~€„€ƒ~‚ƒ€€ƒƒ„ƒ‚‚‚ƒ„‚€‚†€€ƒƒƒ‚ƒƒ€€ƒ€€„€€€ƒƒƒƒƒƒƒ…‚ƒƒ„‚‚‚ƒ€‚ƒƒ„„€†ƒ‚ƒƒ„ƒƒ€„ƒƒ…„‡…ƒƒ„ƒ„„„ƒ‚ƒƒ‚ƒ‚‚€ƒ‚‚„…‚ƒ„ƒƒ‚ƒƒ‚‚ƒ‚~†„ƒ‚ƒ„ƒ‚…„ƒ‚„ƒƒ„ƒ„…„…‡„ˆ€…„„„…‚ƒ„€€€ƒ……‚…†„ƒƒ…„†„€…ƒƒ‚ƒ„„€ƒƒƒ„‚†ƒƒ„‡‚…‚†‡††‚‚„…ˆ‡ˆ‡†ƒ……„…‰…ˆ…†…‡…‰„†„‰………††…„†‚†Š…†ƒ……‡‡‡†„…„ƒ‡ƒƒƒ‚…†‡……ƒ‡‡ˆ†‡„ˆˆˆˆˆ…„†Š„……†‡‰…ˆ†…ƒ‡‡†……ƒ‰‡ˆƒ„„…‡‡‡†‡Š…†ˆˆ‡ˆ‰‰…‡ˆˆˆ‹‡ˆ…‰†‰‡‹‰‰‡‹„‰‰Š‰‰‡‡ˆ‰‡ˆ‚‰ˆˆ…††‹ŒŒŒ‰‰‰z~xy|{{zw|yy{{zzxy{y{{y{zzz{{z{zz{|{z|}{|{x~€z{{{}~zyz{|zyyz{y{||{{}~{|wxv{{{wvwwwvxvututqsrvsvvvurnrtuwuwxrtusrtorvwwuwwuwrwrvuuwwuuuvvvwyouvuvzyz|yy{{x|z{{{{{{z{{|}}}z}~}}~|}~~~||}}{~}||~}}€€€€~€€€~‚}~}‚~€~ƒ|~‚ƒ€„€~„€€‚ƒ‚‚‚„ƒ‚‚‚‚ƒ€~€‚€€‚‚ƒ„‚ƒƒ‚‚ƒƒƒƒ„„€‚„~€„ƒ~„„ƒ€ƒ€‚ƒ‚€ƒ€~ƒ‚„ƒƒ‚‚ƒ‚ƒƒ„‚€‚€‚„„‡„‚…‚‚‚ƒƒ†ƒƒ~†…ƒ„‡‚€€‚ƒƒƒ‚ƒƒƒƒ„ƒ‚€ƒ„ƒ‚‚‚‚„„…ƒƒ‚‚ƒ€‚ƒ€€€‚‚ƒƒƒƒ†„ƒ„„ƒ„€ƒ„ƒ„‚€„…ƒ„…‚‚„ƒƒ„…„„†…‚ƒƒ‚„„‚„‡‚‡ƒ€ƒ€€„‚‚€‚ƒ…ƒ‚‚„…ˆ„ˆˆ…„„†„„‚„‚„„…ˆ…†‡……‡……††„„ƒƒ…††……ƒ„††††„…†‡ƒ‡†‚‡……ƒƒƒ…„‚„„„ƒƒ…‡†„‡…„ƒƒ†ˆ„…‡……………………ˆ…†…†‡†……„ƒ…„ƒ…†‡†…‰†‡†††‰‡………†††ˆŠ……ˆˆƒ‡‡‡‡ˆˆˆ†‰„ˆˆ‹…Œˆ‰Šˆˆˆ„ˆ‡ˆ‰…†…†ˆŠˆ‰‰~||{||z{~y|{{z{wzyyz|xwz{zzzy{|z}y|x~}~|€|{|€{~||{|||yz{{{|~~|~}}{}~{}tyzxwvuwxvvvuwutxzsvvwvvuvuvwvvvuuvwvuutrvttvyvxtwr}swuwxzyysuu{vwy|uuv{yxxz||{w{{{{{||~||{}}}}€~|}||}~€|~~}}~{||}}~€‚€~~~}||~~~€~~~~‚ƒƒ‚~~~~‚ƒƒƒƒ‚ƒ„ƒ„~ƒƒ„€~‚‚‚‚‚ƒƒ~€ƒ€€‚‚€€€‚…††„ƒ‚ƒ‚€ƒ„€‚„ƒƒ‚ƒ„„ƒ‚‚‚ƒƒ€€ƒƒ€‚ƒ€€„‚„‚ƒƒƒƒ†ƒƒƒ‚‚ƒƒ‚ƒ„„„ƒƒ‚‚‚ƒ„‚‚‚†ƒƒ…‡~„€€ƒƒƒ€€‚€‚ƒƒƒƒƒ„„ƒƒ‚ƒƒ†„„‚‚‚ƒƒƒƒƒ€€€…‚ƒ„„„…„†„ƒ„„ƒ€€„…‚ƒ„…„ƒ‚ƒ„ƒ……€…ƒƒƒ„…„„ƒ…‚„ƒƒƒ‚ƒƒ‡„ƒ‚€€‚ƒ…„‚…ˆ‚†„ƒˆ„„†…„„‡„………„„…ˆ„‰„†………ˆ‡ˆ†‡…‡‡‡‚…„†ƒ††††‡…†ƒˆ‡‰…†…ƒ„‡……„„‚‡€†‚‚……ˆƒ‡†……ˆƒƒƒ„„‰„…ƒ……‡ˆŠ„†ˆ‰…ˆ…„„‡„‰……„†ƒ††‡†‡……‡‡…Šˆˆ‡ˆˆ‰‡‡‡ˆ†‡†ˆ„‰‡ˆˆˆ‰‰‰Š†ˆˆŒ…‹‰‰‰ˆ†‡ˆ‰†ˆ‡…‡Š†…ˆ‰ˆŠ{yy{y}|y{{{xw|y{xzxyzzvw{{zz{zzz{{yxx{{~{z||{~z{~~{~~{~}{|||{~|}}~||||~|{~ww{xvyxx{{xxwxwuwutsrtqvqvsuswxwxtyxtvuuuu{wvuvvuxrsusuzvywvtuuwzzvwvvw{~|y{|{{{{{~|~||~~~€}{~~{~€€|~|}}€|}|||{{}ƒ}€‚}€}~€~~~~€ƒ~~€ƒ~€„€ƒ~€‚ƒƒ‚€ƒƒƒ„„€ƒƒ„‚€ƒ‚‚ƒ…‚ƒƒƒƒ~‚‚ƒƒƒ…€ƒ‚~‚‚ƒƒ€€€€ƒ€ƒƒ‚ƒ€ƒƒƒƒƒƒ‚„…„ƒƒ‚‚‚~ƒƒ‚‚ƒƒƒ‡‚ƒƒƒ‚„„ƒ‚ƒ„…„†ƒƒƒ€€ƒ†‚„‚€‚€ƒ‚…‚„ƒ‚‚€„„ƒƒ‚ƒƒƒƒ„†ƒ‚ƒƒ‚‚‚„‚€‚ƒƒƒƒƒ‚ƒƒ€€‚„ƒ„„‚ƒ€€„‚€ƒ…„„‚…‚ƒ‚ƒƒƒ„…†‡„€ƒ„„ƒ………„ƒƒ†ƒƒƒ€‚„‚‡„„„…‡„„„ƒ‡‚„ƒ…„„…„„†ˆ…‚„„ƒ„…„††…†ƒˆ‡…„ƒƒˆ……„‡‚ƒ…††††„‚ƒ†‡‡„„††ƒ„„„ƒƒƒƒ„‚…††„ƒ…„…ˆ‚„„„ˆ„…†‡††…†……ˆ‡……„…†……‡„……„„†ˆ†…†ˆˆˆ††‡ˆ‡‡‡…‡‡‡‡††‰ˆˆ‡†…‡‡ˆ†ˆ„ˆ‡ˆˆˆ†‡ˆˆˆˆ‰‡‰ˆ††…„†‡ˆ…‡†‰‹|}y}z}|{{|y|{}z~w{xzz{z||z{{{{|zz~€w{z~|}}|~y~}{~~€€€{}yy{~{€z~~~|||~zˆv}|{{{{|z}wzvwwyy{swryvvvvsvwwwwvwxyuuwzwutwwwvwvzrvtxvxwyvvuvvxw|vxw{yyy{v|{{z{{||||~~}~~}}~{}~~}}||||~}€‚~|}||}~{{€|~€~}~~‚~€€€€‚€~‚‚ƒ~~ƒƒƒƒ„„„ƒƒƒ‚ƒƒƒ„‚ƒ„€„„€€„‚ƒƒ„ƒƒ‚‚‚ƒ‡‚„„€€‚€ƒ„ƒƒ€€€€€‚‚‚€‚ƒƒƒ‚ƒƒƒ„„ƒ…ƒƒ‚ƒƒ„‚€‚ƒƒƒ‚‚ƒƒƒƒƒƒƒ‚ƒ„‚„†ƒ‚ƒ„‚‚‚€€ƒƒ‚‚ƒ€€‚‚„ƒ„„ƒ„„„„ƒ„€‚ƒƒ…‚†„‚€€€€ƒƒ€…„„ƒ‚ƒƒƒ…„ƒ€ƒƒƒ…„‚‚€€…„†ƒƒ„†„ƒ‚„„„ƒ†€€‚„„††…ƒ€ƒ…„‚…„„……‡…ƒ„ƒ€€ƒ‚‚€„„…„‡„ˆ„…„ƒ………ˆ„…‚„…‡…„„…‚„„……‡„…†ˆ…„…††††‡ƒ‰ˆ‚‡…†„“‚‡‚…††††ƒ†„…„„‚†…‡‚ƒ…‡…„…‰ƒ†ƒ„…†ˆ…†…‰ˆˆ‡‡……ƒ„†…„†…‰‰‰ŠŠ…†……‡†…†‡ˆŠ‹†‡‡†…ˆ†ˆˆ‡ˆˆ‡Š‰‰‡‹„ˆˆ‰ˆ‰ƒˆˆŠ‡‰†‡ˆ‰‡ˆˆ‡…†…†ƒ‡‡…‡‡‡‰‰‰}yz~||}y{x{|}|zw{xyvvvuwzz{{{{{||}xvy{}€y|~~yzzz}{|~~||yz}||y}y~}|||zxyyx}wz||{zwxuvvyuwsxvyvvvvsvwuwyurvxvuxxzvutrssxyxrtxsvvvutwwwwvxvvwxz{z|wxw|{yyvx{{{{|}|}‚‚}~}}€{}|||||~€„~}|||~||{||€}~|~€~~~{~€~~€ƒ~ƒ~€}‚ƒƒ„€~~‚€€~‚€€€}ƒƒƒ„‚‚~„„€€ƒƒƒ‚‚‚ƒƒ€‚‚ƒ‚‚‚ƒƒ€~ƒ„‚~ƒ‚€€‚‚ƒ€Š€‚„…„‚†‚‚~€‚ƒ„…‚ƒƒ~€ƒ‚ƒ‚ƒ‚„ƒƒƒ€‚ƒ„ƒƒƒ‚ƒ‚ƒ‚‚‚ƒ‚‚‚ƒ‚~‚‚‚„„„„ƒ„ƒ„„‚ƒ€€‚‚€€ƒ…‚ƒ€€ƒ„€ƒƒ€€€€‚‚‚‚€ƒ€„ƒ„„‡‚ƒ‚ƒ‚„ƒƒƒ„€€…ƒ€€„…ƒ‚€€…„ƒ„ƒƒƒƒƒ‚†„€z„‚ƒƒ„‰ƒ‚‚ƒƒ„…„ˆƒ‚„„‚„„…ƒ„…†‡„……„„††„ˆ„…‡ˆ„€††…‚‚€€‚‚‚†††ƒ„‡ƒ††„‚†ƒ†~ƒƒ‚ƒ‚€‚‡ƒ„†„ƒ„ƒƒ€‚††…„‚„ƒ……†„„…Š„…„…ˆ…††……†ˆ‡†††…………‡ˆˆ††‰ˆ‡‰†…†Šˆ‰††ˆˆ†ˆˆ‰…‰‰‰ˆˆ…‡ˆ‡…ˆ‰‰……†……‡ˆ‡ƒ‡……††{x{}zz}zz€z||}z{yzz{z||{{{zzyz{{z~{{zƒz{yx|~{|~|}}z{}|}{|{||||}{~}}~|}}}x†x}xwwzywxxw{vzwzzzyzxwuuwwwzvzutwvvuvxwuuvxuwwvutstrvwuvxwwvzvywywwwwvwwvwxz|ywz{~~}}}~€‚‚}~}}||{{{|{€~{{€‚~~|}|€€{||}||~|}|~}}~~ƒ~~~~‚ƒ€~€‚ƒ}‚€€€€€ƒ€}‚„ƒ„‚‚‚ƒ€‚„‚‚ƒƒ‚…‚ƒ‚‚ƒ‚‚ƒ€‚€€€€ƒƒƒƒ„ƒ‚€€€€~~ƒƒ‚‚ƒ†ƒƒ„ƒƒ~‚…„ƒƒ‚‚‚ƒƒ‚‚‚ƒ‚ƒ„ƒƒƒ†„„ƒƒ‚ƒƒ…€€ƒ‚‚‚‚ƒ€€€‚‚ƒ„„…ƒƒ€ƒ„€ƒ€€‚€‚‚‚‚ƒ‚ƒ‚€€~‚€€€€€ƒƒ‚ƒ€ƒ…€ƒ„„„…ƒƒƒ„‚…ƒ†…„‚„†…†€„‚‚ƒƒ…„ƒ„…‚„…„ƒƒƒƒ€~}|~€‚‚ƒ„„ƒ…„†ƒ…ƒ„‚ƒƒƒ‚„‡ˆˆ…†††………†‡ˆ‡ˆˆˆ€ƒ…‡†…ˆŠˆˆ…„‚…†…††‡‡ˆ†‡‡ˆ‚ˆ„…„‚‚ƒ…ƒƒ„…ƒ…†‡†…ƒ‚€‚„„…†„„……‚ƒ„ˆ‚††††‡†‰…†……†ˆˆ‰†……‡„‡‡‡‡‡‡‡ˆˆˆŠ†‡…‰ˆˆ†‡‰ˆ‰ˆ‡ˆˆ…ˆ†‹ˆ‰†„‡‰ˆ‰ˆ‰…ƒ„†……‡‹‰‰†……Š|{{{z{}zy~|{{~{y{ywz||w{vz}{zyyyz{{}z~{zy}y}~{}{y€~w~{z~}|{zz|~|y{|{}~}{{{wwx{yyw}yz{wvwxwvyu{vxvwwvwwwvvwvwwvuuuuvuxuxzurvwzvsuuvzuwuvuvvvwyyzwxvwwxux{}||z|{}}}}}€‚|~}|}~|}}||~||€€~~‚~~€~~|€~~}~||‚{~|{}}|~€}~~„ƒ‚‚ƒ€€ƒƒ€|~ƒ€‚~‚~ƒ€}ƒ„ƒƒ‚‚„„ƒƒ„ƒ‚€‚ƒ…ƒƒƒ‚‚€€„„„€„ƒƒ„ƒƒ„ƒ€ƒ~€‚ƒƒƒ~‚…ƒƒ‚ƒ„………~ƒ‚ƒƒƒƒ~‚‚‚ƒ‚ƒƒƒ„‚„ƒƒ‚‚†…†„~ƒ„ƒ‚ƒƒƒ‚‚€ƒ~„„„ƒ„„ƒ‚€ƒ‚„€‚‚€‚‚‚‚„„……„‚‚€€~„„„‚€€ƒ„ƒ€ƒ„ƒ„„„‚‚ƒ„„„„„‚ƒ†ƒ„„€€‚€‚ƒ~€ƒ‚…„ƒ„………‚„ƒƒ„~ƒ†‹†„ƒƒ~€ƒ€…„ƒ…ƒ‚‚‚„ƒƒ†…‚ƒ„…„…„…„„…‡…ƒ„‡ˆ€„†……†‡……‰„‡………†ƒ‡…„‚„‡‡‡†‚†„„‚„‚ƒ‡ƒƒ……‡……„……ƒ„ƒ„‚„„„…ƒ……†…„…„„„‡†††……„†ˆ†…‡‡†……ƒ†…†…„ˆˆ……†‡ˆ†……ˆ‡…†…ˆŠ‰ˆ†‡ˆ‰ˆˆ‡‡ˆ†‰„†‡‰‰„…„ƒ„†‡……‰‰‰ˆ…‡‹||{||||~x||}z~|y~{zz{{{{{{}~~{yx{{€y|z~}}}~}y~zx~}{}~€x}}}|~{{}~|{{~€x{{{v}wzywvwwww{yzyytwuwwwvvvzuzv|uvvvuwuuwzyywyyyxxwtsuvtvwvvvwy|yxvz{{{{z|{{{|||{}}~~~}}|||{{{{}}}}}~|{}~~}‚€~~||{|}~|„|}†{€}€~~~~~€„„„€€~~~~~€€€~ƒƒ€‚ƒ}ƒƒƒ‚ƒƒ‚ƒ‚€„‚‚…„„€‚„ƒ‚ƒ€ƒƒ‚‚~ƒ„ƒ‚‚ƒƒƒ‚~~ƒƒ‚ƒ€€ƒƒ…†‡……†…ƒƒ‚€‚ƒƒ‚€ƒƒƒƒ‚‚‚ƒƒ…‚†‚‚‡~„„„ƒƒ‚ƒƒ„ƒ„ƒ‚€ƒ‚ƒ‚ƒ‚‚‚ƒƒƒ€~‚ƒƒƒƒƒ‚…ƒ‚ƒƒ€€…„ƒ€ƒ…€„ƒ…„‚€‚€€€„„„€ƒƒƒ†‚…€ƒ†ƒ‚„………„ƒƒƒƒ‡„ƒƒ…‚‚ƒƒ‡ƒƒƒ‚‚„€†…‚ƒƒ…†ƒ†‚ƒ„ˆ‚„„‡…‡………‡†……†…„†‰„„ƒ†‡…‡†‰………ƒ…ƒ‡…„‚ƒ††…†…‡€„ˆ‚„„„„†…„„…‡„„‚„„„‚…„……‚…„…ƒƒ…‡„……‡††……„‡…‰†‡…†…‡…†ˆ‡††‡ˆ†††…ˆ‡‡‡‡‰ˆ‰†ˆ‡‡‡‡‡ˆˆ‰ˆ‡‡‰‰‡„†‡ˆˆˆˆ‰†„„†ˆ‰ƒŠ‹‹‡‡‡‹|yx~|~{z{}{}}|{|}{yyzz}|z{{~{zyzy{{zy{{y|{y{x{{x{}}}~|{|~€z||{|~|{}}}|{{||xyyxv{wwwvvywvx{wvtvvuvvwvwwwuvuvvuvuvwxwvuvvzyvzzyzyyxrwvwvytvwywxyxy|{xy~zyz}{{z{}{}}}}{~~~|~‚}||||{|{€~~~‚~|z{{{}{~~}~€~~€||~‚€€€„€€‚ƒ„„…ƒ‚„€~~€ƒƒ‚‚…‚ƒ‚‚‚‚‚‚‚„~‚ƒ‚ƒ„…„……ƒ‡…‚ƒ„‚€‚‚‚ƒ‚„†‡ƒ‚‚†‚ƒƒ„‚ƒƒ……‚ƒ„„ƒ„…†‡‡€ƒ†€€‚ƒ‚„‚ƒƒ…€„…ƒƒ‡‚ƒ‡…ƒ‚ƒ‡„„‚ƒ„‚‚~~ƒ€€ƒƒ€ƒ‚ƒƒ€„‚‚„„ƒ…ƒ…ƒƒƒ„„„…ƒ‚‚ƒƒ‚ƒƒ„‚€‚‚€~€ƒ„…ƒ‚€‚ƒ…€ƒ€€€‚ƒƒ‚……………‡ƒƒ€ƒƒƒƒƒƒ‚ƒ……‡…ƒ†„ƒ‡‡‡†„„„ˆ…ƒ…ƒƒ„ƒ„……„†„ƒ†„…„…†…‚„ƒ„„„†„……†…„…†„ƒƒ…††„ƒ‡„€„†‡|…„…„…„†„‡…„„„„ƒƒ„„…‚ƒƒ„‚‚……„„ƒ‡†„„†…„…ˆ‡‡†„†‡†…„…„…†ƒ†„ˆ…„‰ˆ†…ˆ…‚ˆ‡†‡ˆ‡ˆ‡……†……ˆˆ‰ˆ…ˆ‚ƒ……‰†„„„††ƒ„„ˆ„†………„†‰{~€{~|{}}€}}}z{{{||~{{||||{~~~{{yz|z{zzx}||w}~{{{{|€~{ƒ|‚~{~|~}||}||{}{}|}|{w|wzwwwyvzuvuuuyxwwvwwwzyywwwxvuxywxxxwwvwz}|}yxyuwuvwvvw{wwvy{z|yxxyyyz|x{{{}}~}}}}}}{{~}|~|}~|{€€~ƒ€€€ƒ~||||€‚€ƒƒ€€€~€€‚€€~€‚ƒ‚„ƒƒ„ƒ‚‚‚~ƒƒ„€„€‚‚„‚ƒ„‚ƒ…‚„„…„„‚‚ƒ†„„‚‚‚‚ƒƒƒ‚ƒ‚‚‚„„„…ƒƒ‚ƒ……„ƒƒ…ƒ€‚†‚ƒƒƒ„„ƒƒ…„‚„…„‚„„„„ƒ€„ƒ‚„†„ƒ‚ƒƒ‡~…€„€‚‡ƒƒƒƒ„‚‚ƒ€~ƒƒ‚€ƒ€€ƒƒ€‚ƒƒ‚„€‚ƒƒ…zƒ„ƒ„„„„ƒƒƒ„‚‚ƒƒ‚„„………„…€€€~„‚ƒ„€„‚€‚€‡€‡€‡‚‚„…„ƒ„†„‚„†„„„‡…„„‡„‡‡‡ƒ‡„ƒ‡„†‡…ƒ„„…€„…†„ƒ„……„†……„ƒ††ˆ‡‡„„„†„‡„…„ƒ†††††‡ˆ‡††„ƒ„ƒ†ˆ„„‚‚ƒ‡‡‡†…„†…†…„„†‡ˆƒ…„„ƒ„„…†ƒ…„„„…„……ƒ†……†††„†‡‰‡††…†††‡†ˆ„…†ˆ‡ˆ……‡…†‡…„…ˆ‡‡†ˆˆ‡†ˆ‡††‡††‰‰†ˆ‡†††‚‰ˆ‚†‡ˆ……„‰ŠŠ„…†Š‰Š|{||~~~~||{|||~||||||||y~{{}yzyy|{{x{{|}~zyzyx|~}{~|{|}{}}|~z{|}~|~~|{{|}wx{xx}wvxvwxuuvvwwvzzw{wwvwvuvyzywwwxwwyxzxxuxzyz}xwzwvvvxzvvvxwwvz{|xyz~}}y{{}}‚~~}{}}}}}~~}|~~~~}~~{~}ƒ€€€{~~}|}‚€€€€€}€€~€ƒ€€€€€~€„ƒƒ€ƒƒƒ„ƒ‚‚…ƒ‚‚ƒƒƒ€ƒ„…ƒƒ„ƒ„ƒ‚ƒˆƒƒƒ‚ƒ„‚ƒƒƒ‚„ƒƒ„…‡…„„ƒ‚„‚ƒ„ƒ‚ƒƒ†ƒƒƒ„ƒ††…„†……„‚‚ƒƒ„„ƒ‚‚‚€‚ƒƒƒƒ„ƒƒ„†††‚…ƒ†„„ƒ…„„…ƒƒƒ‚‡…ˆ‚ˆ‚ƒ‚ƒ€‚€ƒ‚ƒ„‚€‚ƒ‚€‚ƒƒ…„„„ƒƒ„„…ƒƒ‚‚ƒƒƒ„ƒƒƒ„„„ƒƒ„„ƒƒ„„„ƒ€‚ƒ‡…„„„ƒƒ€ƒ€„€†‚‡„„ƒ……†„ƒ…†‚‡…ƒ‚ƒ„„††„‚ƒ††„„ƒ‡„„‚ƒ„„…‚ƒƒ…ƒ…„‡†‡„†„………„‚€€„„€†„„…†‡‡‡†ƒ‚‚………†ƒ……††„‚‚ƒ„„†„†€„„„…†„„„†€ƒ„„ƒƒ„ƒ{„„ƒ„…„ƒ……„ƒ…„…„††ƒ„…………ˆ‡„„…………†‡…ƒˆ‰†‡†…‡ˆ‰ˆƒƒ†‡‰‡ˆˆ‰‡……†…‡†…‡‰‡ˆ‰‰†……†‰‰…†ˆ„††„……„‚†„†‰†ˆ|{~||{{|}{|}~|~}}}}|}{zyz{}|}|y{z{zz{||{zyyyzx{|}}|}{{|}€€}~}|~~|||}z|xxz{|vyxxwyxwvvvvwwvxw|tzwwxzzywwwyyzywvxwwvxyzzzwxxyy}xzzzyyzuzwwywywyz||~}}|{}y{}{}~~~€}{{{{|}€}{}|€|‚€|{~|‚~€~|€|{€€}€‚€€€€€~~€€~€€€€€‚ƒƒƒ‚ƒ‚„„€…ƒƒ„ƒƒƒ€€‚ƒ„~„ƒ„…„ƒ„…ƒ‚ƒƒ‚ƒƒƒ„†‚ƒƒ‚„„…„‚†…„…„ƒ‚„ƒƒƒƒ‚ƒ‚‚‚‚ƒ‚„„†{‚‚ƒƒ„†ƒƒ„………ƒƒƒƒƒ…ƒ‚ƒƒ„†ƒ„„‚„…„…†…†…„ƒƒƒƒ‚„‡‡‡…„„‚ƒ‚‚‚‚€ƒ‚„€€‚„ƒ€€ƒƒ„„„ƒƒƒƒ„ƒ€€„…‚ƒƒ„‚„ƒƒƒ†„€ƒ†ƒ‚ƒ€ƒ„…ƒ„‚„‚ƒ„†„„€††…€„„„ƒ‚‡ƒ‚„‡ƒ„ƒ……†ƒ…‡……‚ƒ„ƒ„…†…ƒ„„„„„ƒ„„ƒ…‚ƒ„ƒƒˆ…„…„‚„‡ƒƒ„……†„†…„††…†„ƒ‚‚……†„„„…„„„…„„ƒ………‚†ƒ†„„ƒƒ„„„„„†…„ƒ……„„†…†„…„„…‚‚…„…„…††…††‡…„†‰†‡ˆ‡…†…‡…††‡‡‡†‡‡††……‡…‰‡ˆ††ˆ„‚ƒ…ˆ†„„†‡‰ˆˆˆˆ……‡‰‰†…‰…†…„‚„„ˆ†…†‡‡‰|{{~}|{|{{|}|{}~~~}|z||{{||||}{{z{{{z||yyyxyxxzz{~|}x}~~|xxy~~xyzzz{|{{yywwwvyyxwwxwwvvuwxwwxwv{vyvwvyvzyxy{zwzzzzzzzzzyyzuywwyzzzzwzyzzzzwxzx|{wz}{|}}w~}}}~~}~}|}y~~~|}|}}|{|~€~|€|~~~}}}|‚€}}‚€€€~ƒ€~€…‚„~€€‚€ƒ€ƒƒƒƒƒƒ„…„„„€ƒ‚ƒƒƒƒ€ƒƒ„…ƒ„ƒ…„ƒƒ„„„ƒƒ„‡ƒ…‚ƒ……‚…†ˆ…………‚„ƒˆ‡ƒƒƒ‚ƒ‚†‚„ƒ††„‡‚‡‡‡„ƒƒ‚‚„„„‚„„ƒƒƒƒ†‚„„ƒ‚„ƒ„„„ƒ„……ƒƒ„„†‡ƒˆ†…‚ƒƒƒƒˆ‡ƒ„„‚„ƒ„ƒƒƒ„ƒ„ƒ„€‰„ƒƒ€‚„ƒƒ‚„„‚„ƒ„……„…„„„……„ƒ‚„ƒ‚…ƒ„„ƒƒ€‡ˆ‡…ƒ‚€‚‚‡…†ƒ‚………ƒ‚…ƒ‚€ˆ…„ƒ‡€‹„ƒƒƒ„‡†„„‡ƒ„ƒ…„…„„‚‚…‚ƒ„ƒ†„„ƒ‚ƒ……ˆ…ƒ€„„„…„ƒ„††……„…††€ƒ†ƒƒƒ„ƒ…†‚„‡„„ƒ‚…‡‡ˆ†……ƒ‚„ƒ…„…‚…„„ƒ„ƒ……„„€€ƒ€ƒ„‚‡„„ƒ„„„€ƒv…€„„„……„…‡‡††………………ƒ†…‡‡ˆˆƒ†…†‡†††ˆˆˆˆ…‡‡………‡ƒ‰Š‰ˆˆ‰††‡Š……„………†…ƒ„‡…ƒˆ‡‡ˆ||||}}}|~{|{€z}|}}{{|{|}{{|~{{{|zzz{yzxzxx{~x{z{}zyyzx~|y{yz}|||zy|~~z{}z}}|{{wwwww‚wxwzwxx|vvz{{|wvyyw|wzxzywvvzzzzw{zywzyywzxzzzyyxxxxwxwxwwwwvv||}x}xxy{{{||~~~|~||{}~}{{{{{||{~|€}‚~~}~~~~~~‚~~~‚€€~~~~€~~~~‚€€~‚ƒ„€„†ƒƒ„€€€ƒƒ„ƒ‚ƒƒƒƒ„„„ƒ„„……„ƒƒ„…„ƒƒ„„‚‚……„„„‚†ƒ†‚ƒƒ‚ƒ‚ƒ„‚ƒ‚„ƒ„ƒ‡ƒƒ„ƒƒ‚‚„„„…ƒƒƒƒ‚‚‚‚„ƒ„„„„ƒ‚„„…ƒƒ„„„ƒ…„ƒ†„„ƒ‚ƒ„ƒƒ…„„‚‚ƒ…„ƒ„„ƒ„ƒƒ„€€ƒ„€~~}…ƒ„‚€~‚…ƒ€€€€€‚…„…„…€ƒ„„„‚‚‚‚ƒ…€ƒƒƒ‚‚‚…ƒ…†…†„…ƒ‚ƒ‚‚‚‚ƒ„‚€‚„€‚~ƒ~‡ƒ†„„„ƒƒ„………ƒ‚‚„ƒƒƒ„ƒƒ„†„ƒ‚‚€„…ƒ€‡„„ƒ„„ƒ„†…„†…„†…„………„…„‚„……ƒ†„„……‡…„„„‡„ƒ„„…………‚ƒƒƒ…ƒ„„ˆ‡†‚…€…ƒ‚ƒ‚„‚…„ƒ„……„†‡„ƒ…„†„€†……‡……ƒ„…ˆ…ˆ††ˆ‡‡ˆ†…ˆ†‡ˆ††‡†‡‡ˆ†‡………ˆƒ‰ˆˆŠŠˆˆ‡†††…………†ˆ‡……†„…‡ˆˆˆ||}}}|||~{z{|{}|}€}~|{z{{{|}|{zyz}|{x|{z|€yzyz~y{z~~|wx~{||{|}}~~}{zy||{ywywvywwwuwwyxxw{zwxvv{wwvw|yzxwxzzyzzzz|xwxzxxyyzzyzxyyyyyxzxxyxw|{{{}}}|}{||~{{{{}~€~~{~}||{{{{z€}}~}~€€}€~|~~~~~~€~€„~~~~€€~…‚‚‚…€€‚‚„……„„„ƒ„„„„„…„„ƒƒƒƒƒ‡„ƒ‚„„…ƒ…ƒ‚ƒ………ƒ„„…ƒƒƒ‡ƒ„…„ƒˆ„‡‡ˆ‡‡‚„„…‚‡†…ƒƒƒƒƒ„…‡†‡ƒ‚…ƒ€„„†ƒ„ƒˆ€„ƒ„„†…‡…ƒ‚‚‚†‚„„„ƒƒƒ‡ƒ„„‡„…ƒ„ƒ„ƒ‡„ƒƒƒ‚€€€‚ƒƒ€ƒ€ƒƒ‚„ƒƒ„ƒ€€~€ƒ€€€…€€€€€ƒ„ƒ„‚ƒ€€~„„ƒ€‚€ƒ………„‚‚ƒ‚ƒ„…ƒ‚€‡‚‚ƒ„†ƒ†…„„ƒƒƒ…„‚‚„€ƒ€†ƒ‚ƒ„‡††…ƒ€ƒƒ…„ƒ„„‚‚‚ƒ„†‚„ƒƒ‚…„„ƒ€„€„ƒ„„†††„…†€…‡„€„…ƒƒ‚„‚„„†„…„……„ƒ‚‚ƒ‡ƒƒƒ„ƒ‚„ƒƒ„„„„ƒ…„ƒƒ‚€ƒƒ…ƒƒ…„„†…„†~…„„……„ƒ„ƒ†……†ƒ‡…„†„…„…ƒƒƒ†„„…„ƒ†ˆˆ„ˆ‰†„…ˆˆ‡ˆˆ„‡‡†……‰†ˆ‡‡†…†…†„ˆ†…‡…„…†„„„†„‡}||}|~|~~|}{~€ƒ{}}}||}~zzz{{{{{}{{|~~}~~{{{yyy}}~}{z}}~~{||}{}}{}yxx{{zwwvvwv|u{uywwuzywwxwxwwz{|xxyxxyyyz{{z{yyxxzy}yz{}{zy||{z}}~w}wwz|zyz{}{{||}|{z}~}||~~~}~{}||{|{{{z{{}}}}}~€€€~€€€€€|‚~~~}‚ƒ~~~‚ƒƒ‚€ƒƒƒ…€„„‚‚‚ƒ„†‚…„€‚‚‚ƒ„„„………„†……„„ƒ‚ƒ‡…„„…„†ƒ………„„‡„‡„…ƒ‚ƒƒƒ„‡„„„…ƒ‚ƒ…„„„„ƒ‡„ƒ…ƒƒ„ƒ„………‡‚‡„„ƒƒ„ƒƒ„„„„ƒƒƒ„ƒƒ„……ƒ…ƒ„„„„„„ƒ‚€…†‡…„‚‚‚€€ƒƒƒ€ƒ€~ƒƒƒ…ƒƒ€€€~}ƒƒ„„†€‚‚€ƒ…€†„ƒƒƒ„ƒ†„‚…„„„ƒ‚ƒ†„„‚ƒƒƒƒƒƒ…………‡…†††„ƒ‚‚‚„‚€ƒƒƒ„†…†……………‚‚…„ƒ„…„…„‚„‚ƒ„€€…‰‚„€€…„ƒ„ƒ…††‡‡…………„ƒƒ„€…‚‚ƒ†‡„ƒƒ†……‚‡…ƒƒ„‡‚…ƒƒ„‡‡ˆ‚…………„„ƒ‚ƒ………„……†††€………‚……†‚ƒ……†‡ƒ‡‚††††ˆ†ˆ…‡††ˆˆ„†…†…‡ˆ†‡ˆ‡‰ƒ‚…‡‡ƒƒ…†‡…ˆ‡‡‡ˆ††‡‡…‰…„††…‰„…ƒ‡ˆˆ†…ˆ}z}~~|{}~~~||€~{{{{||{||y{{y{{z{{~|yyz{{zyyx{}{{zz{z{yxxz||}}|{|z|~}~{}}}~~{{{ywy||{xvwwwxxvxz{{zz{xvwv|zzz{{|zwzz{{{{z{yxv{y~z{{}}{y|{zy|{{yyyz|z{xy}}{x{}|{|y|~}}|}}}}~}}z~}~z|z{{z{{{|}~~€€€‚‚~~€~|~~~|~€€~~ƒ€~€‚„ƒ€„„„‚‡„ƒ‚‚‚‚ƒƒƒ…„ƒƒƒ‚ƒ„…ƒƒƒƒƒ‡†ˆˆ„„ƒƒ„ƒ„ƒ…†…ƒ„…‚ƒ††…ƒ‡‡‡†…ƒ‡„„…„‡…„…†‡…ƒƒ…„„‚ƒ‚‚€ƒ„„ƒ……†ƒ„ƒ„…‡‚‡„…ƒ„ƒƒ„„ƒƒƒ…ƒƒƒˆ†„„…ƒ…ƒ„…ƒƒ‡ƒ„ƒ€‚ƒƒ‚‚ƒƒƒ€€‚ƒƒ‚‚ƒ€‚ƒ……ƒƒ„……ƒ‚‚ƒ„„„€‚‚€ƒ€€ƒƒ€„‚„…†„„ƒ„ƒ„……†ƒ…ƒƒƒ…€†„ƒ„……‚…‡…„ƒƒ‚ƒ€ƒ„ˆƒ„€ƒƒ„„„…‡†„„…„„„…„„ƒƒƒ„„„„ƒƒ„ƒ„ƒƒ‚†……ƒ…ƒ…‚ƒ„€…†…‚~‚†„ƒƒ‚„†ƒ‚„††ƒƒƒ‡„„„†ƒ‡…ƒƒ„ˆ€…„†‚ƒ‚……†‚~ƒƒ…‚~~‚‚ƒ†ƒ‡„€…ƒ…„„„„„…„ƒƒ„ˆ†…††…††‡‡ˆ‡‡††…‡‡‡‡†„ƒ„‚ƒ„……„„…‡……„ƒƒƒƒ††‰ˆ………„†‡„‰„……‚‡‡†……~}}}}~}}~~ƒƒ„€‚~~}€{|}{z€y{z|{}~|{yy{{|x||y{}{{|~~zxxz|zy||{|z|}|{|{{{{}||||||{|y{{|{{{|{{{{zzzzyz{}|{zzz{zzzyzyz{z{vyz{{}|}|}}}y{|}{|{{|}{{{yyxv}{||}}|{|}|{|}~}‚|}~}}{z{{|z{|}|}|€}|~€€ƒ€~€€€€„€€€‚€€…ƒ„…ƒƒƒ„ƒ‡……‚‚„……„ƒ„„ƒƒ„„…ƒ„ƒ„ƒ‡‡‰‡ˆ‡ˆ………ˆ…„‡ˆ‡…ƒ…„………†…†‡‡†„…‡…„„†ˆ…„„…‡ˆ†„„„„‡††ƒƒ€‚€€€€ƒ„ƒ‚…„…‚ˆ†…ƒ„……†ƒƒ…†…„‚ƒ‡‡‚ƒƒƒ„…„„„ƒƒƒ…„€ƒƒ„ƒ„„„‚ƒƒ„„„„„‚ƒƒ…ƒƒ„€€„ƒ{„‚ƒ„……………„ƒ€Š€…‚„ƒ………„ƒ„…„…„…‡†‡„‚„…ƒƒ€€ƒ……ƒ„†„‚„†‡‡ƒ„„„„…‚ƒ‚ƒ„ƒ‡ƒ‚…‡„ˆ‚‚‚…„‚„…„„„ƒƒ…ƒ„…ƒƒ„„ƒ‡„†…„…„……„„………‚‚„ƒ……ƒ„ƒ†„ƒ€…„…„„ƒ†…„„ƒ„„…ƒ…†‡†‡…„„†ƒ…†ƒƒ„„„……„„†ƒƒ€†…„‡„„€‡†€…ƒ„†…†ƒ………†…„‡‡†…„……†ˆ‡ˆ…†ˆˆ‡…„…‡ˆ‚ƒ……„†‡†„„„„………„„‡Š†ˆ„‰†‡‚‰„……†††…ƒ‡y}}||~~}ƒ~||~~~ƒ~~|z}{||xzy{{|~{{{|{y|}~~|zy||~~~|{|{y|~{{{|}~zxz{{{|||~|yzyy||y{{{{{{zz{|{z{|{{}y|{{zz~{zzz{zyzzxxz{{zz~}}}{zzz||{}|~}}z}{yyx|z|~~~||||}~}~~~€||~~{||~}~{|{~}}~~{|}ƒ~~~€~€€€~‚‚€€„‚€ƒ€‚€…€„‚„„€ƒ„ƒƒ„„„„…ƒƒ„ƒƒƒ„„…ƒ„ƒƒ‚ƒƒ…ƒƒ„‡‡‡…†…„‡ˆ…‡‡‡‡„†ˆ……‡‡‚‡‡…†ˆ‡‡‡‡‡ˆ†‡„ˆ†„†„…„„†‡ˆ‡†ƒ…„†‡†…„‚‡‡†‡‡†ˆ„„ƒ……ƒƒƒ‡„ˆ‚„„„……ƒ„ƒƒƒ‡‡‡…ƒƒ…ƒƒƒƒ„…ˆ†ƒƒƒƒ‚ƒƒƒ„ƒ…„„„„„„~……„ƒƒ‚„…‡………‚‚‚‚‚‚‚‚†‡†„‡‡‡ˆ‡††‚‚…………………„ƒ„‚ƒ‡…‚„†‡ƒƒƒ‡‡ƒ‚ƒ„‡ƒ†‚‡…„ƒ„„…„„ƒ…„ƒ‚„…‚ƒ„ƒ……„‚„„„…„„ƒ‚„„‡„„…ƒ…ƒ„…„„„„€„€‚†„…ƒ€…†„ƒ‚ƒ‚ƒƒ††„ƒ„…„…‡…††…„„†„„„ˆ…„„†ƒ‚ƒƒ……ƒ€ˆ‚‚‚„„†€†€ƒ†|…ƒ…‡…†„„ƒƒ†…†ˆ…ƒ„…ˆ‡‡…ƒ€‚…†……†…„„…‡ƒ………ƒ„†‰††ƒ‚„†ƒˆ„†‚‚‚€ƒ‚yx|~~~{}€|}{~~}€„ƒ„~|z{||}||yxy{{|}}{|{|yx}y{~zyz|}}||{{~}{z|~}}}|}z}}}|y||||||yw|{{{||{}|z{{zzxzzy{}y|{zz{|~|~{|zzz{x}{{{{|}}~}{{{|}||||{z{z}}z{x|z{~}|}|||}~~}}~|{|{||}}€~{~~}~|{}€~‰€€€€‚€ƒ€ƒ€„€ƒ…„†„‚…ƒ€‚………„„„„ƒ„ƒƒƒ…„„ƒ„ƒ…‚……†„„„…„„„…†‡‡‡‡‰†…ˆˆ‡…„ˆ‡‚…‡…‡ˆ…„‚†ƒˆˆ‡ƒ‰ˆ‡†‡ˆ‡ˆ‡‡ˆ‰„ƒ…‡…„…ˆ…ƒ†ˆ…ƒ…„‡…„„„„…‡ƒƒ‡„‰„…‡†ˆ†ƒ„„ƒƒ†„‡‡„ƒƒ„ƒƒƒƒƒ„ƒ…ƒ„ƒ…ƒ‚ƒƒƒƒƒ€„…ƒ„…~„€…ƒƒ‚ƒ„ƒƒ…ƒƒ†€„‡‚‚‚‡†‡„†††„ƒ„‡ƒ„†…„„‚ƒƒ„………ˆ…ƒ„…†ƒƒ‚‚…ƒ‡„ƒ‡ƒƒƒ„„ƒƒ„‡……„„„‚‚€‚„………„ƒ„„„„ƒƒ„……†……„…„…‡„…ƒ…‚ƒƒ‚ƒ‚†‚„…‡ƒ†„…„…††ˆ†‡ƒ„…ˆ„†…††‡„…„„„ˆ„ˆ„…‚„„…ƒƒƒ‚ƒ„……„………„……†…………ƒ‚‚ƒ†‡‰„††ƒ„„„…„…ƒ„†ƒ‚„„„‚ƒ…ˆ‚„………„…‰†„…„‰‡†…†……„‰…†………‡ˆ‚ˆ……‚‚‚ƒ†{xx}y|~~~}~y}{{|~{~€|}}yx~~zy~{|~|{{||zyƒx{|zwy~x|{~|~{~|€z€~z{|~€€y}~~|~z|}zy{|||{www{|{||wywz{y|z{{}{{{u{}|{{{z{{y|}{~}~{z}}|}~}zyz|}{z{}}||y{|y~~~~~}}|~~€~}ƒ|~}}~€~}}~~}~€‚„~~~~ƒ€ƒ€ƒ€„€„€„„ƒ…ƒ…‡„~‚€€€ƒy…‚…€†„„‡†„‚ƒ…„„„„ƒƒ†‰†„†‡„……„…ˆ‰‰ˆˆˆ……ˆ‡‡…„†ˆ‡ˆ‡†‡‰ˆ†…‡†‰‡ˆ„ˆˆˆˆ†…†‡‰……„„ƒ‡„ˆ„…ˆˆ†…„„ƒ„„ˆ†…†‡ˆˆ‡…ƒ…„‰‰†……‡†‡‡ƒ„†…ƒ‡…‡ƒ‡ˆˆƒˆˆˆ„„ƒ„„„ƒ„„ƒ„†…†ƒ„ƒ‰ˆ‰……ƒ……„…„ƒ‚ƒ„€ƒ‚‚€…‚‡„‡††„„„„†‡…†……„…‡‡‡‡‚‚…††„ƒˆƒ†ƒ‚ƒƒ……†„‡‚‡„„ƒ„ƒ„ˆ„…„„…‚‚ƒ…„ƒ‚„ƒ„‚……„…ƒ„ƒ„ƒ………†…ƒƒ„€€€‚ƒ…………ƒ„„€…„…‚€ƒ……„†€„„…‡…„…ƒƒ‚‚…†‡…„ƒ‚„„„„…ƒ„„…„„„„€€„ƒ„„„„……„†„„†ƒƒ„ƒ‚ƒ‚†„…‡…„……†‡…„‡…ƒ‚„‚ƒ„…†„ƒ„ƒƒƒ‚…ƒƒ„ƒ|ˆ„……„„ˆƒ†‚ƒ‚…„‰……†……ˆƒˆ†|…„„‚ƒ|~||{||}}}z}|{|}||{€xw}~~}{zyz{z{||{{yzy{zz~}|z|}~~{{~€{zzy€}||{~€||}}~~}zx|{{z||{{|{{zzwywz|{yxxz}|zzwzz|z{z~{{x||{zzz{{|{~}}}~y}}}}|||}}}~|{}y}{}~}}}}~~}‚ƒ~~||||{|~}ƒ||~~}€‚‚‚ƒ……†}‚€€€ƒ€€„}~~}}€€„€€€€€ƒ‚‚ƒ……†„……„†„„…„†„„ƒ†€„€€„†††…„„‡‡ˆ‡„ˆˆˆ‰‡†ˆŠ…ˆ…„†…„ˆˆˆ††„‰ˆ…‡ˆˆ„ˆˆˆ…„ƒ„…„‡†…‡ƒ‡‡…„‡ˆ‡„„…„…ˆ‡†ˆ‡ˆ†…„„„…ˆ‡‡…‡ˆ†„„…„ˆ……‡ƒ„……ƒ‡„‚…„„ƒƒƒƒ‚„†…‡………„ƒ€ƒ†‡ƒƒ„ƒ€„‡……ƒ„„„„„ƒƒ„‚‡ƒ‡‡…„…‡…ˆ„„…†……†††‡ƒ„…„‡ƒ„„†…ƒ„„…„……ƒƒ……„„„„††„…ƒƒ„„………€€ƒ„„……††…„„„ƒƒ…†††‡‚…„…„„ƒ†€„…„†…‡€……†‚‚‚„…ƒ‡†„…ƒƒ„…„„„‚ƒƒ„†„„ƒƒƒ„„„„„„†„„…ƒ……ƒ„„„„„„„„†………‚ƒ†„„„†…‡ƒ€………†………†…†…ˆˆƒƒ…ƒˆ€‡ˆˆ†…„‚‡„ƒ„…ƒ…ƒ„†ˆ‡ˆƒ„……†…„ƒ‚‡…‰…†……ˆŠ‡†………„„„‚‚ƒ|}~~}}{}}~|||{{{{y~{{}|~zw|}zz}z{{z|zz||{|yzy{yy~y|{{}{{~{{€zzz{}~€|}{{~~~}{||{{zxwy{{|{z{{zzzzwxwy{y€}|z{z{{vzy{xz|zyzyyzz~z{{z|{}}}|{||}}{~}{}{{|}}}}}}}|€}~}‚|~}|}~}€~}~}~‚‚ƒ‡ƒƒ€~ƒ‚‚‚…„ƒ‚„€…ƒ„„~€€ƒƒƒ€†ƒ€€ƒ€„„†„{…†ƒ„„…„„…‡…€„………„ƒ„…‡‰‡„„„ƒ††ˆ‰ˆ„ˆ…ˆ‡‡‡……„†……ˆ†…ˆˆ††……†ˆ„„†ˆ†ˆ‰‰…ˆ„†„„†ˆƒ‡ƒ‡…………‡‡ƒ„…†„ˆˆ…†‡………‡‡‰„‹‰ˆ…‰‡ˆ‡ˆˆ………‡ˆ†…„ƒ„…„ƒƒ…„Šˆ‰‡„ƒƒ„ƒƒ„††„„„„…†‰ƒ„…†€„……ƒƒƒ„„…„‡ƒ…‡‡‡†‡…††ƒ‡„…‡†„…†……„†‡‡ƒ„„„ƒ„ƒ……†„…‡ƒ‡„……†„ƒƒ„…ˆ…„„‡„„‚ƒ………ƒƒƒ‚„………‡…„‚„„„…†‡††‚ƒ€…„…„„€€ƒ€„……†„……‚ƒƒ…ƒƒ†€„…„‚€„„…„„„„„„…„…„„…†„‡„……„…ƒ„„…†…†„„…€……„‚…‡„„…‡…„…ƒƒ„…ˆˆ………†„‚„ˆ†……„„„ƒ……ƒ…ƒ…„„…†€‚ƒ††………ƒƒ‡……†‡„„€€€€…€…†‚‚€…‚~}~~~|{||}|}{z{|z€y~{|y}x|{{{}}}z{z{{|{z{|yy{y{|w~y}{{z{~}z{{~~{|}}}}}{|~|{{}||w{{|z{|zz|z{yy{z|w~}{{zz{{z|{{{{{zyzyzz{~{yy{z|~|}{}}}}}}€~|{}}|~~}|||}}{|}~~~~€~ƒ}|~|}~~~~}~~~~‚ƒ€}‚‚…‚ƒ„„‚ƒ‚„‚€€€‚€ƒƒƒ€}ƒ‚„„‡‡„‚ƒ€„……ƒ…„…„……„…„„………„€€„„„‚†…‡ˆˆ„‡ƒˆ…‡…„……ˆˆˆˆ‰ˆˆˆ†„ˆ‡……………‡‡ˆˆ‰†……„…†‡ˆ„„…„…„……†ˆ‡……††‡ˆ‡†††„„ˆˆˆ„‡‹‡…‡‡ˆ‰‰‰„…„‰ˆˆ„…‡‰…ˆ„ˆ………ˆˆ‰‡†„ƒ„……„ƒ„„‚………ˆ‡†ƒ„ƒ‚„……„…‚„„ƒ„„ƒƒ„ƒ†‡†‡………„„‡‡‡……„…‡…„‚‡…„„…‡……†…„‚†‚„„†ƒƒ…‡‡‡‡‡…†ˆ‡…„‡‡…†…†…††…‚ƒ„‡„ƒ………ƒƒƒ†‡…‡‡†‚ˆ…………„„…†‚‚„…„…ƒƒƒ†††„‡…………€ƒ„ƒ‚ƒ‚‡„„„„ƒ‚„…„…„„…‡„…€„…„ƒ…ƒ„…………†‡††ƒ…ƒƒ…‡……„…„‡…†…„……„……‡††…‡…†…ƒ„„‡†…„‡„…‡……„…ƒƒƒ‡……‚‚„„„…„ƒ…‡…Šˆˆƒ†‚€…………ˆ‚€„…„‚~€~z}~{|~||~||||xxxyzzxy~|z~|{z{{{zz}{zzzzy{y{{z~{xz|{~z{|~{~~zz{~|~|z~}}{}|||x{||x||z}~}|yz|{z~}{zz{}z|||z|{{y|{{z{{~}~zyz~}~~~}€~}}~~}~{~€~}|{}|~}~|}}ƒ}}~~~~}~~€€~~€~‚ƒ‚ƒ|‚ƒƒ‚ƒƒƒ„†ƒƒ„„ƒƒ…„‡ƒ€€€‚‚€„ƒ‚‚…„†„†€…ƒ„‚„„‡†…ƒ„…ˆ‡…†‡ƒ†„…‚†……€†‡††‡„ˆ†ˆ†‡…ˆ„†ˆ‡‡‰‰ˆˆˆ‡…ˆ‡„ˆ„Š…Œ‹Š‹…‰……†ˆ‰‰…††…‡‰†‰…‰ˆˆ„††ˆ‡ˆ‡ˆ††…‰†‡ˆˆˆˆˆˆˆ‰ˆˆˆ„„…‡ˆˆ…†‰‡ƒ„…………„ˆ‰‡ˆ‡„ƒ…„…„„††……„„„„„†††€……†††‚‡‡„ƒˆ„„„…†‡‡‡††…†„„†ƒ…†………‡‡ˆˆ‡†ƒƒˆ„„‡…‡„†„ƒ€…‚‚†………‡†††‡†ˆ‡‡‡…„„…„†‚ƒƒ„………†‡„††„‡…‡‡‡†‡†††„†…€‡†„ƒ‡ƒ…„„„‡‡…„†…„„€„„‚…‚………„„ƒ…‚……ƒ…„„…„…„„„ƒ„…„‚„„„‚‡†„ƒ„ƒ‚‚„ƒ„„„ƒ…†…ƒ…„…†„ƒ‡†††„…†††…†„„‚…†ƒ‚……†…†…‚ƒ„‚„…………‚…………ƒ‚…††Š‰†‚………‚„…‡‚€„†…‚~~yzzyy||}z||{{€z|xƒz{}v|{|{z{yz{{|{z{x{y{y~z|{€z|zz~}}y~{z}~y|||~~||~|z|{{{{{}~||}|~w{{x}}{|{{{{{|y}z{|{{{{y{{z{{~z||{}~}{yz}}~~~}}}}~~~~~~}~~z~~}~~~~~}||~}ƒ€~~€}‚€‚€ƒ‚‚~}}{}~~‚‚ƒ|‚}‚ƒƒƒ‚‚‡„„„ƒƒ†ƒ„ƒ†ƒ‚„„ƒ„€ƒ€‚~…‚ƒƒ…„†‚„ƒ‚………‡…………†„††‡‚‚‚ƒ…€€€……‚†„„„„†‡…‡†‡‰ˆˆˆ†„†‡ˆˆˆ‡„„„……Œ‰‰†…†„…‡‰‰………………ˆ‡……………„…„†ˆŠˆˆ†…‡…„†‡ˆˆˆˆ‹‡ˆ‰…‰ˆ‡ˆˆˆˆˆ‡…ˆˆ‡ˆ†…„‡…ˆˆƒ†………„€…„„……„„„……‡ˆ†……ƒ„…„…‡ƒ…‡†……†…†„…††…‡‡‡…†„„†…†…†‡‡……†…‡†ˆˆ†…‡„ƒ‡ƒ††††…‡ˆˆ†‡…„††…‡‡ˆ‡†‡…‡‰††††…„ƒ‡„ƒ†ƒ„„…†„ƒ„†‡‡…ˆƒƒ†…‡‡††ƒ‚ƒ‡††…†ƒ„†‡……„…‡†„ƒ„„…„‚ƒ…‚……„†‚„ƒ‚„„„………„„„„……„„„††ƒƒ…‡……†††……€ƒ„„„……ƒ……†††‡………ˆ‡‡‡†…‡†…„‡…„ˆ‚ƒ€‡…ƒ……„…ƒ„‚ƒ„…„ƒƒƒ…†…†…„††‡Š‰‡€ƒ…„„„…†…†„†ƒ€ƒ€|y{€|z|||~|{{{|{{€zz}€wz~|}yyz||{yyxxx{yyy{x{|{|zz~€|}}}}~{~~}{y|}|}}|x~{||}|}y|{{xyyx}z|z}€||{z{z{z{{{z||{z{z{{~{{|z|~|}~~z||~|{~~~€€~~}}}~~~}}~~~~~}|~~ƒ€€ƒ}}|~~}‚|}}}~‚~ƒƒƒ€ƒ‚ƒƒ„ƒ‚ƒ‚€€ƒ‚ƒ„…ƒ„ƒ„‚€…~ƒ‚„‚„„‡„‚„ƒƒ„†€„ƒƒ„†„„…‡†‡„ˆ‡Œˆ‡……ƒŽ„…„…ƒ…„ƒ‰……„†…††ˆ†‰‡…‡‰†…‡†ˆˆ‰‡‡ˆ‹ˆ‰‰‰ƒŒ‡……ˆˆ‹„‰ƒˆˆ‰‡†…†‡‡ƒ‡……‡ˆˆ‰†‡„………†ˆ‡ˆ‰‰‰ˆˆ‰ˆ‰ˆˆ‡ˆˆ…‰ˆ…‡ˆˆ……„…„……†††„‡‡†‚ƒ………†…ˆ„†‡ˆ††„…„…………†‡‰‡‡‡†…†ˆˆˆ‡…‰ˆˆ…‡ˆ…„‡‡†„‡†ˆ‡‡‡ˆ‡ˆˆ‡†‡‰ƒƒˆƒ„…‡‡‡†…„‡……ƒ‡…ƒˆ…ˆ††‰†ˆ†ˆ††„…„………„……„ƒ……‰†„†‡†‡ˆ†ƒ…ˆ†„€„†ƒ†‡ˆˆ…ƒ†‚€†ƒ‚ƒƒ„ƒ…††„‡„‡„ƒ‚„ƒƒ„„„…ƒƒ‚ƒ……………„„‚‚‚ƒ„„………ƒ„ƒ…†ˆ…†……„………€…„„ƒ…ƒ††…………†ƒƒƒ†‡ˆ†„†‡ƒƒ†…„€‡…„…„……‚‚„‚~„ƒ„‡†…„‚…†‡‡‰Š‡€ƒƒ…ˆ…‚‚ƒ‡†~~€~z|{|}|{~|y}€€y{|~~~€}}|{|}|{z{yyyzzyyyz|zxzy|€€~zy{}}|}€|{|{}}{~||~||~|x}{{wyww{y|{|€~}{|{{{y{|{|||zz{{{z{{|{~|~~}~{z|~~~~~€~~~~||{|z|‚}}~|~~~}~€~~€€~€}}~~~‚€‚‚~~‚ƒ‚~~ƒƒ„€‚ƒ…ƒƒ€ƒ†„„|‚€ƒƒƒ€„ƒ„€‚€‚„‚‚„„„ƒ„„…„††……‡‰‡††…„„…„†…„„‚…„†…‚ƒ††…ˆˆ„ˆ…†ˆˆ‰‡††‡††††ˆˆˆƒˆ‰‰…‰‰ˆ‡‡ˆ‰……„‡ˆ‰……‡„ˆ‡ƒ‡…‡‰‰‡‡†ˆˆ„„†ˆ‡…ˆˆ‰†‰ˆˆŠ‡„ˆ‹ˆˆ…ˆˆˆˆˆˆ‡…„‡}‡„…ˆ†………‡„ƒ„„‡„„„……†‡„„…„……†ƒ„……ƒ…ˆ‰‡…†‡†…‡‡‰‡…ƒˆƒ……†ˆ…‡…††‡‡†…„†‡……†‡‡‡………‡‡‚‡……‡‡‡ƒƒ‡ˆƒˆ‡ˆ††……ˆˆ‡†‡ˆ………„…„…‡‡‰‰‡‡†…††‡ˆ†„‚…‡…„„„„…„†‡ˆ„…ƒ…†……†‚ƒƒ……„„„„„„ƒƒ€€ƒƒƒ„…‚‚……ƒ„…ƒ†‡‡…ƒ…„„„…ƒ„ƒ†……„…††„……†…ƒ€„……„‚„ƒ„„„‡ˆƒ€………‡ƒƒƒ„…‡…„‡…………„‚…„„„……††„„ƒ‚†…Š‰ŠŒ‡‡Œ„€„……„ƒ……‡†…~}|}~|zyyxy||}~|{{{}}t~~}|zzx{zyy}zzzxy||yyzy~z{{|zzy|y||}~~}zyx~~yz{}~~~yz|{zz|{{z{{||~}}|{{|y{|{||||{|}{zyyy{z{{{~~}~{|€~€~€~~~~|}}}}}}~||~}~}€}~‚€€}€ƒ…€~|~€}~‚‚€~€„ƒƒƒ„ƒƒ€€€€‚€…………ƒ‚‚ƒ‚ƒ…ƒ}„€†„„€‡„…€ƒƒ‚ƒ…„„…†…„‚…††………‡‡ˆˆ‰‡‡†…„†…††††††…„…††††…‰ˆˆˆˆˆ†‡‰†Š‰‹ˆ‰‰ˆ†‰‰ŒŠ‰ˆˆˆ‰‹‰‰……ˆˆ‰‰‹‡„ˆ‰„ˆ„ˆ†…‡ˆ†ˆˆˆ‡ˆˆ†„ˆˆˆ†‰ˆŽŠˆˆˆˆˆ‡†ˆ‰†ˆ‰ˆˆˆ„ˆ‰‡…††‡ˆˆ„‡…„„‚„†‡‡„‰ˆ†…‡…‡…‡†„…„…‡ˆˆˆˆ…ˆ†‡‡‡‡‰‡…‰††ˆ‡††††„‹Š‹‰ˆ„‡††‡‡„†ˆ„……†‡‡‡„‡‡‡………†‡„‚„‡……‡„‡‡……ˆ†…†„…††††‡‰‰†‰‡†‡†‡‰†…ƒ‡‡…„†„ƒ„ƒ…ˆ…ƒ‚„‚„‚†‡‡‚„……„‚€€‚‚ƒ€ƒ€„…‚„„…ƒ„ƒƒ‚„‡†„‚„„„„………ƒ€††……„~„„†ƒ………††‚††ƒ„‡…‚‚€……‚ƒƒ…ƒƒ„‚„€…ƒ…„‚„ƒ€€ƒ……„„„ƒ‚……‡‡Ž†„‚‚ƒ€‚„††‡††~€z~}~zzyyz~}~yy{}~z{|~~}}~~~||}{x{|y||||zx{|zzy~€~}~~|~}}||~~}~}~~€}{{|~}|yy}||}|{{|{{{|z|y||{|z}z{~~|{yz{{{zz{~~{~|{{|~€€€~~}~||€€€~~|}}~€}}~~~~~ƒ~}~}~~ƒ„ƒ„‚‚€ƒƒƒ‚ƒƒƒ„…€€ƒ„€‚‚‚€~…€‚„ƒ…ƒƒ‚‚‚‚€„‚ƒ†„„„…„………„†„„„……‡†‡„…‚„„„†„„……„…†„‰†ˆ‡††…………ˆˆ‰ˆ‰‰ˆƒˆŠ‹…‹‹‹Š‰Šˆˆˆˆ†ˆ‰ˆˆ†‡‡‡‡†ˆ…†…††ˆ…†‡‡ˆ‡ˆˆ„‡ˆˆ††ˆˆ†ˆŠ‡†ˆ‡‡‰ˆ‡ˆ„‡ˆˆ„ˆƒˆˆ…ˆ‡ˆˆ…‡†„†„„†‰‡†‡ƒ‡††…‡††‡‡‡†‡‡†ˆˆ††‡††‡…‡‡‡…†‡†„‡†ƒ‡†…†‡†„„„ƒ‡‡…‡‡„…ˆ„††„‡‡‡ƒ‡‡†‡‡‡…ˆˆˆ‡†„‡…„ˆ…„…„…†‡……„………†……‡‡‰ˆ‡…†‡‡†††‡…†…‡†…„„…‚‚„†„ƒ…‚‚†„‚‚†…‡…„„…ƒ„„„„„„ƒƒƒ…„ƒƒƒƒƒ…ˆƒƒ‚ƒ‚„ƒ‡„„„ƒ‚†……ƒ„„‚‚ƒ„…„†„……†„‚€…†„…†‚‚€ƒ„…‹‚ƒ‚†…†ƒ‚ƒ…€„€€„„„‡…„‚„†„ƒƒ†ˆ‰ˆ‹†‡†††„ƒ‚ƒ†ˆ…ƒƒ‡{~xwyxy|y{ww||€}|~~~~}z{||y{~~}~||{|}}yƒz€}z|||{{}z}~~|yy{}z{|~|||~€{|||~||{}}yyz{yz{||}||}}~|{||~{|~{{|||}~|y|{z{|{€}|zzz|~~}€~€}~~~~~~|~~}{~~}ƒ}‚ƒ~~}~|ƒ„ƒƒƒ‚‚ƒ‚‚ƒ€„€ƒ‚ƒ‚ƒ€„„„~„‚…€ˆ†„€€€†‚€ƒ„„„…„‡„‚ƒ‚ƒ„ƒ‚…ƒƒ…„‡‡‡‡‡…„††‹†…„…„…„‰‡ˆ†……‡†‡…‡†…„‰…†‡†„…†……‡‡‰ˆ‹ˆ‹‰‰ˆŒ‡Œ‰‰Š‹‰‹ˆ‹‹Œ‹Œˆ‹‰‡…‰‡‹‡ˆƒ‰‰ˆ†ˆ„І‰‡ˆ‡‰…†…ˆˆ†ˆ‡ˆˆ‰‰††‡‡ˆ‰ˆ†††ˆ‰‹„ˆˆ†‡…‡ˆ…‰‡†…‰„‡ˆ‡†ˆ‡ˆ‡ˆ…‡…ˆ†‰‡Š…†‡‰ˆ††……‡††……‡„‚‚ƒ‚ƒ…ƒˆ‡‡‡‡‡‡†‡‡ˆ‡ˆ‡ˆ„…†„‡ˆ†ˆ‡‡ƒ‡ˆ‡ˆˆ†‰Šˆˆˆ†„…‡‚‰‡ˆ…„„…„„………„…‡†ˆˆ‰ŒŒ†‰‡‹Š……†††‡…‰……‡‚ƒ„ˆ…†ˆ„‡‚„…ƒƒ„„„ƒƒ†„„ƒƒ„…„ƒƒ„„ƒƒ‚‚„„„„ƒƒ„…„………„ƒ……‚„…„„……„‚…„„……„ƒ…‡„……ƒ†††„ƒƒƒƒ„ƒ€‚€„€…‚„……†„…†ƒƒƒ„‡‡†ŠŒˆ‹Ž†……ƒ„ƒ‚‚‚„€ƒ‡‡}~yy|y}xzyzz}~~{z~~{||}|}€‚{{y~|zyz~‚~||}|‹y~y€€~|}|z}z||}~~~~|||}~}|~|€|yx{~~wz}~}€|||||~|||y}yz€{{||z|{{z{{|||x{z{~}}~}~~{|€~€|ƒ|||}}}~~~~~}‚‚‚€~~ƒ~~~‚‚‚‚‚€€ƒ€‚ƒ‚‚~ƒ€€‚€ƒƒ‚‚€‚‚„…€ƒ€†ƒ„„‚…„†‡‡€‡ƒ†ƒƒƒ‚ƒ†……‡ˆ‡†‡†„…‰†…‡‰‹…‡ˆ„„†………†„„…‡………„…ˆ‡ˆ‡ˆ…………ˆ‰ˆˆ‰Šˆ‰‰†…„‰‹‰ˆˆ‡‹………‹ˆ‰…ˆ…‰‰ˆˆ‡„ˆˆˆ‰ˆ…„…ˆ‡ˆˆˆ†„ˆˆˆ‡…ˆŠ‡†‰‰…†‡‰ˆ‰†ˆˆˆˆˆ‰…ˆˆ…‡„‡‡‡„…†…‡„ˆˆ†…ˆ‰…‡……†„ˆ†‰‡‡„†‡†ˆˆ‰ˆˆ‡††‡†ˆ…ƒƒ…„ˆ††‡„…‡‡‡…‡‡‡‡‚‡‡†…„„…†‡ˆˆƒ‡„†‡‡‡…„‡†‰‡††…††…ˆ†……„‰„ˆ‚…††……„††………„…‰ˆ‰‹„‡†‹†‰‰Š†‡…„ƒ…††‡…„„‡ƒˆƒƒ‚…ƒ‚ƒ…„„€„…„€‹„„„„„„„…ƒ‚‚„…………„„„ƒ†…ˆ‚‚ƒ………„„…‡„ƒ……………„ƒ……„€€‚†…„€€…„…„„ƒ…„~‚‚‚„„†„ˆ…†ƒƒ……†‰‡‡‹ŠŒŠ„‚„€‡††…†‚ƒ€†ˆˆ~}z~}x}xwyxw~z|yz€y|{|yx|{|~{}}~{}|z€|}€{~€}€z~zyz}{}|{}{|}x{yxyzxyz{|{~}|~~||zxzx~yz}|~}||z€|}{~~zz|z|{z{|{{{~|z{{{||{zz{}||z}~}~|}}}}~€~|{||€{}€ƒ|~~~„}€~€ƒƒƒ€€~~~‚‚ƒ„†€„ƒ‚ƒƒƒ„‚ƒƒ€„~‚ƒƒƒƒ…ƒƒ„ˆ‡ˆ‰…„‚ƒ‡„„„ƒƒ…„‚€ƒ‡‡‰ˆ‡†…‡‰ˆ†„‰†……‡…‡ˆˆˆ‡…‡……‡…„„…‡…ˆ‚…ˆƒ„†ˆ†………ˆˆ‰ˆˆˆ‰ˆ‰ˆ‰ˆ‹‹‰‰ˆ†‹†ŠŠ‰‰‹…‰…‰ˆŠˆˆ„‹ˆ‰ˆˆ‰ˆ‰ˆ‡†††…†‰‹ˆ‰ˆˆŠˆ‡ˆˆ‡ˆ‡ˆˆ‡†††‡ˆˆˆ‡‰‰…„……††……‰…†‡‰‡…‡ˆ‡†‡‰„…‡‰ˆˆˆ‡ˆŠ‡‡†††‡ˆˆ‡……‡†‡‡‡‡ˆˆ‡‡‡…„†ˆ‡†…„‡‡†‡‡‡†……‡…†ˆˆ„ˆ‚‡„…†……‡‡ˆ‡…„…………‰„„…„…„ƒ‚…ƒ„†…„…†…‰ˆ‡‡‰ˆŠ‰„‡‡…†ˆ‰Š‡††…†…†…‡‡†ƒ„ƒƒ…€€ƒ€„„ƒƒ~ƒ„ƒƒ„„„„……€‚‚„„ƒƒ„‚ƒ……†‚ƒƒƒƒ‚‚„…„ƒ…€†……„„‚ƒƒ†‡…„„€…„‚‚€€€„„„…„…„‚„………„„„‡‰‰ˆˆ‰ŠŒŠŠ†€€ƒ†€ƒ‚‚„€‡|}yz|{~~{}€~~z|~~~|z|{||||~y‚{~~y{€€}|{€|~~z|yz{}}|}€|~zy€€€~~}}€|y{}~~}~}}x}||||{}y~|z|~~y}z|||z{z{z~~~|}z}|}zz}~|~~~}|{~{~~€~€}|}}‚~€}~€~~~ƒ~„€}‚~€‚ƒz„‚‚ƒƒƒ€ƒƒƒ‚‚€„ƒ‚€‚‚ƒ„„‚‚…„„ƒƒ€ƒƒƒ„‚ƒ€€€€‚‡ƒƒ„……‡†……††…††‰……†…‡‡‰‡…†‡…‡„‡†‡‡‡……ˆƒ„‰ˆ‰ˆˆ†„…†‡††‰‡‡‡ˆˆ‰ˆˆ‹‡ˆ‰‹…‰†††……„……„ˆˆ†ˆˆ…‹ˆˆŠˆ‰ˆˆˆˆˆ…†‰‡†„„‡ˆ‰ŒˆŠ‡ˆˆ‡‡ˆ‡ˆ†‹‹‰††ˆ‹ˆˆ††…„„‡‡‡‰„†‰‡…†‰‡ƒ‡††……†‡‡†…†ˆˆ‡†‡ˆ‡†‡ˆˆ†‡‡ˆˆ…‡†‡‡‡ƒ‡ˆ…‡‡‡‡Š„ˆ††ˆƒ†ˆ†‡††‡‰‡‡ˆƒƒ„„†…„…†„„„ˆ‰ˆ……„„…†…„ƒ‡†…„„……††„„…„†„‡‡‡ˆƒŠŠŠ†ˆˆ‰‡ˆ…‡‡‰†‡…„……ƒ‚ƒ‡‚‡‡‡‡†„…€ƒ€ƒ€ƒ€‰‚„„„„„„€ƒƒ…„„ƒ„„‚‚‡‚€„……‡……„ƒ†………†……„…†‚…„„‚„„ƒƒ…€‚ƒ‡€‚‚‚€‡…„„…„…„‚„ƒ‚„„†…ˆ„…‡‡‡‰‰‹ŒŽŽŠˆ€€†‚€…ƒ„‡z{xyyz{{|~||~~z}~{€v}{z~€}{y|z†|}}{|~€|}€y}€|||{|z|{€€€{|}~~{~~€€}~{}||~{‚|yz|}zw}~||{{z~{wy|{zyz||zz{z|zz|||{{|||{|{}}zyy{z{~}}~}€~€~}}~~}~‚€~~€€€€~€}~€‚}ƒƒ~ƒ€€€‚ƒƒ‚ƒ‚„‚‚€~~ƒƒ‚€ƒƒ‚€‚ƒ‚„€‚ƒ‡„‚ƒ„ƒƒ„‡ƒ„‚ˆ†††€„†„„†…‡†‚„†…†††ˆ‰…†‡…†‡ˆˆ„‰‡……„ˆ„„ˆ‰ˆ…ˆ†„†ˆ‡ˆ‰ˆ„‡‡‰………‡ˆ‰‡‹Œ‹‹Œ…‰‰‹‰ˆ‹Œ…‰ˆˆ…†††ˆ‰ˆ‰‰Œ‡…ˆˆ‡ˆˆ‡‰Š…ˆ‡‡„‰„…†ˆ‰‰‰‰ˆ‰‰‹‡‹‰ˆ‰‹ˆ‡……„…ˆˆ„‡ƒ††ˆ„‰†‡…†‡‡…‡‡……‡‡‡††ˆˆ†‰‡‡†ˆ†‰ˆˆ‡ˆˆ‡†ˆˆŠ„ˆ‡‹‰‰‡‡‡„‡ˆ‰‰†„„‡†ˆ‡††…†‡…‡‡‡…ˆ‰†„„…†„††ˆ„„…ˆ‡†……„ƒ„†ƒ„„‡†‡„€ƒ……„…‡………†‡‡……‚…‰†‡ˆˆ‹‡…‡†‰‡†„„‡†„‚„‚ƒ„ƒ„…€„€€ƒ„„„…‚ƒ……„„€€„ƒ„€…„„€…†……†‡†„‚ƒƒ†…€…€‚‚„…‚€€€‡€‡…ƒ€€€€‚~~…„‚„„ƒ„„€†…„„…„…„…„††‡‰‰ŒŒ‹‹Žˆƒƒ‚ƒ…€ƒ„„ƒ…x{y|y~{{z{{z|~~~x}|xyw{|||~|~~z{{|{{z{‚||y{|||zzzzz{zz{|~~~~~~~}}~€|~|}€z{|{|~x|~}}~|w}{|zww|zzyy{||x{|}|{||}z{||||{{{}|||{{{~}{~z~{}~}~~~~~„~ƒ~~~„ƒ…~}~~~~~}~~~ƒ~„ƒƒ~€€~€€ƒƒ…ƒƒƒ„‚ƒƒƒ‚€€ƒƒ‚ƒ€€ƒ…ƒ‚ƒ‚…ƒz„ƒ‚‡ƒƒ„†…††…„‡‡„ˆˆ…„†…‡‡………†„„„‡‡‡‰………„…„…‡„ƒ„„‡‡‡‡†……‡‡†‡‡‡†††‡‰‰ˆ…‰ˆ‰†ˆˆ‡†ˆˆ‡‰‰††‡‰†‡ˆˆ‡ˆˆˆ†…††…ˆ…‡„…„ˆ‡ˆ‰‡ˆˆ‰‰†„…ˆ‹ˆ††††‡†‹ˆ……‚ˆ†…„ˆ‡†„…††ƒ‡‡„‡‡ƒ€‚…ˆˆ„‡‡‡ˆ†…ˆ‡†…‡ˆ‡‡‡ˆ‡‡ˆˆ‡†„…‡…ˆ†ƒƒƒ„…‡‡†‡†‡†‡………ˆ††‡‰‡ƒ……‡††„‡…„„‡ˆ‡†„‡ƒ„„„……„„„„‚‚ƒ……v…‚……„„ƒ‡…†‡ˆ††‰†‡†‡ˆˆ†‡‰…†……„„‡ƒ‚…‡ƒ€„ƒ‚ƒ„„„„ƒ‚‚€€€‚„„„„‚…„ƒƒ…‚ƒƒ„……††…„„…ƒ„€„‹………ƒ‚‚…‡€€‚‚€ƒƒ‚‚‚ˆ„ƒ‚‚„‚„†…ƒ„…‡Šˆˆˆ‰ŠŒŒŠŠŠŽŽ†„……€€…€„…†„z}~zxwy{vwww}|x{y{}wwy|xyxx|}|}|{|{}{|{|€}{z}}{zz{{{{zxy|~~}}{~€~~~{‡|~~y{||{}{|~~~xy~~}}}{|{x~~~{|{|y{{€€€z~~|{zy{{{z{|€}{{}}y}~~€{~}€~~z{{|~€~}}~~~}~}~~}~€|ƒ€~~~~~ƒ‚‚‚„„„~‚€~~‚~~€„„‡…‚ƒ‡„‚ƒƒƒƒ„ƒ„„ƒ„ƒ‚ƒ‡…„„…ƒ„ƒ„„„„ƒ„……††‡„‡…†‡†ƒˆ……„„…‚„…„ˆˆ‹„†…†…‡„‡‰‹……†‰‰‹‡……‡„†…‡ˆ‰ŠŠˆ‰ˆˆ‰‰‡ˆ‡‰ˆˆ‡ˆˆ„‡‰…††‰ˆ†…‡…ˆˆ‰ˆŠ‡ˆˆ‰…‰…‡‰ˆ††‰†……‰ˆ„‡‡ˆ‡‡ˆ†‡‹ˆ‡‰‰‹‹‚ˆ…‡‡ˆ†„ƒ„„‡‡‡„„…††‡‡†ˆ‰††…‰††…‡‡ˆ‡‡†‰ˆ‡‡‡‡ˆˆ„…„„ˆ†…‡‡†‰‡„„‡…ƒ‚‚„‡……„„†…‡ˆ‰„………‡‡‡„†……†ˆ‡‡†…‡‡ƒ‡„…†ƒƒ„ƒ‚……„„„†††„……‚ƒ‚…††…‡……Š…‹†‡†‡‡…‰†………„†ƒ€‚ƒ„ƒ‚„„…ƒ„ƒ…ƒ„ƒ„ƒ„„€…„€„…„…„ƒ……†††††…„‚………€„ƒ‚†ƒ„~€€€~†ƒƒ€ƒ‚„‚}…‚€†…‡„†ƒ………†…†ˆ‡‡‹‹Œ„ŠŠˆ‹‰‰ˆ‹‹‹…€…€…€ƒƒ††‚~}|zxyx}}~xz{yzz{|}yxy|y|{|~||z}|~||{}{{|~z{|y}{|z{wy~||z{€€|Œ|~}|~~~}~~|}|~||{{}~}|zz}|||{{||~|||||{{{{z{||z{{{|{}~€~€{~}|}|{|~~~~~€~~}~~~~€~}~€€€€}€€~}€ƒ‚ƒ~~~€€~}‚~ƒƒƒ€€€ƒ„ƒƒ„„ƒƒ…zzƒ‚ƒ„‚€€€ƒƒ……ƒƒ„ƒ„„ƒ††‡†‡„†ƒ„ƒ…„…††„ƒ††„‡†‡„†„„†‡†‡†„„†‡…†‡†…‡ˆ……‰‡†‡‡‡ˆˆˆˆ†‹„‡ˆˆ‰ˆ‰‡ˆ‡†‰…†…‰‡‡†…‡ˆ‹Š‰‰††‡††ˆ‡ˆ‰‡‰‹‹†‰ˆ‰‡…‡ˆˆ†‡‹ŒŒ‰ˆ‡„ˆˆ‡…ˆ„‡…‚‚…†………„…………†‡‡ˆ‡‡……‡†ˆ†‡…†††‡‡‡ˆˆ‰‹‹‹‡ˆ‡‡…‚ˆˆ…††ˆ‡„„…†‡†‡†…‡„………ˆ…‡‡ˆ„ƒ‡‡‡‡‡‡…†‡„ƒƒ‡Š…†…„‡……„„‡„…€…††††………ˆ„……†ƒ‡…ƒ†ˆ„‡ˆ…†……ˆˆ‡…‰‰‰…†…„†ˆ„ƒƒƒƒ‡„„„„„„ƒ…„„…„‚‡„………„…†‡……„„…„„…ƒ†‡…‡……„„ƒƒ‚ƒ…†…€†………ƒ‚€…€€ƒ~‚‚ƒ€ƒ……†„ƒ„……ˆ‡ˆ‰Š‹‹ŒŠŒŒŽŽŽ–ŒŠ‹‡ƒ€€†€€…€‚€ƒuxzxyyxxzwy{}{zzz{~|||}|{|{}~|}}}{~{{|€z{}}|||z|z{~{}~}~~}{€~~€~~~||}|||{|z}}}}|}|~||z||~|~}|{|{}z{zz}||{z|z|}}~~z€{€|‚}~{€|ƒ~~€~€~}~~€~€€€€}€„€}~‚~‚„€€€‚‚‚€ƒƒƒƒ‡ƒƒƒ‚„„„„‚‡†‡†ƒ‡ˆ‡†‡€ƒ‚ƒ…„‡ƒ€ƒ†„„ƒˆˆˆˆˆ…‡††ƒ‡†‰‚‡†‡……„…„‡…ˆ††………‡…ˆ………‡…ˆ‡‰‡‡…ˆ…†‡‰ˆˆ†ˆˆ‰…Š‹‹…ˆˆ‹ˆ‹‡‰‡‡…‰‡‡…‰Š‰ˆ‰‡ˆ‡Š‰‰„…‡‡ˆˆˆ‰ˆ‡‡ˆ‰††ˆˆ‰‹‰ˆ‰‡ˆ…ˆŠˆ‡‡†ˆ‡‡†‰ˆ‡……†‡…†„„…†‡†„‡‡ˆ‡‡‡‡‡‡†‰ˆ‡……‡…††ˆˆ‡‹‰ˆˆ‡‡‡‡‡‡ˆˆ‡…††‡††…‡‡‰„‡‡‡……†‡………‡‡†ˆˆ‡‡‡‡‡‡ˆ……ƒˆ‰„‡‡…†‡‡‡††……‚…ƒ…„…„†‡†„…‚„…‡…ƒ„†ˆ†‡†„ƒ„‡‡ˆ†ˆ„„‡†‡‡„„„ƒ„†…†„ƒ‚‚ƒ„…„†„„ƒ…†„„„„‡…‚„…„‡…††ƒ‚……ˆ…ƒ……€†…†„„}|y„ƒƒ„ƒƒƒ‚ƒy}|~ƒ„‚€……ˆ…‡‡ˆ‡ˆˆˆˆ‰‰ˆˆˆ‹…ŒŠ‰ŠŒ‹‹Ž‹Š……‚…€ƒƒ†€~€xyyyyzyxzyz|{zyyx|{€~|y~|ƒ||z|~{}|~|}||~}y{~{z|z€{||||€{~}|}}~z€ƒƒ~|}||~||~~||}}|{{{|}|{{{z|~~~}|{{|||{{||}{|~z||~€{}~€~~‚}{~~}€~~€€~}|~€~}~~}~}}‚ƒ‚€„~€}~€~‚‚‚„ƒ€€}€…ƒ‚‚~„…ƒƒƒ‚‚ƒƒƒ„„„ƒ‚ƒƒƒ‡†„ƒƒƒ‚ƒ„„…„„…†ƒƒƒ„ƒƒƒ„ˆ‡††‡ˆˆƒ‡…„„„„ƒ„……ˆ……………„………†††…‡ˆ†„…‡…†‡‰‡…†‡‡‰ˆ†‰ˆ‰†ˆ„ˆ‡‡‡ˆ†‡…ˆ‡‡…ˆ†††‡‡‰‰‰ˆ‡ˆˆ‰†ŠŒŒˆ‰‡†‡‰††‡ˆ‰ˆ‡‡…‡‡…ˆˆˆ‰‰‰†††‡ˆ‰‡…„„†„††‡ˆˆˆ…„……‡‡‡†††‡„ˆ‹‹‹…†ˆˆ‡„„„„†‡‡‡‡†‹‡…‡ˆ‡…‡‡‡††…†‡‰‚‡‡†‡…ˆ‡†…„…††ˆ‡†…‰ˆ‰ˆˆ……„‚‰‡‡…†…„………†…„†…„„…†…†…ƒ‚‡ˆ‡…††„„ƒ†……††……†ˆ‡ˆ‡ˆƒ‚‡‡‡†‡‡ƒ…ƒˆƒ‚……‚ƒ‚ƒ„„…………„…………‡‡‡‡††…††…†…„„…„„…„…€‚†…~……†ƒ…|€€…€ƒ€„ƒ„}€€„„„„„†ˆˆˆˆŒ‰‡‡…‡ˆŠ‰ŠŠ‹‰ŽŠ‘‘’‹‹‹Œ‰†††€‚„„‚€„…‡€„€‚ww{yyyyyy{~|zxwwy}}}yyz|}ƒ||zy{}{x{~}~~~{{{||}{|~z}zz|{}{{€€€~~~€|{€{€{~}}}{|}||}{|{}~~{|||}~}||}}~|}}{{|{z{||t|~~|{}}~~~~€€‚~}~€|}}~€~}€}~~~|}~~~~|€~„ƒƒ~~ƒ‚‚}~ƒ‚‚ƒ…„†‚‡„‡ƒ€ƒ„ƒ„‚†ƒ„ƒ„‚ƒƒ…€‡„„‚ƒ‚ƒ‚„‚‡…„†ƒ„„„„„„‡„‰ˆ‰‰†………ˆ‡ˆ†‰ˆˆˆˆ‡‡…‡††ˆ‡†…ƒ…‡ˆ‡‡††‡††……‡‡‰‡‡†‡†ˆ‰‡‡ˆ†‹‡‰…‰‡ˆ‡‰‡‰‰‰†ˆ…ˆ‰Œ‡ˆ‡Œ‰‡ˆ‡ˆŒˆ‡Š‰‰‰ˆˆ‡‡††‡‡‡‹ˆ‰‰‡†ˆ‡‰ˆŒ‰ˆŒˆ‡‡‰ˆˆˆ‡………‰ˆ‰‰ˆˆ†……‡ˆ†‡ˆ‰‰ˆ‡‡ˆ‰‰‡‡‡‡‡…††‡‡‡…†ˆˆˆ‡†‡†‡ˆ‡††‡‡…‡‡ˆˆˆ„†ˆ†††‡†…†…†‡†„†ƒ„ˆ„„…ƒƒ„ˆ…ƒ†……„ƒ……†††………„†€…‡††‚‚…†……„…‚„„„…‡…ƒ…††ˆˆ‡††ˆƒ„‡†‡†ˆˆ„ƒ…ˆ„ƒ„„ƒ…ƒƒ††……†……†……ˆ‡…††…ƒ„…„††‡†…‡ƒƒ„‚ƒ‚€€„€„€€€‚|€€€ƒ‚€€„ƒ…„‚ƒ‚ˆ‰Š‰Šˆ‰ˆ†‰ˆŠŠ‹ŠŠ‰ŒŒŒ‹‰ŠŠˆ‡‡…€ƒ„‚‚€„ƒƒxxwyyywzx~~~y‚wƒy{|{{w}†{zy{~}|}~|{|{|}{{{~{{{}z{{~~€~}~~‚ƒ„€€~~~~~~}~{~~{|}|}{{}~{{{|{|y~||z{~|||z{z{y~|~~~~~~|€}~~~~}€€€€€~„~~~~}~}ƒ‚‚}€}~~~}~€~~~~~~}~‚„€}}‚~‚ƒƒ„„…………ƒƒƒ…„ƒ„†„‚ƒ„„…„ƒƒ†ƒƒƒ…„‚€‚‚„„ƒƒ†ƒ„††‰‰‰‰ˆ‡‡†‡†ˆ‚‡ƒ…„…††‡…„ƒƒ„ˆ‡ƒ‡†††…„†ˆ‡†‰ˆ‡‡‡†‡‡ˆˆ†…†ˆˆ‡††‡‰‡ˆˆ‰ˆ‡‡‡ˆŠ‰ˆˆ‡‰††ˆ‰‰ˆ‡ˆ‹ˆ…‰Šˆ‡‡„„ƒ‡‡‰‰‹Œ††‡†‡ˆ‡‡‰ˆ‡‡ˆˆ‡Šˆˆ‡ˆ‡‡ˆˆ‰‰†ˆ……‡ˆ‡†‡…‰‹‡‹†„‰‡‡‡…‹†‡ˆƒ‡ˆ†‰ˆˆˆ„‡ˆ‡‡…†‡‡†‡‡„…„ˆˆ†„…‡‡††Šˆˆ‡ˆ…†…†…‡„‰…‡†…„…ƒƒ…‡…†„„††…†……†…„…„…†…„„ƒ‚…„„ƒƒ„ƒ„ƒ…†‡††„ƒ‡‰ˆˆˆˆ…„††‰‡†ˆƒ‡ƒˆ†„ƒ„‚‚‚ˆ„…††…†ƒ„ƒ‡†…ƒ„…†‚‚…†…†ˆ‰ˆˆ‚ƒ……€‚€„…†…„€‚€€…€}€…€ƒ‚€‚‚‚‚„‡…„ƒ„ƒ‰‰‰‰‹‰‰‡ˆ‰‡ˆŒ”‹ŒŒ‹‹Œ‹ŒŒŽŒ‚…ƒ€ƒ„„ƒ€€€†‡ƒzwyxwzyyw{||zyx~xy||~yxyzy{~{z|~y|}|}}|{{}€€~|z{~{{||~}~‚€}}~€€„x€€}€~€{|{~}~}{{{{{||~{}yy||{|{~{|z|||~|{€|}}{|{~|||}~~€€~‚~€€~~}€~}~~~~~}~}~~~€~~€~~|~|~ˆ}€}ƒ‚€~ƒƒ†„ƒ‚ƒƒ‚€ƒƒ€€„ƒ„‚„…‡‚„ƒ‚ƒ…€ƒƒ‚ƒƒ„…„€‡„„‚‚€€‚†‡‰†‹‡‰ˆ‰‰‰……‚‰„†‡„„ƒƒ„„…†‡„„„†ƒˆ†…„‰ˆ‡‡ˆˆ‰‰‹…‡†ˆ……ˆ…„‡†‰ˆ‰ˆ†‡…‡‡†ˆˆŒˆ‡‡ˆ…ŒˆŠ††ˆ…‰‡‡‰ˆ‰‰‰‰‰‰‰‰ˆ†‡‡ˆˆ‰ˆ††‡†‡…‡‹ˆŒ†Œ‰ˆ‡†‡†‡‰‡††‰ˆˆ‡…‰ˆ†‡†‡†‹‰‡ˆ‡‚‹Š‹ˆˆ‡‡‡ˆ‡‡ˆ‡†…ˆ‡†‡†‡†‡‡‡ˆ‡‡‡†‰‡ˆ††…Ї‡„ˆ‡‰ˆ‰…††Šˆ‚ƒ†…††ˆ‰ˆ‡†ˆ‡‡…†ˆƒ‡†……‡‡……††††…ƒƒ……††………„„ƒƒƒƒ‡ˆ†„„……‰‡‡ˆ‡‡‡‡Š‰„†ƒƒƒ†ˆ„ƒƒ‚‚„…„„‰ˆˆ‚…ƒ…†‰†„…‡‡†…††††ˆ‡ˆˆ‚‚‚€…‚€€„ƒƒ€€€„|~‚€€ƒ……€‚………„„…‡…ˆ‡†‰‹‹Š‡ˆ‰†‡‰ŠŒŠˆ‡Š‰Š‰ŽŒ‰ˆŠ„…‚€~€€zzxxy}zzwz~yyyy~yxz|zywwy}{~}{|}z|}}}}|}|{{~}~}|{{}€~{|}€~}}€‚€€€€~ƒ€~€~}}|}|~}~~}~~}~~||||}~|}}{}}{{{~€{{|}||~|{{~{|~~}~~€w€€~ƒ~~~~€€€~~~~}}}~~|€~~~~~~~~}ƒƒ~}|~}|}~~ƒ€‚‚‚€€„ƒƒ}…ƒƒ‚ƒ„„„ƒƒƒ€‚ƒƒ‚†„ƒ„ƒ„…„„„„ƒ„„‡……‡‡ˆ‡††‡‡†‚‰ƒ…‰‡‡…‡ƒ‚‡‡†‡„‡†…††…„‡‡‡‡ˆ…‰‡…„…‡‡‡‡‡†‡‡‡‰‰‰Œ…‡…‡‡‡‡„‹Š‰ˆ‡„Œˆ……‡‰ˆˆ‡‡‰‰‰ˆ‡…‰ˆ‡ˆˆˆˆ†ˆ„ˆˆˆ‹‡Œ‡„Œˆˆ‰‹‡Œ†…†‡††‡ˆ‡†‡„„†‡‡ˆˆ††…‡ˆ‡„‡„„„І†‡ˆˆˆƒ‡ƒ‡ˆˆ‰‡‡††……‡††‡‡ˆ‡‡ˆ………‡„……„„‡ˆˆ…ˆˆ‡ˆ‡†…‡…‰‡„……‡‡‡‰ˆˆ‡‡…„ƒ„††…‰ˆˆ‡‡……‡†„ƒ…‡…ƒ…ˆƒ…„ƒ„ƒ‚„„ƒ†ˆƒ‡…„‡ƒ…ˆ…†††‡ˆ…†ƒƒ„……ƒ…ƒ††‹†ˆ††……ƒ„ƒƒ…ˆ‡ˆˆŠ‡†…‰‡…„†…„„ƒƒ……ƒƒƒ„‚ƒ€‚…€‚€€€…‡†…€„…ˆˆ‰……ƒ„ˆˆ‰Š‰‰‰‹ˆˆŠ‹‰‡Š‰‰‰Šˆ‰ŠŒŠ‰‹‹Œ†„€€}}}€}€|}{x|yy|yxxz~~€z~~~yy{y||zy||{~|||z|~}}|Šy|{|}€}|€~€~~~z~|z|ƒ€~€~€‚~€€‚ƒz}~|}~~}~€y{|{z{|{|||{}{|{€}}{{||€€|{{|{~}|}€€€„‚€}ƒ€|~~€€€„~~€ƒ~€€|~ƒ„|~ƒ~€ƒ„„„„€€~‰~ƒ~~ƒ~‚‚€……„ƒƒ€€€„ƒ‡ƒƒƒ„„…ƒ„…‡‡‡ƒ……„‚„…ƒ‚ƒ…€‚†ƒ‚…†……†‰††…ˆ††ƒ‰„„‰ˆˆ‡…ƒ‚‡…ƒ…„‡†„‡…‹…ˆ†‰†‡„‰ˆ‡………†‡‡…‡‡‡ˆ‰‰ˆ‡†‡Œ‡‰„ˆ…‰‰‹‰‰†Œ†‡†ˆ…ˆˆ‹ˆ‰ˆ‰ˆˆ‡‹ˆŒ†Œˆ‰ˆˆˆˆ‡Œ‹‡‡‡‡‰ˆŒ‹ŒŒ‹‹‹‡ˆ‡††‰„†ˆ‡†‡ˆ‡…ˆ…‹‡ˆ‡ˆ†ˆˆ†ƒ‹‰‰†‡‡ˆ‚ˆˆˆˆˆ‡‡†‡ˆŒ†‡…Œ‹‰ˆˆˆˆˆˆ‡ˆˆ‡…ˆˆˆˆ‰ˆ‰ˆˆ‰‰‡‡„†‡ˆ‡‡†‡‡‡‡‡‡†…†…†……‡…†‡ˆ†…†††…‰‡‡††ƒ†ˆ€„„„„‚‚…‡‚„‚…††‡‡…‡††††‡‰‰‡‡„ƒ„…„‚„„…ƒ…†††††‡ˆ‰†ˆƒ‚†ˆ‰…ˆ‰‡†‡ˆ†……………‚„†………„…„„ƒ…‚€€€„‚‚ƒ|ƒƒ„‚ƒ€€ƒƒ„‚„…†……‡‰‹ŒŠ‰‹ŒŒ‹ŠŒŒ‰Šˆ‰‰‹ˆ‰‰‹Ž‹Œƒ…€€~~€}€}}zy|yy}w{w{~z}z{yzw}z~|~yy{|{|{||~z|}|zx}{{y~€}|~~~~}}||}|{€€€€~~„€~€€~~|}€€}~~~~y…€€~{|~€z}~|||{{z{||{{{{~{}~||||||||xy|€€€{~~|~~~{€~€ƒ~}}€€}€€€~~}~~~~€‚„}‚‚„ƒ„ƒ€ƒ‚~€ƒ‚‚‚ƒ…‚€„„ƒ€‚„‚ƒ‚ƒ„„ƒ}€ƒƒƒ„‡‡…‚‚„ƒ‚„…‡†‡……‡‡†…„…‡†‡ˆ‡Š†‚„†„ƒƒ‡‡†…ƒ††ˆ„…ƒˆ†…††‡‡ˆ…………†‡ˆ„‡‡‡‰‡‡†…ˆ†‹„…†‰‹ˆ‡ˆ‡‡‡‡‡†ˆ‡†‰ˆˆ†‡ˆ‡‡‡ˆ‡ˆ††ˆ‰ˆ‰ˆ‡‹‰††ˆ‹ˆˆ…†††††…††‡……„…„„‡‡‡ƒ‡ˆ‡ƒ…‡‡‡…†ˆˆ†„ˆ‡ˆ‡ˆ‡‰…‡ˆˆ†ˆˆ†…ˆ‡‡ˆ……„„ˆ‰‡…‡ˆ‡‡ˆˆ†………‡ˆˆ„ˆˆˆˆˆˆ‡†…‡‡‰‰ˆ„‡‡‡‡ˆˆŠ†…„……………ˆ‰ˆ†ˆˆ…„ˆ‡†…†‚…‡…†„†„†„ƒ…‡…†ˆˆ…‡‡„…††‡ˆ‡‡‰„‡…ƒ„„‡‹‰ˆ…‡‡‡…†…†‰†‰ˆ„‡†Š†††‡…„ƒ„„…ƒ…†…„„ƒ„ƒƒƒ‚‚~……ƒƒ€€ƒƒ€…ƒƒƒ‚„‚„……ƒˆˆ‰‡†‡Œ‹‹‰‹ŒŒ‹ŒŒŠŠŠ‡ˆˆŠˆ‰Œ‹Œ‹‹‰€€€€}€€}‚„yy|zyxy|yx~|~zyy{yyzyxyzz|~~|~|||y{€|~~~|~z{~{}€}~~~~~~z|||}~|…|€‚€~~}~~z{}~€~}|}}|~|}||{€||{|{{{~{{|||}{~~|~}|{|}€€€„~€}~~€|}€~~€ƒ‚€€}ƒ~~„„„ƒƒ„„}~‚€~€~}}€€ƒ€…„„‚ƒ€~€ƒ~ƒ‚ƒƒ‚ƒ‚‚‚„€€€ƒ„‡‚„€‚‡„€‚€„ƒƒƒƒ‚„„‡ƒƒ‡„…‡…††‡‡‡†‡ˆˆ‰ˆ‡‡…†ˆ‰††‡…„…††ˆƒŽŠ‰ˆ‡†‡…‡‡‡…†‡‡†‰ƒˆ‡†ˆˆ†‰‰‹…Œ‹Œ„‰Šˆ‰‰†‰†ˆ‡‰Œ‹„‰‰‰ˆˆ‡Œ…Œ‡ŒˆŠ‡ˆ‰‰‰Œ‹‰‰‡ˆ‰‰ˆˆ‡ˆ‰ˆ‰‡‡‡ˆ…‡‡ˆ„‹ˆ‡‡ˆˆ‡‡‡„‡†‡…‡ˆˆ†‡‡‡‡Š‡‡‡Œ†‡‡ˆˆ‰ˆ‡ˆˆ†ˆˆ…‡‡ˆ‡ˆ‡‰ˆˆ‡ˆ‡‡‡ˆˆˆ‡†‡‡………‡‰‚‡ˆ††‡ƒˆ„…††‰ˆ‡†…‡‰†‰‡‡‡‰Š‡‰‡†‰ˆ‡ˆˆ†‡ƒ†‡‡„€‚‚‡………‡††‡‡†‡†…†‡†‰ˆˆˆ†‡…‡‡‡ˆ‡‰‰‰ˆ†‡‡†…ƒ…‰‰†‹‰…„†††††…„…†„ƒ……ƒ‡ˆ…€€€„„…„…€†‡†ƒ…€„€€„…‚ƒ„…ˆ…ƒ…ƒƒ…„…‰‰ˆˆ‹‹ŠŒŒŽŒŽ‹Š‰‰‡Š‰‹‰†‡ˆ‡ˆ‡‚€}}„}€‚|ƒ~x{y{zyz|xyyxwwwz|yyx~{|{||{|||{}|||}|}||{z|€y||~}}}~}|~y€~}~~}~}€€€{€€~€‚‚€€€~„~|}{~}}~|‚|€~~{{|{||}~~|}z{~~||||z~}|}€|~x{|{ƒ}€~€}~€€€}€€‚ƒ~}€~}~€„}‚‚„€~‚~~~‚~ƒ‚~~|}‚‚„„‚‚ƒƒƒ„€„ƒƒ„…„€€ƒƒ‚~€ƒƒ€€ƒƒ„††……†„ƒ€‚††„††‡‡…‡‡„ƒ‡……‡…ƒ……‡„„††„‡ƒƒ…„„€‚…‡‡†‡‡†‡††…„‡‡‡ˆ‡‡‰‡‰„ˆŒ€ŒŠ‡‡‰‡†‡†‡†„‰‡‡‡‡ˆ‡†‰ˆˆˆˆˆ‡ˆ‡‹‹‹‹Œ‹ŠˆŒ‹Š‡‡ˆ‹‰ˆ‰††ˆˆ‡‡ˆ‡…………†‡‡‡„ˆˆ††…†‡ˆ‡‡ˆŒ††‹‰‡ˆˆ„‡ˆˆ‰‡†‡ˆˆ‰ˆ‡‡‡†ˆ‰††††ˆ‡„††ˆˆ†‡‡…‡ˆ††…†…†…‰‡††‡ƒ‰ƒ……‡‡…‡ˆ…ŠŠ…ˆ…‡ˆ‰‰ˆŠˆ‡ˆ‡„………†††‡„ƒ‹„‚‡†‡‡„„††‡‰††…‡‡‡‰‡ˆˆˆ†‡ˆ‡ˆ‡‹‰ˆ‡ˆ………„„……ˆ‰‰‰‰Š‹Š‰ˆŠŠŠ…‡…„ƒ…………„…………„……†ƒ……ƒ€€„ƒƒ…ƒ…‡……„…„„†ˆˆ‰…‰ˆ‹ŠŠˆ‰Š•ŒŽŽŒ’‹ŽŠŒ‹Š‰‰‡†Š‡ˆ‹ˆ‡‡€}}€ƒƒ€ƒ„‚z~~yyzzuxwxwwyyx~zzy{~{|}yzy|~|}|z|{y}}|{z|{{|~||}}||€}||{{}{|‚€€€‚}|~€…ƒ€€€€~~||}|z}||~}}|z|~y{{~||{~|{}{}~{z{||||}~~~~|{zzz~€‚~~€€€€|~~ƒ€}}€~€~€€€~~‚~~~~‚‚}ˆ„€€„~€~‚~‚„ƒ~ƒ„‚‚‚ƒƒ…‚ƒ…„„‚‚‡€‚„„„ƒ„‚ƒ„‚‚ƒ…ƒ„„‡…ˆ…€†‡†‡†‡‡‡…†ˆˆ‡…ˆ†‡„„‡‡†‰…†…‡„…††‚‡†‡†‡‡ˆˆˆˆ‡ˆ†„†‡‡‡‡‡ˆ‡ˆƒ†‹…‹‰‡ˆ†‹‰ˆ‡„‰‡‡†‡†‰ˆ‰ˆŠˆˆˆˆˆˆ‰Œ‹‰‰‹‹‹ŠŒˆˆˆŒ‰‡ˆ‰‰‰‰‰ˆ‡‡‡…†ˆ‰ˆˆ‡ˆˆˆ†…„ƒƒ„…‡ŒŠ‡…Œˆˆˆ‡‡‡‡‡‡ˆˆ‰‰‹‰‰…‡†Œ‰ˆ†ˆ†ˆˆ††…‡‡‡†ˆ‡ˆ†…†…ƒ„…‡†…†ˆ…„‰‚…‡………††‡‰‹‰ŠŠŠŒŠˆ‰‰ˆˆ‡…†……‡†ƒ…†„ƒ‚‚„†‡†ƒ‚ƒ„†††ˆ…„…†††ˆ‡‰ˆˆ‡‰ˆ‰‡ˆŠ†‡ˆ†‰†ˆ…ˆ†„…ˆ…‡‡ˆ‡ˆ„„…„††…ƒ„„……†„„…ƒƒ‚…ƒƒƒ€‚ƒ‚‚‹€€ƒ€…‡‚ƒ‡‡……‚†‡ˆ‡†ˆ‡ˆˆ‰ŒŒŠŠˆ‰Š‹‘‹‹ŠŠ‰Š‰Œ‡‡Šˆ‹‰ˆˆ‰~|}~€|ƒ€€~~~yy{{xyxxzzwzw‚x{x~~zyz|||y~{|}||~}~}€|~}€€|€{v~|z|}~}{~{~{}{{~|~€~~~€€€€ƒ~~~ƒ~ƒƒ„|}|z}|}|}{{y|{|~}|{~~|}|||}|{{|{€{|€€|~€~€~€€|€€€}€€€€€~~}~€€}€€ƒ…€~~~~~‚‚~€ƒ‚~~€ƒ~‚‚‚‚ƒ‚ƒ‚ƒ†~ƒ€ƒ‚‚€„ƒ„ƒ€ƒ‚„„‚‚€€€‚€„‚‚ƒ„ƒ‡„‚†ƒƒƒ…}ˆ†€ƒ‡†…†„„„ƒ‡Š‡†‡…†‡„„…ƒ‡„„……‡‡‡‡‰‰‰†‡„‡†‡ˆˆˆ‡‰„…‡‡‡‡‡…††††††…Œ‡‰‡‡‡‡‡ˆ„ˆ‡ˆˆ‡ˆ‡ˆ‡†‰Œˆˆˆ‰‰ˆ‹ˆˆˆˆˆˆ‰‰‡‰‡‰‡†‡…‡‡‡ˆ…‡ˆ‡ˆ‡ˆ††…ˆ‡ˆ„ˆŒ‡‡‡‡ˆˆˆ‡…ˆ‡‡‡‡‡‡ˆˆ„…„ˆ‡‡ˆˆ†…‡‡‡ˆˆˆˆˆˆˆˆ‡…‡‡†ˆ‡„„‰‡……ƒˆ…………………‡ˆˆ‰‰ˆ‰ˆ„‰ˆ‡‡…‡‡‡‡ˆ††…‡‡†…„€…†ƒˆ†‡ƒƒƒƒƒ‡‰„„„†‡‡ˆ†…„‡‡‡ˆˆˆ‡‰ˆ‡ˆˆˆ‡ˆ‰ˆ†…‰…‡…І‡…‡ƒ„„ˆ„††ˆ„†‡ˆ…†‡ˆ…ƒ……‚‡†‚„…‚ƒ……€†ˆˆ…ƒ…ˆ‡…‡‰ˆ‰‹‹ŽŠŒ‰‹‹ŠŠŒ‘ŽŒ‹‘‹‹Œ‹Ž‹‹ˆ‡ˆ‡‡‹ˆ€€€z„‚€€~€‚}{x{{{y~{|{xzy}xyy~}{xxx||{z|}||{|~}|}~}|}}}~~{zz~|~y|~|{~|z~zyz}~~}z||y}€‚€~~ƒ€€~€~||~‚}~~}}{~}~|{y|{|~~}~|||||||€€|{~||€€€€€€~}€‚}€|€€~€~€~~}€€~€~…€‚ƒ€ƒƒ~‚ƒ„€~€€ƒ€„€‚‚‚„„„„‚ƒ‚€ƒ‚ƒ…ƒ€‚ƒƒ„ƒ…ƒ€€€ƒ€‚‚ƒƒ€ƒ‚‚ƒ„‡………††ˆ†‡„‡ˆ‡†ˆ†…‡‡‡†…„ƒ†…ˆ‰‰‡†ˆ‰†‡‡†…‡†…†…„„…‡…‡‡ˆ‡†ˆ‡†‡†ˆ†ŒŠ‹…Œ‰‡†Œ†Š†ŒŠ‰‹‡‰‡‹ˆŒˆ‡„‡ˆˆ‡‹Š‹ˆˆ‰ˆˆˆ‰ˆ‰ˆ‰‰Œ‡‡‡†‡‰ˆ‹‹‹ˆ‰‡ˆ‹Œ‡‡…ˆ‰ˆ‰‡†ˆ†††‡‰„‡‰ˆ†‡‡‰…ˆ†ˆ†ˆ‰ˆ†ˆ‡‡‡ˆ‡ˆ‡ˆ‰ˆ‡‡‡‰…‰‡„‡‡ƒˆ‡†„†…†‡‡ƒ…ˆ‡‡‡‡ˆ‡‡†…………‰†‹Š‰ˆ‡‡‡‡‹‡‡†ˆ†…‡†…‡„ˆ†‡…‡†‡…‡„„…†…‚…„‡…‡‡…„„‡‡‡‡ˆ‰ˆ†‡‡†ˆˆ„‰‰†ˆ‡‰‡ˆ†‰„‡††………††‡‡„‚…„†‡††††‡‡…ƒ††„ƒ„†ƒˆ€„……€ƒƒ…†‡……‚€„…‡††ˆˆˆ‰‰‰‡‰‹‹ŠŒ‹‹‰‹‹‘‹ŒŒŒ‹‹Œ‹‰‰‡‡‡‡‹‡ƒ}|}„€ƒ|€€}yy{xxzzyx{w{x~{yx~|{yy{~|}}~|{€~~|}~}~~|||~zy{z|~}~~~~}y€€z{{~{y{|{|€€~ƒƒ„~€~~‚~~ƒ~|{||~~~}~|||~~|~}|||||{|||}||{€~}}~z~€€‚~€~~‚{~~€~€€€~~€~~~}}~‚~~~€€~€ƒ„‚‚€ƒ~€‚~ƒ‚„„ƒ„ƒ€‚ƒƒ‚ƒ„„„„ƒ„ƒ€‚‚‚€€ƒƒƒƒƒƒ‡„†ƒ…†‚‡††…‡††……‚‚„ƒ„„ƒ…ˆ‡…†‡‡‡…††‡…‡†ˆˆ‡……†‡……„„„…†‡‡‡‡‡„†Œ…„‚Š‹‹‰Œ††‡‡‡‡‡‡‡‡‡‡ˆŒŠ‡‡‡Š‰‡‰ŒŒˆ‡‡‡‡‡‰‡ˆ‹‰‰Š‰ˆ‡‡ˆ‰ˆ‰‹‰Œˆˆ†ˆˆ‰‡ˆˆˆŠˆ‹ˆ‡………‡ˆ‰ˆ‡Œ†ˆ‡‡‡…†‰†ˆˆˆˆ‡‡‡‡‡ˆ‡ˆ‰ˆˆ‰ˆ‰†‰‡††…‡ˆˆ‰ˆˆ†„†††‡ˆ‡††‡‰‡‡ˆ‡†‰…‡…‡ˆ‡„‡‡‡ˆ‰ˆ†‰ˆ‡‡ˆˆ………ˆ‡‡…††„…ˆˆˆˆˆˆˆ‡†††‚†‡…ˆ‡„‡†‡†ˆ‰‰‡Š‰ˆ‡…†‰‰‰ˆ‡ˆˆˆˆ‡ˆˆ„ˆ†‰Š…†…‡…†‚…††…†„†ˆ‡…‡…†…†††…††…‡………ƒƒ…„…„†ƒ‡„ƒƒ†‡ˆ‡‰ˆ‡ˆŠ‡ˆˆ‰‰ŠŠ‹Š–ŒŽŽŒ‹Œ“““““‘“Œ‘‹‹Šˆ‰‰ˆ‡Š†„€‚}}~ƒ€€€€€€||„y{zz{zxxyyv|x{~zyyzzzxw{||y{||~~|{z|~y~}|}~}}|zy{{{}}|zx{{{{{y|~~}~|}~~€~€„€~~~€~~€~|~~~|x|}~~~~~}~~{}}{|{~}}}}{~|}{~€~{~~€~~~‚‚~€~€‚€€|€~€~€€‚…„~~€€}~„ƒƒƒƒƒ‚ƒ€€~€€„~ƒ€„ƒƒƒ€‚‚ƒƒƒƒƒ‚‚€ƒƒƒƒƒƒƒ€€€‚‚‚‚„‚‚‚„„‡‡‡ƒ†…†ƒ†„‡…‡‡‡†…†‡ƒ„ƒ…†‡ƒ„„‡‡ˆ…„…Œ…†…†‡‡‡‡‡ˆ„І‡…‰……†…‡‡‡‡ˆ‹‹Ž‹Œ‹‹‰Œ…ˆ‡ˆˆŒŒ‹Šˆˆ‹ˆˆ‡‡ŒŠˆ‰Šˆˆ‰ŒŒŒˆ‰‡ŒŒ‹‰Œ‰‡ˆ‹‰‰‰‹‹Œ‰ˆˆˆˆ‰†ˆˆ‹‹Œ‰‰†‰‰ˆ‡Œ‰‡…Œˆ‰ˆ‡…ˆŠ‰ˆ‰ˆˆ‡‡‡ˆ‡‡ˆ‡ˆ‰ˆˆ†‡‡‡‰†††…‰…‡Œ……‡‡‡…‰‡†ˆˆ‡……‡…‰ˆ†ˆ‡†…‡†…‡††ˆ‰‰ŠŠ‹Š‰ˆ…ˆ‰ˆ‹‰‡‡ˆ††„‡ˆˆ‡ˆˆ†ˆ„…‡„„‡‡„ˆ…„…‡……ˆˆ‰‰‰‡ˆ†„…‰ˆ‰†…ƒ„„‚„ˆˆ„ˆ…ƒƒ…„ƒƒ„ƒ„„…†…„ˆ……ƒ……†…„…„„…‡††€†€†‡…ƒ†„ƒ‡†„„ˆ†„‡ˆ‡ˆ‡‰ˆ†ˆ‰ŒŒ‰‹‘‹‘’ŠŽ‰’Œ“‘’ŽŒŒ‹ŽŒ‹Šˆ‰‡ˆ‹‡„~~}~€€€ƒ‚ƒ€‚…xxz}}|y{zxu‚z{yzy{{{zzz|}|~|}}|}~~}||~}{~{z{€z{{xz|z|z{z{|~}}~~}€€€~€}}~‚}{‚ƒ‚|}}}||~|~~}}|}}}}{}}|}}|~||~}|{|~€~€€~€ƒ€|~€}{€€€€ƒ€‚€€€‚ƒ„ƒ‚~~€€}€~€ƒƒƒ}‚€~~~ƒƒ‚ƒ€„€€‚ƒ‚…ƒ„„ƒƒ€ƒƒƒƒ~……‚…„ƒ€ƒƒ„ƒƒƒƒƒƒƒ„ƒƒ‚ƒƒƒ„ƒ…ƒ‚…„…„ƒƒƒƒ„‡…‚…‡„‚†ƒ†ƒ…ƒ‡†„ƒ„‡‹†„„„„„ƒ†……………†…‡‡‡‡‡‡‡„‡ˆˆˆˆŒ‹ŠŒ‹‰‰‡‡‡‡‡ˆ†ˆ…„†‡……†‡††‡‰ˆŒˆˆŒŒ‹Œˆˆ‡‡ˆˆŒ‹‰ˆˆ‰Œ‹‰‡‰‹‡ˆ‰‰‹‡ˆ‡‡ˆˆ‡ˆ‡†…†ˆˆ‡†‰‹‡…†ˆ‡‡‡‡†ˆ‡ˆˆ‡ˆ‡‡ˆˆˆ‡ˆˆˆ‰‡‡†‰‹‰Š‰Š‡…ˆˆ‡‡‡ˆˆ‰ˆ„‡‰‡……Š„†††ˆ‰ˆ‡…†‡Š‡ˆ‡‡ˆ‰‰‹‰ˆˆ‡‡‡ˆ‰‹‹Š‰‡‡‡‡‡‡‡ˆ‡ˆ‡‹‡‡‰‰…‰ˆˆ……ˆ‚………†ƒ†‰‰†…†ˆ…„…‡‡ˆˆ„‚ˆ‚„……†††……„ƒ„„…………†„„„ƒ„„„†…†‡‡„††ˆ„†‡‡††‡††‡ƒ‚…ˆ‡‡…ƒ…†ˆ‰ˆ‡‰ŠŒŽŽŒŽ’’’‘ˆ‘ŽŒ’‘’ŽŽŽŒ‹Š‰ˆˆ‰‡Œ„„||€€€€…yw|yy{y{|yvx{{yyy}|{yywyywx|~}||x~€{|{|}}z{z{z{yz|}{{{zz||~{|}z|~z~€|{{}|z|{~|zz~||z}|||||~~€~}{~~~}€{}}||€|~||{z{|‚~~ƒƒ‚€€€‚€€€€ƒ€€€€€~€€€€€€ƒ€€~ƒ~ƒ€ƒ…~ƒ„€~~€„}‚€„„„„ƒ‚‚ƒƒƒƒ…„ƒ„……†„„‚ƒ‚„ƒ‚ƒ…€ƒ„ƒƒ€…‚ƒ„„ƒ€‚‚‚~€†…‡„†‚ƒ…†‡ƒˆƒ†ƒƒ‡„„…„ˆ‡…„†…‡‡‡…„ƒ……‰†‡ƒ‰…‡‡‡‡ˆ‰Š…‡†‡ˆˆ‡†‡‰ƒˆˆ‡‡ˆˆŒŒŒˆ†‡‰Š‰†ˆˆ‡‡ˆˆ‡†‰‡……‡ˆˆˆˆ‰ˆ‡‹‡‡‡†‰‰‹‹ˆˆˆ‰Œˆ‰…‹†Œ†ˆ‰ˆ†…‡‡‰ˆŒ…‡‡‡ˆ‡†‡†ˆ‹ˆˆ‰ˆˆ‡ˆˆˆ†‡‡ˆ‡ˆˆ‰†‡‡‡ˆ‡ˆŠ„†ˆ‰ˆ†‡‡‰ˆ…ˆ‰‡‡†ˆˆ‡‡†ˆˆŠˆ…„…†Šˆˆˆ‹‡‰…‡‡‡ˆŠ‹Š‰Š‡ˆ†ˆ‡ˆ‹‡ŒŠˆˆˆ‰ˆ†‡‡‡‡ˆˆˆ‰†…‡‡‡†‡………ˆ…†…„…„…ˆƒ„†ƒ„……ˆ‡ˆ‡‰…‚‚…„ƒƒ…„†…‚‚‚ƒ…„€……„…†…„†……†„…„…†…†‡…†ƒ„‡ˆ‡†„ƒƒ……ƒˆ‡…††‰ˆ‰ˆˆˆ‹ŠˆŠŽˆˆŒŒŒ‘‘‘‹‰‘ŽŽŒŽŒ’‹ŠˆˆˆˆˆŠ€€||„€€€€‚‚€~…z{vuyxyyz|zxy|xyy{zzwyyzwzzzy{z|v€|z{|{{}{||{{|||}{}}~yy}|~€|{|}~x}~{~{xz{|z}y}}|~~}}~~€~€}}~}||{{|{{{{|||{{~€~€‚ƒ€ƒ€|‚€ƒ€‚ƒ€‚€~}}€~„€~‚ƒ€~~~~€‚ƒ€€ƒ„‚ƒ€€ƒ„„„ƒƒ„……ƒ‚‚„ƒ~~~€€‚‚ƒ†…†ƒ„ƒ„ƒ‚‚€ƒƒ€†„‚…€€‚‚‚ƒƒ…‡„„ƒƒ‡„ƒ‚„……††„†‡‡…‡…ƒ†„ˆ‡‡‡††…‡‡‡†‡†‡‡ƒ†‡†‡ˆ‡ƒˆˆ†‡‡ˆ‡ˆ‡†‡‡‰‡‡‡……ˆ‹‡„‡†‡ˆˆˆ‡‡…‡‡‰‡†…†††‡‰Š†……Œ†„†‡ˆ‡ˆˆˆ‡‡‡ˆ‡Œ‹‡‡Œˆˆ‡††‡†‡‡‡‡‡‰ˆˆŒˆˆˆ‰‡‡‡ˆˆˆ‡ˆˆˆˆˆˆˆ†…†‰ˆ‡……‡ˆ‡‡‡‰ˆ‰†‡ˆ‡†ˆ†††„‡ˆ†‡ˆ‰‡‡‡†…†‡ˆ‰ˆ‡†‡‰‰‹‹Šˆˆˆˆˆˆ‰Š‰Š‰ˆˆˆ‡‰‡ˆˆŠ‰Œ‡‡ˆˆ‡ˆ‰ˆ‡‡‡††„…‡…………ƒ„ƒ„‡‚„ˆ……ƒ†……ˆ…„…†‡……ƒƒ„…………‚„„…ƒ„ƒ†…………„…„„……ƒ„†‡‡ˆ………†…†‡ƒ…ƒ„„†ˆ‰…†‡Š‰ŒŠŠ‰‰ˆŠˆ‰‹ŒŠ”‹ŠŒ‘‘’Œ‰•ŽŽŒŽŒ’‰‹ŽŠˆˆ‰ˆˆ‰„~€€}~€€€ƒƒ…|wwxxv{yywwxy{xywwwvyyzywy~zwwxvwwwy|||{}||}z|{y{{{{yzy{|{~~~zyz}~~z{|||{~{{x{||yz}z{€{}€{y{~~€€€~~|}~~|||€€}}}{z||{{|z€~}{{|€€‚‚ƒ€ƒ|‚ƒ€€€€„„€€ƒ‚…ƒƒ|~~~…~~„„~~€ƒ„ƒ…‚€„ƒƒ‚…~ƒ„„„„€„„ƒƒƒƒ„ƒ€…€‚„‚ƒ„ƒ…ƒƒƒ‚‚ƒ€…„ƒƒ†‚„€„ƒ‚ƒ„†‡ƒƒƒƒƒ…ƒƒƒ†ƒƒ†‡…‡‡†…‡††„ˆ…Їˆ‡‡‡ˆ‡‡‡†„…„†ƒ‡‡‰‡†ƒˆˆ‡†‹ˆˆ‡‡‡ˆˆ‰‰ˆ‡‰…‡†‡ˆ‡†ˆˆˆ‡‡†‡‡‡ˆˆˆ‡‡ˆ„‘‰ˆ†Œ‹Œ„‰‡‰ˆ‰ˆ‹†ˆ†ˆ‡ˆˆ…‰‰ˆ‡Œ†‡‰ˆ†‡‡ŒŒŒ‰‰ˆ‰‰‡…†‡‰‡‡‡‡†ˆ…‰‡‰‰ˆ†‡…†‡†‡‰‡…‡ˆ‡‡‡‡‡ˆ‡ˆˆ†…„†‡…Ї‡‡ˆˆˆ†‡†‡ˆ‹‰Š†‰ˆ‡Œ‡‰‡Šˆˆ‹ˆ†ŠŠˆˆ‡ŒŽŒˆ‡‡‹‹‰‰ˆˆˆ‡ˆˆ‰Š‡‡ˆˆˆˆŠ‡„„……„…ˆ„†‚„…†††‚„‡…І„…††‡†‡…ƒƒƒ‚€€„ƒ„†…ƒ…„„…†………„†‡ˆ‡‡††…ˆ…†…ˆ‰ˆ†…†„ˆˆˆˆ‰‰Š‹‰†ŠŒˆŒŒ‰‹‰‰‹‹ŒŒŽŒŒŒŒ‹ŒŽŽŽŒŒŒŒŽŽŽŠ‰Œ‹ˆˆ‰ŽŒ‡‡ƒ€~ƒƒ€|}‚„uuutwx|uzxwyz{yywzwyzyxwwwyy~xzvyy~}}}}|}{|{|{€|{|}|~z}|{||||{|{}~~}}}|}}|||{yyy{{}}}~~~~yz|~~|€~}|{~€€|€~~€~|x~{|}~€~|{|}~}~~}€€€~€ƒƒ‚€‚€€€€€€~ƒ€€‚}~€€„„‚…€€€€ƒ€€ƒƒƒ€€€ƒƒ‚„„ƒ†€~ƒ„‚‚……„ƒ‚ƒƒƒƒ‚„„„„ƒƒ…‚‚‚‚ƒƒ„„ƒƒ…„„„ƒƒ‚‡„ƒ‡‚ƒƒƒƒ„„ƒ„ƒ„„‡‚ƒ…‡‰‡†…†…„„‚†‡ˆ‡‡ˆˆ‡…ƒƒ‡…ƒ‡‚‡‡‡ƒˆ‡ˆ‡‡‡ˆˆˆ‡‡†Š‹ˆ†ˆ†…†‡Œ†ˆˆˆ‡ˆ‡‡‡ˆˆ‡‡ˆˆˆ…ƒŒˆˆ‡‰‰‹‡†…‡ˆ‰‰ˆ‡‡ˆ‡‡‡ˆ‡†‰‡ˆˆŒ‰ˆˆ‰ˆˆˆˆ‡‹ˆˆˆˆˆ‡…‡Šˆˆ‡ˆ‰††…‡ˆˆˆ‡†‡ƒ††…‰ˆ‡…„…‡‡ˆ‡ˆ‡‡ˆ‡††…††…Љˆ‰ˆˆˆ‡††‡‰ˆ‡…††‡‡Œ†‰†ˆ†ˆ‰ˆˆŠŠ‡ˆ‰Œ‰ˆ‡‡‰Œ‰ˆˆ‰ˆ‰ˆˆ‡Œ‡‰‡‡‡‡†ˆ‡ˆ‡‡ˆˆ‡‡†„…†…„†ˆ………†‡‡…„ˆ†…‡……†…ƒˆƒ„ƒ„ˆ‡……„„……„†‡ˆ„…†ˆ…‡†‡‡ˆ††ƒ„‡ˆ‡…ˆ‡„‡ˆ‰ˆˆ‡ˆ‰‰‹‡Š‹ŠŒŠŒ‰‘Œ‹‹ŒŽ‰‹‹Œ“Œ‹Œ‹ŒŽŒˆŽŽŽ‹‹‰Š‹‡…†ƒ€€ƒ„~€}„zuvvz{|tzxyyyyyyyzywxz~wzyxw~zzzwy|{~|{z||yzy|{{||~z|~}|~|{||zyzv{||€|{}~z{{€z{{}}}{€|z~}~{|{~~~}}€€€€€€‚ƒ|}€}~€}|~€€}}z}~„„ƒ}€|||€~||ƒ€~x‚ƒ€€€~€„„…„„€€…„ƒ„€‚‚„ƒ…€€€ƒ„…~€ƒƒ‚€€€ƒ„„ƒ}‰~~ƒ„€ƒ€„~~ƒ…„ƒ†ƒ„„„‚‡€‚„‚„„‚€‚€†€„„ƒ‚………ƒ„…ƒƒ…†ƒ…†„„‚‡„ƒƒ‚ƒ†€„„††‡†‡ˆ…†……‡„†……†‡†‡†‚‡††……‡‡ˆƒ†‡‡ƒŒˆ‹†ˆ‡Œˆˆ…ŒŠ‹Š‹‰‰†ˆ†‡‡‡ˆ‰ˆˆˆˆ‡‰‰ŒˆŒ‰‡ˆŒ‹Œ‡‰ˆ‡ˆ‰Š‰Šˆ‡‡ˆ‰‹‡‹ˆŒˆˆˆŒ‰‰ˆˆˆ‡‰ŠŒ‡‡‡‹‰‰ˆˆ‡ˆ‡‡‡‡ˆˆ‡‰‰ˆ‡‡‰‰ˆ…‡††‡ˆ†‡‡‰‡†‡ˆ‡‡‡‡‡ˆ†‡ˆŠ…ˆˆŠ‹‹ˆ†……††…‰‰Œ‰‡…‹††……ˆ…‰‡‡‡‡‡‰Š‹Œ‰ˆˆ‡ˆŒŠˆŒ†‡ˆˆ…ˆŠ‰ˆ‡†‡‰‰‰‰‡†‡†‚‡ˆ‡††ˆ…„„††‡‡‡ˆ‰ˆ„‡‰‰Šˆ††‡…‡‡†………†…†‡†……„„„……‡„…„†ˆ…ˆ‡ˆ†‡…†…‡ˆˆˆ‰‡†ˆˆ‡ˆˆ‰‹‰‰ˆŠŒ‡‰‹Š‰ŠŠŠŠ’ŽŽ‰ŒŠ‰‰‹ŒŒ‹ŒŠŽ‹‰Ž‹Œ‹Œ‰‡ˆ‰‹†…ƒ|€€ƒƒ‚‚…z{wyxvuuyvxyxyxyzzyxwywx{yy{|zy~~}||{{z}|}wxz|{{z€~yxw{{~wy~y„y{{}|{{{|~~~|}}{y{}{x}}}z}{z|}~~~}|€€~u‚‚„~~|}||}~~}}€}}„|}~~~}|||~€€„ƒ„€€€€€€ƒ€€€€€€ƒ„‚~ƒ„‚€€‚ƒ€€€„„ƒƒ€„ƒ„ƒ~}„…ƒ€ƒ€…ƒƒ……†„„€ƒ…€}‚‚„‚‚‚‚‚€ƒ‚ƒ„{„„„…ƒ„„†ƒ‡…‡…ƒ‡‡„ƒ‚†‚„‡…ƒ…†…‚ƒ†…‡‡‡ˆ†ƒƒ…†††„…‡‡‡††‡ˆ‡ƒˆˆ‡‡‡‡ˆˆˆ‡†‡‹‡‡‡…†‡ˆˆ‰‡‰ˆ‡ˆˆŒ‰ˆ‡‡‹Œ‡‰‡‡††ˆˆ‰ŒŽŒŒˆ‡Š‹‡ˆ‡ˆˆ‰Œ‡‡‡Œ‰‰‡Œˆ‡‡ˆ‰ˆˆ‰„ˆ…ˆ‡ˆ†ˆˆˆˆˆˆˆ‡ˆ‹‰‹ŒŒ‰‰Œ…„„†ˆ‡†ˆ‰ˆ‡‡…†ˆ‡…‡ˆˆ‰ˆˆ‡‡‡‡…†ˆ‡‡‹‰‰‰†‡‰‡‡††……ˆŒ…‰†Œ‰‡ˆ‹‰‰ŠŒ‹Œ‰‰†‡Š‹ŠŠ‡ˆˆˆ†‡ˆ‰‰Šˆˆ‡‡ˆ„ˆ‡ˆˆ†‡‡…ƒ„……„†‡ˆ‡‡†‡‡ˆˆˆˆ‰‡‡ˆ‰‡‰ˆŠ…‰†‡‡ˆ†Š†††‡„†ƒ…ˆ‡„‡†ˆ…‹…ˆ‡‡†‡ˆ‰‡‡‡‡†ˆ‰‰‰ˆ‡‰ŠŠ‰‰‰Œ‡‹‹ŒŠŒŠ”‰Š‰‹”ŠŒŠŽŒ’Œ‹ˆŠŠ‹ŽŽŒŒŽŒŒ‹‹‹‡„„„€€€ƒ‚‚„yyyyvyzvxvyxyzxyyzyxwwyy|y{ywxy{}~||~~z~|}yz|{||||~~~{|{~yz}{}{z|{x{x|}~~€~|~~{~~{|}~~}{{~~~|}€~~y„ƒ€~|||€||}}€ƒ|€|}€}~}~}||€€€ƒ€~€‚„‡„ƒ€€€ƒ„€€‚„ƒ‚€€€‚ƒ……ƒƒ€€„…~€€€ƒƒ‚„}€€‚…„ƒ‚€†ƒƒƒ…‚‚„ƒ…„„†‡ƒ„„…‚„„‚„‚„„ƒ‚ƒ€‚‚„ƒ„„‡…†‚„‚„†††ˆ‡‡†…„‡††…‡…†…‡†ˆ‡ˆ†ˆ‡‡†‹‹Œ†‡„†…ˆ„†††‡†‡†‡‡ˆˆ†ˆˆˆŒ†‡‰‹Šˆ‰‹†‡‡‡…ˆ‡‡‡‡‡ˆˆˆ†‰Š‹ˆˆ‹ˆˆˆˆ‰…‰‰‹‹‡‡‡†‡‡ˆ†ˆˆˆˆ‡Œ†‡‡Œ‹‹‰‹Œ‹‰Š‰‰ˆŒ‰ˆˆ‰‰‰‰‰‰Š‰‰‰ˆ†ˆ‡‡‰‹‰ˆˆŒ…˜ƒ‹…ˆ‰‰‰‰‡ˆ‡‡‰‡‡‹‡‰‰‹ˆ……†‡ˆ‰……ˆ‡‡‰‰……І…‹†††ˆ‰‡ˆ‡‰Šˆ‹‰ˆ†‡‡‹‰Š‡‡‡ˆ†‡ˆˆ‡‡†‰Š…ˆˆˆˆˆ‡‡‰ˆŠ‰ˆˆ‰‰‰…„‡‰‡ƒ†‡……‡‰‡‡‡ˆ‡ˆ‡†ˆˆ‹‡‰‹Š†ˆˆ‰ˆ‡ˆ†ˆ…ˆˆ„ƒ…ˆ‹‡……‡‡…Ї‰‰ˆ‡‡‡‡ˆ‡…‡ˆˆˆ†‡ˆ†‰…ˆˆ‰‹‹‰ŠŒŠŽŒˆ‰Ž‰‡ŠŒ‰‹ŒŒŠŠ‹ŽŽ‹‰‹‹Ž‹‹‹ŽŒŒŒ‹Š…„„ˆ‚~€{€~ƒ‚…ƒ‚‚vwvzv{yvxyywwzzzy{xxxxyyy}~w~z}~{~}~~€{~y{|||}{{}z~~|~{}~}||||{z{yz~€~€{~}}z|zz~€€€~€|~~||~||~€~||~Œ€~~~~}}||||}|{}|~~}~€ƒ€‚„„€ƒ€‚€ƒ„ƒ€‚€ƒ€€~‚ƒ‚‚€ƒ~€„„„„…€€„€ƒ„~„ƒ„ƒ„ƒ‚†„„€…ƒ€‚„†…‚‡ƒ„…„ƒ‚„„„ƒ„‡……‚ƒ€ƒƒƒƒ„ƒ…„€€€†‡†ƒ„……„…‡††„ƒƒ„‡ƒ……‡‡†„†‡‰…„ƒƒ†‡…„……†ƒ‡‡‡ˆ†††ˆ…ƒ‡‡‡ˆˆ‡…‹Š‹ˆˆ‡‡‡ˆ‡†‡ˆ‡Šˆ‡ˆŒˆˆˆ‰ŠŒŒ‹‹Œ‹Œ‹‰‡‡‰Œ‹ŒŒŒˆ‡ˆŒˆ‰‹‰ˆ‡ˆ‡‡ˆˆˆ‡ˆ‡‹ˆ‡ˆŠ‰ˆˆ‡ˆˆˆˆˆ‡‰ˆˆ‡†ˆŠˆ…ˆ‡ˆˆ‡ˆˆ‡‰…†…ˆˆ‡‰ˆ†‰ˆ‰‰ˆ‰‡‡††‹ˆ‰ˆ†…ˆˆ‡‰††……‡‰‰ˆ…‰††………†ˆˆ‡‹‡‡ˆƒˆˆ‡†…„„„…††‰ˆˆ††ˆŠŠ‰‡…Šˆ‡‡‡ˆˆ‡‡ˆ‰ˆ‡ˆˆ†……ˆ‡‡‡ˆ‡‰ˆˆˆ‰……ˆ…‰‡ˆ‹ŒŒ‹‹‹Šˆ‰ˆˆ‰‹‰‡‡Š…†„‡‡ˆˆˆ‡ˆ…Œ‡Œ†‡‡ˆ‰ˆ†‰‰ˆˆˆ†…ˆ†…‡ˆ‰ˆˆˆ‡‰‰ˆ‰ŒŠŠŠ‰‰ŠŒ‡Œ‰ŽŽŽŠ‘‹Šˆ‹ŠŽŽ‰ŒŒ‹ŽŽŽ“Œ…ƒ‚„†€€‚„……‚‚‚…vstuw{yyzyzxwyzzzyxwxxywzyyyyyz}z~|{}~}|}|}|}}||{|z}}}}~}}}|}{|yx{}}|{{|€{~ywz|€|{~~z{}|z||€~~~~y}|~~|~}~|~~~~{||~{€||||€|€€€ƒ„€~‚€„‚„‚€‚€€‚€‚~†ƒ€€ƒ€…‚ƒ„„…€€ƒ€€ƒ€€~„ƒ„‰€†€€‚‚ƒ„ƒƒƒƒƒƒ„…„†††…„ƒ…„„†~€ƒ‚†…‚………‚ƒƒ„„…ƒ…€„…‚ƒ……‡…„†ˆ……„„ƒ„††‚„„ˆ…††ˆ†„€ƒƒ††ˆ†‡…‡„ˆƒƒˆ„„‡†ƒ…†…‡‡„„ˆˆˆ‡‡†‡†‡ˆˆ…Œ‡ˆˆ‡ƒ†‡‡‡ˆˆ‡†‡‡ˆ‹Œˆ‰‰Œ‹ŠŒ‰‡‡‹ŒˆŒŒ‹†ˆ‹ŠˆˆŒ†ˆ…ˆˆ‰ŠŒŠˆˆ‹‰ŠˆŠ‰ˆ‰‰ˆ‡‡‡‰‡‡‰‰‡‡ˆ‰ˆ…ˆ‡‹‡…ˆ†ˆˆˆˆ‰‰ˆ‡Š‡ˆ‡†‡‰ˆ‡‡‰ˆŠˆ‰‡…„‹…‡‡ˆ†‡†‰ˆ‰ˆ†…‡††††‡‡ˆ‡Š‡…†‡‡ˆ‡‡ˆ‡ˆˆ‡†‰‡‡‡††ˆ††‰‡‡‡„„†‡ˆ‰ˆ„†ˆ‡‰ˆŠ‡……ˆ†ˆˆ‰†„€ƒˆƒ„…†Š„ˆ…‹Š‹‹‹Š‰‹Š‰‡‰ŠŒˆˆŠ…„ƒ‡ˆ‡ˆˆ……†‡†‡‡‡‡ŽŠ‹ˆ…ˆ‡ˆ‡‡ƒˆ‰ˆ‡ˆŠ‰‰ˆ‡ˆŒŒ‹ˆ‹ŒŠ‰‰ˆ‡‰‰ŠŒŒ‹ŽŒŠˆ‹ŠŒŠ‰‹‰‹Ž‘ŽŒŽ’”ˆ€€‚„ƒ‚‚‚ƒ„u{u|zzzzyyyzyzzzy{yxwyxzzyxxy|x~|~}|{~~}zxx{yy}|xzz|||}}{}{{||}|~{}z~{|z}€|{~{{~~~}~|}~z…z~~~}€~~€ˆ|~~~~}~€}z~~}~~|€||~~~|~~~~€~€€„€€ƒƒ€€€‚‚‚‚€€‚€€€}€ƒ‡„€ƒ€€€€€€€€€€€€‚ƒ„„ƒƒ€€ƒƒƒ…ƒ„„„„…ƒ„ƒ„‚ƒ‚~~~€‚„‡„‚‚‚ƒƒ‚ƒƒƒ„…„„„~~~„ˆ…‚ƒ‡ƒ‚ƒ††ƒ…†‡ƒ‡†‡ƒ„ƒ†‡‡ˆ††‡‚‡†‡‡…‡‡‡ˆˆ‡‡‡‡„†ˆ„ˆƒ‡„‡„„††‡‹†‡‡‡†‡‡‡ˆ‡‡ˆ‡‡‡‡ŒŒ‹‡‡ˆ‹‹‹ŒŒ‰‡ŒˆŒ‹ˆ‹‡ˆ‹ŒŠˆˆ„„……ˆˆŒ‰ˆ‰ˆŠ‡ˆˆˆˆˆ‡‡ˆ‡‡‡‹ˆ†‡Š‡ˆ‡„ˆ……ˆˆˆˆ‡†‡‡…ˆ†‡‡‡‡……‡‡‡…††‡„ˆ‰ˆ‰‰‡††‡……†ˆ‰‡ˆ‰……‡†‡‡ˆ‡„…ˆ‡ˆ‰‹‡……„‡‰‰‡‡ˆˆ‡‡„‡‡ˆ‡…††‡†‰…‡‡‹‰ˆ‡ˆ‡ˆˆˆ‡†ˆˆ‡…„„ˆ†ˆˆ†…„„‡€„…†††…†„‡ˆ‰ŠŒ‰‰ˆŠ‰‡ˆ‹Œ‹ŠŠ‹‹ˆˆˆˆˆˆˆˆ‡‹‡ˆ‰‰ˆˆ‰‰ˆŠŠˆˆ…‹‡‰‹‹ˆŠ†ˆˆˆ‡‡ˆŠ‡‡Š‹‰Šˆˆ‹ˆŒ‹‰Š‹‹‰‹‰‡‹‹‹‰Š‹‰ŠŽŽŽ’”•Œ‚‚…€ƒ……‚‚‚„‚‚€ƒxvv{yzvuyy|zyzxzzz{ywwxyttxyxxxxz}}|~~~wy||zw{zy{{zx{|{z{{{|~}y}}}{}€€~|}€~yy|}€~€~}{{}yyz{}~~~~~{€~|}~~~€€~~{€€|}}~€}{|}}ƒƒ~~|ƒ€ƒ„„„‚€‚€€‚ƒƒƒ€……„‚€€€„„~ƒ€€ƒ~ƒƒ€~~ƒƒ„…ƒ„„……„„ƒ‡€„ƒ†‚‚ƒ…ƒ„„‚ƒ„ƒ‚„ƒ„ƒƒƒ‚ƒ‚‚‡…‡€~„„„ƒƒ…„…„„„‡ˆˆ‡†…‡††…‡‡‡‚ƒ†††††ˆ‡††ˆ†‡ˆˆ†††„†‡…ˆˆ‡ˆ‡†ˆˆˆ‡ˆƒ‡‡‰‡‹ŒŒ…‡†‡ˆ‡ƒ‡‰ˆ‡ˆ†ˆˆˆˆŒŒ‰‡‰ˆˆ‰‹ˆ‰ˆ‰‰ˆ‹‹ŒŒŒ‡‹Œ„ˆ‰ˆ‡ˆ‡‡‡ˆ‰‹Œˆˆ…‡‡ˆˆˆ„‡‹‡……Љ‡‰‡‡‡‹‰†‹††‡‡‡‡†‰††ˆ‰‡‡ˆ‰…†‡‡…ˆˆ‹‰‰†††‡†‹†‰†‡ˆ‰„‡…‡†ˆ‰ˆ†…ˆ‡…‹‰ˆ‡‡‡ˆ‡Š‡Š†‡‡‡ˆˆ‡‡†‡‡††‡†‡†‡‡ˆˆ‡††‰‰ˆ‡ˆ‰‰Š‡†„ˆˆ‰ˆ‚„„…„ƒƒˆ‰…‡†……‡†‡ˆˆˆŠˆ‡‡‰‰‰‹Œ‹‡†ˆˆŠ‰ˆ‰ˆ‰‰ˆˆ…‹ˆŠˆ‰ˆˆ‡‰‡‡‰ˆ‰‡ˆˆˆ‰‰†ˆ‰‡ˆ‡ˆ‡ˆˆ‡‰‰ˆ‰‰‹‹ŠŒŒŠ‰‹‡ˆŠŠ‰‰ŠŒŽŽŽŒ‹Š‹ŽŒŽ’”‘••’‹‚……‚……„‚……ƒ€„…vwy{|{vzx|zxxzz{z{yyyxxzv†x{yyxzy|z}~|~{zw{||w{{|{{yx{~|{{{{|y|~{|}€~{~z{{z€€€~~~z~|~~€~~~€‚~}‚~~|~‚~|~|€€|~~}{|€€€€ƒƒƒ‚‚‚ƒƒ~ƒ€~€~ƒƒ€€€~„‚„…‚€€ƒƒ~€‚…„€ƒ€€„‚€ƒ‚ƒƒƒ„…„~€ƒ‚‚€……„€€ƒƒ„ƒ‚…„ƒ„„„ƒƒƒ‚ƒ‚‚…~„…~‚‚…|„‡„‚‡†„ƒ…‡„ƒƒƒ…ƒ†‚ƒ‡„„‡ƒ‡„‡‚‡†…‚†‡††„‡‡‚‡…‡ˆ‡†ˆ„…‡‡‚‡‡‡‡‰……‡ˆˆ‡ˆˆˆˆˆ‡‡‡ˆˆˆˆ‡ŒŒ‡ˆˆˆˆˆˆ‰ˆ‡ˆ‰Šˆ‹ŒŒˆˆˆŒ†‹ˆˆŒˆ‰ˆˆ‡‰ˆˆ‰ˆˆ‡†‡‹ˆˆ‰…ˆ‰‡‡ˆŠ‡‡ŠŠ…‡‡‡ˆˆˆ‡ˆˆ‡‡…†‰ˆ††ˆ‰†‡††……†…†ˆ‰‹ˆ†……††‰…†††‡‰†…‡†…†‡‡ˆˆˆ…†Š…†ˆˆˆˆ‡‡‰ˆˆ‡„‡ˆ‡†‡ˆ‡ˆ‡ˆ‡‡‡ˆ†„ˆˆ‡‡†ˆ‰ˆ…‰‰ˆ‡ˆ‡„‡ˆ……„†‡‰€…ˆ‰‡‡†……†„…„„ƒ„‡ˆ„Љ‹‹‹ˆ‡††…†‡ˆˆ‹‰ˆˆˆˆ‹ŽŽˆ‹Œ‰‰‰‰ˆˆˆˆ‰ˆ‰ˆˆ‡ˆˆˆ‡‡ˆˆ†ˆˆ‰‰ˆ‰Š‰‰‰‰ŠŒŠ‰‰‰‰Š‹ŽŽŒ‹‹Š‹Œ‹ŠŒ‹‹ŽŽ’””‘‘•‘‡ƒ„‚…ƒ‡…………ƒƒƒ‚‚„wwzwzyvxvwxwxxz{zy{yyyyyywxy{xxyyy}|}zyxyyw|}{yyzyy{yw|{zz|{zzyz{z|~{{{~|~|||||{€~}€{~{}~~~€~|~€€€~}}}€~~~~€„}~||{~‚‚„}~~~|~}~|||{~€€€„„ƒƒ„„ƒ€ƒ‚ƒ~ƒ„€€€„…†„€ƒ„€~ƒ…ƒƒ~€‚„…€~„ƒ„~€…„€€€„…€‚€€‡„„‚†€€€„„€ˆ‡…ƒƒƒƒ…„ƒ‚‚…€ƒ‚‡ƒ‚‚„„„‚‡…„ƒƒ‚†„„…„‚„ƒ…„†‚†‡†„ƒ‡…‡‡‡€‡‡ˆ†‡‡‰‡…„†ˆ‡„‡††…ˆƒ‡†‡†ˆ…‡ˆˆ„ˆ‡‡„‰ˆŒ‰ˆ‡††ˆˆŒŠ‹†‹‡ˆ‰Š‰‡ˆ‡ˆ‡‰‡‡‰ŒŒŒŒŒ‰‰‡‹Š†ŠˆŒˆ‰ˆˆ‡‰‰ˆ‡ˆˆ‡ˆ‡‡ˆ‹…‹Šˆ‡ˆ‡‡…†Š…„……‹‰‰‰Š‡‰‡…†‰‡†…‡ˆˆ‡†…‹†‡†‡‡‡ˆ‰ˆ‡‡†…‹‡…‡‡…‹‡ˆˆˆ‡‡ˆŠ‡‡†……ЉЉˆˆ‡„‡ˆ‡†ˆ‡‡†ˆˆ†…ˆ‡‡……„‰†‡„ˆ‡‡‡ˆ‡‡ˆ†‰ˆˆˆ‡‡‡ˆ‡……‡„……ˆˆ„……„ƒ†…‚„„„„…†‡ˆŠ‰†‡‡†††Œ†‡ˆŒ‡ˆ‡ˆŠ‰‰‰‰‹Š‰‰Š‹‰ˆˆ‡‡‡‡‡‰ˆˆ…ˆ‡ˆˆ‡‰Š‡Š„ˆ‡‡‰ˆˆ‹‹‡‡ˆ‹Œ‹Š‰‰‡ˆŠŒ‹‰ŠŽŽ’”’•‘‡„…ƒ„ƒˆƒ‚„„ƒ‚…‚‚ƒxywvwyxzvuxzzzz||zy{xwzywwx{y}x‚z}yxwxwyy}w}}|{zw|y|xx{|{|{zz{{{zzz~|}{€€}{|{}||}€~|~~€}~~~}€~}~„€}„ƒƒ€ƒ|~€„€}|||~||||~|†|€~}}|}ƒ€‚‚‚€€‚€~~€€€€‚~„€„„‚€‚ƒ€€€ƒ„~~€€€ƒ€}„€€……}ƒƒ‚ƒƒ€~ƒƒƒƒ„€„ƒ„€ƒ€~}‚„„„…††‚ƒ‚ƒƒƒƒ„„ƒ„‚‚ƒ‚„„„…ˆ……ƒ‚‡ƒ„‡„ƒ…ƒ‚„…‡‡‡†ˆƒƒ„ƒƒ‡‡‡ˆ†„„ƒ‡ƒ‡‚‡ˆˆ„‡ˆ‡†‡…‡‡‡‡ˆˆ‡‡†ˆ‡‡†„‹‡ˆ‹ˆ‰‡‹‰ˆˆ‡‰‰‰‡ˆ‰‰‡Œˆˆ‰‰ˆ‹Š‹…„„…ˆ‰‡‡ˆ‡‰‰ˆˆ…ˆˆ‡‡‡‡‡†ˆ‰‰‰ˆ……†‡Š†‡‡‡†„ˆˆ‡ˆ‰‰†‡ˆ‡††‡ˆˆ„…†………†……„…ˆ‰…‡……†ˆ‡ˆ†††‡‡‡‡…‡‹ˆˆ‡‡…†‡ˆ……†‡……‡†……ˆ‡‡‡‡ˆ†‚ƒ…‡…„„ˆ†…„‰ˆ‡‰…„…‰…†ˆ„†…†……‡ˆˆ†…„†ˆˆ‰…††……‡‡†…‡††…†„‡‡Œ‰‰„ˆ…†‡‡‡†…†‡‰ˆ‰‡‰‰‹ˆ‰Ž‰‰Œ‹ˆˆ‡‡ˆŠ‡‰‡ˆ…Š…‡ˆ†ˆ‰‡Š‡‰‰Œˆˆ‡‹‰‰ˆˆ‹‡Š‰ˆ‰ŒŒ‹ŠŽ‹ŒŽŽ‘‘’“’”’ˆ„…ƒˆ†ƒ‚‚€„„ƒ„…wvvwvvwwxsxyzyzvvxyxxyzzzwxyyyzxwzxwwxyzxzxy~{|yy{w{xy{z|||{|{zz{{{{{{}~||}}}}€~{€€}{{€~}€€|€€~~‚€~~ƒƒ€€„‚€€~~€€€‚€ƒ€€~}|~€~||{}{‚ƒ€€€€~|€€€€ƒ€€~€ƒƒƒ„~€ƒ€€€„€‚…††…€„†ƒƒ€€ƒ„ƒƒƒ…ƒ‚ƒ‚ƒ„„„„„†„„ƒ‡€‚€„€†ƒ„„‚‚€…„†……ƒƒƒ„ƒ‡…„ƒƒ‚…‚„‚…ƒƒƒƒ‡‡‡‡ˆ…„………‡…ƒ‚„ˆ†‡„‡††‡ˆ†‡†‡„„…‡ˆ‡ƒ‡†††ˆ†ˆ‡‡††…‡ˆˆ‡ˆ‡ˆ‡‡‡‡‡‰‹Œ‡‡ŠŒ‡‡ˆŒŠˆˆŒŒŠŠ‰ˆˆ‡†ˆˆ‰ˆ‹‹‹„Œ‹ˆˆ‰ˆ‰†‡‡‰ˆˆˆˆˆ‡‡‰ˆˆ†ˆ‡ˆˆ‡‡‡ˆˆ†ˆ†…‰ˆ‰‡Œ‹‹ˆˆ‡ˆ†‡ˆŠ‰ˆ‰ˆ†‹‰Š†Š…Šˆˆ†…†‹…†‡‰ˆˆ††ˆ…„…ˆ‡‡ˆ‡†…‡‹ˆ‡†‡†ˆ†‡‡‡‡ˆ‡‡‡‡‡…„…ƒˆ†††‰†ˆ…‰ˆ‡ˆ……††……ˆ„‡†……ˆ†ˆ‡‡……†„ˆˆ……‡……‡„ƒ…‡†„…†ƒ†…‡ˆˆ………†ˆ‰‡……†ˆˆ…‡‡ˆˆˆˆŠŒŽŽ‰ŠŽ‹‰ˆ‰ˆ‡‡‡‡ˆ‰…†…†‰‰ˆ‹‰‰ˆ‰‰‹‰‡ˆˆ‡‹Œ‡ˆ‡‰‰‰ŠŒˆ‡Œ‹Š‹ŠŠŽŒŒ‘‘”‘•”””’ˆ††‚…„ˆƒ„„„„„uwzyy{wvwuvyzyyxzyy{xwzuyyxyyyzzxzxxwzy|{{ywxw|zw}x|{|x}{||||{{z||{zz}}~||{~|}||}‚|~€{~~€|}~€~‚€~†€€€ƒ„ƒ€ƒ‚ƒ‚‚ƒƒ}€‚€|€|…|‡~ƒ€€€€~~~€€~~‚€~|€{~}~~ƒ„€€€€€€~€€€€„€€€‚…ƒ€€ƒ…‚ƒƒ„ƒ~~~…„„…ƒ„„ƒ‚„„€€ƒ‚€€ƒ„ƒ„ƒƒƒƒ„†……ƒ†„…†„ƒ‚‚ƒ…„ƒ‚„„ƒ„ƒ……{„‚„………‡ˆ‚ƒƒƒ„‡ƒ††‡„„‚…ˆ†‚„ˆ…ƒ„„ˆ‡†…‡‡‡†ˆ‡†‡‡……„†‡‡…‡‡ˆ‡ˆ‡ˆ‰ˆ‹‰ˆˆˆŠŒ‡Œ‰‡‰Œ‰‰ˆ‡‡ˆ‡‡ˆˆ‰†…„‰Œ‰Œ‡ˆˆˆ‡ˆˆˆ‰…‡‡‡‰‡†„††…‡ˆ‡‰ˆˆˆ†………†‰…ˆ†Š‰ˆ†‡‡‡‡‡Š‡†‡ˆ‡†‡…‰††…‰Š‡……†Šƒˆˆ‡†„……‡†……†ˆˆ‡‰‡ˆ††‹‹…††ˆ‡†‡††‡‡‡‡‡‡ˆ„„‚‚…†…ˆ‰‰ˆ‡…ƒ‡ˆ‡†††„‡……„…†‰………ˆ†……ƒ„„…‡††…„…‰„………„……†…†…†…ˆˆ‡†‡††‡………††‚†ˆ‰ˆŠ‡Œ‰ˆ‹ŽˆŠ‰ˆˆˆ‰Š‡†…ˆˆˆ‡‰………†…‹…ˆˆ‰‡‰ˆ‡‡ˆ…Šˆ‡‰‰‰ˆˆ‹‹‹‹ˆ‰ŠŒ‰ŽŒŽ‹‘‘’‘“–‰ˆˆ‹…„ƒ„†ƒ~…ƒ„…„vwvwyzzxwvu{vxzwxytwxwytzyyxyxyxyxyxyxx|||{|||}yyxx{yzyyz{wwwyzz}~{yy|{}|}€}}z€~€€|||{{~{~~}€€€„€€€ƒƒƒ€€„…†ƒ‚„ƒƒ‚ƒƒ€}€€ƒ~||||}‚‚€€€~~‚€€€„€}~„€€€€~~ƒ€€~‚€€€„ƒ†‚€‚‚ƒ„„€€‚†€€‚‚€€~€ƒ„†„……ƒ…„………„„€…„„‚„ƒ„€„‚ƒƒƒƒ„„ƒ€ƒƒ…„……„„‡ƒƒƒƒ…ƒ…ƒ„ƒ‡„‡…„…‚„‚ƒƒ…†‡………„„ƒ‡†‡‡ˆƒ†€„…‡‡†‡…ƒ„…ˆˆ‡‡ˆˆˆ‹‰‡ˆ‡…†‡ˆˆ‡ˆ‡ˆˆŠ‰ˆ‡‡ˆˆ††‰ˆ‰‰‰‡‹ˆ‰‹Š‰ŠŒŠˆ‡‡‡‰‰‰‰‰ˆˆˆ‰‰ˆˆ‰‰‡†ˆ†‰ˆˆˆˆˆ†„Љ‡†‡ˆ‰ˆ‰‡‡…‡‡…‡Š„ˆˆŠˆ‡……„‡ˆŒŠ……‰ˆ‹‡‰†Š…‡ˆ‰Š‡†ˆ…‰…‰‡‰‡‡††…‹‡‡†‡†‡Šˆ‡…‡ˆ‡‡ˆˆˆ‡ˆˆ‡‡†‡†‡…†‡……‡„……ˆ†…ˆˆ‡†…‰†„„…††ˆ………„†ƒ„„……†……‚ƒ…†…ˆ…†……ƒ…†ˆ…Ž…ˆ…†„„„…‡ˆ‡‡‡††…„†ƒ……ƒ‡ˆˆ‡‰ˆ‰ˆ‰‰ˆ‰Œ‹‰‰‰ˆ‡ˆ†„ˆˆˆ‰†††„„…„…‡‡…………†‡†‡‰…‡ˆ‰‰‰‰ˆŠŒ‰‰‰‹ˆ‰‰‰ŒŒ‘’‘‘’‘‘”‰Š…ƒ€„…‚~„ƒƒ…†vxvwzzzzwwy{xww{xwwzxyxtxxxxxxwyxyyyywx{{zxx||zxyyx|w{y|}~||{z{}|z{y{{}|}~~}|z€€€~~„~~~€€„ƒ€ƒƒ€~€ƒ…„ƒ„„ƒ„„ƒ…„‚‚}ƒƒ€………ƒ„‚‚~€€€€‚€~|€€€~~~€~€€€€~€€€}~~ƒƒ€‚€€€„ƒ~€ƒ€€‚‚€„€ƒƒ„„‚ƒ…ƒ„„„ƒ„„€……ƒ„ƒƒ„„„ƒƒƒƒƒ€€ƒ€‚ƒƒƒ„…†‡€„ƒƒ…ƒ„„……ƒ…„€ƒ€€ƒ…ƒ€€…{…ƒƒ‚……„„ƒ‡ƒ„„ƒ„„„…ˆ‡‡…‡††…‡ˆ…ˆˆ…†ˆˆ‡‡……‡‰†…„………„…ˆ‡‡Œ‡‡ˆ‰ˆˆ‡ˆ‹ˆ‡‹‹ŠŠ‹ˆ‰‰‡ˆ‰ˆˆ‰ˆˆŠ‹‰ˆ‰ŠŒˆ‰ˆ…‡…ˆ‹†ˆ‡…‰‰ˆˆ†††‡ˆ†…‡†‰…††‡ˆ‡‰ˆˆ†‡‰†‰ˆ‰ˆ‡†…‡‰‡†‡†…†ˆŠ…„‡‡‡„ˆ‡Œˆˆˆ…†‰‰ˆ‡‡†…†…††‡‰‡‡Š‡‡‡‡‡ˆ‡‡†‡‡†‡‡†‡‡‰ƒ„„„………‡†ˆ†……‡„…†ˆ…„…ˆ…‡…„ƒ†……††‚……†‡ƒ…‡„„…†ƒ„„……„…†„†„ƒ‡†‡ˆ……††‡†…†ƒˆˆˆ‡‡ˆ‰‰‰Š‹ˆ‰Š‰Šˆˆ†‡††‡ˆ‰Š‰ˆ‰‰…ˆ…‡……†‰ˆ‰†‡‡ˆ„ˆ‰Š‹Šˆ‰Ž‹‹Œ‹ˆ‹ŠŒŽ‘–’–’•‘‰‰†ƒ„†…ƒ„‚ƒ„ƒ„„…wz{{{zz|vxzyywvyyvvx||{xxvvwyxxz{xxyyxvywx{x{zx~|zyzxxxyvwz{|}{zy||~~~|}|z|~€€}z‚€v}~}ƒ„„…ƒ€ƒƒ…„„€~€‚„~†„€‚„„‚ƒƒƒ€„„€€ƒ„„ƒ~ƒƒƒ€ƒ„ƒ€€ƒƒ„ƒ€€ƒƒƒ„ƒƒ„~€€~ƒ€€ƒ„„ƒ„…ƒ€€~€‚‚€~‚„€€€ƒ‚†…„…„‹„†‚‰†„‚„…„€„‚……€‚„ƒƒ€‚€‚€„„„ƒƒƒ„ƒ„ˆ……………ƒ…ƒ‡…‚€ƒ„„……ƒƒƒƒ…„„‚‡ˆˆ…‡ƒƒ‚‡†ˆƒ‡‡‡„„„‡…ƒˆ‡‡††ˆ‡ˆˆˆ†‰ˆ‡…„„ˆˆ……Ї‰ˆˆˆ‡ˆ‡‡ˆˆ‰ˆ‡†ŒŒ‰ˆ‰‰‰‡ˆ‹‹Œ‰‹‹Œ‰ˆ‰Š‰‰‰‹‰‡ˆŠ‰‡†‹†ˆˆŠŠ†‡‰‹‰‡‰‰…‡ˆ‡‡‰ˆ†‰ˆ…†ˆ‰‰‰ˆˆ‰ƒˆˆ‡‡‰Š‰…Ї‡ˆ‡‡‰†ˆˆ††‡‡ˆ‡ˆˆŠˆŒ‹‹‰„‡‰‰‹‡‡‡ˆ‡‡‡‡†‹†‡‰‡†‰‡‡ˆˆˆ‡„‡‡ˆ‡‡‡ˆˆ…„‡„……„‡„…„„ˆ‡…††‡ˆ„…†‡ˆ‡……ƒ„„…„ƒ†„„…ƒ…ˆƒ„…ƒ„ƒ†…‡‡ˆ†……†‡…„‡‡†„„…††‡††ƒˆˆˆˆˆˆ‡Š‹ˆˆ‡Š‹‹ˆˆ‰ˆ‡‡ˆ‡ˆˆˆˆˆ‡…†††……ˆ‡‡†‡††……‰‰ˆŒŒ‰‰‰ŠŽŠŽŒŒŽŒ”’”•”‘Ї†…„„…„…ƒ‚ƒ…„„†wv||xwxzuxyzyzyzwwwz{{zzwuttzxxyxxyzxywywxyzv}xyy{{zyyw}w{y{|}{{{zz~~{z~}{}|{{|}~€|€{‚~~€€~€€ƒƒ~„€€…~€ƒ€…„€ƒƒ„„ƒ………€„}€ƒ„‚€€}€ƒ€€€„€€€€€€€€€€~~‚‚~ƒ‚€€€€ƒƒƒ}‚€€€„‚~ƒƒ~€~ƒ€€„ƒ‚…€„ƒ…†„‚„„ƒ‚€…ƒ……ƒ‚‚ƒƒƒƒƒ€ƒ‚ƒ€€„…‡ƒƒ„„„‚ƒ‚ˆ€„„ƒƒ„…‡…„ƒ‚ƒƒ…ƒˆ…ƒƒƒ„†…ƒƒƒ„……„†„…€ˆˆ‡‡……‡‡ƒ„ƒˆ…………‡……††„…ˆ‡ˆˆ‡‰ˆˆ‡‡‡ˆˆ††…†ˆˆŠŠˆˆŠˆˆ‰ŠˆŠ‹Œ‹Š‰ˆˆˆˆˆ‡‡‡ˆˆˆ‰†ˆŠ‡ˆ‰‡‰‰‰‰ˆˆˆ‰‡‰‰‰ˆ‡‰‰…†ˆ‰ˆ‰ˆ†Šƒ‡‡……‰‰‡†Š‡‡‰†ˆ‡‡‡‡‰‡‡†ˆˆ‡‡‡‡†‡‡††ˆ‰‰‰ˆ‡ˆ‡‡ˆˆˆ††‡‡ˆ‡†‡…ƒ„ˆ‡‡…††…‰‰ˆ‡‰…„……„……‡„ƒ„……††‡…†„„„„†ˆ…„……„‡†……†ƒƒ…ˆ‡‡„…†……„……„„……ƒ…ˆ……†‡†‡†„ƒƒ†ƒ†„ˆ‡ˆˆˆˆ‡ˆ‹‰ˆ‰‹Š‹ˆ‰ˆˆˆˆˆ…„Ї……‡…‰†…„††‡†‡ˆ†…‰ˆŠ‰ŠˆˆŒˆ‹Š‹‰‹‘Ž‘’“’•Ž……†††‡‚††„„‚ƒƒƒ†vwxwvvxxvsuuvyxxvxyyw{zwvutvzxxyxwwxxxwvwvxvuwyy{{zyyyxyzzz€zzz{{||}{{z}~€|z{€|€}||~}|~€ƒ~z~~~{~‚ƒ‚~„€~€†‚„ƒƒ‚‚ƒ‚‚„~~€€‚‚ƒ„€€|€€€€„€€|||€ƒ‚€€€‚€€€€€„ƒƒ€ƒƒ€€€ƒ‚‚‚†…€€„‚€…~€„„…†€………‡†…„„’ƒ…„„ƒ€€€‚ƒ„€€ƒ…„ƒƒ€€†ƒƒ„…€„…‡€ƒ„„ƒ‡ƒƒ…ƒƒ„ƒ…‚„……„ƒ„„‚‡‡ˆˆ†ƒ„†…„†ƒ„„„ƒ„„„…„‡……„„……ˆ‰‰ˆ‰††…………„„…„†‰ˆˆˆ„‰‡‡ˆ…‡†‰…ˆ†‡‡‡ˆ‰ˆ‡‡‰ˆ‰ˆˆˆ‹ˆ‰‰‰ˆ‰ˆ‰ˆ‰Š‹ˆ‰Šˆ…‰‡‡ˆŠ‰‰ˆˆˆ‡‡ˆ‡ˆ‰ˆ†ˆŠ‰‹†…ˆ‡†ˆŠ‡Œ‰†„……ˆ‡†…Š…ˆ††‰‡‡‡‡ˆ‡†††ˆ‡†‡‡†………‡††‰‹‡‡‡‡‡ˆ‡‡†‡††ˆ‹„ˆ‡ˆˆ†‡‡‡‡†…„…ˆ‡‡ˆ‰‰…‡„„ƒ……„ƒ…‚‚‚„…„„…„…††…‚…‡†……††…„„„‚†……†‡‡…‚……††………†„†‡‡…†…ˆ…„ƒˆˆˆˆ‡ˆ‡ˆŠ‹‰ˆˆ‰ˆ‡‡†‡‡‡‡„‚ˆ…„†„……†………‡…‰…Š…„‰…ˆ‹‹‹‹Šˆ‹Š‡Šˆ‹Œ‰‰ŽŒ‘‘””Ž•Œ…‚‚„‚‚‚€‚ƒ„„ƒƒuwwwvwvvvvxwuyyzvzyyz{wuwvqxxzzzwwtxwwwxwvwxxxyyy|zz{|zyy{zz||zxy~{z{~{{|||{z}~|z|~€|~€€|{~~}~ƒ‚ƒ€€€€€}~€€€€ƒ†ƒ€ƒƒ‚€‡‡†€€‚ƒ…€€~}€€~~~|ƒ€ƒƒƒ€‚ƒ~ƒƒ‚~~~€€~‚€‚}€~€€~€€‚„€ƒ€ƒ……ƒ„‚„„…ƒ„…„‚…†…†…ƒ„……„ƒ€ƒ€„ƒ€ƒ…ƒƒ…€„‡…€‚…„…„ƒ„ƒ…†ƒƒƒ„†…„…†ƒ„ƒƒƒ„„„„…………………ƒ„ƒ‡„„…„ƒ…„„ƒƒˆˆ‡‚‡ˆ…„„…†……‡‡ˆˆ‰ˆˆˆ‰‡„‡ƒ‡†‡„ˆ„‡ˆ‡‡‡ˆ‡ˆ‡‡††ˆˆ‡‡‹‹‡‡‡†ˆˆ‹‰ˆ‰ˆ‹‰…ˆ‡ˆ‰‰„ˆ‡‡‡ˆ†††ˆ†ˆ…‡Œ‰‰‡…‰‡‰‰‰‡ˆ‰ˆ‰‡‡‡‡†…‰‡††‡Š‡†ˆ‡„ƒ…‡…ˆ‡ˆ‡†‡‹‹‹‡‡†‡Š‹‡‡†ˆˆ‡‡„‡†‡‡‰…‡ˆ‡††„‡ˆ‡ˆ„‡†‡‡‡†„‡„…†……„„†‰……ƒŽ………ˆ‡‰ˆ‡…†…„††…„……„€…†ƒ…………†ƒ…†……„…„………‡††††‡‡…ƒ‡„†ƒ„ˆ€ˆˆ‡…††ˆˆŠ‰Ž‰ˆˆŒˆŠˆ‡ˆ†ˆ‹„„ƒ„…†………†‡†ˆ†††…‰‡‡ˆ‹‹‹‹‹‹‹‹‹Š‹ŒŽŒ—“–‘’‘’•””’—Ž‹ƒˆ‡„ƒ‚‚‚…ƒ„€ƒuvvvwwttxt|wu{zyvvywzwutwxsuwvttwwtxwvxvwwxvyyzxwwxy|{yz||||{{||{|{{{z{z}{zz{{|{~ƒ€}~}}~ƒ{zz||€~ƒ…€€€~ƒƒƒ‚ƒƒ‚‚‚‚„ƒƒ…„€}€‚€€ƒ}€€~„€ƒƒƒƒƒ‚~€ƒ‚€€ƒ€~~}„€€‚€‚‚ƒ†€~€…~€ƒ„„€‚…€…ƒ…€†€„…€€††‚…†‚‚…„„……„ƒ€€………€€€„†‚ˆ‡…ƒ‚†††ƒ„„ˆƒ…ƒ„„…€‡‡…ƒ„ƒƒ„„ƒ„ƒ„‚ƒ„‡„†…„…†„„…†ƒ‰‡ŠŠŠ„…ƒˆ‡‡ƒ…‡‡‡…„ˆ‡‡‡……‹……‡ˆˆ…„ˆ‡‡‡ˆˆˆ„†‡‡ˆ‰‡‡‡‡ˆ……‡„ˆ‰‹ˆˆ†„‰‰Š‡‡ˆˆ‰ˆˆŒ‰ˆˆˆ†‰‡ˆˆˆˆˆ†‰ˆ‰Š‹ˆˆ‡‰ˆ‹‡Œ‹‰ˆ‡ˆ‰‰†…ˆ†…„ˆˆˆ‡ˆˆ‰‡‰‡ˆ„‡„†‡‡‡‰‡‰‡‡†‡‡ˆ‰‰ˆ‡…††‰‰‡ƒ‡‡‹ˆ‰…‡……‡ˆ…ˆ†ƒ„„…ˆ‡„„„…‡………‡…†……†‡„‰†„……‡‰‡……†…††ƒ…„……†…„ƒ……„…‡„„†ƒ†…ƒ…‡††‰‡‡…†ˆˆˆ‡…††ˆ€„…‡‡ˆ‡Š‹‹ŒˆŽ‰‰‹Šˆˆ‡‡ˆˆˆŒ……‰†‡……………†ˆ‰‡„…‡‡‡ˆ‡‹‰ŠŠ‹Š‹‹‹‹Œ‰Œ•Ž’•’‘’•Šƒ†€„‚‚‚„‚„„tutuwwtuuvwyy{zywywyvuvvwzututuvvywxwwxzvxwwxyxyxywzzzwvy}|y{{|}y{|}||{|{|y{||}~|}€{z~~~~~€€}‚„…€€„ƒ‚‚‚€€„…ƒ€‚~~|‚€~€~}ƒ|€€~ƒ€‚€‚ƒ„ƒ‚€€€€~|€€€~€~€~‚‚€€€€ƒ‚€~…~‚€€€ƒ„„€€…ƒ„ƒƒ…†…„„ƒ„‡ƒ€ƒ†ƒ„ƒ…‚€€„€ƒƒƒ…‚ƒ…ƒ‚„…„ƒƒ„ƒƒ…„…„„€ƒ„ƒƒ„‡……ƒ‚ƒ€„…„ƒ„„…€ƒ„…‡ƒˆ…„ƒ‚„„„„…„„……†‡…„……………………„„…ˆ…„‡„‡‡‡…ˆ„…‡‡ˆ‡†‡‰‰‰……„†‡†‰‰‰ˆ†…††ˆ‡ˆ‰‰‰ˆˆ‰Šˆ‡…‡‰‰‡†‰……‡ˆˆˆ††…‡‰‡‰ˆ††‡ˆ‰†‡††‡†ˆ‰‡…ˆˆ‡‰‡‡‡‡‰†…†‡Š„‡‡ˆˆ‰‰‡‡‡‡†‡ˆˆˆ‡„‡‡†„ˆ…‡‡Š„„…‡……†‡…ƒ„ƒ„„„‡‚…„…†‡‡…„‡‡†„„…‰„††‡†…†ˆ‡………ƒ……†…„††„ƒ…„ƒ…„……‡†……†††…„…†„†ˆ‡††‡‡††††‡ˆˆ‰ƒ„ƒ‡ˆ…ˆ‹‹‰‰‰Œˆ‰‹‹ˆ‰ˆŠˆˆˆ†ˆ††………†……†Š‡ˆ‡…………ˆ‡ŠŠŠˆ‹‹‹ŽŽ•‘“–‘˜•”‘‰‰ˆ~„ƒƒƒƒ€€€€„€€…vvuwvtvuuwwyuuvwyxttvtvvvuuuuuustwwwxwyywwwvxxyyv{xzyxwwwz|{{|{zzz{zxwzx~}€z{}yz|||€}€€~~}~}~~…€„€}€€„ƒ€€€€ƒ†€„ƒƒ€€€€ƒ~ƒ~}~~}ƒ~‚€€~…ƒ„‚ƒƒƒƒ€„€€€€ƒƒ€‚‚ƒ‚…€Š€~ƒ€„€…………„€ƒ€€„€ƒ……„„†ƒ€ƒ„…„†„„……ƒƒ…€‚‚€„……€€~~€ƒ„†…„‚…„„ƒ„ƒ„„„ƒ…‚ˆ‡‡Œ€„„„„…ƒ…„„ƒƒ„„ƒ‡‚„‚†„‚…††…‚ˆƒ„ƒƒ„‡‡‡„„ƒ„ƒ„ˆ†„…„…………‰…ˆƒ†‡†ƒ‡…ˆ‡ˆ†ˆˆ‰…††Šˆ‡ˆ‰‡†…‡ˆˆ…ˆ‰‰ˆ‡†††‰ˆ‰‡…‡‡‰‹ˆ‡ˆ‰‡‰ˆ‰„‹„…‰‰‹……„‹‡…ˆ‰ˆ‰ˆ‰‰‰‹‹ˆŠ‰‰ŠŠ†‡…ˆ‰‰ˆŒ‡‰†ˆ‡‡„††‡ˆˆ‰‡…‡‡ˆ†‡‡ˆ‰ˆ„ˆ†‡‡ˆ‡ˆ‡Œ…ˆˆˆ…‡‡ˆ…ƒ„…„†‡ˆ†……‰‡ˆˆ…„‰‡…„‚…„……„…ˆ…„‡…ƒ…„„„†………‡„…„„‚…………„……„„‡†‰ƒƒ…„†‡‰ˆ‡‡ˆˆ†„†‡ˆ†„‡ƒ…„„ˆ‰…ˆ‹Š‹ˆŠŒˆ‰Œ‹‰ˆ‰Š‡‡Š‰ˆ††††…‡†‡…ˆ‡‡‡‰ˆ†ˆŠŠŠŠŒŒŽ‹Œ‘Ž’‘‘”–‘˜–•’‹…†€ƒƒƒ„…‚€…ƒƒ„tuwwuuvuuvr{wwwwtuvvvvvwvuwttswstuuuwxxyvwwwvwwwv|w{z~wwvwz|||}~|||}|~~|w{}}€|zz{|~z€€}}€~€€€~€€€€€~ƒ€€†‚ƒ„‚ƒ€‚„„ƒ~ƒƒ€‚‚ƒ€€~~~}}}ƒ}€€~|}ƒƒƒƒƒ‚€€ƒ€€€€€€€~€~‚€~€}‚€‚€}ƒ~€…~‚€€€€ƒƒƒ€„†„‚†„ƒ…ƒ…€ƒ€€€……€€„„„…‡„~€„„„„€ƒ~ƒ„„…„‡„ƒ€„†…………„ƒˆƒ†ƒ„‚ƒ„€…„ƒƒ†…ƒ‚…‚„„ƒ‚ƒƒ„ƒ€„ƒƒ‚„ƒƒƒƒ„„……ƒˆƒ……†„…††…†„ƒ†ˆ†„‡‡‡†‡ˆˆ‡………„……‡ˆˆ„…ˆˆ†‡„‡‡‰†…†………„ˆ†…†ˆ‰ˆ†…†‰ˆ‡‡‡ƒˆ†‡‡‰‡††…„…†…†‰‰‰‰ˆ‰‡‡‰‰ˆˆ‰‰ˆˆ††‡‰ˆˆ†……†‡‡ˆˆ„‰†‡‡‡†‡‡†………ˆ‡‰ˆ‡…‰†‡‡†‡…„†‡…„†ˆ‡„„„ƒƒ…†††…†‰ˆ†‰„…ˆ‡ƒƒƒ‡…„………†…„„„…†……†„…„ƒ„…†……„‚†…†ˆ…„……‡‡ˆ‰‡ˆˆ‡…‡ˆ„‡‡ˆˆ‡ˆ‡‡‡…„‡„‰ˆˆ‡‰‰‡‰Š‰ˆ‹‹‡‰‹ˆ‰Œ‰‰‡‡ˆ‰‰†…‰†ˆˆ††…††ˆ†‡Š†‰‰‰Š‹Œ‹‹Œ‘‘’‘‘’“”•‘™—–•Ž……‚ˆ‚ƒ‚†‚‚ƒ€€ƒ‚ƒtopsutrwvrtvtsttssvtssvwwxwuvvvusrsuvvtttvywwvvvwwzzyxyxxxxwxz~|}~~~|{xxz{|}||}{y|z{€}{|}€€}€€€~€€…€„€€~…€„„€~ƒ~€ƒ„ƒƒ€€~~~~~|‚€~…~ƒƒƒ€‚‚ƒ€„ƒ€€„}‚‚‚€~||~~~€ƒ…€ƒ‚€}~‚…€€€€~}€€ƒ„…ƒy†…ƒ„†„„„„„€‚‡ƒƒ‚…ƒ…††††„‡ƒ„‚ƒ‚………ƒ„ƒ€ƒ…„„„„ƒ‡ƒ„ƒ‡……‚ˆ€…ƒ„ƒ„„…„…ƒ„†…‡„„ƒƒ…‹„ƒƒ„„‚ƒ„…„††…„„…„„ƒ„…„„ƒ‡‡ˆˆ†ƒ…‡ˆˆ‰…ЅЇ‡…………„‰‡ˆ‰Š‰‰‡†ƒŠ†‡‡‰‡ˆˆ‰ˆ†‡‰†‡ˆŠ††…‰ˆ‰‰‡††‡ˆ…‰…ˆ…‰…‹†‡…‡ˆ‰ˆ‰‰‰‰ˆˆ‰ˆ‡ˆ‡‡††††‡†††……‡‡ˆ‡Œ††…‡‡ˆˆ‡‡‡…ˆ…‹‡‰…Šˆ‡‡‡†‡‡Š‡‡†‡†……„…‡…„…‡ƒ……‰ˆˆ†‰‡ƒƒ„„„„ƒ……‚…‚†ƒ……‡„…ƒ‰‡‡„†„…„ƒƒ€€ƒ…†ˆ…„ˆˆˆ†ˆˆ……‡„…‡ˆ‡‡ˆ‰‰ˆ‡†…†ˆˆ†ˆ……ˆ‡††‡‰‡‰‰‰ˆ‹ˆ‹Š‰‰‹Š‰ŠŠ‰‰‰ˆ‡Œ†…ˆ…†‰Šˆ‡…†ˆ……‰ˆŠ‹Œ‹ŠŒŽŽŒŒ‘‘‘‘’‘‘•‘•—––‰…ƒƒƒ€‚†ƒƒ€€„„tpotvvvwtvvwutttrutttruwvvvrsusxsttxwtvxwywwvvwyxvzzzzx|}}yxxyz||{|z{|yyy{|}~||}~|~zy~~€ƒ~~~}~€€|{~~~€€~~€€€€‚„„€€‚€€„„ƒ€€€ƒ~~}|{}{{~€ƒ~€ƒ€}€‚‚„€ƒ€ƒ€€‚€€ƒ‚€ƒ}}}~€ƒƒ~~ƒ€‚€ƒ{~€€€€€€‚‚ƒ€€€€‚…„…ƒ‚………„€‚€‡…„‚ƒ„ƒ€………„ƒ‚„ƒƒ…………„……ƒ†‡|„„„……„‚……ƒ‰|†‡…{…ƒ„„„„…|ƒ………ƒƒ‚‚‚„……†„„„„…†„„„„…„‚„„„…€„„…„„‡…‚…‡…„…†„„Š…………†‡„‰‡‡†††††„„…††„†‡ˆˆ‰‰†ˆ‡ˆ‡‡……††…†…ˆ†††…ˆ………І………‡†„ˆ‹ˆˆˆ‡ˆ‰†…‡‡ˆˆ††…†‡†……†††††ˆ‡…†††‡…†‡‡‡ˆ†ˆ‡‡ˆˆ‡‡‡„‰ˆˆ†…‡‡‡ˆ‡‡‰…„†ˆ‡‡…‡ˆ…„„Їˆ‡…ˆ„‡††…………………„ƒ‚……„…ƒ„„……„††„…ƒ……††‡†ˆ†ˆ…†……„††††ˆ‡‡‡†‡‰‰ˆŠ†…†„ƒ„ƒ‡‡‡ˆ‡‡‡‰‡ˆ‰‰Š‰ˆˆŠŒ‰ˆ‰ˆ‰ˆŠŠŠˆ…„†‡‰ˆ†Š‰Š‹†‡‡ˆˆ‰Š‹‹ŒŽ‘ŒŽ’Œ‘’‘’’’’’“”•–••””’ˆˆ„ƒ€~…†…‚€‚ƒ…soqoptqstwvsvurssstsvvuuvvvsssryussvuvxxyxzxwxxx|yzxwxxvxz{wxy||y}~{{|{w~|~}{y{z~~{}€}}~€‚{~~}ƒ~€€€…€}~~€€€‚€ƒƒ‚€}„ƒƒ€~„„„ƒ€~~||ƒƒƒ€~ƒ€€‚‚‚ƒ‚ƒ€ƒ€€€ƒ‚€ƒ‚€ƒ„ƒ„ƒƒ€€ƒ‚€~„…†ƒ€|ƒ€‚~~{„€€ƒ€€ƒ„ƒ~ƒ…‚€………††ƒ„„‡‡‡‡‡„„ƒ………„ƒ‚„…„ƒ„…†………†ƒ‰‰‰‚†…†††‚…„„ˆ†…„ˆ‡‡‚ˆƒ‡†‡ˆˆ††…†„„„ˆ‚Œ‚†…„ƒˆˆ‡…„ƒ…„…††‚‡†ˆ„…„†„‚ƒƒ……………†††‡‡‰ˆ†…†…‡ƒ‰ˆ‰Šˆˆ‹‡ˆˆ‰…†„†‡‰†‡…†ˆ‡‡††‹‹‹…ˆ„‡‡‰‡Œ…‰‰‹‹‹‰‡…Љ†‹Šˆ†‰‰‹‰††‡ˆ‰‡‡‡ˆŠ‹†‡††………‡‡††Šˆˆ‡†‡‡‡‡‡ˆˆ‹‡‰ˆ‹†††‡††…Šˆ‡‡„‡„†ˆ‰‡„„„…‡‰ˆ„…‹‡ˆˆ†……‡‡„‡‡†ƒ…………‰…††‡…‰ƒ††††…†‡„……‡‡†…†…†ˆ†‡ˆˆ‹‡ˆ†…‡ˆˆ†…ˆ‰‹‹Œ†Š‹Š‰‰†‡†‡ˆˆ†‡ˆˆ‰ŽŠ‰‰‰ŠŠŠ‰‹ŒŠ‹‰ŠŠŠˆˆˆ‡‹ˆŠ‹ŠŠ‹‹ˆ‡ˆ‰‰ˆ†ŠŽ‘ŒŒŽ‹Ž’’“““““‘“•”•“‘•‘–Ž‹‡„ƒ‚ƒ…‚‚ƒƒ„ƒ„†ppqqnsqqqsuusrrurvuqrvuqvvssssr{vssutwwrrtyvwwvswzzwwvxzwyzwy|zzz{{|{yxx}{z|{|{z}€{z|€||€ƒ}~€~ƒ}~€€€€€€ƒ€€€€€‚„„ƒ€‚€€€~ƒ€~~€€ƒ€‚}}„€€}‚€‚ƒ€~‚€‚ƒƒ|„€€€xƒ€€~‚€~~~„€|ƒ€ƒƒ~}~‚€‚‚ƒƒ€€ƒ…ƒ†…‚…ƒ€†„„ˆ†ƒƒ…€„„„…„…„€ƒ‚††ƒ…„ƒƒƒˆƒƒ„„†ƒ„…„€…„„†‡€„…ƒ€†…„………†ƒƒ„‡ƒƒ€……‚ƒ„…„…„„„{‡……‚‰……„……††ƒˆ†……€†‡…„…‡‡ˆ‡ˆ……††ˆ„‡ˆˆ…„ƒ‰ˆˆ…††††………†‡ˆ††………††††††††‡‰…‹†‰‡……………‡†‡‡‡ˆ„ˆ†…†ˆˆ††‡ˆˆˆ‡†‡ˆ‡†‡‡‡…†……††‡††‡‰‰‰ˆ‡ˆ‡‡ˆ‡‡‰„……‡†……‡ˆˆ…‰ˆ‡ˆ††‡ˆ…„‡ˆ‡‡‡ˆ†„‰ˆˆ‰…‰…‡‡„…ˆ………„…†ˆ†„„ˆ‡…††„…†„…„„…ˆ‡‡ˆŒŒŒˆˆˆ‰ˆˆˆ‡‰Š‹†ˆˆˆŠ‰Š‰‹†„…‰ˆˆˆ‰‰‰‰ˆ‡†‡ˆ‰Žˆˆ‹Œˆ…‰‹‰Š‹‹ˆŠŠˆ‰Šˆˆ‹ŠŠ‹‰‡‰ŠŠ‰‰Š‰‹‰‰‹Ž“’’’”’‘‘‘‘‘’’“Ž‘‘’“•–’‘‘•‘•މЄ„ƒƒƒ‚ƒ‚…‚ƒƒ…rnrrpnooqrsutqtussurssvuutvutqtvvrussvxrtuxvwwvruwzwwuyvzzxw|{|{{{yz|{}y|||{y}}z~~€~|€~€~€|‡|~ƒ€€€€€~~€€‚„ƒ„€ƒ‚€€„„„ƒƒ‚ƒƒ}~€~„}„ƒƒƒ‚„ƒ‚ƒ€ƒ„„‚ƒ„„„|ƒƒ€€~‚€€ƒ…‚„„…ƒ€‚„„„„…€|ƒ€ƒ†ƒƒ…€…€‚€………„……ˆ†„ƒ†‡€……ˆ………‡ˆ……„‚„„……„…„„‚„„…„ˆ‰‰„…‚†ƒ†‚‡…„…‡„………„…„„ƒ…„†„……‡‚ƒƒ†‚†…†‡‡ƒ‡„…„„„…‡‡…‡†ˆ„…„†…‡ƒ„…ˆ„…………††…‡ˆ‡††††††‰ƒ…ˆˆ†‡‚‰ˆˆ††…Š…‡…ˆ‡ˆˆˆ…†…††ˆˆ‰ˆ‰†‡ˆŒŒŒŒ‡‰‡ˆˆˆ†Š…‰‡‡…ˆ†ˆ…‰‰‰†‡‡Šˆ†…‰ˆŠ„†ˆˆ…І‡…†……‡ˆŠˆ†Šˆˆ‡‡†‡†‡ƒ„ƒ‡ˆ„„ˆˆ‡ˆ‰Š‰‰ˆ„ƒ‚„ˆˆˆ†…†‡‡‰ˆ‰ˆ……††‡‡‡‡‡†…„‰…‰………‡†‡‡……‡ˆˆ†…†‡ˆŠ‰ŒŠ‹‹Š‹Œ‹‰Š‰‡‡ˆ‡†‰ŠŒŒ†‰ˆ‰ˆ‹‡ˆˆ‰ˆ‹ŠŠ‹Ž‡‰‰ŒŽˆŒ‰‰Š‰ˆ‹‹‹Š‹‹Š‰‹‹Š‰‰Ž‰‰‰‰Œ‡‰‹ŽŽ”””‘’’‘•””“’’••“‘’Œ‰‡‚‚†‚‚ƒƒ„ƒƒ„ƒ„nqmmpqpprtrsrqswsustqoprstsusuuurrusrrvwvuuuvuwystyywvvwzwxwxxxz||zyyyxxxyxyz{{{{}~€{{~}€€~€~~€ƒƒ…€~€€€€~€~~€~~€„€ƒƒ‚€€€€‚ƒ„ƒƒƒƒƒ„~€}}|{‚}~~zz‚ƒƒ€‚ƒƒ€„€€ƒ„ƒ}ƒ‚|€ƒ€{€€€€‚ƒ‚€€~~‚‚‚ƒ}€€€€€€ƒ€~……………‚…‡ƒƒƒ„…ˆ†…€‡…„„‡…„†…„€……„~ƒ„……ˆw‡…„ƒ„„„ƒƒ€€‡„…†„„ƒ„„†ƒ€…„†ƒ†………„…„„|‡…ƒ……†……‡x‡‡‡…ƒ†…ƒ„„…„ƒ†ƒ„‚‡†…†‡…ˆ††‡…………ˆ„„…†Š†ƒˆˆˆ‡…‡†……†‡ˆˆ†…ƒƒ…††………††‡ˆˆˆˆˆ††‡‹†ˆ…‡‡…‡…‡††…†…†…††††‡‡ˆ…††……„†‰ˆ‡‹‡ˆ„‡‡†ˆˆ‡Š‡‡‡‡ˆ‡‡…†…„ƒ‚†‡……‡…†‡ˆ„†ˆ‡„„……‡‡ˆ‰†…‰ˆ‡ˆ…ˆˆ…‡‡‡…†…„…‰…†„„‰†………‡†‹…‡…†ˆˆ…†††ˆ‹ŠŠ‰Š‹‹‹‹‹‹ŒŽˆ‰‡ˆˆˆˆˆˆ‰‰Š‰†ˆ‡ˆ‰ˆˆ‡‡ˆˆŠ‰‹ˆˆˆˆ‰Š‰ˆ‰ˆ‰ˆ‰‹Š‹‡ˆˆ‰ŠŠ‹Š‹Š‰ˆ‰‰‹ŠŠŽŒ‹ŽŽ’‘“’“‘‘Ž’•’’’’’””‘”‘‡†…„†„ƒƒ‚„ƒƒ‚‚‚„pqnlnppopsqrpqpurtpnoppqrtvuwvtrrrtuwotyxsuvwr{{ttxxwtwwywywvwyw~|{{yx{x}|||}~~|{}{€||~}€€€€€~†ƒ…‚~€€€„}~~ƒ€€‚{†„ƒ€ƒƒ€€€€ƒ‚„„~‚„ƒƒƒ~~}~z„~„~|„€„‚€ƒ€€ƒ|ƒ€|€€|€~‚€‚‚ƒ‚„€„€‚ƒ‚€€}ƒ‚†‚‚ƒ‚„‚ƒ}…†ƒ………„„†ƒ…‚……ˆ……‡…ˆƒ‡††€……†………y…………ˆ~…„†‚…ƒ„ƒ„‚‡††‡††…„‚…†„‚…ƒ†…†„……„…‡ƒˆ…†„†…ˆ…†„ˆ‡ˆ…†…ƒ…ƒ„„……………ƒ†ƒˆ‡……‡„ˆ……ˆ……‰…‰…Ž}‡‹‡†‡†‡†††‡…‰†‡ˆˆ…‰ˆŠ…ˆ†Š‡‡……†‰‰‰‡‰…‹‰‹‡ˆ„‡†‰‡„‡‡‡‡††‡‡†‡†ˆ…ˆ†Š†‹…ˆ…‡‰ˆ†‹‡Œ„ˆ‡‹†ˆˆŠ‡ˆ‰‡ˆ‰†‰‡ˆ„‰ƒˆ‡Š…ˆ…‡†ˆƒ‡†ˆ‡‡‡†‡‡ˆŠˆ†Š‰ˆ‰ˆ‰ˆ…‡‰‡††…„†ˆ†‡‡„Šˆ†…„„…‰…†‡……„†„†††‡ŠŒ‹Š‹Ž’Œˆ‰ˆˆˆˆ‡‡‡ˆ‰‰ˆˆ‰‰‰Š‰‰‰‡‹ŠŠŠ‰ˆˆŠˆŠ‰‰‰‰‰Œ‰ŒŒŒ‹‰ˆŠŠ‹ŒŽŒŠŠŒ‹ŠŽ‹ŽŒ‘•‘”Ž“‘”‘•‘–’’’’‘“““††‡††…‚ƒ„~‚‚‚€„lrmnnqoqopqqrrpssrpvvwntrvuuuvvwrrutwsrvxtrxvuzxtwwxwvwwywvwtxwxxw{zxx{{{{{||{|}}z€|‚|€€€~†€€€„€~€€…€€€~‚„ƒƒƒƒƒƒƒ‚~„€„€~~~|‚}‚~~~„ƒ‚ƒƒƒ€„‚ƒƒ‚€‚„€|€€€€€€€€‚€~}~‚‚~€~…€‚‚€‚‚€€€€…ƒ„‚ƒ…†„……†€†|ˆƒˆ‚†€€€ƒz…€€…ƒ€„‚‡ƒ……†…ƒ„…ƒ†…‡†…†…†…„„„…„„„ˆƒ„ƒ„‡„‡‚†ƒ…ƒ†„ƒ„‡ƒ„„……„…††……„„„„‚††…‡†ˆˆˆ‰‰……‡‡‰†Œ‡†…‡‡‡†‡†……‡…‰‡…………„ƒ‰‡††‡‰‡‰…ˆ‰‡ŠˆŠ…‰……†ˆ††‡‡‡…†‡†‡‡‡‡‡ˆ††……†‡††‰ˆ‡†ˆ‰ˆ‡‡‡Œ†‡‡††‡ˆ‹ˆ‡‡‡ˆˆˆ‡‡†‡‡‡‡‡‰‡ˆ‡‡…‡„‡†ˆˆ‡Š…ˆ†‡‹‡…„………„‰ˆ„‰‰ˆ‡‡…………„…††…††ˆ…Іˆ………„……ˆ‰…‹…ŠŠŒŽŠŽ‹ŽŽŽŽŽŽ‹‡Œ‰ˆ‡‡‡‹†‰Š‰‡ˆ‰Šˆˆˆ‡ˆˆŠ‰‰‰‰‰‹Œ‹‹‰‹‹‹‰‰‹‰Š‰ŒŽŽŒ‰‹ŒŠ‹‹‹‹‹‹‘ŽŽŽŽŽ”‘”“Ž’’‘“’’“•••”•“‘ކ‰‡†„„ƒ„‚‚ƒ‚€„nvnmnlmnoqqpqqpoorrqotrrttrsrsstusutrtvtvtutttwwustwvttsssuvszwyvxwywx|z{{zxw|{}|~}~€€€z‚€€€€€‚‚„€€ƒ‚€€€€‚€~€‚‚‚„ƒ‚€€ƒ~~~|‚‚ƒƒƒ‚„„„„ƒ‚ƒƒƒƒƒ~€€|€€€€€‚€€‚€€ƒ„„ƒƒ‚‚‚€€€€~„ƒƒ„ƒ„„ƒ€†‡‡†…€…„„‚„…………„…„…„„„††……††…„‚„„…††„‡€†€…„„ƒ„„„„…„‰……†††……††…ƒ…„†…†…††‡‡‡……ƒ‰„…„…ƒ‡†‡ˆ‰‰ˆ†…†………†…†‡‡„†ƒ………†‰ˆ‡ˆ‹‰‰ˆ‰‰ˆˆŒŒ‹‰Š†‹‹‹Š‰ˆˆ‡ˆ‡‡†‡†‰‰‰Š‹†‹ˆˆˆ†‡ˆ†…‡ˆ…ˆ†ˆ…‰‡‡‡ˆˆ†ˆ‰ˆ‡‡ˆˆ‡‡ˆˆ†††‡ˆ†ˆˆŠˆˆˆˆ‡‡‡‰†ˆˆˆˆ‡ˆˆˆ‹‰‹ˆ‡‰…ƒ„‡‡‡‡‡ˆ‡ˆ‡‡…‰„‰‰‰ˆ‡‹‡‡‡ˆˆˆˆ‡‰…‰ˆˆ‰„††……‡‰ˆ‰ˆ‰†‰…‡‡ˆ‡‡Š…‡‡†ƒ†Šˆ‡ˆˆˆˆˆˆˆ‰ŒŽŽŽŽŠ‹‹‰ˆˆˆˆ‡‰‰‰‰ˆˆ‰‰‰ˆ‰‹‹‰ŒŒŒŒŒ‹‹‹‹‰‰ˆ‹ŒŒŒŒŒŒ‹ŒŒŒ‹ŒŽŽŒŽ’’Ž’’‘’’Ž““’‘““’‘•’’“’’‘ŽŠ‰…†„„…„…ƒƒ~€ƒ \ No newline at end of file diff --git a/tutorial/detection/barcode/tutorial-barcode-detector-live.cpp b/tutorial/detection/barcode/tutorial-barcode-detector-live.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60297d0fa99d255a310c4262bca7cceb97f57114 --- /dev/null +++ b/tutorial/detection/barcode/tutorial-barcode-detector-live.cpp @@ -0,0 +1,121 @@ +//! \example tutorial-barcode-detector-live.cpp +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpImageConvert.h> +#include <visp/vpDetectorDataMatrixCode.h> +#include <visp/vpDetectorQRCode.h> +#include <visp/vpV4l2Grabber.h> + +int main(int argc, const char** argv) +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020100) && (defined(VISP_HAVE_ZBAR) || defined(VISP_HAVE_DMTX)) + int opt_device = 0; + int opt_barcode = 0; // 0=QRCode, 1=DataMatrix + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--device") + opt_device = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--code-type") + opt_barcode = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] + << " [--device <camera number>] [--code-type <0 for QRcode | 1 for DataMatrix>] [--help]" + << std::endl; + return 0; + } + } + std::cout << "Use device: " << opt_device << std::endl; + + try { + vpImage<unsigned char> I; // for gray images + + //! [Construct grabber] +#if defined(VISP_HAVE_V4L2) + vpV4l2Grabber g; + std::ostringstream device; + device << "/dev/video" << opt_device; + g.setDevice(device.str()); + g.setScale(1); + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture cap(opt_device); // open the default camera + if(!cap.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + cap >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + //! [Construct grabber] + +#if defined(VISP_HAVE_X11) + vpDisplayX d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#endif + vpDisplay::setTitle(I, "ViSP viewer"); + + vpDetectorBase *detector = NULL; +#if (defined(VISP_HAVE_ZBAR) && defined(VISP_HAVE_DMTX)) + if (opt_barcode == 0) + detector = new vpDetectorQRCode; + else + detector = new vpDetectorDataMatrixCode; +#elif defined(VISP_HAVE_ZBAR) + detector = new vpDetectorQRCode; + (void)opt_barcode; +#elif defined(VISP_HAVE_DMTX) + detector = new vpDetectorDataMatrixCode; + (void)opt_barcode; +#endif + + for(;;) { + //! [Acquisition] +#if defined(VISP_HAVE_V4L2) + g.acquire(I); +#else + cap >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + //! [Acquisition] + vpDisplay::display(I); + + bool status = detector->detect(I); + std::ostringstream legend; + legend << detector->getNbObjects() << " bar code detected"; + vpDisplay::displayText(I, 10, 10, legend.str(), vpColor::red); + + if (status) { + for(size_t i=0; i < detector->getNbObjects(); i++) { + std::vector<vpImagePoint> p = detector->getPolygon(i); + vpRect bbox = detector->getBBox(i); + vpDisplay::displayRectangle(I, bbox, vpColor::green); + vpDisplay::displayText(I, bbox.getTop()-20, bbox.getLeft(), "Message: \"" + detector->getMessage(i) + "\"", vpColor::red); + for(size_t j=0; j < p.size(); j++) { + vpDisplay::displayCross(I, p[j], 14, vpColor::red, 3); + std::ostringstream number; + number << j; + vpDisplay::displayText(I, p[j]+vpImagePoint(10,0), number.str(), vpColor::blue); + } + } + } + + vpDisplay::displayText(I, (int)I.getHeight()-25, 10, "Click to quit...", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // a click to exit + break; + } + delete detector; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/detection/barcode/tutorial-barcode-detector.cpp b/tutorial/detection/barcode/tutorial-barcode-detector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d9523f5b66396638602fa1cd06d94f3c937a997 --- /dev/null +++ b/tutorial/detection/barcode/tutorial-barcode-detector.cpp @@ -0,0 +1,105 @@ +//! \example tutorial-barcode-detector.cpp +//! [Include] +#include <visp/vpDetectorDataMatrixCode.h> +#include <visp/vpDetectorQRCode.h> +//! [Include] +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpImageIo.h> + +int main(int argc, const char** argv) +{ + //! [Macro defined] +#if (defined(VISP_HAVE_ZBAR) || defined(VISP_HAVE_DMTX)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) + //! [Macro defined] + try { + vpImage<unsigned char> I; + vpImageIo::read(I, "bar-code.pgm"); + +#ifdef VISP_HAVE_X11 + vpDisplayX d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#endif + + //! [Create base detector] + vpDetectorBase *detector = NULL; + //! [Create base detector] + +#if (defined(VISP_HAVE_ZBAR) && defined(VISP_HAVE_DMTX)) + int opt_barcode = 0; // 0=QRCode, 1=DataMatrix + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--code-type") + opt_barcode = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] + << " [--code-type <0 for QRcode | 1 for DataMatrix>] [--help]" + << std::endl; + return 0; + } + } + //! [Create detector] + if (opt_barcode == 0) + detector = new vpDetectorQRCode; + else + detector = new vpDetectorDataMatrixCode; + //! [Create detector] +#elif defined(VISP_HAVE_ZBAR) + detector = new vpDetectorQRCode; + (void)argc; + (void)argv; +#elif defined(VISP_HAVE_DMTX) + detector = new vpDetectorDataMatrixCode; + (void)argc; + (void)argv; +#endif + + vpDisplay::display(I); + + //! [Detection] + bool status = detector->detect(I); + //! [Detection] + std::ostringstream legend; + legend << detector->getNbObjects() << " bar code detected"; + vpDisplay::displayText(I, (int)I.getHeight()-30, 10, legend.str(), vpColor::red); + + //! [Parse detected codes] + if (status) { + for(size_t i=0; i < detector->getNbObjects(); i++) { + //! [Parse detected codes] + //! [Get location] + std::vector<vpImagePoint> p = detector->getPolygon(i); + vpRect bbox = detector->getBBox(i); + //! [Get location] + vpDisplay::displayRectangle(I, bbox, vpColor::green); + //! [Get message] + vpDisplay::displayText(I, (int)(bbox.getTop()-10), (int)bbox.getLeft(), + "Message: \"" + detector->getMessage(i) + "\"", + vpColor::red); + //! [Get message] + for(size_t j=0; j < p.size(); j++) { + vpDisplay::displayCross(I, p[j], 14, vpColor::red, 3); + std::ostringstream number; + number << j; + vpDisplay::displayText(I, p[j]+vpImagePoint(10,0), number.str(), vpColor::blue); + } + } + + vpDisplay::displayText(I, (int)I.getHeight()-15, 10, "A click to quit...", vpColor::red); + vpDisplay::flush(I); + vpDisplay::getClick(I); + } + delete detector; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/detection/face/CMakeLists.txt b/tutorial/detection/face/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..258eaf1512e6eb094cbea5681e19f64b4dc237d6 --- /dev/null +++ b/tutorial/detection/face/CMakeLists.txt @@ -0,0 +1,26 @@ +project(tutorial-detection-face) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +# set the list of source files +set(tutorial_cpp + tutorial-face-detector.cpp + tutorial-face-detector-live.cpp) + +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/haarcascade_frontalface_alt.xml" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/video.mpeg" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-face-detector-live.cpp ${data}) +endforeach() + diff --git a/tutorial/detection/face/haarcascade_frontalface_alt.xml b/tutorial/detection/face/haarcascade_frontalface_alt.xml new file mode 100644 index 0000000000000000000000000000000000000000..5a6f275458055536d92cd5ec491f20c06fa6431c --- /dev/null +++ b/tutorial/detection/face/haarcascade_frontalface_alt.xml @@ -0,0 +1,26161 @@ +<?xml version="1.0"?> +<!-- + Stump-based 20x20 gentle adaboost frontal face detector. + Created by Rainer Lienhart. + +//////////////////////////////////////////////////////////////////////////////////////// + + IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + + By downloading, copying, installing or using the software you agree to this license. + If you do not agree to this license, do not download, install, + copy or use the software. + + + Intel License Agreement + For Open Source Computer Vision Library + + Copyright (C) 2000, Intel Corporation, all rights reserved. + Third party copyrights are property of their respective owners. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistribution's of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistribution's in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * The name of Intel Corporation may not be used to endorse or promote products + derived from this software without specific prior written permission. + + This software is provided by the copyright holders and contributors "as is" and + any express or implied warranties, including, but not limited to, the implied + warranties of merchantability and fitness for a particular purpose are disclaimed. + In no event shall the Intel Corporation or contributors be liable for any direct, + indirect, incidental, special, exemplary, or consequential damages + (including, but not limited to, procurement of substitute goods or services; + loss of use, data, or profits; or business interruption) however caused + and on any theory of liability, whether in contract, strict liability, + or tort (including negligence or otherwise) arising in any way out of + the use of this software, even if advised of the possibility of such damage. +--> +<opencv_storage> +<haarcascade_frontalface_alt type_id="opencv-haar-classifier"> + <size>20 20</size> + <stages> + <_> + <!-- stage 0 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 14 4 -1.</_> + <_>3 9 14 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0141958743333817e-003</threshold> + <left_val>0.0337941907346249</left_val> + <right_val>0.8378106951713562</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 4 -1.</_> + <_>7 2 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0151513395830989</threshold> + <left_val>0.1514132022857666</left_val> + <right_val>0.7488812208175659</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 15 9 -1.</_> + <_>1 10 15 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2109931819140911e-003</threshold> + <left_val>0.0900492817163467</left_val> + <right_val>0.6374819874763489</right_val></_></_></trees> + <stage_threshold>0.8226894140243530</stage_threshold> + <parent>-1</parent> + <next>-1</next></_> + <_> + <!-- stage 1 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 6 -1.</_> + <_>5 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6227109590545297e-003</threshold> + <left_val>0.0693085864186287</left_val> + <right_val>0.7110946178436279</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 3 -1.</_> + <_>9 5 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2906649392098188e-003</threshold> + <left_val>0.1795803010463715</left_val> + <right_val>0.6668692231178284</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 12 9 -1.</_> + <_>4 3 12 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0025708042085171e-003</threshold> + <left_val>0.1693672984838486</left_val> + <right_val>0.6554006934165955</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 10 8 -1.</_> + <_>6 13 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9659894108772278e-003</threshold> + <left_val>0.5866332054138184</left_val> + <right_val>0.0914145186543465</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 14 8 -1.</_> + <_>3 10 14 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5227010957896709e-003</threshold> + <left_val>0.1413166970014572</left_val> + <right_val>0.6031895875930786</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 10 -1.</_> + <_>14 1 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0366676896810532</threshold> + <left_val>0.3675672113895416</left_val> + <right_val>0.7920318245887756</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 5 12 -1.</_> + <_>7 12 5 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3361474573612213e-003</threshold> + <left_val>0.6161385774612427</left_val> + <right_val>0.2088509947061539</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 3 -1.</_> + <_>7 1 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6961314082145691e-003</threshold> + <left_val>0.2836230993270874</left_val> + <right_val>0.6360273957252502</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 17 2 -1.</_> + <_>1 9 17 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1488880263641477e-003</threshold> + <left_val>0.2223580926656723</left_val> + <right_val>0.5800700783729553</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 6 4 2 -1.</_> + <_>16 7 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1484689787030220e-003</threshold> + <left_val>0.2406464070081711</left_val> + <right_val>0.5787054896354675</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 17 2 2 -1.</_> + <_>5 18 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1219060290604830e-003</threshold> + <left_val>0.5559654831886292</left_val> + <right_val>0.1362237036228180</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 12 -1.</_> + <_>14 2 3 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0939491465687752</threshold> + <left_val>0.8502737283706665</left_val> + <right_val>0.4717740118503571</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 4 12 -1.</_> + <_>4 0 2 6 2.</_> + <_>6 6 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3777789426967502e-003</threshold> + <left_val>0.5993673801422119</left_val> + <right_val>0.2834529876708984</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 18 8 -1.</_> + <_>8 11 6 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0730631574988365</threshold> + <left_val>0.4341886043548584</left_val> + <right_val>0.7060034275054932</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>5 8 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6767389974556863e-004</threshold> + <left_val>0.3027887940406799</left_val> + <right_val>0.6051574945449829</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 5 3 -1.</_> + <_>15 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0479710809886456e-003</threshold> + <left_val>0.1798433959484100</left_val> + <right_val>0.5675256848335266</right_val></_></_></trees> + <stage_threshold>6.9566087722778320</stage_threshold> + <parent>0</parent> + <next>-1</next></_> + <_> + <!-- stage 2 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 10 9 -1.</_> + <_>5 6 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0165106896311045</threshold> + <left_val>0.6644225120544434</left_val> + <right_val>0.1424857974052429</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 14 -1.</_> + <_>9 11 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7052499353885651e-003</threshold> + <left_val>0.6325352191925049</left_val> + <right_val>0.1288477033376694</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 5 4 12 -1.</_> + <_>3 9 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8069869149476290e-003</threshold> + <left_val>0.1240288019180298</left_val> + <right_val>0.6193193197250366</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 5 -1.</_> + <_>8 5 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5402400167658925e-003</threshold> + <left_val>0.1432143002748489</left_val> + <right_val>0.5670015811920166</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 10 8 -1.</_> + <_>5 10 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6386279175058007e-004</threshold> + <left_val>0.1657433062791824</left_val> + <right_val>0.5905207991600037</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 6 9 -1.</_> + <_>8 3 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9253729842603207e-003</threshold> + <left_val>0.2695507109165192</left_val> + <right_val>0.5738824009895325</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 1 8 -1.</_> + <_>9 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0214841030538082e-003</threshold> + <left_val>0.1893538981676102</left_val> + <right_val>0.5782774090766907</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 6 -1.</_> + <_>0 9 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6365420781075954e-003</threshold> + <left_val>0.2309329062700272</left_val> + <right_val>0.5695425868034363</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 17 -1.</_> + <_>9 0 2 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5127769438549876e-003</threshold> + <left_val>0.2759602069854736</left_val> + <right_val>0.5956642031669617</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 4 -1.</_> + <_>11 0 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0101574398577213</threshold> + <left_val>0.1732538044452667</left_val> + <right_val>0.5522047281265259</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 6 4 -1.</_> + <_>7 1 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0119536602869630</threshold> + <left_val>0.1339409947395325</left_val> + <right_val>0.5559014081954956</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 1 6 16 -1.</_> + <_>14 1 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8859491944313049e-003</threshold> + <left_val>0.3628703951835632</left_val> + <right_val>0.6188849210739136</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 18 8 -1.</_> + <_>0 5 9 4 2.</_> + <_>9 9 9 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0801329165697098</threshold> + <left_val>0.0912110507488251</left_val> + <right_val>0.5475944876670837</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 10 4 -1.</_> + <_>13 15 5 2 2.</_> + <_>8 17 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0643280111253262e-003</threshold> + <left_val>0.3715142905712128</left_val> + <right_val>0.5711399912834168</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 4 8 -1.</_> + <_>3 1 2 4 2.</_> + <_>5 5 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3419450260698795e-003</threshold> + <left_val>0.5953313708305359</left_val> + <right_val>0.3318097889423370</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 14 10 -1.</_> + <_>10 6 7 5 2.</_> + <_>3 11 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0546011403203011</threshold> + <left_val>0.1844065934419632</left_val> + <right_val>0.5602846145629883</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 6 16 -1.</_> + <_>4 1 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9071690514683723e-003</threshold> + <left_val>0.3594244122505188</left_val> + <right_val>0.6131715178489685</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 18 20 2 -1.</_> + <_>0 19 20 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4718717951327562e-004</threshold> + <left_val>0.5994353294372559</left_val> + <right_val>0.3459562957286835</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3013808317482471e-003</threshold> + <left_val>0.4172652065753937</left_val> + <right_val>0.6990845203399658</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5017572119832039e-003</threshold> + <left_val>0.4509715139865875</left_val> + <right_val>0.7801457047462463</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 9 6 -1.</_> + <_>0 14 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0241385009139776</threshold> + <left_val>0.5438212752342224</left_val> + <right_val>0.1319826990365982</right_val></_></_></trees> + <stage_threshold>9.4985427856445313</stage_threshold> + <parent>1</parent> + <next>-1</next></_> + <_> + <!-- stage 3 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 3 4 -1.</_> + <_>5 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9212230108678341e-003</threshold> + <left_val>0.1415266990661621</left_val> + <right_val>0.6199870705604553</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 16 -1.</_> + <_>9 11 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2748669541906565e-004</threshold> + <left_val>0.6191074252128601</left_val> + <right_val>0.1884928941726685</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 13 8 -1.</_> + <_>3 10 13 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1409931620582938e-004</threshold> + <left_val>0.1487396955490112</left_val> + <right_val>0.5857927799224854</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 8 2 -1.</_> + <_>12 3 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1878609918057919e-003</threshold> + <left_val>0.2746909856796265</left_val> + <right_val>0.6359239816665649</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 12 -1.</_> + <_>8 12 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1015717908740044e-003</threshold> + <left_val>0.5870851278305054</left_val> + <right_val>0.2175628989934921</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 8 6 -1.</_> + <_>15 3 4 3 2.</_> + <_>11 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1448440384119749e-003</threshold> + <left_val>0.5880944728851318</left_val> + <right_val>0.2979590892791748</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 19 -1.</_> + <_>9 1 2 19 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8977119363844395e-003</threshold> + <left_val>0.2373327016830444</left_val> + <right_val>0.5876647233963013</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 4 -1.</_> + <_>11 0 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0216106791049242</threshold> + <left_val>0.1220654994249344</left_val> + <right_val>0.5194202065467835</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 9 3 -1.</_> + <_>6 1 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6299318782985210e-003</threshold> + <left_val>0.2631230950355530</left_val> + <right_val>0.5817409157752991</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 10 4 -1.</_> + <_>13 15 5 2 2.</_> + <_>8 17 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9393711853772402e-004</threshold> + <left_val>0.3638620078563690</left_val> + <right_val>0.5698544979095459</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 6 10 -1.</_> + <_>3 3 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0538786612451077</threshold> + <left_val>0.4303531050682068</left_val> + <right_val>0.7559366226196289</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 15 15 -1.</_> + <_>3 9 15 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8887349870055914e-003</threshold> + <left_val>0.2122603058815002</left_val> + <right_val>0.5613427162170410</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 8 6 -1.</_> + <_>6 7 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3635339457541704e-003</threshold> + <left_val>0.5631849169731140</left_val> + <right_val>0.2642767131328583</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 12 10 -1.</_> + <_>10 4 6 5 2.</_> + <_>4 9 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0240177996456623</threshold> + <left_val>0.5797107815742493</left_val> + <right_val>0.2751705944538117</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 4 4 -1.</_> + <_>8 4 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0543030404951423e-004</threshold> + <left_val>0.2705242037773132</left_val> + <right_val>0.5752568840980530</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 1 2 -1.</_> + <_>15 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4790197433903813e-004</threshold> + <left_val>0.5435624718666077</left_val> + <right_val>0.2334876954555512</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 2 2 -1.</_> + <_>3 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4091329649090767e-003</threshold> + <left_val>0.5319424867630005</left_val> + <right_val>0.2063155025243759</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 11 1 3 -1.</_> + <_>16 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4642629539594054e-003</threshold> + <left_val>0.5418980717658997</left_val> + <right_val>0.3068861067295075</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 6 4 -1.</_> + <_>3 15 3 2 2.</_> + <_>6 17 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6352549428120255e-003</threshold> + <left_val>0.3695372939109802</left_val> + <right_val>0.6112868189811707</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 8 2 -1.</_> + <_>6 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3172752056270838e-004</threshold> + <left_val>0.3565036952495575</left_val> + <right_val>0.6025236248970032</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 1 3 -1.</_> + <_>3 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0998890977352858e-003</threshold> + <left_val>0.1913982033729553</left_val> + <right_val>0.5362827181816101</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 12 2 -1.</_> + <_>6 1 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4213981861248612e-004</threshold> + <left_val>0.3835555016994476</left_val> + <right_val>0.5529310107231140</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2655049581080675e-003</threshold> + <left_val>0.4312896132469177</left_val> + <right_val>0.7101895809173584</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 6 2 -1.</_> + <_>7 16 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9134991867467761e-004</threshold> + <left_val>0.3984830975532532</left_val> + <right_val>0.6391963958740234</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 4 6 -1.</_> + <_>0 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0152841797098517</threshold> + <left_val>0.2366732954978943</left_val> + <right_val>0.5433713793754578</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 12 2 -1.</_> + <_>8 12 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8381411470472813e-003</threshold> + <left_val>0.5817500948905945</left_val> + <right_val>0.3239189088344574</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 1 9 -1.</_> + <_>6 6 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1093179071322083e-004</threshold> + <left_val>0.5540593862533569</left_val> + <right_val>0.2911868989467621</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 17 3 2 -1.</_> + <_>11 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1275060288608074e-003</threshold> + <left_val>0.1775255054235458</left_val> + <right_val>0.5196629166603088</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4576259097084403e-004</threshold> + <left_val>0.3024170100688934</left_val> + <right_val>0.5533593893051148</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 4 -1.</_> + <_>9 6 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0226465407758951</threshold> + <left_val>0.4414930939674377</left_val> + <right_val>0.6975377202033997</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 3 2 -1.</_> + <_>8 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8804960418492556e-003</threshold> + <left_val>0.2791394889354706</left_val> + <right_val>0.5497952103614807</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 17 3 3 -1.</_> + <_>11 17 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0889107882976532e-003</threshold> + <left_val>0.5263199210166931</left_val> + <right_val>0.2385547012090683</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 2 -1.</_> + <_>8 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7318050377070904e-003</threshold> + <left_val>0.4319379031658173</left_val> + <right_val>0.6983600854873657</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 6 2 -1.</_> + <_>11 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8482700735330582e-003</threshold> + <left_val>0.3082042932510376</left_val> + <right_val>0.5390920042991638</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 14 4 -1.</_> + <_>3 13 14 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5062530110299122e-005</threshold> + <left_val>0.5521922111511231</left_val> + <right_val>0.3120366036891937</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 18 4 -1.</_> + <_>10 10 9 2 2.</_> + <_>1 12 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0294755697250366</threshold> + <left_val>0.5401322841644287</left_val> + <right_val>0.1770603060722351</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 3 3 -1.</_> + <_>0 11 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1387329846620560e-003</threshold> + <left_val>0.5178617835044861</left_val> + <right_val>0.1211019009351730</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 6 6 -1.</_> + <_>11 1 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0209429506212473</threshold> + <left_val>0.5290294289588928</left_val> + <right_val>0.3311221897602081</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.5665529370307922e-003</threshold> + <left_val>0.7471994161605835</left_val> + <right_val>0.4451968967914581</right_val></_></_></trees> + <stage_threshold>18.4129695892333980</stage_threshold> + <parent>2</parent> + <next>-1</next></_> + <_> + <!-- stage 4 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 9 -1.</_> + <_>1 3 18 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8206960996612906e-004</threshold> + <left_val>0.2064086049795151</left_val> + <right_val>0.6076732277870178</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 10 2 6 -1.</_> + <_>12 13 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6790600493550301e-003</threshold> + <left_val>0.5851997137069702</left_val> + <right_val>0.1255383938550949</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 19 8 -1.</_> + <_>0 9 19 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9827912375330925e-004</threshold> + <left_val>0.0940184295177460</left_val> + <right_val>0.5728961229324341</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 9 -1.</_> + <_>9 0 2 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8959012171253562e-004</threshold> + <left_val>0.1781987994909287</left_val> + <right_val>0.5694308876991272</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 6 1 -1.</_> + <_>7 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8560499195009470e-003</threshold> + <left_val>0.1638399064540863</left_val> + <right_val>0.5788664817810059</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 6 1 -1.</_> + <_>13 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8122469559311867e-003</threshold> + <left_val>0.2085440009832382</left_val> + <right_val>0.5508564710617065</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 4 6 -1.</_> + <_>5 13 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5896620461717248e-003</threshold> + <left_val>0.5702760815620422</left_val> + <right_val>0.1857215017080307</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 6 1 -1.</_> + <_>13 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0100783398374915</threshold> + <left_val>0.5116943120956421</left_val> + <right_val>0.2189770042896271</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 12 6 -1.</_> + <_>4 6 12 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0635263025760651</threshold> + <left_val>0.7131379842758179</left_val> + <right_val>0.4043813049793243</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 12 2 6 -1.</_> + <_>15 14 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1031491756439209e-003</threshold> + <left_val>0.2567181885242462</left_val> + <right_val>0.5463973283767700</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 2 -1.</_> + <_>10 3 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4035000242292881e-003</threshold> + <left_val>0.1700665950775147</left_val> + <right_val>0.5590974092483521</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 3 1 -1.</_> + <_>10 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5226360410451889e-003</threshold> + <left_val>0.5410556793212891</left_val> + <right_val>0.2619054019451141</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 4 14 -1.</_> + <_>3 1 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0179974399507046</threshold> + <left_val>0.3732436895370483</left_val> + <right_val>0.6535220742225647</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 4 4 -1.</_> + <_>11 0 2 2 2.</_> + <_>9 2 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4538191072642803e-003</threshold> + <left_val>0.2626481950283051</left_val> + <right_val>0.5537446141242981</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 1 14 -1.</_> + <_>7 12 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0118807600811124</threshold> + <left_val>0.2003753930330277</left_val> + <right_val>0.5544745922088623</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 0 1 4 -1.</_> + <_>19 2 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2713660253211856e-003</threshold> + <left_val>0.5591902732849121</left_val> + <right_val>0.3031975924968720</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 4 -1.</_> + <_>8 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1376109905540943e-003</threshold> + <left_val>0.2730407118797302</left_val> + <right_val>0.5646508932113648</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 18 3 2 -1.</_> + <_>10 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2651998810470104e-003</threshold> + <left_val>0.1405909061431885</left_val> + <right_val>0.5461820960044861</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 2 -1.</_> + <_>9 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9602861031889915e-003</threshold> + <left_val>0.1795035004615784</left_val> + <right_val>0.5459290146827698</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 6 -1.</_> + <_>4 7 12 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8448226451873779e-003</threshold> + <left_val>0.5736783146858215</left_val> + <right_val>0.2809219956398010</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 6 -1.</_> + <_>3 14 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6430689767003059e-003</threshold> + <left_val>0.2370675951242447</left_val> + <right_val>0.5503826141357422</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 2 12 -1.</_> + <_>10 12 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9997808635234833e-003</threshold> + <left_val>0.5608199834823608</left_val> + <right_val>0.3304282128810883</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 18 3 2 -1.</_> + <_>8 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1221720166504383e-003</threshold> + <left_val>0.1640105992555618</left_val> + <right_val>0.5378993153572083</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 2 -1.</_> + <_>11 0 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0156249096617103</threshold> + <left_val>0.5227649211883545</left_val> + <right_val>0.2288603931665421</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 9 3 -1.</_> + <_>5 12 9 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103564197197557</threshold> + <left_val>0.7016193866729736</left_val> + <right_val>0.4252927899360657</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 2 -1.</_> + <_>11 0 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7960809469223022e-003</threshold> + <left_val>0.2767347097396851</left_val> + <right_val>0.5355830192565918</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 5 -1.</_> + <_>7 1 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1622693985700607</threshold> + <left_val>0.4342240095138550</left_val> + <right_val>0.7442579269409180</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 4 4 -1.</_> + <_>10 0 2 2 2.</_> + <_>8 2 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5542530715465546e-003</threshold> + <left_val>0.5726485848426819</left_val> + <right_val>0.2582125067710877</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 1 3 -1.</_> + <_>3 13 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1309209987521172e-003</threshold> + <left_val>0.2106848061084747</left_val> + <right_val>0.5361018776893616</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 5 3 -1.</_> + <_>8 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0132084200158715</threshold> + <left_val>0.7593790888786316</left_val> + <right_val>0.4552468061447144</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 10 12 -1.</_> + <_>5 4 5 6 2.</_> + <_>10 10 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0659966766834259</threshold> + <left_val>0.1252475976943970</left_val> + <right_val>0.5344039797782898</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 9 12 -1.</_> + <_>9 10 9 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9142656177282333e-003</threshold> + <left_val>0.3315384089946747</left_val> + <right_val>0.5601043105125427</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 12 14 -1.</_> + <_>2 2 6 7 2.</_> + <_>8 9 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0208942797034979</threshold> + <left_val>0.5506049990653992</left_val> + <right_val>0.2768838107585907</right_val></_></_></trees> + <stage_threshold>15.3241395950317380</stage_threshold> + <parent>3</parent> + <next>-1</next></_> + <_> + <!-- stage 5 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1961159761995077e-003</threshold> + <left_val>0.1762690991163254</left_val> + <right_val>0.6156241297721863</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 6 4 -1.</_> + <_>7 6 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8679830245673656e-003</threshold> + <left_val>0.6118106842041016</left_val> + <right_val>0.1832399964332581</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 11 8 -1.</_> + <_>4 9 11 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9579799845814705e-004</threshold> + <left_val>0.0990442633628845</left_val> + <right_val>0.5723816156387329</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 10 16 4 -1.</_> + <_>3 12 16 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0255657667294145e-004</threshold> + <left_val>0.5579879879951477</left_val> + <right_val>0.2377282977104187</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 16 2 -1.</_> + <_>0 1 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4510810617357492e-003</threshold> + <left_val>0.2231457978487015</left_val> + <right_val>0.5858935117721558</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 2 -1.</_> + <_>9 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0361850298941135e-004</threshold> + <left_val>0.2653993964195252</left_val> + <right_val>0.5794103741645813</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 6 10 -1.</_> + <_>3 2 3 5 2.</_> + <_>6 7 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0293349884450436e-003</threshold> + <left_val>0.5803827047348023</left_val> + <right_val>0.2484865039587021</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 8 15 -1.</_> + <_>10 10 8 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0144517095759511</threshold> + <left_val>0.1830351948738098</left_val> + <right_val>0.5484204888343811</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 8 6 -1.</_> + <_>3 14 4 3 2.</_> + <_>7 17 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0380979403853416e-003</threshold> + <left_val>0.3363558948040009</left_val> + <right_val>0.6051092743873596</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 2 2 -1.</_> + <_>14 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6155190533027053e-003</threshold> + <left_val>0.2286642044782639</left_val> + <right_val>0.5441246032714844</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 7 6 -1.</_> + <_>1 13 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3458340913057327e-003</threshold> + <left_val>0.5625913143157959</left_val> + <right_val>0.2392338067293167</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 4 4 3 -1.</_> + <_>15 4 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6379579901695251e-003</threshold> + <left_val>0.3906993865966797</left_val> + <right_val>0.5964621901512146</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 14 6 -1.</_> + <_>2 9 7 3 2.</_> + <_>9 12 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0302512105554342</threshold> + <left_val>0.5248482227325440</left_val> + <right_val>0.1575746983289719</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 4 -1.</_> + <_>5 9 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0372519902884960</threshold> + <left_val>0.4194310903549194</left_val> + <right_val>0.6748418807983398</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 8 8 -1.</_> + <_>6 9 4 4 2.</_> + <_>10 13 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0251097902655602</threshold> + <left_val>0.1882549971342087</left_val> + <right_val>0.5473451018333435</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 3 2 -1.</_> + <_>14 2 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3099058568477631e-003</threshold> + <left_val>0.1339973062276840</left_val> + <right_val>0.5227110981941223</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 4 2 -1.</_> + <_>3 4 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2086479691788554e-003</threshold> + <left_val>0.3762088119983673</left_val> + <right_val>0.6109635829925537</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 2 8 -1.</_> + <_>11 14 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0219076797366142</threshold> + <left_val>0.2663142979145050</left_val> + <right_val>0.5404006838798523</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 5 3 -1.</_> + <_>0 1 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4116579703986645e-003</threshold> + <left_val>0.5363578796386719</left_val> + <right_val>0.2232273072004318</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 5 18 8 -1.</_> + <_>11 5 9 4 2.</_> + <_>2 9 9 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0699463263154030</threshold> + <left_val>0.5358232855796814</left_val> + <right_val>0.2453698068857193</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 6 -1.</_> + <_>6 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4520021290518343e-004</threshold> + <left_val>0.2409671992063522</left_val> + <right_val>0.5376930236816406</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 1 1 3 -1.</_> + <_>19 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2627709656953812e-003</threshold> + <left_val>0.5425856709480286</left_val> + <right_val>0.3155693113803864</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 6 -1.</_> + <_>9 6 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0227195098996162</threshold> + <left_val>0.4158405959606171</left_val> + <right_val>0.6597865223884583</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 1 1 3 -1.</_> + <_>19 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8111000536009669e-003</threshold> + <left_val>0.2811253070831299</left_val> + <right_val>0.5505244731903076</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 2 3 -1.</_> + <_>3 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3469670452177525e-003</threshold> + <left_val>0.5260028243064880</left_val> + <right_val>0.1891465038061142</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 8 12 -1.</_> + <_>12 4 4 6 2.</_> + <_>8 10 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0791751234792173e-004</threshold> + <left_val>0.5673509240150452</left_val> + <right_val>0.3344210088253021</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 6 3 -1.</_> + <_>7 2 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0127347996458411</threshold> + <left_val>0.5343592166900635</left_val> + <right_val>0.2395612001419067</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 9 10 -1.</_> + <_>6 6 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3119727894663811e-003</threshold> + <left_val>0.6010890007019043</left_val> + <right_val>0.4022207856178284</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 6 12 -1.</_> + <_>2 4 2 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0569487512111664</threshold> + <left_val>0.8199151158332825</left_val> + <right_val>0.4543190896511078</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 3 -1.</_> + <_>15 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0116591155529022e-003</threshold> + <left_val>0.2200281023979187</left_val> + <right_val>0.5357710719108582</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 5 3 -1.</_> + <_>7 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0334368608891964e-003</threshold> + <left_val>0.4413081109523773</left_val> + <right_val>0.7181751132011414</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 3 3 -1.</_> + <_>15 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9437441155314445e-003</threshold> + <left_val>0.5478860735893250</left_val> + <right_val>0.2791733145713806</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 8 3 -1.</_> + <_>6 15 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6591119132936001e-003</threshold> + <left_val>0.6357867717742920</left_val> + <right_val>0.3989723920822144</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 3 3 -1.</_> + <_>15 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8456181064248085e-003</threshold> + <left_val>0.3493686020374298</left_val> + <right_val>0.5300664901733398</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 3 3 -1.</_> + <_>2 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1926261298358440e-003</threshold> + <left_val>0.1119614988565445</left_val> + <right_val>0.5229672789573669</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 12 -1.</_> + <_>10 7 6 6 2.</_> + <_>4 13 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0527989417314529</threshold> + <left_val>0.2387102991342545</left_val> + <right_val>0.5453451275825501</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 6 -1.</_> + <_>10 7 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9537667334079742e-003</threshold> + <left_val>0.7586917877197266</left_val> + <right_val>0.4439376890659332</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 5 2 -1.</_> + <_>8 10 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7344180271029472e-003</threshold> + <left_val>0.2565476894378662</left_val> + <right_val>0.5489321947097778</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8507939530536532e-003</threshold> + <left_val>0.6734347939491272</left_val> + <right_val>0.4252474904060364</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 8 -1.</_> + <_>9 10 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0159189198166132</threshold> + <left_val>0.5488352775573731</left_val> + <right_val>0.2292661964893341</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 6 -1.</_> + <_>8 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2687679845839739e-003</threshold> + <left_val>0.6104331016540527</left_val> + <right_val>0.4022389948368073</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 3 3 -1.</_> + <_>12 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2883910723030567e-003</threshold> + <left_val>0.5310853123664856</left_val> + <right_val>0.1536193042993546</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 6 1 -1.</_> + <_>7 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2259892001748085e-003</threshold> + <left_val>0.1729111969470978</left_val> + <right_val>0.5241606235504150</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 10 3 -1.</_> + <_>5 7 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0121325999498367</threshold> + <left_val>0.6597759723663330</left_val> + <right_val>0.4325182139873505</right_val></_></_></trees> + <stage_threshold>21.0106391906738280</stage_threshold> + <parent>4</parent> + <next>-1</next></_> + <_> + <!-- stage 6 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 6 9 -1.</_> + <_>7 6 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9184908382594585e-003</threshold> + <left_val>0.6103435158729553</left_val> + <right_val>0.1469330936670303</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 9 1 -1.</_> + <_>9 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5971299726516008e-003</threshold> + <left_val>0.2632363140583038</left_val> + <right_val>0.5896466970443726</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 16 8 -1.</_> + <_>2 12 16 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0177801102399826</threshold> + <left_val>0.5872874259948731</left_val> + <right_val>0.1760361939668655</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 2 6 -1.</_> + <_>14 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5334769897162914e-004</threshold> + <left_val>0.1567801982164383</left_val> + <right_val>0.5596066117286682</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 6 15 -1.</_> + <_>1 10 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8353091329336166e-004</threshold> + <left_val>0.1913153976202011</left_val> + <right_val>0.5732036232948303</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 6 9 -1.</_> + <_>10 3 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6104689566418529e-003</threshold> + <left_val>0.2914913892745972</left_val> + <right_val>0.5623080730438232</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 7 14 -1.</_> + <_>6 13 7 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0977506190538406</threshold> + <left_val>0.1943476945161820</left_val> + <right_val>0.5648233294487000</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 3 6 -1.</_> + <_>13 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5182358482852578e-004</threshold> + <left_val>0.3134616911411285</left_val> + <right_val>0.5504639744758606</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 15 4 -1.</_> + <_>6 8 5 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128582203760743</threshold> + <left_val>0.2536481916904450</left_val> + <right_val>0.5760142803192139</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 3 10 -1.</_> + <_>11 7 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1530239395797253e-003</threshold> + <left_val>0.5767722129821777</left_val> + <right_val>0.3659774065017700</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 4 6 -1.</_> + <_>3 9 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7092459602281451e-003</threshold> + <left_val>0.2843191027641296</left_val> + <right_val>0.5918939113616943</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 3 6 10 -1.</_> + <_>15 3 2 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5217359699308872e-003</threshold> + <left_val>0.4052427113056183</left_val> + <right_val>0.6183109283447266</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 10 -1.</_> + <_>5 7 4 5 2.</_> + <_>9 12 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2479810286313295e-003</threshold> + <left_val>0.5783755183219910</left_val> + <right_val>0.3135401010513306</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 12 12 -1.</_> + <_>10 4 6 6 2.</_> + <_>4 10 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0520062111318111</threshold> + <left_val>0.5541312098503113</left_val> + <right_val>0.1916636973619461</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 6 9 -1.</_> + <_>3 4 2 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0120855299755931</threshold> + <left_val>0.4032655954360962</left_val> + <right_val>0.6644591093063355</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 2 5 -1.</_> + <_>11 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4687820112158079e-005</threshold> + <left_val>0.3535977900028229</left_val> + <right_val>0.5709382891654968</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 2 5 -1.</_> + <_>8 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1395188570022583e-006</threshold> + <left_val>0.3037444949150085</left_val> + <right_val>0.5610269904136658</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 2 3 -1.</_> + <_>10 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6001640148460865e-003</threshold> + <left_val>0.7181087136268616</left_val> + <right_val>0.4580326080322266</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 6 2 -1.</_> + <_>8 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0058949012309313e-003</threshold> + <left_val>0.5621951818466187</left_val> + <right_val>0.2953684031963348</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5050270855426788e-003</threshold> + <left_val>0.4615387916564941</left_val> + <right_val>0.7619017958641052</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 12 6 -1.</_> + <_>4 14 12 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0117468303069472</threshold> + <left_val>0.5343837141990662</left_val> + <right_val>0.1772529035806656</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 5 9 -1.</_> + <_>11 14 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0583163388073444</threshold> + <left_val>0.1686245948076248</left_val> + <right_val>0.5340772271156311</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 3 2 -1.</_> + <_>6 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3629379575140774e-004</threshold> + <left_val>0.3792056143283844</left_val> + <right_val>0.6026803851127625</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 5 -1.</_> + <_>12 0 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8156180679798126e-003</threshold> + <left_val>0.1512867063283920</left_val> + <right_val>0.5324323773384094</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 7 -1.</_> + <_>8 5 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108761601150036</threshold> + <left_val>0.2081822007894516</left_val> + <right_val>0.5319945216178894</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 1 9 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7745519764721394e-003</threshold> + <left_val>0.4098246991634369</left_val> + <right_val>0.5210328102111816</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 4 8 -1.</_> + <_>3 2 2 4 2.</_> + <_>5 6 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8276381827890873e-004</threshold> + <left_val>0.5693274140357971</left_val> + <right_val>0.3478842079639435</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 12 4 6 -1.</_> + <_>13 14 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0138704096898437</threshold> + <left_val>0.5326750874519348</left_val> + <right_val>0.2257698029279709</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 4 6 -1.</_> + <_>3 14 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0236749108880758</threshold> + <left_val>0.1551305055618286</left_val> + <right_val>0.5200707912445068</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 4 -1.</_> + <_>13 13 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4879409718560055e-005</threshold> + <left_val>0.5500566959381104</left_val> + <right_val>0.3820176124572754</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 4 3 -1.</_> + <_>4 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6190641112625599e-003</threshold> + <left_val>0.4238683879375458</left_val> + <right_val>0.6639748215675354</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 11 8 -1.</_> + <_>7 9 11 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0198171101510525</threshold> + <left_val>0.2150038033723831</left_val> + <right_val>0.5382357835769653</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 4 -1.</_> + <_>8 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8154039066284895e-003</threshold> + <left_val>0.6675711274147034</left_val> + <right_val>0.4215297102928162</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 6 1 -1.</_> + <_>11 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9775829538702965e-003</threshold> + <left_val>0.2267289012670517</left_val> + <right_val>0.5386328101158142</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 3 3 -1.</_> + <_>5 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2441020701080561e-003</threshold> + <left_val>0.4308691024780273</left_val> + <right_val>0.6855735778808594</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 20 6 -1.</_> + <_>10 9 10 3 2.</_> + <_>0 12 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0122824599966407</threshold> + <left_val>0.5836614966392517</left_val> + <right_val>0.3467479050159454</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 5 -1.</_> + <_>9 6 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8548699337989092e-003</threshold> + <left_val>0.7016944885253906</left_val> + <right_val>0.4311453998088837</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 1 3 -1.</_> + <_>11 1 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7875669077038765e-003</threshold> + <left_val>0.2895345091819763</left_val> + <right_val>0.5224946141242981</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 4 2 -1.</_> + <_>4 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2201230274513364e-003</threshold> + <left_val>0.2975570857524872</left_val> + <right_val>0.5481644868850708</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 6 4 3 -1.</_> + <_>12 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0101605998352170</threshold> + <left_val>0.4888817965984345</left_val> + <right_val>0.8182697892189026</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 6 4 -1.</_> + <_>7 0 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0161745697259903</threshold> + <left_val>0.1481492966413498</left_val> + <right_val>0.5239992737770081</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 8 -1.</_> + <_>10 7 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0192924607545137</threshold> + <left_val>0.4786309897899628</left_val> + <right_val>0.7378190755844116</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2479539513587952e-003</threshold> + <left_val>0.7374222874641419</left_val> + <right_val>0.4470643997192383</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 14 4 -1.</_> + <_>13 7 7 2 2.</_> + <_>6 9 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3803480267524719e-003</threshold> + <left_val>0.3489154875278473</left_val> + <right_val>0.5537996292114258</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 3 6 -1.</_> + <_>0 7 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0126061299815774</threshold> + <left_val>0.2379686981439591</left_val> + <right_val>0.5315443277359009</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 4 -1.</_> + <_>13 13 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0256219301372766</threshold> + <left_val>0.1964688003063202</left_val> + <right_val>0.5138769745826721</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 3 4 -1.</_> + <_>4 13 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5741496402770281e-005</threshold> + <left_val>0.5590522885322571</left_val> + <right_val>0.3365853130817413</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 12 8 -1.</_> + <_>11 9 6 4 2.</_> + <_>5 13 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0892108827829361</threshold> + <left_val>0.0634046569466591</left_val> + <right_val>0.5162634849548340</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 1 3 -1.</_> + <_>9 13 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7670480776578188e-003</threshold> + <left_val>0.7323467731475830</left_val> + <right_val>0.4490706026554108</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 2 4 -1.</_> + <_>10 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7152578695677221e-004</threshold> + <left_val>0.4114834964275360</left_val> + <right_val>0.5985518097877502</right_val></_></_></trees> + <stage_threshold>23.9187908172607420</stage_threshold> + <parent>5</parent> + <next>-1</next></_> + <_> + <!-- stage 7 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 1 -1.</_> + <_>9 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4786219689995050e-003</threshold> + <left_val>0.2663545012474060</left_val> + <right_val>0.6643316745758057</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 6 6 -1.</_> + <_>15 3 3 3 2.</_> + <_>12 6 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8741659587249160e-003</threshold> + <left_val>0.6143848896026611</left_val> + <right_val>0.2518512904644013</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 10 6 -1.</_> + <_>0 6 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7151009524241090e-003</threshold> + <left_val>0.5766341090202332</left_val> + <right_val>0.2397463023662567</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 8 14 -1.</_> + <_>12 3 4 7 2.</_> + <_>8 10 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8939269939437509e-003</threshold> + <left_val>0.5682045817375183</left_val> + <right_val>0.2529144883155823</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 7 15 -1.</_> + <_>4 9 7 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3006052039563656e-003</threshold> + <left_val>0.1640675961971283</left_val> + <right_val>0.5556079745292664</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 6 8 -1.</_> + <_>15 2 3 4 2.</_> + <_>12 6 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0466625317931175</threshold> + <left_val>0.6123154163360596</left_val> + <right_val>0.4762830138206482</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 6 8 -1.</_> + <_>2 2 3 4 2.</_> + <_>5 6 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9431332414969802e-004</threshold> + <left_val>0.5707858800888062</left_val> + <right_val>0.2839404046535492</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 18 7 -1.</_> + <_>8 13 6 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0148916700854898</threshold> + <left_val>0.4089672863483429</left_val> + <right_val>0.6006367206573486</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 8 14 -1.</_> + <_>4 3 4 7 2.</_> + <_>8 10 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2046529445797205e-003</threshold> + <left_val>0.5712450742721558</left_val> + <right_val>0.2705289125442505</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 6 -1.</_> + <_>18 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0619381256401539e-003</threshold> + <left_val>0.5262504220008850</left_val> + <right_val>0.3262225985527039</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5286648888140917e-003</threshold> + <left_val>0.6853830814361572</left_val> + <right_val>0.4199256896972656</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 6 -1.</_> + <_>18 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9010218828916550e-003</threshold> + <left_val>0.3266282081604004</left_val> + <right_val>0.5434812903404236</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 2 6 -1.</_> + <_>0 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6702760048210621e-003</threshold> + <left_val>0.5468410849571228</left_val> + <right_val>0.2319003939628601</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 18 6 -1.</_> + <_>1 7 18 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0304100364446640e-003</threshold> + <left_val>0.5570667982101440</left_val> + <right_val>0.2708238065242767</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 7 -1.</_> + <_>3 2 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9803649522364140e-003</threshold> + <left_val>0.3700568974018097</left_val> + <right_val>0.5890625715255737</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 6 14 -1.</_> + <_>7 10 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0758405104279518</threshold> + <left_val>0.2140070050954819</left_val> + <right_val>0.5419948101043701</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 13 10 -1.</_> + <_>3 12 13 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0192625392228365</threshold> + <left_val>0.5526772141456604</left_val> + <right_val>0.2726590037345886</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 2 2 -1.</_> + <_>11 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8888259364757687e-004</threshold> + <left_val>0.3958011865615845</left_val> + <right_val>0.6017209887504578</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 16 4 -1.</_> + <_>2 11 8 2 2.</_> + <_>10 13 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0293695498257875</threshold> + <left_val>0.5241373777389526</left_val> + <right_val>0.1435758024454117</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0417619487270713e-003</threshold> + <left_val>0.3385409116744995</left_val> + <right_val>0.5929983258247376</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 9 -1.</_> + <_>6 13 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6125640142709017e-003</threshold> + <left_val>0.5485377907752991</left_val> + <right_val>0.3021597862243652</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 1 6 -1.</_> + <_>14 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6977467183023691e-004</threshold> + <left_val>0.3375276029109955</left_val> + <right_val>0.5532032847404480</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 4 1 -1.</_> + <_>7 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9512659208849072e-004</threshold> + <left_val>0.5631743073463440</left_val> + <right_val>0.3359399139881134</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 8 15 5 -1.</_> + <_>8 8 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1015655994415283</threshold> + <left_val>0.0637350380420685</left_val> + <right_val>0.5230425000190735</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 6 5 4 -1.</_> + <_>1 8 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0361566990613937</threshold> + <left_val>0.5136963129043579</left_val> + <right_val>0.1029528975486755</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 17 6 -1.</_> + <_>3 3 17 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4624140243977308e-003</threshold> + <left_val>0.3879320025444031</left_val> + <right_val>0.5558289289474487</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 8 2 -1.</_> + <_>10 7 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0195549800992012</threshold> + <left_val>0.5250086784362793</left_val> + <right_val>0.1875859946012497</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 2 -1.</_> + <_>10 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3121440317481756e-003</threshold> + <left_val>0.6672028899192810</left_val> + <right_val>0.4679641127586365</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>9 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8605289515107870e-003</threshold> + <left_val>0.7163379192352295</left_val> + <right_val>0.4334670901298523</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>8 10 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4026362057775259e-004</threshold> + <left_val>0.3021360933780670</left_val> + <right_val>0.5650203227996826</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2418331615626812e-003</threshold> + <left_val>0.1820009052753449</left_val> + <right_val>0.5250256061553955</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 4 -1.</_> + <_>9 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1729019752237946e-004</threshold> + <left_val>0.3389188051223755</left_val> + <right_val>0.5445973277091980</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1878840159624815e-003</threshold> + <left_val>0.4085349142551422</left_val> + <right_val>0.6253563165664673</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 6 -1.</_> + <_>10 7 6 3 2.</_> + <_>4 10 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108813596889377</threshold> + <left_val>0.3378399014472961</left_val> + <right_val>0.5700082778930664</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7354859737679362e-003</threshold> + <left_val>0.4204635918140411</left_val> + <right_val>0.6523038744926453</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>9 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5119052305817604e-003</threshold> + <left_val>0.2595216035842896</left_val> + <right_val>0.5428143739700317</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 3 8 -1.</_> + <_>8 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2136430013924837e-003</threshold> + <left_val>0.6165143847465515</left_val> + <right_val>0.3977893888950348</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 3 6 -1.</_> + <_>11 0 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103542404249310</threshold> + <left_val>0.1628028005361557</left_val> + <right_val>0.5219504833221436</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 4 8 -1.</_> + <_>8 3 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5858830455690622e-004</threshold> + <left_val>0.3199650943279266</left_val> + <right_val>0.5503574013710022</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 13 -1.</_> + <_>14 3 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0152996499091387</threshold> + <left_val>0.4103994071483612</left_val> + <right_val>0.6122388243675232</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 3 6 -1.</_> + <_>8 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215882100164890</threshold> + <left_val>0.1034912988543510</left_val> + <right_val>0.5197384953498840</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 13 -1.</_> + <_>14 3 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1283462941646576</threshold> + <left_val>0.8493865132331848</left_val> + <right_val>0.4893102943897247</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 10 4 -1.</_> + <_>0 7 5 2 2.</_> + <_>5 9 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2927189711481333e-003</threshold> + <left_val>0.3130157887935638</left_val> + <right_val>0.5471575260162354</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 13 -1.</_> + <_>14 3 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0799151062965393</threshold> + <left_val>0.4856320917606354</left_val> + <right_val>0.6073989272117615</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 6 13 -1.</_> + <_>3 3 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0794410929083824</threshold> + <left_val>0.8394674062728882</left_val> + <right_val>0.4624533057212830</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 4 1 -1.</_> + <_>9 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2800010889768600e-003</threshold> + <left_val>0.1881695985794067</left_val> + <right_val>0.5306698083877564</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 2 1 -1.</_> + <_>9 0 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0463109938427806e-003</threshold> + <left_val>0.5271229147911072</left_val> + <right_val>0.2583065927028656</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 4 4 -1.</_> + <_>12 16 2 2 2.</_> + <_>10 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6317298761568964e-004</threshold> + <left_val>0.4235304892063141</left_val> + <right_val>0.5735440850257874</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 3 -1.</_> + <_>10 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6173160187900066e-003</threshold> + <left_val>0.6934396028518677</left_val> + <right_val>0.4495444893836975</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 2 -1.</_> + <_>8 5 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0114218797534704</threshold> + <left_val>0.5900921225547791</left_val> + <right_val>0.4138193130493164</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9963278900831938e-003</threshold> + <left_val>0.6466382741928101</left_val> + <right_val>0.4327239990234375</right_val></_></_></trees> + <stage_threshold>24.5278797149658200</stage_threshold> + <parent>6</parent> + <next>-1</next></_> + <_> + <!-- stage 8 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 8 6 -1.</_> + <_>6 6 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9691245704889297e-003</threshold> + <left_val>0.6142324209213257</left_val> + <right_val>0.2482212036848068</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 12 -1.</_> + <_>9 11 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3073059320449829e-004</threshold> + <left_val>0.5704951882362366</left_val> + <right_val>0.2321965992450714</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 6 8 -1.</_> + <_>4 10 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4045301405712962e-004</threshold> + <left_val>0.2112251967191696</left_val> + <right_val>0.5814933180809021</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 8 5 -1.</_> + <_>12 2 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5424019917845726e-003</threshold> + <left_val>0.2950482070446014</left_val> + <right_val>0.5866311788558960</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 18 3 -1.</_> + <_>0 9 18 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2477443104144186e-005</threshold> + <left_val>0.2990990877151489</left_val> + <right_val>0.5791326761245728</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 8 -1.</_> + <_>8 16 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6603146046400070e-003</threshold> + <left_val>0.2813029885292053</left_val> + <right_val>0.5635542273521423</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 8 5 -1.</_> + <_>4 2 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0515816807746887e-003</threshold> + <left_val>0.3535369038581848</left_val> + <right_val>0.6054757237434387</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 4 -1.</_> + <_>13 13 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3835240649059415e-004</threshold> + <left_val>0.5596532225608826</left_val> + <right_val>0.2731510996818543</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.8168973636347800e-005</threshold> + <left_val>0.5978031754493713</left_val> + <right_val>0.3638561069965363</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 3 1 -1.</_> + <_>12 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1298790341243148e-003</threshold> + <left_val>0.2755252122879028</left_val> + <right_val>0.5432729125022888</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 5 3 -1.</_> + <_>7 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4356150105595589e-003</threshold> + <left_val>0.4305641949176788</left_val> + <right_val>0.7069833278656006</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 7 6 -1.</_> + <_>11 14 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0568293295800686</threshold> + <left_val>0.2495242953300476</left_val> + <right_val>0.5294997096061707</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 7 6 -1.</_> + <_>2 14 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0668169967830181e-003</threshold> + <left_val>0.5478553175926209</left_val> + <right_val>0.2497723996639252</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 14 2 6 -1.</_> + <_>12 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8164798499783501e-005</threshold> + <left_val>0.3938601016998291</left_val> + <right_val>0.5706356167793274</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 3 3 -1.</_> + <_>8 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1795017682015896e-003</threshold> + <left_val>0.4407606124877930</left_val> + <right_val>0.7394766807556152</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 5 -1.</_> + <_>12 0 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4985752105712891e-003</threshold> + <left_val>0.5445243120193481</left_val> + <right_val>0.2479152977466583</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 4 9 -1.</_> + <_>8 1 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0211090557277203e-003</threshold> + <left_val>0.2544766962528229</left_val> + <right_val>0.5338971018791199</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 6 1 -1.</_> + <_>12 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4247528314590454e-003</threshold> + <left_val>0.2718858122825623</left_val> + <right_val>0.5324069261550903</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>8 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0559899965301156e-003</threshold> + <left_val>0.3178288042545319</left_val> + <right_val>0.5534508824348450</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6465808777138591e-004</threshold> + <left_val>0.4284219145774841</left_val> + <right_val>0.6558194160461426</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 18 4 2 -1.</_> + <_>5 19 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7524109464138746e-004</threshold> + <left_val>0.5902860760688782</left_val> + <right_val>0.3810262978076935</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 18 6 -1.</_> + <_>2 3 18 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2293202131986618e-003</threshold> + <left_val>0.3816489875316620</left_val> + <right_val>0.5709385871887207</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 2 -1.</_> + <_>7 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2868210691958666e-003</threshold> + <left_val>0.1747743934392929</left_val> + <right_val>0.5259544253349304</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 8 6 2 -1.</_> + <_>16 8 3 1 2.</_> + <_>13 9 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5611879643984139e-004</threshold> + <left_val>0.3601722121238709</left_val> + <right_val>0.5725612044334412</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3621381488919724e-006</threshold> + <left_val>0.5401858091354370</left_val> + <right_val>0.3044497072696686</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 20 4 -1.</_> + <_>10 13 10 2 2.</_> + <_>0 15 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0147672500461340</threshold> + <left_val>0.3220770061016083</left_val> + <right_val>0.5573434829711914</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 5 -1.</_> + <_>9 7 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0244895908981562</threshold> + <left_val>0.4301528036594391</left_val> + <right_val>0.6518812775611877</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 2 2 -1.</_> + <_>11 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7652091123163700e-004</threshold> + <left_val>0.3564583063125610</left_val> + <right_val>0.5598236918449402</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 6 2 -1.</_> + <_>1 8 3 1 2.</_> + <_>4 9 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3657688517414499e-006</threshold> + <left_val>0.3490782976150513</left_val> + <right_val>0.5561897754669190</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 2 -1.</_> + <_>10 2 10 1 2.</_> + <_>0 3 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0150999398902059</threshold> + <left_val>0.1776272058486939</left_val> + <right_val>0.5335299968719482</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 5 3 -1.</_> + <_>7 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8316650316119194e-003</threshold> + <left_val>0.6149687767028809</left_val> + <right_val>0.4221394062042236</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>10 13 3 3 2.</_> + <_>7 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0169254001230001</threshold> + <left_val>0.5413014888763428</left_val> + <right_val>0.2166585028171539</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0477850232273340e-003</threshold> + <left_val>0.6449490785598755</left_val> + <right_val>0.4354617893695831</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 11 1 6 -1.</_> + <_>16 13 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2140589319169521e-003</threshold> + <left_val>0.5400155186653137</left_val> + <right_val>0.3523217141628265</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 1 6 -1.</_> + <_>3 13 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0023201145231724e-003</threshold> + <left_val>0.2774524092674255</left_val> + <right_val>0.5338417291641235</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 14 12 -1.</_> + <_>11 4 7 6 2.</_> + <_>4 10 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4182129465043545e-003</threshold> + <left_val>0.5676739215850830</left_val> + <right_val>0.3702817857265472</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8764587417244911e-003</threshold> + <left_val>0.7749221920967102</left_val> + <right_val>0.4583688974380493</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7311739977449179e-003</threshold> + <left_val>0.5338721871376038</left_val> + <right_val>0.3996661007404327</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 3 -1.</_> + <_>6 7 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5082379579544067e-003</threshold> + <left_val>0.5611963272094727</left_val> + <right_val>0.3777498900890350</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0541074275970459e-003</threshold> + <left_val>0.2915228903293610</left_val> + <right_val>0.5179182887077332</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 4 10 -1.</_> + <_>3 1 2 5 2.</_> + <_>5 6 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7938813269138336e-004</threshold> + <left_val>0.5536432862281799</left_val> + <right_val>0.3700192868709564</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>5 7 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8745909482240677e-003</threshold> + <left_val>0.3754391074180603</left_val> + <right_val>0.5679376125335693</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4936719350516796e-003</threshold> + <left_val>0.7019699215888977</left_val> + <right_val>0.4480949938297272</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 12 2 3 -1.</_> + <_>15 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4389229044318199e-003</threshold> + <left_val>0.2310364991426468</left_val> + <right_val>0.5313386917114258</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 4 -1.</_> + <_>8 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5094640487805009e-004</threshold> + <left_val>0.5864868760108948</left_val> + <right_val>0.4129343032836914</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 1 12 -1.</_> + <_>13 10 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4528800420521293e-005</threshold> + <left_val>0.3732407093048096</left_val> + <right_val>0.5619621276855469</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 12 -1.</_> + <_>4 5 6 6 2.</_> + <_>10 11 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0407580696046352</threshold> + <left_val>0.5312091112136841</left_val> + <right_val>0.2720521986484528</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 7 3 -1.</_> + <_>7 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6505931317806244e-003</threshold> + <left_val>0.4710015952587128</left_val> + <right_val>0.6693493723869324</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 3 -1.</_> + <_>3 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5759351924061775e-003</threshold> + <left_val>0.5167819261550903</left_val> + <right_val>0.1637275964021683</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 14 2 -1.</_> + <_>10 2 7 1 2.</_> + <_>3 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5269311890006065e-003</threshold> + <left_val>0.5397608876228333</left_val> + <right_val>0.2938531935214996</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 3 10 -1.</_> + <_>1 1 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0136603796854615</threshold> + <left_val>0.7086488008499146</left_val> + <right_val>0.4532200098037720</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 5 -1.</_> + <_>11 0 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0273588690906763</threshold> + <left_val>0.5206481218338013</left_val> + <right_val>0.3589231967926025</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 6 2 -1.</_> + <_>8 7 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2197551596909761e-004</threshold> + <left_val>0.3507075905799866</left_val> + <right_val>0.5441123247146606</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 10 -1.</_> + <_>7 6 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3077080734074116e-003</threshold> + <left_val>0.5859522819519043</left_val> + <right_val>0.4024891853332520</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 3 -1.</_> + <_>7 1 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0106311095878482</threshold> + <left_val>0.6743267178535461</left_val> + <right_val>0.4422602951526642</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 6 -1.</_> + <_>16 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0194416493177414</threshold> + <left_val>0.5282716155052185</left_val> + <right_val>0.1797904968261719</right_val></_></_></trees> + <stage_threshold>27.1533508300781250</stage_threshold> + <parent>7</parent> + <next>-1</next></_> + <_> + <!-- stage 9 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 7 6 -1.</_> + <_>6 6 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5052167735993862e-003</threshold> + <left_val>0.5914731025695801</left_val> + <right_val>0.2626559138298035</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9562279339879751e-003</threshold> + <left_val>0.2312581986188889</left_val> + <right_val>0.5741627216339111</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 17 10 -1.</_> + <_>0 9 17 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8924784213304520e-003</threshold> + <left_val>0.1656530052423477</left_val> + <right_val>0.5626654028892517</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 15 16 -1.</_> + <_>3 12 15 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0836383774876595</threshold> + <left_val>0.5423449873924255</left_val> + <right_val>0.1957294940948486</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 6 4 -1.</_> + <_>7 17 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2282270472496748e-003</threshold> + <left_val>0.3417904078960419</left_val> + <right_val>0.5992503762245178</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 2 4 9 -1.</_> + <_>15 2 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7629169896245003e-003</threshold> + <left_val>0.3719581961631775</left_val> + <right_val>0.6079903841018677</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 3 2 -1.</_> + <_>2 4 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6417410224676132e-003</threshold> + <left_val>0.2577486038208008</left_val> + <right_val>0.5576915740966797</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 7 9 -1.</_> + <_>13 9 7 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4113149158656597e-003</threshold> + <left_val>0.2950749099254608</left_val> + <right_val>0.5514171719551086</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0110693201422691</threshold> + <left_val>0.7569358944892883</left_val> + <right_val>0.4477078914642334</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 6 -1.</_> + <_>10 2 10 3 2.</_> + <_>0 5 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0348659716546535</threshold> + <left_val>0.5583708882331848</left_val> + <right_val>0.2669621109962463</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 6 10 -1.</_> + <_>3 2 3 5 2.</_> + <_>6 7 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5701099811121821e-004</threshold> + <left_val>0.5627313256263733</left_val> + <right_val>0.2988890111446381</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 10 3 4 -1.</_> + <_>13 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0243391301482916</threshold> + <left_val>0.2771185040473938</left_val> + <right_val>0.5108863115310669</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 3 4 -1.</_> + <_>4 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9435202274471521e-004</threshold> + <left_val>0.5580651760101318</left_val> + <right_val>0.3120341897010803</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 3 -1.</_> + <_>9 5 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2971509024500847e-003</threshold> + <left_val>0.3330250084400177</left_val> + <right_val>0.5679075717926025</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 8 -1.</_> + <_>7 10 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7801829166710377e-003</threshold> + <left_val>0.2990534901618958</left_val> + <right_val>0.5344808101654053</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 20 6 -1.</_> + <_>0 14 20 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1342066973447800</threshold> + <left_val>0.1463858932256699</left_val> + <right_val>0.5392568111419678</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 4 6 -1.</_> + <_>4 13 2 3 2.</_> + <_>6 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5224548345431685e-004</threshold> + <left_val>0.3746953904628754</left_val> + <right_val>0.5692734718322754</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>10 0 4 6 2.</_> + <_>6 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0405455417931080</threshold> + <left_val>0.2754747867584229</left_val> + <right_val>0.5484297871589661</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 15 2 -1.</_> + <_>2 1 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2572970008477569e-003</threshold> + <left_val>0.3744584023952484</left_val> + <right_val>0.5756075978279114</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4249948374927044e-003</threshold> + <left_val>0.7513859272003174</left_val> + <right_val>0.4728231132030487</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 1 2 -1.</_> + <_>3 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0908129196614027e-004</threshold> + <left_val>0.5404896736145020</left_val> + <right_val>0.2932321131229401</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2808450264856219e-003</threshold> + <left_val>0.6169779896736145</left_val> + <right_val>0.4273349046707153</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 3 1 -1.</_> + <_>8 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8348860321566463e-003</threshold> + <left_val>0.2048496007919312</left_val> + <right_val>0.5206472277641296</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 7 3 6 -1.</_> + <_>17 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0274848695844412</threshold> + <left_val>0.5252984762191773</left_val> + <right_val>0.1675522029399872</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 3 2 -1.</_> + <_>8 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2372419480234385e-003</threshold> + <left_val>0.5267782807350159</left_val> + <right_val>0.2777658104896545</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 5 3 -1.</_> + <_>11 5 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8635291904211044e-003</threshold> + <left_val>0.6954557895660400</left_val> + <right_val>0.4812048971652985</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 5 3 -1.</_> + <_>4 5 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1753971017897129e-003</threshold> + <left_val>0.4291887879371643</left_val> + <right_val>0.6349195837974548</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 3 1 2 -1.</_> + <_>19 4 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7098189564421773e-003</threshold> + <left_val>0.2930536866188049</left_val> + <right_val>0.5361248850822449</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 4 3 -1.</_> + <_>5 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5328548662364483e-003</threshold> + <left_val>0.4495325088500977</left_val> + <right_val>0.7409694194793701</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 7 3 6 -1.</_> + <_>17 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.5372907817363739e-003</threshold> + <left_val>0.3149119913578033</left_val> + <right_val>0.5416501760482788</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 3 6 -1.</_> + <_>0 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0253109894692898</threshold> + <left_val>0.5121892094612122</left_val> + <right_val>0.1311707943677902</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 9 -1.</_> + <_>14 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0364609695971012</threshold> + <left_val>0.5175911784172058</left_val> + <right_val>0.2591339945793152</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 5 6 -1.</_> + <_>0 6 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0208543296903372</threshold> + <left_val>0.5137140154838562</left_val> + <right_val>0.1582316011190414</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 6 2 -1.</_> + <_>12 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7207747856155038e-004</threshold> + <left_val>0.5574309825897217</left_val> + <right_val>0.4398978948593140</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 6 2 -1.</_> + <_>6 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5227000403683633e-005</threshold> + <left_val>0.5548940896987915</left_val> + <right_val>0.3708069920539856</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 4 6 -1.</_> + <_>8 3 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4316509310156107e-004</threshold> + <left_val>0.3387419879436493</left_val> + <right_val>0.5554211139678955</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6037859972566366e-003</threshold> + <left_val>0.5358061790466309</left_val> + <right_val>0.3411171138286591</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 3 -1.</_> + <_>6 7 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8057891912758350e-003</threshold> + <left_val>0.6125202775001526</left_val> + <right_val>0.4345862865447998</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 5 9 -1.</_> + <_>0 4 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0470216609537601</threshold> + <left_val>0.2358165979385376</left_val> + <right_val>0.5193738937377930</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 15 -1.</_> + <_>16 0 2 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0369541086256504</threshold> + <left_val>0.7323111295700073</left_val> + <right_val>0.4760943949222565</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 3 2 -1.</_> + <_>1 11 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0439479956403375e-003</threshold> + <left_val>0.5419455170631409</left_val> + <right_val>0.3411330878734589</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 1 10 -1.</_> + <_>14 9 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1050689974799752e-004</threshold> + <left_val>0.2821694016456604</left_val> + <right_val>0.5554947257041931</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 4 12 -1.</_> + <_>2 1 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0808315873146057</threshold> + <left_val>0.9129930138587952</left_val> + <right_val>0.4697434902191162</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 4 2 -1.</_> + <_>11 11 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6579059087671340e-004</threshold> + <left_val>0.6022670269012451</left_val> + <right_val>0.3978292942047119</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 4 2 -1.</_> + <_>7 11 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2545920617412776e-004</threshold> + <left_val>0.5613213181495667</left_val> + <right_val>0.3845539987087250</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 8 15 5 -1.</_> + <_>8 8 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0687864869832993</threshold> + <left_val>0.2261611968278885</left_val> + <right_val>0.5300496816635132</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 6 10 -1.</_> + <_>3 0 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0124157899990678</threshold> + <left_val>0.4075691998004913</left_val> + <right_val>0.5828812122344971</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 3 2 -1.</_> + <_>12 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7174817882478237e-003</threshold> + <left_val>0.2827253937721252</left_val> + <right_val>0.5267757773399353</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 8 -1.</_> + <_>8 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0381368584930897</threshold> + <left_val>0.5074741244316101</left_val> + <right_val>0.1023615971207619</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 5 3 -1.</_> + <_>8 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8168049175292253e-003</threshold> + <left_val>0.6169006824493408</left_val> + <right_val>0.4359692931175232</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 4 3 -1.</_> + <_>7 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1303603947162628e-003</threshold> + <left_val>0.4524433016777039</left_val> + <right_val>0.7606095075607300</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 3 2 -1.</_> + <_>12 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0056019574403763e-003</threshold> + <left_val>0.5240408778190613</left_val> + <right_val>0.1859712004661560</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 14 4 -1.</_> + <_>3 15 7 2 2.</_> + <_>10 17 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0191393196582794</threshold> + <left_val>0.5209379196166992</left_val> + <right_val>0.2332071959972382</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 16 4 -1.</_> + <_>10 2 8 2 2.</_> + <_>2 4 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0164457596838474</threshold> + <left_val>0.5450702905654907</left_val> + <right_val>0.3264234960079193</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 6 12 -1.</_> + <_>3 8 3 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0373568907380104</threshold> + <left_val>0.6999046802520752</left_val> + <right_val>0.4533241987228394</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>5 7 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0197279006242752</threshold> + <left_val>0.2653664946556091</left_val> + <right_val>0.5412809848785400</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 5 -1.</_> + <_>10 7 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6972579807043076e-003</threshold> + <left_val>0.4480566084384918</left_val> + <right_val>0.7138652205467224</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4457528535276651e-004</threshold> + <left_val>0.4231350123882294</left_val> + <right_val>0.5471320152282715</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 8 2 -1.</_> + <_>0 14 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1790640419349074e-003</threshold> + <left_val>0.5341702103614807</left_val> + <right_val>0.3130455017089844</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0349806100130081</threshold> + <left_val>0.5118659734725952</left_val> + <right_val>0.3430530130863190</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 6 4 -1.</_> + <_>1 7 3 2 2.</_> + <_>4 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6859792675822973e-004</threshold> + <left_val>0.3532187044620514</left_val> + <right_val>0.5468639731407166</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 6 1 12 -1.</_> + <_>12 12 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0113406497985125</threshold> + <left_val>0.2842353880405426</left_val> + <right_val>0.5348700881004334</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>10 5 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6228108480572701e-003</threshold> + <left_val>0.6883640289306641</left_val> + <right_val>0.4492664933204651</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 3 -1.</_> + <_>14 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0160330981016159e-003</threshold> + <left_val>0.1709893941879273</left_val> + <right_val>0.5224308967590332</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4206819469109178e-003</threshold> + <left_val>0.5290846228599548</left_val> + <right_val>0.2993383109569550</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 3 -1.</_> + <_>8 13 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7801711112260818e-003</threshold> + <left_val>0.6498854160308838</left_val> + <right_val>0.4460499882698059</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 2 4 -1.</_> + <_>5 2 1 2 2.</_> + <_>6 4 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4747589593753219e-003</threshold> + <left_val>0.3260438144207001</left_val> + <right_val>0.5388113260269165</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 11 3 -1.</_> + <_>5 6 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0238303393125534</threshold> + <left_val>0.7528941035270691</left_val> + <right_val>0.4801219999790192</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 4 12 -1.</_> + <_>7 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9369790144264698e-003</threshold> + <left_val>0.5335165858268738</left_val> + <right_val>0.3261427879333496</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 13 8 5 -1.</_> + <_>12 13 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2806255668401718e-003</threshold> + <left_val>0.4580394029617310</left_val> + <right_val>0.5737829804420471</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 1 12 -1.</_> + <_>7 12 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0104395002126694</threshold> + <left_val>0.2592320144176483</left_val> + <right_val>0.5233827829360962</right_val></_></_></trees> + <stage_threshold>34.5541114807128910</stage_threshold> + <parent>8</parent> + <next>-1</next></_> + <_> + <!-- stage 10 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 6 3 -1.</_> + <_>4 2 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2006587870419025e-003</threshold> + <left_val>0.3258886039257050</left_val> + <right_val>0.6849808096885681</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 10 -1.</_> + <_>12 5 3 5 2.</_> + <_>9 10 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8593589086085558e-003</threshold> + <left_val>0.5838881134986877</left_val> + <right_val>0.2537829875946045</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 8 12 -1.</_> + <_>5 5 4 6 2.</_> + <_>9 11 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8580528022721410e-004</threshold> + <left_val>0.5708081722259522</left_val> + <right_val>0.2812424004077911</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 6 -1.</_> + <_>0 9 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9580191522836685e-003</threshold> + <left_val>0.2501051127910614</left_val> + <right_val>0.5544260740280151</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 2 2 -1.</_> + <_>4 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2124150525778532e-003</threshold> + <left_val>0.2385368049144745</left_val> + <right_val>0.5433350205421448</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 18 12 2 -1.</_> + <_>8 18 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9426132142543793e-003</threshold> + <left_val>0.3955070972442627</left_val> + <right_val>0.6220757961273193</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 4 16 -1.</_> + <_>7 12 4 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4630590341985226e-003</threshold> + <left_val>0.5639708042144775</left_val> + <right_val>0.2992357909679413</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 7 8 -1.</_> + <_>7 10 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0396599583327770e-003</threshold> + <left_val>0.2186512947082520</left_val> + <right_val>0.5411676764488220</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 3 1 -1.</_> + <_>7 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2988339876756072e-003</threshold> + <left_val>0.2350706011056900</left_val> + <right_val>0.5364584922790527</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 2 4 -1.</_> + <_>11 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2299369447864592e-004</threshold> + <left_val>0.3804112970829010</left_val> + <right_val>0.5729606151580811</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 5 4 8 -1.</_> + <_>3 9 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4654280385002494e-003</threshold> + <left_val>0.2510167956352234</left_val> + <right_val>0.5258268713951111</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 12 -1.</_> + <_>7 7 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1210042117163539e-004</threshold> + <left_val>0.5992823839187622</left_val> + <right_val>0.3851158916950226</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 6 2 -1.</_> + <_>6 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3836020370945334e-003</threshold> + <left_val>0.5681396126747131</left_val> + <right_val>0.3636586964130402</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 4 4 6 -1.</_> + <_>16 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0279364492744207</threshold> + <left_val>0.1491317003965378</left_val> + <right_val>0.5377560257911682</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 5 2 -1.</_> + <_>3 4 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6919551095925272e-004</threshold> + <left_val>0.3692429959774017</left_val> + <right_val>0.5572484731674194</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9829659983515739e-003</threshold> + <left_val>0.6758509278297424</left_val> + <right_val>0.4532504081726074</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 4 2 -1.</_> + <_>2 17 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8815309740602970e-003</threshold> + <left_val>0.5368022918701172</left_val> + <right_val>0.2932539880275726</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>10 13 3 3 2.</_> + <_>7 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0190675500780344</threshold> + <left_val>0.1649377048015595</left_val> + <right_val>0.5330067276954651</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 3 4 -1.</_> + <_>8 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6906559728085995e-003</threshold> + <left_val>0.1963925957679749</left_val> + <right_val>0.5119361877441406</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9777139686048031e-003</threshold> + <left_val>0.4671171903610230</left_val> + <right_val>0.7008398175239563</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 4 6 -1.</_> + <_>0 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0333031304180622</threshold> + <left_val>0.1155416965484619</left_val> + <right_val>0.5104162096977234</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 12 3 -1.</_> + <_>9 6 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0907441079616547</threshold> + <left_val>0.5149660110473633</left_val> + <right_val>0.1306173056364059</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 14 -1.</_> + <_>9 6 2 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3555898638442159e-004</threshold> + <left_val>0.3605481088161469</left_val> + <right_val>0.5439859032630920</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0149016501381993</threshold> + <left_val>0.4886212050914764</left_val> + <right_val>0.7687569856643677</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 2 4 -1.</_> + <_>6 14 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1594118596985936e-004</threshold> + <left_val>0.5356813073158264</left_val> + <right_val>0.3240939080715179</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 7 6 -1.</_> + <_>10 14 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0506709888577461</threshold> + <left_val>0.1848621964454651</left_val> + <right_val>0.5230404138565064</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 15 2 -1.</_> + <_>1 1 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8665749859064817e-004</threshold> + <left_val>0.3840579986572266</left_val> + <right_val>0.5517945885658264</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 6 -1.</_> + <_>14 0 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3712432533502579e-003</threshold> + <left_val>0.4288564026355743</left_val> + <right_val>0.6131753921508789</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 1 -1.</_> + <_>6 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2953069526702166e-003</threshold> + <left_val>0.2913674116134644</left_val> + <right_val>0.5280737876892090</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 6 -1.</_> + <_>14 0 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0419416800141335</threshold> + <left_val>0.7554799914360046</left_val> + <right_val>0.4856030941009522</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 20 10 -1.</_> + <_>0 8 20 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0235293805599213</threshold> + <left_val>0.2838279902935028</left_val> + <right_val>0.5256081223487854</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 6 -1.</_> + <_>14 0 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0408574491739273</threshold> + <left_val>0.4870935082435608</left_val> + <right_val>0.6277297139167786</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 6 6 -1.</_> + <_>3 0 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0254068691283464</threshold> + <left_val>0.7099707722663879</left_val> + <right_val>0.4575029015541077</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 15 1 2 -1.</_> + <_>19 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1415440500713885e-004</threshold> + <left_val>0.4030886888504028</left_val> + <right_val>0.5469412207603455</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 4 8 -1.</_> + <_>2 2 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0218241196125746</threshold> + <left_val>0.4502024054527283</left_val> + <right_val>0.6768701076507568</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 18 4 -1.</_> + <_>11 1 9 2 2.</_> + <_>2 3 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0141140399500728</threshold> + <left_val>0.5442860722541809</left_val> + <right_val>0.3791700005531311</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 1 2 -1.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7214590671937913e-005</threshold> + <left_val>0.4200463891029358</left_val> + <right_val>0.5873476266860962</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 10 6 -1.</_> + <_>10 2 5 3 2.</_> + <_>5 5 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9417638480663300e-003</threshold> + <left_val>0.3792561888694763</left_val> + <right_val>0.5585265755653381</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 4 -1.</_> + <_>10 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2144409641623497e-003</threshold> + <left_val>0.7253103852272034</left_val> + <right_val>0.4603548943996429</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5817339774221182e-003</threshold> + <left_val>0.4693301916122437</left_val> + <right_val>0.5900238752365112</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 8 -1.</_> + <_>8 5 4 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1340931951999664</threshold> + <left_val>0.5149213075637817</left_val> + <right_val>0.1808844953775406</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 15 4 3 -1.</_> + <_>15 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2962710354477167e-003</threshold> + <left_val>0.5399743914604187</left_val> + <right_val>0.3717867136001587</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 1 -1.</_> + <_>9 18 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1575849968940020e-003</threshold> + <left_val>0.2408495992422104</left_val> + <right_val>0.5148863792419434</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 4 3 -1.</_> + <_>9 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9196188338100910e-003</threshold> + <left_val>0.6573588252067566</left_val> + <right_val>0.4738740026950836</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 4 3 -1.</_> + <_>7 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6267469618469477e-003</threshold> + <left_val>0.4192821979522705</left_val> + <right_val>0.6303114295005798</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 15 1 2 -1.</_> + <_>19 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3413388882763684e-004</threshold> + <left_val>0.5540298223495483</left_val> + <right_val>0.3702101111412048</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 8 4 -1.</_> + <_>0 17 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0266980808228254</threshold> + <left_val>0.1710917949676514</left_val> + <right_val>0.5101410746574402</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 6 4 -1.</_> + <_>11 3 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0305618792772293</threshold> + <left_val>0.1904218047857285</left_val> + <right_val>0.5168793797492981</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8511548880487680e-003</threshold> + <left_val>0.4447506964206696</left_val> + <right_val>0.6313853859901428</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 14 6 -1.</_> + <_>3 16 14 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0362114794552326</threshold> + <left_val>0.2490727007389069</left_val> + <right_val>0.5377349257469177</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 6 6 -1.</_> + <_>6 6 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4115189444273710e-003</threshold> + <left_val>0.5381243228912354</left_val> + <right_val>0.3664236962795258</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 10 6 -1.</_> + <_>5 14 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7253201743587852e-004</threshold> + <left_val>0.5530232191085815</left_val> + <right_val>0.3541550040245056</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 10 3 4 -1.</_> + <_>4 10 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9481729143299162e-004</threshold> + <left_val>0.4132699072360992</left_val> + <right_val>0.5667243003845215</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 2 -1.</_> + <_>13 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2334560789167881e-003</threshold> + <left_val>0.0987872332334518</left_val> + <right_val>0.5198668837547302</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 6 4 -1.</_> + <_>7 3 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0262747295200825</threshold> + <left_val>0.0911274924874306</left_val> + <right_val>0.5028107166290283</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3212260827422142e-003</threshold> + <left_val>0.4726648926734924</left_val> + <right_val>0.6222720742225647</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 2 3 -1.</_> + <_>2 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1129058226943016e-003</threshold> + <left_val>0.2157457023859024</left_val> + <right_val>0.5137804746627808</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 12 -1.</_> + <_>9 12 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2457809429615736e-003</threshold> + <left_val>0.5410770773887634</left_val> + <right_val>0.3721776902675629</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 4 6 -1.</_> + <_>3 14 2 3 2.</_> + <_>5 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163597092032433</threshold> + <left_val>0.7787874937057495</left_val> + <right_val>0.4685291945934296</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 15 2 2 -1.</_> + <_>16 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2166109303943813e-004</threshold> + <left_val>0.5478987097740173</left_val> + <right_val>0.4240373969078064</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 2 2 -1.</_> + <_>2 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4452440710738301e-004</threshold> + <left_val>0.5330560803413391</left_val> + <right_val>0.3501324951648712</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 3 -1.</_> + <_>8 13 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8909732401371002e-003</threshold> + <left_val>0.6923521161079407</left_val> + <right_val>0.4726569056510925</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 1 -1.</_> + <_>10 7 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0483362115919590</threshold> + <left_val>0.5055900216102600</left_val> + <right_val>0.0757492035627365</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 8 3 -1.</_> + <_>7 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5178127735853195e-004</threshold> + <left_val>0.3783741891384125</left_val> + <right_val>0.5538573861122131</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 2 -1.</_> + <_>9 7 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4953910615295172e-003</threshold> + <left_val>0.3081651031970978</left_val> + <right_val>0.5359612107276917</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2385010961443186e-003</threshold> + <left_val>0.6633958816528320</left_val> + <right_val>0.4649342894554138</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7988430336117744e-003</threshold> + <left_val>0.6596844792366028</left_val> + <right_val>0.4347187876701355</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 3 5 -1.</_> + <_>12 1 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7860915809869766e-003</threshold> + <left_val>0.5231832861900330</left_val> + <right_val>0.2315579950809479</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 3 6 -1.</_> + <_>7 2 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6715380847454071e-003</threshold> + <left_val>0.5204250216484070</left_val> + <right_val>0.2977376878261566</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 14 6 5 -1.</_> + <_>14 14 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0353364497423172</threshold> + <left_val>0.7238878011703491</left_val> + <right_val>0.4861505031585693</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 2 -1.</_> + <_>9 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9189240457490087e-004</threshold> + <left_val>0.3105022013187408</left_val> + <right_val>0.5229824781417847</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 1 3 -1.</_> + <_>10 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3946109469980001e-003</threshold> + <left_val>0.3138968050479889</left_val> + <right_val>0.5210173726081848</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 2 2 -1.</_> + <_>6 6 1 1 2.</_> + <_>7 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8569283727556467e-004</threshold> + <left_val>0.4536580145359039</left_val> + <right_val>0.6585097908973694</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 18 4 -1.</_> + <_>11 11 9 2 2.</_> + <_>2 13 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0501631014049053</threshold> + <left_val>0.1804454028606415</left_val> + <right_val>0.5198916792869568</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 2 2 -1.</_> + <_>6 6 1 1 2.</_> + <_>7 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2367259953171015e-003</threshold> + <left_val>0.7255702018737793</left_val> + <right_val>0.4651359021663666</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 20 2 -1.</_> + <_>0 16 20 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4326287722215056e-004</threshold> + <left_val>0.4412921071052551</left_val> + <right_val>0.5898545980453491</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 2 3 -1.</_> + <_>4 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3485182151198387e-004</threshold> + <left_val>0.3500052988529205</left_val> + <right_val>0.5366017818450928</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0174979399889708</threshold> + <left_val>0.4912194907665253</left_val> + <right_val>0.8315284848213196</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 2 3 -1.</_> + <_>8 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5200000489130616e-003</threshold> + <left_val>0.3570275902748108</left_val> + <right_val>0.5370560288429260</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8003940870985389e-004</threshold> + <left_val>0.4353772103786469</left_val> + <right_val>0.5967335104942322</right_val></_></_></trees> + <stage_threshold>39.1072883605957030</stage_threshold> + <parent>9</parent> + <next>-1</next></_> + <_> + <!-- stage 11 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 10 4 -1.</_> + <_>5 6 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9945552647113800e-003</threshold> + <left_val>0.6162583231925964</left_val> + <right_val>0.3054533004760742</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 6 4 -1.</_> + <_>12 7 3 2 2.</_> + <_>9 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1085229925811291e-003</threshold> + <left_val>0.5818294882774353</left_val> + <right_val>0.3155578076839447</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 3 6 -1.</_> + <_>4 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0364380432292819e-003</threshold> + <left_val>0.2552052140235901</left_val> + <right_val>0.5692911744117737</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 4 4 -1.</_> + <_>13 15 2 2 2.</_> + <_>11 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8211311008781195e-004</threshold> + <left_val>0.3685089945793152</left_val> + <right_val>0.5934931039810181</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 4 2 -1.</_> + <_>7 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8057340104132891e-004</threshold> + <left_val>0.2332392036914825</left_val> + <right_val>0.5474792122840881</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 1 4 3 -1.</_> + <_>13 1 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6068789884448051e-004</threshold> + <left_val>0.3257457017898560</left_val> + <right_val>0.5667545795440674</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 15 4 4 -1.</_> + <_>5 15 2 2 2.</_> + <_>7 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1607372006401420e-004</threshold> + <left_val>0.3744716942310333</left_val> + <right_val>0.5845472812652588</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 4 7 -1.</_> + <_>9 5 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5007521556690335e-004</threshold> + <left_val>0.3420371115207672</left_val> + <right_val>0.5522807240486145</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 8 3 -1.</_> + <_>9 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8607829697430134e-003</threshold> + <left_val>0.2804419994354248</left_val> + <right_val>0.5375424027442932</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5033970121294260e-003</threshold> + <left_val>0.2579050958156586</left_val> + <right_val>0.5498952269554138</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 5 3 -1.</_> + <_>7 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3478909861296415e-003</threshold> + <left_val>0.4175156056880951</left_val> + <right_val>0.6313710808753967</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 4 3 -1.</_> + <_>11 10 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8880240279249847e-004</threshold> + <left_val>0.5865169763565064</left_val> + <right_val>0.4052666127681732</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 8 10 -1.</_> + <_>6 14 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9405477046966553e-003</threshold> + <left_val>0.5211141109466553</left_val> + <right_val>0.2318654060363770</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 6 2 -1.</_> + <_>10 11 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0193277392536402</threshold> + <left_val>0.2753432989120483</left_val> + <right_val>0.5241525769233704</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 6 2 -1.</_> + <_>7 11 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0202060113660991e-004</threshold> + <left_val>0.5722978711128235</left_val> + <right_val>0.3677195906639099</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 8 1 -1.</_> + <_>11 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1179069299250841e-003</threshold> + <left_val>0.4466108083724976</left_val> + <right_val>0.5542430877685547</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 3 2 -1.</_> + <_>7 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7743760254234076e-003</threshold> + <left_val>0.2813253104686737</left_val> + <right_val>0.5300959944725037</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 5 6 5 -1.</_> + <_>14 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2234458960592747e-003</threshold> + <left_val>0.4399709999561310</left_val> + <right_val>0.5795428156852722</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 2 12 -1.</_> + <_>7 11 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0143752200528979</threshold> + <left_val>0.2981117963790894</left_val> + <right_val>0.5292059183120728</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0153491804376245</threshold> + <left_val>0.7705215215682983</left_val> + <right_val>0.4748171865940094</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 1 2 3 -1.</_> + <_>5 1 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5152279956964776e-005</threshold> + <left_val>0.3718844056129456</left_val> + <right_val>0.5576897263526917</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 3 2 6 -1.</_> + <_>18 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1293919831514359e-003</threshold> + <left_val>0.3615196049213409</left_val> + <right_val>0.5286766886711121</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 2 6 -1.</_> + <_>0 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2512159775942564e-003</threshold> + <left_val>0.5364704728126526</left_val> + <right_val>0.3486298024654388</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9696918576955795e-003</threshold> + <left_val>0.6927651762962341</left_val> + <right_val>0.4676836133003235</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 4 3 -1.</_> + <_>7 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128290103748441</threshold> + <left_val>0.7712153792381287</left_val> + <right_val>0.4660735130310059</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 0 2 6 -1.</_> + <_>18 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3660065904259682e-003</threshold> + <left_val>0.3374983966350555</left_val> + <right_val>0.5351287722587585</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 6 -1.</_> + <_>0 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2452319283038378e-003</threshold> + <left_val>0.5325189828872681</left_val> + <right_val>0.3289610147476196</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 6 3 -1.</_> + <_>8 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0117235602810979</threshold> + <left_val>0.6837652921676636</left_val> + <right_val>0.4754300117492676</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 2 4 -1.</_> + <_>8 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9257940695970319e-005</threshold> + <left_val>0.3572087883949280</left_val> + <right_val>0.5360502004623413</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 4 6 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2244219508138485e-005</threshold> + <left_val>0.5541427135467529</left_val> + <right_val>0.3552064001560211</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 2 2 -1.</_> + <_>7 4 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0881509669125080e-003</threshold> + <left_val>0.5070844292640686</left_val> + <right_val>0.1256462037563324</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 14 4 -1.</_> + <_>10 14 7 2 2.</_> + <_>3 16 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0274296794086695</threshold> + <left_val>0.5269560217857361</left_val> + <right_val>0.1625818014144898</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 6 2 -1.</_> + <_>6 15 3 1 2.</_> + <_>9 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4142867922782898e-003</threshold> + <left_val>0.7145588994026184</left_val> + <right_val>0.4584197103977203</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 6 2 -1.</_> + <_>14 16 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3479959238320589e-003</threshold> + <left_val>0.5398612022399902</left_val> + <right_val>0.3494696915149689</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 12 8 -1.</_> + <_>2 16 12 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0826354920864105</threshold> + <left_val>0.2439192980527878</left_val> + <right_val>0.5160226225852966</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 7 2 -1.</_> + <_>7 8 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0261740535497665e-003</threshold> + <left_val>0.3886891901493073</left_val> + <right_val>0.5767908096313477</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 18 2 -1.</_> + <_>0 3 18 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6307090409100056e-003</threshold> + <left_val>0.3389458060264587</left_val> + <right_val>0.5347700715065002</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 5 -1.</_> + <_>9 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4546680506318808e-003</threshold> + <left_val>0.4601413905620575</left_val> + <right_val>0.6387246847152710</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 3 8 -1.</_> + <_>8 5 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9476519972085953e-004</threshold> + <left_val>0.5769879221916199</left_val> + <right_val>0.4120396077632904</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 3 4 -1.</_> + <_>10 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0154091902077198</threshold> + <left_val>0.4878709018230438</left_val> + <right_val>0.7089822292327881</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 3 2 -1.</_> + <_>4 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1784400558099151e-003</threshold> + <left_val>0.5263553261756897</left_val> + <right_val>0.2895244956016541</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 6 3 -1.</_> + <_>11 4 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0277019198983908</threshold> + <left_val>0.1498828977346420</left_val> + <right_val>0.5219606757164002</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 6 3 -1.</_> + <_>7 4 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0295053999871016</threshold> + <left_val>0.0248933192342520</left_val> + <right_val>0.4999816119670868</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 11 5 2 -1.</_> + <_>14 12 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5159430010244250e-004</threshold> + <left_val>0.5464622974395752</left_val> + <right_val>0.4029662907123566</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 6 9 -1.</_> + <_>3 2 2 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1772639639675617e-003</threshold> + <left_val>0.4271056950092316</left_val> + <right_val>0.5866296887397766</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 6 13 -1.</_> + <_>14 6 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0741820484399796</threshold> + <left_val>0.6874179244041443</left_val> + <right_val>0.4919027984142304</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 14 8 -1.</_> + <_>3 6 7 4 2.</_> + <_>10 10 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0172541607171297</threshold> + <left_val>0.3370676040649414</left_val> + <right_val>0.5348739027976990</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 11 -1.</_> + <_>16 0 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0148515598848462</threshold> + <left_val>0.4626792967319489</left_val> + <right_val>0.6129904985427856</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 12 12 -1.</_> + <_>3 4 6 6 2.</_> + <_>9 10 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0100020002573729</threshold> + <left_val>0.5346122980117798</left_val> + <right_val>0.3423453867435455</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 5 3 -1.</_> + <_>11 5 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0138120744377375e-003</threshold> + <left_val>0.4643830060958862</left_val> + <right_val>0.5824304223060608</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 4 2 -1.</_> + <_>4 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5135470312088728e-003</threshold> + <left_val>0.5196396112442017</left_val> + <right_val>0.2856149971485138</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1381431035697460e-003</threshold> + <left_val>0.4838162958621979</left_val> + <right_val>0.5958529710769653</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 2 2 -1.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1450440660119057e-003</threshold> + <left_val>0.8920302987098694</left_val> + <right_val>0.4741412103176117</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4736708514392376e-003</threshold> + <left_val>0.2033942937850952</left_val> + <right_val>0.5337278842926025</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 3 3 -1.</_> + <_>5 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9628470763564110e-003</threshold> + <left_val>0.4571633934974670</left_val> + <right_val>0.6725863218307495</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 3 3 -1.</_> + <_>11 0 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4260450415313244e-003</threshold> + <left_val>0.5271108150482178</left_val> + <right_val>0.2845670878887177</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 6 2 -1.</_> + <_>5 6 3 1 2.</_> + <_>8 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9611460417509079e-004</threshold> + <left_val>0.4138312935829163</left_val> + <right_val>0.5718597769737244</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 16 4 3 -1.</_> + <_>12 17 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3728788197040558e-003</threshold> + <left_val>0.5225151181221008</left_val> + <right_val>0.2804847061634064</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 3 2 -1.</_> + <_>3 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0500897234305739e-004</threshold> + <left_val>0.5236768722534180</left_val> + <right_val>0.3314523994922638</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 2 -1.</_> + <_>9 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6792551185935736e-004</threshold> + <left_val>0.4531059861183167</left_val> + <right_val>0.6276971101760864</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 16 4 -1.</_> + <_>1 11 8 2 2.</_> + <_>9 13 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0246443394571543</threshold> + <left_val>0.5130851864814758</left_val> + <right_val>0.2017143964767456</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0102904504165053</threshold> + <left_val>0.7786595225334168</left_val> + <right_val>0.4876641035079956</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 5 3 -1.</_> + <_>4 5 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0629419013857841e-003</threshold> + <left_val>0.4288598895072937</left_val> + <right_val>0.5881264209747315</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 16 4 3 -1.</_> + <_>12 17 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0519481301307678e-003</threshold> + <left_val>0.3523977994918823</left_val> + <right_val>0.5286008715629578</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7692620903253555e-003</threshold> + <left_val>0.6841086149215698</left_val> + <right_val>0.4588094055652618</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 2 2 -1.</_> + <_>9 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5789941214025021e-004</threshold> + <left_val>0.3565520048141480</left_val> + <right_val>0.5485978126525879</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>8 10 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5918837683275342e-004</threshold> + <left_val>0.3368793129920960</left_val> + <right_val>0.5254197120666504</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7737259622663260e-003</threshold> + <left_val>0.3422161042690277</left_val> + <right_val>0.5454015135765076</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 6 3 -1.</_> + <_>2 13 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5610467940568924e-003</threshold> + <left_val>0.6533612012863159</left_val> + <right_val>0.4485856890678406</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 14 3 2 -1.</_> + <_>16 15 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7277270089834929e-003</threshold> + <left_val>0.5307580232620239</left_val> + <right_val>0.3925352990627289</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 18 18 2 -1.</_> + <_>7 18 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0281996093690395</threshold> + <left_val>0.6857458949089050</left_val> + <right_val>0.4588584005832672</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 14 3 2 -1.</_> + <_>16 15 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7781109781935811e-003</threshold> + <left_val>0.4037851095199585</left_val> + <right_val>0.5369856953620911</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 3 2 -1.</_> + <_>1 15 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3177141449414194e-004</threshold> + <left_val>0.5399798750877380</left_val> + <right_val>0.3705750107765198</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6385399978607893e-003</threshold> + <left_val>0.4665437042713165</left_val> + <right_val>0.6452730894088745</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 14 8 3 -1.</_> + <_>5 15 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1183069329708815e-003</threshold> + <left_val>0.5914781093597412</left_val> + <right_val>0.4064677059650421</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 4 14 -1.</_> + <_>10 6 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0147732896730304</threshold> + <left_val>0.3642038106918335</left_val> + <right_val>0.5294762849807739</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 4 14 -1.</_> + <_>8 6 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0168154407292604</threshold> + <left_val>0.2664231956005096</left_val> + <right_val>0.5144972801208496</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 3 -1.</_> + <_>13 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3370140269398689e-003</threshold> + <left_val>0.6779531240463257</left_val> + <right_val>0.4852097928524017</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 16 6 1 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4560048991115764e-005</threshold> + <left_val>0.5613964796066284</left_val> + <right_val>0.4153054058551788</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>9 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0240620467811823e-003</threshold> + <left_val>0.5964478254318237</left_val> + <right_val>0.4566304087638855</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 3 3 -1.</_> + <_>8 0 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3161689750850201e-003</threshold> + <left_val>0.2976115047931671</left_val> + <right_val>0.5188159942626953</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 16 18 -1.</_> + <_>4 9 16 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.5321757197380066</threshold> + <left_val>0.5187839269638062</left_val> + <right_val>0.2202631980180740</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 16 14 -1.</_> + <_>1 8 16 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1664305031299591</threshold> + <left_val>0.1866022944450378</left_val> + <right_val>0.5060343146324158</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 15 4 -1.</_> + <_>8 9 5 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1125352978706360</threshold> + <left_val>0.5212125182151794</left_val> + <right_val>0.1185022965073586</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 7 3 -1.</_> + <_>6 13 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3046864494681358e-003</threshold> + <left_val>0.4589937031269074</left_val> + <right_val>0.6826149225234985</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 2 3 -1.</_> + <_>14 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6255099587142467e-003</threshold> + <left_val>0.3079940974712372</left_val> + <right_val>0.5225008726119995</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 16 14 -1.</_> + <_>2 3 8 7 2.</_> + <_>10 10 8 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1111646965146065</threshold> + <left_val>0.2101044058799744</left_val> + <right_val>0.5080801844596863</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 4 18 -1.</_> + <_>18 2 2 9 2.</_> + <_>16 11 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108884396031499</threshold> + <left_val>0.5765355229377747</left_val> + <right_val>0.4790464043617249</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 15 2 3 -1.</_> + <_>4 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8564301580190659e-003</threshold> + <left_val>0.5065100193023682</left_val> + <right_val>0.1563598960638046</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 4 18 -1.</_> + <_>18 2 2 9 2.</_> + <_>16 11 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0548543892800808</threshold> + <left_val>0.4966914951801300</left_val> + <right_val>0.7230510711669922</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 8 3 -1.</_> + <_>1 2 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0111973397433758</threshold> + <left_val>0.2194979041814804</left_val> + <right_val>0.5098798274993897</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4069071300327778e-003</threshold> + <left_val>0.4778401851654053</left_val> + <right_val>0.6770902872085571</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 5 9 -1.</_> + <_>5 14 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0636652931571007</threshold> + <left_val>0.1936362981796265</left_val> + <right_val>0.5081024169921875</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 11 -1.</_> + <_>16 0 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.8081491887569427e-003</threshold> + <left_val>0.5999063253402710</left_val> + <right_val>0.4810341000556946</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 1 -1.</_> + <_>9 0 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1717099007219076e-003</threshold> + <left_val>0.3338333964347839</left_val> + <right_val>0.5235472917556763</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 7 -1.</_> + <_>17 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0133155202493072</threshold> + <left_val>0.6617069840431213</left_val> + <right_val>0.4919213056564331</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 3 7 -1.</_> + <_>2 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5442079640924931e-003</threshold> + <left_val>0.4488744139671326</left_val> + <right_val>0.6082184910774231</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 6 12 -1.</_> + <_>7 12 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0120378397405148</threshold> + <left_val>0.5409392118453980</left_val> + <right_val>0.3292432129383087</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 11 -1.</_> + <_>2 0 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0207010507583618</threshold> + <left_val>0.6819120049476624</left_val> + <right_val>0.4594995975494385</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 20 -1.</_> + <_>14 0 3 20 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0276082791388035</threshold> + <left_val>0.4630792140960693</left_val> + <right_val>0.5767282843589783</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 1 2 -1.</_> + <_>0 4 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2370620388537645e-003</threshold> + <left_val>0.5165379047393799</left_val> + <right_val>0.2635016143321991</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 10 8 -1.</_> + <_>10 5 5 4 2.</_> + <_>5 9 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0376693382859230</threshold> + <left_val>0.2536393105983734</left_val> + <right_val>0.5278980135917664</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 4 -1.</_> + <_>4 7 6 2 2.</_> + <_>10 9 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8057259730994701e-003</threshold> + <left_val>0.3985156118869782</left_val> + <right_val>0.5517500042915344</right_val></_></_></trees> + <stage_threshold>50.6104812622070310</stage_threshold> + <parent>10</parent> + <next>-1</next></_> + <_> + <!-- stage 12 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 6 4 -1.</_> + <_>5 1 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4299028813838959e-003</threshold> + <left_val>0.2891018092632294</left_val> + <right_val>0.6335226297378540</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 6 4 -1.</_> + <_>12 7 3 2 2.</_> + <_>9 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3813319858163595e-003</threshold> + <left_val>0.6211789250373840</left_val> + <right_val>0.3477487862110138</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 6 -1.</_> + <_>5 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2915711160749197e-003</threshold> + <left_val>0.2254412025213242</left_val> + <right_val>0.5582118034362793</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 6 4 -1.</_> + <_>12 16 3 2 2.</_> + <_>9 18 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9457940086722374e-004</threshold> + <left_val>0.3711710870265961</left_val> + <right_val>0.5930070877075195</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 12 -1.</_> + <_>9 10 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7164667891338468e-004</threshold> + <left_val>0.5651720166206360</left_val> + <right_val>0.3347995877265930</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 18 -1.</_> + <_>9 1 2 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1386410333216190e-003</threshold> + <left_val>0.3069126009941101</left_val> + <right_val>0.5508630871772766</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 12 2 -1.</_> + <_>8 12 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6403039626311511e-004</threshold> + <left_val>0.5762827992439270</left_val> + <right_val>0.3699047863483429</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 6 2 -1.</_> + <_>8 9 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9793529392918572e-005</threshold> + <left_val>0.2644244134426117</left_val> + <right_val>0.5437911152839661</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 3 6 -1.</_> + <_>9 0 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5774902254343033e-003</threshold> + <left_val>0.5051138997077942</left_val> + <right_val>0.1795724928379059</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 3 2 -1.</_> + <_>11 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6032689493149519e-004</threshold> + <left_val>0.5826969146728516</left_val> + <right_val>0.4446826875209808</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 17 4 -1.</_> + <_>1 3 17 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1404630541801453e-003</threshold> + <left_val>0.3113852143287659</left_val> + <right_val>0.5346971750259399</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 8 4 12 -1.</_> + <_>11 8 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0230869501829147</threshold> + <left_val>0.3277946114540100</left_val> + <right_val>0.5331197977066040</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0142436502501369</threshold> + <left_val>0.7381709814071655</left_val> + <right_val>0.4588063061237335</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 2 17 -1.</_> + <_>12 3 1 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0194871295243502</threshold> + <left_val>0.5256630778312683</left_val> + <right_val>0.2274471968412399</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 6 1 -1.</_> + <_>6 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.6681108698248863e-004</threshold> + <left_val>0.5511230826377869</left_val> + <right_val>0.3815006911754608</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 3 2 3 -1.</_> + <_>18 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1474709976464510e-003</threshold> + <left_val>0.5425636768341065</left_val> + <right_val>0.2543726861476898</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 3 4 -1.</_> + <_>8 6 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8026070029009134e-004</threshold> + <left_val>0.5380191802978516</left_val> + <right_val>0.3406304121017456</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 10 -1.</_> + <_>4 10 12 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0266260989010334e-003</threshold> + <left_val>0.3035801947116852</left_val> + <right_val>0.5420572161674500</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 18 4 2 -1.</_> + <_>7 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4462960795499384e-004</threshold> + <left_val>0.3990997076034546</left_val> + <right_val>0.5660110116004944</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 2 3 6 -1.</_> + <_>17 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2609760053455830e-003</threshold> + <left_val>0.5562806725502014</left_val> + <right_val>0.3940688073635101</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 6 -1.</_> + <_>9 7 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0511330589652061</threshold> + <left_val>0.4609653949737549</left_val> + <right_val>0.7118561863899231</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 2 3 6 -1.</_> + <_>17 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0177863091230392</threshold> + <left_val>0.2316166013479233</left_val> + <right_val>0.5322144031524658</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 3 4 -1.</_> + <_>9 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9679628573358059e-003</threshold> + <left_val>0.2330771982669830</left_val> + <right_val>0.5122029185295105</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0667689386755228e-003</threshold> + <left_val>0.4657444059848785</left_val> + <right_val>0.6455488204956055</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 6 3 -1.</_> + <_>0 13 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4413768015801907e-003</threshold> + <left_val>0.5154392123222351</left_val> + <right_val>0.2361633926630020</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6277279723435640e-003</threshold> + <left_val>0.6219773292541504</left_val> + <right_val>0.4476661086082459</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 3 -1.</_> + <_>3 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3530759178102016e-003</threshold> + <left_val>0.1837355047464371</left_val> + <right_val>0.5102208256721497</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 12 7 -1.</_> + <_>9 6 4 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1453091949224472</threshold> + <left_val>0.5145987272262573</left_val> + <right_val>0.1535930931568146</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4394490756094456e-003</threshold> + <left_val>0.5343660116195679</left_val> + <right_val>0.3624661862850189</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 1 3 -1.</_> + <_>14 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1283390708267689e-003</threshold> + <left_val>0.6215007901191711</left_val> + <right_val>0.4845592081546783</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 3 14 -1.</_> + <_>3 0 1 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7940260004252195e-003</threshold> + <left_val>0.4299261868000031</left_val> + <right_val>0.5824198126792908</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 14 5 6 -1.</_> + <_>12 16 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0362538211047649</threshold> + <left_val>0.5260334014892578</left_val> + <right_val>0.1439467966556549</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 5 6 -1.</_> + <_>4 16 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1746722310781479e-003</threshold> + <left_val>0.3506538867950440</left_val> + <right_val>0.5287045240402222</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 2 2 -1.</_> + <_>12 10 1 1 2.</_> + <_>11 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5383297624066472e-004</threshold> + <left_val>0.4809640944004059</left_val> + <right_val>0.6122040152549744</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 3 14 -1.</_> + <_>6 0 1 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0264802295714617</threshold> + <left_val>0.1139362007379532</left_val> + <right_val>0.5045586228370667</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 2 3 -1.</_> + <_>10 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0440660193562508e-003</threshold> + <left_val>0.6352095007896423</left_val> + <right_val>0.4794734120368958</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 3 -1.</_> + <_>0 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6993520334362984e-003</threshold> + <left_val>0.5131118297576904</left_val> + <right_val>0.2498510926961899</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 12 6 -1.</_> + <_>5 14 12 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6762931267730892e-004</threshold> + <left_val>0.5421394705772400</left_val> + <right_val>0.3709532022476196</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 3 9 -1.</_> + <_>6 14 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0413822606205940</threshold> + <left_val>0.1894959956407547</left_val> + <right_val>0.5081691741943359</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 2 2 -1.</_> + <_>12 10 1 1 2.</_> + <_>11 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0532729793339968e-003</threshold> + <left_val>0.6454367041587830</left_val> + <right_val>0.4783608913421631</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 1 3 -1.</_> + <_>5 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1648600231856108e-003</threshold> + <left_val>0.6215031147003174</left_val> + <right_val>0.4499826133251190</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 13 3 -1.</_> + <_>4 10 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6747748749330640e-004</threshold> + <left_val>0.3712610900402069</left_val> + <right_val>0.5419334769248962</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 15 6 -1.</_> + <_>6 7 5 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1737584024667740</threshold> + <left_val>0.5023643970489502</left_val> + <right_val>0.1215742006897926</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 6 -1.</_> + <_>8 5 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9049699660390615e-003</threshold> + <left_val>0.3240267932415009</left_val> + <right_val>0.5381883978843689</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 4 3 -1.</_> + <_>8 11 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2299539521336555e-003</threshold> + <left_val>0.4165507853031158</left_val> + <right_val>0.5703486204147339</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 1 3 -1.</_> + <_>15 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4329237900674343e-004</threshold> + <left_val>0.3854042887687683</left_val> + <right_val>0.5547549128532410</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 5 3 -1.</_> + <_>1 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3297258242964745e-003</threshold> + <left_val>0.2204494029283524</left_val> + <right_val>0.5097082853317261</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 7 12 -1.</_> + <_>7 7 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0417630255687982e-004</threshold> + <left_val>0.5607066154479981</left_val> + <right_val>0.4303036034107208</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 6 10 -1.</_> + <_>0 1 3 5 2.</_> + <_>3 6 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0312047004699707</threshold> + <left_val>0.4621657133102417</left_val> + <right_val>0.6982004046440125</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 3 -1.</_> + <_>16 2 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8943502157926559e-003</threshold> + <left_val>0.5269594192504883</left_val> + <right_val>0.2269068062305450</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3645310215651989e-003</threshold> + <left_val>0.6359223127365112</left_val> + <right_val>0.4537956118583679</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 3 5 -1.</_> + <_>13 2 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6793059706687927e-003</threshold> + <left_val>0.5274767875671387</left_val> + <right_val>0.2740483880043030</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 4 6 -1.</_> + <_>0 5 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0254311393946409</threshold> + <left_val>0.2038519978523254</left_val> + <right_val>0.5071732997894287</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2000601105391979e-004</threshold> + <left_val>0.4587455093860626</left_val> + <right_val>0.6119868159294128</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 1 -1.</_> + <_>9 18 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9284600168466568e-003</threshold> + <left_val>0.5071274042129517</left_val> + <right_val>0.2028204947710037</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 2 2 -1.</_> + <_>12 10 1 1 2.</_> + <_>11 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5256470912136137e-005</threshold> + <left_val>0.4812104105949402</left_val> + <right_val>0.5430821776390076</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 2 2 -1.</_> + <_>7 10 1 1 2.</_> + <_>8 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3158309739083052e-003</threshold> + <left_val>0.4625813961029053</left_val> + <right_val>0.6779323220252991</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 4 4 -1.</_> + <_>11 13 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5870389761403203e-003</threshold> + <left_val>0.5386291742324829</left_val> + <right_val>0.3431465029716492</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 8 -1.</_> + <_>9 12 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215396601706743</threshold> + <left_val>0.0259425006806850</left_val> + <right_val>0.5003222823143005</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 6 3 -1.</_> + <_>13 1 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0143344802781940</threshold> + <left_val>0.5202844738960266</left_val> + <right_val>0.1590632945299149</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>9 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3881383761763573e-003</threshold> + <left_val>0.7282481193542481</left_val> + <right_val>0.4648044109344482</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 10 -1.</_> + <_>10 7 5 5 2.</_> + <_>5 12 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1906841844320297e-003</threshold> + <left_val>0.5562356710433960</left_val> + <right_val>0.3923191130161285</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 18 8 2 -1.</_> + <_>3 18 4 1 2.</_> + <_>7 19 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8453059755265713e-003</threshold> + <left_val>0.6803392767906189</left_val> + <right_val>0.4629127979278565</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 2 6 8 -1.</_> + <_>12 2 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0547077991068363</threshold> + <left_val>0.2561671137809753</left_val> + <right_val>0.5206125974655151</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 6 8 -1.</_> + <_>6 2 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1142775490880013e-003</threshold> + <left_val>0.5189620256423950</left_val> + <right_val>0.3053877055644989</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 7 -1.</_> + <_>12 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0155750000849366</threshold> + <left_val>0.1295074969530106</left_val> + <right_val>0.5169094800949097</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 2 1 -1.</_> + <_>8 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2050600344082341e-004</threshold> + <left_val>0.5735098123550415</left_val> + <right_val>0.4230825006961823</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 1 3 -1.</_> + <_>15 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2273970060050488e-003</threshold> + <left_val>0.5289878249168396</left_val> + <right_val>0.4079791903495789</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 2 2 -1.</_> + <_>7 15 1 1 2.</_> + <_>8 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2186600361019373e-003</threshold> + <left_val>0.6575639843940735</left_val> + <right_val>0.4574409127235413</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 1 3 -1.</_> + <_>15 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3256649039685726e-003</threshold> + <left_val>0.3628047108650208</left_val> + <right_val>0.5195019841194153</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 7 -1.</_> + <_>7 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0132883097976446</threshold> + <left_val>0.1284265965223312</left_val> + <right_val>0.5043488740921021</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 7 -1.</_> + <_>18 1 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3839771058410406e-003</threshold> + <left_val>0.6292240023612976</left_val> + <right_val>0.4757505953311920</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 8 20 -1.</_> + <_>2 10 8 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2195422053337097</threshold> + <left_val>0.1487731933593750</left_val> + <right_val>0.5065013766288757</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 15 6 -1.</_> + <_>3 2 15 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9111708067357540e-003</threshold> + <left_val>0.4256102144718170</left_val> + <right_val>0.5665838718414307</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 12 2 -1.</_> + <_>4 4 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8744950648397207e-004</threshold> + <left_val>0.4004144072532654</left_val> + <right_val>0.5586857199668884</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 5 -1.</_> + <_>16 0 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2178641781210899e-003</threshold> + <left_val>0.6009116172790527</left_val> + <right_val>0.4812706112861633</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 3 4 -1.</_> + <_>8 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1111519997939467e-003</threshold> + <left_val>0.3514933884143829</left_val> + <right_val>0.5287089943885803</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 5 -1.</_> + <_>16 0 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4036400504410267e-003</threshold> + <left_val>0.4642275869846344</left_val> + <right_val>0.5924085974693298</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 6 13 -1.</_> + <_>3 7 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1229949966073036</threshold> + <left_val>0.5025529265403748</left_val> + <right_val>0.0691524818539619</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 5 -1.</_> + <_>16 0 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0123135102912784</threshold> + <left_val>0.5884591937065125</left_val> + <right_val>0.4934012889862061</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 5 -1.</_> + <_>2 0 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1471039876341820e-003</threshold> + <left_val>0.4372239112854004</left_val> + <right_val>0.5893477797508240</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 3 6 -1.</_> + <_>14 14 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5502649843692780e-003</threshold> + <left_val>0.4327551126480103</left_val> + <right_val>0.5396270155906677</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 3 6 -1.</_> + <_>3 14 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0192242693156004</threshold> + <left_val>0.1913134008646011</left_val> + <right_val>0.5068330764770508</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 3 -1.</_> + <_>16 2 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4395059552043676e-003</threshold> + <left_val>0.5308178067207336</left_val> + <right_val>0.4243533015251160</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 2 10 -1.</_> + <_>8 7 1 5 2.</_> + <_>9 12 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7751999013125896e-003</threshold> + <left_val>0.6365395784378052</left_val> + <right_val>0.4540086090564728</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 4 4 -1.</_> + <_>11 13 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0119630545377731e-003</threshold> + <left_val>0.5189834237098694</left_val> + <right_val>0.3026199936866760</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 4 3 -1.</_> + <_>0 2 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4014651104807854e-003</threshold> + <left_val>0.5105062127113342</left_val> + <right_val>0.2557682991027832</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 1 3 -1.</_> + <_>13 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0274988906458020e-004</threshold> + <left_val>0.4696914851665497</left_val> + <right_val>0.5861827731132507</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 3 5 -1.</_> + <_>8 15 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0114744501188397</threshold> + <left_val>0.5053645968437195</left_val> + <right_val>0.1527177989482880</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7023430019617081e-003</threshold> + <left_val>0.6508980989456177</left_val> + <right_val>0.4890604019165039</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0462959073483944e-003</threshold> + <left_val>0.6241816878318787</left_val> + <right_val>0.4514600038528442</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 4 14 -1.</_> + <_>10 6 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9951568990945816e-003</threshold> + <left_val>0.3432781100273132</left_val> + <right_val>0.5400953888893127</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 5 6 -1.</_> + <_>0 7 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0357007086277008</threshold> + <left_val>0.1878059059381485</left_val> + <right_val>0.5074077844619751</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 4 -1.</_> + <_>9 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5584561303257942e-004</threshold> + <left_val>0.3805277049541473</left_val> + <right_val>0.5402569770812988</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 18 10 -1.</_> + <_>6 0 6 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0542606003582478</threshold> + <left_val>0.6843714714050293</left_val> + <right_val>0.4595097005367279</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 4 14 -1.</_> + <_>10 6 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0600461438298225e-003</threshold> + <left_val>0.5502905249595642</left_val> + <right_val>0.4500527977943420</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 4 14 -1.</_> + <_>8 6 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4791832119226456e-003</threshold> + <left_val>0.3368858098983765</left_val> + <right_val>0.5310757160186768</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 1 3 -1.</_> + <_>13 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4939469983801246e-003</threshold> + <left_val>0.6487640142440796</left_val> + <right_val>0.4756175875663757</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 2 3 -1.</_> + <_>6 1 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4610530342906713e-005</threshold> + <left_val>0.4034579098224640</left_val> + <right_val>0.5451064109802246</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 18 -1.</_> + <_>19 1 1 9 2.</_> + <_>18 10 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2321938350796700e-003</threshold> + <left_val>0.6386873722076416</left_val> + <right_val>0.4824739992618561</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 4 3 -1.</_> + <_>2 2 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0645818226039410e-003</threshold> + <left_val>0.2986421883106232</left_val> + <right_val>0.5157335996627808</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 18 -1.</_> + <_>19 1 1 9 2.</_> + <_>18 10 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0304630808532238</threshold> + <left_val>0.5022199749946594</left_val> + <right_val>0.7159956097602844</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 4 6 -1.</_> + <_>1 14 2 3 2.</_> + <_>3 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0544911324977875e-003</threshold> + <left_val>0.6492452025413513</left_val> + <right_val>0.4619275033473969</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 7 6 -1.</_> + <_>10 13 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0395051389932632</threshold> + <left_val>0.5150570869445801</left_val> + <right_val>0.2450613975524902</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 6 10 -1.</_> + <_>0 10 3 5 2.</_> + <_>3 15 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4530208259820938e-003</threshold> + <left_val>0.4573669135570526</left_val> + <right_val>0.6394037008285523</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 4 -1.</_> + <_>12 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1688120430335402e-003</threshold> + <left_val>0.3865512013435364</left_val> + <right_val>0.5483661293983460</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 5 6 -1.</_> + <_>5 13 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8070670086890459e-003</threshold> + <left_val>0.5128579139709473</left_val> + <right_val>0.2701480090618134</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 1 8 -1.</_> + <_>14 10 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7365209320560098e-004</threshold> + <left_val>0.4051581919193268</left_val> + <right_val>0.5387461185455322</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 18 6 -1.</_> + <_>1 7 9 3 2.</_> + <_>10 10 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0117410803213716</threshold> + <left_val>0.5295950174331665</left_val> + <right_val>0.3719413876533508</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1833238899707794e-003</threshold> + <left_val>0.4789406955242157</left_val> + <right_val>0.6895126104354858</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 4 5 -1.</_> + <_>7 9 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0241501089185476e-004</threshold> + <left_val>0.5384489297866821</left_val> + <right_val>0.3918080925941467</right_val></_></_></trees> + <stage_threshold>54.6200714111328130</stage_threshold> + <parent>11</parent> + <next>-1</next></_> + <_> + <!-- stage 13 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 3 -1.</_> + <_>9 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0170599296689034</threshold> + <left_val>0.3948527872562408</left_val> + <right_val>0.7142534852027893</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0218408405780792</threshold> + <left_val>0.3370316028594971</left_val> + <right_val>0.6090016961097717</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 2 4 -1.</_> + <_>7 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4520049919374287e-004</threshold> + <left_val>0.3500576019287109</left_val> + <right_val>0.5987902283668518</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 19 9 -1.</_> + <_>1 3 19 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3272606134414673e-003</threshold> + <left_val>0.3267528116703033</left_val> + <right_val>0.5697240829467773</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 3 6 -1.</_> + <_>3 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7148298947140574e-004</threshold> + <left_val>0.3044599890708923</left_val> + <right_val>0.5531656742095947</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 4 4 -1.</_> + <_>15 7 2 2 2.</_> + <_>13 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7373987985774875e-004</threshold> + <left_val>0.3650012016296387</left_val> + <right_val>0.5672631263732910</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 4 4 -1.</_> + <_>3 7 2 2 2.</_> + <_>5 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4681590477703139e-005</threshold> + <left_val>0.3313541114330292</left_val> + <right_val>0.5388727188110352</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 10 8 -1.</_> + <_>9 10 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8563398197293282e-003</threshold> + <left_val>0.2697942852973938</left_val> + <right_val>0.5498778820037842</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 8 14 12 -1.</_> + <_>3 14 14 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5102273151278496e-003</threshold> + <left_val>0.5269358158111572</left_val> + <right_val>0.2762879133224487</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 10 12 -1.</_> + <_>11 5 5 6 2.</_> + <_>6 11 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0698172077536583</threshold> + <left_val>0.2909603118896484</left_val> + <right_val>0.5259246826171875</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6113670840859413e-004</threshold> + <left_val>0.5892577171325684</left_val> + <right_val>0.4073697924613953</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 5 -1.</_> + <_>9 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7149249631911516e-004</threshold> + <left_val>0.3523564040660858</left_val> + <right_val>0.5415862202644348</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 4 -1.</_> + <_>9 6 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4727490452060010e-005</threshold> + <left_val>0.5423017740249634</left_val> + <right_val>0.3503156006336212</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 5 -1.</_> + <_>9 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0484202913939953</threshold> + <left_val>0.5193945765495300</left_val> + <right_val>0.3411195874214172</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 5 -1.</_> + <_>8 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3257140526548028e-003</threshold> + <left_val>0.3157769143581390</left_val> + <right_val>0.5335376262664795</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 6 1 -1.</_> + <_>13 2 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4922149603080470e-005</threshold> + <left_val>0.4451299905776978</left_val> + <right_val>0.5536553859710693</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 6 1 -1.</_> + <_>5 2 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7173398993909359e-003</threshold> + <left_val>0.3031741976737976</left_val> + <right_val>0.5248088836669922</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 3 -1.</_> + <_>13 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9219500720500946e-003</threshold> + <left_val>0.4781453013420105</left_val> + <right_val>0.6606041789054871</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 1 4 -1.</_> + <_>0 12 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9804988987743855e-003</threshold> + <left_val>0.3186308145523071</left_val> + <right_val>0.5287625193595886</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 3 -1.</_> + <_>13 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0012109093368053e-003</threshold> + <left_val>0.6413596868515015</left_val> + <right_val>0.4749928116798401</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 2 -1.</_> + <_>9 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3491991236805916e-003</threshold> + <left_val>0.1507498025894165</left_val> + <right_val>0.5098996758460999</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 9 2 -1.</_> + <_>6 16 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3490889687091112e-003</threshold> + <left_val>0.4316158890724182</left_val> + <right_val>0.5881167054176331</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0185970701277256</threshold> + <left_val>0.4735553860664368</left_val> + <right_val>0.9089794158935547</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 4 -1.</_> + <_>18 6 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8562379991635680e-003</threshold> + <left_val>0.3553189039230347</left_val> + <right_val>0.5577837228775024</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2940430790185928e-003</threshold> + <left_val>0.4500094950199127</left_val> + <right_val>0.6580877900123596</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 16 3 2 -1.</_> + <_>15 17 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9982850537635386e-004</threshold> + <left_val>0.5629242062568665</left_val> + <right_val>0.3975878953933716</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 3 9 -1.</_> + <_>0 3 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5455459728837013e-003</threshold> + <left_val>0.5381547212600708</left_val> + <right_val>0.3605485856533051</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>9 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6104722470045090e-003</threshold> + <left_val>0.5255997180938721</left_val> + <right_val>0.1796745955944061</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>8 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2783220782876015e-003</threshold> + <left_val>0.2272856980562210</left_val> + <right_val>0.5114030241966248</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>9 5 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4598479978740215e-003</threshold> + <left_val>0.4626308083534241</left_val> + <right_val>0.6608219146728516</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3112019514665008e-003</threshold> + <left_val>0.6317539811134338</left_val> + <right_val>0.4436857998371124</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 8 12 -1.</_> + <_>11 6 4 6 2.</_> + <_>7 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6876179035753012e-003</threshold> + <left_val>0.5421109795570374</left_val> + <right_val>0.4054022133350372</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 8 12 -1.</_> + <_>5 6 4 6 2.</_> + <_>9 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9118169806897640e-003</threshold> + <left_val>0.5358477830886841</left_val> + <right_val>0.3273454904556274</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0142064504325390</threshold> + <left_val>0.7793576717376709</left_val> + <right_val>0.4975781142711639</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 3 2 -1.</_> + <_>2 17 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1705528534948826e-004</threshold> + <left_val>0.5297319889068604</left_val> + <right_val>0.3560903966426849</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6635019565001130e-003</threshold> + <left_val>0.4678094089031220</left_val> + <right_val>0.5816481709480286</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 6 6 -1.</_> + <_>2 14 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3686188980937004e-003</threshold> + <left_val>0.5276734232902527</left_val> + <right_val>0.3446420133113861</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0127995302900672</threshold> + <left_val>0.4834679961204529</left_val> + <right_val>0.7472159266471863</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 6 3 -1.</_> + <_>6 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3901201095432043e-003</threshold> + <left_val>0.4511859118938446</left_val> + <right_val>0.6401721239089966</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 5 3 -1.</_> + <_>14 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7070779837667942e-003</threshold> + <left_val>0.5335658788681030</left_val> + <right_val>0.3555220961570740</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4819339849054813e-003</threshold> + <left_val>0.4250707030296326</left_val> + <right_val>0.5772724151611328</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 5 3 -1.</_> + <_>14 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9995759986341000e-003</threshold> + <left_val>0.3003320097923279</left_val> + <right_val>0.5292900204658508</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 6 2 -1.</_> + <_>7 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0159390103071928</threshold> + <left_val>0.5067319273948669</left_val> + <right_val>0.1675581932067871</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6377349905669689e-003</threshold> + <left_val>0.4795069992542267</left_val> + <right_val>0.7085601091384888</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 15 5 3 -1.</_> + <_>1 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7334040068089962e-003</threshold> + <left_val>0.5133113265037537</left_val> + <right_val>0.2162470072507858</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 6 -1.</_> + <_>10 13 2 3 2.</_> + <_>8 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128588099032640</threshold> + <left_val>0.1938841938972473</left_val> + <right_val>0.5251371860504150</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 3 -1.</_> + <_>8 8 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2270800117403269e-004</threshold> + <left_val>0.5686538219451904</left_val> + <right_val>0.4197868108749390</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 5 4 -1.</_> + <_>12 2 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2651681471616030e-004</threshold> + <left_val>0.4224168956279755</left_val> + <right_val>0.5429695844650269</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 2 -1.</_> + <_>0 2 10 1 2.</_> + <_>10 3 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110750999301672</threshold> + <left_val>0.5113775134086609</left_val> + <right_val>0.2514517903327942</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0367282517254353</threshold> + <left_val>0.7194662094116211</left_val> + <right_val>0.4849618971347809</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 1 -1.</_> + <_>6 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8207109426148236e-004</threshold> + <left_val>0.3840261995792389</left_val> + <right_val>0.5394446253776550</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 18 13 2 -1.</_> + <_>4 19 13 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7489690110087395e-003</threshold> + <left_val>0.5937088727951050</left_val> + <right_val>0.4569182097911835</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 3 6 -1.</_> + <_>2 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0100475195795298</threshold> + <left_val>0.5138576030731201</left_val> + <right_val>0.2802298069000244</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 6 8 -1.</_> + <_>17 12 3 4 2.</_> + <_>14 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1497840583324432e-003</threshold> + <left_val>0.6090037226676941</left_val> + <right_val>0.4636121094226837</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 10 6 -1.</_> + <_>4 13 5 3 2.</_> + <_>9 16 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8833888508379459e-003</threshold> + <left_val>0.3458611071109772</left_val> + <right_val>0.5254660248756409</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 1 2 -1.</_> + <_>14 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4039360394235700e-005</threshold> + <left_val>0.5693104267120361</left_val> + <right_val>0.4082083106040955</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5498419525101781e-003</threshold> + <left_val>0.4350537061691284</left_val> + <right_val>0.5806517004966736</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 2 -1.</_> + <_>14 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7841499112546444e-003</threshold> + <left_val>0.1468873023986816</left_val> + <right_val>0.5182775259017944</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 2 -1.</_> + <_>4 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1705629478674382e-004</threshold> + <left_val>0.5293524265289307</left_val> + <right_val>0.3456174135208130</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 9 2 -1.</_> + <_>8 13 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1198898795992136e-004</threshold> + <left_val>0.4652450978755951</left_val> + <right_val>0.5942413806915283</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4507530294358730e-003</threshold> + <left_val>0.4653508961200714</left_val> + <right_val>0.7024846076965332</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 6 -1.</_> + <_>11 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5818689027801156e-004</threshold> + <left_val>0.5497295260429382</left_val> + <right_val>0.3768967092037201</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 9 12 -1.</_> + <_>5 12 9 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0174425393342972</threshold> + <left_val>0.3919087946414948</left_val> + <right_val>0.5457497835159302</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 6 -1.</_> + <_>11 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0453435294330120</threshold> + <left_val>0.1631357073783875</left_val> + <right_val>0.5154908895492554</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9190689781680703e-003</threshold> + <left_val>0.5145897865295410</left_val> + <right_val>0.2791895866394043</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 11 3 -1.</_> + <_>5 5 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0177869163453579e-003</threshold> + <left_val>0.6517636179924011</left_val> + <right_val>0.4756332933902741</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 5 10 -1.</_> + <_>7 6 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0720738470554352e-003</threshold> + <left_val>0.5514652729034424</left_val> + <right_val>0.4092685878276825</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 18 2 -1.</_> + <_>2 9 18 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9855059003457427e-004</threshold> + <left_val>0.3165240883827210</left_val> + <right_val>0.5285550951957703</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 5 3 -1.</_> + <_>7 18 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5418570302426815e-003</threshold> + <left_val>0.6853377819061279</left_val> + <right_val>0.4652808904647827</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 12 1 -1.</_> + <_>9 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4845089539885521e-003</threshold> + <left_val>0.5484588146209717</left_val> + <right_val>0.4502759873867035</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 6 6 -1.</_> + <_>0 14 3 3 2.</_> + <_>3 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0136967804282904</threshold> + <left_val>0.6395779848098755</left_val> + <right_val>0.4572555124759674</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 12 1 -1.</_> + <_>9 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0173471402376890</threshold> + <left_val>0.2751072943210602</left_val> + <right_val>0.5181614756584168</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 12 1 -1.</_> + <_>7 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0885428898036480e-003</threshold> + <left_val>0.3325636088848114</left_val> + <right_val>0.5194984078407288</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 6 7 -1.</_> + <_>14 10 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4687901437282562e-003</threshold> + <left_val>0.5942280888557434</left_val> + <right_val>0.4851819872856140</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 16 2 -1.</_> + <_>1 1 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7084840219467878e-003</threshold> + <left_val>0.4167110919952393</left_val> + <right_val>0.5519806146621704</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 10 9 -1.</_> + <_>10 12 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4809094443917274e-003</threshold> + <left_val>0.5433894991874695</left_val> + <right_val>0.4208514988422394</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 10 2 -1.</_> + <_>5 1 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7389650717377663e-003</threshold> + <left_val>0.6407189965248108</left_val> + <right_val>0.4560655057430267</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 3 2 3 -1.</_> + <_>17 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5761050209403038e-003</threshold> + <left_val>0.5214555263519287</left_val> + <right_val>0.2258227020502091</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 2 3 -1.</_> + <_>1 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1690549328923225e-003</threshold> + <left_val>0.3151527941226959</left_val> + <right_val>0.5156704783439636</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>10 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0146601703017950</threshold> + <left_val>0.4870837032794952</left_val> + <right_val>0.6689941287040710</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 4 3 -1.</_> + <_>8 5 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7231999663636088e-004</threshold> + <left_val>0.3569748997688294</left_val> + <right_val>0.5251078009605408</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 6 -1.</_> + <_>9 5 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0218037609010935</threshold> + <left_val>0.8825920820236206</left_val> + <right_val>0.4966329932212830</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 12 12 -1.</_> + <_>3 4 6 6 2.</_> + <_>9 10 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0947361066937447</threshold> + <left_val>0.1446162015199661</left_val> + <right_val>0.5061113834381104</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 6 15 -1.</_> + <_>11 2 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5825551971793175e-003</threshold> + <left_val>0.5396478772163391</left_val> + <right_val>0.4238066077232361</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 6 17 -1.</_> + <_>4 2 2 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9517090404406190e-003</threshold> + <left_val>0.4170410931110382</left_val> + <right_val>0.5497786998748779</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 6 7 -1.</_> + <_>14 10 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0121499001979828</threshold> + <left_val>0.4698367118835449</left_val> + <right_val>0.5664274096488953</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 6 7 -1.</_> + <_>3 10 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5169620104134083e-003</threshold> + <left_val>0.6267772912979126</left_val> + <right_val>0.4463135898113251</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 6 15 -1.</_> + <_>11 2 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0716679096221924</threshold> + <left_val>0.3097011148929596</left_val> + <right_val>0.5221003293991089</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 6 15 -1.</_> + <_>7 2 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0882924199104309</threshold> + <left_val>0.0811238884925842</left_val> + <right_val>0.5006365180015564</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 9 3 6 -1.</_> + <_>17 11 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0310630798339844</threshold> + <left_val>0.5155503749847412</left_val> + <right_val>0.1282255947589874</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 6 6 -1.</_> + <_>8 7 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0466218404471874</threshold> + <left_val>0.4699777960777283</left_val> + <right_val>0.7363960742950440</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 18 6 -1.</_> + <_>10 10 9 3 2.</_> + <_>1 13 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0121894897893071</threshold> + <left_val>0.3920530080795288</left_val> + <right_val>0.5518996715545654</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 10 9 -1.</_> + <_>0 12 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130161102861166</threshold> + <left_val>0.5260658264160156</left_val> + <right_val>0.3685136139392853</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4952899441123009e-003</threshold> + <left_val>0.6339294910430908</left_val> + <right_val>0.4716280996799469</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 3 4 -1.</_> + <_>5 14 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4015039748046547e-005</threshold> + <left_val>0.5333027243614197</left_val> + <right_val>0.3776184916496277</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 16 12 -1.</_> + <_>3 9 16 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1096649020910263</threshold> + <left_val>0.1765342056751251</left_val> + <right_val>0.5198346972465515</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 12 12 -1.</_> + <_>1 1 6 6 2.</_> + <_>7 7 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0279558207839727e-004</threshold> + <left_val>0.5324159860610962</left_val> + <right_val>0.3838908076286316</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 4 2 4 -1.</_> + <_>11 4 1 2 2.</_> + <_>10 6 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1126641705632210e-004</threshold> + <left_val>0.4647929966449738</left_val> + <right_val>0.5755224227905273</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 10 2 -1.</_> + <_>0 9 5 1 2.</_> + <_>5 10 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1250279862433672e-003</threshold> + <left_val>0.3236708939075470</left_val> + <right_val>0.5166770815849304</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 3 3 -1.</_> + <_>9 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4144679773598909e-003</threshold> + <left_val>0.4787439107894898</left_val> + <right_val>0.6459717750549316</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 9 2 -1.</_> + <_>3 13 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4391240226104856e-004</threshold> + <left_val>0.4409308135509491</left_val> + <right_val>0.6010255813598633</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2611189342569560e-004</threshold> + <left_val>0.4038113951683044</left_val> + <right_val>0.5493255853652954</right_val></_></_></trees> + <stage_threshold>50.1697311401367190</stage_threshold> + <parent>12</parent> + <next>-1</next></_> + <_> + <!-- stage 14 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 13 6 -1.</_> + <_>3 6 13 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0469012893736362</threshold> + <left_val>0.6600171923637390</left_val> + <right_val>0.3743801116943359</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 6 4 -1.</_> + <_>12 7 3 2 2.</_> + <_>9 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4568349579349160e-003</threshold> + <left_val>0.5783991217613220</left_val> + <right_val>0.3437797129154205</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 6 8 -1.</_> + <_>4 0 3 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5598369799554348e-003</threshold> + <left_val>0.3622266948223114</left_val> + <right_val>0.5908216238021851</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 12 -1.</_> + <_>9 11 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3170487303286791e-004</threshold> + <left_val>0.5500419139862061</left_val> + <right_val>0.2873558104038239</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 3 10 -1.</_> + <_>4 9 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3318009441718459e-003</threshold> + <left_val>0.2673169970512390</left_val> + <right_val>0.5431019067764282</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 17 8 3 -1.</_> + <_>6 18 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4347059661522508e-004</threshold> + <left_val>0.3855027854442596</left_val> + <right_val>0.5741388797760010</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 10 6 -1.</_> + <_>0 7 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0512469820678234e-003</threshold> + <left_val>0.5503209829330444</left_val> + <right_val>0.3462845087051392</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 3 2 -1.</_> + <_>13 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8657199153676629e-004</threshold> + <left_val>0.3291221857070923</left_val> + <right_val>0.5429509282112122</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 4 5 -1.</_> + <_>9 5 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4668200165033340e-003</threshold> + <left_val>0.3588382005691528</left_val> + <right_val>0.5351811051368713</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 14 3 6 -1.</_> + <_>12 16 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2021870720200241e-004</threshold> + <left_val>0.4296841919422150</left_val> + <right_val>0.5700234174728394</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 8 2 -1.</_> + <_>1 12 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4122188379988074e-004</threshold> + <left_val>0.5282164812088013</left_val> + <right_val>0.3366870880126953</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8330298848450184e-003</threshold> + <left_val>0.4559567868709564</left_val> + <right_val>0.6257336139678955</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 3 6 -1.</_> + <_>0 7 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0154564399272203</threshold> + <left_val>0.2350116968154907</left_val> + <right_val>0.5129452943801880</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 3 2 -1.</_> + <_>13 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6796779129654169e-003</threshold> + <left_val>0.5329415202140808</left_val> + <right_val>0.4155062139034271</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 4 6 -1.</_> + <_>4 14 2 3 2.</_> + <_>6 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8296569362282753e-003</threshold> + <left_val>0.4273087978363037</left_val> + <right_val>0.5804538130760193</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 3 2 -1.</_> + <_>13 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9444249123334885e-003</threshold> + <left_val>0.2912611961364746</left_val> + <right_val>0.5202686190605164</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 4 12 -1.</_> + <_>8 6 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7179559692740440e-003</threshold> + <left_val>0.5307688117027283</left_val> + <right_val>0.3585677146911621</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 8 -1.</_> + <_>17 0 3 4 2.</_> + <_>14 4 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9077627956867218e-003</threshold> + <left_val>0.4703775048255920</left_val> + <right_val>0.5941585898399353</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 3 2 -1.</_> + <_>8 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2240349575877190e-003</threshold> + <left_val>0.2141567021608353</left_val> + <right_val>0.5088796019554138</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0725888684391975e-003</threshold> + <left_val>0.4766413867473602</left_val> + <right_val>0.6841061115264893</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>6 0 4 6 2.</_> + <_>10 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0101495301350951</threshold> + <left_val>0.5360798835754395</left_val> + <right_val>0.3748497068881989</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 2 10 -1.</_> + <_>15 0 1 5 2.</_> + <_>14 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8864999583456665e-004</threshold> + <left_val>0.5720130205154419</left_val> + <right_val>0.3853805065155029</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 8 6 -1.</_> + <_>5 3 4 3 2.</_> + <_>9 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8864358104765415e-003</threshold> + <left_val>0.3693122863769531</left_val> + <right_val>0.5340958833694458</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 10 -1.</_> + <_>17 0 3 5 2.</_> + <_>14 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0261584799736738</threshold> + <left_val>0.4962374866008759</left_val> + <right_val>0.6059989929199219</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 1 2 -1.</_> + <_>9 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8560759751126170e-004</threshold> + <left_val>0.4438945949077606</left_val> + <right_val>0.6012468934059143</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 10 4 3 -1.</_> + <_>15 11 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0112687097862363</threshold> + <left_val>0.5244250297546387</left_val> + <right_val>0.1840388029813767</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 2 3 -1.</_> + <_>8 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8114619199186563e-003</threshold> + <left_val>0.6060283780097961</left_val> + <right_val>0.4409897029399872</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 14 4 -1.</_> + <_>10 13 7 2 2.</_> + <_>3 15 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6112729944288731e-003</threshold> + <left_val>0.3891170918941498</left_val> + <right_val>0.5589237213134766</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 4 3 -1.</_> + <_>1 11 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5680093616247177e-003</threshold> + <left_val>0.5069345831871033</left_val> + <right_val>0.2062619030475617</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 6 1 -1.</_> + <_>11 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8172779022715986e-004</threshold> + <left_val>0.5882201790809631</left_val> + <right_val>0.4192610979080200</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7680290329735726e-004</threshold> + <left_val>0.5533605813980103</left_val> + <right_val>0.4003368914127350</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 5 16 15 -1.</_> + <_>3 10 16 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5112537704408169e-003</threshold> + <left_val>0.3310146927833557</left_val> + <right_val>0.5444191098213196</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 4 2 -1.</_> + <_>8 12 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5948683186434209e-005</threshold> + <left_val>0.5433831810951233</left_val> + <right_val>0.3944905996322632</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 12 10 -1.</_> + <_>10 4 6 5 2.</_> + <_>4 9 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9939051754772663e-003</threshold> + <left_val>0.5600358247756958</left_val> + <right_val>0.4192714095115662</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6744439750909805e-003</threshold> + <left_val>0.6685466766357422</left_val> + <right_val>0.4604960978031158</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 8 -1.</_> + <_>10 12 2 4 2.</_> + <_>8 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0115898502990603</threshold> + <left_val>0.5357121229171753</left_val> + <right_val>0.2926830053329468</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130078401416540</threshold> + <left_val>0.4679817855358124</left_val> + <right_val>0.7307463288307190</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 3 2 -1.</_> + <_>13 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1008579749614000e-003</threshold> + <left_val>0.3937501013278961</left_val> + <right_val>0.5415065288543701</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 3 2 -1.</_> + <_>8 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0472649056464434e-004</threshold> + <left_val>0.4242376089096069</left_val> + <right_val>0.5604041218757629</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 9 14 -1.</_> + <_>9 0 3 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0144948400557041</threshold> + <left_val>0.3631210029125214</left_val> + <right_val>0.5293182730674744</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 3 -1.</_> + <_>10 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3056948818266392e-003</threshold> + <left_val>0.6860452294349670</left_val> + <right_val>0.4621821045875549</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 2 3 -1.</_> + <_>10 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1829127157106996e-004</threshold> + <left_val>0.3944096863269806</left_val> + <right_val>0.5420439243316650</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 4 6 -1.</_> + <_>0 11 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0190775208175182</threshold> + <left_val>0.1962621957063675</left_val> + <right_val>0.5037891864776611</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 2 -1.</_> + <_>6 1 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5549470339901745e-004</threshold> + <left_val>0.4086259007453919</left_val> + <right_val>0.5613973140716553</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 7 3 -1.</_> + <_>6 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9679730758070946e-003</threshold> + <left_val>0.4489121139049530</left_val> + <right_val>0.5926123261451721</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 8 9 -1.</_> + <_>8 13 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9189141504466534e-003</threshold> + <left_val>0.5335925817489624</left_val> + <right_val>0.3728385865688324</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 3 2 -1.</_> + <_>6 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9872779268771410e-003</threshold> + <left_val>0.5111321210861206</left_val> + <right_val>0.2975643873214722</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 8 -1.</_> + <_>17 1 3 4 2.</_> + <_>14 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2264618463814259e-003</threshold> + <left_val>0.5541489720344544</left_val> + <right_val>0.4824537932872772</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 6 8 -1.</_> + <_>0 1 3 4 2.</_> + <_>3 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0133533002808690</threshold> + <left_val>0.4586423933506012</left_val> + <right_val>0.6414797902107239</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 6 -1.</_> + <_>10 2 9 3 2.</_> + <_>1 5 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0335052385926247</threshold> + <left_val>0.5392425060272217</left_val> + <right_val>0.3429994881153107</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 1 -1.</_> + <_>10 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5294460356235504e-003</threshold> + <left_val>0.1703713983297348</left_val> + <right_val>0.5013315081596375</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 4 6 -1.</_> + <_>15 2 2 3 2.</_> + <_>13 5 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2801629491150379e-003</threshold> + <left_val>0.5305461883544922</left_val> + <right_val>0.4697405099868774</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0687388069927692e-003</threshold> + <left_val>0.4615545868873596</left_val> + <right_val>0.6436504721641541</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 1 3 -1.</_> + <_>13 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6880499040707946e-004</threshold> + <left_val>0.4833599030971527</left_val> + <right_val>0.6043894290924072</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 5 3 -1.</_> + <_>2 17 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9647659286856651e-003</threshold> + <left_val>0.5187637209892273</left_val> + <right_val>0.3231816887855530</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 4 6 -1.</_> + <_>15 2 2 3 2.</_> + <_>13 5 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0220577307045460</threshold> + <left_val>0.4079256951808929</left_val> + <right_val>0.5200980901718140</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 4 6 -1.</_> + <_>3 2 2 3 2.</_> + <_>5 5 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6906312713399529e-004</threshold> + <left_val>0.5331609249114990</left_val> + <right_val>0.3815600872039795</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 1 2 -1.</_> + <_>13 6 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7009328631684184e-004</threshold> + <left_val>0.5655422210693359</left_val> + <right_val>0.4688901901245117</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 2 -1.</_> + <_>5 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4284552829340100e-004</threshold> + <left_val>0.4534381031990051</left_val> + <right_val>0.6287400126457214</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 2 -1.</_> + <_>13 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2227810695767403e-003</threshold> + <left_val>0.5350633263587952</left_val> + <right_val>0.3303655982017517</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 2 2 -1.</_> + <_>6 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4130521602928638e-003</threshold> + <left_val>0.1113687008619309</left_val> + <right_val>0.5005434751510620</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 17 3 2 -1.</_> + <_>13 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4520040167553816e-005</threshold> + <left_val>0.5628737807273865</left_val> + <right_val>0.4325133860111237</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 16 4 4 -1.</_> + <_>6 16 2 2 2.</_> + <_>8 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3369169502984732e-004</threshold> + <left_val>0.4165835082530975</left_val> + <right_val>0.5447791218757629</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 3 -1.</_> + <_>9 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2894547805190086e-003</threshold> + <left_val>0.4860391020774841</left_val> + <right_val>0.6778649091720581</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 9 6 -1.</_> + <_>0 15 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9103150852024555e-003</threshold> + <left_val>0.5262305140495300</left_val> + <right_val>0.3612113893032074</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 6 -1.</_> + <_>9 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0129005396738648</threshold> + <left_val>0.5319377183914185</left_val> + <right_val>0.3250288069248200</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6982979401946068e-003</threshold> + <left_val>0.4618245065212250</left_val> + <right_val>0.6665925979614258</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 18 6 -1.</_> + <_>1 12 18 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0104398597031832</threshold> + <left_val>0.5505670905113220</left_val> + <right_val>0.3883604109287262</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 2 -1.</_> + <_>8 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0443191062659025e-003</threshold> + <left_val>0.4697853028774262</left_val> + <right_val>0.7301844954490662</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 9 6 2 -1.</_> + <_>7 10 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1593751888722181e-004</threshold> + <left_val>0.3830839097499847</left_val> + <right_val>0.5464984178543091</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 2 3 -1.</_> + <_>8 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4247159492224455e-003</threshold> + <left_val>0.2566300034523010</left_val> + <right_val>0.5089530944824219</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 5 3 4 -1.</_> + <_>18 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3538565561175346e-003</threshold> + <left_val>0.6469966173171997</left_val> + <right_val>0.4940795898437500</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 19 18 1 -1.</_> + <_>7 19 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0523389987647533</threshold> + <left_val>0.4745982885360718</left_val> + <right_val>0.7878770828247070</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 3 2 -1.</_> + <_>10 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5765620414167643e-003</threshold> + <left_val>0.5306664705276489</left_val> + <right_val>0.2748498022556305</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 1 6 -1.</_> + <_>1 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1555317845195532e-004</threshold> + <left_val>0.5413125753402710</left_val> + <right_val>0.4041908979415894</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 17 8 3 -1.</_> + <_>12 17 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0105166798457503</threshold> + <left_val>0.6158512234687805</left_val> + <right_val>0.4815283119678497</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 3 4 -1.</_> + <_>1 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7347927726805210e-003</threshold> + <left_val>0.4695805907249451</left_val> + <right_val>0.7028980851173401</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3226778507232666e-003</threshold> + <left_val>0.2849566042423248</left_val> + <right_val>0.5304684042930603</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 2 2 -1.</_> + <_>7 11 1 1 2.</_> + <_>8 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5534399319440126e-003</threshold> + <left_val>0.7056984901428223</left_val> + <right_val>0.4688892066478729</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 2 5 -1.</_> + <_>11 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0268510231981054e-004</threshold> + <left_val>0.3902932107448578</left_val> + <right_val>0.5573464035987854</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 2 5 -1.</_> + <_>8 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1395188570022583e-006</threshold> + <left_val>0.3684231936931610</left_val> + <right_val>0.5263987779617310</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 3 -1.</_> + <_>15 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6711989883333445e-003</threshold> + <left_val>0.3849175870418549</left_val> + <right_val>0.5387271046638489</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 3 -1.</_> + <_>5 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9260449595749378e-003</threshold> + <left_val>0.4729771912097931</left_val> + <right_val>0.7447251081466675</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 19 15 1 -1.</_> + <_>9 19 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3908702209591866e-003</threshold> + <left_val>0.4809181094169617</left_val> + <right_val>0.5591921806335449</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 19 15 1 -1.</_> + <_>6 19 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0177936293184757</threshold> + <left_val>0.6903678178787231</left_val> + <right_val>0.4676927030086517</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 3 -1.</_> + <_>15 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0469669252634048e-003</threshold> + <left_val>0.5370690226554871</left_val> + <right_val>0.3308162093162537</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 4 15 -1.</_> + <_>7 0 2 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0298914890736341</threshold> + <left_val>0.5139865279197693</left_val> + <right_val>0.3309059143066406</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 5 -1.</_> + <_>9 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5494900289922953e-003</threshold> + <left_val>0.4660237133502960</left_val> + <right_val>0.6078342795372009</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 7 -1.</_> + <_>10 5 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4956969534978271e-003</threshold> + <left_val>0.4404835999011993</left_val> + <right_val>0.5863919854164124</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 11 3 3 -1.</_> + <_>16 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.5885928021743894e-004</threshold> + <left_val>0.5435971021652222</left_val> + <right_val>0.4208523035049439</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 3 3 -1.</_> + <_>1 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9643701640889049e-004</threshold> + <left_val>0.5370578169822693</left_val> + <right_val>0.4000622034072876</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 3 -1.</_> + <_>6 7 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7280810754746199e-003</threshold> + <left_val>0.5659412741661072</left_val> + <right_val>0.4259642958641052</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 6 2 -1.</_> + <_>0 16 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3026480339467525e-003</threshold> + <left_val>0.5161657929420471</left_val> + <right_val>0.3350869119167328</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 6 -1.</_> + <_>7 0 6 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.2515163123607636</threshold> + <left_val>0.4869661927223206</left_val> + <right_val>0.7147309780120850</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 4 -1.</_> + <_>7 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6328022144734859e-003</threshold> + <left_val>0.2727448940277100</left_val> + <right_val>0.5083789825439453</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 4 10 -1.</_> + <_>16 10 2 5 2.</_> + <_>14 15 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0404344908893108</threshold> + <left_val>0.6851438879966736</left_val> + <right_val>0.5021767020225525</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 3 2 -1.</_> + <_>4 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4972220014897175e-005</threshold> + <left_val>0.4284465014934540</left_val> + <right_val>0.5522555112838745</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 2 2 -1.</_> + <_>11 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4050309730228037e-004</threshold> + <left_val>0.4226118922233582</left_val> + <right_val>0.5390074849128723</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 4 10 -1.</_> + <_>2 10 2 5 2.</_> + <_>4 15 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0236578397452831</threshold> + <left_val>0.4744631946086884</left_val> + <right_val>0.7504366040229797</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 20 6 -1.</_> + <_>10 13 10 3 2.</_> + <_>0 16 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1449104472994804e-003</threshold> + <left_val>0.4245058894157410</left_val> + <right_val>0.5538362860679627</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 2 15 -1.</_> + <_>1 5 1 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6992130335420370e-003</threshold> + <left_val>0.5952357053756714</left_val> + <right_val>0.4529713094234467</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 18 4 -1.</_> + <_>10 7 9 2 2.</_> + <_>1 9 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7718601785600185e-003</threshold> + <left_val>0.4137794077396393</left_val> + <right_val>0.5473399758338928</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 17 -1.</_> + <_>1 0 1 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2669530957937241e-003</threshold> + <left_val>0.4484114944934845</left_val> + <right_val>0.5797994136810303</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 6 16 6 -1.</_> + <_>10 6 8 3 2.</_> + <_>2 9 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7791989957913756e-003</threshold> + <left_val>0.5624858736991882</left_val> + <right_val>0.4432444870471954</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 1 3 -1.</_> + <_>8 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6774770338088274e-003</threshold> + <left_val>0.4637751877307892</left_val> + <right_val>0.6364241838455200</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 2 -1.</_> + <_>8 16 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1732629500329494e-003</threshold> + <left_val>0.4544503092765808</left_val> + <right_val>0.5914415717124939</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 8 2 -1.</_> + <_>5 2 4 1 2.</_> + <_>9 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6998171173036098e-004</threshold> + <left_val>0.5334752798080444</left_val> + <right_val>0.3885917961597443</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 8 6 -1.</_> + <_>6 14 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6378340600058436e-004</threshold> + <left_val>0.5398585200309753</left_val> + <right_val>0.3744941949844360</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 2 -1.</_> + <_>9 14 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5684569370932877e-004</threshold> + <left_val>0.4317873120307922</left_val> + <right_val>0.5614616274833679</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 6 -1.</_> + <_>18 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215113703161478</threshold> + <left_val>0.1785925030708313</left_val> + <right_val>0.5185542702674866</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 2 -1.</_> + <_>9 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3081369979772717e-004</threshold> + <left_val>0.4342499077320099</left_val> + <right_val>0.5682849884033203</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 6 -1.</_> + <_>18 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0219920407980680</threshold> + <left_val>0.5161716938018799</left_val> + <right_val>0.2379394024610519</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 1 3 -1.</_> + <_>9 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0136500764638186e-004</threshold> + <left_val>0.5986763238906860</left_val> + <right_val>0.4466426968574524</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 6 -1.</_> + <_>18 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2736099138855934e-003</threshold> + <left_val>0.4108217954635620</left_val> + <right_val>0.5251057147979736</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 2 6 -1.</_> + <_>0 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6831789184361696e-003</threshold> + <left_val>0.5173814296722412</left_val> + <right_val>0.3397518098354340</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>9 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9525681212544441e-003</threshold> + <left_val>0.6888983249664307</left_val> + <right_val>0.4845924079418182</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 2 3 -1.</_> + <_>3 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5382299898192286e-003</threshold> + <left_val>0.5178567171096802</left_val> + <right_val>0.3454113900661469</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 4 3 -1.</_> + <_>13 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0140435304492712</threshold> + <left_val>0.1678421050310135</left_val> + <right_val>0.5188667774200440</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4315890148282051e-003</threshold> + <left_val>0.4368256926536560</left_val> + <right_val>0.5655773878097534</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 10 6 -1.</_> + <_>5 4 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0340142287313938</threshold> + <left_val>0.7802296280860901</left_val> + <right_val>0.4959217011928558</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 4 3 -1.</_> + <_>3 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0120272999629378</threshold> + <left_val>0.1585101038217545</left_val> + <right_val>0.5032231807708740</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 15 5 -1.</_> + <_>8 7 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1331661939620972</threshold> + <left_val>0.5163304805755615</left_val> + <right_val>0.2755128145217896</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 12 2 -1.</_> + <_>7 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5221949433907866e-003</threshold> + <left_val>0.3728317916393280</left_val> + <right_val>0.5214552283287048</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 3 9 -1.</_> + <_>11 3 1 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3929271679371595e-004</threshold> + <left_val>0.5838379263877869</left_val> + <right_val>0.4511165022850037</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 4 6 -1.</_> + <_>10 6 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0277197398245335</threshold> + <left_val>0.4728286862373352</left_val> + <right_val>0.7331544756889343</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 4 3 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1030150130391121e-003</threshold> + <left_val>0.5302202105522156</left_val> + <right_val>0.4101563096046448</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 4 9 -1.</_> + <_>2 9 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0778612196445465</threshold> + <left_val>0.4998334050178528</left_val> + <right_val>0.1272961944341660</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 3 5 -1.</_> + <_>10 13 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0158549398183823</threshold> + <left_val>0.0508333593606949</left_val> + <right_val>0.5165656208992004</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 3 -1.</_> + <_>9 7 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9725300632417202e-003</threshold> + <left_val>0.6798133850097656</left_val> + <right_val>0.4684231877326965</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7676506265997887e-004</threshold> + <left_val>0.6010771989822388</left_val> + <right_val>0.4788931906223297</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 2 -1.</_> + <_>9 7 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4647710379213095e-003</threshold> + <left_val>0.3393397927284241</left_val> + <right_val>0.5220503807067871</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 12 2 -1.</_> + <_>9 9 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7937700077891350e-003</threshold> + <left_val>0.4365136921405792</left_val> + <right_val>0.5239663124084473</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 10 3 -1.</_> + <_>10 6 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0326080210506916</threshold> + <left_val>0.5052723884582520</left_val> + <right_val>0.2425214946269989</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 3 1 -1.</_> + <_>11 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8514421107247472e-004</threshold> + <left_val>0.5733973979949951</left_val> + <right_val>0.4758574068546295</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 11 15 -1.</_> + <_>0 6 11 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0296326000243425</threshold> + <left_val>0.3892289102077484</left_val> + <right_val>0.5263597965240479</right_val></_></_></trees> + <stage_threshold>66.6691207885742190</stage_threshold> + <parent>13</parent> + <next>-1</next></_> + <_> + <!-- stage 15 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 6 -1.</_> + <_>7 0 6 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0465508513152599</threshold> + <left_val>0.3276950120925903</left_val> + <right_val>0.6240522861480713</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 1 -1.</_> + <_>9 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9537127166986465e-003</threshold> + <left_val>0.4256485104560852</left_val> + <right_val>0.6942939162254334</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 16 6 4 -1.</_> + <_>5 16 3 2 2.</_> + <_>8 18 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8221561377868056e-004</threshold> + <left_val>0.3711487054824829</left_val> + <right_val>0.5900732874870300</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 9 8 -1.</_> + <_>6 9 9 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9348249770700932e-004</threshold> + <left_val>0.2041133940219879</left_val> + <right_val>0.5300545096397400</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 2 6 -1.</_> + <_>5 13 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6710508973337710e-004</threshold> + <left_val>0.5416126251220703</left_val> + <right_val>0.3103179037570953</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 8 10 -1.</_> + <_>11 6 4 5 2.</_> + <_>7 11 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7818060480058193e-003</threshold> + <left_val>0.5277832746505737</left_val> + <right_val>0.3467069864273071</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 8 10 -1.</_> + <_>5 6 4 5 2.</_> + <_>9 11 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6779078547842801e-004</threshold> + <left_val>0.5308231115341187</left_val> + <right_val>0.3294492065906525</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 2 -1.</_> + <_>9 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0335160772665404e-005</threshold> + <left_val>0.5773872733116150</left_val> + <right_val>0.3852097094058991</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 8 2 -1.</_> + <_>5 13 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8038009814918041e-004</threshold> + <left_val>0.4317438900470734</left_val> + <right_val>0.6150057911872864</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 2 8 2 -1.</_> + <_>10 3 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2553851380944252e-003</threshold> + <left_val>0.2933903932571411</left_val> + <right_val>0.5324292778968811</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 2 10 -1.</_> + <_>4 0 1 5 2.</_> + <_>5 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4735610350035131e-004</threshold> + <left_val>0.5468844771385193</left_val> + <right_val>0.3843030035495758</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 2 -1.</_> + <_>9 11 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4724259381182492e-004</threshold> + <left_val>0.4281542897224426</left_val> + <right_val>0.5755587220191956</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 15 3 -1.</_> + <_>2 9 15 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1864770203828812e-003</threshold> + <left_val>0.3747301101684570</left_val> + <right_val>0.5471466183662415</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3936580400913954e-003</threshold> + <left_val>0.4537783861160278</left_val> + <right_val>0.6111528873443604</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 3 2 -1.</_> + <_>8 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5390539774671197e-003</threshold> + <left_val>0.2971341907978058</left_val> + <right_val>0.5189538002014160</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1968790143728256e-003</threshold> + <left_val>0.6699066758155823</left_val> + <right_val>0.4726476967334747</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1499789222143590e-004</threshold> + <left_val>0.3384954035282135</left_val> + <right_val>0.5260317921638489</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 2 3 6 -1.</_> + <_>17 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4359830208122730e-003</threshold> + <left_val>0.5399122238159180</left_val> + <right_val>0.3920140862464905</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 3 4 -1.</_> + <_>2 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6606200262904167e-003</threshold> + <left_val>0.4482578039169312</left_val> + <right_val>0.6119617819786072</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 4 6 -1.</_> + <_>14 10 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5287200221791863e-003</threshold> + <left_val>0.3711237907409668</left_val> + <right_val>0.5340266227722168</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 3 8 -1.</_> + <_>2 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7397250309586525e-003</threshold> + <left_val>0.6031088232994080</left_val> + <right_val>0.4455145001411438</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 6 -1.</_> + <_>8 16 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0148291299119592</threshold> + <left_val>0.2838754057884216</left_val> + <right_val>0.5341861844062805</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 2 2 -1.</_> + <_>3 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2275557108223438e-004</threshold> + <left_val>0.5209547281265259</left_val> + <right_val>0.3361653983592987</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 4 6 -1.</_> + <_>14 10 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0835298076272011</threshold> + <left_val>0.5119969844818115</left_val> + <right_val>0.0811644494533539</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 4 6 -1.</_> + <_>2 10 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5633148662745953e-004</threshold> + <left_val>0.3317120075225830</left_val> + <right_val>0.5189831256866455</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 1 6 -1.</_> + <_>10 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8403859883546829e-003</threshold> + <left_val>0.5247598290443420</left_val> + <right_val>0.2334959059953690</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 3 6 -1.</_> + <_>8 5 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5953830443322659e-003</threshold> + <left_val>0.5750094056129456</left_val> + <right_val>0.4295622110366821</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 2 6 -1.</_> + <_>12 2 1 3 2.</_> + <_>11 5 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4766020689858124e-005</threshold> + <left_val>0.4342445135116577</left_val> + <right_val>0.5564029216766357</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 6 5 -1.</_> + <_>8 6 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0298629105091095</threshold> + <left_val>0.4579147100448608</left_val> + <right_val>0.6579188108444214</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 1 3 6 -1.</_> + <_>17 3 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0113255903124809</threshold> + <left_val>0.5274311900138855</left_val> + <right_val>0.3673888146877289</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7828645482659340e-003</threshold> + <left_val>0.7100368738174439</left_val> + <right_val>0.4642167091369629</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 18 3 2 -1.</_> + <_>10 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3639959767460823e-003</threshold> + <left_val>0.5279216170310974</left_val> + <right_val>0.2705877125263214</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 2 -1.</_> + <_>9 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1804728098213673e-003</threshold> + <left_val>0.5072525143623352</left_val> + <right_val>0.2449083030223846</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 5 2 -1.</_> + <_>12 4 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5668511302210391e-004</threshold> + <left_val>0.4283105134963989</left_val> + <right_val>0.5548691153526306</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 5 12 -1.</_> + <_>7 7 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7140368949621916e-003</threshold> + <left_val>0.5519387722015381</left_val> + <right_val>0.4103653132915497</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0253042895346880</threshold> + <left_val>0.6867002248764038</left_val> + <right_val>0.4869889020919800</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 2 2 -1.</_> + <_>4 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4454080741852522e-004</threshold> + <left_val>0.3728874027729034</left_val> + <right_val>0.5287693142890930</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 4 2 -1.</_> + <_>13 14 2 1 2.</_> + <_>11 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3935231668874621e-004</threshold> + <left_val>0.6060152053833008</left_val> + <right_val>0.4616062045097351</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0172800496220589</threshold> + <left_val>0.5049635767936707</left_val> + <right_val>0.1819823980331421</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3595077954232693e-003</threshold> + <left_val>0.1631239950656891</left_val> + <right_val>0.5232778787612915</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 1 3 -1.</_> + <_>5 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0298109846189618e-003</threshold> + <left_val>0.4463278055191040</left_val> + <right_val>0.6176549196243286</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 10 6 1 -1.</_> + <_>10 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0117109632119536e-003</threshold> + <left_val>0.5473384857177734</left_val> + <right_val>0.4300698935985565</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 6 1 -1.</_> + <_>7 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103088002651930</threshold> + <left_val>0.1166985034942627</left_val> + <right_val>0.5000867247581482</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 3 -1.</_> + <_>9 18 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4682018235325813e-003</threshold> + <left_val>0.4769287109375000</left_val> + <right_val>0.6719213724136353</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 1 3 -1.</_> + <_>4 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1696460731327534e-004</threshold> + <left_val>0.3471089899539948</left_val> + <right_val>0.5178164839744568</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 3 3 -1.</_> + <_>12 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3922820109874010e-003</threshold> + <left_val>0.4785236120223999</left_val> + <right_val>0.6216310858726502</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 3 -1.</_> + <_>4 6 12 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5573818758130074e-003</threshold> + <left_val>0.5814796090126038</left_val> + <right_val>0.4410085082054138</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 3 -1.</_> + <_>9 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7024032361805439e-004</threshold> + <left_val>0.3878000080585480</left_val> + <right_val>0.5465722084045410</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 3 3 -1.</_> + <_>5 9 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7125990539789200e-003</threshold> + <left_val>0.1660051047801971</left_val> + <right_val>0.4995836019515991</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 9 17 -1.</_> + <_>9 0 3 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103063201531768</threshold> + <left_val>0.4093391001224518</left_val> + <right_val>0.5274233818054199</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 1 3 -1.</_> + <_>9 13 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0940979011356831e-003</threshold> + <left_val>0.6206194758415222</left_val> + <right_val>0.4572280049324036</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 15 -1.</_> + <_>9 10 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8099051713943481e-003</threshold> + <left_val>0.5567759275436401</left_val> + <right_val>0.4155600070953369</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 2 3 -1.</_> + <_>8 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0746059706434608e-003</threshold> + <left_val>0.5638927817344666</left_val> + <right_val>0.4353024959564209</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 1 3 -1.</_> + <_>10 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1550289820879698e-003</threshold> + <left_val>0.4826265871524811</left_val> + <right_val>0.6749758124351502</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 5 -1.</_> + <_>9 1 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0317423194646835</threshold> + <left_val>0.5048379898071289</left_val> + <right_val>0.1883248984813690</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 20 2 -1.</_> + <_>0 0 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0783827230334282</threshold> + <left_val>0.2369548976421356</left_val> + <right_val>0.5260158181190491</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 3 -1.</_> + <_>2 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7415119372308254e-003</threshold> + <left_val>0.5048828721046448</left_val> + <right_val>0.2776469886302948</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9014600440859795e-003</threshold> + <left_val>0.6238604784011841</left_val> + <right_val>0.4693317115306854</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 5 9 15 -1.</_> + <_>2 10 9 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6427931152284145e-003</threshold> + <left_val>0.3314141929149628</left_val> + <right_val>0.5169777274131775</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 12 10 -1.</_> + <_>11 0 6 5 2.</_> + <_>5 5 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1094966009259224</threshold> + <left_val>0.2380045056343079</left_val> + <right_val>0.5183441042900085</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 2 3 -1.</_> + <_>6 1 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4075913289561868e-005</threshold> + <left_val>0.4069635868072510</left_val> + <right_val>0.5362150073051453</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 6 1 -1.</_> + <_>12 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0593802006915212e-004</threshold> + <left_val>0.5506706237792969</left_val> + <right_val>0.4374594092369080</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 2 10 -1.</_> + <_>3 1 1 5 2.</_> + <_>4 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2131777890026569e-004</threshold> + <left_val>0.5525709986686707</left_val> + <right_val>0.4209375977516174</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0276539443293586e-005</threshold> + <left_val>0.5455474853515625</left_val> + <right_val>0.4748266041278839</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 4 6 -1.</_> + <_>4 15 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8065142259001732e-003</threshold> + <left_val>0.5157995820045471</left_val> + <right_val>0.3424577116966248</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7202789895236492e-003</threshold> + <left_val>0.5013207793235779</left_val> + <right_val>0.6331263780593872</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 1 -1.</_> + <_>6 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3016929733566940e-004</threshold> + <left_val>0.5539718270301819</left_val> + <right_val>0.4226869940757752</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 18 4 -1.</_> + <_>11 12 9 2 2.</_> + <_>2 14 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8016388900578022e-003</threshold> + <left_val>0.4425095021724701</left_val> + <right_val>0.5430780053138733</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 2 -1.</_> + <_>5 7 1 1 2.</_> + <_>6 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5399310979992151e-003</threshold> + <left_val>0.7145782113075256</left_val> + <right_val>0.4697605073451996</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 4 2 -1.</_> + <_>16 4 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4278929447755218e-003</threshold> + <left_val>0.4070445001125336</left_val> + <right_val>0.5399605035781860</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 18 -1.</_> + <_>0 2 1 9 2.</_> + <_>1 11 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0251425504684448</threshold> + <left_val>0.7884690761566162</left_val> + <right_val>0.4747352004051209</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 4 -1.</_> + <_>10 2 9 2 2.</_> + <_>1 4 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8899609353393316e-003</threshold> + <left_val>0.4296191930770874</left_val> + <right_val>0.5577110052108765</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 1 3 -1.</_> + <_>9 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3947459198534489e-003</threshold> + <left_val>0.4693162143230438</left_val> + <right_val>0.7023944258689880</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 18 4 -1.</_> + <_>11 12 9 2 2.</_> + <_>2 14 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0246784202754498</threshold> + <left_val>0.5242322087287903</left_val> + <right_val>0.3812510073184967</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 18 4 -1.</_> + <_>0 12 9 2 2.</_> + <_>9 14 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0380476787686348</threshold> + <left_val>0.5011739730834961</left_val> + <right_val>0.1687828004360199</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 5 3 -1.</_> + <_>11 5 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9424865543842316e-003</threshold> + <left_val>0.4828582108020783</left_val> + <right_val>0.6369568109512329</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 7 3 -1.</_> + <_>6 5 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5110049862414598e-003</threshold> + <left_val>0.5906485915184021</left_val> + <right_val>0.4487667977809906</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 17 3 3 -1.</_> + <_>13 18 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4201741479337215e-003</threshold> + <left_val>0.5241097807884216</left_val> + <right_val>0.2990570068359375</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 4 -1.</_> + <_>9 1 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9802159406244755e-003</threshold> + <left_val>0.3041465878486633</left_val> + <right_val>0.5078489780426025</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 2 4 -1.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4580078944563866e-004</threshold> + <left_val>0.4128139019012451</left_val> + <right_val>0.5256826281547546</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 9 3 -1.</_> + <_>3 17 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0104709500446916</threshold> + <left_val>0.5808395147323608</left_val> + <right_val>0.4494296014308929</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 2 8 -1.</_> + <_>12 0 1 4 2.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3369204550981522e-003</threshold> + <left_val>0.5246552824974060</left_val> + <right_val>0.2658948898315430</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 6 12 -1.</_> + <_>0 8 3 6 2.</_> + <_>3 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0279369000345469</threshold> + <left_val>0.4674955010414124</left_val> + <right_val>0.7087256908416748</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 4 12 -1.</_> + <_>10 13 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4277678504586220e-003</threshold> + <left_val>0.5409486889839172</left_val> + <right_val>0.3758518099784851</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 8 14 -1.</_> + <_>5 10 8 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0235845092684031</threshold> + <left_val>0.3758639991283417</left_val> + <right_val>0.5238550901412964</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 6 1 -1.</_> + <_>14 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1452640173956752e-003</threshold> + <left_val>0.4329578876495361</left_val> + <right_val>0.5804247260093689</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 10 4 -1.</_> + <_>0 6 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3468660442158580e-004</threshold> + <left_val>0.5280618071556091</left_val> + <right_val>0.3873069882392883</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 5 8 -1.</_> + <_>10 4 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0106485402211547</threshold> + <left_val>0.4902113080024719</left_val> + <right_val>0.5681251883506775</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 4 8 -1.</_> + <_>8 1 2 4 2.</_> + <_>10 5 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9418050437234342e-004</threshold> + <left_val>0.5570880174636841</left_val> + <right_val>0.4318251013755798</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 6 1 -1.</_> + <_>11 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3270479394122958e-004</threshold> + <left_val>0.5658439993858337</left_val> + <right_val>0.4343554973602295</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 3 4 -1.</_> + <_>9 9 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0125510636717081e-003</threshold> + <left_val>0.6056739091873169</left_val> + <right_val>0.4537523984909058</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 6 -1.</_> + <_>18 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4854319635778666e-003</threshold> + <left_val>0.5390477180480957</left_val> + <right_val>0.4138010144233704</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>9 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8237880431115627e-003</threshold> + <left_val>0.4354828894138336</left_val> + <right_val>0.5717188715934753</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 13 3 -1.</_> + <_>7 2 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0166566595435143</threshold> + <left_val>0.3010913133621216</left_val> + <right_val>0.5216122865676880</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 1 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0349558265879750e-004</threshold> + <left_val>0.5300151109695435</left_val> + <right_val>0.3818396925926209</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 3 6 -1.</_> + <_>12 13 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4170378930866718e-003</threshold> + <left_val>0.5328028798103333</left_val> + <right_val>0.4241400063037872</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6222729249857366e-004</threshold> + <left_val>0.5491728186607361</left_val> + <right_val>0.4186977148056030</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 18 10 -1.</_> + <_>10 4 9 5 2.</_> + <_>1 9 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1163002029061317</threshold> + <left_val>0.1440722048282623</left_val> + <right_val>0.5226451158523560</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 4 9 -1.</_> + <_>8 9 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146950101479888</threshold> + <left_val>0.7747725248336792</left_val> + <right_val>0.4715717136859894</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 4 3 -1.</_> + <_>8 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1972130052745342e-003</threshold> + <left_val>0.5355433821678162</left_val> + <right_val>0.3315644860267639</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6965209185145795e-004</threshold> + <left_val>0.5767235159873962</left_val> + <right_val>0.4458136856555939</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 4 3 -1.</_> + <_>14 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5144998952746391e-003</threshold> + <left_val>0.5215674042701721</left_val> + <right_val>0.3647888898849487</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 3 10 -1.</_> + <_>6 10 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0213000606745481</threshold> + <left_val>0.4994204938411713</left_val> + <right_val>0.1567950993776321</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1881409231573343e-003</threshold> + <left_val>0.4742200076580048</left_val> + <right_val>0.6287270188331604</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 1 6 -1.</_> + <_>0 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0019777417182922e-004</threshold> + <left_val>0.5347954034805298</left_val> + <right_val>0.3943752050399780</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 1 3 -1.</_> + <_>10 16 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1772277802228928e-003</threshold> + <left_val>0.6727191805839539</left_val> + <right_val>0.5013138055801392</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 4 3 -1.</_> + <_>2 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3764649890363216e-003</threshold> + <left_val>0.3106675148010254</left_val> + <right_val>0.5128793120384216</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 3 2 8 -1.</_> + <_>19 3 1 4 2.</_> + <_>18 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6299960445612669e-003</threshold> + <left_val>0.4886310100555420</left_val> + <right_val>0.5755215883255005</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 2 8 -1.</_> + <_>0 3 1 4 2.</_> + <_>1 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0458688959479332e-003</threshold> + <left_val>0.6025794148445129</left_val> + <right_val>0.4558076858520508</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 14 10 -1.</_> + <_>10 7 7 5 2.</_> + <_>3 12 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0694827064871788</threshold> + <left_val>0.5240747928619385</left_val> + <right_val>0.2185259014368057</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 19 3 -1.</_> + <_>0 8 19 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0240489393472672</threshold> + <left_val>0.5011867284774780</left_val> + <right_val>0.2090622037649155</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 6 3 3 -1.</_> + <_>12 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1095340382307768e-003</threshold> + <left_val>0.4866712093353272</left_val> + <right_val>0.7108548283576965</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 1 3 -1.</_> + <_>0 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2503260513767600e-003</threshold> + <left_val>0.3407891094684601</left_val> + <right_val>0.5156195163726807</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 6 3 3 -1.</_> + <_>12 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0281190043315291e-003</threshold> + <left_val>0.5575572252273560</left_val> + <right_val>0.4439432024955750</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 3 3 -1.</_> + <_>5 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8893622159957886e-003</threshold> + <left_val>0.6402000784873962</left_val> + <right_val>0.4620442092418671</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 4 2 -1.</_> + <_>8 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1094801640138030e-004</threshold> + <left_val>0.3766441941261292</left_val> + <right_val>0.5448899865150452</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 4 12 -1.</_> + <_>8 3 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7686357758939266e-003</threshold> + <left_val>0.3318648934364319</left_val> + <right_val>0.5133677124977112</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 2 3 -1.</_> + <_>13 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8506490159779787e-003</threshold> + <left_val>0.4903570115566254</left_val> + <right_val>0.6406934857368469</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 20 4 -1.</_> + <_>0 12 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0997994691133499</threshold> + <left_val>0.1536051034927368</left_val> + <right_val>0.5015562176704407</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 17 14 -1.</_> + <_>2 7 17 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.3512834906578064</threshold> + <left_val>0.0588231310248375</left_val> + <right_val>0.5174378752708435</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 6 10 -1.</_> + <_>0 0 3 5 2.</_> + <_>3 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0452445708215237</threshold> + <left_val>0.6961488723754883</left_val> + <right_val>0.4677872955799103</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 6 4 -1.</_> + <_>14 6 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0714815780520439</threshold> + <left_val>0.5167986154556274</left_val> + <right_val>0.1038092970848084</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 6 4 -1.</_> + <_>3 6 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1895780228078365e-003</threshold> + <left_val>0.4273078143596649</left_val> + <right_val>0.5532060861587524</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 7 2 -1.</_> + <_>13 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9242651332169771e-004</threshold> + <left_val>0.4638943970203400</left_val> + <right_val>0.5276389122009277</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 7 2 -1.</_> + <_>0 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6788389766588807e-003</threshold> + <left_val>0.5301648974418640</left_val> + <right_val>0.3932034969329834</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 14 2 -1.</_> + <_>13 11 7 1 2.</_> + <_>6 12 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2163488902151585e-003</threshold> + <left_val>0.5630694031715393</left_val> + <right_val>0.4757033884525299</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 2 2 -1.</_> + <_>8 5 1 1 2.</_> + <_>9 6 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1568699846975505e-004</threshold> + <left_val>0.4307535886764526</left_val> + <right_val>0.5535702705383301</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 3 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2017288766801357e-003</threshold> + <left_val>0.1444882005453110</left_val> + <right_val>0.5193064212799072</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 3 12 -1.</_> + <_>2 1 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9081272017210722e-004</threshold> + <left_val>0.4384432137012482</left_val> + <right_val>0.5593621134757996</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 4 1 3 -1.</_> + <_>17 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9605009583756328e-004</threshold> + <left_val>0.5340415835380554</left_val> + <right_val>0.4705956876277924</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 4 1 3 -1.</_> + <_>2 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2022142335772514e-004</threshold> + <left_val>0.5213856101036072</left_val> + <right_val>0.3810079097747803</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 5 1 3 -1.</_> + <_>14 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4588572392240167e-004</threshold> + <left_val>0.4769414961338043</left_val> + <right_val>0.6130738854408264</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 16 2 3 -1.</_> + <_>7 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1698471806012094e-005</threshold> + <left_val>0.4245009124279022</left_val> + <right_val>0.5429363250732422</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 6 -1.</_> + <_>10 13 2 3 2.</_> + <_>8 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1833200007677078e-003</threshold> + <left_val>0.5457730889320374</left_val> + <right_val>0.4191075861454010</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 1 3 -1.</_> + <_>5 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6039671441540122e-004</threshold> + <left_val>0.5764588713645935</left_val> + <right_val>0.4471659958362579</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 20 -1.</_> + <_>16 0 2 20 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0132362395524979</threshold> + <left_val>0.6372823119163513</left_val> + <right_val>0.4695009887218475</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 2 6 -1.</_> + <_>5 1 1 3 2.</_> + <_>6 4 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3376701069064438e-004</threshold> + <left_val>0.5317873954772949</left_val> + <right_val>0.3945829868316650</right_val></_></_></trees> + <stage_threshold>67.6989212036132810</stage_threshold> + <parent>14</parent> + <next>-1</next></_> + <_> + <!-- stage 16 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 10 4 -1.</_> + <_>5 6 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0248471498489380</threshold> + <left_val>0.6555516719818115</left_val> + <right_val>0.3873311877250671</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 2 4 12 -1.</_> + <_>15 2 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1348611488938332e-003</threshold> + <left_val>0.3748072087764740</left_val> + <right_val>0.5973997712135315</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 4 12 -1.</_> + <_>7 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4498498104512691e-003</threshold> + <left_val>0.5425491929054260</left_val> + <right_val>0.2548811137676239</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 5 1 8 -1.</_> + <_>14 9 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3491211039945483e-004</threshold> + <left_val>0.2462442070245743</left_val> + <right_val>0.5387253761291504</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 14 10 -1.</_> + <_>1 4 7 5 2.</_> + <_>8 9 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4023890253156424e-003</threshold> + <left_val>0.5594322085380554</left_val> + <right_val>0.3528657853603363</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 6 6 14 -1.</_> + <_>14 6 3 7 2.</_> + <_>11 13 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0044000595808029e-004</threshold> + <left_val>0.3958503901958466</left_val> + <right_val>0.5765938162803650</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 6 14 -1.</_> + <_>3 6 3 7 2.</_> + <_>6 13 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0042409849120304e-004</threshold> + <left_val>0.3698996901512146</left_val> + <right_val>0.5534998178482056</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 15 2 -1.</_> + <_>9 9 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0841490738093853e-003</threshold> + <left_val>0.3711090981960297</left_val> + <right_val>0.5547800064086914</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0195372607558966</threshold> + <left_val>0.7492755055427551</left_val> + <right_val>0.4579297006130219</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 14 4 -1.</_> + <_>13 3 7 2 2.</_> + <_>6 5 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4532740654831287e-006</threshold> + <left_val>0.5649787187576294</left_val> + <right_val>0.3904069960117340</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 15 2 -1.</_> + <_>6 9 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6079459823668003e-003</threshold> + <left_val>0.3381088078022003</left_val> + <right_val>0.5267801284790039</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 8 9 -1.</_> + <_>6 14 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0697501022368670e-003</threshold> + <left_val>0.5519291162490845</left_val> + <right_val>0.3714388906955719</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 3 8 -1.</_> + <_>8 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6463840408250690e-004</threshold> + <left_val>0.5608214735984802</left_val> + <right_val>0.4113566875457764</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 2 6 -1.</_> + <_>14 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5490452582016587e-004</threshold> + <left_val>0.3559206128120422</left_val> + <right_val>0.5329356193542481</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 6 4 -1.</_> + <_>5 7 3 2 2.</_> + <_>8 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.8322238773107529e-004</threshold> + <left_val>0.5414795875549316</left_val> + <right_val>0.3763205111026764</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 19 -1.</_> + <_>7 1 6 19 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0199406407773495</threshold> + <left_val>0.6347903013229370</left_val> + <right_val>0.4705299139022827</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 6 5 -1.</_> + <_>4 2 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7680300883948803e-003</threshold> + <left_val>0.3913489878177643</left_val> + <right_val>0.5563716292381287</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 17 6 2 -1.</_> + <_>12 18 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4528505578637123e-003</threshold> + <left_val>0.2554892897605896</left_val> + <right_val>0.5215116739273071</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 17 6 2 -1.</_> + <_>2 18 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9560849070549011e-003</threshold> + <left_val>0.5174679160118103</left_val> + <right_val>0.3063920140266419</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 3 3 6 -1.</_> + <_>17 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1078737750649452e-003</threshold> + <left_val>0.5388448238372803</left_val> + <right_val>0.2885963022708893</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 3 -1.</_> + <_>8 18 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8219229532405734e-003</threshold> + <left_val>0.4336043000221252</left_val> + <right_val>0.5852196812629700</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 13 2 6 -1.</_> + <_>10 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0146887395530939</threshold> + <left_val>0.5287361741065979</left_val> + <right_val>0.2870005965232849</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0143879903480411</threshold> + <left_val>0.7019448876380920</left_val> + <right_val>0.4647370874881744</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 3 3 6 -1.</_> + <_>17 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0189866498112679</threshold> + <left_val>0.2986552119255066</left_val> + <right_val>0.5247011780738831</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 2 3 -1.</_> + <_>8 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1527639580890536e-003</threshold> + <left_val>0.4323473870754242</left_val> + <right_val>0.5931661725044251</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 6 2 -1.</_> + <_>11 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0109336702153087</threshold> + <left_val>0.5286864042282105</left_val> + <right_val>0.3130319118499756</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 3 6 -1.</_> + <_>0 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0149327302351594</threshold> + <left_val>0.2658419013023377</left_val> + <right_val>0.5084077119827271</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 4 6 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9970539617352188e-004</threshold> + <left_val>0.5463526844978333</left_val> + <right_val>0.3740724027156830</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 3 2 -1.</_> + <_>5 6 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1677621193230152e-003</threshold> + <left_val>0.4703496992588043</left_val> + <right_val>0.7435721755027771</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 3 4 -1.</_> + <_>11 1 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3905320130288601e-003</threshold> + <left_val>0.2069258987903595</left_val> + <right_val>0.5280538201332092</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 5 9 -1.</_> + <_>1 5 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5029609464108944e-003</threshold> + <left_val>0.5182648897171021</left_val> + <right_val>0.3483543097972870</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 2 3 -1.</_> + <_>13 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2040365561842918e-003</threshold> + <left_val>0.6803777217864990</left_val> + <right_val>0.4932360053062439</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 14 3 -1.</_> + <_>7 6 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0813272595405579</threshold> + <left_val>0.5058398842811585</left_val> + <right_val>0.2253051996231079</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 18 8 -1.</_> + <_>2 15 18 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1507928073406220</threshold> + <left_val>0.2963424921035767</left_val> + <right_val>0.5264679789543152</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 3 -1.</_> + <_>5 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3179009333252907e-003</threshold> + <left_val>0.4655495882034302</left_val> + <right_val>0.7072932124137878</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 4 2 -1.</_> + <_>12 6 2 1 2.</_> + <_>10 7 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7402801252901554e-004</threshold> + <left_val>0.4780347943305969</left_val> + <right_val>0.5668237805366516</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 4 2 -1.</_> + <_>6 6 2 1 2.</_> + <_>8 7 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8199541419744492e-004</threshold> + <left_val>0.4286996126174927</left_val> + <right_val>0.5722156763076782</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 3 4 -1.</_> + <_>11 1 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3671570494771004e-003</threshold> + <left_val>0.5299307107925415</left_val> + <right_val>0.3114621937274933</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 2 7 -1.</_> + <_>8 1 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7018666565418243e-005</threshold> + <left_val>0.3674638867378235</left_val> + <right_val>0.5269461870193481</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 15 14 -1.</_> + <_>4 9 15 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1253408938646317</threshold> + <left_val>0.2351492047309876</left_val> + <right_val>0.5245791077613831</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>9 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2516269497573376e-003</threshold> + <left_val>0.7115936875343323</left_val> + <right_val>0.4693767130374908</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 18 4 -1.</_> + <_>11 3 9 2 2.</_> + <_>2 5 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8342109918594360e-003</threshold> + <left_val>0.4462651014328003</left_val> + <right_val>0.5409085750579834</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1310069821774960e-003</threshold> + <left_val>0.5945618748664856</left_val> + <right_val>0.4417662024497986</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 3 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7601120052859187e-003</threshold> + <left_val>0.5353249907493591</left_val> + <right_val>0.3973453044891357</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 6 2 -1.</_> + <_>7 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1581249833106995e-004</threshold> + <left_val>0.3760268092155457</left_val> + <right_val>0.5264726877212524</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 7 -1.</_> + <_>9 5 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8687589112669230e-003</threshold> + <left_val>0.6309912800788879</left_val> + <right_val>0.4749819934368134</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 2 3 -1.</_> + <_>6 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5207129763439298e-003</threshold> + <left_val>0.5230181813240051</left_val> + <right_val>0.3361223936080933</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 14 18 -1.</_> + <_>6 9 14 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.5458673834800720</threshold> + <left_val>0.5167139768600464</left_val> + <right_val>0.1172635033726692</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 6 3 -1.</_> + <_>2 17 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0156501904129982</threshold> + <left_val>0.4979439079761505</left_val> + <right_val>0.1393294930458069</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>10 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0117318602278829</threshold> + <left_val>0.7129650712013245</left_val> + <right_val>0.4921196103096008</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 4 3 -1.</_> + <_>7 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1765122227370739e-003</threshold> + <left_val>0.2288102954626083</left_val> + <right_val>0.5049701929092407</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 3 -1.</_> + <_>7 13 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2457661107182503e-003</threshold> + <left_val>0.4632433950901032</left_val> + <right_val>0.6048725843429565</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1915869116783142e-003</threshold> + <left_val>0.6467421054840088</left_val> + <right_val>0.4602192938327789</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 2 -1.</_> + <_>9 12 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0238278806209564</threshold> + <left_val>0.1482000946998596</left_val> + <right_val>0.5226079225540161</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 4 6 -1.</_> + <_>5 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0284580057486892e-003</threshold> + <left_val>0.5135489106178284</left_val> + <right_val>0.3375957012176514</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 7 2 -1.</_> + <_>11 13 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0100788502022624</threshold> + <left_val>0.2740561068058014</left_val> + <right_val>0.5303567051887512</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 8 6 -1.</_> + <_>6 10 4 3 2.</_> + <_>10 13 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6168930344283581e-003</threshold> + <left_val>0.5332670807838440</left_val> + <right_val>0.3972454071044922</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 4 -1.</_> + <_>11 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4385367548093200e-004</threshold> + <left_val>0.5365604162216187</left_val> + <right_val>0.4063411951065064</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 3 -1.</_> + <_>9 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3510512225329876e-003</threshold> + <left_val>0.4653759002685547</left_val> + <right_val>0.6889045834541321</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 3 1 9 -1.</_> + <_>13 6 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5274790348485112e-003</threshold> + <left_val>0.5449501276016235</left_val> + <right_val>0.3624723851680756</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 13 14 6 -1.</_> + <_>1 15 14 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0806244164705276</threshold> + <left_val>0.1656087040901184</left_val> + <right_val>0.5000287294387817</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 1 6 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0221920292824507</threshold> + <left_val>0.5132731199264526</left_val> + <right_val>0.2002808004617691</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 3 8 -1.</_> + <_>1 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3100631125271320e-003</threshold> + <left_val>0.4617947936058044</left_val> + <right_val>0.6366536021232605</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 0 2 18 -1.</_> + <_>18 0 1 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4063072204589844e-003</threshold> + <left_val>0.5916250944137573</left_val> + <right_val>0.4867860972881317</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 6 2 -1.</_> + <_>2 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6415040530264378e-004</threshold> + <left_val>0.3888409137725830</left_val> + <right_val>0.5315797924995422</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 8 6 -1.</_> + <_>9 2 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6734489994123578e-004</threshold> + <left_val>0.4159064888954163</left_val> + <right_val>0.5605279803276062</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 6 -1.</_> + <_>6 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1474501853808761e-004</threshold> + <left_val>0.3089022040367127</left_val> + <right_val>0.5120148062705994</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 6 3 -1.</_> + <_>14 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0105270929634571e-003</threshold> + <left_val>0.3972199857234955</left_val> + <right_val>0.5207306146621704</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 18 -1.</_> + <_>1 0 1 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6909132078289986e-003</threshold> + <left_val>0.6257408261299133</left_val> + <right_val>0.4608575999736786</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 18 18 2 -1.</_> + <_>10 18 9 1 2.</_> + <_>1 19 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163914598524570</threshold> + <left_val>0.2085209935903549</left_val> + <right_val>0.5242266058921814</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 2 2 -1.</_> + <_>3 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0973909199237823e-004</threshold> + <left_val>0.5222427248954773</left_val> + <right_val>0.3780320882797241</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 5 3 -1.</_> + <_>8 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5242289993911982e-003</threshold> + <left_val>0.5803927183151245</left_val> + <right_val>0.4611890017986298</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 2 3 -1.</_> + <_>8 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0945312250405550e-004</threshold> + <left_val>0.4401271939277649</left_val> + <right_val>0.5846015810966492</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9656419754028320e-003</threshold> + <left_val>0.5322325229644775</left_val> + <right_val>0.4184590876102448</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 2 -1.</_> + <_>9 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6298897834494710e-004</threshold> + <left_val>0.3741844892501831</left_val> + <right_val>0.5234565734863281</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 5 5 2 -1.</_> + <_>15 6 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7946797935292125e-004</threshold> + <left_val>0.4631041884422302</left_val> + <right_val>0.5356478095054627</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 5 2 -1.</_> + <_>0 6 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2856349870562553e-003</threshold> + <left_val>0.5044670104980469</left_val> + <right_val>0.2377564013004303</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 14 1 6 -1.</_> + <_>17 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0174594894051552</threshold> + <left_val>0.7289121150970459</left_val> + <right_val>0.5050435066223145</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 9 3 -1.</_> + <_>5 9 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0254217498004436</threshold> + <left_val>0.6667134761810303</left_val> + <right_val>0.4678100049495697</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5647639520466328e-003</threshold> + <left_val>0.4391759037971497</left_val> + <right_val>0.5323626995086670</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 18 -1.</_> + <_>2 0 2 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0114443600177765</threshold> + <left_val>0.4346440136432648</left_val> + <right_val>0.5680012106895447</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 6 1 3 -1.</_> + <_>17 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7352550104260445e-004</threshold> + <left_val>0.4477140903472900</left_val> + <right_val>0.5296812057495117</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 14 1 6 -1.</_> + <_>2 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3194209039211273e-003</threshold> + <left_val>0.4740200042724609</left_val> + <right_val>0.7462607026100159</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 8 1 2 -1.</_> + <_>19 9 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3328490604180843e-004</threshold> + <left_val>0.5365061759948731</left_val> + <right_val>0.4752134978771210</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 3 -1.</_> + <_>6 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8815799206495285e-003</threshold> + <left_val>0.1752219051122665</left_val> + <right_val>0.5015255212783814</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 3 -1.</_> + <_>9 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7985680177807808e-003</threshold> + <left_val>0.7271236777305603</left_val> + <right_val>0.4896200895309448</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 6 1 3 -1.</_> + <_>2 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8922499516047537e-004</threshold> + <left_val>0.4003908932209015</left_val> + <right_val>0.5344941020011902</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 8 2 -1.</_> + <_>16 4 4 1 2.</_> + <_>12 5 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9288610201328993e-003</threshold> + <left_val>0.5605612993240356</left_val> + <right_val>0.4803955852985382</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 8 2 -1.</_> + <_>0 4 4 1 2.</_> + <_>4 5 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4214154630899429e-003</threshold> + <left_val>0.4753246903419495</left_val> + <right_val>0.7623608708381653</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 18 4 -1.</_> + <_>2 18 18 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1655876711010933e-003</threshold> + <left_val>0.5393261909484863</left_val> + <right_val>0.4191643893718720</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 2 4 -1.</_> + <_>7 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8280550981871784e-004</threshold> + <left_val>0.4240800142288208</left_val> + <right_val>0.5399821996688843</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 14 3 -1.</_> + <_>4 1 14 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7186630759388208e-003</threshold> + <left_val>0.4244599938392639</left_val> + <right_val>0.5424923896789551</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 20 -1.</_> + <_>2 0 2 20 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0125072300434113</threshold> + <left_val>0.5895841717720032</left_val> + <right_val>0.4550411105155945</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 4 8 -1.</_> + <_>14 4 2 4 2.</_> + <_>12 8 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0242865197360516</threshold> + <left_val>0.2647134959697723</left_val> + <right_val>0.5189179778099060</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 2 2 -1.</_> + <_>6 7 1 1 2.</_> + <_>7 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9676330741494894e-003</threshold> + <left_val>0.7347682714462280</left_val> + <right_val>0.4749749898910523</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 2 3 -1.</_> + <_>10 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0125289997085929</threshold> + <left_val>0.2756049931049347</left_val> + <right_val>0.5177599787712097</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>8 8 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0104000102728605e-003</threshold> + <left_val>0.3510560989379883</left_val> + <right_val>0.5144724249839783</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 6 12 -1.</_> + <_>8 8 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1348530426621437e-003</threshold> + <left_val>0.5637925863265991</left_val> + <right_val>0.4667319953441620</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 11 12 -1.</_> + <_>4 4 11 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0195642597973347</threshold> + <left_val>0.4614573121070862</left_val> + <right_val>0.6137639880180359</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 9 6 11 -1.</_> + <_>16 9 2 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0971463471651077</threshold> + <left_val>0.2998378872871399</left_val> + <right_val>0.5193555951118469</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 4 3 -1.</_> + <_>0 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5014568604528904e-003</threshold> + <left_val>0.5077884793281555</left_val> + <right_val>0.3045755922794342</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3706971704959869e-003</threshold> + <left_val>0.4861018955707550</left_val> + <right_val>0.6887500882148743</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 3 2 -1.</_> + <_>5 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0721528977155685e-003</threshold> + <left_val>0.1673395931720734</left_val> + <right_val>0.5017563104629517</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 3 3 -1.</_> + <_>10 15 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3537208586931229e-003</threshold> + <left_val>0.2692756950855255</left_val> + <right_val>0.5242633223533630</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>9 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0109328404068947</threshold> + <left_val>0.7183864116668701</left_val> + <right_val>0.4736028909683228</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 3 3 -1.</_> + <_>10 15 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2356072962284088e-003</threshold> + <left_val>0.5223966836929321</left_val> + <right_val>0.2389862984418869</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 2 -1.</_> + <_>8 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0038160253316164e-003</threshold> + <left_val>0.5719355940818787</left_val> + <right_val>0.4433943033218384</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 16 4 -1.</_> + <_>10 10 8 2 2.</_> + <_>2 12 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0859128348529339e-003</threshold> + <left_val>0.5472841858863831</left_val> + <right_val>0.4148836135864258</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 4 17 -1.</_> + <_>4 3 2 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1548541933298111</threshold> + <left_val>0.4973812103271484</left_val> + <right_val>0.0610615983605385</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 7 -1.</_> + <_>15 13 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0897459762636572e-004</threshold> + <left_val>0.4709174036979675</left_val> + <right_val>0.5423889160156250</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 6 1 -1.</_> + <_>5 2 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3316991175524890e-004</threshold> + <left_val>0.4089626967906952</left_val> + <right_val>0.5300992131233215</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 12 4 -1.</_> + <_>9 2 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108134001493454</threshold> + <left_val>0.6104369759559631</left_val> + <right_val>0.4957334101200104</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>6 0 4 6 2.</_> + <_>10 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0456560105085373</threshold> + <left_val>0.5069689154624939</left_val> + <right_val>0.2866660058498383</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 2 -1.</_> + <_>14 7 1 1 2.</_> + <_>13 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2569549726322293e-003</threshold> + <left_val>0.4846917092800140</left_val> + <right_val>0.6318171024322510</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 20 6 -1.</_> + <_>0 14 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1201507002115250</threshold> + <left_val>0.0605261400341988</left_val> + <right_val>0.4980959892272949</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 7 2 3 -1.</_> + <_>14 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0533799650147557e-004</threshold> + <left_val>0.5363109707832336</left_val> + <right_val>0.4708042144775391</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 9 12 -1.</_> + <_>3 8 3 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2070319056510925</threshold> + <left_val>0.0596603304147720</left_val> + <right_val>0.4979098141193390</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 16 2 -1.</_> + <_>3 0 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2909180077258497e-004</threshold> + <left_val>0.4712977111339569</left_val> + <right_val>0.5377997756004334</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 3 3 -1.</_> + <_>6 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8818528992123902e-004</threshold> + <left_val>0.4363538026809692</left_val> + <right_val>0.5534191131591797</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 6 3 -1.</_> + <_>8 16 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9243610333651304e-003</threshold> + <left_val>0.5811185836791992</left_val> + <right_val>0.4825215935707092</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 1 6 -1.</_> + <_>0 12 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3882332546636462e-004</threshold> + <left_val>0.5311700105667114</left_val> + <right_val>0.4038138985633850</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 4 3 -1.</_> + <_>10 10 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9061550265178084e-003</threshold> + <left_val>0.3770701885223389</left_val> + <right_val>0.5260015130043030</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9514348655939102e-003</threshold> + <left_val>0.4766167998313904</left_val> + <right_val>0.7682183980941773</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 1 -1.</_> + <_>5 7 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130834598094225</threshold> + <left_val>0.5264462828636169</left_val> + <right_val>0.3062222003936768</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 12 19 -1.</_> + <_>10 0 6 19 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2115933001041412</threshold> + <left_val>0.6737198233604431</left_val> + <right_val>0.4695810079574585</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 6 -1.</_> + <_>10 6 10 3 2.</_> + <_>0 9 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1493250280618668e-003</threshold> + <left_val>0.5644835233688355</left_val> + <right_val>0.4386953115463257</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 2 2 -1.</_> + <_>3 6 1 1 2.</_> + <_>4 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9754100725986063e-004</threshold> + <left_val>0.4526061117649078</left_val> + <right_val>0.5895630121231079</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 6 2 2 -1.</_> + <_>16 6 1 1 2.</_> + <_>15 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3814480043947697e-003</threshold> + <left_val>0.6070582270622253</left_val> + <right_val>0.4942413866519928</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 2 2 -1.</_> + <_>3 6 1 1 2.</_> + <_>4 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8122188784182072e-004</threshold> + <left_val>0.5998213291168213</left_val> + <right_val>0.4508252143859863</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 1 12 -1.</_> + <_>14 10 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3905329871922731e-003</threshold> + <left_val>0.4205588996410370</left_val> + <right_val>0.5223848223686218</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 5 16 10 -1.</_> + <_>2 5 8 5 2.</_> + <_>10 10 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0272689294070005</threshold> + <left_val>0.5206447243690491</left_val> + <right_val>0.3563301861286163</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7658358924090862e-003</threshold> + <left_val>0.3144704103469849</left_val> + <right_val>0.5218814015388489</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 2 2 -1.</_> + <_>1 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4903489500284195e-003</threshold> + <left_val>0.3380196094512940</left_val> + <right_val>0.5124437212944031</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 15 5 -1.</_> + <_>10 0 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0174282304942608</threshold> + <left_val>0.5829960703849793</left_val> + <right_val>0.4919725954532623</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 15 5 -1.</_> + <_>5 0 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0152780301868916</threshold> + <left_val>0.6163144707679749</left_val> + <right_val>0.4617887139320374</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 2 17 -1.</_> + <_>11 2 1 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0319956094026566</threshold> + <left_val>0.5166357159614563</left_val> + <right_val>0.1712764054536820</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 2 17 -1.</_> + <_>8 2 1 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8256710395216942e-003</threshold> + <left_val>0.3408012092113495</left_val> + <right_val>0.5131387710571289</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 2 9 -1.</_> + <_>15 11 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5186436772346497e-003</threshold> + <left_val>0.6105518937110901</left_val> + <right_val>0.4997941851615906</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 2 9 -1.</_> + <_>4 11 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0641621500253677e-004</threshold> + <left_val>0.4327270984649658</left_val> + <right_val>0.5582311153411865</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 16 14 4 -1.</_> + <_>5 16 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0103448498994112</threshold> + <left_val>0.4855653047561646</left_val> + <right_val>0.5452420115470886</right_val></_></_></trees> + <stage_threshold>69.2298736572265630</stage_threshold> + <parent>15</parent> + <next>-1</next></_> + <_> + <!-- stage 17 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 18 1 -1.</_> + <_>7 4 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8981826081871986e-003</threshold> + <left_val>0.3332524895668030</left_val> + <right_val>0.5946462154388428</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6170160379260778e-003</threshold> + <left_val>0.3490641117095947</left_val> + <right_val>0.5577868819236755</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 12 -1.</_> + <_>9 12 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5449741194024682e-004</threshold> + <left_val>0.5542566180229187</left_val> + <right_val>0.3291530013084412</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 1 6 6 -1.</_> + <_>12 3 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5428980113938451e-003</threshold> + <left_val>0.3612579107284546</left_val> + <right_val>0.5545979142189026</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 6 6 -1.</_> + <_>5 2 3 3 2.</_> + <_>8 5 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0329450014978647e-003</threshold> + <left_val>0.3530139029026032</left_val> + <right_val>0.5576140284538269</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 6 4 -1.</_> + <_>12 16 3 2 2.</_> + <_>9 18 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7698158565908670e-004</threshold> + <left_val>0.3916778862476349</left_val> + <right_val>0.5645321011543274</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 3 -1.</_> + <_>7 2 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1432030051946640</threshold> + <left_val>0.4667482078075409</left_val> + <right_val>0.7023633122444153</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 9 10 -1.</_> + <_>7 9 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3866490274667740e-003</threshold> + <left_val>0.3073684871196747</left_val> + <right_val>0.5289257764816284</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 4 4 -1.</_> + <_>7 9 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2936742324382067e-004</threshold> + <left_val>0.5622118115425110</left_val> + <right_val>0.4037049114704132</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 6 -1.</_> + <_>11 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8893528552725911e-004</threshold> + <left_val>0.5267661213874817</left_val> + <right_val>0.3557874858379364</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 5 3 -1.</_> + <_>7 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0122280502691865</threshold> + <left_val>0.6668320894241333</left_val> + <right_val>0.4625549912452698</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 6 6 -1.</_> + <_>10 11 3 3 2.</_> + <_>7 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5420239437371492e-003</threshold> + <left_val>0.5521438121795654</left_val> + <right_val>0.3869673013687134</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 10 9 -1.</_> + <_>0 3 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0585320414975286e-003</threshold> + <left_val>0.3628678023815155</left_val> + <right_val>0.5320926904678345</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 1 6 -1.</_> + <_>13 16 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4935660146875307e-005</threshold> + <left_val>0.4632444977760315</left_val> + <right_val>0.5363323092460632</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2537708543241024e-003</threshold> + <left_val>0.5132231712341309</left_val> + <right_val>0.3265708982944489</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2338023930788040e-003</threshold> + <left_val>0.6693689823150635</left_val> + <right_val>0.4774140119552612</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 1 6 -1.</_> + <_>6 16 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1866810129722580e-005</threshold> + <left_val>0.4053862094879150</left_val> + <right_val>0.5457931160926819</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8150229956954718e-003</threshold> + <left_val>0.6454995870590210</left_val> + <right_val>0.4793178141117096</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 3 3 -1.</_> + <_>7 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1105879675596952e-003</threshold> + <left_val>0.5270407199859619</left_val> + <right_val>0.3529678881168366</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 11 3 -1.</_> + <_>9 1 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7707689702510834e-003</threshold> + <left_val>0.3803547024726868</left_val> + <right_val>0.5352957844734192</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 3 -1.</_> + <_>0 7 20 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0158339068293571e-003</threshold> + <left_val>0.5339403152465820</left_val> + <right_val>0.3887133002281189</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 1 2 -1.</_> + <_>10 2 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5453689098358154e-004</threshold> + <left_val>0.3564616143703461</left_val> + <right_val>0.5273603796958923</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 6 -1.</_> + <_>10 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110505102202296</threshold> + <left_val>0.4671907126903534</left_val> + <right_val>0.6849737763404846</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 8 12 1 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0426058396697044</threshold> + <left_val>0.5151473283767700</left_val> + <right_val>0.0702200904488564</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 8 12 1 -1.</_> + <_>7 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0781750101596117e-003</threshold> + <left_val>0.3041661083698273</left_val> + <right_val>0.5152602195739746</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4815728217363358e-003</threshold> + <left_val>0.6430295705795288</left_val> + <right_val>0.4897229969501495</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 6 2 -1.</_> + <_>6 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1881860923022032e-003</threshold> + <left_val>0.5307493209838867</left_val> + <right_val>0.3826209902763367</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5947180003859103e-004</threshold> + <left_val>0.4650047123432159</left_val> + <right_val>0.5421904921531677</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 1 -1.</_> + <_>9 0 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0705031715333462e-003</threshold> + <left_val>0.2849679887294769</left_val> + <right_val>0.5079116225242615</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0145941702648997</threshold> + <left_val>0.2971645891666412</left_val> + <right_val>0.5128461718559265</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 2 1 -1.</_> + <_>8 10 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1947689927183092e-004</threshold> + <left_val>0.5631098151206970</left_val> + <right_val>0.4343082010746002</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 9 13 -1.</_> + <_>9 4 3 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9344649091362953e-004</threshold> + <left_val>0.4403578042984009</left_val> + <right_val>0.5359959006309509</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 8 4 2 -1.</_> + <_>6 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4834799912932795e-005</threshold> + <left_val>0.3421008884906769</left_val> + <right_val>0.5164697766304016</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 4 6 -1.</_> + <_>16 2 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0296985581517220e-003</threshold> + <left_val>0.4639343023300171</left_val> + <right_val>0.6114075183868408</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 6 3 -1.</_> + <_>0 18 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0640818923711777e-003</threshold> + <left_val>0.2820158898830414</left_val> + <right_val>0.5075494050979614</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 10 3 10 -1.</_> + <_>10 15 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0260621197521687</threshold> + <left_val>0.5208905935287476</left_val> + <right_val>0.2688778042793274</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0173146594315767</threshold> + <left_val>0.4663713872432709</left_val> + <right_val>0.6738539934158325</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 4 4 3 -1.</_> + <_>10 4 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0226666405797005</threshold> + <left_val>0.5209349989891052</left_val> + <right_val>0.2212723940610886</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 3 8 -1.</_> + <_>9 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1965929772704840e-003</threshold> + <left_val>0.6063101291656494</left_val> + <right_val>0.4538190066814423</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 9 13 -1.</_> + <_>9 6 3 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.5282476395368576e-003</threshold> + <left_val>0.4635204970836639</left_val> + <right_val>0.5247430801391602</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>6 0 4 6 2.</_> + <_>10 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0943619832396507e-003</threshold> + <left_val>0.5289440155029297</left_val> + <right_val>0.3913882076740265</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 8 -1.</_> + <_>16 2 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0728773325681686</threshold> + <left_val>0.7752001881599426</left_val> + <right_val>0.4990234971046448</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 6 -1.</_> + <_>7 0 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9009521976113319e-003</threshold> + <left_val>0.2428039014339447</left_val> + <right_val>0.5048090219497681</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 8 -1.</_> + <_>16 2 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0113082397729158</threshold> + <left_val>0.5734364986419678</left_val> + <right_val>0.4842376112937927</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 6 6 -1.</_> + <_>0 8 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0596132017672062</threshold> + <left_val>0.5029836297035217</left_val> + <right_val>0.2524977028369904</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 6 2 -1.</_> + <_>12 12 3 1 2.</_> + <_>9 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8624620754271746e-003</threshold> + <left_val>0.6073045134544373</left_val> + <right_val>0.4898459911346436</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4781449250876904e-003</threshold> + <left_val>0.5015289187431335</left_val> + <right_val>0.2220316976308823</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 6 2 2 -1.</_> + <_>12 6 1 1 2.</_> + <_>11 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7513240454718471e-003</threshold> + <left_val>0.6614428758621216</left_val> + <right_val>0.4933868944644928</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 18 2 -1.</_> + <_>7 9 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0401634201407433</threshold> + <left_val>0.5180878043174744</left_val> + <right_val>0.3741044998168945</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 6 2 2 -1.</_> + <_>12 6 1 1 2.</_> + <_>11 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4768949262797832e-004</threshold> + <left_val>0.4720416963100433</left_val> + <right_val>0.5818032026290894</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 12 8 -1.</_> + <_>7 4 4 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6551650371402502e-003</threshold> + <left_val>0.3805010914802551</left_val> + <right_val>0.5221335887908936</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 5 3 -1.</_> + <_>13 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7706279009580612e-003</threshold> + <left_val>0.2944166064262390</left_val> + <right_val>0.5231295228004456</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5122091434895992e-003</threshold> + <left_val>0.7346177101135254</left_val> + <right_val>0.4722816944122315</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 7 2 3 -1.</_> + <_>14 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8672042107209563e-004</threshold> + <left_val>0.5452876091003418</left_val> + <right_val>0.4242413043975830</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 1 3 -1.</_> + <_>5 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6019669864326715e-004</threshold> + <left_val>0.4398862123489380</left_val> + <right_val>0.5601285099983215</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 2 3 -1.</_> + <_>13 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4143769405782223e-003</threshold> + <left_val>0.4741686880588532</left_val> + <right_val>0.6136621832847595</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5680900542065501e-003</threshold> + <left_val>0.6044552922248840</left_val> + <right_val>0.4516409933567047</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 3 -1.</_> + <_>9 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6827491130679846e-003</threshold> + <left_val>0.2452459037303925</left_val> + <right_val>0.5294982194900513</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 2 2 -1.</_> + <_>8 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9409190756268799e-004</threshold> + <left_val>0.3732838034629822</left_val> + <right_val>0.5251451134681702</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 1 4 -1.</_> + <_>15 16 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2847759323194623e-004</threshold> + <left_val>0.5498809814453125</left_val> + <right_val>0.4065535068511963</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 2 -1.</_> + <_>3 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8817070201039314e-003</threshold> + <left_val>0.2139908969402313</left_val> + <right_val>0.4999957084655762</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 15 2 2 -1.</_> + <_>13 15 1 1 2.</_> + <_>12 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7272020815871656e-004</threshold> + <left_val>0.4650287032127380</left_val> + <right_val>0.5813428759574890</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 2 -1.</_> + <_>9 14 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0947199664078653e-004</threshold> + <left_val>0.4387486875057221</left_val> + <right_val>0.5572792887687683</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 14 9 -1.</_> + <_>4 14 14 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0485011897981167</threshold> + <left_val>0.5244972705841065</left_val> + <right_val>0.3212889134883881</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 4 3 -1.</_> + <_>7 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5166411437094212e-003</threshold> + <left_val>0.6056813001632690</left_val> + <right_val>0.4545882046222687</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 1 4 -1.</_> + <_>15 16 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0122916800901294</threshold> + <left_val>0.2040929049253464</left_val> + <right_val>0.5152214169502258</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 1 4 -1.</_> + <_>4 16 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8549679922871292e-004</threshold> + <left_val>0.5237604975700378</left_val> + <right_val>0.3739503026008606</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 13 -1.</_> + <_>16 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0305560491979122</threshold> + <left_val>0.4960533976554871</left_val> + <right_val>0.5938246250152588</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 1 2 12 -1.</_> + <_>4 1 1 6 2.</_> + <_>5 7 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5105320198927075e-004</threshold> + <left_val>0.5351303815841675</left_val> + <right_val>0.4145204126834869</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 6 6 -1.</_> + <_>14 14 3 3 2.</_> + <_>11 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4937440175563097e-003</threshold> + <left_val>0.4693366885185242</left_val> + <right_val>0.5514941215515137</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 6 6 -1.</_> + <_>3 14 3 3 2.</_> + <_>6 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0123821301385760</threshold> + <left_val>0.6791396737098694</left_val> + <right_val>0.4681667983531952</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 17 3 2 -1.</_> + <_>14 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1333461888134480e-003</threshold> + <left_val>0.3608739078044891</left_val> + <right_val>0.5229160189628601</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 17 3 2 -1.</_> + <_>3 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1919277757406235e-004</threshold> + <left_val>0.5300073027610779</left_val> + <right_val>0.3633613884449005</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 13 -1.</_> + <_>16 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1506042033433914</threshold> + <left_val>0.5157316923141480</left_val> + <right_val>0.2211782038211823</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 6 13 -1.</_> + <_>2 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7144149690866470e-003</threshold> + <left_val>0.4410496950149536</left_val> + <right_val>0.5776609182357788</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 10 7 6 -1.</_> + <_>10 12 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4443522393703461e-003</threshold> + <left_val>0.5401855111122131</left_val> + <right_val>0.3756650090217590</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 2 2 -1.</_> + <_>6 15 1 1 2.</_> + <_>7 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5006249779835343e-004</threshold> + <left_val>0.4368270933628082</left_val> + <right_val>0.5607374906539917</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 8 6 -1.</_> + <_>10 11 4 3 2.</_> + <_>6 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3077150583267212e-003</threshold> + <left_val>0.4244799017906189</left_val> + <right_val>0.5518230795860291</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 2 2 -1.</_> + <_>7 6 1 1 2.</_> + <_>8 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4048910755664110e-004</threshold> + <left_val>0.4496962130069733</left_val> + <right_val>0.5900576710700989</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 16 6 -1.</_> + <_>10 2 8 3 2.</_> + <_>2 5 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0440920516848564</threshold> + <left_val>0.5293493270874023</left_val> + <right_val>0.3156355023384094</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3639909233897924e-003</threshold> + <left_val>0.4483296871185303</left_val> + <right_val>0.5848662257194519</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 7 3 10 -1.</_> + <_>11 12 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9760079234838486e-003</threshold> + <left_val>0.4559507071971893</left_val> + <right_val>0.5483639240264893</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 3 10 -1.</_> + <_>6 12 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7716930489987135e-003</threshold> + <left_val>0.5341786146163940</left_val> + <right_val>0.3792484104633331</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 2 -1.</_> + <_>11 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4123019829858094e-004</threshold> + <left_val>0.5667188763618469</left_val> + <right_val>0.4576973021030426</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9425667384639382e-004</threshold> + <left_val>0.4421244859695435</left_val> + <right_val>0.5628787279129028</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 1 3 -1.</_> + <_>10 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8876468897797167e-004</threshold> + <left_val>0.4288370907306671</left_val> + <right_val>0.5391063094139099</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 4 18 -1.</_> + <_>1 2 2 9 2.</_> + <_>3 11 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0500488989055157</threshold> + <left_val>0.6899513006210327</left_val> + <right_val>0.4703742861747742</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 4 12 -1.</_> + <_>12 10 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0366354808211327</threshold> + <left_val>0.2217779010534287</left_val> + <right_val>0.5191826224327087</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 1 6 -1.</_> + <_>0 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4273579474538565e-003</threshold> + <left_val>0.5136224031448364</left_val> + <right_val>0.3497397899627686</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9558030180633068e-003</threshold> + <left_val>0.4826192855834961</left_val> + <right_val>0.6408380866050720</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 4 3 -1.</_> + <_>8 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7494610510766506e-003</threshold> + <left_val>0.3922835886478424</left_val> + <right_val>0.5272685289382935</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 2 -1.</_> + <_>11 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0139550799503922</threshold> + <left_val>0.5078201889991760</left_val> + <right_val>0.8416504859924316</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 2 -1.</_> + <_>8 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1896739781368524e-004</threshold> + <left_val>0.5520489811897278</left_val> + <right_val>0.4314234852790833</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 6 1 -1.</_> + <_>11 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5131309628486633e-003</threshold> + <left_val>0.3934605121612549</left_val> + <right_val>0.5382571220397949</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 2 3 -1.</_> + <_>9 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3622800149023533e-003</threshold> + <left_val>0.7370628714561462</left_val> + <right_val>0.4736475944519043</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 8 6 -1.</_> + <_>16 7 4 3 2.</_> + <_>12 10 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0651605874300003</threshold> + <left_val>0.5159279704093933</left_val> + <right_val>0.3281595110893250</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 8 6 -1.</_> + <_>0 7 4 3 2.</_> + <_>4 10 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3567399475723505e-003</threshold> + <left_val>0.3672826886177063</left_val> + <right_val>0.5172886252403259</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 2 2 10 -1.</_> + <_>19 2 1 5 2.</_> + <_>18 7 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0151466596871614</threshold> + <left_val>0.5031493902206421</left_val> + <right_val>0.6687604188919067</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 4 -1.</_> + <_>3 2 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0228509604930878</threshold> + <left_val>0.6767519712448120</left_val> + <right_val>0.4709596931934357</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 6 1 -1.</_> + <_>11 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8867650330066681e-003</threshold> + <left_val>0.5257998108863831</left_val> + <right_val>0.4059878885746002</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 2 2 -1.</_> + <_>7 15 1 1 2.</_> + <_>8 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7619599821045995e-003</threshold> + <left_val>0.4696272909641266</left_val> + <right_val>0.6688278913497925</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 13 1 6 -1.</_> + <_>11 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2942519970238209e-003</threshold> + <left_val>0.4320712983608246</left_val> + <right_val>0.5344281792640686</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 1 6 -1.</_> + <_>8 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0109299495816231</threshold> + <left_val>0.4997706115245819</left_val> + <right_val>0.1637486070394516</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 2 1 -1.</_> + <_>14 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9958489903947338e-005</threshold> + <left_val>0.4282417893409729</left_val> + <right_val>0.5633224248886108</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 2 3 -1.</_> + <_>8 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5884361974895000e-003</threshold> + <left_val>0.6772121191024780</left_val> + <right_val>0.4700526893138886</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 15 7 4 -1.</_> + <_>12 17 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2527779694646597e-003</threshold> + <left_val>0.5313397049903870</left_val> + <right_val>0.4536148905754089</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 12 3 -1.</_> + <_>4 15 12 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0435739792883396e-003</threshold> + <left_val>0.5660061836242676</left_val> + <right_val>0.4413388967514038</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 3 2 -1.</_> + <_>11 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2523540062829852e-003</threshold> + <left_val>0.3731913864612579</left_val> + <right_val>0.5356451869010925</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 2 -1.</_> + <_>4 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9246719602961093e-004</threshold> + <left_val>0.5189986228942871</left_val> + <right_val>0.3738811016082764</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 4 6 -1.</_> + <_>10 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0385896712541580</threshold> + <left_val>0.2956373989582062</left_val> + <right_val>0.5188810825347900</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 2 2 -1.</_> + <_>7 13 1 1 2.</_> + <_>8 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5489870565943420e-004</threshold> + <left_val>0.4347135126590729</left_val> + <right_val>0.5509533286094666</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 14 4 -1.</_> + <_>11 11 7 2 2.</_> + <_>4 13 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0337638482451439</threshold> + <left_val>0.3230330049991608</left_val> + <right_val>0.5195475816726685</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 18 18 2 -1.</_> + <_>7 18 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2657067105174065e-003</threshold> + <left_val>0.5975489020347595</left_val> + <right_val>0.4552114009857178</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 2 2 -1.</_> + <_>12 18 1 1 2.</_> + <_>11 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4481440302915871e-005</threshold> + <left_val>0.4745678007602692</left_val> + <right_val>0.5497426986694336</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 18 2 2 -1.</_> + <_>7 18 1 1 2.</_> + <_>8 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4951299817766994e-005</threshold> + <left_val>0.4324473142623901</left_val> + <right_val>0.5480644106864929</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 18 8 2 -1.</_> + <_>12 19 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0187417995184660</threshold> + <left_val>0.1580052971839905</left_val> + <right_val>0.5178533196449280</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 2 -1.</_> + <_>7 15 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7572239739820361e-003</threshold> + <left_val>0.4517636895179749</left_val> + <right_val>0.5773764252662659</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 8 -1.</_> + <_>10 12 2 4 2.</_> + <_>8 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1391119118779898e-003</threshold> + <left_val>0.4149647951126099</left_val> + <right_val>0.5460842251777649</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 3 3 -1.</_> + <_>4 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6656779381446540e-005</threshold> + <left_val>0.4039090871810913</left_val> + <right_val>0.5293084979057312</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 6 2 -1.</_> + <_>9 10 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7743421532213688e-003</threshold> + <left_val>0.4767651855945587</left_val> + <right_val>0.6121956110000610</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 4 15 -1.</_> + <_>7 0 2 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3868161998689175e-003</threshold> + <left_val>0.3586258888244629</left_val> + <right_val>0.5187280774116516</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 12 14 -1.</_> + <_>12 6 4 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0140409301966429</threshold> + <left_val>0.4712139964103699</left_val> + <right_val>0.5576155781745911</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 16 3 3 -1.</_> + <_>5 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5258329957723618e-003</threshold> + <left_val>0.2661027014255524</left_val> + <right_val>0.5039281249046326</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 12 19 -1.</_> + <_>12 1 4 19 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.3868423998355866</threshold> + <left_val>0.5144339799880981</left_val> + <right_val>0.2525899112224579</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 3 2 -1.</_> + <_>3 1 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1459240340627730e-004</threshold> + <left_val>0.4284994900226593</left_val> + <right_val>0.5423371195793152</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 4 5 -1.</_> + <_>10 12 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0184675697237253</threshold> + <left_val>0.3885835111141205</left_val> + <right_val>0.5213062167167664</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 4 5 -1.</_> + <_>8 12 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5907011372037232e-004</threshold> + <left_val>0.5412563085556030</left_val> + <right_val>0.4235909879207611</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 2 2 -1.</_> + <_>12 11 1 1 2.</_> + <_>11 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2527540093287826e-003</threshold> + <left_val>0.4899305105209351</left_val> + <right_val>0.6624091267585754</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4910609461367130e-003</threshold> + <left_val>0.5286778211593628</left_val> + <right_val>0.4040051996707916</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 2 2 -1.</_> + <_>12 11 1 1 2.</_> + <_>11 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5435562757775187e-004</threshold> + <left_val>0.6032990217208862</left_val> + <right_val>0.4795120060443878</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 4 10 -1.</_> + <_>7 11 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9478838704526424e-003</threshold> + <left_val>0.4084401130676270</left_val> + <right_val>0.5373504161834717</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 2 2 -1.</_> + <_>12 11 1 1 2.</_> + <_>11 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8092920547351241e-004</threshold> + <left_val>0.4846062958240509</left_val> + <right_val>0.5759382247924805</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 2 -1.</_> + <_>2 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6073717577382922e-004</threshold> + <left_val>0.5164741277694702</left_val> + <right_val>0.3554979860782623</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 2 2 -1.</_> + <_>12 11 1 1 2.</_> + <_>11 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6883929967880249e-004</threshold> + <left_val>0.5677582025527954</left_val> + <right_val>0.4731765985488892</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 2 2 -1.</_> + <_>7 11 1 1 2.</_> + <_>8 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1599370520561934e-003</threshold> + <left_val>0.4731487035751343</left_val> + <right_val>0.7070567011833191</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 13 3 3 -1.</_> + <_>14 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6235301308333874e-003</threshold> + <left_val>0.5240243077278137</left_val> + <right_val>0.2781791985034943</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 3 3 -1.</_> + <_>3 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0243991427123547e-003</threshold> + <left_val>0.2837013900279999</left_val> + <right_val>0.5062304139137268</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7611639648675919e-003</threshold> + <left_val>0.7400717735290527</left_val> + <right_val>0.4934569001197815</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>8 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1515100747346878e-003</threshold> + <left_val>0.5119131207466126</left_val> + <right_val>0.3407008051872253</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 3 3 -1.</_> + <_>13 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2465080991387367e-003</threshold> + <left_val>0.4923788011074066</left_val> + <right_val>0.6579058766365051</right_val></_></_> + <_> + <!-- tree 140 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 5 3 -1.</_> + <_>0 10 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0597478188574314e-003</threshold> + <left_val>0.2434711009263992</left_val> + <right_val>0.5032842159271240</right_val></_></_> + <_> + <!-- tree 141 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 3 3 -1.</_> + <_>13 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0587709732353687e-003</threshold> + <left_val>0.5900310873985291</left_val> + <right_val>0.4695087075233460</right_val></_></_> + <_> + <!-- tree 142 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 8 -1.</_> + <_>9 12 1 4 2.</_> + <_>10 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4146060459315777e-003</threshold> + <left_val>0.3647317886352539</left_val> + <right_val>0.5189201831817627</right_val></_></_> + <_> + <!-- tree 143 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 7 2 2 -1.</_> + <_>12 7 1 1 2.</_> + <_>11 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4817609917372465e-003</threshold> + <left_val>0.6034948229789734</left_val> + <right_val>0.4940128028392792</right_val></_></_> + <_> + <!-- tree 144 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 16 6 4 -1.</_> + <_>3 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3016400672495365e-003</threshold> + <left_val>0.5818989872932434</left_val> + <right_val>0.4560427963733673</right_val></_></_> + <_> + <!-- tree 145 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 2 3 -1.</_> + <_>10 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4763428848236799e-003</threshold> + <left_val>0.5217475891113281</left_val> + <right_val>0.3483993113040924</right_val></_></_> + <_> + <!-- tree 146 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>9 7 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0222508702427149</threshold> + <left_val>0.2360700070858002</left_val> + <right_val>0.5032082796096802</right_val></_></_> + <_> + <!-- tree 147 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 15 8 4 -1.</_> + <_>12 15 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0306125506758690</threshold> + <left_val>0.6499186754226685</left_val> + <right_val>0.4914919137954712</right_val></_></_> + <_> + <!-- tree 148 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 8 6 -1.</_> + <_>4 14 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130574796348810</threshold> + <left_val>0.4413323104381561</left_val> + <right_val>0.5683764219284058</right_val></_></_> + <_> + <!-- tree 149 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 3 2 -1.</_> + <_>10 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0095742810517550e-004</threshold> + <left_val>0.4359731078147888</left_val> + <right_val>0.5333483219146729</right_val></_></_> + <_> + <!-- tree 150 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 15 4 2 -1.</_> + <_>6 15 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1514250915497541e-004</threshold> + <left_val>0.5504062771797180</left_val> + <right_val>0.4326060116291046</right_val></_></_> + <_> + <!-- tree 151 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 3 13 -1.</_> + <_>13 7 1 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0137762902304530</threshold> + <left_val>0.4064112901687622</left_val> + <right_val>0.5201548933982849</right_val></_></_> + <_> + <!-- tree 152 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 3 13 -1.</_> + <_>6 7 1 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0322965085506439</threshold> + <left_val>0.0473519712686539</left_val> + <right_val>0.4977194964885712</right_val></_></_> + <_> + <!-- tree 153 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 3 9 -1.</_> + <_>9 9 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0535569787025452</threshold> + <left_val>0.4881733059883118</left_val> + <right_val>0.6666939258575440</right_val></_></_> + <_> + <!-- tree 154 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 7 12 -1.</_> + <_>4 10 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1889545544981956e-003</threshold> + <left_val>0.5400037169456482</left_val> + <right_val>0.4240820109844208</right_val></_></_> + <_> + <!-- tree 155 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 12 2 2 -1.</_> + <_>13 12 1 1 2.</_> + <_>12 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1055320394225419e-004</threshold> + <left_val>0.4802047908306122</left_val> + <right_val>0.5563852787017822</right_val></_></_> + <_> + <!-- tree 156 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 2 2 -1.</_> + <_>6 12 1 1 2.</_> + <_>7 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4382730480283499e-003</threshold> + <left_val>0.7387793064117432</left_val> + <right_val>0.4773685038089752</right_val></_></_> + <_> + <!-- tree 157 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>10 9 2 1 2.</_> + <_>8 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2835570164024830e-003</threshold> + <left_val>0.5288546085357666</left_val> + <right_val>0.3171291947364807</right_val></_></_> + <_> + <!-- tree 158 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 2 2 -1.</_> + <_>3 6 1 1 2.</_> + <_>4 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3729570675641298e-003</threshold> + <left_val>0.4750812947750092</left_val> + <right_val>0.7060170769691467</right_val></_></_> + <_> + <!-- tree 159 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 6 3 2 -1.</_> + <_>16 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4541699783876538e-003</threshold> + <left_val>0.3811730146408081</left_val> + <right_val>0.5330739021301270</right_val></_></_></trees> + <stage_threshold>79.2490768432617190</stage_threshold> + <parent>16</parent> + <next>-1</next></_> + <_> + <!-- stage 18 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 19 4 -1.</_> + <_>0 9 19 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0557552389800549</threshold> + <left_val>0.4019156992435455</left_val> + <right_val>0.6806036829948425</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 2 10 1 -1.</_> + <_>10 2 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4730248842388391e-003</threshold> + <left_val>0.3351148962974548</left_val> + <right_val>0.5965719819068909</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 12 -1.</_> + <_>9 10 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5031698644161224e-004</threshold> + <left_val>0.5557708144187927</left_val> + <right_val>0.3482286930084229</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 18 4 1 -1.</_> + <_>12 18 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4167630150914192e-004</threshold> + <left_val>0.4260858893394470</left_val> + <right_val>0.5693380832672119</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 6 4 -1.</_> + <_>1 7 3 2 2.</_> + <_>4 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7193678589537740e-004</threshold> + <left_val>0.3494240045547485</left_val> + <right_val>0.5433688759803772</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 6 13 -1.</_> + <_>14 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5999219613149762e-003</threshold> + <left_val>0.4028499126434326</left_val> + <right_val>0.5484359264373779</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 6 13 -1.</_> + <_>4 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1832080053864047e-004</threshold> + <left_val>0.3806901872158051</left_val> + <right_val>0.5425465106964111</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 8 8 -1.</_> + <_>10 9 8 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2909031142480671e-004</threshold> + <left_val>0.2620100080966950</left_val> + <right_val>0.5429521799087524</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 2 5 -1.</_> + <_>9 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9518108931370080e-004</threshold> + <left_val>0.3799768984317780</left_val> + <right_val>0.5399264097213745</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 9 1 -1.</_> + <_>11 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0466710389591753e-005</threshold> + <left_val>0.4433645009994507</left_val> + <right_val>0.5440226197242737</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 9 1 -1.</_> + <_>6 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5007190086180344e-005</threshold> + <left_val>0.3719654977321625</left_val> + <right_val>0.5409119725227356</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 10 -1.</_> + <_>7 0 6 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1393561065196991</threshold> + <left_val>0.5525395870208740</left_val> + <right_val>0.4479042887687683</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 5 3 -1.</_> + <_>7 18 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6461990308016539e-003</threshold> + <left_val>0.4264501035213471</left_val> + <right_val>0.5772169828414917</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 6 1 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9984431825578213e-004</threshold> + <left_val>0.4359526038169861</left_val> + <right_val>0.5685871243476868</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 3 2 -1.</_> + <_>2 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0971280280500650e-003</threshold> + <left_val>0.3390136957168579</left_val> + <right_val>0.5205408930778503</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6919892560690641e-004</threshold> + <left_val>0.4557456076145172</left_val> + <right_val>0.5980659723281860</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6471042595803738e-004</threshold> + <left_val>0.5134841203689575</left_val> + <right_val>0.2944033145904541</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 2 4 -1.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7182599296793342e-004</threshold> + <left_val>0.3906578123569489</left_val> + <right_val>0.5377181172370911</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 2 4 -1.</_> + <_>8 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0249499104684219e-005</threshold> + <left_val>0.3679609894752502</left_val> + <right_val>0.5225688815116882</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>9 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5225896909832954e-003</threshold> + <left_val>0.7293102145195007</left_val> + <right_val>0.4892365038394928</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 13 8 3 -1.</_> + <_>6 14 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6705560265108943e-003</threshold> + <left_val>0.4345324933528900</left_val> + <right_val>0.5696138143539429</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 3 4 -1.</_> + <_>10 15 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1433838456869125e-003</threshold> + <left_val>0.2591280043125153</left_val> + <right_val>0.5225623846054077</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 2 17 -1.</_> + <_>10 2 1 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163193698972464</threshold> + <left_val>0.6922279000282288</left_val> + <right_val>0.4651575982570648</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 1 -1.</_> + <_>9 0 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8034260980784893e-003</threshold> + <left_val>0.5352262854576111</left_val> + <right_val>0.3286302983760834</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 3 4 -1.</_> + <_>9 15 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5421929359436035e-003</threshold> + <left_val>0.2040544003248215</left_val> + <right_val>0.5034546256065369</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 7 3 -1.</_> + <_>7 14 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0143631100654602</threshold> + <left_val>0.6804888844490051</left_val> + <right_val>0.4889059066772461</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 3 3 -1.</_> + <_>9 16 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9063588529825211e-004</threshold> + <left_val>0.5310695767402649</left_val> + <right_val>0.3895480930805206</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 8 10 -1.</_> + <_>6 7 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4060191139578819e-003</threshold> + <left_val>0.5741562843322754</left_val> + <right_val>0.4372426867485046</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 5 8 8 -1.</_> + <_>2 9 8 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8862540309783071e-004</threshold> + <left_val>0.2831785976886749</left_val> + <right_val>0.5098205208778381</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 16 2 2 -1.</_> + <_>14 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7979281041771173e-003</threshold> + <left_val>0.3372507989406586</left_val> + <right_val>0.5246580243110657</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 16 2 2 -1.</_> + <_>4 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4627049677073956e-004</threshold> + <left_val>0.5306674242019653</left_val> + <right_val>0.3911710083484650</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 4 6 -1.</_> + <_>10 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9164638767251745e-005</threshold> + <left_val>0.5462496280670166</left_val> + <right_val>0.3942720890045166</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 4 6 -1.</_> + <_>6 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0335825011134148</threshold> + <left_val>0.2157824039459229</left_val> + <right_val>0.5048211812973023</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 1 3 -1.</_> + <_>10 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5339309833943844e-003</threshold> + <left_val>0.6465312242507935</left_val> + <right_val>0.4872696995735169</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0144111737608910e-003</threshold> + <left_val>0.4617668092250824</left_val> + <right_val>0.6248074769973755</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 4 6 -1.</_> + <_>12 0 2 3 2.</_> + <_>10 3 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0188173707574606</threshold> + <left_val>0.5220689177513123</left_val> + <right_val>0.2000052034854889</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 20 2 -1.</_> + <_>0 4 20 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3434339780360460e-003</threshold> + <left_val>0.4014537930488586</left_val> + <right_val>0.5301619768142700</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 8 2 -1.</_> + <_>16 0 4 1 2.</_> + <_>12 1 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7557960236445069e-003</threshold> + <left_val>0.4794039130210877</left_val> + <right_val>0.5653169751167297</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 10 8 -1.</_> + <_>2 16 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0956374630331993</threshold> + <left_val>0.2034195065498352</left_val> + <right_val>0.5006706714630127</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 7 2 10 -1.</_> + <_>18 7 1 5 2.</_> + <_>17 12 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0222412291914225</threshold> + <left_val>0.7672473192214966</left_val> + <right_val>0.5046340227127075</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 2 10 -1.</_> + <_>1 7 1 5 2.</_> + <_>2 12 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0155758196488023</threshold> + <left_val>0.7490342259407044</left_val> + <right_val>0.4755851030349731</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 10 3 6 -1.</_> + <_>15 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3599118255078793e-003</threshold> + <left_val>0.5365303754806519</left_val> + <right_val>0.4004670977592468</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 6 2 -1.</_> + <_>6 4 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0217634998261929</threshold> + <left_val>0.0740154981613159</left_val> + <right_val>0.4964174926280975</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 20 6 -1.</_> + <_>0 7 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1656159013509750</threshold> + <left_val>0.2859103083610535</left_val> + <right_val>0.5218086242675781</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 8 2 -1.</_> + <_>0 0 4 1 2.</_> + <_>4 1 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6461320046801120e-004</threshold> + <left_val>0.4191615879535675</left_val> + <right_val>0.5380793213844299</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9077502489089966e-003</threshold> + <left_val>0.6273192763328552</left_val> + <right_val>0.4877404868602753</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 13 6 2 -1.</_> + <_>1 14 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6346449097618461e-004</threshold> + <left_val>0.5159940719604492</left_val> + <right_val>0.3671025931835175</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 3 4 -1.</_> + <_>11 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3751760125160217e-003</threshold> + <left_val>0.5884376764297485</left_val> + <right_val>0.4579083919525147</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 6 1 -1.</_> + <_>8 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4081239933148026e-003</threshold> + <left_val>0.3560509979724884</left_val> + <right_val>0.5139945149421692</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9342888630926609e-003</threshold> + <left_val>0.5994288921356201</left_val> + <right_val>0.4664272069931030</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 6 18 2 -1.</_> + <_>10 6 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0319669283926487</threshold> + <left_val>0.3345462083816528</left_val> + <right_val>0.5144183039665222</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 1 2 -1.</_> + <_>15 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5089280168467667e-005</threshold> + <left_val>0.5582656264305115</left_val> + <right_val>0.4414057135581970</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 1 2 -1.</_> + <_>6 6 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1994470413774252e-004</threshold> + <left_val>0.4623680114746094</left_val> + <right_val>0.6168993711471558</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 1 3 -1.</_> + <_>13 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4220460802316666e-003</threshold> + <left_val>0.6557074785232544</left_val> + <right_val>0.4974805116653442</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 1 2 -1.</_> + <_>2 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7723299970384687e-004</threshold> + <left_val>0.5269501805305481</left_val> + <right_val>0.3901908099651337</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 4 3 -1.</_> + <_>12 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5716759953647852e-003</threshold> + <left_val>0.4633373022079468</left_val> + <right_val>0.5790457725524902</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 7 3 -1.</_> + <_>0 1 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9041329920291901e-003</threshold> + <left_val>0.2689608037471771</left_val> + <right_val>0.5053591132164002</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 6 2 -1.</_> + <_>9 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0677518700249493e-004</threshold> + <left_val>0.5456603169441223</left_val> + <right_val>0.4329898953437805</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7604780197143555e-003</threshold> + <left_val>0.4648993909358978</left_val> + <right_val>0.6689761877059937</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 4 2 3 -1.</_> + <_>18 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9100088868290186e-003</threshold> + <left_val>0.5309703946113586</left_val> + <right_val>0.3377839922904968</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 8 6 -1.</_> + <_>3 2 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3885459629818797e-003</threshold> + <left_val>0.4074738919734955</left_val> + <right_val>0.5349133014678955</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 6 -1.</_> + <_>10 2 10 3 2.</_> + <_>0 5 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0767642632126808</threshold> + <left_val>0.1992176026105881</left_val> + <right_val>0.5228242278099060</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 2 4 -1.</_> + <_>5 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2688310127705336e-004</threshold> + <left_val>0.5438501834869385</left_val> + <right_val>0.4253072142601013</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 10 15 2 -1.</_> + <_>8 10 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3094152137637138e-003</threshold> + <left_val>0.4259178936481476</left_val> + <right_val>0.5378909707069397</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 12 11 -1.</_> + <_>9 0 6 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1100727990269661</threshold> + <left_val>0.6904156804084778</left_val> + <right_val>0.4721749126911163</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 2 6 -1.</_> + <_>13 0 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8619659133255482e-004</threshold> + <left_val>0.4524914920330048</left_val> + <right_val>0.5548306107521057</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 19 2 1 -1.</_> + <_>1 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9425329557852820e-005</threshold> + <left_val>0.5370373725891113</left_val> + <right_val>0.4236463904380798</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 10 4 10 -1.</_> + <_>18 10 2 5 2.</_> + <_>16 15 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0248865708708763</threshold> + <left_val>0.6423557996749878</left_val> + <right_val>0.4969303905963898</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 10 3 -1.</_> + <_>4 9 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0331488512456417</threshold> + <left_val>0.4988475143909454</left_val> + <right_val>0.1613811999559403</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 3 3 -1.</_> + <_>14 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8491691965609789e-004</threshold> + <left_val>0.5416026115417481</left_val> + <right_val>0.4223009049892426</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 4 10 -1.</_> + <_>0 10 2 5 2.</_> + <_>2 15 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7087189741432667e-003</threshold> + <left_val>0.4576328992843628</left_val> + <right_val>0.6027557849884033</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 3 2 6 -1.</_> + <_>18 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4144479539245367e-003</threshold> + <left_val>0.5308973193168640</left_val> + <right_val>0.4422498941421509</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 3 -1.</_> + <_>6 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9523180089890957e-003</threshold> + <left_val>0.4705634117126465</left_val> + <right_val>0.6663324832916260</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 7 2 -1.</_> + <_>7 8 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3031980488449335e-003</threshold> + <left_val>0.4406126141548157</left_val> + <right_val>0.5526962280273438</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 2 6 -1.</_> + <_>0 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4735497795045376e-003</threshold> + <left_val>0.5129023790359497</left_val> + <right_val>0.3301498889923096</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 3 1 -1.</_> + <_>12 1 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6652868837118149e-003</threshold> + <left_val>0.3135471045970917</left_val> + <right_val>0.5175036191940308</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 2 6 -1.</_> + <_>6 0 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3666770246345550e-004</threshold> + <left_val>0.4119370877742767</left_val> + <right_val>0.5306876897811890</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 14 -1.</_> + <_>7 1 6 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0171264503151178</threshold> + <left_val>0.6177806258201599</left_val> + <right_val>0.4836578965187073</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 8 3 -1.</_> + <_>8 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6601430727168918e-004</threshold> + <left_val>0.3654330968856812</left_val> + <right_val>0.5169736742973328</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 6 2 -1.</_> + <_>9 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0229323804378510</threshold> + <left_val>0.3490915000438690</left_val> + <right_val>0.5163992047309876</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 6 2 -1.</_> + <_>8 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3316550068557262e-003</threshold> + <left_val>0.5166299939155579</left_val> + <right_val>0.3709389865398407</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 5 -1.</_> + <_>11 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0169256608933210</threshold> + <left_val>0.5014736056327820</left_val> + <right_val>0.8053988218307495</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 5 -1.</_> + <_>8 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9858826249837875e-003</threshold> + <left_val>0.6470788717269898</left_val> + <right_val>0.4657020866870880</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 10 -1.</_> + <_>14 0 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0118746999651194</threshold> + <left_val>0.3246378898620606</left_val> + <right_val>0.5258755087852478</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 3 2 -1.</_> + <_>4 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9350569345988333e-004</threshold> + <left_val>0.5191941857337952</left_val> + <right_val>0.3839643895626068</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 3 3 6 -1.</_> + <_>18 3 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8713490143418312e-003</threshold> + <left_val>0.4918133914470673</left_val> + <right_val>0.6187043190002441</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 18 10 -1.</_> + <_>1 13 18 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2483879029750824</threshold> + <left_val>0.1836802959442139</left_val> + <right_val>0.4988150000572205</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 10 -1.</_> + <_>14 0 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0122560001909733</threshold> + <left_val>0.5227053761482239</left_val> + <right_val>0.3632029891014099</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3990179700776935e-004</threshold> + <left_val>0.4490250051021576</left_val> + <right_val>0.5774148106575012</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 7 -1.</_> + <_>17 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5407369248569012e-003</threshold> + <left_val>0.4804787039756775</left_val> + <right_val>0.5858299136161804</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 3 10 -1.</_> + <_>5 0 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0148224299773574</threshold> + <left_val>0.2521049976348877</left_val> + <right_val>0.5023537278175354</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 7 -1.</_> + <_>17 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7973959483206272e-003</threshold> + <left_val>0.5996695756912231</left_val> + <right_val>0.4853715002536774</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 1 2 -1.</_> + <_>0 10 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2662148158997297e-004</threshold> + <left_val>0.5153716802597046</left_val> + <right_val>0.3671779930591583</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 10 -1.</_> + <_>18 1 1 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0172325801104307</threshold> + <left_val>0.6621719002723694</left_val> + <right_val>0.4994656145572662</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 2 10 -1.</_> + <_>1 1 1 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8624086454510689e-003</threshold> + <left_val>0.4633395075798035</left_val> + <right_val>0.6256101727485657</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 3 4 -1.</_> + <_>11 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7343620099127293e-003</threshold> + <left_val>0.3615573048591614</left_val> + <right_val>0.5281885266304016</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 3 3 -1.</_> + <_>3 8 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3048478700220585e-004</threshold> + <left_val>0.4442889094352722</left_val> + <right_val>0.5550957918167114</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 2 6 -1.</_> + <_>12 0 1 3 2.</_> + <_>11 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6602199114859104e-003</threshold> + <left_val>0.5162935256958008</left_val> + <right_val>0.2613354921340942</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 2 6 -1.</_> + <_>7 0 1 3 2.</_> + <_>8 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1048377752304077e-003</threshold> + <left_val>0.2789632081985474</left_val> + <right_val>0.5019031763076782</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 7 -1.</_> + <_>17 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8512578941881657e-003</threshold> + <left_val>0.4968984127044678</left_val> + <right_val>0.5661668181419373</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 3 7 -1.</_> + <_>2 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9896453320980072e-004</threshold> + <left_val>0.4445607960224152</left_val> + <right_val>0.5551813244819641</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 16 -1.</_> + <_>16 1 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2702363133430481</threshold> + <left_val>0.0293882098048925</left_val> + <right_val>0.5151314139366150</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 6 16 -1.</_> + <_>2 1 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0130906803533435</threshold> + <left_val>0.5699399709701538</left_val> + <right_val>0.4447459876537323</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 16 8 -1.</_> + <_>10 0 8 4 2.</_> + <_>2 4 8 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4342790544033051e-003</threshold> + <left_val>0.4305466115474701</left_val> + <right_val>0.5487895011901856</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 8 5 3 -1.</_> + <_>6 9 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5482039889320731e-003</threshold> + <left_val>0.3680317103862763</left_val> + <right_val>0.5128080844879150</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3746132180094719e-003</threshold> + <left_val>0.4838916957378388</left_val> + <right_val>0.6101555824279785</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5786769799888134e-003</threshold> + <left_val>0.5325223207473755</left_val> + <right_val>0.4118548035621643</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>9 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6856050137430429e-003</threshold> + <left_val>0.4810948073863983</left_val> + <right_val>0.6252303123474121</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 15 1 -1.</_> + <_>5 7 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3887019902467728e-003</threshold> + <left_val>0.5200229883193970</left_val> + <right_val>0.3629410862922669</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 7 9 -1.</_> + <_>8 5 7 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0127926301211119</threshold> + <left_val>0.4961709976196289</left_val> + <right_val>0.6738016009330750</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 16 4 -1.</_> + <_>1 7 8 2 2.</_> + <_>9 9 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3661040943115950e-003</threshold> + <left_val>0.4060279130935669</left_val> + <right_val>0.5283598899841309</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 8 2 -1.</_> + <_>6 13 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9771420415490866e-004</threshold> + <left_val>0.4674113988876343</left_val> + <right_val>0.5900775194168091</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 3 3 -1.</_> + <_>8 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4868030557408929e-003</threshold> + <left_val>0.4519116878509522</left_val> + <right_val>0.6082053780555725</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 14 10 -1.</_> + <_>11 5 7 5 2.</_> + <_>4 10 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0886867493391037</threshold> + <left_val>0.2807899117469788</left_val> + <right_val>0.5180991888046265</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 3 2 -1.</_> + <_>4 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4296112870797515e-005</threshold> + <left_val>0.5295584201812744</left_val> + <right_val>0.4087625145912170</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 6 1 -1.</_> + <_>11 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4932939848222304e-005</threshold> + <left_val>0.5461400151252747</left_val> + <right_val>0.4538542926311493</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 7 6 -1.</_> + <_>4 11 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9162238612771034e-003</threshold> + <left_val>0.5329161286354065</left_val> + <right_val>0.4192134141921997</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 6 3 -1.</_> + <_>7 11 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1141640134155750e-003</threshold> + <left_val>0.4512017965316773</left_val> + <right_val>0.5706217288970947</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 2 -1.</_> + <_>9 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9249362645205110e-005</threshold> + <left_val>0.4577805995941162</left_val> + <right_val>0.5897638201713562</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 20 6 -1.</_> + <_>0 7 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5319510605186224e-003</threshold> + <left_val>0.5299603939056397</left_val> + <right_val>0.3357639014720917</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 6 1 -1.</_> + <_>8 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0124262003228068</threshold> + <left_val>0.4959059059619904</left_val> + <right_val>0.1346601992845535</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 6 1 -1.</_> + <_>11 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0283357501029968</threshold> + <left_val>0.5117079019546509</left_val> + <right_val>6.1043637106195092e-004</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6165882162749767e-003</threshold> + <left_val>0.4736349880695343</left_val> + <right_val>0.7011628150939941</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 3 4 -1.</_> + <_>11 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0468766391277313e-003</threshold> + <left_val>0.5216417908668518</left_val> + <right_val>0.3282819986343384</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1193980462849140e-003</threshold> + <left_val>0.5809860825538635</left_val> + <right_val>0.4563739001750946</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 16 8 -1.</_> + <_>2 16 16 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0132775902748108</threshold> + <left_val>0.5398362278938294</left_val> + <right_val>0.4103901088237763</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 15 2 -1.</_> + <_>0 16 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8794739996083081e-004</threshold> + <left_val>0.4249286055564880</left_val> + <right_val>0.5410590767860413</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 4 5 6 -1.</_> + <_>15 6 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0112431701272726</threshold> + <left_val>0.5269963741302490</left_val> + <right_val>0.3438215851783752</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 4 -1.</_> + <_>10 5 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9896668214350939e-004</threshold> + <left_val>0.5633075833320618</left_val> + <right_val>0.4456613063812256</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 9 6 -1.</_> + <_>8 12 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6677159629762173e-003</threshold> + <left_val>0.5312889218330383</left_val> + <right_val>0.4362679123878479</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 19 15 1 -1.</_> + <_>7 19 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0289472993463278</threshold> + <left_val>0.4701794981956482</left_val> + <right_val>0.6575797796249390</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 3 4 -1.</_> + <_>11 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0234000496566296</threshold> + <left_val>0.</left_val> + <right_val>0.5137398838996887</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 20 4 -1.</_> + <_>0 17 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0891170501708984</threshold> + <left_val>0.0237452797591686</left_val> + <right_val>0.4942430853843689</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 3 4 -1.</_> + <_>11 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0140546001493931</threshold> + <left_val>0.3127323091030121</left_val> + <right_val>0.5117511153221130</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 16 3 4 -1.</_> + <_>8 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1239398568868637e-003</threshold> + <left_val>0.5009049177169800</left_val> + <right_val>0.2520025968551636</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 3 -1.</_> + <_>9 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9964650534093380e-003</threshold> + <left_val>0.6387143731117249</left_val> + <right_val>0.4927811920642853</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 6 -1.</_> + <_>8 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1253970228135586e-003</threshold> + <left_val>0.5136849880218506</left_val> + <right_val>0.3680452108383179</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 12 -1.</_> + <_>9 10 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7669642157852650e-003</threshold> + <left_val>0.5509843826293945</left_val> + <right_val>0.4363631904125214</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 4 3 -1.</_> + <_>8 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3711440153419971e-003</threshold> + <left_val>0.6162335276603699</left_val> + <right_val>0.4586946964263916</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 18 8 2 -1.</_> + <_>13 18 4 1 2.</_> + <_>9 19 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3522791713476181e-003</threshold> + <left_val>0.6185457706451416</left_val> + <right_val>0.4920490980148315</right_val></_></_> + <_> + <!-- tree 140 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 18 8 2 -1.</_> + <_>1 19 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0159688591957092</threshold> + <left_val>0.1382617950439453</left_val> + <right_val>0.4983252882957459</right_val></_></_> + <_> + <!-- tree 141 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 6 15 -1.</_> + <_>15 5 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7676060348749161e-003</threshold> + <left_val>0.4688057899475098</left_val> + <right_val>0.5490046143531799</right_val></_></_> + <_> + <!-- tree 142 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 2 -1.</_> + <_>9 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4714691098779440e-003</threshold> + <left_val>0.2368514984846115</left_val> + <right_val>0.5003952980041504</right_val></_></_> + <_> + <!-- tree 143 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 3 -1.</_> + <_>9 5 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1033788844943047e-004</threshold> + <left_val>0.5856394171714783</left_val> + <right_val>0.4721533060073853</right_val></_></_> + <_> + <!-- tree 144 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 6 15 -1.</_> + <_>3 5 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1411755979061127</threshold> + <left_val>0.0869000628590584</left_val> + <right_val>0.4961591064929962</right_val></_></_> + <_> + <!-- tree 145 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 1 14 8 -1.</_> + <_>11 1 7 4 2.</_> + <_>4 5 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1065180972218514</threshold> + <left_val>0.5138837099075317</left_val> + <right_val>0.1741005033254623</right_val></_></_> + <_> + <!-- tree 146 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 4 4 16 -1.</_> + <_>2 4 2 8 2.</_> + <_>4 12 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0527447499334812</threshold> + <left_val>0.7353636026382446</left_val> + <right_val>0.4772881865501404</right_val></_></_> + <_> + <!-- tree 147 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 12 -1.</_> + <_>12 10 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7431760467588902e-003</threshold> + <left_val>0.3884406089782715</left_val> + <right_val>0.5292701721191406</right_val></_></_> + <_> + <!-- tree 148 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 10 12 -1.</_> + <_>4 5 5 6 2.</_> + <_>9 11 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9676765967160463e-004</threshold> + <left_val>0.5223492980003357</left_val> + <right_val>0.4003424048423767</right_val></_></_> + <_> + <!-- tree 149 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0284131690859795e-003</threshold> + <left_val>0.4959106147289276</left_val> + <right_val>0.7212964296340942</right_val></_></_> + <_> + <!-- tree 150 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6025858763605356e-004</threshold> + <left_val>0.4444884061813355</left_val> + <right_val>0.5538476109504700</right_val></_></_> + <_> + <!-- tree 151 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 4 10 -1.</_> + <_>14 2 2 5 2.</_> + <_>12 7 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3191501218825579e-004</threshold> + <left_val>0.5398371219635010</left_val> + <right_val>0.4163244068622589</right_val></_></_> + <_> + <!-- tree 152 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 7 3 -1.</_> + <_>6 5 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5082060601562262e-003</threshold> + <left_val>0.5854265093803406</left_val> + <right_val>0.4562500119209290</right_val></_></_> + <_> + <!-- tree 153 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 18 2 -1.</_> + <_>11 0 9 1 2.</_> + <_>2 1 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1378761157393456e-003</threshold> + <left_val>0.4608069062232971</left_val> + <right_val>0.5280259251594544</right_val></_></_> + <_> + <!-- tree 154 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 18 2 -1.</_> + <_>0 0 9 1 2.</_> + <_>9 1 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1546049974858761e-003</threshold> + <left_val>0.3791126906871796</left_val> + <right_val>0.5255997180938721</right_val></_></_> + <_> + <!-- tree 155 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 4 6 -1.</_> + <_>15 13 2 3 2.</_> + <_>13 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6214009895920753e-003</threshold> + <left_val>0.5998609066009522</left_val> + <right_val>0.4952073991298676</right_val></_></_> + <_> + <!-- tree 156 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 4 6 -1.</_> + <_>3 13 2 3 2.</_> + <_>5 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2055360022932291e-003</threshold> + <left_val>0.4484206140041351</left_val> + <right_val>0.5588530898094177</right_val></_></_> + <_> + <!-- tree 157 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 2 6 -1.</_> + <_>10 15 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2586950324475765e-003</threshold> + <left_val>0.5450747013092041</left_val> + <right_val>0.4423840939998627</right_val></_></_> + <_> + <!-- tree 158 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 10 10 -1.</_> + <_>5 9 5 5 2.</_> + <_>10 14 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0926720723509789e-003</threshold> + <left_val>0.4118275046348572</left_val> + <right_val>0.5263035893440247</right_val></_></_> + <_> + <!-- tree 159 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 2 -1.</_> + <_>13 4 2 1 2.</_> + <_>11 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5095739401876926e-003</threshold> + <left_val>0.5787907838821411</left_val> + <right_val>0.4998494982719421</right_val></_></_> + <_> + <!-- tree 160 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 8 -1.</_> + <_>10 12 3 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0773275569081306</threshold> + <left_val>0.8397865891456604</left_val> + <right_val>0.4811120033264160</right_val></_></_> + <_> + <!-- tree 161 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 4 10 -1.</_> + <_>14 2 2 5 2.</_> + <_>12 7 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0414858199656010</threshold> + <left_val>0.2408611029386520</left_val> + <right_val>0.5176993012428284</right_val></_></_> + <_> + <!-- tree 162 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 2 1 -1.</_> + <_>9 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0355669655837119e-004</threshold> + <left_val>0.4355360865592957</left_val> + <right_val>0.5417054295539856</right_val></_></_> + <_> + <!-- tree 163 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 1 12 -1.</_> + <_>10 9 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3255809899419546e-003</threshold> + <left_val>0.5453971028327942</left_val> + <right_val>0.4894095063209534</right_val></_></_> + <_> + <!-- tree 164 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 6 9 -1.</_> + <_>3 11 3 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0598732456564903e-003</threshold> + <left_val>0.5771024227142334</left_val> + <right_val>0.4577918946743012</right_val></_></_> + <_> + <!-- tree 165 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 4 10 -1.</_> + <_>14 2 2 5 2.</_> + <_>12 7 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0190586205571890</threshold> + <left_val>0.5169867873191834</left_val> + <right_val>0.3400475084781647</right_val></_></_> + <_> + <!-- tree 166 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 4 10 -1.</_> + <_>4 2 2 5 2.</_> + <_>6 7 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0350578911602497</threshold> + <left_val>0.2203243970870972</left_val> + <right_val>0.5000503063201904</right_val></_></_> + <_> + <!-- tree 167 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 2 -1.</_> + <_>13 4 2 1 2.</_> + <_>11 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7296059094369411e-003</threshold> + <left_val>0.5043408274650574</left_val> + <right_val>0.6597570776939392</right_val></_></_> + <_> + <!-- tree 168 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 6 3 -1.</_> + <_>0 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116483299061656</threshold> + <left_val>0.2186284959316254</left_val> + <right_val>0.4996652901172638</right_val></_></_> + <_> + <!-- tree 169 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 2 -1.</_> + <_>13 4 2 1 2.</_> + <_>11 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4544479781761765e-003</threshold> + <left_val>0.5007681846618652</left_val> + <right_val>0.5503727793693543</right_val></_></_> + <_> + <!-- tree 170 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 3 2 -1.</_> + <_>7 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5030909455381334e-004</threshold> + <left_val>0.4129841029644013</left_val> + <right_val>0.5241670012474060</right_val></_></_> + <_> + <!-- tree 171 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 2 -1.</_> + <_>13 4 2 1 2.</_> + <_>11 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2907272735610604e-004</threshold> + <left_val>0.5412868261337280</left_val> + <right_val>0.4974496066570282</right_val></_></_> + <_> + <!-- tree 172 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 4 2 -1.</_> + <_>5 4 2 1 2.</_> + <_>7 5 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0862209601327777e-003</threshold> + <left_val>0.4605529904365540</left_val> + <right_val>0.5879228711128235</right_val></_></_> + <_> + <!-- tree 173 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 2 12 -1.</_> + <_>14 0 1 6 2.</_> + <_>13 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0000500080641359e-004</threshold> + <left_val>0.5278854966163635</left_val> + <right_val>0.4705209136009216</right_val></_></_> + <_> + <!-- tree 174 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 10 -1.</_> + <_>7 0 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9212920926511288e-003</threshold> + <left_val>0.5129609704017639</left_val> + <right_val>0.3755536973476410</right_val></_></_> + <_> + <!-- tree 175 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 17 8 -1.</_> + <_>3 4 17 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0253874007612467</threshold> + <left_val>0.4822691977024078</left_val> + <right_val>0.5790768265724182</right_val></_></_> + <_> + <!-- tree 176 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 4 -1.</_> + <_>0 6 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1968469265848398e-003</threshold> + <left_val>0.5248395204544067</left_val> + <right_val>0.3962840139865875</right_val></_></_></trees> + <stage_threshold>87.6960296630859380</stage_threshold> + <parent>17</parent> + <next>-1</next></_> + <_> + <!-- stage 19 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 8 2 -1.</_> + <_>4 3 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8031738735735416e-003</threshold> + <left_val>0.3498983979225159</left_val> + <right_val>0.5961983203887940</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0003069490194321e-003</threshold> + <left_val>0.6816636919975281</left_val> + <right_val>0.4478552043437958</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 6 4 -1.</_> + <_>5 7 3 2 2.</_> + <_>8 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1549659539014101e-003</threshold> + <left_val>0.5585706233978272</left_val> + <right_val>0.3578251004219055</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 4 9 -1.</_> + <_>8 6 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1069850297644734e-003</threshold> + <left_val>0.5365036129951477</left_val> + <right_val>0.3050428032875061</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 1 4 -1.</_> + <_>8 17 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0308309720130637e-004</threshold> + <left_val>0.3639095127582550</left_val> + <right_val>0.5344635844230652</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 7 -1.</_> + <_>8 5 4 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0984839908778667e-003</threshold> + <left_val>0.2859157025814056</left_val> + <right_val>0.5504264831542969</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 4 10 -1.</_> + <_>4 2 2 5 2.</_> + <_>6 7 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2572200335562229e-004</threshold> + <left_val>0.5236523747444153</left_val> + <right_val>0.3476041853427887</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 17 2 -1.</_> + <_>3 1 17 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9783325567841530e-003</threshold> + <left_val>0.4750322103500366</left_val> + <right_val>0.6219646930694580</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 16 15 -1.</_> + <_>2 7 16 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0374025292694569</threshold> + <left_val>0.3343375921249390</left_val> + <right_val>0.5278062820434570</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 2 5 2 -1.</_> + <_>15 3 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8548257909715176e-003</threshold> + <left_val>0.5192180871963501</left_val> + <right_val>0.3700444102287293</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 2 -1.</_> + <_>10 3 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8664470408111811e-003</threshold> + <left_val>0.2929843962192535</left_val> + <right_val>0.5091944932937622</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 16 15 -1.</_> + <_>4 10 16 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0168888904154301</threshold> + <left_val>0.3686845898628235</left_val> + <right_val>0.5431225895881653</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 5 6 -1.</_> + <_>7 16 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8372621424496174e-003</threshold> + <left_val>0.3632183969020844</left_val> + <right_val>0.5221335887908936</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 2 -1.</_> + <_>11 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4713739510625601e-003</threshold> + <left_val>0.5870683789253235</left_val> + <right_val>0.4700650870800018</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 3 1 -1.</_> + <_>9 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1522950371727347e-003</threshold> + <left_val>0.3195894956588745</left_val> + <right_val>0.5140954256057739</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 3 -1.</_> + <_>9 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2560300789773464e-003</threshold> + <left_val>0.6301859021186829</left_val> + <right_val>0.4814921021461487</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 5 2 -1.</_> + <_>0 3 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7378291860222816e-003</threshold> + <left_val>0.1977048069238663</left_val> + <right_val>0.5025808215141296</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0113826701417565</threshold> + <left_val>0.4954132139682770</left_val> + <right_val>0.6867045760154724</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 12 1 -1.</_> + <_>5 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1794708706438541e-003</threshold> + <left_val>0.5164427757263184</left_val> + <right_val>0.3350647985935211</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 14 -1.</_> + <_>7 12 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1174378991127014</threshold> + <left_val>0.2315246015787125</left_val> + <right_val>0.5234413743019104</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 8 10 -1.</_> + <_>0 0 4 5 2.</_> + <_>4 5 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0287034492939711</threshold> + <left_val>0.4664297103881836</left_val> + <right_val>0.6722521185874939</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 3 2 -1.</_> + <_>10 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8231030814349651e-003</threshold> + <left_val>0.5220875144004822</left_val> + <right_val>0.2723532915115356</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 2 -1.</_> + <_>9 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6798530016094446e-003</threshold> + <left_val>0.5079277157783508</left_val> + <right_val>0.2906948924064636</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0504082143306732e-003</threshold> + <left_val>0.4885950982570648</left_val> + <right_val>0.6395021080970764</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 6 16 -1.</_> + <_>7 12 6 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8054959625005722e-003</threshold> + <left_val>0.5197256803512573</left_val> + <right_val>0.3656663894653320</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2420159075409174e-003</threshold> + <left_val>0.6153467893600464</left_val> + <right_val>0.4763701856136322</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 2 6 -1.</_> + <_>2 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0137577103450894</threshold> + <left_val>0.2637344896793366</left_val> + <right_val>0.5030903220176697</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 9 -1.</_> + <_>14 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1033829972147942</threshold> + <left_val>0.2287521958351135</left_val> + <right_val>0.5182461142539978</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4432085752487183e-003</threshold> + <left_val>0.6953303813934326</left_val> + <right_val>0.4694949090480804</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0271181650459766e-004</threshold> + <left_val>0.5450655221939087</left_val> + <right_val>0.4268783926963806</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1945669800043106e-003</threshold> + <left_val>0.6091387867927551</left_val> + <right_val>0.4571642875671387</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 6 -1.</_> + <_>13 13 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0109422104433179</threshold> + <left_val>0.5241063237190247</left_val> + <right_val>0.3284547030925751</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 2 6 -1.</_> + <_>3 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7841069065034389e-004</threshold> + <left_val>0.5387929081916809</left_val> + <right_val>0.4179368913173676</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 2 -1.</_> + <_>14 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0888620056211948e-003</threshold> + <left_val>0.4292691051959992</left_val> + <right_val>0.5301715731620789</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 16 2 -1.</_> + <_>0 9 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2383969519287348e-003</threshold> + <left_val>0.3792347908020020</left_val> + <right_val>0.5220744013786316</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 2 -1.</_> + <_>14 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9075027927756310e-003</threshold> + <left_val>0.5237283110618591</left_val> + <right_val>0.4126757979393005</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 5 6 -1.</_> + <_>0 2 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0322779417037964</threshold> + <left_val>0.1947655975818634</left_val> + <right_val>0.4994502067565918</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9711230248212814e-003</threshold> + <left_val>0.6011285185813904</left_val> + <right_val>0.4929032027721405</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 3 6 -1.</_> + <_>4 13 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0153210898861289</threshold> + <left_val>0.5009753704071045</left_val> + <right_val>0.2039822041988373</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0855569746345282e-003</threshold> + <left_val>0.4862189888954163</left_val> + <right_val>0.5721694827079773</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 1 3 -1.</_> + <_>9 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0615021027624607e-003</threshold> + <left_val>0.5000218749046326</left_val> + <right_val>0.1801805943250656</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7174751050770283e-003</threshold> + <left_val>0.5530117154121399</left_val> + <right_val>0.4897592961788178</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 12 -1.</_> + <_>6 12 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0121705001220107</threshold> + <left_val>0.4178605973720551</left_val> + <right_val>0.5383723974227905</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6248398721218109e-003</threshold> + <left_val>0.4997169971466065</left_val> + <right_val>0.5761327147483826</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 9 2 -1.</_> + <_>8 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1040429419372231e-004</threshold> + <left_val>0.5331807136535645</left_val> + <right_val>0.4097681045532227</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146417804062366</threshold> + <left_val>0.5755925178527832</left_val> + <right_val>0.5051776170730591</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 4 3 -1.</_> + <_>4 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3199489116668701e-003</threshold> + <left_val>0.4576976895332336</left_val> + <right_val>0.6031805872917175</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 9 2 -1.</_> + <_>9 6 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7236879579722881e-003</threshold> + <left_val>0.4380396902561188</left_val> + <right_val>0.5415883064270020</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 1 3 -1.</_> + <_>4 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2951161311939359e-004</threshold> + <left_val>0.5163031816482544</left_val> + <right_val>0.3702219128608704</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 6 6 -1.</_> + <_>14 12 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0114084901288152</threshold> + <left_val>0.6072946786880493</left_val> + <right_val>0.4862565100193024</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 3 7 -1.</_> + <_>8 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5320121571421623e-003</threshold> + <left_val>0.3292475938796997</left_val> + <right_val>0.5088962912559509</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 3 -1.</_> + <_>10 8 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1276017911732197e-003</threshold> + <left_val>0.4829767942428589</left_val> + <right_val>0.6122708916664124</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 3 -1.</_> + <_>9 8 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8583158105611801e-003</threshold> + <left_val>0.4660679996013641</left_val> + <right_val>0.6556177139282227</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 11 3 -1.</_> + <_>5 11 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0369859188795090</threshold> + <left_val>0.5204849243164063</left_val> + <right_val>0.1690472066402435</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 1 -1.</_> + <_>10 7 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6491161920130253e-003</threshold> + <left_val>0.5167322158813477</left_val> + <right_val>0.3725225031375885</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 2 -1.</_> + <_>10 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2664702050387859e-003</threshold> + <left_val>0.6406493186950684</left_val> + <right_val>0.4987342953681946</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>9 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7956590424291790e-004</threshold> + <left_val>0.5897293090820313</left_val> + <right_val>0.4464873969554901</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 9 4 2 -1.</_> + <_>11 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6827160511165857e-003</threshold> + <left_val>0.5441560745239258</left_val> + <right_val>0.3472662866115570</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 4 2 -1.</_> + <_>7 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0100598800927401</threshold> + <left_val>0.2143162935972214</left_val> + <right_val>0.5004829764366150</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 2 4 -1.</_> + <_>14 12 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0361840617842972e-004</threshold> + <left_val>0.5386424064636231</left_val> + <right_val>0.4590323865413666</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 2 -1.</_> + <_>8 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4545479789376259e-003</threshold> + <left_val>0.5751184225082398</left_val> + <right_val>0.4497095048427582</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 17 6 3 -1.</_> + <_>14 18 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6515209572389722e-003</threshold> + <left_val>0.5421937704086304</left_val> + <right_val>0.4238520860671997</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 12 -1.</_> + <_>4 5 6 6 2.</_> + <_>10 11 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8468639403581619e-003</threshold> + <left_val>0.4077920913696289</left_val> + <right_val>0.5258157253265381</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 8 8 -1.</_> + <_>10 9 4 4 2.</_> + <_>6 13 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1259850151836872e-003</threshold> + <left_val>0.4229275882244110</left_val> + <right_val>0.5479453206062317</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 15 4 -1.</_> + <_>5 4 5 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0368909612298012</threshold> + <left_val>0.6596375703811646</left_val> + <right_val>0.4674678146839142</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 4 1 -1.</_> + <_>13 2 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4035639944486320e-004</threshold> + <left_val>0.4251135885715485</left_val> + <right_val>0.5573202967643738</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 2 -1.</_> + <_>4 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5150169929256663e-005</threshold> + <left_val>0.5259246826171875</left_val> + <right_val>0.4074114859104157</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2108471021056175e-003</threshold> + <left_val>0.4671722948551178</left_val> + <right_val>0.5886352062225342</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 3 -1.</_> + <_>9 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1568620102480054e-003</threshold> + <left_val>0.5711066126823425</left_val> + <right_val>0.4487161934375763</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 2 3 -1.</_> + <_>13 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9996292218565941e-003</threshold> + <left_val>0.5264198184013367</left_val> + <right_val>0.2898327112197876</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 4 4 -1.</_> + <_>7 12 2 2 2.</_> + <_>9 14 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4656189596280456e-003</threshold> + <left_val>0.3891738057136536</left_val> + <right_val>0.5197871923446655</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 2 2 -1.</_> + <_>11 11 1 1 2.</_> + <_>10 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1975039960816503e-003</threshold> + <left_val>0.5795872807502747</left_val> + <right_val>0.4927955865859985</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4954330660402775e-003</threshold> + <left_val>0.2377603054046631</left_val> + <right_val>0.5012555122375488</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 2 2 -1.</_> + <_>11 11 1 1 2.</_> + <_>10 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4997160178609192e-004</threshold> + <left_val>0.4876626133918762</left_val> + <right_val>0.5617607831954956</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 6 3 -1.</_> + <_>0 18 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6391509454697371e-003</threshold> + <left_val>0.5168088078498840</left_val> + <right_val>0.3765509128570557</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 2 2 -1.</_> + <_>11 11 1 1 2.</_> + <_>10 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9368131072260439e-004</threshold> + <left_val>0.5446649193763733</left_val> + <right_val>0.4874630868434906</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 2 2 -1.</_> + <_>8 11 1 1 2.</_> + <_>9 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4211760135367513e-003</threshold> + <left_val>0.4687897861003876</left_val> + <right_val>0.6691331863403320</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 8 4 -1.</_> + <_>12 5 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0794276371598244</threshold> + <left_val>0.5193443894386292</left_val> + <right_val>0.2732945978641510</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 8 4 -1.</_> + <_>4 5 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0799375027418137</threshold> + <left_val>0.4971731007099152</left_val> + <right_val>0.1782083958387375</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 2 4 1 -1.</_> + <_>13 2 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110892597585917</threshold> + <left_val>0.5165994763374329</left_val> + <right_val>0.3209475874900818</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 4 1 -1.</_> + <_>5 2 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6560709627810866e-004</threshold> + <left_val>0.4058471918106079</left_val> + <right_val>0.5307276248931885</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 4 2 -1.</_> + <_>12 0 2 1 2.</_> + <_>10 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3354292176663876e-003</threshold> + <left_val>0.3445056974887848</left_val> + <right_val>0.5158129930496216</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 3 1 -1.</_> + <_>8 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1287260567769408e-003</threshold> + <left_val>0.4594863057136536</left_val> + <right_val>0.6075533032417297</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 8 -1.</_> + <_>10 11 2 4 2.</_> + <_>8 15 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0219692196696997</threshold> + <left_val>0.1680400967597961</left_val> + <right_val>0.5228595733642578</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1775320055894554e-004</threshold> + <left_val>0.3861596882343292</left_val> + <right_val>0.5215672850608826</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 18 15 2 -1.</_> + <_>3 19 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0200149447191507e-004</threshold> + <left_val>0.5517979264259338</left_val> + <right_val>0.4363039135932922</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 6 2 12 -1.</_> + <_>2 6 1 6 2.</_> + <_>3 12 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0217331498861313</threshold> + <left_val>0.7999460101127625</left_val> + <right_val>0.4789851009845734</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 3 -1.</_> + <_>9 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4399932529777288e-004</threshold> + <left_val>0.4085975885391235</left_val> + <right_val>0.5374773144721985</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 3 2 -1.</_> + <_>8 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3895249837078154e-004</threshold> + <left_val>0.5470405220985413</left_val> + <right_val>0.4366143047809601</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 11 3 1 -1.</_> + <_>12 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5092400135472417e-003</threshold> + <left_val>0.4988996982574463</left_val> + <right_val>0.5842149257659912</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 3 1 -1.</_> + <_>7 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5547839943319559e-003</threshold> + <left_val>0.6753690242767334</left_val> + <right_val>0.4721005856990814</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 4 2 -1.</_> + <_>11 2 2 1 2.</_> + <_>9 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8191400128416717e-004</threshold> + <left_val>0.5415853857994080</left_val> + <right_val>0.4357109069824219</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0264398343861103e-003</threshold> + <left_val>0.2258509993553162</left_val> + <right_val>0.4991880953311920</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 18 3 -1.</_> + <_>8 1 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116681400686502</threshold> + <left_val>0.6256554722785950</left_val> + <right_val>0.4927498996257782</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 4 14 -1.</_> + <_>7 1 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8718370012938976e-003</threshold> + <left_val>0.3947784900665283</left_val> + <right_val>0.5245801806449890</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 12 3 -1.</_> + <_>8 16 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0170511696487665</threshold> + <left_val>0.4752511084079742</left_val> + <right_val>0.5794224143028259</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 17 18 3 -1.</_> + <_>7 17 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0133520802482963</threshold> + <left_val>0.6041104793548584</left_val> + <right_val>0.4544535875320435</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 6 -1.</_> + <_>9 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9301801007241011e-004</threshold> + <left_val>0.4258275926113129</left_val> + <right_val>0.5544905066490173</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 1 8 -1.</_> + <_>9 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0483349692076445e-003</threshold> + <left_val>0.5233420133590698</left_val> + <right_val>0.3780272901058197</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3579288758337498e-003</threshold> + <left_val>0.6371889114379883</left_val> + <right_val>0.4838674068450928</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 12 -1.</_> + <_>9 10 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6661018170416355e-003</threshold> + <left_val>0.5374705791473389</left_val> + <right_val>0.4163666069507599</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0677339206449687e-005</threshold> + <left_val>0.4638795852661133</left_val> + <right_val>0.5311625003814697</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 4 8 -1.</_> + <_>2 1 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0367381609976292</threshold> + <left_val>0.4688656032085419</left_val> + <right_val>0.6466524004936218</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 6 2 -1.</_> + <_>12 1 3 1 2.</_> + <_>9 2 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6528137326240540e-003</threshold> + <left_val>0.5204318761825562</left_val> + <right_val>0.2188657969236374</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 12 14 -1.</_> + <_>1 10 12 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1537135988473892</threshold> + <left_val>0.1630371958017349</left_val> + <right_val>0.4958840012550354</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>10 12 2 1 2.</_> + <_>8 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1560421232134104e-004</threshold> + <left_val>0.5774459242820740</left_val> + <right_val>0.4696458876132965</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 10 2 -1.</_> + <_>1 9 5 1 2.</_> + <_>6 10 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2640169588848948e-003</threshold> + <left_val>0.3977175951004028</left_val> + <right_val>0.5217198133468628</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5473341122269630e-003</threshold> + <left_val>0.6046528220176697</left_val> + <right_val>0.4808315038681030</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 8 8 3 -1.</_> + <_>6 9 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0019069527043030e-005</threshold> + <left_val>0.3996723890304565</left_val> + <right_val>0.5228201150894165</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 5 3 -1.</_> + <_>9 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3113019522279501e-003</threshold> + <left_val>0.4712158143520355</left_val> + <right_val>0.5765997767448425</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 4 3 -1.</_> + <_>8 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3374709524214268e-003</threshold> + <left_val>0.4109584987163544</left_val> + <right_val>0.5253170132637024</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 2 -1.</_> + <_>7 8 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0208767093718052</threshold> + <left_val>0.5202993750572205</left_val> + <right_val>0.1757981926202774</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 2 -1.</_> + <_>5 7 4 1 2.</_> + <_>9 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5497948564589024e-003</threshold> + <left_val>0.6566609740257263</left_val> + <right_val>0.4694975018501282</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0241885501891375</threshold> + <left_val>0.5128673911094666</left_val> + <right_val>0.3370220959186554</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 4 2 -1.</_> + <_>4 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9358828905969858e-003</threshold> + <left_val>0.6580786705017090</left_val> + <right_val>0.4694541096687317</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 9 -1.</_> + <_>14 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0575579293072224</threshold> + <left_val>0.5146445035934448</left_val> + <right_val>0.2775259912014008</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 3 3 -1.</_> + <_>5 9 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1343370424583554e-003</threshold> + <left_val>0.3836601972579956</left_val> + <right_val>0.5192667245864868</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0168169997632504</threshold> + <left_val>0.5085592865943909</left_val> + <right_val>0.6177260875701904</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 9 -1.</_> + <_>0 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0535178743302822e-003</threshold> + <left_val>0.5138763189315796</left_val> + <right_val>0.3684791922569275</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 3 3 6 -1.</_> + <_>18 3 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5874710194766521e-003</threshold> + <left_val>0.5989655256271362</left_val> + <right_val>0.4835202097892761</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 3 6 -1.</_> + <_>1 3 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6882460331544280e-003</threshold> + <left_val>0.4509486854076386</left_val> + <right_val>0.5723056793212891</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 14 1 2 -1.</_> + <_>17 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6554000321775675e-003</threshold> + <left_val>0.3496770858764648</left_val> + <right_val>0.5243319272994995</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 4 3 -1.</_> + <_>6 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0193738006055355</threshold> + <left_val>0.1120536997914314</left_val> + <right_val>0.4968712925910950</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>12 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0103744501248002</threshold> + <left_val>0.5148196816444397</left_val> + <right_val>0.4395213127136231</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 3 3 -1.</_> + <_>5 10 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4973050565458834e-004</threshold> + <left_val>0.4084999859333038</left_val> + <right_val>0.5269886851310730</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 8 -1.</_> + <_>12 5 3 4 2.</_> + <_>9 9 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0429819300770760</threshold> + <left_val>0.6394104957580566</left_val> + <right_val>0.5018504261970520</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 8 -1.</_> + <_>5 5 3 4 2.</_> + <_>8 9 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3065936341881752e-003</threshold> + <left_val>0.4707553982734680</left_val> + <right_val>0.6698353290557861</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 6 -1.</_> + <_>16 4 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1285790503025055e-003</threshold> + <left_val>0.4541369080543518</left_val> + <right_val>0.5323647260665894</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 6 20 -1.</_> + <_>3 0 2 20 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7399420030415058e-003</threshold> + <left_val>0.4333961904048920</left_val> + <right_val>0.5439866185188294</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 3 2 -1.</_> + <_>13 11 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1739750334527344e-004</threshold> + <left_val>0.4579687118530273</left_val> + <right_val>0.5543426275253296</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 3 2 -1.</_> + <_>6 11 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8585780344437808e-004</threshold> + <left_val>0.4324643909931183</left_val> + <right_val>0.5426754951477051</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 6 1 -1.</_> + <_>11 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5587692186236382e-003</threshold> + <left_val>0.5257220864295960</left_val> + <right_val>0.3550611138343811</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 8 3 -1.</_> + <_>4 0 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9851560294628143e-003</threshold> + <left_val>0.6043018102645874</left_val> + <right_val>0.4630635976791382</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 0 2 5 -1.</_> + <_>15 0 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0594122624024749e-004</threshold> + <left_val>0.4598254859447479</left_val> + <right_val>0.5533195137977600</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 1 3 2 -1.</_> + <_>5 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2983040253166109e-004</threshold> + <left_val>0.4130752086639404</left_val> + <right_val>0.5322461128234863</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 15 -1.</_> + <_>9 0 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3740210821852088e-004</threshold> + <left_val>0.4043039977550507</left_val> + <right_val>0.5409289002418518</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 3 1 -1.</_> + <_>7 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9482020181603730e-004</threshold> + <left_val>0.4494963884353638</left_val> + <right_val>0.5628852248191834</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 3 4 -1.</_> + <_>13 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0103126596659422</threshold> + <left_val>0.5177510976791382</left_val> + <right_val>0.2704316973686218</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 6 1 -1.</_> + <_>7 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7241109684109688e-003</threshold> + <left_val>0.1988019049167633</left_val> + <right_val>0.4980553984642029</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 3 2 -1.</_> + <_>12 8 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6797208487987518e-003</threshold> + <left_val>0.6644750237464905</left_val> + <right_val>0.5018296241760254</right_val></_></_> + <_> + <!-- tree 140 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 4 6 -1.</_> + <_>0 4 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0755459815263748e-003</threshold> + <left_val>0.3898304998874664</left_val> + <right_val>0.5185269117355347</right_val></_></_> + <_> + <!-- tree 141 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 3 2 -1.</_> + <_>12 8 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2479740437120199e-003</threshold> + <left_val>0.4801808893680573</left_val> + <right_val>0.5660336017608643</right_val></_></_> + <_> + <!-- tree 142 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 3 3 -1.</_> + <_>2 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3327008178457618e-004</threshold> + <left_val>0.5210919976234436</left_val> + <right_val>0.3957188129425049</right_val></_></_> + <_> + <!-- tree 143 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 8 6 10 -1.</_> + <_>16 8 3 5 2.</_> + <_>13 13 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0412793308496475</threshold> + <left_val>0.6154541969299316</left_val> + <right_val>0.5007054209709168</right_val></_></_> + <_> + <!-- tree 144 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 5 2 -1.</_> + <_>0 10 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0930189900100231e-004</threshold> + <left_val>0.3975942134857178</left_val> + <right_val>0.5228403806686401</right_val></_></_> + <_> + <!-- tree 145 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 2 2 -1.</_> + <_>13 11 1 1 2.</_> + <_>12 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2568780221045017e-003</threshold> + <left_val>0.4979138076305389</left_val> + <right_val>0.5939183235168457</right_val></_></_> + <_> + <!-- tree 146 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 3 3 -1.</_> + <_>3 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0048497766256332e-003</threshold> + <left_val>0.4984497129917145</left_val> + <right_val>0.1633366048336029</right_val></_></_> + <_> + <!-- tree 147 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 3 2 -1.</_> + <_>12 8 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1879300000146031e-003</threshold> + <left_val>0.5904964804649353</left_val> + <right_val>0.4942624866962433</right_val></_></_> + <_> + <!-- tree 148 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 3 2 -1.</_> + <_>5 8 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1948952497914433e-004</threshold> + <left_val>0.4199557900428772</left_val> + <right_val>0.5328726172447205</right_val></_></_> + <_> + <!-- tree 149 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 9 9 -1.</_> + <_>9 8 9 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6829859279096127e-003</threshold> + <left_val>0.5418602824211121</left_val> + <right_val>0.4905889034271240</right_val></_></_> + <_> + <!-- tree 150 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 3 7 -1.</_> + <_>6 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7062340416014194e-003</threshold> + <left_val>0.3725939095020294</left_val> + <right_val>0.5138000249862671</right_val></_></_> + <_> + <!-- tree 151 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 12 5 -1.</_> + <_>9 2 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0397394113242626</threshold> + <left_val>0.6478961110115051</left_val> + <right_val>0.5050346851348877</right_val></_></_> + <_> + <!-- tree 152 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 2 2 -1.</_> + <_>6 11 1 1 2.</_> + <_>7 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4085009461268783e-003</threshold> + <left_val>0.4682339131832123</left_val> + <right_val>0.6377884149551392</right_val></_></_> + <_> + <!-- tree 153 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 15 3 2 -1.</_> + <_>15 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9322688826359808e-004</threshold> + <left_val>0.5458530187606812</left_val> + <right_val>0.4150482118129730</right_val></_></_> + <_> + <!-- tree 154 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 3 2 -1.</_> + <_>2 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8979819724336267e-003</threshold> + <left_val>0.3690159916877747</left_val> + <right_val>0.5149704217910767</right_val></_></_> + <_> + <!-- tree 155 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 6 8 -1.</_> + <_>17 12 3 4 2.</_> + <_>14 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0139704402536154</threshold> + <left_val>0.6050562858581543</left_val> + <right_val>0.4811357855796814</right_val></_></_> + <_> + <!-- tree 156 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 15 6 -1.</_> + <_>7 8 5 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1010081991553307</threshold> + <left_val>0.2017080038785934</left_val> + <right_val>0.4992361962795258</right_val></_></_> + <_> + <!-- tree 157 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 18 17 -1.</_> + <_>8 2 6 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0173469204455614</threshold> + <left_val>0.5713148713111877</left_val> + <right_val>0.4899486005306244</right_val></_></_> + <_> + <!-- tree 158 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 4 1 -1.</_> + <_>7 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5619759506080300e-004</threshold> + <left_val>0.4215388894081116</left_val> + <right_val>0.5392642021179199</right_val></_></_> + <_> + <!-- tree 159 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 12 5 -1.</_> + <_>9 2 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1343892961740494</threshold> + <left_val>0.5136151909828186</left_val> + <right_val>0.3767612874507904</right_val></_></_> + <_> + <!-- tree 160 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 12 5 -1.</_> + <_>7 2 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0245822407305241</threshold> + <left_val>0.7027357816696167</left_val> + <right_val>0.4747906923294067</right_val></_></_> + <_> + <!-- tree 161 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 12 4 -1.</_> + <_>10 9 6 2 2.</_> + <_>4 11 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8553720805794001e-003</threshold> + <left_val>0.4317409098148346</left_val> + <right_val>0.5427716970443726</right_val></_></_> + <_> + <!-- tree 162 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 15 6 2 -1.</_> + <_>5 15 3 1 2.</_> + <_>8 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3165249731391668e-003</threshold> + <left_val>0.5942698717117310</left_val> + <right_val>0.4618647992610931</right_val></_></_> + <_> + <!-- tree 163 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 2 3 -1.</_> + <_>10 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8518120311200619e-003</threshold> + <left_val>0.6191568970680237</left_val> + <right_val>0.4884895086288452</right_val></_></_> + <_> + <!-- tree 164 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 20 2 -1.</_> + <_>0 13 10 1 2.</_> + <_>10 14 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4699938949197531e-003</threshold> + <left_val>0.5256664752960205</left_val> + <right_val>0.4017199873924255</right_val></_></_> + <_> + <!-- tree 165 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 12 8 -1.</_> + <_>10 9 6 4 2.</_> + <_>4 13 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0454969592392445</threshold> + <left_val>0.5237867832183838</left_val> + <right_val>0.2685773968696594</right_val></_></_> + <_> + <!-- tree 166 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 3 6 -1.</_> + <_>8 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0203195996582508</threshold> + <left_val>0.2130445986986160</left_val> + <right_val>0.4979738891124725</right_val></_></_> + <_> + <!-- tree 167 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 2 2 -1.</_> + <_>10 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6994998916052282e-004</threshold> + <left_val>0.4814041852951050</left_val> + <right_val>0.5543122291564941</right_val></_></_> + <_> + <!-- tree 168 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 2 -1.</_> + <_>9 12 1 1 2.</_> + <_>10 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8232699949294329e-003</threshold> + <left_val>0.6482579708099365</left_val> + <right_val>0.4709989130496979</right_val></_></_> + <_> + <!-- tree 169 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 14 4 -1.</_> + <_>11 11 7 2 2.</_> + <_>4 13 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3015790656208992e-003</threshold> + <left_val>0.4581927955150604</left_val> + <right_val>0.5306236147880554</right_val></_></_> + <_> + <!-- tree 170 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 4 2 -1.</_> + <_>8 6 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4139499873854220e-004</threshold> + <left_val>0.5232086777687073</left_val> + <right_val>0.4051763117313385</right_val></_></_> + <_> + <!-- tree 171 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 10 6 3 -1.</_> + <_>12 10 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0330369696021080e-003</threshold> + <left_val>0.5556201934814453</left_val> + <right_val>0.4789193868637085</right_val></_></_> + <_> + <!-- tree 172 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 14 1 2 -1.</_> + <_>2 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8041160365100950e-004</threshold> + <left_val>0.5229442715644836</left_val> + <right_val>0.4011810123920441</right_val></_></_> + <_> + <!-- tree 173 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 8 6 12 -1.</_> + <_>16 8 3 6 2.</_> + <_>13 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0614078603684902</threshold> + <left_val>0.6298682093620300</left_val> + <right_val>0.5010703206062317</right_val></_></_> + <_> + <!-- tree 174 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 6 12 -1.</_> + <_>1 8 3 6 2.</_> + <_>4 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0695439130067825</threshold> + <left_val>0.7228280901908875</left_val> + <right_val>0.4773184061050415</right_val></_></_> + <_> + <!-- tree 175 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 6 10 -1.</_> + <_>12 0 2 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0705426633358002</threshold> + <left_val>0.2269513010978699</left_val> + <right_val>0.5182529091835022</right_val></_></_> + <_> + <!-- tree 176 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 8 4 -1.</_> + <_>5 11 4 2 2.</_> + <_>9 13 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4423799477517605e-003</threshold> + <left_val>0.5237097144126892</left_val> + <right_val>0.4098151028156281</right_val></_></_> + <_> + <!-- tree 177 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 16 8 4 -1.</_> + <_>14 16 4 2 2.</_> + <_>10 18 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5494349645450711e-003</threshold> + <left_val>0.4773750901222229</left_val> + <right_val>0.5468043088912964</right_val></_></_> + <_> + <!-- tree 178 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 6 -1.</_> + <_>9 7 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0239142198115587</threshold> + <left_val>0.7146975994110107</left_val> + <right_val>0.4783824980258942</right_val></_></_> + <_> + <!-- tree 179 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 2 4 10 -1.</_> + <_>10 2 2 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0124536901712418</threshold> + <left_val>0.2635296881198883</left_val> + <right_val>0.5241122841835022</right_val></_></_> + <_> + <!-- tree 180 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 4 9 -1.</_> + <_>8 1 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0760179904755205e-004</threshold> + <left_val>0.3623757064342499</left_val> + <right_val>0.5113608837127686</right_val></_></_> + <_> + <!-- tree 181 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 19 2 1 -1.</_> + <_>12 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9781080229440704e-005</threshold> + <left_val>0.4705932140350342</left_val> + <right_val>0.5432801842689514</right_val></_></_></trees> + <stage_threshold>90.2533493041992190</stage_threshold> + <parent>18</parent> + <next>-1</next></_> + <_> + <!-- stage 20 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 4 9 -1.</_> + <_>3 2 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0117727499455214</threshold> + <left_val>0.3860518932342529</left_val> + <right_val>0.6421167254447937</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 4 -1.</_> + <_>9 5 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0270375702530146</threshold> + <left_val>0.4385654926300049</left_val> + <right_val>0.6754038929939270</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 4 -1.</_> + <_>9 6 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6419500247575343e-005</threshold> + <left_val>0.5487101078033447</left_val> + <right_val>0.3423315882682800</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 5 2 8 -1.</_> + <_>14 9 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9995409529656172e-003</threshold> + <left_val>0.3230532109737396</left_val> + <right_val>0.5400317907333374</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 5 12 -1.</_> + <_>7 12 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5278300531208515e-003</threshold> + <left_val>0.5091639757156372</left_val> + <right_val>0.2935043871402741</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 2 6 -1.</_> + <_>14 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7890920541249216e-004</threshold> + <left_val>0.4178153872489929</left_val> + <right_val>0.5344064235687256</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 2 6 -1.</_> + <_>4 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1720920447260141e-003</threshold> + <left_val>0.2899182140827179</left_val> + <right_val>0.5132070779800415</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 10 4 -1.</_> + <_>13 15 5 2 2.</_> + <_>8 17 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.5305702416226268e-004</threshold> + <left_val>0.4280124902725220</left_val> + <right_val>0.5560845136642456</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 18 2 2 -1.</_> + <_>7 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5099150004971307e-005</threshold> + <left_val>0.4044871926307678</left_val> + <right_val>0.5404760241508484</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 6 2 -1.</_> + <_>11 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0817901976406574e-004</threshold> + <left_val>0.4271768927574158</left_val> + <right_val>0.5503466129302979</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 16 6 -1.</_> + <_>2 2 16 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3224520739167929e-003</threshold> + <left_val>0.3962723910808563</left_val> + <right_val>0.5369734764099121</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 6 2 -1.</_> + <_>11 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1037490330636501e-003</threshold> + <left_val>0.4727177917957306</left_val> + <right_val>0.5237749814987183</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 10 3 -1.</_> + <_>4 12 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4350269921123981e-003</threshold> + <left_val>0.5603008270263672</left_val> + <right_val>0.4223509132862091</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 6 2 -1.</_> + <_>11 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0767399109899998e-003</threshold> + <left_val>0.5225917100906372</left_val> + <right_val>0.4732725918292999</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 6 2 -1.</_> + <_>3 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6412809782195836e-004</threshold> + <left_val>0.3999075889587402</left_val> + <right_val>0.5432739853858948</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 7 -1.</_> + <_>16 0 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8302437216043472e-003</threshold> + <left_val>0.4678385853767395</left_val> + <right_val>0.6027327179908752</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 9 6 -1.</_> + <_>0 16 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0105520701035857</threshold> + <left_val>0.3493967056274414</left_val> + <right_val>0.5213974714279175</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 3 -1.</_> + <_>9 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2731600329279900e-003</threshold> + <left_val>0.6185818910598755</left_val> + <right_val>0.4749062955379486</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 6 2 -1.</_> + <_>6 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4786332445219159e-004</threshold> + <left_val>0.5285341143608093</left_val> + <right_val>0.3843482136726379</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 1 3 -1.</_> + <_>15 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2081359745934606e-003</threshold> + <left_val>0.5360640883445740</left_val> + <right_val>0.3447335958480835</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6512730401009321e-003</threshold> + <left_val>0.4558292031288147</left_val> + <right_val>0.6193962097167969</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 2 2 -1.</_> + <_>10 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1012479662895203e-003</threshold> + <left_val>0.3680230081081390</left_val> + <right_val>0.5327628254890442</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 4 3 -1.</_> + <_>5 1 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9561518244445324e-004</threshold> + <left_val>0.3960595130920410</left_val> + <right_val>0.5274940729141235</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 7 -1.</_> + <_>16 0 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0439017713069916</threshold> + <left_val>0.7020444869995117</left_val> + <right_val>0.4992839097976685</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 20 1 -1.</_> + <_>10 0 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0346903502941132</threshold> + <left_val>0.5049164295196533</left_val> + <right_val>0.2766602933406830</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 1 3 -1.</_> + <_>15 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7442190330475569e-003</threshold> + <left_val>0.2672632932662964</left_val> + <right_val>0.5274971127510071</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 3 4 -1.</_> + <_>1 4 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3316588960587978e-003</threshold> + <left_val>0.4579482972621918</left_val> + <right_val>0.6001101732254028</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 3 6 -1.</_> + <_>16 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0200445707887411</threshold> + <left_val>0.3171594142913818</left_val> + <right_val>0.5235717892646790</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 3 6 -1.</_> + <_>1 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3492030557245016e-003</threshold> + <left_val>0.5265362858772278</left_val> + <right_val>0.4034324884414673</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 12 6 -1.</_> + <_>12 2 6 3 2.</_> + <_>6 5 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9702018946409225e-003</threshold> + <left_val>0.5332456827163696</left_val> + <right_val>0.4571984112262726</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 4 3 -1.</_> + <_>8 11 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3039981760084629e-003</threshold> + <left_val>0.4593310952186585</left_val> + <right_val>0.6034635901451111</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 14 6 -1.</_> + <_>11 2 7 3 2.</_> + <_>4 5 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0129365902394056</threshold> + <left_val>0.4437963962554932</left_val> + <right_val>0.5372971296310425</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0148729458451271e-003</threshold> + <left_val>0.4680323898792267</left_val> + <right_val>0.6437833905220032</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 3 -1.</_> + <_>15 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6401679497212172e-003</threshold> + <left_val>0.3709631860256195</left_val> + <right_val>0.5314332842826843</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 3 -1.</_> + <_>8 13 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0139184398576617</threshold> + <left_val>0.4723555147647858</left_val> + <right_val>0.7130808830261231</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 1 3 -1.</_> + <_>15 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5087869511917233e-004</threshold> + <left_val>0.4492394030094147</left_val> + <right_val>0.5370404124259949</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 5 2 -1.</_> + <_>7 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5384349282830954e-004</threshold> + <left_val>0.4406864047050476</left_val> + <right_val>0.5514402985572815</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 3 -1.</_> + <_>7 13 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2710000630468130e-003</threshold> + <left_val>0.4682416915893555</left_val> + <right_val>0.5967984199523926</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 4 4 -1.</_> + <_>5 13 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4120779708027840e-003</threshold> + <left_val>0.5079392194747925</left_val> + <right_val>0.3018598854541779</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 3 3 -1.</_> + <_>12 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6025670851813629e-005</threshold> + <left_val>0.5601037144660950</left_val> + <right_val>0.4471096992492676</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 3 3 -1.</_> + <_>7 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4905529618263245e-003</threshold> + <left_val>0.2207535058259964</left_val> + <right_val>0.4989944100379944</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 5 3 6 -1.</_> + <_>17 5 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0175131205469370</threshold> + <left_val>0.6531215906143189</left_val> + <right_val>0.5017648935317993</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 12 7 -1.</_> + <_>7 6 4 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1428163051605225</threshold> + <left_val>0.4967963099479675</left_val> + <right_val>0.1482062041759491</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 5 3 6 -1.</_> + <_>17 5 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5345268920063972e-003</threshold> + <left_val>0.4898946881294251</left_val> + <right_val>0.5954223871231079</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 2 3 -1.</_> + <_>3 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.6323591424152255e-004</threshold> + <left_val>0.3927116990089417</left_val> + <right_val>0.5196074247360230</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 5 3 6 -1.</_> + <_>17 5 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0370010752230883e-003</threshold> + <left_val>0.5613325238227844</left_val> + <right_val>0.4884858131408691</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 3 6 -1.</_> + <_>2 5 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6614829655736685e-003</threshold> + <left_val>0.4472880065441132</left_val> + <right_val>0.5578880906105042</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 18 1 -1.</_> + <_>7 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1188090797513723e-003</threshold> + <left_val>0.3840532898902893</left_val> + <right_val>0.5397477746009827</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 8 7 -1.</_> + <_>4 9 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4000617712736130e-003</threshold> + <left_val>0.5843983888626099</left_val> + <right_val>0.4533218145370483</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 8 2 -1.</_> + <_>12 12 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1319601112045348e-004</threshold> + <left_val>0.5439221858978272</left_val> + <right_val>0.4234727919101715</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 8 2 -1.</_> + <_>0 12 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0182220991700888</threshold> + <left_val>0.1288464963436127</left_val> + <right_val>0.4958404898643494</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 3 -1.</_> + <_>9 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7969247251749039e-003</threshold> + <left_val>0.4951297938823700</left_val> + <right_val>0.7153480052947998</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 12 4 -1.</_> + <_>4 10 6 2 2.</_> + <_>10 12 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2395070195198059e-003</threshold> + <left_val>0.3946599960327148</left_val> + <right_val>0.5194936990737915</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 3 7 -1.</_> + <_>10 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7086271271109581e-003</threshold> + <left_val>0.4897503852844238</left_val> + <right_val>0.6064900159835815</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 3 5 -1.</_> + <_>8 2 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9934171363711357e-003</threshold> + <left_val>0.3245440125465393</left_val> + <right_val>0.5060828924179077</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 4 6 -1.</_> + <_>11 12 2 3 2.</_> + <_>9 15 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0167850591242313</threshold> + <left_val>0.1581953018903732</left_val> + <right_val>0.5203778743743897</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0182720907032490</threshold> + <left_val>0.4680935144424439</left_val> + <right_val>0.6626979112625122</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 4 4 2 -1.</_> + <_>15 5 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6872838176786900e-003</threshold> + <left_val>0.5211697816848755</left_val> + <right_val>0.3512184917926788</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0739039862528443e-003</threshold> + <left_val>0.5768386125564575</left_val> + <right_val>0.4529845118522644</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 4 -1.</_> + <_>14 4 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7093870341777802e-003</threshold> + <left_val>0.4507763087749481</left_val> + <right_val>0.5313581228256226</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 16 6 1 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1110709349159151e-004</threshold> + <left_val>0.5460820198059082</left_val> + <right_val>0.4333376884460449</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 2 3 -1.</_> + <_>15 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0670139454305172e-003</threshold> + <left_val>0.5371856093406677</left_val> + <right_val>0.4078390896320343</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 10 -1.</_> + <_>9 7 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5943021066486835e-003</threshold> + <left_val>0.4471287131309509</left_val> + <right_val>0.5643836259841919</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 2 6 -1.</_> + <_>11 12 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1776031032204628e-003</threshold> + <left_val>0.4499393105506897</left_val> + <right_val>0.5280330181121826</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 4 1 -1.</_> + <_>8 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5414369883947074e-004</threshold> + <left_val>0.5516173243522644</left_val> + <right_val>0.4407708048820496</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 2 2 -1.</_> + <_>10 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3522560521960258e-003</threshold> + <left_val>0.5194190144538879</left_val> + <right_val>0.2465227991342545</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 2 2 -1.</_> + <_>8 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4205080484971404e-004</threshold> + <left_val>0.3830705881118774</left_val> + <right_val>0.5139682292938232</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 2 2 -1.</_> + <_>13 7 1 1 2.</_> + <_>12 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4488727841526270e-004</threshold> + <left_val>0.4891090989112854</left_val> + <right_val>0.5974786877632141</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 2 -1.</_> + <_>5 7 1 1 2.</_> + <_>6 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5116379149258137e-003</threshold> + <left_val>0.7413681745529175</left_val> + <right_val>0.4768764972686768</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 14 -1.</_> + <_>14 0 1 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0125409103929996</threshold> + <left_val>0.3648819029331207</left_val> + <right_val>0.5252826809883118</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 3 14 -1.</_> + <_>5 0 1 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4931852072477341e-003</threshold> + <left_val>0.5100492835044861</left_val> + <right_val>0.3629586994647980</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 3 14 -1.</_> + <_>14 4 1 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0129611501470208</threshold> + <left_val>0.5232442021369934</left_val> + <right_val>0.4333561062812805</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7209449112415314e-003</threshold> + <left_val>0.4648149013519287</left_val> + <right_val>0.6331052780151367</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3119079414755106e-003</threshold> + <left_val>0.5930309891700745</left_val> + <right_val>0.4531058073043823</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 3 16 -1.</_> + <_>5 2 1 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8262299019843340e-003</threshold> + <left_val>0.3870477974414825</left_val> + <right_val>0.5257101058959961</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 8 10 -1.</_> + <_>7 7 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4311339473351836e-003</threshold> + <left_val>0.5522503256797791</left_val> + <right_val>0.4561854898929596</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 7 3 -1.</_> + <_>6 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9378310535103083e-003</threshold> + <left_val>0.4546220898628235</left_val> + <right_val>0.5736966729164124</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 10 12 -1.</_> + <_>14 2 5 6 2.</_> + <_>9 8 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6343559147790074e-004</threshold> + <left_val>0.5345739126205444</left_val> + <right_val>0.4571875035762787</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 8 2 -1.</_> + <_>6 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8257522545754910e-004</threshold> + <left_val>0.3967815935611725</left_val> + <right_val>0.5220187902450562</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 6 -1.</_> + <_>8 16 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0195504408329725</threshold> + <left_val>0.2829642891883850</left_val> + <right_val>0.5243508219718933</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 3 -1.</_> + <_>6 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3914958951063454e-004</threshold> + <left_val>0.4590066969394684</left_val> + <right_val>0.5899090170860291</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 4 6 -1.</_> + <_>16 4 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0214520003646612</threshold> + <left_val>0.5231410861015320</left_val> + <right_val>0.2855378985404968</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 4 2 -1.</_> + <_>6 6 2 1 2.</_> + <_>8 7 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8973580598831177e-004</threshold> + <left_val>0.4397256970405579</left_val> + <right_val>0.5506421923637390</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 4 6 -1.</_> + <_>16 4 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0261576101183891</threshold> + <left_val>0.3135079145431519</left_val> + <right_val>0.5189175009727478</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 4 6 -1.</_> + <_>0 4 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0139598604291677</threshold> + <left_val>0.3213272988796234</left_val> + <right_val>0.5040717720985413</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 6 -1.</_> + <_>9 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3699018210172653e-003</threshold> + <left_val>0.6387544870376587</left_val> + <right_val>0.4849506914615631</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 6 10 -1.</_> + <_>3 9 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5613820701837540e-003</threshold> + <left_val>0.2759132087230682</left_val> + <right_val>0.5032019019126892</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>9 5 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6622901037335396e-004</threshold> + <left_val>0.4685640931129456</left_val> + <right_val>0.5834879279136658</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 2 3 -1.</_> + <_>3 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6550268568098545e-004</threshold> + <left_val>0.5175207257270813</left_val> + <right_val>0.3896422088146210</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 3 2 -1.</_> + <_>13 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1833340227603912e-003</threshold> + <left_val>0.2069136947393417</left_val> + <right_val>0.5208122134208679</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 10 4 -1.</_> + <_>2 16 5 2 2.</_> + <_>7 18 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3976939097046852e-003</threshold> + <left_val>0.6134091019630432</left_val> + <right_val>0.4641222953796387</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 10 6 -1.</_> + <_>10 6 5 3 2.</_> + <_>5 9 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8028980381786823e-003</threshold> + <left_val>0.5454108119010925</left_val> + <right_val>0.4395219981670380</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 1 3 -1.</_> + <_>7 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5680569708347321e-003</threshold> + <left_val>0.6344485282897949</left_val> + <right_val>0.4681093990802765</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 16 6 3 -1.</_> + <_>14 17 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0733120404183865e-003</threshold> + <left_val>0.5292683243751526</left_val> + <right_val>0.4015620052814484</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2568129459396005e-003</threshold> + <left_val>0.4392988085746765</left_val> + <right_val>0.5452824831008911</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 10 3 -1.</_> + <_>7 5 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9065010603517294e-003</threshold> + <left_val>0.5898832082748413</left_val> + <right_val>0.4863379895687103</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 5 4 -1.</_> + <_>0 6 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4409340694546700e-003</threshold> + <left_val>0.4069364964962006</left_val> + <right_val>0.5247421860694885</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 9 -1.</_> + <_>13 14 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0248307008296251</threshold> + <left_val>0.5182725787162781</left_val> + <right_val>0.3682524859905243</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 3 9 -1.</_> + <_>4 14 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0488540083169937</threshold> + <left_val>0.1307577937841415</left_val> + <right_val>0.4961281120777130</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 1 -1.</_> + <_>9 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6110379947349429e-003</threshold> + <left_val>0.6421005725860596</left_val> + <right_val>0.4872662127017975</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 6 17 -1.</_> + <_>7 0 2 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0970094799995422</threshold> + <left_val>0.0477693490684032</left_val> + <right_val>0.4950988888740540</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 6 3 -1.</_> + <_>10 3 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1209240183234215e-003</threshold> + <left_val>0.4616267085075378</left_val> + <right_val>0.5354745984077454</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 15 4 -1.</_> + <_>7 2 5 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3064090162515640e-003</threshold> + <left_val>0.6261854171752930</left_val> + <right_val>0.4638805985450745</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 8 2 -1.</_> + <_>12 2 4 1 2.</_> + <_>8 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5771620352752507e-004</threshold> + <left_val>0.5384417772293091</left_val> + <right_val>0.4646640121936798</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 6 -1.</_> + <_>8 3 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3149951165542006e-004</threshold> + <left_val>0.3804047107696533</left_val> + <right_val>0.5130257010459900</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 2 2 -1.</_> + <_>9 18 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4505970466416329e-004</threshold> + <left_val>0.4554310142993927</left_val> + <right_val>0.5664461851119995</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 14 -1.</_> + <_>1 0 1 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0164745505899191</threshold> + <left_val>0.6596958041191101</left_val> + <right_val>0.4715859889984131</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 7 3 -1.</_> + <_>12 1 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0133695797994733</threshold> + <left_val>0.5195466279983521</left_val> + <right_val>0.3035964965820313</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 1 2 -1.</_> + <_>1 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0271780047332868e-004</threshold> + <left_val>0.5229176282882690</left_val> + <right_val>0.4107066094875336</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 8 -1.</_> + <_>15 12 1 4 2.</_> + <_>14 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5311559699475765e-003</threshold> + <left_val>0.6352887749671936</left_val> + <right_val>0.4960907101631165</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 7 3 -1.</_> + <_>1 1 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6187049224972725e-003</threshold> + <left_val>0.3824546039104462</left_val> + <right_val>0.5140984058380127</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 8 -1.</_> + <_>15 12 1 4 2.</_> + <_>14 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0834268331527710e-003</threshold> + <left_val>0.4950439929962158</left_val> + <right_val>0.6220818758010864</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>6 0 4 6 2.</_> + <_>10 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0798181593418121</threshold> + <left_val>0.4952335953712463</left_val> + <right_val>0.1322475969791412</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 8 9 -1.</_> + <_>6 4 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0992265865206718</threshold> + <left_val>0.7542728781700134</left_val> + <right_val>0.5008416771888733</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 2 2 -1.</_> + <_>5 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5174017800018191e-004</threshold> + <left_val>0.3699302971363068</left_val> + <right_val>0.5130121111869812</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 6 6 -1.</_> + <_>16 14 3 3 2.</_> + <_>13 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0189968496561050</threshold> + <left_val>0.6689178943634033</left_val> + <right_val>0.4921202957630158</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 20 2 -1.</_> + <_>0 17 10 1 2.</_> + <_>10 18 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0173468999564648</threshold> + <left_val>0.4983300864696503</left_val> + <right_val>0.1859198063611984</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 2 6 -1.</_> + <_>11 3 1 3 2.</_> + <_>10 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5082101607695222e-004</threshold> + <left_val>0.4574424028396606</left_val> + <right_val>0.5522121787071228</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 6 2 -1.</_> + <_>8 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0056050270795822e-003</threshold> + <left_val>0.5131744742393494</left_val> + <right_val>0.3856469988822937</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 6 13 -1.</_> + <_>10 7 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7688191086053848e-003</threshold> + <left_val>0.4361700117588043</left_val> + <right_val>0.5434309244155884</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 15 10 5 -1.</_> + <_>10 15 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0508782789111137</threshold> + <left_val>0.4682720899581909</left_val> + <right_val>0.6840639710426331</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 4 4 10 -1.</_> + <_>10 4 2 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2901780903339386e-003</threshold> + <left_val>0.4329245090484619</left_val> + <right_val>0.5306099057197571</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 1 -1.</_> + <_>6 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5715380141045898e-004</threshold> + <left_val>0.5370057225227356</left_val> + <right_val>0.4378164112567902</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 6 7 -1.</_> + <_>10 3 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1051924005150795</threshold> + <left_val>0.5137274265289307</left_val> + <right_val>0.0673614665865898</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 7 -1.</_> + <_>7 3 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7198919560760260e-003</threshold> + <left_val>0.4112060964107513</left_val> + <right_val>0.5255665183067322</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 18 5 -1.</_> + <_>7 7 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0483377799391747</threshold> + <left_val>0.5404623746871948</left_val> + <right_val>0.4438967108726502</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 17 4 3 -1.</_> + <_>5 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.5703761326149106e-004</threshold> + <left_val>0.4355969130992889</left_val> + <right_val>0.5399510860443115</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 12 6 -1.</_> + <_>14 14 6 3 2.</_> + <_>8 17 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0253712590783834</threshold> + <left_val>0.5995175242424011</left_val> + <right_val>0.5031024813652039</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 20 4 -1.</_> + <_>0 13 10 2 2.</_> + <_>10 15 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0524579510092735</threshold> + <left_val>0.4950287938117981</left_val> + <right_val>0.1398351043462753</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 14 2 -1.</_> + <_>11 5 7 1 2.</_> + <_>4 6 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0123656298965216</threshold> + <left_val>0.6397299170494080</left_val> + <right_val>0.4964106082916260</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 10 12 -1.</_> + <_>1 2 5 6 2.</_> + <_>6 8 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1458971947431564</threshold> + <left_val>0.1001669988036156</left_val> + <right_val>0.4946322143077850</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 14 3 -1.</_> + <_>6 2 14 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0159086007624865</threshold> + <left_val>0.3312329947948456</left_val> + <right_val>0.5208340883255005</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 2 3 -1.</_> + <_>8 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9486068999394774e-004</threshold> + <left_val>0.4406363964080811</left_val> + <right_val>0.5426102876663208</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2454001270234585e-003</threshold> + <left_val>0.2799589931964874</left_val> + <right_val>0.5189967155456543</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 15 4 2 -1.</_> + <_>5 15 2 1 2.</_> + <_>7 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0421799533069134e-003</threshold> + <left_val>0.6987580060958862</left_val> + <right_val>0.4752142131328583</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 1 3 -1.</_> + <_>10 16 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9812189750373363e-003</threshold> + <left_val>0.4983288943767548</left_val> + <right_val>0.6307479739189148</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 4 4 -1.</_> + <_>8 16 2 2 2.</_> + <_>10 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2884308174252510e-003</threshold> + <left_val>0.2982333004474640</left_val> + <right_val>0.5026869773864746</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 11 8 6 -1.</_> + <_>6 14 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5094350092113018e-003</threshold> + <left_val>0.5308442115783691</left_val> + <right_val>0.3832970857620239</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 2 -1.</_> + <_>2 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3340799212455750e-003</threshold> + <left_val>0.2037964016199112</left_val> + <right_val>0.4969817101955414</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 6 6 -1.</_> + <_>16 14 3 3 2.</_> + <_>13 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0286671407520771</threshold> + <left_val>0.5025696754455566</left_val> + <right_val>0.6928027272224426</right_val></_></_> + <_> + <!-- tree 140 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 18 4 -1.</_> + <_>7 9 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1701968014240265</threshold> + <left_val>0.4960052967071533</left_val> + <right_val>0.1476442962884903</right_val></_></_> + <_> + <!-- tree 141 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 6 6 -1.</_> + <_>16 14 3 3 2.</_> + <_>13 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2614478841423988e-003</threshold> + <left_val>0.5603063702583313</left_val> + <right_val>0.4826056063175201</right_val></_></_> + <_> + <!-- tree 142 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 1 6 -1.</_> + <_>0 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5769277969375253e-004</threshold> + <left_val>0.5205562114715576</left_val> + <right_val>0.4129633009433746</right_val></_></_> + <_> + <!-- tree 143 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 15 20 -1.</_> + <_>5 10 15 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.3625833988189697</threshold> + <left_val>0.5221652984619141</left_val> + <right_val>0.3768612146377564</right_val></_></_> + <_> + <!-- tree 144 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 6 6 -1.</_> + <_>1 14 3 3 2.</_> + <_>4 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116151301190257</threshold> + <left_val>0.6022682785987854</left_val> + <right_val>0.4637489914894104</right_val></_></_> + <_> + <!-- tree 145 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 6 -1.</_> + <_>10 14 2 3 2.</_> + <_>8 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0795197710394859e-003</threshold> + <left_val>0.4070447087287903</left_val> + <right_val>0.5337479114532471</right_val></_></_> + <_> + <!-- tree 146 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 2 1 -1.</_> + <_>8 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7204300537705421e-004</threshold> + <left_val>0.4601835012435913</left_val> + <right_val>0.5900393128395081</right_val></_></_> + <_> + <!-- tree 147 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7543348995968699e-004</threshold> + <left_val>0.5398252010345459</left_val> + <right_val>0.4345428943634033</right_val></_></_> + <_> + <!-- tree 148 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3295697327703238e-004</threshold> + <left_val>0.5201563239097595</left_val> + <right_val>0.4051358997821808</right_val></_></_> + <_> + <!-- tree 149 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 14 4 6 -1.</_> + <_>14 14 2 3 2.</_> + <_>12 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2435320531949401e-003</threshold> + <left_val>0.4642387926578522</left_val> + <right_val>0.5547441244125366</right_val></_></_> + <_> + <!-- tree 150 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 4 6 -1.</_> + <_>4 14 2 3 2.</_> + <_>6 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7363857738673687e-003</threshold> + <left_val>0.6198567152023315</left_val> + <right_val>0.4672552049160004</right_val></_></_> + <_> + <!-- tree 151 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 2 6 -1.</_> + <_>14 14 1 3 2.</_> + <_>13 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4658462069928646e-003</threshold> + <left_val>0.6837332844734192</left_val> + <right_val>0.5019000768661499</right_val></_></_> + <_> + <!-- tree 152 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 14 2 6 -1.</_> + <_>5 14 1 3 2.</_> + <_>6 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5017321351915598e-004</threshold> + <left_val>0.4344803094863892</left_val> + <right_val>0.5363622903823853</right_val></_></_> + <_> + <!-- tree 153 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 12 -1.</_> + <_>7 4 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5754920605104417e-004</threshold> + <left_val>0.4760079085826874</left_val> + <right_val>0.5732020735740662</right_val></_></_> + <_> + <!-- tree 154 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 12 2 -1.</_> + <_>4 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9774366244673729e-003</threshold> + <left_val>0.5090985894203186</left_val> + <right_val>0.3635039925575256</right_val></_></_> + <_> + <!-- tree 155 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 3 13 -1.</_> + <_>11 3 1 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1464529931545258e-004</threshold> + <left_val>0.5570064783096314</left_val> + <right_val>0.4593802094459534</right_val></_></_> + <_> + <!-- tree 156 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 3 13 -1.</_> + <_>8 3 1 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5888899583369493e-004</threshold> + <left_val>0.5356845855712891</left_val> + <right_val>0.4339134991168976</right_val></_></_> + <_> + <!-- tree 157 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 6 3 -1.</_> + <_>10 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0463250479660928e-004</threshold> + <left_val>0.4439803063869476</left_val> + <right_val>0.5436776876449585</right_val></_></_> + <_> + <!-- tree 158 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 3 2 -1.</_> + <_>4 11 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2184787606820464e-004</threshold> + <left_val>0.4042294919490814</left_val> + <right_val>0.5176299214363098</right_val></_></_> + <_> + <!-- tree 159 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 12 6 8 -1.</_> + <_>16 12 3 4 2.</_> + <_>13 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9467419050633907e-003</threshold> + <left_val>0.4927651882171631</left_val> + <right_val>0.5633779764175415</right_val></_></_> + <_> + <!-- tree 160 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 5 -1.</_> + <_>9 6 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0217533893883228</threshold> + <left_val>0.8006293773651123</left_val> + <right_val>0.4800840914249420</right_val></_></_> + <_> + <!-- tree 161 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 11 2 7 -1.</_> + <_>17 11 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0145403798669577</threshold> + <left_val>0.3946054875850678</left_val> + <right_val>0.5182222723960877</right_val></_></_> + <_> + <!-- tree 162 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 8 2 -1.</_> + <_>7 13 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0405107699334621</threshold> + <left_val>0.0213249903172255</left_val> + <right_val>0.4935792982578278</right_val></_></_> + <_> + <!-- tree 163 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 8 3 -1.</_> + <_>6 10 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8458268176764250e-004</threshold> + <left_val>0.4012795984745026</left_val> + <right_val>0.5314025282859802</right_val></_></_> + <_> + <!-- tree 164 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 4 3 -1.</_> + <_>4 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5151800625026226e-003</threshold> + <left_val>0.4642418920993805</left_val> + <right_val>0.5896260738372803</right_val></_></_> + <_> + <!-- tree 165 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 4 3 -1.</_> + <_>11 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0626221820712090e-003</threshold> + <left_val>0.6502159237861633</left_val> + <right_val>0.5016477704048157</right_val></_></_> + <_> + <!-- tree 166 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 17 12 -1.</_> + <_>1 8 17 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0945358425378799</threshold> + <left_val>0.5264708995819092</left_val> + <right_val>0.4126827120780945</right_val></_></_> + <_> + <!-- tree 167 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 4 3 -1.</_> + <_>11 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7315051779150963e-003</threshold> + <left_val>0.4879199862480164</left_val> + <right_val>0.5892447829246521</right_val></_></_> + <_> + <!-- tree 168 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 6 3 -1.</_> + <_>4 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2571471314877272e-004</threshold> + <left_val>0.3917280137538910</left_val> + <right_val>0.5189412832260132</right_val></_></_> + <_> + <!-- tree 169 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 5 3 -1.</_> + <_>12 4 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5464049540460110e-003</threshold> + <left_val>0.5837599039077759</left_val> + <right_val>0.4985705912113190</right_val></_></_> + <_> + <!-- tree 170 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 2 7 -1.</_> + <_>2 11 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0260756891220808</threshold> + <left_val>0.1261983960866928</left_val> + <right_val>0.4955821931362152</right_val></_></_> + <_> + <!-- tree 171 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 12 2 8 -1.</_> + <_>16 12 1 4 2.</_> + <_>15 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4779709316790104e-003</threshold> + <left_val>0.5722513794898987</left_val> + <right_val>0.5010265707969666</right_val></_></_> + <_> + <!-- tree 172 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 11 3 -1.</_> + <_>4 9 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1337741315364838e-003</threshold> + <left_val>0.5273262262344360</left_val> + <right_val>0.4226376116275787</right_val></_></_> + <_> + <!-- tree 173 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 6 2 -1.</_> + <_>12 13 3 1 2.</_> + <_>9 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7944980906322598e-004</threshold> + <left_val>0.4450066983699799</left_val> + <right_val>0.5819587111473084</right_val></_></_> + <_> + <!-- tree 174 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 13 4 3 -1.</_> + <_>6 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1114079281687737e-003</threshold> + <left_val>0.5757653117179871</left_val> + <right_val>0.4511714875698090</right_val></_></_> + <_> + <!-- tree 175 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>10 12 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0131799904629588</threshold> + <left_val>0.1884381026029587</left_val> + <right_val>0.5160734057426453</right_val></_></_> + <_> + <!-- tree 176 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 3 -1.</_> + <_>5 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7968099825084209e-003</threshold> + <left_val>0.6589789986610413</left_val> + <right_val>0.4736118912696838</right_val></_></_> + <_> + <!-- tree 177 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 3 -1.</_> + <_>9 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7483168095350266e-003</threshold> + <left_val>0.5259429812431335</left_val> + <right_val>0.3356395065784454</right_val></_></_> + <_> + <!-- tree 178 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 16 3 -1.</_> + <_>0 3 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4623369788751006e-003</threshold> + <left_val>0.5355271100997925</left_val> + <right_val>0.4264092147350311</right_val></_></_> + <_> + <!-- tree 179 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 12 2 8 -1.</_> + <_>16 12 1 4 2.</_> + <_>15 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7645159065723419e-003</threshold> + <left_val>0.5034406781196594</left_val> + <right_val>0.5786827802658081</right_val></_></_> + <_> + <!-- tree 180 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 8 -1.</_> + <_>3 12 1 4 2.</_> + <_>4 16 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.8066660314798355e-003</threshold> + <left_val>0.4756605029106140</left_val> + <right_val>0.6677829027175903</right_val></_></_> + <_> + <!-- tree 181 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 13 3 6 -1.</_> + <_>14 15 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6608621012419462e-003</threshold> + <left_val>0.5369611978530884</left_val> + <right_val>0.4311546981334686</right_val></_></_> + <_> + <!-- tree 182 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 3 6 -1.</_> + <_>3 15 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0214496403932571</threshold> + <left_val>0.4968641996383667</left_val> + <right_val>0.1888816058635712</right_val></_></_> + <_> + <!-- tree 183 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 10 2 -1.</_> + <_>11 5 5 1 2.</_> + <_>6 6 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1678901761770248e-003</threshold> + <left_val>0.4930733144283295</left_val> + <right_val>0.5815368890762329</right_val></_></_> + <_> + <!-- tree 184 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 14 14 6 -1.</_> + <_>2 17 14 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6467564105987549e-003</threshold> + <left_val>0.5205205082893372</left_val> + <right_val>0.4132595062255859</right_val></_></_> + <_> + <!-- tree 185 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 1 3 -1.</_> + <_>10 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6114078829996288e-004</threshold> + <left_val>0.5483555197715759</left_val> + <right_val>0.4800927937030792</right_val></_></_> + <_> + <!-- tree 186 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 16 2 2 -1.</_> + <_>4 16 1 1 2.</_> + <_>5 17 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0808729566633701e-003</threshold> + <left_val>0.4689902067184448</left_val> + <right_val>0.6041421294212341</right_val></_></_> + <_> + <!-- tree 187 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 2 3 -1.</_> + <_>10 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7719959877431393e-003</threshold> + <left_val>0.5171142220497131</left_val> + <right_val>0.3053277134895325</right_val></_></_> + <_> + <!-- tree 188 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 20 2 -1.</_> + <_>0 17 10 1 2.</_> + <_>10 18 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5720770461484790e-003</threshold> + <left_val>0.5219978094100952</left_val> + <right_val>0.4178803861141205</right_val></_></_> + <_> + <!-- tree 189 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 1 3 -1.</_> + <_>13 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9307859474793077e-003</threshold> + <left_val>0.5860369801521301</left_val> + <right_val>0.4812920093536377</right_val></_></_> + <_> + <!-- tree 190 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 3 2 -1.</_> + <_>9 13 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8926272690296173e-003</threshold> + <left_val>0.1749276965856552</left_val> + <right_val>0.4971733987331390</right_val></_></_> + <_> + <!-- tree 191 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 2 3 3 -1.</_> + <_>13 2 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2224679123610258e-003</threshold> + <left_val>0.4342589080333710</left_val> + <right_val>0.5212848186492920</right_val></_></_> + <_> + <!-- tree 192 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 18 2 2 -1.</_> + <_>3 18 1 1 2.</_> + <_>4 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9011989934369922e-003</threshold> + <left_val>0.4765186905860901</left_val> + <right_val>0.6892055273056030</right_val></_></_> + <_> + <!-- tree 193 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 4 -1.</_> + <_>10 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7576119173318148e-003</threshold> + <left_val>0.5262191295623779</left_val> + <right_val>0.4337486028671265</right_val></_></_> + <_> + <!-- tree 194 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 3 -1.</_> + <_>6 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1787449046969414e-003</threshold> + <left_val>0.4804069101810455</left_val> + <right_val>0.7843729257583618</right_val></_></_> + <_> + <!-- tree 195 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 1 5 2 -1.</_> + <_>13 2 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0273341629654169e-004</threshold> + <left_val>0.4120846986770630</left_val> + <right_val>0.5353423953056335</right_val></_></_> + <_> + <!-- tree 196 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 2 -1.</_> + <_>7 14 3 1 2.</_> + <_>10 15 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1797959022223949e-003</threshold> + <left_val>0.4740372896194458</left_val> + <right_val>0.6425960063934326</right_val></_></_> + <_> + <!-- tree 197 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 3 3 4 -1.</_> + <_>12 3 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0101140001788735</threshold> + <left_val>0.2468792051076889</left_val> + <right_val>0.5175017714500427</right_val></_></_> + <_> + <!-- tree 198 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 13 12 6 -1.</_> + <_>5 13 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0186170600354671</threshold> + <left_val>0.5756294131278992</left_val> + <right_val>0.4628978967666626</right_val></_></_> + <_> + <!-- tree 199 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 11 5 2 -1.</_> + <_>14 12 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9225959703326225e-003</threshold> + <left_val>0.5169625878334045</left_val> + <right_val>0.3214271068572998</right_val></_></_> + <_> + <!-- tree 200 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 14 4 -1.</_> + <_>2 15 7 2 2.</_> + <_>9 17 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2945079989731312e-003</threshold> + <left_val>0.3872014880180359</left_val> + <right_val>0.5141636729240418</right_val></_></_> + <_> + <!-- tree 201 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 7 14 2 -1.</_> + <_>10 7 7 1 2.</_> + <_>3 8 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5353019163012505e-003</threshold> + <left_val>0.4853048920631409</left_val> + <right_val>0.6310489773750305</right_val></_></_> + <_> + <!-- tree 202 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 4 2 -1.</_> + <_>1 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0878399480134249e-003</threshold> + <left_val>0.5117315053939819</left_val> + <right_val>0.3723258972167969</right_val></_></_> + <_> + <!-- tree 203 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 14 -1.</_> + <_>16 0 2 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0225422400981188</threshold> + <left_val>0.5692740082740784</left_val> + <right_val>0.4887112975120544</right_val></_></_> + <_> + <!-- tree 204 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 1 3 -1.</_> + <_>4 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0065660830587149e-003</threshold> + <left_val>0.2556012868881226</left_val> + <right_val>0.5003992915153503</right_val></_></_> + <_> + <!-- tree 205 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 14 -1.</_> + <_>16 0 2 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4741272255778313e-003</threshold> + <left_val>0.4810872972011566</left_val> + <right_val>0.5675926804542542</right_val></_></_> + <_> + <!-- tree 206 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 3 7 -1.</_> + <_>2 10 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0261623207479715</threshold> + <left_val>0.4971194863319397</left_val> + <right_val>0.1777237057685852</right_val></_></_> + <_> + <!-- tree 207 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 9 2 -1.</_> + <_>8 13 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4352738233283162e-004</threshold> + <left_val>0.4940010905265808</left_val> + <right_val>0.5491250753402710</right_val></_></_> + <_> + <!-- tree 208 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 1 -1.</_> + <_>10 6 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0333632417023182</threshold> + <left_val>0.5007612109184265</left_val> + <right_val>0.2790724039077759</right_val></_></_> + <_> + <!-- tree 209 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 4 4 -1.</_> + <_>8 4 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0151186501607299</threshold> + <left_val>0.7059578895568848</left_val> + <right_val>0.4973031878471375</right_val></_></_> + <_> + <!-- tree 210 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 2 -1.</_> + <_>0 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8648946732282639e-004</threshold> + <left_val>0.5128620266914368</left_val> + <right_val>0.3776761889457703</right_val></_></_></trees> + <stage_threshold>104.7491989135742200</stage_threshold> + <parent>19</parent> + <next>-1</next></_> + <_> + <!-- stage 21 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 10 9 -1.</_> + <_>5 6 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0951507985591888</threshold> + <left_val>0.6470757126808167</left_val> + <right_val>0.4017286896705627</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 2 4 10 -1.</_> + <_>15 2 2 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2702340073883533e-003</threshold> + <left_val>0.3999822139739990</left_val> + <right_val>0.5746449232101440</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 2 7 -1.</_> + <_>9 2 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0018089455552399e-004</threshold> + <left_val>0.3558770120143890</left_val> + <right_val>0.5538809895515442</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 12 1 -1.</_> + <_>11 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1757409665733576e-003</threshold> + <left_val>0.4256534874439240</left_val> + <right_val>0.5382617712020874</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 9 1 -1.</_> + <_>6 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4235268433112651e-005</threshold> + <left_val>0.3682908117771149</left_val> + <right_val>0.5589926838874817</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 10 1 4 -1.</_> + <_>15 12 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9936920327600092e-005</threshold> + <left_val>0.5452470183372498</left_val> + <right_val>0.4020367860794067</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 6 4 -1.</_> + <_>7 10 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0073199886828661e-003</threshold> + <left_val>0.5239058136940002</left_val> + <right_val>0.3317843973636627</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 9 1 6 -1.</_> + <_>15 12 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0105138896033168</threshold> + <left_val>0.4320689141750336</left_val> + <right_val>0.5307983756065369</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 6 3 -1.</_> + <_>7 18 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3476826548576355e-003</threshold> + <left_val>0.4504637122154236</left_val> + <right_val>0.6453298926353455</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 2 16 -1.</_> + <_>15 3 1 8 2.</_> + <_>14 11 1 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1492270063608885e-003</threshold> + <left_val>0.4313425123691559</left_val> + <right_val>0.5370525121688843</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 1 6 -1.</_> + <_>4 12 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4435649973165710e-005</threshold> + <left_val>0.5326603055000305</left_val> + <right_val>0.3817971944808960</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 1 5 2 -1.</_> + <_>12 2 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2855090578086674e-004</threshold> + <left_val>0.4305163919925690</left_val> + <right_val>0.5382009744644165</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 18 4 2 -1.</_> + <_>6 18 2 1 2.</_> + <_>8 19 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5062429883982986e-004</threshold> + <left_val>0.4235970973968506</left_val> + <right_val>0.5544965267181397</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 4 16 10 -1.</_> + <_>10 4 8 5 2.</_> + <_>2 9 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0715598315000534</threshold> + <left_val>0.5303059816360474</left_val> + <right_val>0.2678802907466888</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 1 10 -1.</_> + <_>6 10 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4095180500298738e-004</threshold> + <left_val>0.3557108938694000</left_val> + <right_val>0.5205433964729309</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 15 2 -1.</_> + <_>9 8 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0629865005612373</threshold> + <left_val>0.5225362777709961</left_val> + <right_val>0.2861376106739044</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 15 2 -1.</_> + <_>6 8 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3798629883676767e-003</threshold> + <left_val>0.3624185919761658</left_val> + <right_val>0.5201697945594788</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 3 6 -1.</_> + <_>9 7 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1810739670181647e-004</threshold> + <left_val>0.5474476814270020</left_val> + <right_val>0.3959893882274628</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 2 -1.</_> + <_>9 7 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4505601292476058e-004</threshold> + <left_val>0.3740422129631043</left_val> + <right_val>0.5215715765953064</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8454910023137927e-003</threshold> + <left_val>0.5893052220344544</left_val> + <right_val>0.4584448933601379</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 16 3 -1.</_> + <_>1 1 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3832371011376381e-004</threshold> + <left_val>0.4084582030773163</left_val> + <right_val>0.5385351181030273</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 2 7 2 -1.</_> + <_>11 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4000830017030239e-003</threshold> + <left_val>0.3777455091476440</left_val> + <right_val>0.5293580293655396</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 10 18 -1.</_> + <_>5 7 10 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0987957417964935</threshold> + <left_val>0.2963612079620361</left_val> + <right_val>0.5070089101791382</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 4 3 2 -1.</_> + <_>18 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1798239797353745e-003</threshold> + <left_val>0.4877632856369019</left_val> + <right_val>0.6726443767547607</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 1 3 -1.</_> + <_>8 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2406419632025063e-004</threshold> + <left_val>0.4366911053657532</left_val> + <right_val>0.5561109781265259</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 14 6 -1.</_> + <_>3 16 14 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0325472503900528</threshold> + <left_val>0.3128157854080200</left_val> + <right_val>0.5308616161346436</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 4 -1.</_> + <_>1 2 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7561130747199059e-003</threshold> + <left_val>0.6560224890708923</left_val> + <right_val>0.4639872014522553</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 1 5 2 -1.</_> + <_>12 2 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0160272493958473</threshold> + <left_val>0.5172680020332336</left_val> + <right_val>0.3141897916793823</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 5 2 -1.</_> + <_>3 2 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1002350523485802e-006</threshold> + <left_val>0.4084446132183075</left_val> + <right_val>0.5336294770240784</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 13 2 3 -1.</_> + <_>10 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3422808200120926e-003</threshold> + <left_val>0.4966922104358673</left_val> + <right_val>0.6603465080261231</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 2 3 -1.</_> + <_>8 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6970280557870865e-003</threshold> + <left_val>0.5908237099647522</left_val> + <right_val>0.4500182867050171</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 3 -1.</_> + <_>14 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4118260480463505e-003</threshold> + <left_val>0.5315160751342773</left_val> + <right_val>0.3599720895290375</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 2 3 -1.</_> + <_>7 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5300937965512276e-003</threshold> + <left_val>0.2334040999412537</left_val> + <right_val>0.4996814131736755</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 10 4 -1.</_> + <_>10 6 5 2 2.</_> + <_>5 8 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6478730142116547e-003</threshold> + <left_val>0.5880935788154602</left_val> + <right_val>0.4684734046459198</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 1 6 -1.</_> + <_>9 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0112956296652555</threshold> + <left_val>0.4983777105808258</left_val> + <right_val>0.1884590983390808</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 2 2 -1.</_> + <_>11 12 1 1 2.</_> + <_>10 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6952878842130303e-004</threshold> + <left_val>0.5872138142585754</left_val> + <right_val>0.4799019992351532</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4410680159926414e-003</threshold> + <left_val>0.5131189227104187</left_val> + <right_val>0.3501011133193970</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 6 6 -1.</_> + <_>14 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4637870956212282e-003</threshold> + <left_val>0.5339372158050537</left_val> + <right_val>0.4117639064788818</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 2 3 -1.</_> + <_>8 18 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3114518737420440e-004</threshold> + <left_val>0.4313383102416992</left_val> + <right_val>0.5398246049880981</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 4 4 6 -1.</_> + <_>16 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0335572697222233</threshold> + <left_val>0.2675336897373200</left_val> + <right_val>0.5179154872894287</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 4 6 -1.</_> + <_>0 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0185394193977118</threshold> + <left_val>0.4973869919776917</left_val> + <right_val>0.2317177057266235</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 6 2 3 -1.</_> + <_>14 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9698139405809343e-004</threshold> + <left_val>0.5529708266258240</left_val> + <right_val>0.4643664062023163</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 8 1 -1.</_> + <_>8 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5577259152196348e-004</threshold> + <left_val>0.5629584193229675</left_val> + <right_val>0.4469191133975983</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 3 -1.</_> + <_>8 13 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0101589802652597</threshold> + <left_val>0.6706212759017944</left_val> + <right_val>0.4925918877124786</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 10 6 -1.</_> + <_>5 14 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2413829356082715e-005</threshold> + <left_val>0.5239421725273132</left_val> + <right_val>0.3912901878356934</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 1 2 -1.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2034963523037732e-005</threshold> + <left_val>0.4799438118934631</left_val> + <right_val>0.5501788854598999</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 2 -1.</_> + <_>8 16 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9267209619283676e-003</threshold> + <left_val>0.6930009722709656</left_val> + <right_val>0.4698084890842438</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 8 8 -1.</_> + <_>10 9 4 4 2.</_> + <_>6 13 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6997838914394379e-003</threshold> + <left_val>0.4099623858928680</left_val> + <right_val>0.5480883121490479</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 4 6 -1.</_> + <_>7 12 2 3 2.</_> + <_>9 15 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3130549862980843e-003</threshold> + <left_val>0.3283475935459137</left_val> + <right_val>0.5057886242866516</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 3 1 -1.</_> + <_>11 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9650589674711227e-003</threshold> + <left_val>0.4978047013282776</left_val> + <right_val>0.6398249864578247</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 10 -1.</_> + <_>9 7 1 5 2.</_> + <_>10 12 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1647600270807743e-003</threshold> + <left_val>0.4661160111427307</left_val> + <right_val>0.6222137212753296</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 6 6 -1.</_> + <_>10 0 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0240786392241716</threshold> + <left_val>0.2334644943475723</left_val> + <right_val>0.5222162008285523</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 2 6 -1.</_> + <_>3 13 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0210279691964388</threshold> + <left_val>0.1183653995394707</left_val> + <right_val>0.4938226044178009</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 12 1 2 -1.</_> + <_>16 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6017020465806127e-004</threshold> + <left_val>0.5325019955635071</left_val> + <right_val>0.4116711020469666</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 6 6 -1.</_> + <_>1 14 3 3 2.</_> + <_>4 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0172197297215462</threshold> + <left_val>0.6278762221336365</left_val> + <right_val>0.4664269089698792</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 1 3 6 -1.</_> + <_>14 1 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8672142699360847e-003</threshold> + <left_val>0.3403415083885193</left_val> + <right_val>0.5249736905097961</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 2 2 -1.</_> + <_>8 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4777389848604798e-004</threshold> + <left_val>0.3610411882400513</left_val> + <right_val>0.5086259245872498</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 3 3 -1.</_> + <_>10 9 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5486010387539864e-003</threshold> + <left_val>0.4884265959262848</left_val> + <right_val>0.6203498244285584</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>8 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9461148232221603e-003</threshold> + <left_val>0.2625930011272430</left_val> + <right_val>0.5011097192764282</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 2 3 -1.</_> + <_>14 0 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3569870498031378e-004</threshold> + <left_val>0.4340794980525971</left_val> + <right_val>0.5628312230110169</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 9 -1.</_> + <_>7 0 6 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0458802506327629</threshold> + <left_val>0.6507998704910278</left_val> + <right_val>0.4696274995803833</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 5 4 15 -1.</_> + <_>11 5 2 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215825606137514</threshold> + <left_val>0.3826502859592438</left_val> + <right_val>0.5287616848945618</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 4 15 -1.</_> + <_>7 5 2 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0202095396816731</threshold> + <left_val>0.3233368098735809</left_val> + <right_val>0.5074477195739746</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 2 3 -1.</_> + <_>14 0 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8496710844337940e-003</threshold> + <left_val>0.5177603960037231</left_val> + <right_val>0.4489670991897583</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 2 3 -1.</_> + <_>5 0 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7476379879517481e-005</threshold> + <left_val>0.4020850956439972</left_val> + <right_val>0.5246363878250122</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 2 2 -1.</_> + <_>12 12 1 1 2.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1513100471347570e-003</threshold> + <left_val>0.6315072178840637</left_val> + <right_val>0.4905154109001160</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 2 2 -1.</_> + <_>7 12 1 1 2.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9862831104546785e-003</threshold> + <left_val>0.4702459871768951</left_val> + <right_val>0.6497151255607605</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 3 4 -1.</_> + <_>13 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2719512023031712e-003</threshold> + <left_val>0.3650383949279785</left_val> + <right_val>0.5227652788162231</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 3 3 -1.</_> + <_>4 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2662699446082115e-003</threshold> + <left_val>0.5166100859642029</left_val> + <right_val>0.3877618014812470</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 4 2 -1.</_> + <_>12 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2919440679252148e-003</threshold> + <left_val>0.7375894188880920</left_val> + <right_val>0.5023847818374634</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 3 2 -1.</_> + <_>9 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7360111279413104e-004</threshold> + <left_val>0.4423226118087769</left_val> + <right_val>0.5495585799217224</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 3 2 -1.</_> + <_>10 9 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0523450328037143e-003</threshold> + <left_val>0.5976396203041077</left_val> + <right_val>0.4859583079814911</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 3 2 -1.</_> + <_>9 9 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4216238893568516e-004</threshold> + <left_val>0.5955939292907715</left_val> + <right_val>0.4398930966854096</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 3 4 -1.</_> + <_>13 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1747940443456173e-003</threshold> + <left_val>0.5349888205528259</left_val> + <right_val>0.4605058133602142</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 3 4 -1.</_> + <_>6 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2457437850534916e-003</threshold> + <left_val>0.5049191117286682</left_val> + <right_val>0.2941577136516571</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 12 4 -1.</_> + <_>10 14 6 2 2.</_> + <_>4 16 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0245397202670574</threshold> + <left_val>0.2550177872180939</left_val> + <right_val>0.5218586921691895</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 2 3 -1.</_> + <_>8 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3793041519820690e-004</threshold> + <left_val>0.4424861073493958</left_val> + <right_val>0.5490816235542297</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 10 3 8 -1.</_> + <_>10 14 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4233799884095788e-003</threshold> + <left_val>0.5319514274597168</left_val> + <right_val>0.4081355929374695</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 4 8 -1.</_> + <_>8 10 2 4 2.</_> + <_>10 14 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4149110540747643e-003</threshold> + <left_val>0.4087659120559692</left_val> + <right_val>0.5238950252532959</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 3 1 -1.</_> + <_>11 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2165299849584699e-003</threshold> + <left_val>0.5674579143524170</left_val> + <right_val>0.4908052980899811</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 1 6 -1.</_> + <_>9 15 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2438809499144554e-003</threshold> + <left_val>0.4129425883293152</left_val> + <right_val>0.5256118178367615</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 3 1 -1.</_> + <_>11 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1942739412188530e-003</threshold> + <left_val>0.5060194134712219</left_val> + <right_val>0.7313653230667114</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 1 -1.</_> + <_>8 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6607169527560472e-003</threshold> + <left_val>0.5979632139205933</left_val> + <right_val>0.4596369862556458</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 2 15 14 -1.</_> + <_>5 9 15 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0273162592202425</threshold> + <left_val>0.4174365103244782</left_val> + <right_val>0.5308842062950134</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 2 10 -1.</_> + <_>2 1 1 5 2.</_> + <_>3 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5845570014789701e-003</threshold> + <left_val>0.5615804791450501</left_val> + <right_val>0.4519486129283905</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 14 2 3 -1.</_> + <_>14 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5514739789068699e-003</threshold> + <left_val>0.4076187014579773</left_val> + <right_val>0.5360785126686096</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 7 3 3 -1.</_> + <_>3 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8446558755822480e-004</threshold> + <left_val>0.4347293972969055</left_val> + <right_val>0.5430442094802856</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 4 3 3 -1.</_> + <_>17 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146722598001361</threshold> + <left_val>0.1659304946660996</left_val> + <right_val>0.5146093964576721</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 3 3 -1.</_> + <_>0 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1608882173895836e-003</threshold> + <left_val>0.4961819052696228</left_val> + <right_val>0.1884745955467224</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 6 2 -1.</_> + <_>16 5 3 1 2.</_> + <_>13 6 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1121659772470593e-003</threshold> + <left_val>0.4868263900279999</left_val> + <right_val>0.6093816161155701</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 19 12 1 -1.</_> + <_>8 19 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2603770531713963e-003</threshold> + <left_val>0.6284325122833252</left_val> + <right_val>0.4690375924110413</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 12 2 4 -1.</_> + <_>12 14 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4046430189628154e-004</threshold> + <left_val>0.5575000047683716</left_val> + <right_val>0.4046044051647186</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 1 3 -1.</_> + <_>3 16 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3348190006799996e-004</threshold> + <left_val>0.4115762114524841</left_val> + <right_val>0.5252848267555237</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 16 6 4 -1.</_> + <_>11 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5736480280756950e-003</threshold> + <left_val>0.4730072915554047</left_val> + <right_val>0.5690100789070129</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 3 10 -1.</_> + <_>3 10 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0306237693876028</threshold> + <left_val>0.4971886873245239</left_val> + <right_val>0.1740095019340515</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 8 2 4 -1.</_> + <_>12 8 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2074798885732889e-004</threshold> + <left_val>0.5372117757797241</left_val> + <right_val>0.4354872107505798</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 8 2 4 -1.</_> + <_>7 8 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3550739064812660e-005</threshold> + <left_val>0.5366883873939514</left_val> + <right_val>0.4347316920757294</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 2 3 -1.</_> + <_>10 14 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6452710889279842e-003</threshold> + <left_val>0.3435518145561218</left_val> + <right_val>0.5160533189773560</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 10 3 -1.</_> + <_>10 1 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0432219989597797</threshold> + <left_val>0.4766792058944702</left_val> + <right_val>0.7293652892112732</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 2 -1.</_> + <_>11 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2331769578158855e-003</threshold> + <left_val>0.5029315948486328</left_val> + <right_val>0.5633171200752258</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 9 2 -1.</_> + <_>8 6 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1829739455133677e-003</threshold> + <left_val>0.4016092121601105</left_val> + <right_val>0.5192136764526367</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 2 -1.</_> + <_>9 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8027749320026487e-004</threshold> + <left_val>0.4088315963745117</left_val> + <right_val>0.5417919754981995</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 16 6 -1.</_> + <_>2 11 8 3 2.</_> + <_>10 14 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2934689447283745e-003</threshold> + <left_val>0.4075677096843720</left_val> + <right_val>0.5243561863899231</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 2 2 -1.</_> + <_>13 7 1 1 2.</_> + <_>12 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2750959722325206e-003</threshold> + <left_val>0.4913282990455627</left_val> + <right_val>0.6387010812759399</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 3 -1.</_> + <_>9 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3385322205722332e-003</threshold> + <left_val>0.5031672120094299</left_val> + <right_val>0.2947346866130829</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 2 -1.</_> + <_>10 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5250744596123695e-003</threshold> + <left_val>0.4949789047241211</left_val> + <right_val>0.6308869123458862</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 8 12 -1.</_> + <_>5 7 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4266352243721485e-004</threshold> + <left_val>0.5328366756439209</left_val> + <right_val>0.4285649955272675</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 2 -1.</_> + <_>13 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3609660090878606e-003</threshold> + <left_val>0.4991525113582611</left_val> + <right_val>0.5941501259803772</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 2 -1.</_> + <_>5 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4782509212382138e-004</threshold> + <left_val>0.4573504030704498</left_val> + <right_val>0.5854480862617493</right_val></_></_> + <_> + <!-- tree 109 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3360050506889820e-003</threshold> + <left_val>0.4604358971118927</left_val> + <right_val>0.5849052071571350</right_val></_></_> + <_> + <!-- tree 110 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 14 2 3 -1.</_> + <_>4 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0967548051849008e-004</threshold> + <left_val>0.3969388902187347</left_val> + <right_val>0.5229423046112061</right_val></_></_> + <_> + <!-- tree 111 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3656780831515789e-003</threshold> + <left_val>0.5808320045471191</left_val> + <right_val>0.4898357093334198</right_val></_></_> + <_> + <!-- tree 112 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0734340175986290e-003</threshold> + <left_val>0.4351210892200470</left_val> + <right_val>0.5470039248466492</right_val></_></_> + <_> + <!-- tree 113 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 6 -1.</_> + <_>10 14 1 3 2.</_> + <_>9 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1923359017819166e-003</threshold> + <left_val>0.5355060100555420</left_val> + <right_val>0.3842903971672058</right_val></_></_> + <_> + <!-- tree 114 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 3 2 -1.</_> + <_>9 14 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4968618787825108e-003</threshold> + <left_val>0.5018138885498047</left_val> + <right_val>0.2827191948890686</right_val></_></_> + <_> + <!-- tree 115 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 6 -1.</_> + <_>11 5 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0753688216209412</threshold> + <left_val>0.1225076019763947</left_val> + <right_val>0.5148826837539673</right_val></_></_> + <_> + <!-- tree 116 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 6 -1.</_> + <_>7 5 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0251344703137875</threshold> + <left_val>0.4731766879558563</left_val> + <right_val>0.7025446295738220</right_val></_></_> + <_> + <!-- tree 117 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 1 2 -1.</_> + <_>13 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9358599931583740e-005</threshold> + <left_val>0.5430532097816467</left_val> + <right_val>0.4656086862087250</right_val></_></_> + <_> + <!-- tree 118 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 10 2 -1.</_> + <_>0 3 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8355910005047917e-004</threshold> + <left_val>0.4031040072441101</left_val> + <right_val>0.5190119743347168</right_val></_></_> + <_> + <!-- tree 119 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 1 2 -1.</_> + <_>13 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6639450807124376e-003</threshold> + <left_val>0.4308126866817474</left_val> + <right_val>0.5161771178245544</right_val></_></_> + <_> + <!-- tree 120 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 2 -1.</_> + <_>5 7 1 1 2.</_> + <_>6 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3804089976474643e-003</threshold> + <left_val>0.6219829916954041</left_val> + <right_val>0.4695515930652618</right_val></_></_> + <_> + <!-- tree 121 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 7 -1.</_> + <_>13 5 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2313219485804439e-003</threshold> + <left_val>0.5379363894462585</left_val> + <right_val>0.4425831139087677</right_val></_></_> + <_> + <!-- tree 122 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 13 1 2 -1.</_> + <_>6 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4644179827882908e-005</threshold> + <left_val>0.5281640291213989</left_val> + <right_val>0.4222503006458283</right_val></_></_> + <_> + <!-- tree 123 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 7 -1.</_> + <_>12 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128188095986843</threshold> + <left_val>0.2582092881202698</left_val> + <right_val>0.5179932713508606</right_val></_></_> + <_> + <!-- tree 124 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 2 16 -1.</_> + <_>0 3 1 8 2.</_> + <_>1 11 1 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0228521898388863</threshold> + <left_val>0.4778693020343781</left_val> + <right_val>0.7609264254570007</right_val></_></_> + <_> + <!-- tree 125 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 3 7 -1.</_> + <_>12 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2305970136076212e-004</threshold> + <left_val>0.5340992212295532</left_val> + <right_val>0.4671724140644074</right_val></_></_> + <_> + <!-- tree 126 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 7 -1.</_> + <_>7 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0127701200544834</threshold> + <left_val>0.4965761005878449</left_val> + <right_val>0.1472366005182266</right_val></_></_> + <_> + <!-- tree 127 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 16 8 4 -1.</_> + <_>11 16 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0500515103340149</threshold> + <left_val>0.6414994001388550</left_val> + <right_val>0.5016592144966126</right_val></_></_> + <_> + <!-- tree 128 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 16 8 4 -1.</_> + <_>5 16 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0157752707600594</threshold> + <left_val>0.4522320032119751</left_val> + <right_val>0.5685362219810486</right_val></_></_> + <_> + <!-- tree 129 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 7 -1.</_> + <_>13 5 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0185016207396984</threshold> + <left_val>0.2764748930931091</left_val> + <right_val>0.5137959122657776</right_val></_></_> + <_> + <!-- tree 130 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 7 -1.</_> + <_>6 5 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4626250378787518e-003</threshold> + <left_val>0.5141941905021668</left_val> + <right_val>0.3795408010482788</right_val></_></_> + <_> + <!-- tree 131 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 6 2 14 -1.</_> + <_>18 13 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0629161670804024</threshold> + <left_val>0.5060648918151856</left_val> + <right_val>0.6580433845520020</right_val></_></_> + <_> + <!-- tree 132 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 4 -1.</_> + <_>6 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1648500478477217e-005</threshold> + <left_val>0.5195388197898865</left_val> + <right_val>0.4019886851310730</right_val></_></_> + <_> + <!-- tree 133 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 7 1 2 -1.</_> + <_>14 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1180990152060986e-003</threshold> + <left_val>0.4962365031242371</left_val> + <right_val>0.5954458713531494</right_val></_></_> + <_> + <!-- tree 134 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 18 6 -1.</_> + <_>0 1 9 3 2.</_> + <_>9 4 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0166348908096552</threshold> + <left_val>0.3757933080196381</left_val> + <right_val>0.5175446867942810</right_val></_></_> + <_> + <!-- tree 135 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 7 1 2 -1.</_> + <_>14 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8899470344185829e-003</threshold> + <left_val>0.6624013781547546</left_val> + <right_val>0.5057178735733032</right_val></_></_> + <_> + <!-- tree 136 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 2 14 -1.</_> + <_>0 13 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0767832621932030</threshold> + <left_val>0.4795796871185303</left_val> + <right_val>0.8047714829444885</right_val></_></_> + <_> + <!-- tree 137 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 0 3 12 -1.</_> + <_>18 0 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9170677773654461e-003</threshold> + <left_val>0.4937882125377655</left_val> + <right_val>0.5719941854476929</right_val></_></_> + <_> + <!-- tree 138 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 18 3 -1.</_> + <_>0 7 18 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0726706013083458</threshold> + <left_val>0.0538945607841015</left_val> + <right_val>0.4943903982639313</right_val></_></_> + <_> + <!-- tree 139 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 14 16 -1.</_> + <_>6 8 14 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.5403950214385986</threshold> + <left_val>0.5129774212837219</left_val> + <right_val>0.1143338978290558</right_val></_></_> + <_> + <!-- tree 140 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 3 12 -1.</_> + <_>1 0 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9510019812732935e-003</threshold> + <left_val>0.4528343975543976</left_val> + <right_val>0.5698574185371399</right_val></_></_> + <_> + <!-- tree 141 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 7 -1.</_> + <_>14 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4508369863033295e-003</threshold> + <left_val>0.5357726812362671</left_val> + <right_val>0.4218730926513672</right_val></_></_> + <_> + <!-- tree 142 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 1 2 -1.</_> + <_>5 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2077939724549651e-004</threshold> + <left_val>0.5916172862052918</left_val> + <right_val>0.4637925922870636</right_val></_></_> + <_> + <!-- tree 143 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 6 6 -1.</_> + <_>14 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3051050268113613e-003</threshold> + <left_val>0.5273385047912598</left_val> + <right_val>0.4382042884826660</right_val></_></_> + <_> + <!-- tree 144 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 7 2 -1.</_> + <_>5 8 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7735060798004270e-004</threshold> + <left_val>0.4046528041362763</left_val> + <right_val>0.5181884765625000</right_val></_></_> + <_> + <!-- tree 145 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 6 9 -1.</_> + <_>8 9 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0259285103529692</threshold> + <left_val>0.7452235817909241</left_val> + <right_val>0.5089386105537415</right_val></_></_> + <_> + <!-- tree 146 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 6 1 -1.</_> + <_>7 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9729790985584259e-003</threshold> + <left_val>0.3295435905456543</left_val> + <right_val>0.5058795213699341</right_val></_></_> + <_> + <!-- tree 147 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 6 4 -1.</_> + <_>16 0 3 2 2.</_> + <_>13 2 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8508329093456268e-003</threshold> + <left_val>0.4857144057750702</left_val> + <right_val>0.5793024897575378</right_val></_></_> + <_> + <!-- tree 148 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 12 -1.</_> + <_>1 6 18 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0459675192832947</threshold> + <left_val>0.4312731027603149</left_val> + <right_val>0.5380653142929077</right_val></_></_> + <_> + <!-- tree 149 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 17 12 -1.</_> + <_>3 6 17 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1558596044778824</threshold> + <left_val>0.5196170210838318</left_val> + <right_val>0.1684713959693909</right_val></_></_> + <_> + <!-- tree 150 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 14 7 3 -1.</_> + <_>5 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0151648297905922</threshold> + <left_val>0.4735757112503052</left_val> + <right_val>0.6735026836395264</right_val></_></_> + <_> + <!-- tree 151 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 1 3 -1.</_> + <_>10 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0604249546304345e-003</threshold> + <left_val>0.5822926759719849</left_val> + <right_val>0.4775702953338623</right_val></_></_> + <_> + <!-- tree 152 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 3 3 -1.</_> + <_>3 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6476291976869106e-003</threshold> + <left_val>0.4999198913574219</left_val> + <right_val>0.2319535017013550</right_val></_></_> + <_> + <!-- tree 153 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 6 6 -1.</_> + <_>14 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0122311301529408</threshold> + <left_val>0.4750893115997315</left_val> + <right_val>0.5262982249259949</right_val></_></_> + <_> + <!-- tree 154 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 6 6 -1.</_> + <_>0 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6528882123529911e-003</threshold> + <left_val>0.5069767832756043</left_val> + <right_val>0.3561818897724152</right_val></_></_> + <_> + <!-- tree 155 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 4 3 -1.</_> + <_>12 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2977829901501536e-003</threshold> + <left_val>0.4875693917274475</left_val> + <right_val>0.5619062781333923</right_val></_></_> + <_> + <!-- tree 156 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 4 3 -1.</_> + <_>4 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0107815898954868</threshold> + <left_val>0.4750770032405853</left_val> + <right_val>0.6782308220863342</right_val></_></_> + <_> + <!-- tree 157 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 0 2 6 -1.</_> + <_>18 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8654779307544231e-003</threshold> + <left_val>0.5305461883544922</left_val> + <right_val>0.4290736019611359</right_val></_></_> + <_> + <!-- tree 158 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 4 9 -1.</_> + <_>10 1 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8663428965955973e-003</threshold> + <left_val>0.4518479108810425</left_val> + <right_val>0.5539351105690002</right_val></_></_> + <_> + <!-- tree 159 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 2 -1.</_> + <_>6 6 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1983320154249668e-003</threshold> + <left_val>0.4149119853973389</left_val> + <right_val>0.5434188842773438</right_val></_></_> + <_> + <!-- tree 160 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 4 2 -1.</_> + <_>6 5 2 1 2.</_> + <_>8 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3739990107715130e-003</threshold> + <left_val>0.4717896878719330</left_val> + <right_val>0.6507657170295715</right_val></_></_> + <_> + <!-- tree 161 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 2 3 -1.</_> + <_>10 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146415298804641</threshold> + <left_val>0.2172164022922516</left_val> + <right_val>0.5161777138710022</right_val></_></_> + <_> + <!-- tree 162 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 1 3 -1.</_> + <_>9 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5042580344015732e-005</threshold> + <left_val>0.5337383747100830</left_val> + <right_val>0.4298836886882782</right_val></_></_> + <_> + <!-- tree 163 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 2 -1.</_> + <_>9 11 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1875660129589960e-004</threshold> + <left_val>0.4604594111442566</left_val> + <right_val>0.5582447052001953</right_val></_></_> + <_> + <!-- tree 164 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 4 3 -1.</_> + <_>0 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0169955305755138</threshold> + <left_val>0.4945895075798035</left_val> + <right_val>0.0738800764083862</right_val></_></_> + <_> + <!-- tree 165 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 8 6 -1.</_> + <_>6 3 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0350959412753582</threshold> + <left_val>0.7005509138107300</left_val> + <right_val>0.4977591037750244</right_val></_></_> + <_> + <!-- tree 166 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 6 4 -1.</_> + <_>1 0 3 2 2.</_> + <_>4 2 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4217350874096155e-003</threshold> + <left_val>0.4466265141963959</left_val> + <right_val>0.5477694272994995</right_val></_></_> + <_> + <!-- tree 167 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 7 -1.</_> + <_>14 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.6340337768197060e-004</threshold> + <left_val>0.4714098870754242</left_val> + <right_val>0.5313338041305542</right_val></_></_> + <_> + <!-- tree 168 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 2 -1.</_> + <_>9 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6391130338888615e-004</threshold> + <left_val>0.4331546127796173</left_val> + <right_val>0.5342242121696472</right_val></_></_> + <_> + <!-- tree 169 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 6 10 -1.</_> + <_>11 9 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0211414601653814</threshold> + <left_val>0.2644700109958649</left_val> + <right_val>0.5204498767852783</right_val></_></_> + <_> + <!-- tree 170 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 19 2 -1.</_> + <_>0 11 19 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7775202700868249e-004</threshold> + <left_val>0.5208349823951721</left_val> + <right_val>0.4152742922306061</right_val></_></_> + <_> + <!-- tree 171 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 8 9 -1.</_> + <_>9 8 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0279439203441143</threshold> + <left_val>0.6344125270843506</left_val> + <right_val>0.5018811821937561</right_val></_></_> + <_> + <!-- tree 172 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 3 7 -1.</_> + <_>5 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7297378554940224e-003</threshold> + <left_val>0.5050438046455383</left_val> + <right_val>0.3500863909721375</right_val></_></_> + <_> + <!-- tree 173 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 4 12 -1.</_> + <_>10 6 2 6 2.</_> + <_>8 12 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0232810396701097</threshold> + <left_val>0.4966318011283875</left_val> + <right_val>0.6968677043914795</right_val></_></_> + <_> + <!-- tree 174 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 4 -1.</_> + <_>0 4 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116449799388647</threshold> + <left_val>0.3300260007381439</left_val> + <right_val>0.5049629807472229</right_val></_></_> + <_> + <!-- tree 175 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0157643090933561</threshold> + <left_val>0.4991598129272461</left_val> + <right_val>0.7321153879165649</right_val></_></_> + <_> + <!-- tree 176 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 3 7 -1.</_> + <_>9 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3611479662358761e-003</threshold> + <left_val>0.3911735117435455</left_val> + <right_val>0.5160670876502991</right_val></_></_> + <_> + <!-- tree 177 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 3 4 -1.</_> + <_>10 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1522337859496474e-004</threshold> + <left_val>0.5628911256790161</left_val> + <right_val>0.4949719011783600</right_val></_></_> + <_> + <!-- tree 178 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 3 4 -1.</_> + <_>9 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0066272271797061e-004</threshold> + <left_val>0.5853595137596130</left_val> + <right_val>0.4550595879554749</right_val></_></_> + <_> + <!-- tree 179 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 1 -1.</_> + <_>9 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9715518252924085e-004</threshold> + <left_val>0.4271470010280609</left_val> + <right_val>0.5443599224090576</right_val></_></_> + <_> + <!-- tree 180 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 4 4 -1.</_> + <_>7 14 2 2 2.</_> + <_>9 16 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3475370835512877e-003</threshold> + <left_val>0.5143110752105713</left_val> + <right_val>0.3887656927108765</right_val></_></_> + <_> + <!-- tree 181 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 4 6 -1.</_> + <_>15 14 2 3 2.</_> + <_>13 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9261569082736969e-003</threshold> + <left_val>0.6044502258300781</left_val> + <right_val>0.4971720874309540</right_val></_></_> + <_> + <!-- tree 182 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 1 8 -1.</_> + <_>7 12 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0139199104160070</threshold> + <left_val>0.2583160996437073</left_val> + <right_val>0.5000367760658264</right_val></_></_> + <_> + <!-- tree 183 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 2 8 -1.</_> + <_>17 0 1 4 2.</_> + <_>16 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0209949687123299e-003</threshold> + <left_val>0.4857374131679535</left_val> + <right_val>0.5560358166694641</right_val></_></_> + <_> + <!-- tree 184 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 2 8 -1.</_> + <_>2 0 1 4 2.</_> + <_>3 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7441629208624363e-003</threshold> + <left_val>0.5936884880065918</left_val> + <right_val>0.4645777046680450</right_val></_></_> + <_> + <!-- tree 185 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 14 3 -1.</_> + <_>6 2 14 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0162001308053732</threshold> + <left_val>0.3163014948368073</left_val> + <right_val>0.5193495154380798</right_val></_></_> + <_> + <!-- tree 186 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 9 3 10 -1.</_> + <_>7 14 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3331980705261230e-003</threshold> + <left_val>0.5061224102973938</left_val> + <right_val>0.3458878993988037</right_val></_></_> + <_> + <!-- tree 187 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 2 -1.</_> + <_>9 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8497930876910686e-004</threshold> + <left_val>0.4779017865657806</left_val> + <right_val>0.5870177745819092</right_val></_></_> + <_> + <!-- tree 188 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 8 -1.</_> + <_>7 11 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2466450463980436e-003</threshold> + <left_val>0.4297851026058197</left_val> + <right_val>0.5374773144721985</right_val></_></_> + <_> + <!-- tree 189 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>9 10 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3146099410951138e-003</threshold> + <left_val>0.5438671708106995</left_val> + <right_val>0.4640969932079315</right_val></_></_> + <_> + <!-- tree 190 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 3 3 -1.</_> + <_>7 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7679121643304825e-003</threshold> + <left_val>0.4726893007755280</left_val> + <right_val>0.6771789789199829</right_val></_></_> + <_> + <!-- tree 191 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2448020172305405e-004</threshold> + <left_val>0.4229173064231873</left_val> + <right_val>0.5428048968315125</right_val></_></_> + <_> + <!-- tree 192 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 18 2 -1.</_> + <_>6 1 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4336021207273006e-003</threshold> + <left_val>0.6098880767822266</left_val> + <right_val>0.4683673977851868</right_val></_></_> + <_> + <!-- tree 193 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 14 -1.</_> + <_>7 8 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3189240600913763e-003</threshold> + <left_val>0.5689436793327332</left_val> + <right_val>0.4424242079257965</right_val></_></_> + <_> + <!-- tree 194 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 18 1 -1.</_> + <_>7 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1042178850620985e-003</threshold> + <left_val>0.3762221038341522</left_val> + <right_val>0.5187087059020996</right_val></_></_> + <_> + <!-- tree 195 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6034841216169298e-004</threshold> + <left_val>0.4699405133724213</left_val> + <right_val>0.5771207213401794</right_val></_></_> + <_> + <!-- tree 196 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 9 -1.</_> + <_>10 3 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0547629790380597e-003</threshold> + <left_val>0.4465216994285584</left_val> + <right_val>0.5601701736450195</right_val></_></_> + <_> + <!-- tree 197 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 14 2 3 -1.</_> + <_>18 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7148818420246243e-004</threshold> + <left_val>0.5449805259704590</left_val> + <right_val>0.3914709091186523</right_val></_></_> + <_> + <!-- tree 198 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 11 3 1 -1.</_> + <_>8 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3364820410497487e-004</threshold> + <left_val>0.4564009010791779</left_val> + <right_val>0.5645738840103149</right_val></_></_> + <_> + <!-- tree 199 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 3 4 -1.</_> + <_>11 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4853250468149781e-003</threshold> + <left_val>0.5747377872467041</left_val> + <right_val>0.4692778885364533</right_val></_></_> + <_> + <!-- tree 200 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 3 6 -1.</_> + <_>8 14 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0251620337367058e-003</threshold> + <left_val>0.5166196823120117</left_val> + <right_val>0.3762814104557037</right_val></_></_> + <_> + <!-- tree 201 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 3 4 -1.</_> + <_>11 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0280741415917873e-003</threshold> + <left_val>0.5002111792564392</left_val> + <right_val>0.6151527166366577</right_val></_></_> + <_> + <!-- tree 202 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 4 -1.</_> + <_>8 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8164511574432254e-004</threshold> + <left_val>0.5394598245620728</left_val> + <right_val>0.4390751123428345</right_val></_></_> + <_> + <!-- tree 203 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 9 6 9 -1.</_> + <_>7 12 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0451415292918682</threshold> + <left_val>0.5188326835632324</left_val> + <right_val>0.2063035964965820</right_val></_></_> + <_> + <!-- tree 204 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 2 3 -1.</_> + <_>0 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0795620037242770e-003</threshold> + <left_val>0.3904685080051422</left_val> + <right_val>0.5137907266616821</right_val></_></_> + <_> + <!-- tree 205 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 1 2 -1.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5995999274309725e-004</threshold> + <left_val>0.4895322918891907</left_val> + <right_val>0.5427504181861877</right_val></_></_> + <_> + <!-- tree 206 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 8 3 -1.</_> + <_>8 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0193592701107264</threshold> + <left_val>0.6975228786468506</left_val> + <right_val>0.4773507118225098</right_val></_></_> + <_> + <!-- tree 207 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 6 -1.</_> + <_>0 4 10 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.2072550952434540</threshold> + <left_val>0.5233635902404785</left_val> + <right_val>0.3034991919994354</right_val></_></_> + <_> + <!-- tree 208 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 1 3 -1.</_> + <_>9 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1953290929086506e-004</threshold> + <left_val>0.5419396758079529</left_val> + <right_val>0.4460186064243317</right_val></_></_> + <_> + <!-- tree 209 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2582069505006075e-003</threshold> + <left_val>0.4815764129161835</left_val> + <right_val>0.6027408838272095</right_val></_></_> + <_> + <!-- tree 210 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 14 4 -1.</_> + <_>0 17 14 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7811207845807076e-003</threshold> + <left_val>0.3980278968811035</left_val> + <right_val>0.5183305740356445</right_val></_></_> + <_> + <!-- tree 211 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 18 6 -1.</_> + <_>1 17 18 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0111543098464608</threshold> + <left_val>0.5431231856346130</left_val> + <right_val>0.4188759922981262</right_val></_></_> + <_> + <!-- tree 212 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 10 6 -1.</_> + <_>0 0 5 3 2.</_> + <_>5 3 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0431624315679073</threshold> + <left_val>0.4738228023052216</left_val> + <right_val>0.6522961258888245</right_val></_></_></trees> + <stage_threshold>105.7611007690429700</stage_threshold> + <parent>20</parent> + <next>-1</next></_></stages></haarcascade_frontalface_alt> +</opencv_storage> diff --git a/tutorial/detection/face/tutorial-face-detector-live.cpp b/tutorial/detection/face/tutorial-face-detector-live.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e50b4c338f90d7b16be343ae07ae036a3c60b692 --- /dev/null +++ b/tutorial/detection/face/tutorial-face-detector-live.cpp @@ -0,0 +1,101 @@ +//! \example tutorial-face-detector-live.cpp +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDetectorFace.h> +#include <visp/vpV4l2Grabber.h> + +int main(int argc, const char* argv[]) +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020200) + try { + std::string opt_face_cascade_name = "./haarcascade_frontalface_alt.xml"; + + int opt_device = 0; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--haar") + opt_face_cascade_name = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--device") + opt_device = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] << " [--haar <haarcascade xml filename>] [--device <camera device>] [--help]" << std::endl; + return 0; + } + } + + vpImage<unsigned char> I; // for gray images + + //! [Construct grabber] +#if defined(VISP_HAVE_V4L2) + vpV4l2Grabber g; + std::ostringstream device; + device << "/dev/video" << opt_device; + g.setDevice(device.str()); + g.setScale(2); + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture cap(opt_device); // open the default camera + if(!cap.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + cap >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + //! [Construct grabber] + +#if defined(VISP_HAVE_X11) + vpDisplayX d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#endif + vpDisplay::setTitle(I, "ViSP viewer"); + + vpDetectorFace face_detector; + face_detector.setCascadeClassifierFile(opt_face_cascade_name); + + while(1) { + double t = vpTime::measureTimeMs(); + //! [Acquisition] +#if defined(VISP_HAVE_V4L2) + g.acquire(I); +#else + cap >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + //! [Acquisition] + + vpDisplay::display(I); + bool face_found = face_detector.detect(I); + + if (face_found) { + std::ostringstream text; + text << "Found " << face_detector.getNbObjects() << " face(s)"; + vpDisplay::displayText(I, 10, 10, text.str(), vpColor::red); + for(size_t i=0; i < face_detector.getNbObjects(); i++) { + std::vector<vpImagePoint> p = face_detector.getPolygon(i); + vpRect bbox = face_detector.getBBox(i); + vpDisplay::displayRectangle(I, bbox, vpColor::green, false, 4); + vpDisplay::displayText(I, (int)bbox.getTop()-10, (int)bbox.getLeft(), "Message: \"" + face_detector.getMessage(i) + "\"", vpColor::red); + } + } + vpDisplay::displayText(I, (int)I.getHeight()-25, 10, "Click to quit...", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // a click to exit + break; + + std::cout << "Loop time: " << vpTime::measureTimeMs() - t << " ms" << std::endl; + } + } + catch(vpException &e) { + std::cout << e.getMessage() << std::endl; + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/detection/face/tutorial-face-detector.cpp b/tutorial/detection/face/tutorial-face-detector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a6927bd95ed28e07f3d272faaf4ae406968410f --- /dev/null +++ b/tutorial/detection/face/tutorial-face-detector.cpp @@ -0,0 +1,94 @@ +//! \example tutorial-face-detector.cpp +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +//! [Include] +#include <visp/vpDetectorFace.h> +//! [Include] +#include <visp/vpVideoReader.h> + +int main(int argc, const char* argv[]) +{ +//! [Macro defined] +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020200) + //! [Macro defined] + try { + //! [Default settings] + std::string opt_face_cascade_name = "./haarcascade_frontalface_alt.xml"; + std::string opt_video = "video.mpeg"; + //! [Default settings] + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--haar") + opt_face_cascade_name = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--video") + opt_video = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] << " [--haar <haarcascade xml filename>] [--video <input video file>] [--help]" << std::endl; + return 0; + } + } + + vpImage<unsigned char> I; + + vpVideoReader g; + g.setFileName(opt_video); + g.open(I); + +#if defined(VISP_HAVE_X11) + vpDisplayX d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#endif + vpDisplay::setTitle(I, "ViSP viewer"); + + //! [Face detector construction] + vpDetectorFace face_detector; + //! [Face detector construction] + //! [Face detector setting] + face_detector.setCascadeClassifierFile(opt_face_cascade_name); + //! [Face detector setting] + + bool exit_requested = false; + while( ! g.end() && ! exit_requested) { + g.acquire(I); + + vpDisplay::display(I); + //! [Face detection] + bool face_found = face_detector.detect(I); + //! [Face detection] + + if (face_found) { + std::ostringstream text; + //! [Get number faces] + text << "Found " << face_detector.getNbObjects() << " face(s)"; + //! [Get number faces] + vpDisplay::displayText(I, 10, 10, text.str(), vpColor::red); + //! [Get face characteristics] + for(size_t i=0; i < face_detector.getNbObjects(); i++) { + std::vector<vpImagePoint> p = face_detector.getPolygon(i); + vpRect bbox = face_detector.getBBox(i); + vpDisplay::displayRectangle(I, bbox, vpColor::green, false, 4); + vpDisplay::displayText(I, (int)bbox.getTop()-10, (int)bbox.getLeft(), + "Message: \"" + face_detector.getMessage(i) + "\"", vpColor::red); + } + //! [Get face characteristics] + } + vpDisplay::displayText(I, (int)I.getHeight()-25, 10, "Click to quit...", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // a click to exit + exit_requested = true; + } + if (! exit_requested) + vpDisplay::getClick(I); + } + catch(vpException &e) { + std::cout << e.getMessage() << std::endl; + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/detection/face/video.mpeg b/tutorial/detection/face/video.mpeg new file mode 100644 index 0000000000000000000000000000000000000000..d01f912fa5ab9893a4e22f0b2489c28ab0b672cb Binary files /dev/null and b/tutorial/detection/face/video.mpeg differ diff --git a/tutorial/detection/matching/CMakeLists.txt b/tutorial/detection/matching/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb29651a5e98f7f4d901f9035aab81ac2200a60e --- /dev/null +++ b/tutorial/detection/matching/CMakeLists.txt @@ -0,0 +1,27 @@ +project(tutorial-matching-keypoint) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +# set the list of source files +set(tutorial_cpp + tutorial-matching-keypoint.cpp + tutorial-matching-keypoint-SIFT.cpp + tutorial-matching-keypoint-homography.cpp + tutorial-matching-surf-deprecated.cpp + tutorial-matching-surf-homography-deprecated.cpp) + +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/video-postcard.mpeg" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-matching-keypoint.cpp ${data}) +endforeach() diff --git a/tutorial/detection/matching/tutorial-matching-keypoint-SIFT.cpp b/tutorial/detection/matching/tutorial-matching-keypoint-SIFT.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5f4b81c834250821811c68568c97025b6acfc99 --- /dev/null +++ b/tutorial/detection/matching/tutorial-matching-keypoint-SIFT.cpp @@ -0,0 +1,84 @@ +//! \example tutorial-matching-keypoint-SIFT.cpp +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> +#include <visp/vpImageIo.h> +//! [Include] +#include <visp/vpKeyPoint.h> +//! [Include] + +int main() { + //! [Define] +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) && (defined(VISP_HAVE_OPENCV_NONFREE) || defined(VISP_HAVE_OPENCV_XFEATURES2D)) + //! [Define] + vpImage<unsigned char> I; + + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + reader.acquire(I); + + //! [Construction] + const std::string detectorName = "SIFT"; + const std::string extractorName = "SIFT"; + //Use L2 distance with a matching done using FLANN (Fast Library for Approximate Nearest Neighbors) + const std::string matcherName = "FlannBased"; + vpKeyPoint::vpFilterMatchingType filterType = vpKeyPoint::ratioDistanceThreshold; + vpKeyPoint keypoint(detectorName, extractorName, matcherName, filterType); + //! [Construction] + + //! [Build Reference] + std::cout << "Reference keypoints=" << keypoint.buildReference(I) << std::endl; + //! [Build Reference] + + //! [Create image] + vpImage<unsigned char> Idisp; + Idisp.resize(I.getHeight(), 2*I.getWidth()); + Idisp.insert(I, vpImagePoint(0, 0)); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Create image] + //! [Init display] + vpDisplayOpenCV d(Idisp, 0, 0, "Matching keypoints with SIFT keypoints") ; + vpDisplay::display(Idisp); + vpDisplay::flush(Idisp); + //! [Init display] + + while ( ! reader.end() ) + { + //! [Acquisition] + reader.acquire(I); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Acquisition] + + //! [Display] + vpDisplay::display(Idisp); + vpDisplay::displayLine(Idisp, vpImagePoint(0, I.getWidth()), vpImagePoint(I.getHeight(), I.getWidth()), vpColor::white, 2); + //! [Display] + + //! [Matching] + int nbMatch = keypoint.matchPoint(I); + //! [Matching] + + std::cout << "Matches=" << nbMatch << std::endl; + + //! [Get matches] + vpImagePoint iPref, iPcur; + for (int i = 0; i < nbMatch; i++) + { + keypoint.getMatchedPoints(i, iPref, iPcur); + //! [Get matches] + //! [Display matches] + vpDisplay::displayLine(Idisp, iPref, iPcur + vpImagePoint(0, I.getWidth()), vpColor::green); + //! [Display matches] + } + //! [Display flush] + vpDisplay::flush(Idisp); + //! [Display flush] + + if (vpDisplay::getClick(Idisp, false)) + break; + } + + vpDisplay::getClick(Idisp); +#endif + + return 0; +} diff --git a/tutorial/detection/matching/tutorial-matching-keypoint-homography.cpp b/tutorial/detection/matching/tutorial-matching-keypoint-homography.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6aa46d5fe0d372c6c2d311507c0f254c3d9ddf63 --- /dev/null +++ b/tutorial/detection/matching/tutorial-matching-keypoint-homography.cpp @@ -0,0 +1,151 @@ +//! \example tutorial-matching-keypoint-homography.cpp +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpHomography.h> +#include <visp/vpKeyPoint.h> +#include <visp/vpPixelMeterConversion.h> +#include <visp/vpVideoReader.h> + +int main(int argc, const char **argv) +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + //! [Select method] + int method = 0; + + if (argc > 1) + method = atoi(argv[1]); + + if (method == 0) + std::cout << "Uses Ransac to estimate the homography" << std::endl; + else + std::cout << "Uses a robust scheme to estimate the homography" << std::endl; + //! [Select method] + + vpImage<unsigned char> I; + + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + reader.acquire(I); + + const std::string detectorName = "ORB"; + const std::string extractorName = "ORB"; + //Hamming distance must be used with ORB + const std::string matcherName = "BruteForce-Hamming"; + vpKeyPoint::vpFilterMatchingType filterType = vpKeyPoint::ratioDistanceThreshold; + vpKeyPoint keypoint(detectorName, extractorName, matcherName, filterType); + keypoint.buildReference(I); + + vpImage<unsigned char> Idisp; + Idisp.resize(I.getHeight(), 2*I.getWidth()); + Idisp.insert(I, vpImagePoint(0, 0)); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + + vpDisplayOpenCV d(Idisp, 0, 0, "Homography from matched keypoints") ; + vpDisplay::display(Idisp); + vpDisplay::flush(Idisp); + + //! [Set coordinates] + vpImagePoint corner_ref[4]; + corner_ref[0].set_ij(115, 64); + corner_ref[1].set_ij( 83, 253); + corner_ref[2].set_ij(282, 307); + corner_ref[3].set_ij(330, 72); + //! [Set coordinates] + //! [Display] + for (unsigned int i=0; i<4; i++) { + vpDisplay::displayCross(Idisp, corner_ref[i], 12, vpColor::red); + } + vpDisplay::flush(Idisp); + //! [Display] + + //! [Camera] + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + //! [Camera] + + vpHomography curHref; + + while ( ! reader.end() ) + { + reader.acquire(I); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + vpDisplay::display(Idisp); + vpDisplay::displayLine(Idisp, vpImagePoint(0, I.getWidth()), vpImagePoint(I.getHeight(), I.getWidth()), vpColor::white, 2); + + //! [Matching] + unsigned int nbMatch = keypoint.matchPoint(I); + std::cout << "Nb matches: " << nbMatch << std::endl; + //! [Matching] + + std::vector<vpImagePoint> iPref(nbMatch), iPcur(nbMatch); // Coordinates in pixels (for display only) + //! [Allocation] + std::vector<double> mPref_x(nbMatch), mPref_y(nbMatch); + std::vector<double> mPcur_x(nbMatch), mPcur_y(nbMatch); + std::vector<bool> inliers(nbMatch); + //! [Allocation] + + for (unsigned int i = 0; i < nbMatch; i++) { + vpImagePoint matched_ref, matched_cur; + keypoint.getMatchedPoints(i, matched_ref, matched_cur); + //! [Pixel conversion] + vpPixelMeterConversion::convertPoint(cam, matched_ref, mPref_x[i], mPref_y[i]); + vpPixelMeterConversion::convertPoint(cam, matched_cur, mPcur_x[i], mPcur_y[i]); + //! [Pixel conversion] + + // Store the image coordinates in pixel of the matched points + iPref[i] = matched_ref; + iPcur[i] = matched_cur; + } + + //! [Homography estimation] + try{ + double residual; + if (method == 0) + vpHomography::ransac(mPref_x, mPref_y, mPcur_x, mPcur_y, curHref, inliers, residual, + (unsigned int)(mPref_x.size()*0.25), 2.0/cam.get_px(), true); + else + vpHomography::robust(mPref_x, mPref_y, mPcur_x, mPcur_y, curHref, inliers, residual, + 0.4, 4, true); + } catch(...) + { + std::cout << "Cannot compute homography from matches..." << std::endl; + } + + //! [Homography estimation] + + //! [Projection] + vpImagePoint corner_cur[4]; + for (int i=0; i< 4; i++) { + corner_cur[i] = vpHomography::project(cam, curHref, corner_ref[i]); + } + //! [Projection] + + //! [Display contour] + vpImagePoint offset(0, I.getWidth()); + for (int i=0; i< 4; i++) { + vpDisplay::displayLine(Idisp, + corner_cur[i] + offset, + corner_cur[(i+1)%4] + offset, + vpColor::blue, 3); + } + //! [Display contour] + + //! [Display matches] + for (unsigned int i = 0; i < nbMatch; i++) { + if(inliers[i] == true) + vpDisplay::displayLine(Idisp, iPref[i], iPcur[i] + offset, vpColor::green); + else + vpDisplay::displayLine(Idisp, iPref[i], iPcur[i] + offset, vpColor::red); + } + //! [Display matches] + + vpDisplay::flush(Idisp); + + if (vpDisplay::getClick(Idisp, false)) + break; + } + + vpDisplay::getClick(Idisp); +#else + (void)argc; (void)argv; +#endif + return 0; +} diff --git a/tutorial/detection/matching/tutorial-matching-keypoint.cpp b/tutorial/detection/matching/tutorial-matching-keypoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a70938f0a430b9818f687503da0db6b20c7292f7 --- /dev/null +++ b/tutorial/detection/matching/tutorial-matching-keypoint.cpp @@ -0,0 +1,81 @@ +//! \example tutorial-matching-keypoint.cpp +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> +#include <visp/vpImageIo.h> +//! [Include] +#include <visp/vpKeyPoint.h> +//! [Include] + +int main() { + //! [Define] +#if (VISP_HAVE_OPENCV_VERSION >= 0x020101) + //! [Define] + vpImage<unsigned char> I; + + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + reader.acquire(I); + + //! [Construction] + const std::string detectorName = "ORB"; + const std::string extractorName = "ORB"; + //Hamming distance must be used with ORB + const std::string matcherName = "BruteForce-Hamming"; + vpKeyPoint::vpFilterMatchingType filterType = vpKeyPoint::ratioDistanceThreshold; + vpKeyPoint keypoint(detectorName, extractorName, matcherName, filterType); + std::cout << "Reference keypoints=" << keypoint.buildReference(I) << std::endl; + //! [Construction] + + //! [Create image] + vpImage<unsigned char> Idisp; + Idisp.resize(I.getHeight(), 2*I.getWidth()); + Idisp.insert(I, vpImagePoint(0, 0)); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Create image] + //! [Init display] + vpDisplayOpenCV d(Idisp, 0, 0, "Matching keypoints with ORB keypoints") ; + vpDisplay::display(Idisp); + vpDisplay::flush(Idisp); + //! [Init display] + + while ( ! reader.end() ) + { + //! [Acquisition] + reader.acquire(I); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Acquisition] + + //! [Display] + vpDisplay::display(Idisp); + vpDisplay::displayLine(Idisp, vpImagePoint(0, I.getWidth()), vpImagePoint(I.getHeight(), I.getWidth()), vpColor::white, 2); + //! [Display] + + //! [Matching] + unsigned int nbMatch = keypoint.matchPoint(I); + //! [Matching] + + std::cout << "Matches=" << nbMatch << std::endl; + + //! [Get matches] + vpImagePoint iPref, iPcur; + for (unsigned int i = 0; i < nbMatch; i++) + { + keypoint.getMatchedPoints(i, iPref, iPcur); + //! [Get matches] + //! [Display matches] + vpDisplay::displayLine(Idisp, iPref, iPcur + vpImagePoint(0, I.getWidth()), vpColor::green); + //! [Display matches] + } + //! [Display flush] + vpDisplay::flush(Idisp); + //! [Display flush] + + if (vpDisplay::getClick(Idisp, false)) + break; + } + + vpDisplay::getClick(Idisp); +#endif + + return 0; +} diff --git a/tutorial/detection/matching/tutorial-matching-surf-deprecated.cpp b/tutorial/detection/matching/tutorial-matching-surf-deprecated.cpp new file mode 100755 index 0000000000000000000000000000000000000000..243e582f55f0fa3114df5121c18ddf840be9b125 --- /dev/null +++ b/tutorial/detection/matching/tutorial-matching-surf-deprecated.cpp @@ -0,0 +1,74 @@ +//! \example tutorial-matching-surf-deprecated.cpp +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> +//! [Include] +#include <visp/vpKeyPointSurf.h> +//! [Include] + +int main() +{ + //! [Define] +#if defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000) + //! [Define] + vpImage<unsigned char> I; + + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + reader.acquire(I); + + //! [Construction] + vpKeyPointSurf surf; + surf.buildReference(I); + //! [Construction] + + //! [Create image] + vpImage<unsigned char> Idisp; + Idisp.resize(I.getHeight(), 2*I.getWidth()); + Idisp.insert(I, vpImagePoint(0, 0)); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Create image] + //! [Init display] + vpDisplayOpenCV d(Idisp, 0, 0, "Matching surf keypoints") ; + vpDisplay::display(Idisp); + vpDisplay::flush(Idisp); + //! [Init display] + + while ( ! reader.end() ) + { + //! [Acquisition] + reader.acquire(I); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + //! [Acquisition] + + //! [Display] + vpDisplay::display(Idisp); + vpDisplay::displayLine(Idisp, vpImagePoint(0, I.getWidth()), vpImagePoint(I.getHeight(), I.getWidth()), vpColor::white, 2); + //! [Display] + + //! [Matching] + int nbMatch = surf.matchPoint(I); + //! [Matching] + + //! [Get matches] + vpImagePoint iPref, iPcur; + for (int i = 0; i < nbMatch; i++) + { + surf.getMatchedPoints(i, iPref, iPcur); + //! [Get matches] + //! [Display matches] + vpDisplay::displayLine(Idisp, iPref, iPcur + vpImagePoint(0, I.getWidth()), vpColor::green); + //! [Display matches] + } + //! [Display flush] + vpDisplay::flush(Idisp); + //! [Display flush] + + if (vpDisplay::getClick(Idisp, false)) + break; + } + + vpDisplay::getClick(Idisp); +#endif + + return 0; +} diff --git a/tutorial/detection/matching/tutorial-matching-surf-homography-deprecated.cpp b/tutorial/detection/matching/tutorial-matching-surf-homography-deprecated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7e17a68d1584c60c365e802444f965cdf952a88 --- /dev/null +++ b/tutorial/detection/matching/tutorial-matching-surf-homography-deprecated.cpp @@ -0,0 +1,138 @@ +//! \example tutorial-matching-surf-homography-deprecated.cpp +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpHomography.h> +#include <visp/vpKeyPointSurf.h> +#include <visp/vpPixelMeterConversion.h> +#include <visp/vpVideoReader.h> + +int main(int argc, const char **argv) +{ +#if defined(VISP_HAVE_OPENCV_NONFREE) && (VISP_HAVE_OPENCV_VERSION < 0x030000) + //! [Select method] + int method = 0; + + if (argc > 1) + method = atoi(argv[1]); + + if (method == 0) + std::cout << "Uses Ransac to estimate the homography" << std::endl; + else + std::cout << "Uses a robust scheme to estimate the homography" << std::endl; + //! [Select method] + + vpImage<unsigned char> I; + + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + reader.acquire(I); + + vpKeyPointSurf surf; + surf.buildReference(I); + + vpImage<unsigned char> Idisp; + Idisp.resize(I.getHeight(), 2*I.getWidth()); + Idisp.insert(I, vpImagePoint(0, 0)); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + + vpDisplayOpenCV d(Idisp, 0, 0, "Homography from matched Surf keypoints"); + vpDisplay::display(Idisp); + vpDisplay::flush(Idisp); + + //! [Set coordinates] + vpImagePoint corner_ref[4]; + corner_ref[0].set_ij(115, 64); + corner_ref[1].set_ij( 83, 253); + corner_ref[2].set_ij(282, 307); + corner_ref[3].set_ij(330, 72); + //! [Set coordinates] + //! [Display] + for (unsigned int i=0; i<4; i++) { + vpDisplay::displayCross(Idisp, corner_ref[i], 12, vpColor::red); + } + vpDisplay::flush(Idisp); + //! [Display] + + //! [Camera] + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + //! [Camera] + + vpHomography curHref; + while ( ! reader.end() ) + { + reader.acquire(I); + Idisp.insert(I, vpImagePoint(0, I.getWidth())); + vpDisplay::display(Idisp); + vpDisplay::displayLine(Idisp, vpImagePoint(0, I.getWidth()), vpImagePoint(I.getHeight(), I.getWidth()), vpColor::white, 2); + + //! [Matching] + unsigned int nbMatch = surf.matchPoint(I); + //! [Matching] + + std::vector<vpImagePoint> iPref(nbMatch), iPcur(nbMatch); // Coordinates in pixels (for display only) + //! [Allocation] + std::vector<double> mPref_x(nbMatch), mPref_y(nbMatch); + std::vector<double> mPcur_x(nbMatch), mPcur_y(nbMatch); + std::vector<bool> inliers(nbMatch); + //! [Allocation] + + for (unsigned int i = 0; i < nbMatch; i++) { + vpImagePoint matched_ref, matched_cur; + surf.getMatchedPoints(i, matched_ref, matched_cur); + //! [Pixel conversion] + vpPixelMeterConversion::convertPoint(cam, matched_ref, mPref_x[i], mPref_y[i]); + vpPixelMeterConversion::convertPoint(cam, matched_cur, mPcur_x[i], mPcur_y[i]); + //! [Pixel conversion] + + // Store the image coordinates in pixel of the matched points + iPref[i] = matched_ref; + iPcur[i] = matched_cur; + } + + //! [Homography estimation] + double residual; + if (method == 0) + vpHomography::ransac(mPref_x, mPref_y, mPcur_x, mPcur_y, curHref, inliers, residual, + (unsigned int)mPref_x.size()/2, 2.0/cam.get_px(), true); + else + vpHomography::robust(mPref_x, mPref_y, mPcur_x, mPcur_y, curHref, inliers, residual, + 0.4, 4, true); + //! [Homography estimation] + + //! [Projection] + vpImagePoint corner_cur[4]; + for (int i=0; i< 4; i++) { + corner_cur[i] = vpHomography::project(cam, curHref, corner_ref[i]); + } + //! [Projection] + + //! [Display contour] + vpImagePoint offset(0, I.getWidth()); + for (int i=0; i< 4; i++) { + vpDisplay::displayLine(Idisp, + corner_cur[i] + offset, + corner_cur[(i+1)%4] + offset, + vpColor::blue, 3); + } + //! [Display contour] + + //! [Display matches] + for (unsigned int i = 0; i < nbMatch; i++) { + if(inliers[i] == true) + vpDisplay::displayLine(Idisp, iPref[i], iPcur[i] + offset, vpColor::green); + else + vpDisplay::displayLine(Idisp, iPref[i], iPcur[i] + offset, vpColor::red); + } + //! [Display matches] + + vpDisplay::flush(Idisp); + + if (vpDisplay::getClick(Idisp, false)) + break; + } + + vpDisplay::getClick(Idisp); +#else + (void)argc; (void)argv; +#endif + return 0; +} diff --git a/tutorial/detection/matching/video-postcard.mpeg b/tutorial/detection/matching/video-postcard.mpeg new file mode 100644 index 0000000000000000000000000000000000000000..21fca4da9ab753228612066dac3ce24272eff30d Binary files /dev/null and b/tutorial/detection/matching/video-postcard.mpeg differ diff --git a/tutorial/detection/object/CMakeLists.txt b/tutorial/detection/object/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b6d8a2e5f1747c0766a1b3ede8d26b5c2bcb2a14 --- /dev/null +++ b/tutorial/detection/object/CMakeLists.txt @@ -0,0 +1,29 @@ +project(tutorial-detection-object) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +# set the list of source files +set(tutorial_cpp + tutorial-detection-object-mbt.cpp) + +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/detection-config.xml") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/detection-config-SIFT.xml") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.wrl") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init") +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.mpg") + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-detection-object-mbt.cpp ${data}) +endforeach() diff --git a/tutorial/detection/object/detection-config-SIFT.xml b/tutorial/detection/object/detection-config-SIFT.xml new file mode 100644 index 0000000000000000000000000000000000000000..7417c4aca5c3bd2d2d7af3f28bde2dc2e1f899f4 --- /dev/null +++ b/tutorial/detection/object/detection-config-SIFT.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<conf> + <detector> + <name>SIFT</name> + </detector> + + <extractor> + <name>SIFT</name> + </extractor> + + <matcher> + <name>BruteForce</name> + <matching_method>ratioDistanceThreshold</matching_method> + <matchingRatioThreshold>0.8</matchingRatioThreshold> + </matcher> + + <ransac> + <useRansacVVS>1</useRansacVVS> + <useRansacConsensusPercentage>1</useRansacConsensusPercentage> + <ransacConsensusPercentage>20.0</ransacConsensusPercentage> + <nbRansacIterations>200</nbRansacIterations> + <ransacThreshold>0.005</ransacThreshold> + </ransac> +</conf> + diff --git a/tutorial/detection/object/detection-config.xml b/tutorial/detection/object/detection-config.xml new file mode 100644 index 0000000000000000000000000000000000000000..183c8dea732a7f56fac1e2246b9d9536281d9232 --- /dev/null +++ b/tutorial/detection/object/detection-config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<conf> + <detector> + <name>FAST</name> + </detector> + + <extractor> + <name>ORB</name> + </extractor> + + <matcher> + <name>BruteForce-Hamming</name> + <matching_method>ratioDistanceThreshold</matching_method> + <matchingRatioThreshold>0.8</matchingRatioThreshold> + </matcher> + + <ransac> + <useRansacVVS>1</useRansacVVS> + <useRansacConsensusPercentage>1</useRansacConsensusPercentage> + <ransacConsensusPercentage>20.0</ransacConsensusPercentage> + <nbRansacIterations>200</nbRansacIterations> + <ransacThreshold>0.005</ransacThreshold> + </ransac> +</conf> + diff --git a/tutorial/detection/object/teabox.cao b/tutorial/detection/object/teabox.cao new file mode 100644 index 0000000000000000000000000000000000000000..83c1fdf783e68e78dfa381d4c326f07d88fa8d3f --- /dev/null +++ b/tutorial/detection/object/teabox.cao @@ -0,0 +1,27 @@ +V1 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z +0 0 -0.08 +0.165 0 -0.08 +0.165 0 0 +0.165 0.068 0 +0.165 0.068 -0.08 +0 0.068 -0.08 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +6 # Number of faces +4 0 1 2 3 # Face 0: [number of points] [index of the 3D points]... +4 1 6 5 2 +4 4 5 6 7 +4 0 3 4 7 +4 5 4 3 2 +4 0 7 6 1 # Face 5 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/detection/object/teabox.init b/tutorial/detection/object/teabox.init new file mode 100644 index 0000000000000000000000000000000000000000..f109800a0eab96b97050f4872839f09b0a0c2bd3 --- /dev/null +++ b/tutorial/detection/object/teabox.init @@ -0,0 +1,5 @@ +4 # Number of points +0 0 0 # Point 0 +0.165 0 0 # Point 3 +0.165 0 -0.08 # Point 2 +0.165 0.068 -0.08 # Point 5 diff --git a/tutorial/detection/object/teabox.mpg b/tutorial/detection/object/teabox.mpg new file mode 100644 index 0000000000000000000000000000000000000000..50fb8ad2d9e245e2bcc06b94d22763a07d9b6191 Binary files /dev/null and b/tutorial/detection/object/teabox.mpg differ diff --git a/tutorial/detection/object/teabox.wrl b/tutorial/detection/object/teabox.wrl new file mode 100644 index 0000000000000000000000000000000000000000..a452ea0b7c54d955211974b893b38cf1e3df1eed --- /dev/null +++ b/tutorial/detection/object/teabox.wrl @@ -0,0 +1,33 @@ +#VRML V2.0 utf8 + +DEF fst_0 Group { +children [ + +# Object "teabox" +Shape { + +geometry DEF cube IndexedFaceSet { + +coord Coordinate { +point [ +0 0 0 , +0 0 -0.08, +0.165 0 -0.08, +0.165 0 0 , +0.165 0.068 0 , +0.165 0.068 -0.08, +0 0.068 -0.08, +0 0.068 0 ] +} + +coordIndex [ + 0,1,2,3,-1, + 1,6,5,2,-1, + 4,5,6,7,-1, + 0,3,4,7,-1, + 5,4,3,2,-1, + 0,7,6,1,-1]} +} + +] +} diff --git a/tutorial/detection/object/teabox.xml b/tutorial/detection/object/teabox.xml new file mode 100644 index 0000000000000000000000000000000000000000..6e1762b42c5ba2da16e3f001b0b62c9e9d64b294 --- /dev/null +++ b/tutorial/detection/object/teabox.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<conf> + <klt> + <mask_border>5</mask_border> + <max_features>300</max_features> + <window_size>5</window_size> + <quality>0.015</quality> + <min_distance>8</min_distance> + <harris>0.01</harris> + <size_block>3</size_block> + <pyramid_lvl>3</pyramid_lvl> + </klt> + <camera> + <u0>325.66776</u0> + <v0>243.69727</v0> + <px>839.21470</px> + <py>839.44555</py> + </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> +</conf> + diff --git a/tutorial/detection/object/tutorial-detection-object-mbt.cpp b/tutorial/detection/object/tutorial-detection-object-mbt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c05e3b8071b0c7aefd58b614671f7a0b6f3358f5 --- /dev/null +++ b/tutorial/detection/object/tutorial-detection-object-mbt.cpp @@ -0,0 +1,241 @@ +//! \example tutorial-detection-object-mbt.cpp +#include <visp/vpConfig.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpMbEdgeTracker.h> +#include <visp/vpVideoReader.h> +#include <visp/vpKeyPoint.h> + + +int main(int argc, char ** argv) { +#if defined(VISP_HAVE_OPENCV) && ((VISP_HAVE_OPENCV_VERSION >= 0x020100) || defined(VISP_HAVE_FFMPEG)) + //! [MBT code] + try { + std::string videoname = "teabox.mpg"; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--name") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + std::string parentname = vpIoTools::getParent(videoname); + std::string objectname = vpIoTools::getNameWE(videoname); + + if(! parentname.empty()) + objectname = parentname + "/" + objectname; + + std::cout << "Video name: " << videoname << std::endl; + std::cout << "Tracker requested config files: " << objectname + << ".[init," +#ifdef VISP_HAVE_XML2 + << "xml," +#endif + << "cao or wrl]" << std::endl; + std::cout << "Tracker optional config files: " << objectname << ".[ppm]" << std::endl; + + vpImage<unsigned char> I; + vpCameraParameters cam; + vpHomogeneousMatrix cMo; + + vpVideoReader g; + g.setFileName(videoname); + g.open(I); + +#if defined(VISP_HAVE_X11) + vpDisplayX display; +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI display; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV display; +#else + std::cout << "No image viewer is available..." << std::endl; + return 0; +#endif + + display.init(I, 100, 100,"Model-based edge tracker"); + + vpMbEdgeTracker tracker; + bool usexml = false; +#ifdef VISP_HAVE_XML2 + if(vpIoTools::checkFilename(objectname + ".xml")) { + tracker.loadConfigFile(objectname + ".xml"); + tracker.getCameraParameters(cam); + usexml = true; + } +#endif + if (! usexml) { + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(8); + me.setThreshold(10000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + tracker.setMovingEdge(me); + cam.initPersProjWithoutDistortion(839, 839, 325, 243); + tracker.setCameraParameters(cam); + tracker.setAngleAppear( vpMath::rad(70) ); + tracker.setAngleDisappear( vpMath::rad(80) ); + tracker.setNearClippingDistance(0.1); + tracker.setFarClippingDistance(100.0); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + } + + tracker.setOgreVisibilityTest(false); + if(vpIoTools::checkFilename(objectname + ".cao")) + tracker.loadModel(objectname + ".cao"); + else if(vpIoTools::checkFilename(objectname + ".wrl")) + tracker.loadModel(objectname + ".wrl"); + tracker.setDisplayFeatures(true); + tracker.initClick(I, objectname + ".init", true); + tracker.track(I); + //! [MBT code] + + //! [Keypoint selection] + std::string detectorName = "FAST"; + std::string extractorName = "ORB"; + std::string matcherName = "BruteForce-Hamming"; + std::string configurationFile = "detection-config.xml"; + +#if (defined(VISP_HAVE_OPENCV_NONFREE) || defined(VISP_HAVE_OPENCV_XFEATURES2D)) + detectorName = "SIFT"; + extractorName = "SIFT"; + matcherName = "BruteForce"; + configurationFile = "detection-config-SIFT.xml"; +#endif + //! [Keypoint selection] + + //! [Keypoint declaration] + vpKeyPoint keypoint_learning; + //! [Keypoint declaration] + if(usexml) { + //! [Keypoint xml config] +#ifdef VISP_HAVE_XML2 + keypoint_learning.loadConfigFile(configurationFile); +#endif + //! [Keypoint xml config] + } else { + //! [Keypoint code config] + keypoint_learning.setDetector(detectorName); + keypoint_learning.setExtractor(extractorName); + keypoint_learning.setMatcher(matcherName); + //! [Keypoint code config] + } + //! [Keypoint declaration and initialization] + + //! [Keypoints reference detection] + std::vector<cv::KeyPoint> trainKeyPoints; + double elapsedTime; + keypoint_learning.detect(I, trainKeyPoints, elapsedTime); + //! [Keypoints reference detection] + + //! [Keypoints selection on faces] + std::vector<vpPolygon> polygons; + std::vector<std::vector<vpPoint> > roisPt; + std::pair<std::vector<vpPolygon>, std::vector<std::vector<vpPoint> > > pair = tracker.getPolygonFaces(false); + polygons = pair.first; + roisPt = pair.second; + + std::vector<cv::Point3f> points3f; + tracker.getPose(cMo); + vpKeyPoint::compute3DForPointsInPolygons(cMo, cam, trainKeyPoints, polygons, roisPt, points3f); + //! [Keypoints selection on faces] + + //! [Keypoints build reference] + keypoint_learning.buildReference(I, trainKeyPoints, points3f); + //! [Keypoints build reference] + + //! [Save learning data] + keypoint_learning.saveLearningData("teabox_learning_data.bin", true); + //! [Save learning data] + + //! [Display reference keypoints] + vpDisplay::display(I); + for(std::vector<cv::KeyPoint>::const_iterator it = trainKeyPoints.begin(); it != trainKeyPoints.end(); ++it) { + vpDisplay::displayCross(I, (int) it->pt.y, (int) it->pt.x, 4, vpColor::red); + } + vpDisplay::displayText(I, 10, 10, "Learning step: keypoints are detected on visible teabox faces", vpColor::red); + vpDisplay::displayText(I, 30, 10, "Click to continue with detection...", vpColor::red); + vpDisplay::flush(I); + vpDisplay::getClick(I, true); + //! [Display reference keypoints] + + //! [Init keypoint detection] + vpKeyPoint keypoint_detection; + if(usexml) { +#ifdef VISP_HAVE_XML2 + keypoint_detection.loadConfigFile(configurationFile); +#endif + } else { + keypoint_detection.setDetector(detectorName); + keypoint_detection.setExtractor(extractorName); + keypoint_detection.setMatcher(matcherName); + keypoint_detection.setFilterMatchingType(vpKeyPoint::ratioDistanceThreshold); + keypoint_detection.setMatchingRatioThreshold(0.8); + keypoint_detection.setUseRansacVVS(true); + keypoint_detection.setUseRansacConsensusPercentage(true); + keypoint_detection.setRansacConsensusPercentage(20.0); + keypoint_detection.setRansacIteration(200); + keypoint_detection.setRansacThreshold(0.005); + } + //! [Init keypoint detection] + + //! [Load teabox learning data] + keypoint_detection.loadLearningData("teabox_learning_data.bin", true); + //! [Load teabox learning data] + + double error; + bool click_done = false; + + while(! g.end()) { + g.acquire(I); + vpDisplay::display(I); + + vpDisplay::displayText(I, 10, 10, "Detection and localization in process...", vpColor::red); + + //! [Matching and pose estimation] + if(keypoint_detection.matchPoint(I, cam, cMo, error, elapsedTime)) { + //! [Matching and pose estimation] + + //! [Tracker set pose] + tracker.setPose(I, cMo); + //! [Tracker set pose] + //! [Display] + tracker.display(I, cMo, cam, vpColor::red, 2); + vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); + //! [Display] + } + + vpDisplay::displayText(I, 30, 10, "A click to exit.", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) { + click_done = true; + break; + } + } + if (! click_done) + vpDisplay::getClick(I); +#ifdef VISP_HAVE_XML2 + vpXmlParser::cleanup(); +#endif +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + SoDB::finish(); +#endif + } + catch(vpException &e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV or ffmpeg and rebuild ViSP to use this example." << std::endl; +#endif + + return 0; +} diff --git a/tutorial/grabber/CMakeLists.txt b/tutorial/grabber/CMakeLists.txt index 8c3b9879497245a3ec5a4bf2c1400ce75c4100c5..bf7b7884e4d3f6ee317ad180973ce6388d135a94 100644 --- a/tutorial/grabber/CMakeLists.txt +++ b/tutorial/grabber/CMakeLists.txt @@ -3,24 +3,29 @@ project(tutorial-grabber) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-grabber-1394 tutorial-grabber-1394.cpp) -add_executable(tutorial-grabber-CMU1394 tutorial-grabber-CMU1394.cpp) -add_executable(tutorial-grabber-opencv tutorial-grabber-opencv.cpp) -add_executable(tutorial-grabber-opencv-bis tutorial-grabber-opencv-bis.cpp) -add_executable(tutorial-grabber-video tutorial-grabber-video.cpp) -add_executable(tutorial-grabber-v4l2 tutorial-grabber-v4l2.cpp) - -# copy the data -get_target_property(target_location tutorial-grabber-video LOCATION) -get_filename_component(target_location "${target_location}" PATH) -set(data "${CMAKE_CURRENT_SOURCE_DIR}/video.mpg" ) -add_custom_command( - TARGET tutorial-grabber-video - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" +# set the list of source files +set(tutorial_cpp + tutorial-grabber-1394.cpp + tutorial-grabber-1394-writer.cpp + tutorial-grabber-CMU1394.cpp + tutorial-grabber-opencv.cpp + tutorial-grabber-opencv-bis.cpp + tutorial-grabber-v4l2.cpp + tutorial-video-reader.cpp + tutorial-video-recorder.cpp ) + +set(tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/video.mpg") + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-video-reader.cpp ${data}) +endforeach() diff --git a/tutorial/grabber/tutorial-grabber-1394-writer.cpp b/tutorial/grabber/tutorial-grabber-1394-writer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d764956eb6301872165835af02de69242f5ef3ee --- /dev/null +++ b/tutorial/grabber/tutorial-grabber-1394-writer.cpp @@ -0,0 +1,62 @@ +/*! \example tutorial-grabber-1394-writer.cpp */ +#include <visp/vp1394TwoGrabber.h> +#include <visp/vpDisplayX.h> +#include <visp/vpImage.h> +#include <visp/vpVideoWriter.h> + +int main(int argc, char **) +{ +#ifdef VISP_HAVE_DC1394_2 + try { + bool save = false; + if(argc == 2) { + save = true; + } + + vpImage<unsigned char> I; // Create a gray level image container + bool reset = true; // Enable bus reset during construction (default) + vp1394TwoGrabber g(reset); // Create a grabber based on libdc1394-2.x third party lib + + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_60); + g.open(I); + + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + +#ifdef VISP_HAVE_X11 + vpDisplayX d(I); +#else + std::cout << "No image viewer is available..." << std::endl; +#endif + + vpVideoWriter writer; + writer.setFileName("./I%04d.pgm"); + if (save) + writer.open(I); + + while(1) { + g.acquire(I); + + if (save) + writer.saveFrame(I); + + vpDisplay::display(I); + vpDisplay::flush(I); + + if (vpDisplay::getClick(I, false)) + break; + } + + if (save) + writer.close(); + + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; +#endif + + return 0; +} diff --git a/tutorial/grabber/tutorial-grabber-1394.cpp b/tutorial/grabber/tutorial-grabber-1394.cpp index 36535bb3213fb1ed2c7d9ca5f914ea38765159e2..ea41cfc92469c3e614055980057d54fc4255d5b1 100644 --- a/tutorial/grabber/tutorial-grabber-1394.cpp +++ b/tutorial/grabber/tutorial-grabber-1394.cpp @@ -1,4 +1,4 @@ -/*! \example tutorial-grabber-1394.cpp */ +//! \example tutorial-grabber-1394.cpp #include <visp/vp1394TwoGrabber.h> #include <visp/vpDisplayX.h> #include <visp/vpImage.h> @@ -6,28 +6,43 @@ int main() { #ifdef VISP_HAVE_DC1394_2 - vpImage<unsigned char> I; // Create a gray level image container - bool reset = true; // Enable bus reset during construction (default) - vp1394TwoGrabber g(reset); // Create a grabber based on libdc1394-2.x third party lib + try { + vpImage<unsigned char> I; // Create a gray level image container + bool reset = true; // Enable bus reset during construction (default) + //! [vp1394TwoGrabber construction] + vp1394TwoGrabber g(reset); // Create a grabber based on libdc1394-2.x third party lib + //! [vp1394TwoGrabber construction] - g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); - g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_60); - g.open(I); + //! [vp1394TwoGrabber settings] + g.setVideoMode(vp1394TwoGrabber::vpVIDEO_MODE_640x480_MONO8); + g.setFramerate(vp1394TwoGrabber::vpFRAMERATE_60); + //! [vp1394TwoGrabber settings] + //! [vp1394TwoGrabber open] + g.open(I); + //! [vp1394TwoGrabber open] - std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; #ifdef VISP_HAVE_X11 - vpDisplayX d(I); + vpDisplayX d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - while(1) { - g.acquire(I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; + while(1) { + //! [vp1394TwoGrabber acquire] + g.acquire(I); + //! [vp1394TwoGrabber acquire] + vpDisplay::display(I); + vpDisplay::flush(I); + //! [vp1394TwoGrabber click to exit] + if (vpDisplay::getClick(I, false)) + break; + //! [vp1394TwoGrabber click to exit] + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/grabber/tutorial-grabber-CMU1394.cpp b/tutorial/grabber/tutorial-grabber-CMU1394.cpp index 5b596aeae73230bcba73cdff82081c2517c8dad8..a7b527dac8029a1b974f8a8d9b351c02a1a6be39 100644 --- a/tutorial/grabber/tutorial-grabber-CMU1394.cpp +++ b/tutorial/grabber/tutorial-grabber-CMU1394.cpp @@ -6,29 +6,34 @@ int main() { #ifdef VISP_HAVE_CMU1394 - vpImage<unsigned char> I; + try { + vpImage<unsigned char> I; - vp1394CMUGrabber g; - g.setVideoMode(0, 1); // 640x480 MONO8 - g.setAutoShutter(); - g.setAutoGain(); - g.setFramerate(4); // 30 fps - g.open(I); + vp1394CMUGrabber g; + g.setVideoMode(0, 1); // 640x480 MONO8 + g.setAutoShutter(); + g.setAutoGain(); + g.setFramerate(4); // 30 fps + g.open(I); - std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; #ifdef VISP_HAVE_GDI - vpDisplayGDI d(I); + vpDisplayGDI d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - while(1) { - g.acquire(I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) // A click to exit - break; + while(1) { + g.acquire(I); + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // A click to exit + break; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/grabber/tutorial-grabber-opencv-bis.cpp b/tutorial/grabber/tutorial-grabber-opencv-bis.cpp index 334be3ce7e7b2095f6673573688dd405c110839e..aed7af6ec267c5918a9886520b85f7254364be41 100644 --- a/tutorial/grabber/tutorial-grabber-opencv-bis.cpp +++ b/tutorial/grabber/tutorial-grabber-opencv-bis.cpp @@ -13,28 +13,35 @@ int main(int argc, char** argv) device = atoi(argv[1]); std::cout << "Use device: " << device << std::endl; -#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) - cv::VideoCapture cap(device); // open the default camera - if(!cap.isOpened()) { // check if we succeeded - std::cout << "Failed to open the camera" << std::endl; - return -1; - } - cv::Mat frame; - cap >> frame; // get a new frame from camera - std::cout << "Image size: " << frame.rows << " " << frame.cols << std::endl; - - //vpImage<vpRGBa> I; // for color images - vpImage<unsigned char> I; // for gray images - vpImageConvert::convert(frame, I); - vpDisplayOpenCV d(I); - for(;;) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020100) + try { + cv::VideoCapture cap(device); // open the default camera + if(!cap.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; cap >> frame; // get a new frame from camera - // Convert the image in ViSP format and display it + std::cout << "Image size: " << frame.rows << " " << frame.cols << std::endl; + + //vpImage<vpRGBa> I; // for color images + vpImage<unsigned char> I; // for gray images vpImageConvert::convert(frame, I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) // a click to exit - break; + + vpDisplayOpenCV d(I); + + for(;;) { + cap >> frame; // get a new frame from camera + // Convert the image in ViSP format and display it + vpImageConvert::convert(frame, I); + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) // a click to exit + break; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/grabber/tutorial-grabber-opencv.cpp b/tutorial/grabber/tutorial-grabber-opencv.cpp index 16356f172947f92ae62734b940b376e917f8eab3..2586e2421a693400b74917f9f3cc11c67b0760af 100644 --- a/tutorial/grabber/tutorial-grabber-opencv.cpp +++ b/tutorial/grabber/tutorial-grabber-opencv.cpp @@ -4,21 +4,26 @@ int main() { -#ifdef VISP_HAVE_OPENCV - vpImage<unsigned char> I; +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x020408) + try { + vpImage<unsigned char> I; - vpOpenCVGrabber g; - g.open(I); + vpOpenCVGrabber g; + g.open(I); - std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; - vpDisplayOpenCV d(I); + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + vpDisplayOpenCV d(I); - while(1) { - g.acquire(I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; + while(1) { + g.acquire(I); + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) + break; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/grabber/tutorial-grabber-v4l2.cpp b/tutorial/grabber/tutorial-grabber-v4l2.cpp index a05efe27abd31524dea08f7d9e7524e0e20c35c2..ff9087b123fb286f9a4d0082b7f4385cd3316948 100644 --- a/tutorial/grabber/tutorial-grabber-v4l2.cpp +++ b/tutorial/grabber/tutorial-grabber-v4l2.cpp @@ -6,24 +6,29 @@ int main() { #ifdef VISP_HAVE_V4L2 - vpImage<unsigned char> I; + try { + vpImage<unsigned char> I; - vpV4l2Grabber g; - g.open(I); + vpV4l2Grabber g; + g.open(I); - std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; #ifdef VISP_HAVE_X11 - vpDisplayX d(I); + vpDisplayX d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - while(1) { - g.acquire(I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) break; + while(1) { + g.acquire(I); + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) break; + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/grabber/tutorial-grabber-video.cpp b/tutorial/grabber/tutorial-grabber-video.cpp deleted file mode 100644 index 80b870c16f680ea073895130fd0182d2e73c0527..0000000000000000000000000000000000000000 --- a/tutorial/grabber/tutorial-grabber-video.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*! \example tutorial-grabber-video.cpp */ -#include <visp/vpDisplayX.h> -#include <visp/vpVideoReader.h> - -int main() -{ -#ifdef VISP_HAVE_FFMPEG - vpImage<unsigned char> I; - - vpVideoReader g; - g.setFileName("./video.mpg"); - g.open(I); - - std::cout << "video framerate: " << g.getFramerate() << "Hz" << std::endl; - std::cout << "video dimension: " << I.getWidth() << " " << I.getHeight() << std::endl; - -#ifdef VISP_HAVE_X11 - vpDisplayX d(I); -#else - std::cout << "No image viewer is available..." << std::endl; -#endif - vpDisplay::setTitle(I, "Video grabber"); - while (! g.end() ) { - double t = vpTime::measureTimeMs(); - g.acquire(I); - vpDisplay::display(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) break; - vpTime::wait(t, 1000. / g.getFramerate()); - } -#endif -} diff --git a/tutorial/grabber/tutorial-video-reader.cpp b/tutorial/grabber/tutorial-video-reader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb7dd46887e7e7908896156486efa40db4a6c1c7 --- /dev/null +++ b/tutorial/grabber/tutorial-video-reader.cpp @@ -0,0 +1,80 @@ +//! \example tutorial-video-reader.cpp +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +//! [Include] +#include <visp/vpTime.h> +#include <visp/vpVideoReader.h> +//! [Include] + +/*! + This example allows to read and display a video from a file. + It only requires that ViSP is build with OpenCV or ffmpeg. + + Example: ./tutorial-video-reader --name video.mpg + */ +int main(int argc, char** argv) +{ +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) || defined(VISP_HAVE_FFMPEG) + try { + std::string videoname = "video.mpg"; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--name") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + + vpImage<unsigned char> I; + //! [vpVideoReader construction] + vpVideoReader g; + //! [vpVideoReader construction] + //! [vpVideoReader setting] + g.setFileName(videoname); + //! [vpVideoReader setting] + //! [vpVideoReader open] + g.open(I); + //! [vpVideoReader open] + std::cout << "video name: " << videoname << std::endl; + std::cout << "video framerate: " << g.getFramerate() << "Hz" << std::endl; + std::cout << "video dimension: " << I.getWidth() << " " << I.getHeight() << std::endl; + +#ifdef VISP_HAVE_X11 + vpDisplayX d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#else + std::cout << "No image viewer is available..." << std::endl; +#endif + vpDisplay::setTitle(I, "Video reader"); + //! [vpVideoReader while loop] + while (! g.end() ) { + //! [vpVideoReader while loop] + //! [vpVideoReader loop start time] + double t = vpTime::measureTimeMs(); + //! [vpVideoReader loop start time] + //! [vpVideoReader acquire] + g.acquire(I); + //! [vpVideoReader acquire] + vpDisplay::display(I); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) break; + //! [vpVideoReader loop rate] + vpTime::wait(t, 1000. / g.getFramerate()); + //! [vpVideoReader loop rate] + } + } + catch(vpException e) { + std::cout << e.getMessage() << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV or ffmpeg and rebuild ViSP to use this example." << std::endl; +#endif +} diff --git a/tutorial/grabber/tutorial-video-recorder.cpp b/tutorial/grabber/tutorial-video-recorder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b00d4d7f1580795c835b5b5a2e5ee58e7673402c --- /dev/null +++ b/tutorial/grabber/tutorial-video-recorder.cpp @@ -0,0 +1,122 @@ +/*! \example tutorial-video-recorder.cpp */ +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayGTK.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpTime.h> +#include <visp/vpVideoWriter.h> +#include <visp/vpV4l2Grabber.h> + +/*! + This example allows to record a video from a camera. + It only requires that ViSP is build with OpenCV. + + Example: ./tutorial-video-recorder --device 0 --name myvideo.mpeg + */ +int main(int argc, const char *argv[]) +{ +#if ((defined(VISP_HAVE_V4L2) || (VISP_HAVE_OPENCV_VERSION >= 0x020100)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GTK))) + std::string opt_videoname = "video-recorded.mpg"; + int opt_device = 0; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--device") + opt_device = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--name") + opt_videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--device <device number>] [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + + std::cout << "Use device: " << opt_device << std::endl; + std::cout << "Record video in: " << opt_videoname << std::endl; + + try { + //vpImage<vpRGBa> I; // for color images + vpImage<unsigned char> I; // for gray images + +#if defined(VISP_HAVE_V4L2) + vpV4l2Grabber g; + std::ostringstream device; + device << "/dev/video" << opt_device; + g.setDevice(device.str()); + g.open(I); + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture g(opt_device); + if(!g.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + + std::cout << "Image size: " << I.getWidth() << " " << I.getHeight() << std::endl; + +#if defined(VISP_HAVE_X11) + vpDisplayX d; +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d; +#elif defined(VISP_HAVE_GTK) + vpDisplayGTK d; +#endif + d.init(I, 0, 0, "Camera view"); + vpVideoWriter writer; + +#ifdef VISP_HAVE_FFMPEG + // Set up the bit rate + writer.setBitRate(1000000); + // Set up the codec to use +# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,51,110) // libavcodec 54.51.100 + writer.setCodec(CODEC_ID_MPEG2VIDEO); +# else + writer.setCodec(AV_CODEC_ID_MPEG2VIDEO); +# endif +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + writer.setCodec( cv::VideoWriter::fourcc('P','I','M','1') ); // MPEG-1 codec +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + writer.setCodec( CV_FOURCC('P','I','M','1') ); +#endif + writer.setFileName(opt_videoname); + writer.open(I); + bool recording = false; + + for(;;) { +#if defined(VISP_HAVE_V4L2) + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + g >> frame; + vpImageConvert::convert(frame, I); +#endif + vpDisplay::display(I); + if (recording == false) { + vpDisplay::displayText(I, 10, 10, "A click to start recording", vpColor::green); + if (vpDisplay::getClick(I, false)) + recording = true; + } + else { + writer.saveFrame(I); + vpDisplay::displayText(I, 10, 10, "Recording: A click to stop and exit", vpColor::red); + if (vpDisplay::getClick(I, false)) + break; + } + + vpDisplay::flush(I); + } + std::cout << "The video was recorded in \"" << opt_videoname << "\"" << std::endl; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV and rebuild ViSP to use this example." << std::endl; +#endif +} diff --git a/tutorial/image/CMakeLists.txt b/tutorial/image/CMakeLists.txt index a5f0bb85708e7185af486fbffb1c448ae45ca218..3f72d7f0fd64f429f266b13c1b6d0f6eb65a6f8f 100644 --- a/tutorial/image/CMakeLists.txt +++ b/tutorial/image/CMakeLists.txt @@ -3,29 +3,32 @@ project(tutorial-image) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-image-converter tutorial-image-converter.cpp) -add_executable(tutorial-image-manipulation tutorial-image-manipulation.cpp) -add_executable(tutorial-image-reader tutorial-image-reader.cpp) -add_executable(tutorial-image-viewer tutorial-image-viewer.cpp) -add_executable(tutorial-undistort tutorial-undistort.cpp) -add_executable(tutorial-viewer tutorial-viewer.cpp) +set(tutorial_cpp + tutorial-image-converter.cpp + tutorial-image-manipulation.cpp + tutorial-image-reader.cpp + tutorial-image-viewer.cpp + tutorial-undistort.cpp + tutorial-viewer.cpp + tutorial-image-filter.cpp) -# copy the data -get_target_property(target_location tutorial-image-reader LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/lena.bmp" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/lena.ppm" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/chessboard.pgm" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/camera.xml" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-image-reader - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/lena.bmp" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/lena.ppm" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/lena.pgm" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/lena.jpeg" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/lena.png" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/chessboard.pgm" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/camera.xml" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-image-reader.cpp ${data}) endforeach() diff --git a/tutorial/image/lena.jpeg b/tutorial/image/lena.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..3ca83964d5876a3b61a3bbce47436c3308193338 Binary files /dev/null and b/tutorial/image/lena.jpeg differ diff --git a/tutorial/image/lena.pgm b/tutorial/image/lena.pgm new file mode 100644 index 0000000000000000000000000000000000000000..992d1f0a1e42249eb221a30fe33f44e1407f3533 --- /dev/null +++ b/tutorial/image/lena.pgm @@ -0,0 +1,4 @@ +P5 +225 225 +255 + ŸŸžž¡Ÿœ™˜˜™›¡¤§ª¬¬«ª˜’†td\^bghhijkkklkjikmqswxy{|~€‚‚‚ƒƒƒ‚‚ƒƒ„„„„‡‡‡‡‡‡‡‡††††††††‡†††………„ƒƒƒƒƒƒƒƒ…………………………„„ƒ‚‚€€~}|{{vqkhkt~…–”•œ£¢›’———˜˜™™™šš™™™˜˜˜››››››››»ÑÓÑÙßÜÛtijlqzxxyz{{||yyzz{{{{zzz{|}}~‡yusq… ¨‘ ŸŸžžž¡Ÿœ™˜™š›¢¤§ª¬«ª©˜’…td\]agghijjkklkiijmprwxyz|~‚‚ƒƒƒ‚‚ƒƒ„„„„‡‡‡‡‡‡‡‡……………………††††……„„ƒƒƒƒƒƒƒƒ…………………………„„ƒ‚€}|{{vrlhks|ƒ“’•›¢¢›”˜˜˜˜™™™šššš™™™˜˜››››››››´ÌÔÔ×ÛÜÆŸudhqvxxxyyz{||yyzz{{{{zz{||}~~wsxxtˆ©¸“ŸŸŸŸžžž ž›™˜š›£¥¨ª««©¨—‘„sc[\`gghiijkkkjihjlprwwyz|}~‚‚‚ƒƒ‚‚ƒƒ„„„„††††††††……………………†††………„„ƒƒƒƒƒƒƒƒ…………………………„ƒ‚‚€~}|{wsmjkqyŽ“š ¡œ–™™™ššš››››ššš™™™ššššššššœªÀÓ×ÔÖÜͬbdtytwxxyzz{{yyzz{{{{z{{|}~~ƒwtz€‰‹‚\ŸŸžžŸ›™™›Ÿ¤¦¨ª«©§¦–ƒrbZ[_ggghijjkkjhhiloqvwxz{}~€€‚‚‚‚‚‚ƒƒ„„„„††††††††……………………††………„„„‚‚‚‚‚‚‚‚………………………„„ƒ‚€€€€€€€€€‚€~}||xtokkpuyˆ‹’™žŸœ™šš››œœœœœ›››šššššššššššššŸ³Í×ÑÒÛÕÀ–nbntovwwxyzz{yyzz{{{{{||}~Œ{€rR51žžœœœœš™šœŸ¡¥§©ªª¨¥¤”Ž‚p`YZ^ffghiijjjigghknpvwxy{}~~€€€‚‚‚‚ƒƒ„„„„……………………„„„„„„„„………„„„ƒƒ‚‚‚‚‚‚‚‚……………………„„ƒ‚‚€€€€€€€€€€‚‚€~}||xuqmlmqs‡˜œœœœžžžœœœœ›››ššššššššš™—¦ÁÑÐÒÙÚÔµ‡hekkvvwwxyyzyyzz{{{{||}~€€}‡Š~eG0(0œœ›››œ›š™›ž¡£¦¨©ª©§¤¢“o_XY]ffgghijjihgfgjmovvwy{|}~€€€‚‚‚ƒƒ„„„„……………………„„„„„„„„……„„„ƒƒƒ‚‚‚‚‚‚‚‚……………………„ƒƒ‚€€€ƒ‚€~}|yvrnlllm{ƒ—šœž žžžŸŸŸ œœœ››™™™™™™™™—”œ±ÆÑÕ×ÜÜÍ¥zeekuuvwwxyyyyzz{{{{}}~€€Š‚fG.*7/œœœ›››š›š™™›Ÿ¢¥§¨ªª©¦¢ ’Œ€n^WX\effghiijigfegimouvwyz|}~€€€‚‚ƒƒ„„„„……………………„„„„„„„„……„„ƒƒƒƒ……………………ƒƒƒ‚€€ƒ‚‚€~}}zwtpljiiv€Ž–™šŸ£ŸŸŸŸ ¡¡žžœœœ™™™™™™™™•”•¡ºÓÚÖÚÛÕ½“ofmtuuvwxxxyyzz{{{{}~~€‚†|^A7.*41œœœ›››šš›š™™›Ÿ£¥¨©ªª¨¥¢Ÿ’Œn^VW[effghiiihgfefilnuvwyz|}~€€€‚‚ƒƒ„„„„……………………ƒƒƒƒƒƒƒƒ…„„„ƒƒƒ‚……………………ƒƒ‚‚€ƒƒ‚~}}zxtpliggs–˜šŸ¤ŸŸ ¡¡¡žžžœœ™™™™™™™™”•’—³ÔÞÕØ××Ê¥zjottuvvwxxyyzz{{{{~~€‚‚td?*12,13››œœœŸš™›Ÿ¤§¥¥¤£¢¢¡¡•‚qaYZ^acgikjihhgffhknpuvwxz|}}€€€‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„…………„„„ƒƒ‚‚‚ƒƒƒ‚‚ƒ„„…†‡‡‡ƒƒ‚‚€€€~~~€€~~~zyvspmjity‹“šžŸ¤¤£££¢¢¢ ŸŸžœ™™˜˜˜————žš–²ËÔÙÚÛÜÊŽhqhmqruz{zxwxz}‚{}‡‰|U>++0113-››œœŸ››œ ¤§¥¤¤£¢¡¡¡•‚qaYZ^acgikjihhgffhknpuvwxz|}}€€€‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„………ƒƒƒƒ‚‚‚ƒ‚‚‚€ƒƒ„„…†‡‡ƒƒ‚‚€€€~~~€€~~~zyvspmkisxŠ“™Ÿ££££¢¢¡¡ ŸŸžœœ™™™˜˜˜——–š˜’•©ÂÑ×ÛÝÜÑ«~ggmtvvwy|{zxwx{~{€ƒ…Œ‹w^F6,174.,4œœœœžžŸžŸ¢¥§¤¤¤£¢¡¡ ”‚paYZ^acgikjihhgffhknpuvwxz|}}€€€€‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒ„„………‚‚‚€€‚‚€€€€‚‚ƒ„„…††ƒƒ‚‚€€€~~~€€~~~zywtpmkjsx€Š’™ž££¢¢¢¡¡¡ Ÿžœœšš™™˜˜˜˜””–•”œ³ËÖÛÞÛØÎ gdkuzxstx|zyxy{~€|‚‡Šƒ`==/(/63..7œœœžžžŸŸŸ ¢¤¦§¤¤£¢¢¡ ”Ž‚p`XZ^acgikjihhgffhknpuvwxz|}}€€€€€‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„…€€€€€~‚‚ƒ„……ƒƒ‚‚€€€~~~€€~~~{ywtqnkjrw‰’˜œž¢¢¢¡¡ ŸŸžœœ››ššš™™™˜”‘”˜“¤¿ÕØÝÛÚÞ¾€eir{zssy|{yxy{~€…ƒ‰€]>0;,#'./285œžžžŸŸ ¢¤¥¦§§¤££¢¡ Ÿ”Žp`XY]acgikjihhgffhknpuvwxz|}}€€€€€€‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒ„„„~~}}}~~~}}€€€‚ƒƒ„ƒƒ‚‚€€€~~~€€~~~{zwtqnljqv~ˆ‘—›¡¡¡ ŸŸŸŸžœ›››››ššš™™•‘“˜’Œš¯ÐÑÙÚÙÞÒ©slmwzuv~|{yyy|‡‰‹~\7*/6+%*/.2:6žžžŸŸž¡¤§©©¨§££¢¢¡ ŸŸ“o_XY]acgikjihhgffhknpuvwxz|}}~€€€€€€€‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒ„„~}}}|||{~~~}}}||~€‚‚ƒƒƒ‚‚€€€~~~€€~~~{zxuqnlkpu~‡–šœ ŸŸŸžŸžžœ›››œœœ››ššš˜••–“•ÀÉÕØØÝÛÍ‘yjqwuv~}{zyz|‰Z5'+/.*-792/3:žžžŸŸŸž¡¦©«ª¨§££¢¡ ŸŸ“€o_WY\acgikjihhgffhknpuvwxz|}}~~€€€€€€‚‚‚ƒƒƒ„||||{{zz}}}|||{{~~~€‚ƒƒ‚‚€€€~~~€€~~~|zxurolkpu}‡–š› ŸŸŸžžžžžžœ››šœœ››››››—’”˜•ލÂÓÕÚßÞÝ·kjsqqx}|zyz|zt];')130.3<;2/38žžžŸŸŸž¢§«¬«©§£¢¢¡ ŸŸ“€o_WX\acgikjihhgffhknpuvwxz|}}~~~€€€€€€‚‚ƒƒƒƒ||{{{zzz}}|||{{{}}~€€ƒƒ‚‚€€€~~~€€~~~|{xurolkot}†–™› ŸŸŸžžžžžœ›ššœœ›››Ÿ˜•ž–„–¿ÒÓÞãÞÞÌœmgomlr}|zyz}|[:-,,2;:548502;1ŸŸ ¡¢¤¤¥¨©ªª©§¥¤¡ ŸŸŸŸ•Žk[TW]`bfhjihfgfeegjmottuvxyzz}}}~~~~~~€€€€€ƒ‚‚‚€€}|zxwvwx{}}|zysvz~€‰‡„~~~€€€€~~~yxvurqonpt|„‹’“™™š›œžžŸœœ›››žžœ››šš——–•“’‘‘ƒªÏØ×ÚáåØ¿†aekks~ut}}}„=81+),1498754210/ŸŸ ¡£¤¥¥§§¨¨§¥¤£¡¡ ŸŸŸ•Žk[TW]`bfhjihfgfeegjmptuuwxyzz}}}~~~~~€€€€€‚‚€~~€€~|{zzxy{{yvsqwxz}€‚„…‚‚‚‚|yv€€€€~~~~yxvtrponpt{„‹’“˜™™›œžžœœ››››œ›šš™—–•”“’‘‡»ÐÖÖÛãÛΡsejopswxzƒ‡xb95/++.3788653210/Ÿ ¡¢£¤¥¥¥¥¤££¢¡¡¡¡¡ ŸŸŸ•Žk[TW]`bfhjihfhgeegjnptuvwxyz{}}}~~~~€€€€€€‚‚~}|{}}}|||||~~|xtqtvz~€€~|}}~€€€€~~}}xxvtrponpt{ƒŠ’“——˜™›œœœœ››šššœœ›š™™˜–•”“’‘Œ¢ÁÓÕ×ÞÝÛÅ’jhqnmty~„~`@41.,-27;8764310// ¡¢£¤¥¦£¢ žžŸ¡¡¡¡ Ÿ•Žk[TW]`bfhjihfhgffgjnpuuvwyz{{}}}~~~€€€€€€~}|{z{{|}~€‚ƒ‚|zutsux~ƒ‡‚€}{|€„‡€€~~}}}xwvtrpnnos{ƒŠ‘“•–—˜™š›œ›››ššš™™›››š™˜˜—•”“’‘Ž‹“®ÊØÛÚááÛ³xdmlsszƒxY=1.-,-05:=765420/// ¡¢¤¥¦¦¡ž›˜—˜›¢¢¡¡¡ •Žk[TW]`bfhjihfhgffhknquvwxyz{{}}}~~~€€€€€€€€€€€‚„†ˆ‹Œ¢¤¨«¬«¦œŒ}vvz~~}||{{{€€~~~}}}|wwusqonmosz‚‰Ž‘’””•–—™™š›ššš™™™˜šš™™˜—––““’‘ŽŽ‘ž½ØÞØäàâÍ’kimvz~uV3'.++-/37;>654310../ ¡¢£¤¥¦§Ÿ›–’’”˜›¢¢¢¡¡¡ •Žk[TW]`bfhjihfihgfhkoqvvwxyz{|}}}~~~€€€€‚€€}~€‚„‡‰‰Ž‘”˜›žŸ£¦«¯³´´´Â½´§˜Š~xlqw|~}yw€€~~~}}}||wvusqommnsz‚‰Ž‘’’“”•–—˜˜šš™™˜˜˜˜™™˜——–••’’‘ŽŒŒ‘”¬ÍÚØâÜàÚ³ƒnosyW3$'*+,.148:<55420/.-/¡¡¢£¤¦¦§™“Ž‘•™£¢¢¢¡¡¡ •Žk[TW]`bfhjihfihgghloqvvwxz{||}}}~~~€€€‚‚€€{}€…ŠŽ‘“‘”˜¡¤¦©«¯³µ¶µ´«°¸½»´©¢‚€|zy{}€€~~}}}|||wvusqomlnrz‚‰Ž‘‘‘’“•–——™™™˜˜˜——˜˜——–•””‘‘ŽŒŒ‹Ž‹ž·ÏØßÝÝßѧquq]<(',,,.03678854310.-,/¡¡¢£¥¦§§œ˜‘Œ‹”˜££¢¢¡¡¡¡•Žk[TW]`bfhjihfihggiloqvwwyz{||}}}~~~€€€‚‚‚€€z|†“—š‰‹”™¡£ª¬¯±±°®¬²²²´¸¼Áĸ¨ynox€€€~~}}}|||wvtspomlnry‚‰Ž‘‘’“”•–—™™˜˜˜———˜——–•”””‘ŽŒ‹‹‡†–¥ÂØÝâÝßßÂs|]>2/,,1-/2467664431/.-,/šŸ¦««¦Ÿ›•‰…‰Š‘›¦¦¥¤¤£¢¢™‘€kXPRV^`dghhgffghjkmnouuvwxz{{z{{|}~~€z|‚~†u{xyz|}}}~}ƒ‹’–‘’”—šœžŸ§§¨ª«®¯²³´¶¸º¼¼ÂÇëŠtrxs}ƒ‚ƒ‡‚y}|zywwuusqomlkpt{ƒ‰Ž‘‘’”•––•–˜šš˜–”‘‘‘‘‘‘‘‘ŽŒŒŒ‹”‡…ŒŽ«ÎÕæÑÞäÏ·Ž\B'+76/23345667*06972-+3¡¨¬«¦Ÿš”„z‡Œ“œ¦¦¥¤¤£¢¢™‘€jXPQV\^beffedfghiklnntuvwxyz{z{{|}~~~ƒ}z€}~„w}‡“’Ž‹{‰ŒŒŒ’‘“–™›ž¦§¨ª«®¯²³´¶¸º¼½ÂÄÆÃ´š{fstƒ‚stƒ~}|{yxwvutsqomkkot{ƒŠŽ‘’‘‘’“”•––—™š™—”’‘‘‘‘‘‘‘‘ŽŽŒŒŒŒ”‰‡ŽŒ‹£ÃÖåÛßßà×—>4)&+12055544444/14431/./¡¥«®¬¥—‘|nv…—¦¦¥¤¤£¢¢˜jXOQUY[_bccbaefgijlmnstuvwxyzzz{|}}~~…y}„‹—˜š˜“Š€zx†‰ˆˆŠŽ‘”–™›œ¤¥§©«®¯°²³µ·¹»¼½Ã¿¾Åɽ¡Šj}‚tp{|p{zzyxwvvttrpnlkjnszƒŠ’“‘‘’’““””—˜™™—”‘ŽŽŽŒŒŒ”ŒŒ‘‡–®ÔÙÝãÛàà¡%+-++.287653210630..0233¦©®ª¢˜’‡pbmƒ’šŸ¦¦¥¤¤£¢¢˜iWNPUVY\_aa`_eefhjklmrstuvwxxzz{{|}}~ƒ}}}z–€ƒ“‹…v|ƒ†„ƒ…ˆ‹ŒŽ‘”–˜š¢¤¦¨«®°±³´µ·¹»¼½Ã¿½ÀÄÆÂ½|pqussvwwvvvvuutsqomkjimqy‚Š“•’’’’’’’’–—˜˜–’ŽŒŽŽŽŒŒ“Ž“…‹™ÅÓÙÞÝáËu#-0+).4:97520.-;4-),0457¨«®§œ‘ŠxcYh‚“› ¦¦¥¤¤£¢¢—Ž~hVNOTVX\_aa_^ddfgijklqqrtuvwwyzz{||}}}y‚v®ut|zwvw{€„v|ƒ„‚‚…ˆ‰‹Ž‘”–— ¢¤¨«¯±³³´¶·º»½¾½ÀÄÆÅÄÄÅÅ¡zjhjqxssssttttsrqomkihkpx‚Š‘•–”““’‘‘“”•–”’ŽŒŽŽŽ’‘“‡ˆŽ©ÏØÙäÛ*'+..,-16:97520.-;3+*/331-©«¬ª¢–‰eWTh’›¢¦¦¥¤¤£¢¢–Ž}hUMNSXZ]abba`cdefhjkkppqrtuvvyyzz{|}}{wƒ‚x§ljzz{{zxwvx~„…‚€„…‡‰‹Ž‘“”ž £§«¯²´´´¶¸º¼½¾¸º½ÂÆÈÇÆÇÏ»Šhgnmoopqrsstrqpnljhhinw‚‹’–˜•”“’‘ŽŽ’“”’ŽŽŽ‘’Œ”¼ÕäÚ—G42/--0467654210/60,0681)¨©ª¦ySMSj𣦦¥¤¤£¢¢•|gULNSZ\`cedcbbcdfgijkoopqstuuyyyz{||}|y~„€llwxyyz{||{†‡ƒ‚„ƒ…‡‰Œ‘’Ÿ¢¦«°³µ´µ¶¸º¼¾¾¸¸º¾ÂÇÊËÆÆÍʤtdpllmoprssqqomkihghmw‹’—™–•”’ŽŒ‡ŠŽ‘“”“’ŽŽŽ’““’¥ÉÏ“D%//00//14733333222/,.7@=. §¨¨£™‹}tHHTl‚Ž™¤¦¦¥¤¤£¢¢•|gTLNR\^beffedbcdegijjoopqrstuxyyz{||}}x……Yps‚}uppu|}‚ˆˆ…‚‚…‚ƒ…ˆ‹Ž‘œž¡¦«°³µ´µ¶¸º¼¾¾ºÁÈÈþ½¿ÃÄÄÆÆ³…Yjklnoqrsqpomkigggmv‹“˜š––”’ŽŒŒ„‡‹“•••ŽŽŽŽŽŽ“˜™—‘£·Š;*7*#*12003701123445**0<F@,1°¬¦Ÿ”hVOLVn„𣧍©©§¤ ž”Œ{fTLNSZ\_bddcbfeccdgjlopqrstuvxxyyzzzz}z„Ÿornnoprtwz|}‚ƒ„…†‡‡†‡ˆ‰‹ŒŽŽ•𠣤§®³³µ¸¼¾½º¸½ÁÄÄÁ¿ÂŽÇÎÊÎÛ¸tfU^leV|mlmnnliecdkw…–™šš˜—˜˜’ˆƒ€ƒ‰“”•ŒŽ”–’‘‘Š š‡Œ–”“ž v>-0,**-157520(+046520'9@@>, )s¬©¢™ŠvaSPMWn„™¢§¨©©§¤ ž”Œ{fTLNSZ\_bddcbfeccdgjlopqrstuvxyyyzzz{|~ŒŒrnskopruwz|}€‚ƒ„…††‡ˆ‰‹ŒŽŽ’˜Ÿ£¥©°¶±´¶·¹½ÃȾ¼½ÃÊÉÁ¸ÊÀº¶½ÕÝ˽ª`Q[wa|jklmkhecdkw„–™šš˜—˜˜’ˆ{yy~‡”••Ž“•’‘‘‘—’“™™™¢„T3/1-*-/35541.+.1454100=DB5#3\ž¦¤{gYQQNXo„™¢§¨©©§¤ ž”Œ{fTLNSZ\_bddcbfedcegjmopqrstuvyyyzzz{{}„uuitjqrsuxz{|z{}~€‚ƒ„†‡ˆ‰‹ŒŽŽŽ•¢¥ª²¹·»¾½ºº¾ÂÄÅÆÄÀ»¸·Ã¼ÅÕÖÕÜáéà§R[l\sghjkjgdbckw„–™™š˜—˜˜’ˆ€plkr€“•“Ž’”‘Ž‘‘’‘——šŸ —\-'.0.*234541.,013553106HK9(.Z©¢¡™†o]UTSPYp„˜ §¨©©§¤ ž”Œ{fTLNSZ\_bddcbgfddehkmopqrstuvyyzzz{{{…ˆjvfnpssuvxyz{wwy|~‚ƒ†‡ˆ‰‹ŒŽŽ‹’›¡¥«´»Á¿½º¹º¼½ÇÆÃ»µ·ÁÊÔËÒßÛÎÑÞÞàä‰PMiZceghhfcacjw„•˜™›™˜™˜“ˆ€g`[ct†“’‘‘’‘‘‘ŒŠ–œ•š¡˜r;$*,0+766420-,44555432;NI,+Tƒœ¦¡‘}gZWYURZp„Ž—Ÿ§¨©©§¤ ž”Œ{fTLNSZ\_bddcbgfedehkmopqrstuvzzzz{{{|wmtgfwuuvwxxyytvx{~ƒ…†‡ˆ‰‹ŒŽŽ‹’𠥫´»¾¹³³¸¾ÁÁ¼µ°´ÁÏ×ÚÙÓÏÓÕÏÐÙÛààÌYJaT_acefdbacjvƒŽ•˜™›™˜™™“‰€eXLPd{‰‘’‘‘’‘‘•——¢™xB+%)%*2-97531/..65545567FG<3L}—‘¢”ƒp`XY\WT\q„Ž–ž§¨©©§¤ ž”Œ{fTLNSZ\_bddcbgfedfhlnopqrstuvzzz{{|||yxjqqmfzwwwwxxxxtvy|€„†ˆ†‡ˆ‰‹ŒŽŽ“›¡¤©±¸°´º¿À»´®²µ¼ÄÍÔ××ÏÒÐÒÚÕÊÈÜáÞç§WPY[]`cdca`bjvƒŽ”—˜œš˜™™”‰lV@=Ql€ˆŽ‘”’Ž“‘‘’¤L%*0-'-4286310023543359<>T=:Uy’–‰˜–ˆr`WVXZYU]r„•§¨©©§¤ ž”Œ{fTLNSZ\_bddcbhgeefilnopqrstuvzz{{{|||opinnsovxxxxxxwwvwz~ƒ‡Š‹†‡ˆ‰‹ŒŽŽ–¡£§®´©·ÅÄ·«§©¾ÇÑÓÎÊËÎ××ÌÉÑÏÊÑÕÖääéŠZQXZ^abba`biuƒ”—˜œš™š™”ŠuX7.A_v’•“Ž•‘‘’““ ’_0%0/*/578641/025822126<BFTCS|“˜”‹•‘~fUQTWWZV]r„”œ§¨©©§¤ ž”Œ{fTLNSZ\_bddcbhgeefilnopqrstuvz{{{||||fllimwxqyyyxxwwwwx|€…‰ŒŽ†‡ˆ‰‹ŒŽŽ“˜ž¡¢¥¬±®º¾¯›š²ËÒÏÌÍÐÐÍÉÊÏÌÐÙÑËÙÔÏÎÜåÖsCWY\`aa`_biuƒ”—˜œš™šš”Š|Z3%7Wp|’–”ŽŒ•‘‘‹–˜œž{E*15(%6=9=530.038;10027>EJJRt’—™™Žƒq]TX][WWX]k~™œ¤¦§¨§¥¢ “‡ylZKKT\]^`acdeccdfghiinpsvwwutw‡tr}whoppqrsssvtrqqsvx„}xzƒŠŒŠŒŒŽ‘‡•š§µ³§»® Ÿ¬¾ÊÐÅÈÌÏÐÎËÉÍÍÍÍÌÌÌÌÓÒÙäÛâÇfYFO^WW_Y_jy…Œ’˜œš™˜š›—ކwV6,6H`sŠ”™—”””Ÿ“”žƒF'411113589=0%'3:6..686<JOK\m„“–“ŽžykZUY][VWX]k~™œ¤¥§¨§¥¢ “‡ylZKKT\]^`acdeccdfghiinpsvwwutw€ty{wioppqrsssvtsrrtwy‚}z~…‹‹ˆŒŒŽŽŒŽ• ¨®°Ÿ¨¹ËÓÎÆÉÊËÌÌËÊÉÆÇÉËÎÐÒÓÖÕÖÛÖäá¥MMMOTYZX^ix„Œ’˜š™˜š›—ކxW6+3CXi|…•”‘š‘™•h8()/024666680*,484/2139AHOUm~‘˜”‘–¢j`WV[^ZUWX]k~™œ¤¥§¨§¥¢ “‡ylZKKT\]^`acdeccdfghiinpsvwwutvvw‚€wvkoppqrsssvusrtvy{~ƒ‰‹‰†ŒŒŒŒ~—›Ž›¶¬†›¬ÁËÊÈÌÒÎÌÊÈÈÈÉÉËÌÌÌÍÍÍÎÏÐÎÏÍ×ãÒ£}NAPTPW\gwƒ‹‘˜š™˜š›—ކxX7+/:JYs}Š‘‘ŽŽŽ‘‘‘—–˜|D)(!-0589742001344202/4>@CVlƒœ›–¡¥\XUW\^ZUWX]k~™œ¤¥§§§¤¡Ÿ“‡ylZKKT\]^`acdeccdfghiinpsvwwutvp|ˆ|suloppqrsssvuttuy|~„‰Œ‹ˆ…ŒŒŒ‹‹ŠŠŠŽˆ’§ª™²ÀÌÊÀ¿ÍÜÐÍÉÅÄÅÈÊÎÎÎÍÌËËËÓÖÖØØÖÚÞïÄ…WHOUQYdu‚Š‘˜š™˜š›—ކyY8++1<Hiu…‘ŽŽŒŽ’““œ ‡S-%'&,17;<82.)078410215:<;Ff†‘—𖑍¤TSUY]^[XWX]k~™œ£¤¦§¦¤¡Ÿ“‡ylZKKT\]^`acdeccdfghiinpsvwwutwt„†toqmoppqrsssvutuw{‚€„‰ŒŒŠ‡†ŒŒ‹Š‰ˆ‡†”Œš”ŠœºÀÀÁÆÌÏÎÌÍÊÆÃÃÅÈÊÆÇÉËÎÐÒÓÑÑÓÖÚÔÎ×Ýà×—LEXLVbr€Š‘˜žš™˜š›—ކyZ:,*+2:\j~Œ’’’’‹‹‘“”•ŸŸh-'*&2/38<<71-(1::4/147;96@^~‘“‘ŽŽ“›¤©¡TUWZ\]]\WX]k~™œ£¤¦§¦£ Ÿ“‡ylZKKT\]^`acdeccdfghiinpsvwwutxŽ}jllloppqrsssvuuvy~‚…„ˆŒŒ‰‡ˆŠŒŒŠ‰‡…„ƒ…’Ž€Œ°ÅÂÉÆÃÅÈÈÄ¿ÅÅÄÄÅÇÉËÉÊËÌÍÎÏÐÊÅÉÈÍÐÌÕÊÐèÑx;>LS_p‰‘™žš™˜š›—ކx[=/,),2IZr…’“”‰ŠŒŽ‘”•–™„E+.(:358:961.,2872/26=:5;Y‘Ž‹‰š£§§¡XYZ[[\_`WX]k~™œ¢¤¥¦¥£ ž“‡ylZKKT\]^`acdeccdfghiinpsvwwuty—rakhkoppqrsssvuuw{€„‡‰ŒŽ‹†…ŠŒ‹Šˆ…ƒ‚‡|…¤¸·ºÇÊÉÆÀº¸¼À¼¾ÂÆÉÊËËÏÏÎÍÌËÊÊÎË×ÏÌ×ÖÙÛÐÕá¹^2JQ]o~ˆ‘™Ÿš™˜š›—ކw[?3.**/4HczˆŽ‘’ˆ‰‹Ž‘”–˜‡T+&-+-5888764212443014844=Uw•ŠŒ‘™¡¦¦¥¤\]\[Z\`cWX]k~™œ¢¤¥¦¥£ ž“‡ylZKKT\]^`acdeccdfghiinpsvwwutzšj[jejoppqrsssvuvx{†‰ŒŠ„ƒ‹’Œ‹Š‡…‚€u†¦¿½¯²Â¾º¸ºÀþ¶ºÁÇËÍÌËÈÈÊÌÍÏÑÑÉËáÕÇÒÎÊÒéÛÛãžJBP\n}ˆ™Ÿš™˜š›—ކw\@50+*.&:Xr‚Ї‰‹Ž‘•—˜w.6,&1,<:753333741//269%.JqŠ‘•ˆœ£¥¥¥¥¨YYYZZZ[[YWZfx‹—œ¡£¥¦¦¤¡Ÿ—Ž}hULNRUX\`bcbbcdefhjkkjrrusqrezÀ‘]ikomlmnpqstuzyyz~„ŠŽ’Œ†„‡Š‹Š‡•Œ|}ypqœ®»¼»¾½¸³½ÆÄ»µºÁÂÃÅÇÊÌÎÏÉÉÊËÌÌÍÍÊËÌÍÎÏÐÑÒÖÑèãÉ›6MZ{}…‘¤’˜œ™š›’„nU;11/+)"3EVjyˆ– £~Ž“‰.-++-27;887766660357:<5+>o‘›”‹†—˜›ž¡¤§¨ŸYYYZZ[[[YWZfx‹—œ¡£¥¦¦¤¡Ÿ—Ž}gTLMRUX\`bcbbccefhikkpmpprumgŸ½ƒ^ifkmlmnpqstuyyz|€„ˆŠ…ˆˆ„€ƒ™ƒ„†ƒvi{ž¾³³¿Àµ³½½¿¾¸³µÀËÄÅÆÇÉÊËÌÉÉÊÊËÌÍÍÊËÌÍÎÏÐÐÆÜ×ÙÝäÀQ@Xf€Œ’›•˜œ™š›’„r^@+-61").7:P‘ÇÍÔÔ¸²Â©Q,,,-036877766655/15:93..0T€–“ŠˆŒš›œž¡£¤¥ŸYYZZZ[[[YWZfx‹—œ¢£¥¦¦¤¡ –}gTKMQUX\`bcbbccdfhijkvhpkovgqÅ·taiaimmmnprstu|}‚€Š„€€„ˆŠ{xwstˆ¡Â»²¯µ¾Á¾¸¹¹¹¹½ÂÇÆÆÆÆÆÇÇÇÈÉÉÊËËÌÌÊÊËÌÎÏÐÐÍäÛÔÚíØz,S]ˆ”—˜˜œ™š›’„v_E62/..+##\³ÔÒàÉËÇÌÖÈ&(*-13455666555440.6>6'(6Rr”›‹‰—ŸŸŸŸ ŸZZZZ[[\\YWZfx‹—œ¢¤¦§¦¤¢ •|fSJLPUX\`bcbbbcdfgijkueujkqd‡Ð«ofhbllmnoprtuuvy~‚„„‚€Žˆƒƒ……yrknvˆ¬¼«¹¼´©½Âºµµµ¹¾ÂÄÄÇÆÆÅÄÃÃÂÇÈÈÉÊËËÌÊÊËÌÍÎÏÐÔÙÓÙÛáݱ1Fg{|—›–˜œ™š›’„x_C631+'aÆàÕȸÌÐÚßààº'&).36542555444331-6@4!*Et‰›™Š„‘¢£¢¡ ŸžœŸZZ[[[\\\YWZfx‹—œ¢¤¦§§¥¢ ”Œ{eRJKOUX\`bcbbbbdeghijoezoigh¥ÈŸrjgfqkmnoqrtuvosz…‡‡†u~‰Œ†€}}kd|¥ªµ·º²±·²¦¨´¼·²±¶¾ÆÊÅÄÄÃÂÁÁÀÇÇÈÈÉÊÊËÉÊËÌÍÎÏÏÈÈÊ××Öâßh9gv}™œ•˜œ™š›’„z`<&(.%&‹ÏÓȵ¸ÙßàéèâêÜX&).36542443332224/6>3&:\Œ’—”‘œ§¥¤¢¡ž›šžZ[[[\\\]YWZfx‹—œ£¤¦¨§¥£¡”‹zdRIJOUX\`bcbbabcefhijjiytlbsÀÀ—rhdiqinnpqstuv~€€€~|{x~†‰†~vqp›°°¯°¦«²µ¯©ª¯´¶¹º»½ÀÂÀÀÀÁÁÁÁÁÆÆÇÈÈÉÊÊÉÉÊËÍÎÏÏÃËÏÐÍÒßâ´JXr‹—˜—˜œ™š›’„{Q3.!$K¯ÊÞÀ¡ÃêàÙÙÜÜÛçí–(*-1345533222111745847Ux–“•œ¢¤¤£¢ Ÿœ›ž[[[\\\]]YWZfx‹—œ£¥§¨§¥£¡“‹zdQHJNUX\`bcbbabcdfhiiilpwtcƒÍÁ’kacgjhnopqsuvvˆ†‚~{xxw‰„~~€|ri¦°«³¤œ«±«º»±©µÃÈû··»¼½¾ÀÁÂÃÅÆÆÇÈÉÉÉÉÉÊËÌÍÎÏÍÒÒÍÎÓÕÕÙˆSjŒ˜œ˜œ™š›’„sH/1$!\®åˤ¤Ìà×ÔÝÛÓ×ÞÝê¹,,,-036822111000;9515Kq‘˜“Ž˜žŸž¡¡¡ Ÿžžžž[[[\\\]]YWZfx‹—œ£¥§¨¨¦£¡“ŠycQHINUX\`bcbbabcdfgiiknhx{fÒÇ‘bZbcbhnoprsuvw{zxx{€…‰ytsx}®·µ²¯Ÿž³´ª§±¸µ³¶®ºÆÅ¼´¶¼¸¹»½ÀÂÄÅÅÅÆÇÇÈÉÉÈÉÊËÌÍÎÏÒÊÉÏÙÙÕØáÅ\b‹ ˜œ™š›’„fS/'i¶åî²ÐßÙÕÕÒÏÃÑãØåË.-++-27;111100//=<5-6X„ —“’˜œ›˜ ŸŸŸŸ````aabb`^`izŠ•™¡£¦¨¨§¥¤˜~hULMQY[_bdedchgeefilndwhofe²Î²”p^`ijgg_`oyz{|€|su„~Žscs”ª£§ª©¦§ª¦²½¼²²¹½´³º¼¶µº³±²¸ÂÉÊÈ÷ÁÍÄ¿ÉÍÄÆÌÓÖÓÊÃÎÎÎÎÎÎÎÎÞÓ¥uuœ¥š–œœ|^>";ŽÚྸÀÌÖÙÕÎÉÀÊÑÏÛÛåÐ)'(.8;72).20*(,10-&-Q›’•™Ÿ ŸŸžžžœœ```aabbba_`jzŠ•™¢¤¦¨©§¥¤˜~hULMQY[_beedchgeefilngyntig¾Í©Žn_bijfhnssruz~|{tr|‰†yŠrkuŒž¤««¤¢©±µµ²±³··»¶°¸Å»¥±´¸»¾ÂÆÈÕÀ½ÆÄÃÅÁÈÆÂ½»¼ÀÃÍÍÍÍÍÍÍÍËÕϳ“Ž™– ž’Ž‘†rJJp´ÛÏ·®ÆËÒ××ÓÌÇÅÍÒÐÛÝæÉ((+06864/1320/13/'%:e›—“•™Ÿ Ÿžžžžœœœaaaabbbca_aj{‹–𢤧©©¨¦¤™iULMRY[_ceedcgfedfhlnhytxikÊÉœ†ladjidnz}spvyuxwuv€ˆƒw„pfu‘¡¡œ§’‘§©™ º®°´·¶²®·¶·º¹³¯®±·½½ºº¿ÅÏÄÅÇÀ¾ÄÇÐÁ²±½ÇÇÃËËËËËËËËÊÐÛÞ‰˜‘¥˜•œ“zfmÕæÍ°²ÅÔÕ×ÖÓÏÊÇÌÒÔÑÚßç¹%*03435642136764,"+R‚›™•—›ž Ÿžžžœœœœaabbccccb`ak{Œ–›£¥¨ªª©§¥™iVMNRY\`cefedgfedehkmgutuetÑÀlcfjhdttqosyvnsz€€}{zypkv“¤ —–‰šž’“§³±¨«°·¹¶¯©±¶¿Àœ§Àµ¸»º¸¸º½»¾ÆÈÃÂÇÌ´«§±ÃÍǽÈÈÈÈÈÈÈÈÏÊÉÚݱ¡••—žŸ€‹¢ÇÖж±ÆÓÏ×ÕÓÐÍËÉÈÒÖÕÔÙáç¢%-4620384/,06:72)&=p™¡–Ž—™œŸ žœœ›››bbcccdddcabl|—›¤¦¨ª«ª¨¦š‘€iVMNRZ\`dffedgfddehkmdool_„Ò´‹}mfgjhemfdmsrqsx}‚ƒ{srupw‹¢£•–”›˜Ž—¬Ÿ«¯¯®®°²®½½ªž¦³¸¹·´¶¹º¸µ·º½ÃÌÌú¡¬¹ÁÃÃÅÇÅÅÅÅÅÅÅÅÆÊÅÌÙÌ™œ‰–˜‹¨ÅÒѾ¯·ÊÖÖÒÎÌÊÈÇÈÊËÓ×Ö×Ùãäƒ'/662026/*(,3750*7[‰¢ž”’š›ž ¡Ÿžœœœœ››››cccddeeedbcm}˜œ¥§©«¬ª¨§š‘€jVMNSZ]`dffeefedcegjmfkkd[ŸÏªqhgijia_eooio}zz~}vrt‡Š’˜–‘˜œˆ‹¥«šœ±µ°©¢ §³½¼¯¢ «¶µ¬¸´°²¸»·²µÀÁ¾ÃÁ¶°¸¼ÁÃÃÃÆÈÃÃÃÃÃÃÃÃÂÆÂÃÏÕ—‘‘Š›½ÕÕȱ²¿ÒÔÉÆÍÆÆÅÆÇÉËÍÐÕÖÚÚãÞc,0454223*(),14436Px—Ÿ—“–œž ¡¡Ÿ›œœœ›››ššcdddeeefdbdm~Ž˜¥§ª¬¬«©§š‘€jWNOS[]adfgfefeccdgjllmj_\¼Ì¨“†vjfhkmadjnnnt{zttzzux–˜”“—–”¡¡ §«©µ¤¡¥²´´ 𬹴®±±±³µ··¶°ÂÀ³²³²¸¿¶®±¼ÃÀ¹ÁÁÁÁÁÁÁÁõ·ÀÄ̳œ ´ÌÏÀ¼ÅÅÈÉÉÌÏȽÇÇÉÊËÌÌÌËÑÕÝÛã×G1014662.(+/1248;Hiš—””•žŸ¡¢¡Ÿœ›œœœ›››šš™dddeeeffebdn~Ž™¦§ª¬¬«©¨š’€jWNOS[]adfgfefeccdgjlrpk^_ËÊ©˜‹ykeglohmlip{zqoou{up}“”œšŽ—™’“—ž¥«ª¢™¦¢ªµ·¡‘¥´³¯±±ª¯´´²²·¼³» »»¾µ²±¶¿ÆÅÃÀÀÀÀÀÀÀÀ½¥³ÂµµÓ¥Ÿ¹Íɽ»ÀÃÊÔÚÔÈÃÈÐËÌÎÐÐÎÌËÇÏÔßÛâÓ740/2882+(/5645;BVy–˜’“•“Ÿ ¢£¢Ÿœšœœœ›››šš™eedddccc`afn{Š˜ ¨ª¬®¯®¬ª”‚kWMMQY[^accbabbccdefffklZ~Ôø–‹|rmlif[qqkv{ttrz{tt~‡ˆ“›‡•ˆ›¡žž£¡›š¬º·®¨Ÿ•«¯±°¬¯²§«®¯¯´»Â³±°´»À¿¼ÆÀ»¼ÀÁ½·³º¼¶µ»»µ½»·´µ¹ÀÄÏʼ»ÀÇËÇÇÈÈÈÉÉÉÉÐÐÈÈÍÊÀÒÖÐáÙç»+/*(+374/-).9<;ET’›–”šœ˜ŸŸŸŸžž›ddddccbb`afn{Š˜ ¨ª¬®¯«ª”‚kWMNQY[^bccbabbccdefffjkYÔø™Ž€vrpliaoqjmuxzwr{•’”…“ž‡~ŒŽš–›¢›š°¯¦¦¯«žŸ¬²¬¨«±´°ª·¯©ª±¶´°²¶¹¸µ´¸¼Â¿½¼½½»¸¹®¿Ã¶®°¯±±¯®µÂͼ½ÀÃÈÌÐÒÇÇÈÈÈÉÉÉÌÉËÑÏĽ¼ÊÍÚÔ×ä/+),263/*-5823Jg‡’—“”›ž™ŸŸŸŸžž›ddcccbbb`afn{Š˜ ¨©¬®®«ª”‚kWMNRY[_bdcbabbccdefffhjW‡ÓÁ¶ž”‡}yvrniknifq||~|ww‡™›“‹Œ–Œx‰‘šœ““¢³¬ ˜›¦¯²«¬¬¬¯°¯®¬«¬°²´·º·³°³¶¼½¾¼¸¶·¹±ª®¹¾µ©®¦£®ÀǾ±³ºÃÍÒÓÑÏÇÈÈÈÉÉÉÊÏÄÈ×ϵ¬¸ÇÌÛÌÏÝS!/-+-2420'2;5'.V€“’•ž ›ŸŸŸŸžžœcccbbaaa`afn{Š˜ §©¬®®«©”ƒlXNNRY\_bddcbbbccdeffffhVŽÓ¾µ¢™…}wsmdhlit}thu€…–”ŒŠ™ƒn…š˜™š•‰ŽŸ¥žª©ž“›®´¦«¯¨§«°£§«ª§©²¹¶²¯±µ·´°¶º¾º³°³¸®³°ª°¸°¦²¾À¹³´¸ÀÅÌÐÑÍÈÄÈÈÈÉÉÉÊÊÏÇÊп¤£·ÃÌÏÏÐÂ,20/./1110*5<2'7g“˜“Ž— ¢žŸŸŸŸžžœbbbaaa```afn{Š˜ §¨«®¬ª©ž•ƒlXNOSZ\`cddcbbbccdeffgcfS–Ó»³¥“‹‡‚{ul^cpsyyjbzމ‹’‘ƒpž—‹ž›Ž‹˜ž™–œ¤˜—§¯ª¦ª¬§£¦«®ª¦©¤Ÿ¡©¯¯¬ª©´·¶³²·º¶°¬¯´·®®·´¥ž¢»¿Àº³µÁÍÍÍÌËÉÆÃÂÈÈÉÉÊÊÊÊÍÏ˼¦š¤´¸ÃÏß…201210//0.6947Qy™œ“Ž’š ¢¢ŸŸŸŸžžœaaaa```_`afn{Š˜ ¦¨«¬ª¨ž•ƒlXNOSZ\`ceecbbbccdeffgaeRžÓ¹²¥Ÿ–‹…}vh^bqxxskv‰•‚†‘˜‚pƒž˜“•‹‹Ž•œœ˜””—™ «°¬¤ §¦¥¦¨¨¨§ª¥¢¢¢¢¢£¨®®¯´¹±³³±®¬®²«¥¢£©µÀƹ°·ÉÔÑÉËÉÅÃÂÄÆÈÉÉÉÊÊÊËËÍÑĨ˜§¨“¿ÊÜA #1353/,.1048=QpŠ–š“’™žž ¥ŸŸŸŸžžaa```___`afn{Š˜ ¦¨ª¬¬ª¨ž•„mYOOSZ]`ceedcbbccdeffh_dP£Ò·°¥ž—‘†}uddentmmyŠŽŒ„ƒ‰Š…r|•™Š”œ†ƒ’™“–Ÿš‹“£©¡ ª©ŸŸ¥ª¨¢ ¥«¦ªª¤›™ž¤¥©®««¯³²¯¬«¨¨«¢–ŸºÈůºÇÎÎËËÍÄÂÁ¿ÀÂÅÇÉÉÉÊÊËËËÏÉ´œ›©§–Ÿ·Î¯%$1574.+-1,3<LjŠ–’•’˜¡¢œž§ŸŸŸŸžža```___^`afn{Š˜ ¦¨ª¬«©¨Ÿ–„mYOOS[]`ceedcbbccdeffh^cO¦Òµ°¤ž—’†|tbjijlcj‡ˆ~}‰~fxІ‘Šž•ˆ†” œ“ޤ ¡£ž™¢°¦£¡¢¦¨¦£¥ œœ¡¤¥£°¬¨©®«§³¨©®¯ª¤®¥¥µ¿¹®©¼ÀÇËÌÉÅÃÂÂÂÁÀ¿¾¾ÉÉÊÊÊËËËÒÁ¦˜¥´¥ˆœ´®×x)51695-*,1'2AWy—œ‘’œ§¤šœ¨ŸŸŸŸžž````````_bisŒ—ž§©«¬¬«©§¡—…mXNNQX[^accbaccccccccac]UÉʼ«–—™Š|wmiil€ŽŠ‚„…†y|‚ˆŠŽŽŠˆŽ–œŽ‚•¶›™ š¥ªš˜¤›£©¦§£–†¤»¥¡«¤¢««³¬ ™ªª¨¦£ª˜¡¬¸º¶µ¼ÅÉÈÆÃÁ¾½¼ÃÃÄÅÇÈÉÉÍÊÉËÑÖ××Ò°Ÿ¢®Ÿ¥ÂÎ:/A('8@5,19:,5Lp™™š›œž ¡¢œœœœœœœœœ`````````cis˜ž§©««©§¡—…mXNNQX[^accbacccccccc_a[TÉË¿±±œ›Œ}wfcmv‚‰ƒ„Œ‚„rŽŽŽŒ‰†…‰——ˆ†•œ–Ÿ‹§«–˜’—ªªŸ¥œ¡Ÿ¡¨¡ª£¥ž“¦«¥›¥˜®©’ž¥œÄ¹®ª±ºÀÂÂÂÁ¿¾½¼»ÃÄÅÆÇÈÉÊËËËËÊÉÇÆÓ‘—¢œ¨–°Þª".,806502::2.6S}—˜’“šš›ž ¡¡œœœœœœœœœ`````````cjt€˜Ÿ§©««©§ —„mXMMQX[^accbacccccccc\^XRÉÍÄ··¥¢Ÿ’ƒ{a_uƒ{ˆ„ˆj{{“Ž“‘‰†…„„œ“†‚¤©¢™§ŸŒœ™Žuzta^¨•¨¦¢¥ ¡ª¯¬—¥¤ª¨¢œ˜–›¤Ï½µ°µÂÉž»»¼¼½½¾¾ÄÅÆÇÈÉÊÊÍÊÆÅÈÎÔØ¥›¦ ’‡to•Àæc2 853/.5=9/0>cŽ’•›œœžŸ œœœœœœœœ›aaaaaaaaadktŽ™Ÿ¨©¬¬©¨ —„lXMMPX[^accbaccccccccZ[UPÊÏɾ·©¥ ““—ƒed|…€€ˆ‡l{t—ŒŒˆ„ƒ„†ˆŠ€Œž¤ž™hqppvu‚¡‡„šŽz\•«£Ÿ¤‘³¢§œŽ“ š’˜§–˜’’¼Ñ³«´ÂÌÏËþ¹º¼¾ÀÂÄÄÅÆÇÈÉÊËÌÌËÎÖÛ×ɽ«™–}b_ev³ÖÍ'#*%2222247744Ox˜‘Ž•žžžŸŸŸœœœœœœœœ››››››››šbbbbbbbbbeku‚š ¨ª¬®®¬ª¨Ÿ–„lWLLPX[^accbaccccccccZ[TNÉÐÌ´«§Ÿ••›šŠom€€zŠ…r|v—‡…†„‚‚†‹Ž€ˆ—œ•|b}…‚zs`\p_ƒ¥§¬pnqˆ©“’šnŠ¢›kXNp›£’žÃ»¢¥»ÄÌËþÀļ½ÀÂÅÈÊËÇÇÈÉÊÌÌÍÌÈÉÔÝÕ½§–`?]dg¬Îá‰!-330035555>eš–”””ŸŸŸŸžžžžœœœœœœœœ››››››››™cccccccccflv‚›¡©ª¬®®¬ª©Ÿ–ƒlWLLPX[^accbacccccccc]]TMÈÏÌö°©ž”’™‰ts~{x€†tzy˜‰Ž~‹~~„‡‹…’ž™„rm‰•‰plpfVSŠ¥¤±ƒxouYc€‹—emM_YsJ>ŽŽ§Â¬“§¶ÅÅÃÁÀÂÆÉÀÁÂÅÇÉËÌÈÈÉÊÌÍÎÎÊÈÊÓ×Ê”uK[eWv¡²ÞÁ;*".2,8.*29842Qy˜—•›˜¡¡ Ÿžžœœœœœœœœœšššššššš™cccccccccfmwƒ›¡©ª®®ª©Ÿ•ƒkVLLOX[^accbacccccccca`UMÇÏ˽¹®‹–€rqxy|{qqxŒ“†„|~€ƒ…††…”‹Œ“Œ}ƒ–qcg\G?@E™˜—€|uxyX_hRNBJT8</@"{»¶˜®ÂËÀ¾½ÀÅÈÈÆÀÀÁÃÅÆÇÈÉÉÊËÌÎÎÏÈÌÏʸ™u\XXb`p‘°Õ×q*.97$5--57217i…™”Œ’¢£¢¡ Ÿœœ››››››››šššššššš˜ddddddddcgmwƒ›¢©«¯¯«©Ÿ•ƒkVLLOX[^accbaccccccccdbVMÆÎÊÁÄÀ²„†Žwlmtz€u]s‚‰ŒˆŽ†~}€ƒ††ƒ€}Žš”|r€ŒŠaalnfkiV[}‰Š}{pJC=GjT?V=(2,6,.0M«§ÆÅÈÇÉÆÂ¿¾¿Á¾¿¿ÀÁÂÂÃÉÊÊÌÍÎÏÏЧ„eVW^Oa_ZŒ·ÁÜ¿,2$8,9*-.6;2'/@z‰–”ŒŽœª¤£¢ Ÿœ›››››››››šššššššš˜ggfecbaagiox„›¡¬¬¬¬¬¬¬¬Ÿ™‰oUHJQY\_bddcbddddddddb`ZEÓÏÅÐɨ Œs‚„zuwjtzgt†ˆ‹Š†|zƒƒƒ„†ˆ‰Š† •cw|Ž€s}pu…ˆr?OdR~qUI7?]qEH@W:)5,Dz¼›™¦ÂÔÐĽÃÃÂÁÀÀ¿¿ÁÆÅ¾ÄÌŽͿÂÒÂÊÐȇoVNYcc]Zo}°Á×°--./00110)420'([Ž’•”“—¢«£¢¢¡ ŸŸžžœ›šš››ššš™™™™ggfedcbagiox„›¡¬¬¬¬¬¬¬¬Ÿ™‰oUHJQY\_bddcbdddddddddZ]KxÕÍÉËÇ»™{u‘€€{npqkyŒŽ‰zww††ƒ|wz†‹ƒxkƒŠybƒukWCD>HGZkjdn\p`WW72L:6-&&’¿‚œ«»ÃÃÂÁÁÂÂÁÁÀ¿¿¿ÀÀÁÂÃÂÃÇËÏÎÍØÑ”[RSTWY\_as…©½µÌa%..//001102983,;l’“”–›£©££¢¡ ŸŸžžžœ››š››ššš™™™™hhgfdcbbfiox„š ««««««««Ÿ™‰oUIKRY[_bdcbaccccccccfU`SZÏÑËÀÔ³›{j’ˆŒ{qmjv†‡‡ƒ~|~„†‰‰‰ŠŽ’~l~‡ƒ…quwlSH>6>@<B_loj[e`dp+G/t:2$J°ªuª½Æ½µºÁÁ¿¿¿¿¿¿¿¿Ä¹´¿ÃÂÎØÅÊι°…XhdaZSTc|޵º¶Üu!00000000/<=?72Xƒ”’‘”𡤥££¢¢¡ ŸŸŸžžœ›››››ššš™™™˜ihgfedccfioxƒš «««««««« š‰pVIKRY[^bccbaccccccccfX_XI·ØÊȽÁ˜€‰€”Žtqqo†‡…€{z}ƒ„‹ƒ|}…Œ‹‡‚]€Œ†‚d^7Q_WG>:2BihvXF%a\^hS?+j=Zª¼xy¿À»´¶¾Á¿¼½½½¾¾¿¿ÆÂÁÍÊÄÌÈͧlt€a[^^ak}ž¦³»¼Ðz&.>22211000+@<@8<q“–’–Ÿ¦¥¢¤££¢¡ ŸŸŸžœ››››ššš™™™˜jihgfedcehnwƒš «««««««« šŠpVILRXZ^accbaccccccccda^YIŒÛƾȰ±‘ŽŒ„jqy’ŠŒvv}‚~x€‚ˆŒ‰€x}^kWD9ZXN8,046e„n`($9fSMbU(,m0$X¬ tŠÌÃÆ¸®²»¾¿Á¹¹º»½¾¾¿ÆÅ½ÅÄÉÕ¾¶qXmŠŠmntx„–§®ª£´Æ„&*,>543210//*=7::K‚—–“’™£¨¦¢¤¤£¢¢¡ ŸŸžœœ›››ššš™™™—jjihgfedehnw‚™Ÿªªªªªªªª¡šŠpWJLSXZ^abba`bbbbbbbbcj`ZQgÓÅijɷ¡™ž…wjt€‹“…ysv„€{o|‰‡ƒ‡~iŒbF5GLR83;@7(-€g^8%AZ@YT+<dDj¯žp¢Å¿Ã¼±¯·º¶¹Ã¶·¸º»½¾¿Á»±ÆÍȵtRdgnj›š¦§¨¨§§ª¾È‰1/.6765320/.0623?cŒ’“”˜ž¤¨§¥¤¤¤£¢¡¡ Ÿžœœ››ššš™™™—kkjhgfeeehmw‚Ž™Ÿªªªªªªªª¡›‹qWJLSXZ]`bba`bbbbbbbbene\VU¼ÉÅÇÀº«››š~nu|ƒ‰}mqv{~€ƒ…t|tgj€–‹bSAGMYM1+:7'#8hnCH'Kh†\ZW&@"PG…µ“x˜Âɸ½¯²¶¹¶³³·³´¶¸º¼¾¿»ÁÅÖ¸ˆg&Q~ete}”³™¢©ª¦¨±»Ô•,4+@%986420.-;11/H~“‹–£¤¥¦¨¥¤¤£¢¡¡¡ ŸŸžœœ››ššš™™™—kkjihffeegmv‚Ž™Ÿªªªªªªªª¡›‹qWJLSWZ]`bba`bbbbbbbbgni]WQ¥ÌÒÅë©¶ šyj€ƒ‚wiv‚‚zuz‚x‡•’€lccmR>GJIG9.4D3$.CgJ@B*_fP|^>ca¡um–Î½ÆÆ±ªµº¶µ¶°¦²³µ·º¼¾¿ÀÎȯY'Uf:pnh“𥍫«©©²ÃÏ'+5452:97520.-D/3.O˜‡—¡¦¤£¦ª¥¥¤£¢¢¡¡ ŸŸžœ››ššš™™™—mlkihfedgdcl|œ¢©©ªª«¬Ÿ›ŒsXIHMTW[_abaa``aabccdmnlecLcÉÒÉ˪¡¶¤›W|…x|€sj‚‚€}zyyˆ‹gBSUNRSOPRE/6`D,-6[H158GX8iY,@‘4'–y‰¢¿É¹ºÀµµµµ´³±±¶²°µ¾Ä¾¾½ g;;WnXe‰v|p”’¥¦¬®¸Æ®y)*+-/1348;;6.+06922Hp’†Ž˜£ªª©¨©££££££££ ŸŸŸžžžœœ›šš™˜˜™mlkihfedgcck|Ž›¢©©ª««¬Ÿ›ŒsXIHMTW[_abaa``aabcdd^fkfgVUŽÔÐÏ¿ ±»†p~„‚yst…‡…{onz†ƒvvth[LD;NYN=52.Nu/$E^L,;<;A*dW sj)_n‡¬¶ÂÅÀ¹µ´²·»ºµ®©¹µ³¶¼¿¼¸¾¼¤xSKT\][z‚vi‡—¢©¼Á§_*+,.02448::5.,066/6SvŠ‹„’›¥ª©§¦§££££££££ ŸŸžžžžœœ››š™˜˜™mlkihfedfbbj{›¡©ªª«¬¬Ÿ›ŒsXIHMTW[_abaa``abccdd_gkfjdRV¶×Ðɱ°¼rŒ~€‰ru€ƒ„~ury‹ƒ†nijOQ:Q\K0"&/hy$/FWI&;<44&XF#‚pvm]s·Â¿½¿Á¿¹´®±¶¹¸´®ª»¸¶¶¹º¸µ¿Æ·‹]JUckXh…|t™¬±ÊÏ“A!*+,.024567873..152.?d€„€‚˜ ¨ª§¤£¤££££££££ ŸŸŸžžžœœœ›š™™˜˜mlkihfedeaaizŒ™ ªªª«¬®Ÿ›ŒsXIHMTW[_abaa`aabcddepnmhjn_LtÔÒÁȺ¥u”zz„xp|†|zy{€}zwwVQVJTQ\WD2'*7le1S2B=9EFIK>]GMui£pr§¹Â¾º»¾¾¸²¶´±®®¯°²¸···¹ºº¹ÁÌÇ¡nRVeqe_p‰•§Ã´l0+4--.0145787641/02401Lv‡}yƒŸ¤ªª¥¡ ¢££££££££ ŸŸŸžžœ›šš™™˜mlkihfedd`_hy‹˜žªª«¬®®Ÿ›ŒsXIHMTW[_abaaaabccdeeumkonmhYP¼Û¾ÆÇ¥‹‰vtwot‚ƒ~wrs{‚qtN:CHKCabP=5.1CVZ0AQ)11>?=JH;WPStZ‚x¡³®º¼¾¼¶°ª¨·´¯««°²³µ¸º¼¾ÀÃÂÈȶ“nWOZk`O„™¯]6((,//023679:630/02342<^ƒ‰yx‰¢§ª©¤ ¢££££££££¡¡ ŸŸŸœœ›š™™—mlkihfedb_^gxŠ—ª««¬®®¯Ÿ›ŒsXIHMTW[_abaaabbcdeeehfgutgf`Vœã̷θ•{uqmnz€ytx}€|ywƒ}PEG@G6W[L<8-1L:aLK,2..1.)2&#L]JYZ|˜žº´º·®§¨¬¯°°°°®¬«³¸½¿¿ÁÆËÅÇÉ¿ x\R9Z_?gˆ}‹g1$58-123579;<51,-04438MpІw~’¤§©§¢ ¢¥££££££££¡¡¡ ŸŸžœ›ššš—mlkihfedb^]fw‰–œ««¬¬®¯¯Ÿ›ŒsXIHMTW[_abaabbbcdeefcjfsvggdY”â×ÁЮwztnw|vpt|‡‰{i\pcVO?11,EPJEF51L*]lS82112+-"1\o_JeŽŸ£¦±¯³´®¥¤«´°¯¯®¬¬»ÀÅÅÂÂÇÌÇÇÈÀ£{a[19RNKy~œzL64*&433579;<=4/*+1552@^€‹x‡œ£¦¨¦¢¡¥©££££££££¡¡¡¡ ŸŸžžœ››šš—mlkihfeda^]evˆ–œ««¬¬®¯¯Ÿ›ŒsXIHMTW[_abaabbccdeffjvhmtjmkKÛÖÛÏdy€wt€|mjŠ…ƒ†‡{cNJ3GK=8+0=JHNYE5H(N…Y*362/4+.,HilRt˜–µ»»³ÄÀ¶©ž¦®¸³«¥¤©¯´ÂÇËÉÃÁÅÊȾ¾Ä¶ŽbK> Be@x…’›…N$(4/*3468:<=>4.(*1652EiŠŠzx¢¢¥§¥¢¢§¬££££££££¢¡¡¡ Ÿžžœœ›šš—jjihgeedb^^fw‰–¦¨«®«ª¤™‹x^IITVY]acddcgecbcegihmtxwqjdRŸÓÐÒ¹ekxjl~€rp||–ytˆQ"49OB/,*,4OP:]F/Z+1c„X<7%0-$PŽsY™…¨¸¬¸½Àº°¦¡£ª°±«¤¡¥¯»ÃÃÄÅÆÈÉËËÍÊÈÆ¶•oV?/-CNO›˜§€$3/2/46459?@;7-61+2537Q‚“~{——œž ¡£¤¥¨§¦¤£¡ Ÿ¥¤¢ žœ›šžžžœœ•jjihfeddb^^fw‰–œ¦¨«®¬ª¤™‹x^IITWY]adddcgfdbcegicglqrqmj]wØÐÓ˜ljcoyyss|‡‡wty}b0)SG=+%501BIHPG4S0-2ua\WE#(Vtq©©z¶¬½·½¹°¢™»¬©¥¤©²½ÃÃÄÅÇÈÊËÌÐÎÍʼw]C*+9MJާ(303/47559>?;6.30/40/:_‡‘~‘™˜››œžŸ ¡¢¤¤££¢¡¡¡¥¤£¡Ÿžœœžžœœœ•jihgfedda^]fw‰–œ¦¨«®®®¬«¤™‹x^IITWY^adedcgfdccehioopqqqpoiUÍ×Ï{nmooopx€‚wbXZVJN'+c?+'336.5>OCD9O%E<ƒQ!!@Їl™µ¤§…‹±¡»º¸³«¢Ÿ£«²¥¥¦ª°·¿ÃÄÄÆÇÉÊËÌÓÓÑÎé‚eI&&.GFw£©ƒ030315866:>?:5./.46+,Btއ˜œš™™š›œžžŸŸ ¡¡¢¢¥¥¤¢¡ Ÿžœœœ››•iihgfedca^]evˆ–œ§©¬®¯®¬«¤šŒy_JIUWZ^bdeddhfdccehj‡ƒ}wsqqrqV¤àËuku„mbqƒ„}z\QIP<>ND9b5*J+;.24B>:9UIS(_b&15;†€f”µµš¬•™±¢·¼¶¥§®®£˜Ÿ¢¨¯¶¼ÁÃÅÅÆÈÊËÌÍÒÓÑÎDzŒjP**:D]¤«†:21426977:>>93.-.77'/Q‡’‹‚ŸŸ˜˜™™™ššš™š›ž ¡¢£££¢¡ œœœœ›››š•iihgedcca]\evˆ•›§©¬®¯®«¥šŒy_JJUXZ^beeedhgecdfhjŠ…~wsrtuvl{ÓÏynyupqz~}…}raU,6&*:f( 3D3<18/-C-5_wg2@{%.y—p‹º©¤µ•ž®±¹¾µ¥§¬ªŸ£ª²¹¿ÂÃÅÆÇÉÊÌÍÎÏÑÐÌɺ”mW5,,CJœˆE12547;98:==72..075);d’’ˆ‡š¤¡¡š™™™˜˜˜˜–—˜šœžŸ ŸŸŸŸœ›››šššš•ihgfedcb`]\euˆ•›¨ª¬¯°¯¬¥›Œz`KJUX[_cefeehgeddfikyvsppswyxxm®Ó~vvew€yz†‰€[TBA6B",,l(38.G74;-*F&3_P^YUžA'y‹w´±µ²®¿˜™¡±¼Ê±®©¡œ›ž¡¤§³º¾ÁÃÆÇÈÉËÍÎÎÍÒÐÌÍÄr^B!/%>CН‹O13658=:9;=<60,34300M{’ކޢ¦¡¥œœ›š™˜˜––—˜˜™šššš››œœ››ššš™™™•hhgfeccb`\\du‡”›¨ª¯°¯¬¥›z`KKVX[_ceffeigeddfikqqppruwyvvp†ÅŠxqw€zƒŽ€dJK5?U8!)(XB82:J/77/?D'4SEL9&tXOxx¥Æ»°¼›¡¯¼Æ¥§¥ž—™£®«¬¯³¸½ÀÃÇÇÉÊÌÍÎÏÐÖÔÐÔϨzeL/.&3Gu±V04779>;9;=;5/+98-+8`Œ‡„”¨¦ ¨ Ÿœ›™™˜——––•””••–—˜™ššššš™™™˜˜•hhgfdcbb`\\du‡”š¨ª¯°¯®¬¦›z`KKVY[_cfffeigedegikyyxwwvvurpsu¬™tn“Š‚†‹ƒiP1>,Ao()619a-)\=(700W?+7E=J*Qu;¨xsž¨¤µ´¼µ°„²¼¼¬š›žŸ¡¤¨«°°±²¶»ÀÃÇÈÉÊÌÎÏÏÔÛÙÔÚÔ°iP:++*Mf±Ž[04779?<:;=;4.)<;)'>l›‡ƒ„—«¦Ÿª£¢¡Ÿœ›š™˜—–”’‘‘‘’“”•—˜˜šš™™™˜˜˜•eeeddcccd^[bs‡–ž¨ª®°²±°®¡œŽu[LLQXZ_cfggfihfefhkmq~winsmght€ƒi~cz{„ˆ‹{lf.6@&5R#P.3"0I\P$+?8/G98-/<e"X ƒk £Ÿ ¢¶¹Á¾»‚©±®®š™ •œ™¬´©®³¶¶¸¾ÃÁÂÄÆÉËÍÎÐÒÙÚÕϸ”h\?+)*<^ªŸR923;99>CA:300688,0^Šˆ” ¨©¤Ÿ¢¢¢¢££¤¤ Ÿš–“‘ŽŽ””•–——˜˜˜eeedddccc^[bs‡–ž¨ª®°²±°®¡œŽu[LLQXZ_cfggfihfefhkmqyvtxv}y|d`‡wƒ†q‚oP/O?D_E5(19F[YG&-.&QE&[F(MP# !xs`¥« Ÿ¥§±µ¶¼º¢´³£›™Ÿª©ª¤¨ªª¬®±´¹¿ÃÁÂÄÆÈÊÌÍÓÑÓÏÉÊÀ¨mZ?-(*<U¡¨\838>:9>B@9301664-8gŒŽŠ—¢¦¥£¡¡¡¢¢¢£££¢¡ž›˜•“’‘‘‘’’““”•••–eeeedddcc^[bs†–ž¨ª®°²±°®¡œŽu[LLQXZ_cfggfigfeehkmrtpvyotqƒ¦|·Ÿ{|ƒŽf]5@c0$Fx7(1;SNZF5$)<"\7'f1+W57lŽU¹§©ª¡¡ÃÀ¸¬»³˜ŠŒœ ¢§¥³»°«²¹¾ÀÁÁÃÄÆÈÉÊÑÎÓ×ÔЦjN8-'-?L’´k95?A<:>A?821273/1GtŒ‚ޤ¢ ¡¤ ¡¡¡¢¢¢¤¢¡žœ™—–““’’’‘‘‘‘“ffeeedddc^[ar†–¨ª®°²±°®¡v\MLRXZ_cfggfhgedegjltrovzoo¸‘ˆ;=Ujm{~„uzL-Hc!/Q{+;:Q8>0,+,5)E:l%II1*N›mǰ¡¹³šœ©Æ·È¼¬tQj}|ˆŽ¢¢¯«°¸³®®²·»¼ÀÁÂÃÄÅÆÆÐÅÅËÌÉ·œlJ5-"'57¾|=6DE><?@=611381,7Yˆ…“¢¥Ÿ›Ÿ¥ŸŸ ¡¡¡¤£¢ žœ›š•••”””““ŽŽŽŒŒŒfffeeeddb]Zar†•¨ª®°²±°®¢v\MMRXZ_cfggfhfedegjlqtty}y…£¿`5&r²¦€T`N>JcK!%mn-3,@+20/55);HCc7J-6'˜m]¸ª¶¯Â®•£®½ÂÌ©y^\qZ`b^pw” ¯¸¶³±±³¶·ÀÀÁÁÂÂÂÃÈÄÎÓů˜X9+)#*9;qÀ‰E6DG?=??:301481-@j‹„‹˜¤¥ž™ž¥žžžŸŸŸ £¢¡ Ÿžœ˜˜——––––ŽŽŒ‹Š‰‰Šgfffeeeeb]Zaq…•¨ª®°²±°®¢žw]NMRXZ_cfggfgfdcdgjlmuuv{}‰ ¨g628B‡¥œuCX2;[f)*Bhw0)2>=@E2-+16OROCE=3|ƒd °˜·Àä•®ÁÇǦ~{•˜’}€kL?A]vŠ¥«°°¬°µÀÀÀÀ¿¿¿¿ÅÁĹ“se^P;/-),7@e»’O3>G@>?>81/26952Jw‹“›££žš¡žžžŸŸ ŸŸžžœšš™™™˜˜˜‘ŽŒŠ‰ˆ†ggfffeeeb\Z`q…•œ¨ª®°²±°®¢žw]NNSXZ_cfggfgfdcdfikgrrr€‡†ˆx.,9‹XXV`CQP=%Fc=€#+-G8DC[=&!*"6Z]7H;'L°a›Éµ¶Å±›¢¹Ç¼„€™ž“ˆ³žˆoUMEEB„”¥ª¦¤¬¶À¿¿¾¾½½¼µš„nQJ[jh\I:0#$2]²–Y07F??@=70.27:99T€‘†~› Ÿœœœžžœœœœœ›››œœ››šššš”“’‹Š‰ƒgggffeeeb\Y`q…”œ¨ª®°²±°®£žw]NNSXZ_cfggfgfdcdfikaory“¢–ˆ—NtG@‡‘VRM/3bT9;jUsO2N#13gN2*%6)0V<78~‡¶Ÿ±¿ÅÅ–‘·Ç¸‹h{v~vˆd†uch^_Wi›¤ Ÿª·¿¿¾¾½¼»»h`jov‚†caOA;,,BZ«—_.1E?@@=6/-27:<>Y„‚| ŸžŸ Ÿ›››œœššššššššœœ››››–•”‘ŒŠ‰jjihfeddc^RWr‡’¨ª®°²±°¯§œŽz`KJU[]aeggfemRcn]Zfvcup€“˜Ÿ”Ÿº ´ŽriqL7->R|:KUm(?\+1Y*!$O`2'$2*$;4w†j¯´¥ÇÁ©ÅÃq^_qRScC+he;aiveupy|ˆ—ž§³ÂÀÄÂÀ½¬žƒaiE<<UDGA<7,0EM·r:4E=A9/,/46752IuކРŸž››šœœœœœœœœœœ›››ššš˜™™š›œœ˜˜—•‘‰‡…hggedcbba]QVq‡’¨«®±²±°¯§œŽz`KJUY\_ceeddmaf__neZyƒ§›¦£—““ƒˆ‚?0!!XEFˆW2]Wx& FM91Y#'%CdQ.055,, +@œj¦¾§ªØÌï¿´UQXMC=5+9?2*;(,U…z{}ˆ–›œ¥±´¾ÎǷ®fWLA-0/K:AC>95+/DI·‘mA5E>@8/,/46612Q}Œ†† Ÿžœ›ššœœœœœœœœœ›››ššš™˜˜™™šš››‘‘‹‰ˆ‡edcba`_^_[OTp†‘©«®±²²°¯§œŽz`KJUWZ]accbbh`gbb\Z£È£w˜™ƒ¢’J=o6(QdSnjVPaZ~%)J9F1i+6+,Kl])63!( €‰Œ¾¨«¬ÌÅË®ÉÊP;A5&3<;1.5AM9#%a~ƒ~‡”˜™¢¯¶ËÃÎË™hE<1(D--65;>952(-CC¶—eL6E?>70-0454,6`‡Š‡’ Ÿžœ›šš›››››››››››šš™™™——˜˜˜˜˜˜ŽŽŒ‹‹‹‡baa_^]\\\XMSo…‘©«®±²²±¯§œŽz`KJUVX\`bba``Zdb_K\¼Áß”ªƒ{MU&%2Qt>Qe"RiVat(+D,D/s31,%)YjTK?+"\¡_º£ž·¹ÂǾÿZSC,-)B2/$#5|³hR7:g…~†‘•– ®ÊÐÃÕµR+66JO„]@&848420'-C<²ž_V:G?<60.2442)?p‰‰–˜ŸŸžœ›š™ššššššššššš™™™˜˜——––••””’‘ŽŒ‹‹‹Œ‚aa`_]\[[YUJQn…‘žª¬¯²³²±°§œŽz`KJUWY]accbaa^aU`YaÁ¹¯‘¬d|ˆEC!=:cC4X9':q^k`,)7*5,d@$#2%0;4O05’h’»¤¿²¼ÌÁ¿Å^SK7!5GS69=LxÞ¸—F<^W}}|‚’•¡°ÑÈØÉe,>+/[uˆ™a;-0410/(/E5¤^[@J?:5003530-N~’‰Œ›žŸŸž›š™™™™™™™™™™™™™™˜˜——–••”’‘‘‘ŽŠ…ƒƒ…†bba`^]\\URHOm„‘žª¬¯²³³±°§œŽz`KJUY\`cefedl^b\fPGªœ¬«••¢’uP)3,^d2*RE936|WyQ+*,.*)Rd5"1/(+644<h†kư͹įÈrb`L8/RgEPHLB~ØÏÄ„Px_pyx~‰•¤´ÊÐÒL;NG4LTŠ«~A0-2000*2I0§§bZGO=8411452.6bˆŽŠ›£Ÿžœ›š™˜™™™™™™™™™˜˜˜————••“’ŽŒ‰†}|ƒ…Œddcba__^SPFMl„‘žª¬°²´³²°§œŽz`KJU]_cfiihgo_h\V8.”nh¯È¦šr770)/RR='<S3I/FC†P&2)3*'Aq>,'128/('&*ƒY™¥“¬¶ÅÃÄÃrsp`a,<YpSHPDYºÐÄ̰imuhusy…Ž–§¹ÑÞ»rlkKaKKP |?6-2113.6M-¡©iVMS;7312551-AvއŒ“™¦žžœ›™™˜˜˜˜˜˜˜˜˜˜˜˜——–––•”’ŽŒŠ‰…ƒ~‚‰•¡feecba``RNEMkƒ‘žª¬°²´³²±§œŽz`KJU_aeikkjihjn>7<5{Ÿª´áæ”A/-,;QM;:HEVI\^…SŽU :+5/',U'3'0+)* 0zkzº“·ºÌÃÃËopyŒ‰zl*]h~aPf°ÒÅÒ½’“q…gqpwƒ—©½àÞ·gu—aO\a”ÀfR*-212408P,žªnSQV:6313651,H‚–˜§žžœš™˜˜˜˜˜˜˜˜˜˜˜˜——––––”“’Љˆ‡…„†˜£ª²kjheb_]\[UHLi¨«¯³¶¶¶µª ‘ePOZ]dhotifs[6+/pt¢¶šÙÛs-J:/J|lP8&GJVOGb…A¤cJ,!83'48/+3:82ZŒ`°§ÎƸ½zq}}†‹‚ycb}’¤½º¿º®š…zz~~{}‚…ާÁÞݤo^hƒŒ†¥³¨Œ^<9D79/02GG4»zR]L;*64-11+)d…’Ž’•— ™™˜˜————››šš™™™™š™˜–”’‘‘Œ‡‡‰‰…€ƒ‹—¤±±°¼jigda^\[YTFKh¨«¯³¶¶¶µª ‘ePOZaeflsnp‚6)9Vž–˜š¤É•;02BlLt|`P&)0c7VQQ`€Py~8TC,)(25/-34/,0g“±¨ÅÅÂËrns€ƒŠ†~xiqxs‚•Ž˜¢®²ª›‹|~‚…¦¿ÛäǤ“‰Œ–ŠnU>8FE89/03GF2Ž»UZJ:+510951=l…Ž‹’—™Ÿ™™˜˜————ššš™™™˜˜š™—•“‘Ž‹‡…†‰Œ’™£®µ··¶¼hgeb_]ZYVQDJg€¨«¯³¶¶¶µª ‘ePOZdfeirqx=3r˜ž‡p€P4<.-pz>"iJ/!1cGeN7hb‹_na331*/1/13-#" d\²«¥ºÄÇÍ}fb|††‰”“Žˆ‘}zzrzu{„’ž¢˜ˆ|~‚„Œ£¼ÏßǪ›Œ€uwiSE@K^H99/14GE/Šº‡YVH8,3-4B55Wx…ˆˆ“›œŸ™™˜˜————™™™˜˜˜——˜˜–”’Ž‹†ƒˆ”Ÿ¦«²¹¾¿½¼½fec`]ZXWSNBGfލ«¯³¶¶¶µª ‘ePOZbeejrns†ƒ‚z€ªˆ_TSLF:235nšaT3"@LSi=ih~‡*/98),,-042'@“xƒ§µ¬ÁÃˉXaƒ‹•˜˜–”•‹‰‹‹€“‘’–š•Š€„€ƒƒŠ ¹ÑàÆ¨š’“‰~xk_XZbL;9/36GB+„¹‘^PF502*5B.4g„…ƒˆ”ž™™˜˜————˜˜˜———––—–•“‘Œˆˆ‡‡‹—§³¶¹½ÀÁÁ¿½½cb`]ZXVUOJ>Ed~Žœ¨«¯³¶¶¶µª ‘ePOZ]cfmrigv›®£…XG@Q`HB10?†‹fwG//FW=xkY;en…UJ?10".++260%)xŠh¥»·ÅÜ~Mlc‰Ž‘•™šœž“’˜š““—•¨¦¦¨§Œ~‡ƒ‚ƒ‚‡µÎáи« ›‹…|rmnmP=9/58F@'~¸œdKC241*49%5oŒ„ƒŒ–žŸœ™™˜˜——————–––••••”“‘Œ‹†„…Ž®ºÀ¾¿ÀÀÀ¾¼»¾a`^[XUSRLG<Cb}œ¨«¯³¶¶¶µª ‘ePOZ[bgnrgcpu“„r~\:N]K6/HR;]f`HVo6*%Y<4mv\YZbV,#6+/+,45.((Nšc·ººÀÕ¤@Ul|r”˜››¡¥¥¥¤ž—™¤«¥¦§§¢—ˆ}Š…ƒƒ…š±ÉßϹ¢œŒƒ€yxvnT>9/6:F="y·§iE@/6/.4."?wŽƒ†”™Ÿ›™™˜˜————––•••””””“‘ŒŠ‰…‡Žž±¿ÄÃÁÁÀ¾½»º¹¿_^\YVTRQID9Aa|œ¨«¯³¶¶¶µª ‘ePOZ^cekqjj{rkyvT_}‚H28+A\]pkUBj884/:qm&Ot|IN„Š+F14/+.64,.6|„c ´ÀÁÅ®<X\sw}„”™›šœ£ª´±«¦¥¨³©««¦“ŠŒ‡„ƒ€ƒ˜¯Ñãѹ®§¦š”‡……{wp_W@8/7;F;u¶¯mA>-6-38'(T„‹œœšš™™˜˜————••”””““““’ŽŒŠ‰ˆˆ’¢´ÀÅÃÀÃÂÀ¾¼¼»»À^][XUSQPGC8@`|œ¨«¯³¶¶¶µª ‘ePOZbdchpnu‰’𲕓‚P?:L>YWS~eccj9%*7:jkbUb†¢€CbN@02/,073+2@›Y„±µ¯ÏÈK;Ovix‡„Ž”šš™›¤¬«©¦©°±¬©¦²Ÿ‘‰‡ˆ…„€‚–ÍâѼ²©¦™˜†€€|€‚tY@8/8<F:r¶³o?=+4+7;%0e‹¢ž™œ™™™˜˜————””””““’’’‘ŽŒŠˆˆŠœ´ÄÈþ»ÅÄÁ¿¾½¾¾À[ZYXVUSSK?7@[{“Ÿª±´¶¶¶µ© ”ƒhQNXaZgl}”𩉠›jAMW42>H=kUkbg‰o@"3/0.5DXurtdR2PbQ3++1-(7$.‘d®É±ÏÛZ0C\mtx~…ŒŽ’—œ¡¥§©«®°±°¯³°¬¦Ÿ™”’’Š……ƒ„‘¢ÎáÕ蜥•ƒ~}}{ye<3=:4K4$c¬‰72)6*;1+B|‡†Œ•ž£¡™“›š™˜—–•”••”““’‘‘Ž‹ˆ…ƒ¯³ºÁÆÈÇÆÇÄÀ½¼¾ÁÃÄXXWUSRQPJ?7@[{“žª¬°´¶¶µ´© ”‚hQNWYf|w|y‚}}Q7;WL@D0GVKSrr‚m8+075(9,8TJBf5#0eq`600(-0,#Xs‚¼ÍÕ‰/4G`pux~„‹‘–œ¡¤¦ª¬¯²²²°®²°«¥ž˜“‘‘‰„„‚ƒ‘¢ËßÕŧœ¤—†€|ya;3<:5L3(]¬¢E0&509.)L€ˆ†Ž™ ¡ž™–šš™˜—–•”•””“’‘‘‘‰†‡‹³·¼ÂÅÆÅÄÅÃÀ¾¾ÀÄÆÆTTRQONMLI>6?Zz’©«¯³µµ´³©Ÿ“‚gPMWXbmegwy{YNoWaR9:+He]3jƒsUNB?1/0#E,:W8G;0p~b,;2 -52ƒ†^¹°Áĵ,';Meswy~ƒŠŒ•šŸ£¥¬®±³´³±°°®©£œ–’ˆƒƒ‚ ÆÜ×ȧœ¢™’‰…ƒ}y\83;97N2+T¬–—W,&496+(\…‰†‘ž¢ž™˜ššš™˜–•””””“’’‘’‘Œ„€‡–£º¼¿ÂÃÃÂÁÃÂÁÁÂÅÈÊÉPPOMKJIHH=5>Yy‘œ¨ª®²´´³²¨Ÿ’fPMV[eick‚{EZQbaXu:5qi?<qƒdU>A,%%9:2.$-0-VE)7lˆ}‚:%--0WtxÃ¹ÃÆL%4@Rhvyz~ƒ‰‹“™ž¢¤¬®±³´³²°®«§¡š”‡‚‚€ŽŸÀ×ÙÍ®¦¡›”Œˆ†ƒ}wT5299:P1)J¯™f*(3@3)/mŠŠˆ•¢¤œ–˜žš™˜—–•””““’’‘”‘‰€‹£¶ÀÁÁÂÂÁÀÀÁÂÃÄÇÊÌÎÌNNMKIHGFG<4=Xx›§©°²³²±§ž’€fOLUSgqpmmkaR‚‚qFnPPoeC+KKw]c4@)/<698*4167?5&=hui¯r"&%+%-xj¾ÄÍ38@Rivzz„‡‰’—œ ¢«¯²³²°¯¬©¤ž˜‘Š…€€~Œ¹ÒÛÒ®¥žžš”ŽŠ‡‚zsL2179=S0#C±‘•r-+3B0)>{‹Œ˜¤£›•™Ÿ™™˜—–•”““’’‘ޓކ€„–°ÃÅÄÂÁÀÀÀÁÂÄÆÉÌÍÎÏÍNNMKIHGFF;2<VvŽš¥¨¬¯±²±°¦‘eNKTmgZals|y tV7qsBra2A@B4ib:IC@9&9.89*TK%5$<kzVŒ“t&)&"P“ezÅÇÓÃ/-);<Nftyz…†ˆ‹–›ž¡§©¬®¯®«©§¢œ•Šˆ‹ƒ~~|}‹œ³ÍÝ×®¥žœ˜“‰†€vnE/068@U/ =ª—}5(4@.,S„Œ“›¢¢œ˜šž™™˜—•”““’‘‘ŽŽ‰‚ƒ¤ºÈÆÄÂÀÀÁÃÄÆÈËÎÏÏÎÍÌPONLKIHGE:2;VvŽ™¥§«®°±°¯¦œdMJT‘qSi‡š—7=*d¤Hqc0(gZ:R…~xZ57=8.1:$*9FM59F}™kE{«0&+x‡h ÅË×c!6?6Iaqwz€†„†Š•šŸ£¥¨ª«ª©§§¥ 𔉆Ђ}}{|Š›®ÉÞÛ¯¤Ÿ›•‹ˆ„|qh?,/58BW."9œ‹@5:,0i‰ŒŒšŸŸœ››™˜—–•”““‘‘Ž‹ƒ~‡œ²ÁÆÆÄÁ¿¿ÂÆÈÊÌÐÒÒÐÍËÊQPOMLJIHE91:Uu™¤§ª®°°¯¯¥œ~dMJSneŒ„ug90o\Q#9Fy]C{p^vBC;02 0'<n]H3L\“°™2V„:!5’suÁÀߘ +>G2E^ovz€‡„†Š”™Ÿ¡£¦¨©¨¦¥§¤Ÿ™“Œˆ…Š‚}}{|‰š¬Çßݯ¤Ÿš“Їƒzne<+/48CX.&7¡Œ”G65,3w‹‹ŒžŸžœ™™˜—–•”“’‘‘ŽŽˆ|Š¥»ÄÄÅÃÀ¾¿ÃÈËÍÏÒÔÓÐÌÉÈQQOLJHFE:6,6Xuˆ˜¨©ª¬®°±²¢œ“_EDQb’}jl{lBKŠgQ[XK8`^?aŠ^‚>!7@87>7\z<@YvˆÈ¦E7b$jiˆÕÚ¤(( 9M@K[iqx„…‡‰”–˜›œž ¢££Ÿ©¢”‡€„‡ƒzqq{‰‘¢¾ÕàÈ—¥–’’Š€lS&(43GKF4&@~¦’œE//0M}‘ŒŽ Ÿž›š˜˜˜˜˜—–••”“”••’‹‰Š‚‰¢¸ÀÃȾ¿ÂÅÉÍÏÑÏÏÏÏÏÏÏÏÎRQOMJHFE;6+3Utˆ™¨©ª¬®°±²«|gTPWVck}€{~o.>j_iaNIWEpgGja”ZU? 21/+*'&]bKMtvtHir<":#3‰mÅÀ´R.*"?`AL\iqx„…†‰Œ“–—››œž ¡¢£ ¤•‚|„‚‡‰‚zzƒ£¼ÕãÌ• •”‘‰‚|eI')43HJF3!5t¥”žT#-+2W„Œ’ Ÿžœ›™˜—˜˜——–•””“”””’‹ˆ‡„ލ¼ÁÂÆ¿ÀÃÆÉÍÏÐÏÏÏÏÏÏÏÏÎRQOMJHFE<6)0Rrˆš¨©ª¬®°±²«|dMFJ\irwt}|YIVVSjWN_TGvATcRŸ}D`%'*/2+#)@;UR‘wVEdB#(*)Ylf‰Ç¥˜$-)"BdBN]jrx~ƒ„†ˆŒ“•—ššœŸ ¡¢¡–•|u…………ƒ€†Œ¤·ÔäÏ“™”–‘ˆƒvX;',34HHG2(e£–žh/)%7hŽ‹˜ŸŸœš™˜—˜˜—––•””’“””’ŽŠˆ„‰™°ÀÃÁÃÁÂÄÇÊÍÏÐÏÏÏÏÏÏÏÏÎRQPMKHGF=6)/Rrˆ›¨©ª¬®°±²¬¢“{[FJ[}qhvpGG_\hoNIR5pd&W=™ƒ2x1*?C7-@@VVnŒeNL#'28–Šsp{ z 0#(JcEP_krx~‚ƒ…‡‹’•–™™šœžŸ ¡¡˜‘–‘wp…„ƒ…‰…~~„¢°ÒâÍ‘“’–‡ƒ~kJ.)/36IFG0$"W¡™šw7&#Ay—ŽŒžžž›™˜—–˜——–•”””‘’““‘‰‡ƒ¥ºÄÄÂÁÄÄÆÈËÍÏÐÐÐÐÐÐÐÐÐÏSRPNKIGF>8,4VuŠœ¨©ª¬®°±²³¦“{aVf~‡g`nxzgO6OYtiJD6CˆO(H‰>€‰,sI2B?+B#'<ZY,0etD!4!„Ÿ¦±¨¡¹¢D(%"#-MkGRalsx}ƒ„‡ŠŽ‘”•—˜™›œžŸ ˜‘•vo…‰ˆŽ”€ˆš¨ÍÚÄ‘‘”‡‚v]=(*327JDH.-$IœŸ’;$(O‡š¢žœš™—–•——––•”““‘‘’’Œ‰†„˜²ÁÅÅÃÁÇÇÈÊÌÍÏÏÐÐÐÐÐÐÐÐÏSRPNLIHG>;2<^{œ¨©ª¬®°±² ’„q_]d\U\gxzcVESZziN=+zrJ>4qRhM\e22:;V)1,;<'TeO0$''.§¢±·¿µ’K&11&?mJTcmsw}‚ƒ†‰“•–—˜š›žŸŸœ•”zt„…jZeuy~†Ž¡Ç̶‘”‘Ї€lM4*+629KBI,4(<–¦Œ„A&1_—“£œ›š˜–••——–•””““‘’‘Œˆ…‡¡¼ÅÄÄÅÅÉÊÊËÍÎÎÏÐÐÐÐÐÐÐÐÏSRQNLJHG==8Ehœ¨©ª¬®°±²§ž”‡nRDDH`nblqZ[K[`tM*:QRQ0k\g¨lA{/,ER3 0!"A;v•06!&&B§·ÁªÈ£E&"<4 ;pKVdnsw|€ƒ…‰Œ“”•–—™šœžž¢›“Žz‚wR;H]ekt‚›Áº¦’˜‘Šˆ‡~a?/0,92:K@I+3*0ޮЉK)=m“’Œ—¡œœš™—–•”—––•”““’‘‘‹‡…‹©ÃÇÁÃÇÉËÌÌÍÍÎÎÎÑÑÑÑÑÑÑÑÐTSQOLJHG=?=Kn†‘›¨©ª¬®°±²°§—^FIYefecujOhP[K[W6%rzCXW8~Wrˆ‚=*!3.ek!3! Vw:"=B&7Ds”’³®Áp-)(2;,'M~LWdotw|€‚…ˆŒ’”••—˜š›§ ’…~‚‚z}†{ffxz—¾¯œ“›‘†‡‡|[7-5-:2:K?J*0)'‰´ŠS+Du“Œš œ›š™—•””–––•”““’‘Ž‹‡…ŽÇÇ¿ÂÉËÍÍÍÍÎÎÎÎÑÑÑÑÑÑÑÑÐRNKKOOLHFFK[s‹œ¤¦§¨«°±²¯¥—„gNJSZ_`t…n[=AYKT.dzJZMS9oZZz£Z8!>)ƒ’P#n^.%VWkdir|”Ém' '?F1-+,TxWTdwvtxy‚ƒ…‡Š”•–—™›œœž›˜•’zxƒŒ€t“£±µ§˜”–•„p8+.504;DVGJ750 `´…[!Z‹“”œ›š™˜—––•••”””““”’Œ”ˆ‡£»Ã½¿ÂÅÎÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒQMJKNOKGDEL^uŒ›¢¦§©«°±²¯¥—„gNJS\`ekrs-,ZPA"N|;4JNQMuhlVx—]*v›q%P?=GCa_ˆ¸‹gft¾³!'85>E3.+,RvZVewwv{zƒ…‡Š“”•—˜š›œŸžœ™–”’‘††’’‰—¶ÅÌÈ´¡—’Œ……„V321007:GSHH8;2V«‡a0bŽ“”Ÿœ›š™˜—–••••””“““”’ŒŽ“ˆ€‰¤¼Ã¾ÀÃÆÎÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒPLIJMNJF@CNay™ž¦§©«®°²³¯¥—„gNJSZeabsu,2a@Ibv]'%ARP]rjxQt…‰†15>J€V:EE*@ty†‘®®cd€°ËF+9H%;C71,+Nt_Xfxzz~}‚„‡Š’“”•—˜ššŸž›™–•”’Œ§¹§¥Æ¿Â»«žš“‹ˆˆ{8/65,0;:MOJE9?3KŸˆeGp‘”Ž”ž ››š™˜––•••”””““““’ŒŽ‘†§½Ã¾ÂÆÈÏÐÐÐÐÐÐÐÐÒÒÒÒÒÒÒÒÓOKGHKLIE=BOd{Ž˜›§§©¬®°²³¯¥—„gNJSVf[j‡‹]<?TFu‚^58+FQMYcYua^{p’_=gTbG:Q -i‡—›µÁsW]zËj $>-:8A<4,+Hpd[ew{}‚„‡ŠŒŽ‘’“•—˜˜žž›š˜—–Œ‘µÌ¸¥ÁÆÅº¨žš’‡Š‰^+-37+0?;QKJC:=1#C˜“†c`}”””¡›šš˜—–••””””““’’‘’Ž…ƒ‘ª¾Ã¿ÅÊÊÏÐÐÐÐÐÐÐÐÒÒÒÒÒÒÒÒÓMIFGJJGC=BOd{Ž˜›§¨ª¬®±³³¯¥—„gNJST[g„„‚Y??Fq…>+A:6IFIJTHoWLvpƒu0qmGR ,>H”“¥¯¹¢]]U§v 2-,,64?B7-*Bmh[btz~‚€„†‰ŒŽŽ‘“”––››šš™˜———–—·Ï½¨¿Éƹ¥š—†ˆ€9/0+6/1B?TKHB;6.)?”™ƒ\s…••‘•œŸ›š™˜—–•”””“““’’’‘ŒŒƒ„—®ÀÃÀÈÎÍÏÐÐÐÐÐÐÐÐÒÒÒÒÒÒÒÒÓLHDEHIEB@CNay™ž§¨ª¬¯±³´¯¥—„gNJSUW{“j}f7N\] )NE=A=GEMCjFIszwzM‹Q5275[a†˜¤Ê½lLX†©./)C06 1=G;-*=iiY^px}~€ƒ†‰‹ŒŽ‘“””——–––––•šž¥¿ÒűºÆÅ¹£•‘އh%63)232CDSMDB;4,-8ŒZ~ˆ“–•–šœšš™˜–•”””“““’’’‘Ž‘‹‰†œ²ÁÃÁËÒÏÐÐÐÐÐÐÐÐÐÓÓÓÓÓÓÓÓÔJFCDGHD@DEL^uŒ›¢¨©ª¯±³´¯¥—„gNJSVo‚f~ZBVho8;>FMH:BKOHD\MMXp}z†.1'lvfll™¤º—ZMv¬:*0E+?%:.;K=.*9giWYku{{€ƒ†ˆ‹Ž‹‹ŒŽ‘’“’’’’““““˜ ¬»Èƾ»ÎÐÄ©’‹‰‡yF*460,53BJQP?D;8--,‚^‚‡–˜——™š™™—–•””“““’’’‘‘‘Šˆ€‡Ÿ´ÃÃÂÎÕÑÐÐÐÐÐÐÐÐÐÓÓÓÓÓÓÓÓÔJFBCFGD@FFK[s‹œ¤¨©ª¯²³´¯¥—„gNJSUufx~:[CMn88;I;U9NQ\DBKYL5\Œt ^Qv¥—‡–»Æ•Neežs&0&aO7,6-:M>.)6ehUVhrz~y€ƒ…ˆ‹ŽŠŠŒ‘’‘‘‘Ÿ¡¤¥«½Êɯ¶°š‡…Œt.8-68'64BNOS<E;>/,#wœ„d‚…Ž—š˜––š™˜—–•”““““’’’‘‘Œ‘Їˆ¡¶ÃÃÂÏÖÒÐÐÐÐÐÐÐÐÐÓÓÓÓÓÓÓÓÔ?<:=BEC@9EIUu™ª«¬®°²´µ®¨œ€aUQHR`y‹@1,fsC9:N.9ERlc[RV3vP2NqvƒL!lj‡ÉÊÏÒª]MW–•#*09W6%1?8A>E4+5^S^M_mz‚‰€ƒ…‡ŠŒŒ‹Š–Ž}zƒ…ˆ‹Š†ƒŒ{ˆ“™‰…—sp‚Ž20--0456<CCSFSD0D-+(i¦z{Ž‘”—šž˜——–•””“””“’’‘‹’‘Œ†}†¡ºÂÌÑÑÐÑÓÑÑÑÑÑÑÑÑÔÔÔÓÓÓÒÒÖ?<:<ADB?;HMWw‘šŸª«®±²´µµ²“ylKFn†cSc†C.NGG8E?E=;6YzlkXTJB`^aHwq`ˆŸq›ÆÃϰfbbt·J!1..H>-168@>E5,4]R]L]kx€‡€‚„‡‰‹Œ’ŽŽ‹{dZ^uw{~~|zxjksrƒƒqZcldh‹•u20..0357?A@PIXE-F2*'d£~€Ž‘”—šž˜˜—–••””””“’’‘Œ’‹†}‡¢»ÃÍÒÒÐÑÓÒÒÒÒÒÒÒÒÖÕÕÕÔÔÔÓÖ>:8;@CA>>LR[y’œ¢««¯±³´µ˜~dM\‹sMe}w@;X2*1HEBBJ+[‰onaZi>Un‘SO7JŒ†u«ÎÎÍ|TYl³u&.1.)9D./08@=F7.4ZP[J[hu|‚}~€‚…‡‰Š”’‘kaa[]adfffe^U[axwwntr€|—‹I10///258A=@LL[F-I9)&ZŸ…‡‘“•˜šœ™˜˜—–••”””“’’‘Œ’Š…~‰¥½ÅÏÔÓÑÑÓÒÒÒÒÒÒÒÒ××××ÖÖÕÕÔ<97:?A@<@PW_z‘œ¤«¬¯±³µµ§˜›…^oˆrVNy’zu?CC<11;DK6\-X–hVdeeb[mv\<9_ˆ…†“ÔÕ¨_an–¥!35,0,0C(-28><F:04WNXGWdpw}{|~€‚…‡‡‘““ƒ|zvxz~ƒ„…Œ„ˆ‹¢¥¤—€Œ’“’m00120/05:=:FKLXF2KB((PšŽŽ’“”–˜š›œ™™˜˜—–••””“’’‘Œ‘Žˆ„~Œ©ÁÈÑÕÔÑÑÓÓÓÓÓÓÓÓÓØØØ×××ÖÖÓ:758=@>;@SZay›¤«¬®°²´µ¶¬—”„t‹ƒCJ^‡…ovI<BR80*=W0_8[£g<[elk[Z8[bjz¤¦ƒkÒ»rho§²F*41.2++@+008=;G<33SKVDT_kqwyy{~€‚„…ˆ€y{|}‚…ˆŠ¨¯¹µÂþ§}ˆŠ“™xD,.242./6<67PLIQF:JK),H•–“••–—™š››šš™˜˜—––””“’’‘‘‡ƒŽÄËÔ×ÕÒÑÒÔÔÔÔÔÔÔÔ××ÖÖÖÕÕÕÑ9646<><9?S[avŠ—£¬®°²´µ¶ªšˆˆsN<Js—kp^N?fW-,&AY<SCm§m?O_gnaKCZh‡s–’s‰Ì_€©g&;1774&,=67,8<:H?63PISAP[flrvwy{~€‚ƒŠŽŽ‡{€…ˆ‹˜£«›¦«ª“›”“[(--363--6>23WKINF?GQ+2C’•——˜˜™šš›››š™˜˜——””“’’‘‘‹…‚€‘°ÇÎÖÙÖÓÒÒÔÔÔÔÔÔÔÔÔÔÓÓÓÒÒÒÐ8535:=;8=R[_r†” ¬®°²´¶¶¨²Ÿ‹{P6M[‰Š^uIDJ‡H)-0XPHLO…”k_LdEgzj`[]‰’…vÀ¹h{¤Àp"=-6;96)559=/7;9HA83NHQ>MXchntuwy|~€…‹”›”†€~|zz}ƒŠ”…‡’ޔބqD&-,384-,6@4/VGKQG?CV,8A‘¡•™™™™™ššš›››š™˜˜—””“’’‘ޑЄ€“³ÉÑØÛ×ÓÒÒÕÕÕÕÕÕÕÕÐÐÐÏÏÏÎÎÏ7425:<;7;QZ^pƒ‘Ÿ¬®°²´¶·´Ÿ“…eLIMw–d[zP6JŽ9229oHKPW•{a|PpN>e†d…€f®›’m»ž„©È‹:>4330493@+3?87;8HB93LGP=LVafkttvy{}€ˆ‰ˆ˜€…‚~{z}€‚ƒˆ‰~ˆŒœ–‘~X7)#,495,+6A8-SCOUH<AX.=@£”ššššššššœ››š™™˜˜””“’’‘Ž‘Šƒ€”µËÒÙÛØÓÒÒÕÕÕÕÕÕÕÕÎÎÍÍÍÌÌÌÎ72.06950@X]Yp‹•”¤«±´²±´·¤˜…gLDJt`b[=:^%,$Fe91IX‰HyurjA1k‹xnaw•qίšÁ§@*>9863=-/97463@?=;::;;DOA9WSi`glrssuz‡‡ˆ‰‰Š‹‹‰ˆ„€|xuspprw€Œ—žŒŒRH'/,,644>:4:71fHNXI:D]055¥™¡’”žœšœ™šššššššš˜˜–•“’‘ГЀ}p„ºÑÕÙØÕÔØÜÙØ×ÖÕÔÓÒÐÏÏÎÌËÊÊÑ62.16851F]`]r‹“”¤ª±´²±´·¤˜…gLDJ–p[dZ8>gu*/(GY66+r¢ˆbZ„zh[ahq€rRppv|ǘ³´J!/;++4<=-/97463<<==<;:9@KG:NM^^fkqttw|‚†‡‡ˆ‰Š‹‹†††…„„ƒƒƒ†‹‘•—˜—Œ•u@I-)/-645=:4972gIOXI<E[364Œ¦™¡’”žœšœ™™™™™™™™™˜—–•“‘Š’‰€|q†¼ÒÖÙØÕÔ×ÛØØ×ÖÕÓÓÒÑÒÒÓÔÔÕÕÑ42015642Ocebu‹’“¤ª±³²±³¶¤˜…gLDJŽr_`P=LfT00+KJ2<,ˆ¦ƒ€\‹Œpfr\]‹yAvns‰ÚÐÂk(%78/687=-/9746369<??<85>EM9BHOWbhostx………†ˆ‰‹ŒŒ‡ˆŠ“–—œž ¡Ÿ›–“Œ•Q.H2#3/656<94865iJQXH?GV873‹©š¡’”žœšœ™™™™™™™™™——–”’‘Šˆ€{r‹ÀÓ×ÚÙÕÓÖÚØØ×ÖÔÓÒÒÔÔÔÕÕÕÕÕÒ22123443XgigyŠ“¤ª±³²±³¶¤˜…gLDJwyjWEI]Z:5.,QC.<3g—gtt{“vdeYS{JqR‚†ËËw59;704?=8=-/9746315;@@<73>>K8:HCKY`hnrw†ƒ„…‡‰‹’–›Ÿ£¤«¨£ž™–”““.-B1%52678:85559lKSYHCIQ?91‰š¡’”žœšœ™˜˜˜˜˜˜˜˜—–•“’ŽŠ‡€ys‘ÄÕØÛÙÔÒÕØØ×ÖÕÔÓÒÑÔÔÒÐÎÌËÊÔ01332234\hij{ˆ”£©°³±°²µ¤˜…gLDJdwiTEJ_V.8-*UD,98Nƒ]Uzs’xvigO]‡b’@\o˜/5.51,.0;H=-/97463-28>?<73@9B77K??LS]ejr{ƒ‚„†‰ŒŽ–—š¡¥§©ª¨¥ œ˜•“šY:8-,5669;98634=pLUYGHKLF;/‡°›¡’”žœšœ™————————–•”’‘ŽŠŠ…€wu—É×ÚÜÙÔÑÓ×××ÖÕÔÒÒÑÎÎÎÍÍÍÌÌË.1430025[efj{†‹•£©°²±°²µ¤˜…gLDJ]oYQI=Tb-92)TG*8@e~o>Vcy‚tu^`i©^Bd–{;>)0073+9H=-/97463-059;:86=99;6KB9<DOX`it|€‚†‰Œš› £¦¨©£§¬®ª¢—‘“:F0/3496;=77713AsMWYGLNFM=-…´›¡’”žœšœ™––––––––•”“’Ž‹‡„€uwÎØÛÝÙÔÑÒÕ×ÖÖÔÓÒÑÑÉÊÌÎÐÒÓÔÌ-154/.26X`ai{„Š—¢©¯²°°²µ¤˜…gLDJZpK@B3Pp0:>+NG(;4o}q43AZ}…wwq‰rŠuZi]LH>574>8/<==-/97463./2478994<6B5EH;-5BLU`lt}…‰‘–˜›Ÿ£§ª¬£§°¬£—..E.743;6<?668/2CuNXYFOOBR?,ƒ¶œ¡’”žœšœ™––––––––””“‘ŽŒ‹„ƒ€tx¢ÑÙÜÝÚÔÐÑÔ×ÖÕÔÓÒÑÐÎÍÌËÊÉÈǺ,164/-17U\^gzƒ‰—¢¨¯²°¯²µ¤˜…gLDJVyH+43Ut2:H-HE'?'f…pDB8cek{„oz‰šiWd><@2/66:*&??=-/97463//0147:<-?7I4?L?$-9EOZgo|~…‰‘‘“—¢¨¬®©©¨¦¡›•‘m.=@/@23<6<@668.2EvNYYFQP@U@+‚¸œ¡’”žœšœ™––––––––”“’‘ŽŒŒ‹ƒ‚€sx¤ÓÚÝÞÚÓÐÐÓÖÖÕÔÓÒÑÐ×ÔÎÇ¿·±®z52/-.158;Yjoz…Ž™§«¯¯±µ««‘ƒkD>Bme-*<AYS;18JGE.7*T’€`9SNXtzd‘…Šr©D^oC<+3>4/08K;B.&E7380;71--28=>9??5;HH;504?KPQcfkqx~ƒ†…‹’–˜œ£©žŸ¡ —’Žc4;G?>6489?C:--5DNpTT]QP\SFF2ªž–„ƒŠ’•”““““““““ŽŽ„Šz|o½ÏÜÛÙ×ÕÓÒÑËÑØÚÔÍÏÖÃËÆ°–|X8120.,+*++>Wcer‚‘Ÿ§«¯¯¬°µª˜˜ƒcC;i›Pƒ:$TU?@<8G?9.3)C¡’sK^6Rr„Zhtqˆž†—Ÿ¡‚1:,9A+8:SA.'G7280:61..28<>9??6:FFH@747::7GKS]is{€ˆ”—™œ£© Ÿœ—Š„€P+5?43362>C:2254CNoTT\QP[SGF5‘¨Ž†~|‚‰ŒŒ‘’“““–•“‘‹Š‚ˆxzp‘¿ÐÚÚØ×ÕÓÒÑÛÓÏÔÓÌÉ˵•iE3-*0211/,(%#?SYXi“¥¦ª®®¬¬°´°˜œ}OLÒqQ”1*8RGCD=C63,/*@‹~˜`PGDh†RY…g‹¨‘it•¢h,+4)/>>BM>-)I8170851//27:<:?>79BDKJJQ[dijvwy{~€‚ƒ‹–™™œ¢§©¦¢™–•”a5-32:==;879;;98BOmTT[ROZRHF9•¥›{usw|‡ˆ‰‹Ž˜—”‘Ž‹ˆ‡ƒ†xxršÅÒØ×ÖÕÔÒÑÑØÏËÑÒÇ·®‹jC+##*0-34653-($:KPPd|¡¥©««¯³¬ª€gÓkOb},<$SM=>F?2=*1.Aym”wTG,MnQm–^a šoCJ’~3 907.KQ;-,K906064200368:;?>98=@QT\gs}ƒ†…†ˆ‰‹ŽŽ“˜™˜šŸ¤¤£¢¤¨®µ¹ÃcL943.267532/+APkTTXSMXQIG>›¡˜wtpnortu{|~€‚„…†‘Ž‹‰ˆ‡…†ywx©ÎØÕÕÔÓÓÒÑÑÐÏÊÅĽ©”_=#%268=A568850*'3DILbyŠ—¤©ªª®³§ž—v½ÊPPDSb$-4\G62M;3S.81:€p„{<$9]q–’jcƒ~d>V«£†1>+9DQ7-/N9/50432123567=?=:78<SZepx}€‚wy{ƒ†‰Š“——••™ž“”—ž¨´¾ÅÆÍѸ{G86+@G8.34-?QhTTUTLVOJGD¡œ•ttspooprqqrtuvwx‚ƒ„…††…ƒyu|¶ÖÛÑÑÑÑÑÑÑÑÕÕÈ··¹žyF4*2:>HUNA@>;61,)3ACE\tƒ¤¨¬¬ªª®²§žžÂ¦M@[1xC((ZM63I45a>@2;]yu}€pAHf‹UP^isjkKH›„•!769DR4,1Q:.40222233445>?<<639OZjw~‚…‡‡‡‡††……„Ž’•”“–“˜ ª´¼ÁÈÂÀÔÚ¼Œ[I4,8>65>>RfTTSUJTNKHJ§˜’z|}zvsuwnnnmmmmmrstwy|~~€{rlz¼×ÙÎÏÏÏÐÑÑÑÔÓÈ»¸ª}L0*+7EMRVN_ZRI@830:B<:Ql£§««©©±¢·µŸe8@=`˜7)6@QAE:,4_VF5K/K‹vi}}ƒ’«uMQ\hY[I~“ŠO´\81$Fb2,3S:-30112344333??<=6/6LYkvyyyzƒ„…†ˆŠ‹‹Œ’‹ŠŒ“˜ ©²¹¾ÁÄÏÉÉýÉʇU135'#/=RdTTQUISMLHN«•ƒ‡Š‡€|}€rqpnljhgijklnoppuog`r»ÒÑÌÌÍÎÏÐÑÑÐÉÁµšqG-248@NUM@X|uhYLB=:BE81Hg~Ž£§««©©±Á¬£SJWuœD$'&4*MLY,'2WiH9W+-€|d/Kaq€IaeYmpnkw޵jMN:+HEO0,4T:,30013444322@?<>5-5BRhv{|~€|}ƒ…‡ˆŠˆ†ˆ‹‹’©´»¿Á»ÊÅÊÍÉÖÕËÀŽE %(<ScTSPVHRLMHQ®“‰“ˆ‚„‡xvtqmjhgggfffeeele^Vj·ÌÊËËÌÍÏÐÑÑÖÆµšd- 20BLHHRWRX€„ƒxeRGCLA52@\}’Ÿ¥«¬ª¬¯¯¤‘‡mVv¨p1% .--+Uo-5LvOUa/"3…ŠbW}o5wmo`pŒs{”J8l*>JL)+-O13603.8;B334:49J6=:2EWhov}{t}~€‚…‡‰ŠŽŒ‹‹‘ž¡¥¬²¸½À¿ÀÂÅÈËÍÎË×Û¹o+-=TaQLM@<OCM:_©ˆŽ‹Šˆ‡††„}yurpog`^YWbqssgBX§ËÏÇÌ××ÐÔÕÇ»·¥q5'AUMKKNSWYYU†‰‰ƒvg[UOC50=ZzŸ¥¬®¬«¯³¡˜„m}o«G&'(.2>UTc/^tC_e48SOgtu€JLE}hqW—ˆ}d…k,R”W0=I.12T78;53.8;B344;7?CA=:0IXfkpz}z~ƒ…ˆ‰ŠŽŒ‹ŒŽ’ž¡¥«±·¼¾¾¿ÁÄÇÊÌÍÈÂÍÚ€A"H-e`CNSHPCH9f©‡‘ŒŠ‰ˆˆ‡„€|xvtsjec^\etwteFbÍÎÊÎ×ÖÏÓÔÆ»‘c?*3LWMMORVXXXVŠŠ‹‹‡}ofWI7/:VwŽ ¦¬®«°µ™„†£a‹3+(("$-Q€y[Yi…k9MF%5tZ3L„d];HYVješŽ€ŠMV†U*hŠ76C/13U79;53.8;C344<>D=PB9.IYehmx€„†‰Š‹Œ‹Œ’”ž ¥ª°¶º¼¼½ÀÂÅÈÊËÈÍÐÖâÔ‘I+'`E;RHJQC?7q©†”“’‘ŽŒŒŒŠˆ…~{zxqlkfclz}wdOt·ÏÌÏÐ×ÔÍÒÑÁ•Z/.<KRNMQVZ[ZWVY‡‡‰Ž‰{nbQ:.7RuŒ¡§¯¬®±²œ‘‹º£`X?=**""8IFq~hV?Q<NUeo4,r^C;@7tJdn‡‹}‡™scž<9–X-<*,.P24614.8;C344:GD>ZO8.AVglpx~~€ƒ…‡Š‹ŒŽŒŒ‘”–ž ¤©®³·¹º»½ÀÃÆÈÉÌÝÚÊÒâÍ¢<4G?9I:JQC76~¦†—–•“’Ž‹‰†ƒ€~wttokrƒ{d]‰ÁÑÌÔÓÖÓËÍȵc7(BWZVOPW_b`[WV\‚„Š“˜“…yp\A05QtŒ¡§®°®¯²©£•Ú‚yL;@,4483EF1IOD->KW][4? 3=???Ov[N}„w—”2.©‹Gm&7*-.P34714/9<C4557L@F_b;/6Qktvz{y‚„†‰‹ŽŒŒŒ’–™ž £¨¬±´¶¸¹»½ÀÃÅÆÍÏÕØÓÍÒÜ£I)Y78GPMD16Š ŽŠ˜—–”“‘‹‰‡†…‚|{|wqwƒ‡€gmŸÊÓÎØÕ×ÒÈÆ¹ M13OZXY[V^ghb[XX`t|‰—Ÿœ“‹hH36Qu¢¨¯±¯®°²£«’›ØbŽ_".)47;EQP<PF5.>JF3&443K|W2AHKAhkRY‘‡€~tA{¦n8ž@2024V8:=75/9<D4554K>LeqE0.Nmyyzzx‚ƒ…‡ŠŒŽŒŒ‹”™›ž £¦ª®±²µ¶¸»¾ÁÃÄÈÇÍÕÕÏÒÜáŽ3@14BLGE/9”—Œ––•“‘ŽŽŒŠ‰ˆ‡ƒ|uz…ˆ…n~²ÎÕÓÙÖØÓÆ½¦‡E6?UYWYY^fmkbZZ]dYi’œ›™ŒsO78Sx’£©¯±¯®°³¥«˜™»\xS$*/0-B?@<:03=]=6,%0S;0Ey:9KJO>rmNŽˆ|k–jik’2“z%034V8:=750:<D5562CAKmxS1.Nmvux|}ƒ„†ˆ‹‹‹‹•šž ¢¥©¬¯°´µ·¹¼¿ÁÂÅËÌÉÌÖÙÕÝÓ|--35BAF/;šŠ•””“‘ŽŒŒ‹Š‰ˆˆ„€ƒw{…‡Št‹¿ÐÕØÙÖÙÔÅ´•nCCR`]\]Wfmrl`Y\bf@Ur‰”™œŸ“yS99Ty”£©°²°¯±³¬¦™•šfO-*)025-E2.4)%/:pFF;("g>(<Ze><I>LYvVy‚„‘}•h}/±Rr¥+-/Q358250:=D5661;EIuz]12Pkqpu~ƒ„…†‰‹‹‹‹‘–›žž ¢¥¨«®¯³´¶¸»¾ÀÁÇÅËÕÓËÑáÞÝÌ[0)>B=G0=žŠ‰™“’‘ŽŒ‹ŠŒ‹‹Š‰‰ˆˆƒ€„€x{…†Œx“ÅÐÕÜØÖÙÕįŠ_KScdZ]gejqtl^X]eh+F`z“œŸ¨ž‹e01O~£©°²°¯±´¨žœ¹e[A.,.!%@]'4*90-Mq?B@');f/6W<dAA]T<c[ugn‰ ŸP-I“ZÇ*$.%R>;:4,+3?A947/=A\usaB)Dcsxƒƒ„…ˆŠŽŽŽ‘“”•–œž ¢¥¨ª«¯°³¶º½ÀÁÆÇÉÌÏÒÔÕÛÝß½>(7A(B"Rž†‰–““’‘ŽŒŠ‰‰Œz€|ˆ†„Œ¦ÆØÔÊØÜÔÍÈ¡fEUVW[_eilym`[_dfei%8Mm“¤¤¦›j2-I}“£ª°²°¯±´§¦˜Š™}\4!1+-4FG%41/5-Lz;0M3,.RG0NLIcHmbVBXu]†ws¥Ÿh+%wtÄ3%,(R:B66-,3>@8474:?[tve<)Edry€ƒƒ„†ˆŠŒŽŽ‘’”•–œŸ¢¥§©«®°²¶¹½¿ÁÆÇÉÌÏÒÔÕÖÜäÍw)0;*=!ZŸ…ˆ˜““’‘ŽŒ‹Š‰‰‰€|~z”¡µÊÖØÕÕØÑÉ»]HWX[^bgkmrh^Z^dffg )7^“ª¦šn3)D{–¤ª°²±¯±´¬§š~u`(5'4<F4*28(7+U…49R,,%&RJTS<YRYxmRN_I‘‡~ž…I,"q ×9').Q5K080.3=?74729Galph<)Gdr{‚„…†ˆŠŒŽ’“”•›œž¡¤§©ª¯±µ¹¼¿ÀÆÇÉÌÏÑÓÔÒÚèÞ¼>%1,3 f „†›““’‘ދЉˆˆ…‚€zz‡— ¬»ÇÏÕÜáÒÓÎħvSO[]`cgkmoga[Z^dfge %/V®¨žl0(D{”¤ª±³±°²´«ª¡„l‰d$-/(47:200:,3)k‡.7R,&-HXMO7IMKojmDVN‰œ˜‰~*BB\ È+(&5N1S-:303<=537+>XlbfkC)Jer}ƒ‚ƒ†‡‡ˆŠ‹‹ŒŒŽ‘“””š› £¥§©¬®°´·»½¿ÅÇÉËÎÑÓÔÑÙäâàq'-*"tŸ…‡““’‘Œ‹Š‰ˆ‡‡‚‚}x~’¦´¾ÉÑÓÖÜáÓÑÌ»’bPZ_aeilnoo\ZZ\`cfgg$)3UЍ¬¦‘f+(G{¥«±³±°²µ¯« Ž„{b 8"293/7)/46-0ƒ*Q>@/6B-F1U@cUb^Ka\›ƒ§F/=Vʪ")%;J2U/9723;;427.EapcioF*Nfq€„†ˆˆˆ‰‰ŠŠŠ‹Œ’“”™šœŸ¡¤¦§«¬¯²¶º¼¾ÅÆÈËÎÐÒÔÔÙÙÞá±."+"'€›ŠŠœ““’‘‹‹Š‰ˆ‡†…~ƒ‚|{Ф¸ÆÇËÐÖÙÙØ×ÐÇ®€[Vb`chmoonmUX]`bdfgi$*3Rƒ¡©¯«’d('Gz¥«²´²°²µµ©˜ŸsR 6=?4.61+5-HŽq, @;$L8&<$68BFeXrUXSGv’„j"$IÉi3('@F7R57:44992179O`moxsC*Qgqƒ†ˆ‰‰‰‰‰‰‰‰Š‹ŒŽ‘’“˜™› £¥¦ª«®±µ¸»¼ÅÆÈÊÍÐÒÓÕØÒØÚÜ\#&.ˆ•˜““’‘ŠŠ‰ˆ‡……„~‚€z‚›¹ÊÒÌÉÎÖÛ×Ñ×ͼ™n[_b`dinpoliRZbgfeefg#*I|§®ª”h)#Bx¥«²´²±³µ´¦™‡¢m5,3+=7-2-$4%&8k…e1/..4G1);209(ML\€lK;8†{ d’{Vo¥d')BA=K>4<5487117?[dlx‚uG*Sgq…‡€‰‹ŠŠ‰‰ˆˆ‡ŠŠŒ’’—˜šŸ¢¤¥©ª°´¸º¼ÄÅÇÊÍÐÒÓÓ×ÕÚàäš) 5—–”““’‘‰‰ˆ‡†…„ƒ~}y‰¬ÌÖØÑÌÏ×Û×ÒÔǬ]^f[^cinpmieT^imjfdea@x§¬§•m+<w”¥«²´²±³¶¬¥¡–f;6>7)"5&27#B†x^5#*6A=7<!39(8CDFwz-<O’‹ edyQ¥¥½&+D?BFD2>6486017<cmp{uO*Thq†ˆ€Š‹‹Š‰‰ˆ‡‡‰Š‹Ž‘’–—™œŸ¢¤¥¨ª¬°´·º»ÄÅÇÊÍÐÒÓÐÖÜßêÞÇ.:‹œš‘““’‘‰‰ˆ†…„ƒƒ€zxŽ·ÓÜÚÕÐÒ×ÛÙÖÐÁ¡pR`iT]bhnolgcVamqmgdd[!*Cv©¬¥”w< ;i—£©°²°¯±³¬¬ …Y.:31.,+,-.,anKhI:--2M118!)O+*O/8uvWPo„œ‹~z¡¡^8*6(S.,7E,6:;6118@Pt€xoyq76Wj|€ŠŠŠŠ‰‰‰ˆˆ‰Š‹ŒŽ•–—šœž ¡§¨«¯³·º¼ÁÂÄÇËÎÐÑÜÜÛàÎߨg J–†”ƒŽŒŒŒŒ‹Šˆ†„‚‚|zwœÃÙÝ×ÌÏÝÛÖ×ÔÍ©€nid_^`dqzp^\f^adggea^U!)As›©¬¥•x=!<i–£©°²°¯±³¨¬ ‚a6D..--./00=?Q<8nY//)2O:&...=F#MF3Q^lttIm‰ ¦žh$7M1B3>9E5799624;CWvyt|p79Yl}Š‚ŠŠŠ‰‰‰ˆˆ‰‰ŠŒŽ”•—™œž ¡¦¨«®³¶¹»ÀÂÄÇÊÍÐÑÖ×ÔÜÑÞÞ‡QŒ–˜˜ŽŒŒŒ‹Šˆ†„ƒ‚}zw€ŸÄÖÖÕËÐÝÛÖÕÐÄ¢~plf`^dfovo_[bacegfb_\S"'=o˜§¬¦—z@#=h•£©°²°¯±³²§“‘†v@>*,.01110_;6$7cZ;,.8E>#+3*@C3GY0)6_‡‡|€k[]v†¦}VRM<>+KO787548AHax~{{m7@]q~‚‰ƒ‹ŠŠŠ‰‰‰ˆ‰‰Š‹ŒŽŽ”•–™›Ÿ ¥§©±µ¸ºÀÁÃÆÊÍÏÐÑÓÌÖÖÜá´`…’“Ž¤ŽŽŒŒŒŒŠˆ†„ƒ‚~zw„¦ÉÖÑÒÊÑÜÚÕÒʱ•{rpia_ghmqj^Z^effec_\YR#$8i”¥«¨™}C&>h“£©°²°¯±³·¢”‘ˆuB5./1221/-M21/:XSH'8@2=00,$H18\E;1Io~vˆrvH'.V0Plhs†tpd]486437>IPly}|‚ƒf8Ibw…‰†‹‹ŠŠŠ‰‰‰ˆ‰Š‹ŒŽ“”–˜šžŸ£¥¨¬°´·¸¿ÀÃÆÉÌÎÐÒÔÈÒÙ×àÖCp…ˆ›ŽŒŒ‹‰‡…ƒ‚~zz‰ÑÙÔÎÉÒÜÙÕÎÀˆvsskb`ekolaXZ`hhfc`\YXU#"3c£«©›G)@g‘£©°²°¯±³®¢¦“„^:444320.-,/>>8.XM=.:;(?;1()<0/r.>4/Ou|n|huqM`3E)MCWtclU95129EQWry}~…€\:Rg}€‡ˆˆ‹‹‹‹ŠŠ‰‰ˆˆ‰ŠŒŽŽ’“•—šœžŸ¢£¦ª®²µ·¾¿ÂÅÈËÎÏÖ×ÊÒÚÒÙãxu‹Š’’‘‘‘ŽŽ‹‰‡…„ƒ~|}Œ®ÐÚ×ÉÈÓÛØÔÊ·}qrrjbaakpfXT\eihea^[YY\"$.^‹¡««ž„K,Ag£©°²°¯±³¯¤¥‹_6.751.-/13:C6<,J<7:31-A=+-25)CSH/NJUiyxsjŠKR,:<O=D=$Ii:3.0<KX_sw~…yQ<[lƒ‚‰ˆŠŒŒ‹‹‹ŠŠŠˆˆ‰Š‹ŒŽ‘’”–™›ž ¢¥©±³µ½¿ÁÄÇÊÍÎÖÖÎÓÙÑÒᤠqŽˆ—‘‘ŽŽ‹Š‡†„ƒ€‚ФÃÑÒÃÇÔÛÖÓÆ®ƒvnpogaa_hi]TWagheb^\[[\`$%*Zˆ «¬Ÿ†M.BfŽ£©°²°¯±³´¨—‹—u8-40+(+2;AH<*75=5<93327?,/6;%\2\@Ej^Rf…t_Ox—bG@,*0D<(>B28A:2,/>P^dqu€‚pF>ap‡ƒ‹‡ŒŒŒŒ‹‹ŠŠŠ‡ˆ‰Š‹Œ‘’“–˜›œŸ¡¤§¬¯²´½¾ÀÃÇÊÌÍÐÐÏÔÚÕÑØÉ?sމ”–‘’‘‘ŽŽŽŒŠˆ†„„„‡…ƒ‘ª¾Ä¾ÆÕÙÕÓçsklkc_bbc[RUbgceb_[Z\^`_%%(W†Ÿ« ‡O/Bf£©°²°¯±³–“˜9;/*%$*6DM7:0&3CF90;=1'E3,:;5T>Er%7x‹z€ojDKnŒXL`;#98.8785/;2*/?Sagot€€j@?erŠƒŒ‡ŒŒŒ‹‹‹ŠŠ‡ˆˆŠ‹Œ‘“•˜šœž £§«¯²³¼¾ÀÃÆÉÌÍÉÊÏÔÚÚÓÒÝ_yŽ‹‹†Ž’‘‘ŽŽŽŽŒŠˆ†„„ˆ‹†}‚—¬µ¼ÅÕØÔÑÁ£}qjjg`^bf_PIXll_c`\ZZ]`c\%*$%Gz›£ Šc5Gk„Ÿ¥°¯¯²µ´«›Œ©mLQ.)$7UR>:(/26:E=+7AHD826>3@_:;+fP!Vio€yd96w†Žž,17/47.05-1028EVgqm†xyZ3Xvz‡‹Œ‹ŽŽŽŒŠŠŠŠŠŠŠŠ‘’“•—™››Ÿ ¢¥©¬®¯µºÀÃÃÆÌÒÐÑÒÓÕרØÞ™v”ƒtˆ‰Š‹Ž‘’މ‡‰Œ‘‡‡˜¬ÆØÚÙѰ†nrsl`YZ^`ZOLZkj]\\\\]]^_L%*$%Ey›¤¡¢Œf6Fj„Ÿ¥°¯¯²µ¬¬›…›oMJ8+"/B?2-&31<4E80A@?;7556+?_+1?YfA$C.U…n=9Jq¸l!44.58013+004<IZjts‚}}uI:Yw{‡‹ŒŒ‹ŽŽŽŒŠŠŠŠŠŠŠŠ‘‘“•—™š›ž ¢¥¨«®¯´ºÀÂÃÆÌÑÐÑÒÓÕÖØØÚ¸~‹saroy{~ƒˆŒ’ˆ†ŠŽ“’Ž’ˆ…’¯ÇÙÙÕË©‚ppmdZW\b[SPWbfdaZ[^__][YE%(%%Avœ¥¤¤l9Eg…ž¥¬°¯¯²µ§¬œŠqNF?*#,230*&82A1B47L@41572,%U5.47jDD8 -X‘/GT]\”.%:0.48210)/17BPaoxv…yg6D^{~‚‡ŠŒŒŒŽŽŽŒŠŠŠŠŠŠŠŠ’”–˜™šŸ¡¤§ª®³¸¾ÁÂÄÊÐÐÐÑÓÕÖרÔÙv^M`Wcfls{ƒˆ‹ˆ‹ŒŠ‡ˆŽ”˜˜Œƒ„•¸ÍÚØÐŸ|ojbZVX]bQMUdia^cX[`cb\TO<&'&%<rž¦¨§•s=Cd…ž¥¬¯¯®±µ¬«›†sOL;&'31382)98=592<OA1,03/)'K"EAEn‚}:DD54D>,qZVh(15-.3640-(05>JYht{u‚€uM1Mi„‡ŠŒŽŽŽŒŠŠŠŠŠŠŠŠ‘“•—˜™œž £¦©¬±·½ÀÀÃÉÎÏÐÑÓÔÖרÑã©ZRHSGRUZahotw{~‚ƒ‰“œœž–Œ…|‡£ÅÕÞ×˶”vh^TT[_\WGO^kja]`W\cgcYME8&%'%6nŸ¨¬«›zA@a†ž¤¬¯®®±´¶ª›‘xtPV4&-92/3./8>5?13=JB6/,---.#BN=uZs”X;A;?-9;RdX-,1-0112/*)4:FTbnw{p{†]4=Xu„„…‡‰‹ŽŽŽŒŠŠŠŠŠŠŠŠŽ‘”•—˜›œŸ¢¥¨ª¬°µ»¾¾ÁÇÍÏÐÑÒÔÕ××ÔÝÂJQRPEMNPSVXZ[gknqvšŸŸ•Œˆ„‘®ÑÝßÖç†mZPIRbfYJEWgidb_[X^ehcUF<8'$'%1k ©°® ‚E>^†¤«®®®°´»ªš–lvQ\1+06-#$&78>.G.3=BA=5.,/36-F<)kceoœY@2VhRPe38:+41/2.+.,).:BO]jswyosuA,Pgˆˆ††ˆ‹ŽŽŽŽŒŠŠŠŠŠŠŠŠŒŽ’”–—š›ž¡¤§©«®´º¼½ÀÆËÎÏÐÒÓÕÖ×ÙÒÓVSZWORQPNLJIHQTVX]iy…•™—˜›–³×ÝÞѵ“s`IFGR_aVIO`ic]_^X\afg_QA78'#(%.h¡ª³°¤‡G=\‡£«®°³¶ª›’[xRX220/,"%5=;7.H30==??:41367D3.'7]ax…mQkX~@4!"49</24*$**)2@IWepuvvumO+5`y‚ŒŠ‡†‡ŠŽ‘ŽŽŽŒŠŠŠŠŠŠŠŠ‹ŒŽ’“•–™š £¦¨ª²¸»¼¾ÄÊÎÏÐÒÓÕÖ×ÜÓÙzSZb]ZYUQMIED?@>=>HVav‘©³®ž£»ÕØÖŤ}_O;BLRRRTVbed_ZYZZadfd[M>45'"(%,f¢«´²¦ŠI<[‡£«®°³®ª›‹MySQ16/,0+4OA>00F8-=<=>=:8652/;17%,aw†‚Š~n`Dm;.84/%/46('()5DM[isvus{j-!Cg†‚Ž‹ˆ†‡ŠŽ‘ŽŽŽŒŠŠŠŠŠŠŠŠ‹Œ‘“”•™šœŸ£¦¨©¬²¸»»¾ÄÉÎÏÐÑÓÕÖÖÝÚÚ™QTjea_[VQLIG540*(/;FVŒ¿Ê¸›£ÆÐÒλ˜oPC2CRPFDSdqf^^ZSU^dffbXJ<318<>>Gd°¸¼§‡V<_— «±²²³¶´³¥†XS7.32(8017094299;D<9:<-@9"1Q/8'.9Yrªl€eQinR1H(+(&)*'$'2<RWdqrntm9;h{„ˆˆ‰Š‹‹ŒŒ‹ŒŒŽŒŒŒ‹Š‰‰ˆŒŽ‘—˜š ¢¤¥¯²·»¿ÃÄÈÉËÎÑÔÖ×ÐÚ×ÇTaekjihd_XRN<<7-$"*2q±ÆË³–³ÅÛ×̰€Q?BCLSOIM^ngb[TRUZ^ijh]NB<;5STRNSiŒ§¶¼ª]A`€— «±²²³¶³±£~`€S7'/51@3..1851657A?F:A01/ ,.$D;<3..[‚–˜^j`z],/7(/-121.,1=GGjtktztG1.Mq‚ˆˆˆ‰Š‹‹ŒŒ‹ŒŒŽŒ‹ŠŠ‰‰ŒŽ–—šœŸ¢¤¥¬®±¶º¾ÂÃÈÉËÎÑÓÕÖÓרÎkW`jkjifa[UQGE>3($(.„ÂÎŪ™½ÏÒÓ¿ŠV=9:JKJHJTdpb]VQQV]bmhaYPF>89cb^[`rŽ£´¼®•hIc}— «±²²³¶²¯ qk€T6(.62?30.1870110==I4A:,))1/&6F@;/16Wz•˜~JqT:.)-0-%%$$(3BNIcrq|…j?)6Mfz…‰‹ˆˆ‰Š‹‹ŒŒ‹ŒŒŽŽŒ‹‹ŠŠŒŒŽ•—™›ž¡£¤«¬°´¸½ÀÂÇÈÊÍÐÒÔÖÖÑÚØKZgkkkie_ZWSOG;0**-|ÁÒɳªÅÌÍ¿—^6.6;OHADP_joZUOLOWahof[TPI?6?Y[]`i|•§±»±rRe{— «±²²³¶±¬ŸfrS3103,606517:/..*;<?+:F=(74-N&;B6;NDKG\w€Y65.)4,()'%&-;KV^an‚j> .Mkw|‚ƒ}ˆˆ‰Š‹‹ŒŒ‹ŒŒŽŽŒŒ‹‹ŒŒŽ”•—š ¢£¨ª²¶º¾¿ÆÇÉÌÏÑÓÔÙÍÛà´JTdkklkhd`]YUND<766j²ÍÉÀ¾Ç¼½“aC52;GNE@J]jjeQNJJPZfmle[RMF?:CMV_gp™«®¹± xWgx— «±²²³¶²« bszR03.4.508516?0,.(>H;+5OR(9(>]9+J59MZ<Yq<f}W+--.7('0-+-7DPXmr}yT*%9Hdyz{ƒ‚xˆˆ‰Š‹‹ŒŒ‹ŒŒŽŽŒŒ‹ŒŒŽŽ“”–™œŸ¡¢¦¨«¯´¸»½ÅÆÈÊÍÐÒÓÙÌÛáÎ[P_hikkjgcaZVQMKIGFu±ÊÆÅÄ»¥‹fA58>FNJGJYjncVIIJNV`ipfd]RG@?ACIYips}”§¶®ŸzZgw— «±²²³¶³ª¤fksO+,*88;37,05C2-1*EWI/8SZ/. B]D=9O.EIB_—jH;d9-)-3',&%'1@PZ_b]R>'#>^`pyy}‡ˆ€ˆˆ‰Š‹‹ŒŒ‹ŒŒŽ‘‘ŽŽ‹‹ŒŒŽ’“•˜›Ÿ¡¤¥©±¶¹»ÃÅÇÉÌÏÑÒ×ÎÛÞÙ{O[dfhiigec[XUUXYWT´ÏÎȶ—{QL>16FMJIOZgnhYLCGNV^flodc]PC=@E@:RjplrŠ¡¬³©›xZgv— «±²²³¶¶«©nalL&.)99;38,.4G506/MS[.AWWH,+2[A?-`FA;TMu“N7QB0%+/*-))-9HTXWF6()9Pcoty{|€†ˆ†ˆˆ‰Š‹‹ŒŒ‹ŒŒŽ’’‘‘ŽŽ‹‹‹ŒŽŽ‘’”—šœŸ ¢¤§«°´·¹ÃÄÆÈËÎÐÑÓÒÙØÛžPW_adggged`\Z]bc_Zv©ËÌ»–mYCGC>BLLDO\joh[OJAHS_hlmlf`WLC@AC;%Baichƒ¬²¦˜vYgv— «±²²³¶·¬¬tZhJ$6+6220=3.4I61:2SEc'I[Rc29'PJ=Rt645b|˜h-,</"+/,)2.-2893,/1AZgiq~ƒ‚‚‚€‚„ˆˆ‰Š‹‹ŒŒ‹ŒŒŽ“’’‘Š‹‹ŒŽŽ‘’”–™œžŸ¡£¦ª¯³¶¸ÂÃÅÈËÎÐÑÑÔØÓÙµQU\_beffdcea^afgb[n›½·›tXWWHCOXRHCVesq`PKL?IWenomjj_PGEEC@7.<GQ`i}›¤©¦§†`ky•ž©°²³µ¸µ‡=R+6*13/2:<795V0>1-HRS)>WohK//:o5!=kT@?d|{œb?A$.#(40,--+*/9BMS\gqy~€~~€‚‚††‡‡ˆ‰ŠŠ‹ŒŒŽ‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽ‘’”•˜™›œŸ¡¥ª¯µ¸º»½ÀÃÇËÎÐÐÛÏØÕÕnMV\dhghknadhlnonnt~…|hUNNDOYUJELUekl`MBEL;Nbmv|uiiWD<?C?:-/;DNZbx˜§¬©¨ˆahu•ž©°²³µ¸¨¥ŽEG'8+2302;<796N@032@\B&2akk^(,L^W8TrA=`ipZ|X1$/:-#(*.48;BLT]ahpv{}~~~€‚‚†††‡ˆ‰‰Š‹‹ŒŽŽ‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽ‘’”•˜™›œŸ¡¥ª¯´·¹º¼¿ÃÇËÎÏÏ×ÐÙÕÔƒISZbfghlpfiloqpontsqlcZRMTWWPIJS]gd^VNHEECTfow{shcTC<?@<581:@HQWp–¯´±®ges•ž©°²³µ¸µ²¬„JI5=+2403;=899BP)23<f0**h^h}.)<sr.>T]5NnqZZ[),(;, 4)2?JQZcjnquy|||{~€‚‚ƒ…††‡ˆ‰‰‰‹‹ŒŽ‘‘‘‘‘‘‘‘‘’”•˜™›œ ¡¥©®²µ·¹»¾ÂÆÉÌÎÎÑÒÛÕÓ¥GNU_eginrnprttsqoqmgb_^^^g`VNLS]eeZNKMMG?O_mrvxocYNA<>=71548;AGKi–»¿¾¹žtgv•ž©°²³µ¸·¯š\9A70,3514<>9:@8R6++Hn*.-fSZG*/pvD&FrH&SfyP;05'.*+@.:KYbipuuwz}~~|{€€‚ƒƒ……†‡‡ˆ‰‰‹‹‹ŒŽŽ‘‘‘‘‘‘‘‘‘’”•˜™›œ ¢¤¨¬°³´¸¹¼ÀÄÈËÌÍÍÓÚÔÒÅOJR\cfiosuvxyxvsqoole`doyqfYSV]bd^RFDJNJD^ktutsi[MF?<=;602665;>BfœÆÊËů„l{•ž©°²³µ¸»¬Y?832-4624=>9=F4EN'#_s.*3eWH„t24>kI>JXs43Y`d5B+-,,088EVcimpsrtx|~~}|€€‚ƒƒƒ„……†‡ˆˆ‰ŠŠ‹ŒŽŽ‘‘‘‘‘‘‘‘’’’’’’’’‘’”•˜™›œ¡¢¤§«®°±¶¸»¾ÃÆÉËÌËÓØÒÒÚfHPZadgmrxy{{zxuspnkggmx€nf]\ac`ZUOHEHMPRlvzvpk_QC?;:;;97B74169=h¥ÉÎÑ˺n•ž©°²³µ¸½¬–uW5-?.5635=?:AI98X-&tj4$8hb>f›E!/WQA;7MdT/R\a^\"$(-3;EO^gkmoqprvz}~~}€€‚ƒƒ„„„…†‡‡ˆˆŠŠ‹‹ŒŽŽ‘‘‘‘‘‘‘‘““““““““‘’”•˜™›œ¡¢¤¦©¬®¯µ¶¹½ÁÅÈÉËÌÒÔÐÑÞ‰IQZ`beinuwy{{zxvskehqxxtgc`adbYQQPMJIOZcw~~tjbTF<977:>BDR82.47;k¯ÅÊÐʾ•l•ž©°²³µ¸¯ªlO3'1.5736>@;EHC1M=8L7.=ka;Q‰g$4Ql5E9SCE+?ccenH&&2;JOXcjmoruuwz}~}|€€‚‚ƒ„„„„……†‡ˆˆ‰ŠŠ‹ŒŽ‘‘‘‘‘‘‘‘””””””””‘’”•˜™›œ¡¢¤¦¨ª¬³µ¸¼ÀÄÇÈÊÏÑÏÏÑÛ¬LS[_`aeiqswz{{zyvqnt~€xna`_`_[TNSRPMOWeo~ƒ€reZK<76449ALSY81-36;n¶¿ÅÍÆ¾•i|•ž©°²³µ¸®·ŠW@D3$/6746>@;GFL1<KI‚.8>DiV<MU…X_…D'?7^D<01`8q†=0<AOT\florw{{}}{€€‚ƒƒ„„„„……†‡‡ˆ‰ŠŠ‹Œ‘‘‘‘‘‘‘‘””””””””‘’”•˜™›œ¡¢¤¥¨©«¬³´·»¿ÃÆÇÊÒÑÌÎÑÕÂMT\___bfmptx{{{zx{†‡ƒzt`_^[XTQNWTPOT_lu…€paVF654228DR]a,707/2Y¨ÂÉÈÀ¾•i‘œ©²´³³´µ²ƒY?@.%/D#?0FG>F@MA5^i,F1;k[$SAZ‚>P€30.2GMB,.\G9\dGGRT\^bglquwzz{|}~€~~‚ƒ„„ƒ†ˆ‰ˆ†…‡‡ˆŠ‹Œ‘‘‘‘‘‘‘‘’•——–”“––˜™›œžž£§§¥¦ª®¯³·½ÀÂÂÂÈÊÎÑÒÒÑÐ|k][\\`hkqz‚…„{|~ƒ‰‹„wl][UMHJRZZYWTVaq}~xiUC84*-3=IU`fj \ No newline at end of file diff --git a/tutorial/image/lena.png b/tutorial/image/lena.png new file mode 100644 index 0000000000000000000000000000000000000000..ec368fd292ac2acb2a980cac57bb9ef7112b2300 Binary files /dev/null and b/tutorial/image/lena.png differ diff --git a/tutorial/image/tutorial-image-converter.cpp b/tutorial/image/tutorial-image-converter.cpp index 46ac957cc259771cd2013b9b7d70c3323d6c9624..76f55aa9ff4449e271d29351c4aeb5dc9138f255 100644 --- a/tutorial/image/tutorial-image-converter.cpp +++ b/tutorial/image/tutorial-image-converter.cpp @@ -5,14 +5,23 @@ int main() { #if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) - cv::Mat A; - A = cv::imread("lena.bmp", CV_LOAD_IMAGE_GRAYSCALE); + try { + cv::Mat A; +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) + A = cv::imread("lena.bmp", cv::IMREAD_GRAYSCALE); +#else + A = cv::imread("lena.bmp", CV_LOAD_IMAGE_GRAYSCALE); +#endif - vpImage<unsigned char> I; - vpImageConvert::convert(A, I); + vpImage<unsigned char> I; + vpImageConvert::convert(A, I); # ifdef VISP_HAVE_LIBPNG - vpImageIo::write(I, "lena.png"); // Gray + vpImageIo::write(I, "lena.png"); // Gray # endif + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } #endif } diff --git a/tutorial/image/tutorial-image-filter.cpp b/tutorial/image/tutorial-image-filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c224206f89829114ce4310776582cac8e5d84c27 --- /dev/null +++ b/tutorial/image/tutorial-image-filter.cpp @@ -0,0 +1,121 @@ +//! \example tutorial-image-filter.cpp + +#include <visp/vpDisplayD3D.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayGTK.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpImageIo.h> +#include <visp/vpImageFilter.h> + +void display(vpImage<unsigned char> &I, const std::string &title); +void display(vpImage<double> &D, const std::string &title); + +void display(vpImage<unsigned char> &I, const std::string &title) +{ +#if defined(VISP_HAVE_X11) + vpDisplayX d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); +#elif defined(VISP_HAVE_GTK) + vpDisplayGTK d(I); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_D3D9) + vpDisplayD3d d(I); +#else + std::cout << "No image viewer is available..." << std::endl; +#endif + + vpDisplay::setTitle(I, title.c_str()); + vpDisplay::display(I); + vpDisplay::displayText(I, 15,15, "Click to continue...", vpColor::red); + vpDisplay::flush(I); + vpDisplay::getClick(I); +} + +void display(vpImage<double> &D, const std::string &title) +{ + vpImage<unsigned char> I; // Image to display + vpImageConvert::convert(D, I); + display(I, title); +} + +int main(int argc, char** argv ) +{ + try { + if(argc != 2) { + printf( "Usage: %s <image name.[pgm,ppm,jpeg,png,bmp]>\n", argv[0] ); + return -1; + } + //! [vpImage construction] + vpImage<unsigned char> I; + //! [vpImage construction] + + try { + vpImageIo::read(I, argv[1]); + } + catch(...) { + std::cout << "Cannot read image \"" << argv[1] << "\"" << std::endl; + return -1; + } + + display(I, "Original image"); + + //! [Gaussian blur] + vpImage<double> F; + vpImageFilter::gaussianBlur(I, F); + //! [Gaussian blur] + display(F, "Blur (default)"); + + vpImageFilter::gaussianBlur(I, F, 7, 2); + display(F, "Blur (var=2)"); + + //! [Gradients x] + vpImage<double> dIx; + vpImageFilter::getGradX(I, dIx); + //! [Gradients x] + display(dIx, "Gradient dIx"); + + //! [Gradients y] + vpImage<double> dIy; + vpImageFilter::getGradY(I, dIy); + //! [Gradients y] + display(dIy, "Gradient dIy"); + + //! [Canny] +#if (VISP_HAVE_OPENCV_VERSION >= 0x020100) + vpImage<unsigned char> C; + vpImageFilter::canny(I, C, 5, 15, 3); + display(C, "Canny"); +#endif + //! [Canny] + + //! [Convolution kernel] + vpMatrix K(3,3); // Sobel kernel along x + K[0][0] = 1; K[0][1] = 0; K[0][2] = -1; + K[1][0] = 2; K[1][1] = 0; K[1][2] = -2; + K[2][0] = 1; K[2][1] = 0; K[2][2] = -1; + //! [Convolution kernel] + //! [Convolution] + vpImage<double> Gx; + vpImageFilter::filter(I, Gx, K); + //! [Convolution] + display(Gx, "Sobel x"); + + //! [Gaussian pyramid] + size_t nlevel = 3; + std::vector< vpImage<unsigned char> > pyr(nlevel); + pyr[0] = I; + for (size_t i=1; i < nlevel; i++) { + vpImageFilter::getGaussPyramidal(pyr[i-1], pyr[i]); + display(pyr[i], "Pyramid"); + } + //! [Gaussian pyramid] + return 0; + } + catch(vpException &e) { + std::cout << "Catch an exception: " << e << std::endl; + return 1; + } +} diff --git a/tutorial/image/tutorial-image-manipulation.cpp b/tutorial/image/tutorial-image-manipulation.cpp index 9847ff05698c1426f3f66764d5839e96f08fd125..fe230a7252968d7a9eb79e8f14a986e639b7365d 100644 --- a/tutorial/image/tutorial-image-manipulation.cpp +++ b/tutorial/image/tutorial-image-manipulation.cpp @@ -3,23 +3,28 @@ int main() { - vpImage<unsigned char> gray_image(240, 320); - vpImage<vpRGBa> color_image(240, 320); + try { + vpImage<unsigned char> gray_image(240, 320); + vpImage<vpRGBa> color_image(240, 320); - gray_image = 128; - vpRGBa color(255, 0, 0); - color_image = color; + gray_image = 128; + vpRGBa color(255, 0, 0); + color_image = color; - unsigned int igray_max = gray_image.getHeight() - 1; - unsigned int jgray_max = gray_image.getWidth() - 1; - std::cout << "Gray image, last pixel intensity: " - << (int)gray_image[igray_max][jgray_max] << std::endl; + unsigned int igray_max = gray_image.getHeight() - 1; + unsigned int jgray_max = gray_image.getWidth() - 1; + std::cout << "Gray image, last pixel intensity: " + << (int)gray_image[igray_max][jgray_max] << std::endl; - unsigned int icolor_max = color_image.getHeight() - 1; - unsigned int jcolor_max = color_image.getWidth() - 1; - std::cout << "Color image, last pixel RGB components: " - << (int)color_image[icolor_max][jcolor_max].R << " " - << (int)color_image[icolor_max][jcolor_max].G << " " - << (int)color_image[icolor_max][jcolor_max].B - << std::endl; + unsigned int icolor_max = color_image.getHeight() - 1; + unsigned int jcolor_max = color_image.getWidth() - 1; + std::cout << "Color image, last pixel RGB components: " + << (int)color_image[icolor_max][jcolor_max].R << " " + << (int)color_image[icolor_max][jcolor_max].G << " " + << (int)color_image[icolor_max][jcolor_max].B + << std::endl; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } } diff --git a/tutorial/image/tutorial-image-reader.cpp b/tutorial/image/tutorial-image-reader.cpp index ab0caf23b2e7e67ed196ef54bb16ab907ef234e2..109b806601a32101fac90451b44573be6588301a 100644 --- a/tutorial/image/tutorial-image-reader.cpp +++ b/tutorial/image/tutorial-image-reader.cpp @@ -5,9 +5,12 @@ int main() { try { vpImage<vpRGBa> I; - vpImageIo::read(I, "lena.jpg"); + vpImageIo::read(I, "lena.jpeg"); vpImageIo::write(I, "lena.png"); } + catch(vpException e) { + std::cout << e.getMessage() << std::endl; + } catch(...) { std::cout << "Unsupported image format" << std::endl; } diff --git a/tutorial/image/tutorial-image-viewer.cpp b/tutorial/image/tutorial-image-viewer.cpp index 9bade45c49338ecc37de00f4b6a427cbb54d43e9..a3a6dfa4d23f4fb349bfbb44e525767efd49a0e4 100644 --- a/tutorial/image/tutorial-image-viewer.cpp +++ b/tutorial/image/tutorial-image-viewer.cpp @@ -1,37 +1,45 @@ /*! \example tutorial-image-viewer.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpImageIo.h> #include <visp/vpImagePoint.h> int main() { - vpImage<vpRGBa> I; - vpImageIo::read(I, "lena.ppm"); + try { + vpImage<vpRGBa> I; + vpImageIo::read(I, "lena.ppm"); #if defined(VISP_HAVE_X11) - vpDisplayX d(I); + vpDisplayX d(I); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I); + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::setTitle(I, "Lena"); - vpDisplay::display(I); + vpDisplay::setTitle(I, "Lena"); + vpDisplay::display(I); - vpDisplay::displayRectangle(I, vpImagePoint(90,90), 70, 90, vpColor::red, false, 2); - vpDisplay::flush(I); + vpDisplay::displayRectangle(I, vpImagePoint(90,90), 70, 90, vpColor::red, false, 2); + vpDisplay::flush(I); - vpImage<vpRGBa> O; - vpDisplay::getImage(I, O); + vpImage<vpRGBa> O; + vpDisplay::getImage(I, O); - try { - vpImageIo::write(I, "lena-out.jpg"); - vpImageIo::write(O, "lena-out-with-overlay.jpg"); + try { + vpImageIo::write(I, "lena-out.jpg"); + vpImageIo::write(O, "lena-out-with-overlay.jpg"); + } + catch(...) { + std::cout << "Cannot write the image: unsupported format..." << std::endl; + } + + vpDisplay::getClick(I); } - catch(...) { - std::cout << "Cannot write the image: unsupported format..." << std::endl; + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - - vpDisplay::getClick(I); } diff --git a/tutorial/image/tutorial-undistort.cpp b/tutorial/image/tutorial-undistort.cpp index 410a30d78a97f3a6f8b0c8ac27d5fe1e7cb0b8fa..b26460f3cc00b54e093eeb42609bf9450a2b1da8 100644 --- a/tutorial/image/tutorial-undistort.cpp +++ b/tutorial/image/tutorial-undistort.cpp @@ -1,30 +1,43 @@ -/*! \example tutorial-undistort.cpp */ +//! \example tutorial-undistort.cpp #include <visp/vpImageIo.h> #include <visp/vpImageTools.h> #include <visp/vpXmlParserCamera.h> int main() { - vpImage<unsigned char> I; - vpImageIo::read(I, "chessboard.pgm"); + try { + //! [Load image] + vpImage<unsigned char> I; + vpImageIo::read(I, "chessboard.pgm"); + //! [Load image] - vpCameraParameters cam; + //! [Load camera parameters from xml] + vpCameraParameters cam; #ifdef VISP_HAVE_XML2 - vpXmlParserCamera p; - vpCameraParameters::vpCameraParametersProjType projModel; - projModel = vpCameraParameters::perspectiveProjWithDistortion; - if (p.parse(cam, "camera.xml", "Camera", projModel, I.getWidth(), I.getHeight()) != vpXmlParserCamera::SEQUENCE_OK) { - std::cout << "Cannot found parameters for camera named \"Camera\"" << std::endl; - } + vpXmlParserCamera p; + vpCameraParameters::vpCameraParametersProjType projModel; + projModel = vpCameraParameters::perspectiveProjWithDistortion; + if (p.parse(cam, "camera.xml", "Camera", projModel, I.getWidth(), I.getHeight()) != vpXmlParserCamera::SEQUENCE_OK) { + std::cout << "Cannot found parameters for camera named \"Camera\"" << std::endl; + } + //! [Load camera parameters from xml] + //! [Set camera parameters] #else - cam.initPersProjWithDistortion(582.7, 580.6, 326.6, 215.0, -0.3372, 0.4021); + cam.initPersProjWithDistortion(582.7, 580.6, 326.6, 215.0, -0.3372, 0.4021); #endif + //! [Set camera parameters] - std::cout << cam << std::endl; + std::cout << cam << std::endl; - vpImage<unsigned char> Iud; - vpImageTools::undistort(I, cam, Iud); - vpImageIo::write(Iud, "chessboard-undistort.pgm"); + //! [Create image without distorsion] + vpImage<unsigned char> Iud; + vpImageTools::undistort(I, cam, Iud); + vpImageIo::write(Iud, "chessboard-undistort.pgm"); + //! [Create image without distorsion] + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } return 0; } diff --git a/tutorial/image/tutorial-viewer.cpp b/tutorial/image/tutorial-viewer.cpp index ae58d7b898b871eb0c6ef54685f94d6a47d6737b..cfd843940e6bbb7c51943a15e017d663f280bf69 100644 --- a/tutorial/image/tutorial-viewer.cpp +++ b/tutorial/image/tutorial-viewer.cpp @@ -1,20 +1,27 @@ -/*! \example tutorial-viewer.cpp */ +//! \example tutorial-viewer.cpp +//! [Include display] #include <visp/vpDisplayD3D.h> #include <visp/vpDisplayGDI.h> #include <visp/vpDisplayGTK.h> #include <visp/vpDisplayX.h> #include <visp/vpDisplayOpenCV.h> +//! [Include display] +//! [Include io] #include <visp/vpImageIo.h> +//! [Include io] -int main(int argc, char** argv ) +int main(int argc, char** argv) { if(argc != 2) { printf( "Usage: %s <image name.[pgm,ppm,jpeg,png,tiff,bmp,ras,jp2]>\n", argv[0] ); return -1; } + //! [vpImage construction] vpImage<vpRGBa> I; + //! [vpImage construction] + //! [vpImage reading] try { vpImageIo::read(I, argv[1]); } @@ -22,23 +29,37 @@ int main(int argc, char** argv ) std::cout << "Cannot read image \"" << argv[1] << "\"" << std::endl; return -1; } + //! [vpImage reading] + try { + //! [vpDisplay construction] #if defined(VISP_HAVE_X11) - vpDisplayX d(I); + vpDisplayX d(I); #elif defined(VISP_HAVE_OPENCV) - vpDisplayOpenCV d(I); + vpDisplayOpenCV d(I); #elif defined(VISP_HAVE_GTK) - vpDisplayGTK d(I); + vpDisplayGTK d(I); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I); + vpDisplayGDI d(I); #elif defined(VISP_HAVE_D3D9) - vpDisplayD3d d(I); + vpDisplayD3d d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::setTitle(I, "My image"); - vpDisplay::display(I); - vpDisplay::flush(I); - std::cout << "A click to quit..." << std::endl; - vpDisplay::getClick(I); + //! [vpDisplay construction] + //! [vpDisplay set title] + vpDisplay::setTitle(I, "My image"); + //! [vpDisplay set title] + //! [vpDisplay display] + vpDisplay::display(I); + vpDisplay::flush(I); + //! [vpDisplay display] + std::cout << "A click to quit..." << std::endl; + //! [vpDisplay get click] + vpDisplay::getClick(I); + //! [vpDisplay get click] + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } } diff --git a/tutorial/robot/pioneer/CMakeLists.txt b/tutorial/robot/pioneer/CMakeLists.txt index 342717b4e918597d75f1ebb413998d4c8fb11362..1de807f35eda1b423be5b5ba5ec75950b87f46ed 100644 --- a/tutorial/robot/pioneer/CMakeLists.txt +++ b/tutorial/robot/pioneer/CMakeLists.txt @@ -3,11 +3,17 @@ project(tutorial-robot) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) - -# build the examples -add_executable(tutorial-pioneer-robot tutorial-pioneer-robot.cpp) -add_executable(tutorial-simu-pioneer tutorial-simu-pioneer.cpp) -add_executable(tutorial-simu-pioneer-pan tutorial-simu-pioneer-pan.cpp) + +set(tutorial_cpp + tutorial-pioneer-robot.cpp + tutorial-simu-pioneer.cpp + tutorial-simu-pioneer-continuous-gain-constant.cpp + tutorial-simu-pioneer-continuous-gain-adaptive.cpp + tutorial-simu-pioneer-pan.cpp) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() diff --git a/tutorial/robot/pioneer/tutorial-pioneer-robot.cpp b/tutorial/robot/pioneer/tutorial-pioneer-robot.cpp index 91c3ec57c7eaea343e2578b35ceee6d663677a3d..40c100848d903bb84952321036d9e1722bedaa46 100644 --- a/tutorial/robot/pioneer/tutorial-pioneer-robot.cpp +++ b/tutorial/robot/pioneer/tutorial-pioneer-robot.cpp @@ -5,35 +5,40 @@ int main() { #ifdef VISP_HAVE_PIONEER - ArArgumentBuilder args; - args.add("-rp"); -#ifdef UNIX - args.add("/dev/ttyUSB0"); + try { + ArArgumentBuilder args; + args.add("-rp"); +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX + args.add("/dev/ttyUSB0"); #else - args.add("COM3"); + args.add("COM3"); #endif - ArSimpleConnector conn(&args); + ArSimpleConnector conn(&args); - vpRobotPioneer robot; + vpRobotPioneer robot; - if (!conn.connectRobot(&robot)) - return -1; + if (!conn.connectRobot(&robot)) + return -1; - robot.useSonar(false); - vpTime::wait(2000); + robot.useSonar(false); + vpTime::wait(2000); - vpColVector v(2); - v = 0; - v[0] = 0.10; // Translational velocity in m/s - robot.setVelocity(vpRobot::REFERENCE_FRAME, v); + vpColVector v(2); + v = 0; + v[0] = 0.10; // Translational velocity in m/s + robot.setVelocity(vpRobot::REFERENCE_FRAME, v); - vpTime::wait(1000); - vpColVector v_mes = robot.getVelocity(vpRobot::REFERENCE_FRAME); - std::cout << "Measured vel: " << v_mes.t() << std::endl; - vpTime::wait(1000); + vpTime::wait(1000); + vpColVector v_mes = robot.getVelocity(vpRobot::REFERENCE_FRAME); + std::cout << "Measured vel: " << v_mes.t() << std::endl; + vpTime::wait(1000); - robot.stopRunning(); - robot.waitForRunExit(); + robot.stopRunning(); + robot.waitForRunExit(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } #endif } diff --git a/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-adaptive.cpp b/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-adaptive.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19a028e8f8207d0e27c0f9e93842589ac5101815 --- /dev/null +++ b/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-adaptive.cpp @@ -0,0 +1,148 @@ +/*! + \example tutorial-simu-pioneer-continuous-gain-adaptive.cpp + + Example that shows how to simulate a visual servoing on a Pioneer mobile robot equipped with a camera. + The current visual features that are used are s = (x, log(Z/Z*)). The desired one are s* = (x*, 0), with: + - x the abscisse of the point measured at each iteration + - x* the desired abscisse position of the point (x* = 0) + - Z the depth of the point measured at each iteration + - Z* the desired depth of the point equal to the initial one. + + The degrees of freedom that are controlled are (vx, wz), where wz is the rotational velocity + and vx the translational velocity of the mobile platform at point M located at the middle + between the two wheels. + + The feature x allows to control wy, while log(Z/Z*) allows to control vz. + + */ +#include <iostream> + +#include <visp/vpFeatureBuilder.h> +#include <visp/vpFeatureDepth.h> +#include <visp/vpFeaturePoint.h> +#include <visp/vpHomogeneousMatrix.h> +#include <visp/vpPlot.h> +#include <visp/vpServo.h> +#include <visp/vpSimulatorPioneer.h> +#include <visp/vpVelocityTwistMatrix.h> + +int main() +{ + try { + vpHomogeneousMatrix cdMo; + cdMo[1][3] = 1.2; + cdMo[2][3] = 0.5; + + vpHomogeneousMatrix cMo; + cMo[0][3] = 0.3; + cMo[1][3] = cdMo[1][3]; + cMo[2][3] = 1.; + vpRotationMatrix cRo(0, atan2( cMo[0][3], cMo[1][3]), 0); + cMo.insert(cRo); + + vpSimulatorPioneer robot ; + robot.setSamplingTime(0.04); + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc); + wMo = wMc * cMo; + + vpPoint point; + point.setWorldCoordinates(0,0,0); + point.track(cMo); + + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); + task.setLambda(2, 0.2, 10); + vpVelocityTwistMatrix cVe; + cVe = robot.get_cVe(); + task.set_cVe(cVe); + + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); + + vpFeaturePoint s_x, s_xd; + vpFeatureBuilder::create(s_x, point); + s_xd.buildFrom(0, 0, cdMo[2][3]); + task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); + + vpFeatureDepth s_Z, s_Zd; + double Z = point.get_Z(); + double Zd = cdMo[2][3]; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); + s_Zd.buildFrom(0, 0, Zd, 0); + task.addFeature(s_Z, s_Zd); + +#ifdef VISP_HAVE_DISPLAY + // Create a window (800 by 500) at position (400, 10) with 3 graphics + vpPlot graph(3, 800, 500, 400, 10, "Curves..."); + + // Init the curve plotter + graph.initGraph(0,2); + graph.initGraph(1,2); + graph.initGraph(2,1); + graph.setTitle(0, "Velocities"); + graph.setTitle(1, "Error s-s*"); + graph.setTitle(2, "Depth"); + graph.setLegend(0, 0, "vx"); + graph.setLegend(0, 1, "wz"); + graph.setLegend(1, 0, "x"); + graph.setLegend(1, 1, "log(Z/Z*)"); + graph.setLegend(2, 0, "Z"); +#endif + + int iter = 0; + for (; ;) + { + robot.getPosition(wMc) ; + cMo = wMc.inverse() * wMo; + + point.track(cMo); + + vpFeatureBuilder::create(s_x, point); + + Z = point.get_Z() ; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)) ; + + robot.get_cVe(cVe); + task.set_cVe(cVe); + robot.get_eJe(eJe); + task.set_eJe(eJe); + + vpColVector v = task.computeControlLaw(iter*robot.getSamplingTime()); + robot.setVelocity(vpRobot::ARTICULAR_FRAME, v); + +#ifdef VISP_HAVE_DISPLAY + graph.plot(0, iter, v); // plot velocities applied to the robot + graph.plot(1, iter, task.getError()); // plot error vector + graph.plot(2, 0, iter, Z); // plot the depth +#endif + + iter ++; + + if (task.getError().sumSquare() < 0.0001) { + std::cout << "Reached a small error. We stop the loop... " << std::endl; + break; + } + } +#ifdef VISP_HAVE_DISPLAY + graph.saveData(0, "./v2.dat"); + graph.saveData(1, "./error2.dat"); + + const char *legend = "Click to quit..."; + vpDisplay::displayText(graph.I, (int)graph.I.getHeight()-60, (int)graph.I.getWidth()-150, legend, vpColor::red); + vpDisplay::flush(graph.I); + vpDisplay::getClick(graph.I); +#endif + + + // Kill the servo task + task.print() ; + task.kill(); + + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +} diff --git a/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-constant.cpp b/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2d7371488dd14ebc714327d1aed58a0a489fba3 --- /dev/null +++ b/tutorial/robot/pioneer/tutorial-simu-pioneer-continuous-gain-constant.cpp @@ -0,0 +1,148 @@ +/*! + \example tutorial-simu-pioneer-continuous-gain-constant.cpp + + Example that shows how to simulate a visual servoing on a Pioneer mobile robot equipped with a camera. + The current visual features that are used are s = (x, log(Z/Z*)). The desired one are s* = (x*, 0), with: + - x the abscisse of the point measured at each iteration + - x* the desired abscisse position of the point (x* = 0) + - Z the depth of the point measured at each iteration + - Z* the desired depth of the point equal to the initial one. + + The degrees of freedom that are controlled are (vx, wz), where wz is the rotational velocity + and vx the translational velocity of the mobile platform at point M located at the middle + between the two wheels. + + The feature x allows to control wy, while log(Z/Z*) allows to control vz. + + */ +#include <iostream> + +#include <visp/vpFeatureBuilder.h> +#include <visp/vpFeatureDepth.h> +#include <visp/vpFeaturePoint.h> +#include <visp/vpHomogeneousMatrix.h> +#include <visp/vpPlot.h> +#include <visp/vpServo.h> +#include <visp/vpSimulatorPioneer.h> +#include <visp/vpVelocityTwistMatrix.h> + +int main() +{ + try { + vpHomogeneousMatrix cdMo; + cdMo[1][3] = 1.2; + cdMo[2][3] = 0.5; + + vpHomogeneousMatrix cMo; + cMo[0][3] = 0.3; + cMo[1][3] = cdMo[1][3]; + cMo[2][3] = 1.; + vpRotationMatrix cRo(0, atan2( cMo[0][3], cMo[1][3]), 0); + cMo.insert(cRo); + + vpSimulatorPioneer robot ; + robot.setSamplingTime(0.04); + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc); + wMo = wMc * cMo; + + vpPoint point; + point.setWorldCoordinates(0,0,0); + point.track(cMo); + + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); + task.setLambda(0.2); + vpVelocityTwistMatrix cVe; + cVe = robot.get_cVe(); + task.set_cVe(cVe); + + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); + + vpFeaturePoint s_x, s_xd; + vpFeatureBuilder::create(s_x, point); + s_xd.buildFrom(0, 0, cdMo[2][3]); + task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); + + vpFeatureDepth s_Z, s_Zd; + double Z = point.get_Z(); + double Zd = cdMo[2][3]; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); + s_Zd.buildFrom(0, 0, Zd, 0); + task.addFeature(s_Z, s_Zd); + +#ifdef VISP_HAVE_DISPLAY + // Create a window (800 by 500) at position (400, 10) with 3 graphics + vpPlot graph(3, 800, 500, 400, 10, "Curves..."); + + // Init the curve plotter + graph.initGraph(0,2); + graph.initGraph(1,2); + graph.initGraph(2,1); + graph.setTitle(0, "Velocities"); + graph.setTitle(1, "Error s-s*"); + graph.setTitle(2, "Depth"); + graph.setLegend(0, 0, "vx"); + graph.setLegend(0, 1, "wz"); + graph.setLegend(1, 0, "x"); + graph.setLegend(1, 1, "log(Z/Z*)"); + graph.setLegend(2, 0, "Z"); +#endif + + int iter = 0; + for (; ;) + { + robot.getPosition(wMc) ; + cMo = wMc.inverse() * wMo; + + point.track(cMo); + + vpFeatureBuilder::create(s_x, point); + + Z = point.get_Z() ; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)) ; + + robot.get_cVe(cVe); + task.set_cVe(cVe); + robot.get_eJe(eJe); + task.set_eJe(eJe); + + vpColVector v = task.computeControlLaw(iter*robot.getSamplingTime()); + robot.setVelocity(vpRobot::ARTICULAR_FRAME, v); + +#ifdef VISP_HAVE_DISPLAY + graph.plot(0, iter, v); // plot velocities applied to the robot + graph.plot(1, iter, task.getError()); // plot error vector + graph.plot(2, 0, iter, Z); // plot the depth +#endif + + iter ++; + + if (task.getError().sumSquare() < 0.0001) { + std::cout << "Reached a small error. We stop the loop... " << std::endl; + break; + } + } +#ifdef VISP_HAVE_DISPLAY + graph.saveData(0, "./v2.dat"); + graph.saveData(1, "./error2.dat"); + + const char *legend = "Click to quit..."; + vpDisplay::displayText(graph.I, (int)graph.I.getHeight()-60, (int)graph.I.getWidth()-150, legend, vpColor::red); + vpDisplay::flush(graph.I); + vpDisplay::getClick(graph.I); +#endif + + + // Kill the servo task + task.print() ; + task.kill(); + + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +} diff --git a/tutorial/robot/pioneer/tutorial-simu-pioneer-pan.cpp b/tutorial/robot/pioneer/tutorial-simu-pioneer-pan.cpp index e18d02aa01c9d0817109a40b1bbee559c758880d..a3c587bd53080f5a051633f15aca25873b4603d2 100644 --- a/tutorial/robot/pioneer/tutorial-simu-pioneer-pan.cpp +++ b/tutorial/robot/pioneer/tutorial-simu-pioneer-pan.cpp @@ -29,93 +29,92 @@ int main() { - // Set the position the camera has to reach - vpHomogeneousMatrix cdMo ; - cdMo[1][3] = 1.2; // t_y should be different from zero to be non singular - cdMo[2][3] = 0.5; - - // Set the initial camera position - vpHomogeneousMatrix cMo; - cMo[0][3] = 0.3; - cMo[1][3] = cdMo[1][3]; - cMo[2][3] = 1.; - vpRotationMatrix cdRo(0, atan2(cMo[0][3], cMo[1][3]), 0); - cMo.insert(cdRo); - - vpSimulatorPioneerPan robot ; - robot.setSamplingTime(0.04); - vpHomogeneousMatrix wMc, wMo; - - // Get robot position world frame - robot.getPosition(wMc); - - // Compute the position of the object in the world frame - wMo = wMc * cMo; - - // Define the target - vpPoint point; - point.setWorldCoordinates(0,0,0); // Coordinates in the object frame - point.track(cMo); - - vpServo task; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe); - task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE); - task.setLambda(0.2); - - vpVelocityTwistMatrix cVe; - cVe = robot.get_cVe(); - task.set_cVe(cVe); - - vpMatrix eJe; - robot.get_eJe(eJe); - task.set_eJe(eJe); - - // Current and desired visual feature associated later to the x coordinate of the point - vpFeaturePoint s_x, s_xd; - - // Create the current x visual feature - vpFeatureBuilder::create(s_x, point); - - // Create the desired x* visual feature - s_xd.buildFrom(0, 0, cdMo[2][3]); - - // Add the feature - task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); - - // Create the current and desired log(Z/Z*) visual feature - vpFeatureDepth s_Z, s_Zd; - // Initial depth of the target in front of the camera - double Z = point.get_Z(); - // Desired depth Z* of the target. - double Zd = cdMo[2][3]; - s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); - s_Zd.buildFrom(0, 0, Zd, 0); // log(Z/Z*) = 0 that's why the last parameter is 0 - - // Add the feature - task.addFeature(s_Z, s_Zd); + try { + // Set the position the camera has to reach + vpHomogeneousMatrix cdMo ; + cdMo[1][3] = 1.2; // t_y should be different from zero to be non singular + cdMo[2][3] = 0.5; + + // Set the initial camera position + vpHomogeneousMatrix cMo; + cMo[0][3] = 0.3; + cMo[1][3] = cdMo[1][3]; + cMo[2][3] = 1.; + vpRotationMatrix cdRo(0, atan2(cMo[0][3], cMo[1][3]), 0); + cMo.insert(cdRo); + + vpSimulatorPioneerPan robot ; + robot.setSamplingTime(0.04); + vpHomogeneousMatrix wMc, wMo; + + // Get robot position world frame + robot.getPosition(wMc); + + // Compute the position of the object in the world frame + wMo = wMc * cMo; + + // Define the target + vpPoint point; + point.setWorldCoordinates(0,0,0); // Coordinates in the object frame + point.track(cMo); + + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::CURRENT, vpServo::PSEUDO_INVERSE); + task.setLambda(0.2); + + vpVelocityTwistMatrix cVe; + cVe = robot.get_cVe(); + task.set_cVe(cVe); + + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); + + // Current and desired visual feature associated later to the x coordinate of the point + vpFeaturePoint s_x, s_xd; + + // Create the current x visual feature + vpFeatureBuilder::create(s_x, point); + + // Create the desired x* visual feature + s_xd.buildFrom(0, 0, cdMo[2][3]); + + // Add the feature + task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); + + // Create the current and desired log(Z/Z*) visual feature + vpFeatureDepth s_Z, s_Zd; + // Initial depth of the target in front of the camera + double Z = point.get_Z(); + // Desired depth Z* of the target. + double Zd = cdMo[2][3]; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); + s_Zd.buildFrom(0, 0, Zd, 0); // log(Z/Z*) = 0 that's why the last parameter is 0 + + // Add the feature + task.addFeature(s_Z, s_Zd); #ifdef VISP_HAVE_DISPLAY - // Create a window (800 by 500) at position (400, 10) with 3 graphics - vpPlot graph(3, 800, 500, 400, 10, "Curves..."); - - // Init the curve plotter - graph.initGraph(0,3); - graph.initGraph(1,2); - graph.initGraph(2,1); - graph.setTitle(0, "Velocities"); - graph.setTitle(1, "Error s-s*"); - graph.setTitle(2, "Depth"); - graph.setLegend(0, 0, "vx"); - graph.setLegend(0, 1, "wz"); - graph.setLegend(0, 2, "qdot_pan"); - graph.setLegend(1, 0, "x"); - graph.setLegend(1, 1, "log(Z/Z*)"); - graph.setLegend(2, 0, "Z"); + // Create a window (800 by 500) at position (400, 10) with 3 graphics + vpPlot graph(3, 800, 500, 400, 10, "Curves..."); + + // Init the curve plotter + graph.initGraph(0,3); + graph.initGraph(1,2); + graph.initGraph(2,1); + graph.setTitle(0, "Velocities"); + graph.setTitle(1, "Error s-s*"); + graph.setTitle(2, "Depth"); + graph.setLegend(0, 0, "vx"); + graph.setLegend(0, 1, "wz"); + graph.setLegend(0, 2, "qdot_pan"); + graph.setLegend(1, 0, "x"); + graph.setLegend(1, 1, "log(Z/Z*)"); + graph.setLegend(2, 0, "Z"); #endif - try - { - int iter = 1; + int iter = 0; for (; ;) { robot.getPosition(wMc) ; @@ -155,16 +154,16 @@ int main() } #ifdef VISP_HAVE_DISPLAY const char *legend = "Click to quit..."; - vpDisplay::displayCharString(graph.I, graph.I.getHeight()-60, graph.I.getWidth()-150, legend, vpColor::red); + vpDisplay::displayText(graph.I, (int)graph.I.getHeight()-60, (int)graph.I.getWidth()-150, legend, vpColor::red); vpDisplay::flush(graph.I); vpDisplay::getClick(graph.I); #endif + + // Kill the servo task + task.print(); + task.kill(); } - catch(...) - { + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - - // Kill the servo task - task.print(); - task.kill(); } diff --git a/tutorial/robot/pioneer/tutorial-simu-pioneer.cpp b/tutorial/robot/pioneer/tutorial-simu-pioneer.cpp index 021e95eba2eca0b5370170f2a3a501bb9e76d5da..6484c9a6dfc9905d2787cc3ea3e29bdd6f8aa442 100644 --- a/tutorial/robot/pioneer/tutorial-simu-pioneer.cpp +++ b/tutorial/robot/pioneer/tutorial-simu-pioneer.cpp @@ -28,72 +28,71 @@ int main() { - vpHomogeneousMatrix cdMo; - cdMo[1][3] = 1.2; - cdMo[2][3] = 0.5; - - vpHomogeneousMatrix cMo; - cMo[0][3] = 0.3; - cMo[1][3] = cdMo[1][3]; - cMo[2][3] = 1.; - vpRotationMatrix cRo(0, atan2( cMo[0][3], cMo[1][3]), 0); - cMo.insert(cRo); - - vpSimulatorPioneer robot ; - robot.setSamplingTime(0.04); - vpHomogeneousMatrix wMc, wMo; - robot.getPosition(wMc); - wMo = wMc * cMo; - - vpPoint point; - point.setWorldCoordinates(0,0,0); - point.track(cMo); - - vpServo task; - task.setServo(vpServo::EYEINHAND_L_cVe_eJe); - task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); - task.setLambda(0.2); - vpVelocityTwistMatrix cVe; - cVe = robot.get_cVe(); - task.set_cVe(cVe); - - vpMatrix eJe; - robot.get_eJe(eJe); - task.set_eJe(eJe); - - vpFeaturePoint s_x, s_xd; - vpFeatureBuilder::create(s_x, point); - s_xd.buildFrom(0, 0, cdMo[2][3]); - task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); - - vpFeatureDepth s_Z, s_Zd; - double Z = point.get_Z(); - double Zd = cdMo[2][3]; - s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); - s_Zd.buildFrom(0, 0, Zd, 0); - task.addFeature(s_Z, s_Zd); + try { + vpHomogeneousMatrix cdMo; + cdMo[1][3] = 1.2; + cdMo[2][3] = 0.5; + + vpHomogeneousMatrix cMo; + cMo[0][3] = 0.3; + cMo[1][3] = cdMo[1][3]; + cMo[2][3] = 1.; + vpRotationMatrix cRo(0, atan2( cMo[0][3], cMo[1][3]), 0); + cMo.insert(cRo); + + vpSimulatorPioneer robot ; + robot.setSamplingTime(0.04); + vpHomogeneousMatrix wMc, wMo; + robot.getPosition(wMc); + wMo = wMc * cMo; + + vpPoint point; + point.setWorldCoordinates(0,0,0); + point.track(cMo); + + vpServo task; + task.setServo(vpServo::EYEINHAND_L_cVe_eJe); + task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE); + task.setLambda(0.2); + vpVelocityTwistMatrix cVe; + cVe = robot.get_cVe(); + task.set_cVe(cVe); + + vpMatrix eJe; + robot.get_eJe(eJe); + task.set_eJe(eJe); + + vpFeaturePoint s_x, s_xd; + vpFeatureBuilder::create(s_x, point); + s_xd.buildFrom(0, 0, cdMo[2][3]); + task.addFeature(s_x, s_xd, vpFeaturePoint::selectX()); + + vpFeatureDepth s_Z, s_Zd; + double Z = point.get_Z(); + double Zd = cdMo[2][3]; + s_Z.buildFrom(s_x.get_x(), s_x.get_y(), Z, log(Z/Zd)); + s_Zd.buildFrom(0, 0, Zd, 0); + task.addFeature(s_Z, s_Zd); #ifdef VISP_HAVE_DISPLAY - // Create a window (800 by 500) at position (400, 10) with 3 graphics - vpPlot graph(3, 800, 500, 400, 10, "Curves..."); - - // Init the curve plotter - graph.initGraph(0,2); - graph.initGraph(1,2); - graph.initGraph(2,1); - graph.setTitle(0, "Velocities"); - graph.setTitle(1, "Error s-s*"); - graph.setTitle(2, "Depth"); - graph.setLegend(0, 0, "vx"); - graph.setLegend(0, 1, "wz"); - graph.setLegend(1, 0, "x"); - graph.setLegend(1, 1, "log(Z/Z*)"); - graph.setLegend(2, 0, "Z"); + // Create a window (800 by 500) at position (400, 10) with 3 graphics + vpPlot graph(3, 800, 500, 400, 10, "Curves..."); + + // Init the curve plotter + graph.initGraph(0,2); + graph.initGraph(1,2); + graph.initGraph(2,1); + graph.setTitle(0, "Velocities"); + graph.setTitle(1, "Error s-s*"); + graph.setTitle(2, "Depth"); + graph.setLegend(0, 0, "vx"); + graph.setLegend(0, 1, "wz"); + graph.setLegend(1, 0, "x"); + graph.setLegend(1, 1, "log(Z/Z*)"); + graph.setLegend(2, 0, "Z"); #endif - try - { - int iter = 1; + int iter = 0; for (; ;) { robot.getPosition(wMc) ; @@ -132,16 +131,18 @@ int main() graph.saveData(1, "./error2.dat"); const char *legend = "Click to quit..."; - vpDisplay::displayCharString(graph.I, graph.I.getHeight()-60, graph.I.getWidth()-150, legend, vpColor::red); + vpDisplay::displayText(graph.I, (int)graph.I.getHeight()-60, (int)graph.I.getWidth()-150, legend, vpColor::red); vpDisplay::flush(graph.I); vpDisplay::getClick(graph.I); #endif + + + // Kill the servo task + task.print() ; + task.kill(); + } - catch(...) - { + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - - // Kill the servo task - task.print() ; - task.kill(); } diff --git a/tutorial/simulator/image/CMakeLists.txt b/tutorial/simulator/image/CMakeLists.txt index 551651fe4979d34c18ae422e7f288a1491e1dba0..82c7927294a799d681a948189f746559a5dd3106 100644 --- a/tutorial/simulator/image/CMakeLists.txt +++ b/tutorial/simulator/image/CMakeLists.txt @@ -3,21 +3,21 @@ project(tutorial-simulation-image) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-image-simulator tutorial-image-simulator.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-image-simulator.cpp) -# copy the data -get_target_property(target_location tutorial-image-simulator LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/target_square.pgm" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-image-simulator - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/target_square.pgm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-image-simulator.cpp ${data}) endforeach() diff --git a/tutorial/simulator/image/tutorial-image-simulator.cpp b/tutorial/simulator/image/tutorial-image-simulator.cpp index efe9d0a84d202ce720b8db98fe8858fd4692348d..589a6a5d2e820386cd082ca24d2c9ddf0bfd47b9 100644 --- a/tutorial/simulator/image/tutorial-image-simulator.cpp +++ b/tutorial/simulator/image/tutorial-image-simulator.cpp @@ -1,52 +1,78 @@ -/*! \example tutorial-image-simulator.cpp */ +//! \example tutorial-image-simulator.cpp #include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayGDI.h> #include <visp/vpImageIo.h> +//! [Include] #include <visp/vpImageSimulator.h> +//! [Include] int main() { - vpImage<unsigned char> target; - vpImageIo::read(target, "./target_square.pgm"); + try { + //! [Read image] + vpImage<unsigned char> target; + vpImageIo::read(target, "./target_square.pgm"); + //! [Read image] - vpColVector X[4]; - for (int i = 0; i < 4; i++) X[i].resize(3); - // Top left Top right Bottom right Bottom left - X[0][0] = -0.1; X[1][0] = 0.1; X[2][0] = 0.1; X[3][0] = -0.1; - X[0][1] = -0.1; X[1][1] = -0.1; X[2][1] = 0.1; X[3][1] = 0.1; - X[0][2] = 0; X[1][2] = 0; X[2][2] = 0; X[3][2] = 0; + //! [Set model] + vpColVector X[4]; + for (int i = 0; i < 4; i++) X[i].resize(3); + // Top left Top right Bottom right Bottom left + X[0][0] = -0.1; X[1][0] = 0.1; X[2][0] = 0.1; X[3][0] = -0.1; + X[0][1] = -0.1; X[1][1] = -0.1; X[2][1] = 0.1; X[3][1] = 0.1; + X[0][2] = 0; X[1][2] = 0; X[2][2] = 0; X[3][2] = 0; + //! [Set model] - vpImage<unsigned char> I(480, 640); - vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); - vpHomogeneousMatrix cMo(0, 0, 0.35, 0, vpMath::rad(30), vpMath::rad(15)); + //! [Image construction] + vpImage<unsigned char> I(480, 640); + //! [Image construction] + //! [Camera parameters] + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + //! [Camera parameters] + //! [Set cMo] + vpHomogeneousMatrix cMo(0, 0, 0.35, 0, vpMath::rad(30), vpMath::rad(15)); + //! [Set cMo] - vpImageSimulator sim; - sim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION); - sim.init(target, X); + //! [Create simulator] + vpImageSimulator sim; + sim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION); + sim.init(target, X); + //! [Create simulator] - // Get the new image of the projected planar image target - sim.setCleanPreviousImage(true); - sim.setCameraPosition(cMo); - sim.getImage(I, cam); + // Get the new image of the projected planar image target + //! [Render image] + sim.setCleanPreviousImage(true); + sim.setCameraPosition(cMo); + sim.getImage(I, cam); + //! [Render image] - try { - vpImageIo::write(I, "./rendered_image.jpg"); - } - catch(...) { - std::cout << "Unsupported image format" << std::endl; - } + //! [Write image] + try { + vpImageIo::write(I, "./rendered_image.jpg"); + } + catch(...) { + std::cout << "Unsupported image format" << std::endl; + } + //! [Write image] #if defined(VISP_HAVE_X11) - vpDisplayX d(I); + vpDisplayX d(I); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I); + vpDisplayGDI d(I); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::setTitle(I, "Planar image projection"); - vpDisplay::display(I); - vpDisplay::flush(I); - std::cout << "A click to quit..." << std::endl; - vpDisplay::getClick(I); + vpDisplay::setTitle(I, "Planar image projection"); + vpDisplay::display(I); + vpDisplay::flush(I); + std::cout << "A click to quit..." << std::endl; + vpDisplay::getClick(I); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } } diff --git a/tutorial/trace/CMakeLists.txt b/tutorial/trace/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a220820c1749c233c54f3656bfe3c8cd5b703d8 --- /dev/null +++ b/tutorial/trace/CMakeLists.txt @@ -0,0 +1,16 @@ +project(tutorial-trace) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +# set the list of source files +set(tutorial_cpp + tutorial-trace.cpp) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() diff --git a/tutorial/trace/tutorial-trace.cpp b/tutorial/trace/tutorial-trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..737e285510f7a0286755e99fc3d28b7d10802218 --- /dev/null +++ b/tutorial/trace/tutorial-trace.cpp @@ -0,0 +1,39 @@ +/*! \example tutorial-trace.cpp */ +//#define VP_TRACE // Activate the trace mode +//#define VP_DEBUG // Activate the debug mode +#define VP_DEBUG_MODE 2 // Activate debug level 1 and 2 + +#include <visp/vpDebug.h> + +int main() +{ + vpIN_FCT("main()"); // std::cout if VP_TRACE defined + + // Check the active debug levels set in VP_DEBUG_MODE + std::cout << "Debug level 1 active: " << vpDEBUG_ENABLE(1) << std::endl; + std::cout << "Debug level 2 active: " << vpDEBUG_ENABLE(2) << std::endl; + std::cout << "Debug level 3 active: " << vpDEBUG_ENABLE(3) << std::endl; + + // C-like trace printings if VP_TRACE defined + vpTRACE("C-like trace"); // std::cout + vpTRACE(1, "C-like trace level 1"); // std::cout + + vpERROR_TRACE("C-like error trace"); // std::cerr + vpERROR_TRACE(1, "C-like error trace level 1"); // std::cerr if VP_DEBUG_MODE value is >= 1 + + // C-like debug printings if VP_DEBUG defined + vpDEBUG_TRACE ("C-like debug trace"); // stdout + vpDERROR_TRACE("C-like error trace"); // stderr + + vpDEBUG_TRACE (2, "C-like debug trace level 2"); // std::cout if VP_DEBUG_MODE value >= 2 + vpDERROR_TRACE(2, "C-like error trace level 2"); // std::cerr if VP_DEBUG_MODE value >= 2 + + // C++-like trace printings if VP_TRACE defined + vpCTRACE << "C++-like trace" << std::endl; // std::cout + vpCERROR << "C++-like error trace" << std::endl; // std::cerr + + // C++-like debug printings if VP_DEBUG defined + vpCDEBUG(2) << "C++-like debug trace level 2" << std::endl; // std::cout if VP_DEBUG_MODE value >= 2 + + vpOUT_FCT("main()"); // std::cout if VP_TRACE defined +} diff --git a/tutorial/tracking/blob/CMakeLists.txt b/tutorial/tracking/blob/CMakeLists.txt index 8e482333c90295d82fe56f77706d308b2dc79494..e2c71499b25e1bea692313d988ed788542c8a762 100644 --- a/tutorial/tracking/blob/CMakeLists.txt +++ b/tutorial/tracking/blob/CMakeLists.txt @@ -3,20 +3,23 @@ project(tutorial-tracking-blob) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-blob-tracker tutorial-blob-tracker.cpp) -add_executable(tutorial-blob-auto-tracker tutorial-blob-auto-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-blob-auto-tracker.cpp + tutorial-blob-tracker-live-firewire.cpp + tutorial-blob-tracker-live-v4l2.cpp) -# copy the data -get_target_property(target_location tutorial-blob-auto-tracker LOCATION) -get_filename_component(target_location "${target_location}" PATH) -set(data "${CMAKE_CURRENT_SOURCE_DIR}/target.pgm" ) -add_custom_command( - TARGET tutorial-blob-auto-tracker - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" -) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/target.pgm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-blob-auto-tracker.cpp ${data}) +endforeach() diff --git a/tutorial/tracking/blob/tutorial-blob-auto-tracker.cpp b/tutorial/tracking/blob/tutorial-blob-auto-tracker.cpp index 34be53647c1bf3d7e876586da2371e56481df244..92e608ab9463f1c5a0f1b919f919b983239e97ac 100644 --- a/tutorial/tracking/blob/tutorial-blob-auto-tracker.cpp +++ b/tutorial/tracking/blob/tutorial-blob-auto-tracker.cpp @@ -1,84 +1,104 @@ -/*! \example tutorial-blob-auto-tracker.cpp */ +//! \example tutorial-blob-auto-tracker.cpp #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpDot2.h> #include <visp/vpImageIo.h> int main() { - bool learn = false; - vpImage<unsigned char> I; // Create a gray level image container + try { + bool learn = false; + vpImage<unsigned char> I; // Create a gray level image container - vpImageIo::read(I, "./target.pgm"); + vpImageIo::read(I, "./target.pgm"); #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Camera view"); + vpDisplayX d(I, 0, 0, "Camera view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Camera view"); + vpDisplayGDI d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::display(I); - vpDisplay::flush(I); + vpDisplay::display(I); + vpDisplay::flush(I); - vpDot2 blob; - if (learn) { - // Learn the characteristics of the blob to auto detect - blob.setGraphics(true); - blob.setGraphicsThickness(1); - blob.initTracking(I); - blob.track(I); - std::cout << "Blob characteristics: " << std::endl; - std::cout << " width : " << blob.getWidth() << std::endl; - std::cout << " height: " << blob.getHeight() << std::endl; + //! [Construction] + vpDot2 blob; + //! [Construction] + //! [Learn] + if (learn) { + // Learn the characteristics of the blob to auto detect + blob.setGraphics(true); + blob.setGraphicsThickness(1); + blob.initTracking(I); + blob.track(I); + std::cout << "Blob characteristics: " << std::endl; + std::cout << " width : " << blob.getWidth() << std::endl; + std::cout << " height: " << blob.getHeight() << std::endl; #if VISP_VERSION_INT > VP_VERSION_INT(2,7,0) - std::cout << " area: " << blob.getArea() << std::endl; + std::cout << " area: " << blob.getArea() << std::endl; #endif - std::cout << " gray level min: " << blob.getGrayLevelMin() << std::endl; - std::cout << " gray level max: " << blob.getGrayLevelMax() << std::endl; - std::cout << " grayLevelPrecision: " << blob.getGrayLevelPrecision() << std::endl; - std::cout << " sizePrecision: " << blob.getSizePrecision() << std::endl; - std::cout << " ellipsoidShapePrecision: " << blob.getEllipsoidShapePrecision() << std::endl; - } - else { - // Set blob characteristics for the auto detection - blob.setWidth(50); - blob.setHeight(50); + std::cout << " gray level min: " << blob.getGrayLevelMin() << std::endl; + std::cout << " gray level max: " << blob.getGrayLevelMax() << std::endl; + std::cout << " grayLevelPrecision: " << blob.getGrayLevelPrecision() << std::endl; + std::cout << " sizePrecision: " << blob.getSizePrecision() << std::endl; + std::cout << " ellipsoidShapePrecision: " << blob.getEllipsoidShapePrecision() << std::endl; + } + //! [Learn] + //! [Setting] + else { + // Set blob characteristics for the auto detection + blob.setWidth(50); + blob.setHeight(50); #if VISP_VERSION_INT > VP_VERSION_INT(2,7,0) - blob.setArea(1700); + blob.setArea(1700); #endif - blob.setGrayLevelMin(0); - blob.setGrayLevelMax(30); - blob.setGrayLevelPrecision(0.8); - blob.setSizePrecision(0.65); - blob.setEllipsoidShapePrecision(0.65); - } + blob.setGrayLevelMin(0); + blob.setGrayLevelMax(30); + blob.setGrayLevelPrecision(0.8); + blob.setSizePrecision(0.65); + blob.setEllipsoidShapePrecision(0.65); + } + //! [Setting] - std::list<vpDot2> blob_list; - blob.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), blob_list); + //! [Search] + std::list<vpDot2> blob_list; + blob.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), blob_list); + //! [Search] - if (learn) { - // The blob that is tracked by initTracking() is not in the list of auto detected blobs - // We add it: - blob_list.push_back(blob); - } - std::cout << "Number of auto detected blob: " << blob_list.size() << std::endl; - std::cout << "A click to exit..." << std::endl; + //! [Add learned dot] + if (learn) { + // The blob that is tracked by initTracking() is not in the list of auto detected blobs + // We add it: + blob_list.push_back(blob); + } + //! [Add learned dot] + std::cout << "Number of auto detected blob: " << blob_list.size() << std::endl; + std::cout << "A click to exit..." << std::endl; - while(1) { - vpDisplay::display(I); + while(1) { + vpDisplay::display(I); - for(std::list<vpDot2>::iterator it=blob_list.begin(); it != blob_list.end(); ++it) { - (*it).setGraphics(true); - (*it).setGraphicsThickness(3); - (*it).track(I); - } + //! [Display] + for(std::list<vpDot2>::iterator it=blob_list.begin(); it != blob_list.end(); ++it) { + (*it).setGraphics(true); + (*it).setGraphicsThickness(3); + (*it).track(I); + } + //! [Display] - vpDisplay::flush(I); + vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; + if (vpDisplay::getClick(I, false)) + break; - vpTime::wait(40); + vpTime::wait(40); + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } } diff --git a/tutorial/tracking/blob/tutorial-blob-tracker-live-firewire.cpp b/tutorial/tracking/blob/tutorial-blob-tracker-live-firewire.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f893e6613466f9e296e8b48f3ded394b2071e9bb --- /dev/null +++ b/tutorial/tracking/blob/tutorial-blob-tracker-live-firewire.cpp @@ -0,0 +1,82 @@ +//! \example tutorial-blob-tracker-live-firewire.cpp +#include <visp/vp1394CMUGrabber.h> +#include <visp/vp1394TwoGrabber.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDot2.h> + +int main() +{ +#if (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) || (VISP_HAVE_OPENCV_VERSION >= 0x020100)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) + vpImage<unsigned char> I; // Create a gray level image container + +#if defined(VISP_HAVE_DC1394_2) + vp1394TwoGrabber g(false); + g.open(I); +#elif defined(VISP_HAVE_CMU1394) + vp1394CMUGrabber g; + g.open(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture g(0); // open the default camera + if(!g.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + +#if defined(VISP_HAVE_X11) + vpDisplayX d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view"); +#endif + + //! [Construction] + vpDot2 blob; + //! [Construction] + //! [Setting] + blob.setGraphics(true); + blob.setGraphicsThickness(2); + //! [Setting] + + vpImagePoint germ; + bool init_done = false; + + while(1) { + try { +#if defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + g >> frame; + vpImageConvert::convert(frame, I); +#endif + vpDisplay::display(I); + + if (! init_done) { + vpDisplay::displayText(I, vpImagePoint(10,10), "Click in the blob to initialize the tracker", vpColor::red); + if (vpDisplay::getClick(I, germ, false)) { + //! [Init] + blob.initTracking(I, germ); + //! [Init] + init_done = true; + } + } + else { + //! [Track] + blob.track(I); + //! [Track] + } + + vpDisplay::flush(I); + } + catch(...) { + init_done = false; + } + } +#endif +} diff --git a/tutorial/tracking/blob/tutorial-blob-tracker-live-v4l2.cpp b/tutorial/tracking/blob/tutorial-blob-tracker-live-v4l2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..166682c44b0785f3767b9c76e8571dd502fb0a93 --- /dev/null +++ b/tutorial/tracking/blob/tutorial-blob-tracker-live-v4l2.cpp @@ -0,0 +1,72 @@ +//! \example tutorial-blob-tracker-live-v4l2.cpp +#include <visp/vpV4l2Grabber.h> +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayGTK.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDot2.h> + +int main() +{ +#if ((defined(VISP_HAVE_V4L2) || (VISP_HAVE_OPENCV_VERSION >= 0x020100)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_GTK))) + vpImage<unsigned char> I; // Create a gray level image container + +#if defined(VISP_HAVE_V4L2) + vpV4l2Grabber g; + g.open(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture g(0); // open the default camera + if(!g.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + +#if defined(VISP_HAVE_X11) + vpDisplayX d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_GTK) + vpDisplayGTK d(I, 0, 0, "Camera view"); +#endif + + vpDot2 blob; + blob.setGraphics(true); + blob.setGraphicsThickness(2); + + vpImagePoint germ; + bool init_done = false; + std::cout << "Click!!!" << std::endl; + while(1) { + try { +#if defined(VISP_HAVE_V4L2) + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + g >> frame; + vpImageConvert::convert(frame, I); +#endif + vpDisplay::display(I); + + if (! init_done) { + vpDisplay::displayText(I, vpImagePoint(10,10), "Click in the blob to initialize the tracker", vpColor::red); + if (vpDisplay::getClick(I, germ, false)) { + blob.initTracking(I, germ); + init_done = true; + } + } + else { + blob.track(I); + } + vpDisplay::flush(I); + } + catch(...) { + init_done = false; + } + } +#endif +} diff --git a/tutorial/tracking/blob/tutorial-blob-tracker.cpp b/tutorial/tracking/blob/tutorial-blob-tracker.cpp deleted file mode 100644 index 23dae3bd1a388440f54e8b1527256de8520a9825..0000000000000000000000000000000000000000 --- a/tutorial/tracking/blob/tutorial-blob-tracker.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/*! - \example tutorial-blob-tracker.cpp - */ -#include <visp/vp1394CMUGrabber.h> -#include <visp/vp1394TwoGrabber.h> -#include <visp/vpDisplayGDI.h> -#include <visp/vpDisplayX.h> -#include <visp/vpDot2.h> - -int main() -{ -#if (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394)) - vpImage<unsigned char> I; // Create a gray level image container - -#if defined(VISP_HAVE_DC1394_2) - vp1394TwoGrabber g(false); -#elif defined(VISP_HAVE_CMU1394) - vp1394CMUGrabber g; -#endif - g.open(I); - g.acquire(I); - -#if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Camera view"); -#elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Camera view"); -#else - std::cout << "No image viewer is available..." << std::endl; -#endif - vpDisplay::display(I); - vpDisplay::flush(I); - - vpDot2 blob; - blob.setGraphics(true); - blob.setGraphicsThickness(2); - blob.initTracking(I); - - while(1) { - g.acquire(I); // Acquire an image - vpDisplay::display(I); - blob.track(I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; - } -#endif -} diff --git a/tutorial/tracking/keypoint/CMakeLists.txt b/tutorial/tracking/keypoint/CMakeLists.txt index a407530fad3463453c84fea6d04001d7e4c4682d..007ac47f07dc462e359cfc4af150c34172acc0f9 100644 --- a/tutorial/tracking/keypoint/CMakeLists.txt +++ b/tutorial/tracking/keypoint/CMakeLists.txt @@ -3,21 +3,23 @@ project(tutorial-tracking-keypoint) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-klt-tracker tutorial-klt-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-klt-tracker.cpp + tutorial-klt-tracker-live-v4l2.cpp + tutorial-klt-tracker-with-reinit.cpp) -# copy the data -get_target_property(target_location tutorial-klt-tracker LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/video-postcard.mpeg" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-klt-tracker - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/video-postcard.mpeg" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-klt-tracker.cpp ${data}) endforeach() diff --git a/tutorial/tracking/keypoint/tutorial-klt-tracker-live-v4l2.cpp b/tutorial/tracking/keypoint/tutorial-klt-tracker-live-v4l2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6f824c93f947c825d27620797060555fa60d247 --- /dev/null +++ b/tutorial/tracking/keypoint/tutorial-klt-tracker-live-v4l2.cpp @@ -0,0 +1,126 @@ +/*! \example tutorial-klt-tracker-live-v4l2.cpp */ +#include <visp/vpImageConvert.h> +#include <visp/vpKltOpencv.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> +#include <visp/vpV4l2Grabber.h> + +int main(int argc, const char *argv[]) +{ +#if defined(VISP_HAVE_OPENCV) && (defined(VISP_HAVE_V4L2) || (VISP_HAVE_OPENCV_VERSION >= 0x020100)) + try { + bool opt_init_by_click = false; + int opt_device = 0; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--init-by-click") + opt_init_by_click = true; + else if (std::string(argv[i]) == "--device") + opt_device = atoi(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] << " [--init-by-click] [--device <camera device>] [--help]" << std::endl; + return 0; + } + } + + vpImage<unsigned char> I; + +#if defined(VISP_HAVE_V4L2) + vpV4l2Grabber g; + std::ostringstream device; + device << "/dev/video" << opt_device; + g.setDevice(device.str()); + g.open(I); + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + cv::VideoCapture g(opt_device); + if(!g.isOpened()) { // check if we succeeded + std::cout << "Failed to open the camera" << std::endl; + return -1; + } + cv::Mat frame; + g >> frame; // get a new frame from camera + vpImageConvert::convert(frame, I); +#endif + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + IplImage * cvI = NULL; +#else + cv::Mat cvI; +#endif + vpImageConvert::convert(I, cvI); + + // Display initialisation + vpDisplayOpenCV d(I, 0, 0, "Klt tracking"); + vpDisplay::display(I); + vpDisplay::flush(I); + + vpKltOpencv tracker; + // Set tracker parameters + tracker.setMaxFeatures(200); + tracker.setWindowSize(10); + tracker.setQuality(0.01); + tracker.setMinDistance(15); + tracker.setHarrisFreeParameter(0.04); + tracker.setBlockSize(9); + tracker.setUseHarris(1); + tracker.setPyramidLevels(3); + + // Initialise the tracking + if (opt_init_by_click) { +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + vpMouseButton::vpMouseButtonType button; + std::vector<cv::Point2f> guess; + vpImagePoint ip; + do { + vpDisplay::displayText(I, 10, 10, "Left click to select a point, right to start tracking", vpColor::red); + if (vpDisplay::getClick(I, ip, button, false)) { + if (button == vpMouseButton::button1) { + guess.push_back(cv::Point2f((float)ip.get_u(), (float)ip.get_v())); + vpDisplay::displayText(I, 10, 10, "Left click to select a point, right to start tracking", vpColor::red); + vpDisplay::displayCross(I, ip, 12, vpColor::green); + } + } + vpDisplay::flush(I); + vpTime::wait(20); + } while(button != vpMouseButton::button3); + tracker.initTracking(cvI, guess); +#endif + } + else { + tracker.initTracking(cvI); + } + + while ( 1 ) { +#if defined(VISP_HAVE_V4L2) + g.acquire(I); +#elif defined(VISP_HAVE_OPENCV) + g >> frame; + vpImageConvert::convert(frame, I); +#endif + vpDisplay::display(I); + + vpImageConvert::convert(I, cvI); + tracker.track(cvI); + + tracker.display(I, vpColor::red); + vpDisplay::displayText(I, 10, 10, "Click to quit", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) + break; + } + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvReleaseImage(&cvI); +#endif + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/tracking/keypoint/tutorial-klt-tracker-with-reinit.cpp b/tutorial/tracking/keypoint/tutorial-klt-tracker-with-reinit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7013c0c85d20c26e2944be96a91832d4c434cb9f --- /dev/null +++ b/tutorial/tracking/keypoint/tutorial-klt-tracker-with-reinit.cpp @@ -0,0 +1,150 @@ +//! \example tutorial-klt-tracker-with-reinit.cpp +#include <visp/vpImageConvert.h> +#include <visp/vpKltOpencv.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> + +int main() +{ +#ifdef VISP_HAVE_OPENCV + try { + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + + vpImage<unsigned char> I; + reader.acquire(I); + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + IplImage * cvI = NULL; +#else + cv::Mat cvI; +#endif + vpImageConvert::convert(I, cvI); + + // Display initialisation + vpDisplayOpenCV d(I, 0, 0, "Klt tracking"); + vpDisplay::display(I); + vpDisplay::flush(I); + + vpKltOpencv tracker; + // Set tracker parameters + tracker.setMaxFeatures(200); + tracker.setWindowSize(10); + tracker.setQuality(0.01); + tracker.setMinDistance(15); + tracker.setHarrisFreeParameter(0.04); + tracker.setBlockSize(9); + tracker.setUseHarris(1); + tracker.setPyramidLevels(3); + + // Initialise the tracking + tracker.initTracking(cvI); + + while ( ! reader.end() ) + { + reader.acquire(I); + std::cout << "acquire image " << reader.getFrameIndex() << std::endl; + vpDisplay::display(I); + + vpImageConvert::convert(I, cvI); + + //! [Re-init tracker] + // Restart the initialization to detect new keypoints + if (reader.getFrameIndex() == 25) { + std::cout << "Re initialize the tracker" << std::endl; +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + // Save of previous features + std::vector<cv::Point2f> prev_features = tracker.getFeatures(); + + // Start a new feature detection + tracker.initTracking(cvI); + std::vector<cv::Point2f> new_features = tracker.getFeatures(); + + // Add previous features if they are not to close to detected one + double distance, minDistance_ = tracker.getMinDistance(); + bool is_redundant; + for (size_t i=0; i < prev_features.size(); i++) { + // Test if a previous feature is not redundant with one of the newly detected + is_redundant = false; + for (size_t j=0; j < new_features.size(); j++){ + distance = sqrt(vpMath::sqr(new_features[j].x-prev_features[i].x) + vpMath::sqr(new_features[j].y-prev_features[i].y)); + if(distance < minDistance_){ + is_redundant = true; + break; + } + } + if(is_redundant){ + continue; + } + //std::cout << "Add previous feature with index " << i << std::endl; + tracker.addFeature(prev_features[i]); + } +#else + // Save of previous features + int prev_nfeatures = tracker.getNbFeatures(); + float x,y; + int id, j=0; + + CvPoint2D32f *prev_features = (CvPoint2D32f*)cvAlloc(prev_nfeatures*sizeof(CvPoint2D32f)); + + for (int i=0; i <prev_nfeatures ; i ++) { + tracker.getFeature(i, id, x, y); + prev_features[i].x=x; + prev_features[i].y=y; + //printf("prev feature %d: id %d coord: %g %g\n", i, id, x, y); + } + + // Start a new feature detection + tracker.initTracking(cvI); + std::cout << "Detection of " << tracker.getNbFeatures() << " new features" << std::endl; + + // Add previous features if they are not to close to detected one + double distance, minDistance_ = tracker.getMinDistance(); + bool is_redundant; + for(int i = tracker.getNbFeatures() ; + j<prev_nfeatures && i<tracker.getMaxFeatures() ; + j++){ + // Test if a previous feature is not redundant with new the one that are newly detected + is_redundant = false; + for(int k=0; k<tracker.getNbFeatures(); k++){ + tracker.getFeature(k,id,x,y); + //printf("curr feature %d: id %d coord: %g %g\n", k, id, x, y); + distance = sqrt(vpMath::sqr(x-prev_features[j].x) + vpMath::sqr(y-prev_features[j].y)); + if(distance < minDistance_){ + is_redundant = true; + break; + } + } + if(is_redundant){ + continue; + } + //std::cout << "Add previous feature with index " << i << std::endl; + tracker.addFeature(i, prev_features[j].x, prev_features[j].y); + i++; + } + cvFree(&prev_features); +#endif + } + // Track the features + tracker.track(cvI); + //! [Re-init tracker] + + std::cout << "tracking of " << tracker.getNbFeatures() << " features" << std::endl; + + tracker.display(I, vpColor::red); + vpDisplay::flush(I); + } + + vpDisplay::getClick(I); + +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvReleaseImage(&cvI); +#endif + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#endif +} diff --git a/tutorial/tracking/keypoint/tutorial-klt-tracker.cpp b/tutorial/tracking/keypoint/tutorial-klt-tracker.cpp index 89f429b4ff332dbcde1f6b98cc3acafb6a801f37..2dbe100e663f92f949a364c93859bc21299fbca0 100644 --- a/tutorial/tracking/keypoint/tutorial-klt-tracker.cpp +++ b/tutorial/tracking/keypoint/tutorial-klt-tracker.cpp @@ -1,56 +1,164 @@ -/*! \example tutorial-klt-tracker.cpp */ +//! \example tutorial-klt-tracker.cpp +//! [Include] #include <visp/vpImageConvert.h> #include <visp/vpKltOpencv.h> #include <visp/vpDisplayOpenCV.h> #include <visp/vpVideoReader.h> +//! [Include] -int main() +int main(int argc, const char *argv[]) { -#if (VISP_HAVE_OPENCV_VERSION >= 0x010100) && defined(VISP_HAVE_FFMPEG) - vpVideoReader reader; - reader.setFileName("video-postcard.mpeg"); - - vpImage<unsigned char> I; - reader.acquire(I); - - IplImage * cvI = NULL; - vpImageConvert::convert(I, cvI); - - // Display initialisation - vpDisplayOpenCV d(I, 0, 0, "Klt tracking"); - vpDisplay::display(I); - vpDisplay::flush(I); - - vpKltOpencv tracker; - // Set tracker parameters - tracker.setMaxFeatures(200); - tracker.setWindowSize(10); - tracker.setQuality(0.01); - tracker.setMinDistance(15); - tracker.setHarrisFreeParameter(0.04); - tracker.setBlockSize(9); - tracker.setUseHarris(1); - tracker.setPyramidLevels(3); - - // Initialise the tracking - tracker.initTracking(cvI); - - while ( ! reader.end() ) - { + //! [Check 3rd party] +#ifdef VISP_HAVE_OPENCV + //! [Check 3rd party] + try { + bool opt_init_by_click = false; + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--init-by-click") + opt_init_by_click = true; + else if (std::string(argv[i]) == "--help") { + std::cout << "Usage: " << argv[0] << " [--init-by-click] [--help]" << std::endl; + return 0; + } + } + + //! [Create reader] + vpVideoReader reader; + reader.setFileName("video-postcard.mpeg"); + //! [Create reader] + + //! [Acquire] + vpImage<unsigned char> I; reader.acquire(I); - vpDisplay::display(I); + //! [Acquire] + //! [Convert to OpenCV image] +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + IplImage * cvI = NULL; +#else + cv::Mat cvI; +#endif vpImageConvert::convert(I, cvI); - tracker.track(cvI); + //! [Convert to OpenCV image] - tracker.display(I, vpColor::red); + //! [Init display] + vpDisplayOpenCV d(I, 0, 0, "Klt tracking"); + vpDisplay::display(I); vpDisplay::flush(I); - } + //! [Init display] + + //! [Create tracker] + vpKltOpencv tracker; + tracker.setMaxFeatures(200); + tracker.setWindowSize(10); + //! [Quality] + tracker.setQuality(0.01); + //! [Quality] + tracker.setMinDistance(15); + tracker.setHarrisFreeParameter(0.04); + tracker.setBlockSize(9); + tracker.setUseHarris(1); + tracker.setPyramidLevels(3); + //! [Create tracker] + + // Initialise the tracking + if (opt_init_by_click) { + vpMouseButton::vpMouseButtonType button = vpMouseButton::button1; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + std::vector<CvPoint2D32f> feature; +#else + std::vector<cv::Point2f> feature; +#endif + vpImagePoint ip; + do { + vpDisplay::displayText(I, 10, 10, + "Left click to select a point, right to start tracking", + vpColor::red); + if (vpDisplay::getClick(I, ip, button, false)) { + if (button == vpMouseButton::button1) { + feature.push_back(cv::Point2f((float)ip.get_u(), (float)ip.get_v())); + vpDisplay::displayCross(I, ip, 12, vpColor::green); + } + } + vpDisplay::flush(I); + vpTime::wait(20); + } while(button != vpMouseButton::button3); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + tracker.initTracking(cvI, &feature[0], feature.size()); +#else + tracker.initTracking(cvI, feature); +#endif + } + else { + //! [Init tracker] + tracker.initTracking(cvI); + //! [Init tracker] + } + + //! [How many features] + std::cout << "Tracker initialized with " << tracker.getNbFeatures() << " features" << std::endl; + //! [How many features] - vpDisplay::getClick(I); + //! [While loop] + while ( ! reader.end() ) + { + reader.acquire(I); + vpDisplay::display(I); - cvReleaseImage(&cvI); + vpImageConvert::convert(I, cvI); - return 0; + if (opt_init_by_click && reader.getFrameIndex() == reader.getFirstFrameIndex() + 20) { + vpMouseButton::vpMouseButtonType button = vpMouseButton::button1; +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + std::vector<CvPoint2D32f> feature; +#else + std::vector<cv::Point2f> feature; +#endif + vpImagePoint ip; + do { + vpDisplay::displayText(I, 10, 10, + "Left click to select a point, right to start tracking", + vpColor::red); + if (vpDisplay::getClick(I, ip, button, false)) { + if (button == vpMouseButton::button1) { + feature.push_back(cv::Point2f((float)ip.get_u(), (float)ip.get_v())); + vpDisplay::displayCross(I, ip, 12, vpColor::green); + } + } + vpDisplay::flush(I); + vpTime::wait(20); + } while(button != vpMouseButton::button3); +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + tracker.initTracking(cvI, &feature[0], feature.size()); +#else + tracker.initTracking(cvI, feature); +#endif + } + + tracker.track(cvI); + + tracker.display(I, vpColor::red); + vpDisplay::flush(I); + } + //! [While loop] + + //! [Wait click] + vpDisplay::getClick(I); + //! [Wait click] + + //! [Release IplImage] +#if (VISP_HAVE_OPENCV_VERSION < 0x020408) + cvReleaseImage(&cvI); +#endif + //! [Release IplImage] + + return 0; + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; #endif } diff --git a/tutorial/tracking/model-based/edges/CMakeLists.txt b/tutorial/tracking/model-based/edges/CMakeLists.txt index 6eaad4a22124ad260b012874a2b2ece985d074d8..fbad7e2da068c24dc39f203fcd9cd61c371ea3f5 100644 --- a/tutorial/tracking/model-based/edges/CMakeLists.txt +++ b/tutorial/tracking/model-based/edges/CMakeLists.txt @@ -3,25 +3,26 @@ project(tutorial-tracking-mb-edges) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-mb-edge-tracker tutorial-mb-edge-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-mb-edge-tracker.cpp) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.mpg" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.wrl" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.ppm" ) -# copy the data -get_target_property(target_location tutorial-mb-edge-tracker LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.pgm" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-mb-edge-tracker - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-mb-edge-tracker.cpp ${data}) endforeach() diff --git a/tutorial/tracking/model-based/edges/teabox.cao b/tutorial/tracking/model-based/edges/teabox.cao index 4e6dd5c45d46f8971d2800f333a116dacd0e7078..83c1fdf783e68e78dfa381d4c326f07d88fa8d3f 100644 --- a/tutorial/tracking/model-based/edges/teabox.cao +++ b/tutorial/tracking/model-based/edges/teabox.cao @@ -1,20 +1,27 @@ V1 -8 -0 0 0 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z 0 0 -0.08 0.165 0 -0.08 0.165 0 0 0.165 0.068 0 0.165 0.068 -0.08 0 0.068 -0.08 -0 0.068 0 -0 -0 -6 -4 0 1 2 3 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +6 # Number of faces +4 0 1 2 3 # Face 0: [number of points] [index of the 3D points]... 4 1 6 5 2 4 4 5 6 7 4 0 3 4 7 4 5 4 3 2 -4 0 7 6 1 -0 +4 0 7 6 1 # Face 5 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/tracking/model-based/edges/teabox.init b/tutorial/tracking/model-based/edges/teabox.init index f326e929605734e745c9e9dff9c19939d67d9f75..f109800a0eab96b97050f4872839f09b0a0c2bd3 100644 --- a/tutorial/tracking/model-based/edges/teabox.init +++ b/tutorial/tracking/model-based/edges/teabox.init @@ -1,6 +1,5 @@ -4 -0.000 0 0 -0.165 0 0 -0.165 0 -0.08 -0.165 0.068 -0.08 - +4 # Number of points +0 0 0 # Point 0 +0.165 0 0 # Point 3 +0.165 0 -0.08 # Point 2 +0.165 0.068 -0.08 # Point 5 diff --git a/tutorial/tracking/model-based/edges/teabox.mpg b/tutorial/tracking/model-based/edges/teabox.mpg new file mode 100644 index 0000000000000000000000000000000000000000..50fb8ad2d9e245e2bcc06b94d22763a07d9b6191 Binary files /dev/null and b/tutorial/tracking/model-based/edges/teabox.mpg differ diff --git a/tutorial/tracking/model-based/edges/teabox.pgm b/tutorial/tracking/model-based/edges/teabox.pgm deleted file mode 100644 index 41833d705da80194d014d5e4a442d1a060ddd435..0000000000000000000000000000000000000000 --- a/tutorial/tracking/model-based/edges/teabox.pgm +++ /dev/null @@ -1,73 +0,0 @@ -P5 -640 480 -255 -IKJKKJJJLKJKKJJKKKKKKLLMMMNNOOQONNOPPQPQQQQQTSSSTSUTWUTTWVWWXXYZYYZZ[\[\]]]\`^^_```^^`aababa`__ZUQPPQQSRQRUUVVUUWVWWXVUUVWWUVVVVXVVVYWVVVVWWUUVUUWWWVXWX[YXZ\[[\\[[[[[[XXYXWYXXXXYYWXWWXZZZZ\[]\\]_]]^_^_`c__bcacbaa``a`_`^^b`__aaaaa`b```a```____aababacc`ababaabbaaaabc```a___`bcbbbdddcccfbc``___^_aacbb``^]^^_d```b_`baaeee``acbcccbaeffeddeda`aa`abfdbbbcdddcdddfheffggfddefdcbb`abbbe`_`a`ccaaaaba``_accddeddcccgbc`aa`abbcca`bb````bbbaa__ac`_``^_^`]_`_^^_^\\\\\\YZXZ[[Z]Z[XYZYXXYYXZXXYZ[]\[[[\`\[[\[[[\YXYYW\WUUWVWWYVUVYXXXZWVVVUUUSTWTRRVTVUTSSSURRRSSTSVSSSRRQRTSSSRSUTVSTRQQQPPPQPOOPMNLKMOMNMLLLLKLMLLKJLKKKJLKKJKKMLKJIIIIIHHGGGHHIGGGFEGGGFEEFEKMKKLKKJKLKLLLKKLLLMLLMMMMNNOOPOPRPQQOPQQQQQRSSSSSUTUUUUWVVWXXYZZZZZ[\]]]_^]^__a`a`__`aaaababa_]VQQPQQRSRSTUUVUTWVWWXVVWWVWWWWXWXUVWVUVUWVWXWWVUVXVUVWVW[ZZZ[Z[YZZ[\ZZYXYZXWWVWZWXWVWWWVXXZY\\\\\]^\]_^a^^^__bbabcaaadba``_^___^`aaaa``_`aaa`_``__``abbabbbbaaa```baa`abba`_`___`ab`abccbbccbbca`_`a`abaccb``^^a^`aa`__^_aaabbb```abc_aabddceedbbaaa`_`bddcbccegecddeigfeefddccdddedc`bbbdd`````acb_bbbbcccbcddddedcccbbaabbbcccd`baaaaa`cb_aa```a```__^^]^^_b___\^_][\]\YYXXXXXYYXWYWXWY[ZYXWXYZ\\_^^]\]\\\\[[ZZZZYYXXWWVWWWWYVUTW[XXXWVVUUVUUUVUTSVTUVWWSTSRRRSSSSSSSSRQQRTQRTQTTUTRSRQQRPPPQOOONMLKLMMLMKKKLLLNNMLKLMLLKJKJJJKIJKKJJJHHIHGGGFGGIGHHGEFFFGEEEELLLLOMNMNNNNPLLLNMLMMMNNNNOOPOOOSQPPQPSSSSSRSTVVWUUUUVVVWWWWXXZZ[[[[]\]]]^_^___``acbbaaabccbaa`_VRRQRQQQTTWVVVUTVWXWXVVWXWVYXWXW\WVWVVWVXWWWVVVVUUUUUVUW[[\[[[ZYZ[[[[ZZXXXXV[WWWWWWXYWXXXY\[\\\[^^]^^^^^]]``_^_accbabbaaabd_`_`__`baa```abbaa`__``bcccfba`caaa````bac``bbbb^_``_bbb``bdcabdbbbca``cbb`aafffad`__^_aac_^^`bbabba`aaabd``abciceefcaab`__`acbacdddeedfeefdejegeeddccefdddfbbbc__````bbcccecdccbdefdddfcdbbbdbcdgccdeddc`ac```aaaabaa```b`__``a^_`___\^^^\[\\ZYXYXYY[YXXYWXX[\\XWXYZZZ\^^__\^]\\\[\YYZ[ZYZYXWVWXXXYWWVVWWXYYXXWWXWXUXWXTUTTUUTSSRRSRUSSSTRSSSQRRTRQQPPRSSSURQQRQQQQOONMNOONLLLMLNMLLLLNLLLMMMMKJJJJJKIHIKIJIIGGHHHHGGHIHHIIGGGFEEFHELLLMNMNONMNNNMMMMMNNNNNPNNNNOPPOOOOONNPQTTSSTVUVVWWWWVWXWWXYXXZZZ[[\\\]]^^^^^^`aaabbaaaaacddcca`YSSPRQRPRUUVUUUUVVWWWVVWWXXXYWVVWVWWWWWVWXWVVVVWUUUVVWWWY[\\[[ZY[[[][YZXXWWVZWWWWVWYYWWXZ[ZY[\[[]_]\^^^^^^_`__`ba`aaa`aaba`_`____`aaa`acbbbbaa`aaabbcccba``_``aaaab```aaaeb_`a```___`abbbababccaaacebaabbbbbbc`a`_`ab___aca_cbbbbcbadcadccecddedbbb```_`cabdddeeecfeeedddefededcchedccb`baa`abaaaca`abdbbbbab`bbdccceaaaecccccdgdddecba`````aa`_````aa``_^a^___]]\^^]\\[ZZYYXXYZ[YXXWWXYZ[[WXXYYZZ\^]]\\]]\[\ZZYZZZZYYYWWVWWXWWWVUWXXXXYYYYYWVWUVUUTVUUTTSSRRSRQSUUVSRSSRQQRSRQQQPQSRTSQQRPOPPPOPNNONMMKLLMKKJLJKKLLLNMLLMKKJIJKJIHIIIJGHFGGGFHGGIIHHHGFGEFEFFHENNONOPOOOPPOONONPOQOPNMNOMJKLKJIHIJKLLPQVTTSVVUVVWWWXWWXWWXY\Y[Z[Z\\^^]^^^`___caaacbaacbbbacddb`[VSQRQQQRVUUTTWWXVXXYYYWXXZYYX_WVVWWXWWWVXVWWVWXUVXXXWZYY[_\\[[[\[[[][ZYYXZX[YZZYWVYYXXY][ZZ\ZZ[]]\[^]]^a____````__aaaaacbaa`_`__`abbbbcfdfdgfeccbdcfdbacab```aaebca`_caabba_`abbaaaaaabebcacbdcbadcabcdecccbaabb`cbbccbaa`adbbbbbcadcbdccfgheddccedd```bbdccefefffedffffdddddccddddhcc`eabbbbbabbaa`adbcbddccccdcdeeedddcccccedccddeaa``a``aa__``caaa_a`_`___^]^^^]]]^[Z[_YXXYZ[Z[XXX[Z^Z[[[\]Z]Z\\\\[[\^`[\ZZZZZZYXXXXWWXXZZZWVVVX[XXY\[]YWXYVWWXVWVWTSSTRRSSQRSTTRRTSURRRTRQQRPQQQQQQQPOOSPPQQNNNNNMLLLMLKKLKKJKLMMNMMLLJKJIIKJHGGHJIIGGGJGGGFGIIJHHGIIIFGFHGNNNMNQPPOOOPPPPOONOPPPHHA==>@CBABCCDFGJMOPPPQRSWWXXXYXXYWWWWXXXYYYZ[\]\\\]^```a``aacb`bdccccdgee]VSRQQQQSVUWUSWVWWXXYYYWWWWWWXXXWWVVWWWXVXXXXWWXWVWWWWXYZZ\\[[\Z[[[[ZZZXYYYWYYYZYXXYYYZZZZZZ\YZ\\^\[^]]___`aaaaa`a_`aa`_bbaa```___`cbcbbccdddeedcbbbdecbc```bcaabbbb`_`abbbaaabbaaaabbbddbbabadeaababeccceddcbcca`d_aaaba``aabbdabbbdeedbbfffeeedcefd`aabcccdeeeeefheeeedcddeccdddddcbcaeabbbfabbbbbbaaaaacecbcddcefebccccdedddbccdddaaaaaa`ba`_``abaeaa`a`___^]^_^][Z^Z[[[[YXYYYZYXXXXYZZ[[[\[XYY[[\\\^]`^\[ZZ[YXXXXYYZWXXXYXXWWWWWWXXXZZXWWXXWWUWVVUVTSRSSRRRRSSTRRRTRQQRSRRRQPOPPPQQQQPOOPPPQQONOOONMLJLKKJJIKKKLLNMLLKJJJIIIIIHGGHHIIJHFIHHGGGHGHIHHHHIFEFIGOOQONQQQRPOPPQRQQOPOLD@;53257889;?A?=?GDBDHKLMOQRTXWYYYYXVSSUWZXVVZ[\\][Z\^_a`bacaabdbbdccccdfge_VURQRTSSTUUUVVVVWYXZZ[XWVVWWXXWWWVWZXXWVVVXYXXWWWWXYXXY[Z\\[[\[Z[\[ZZZZZ[]Y]ZYXWY\Z[[\[[ZZZ\Z[\[[]]^]]_a`aafddb`_``bac`cbaac`_`a````bdbddddcccdebbbdddccadddcbaaacaa``bbabbdcdb``abbbcbcdccdaecaaaaddccbdeecdedaaddda```````abaabcdfeebbcgggffffdgeeeeeefefeddegeefeegggdddddabcdbbbbccfabbbbabddddeaaabbceffgddcifecccfeeddddbfcddccdccccbbba`_`abbbaaba`_b_`^^^_][\^[\\\\ZZ[XXZYYZYYZZZ[\[Z[Y[[^[\\\]]^_^_[ZZXXYXXXWWWWXWYWXWWWVVWWWX[YWVXWXXYUWWUUWWWSSTTQRTWTTRQRTRRQQQQRUQPPOOPQRQQQPPQPPOPPOONNQOONMLKJKJLKKKLLMNNLOJLJIIIJHHJHIHHHIIIIKGGHIHGHHHHHIGFGHFONQQPPRSRSQQQRRRONFC=8644010333368887666789:<>CHLORUW\Z[PEDELSNIMRV\\WTRW\]aa``_caabddccbbccdeff^VSQQSTTTTUUUUVVWXWXY\ZYWVWXXXWVXWXYXWWVVUVYWVWWWWXXXZYXY[[Z[\[ZZ][Z[\ZZ[\ZXZZZXYYZZ\]\^\\\^[Y[[[[]]^^__^^``ceca`__^_```c`aca_```c````abcddedddccbbbcdcbc`accebaa````_`bbaaabcbb``acabcbcecbcabbcdb`bbcecceggfddbbdebcbaac``a`abbbceeeeabcefghgffddddddcddddecccdddeeefdefeheecbddccccccccbaaaaabccdcbb`bcdedcdedceeecccddecddccedcccecccedcb_`aaccea`aab`````____^]^^][\^\\[[YXXZXXY[[Z[\\\[ZYXZZYY\]\_^]\Z[[[^YZYXX[XXWWVVWWWWWWWWWWXXZWWVXWVUUUWWUUUUWSTUTQRTSRSSRQRTRQQQQQRQQQPOQQSTQQQRQQPOOPPPNMQPONMLKJJIJKKLKKLONKKJJKKJIJIIIHHHHGIJIHHHGGHJHJHHHIIHHGFEQPPQRRSSRRRRSSTQOD:415578633320334422.*)()*+-04:@FNRUYY[J;6=GII>>ITZ\TKLUY]`bbbccacbecccccbcdefe^VRQQRSTVTRSTUUUTVXVVYZYWWZYYXWXXWWYWVVVVUVWWVYXYYYYXXZXXYYZ[]][Z[[[[[Z[[\YYY[ZX\[[Z`^\]^]a^[Z[\^\]]]]]_caaacda```a_^`a`c`aa``aaaa``__abeeeeedccccbbcccbd``cbba``````_`bda`_abbccba`bddcbcecedddfeebbcabeffffffcccecacddcc_`cccdddddefecbdefghggghhdddfehefdeddccdefgefeedceeeddgddceeecdda``aaabccbbccbeeddccdeccddedccccecebbdhcccdbbcccdca``aaabb```aa`_`aab``^]]]^]\\\[\[ZYYYXXXW[Z[]Z[[\ZYYYXXX\\]]]\[[[[Z^YYXYXXXWVUUVVWWVXXXXWXZYYWWVXUTTUUWVUUVWXUUTTRRSSRRRSQQRQRSQSSRQSQPQVSTRQPQRQQQQQPPPQQQQRONMKJJKNLKKKKKMOMNJJJIIIJKIIHGGHFJHHIIIJGHGGGGHHHHHIGGFRRRRRSTSSRSSSTSPD:506:@FEDCA=86432.)%" #(,5?ENRWX[G656:>:7:?OYXRJ@JUZ^`cbaaabbbbcddcccegfd^URQQTSSSSSSTUUUUUUSUWWXVVVUWXWYXVWYWVUUUVVWVVXWXYZVXXXYXXYY[^][ZYYYYZZZ[\\[[[ZXZZ[Z[\\[\[]\\[]_^[]]^^]_bbabcdbaaa`__`a^`_`_`___bc`___aabdeeecccbgbbcdaa`^`bba`__`a```a````^abbabca`bdcbbbcbcddeedeccccbcefhfdecccdccbcccb__`bbdecegehfeeefefgfeeegcdffdddfdefedeffihegfedcfeeeddbbcedecdecaa`bcbbcbbbbbeedbcddfeedceefeeddbcabddbcdbbbabbcbaa`aa_bb`aabbda`aaa_^]\[]]]\]][\ZZZXXXXXWYZ[ZYZZZYYZZXXXZ[\\][]^[Z[][YYZXVVUVSVUVWWWWWWXXWWVWXWVVUUTTTUUTWUYWWUTSSSSRQQRRRQQRTSRSRRQRQQRRRRQQQQQRRPPQQPOOONNNNNNKJJLLKKKLLKLLKKJKKHHIGIIIJIHHFGGHKJIIHIFFFGHGFGGIGFFSSTSSSVVUUVTSTSI=64:@GNONOOMKHD;42+%#+4;EQWXYE60122257<IWZUI@@LY]_bdbacecccfefdddfgfe^USRRSSSSTWWVVWVUUTTYWVVVVVVVVUVUWXXWVZUWUVVZXXX[ZZY\XWXXXYYZ\][[YZYZ[]\\]\[[\]YZZ\[[\]\^^]\\\^^]]]^_^^`aabcabdbabe`a````_a``__`c``___a`acfegbbbbcabca``abbbca__bcc`dbbaaaa`abeabbbbcccceedcbdeegdededcbcegffffeccfedbdbb````abdeehgfgggifgffgeeiehedfhddcegffeefgghhgjfgdcdfefeedhdedddddddcbdbabddfcfciedcbeeeeeccefffgedddbccddecbccacccba``baaaaaabbaab_aa`]\\\[_]]\[\[[YZ[\\YXWVWZZ[YZZ\ZZ[[YXYZ[^\][^]\\\\]]^ZXVUTVTVUVWYXVWXXZVWWWWWVVVWUVUUTSTUWXVUUTSSSRRRRRSTRSSTRUSRRRRURSRSSSSSRRQQPRPPOPPOOOOPMLKJJLKKKJKLKIJKJKIHHHHIHIIJHIHGGJJKIIIIGEEHGGGHGIGIESSTUTTTVUSVTTTLC>9@IMRSTTSSTOMIC<4-%$,4=IVTUB60/.-.36<FU[XPGDDO[_bccddfdcbgeeeeefgff^RSSSRTWVUUUUUUTUVTUUUWVVVVVVVUVVWYXWWWUVUVVXYXXYZYYYWXWXZYXYZZ[ZXYYZ[\ZZZYYZZYY[[\\\[\\\\]\]]]]]]]]__````bccbdbaba_``_``___`__`c]^]]_a``afefbb`bccbdaada`aaccaabbb`dbbbaaaaaabaaabdcbcddccfdheddbfdedcccdedefeedccecbbbc_``aabcddfffgfffffgggfeedefedddccccefedghiihggfffddeeeeddfdcbcdecdedddabbdddcdcfeeeffeedddbeeegfeedcbccdddbb_`ab`bba_`aaaaaac`a`_]]^^]\\\\\\[\\]\\\[ZZ[[ZYVVUZZ[YYYYZ[][ZZZ[[\\\[^\[[\ZZ[ZYXVUSSSUUVWWWWWWVWVWVWXXWWWWUUTTSSTTTTTVYVUSRSQQQRUSRRRRRSSSSRRRRRQQPPPQRQPQNOOONNMNMMOMLLKJILKKKKKKJIIIJKHHIIHHFGGHHHIGFHIIIIHHIEEHFFFHGIFFFTUUUUUUUUUVTWUJC?FMRVWWVUVVUTSRLG:3( &-6BMVK<101.)).49CU\\[RGBH[`ceeddfegggghghgfhgg]TVTTTUVWVUUUVUTTUUUUUWUVVWWXUVWXWXXWWXWWVWXXXXXXYYX[XXW[YXXXXXYYYYZ\\]\\[[[]YZ[\\\]\\\]]]]^^]`]\\_^^^`__`baaadcab``__ccc`_^````b_^]\]ba`beefffbbbdbdabcbaabcccddcbbdbdbbaaabceedbbcddgddbacddcccdffeccddddeeddddddccdgdccbaaabdedhffeefifkhihggggffhdccccddegefkikihgggeddeffeeedfdcccdddcefedddceeddefgeeeddeecdfedddefefdedgfffhcb_^acbbbc```cba`ac_^^]\]^\[\_]^]\\\]_^`]\\`]\ZZXXXY[Z[[XXZ\][\[[\\_]\[^\[[^ZZZXXWVYUTUUUUWWXZXXVYVXWVWVWZXXVTTVSTSSSSSVUUUVSSSRRTSRRSRTSSSSSSRRQRQPPQPQQOPPOOOPNNMNMLLLLMLLJMLNJJJKJJIIIJGIIIILHFFIIHIIGHIIHIJJIEFGGGGHIIFGFUVUTUWVUUUVUUUMHLPSXWVWXWWWXYZRQD=1*" '/:EBB2(+2*&')05DV_`^\OEIYadeeedeefgfigfhiihhh_TTTUUVUUVVVVUUVUTUUVVWUVVWVYVWXXWY[[ZYXYXXYYYXXXXXXXWWWWXWXWXYYYYZZ[[[[\[[ZZYZ[\\\]]\]]]^]^^]]]]]]]^]^^^_b_aabeaa``_^`aaba_`a``b`_^^^``acbbbbbbbabbbbbbbbbcbbcccccceabaabbbccdccccbcccbbbbbbbdadgfedcdgffdedcccb`acdddccbbabbddddeeefffhggghdhggfeeeedcddefffffggghggfifddeghgeeedbccceddegfeeedbegdcegffdddceheedddddcdeeccddddeedb`aadecbca`_a_`dab`_^]]`^[[\]\]`_^]]__`\]]]\[[ZYXYZ\ZZ[XYYZ][\[Z\]]^\[]\[[[[Z[YXWVVUUUUUUWVXZVVUUUWXWWVWXXVUTSTTTRRRSRTTTSTTSSRRTSRRRQRRRTRRRRRQQQPPOOOPOOOQNPOLNMMMMLLLLLKJKKKJJKKIIJIHIHHIIIIHGGHJJJIHHIILIHIHHGGFFFGFEEFFWVVUVWWWVVVVVRQQQUZYYWZYYWWWXWSJB80)$")3<@0'('&$!"(-6EZaac\WHJZaffehggffhgghhjiiijh`TRTUVZWZXVVXUUVUUVVWVWUVWWWXWYYYXZZYYYY[\\[[ZXXWWYWWWYVVWVXWY[Y\[[YXYZ[^\]ZZ[\\_[\]]\^\\]\\^\\]\]a__^^``_a`baabb`^`^``aaad`aa__caa`_`aa`bcbaaaabbccbccddeffhdecbccceccaacdcedddfddbbccbeddabcddeegdceeeffdcbbdbaabddddccdddcdhdcddeeeefiihghiiihgieffgggfgffffgffhihfeegdcehgheeedccccdfegfdggfeddeedgfgfddddfeeeddfeecddddhdcccdddf`abdedccbaaa```aaa`^^]`^[\[^\\^b^]]c_`]_]\\[[[YYYZZYZ[ZZXXZZ\[Z\]]^^]_\\[[[ZYZXWVWWVUVUUUVWZVXWVUWWWWZXXWUTWSRTTRUTTTUUWTUUUSTRRRSRQQSQQQRRRQQQRQQQOOOOONNNNOPMNMQMNMLLLLNJKJLLLKKJIJHIJHIJLIKIHHGHIJIHHHHHIIHHIGHFFFGGEEEFVVVYYYWWUVVUSQRTTXXWWWYYZWVVUSJA92)# (1*$$*%#!%).9I]^aVSNHH[dfffgfggghgghijjjiig`SSTUVVVWXVVVTUUUVUUUWWVWXXYYXYYXXYYYZZZZ\\\ZYYYYWWXWVWVWXWWWYZYYWYXYZZ[\]]Z[[[ZYY[]\\\[\]\[[[\_]]]^______`^`aaa`__`_babaab``a`_a`aca`aabbccbaaaabdedcdeddeeedebbacccdcbccccddeeebccdddcddcbcddedcdcegfffedcbaaaaaceeedcddeeedddefdddeeegjgfghikhggffeffedgffceefffffeeeeedegffeffdcccddecdfggfeeeedecddegfccdfddieddcddeeddeccccgddeabbcedccdacb`__adbaa```^[[[]]]]^^^]_^`\_^\[[]ZZYZZZ[[ZZZWXYZ[[[[^]]\\\[[[[[ZYZWVUUUVUUVVUVVVVWVWSWYWWXXWVUUUSSSSRRSSSTSWTUUTTTSSQSQQPQPPORRQQPPPPQQPOOMNNMMNNNMNMNMNPNLLKJJJJKKKJJJJIIKJHJJJJIHHHHGHIIIHGHHIIHGGGGEFGGFFEEEWWWWXVUVVWXTSQSTVW[VYWYYZWVPLG@6/(" ##"(*/;OW\PG@<?G_eeegighiiihhhiijllkh`SRTWUVVWWWWWXWTSTUTTUVXZX\[]YZXXZ^ZZZ\ZZ[[ZYY]ZZYXWWXXWXXYWXYZZ[[[\]ZY[[\]\\\[[\ZZ[]\][\\\[[\\]\\]^`___bbb_abb```___`aaaaa``aa``aaaabcbgdcdddddeedfeedddegeddebbbfcdccbbcdddeeffdddgffeeeedcddeicddhgggheebbbdaabefigeeffefggfffedddddefggfghiiigfghffgcdheecdfifgefdeeefhfffffffddfddeeddefedegeiddedddfiecdfccefffcedeedddegccddddddcbcccbbbbca^_bcccdba`_^b\^^``_^`^b^`_`]ZZ[]YYYZ\[[\^ZZYYX[[][[\]]`\\[\[[ZZYZWUTTUXUUVXVWUUVVVWVWWWWYXXWWUUSTSTRSSSSSTWUTUTUWUTSSSSQRPRRQRQQPOPQOPSPONNMLMMLLMPMKKMOOLNLKJIJNKIJNLLJJIJIJJJJIJIIJHHHIIGHIIJIHGGGGFFGGFIFGGWXWVVVVWWWVSPNOORSTUWWWWUTMG?:0)# !#$$#'*-=CLD><5?AQbdefiihhiihhjihijkkkiaSRRVTUUVUVUUVUTSSSTTUVYYXZZZYZWY[[ZZZZZZZZZYZZ[[[[ZYYYXXXWWXXZZZY\][Z[[[[[[[Z]_\\Z[[[[[\Z[[ZY]^]Z^^_^_`_]__a_`````_^^_`aaabbba`abbabcccedeeedddeeefefddefhdddcbcdcaeccabcddefeeeeeffdegffeecfefebdehggggceabcbbccefhifefiffhifdeedddgfgggfeghighjggghfhddecdcdeddeeddfdeeffgigifedddeeeefeeeadefeecegfceeeeeeeaceegdcdddfeeeedddfdcefcbbbcecaaabaaabccddbaa_^^\]^``_]^^_]^^]\[^\]ZWZYYY[\[ZYYXWXYZZZZZ[[Z[XYYZZZYYWUSTUWTUUWWVUUVVVVXWVVWXYXWWUVSSSSSSTSSTUUUTTTTTSRQRRRRQPQSRQQPPPPPONSQONOMMNMKLLLKKKKKLMMMLJJJLKJIKLKJIHHHIKJJJJIIIHHHHJIIIIIIHGGIGFFGGGHFFFYXXWWWYXXXVSMIGKPQSSXWUSPJD90)" - - $%&$ "&',596556<DRadihhhhhkkkiiiiikkklnhaRQRVTVVWWXUTTUTTTTUVVVXYZ[YYYZYYZZYYZZZ\YZZZZ[ZZYYYZ[ZYYXXXXYYZ[Z\[[[\[[]^]]___^][ZZ\Y[][\[\[^]^\^^aaaa``_`a_aab`b^]^^`dbbb``bbedfabccdfeeegddddefddedejfgdfdcbdebaeeebbcddefieeehhjfefgffeceeffedeifgghedbcddeheffkgffehfgihfedffdcdceeeeehhkjkiggefffdeddfddebcedbcfddegghiggfeifdegfefeddcdehdffgfmdgfefeeeccdddcccddeffhgkceedcfdcbbbcccacadaaafddddb``^]^^_]^`_^^]_\]\\[Z[[\[YZYZZ[[[\^YWWWZZYYY[[YY[XZZ^ZZY]WVVWVXUTUVVTUVVUUXXWVYVWWXVWVUTTRSTTRRSUUTTTTTSRRQQQQUUTSTSSQQPPOMMMNQQRNONNNMLNLLKJKKKLLKLLKLKNKJIIKLIIJLIJJJJHHHIHGHHIIHHIIJHHHJIGGGGJILGIFYWXYXXYYY[WSLEEGFGIKORLHB=2(" """ - -#.,,'!""$%,189>AEJR_dggghhhgjjjiijjjkkllkhbQQSTTVVWWVUUTUUTWWWVVVWWWXXY[[YYZYYYZZYYYZ[[ZZ[ZYXXYYZYYYXXYYZZZY[ZZ\\^^]^^]]^]]]\[[[[\[[[[[Z^\\[^^a`ab`_``a_`_``_^^^^^ccba`_aacbbbccdeffecccddddeddeecfegbedccddbbdccbbbdeedeeffhhhhhggdfgegfgffdceeeffeedegggggfgkhhghhgggfggeeeedeedeeeegfffhhhffghiffddedeebcdddbfdeegghigfeefgedefffedddddeeeeggfeghfffeebcdefdbcdeeeffffcdddbccccbdb`a_``aaa`dfffcbab^\]^_]_ca__]_Z\[[ZZ[ZYZYYY[][\\\]ZYWXYYXXZZZYYYXYY[\ZXWVWWXVVTTUWUUUVUUTTUWUVVWWWVVVUUURSTSRSTTUSSTTTRSUQPRQTTSRRRQQPOOONMNNOOONNMMNMKLKJJJKJJKKKLKKJJLMKIIKJIIIIIJJJKIHHIHHHHHHHGHIKIIJIHHIHGGGGFFFYYYYYY[ZZZYUKDDB@?@CFED<4* #(+4:>843/& - -%07+(#!"%*.7>ELQWZbejgggjhhgiiijkjjkklnlkhdPPRUTXXWVUUUVVUTWWXYWWWWWWXXZ\YXXYYY[\YXY\[]ZYYYZ[YYZZYYYZYYY[[\[ZZZ[[]`]^___^]]]^\[[_\[ZYZZ\]]__^^a`aabbb`aba_a``_a___cbbaabb`cbebeeeeggfccdgdddhdedefgfheeefefdddccbcddhdegfefhhhhhgfffhffggfieddfddfeeifhgfgggghkihgghihgfifeehfiehefddeheeehhiffhjigfddefecacffgffffffhgffeddeeeeeeeekghggfeflfjhgfiggeeeeddfgedbdefedfifgffdgcdbfcbbbbba`````accdcccc`]]]^``bab`_^_]\[[ZZ[ZY[YZZ[][\\[][[YXY[WYXXXWWXYZYZ\[YXXZXWVVUUVVWXVXUVUUVVTTUWXXVVUTUVUVSSSSTTSSTTSTSVSQPSRUSSRRSURQPQOPPOMNNONMMLLMLOLMLKKLJJKKKKJJJLMMKJJIIJJJIJJJJJIHHHIJHIHIGGHKJJIIHGHIHGFGGGFZ[ZYZZ[ZZZZVNDA<:9:<<<6/%&*5.00B?=>>:8( - &*0+)%$$&.5@GMX[]^ceghfhhghhhijjkklmnnnmlkhdOOQSTUTVUUVVVVWVVVWWWWVXYYXYYZZXXXXY\ZXXYZ[[ZZZZYZZZ][YYYZZZZZZYY[\[[\]]]]]]^^^^^]]\[[ZZ]\[\^^]]]^__``ab``a````_^^_a_``abbbbbaaaabbegefgffddegcddeeddeffffeeeeddcccdfeedddcffffgihihhhhhhgbfjgffdeefccgeefddehiiiiiijhhhiiihfieedhgfefdddedeeeegfhhgiihhgfdfeebcdegfdfigghjgeeeeffceffffeefffffffffihgffffeedeefhfeecddeedefffefbcdcbddbabbbbaa`_aaccbabca___^^_`bbbb_]^]\[[[[[[\\YZZ[\\\\]][ZZYYXXXXXVVTXWXXY[[XXXYXWVVUVVXYWVUUVVVVUTTUUUVVUTTUUSSRRSTUTUSRSSSRRRQOPQRSRQRSTRRQPNNLOMNMNNNNLKLLKKKMKJJJIJKKKKJKLJMKJKIHIKIHHHHHIIHGIIJIHHHHHHIIJJIIHHHJGFGGHF[[\\]]][][ZXQF?9544563,!'*31--.370/6>/)(& %0.)" %)/9FWLXY[\cdhgjjjhhiijjkkkllmmnmmnnicNNPRUUVYXYVWVVWYUVWWWYZ[XXX[ZYYYYYYYYYXXY[[ZZZZYYYZ[\]Z\ZZ[]Z[ZYY\[[\_^_^^]]]^]^_]]]\[[Z\\\]^^]]]____^`c`a`__\`_\\^aabbbbccccabbbbcedefgfgeefgggeffgdgfifgddeedeccdhhhhhfedfffeiihhhiiilhhghhiffdhfedcffffeefhhhhihjiihjkkhgghffehfgffedeedfeefjghgghhhjihffeeeeeefgefgghiigggfffgghfgfgfeeffgffghgigggfffffdefhgffhfgfgedefffefbdcbceffcdbbbcbb`abcccbaa``_`^^_`fbbb^]`^\\\[[[[\][YY[[[\]]]^][ZXXYXXXXZVWXXXXZZZYXYXXVVVVWZXWUTUWWVVUTTUVTUVUTUVVTTRSRVUUTRQSRSQSQQPPQRQPPTTSRTQPPOOONRNNNNOMLLLQKJMKIHIIJKLLLKLMMMKJJJIHHHHHHIIKIHHIIKHHIIIKHIILIHIKHIHGGIHHF\Z[[[[[[^\[ZSG?84001/,# #&($ #(%"%*#! '*&!#%,<DSIEGGOW[^bffgiiikkkklllllmkmmllmjbMMOQTUVVVVVVVVXWVWYXWXZXXXXYZYYXYYYXXXWYZYYZYZ\ZYYZ[[[Z[Z[[[Z[\ZZ[[[\_]__]^]\]]^^]\]]]^]\]]]]]]]]^___^__`a____`_\]^``aa`_baccccbbccfddffffeeefffeefedgffddcdefdcbbcdefffeeegggffehhhmiikghegfffeeffgiiigeefggghggiijhhgjkkhgffffdeeedgfffededeefgghhggghkgffbeeeeeeeeefhiihgffffdffgefgffedefggghggggghffghgeeeeeeggfeeeeeggfecdbbccecccbbbcdc_aaabbbbbaa`a`_^`_ab_`a_^_`\[\]\[\\\[ZYZ\\\[\[[][ZYYZYWWXWWXYYYYZYYYXYVWWWWWXXVWTUUWVVVUTTUUTTUTTTTTTTSSSSSTRRQSRRQSPPPQPPPQNPQQRQPPOONONNNNNMNNOLKKKKMKIIHJJJIIHIJJJJJJIIGHGHHHHIIIIIHHHHHIGIHHGHHIIHGHGGFFFFEFF`_^\]\\\^\[[UG?82.-,*#!'")!&%#&((/;NE:*$*4@MU[^eelihikjklmkklpppmommibLJOPTUTTUVVVVVXXWWXXXXWWWWXXXXXXXWWXYXXXYYZ\Z[[ZZ\[]]]Z[\][_\^\[\^]^__^_^\\__^]_^]\\\\^a^^^_]]^aa`^^^^_`aa_]^`____`aacab`ccgffddbcdfddeffeejeggheeefeffheeegeecccbccdgeeeeejhigffhhgiiikkmhhfeefgihhgghjgfffggijhiiighgjkjhgffejefeedgddddeefgdefgijhggghffffffeedfhhhhhiggjfffffgfffffhfedefggigfddgggggkhhggggddfhedefefgkfedhdgcdeaccbbccdd`aacb``ea```aa^^^_ac`^`a`^]\[^^]]^]\[]ZZ[[[[]Z[^[Z[\ZZWXXXWXXYY[ZYY][ZZZWWWXWXVVUUVWWWVVUXVTTSTTSTTTSTUSSRSSRTRSSRQSQPQTQOPQQRQQPPPROPOONNNOMMORPMMOLKKJKLJJKMIKIIIKIJJKJHHGGGHHHIJJIJHHHJJIHHIIIKJJJIIIGHGFEFFHH]\]]]^^^^\[\SI@92.+*$ - #'" $',..021/&%09DSY_eiiijjkmllllpmmnpnniaKJPQTTSSTUUVWWXWWWWXWXUWWXVXXXXXXXXXYXXXYYZ\ZZZ[ZZ[\[[[\]^[[\]]]]\\]`^]]]]\]^^]]]]]\\\_^]^__^^^^^____^^`ca`_^`__a`ca`c`aaccefecccddddeeeeeeeeffeeddedfggfedeeecccccceeeejefeefhhfghhhhijehihgfeghhhhgfggggggfgjihijiggghiiihggceefdedgcedfgfffdeehiiiijhggffffgfeffhhhhgjgfgggffgggfcddfffdddffffedefggggihhghhfeeeedeffffghfedddfcccbfcabbbceaaabba`b```aba^_^_`ceaa`_^\\\\[[[^[[[]ZZZZZ[\[Z\[[\][ZWXYXXXXXYZYZYZ[ZXXWWWXVVVVUVWWVVWVVXUTTSSSRSUSRSRSSRQRRRRQPQQRRQPRRQQOMOPPPPOOOONOPOOOONNOPOONLLLKIJJKKJIJJIIIIJKJJIHGGGHHHHHJHIHGGGHIHHKJIIIJKIHHHGFFFFGGG]^^]]^^]^\^ZRKB:2-*& - - - !&*$"%*3::4'#"#-;NYcimjnlllmlmmpnppponlaJJOSTVSSTTUVVVVWWWWXYYWWWYYYXXWWXYYYYYY[Y[Z[ZZ[]]^\\[\]`]^[[[\\\]]\\\\\\]^]]]^^]\]]]]]]^]_`a_a]]]^_^^^_`a`````____```cafccddeebhcgdhefeeeeeeefehecdffffeeeeeffghdcccddeeeeeeeehggggjhihjghgfgkehijihhggggjhgffhiijjiiighiiiighhhhhgeeggifjihgiggfhinkkjkgjhgghgggihjiihghfehhihiiiilccegffeeeefeeefgfkghiiiihhhigfeefgfeegfiffdddgccbabcbbadceaabbcb`dababab``__`bba`_^^\`\][[\^\[[]\[ZYZ\\[Z]\_\^\^WZYXXYX\[[Y[ZZ[\YXYYXXXXVVVXWYWXWXXYUUTSSURTRQRUSSSRQQPPQQQQPUQRRRRRPNOOPQPPOOOPNOPQOMMNNMPOPPMMLLMNLLKKKJJIJKJIIIIJIHHHIIHHHIIIHFFHHIGIJJIIIKJJIHGFFGGGGGG]]^]]^_]][ZXTNF<5-*$ #$)'!#*2<<?0%! - -$<Sfikjmlllllmnnmnooonn`JJNTUURSTTUUWUUXVVUUVWXWYXWWVVVWWXXZZZYYXZZ[\\[]]]\\[\]]\\[[Z]]]\\]]\\\\\]^]\]]]]]]]]]^^^__```]]^^a_^^``a`````__^`a`_`abbddeeebccdcddeefhgfffgeeddddcgeeeegfkgfgccddeefedddddfhghgfhhhhhggfghiefihihihgghhhgfffgghghhghijjiigggghiiffgghfgihfihgfhjjjjkkhijhggghijhijjjhgfeghhhijihjeefggfhedffgfffgegghjikjjhffffefgfeeddcheedddcabbaab`a`dbb`acbaaaaab`baa_````aaa`_]]\]\\\\\\]\\\]\[Z[\^[Z[\\\\[ZXXYXWYX[Z[XYYZZZ[YXXWXXWYVVWWVVVVXVVTTUTVTQQRRRSTRRQQQPPQRRQPOORQRSSOOOPPQOPPPOONOPNMMONNOONMNNMLMMMLKKKJJLJJJIIIIIIJJGGGIHHHHHHIFIGHHHHHIKIIIIIJHFGGGFGEGH]^^^]^^^`\ZYYTLB80*$ - #1-)'(&!$)2:EJ<.*( -0Wgikjmlommmooonnnmnnl_JIOUUUSRSSUUTTTWVVVZVXWXXWVWWWWWXXYZZZYYYYZZ\^\`]]\]\__^\\[[\]]`\]]^^^\\]]^^]\\]]^]]]__]_``a``aa^^``_^_```abaa___ab```adbgedeeciefdbegefggggggfgdddcdheeeedeghhhccdeeeeddeefdggfhgfihhhlhghjihgffhihhhgghfgggggghihggkkjjmmnjjggikighkjjhfgjhiggghklkjkkkkjkjjghiiiikhhigffiiijkjjiiiijigfgeehgghihjihgiiijjjiihffghgffifddifeeeecbabcaaaccdccaabbaaa`bebccdae`a``ac`_^^]^^^\\\\]]]`^][[[\\[[Z\^^^\[Y\Y[WXY[[\ZZY[[[[ZXYXWWVVVVWWVUXVYVVSVSSTTSSRRRTSRRSQRSRQQRRPPQRRRRSNQQQQQPSSSOONNNNLMNNNQNLLPOQNOOPLJJLIJJJIJJJIIIIJJJJJIHHHHHIHGHGGGHIHGIIHIIIIIHJGGGIIJI^`^^^``_`]\\[[SNB6.& - $2@J8,&!%/4=ESI6*.$ - -8Zjjjjjjonnnnooponnook_KMORTTTUUTUUXTTUVWYXVVWWWWVWXXYYZZZZZYXYYYYZ\\\\\\\\]__]\\Z[]]]`[][[Y[\\\]_^^\[\\]a^^___``aa``__^_`_a`_`_aaaa`_``aa`a`adbdfddddedecceeffgghhigeecdgfffeedfeefgigddcehfcdeeeedgggkhfghhhihgghhhigegfgggfhjgjighihhihgfghjijjjiighijjijigjjhgghgefihiihjkkkiijjkfhiihikgghggfijjljhjkjijjihggggggghhghhgfgfiijiiigggiigghhiffffeeehdbbbbaaabccbaaaabba`aaa`aaaaaaaa``ba`_``^]][\\\]]^_`^\\_]]\\\[\\]\[ZZXZWY[[YYZYXYYZYZZYXWYVUTTUXUTWVUTUSSSTUTSRRSSSRQOPQRRSQRRRPPTSSRQQOOOPOPSSSQOPNNNMMNNNMMLLJLMNNMLLKJKJHJKJJIHIIIHIIIIIIHIIHHIIHHHHJHHIIIIHHILIIIHHHHHHHHG^^^^^```a_^^^]ZUN@5-%(.)!!%$-DXPE0$)-=BQRJ-') 'C]ljjlllnnooqpqpoonook^JFNQUTUXTRUTTTTUVXWWVVWWVVWXX[[\YZY[YXXXY[YZ[\\\\^[\]^][[\[[]^^`[][[[[[^____\[\]\]^^_^_^`_`aa`ab`___`c_aacaaaa``aaa```bccbdeegggeicdefgghegjigfeefegeeeffffjhggfeffefhcdeeeedfghihgfhihhhjgghiikhliggjjjifgiihhijihhgghjjjjiihijijmmjhiijlhfffedgghiijjkjiimkmhjijhhihhhhifiiijjkkkiimmlhhgigfghgfgljkfhfjiiiiifgihhggffgfffeeeeedbbbcbaaacbbabbbba``````a``bdbaaa`bcc_a__\`[[[\]_^a``^]]^]\]]]`]^]^[]XZZYYZYWYXWXXY[\[ZXWWVUUTTTUTXUTSUSUSTTTRSRTTTSQPORRSSRRRQPPQQSSQRRQQQNOPOOQPPNLMMNNNPLLLLKKLMMMLLKJJIIIJJJIHHHJHHHIIIIHIJHHIJIJHJIHHJIIIHIKJIIKLKIJJIHHI__`__b```aaa``]\TLC:2)*67:-#""" #,**=WUVC3 )4BFQ:&## -5Qkkkllllnoooopppopoook`IHLQUUUVTTTTTUVVVWVWUUXWVVWXXZ\[WZY[XXXYX[YZ\\^]]\\]^]]\[[[]]]a^[[[[[[ZZZ\[[[\\\\]^^_^^_bacb``cb`aa``a_aabaaaabbbba``adcccbdeffeedddefhghffhigigeefgfffeffgihhhghfffeecceddeefjhjhghhifgghggfiiihijhghjijhfjlijjjijihhhihiiiiijiiknmjhkkkkhgddfeefghhijjijjjjmhgghhiiihghighiijkmmmjijjighhhfgggggfghgeffgjiihhhgghgffefgfeeeeeeedbbcccaabcccbaabb``````caaaaaaa``````^___\\\[[]\^^^_^^]]_^^]]^^]]]\YZYYXXXZYYYXWXXZZ[VYXWTTTTSSSSTTTTSTSSSTUSRRRRSSSRRRRSSSTRSQQQRRSSQQQQPONNNNNOQQMMMMMMMMLLMMMMLLLLLKKJHIJIIJJHHGHGGHHIIIJJIJHHIIHHGHHHIHHIIIIJJJKJJJHIJHGHF``b`_aaaba``cba][VSIA4'#,9CLD9.))((*-1<N\aSC/*28;0%# - - %HeknnoonoonooopqoopqoolaIFLRUUTUUWTUUUUVVZWWUUUVWWWWYZZZYYZ\YZXZY\YY[[]^]^]___]^[\\a]]^\\[[]\\[[\][\\]]]\\]__a`_`aaa``accbbbcdbaabbbbbbccdbaabbbcgbceghlgffffgggggggggfiikghhggfffglihhhhjghfdccddcdefihhhhhhihghihhfiiihgghhhiiijjjjijljiijjjiiikjjjjlnkklmjhilmlghecdeeegiiijniliijmhggijjkkhhghggijjkjjjjijjhijhhgggggffhhgfffegihhgffegffdegghegdeefdedccbbbbedgdea``a_`a```ababab`a`b`b``____\\[[[]]^]^^^^]]___]]^_]\\]YZZZYZY[YYYXXZY\ZZXYVWTSTUTSRRSTTSTTSSSSSSRSSUTVTTRVRSSRRRRSRTRRRTTURSOPOOONNNOQNONNMPNMMLLKLMMLLLLJJKIHHHHJHGGJHGGHHJJKIIJJHIHJHGGHIGGIHKIJIJJKKJJJJMHGHJF``aaaaaabbbacba_^[YWKA0#19LLOC<61.-28@LYgbQA) - - -)2<2# - -4[ilnonnnppnnmpoooooponlaHFJQSUTTUVTUVUTVVVVVTUUUVWWXYYYZYYXYXXXXXYXZ\\^]\]]]]]\\[\]^^]][Z[]]\]]]^^Z]\]\\[\]^^^\__`ab_``dgcacdcdbabcbbbbbbcaaabcddccdehjigfddeffhhgigghdhfgghjhigfggjjhjihhffgeddddedefihhhgghjkihhhhgijhgggiiiihhjljijjkkiijjkmjhhhijkllkklmjjikjigiffdeeefhiiikiihiiighhiijmkhhgihhiijiijiihijjjjjighhhggighgeffffggggfffffgeeeefefdddeeddcccbccddddcabab`aca``_c``_``a`aa`____^^]\[ZZ\_]\^_^[\\^]]\\]]]\\ZXY[YYYYYXYYXWYYYYYVVUTSTUTSRQRSUVVVUTTSRRRRSSSTTTTRQQRQQQRRRQRRQSRRRQRNNNOMMLMMNNNMMMNNNNMLLLKJLMLLLLJJIHIIHGGGHHGGGGKIIIIIIHHGGGGHHHFFHHHIIJJLKKJIIJIFGHHGa`aa`abcbaabcbaab``\WK<'$1:GMKJDE;79<DQYdedQ@ )27% - %@^mlpnmnlpponnoprpqpqonkaGCJPSUUVVVUUUUTVVWVVVWWXXWWXY[YZZ[YXXXY[XZZZ\\\\\\]\]]\\\]^_^]][[[\^\a^^^`\\]^^_\\]]]]^`_bbcbbbdeeccccegccbbbbbgbcaaaabdedcgfihggifeeffhgggfghhhfhgggfghhhhjhgikigfffffeccfefgihhhhhhjjkhgghhijgfgilkkjiikjjkojjkijklkllhihjjmmrllmmjkkljiijjjfedhgjiijmjijihihihjjiijjiiihiiijjjlihhgikijjliijkhggffffggdeeghgfegffefeddgefdedeffdbdfdhdeeddcceccadcb```d`a`_aa```_____^_^_[^Y[[[]]]][[\]]\\\\\[[\YXYYYYXX[Z[[ZX\ZZZYVVUVVVSSRTQQSTTTVUUVSRQQRSRRRRSTSSSSRRRQRSRWQQPQRPQQNNOPNNNMLMNONOOPONNOOOLKLLMMMLKJJKIIHHHHHHHGGIIKIJJIIHHIGGGFFFFFFGHIIIJJIIJKHIJIGFIHHaaaaabbccaaabbcdba``[UC.%&2=ABBBA@>:;BLVace`L2#&- 1OllmmmnomppppopppppppoojcGHJOQTXWYVVVUUTVVWWVVWWWWWYXXYYZ[ZZYXXYYXYYZ\[Y\\]^]_]_]\]^_^]]\\\\]]a_]\]]]\\\\]]]]]]__]`bbbbdcccccbcedccbccccdcbaaaaaddddgeiggggffeffggggfffeheffgegghhhhhgghkkffeeeeedeffggkiggghhjjjiihighigghikjkkjijjjkjijkkklnklkfihjklmmllkjjjkkjjjiiigfehgjiilkiiihihgghhfjjjkihhhifghijiihhhiiiijjiiiiihgfegfgeddfgggffgfeefddddcdddbcdddddddgbbcdeddeccaaaaaaaa`abaa`_`aaa_]^^^]\[ZZ[[[]\\[Z[\]^^^\\[[Z[YYXYXWWWXXXVWXXXXWVVUUUTSQQQTQRRRSTVTTTTSQQSSRQQRQRRRRRSQRPPPORQQPQTRQQOOONNNNMKMOOPNNOPONPONLLLLNNNLKJIIJHGHHGHHJIGHHJIIJIIHHHHHGFFFEGKHHIIIIJIIIKHIIIIIIIHbbbbbcecdbbaacfcccfc_YK8*&)4@==?=@?=;@FN[adf]C( &FdmnmmnpompoproqqqqqqqpplcGBIMPRUWWVWXUVUVVWWXVWWVVVWXXYYZZYYZXXYYZYYZZZZ\\\^]^^^]]^^__\[]\]^_^`^^^^^]\a]^^^]\]]^___baadcccddeeddddcddeedddbbcbedcchegejijhgfiiiffgigffiehfeegfjghhjhhhhhkjffeeeeeeefifgijfgfgijjmkoiihhiihjijjiknlokkkkjkllkmnkjiiijmkkmmmlkkjmkjjmlkihijghhkiijjiihhgggghiijkmjkjiijhghhiiiiiliiikkjjkjjiigffgghdeeffggggggjgeddddcdccbbcdcdccdgdccgfhdddccbaaafaa`___aa`_`db`^^^^^\\[[\[\]\\[\^\\]\^][[ZZ[[YXXXVWVVYWVWXZXVVVUVVVSRQURTRRQQQTTSSVSTRQRRSPQSRRQQPPPQQOOPQSSVQRQQQQONMMNOOMMMMMMNNMNOOPOMLLLLLLLKJKIHJIHHHHGHIJGHIJIHIIIIIHHHGGGGGGHIHJHHIJHIIKKJILIJIJIccccdccccbbbbefccdeea\QB6,.4:;=?@ECADHLS[bcgZ<& - - 7_gnopoooporpppoqpppqsppqmcHAGLPRTUUVWVTTTVVWWXWWWVWVUYZYZYYYYZXYZZZZZZ[ZZ[]]^^^^]^^^]__]\\]^a_]^^^]^`][[[\]]]\]]]___aabbbbeccdfdddedddcddddddcbcdcbdeeeihhggfgiiffghhgeeefffdddfefhggggggijghfffffgffgfffeefefhijjjjihghigghiijjjlkjiiijjiijkkmjijljkkjjmnnmnkjjjkkklkjijjghhhhjjjhihhggfgihijlljjjjijihhhhhhhgggiijklkkkkiiggfgdeddefdfhghggggfefgeddccbbccccgdbdedddddddfcdbbaba``__^^^_a`````_^c``^\\[[[[]]]]]^[[\\^][[YZZZYYXXWWWWYWVWWWVUVVVUTTRSQRTSRQQRRRRRRQQRSQQRRRRRRQPPOPPPPPPQRQQSQQPQQQONMNPNNNMMMMMNLNMNNONMLLKLKLLKJJIIJIHGHHGHIHHIJIJHHIIJJIIHGGFFFGFJGHHHHHHHIHFGGHGHHIJddecdddcccdccefddegffa]NA8478:==BDIHMMSV^_b`V>(! - - - -*HbnnqooopoopqqpqrqpqqqqqpmdG@GKPSSTUUUUSTTVWXYYWVWVVWWZYZYYYYYYY[\[[\[ZZ[[[\_^_^^^`^^^a_^^a^^__]_^[^__]]\[[\_]]]^^a__`bbbbbbbbccddddeeeffeedddebbeeccdieihhghffhhgggiiihgfffheedddegggjhgfhhhhghigghiiigjffefgggijiikhgghhghiiilmkljijjkllpihjjjjklljkhhjmonlmkjkkkkjkkkjjmghhhillkjjighhgghhjkmmkllklkjkhfhihlhiiiijjklmmmjijifhggecdfeefjhhhfgfffffeeddbacdccdfbbcedfffdddcbaabb``a`_^]^_`a`c_``_`a_`]]\[[[^^^]^^[[[\^`\[YYZZZYXYXXWXWVVYXXXXVXVUTTSSRQSTSSPTSRQPQQQRRQQRRTRSRQQPPPQQQRQRQPPSQQPPPPNNMNNMNQNNNMNOMNMMNOORLLLMLMMMJIIJIJHIHIIJIHHMJIHJHIJLJJJJHIGFGHHJFIGGHIGGHHHHGGGLGIIdddcddddddeeeeedeefeed_YPGB>=9:;?BEHIKOVY[\]\JD?0" 0*% $=WpnnonnnpppnqsssrqqrroqqqndH@GJPQQRRTTTSTSUVWYYWVWVUWWXXYYYYYZZYZ[\\\]\[[]\[\\^^^_^]____^^^]]]^]^``___]\]]\\\\]]^_`a```abcbbbbccccddddddfgfdeeecdddddddeiggeffghijgigfghffeeffdcddfdgegigfhgghhhifggggiggfeeddddgiiihhgfggfeggimmjklkjkmlllkjjjjkjkkkkhfjlmnmolklnklkjkkjjihilijkljjjiiiihhgimlnmkkkkjkiihhhhhihhhilkjknlkljjjhghifeddefddghikggfefeeeddddcdcacbbbccfcdeedcccaa`ac`aaa__^_^```c^_^____`]\\[[\^]`^^]\[[\\\\\ZYZZZYYXWWWWWWXWVXYWUVVVSSSTURRRRRPRRQOOOQSRRQQRRRQQPQRPPQQQPPPPOOOPSPQPPPNNNNNOOONNMMMMMMMNONNMMMLLLMKKJJGHGHHIIIHIMHHIHHHHHIIIIJJIHHHFGGGGGGGGGIFGGGGGGGGGHIIfddefddefeffjefegegfgdfa_VTKE?<<>@BFFKMTW\^_`YXTM3 #AS>,! 4ToonnnooorrspqrpsrrqrrruttqeF?EJORQRTWVVUSTUVWXZWVWXXXXXXYYYYYYZYZ[]\\\][[\][\]]]]_^]a_^^^^^^]]^]___`^_\]]^]\]\^\^`bbba`accaaabgdddededddfffdefgggggffefghggffggijhhhgffgggifefddfefegdegihifhhhhhgigfgiihfdebdeefgiiiggfgeeeihhjmjimmjmmmlnlmlkjkjlkjiihjklmmmnmmljjjjljmjihljijiijigghjjijjjknmmjjkkjljfhmjkhjhiiilkjkllknjhiihhhffedddcehgjggggghfgfgdedcccbccbcdcfdfdddbcdaa`______```]]^``b___`ab`_^`\\[_]]]\\]^^^^]^\\[\Z[XWWWWXXXWYWWVWZVVWVZUTTTTSRSTSRSRPOOPRRQQRRSRRRRPQQQQQPPOMNOONOOPOPOPPOPNMMPPOONNOMMMONNNLMMMMMNLMLKKJJLHLKKIIHIJHHIHGGHHJIKKKIJHGGGFFGGGFHHGIHHGGGFGGFFHKIgeeeeddefeefgffffeffheedb^[XQKHFEFIKMOTX]`becba`VJ6%6VTVC6/*+-3:NlnnnmnqqpporpqqrtrqrssstuuseE>DJNPPRUVVWWTVWVVVWWXXVUXXYVXXXWZYYYYZ[[[\\Y[\\Z\_^\]`^]_`^_^]^a^]^\_^```a`^^^^[\[[\^aabbbbbbcbaaacddddcdeedeefdeffeffggecffgghgffhhjghgggffhghgfeddddedddfggggghhhihghfggiihfeeffffgghkigfeedfefiggjijllkmmmlmlkkkkljjjihiijkmmmmmmllkjjjkjjkjiigikjhjihhjkjjkkkkkkjiijkjjkiijjigjhhkikjjjllkjhhiiggggggjgedeeeeefffegffffddccbbcdeeedbbcdedfbba_`__^^]_ca_^\\\_`````_aab`^]]\\_]]\\\]^]\\\\\\[ZYXXXXXXVUXWYUVVWZVVVUUUVWUUTSSTSQRRQQQRRRQPQSSQPOROPPPPRPPOOOOOOOOQPSPOOOONNNOOOONNNNNLNONNLLMOMNMKLLLKJIKGHHIJIHIIIIIJFFGGJGHHHHGGFEFFGHGGFGGFHHGFGGGGFFGHIIgfhggffgjedefghghhgghgffged_^[XURRSUW\`agehhhhiebVQ<+3N^f_ZTOMKQVZimsorooqrpsrqrrsustsstvvwvxteE=CINOPUUUUTTTVUVWVVWYWVUYYZWXXXYYYZZ_Z[Z]\[[[[\Z\]_\]]^]__^^_]]]]]^^__`__``_^__]^]]\`abbbbbcbbdccccdeccddeeegffffefedeeeffffihihgghijiiggghgggjgeffffeeddegijghhhhiinhkhijjhjfeeefighhhiifeeeeffeefgiilkllmmnmnmkjkllkjjihljjlmmppqmolmkmkmkkkmjjiijmhkkikpmnllkmlljiijjnkjjliiihhihgiiijihjmjkiijlgggjhhhgfeeeeeeifgfhfhffedcdcccfdddeccddcccaaa_`___````a_^^`_^__`b`__``a^]]^\`]\]]]^]]\[\]\[[YYXXZYXVVVXXYVUUZWVVVUUUVVUTUTUTSQTRSQQQPPQPPSRQPPRQQQQRRPPPQOPPPPPPPPPNMMMMLLLLLNOOONPLLMONLLLLKMNLLLMLLJJHHHIIIIKIJHHJFFFGIIIIJHHFFFHIIHHGFFFGGFGFHGGHIGIIHHffgigfghhffhhighhgghhiihhgebab_\]^__`abdffhiihggc]XSB4=J[khgc`_\`chknprnooppposqqtsusrsstuuvvvvveF=AIMOQSRUTTTTVUUUUWWVVVVUUUVXWY[[[ZYZZZZ\^[[ZZ[Z[[\[\\]^^]]\\]^__]^\_`______``^\]^]\``bbcbbccddccdcccbcddfegffffeefffjgeeeeeghihhjhiiihffffffggefgefffefeehihghhiiijjfhiiiihjfeeefffhjiiheeffffffefhhijkkllmmnnnllkkkjjlkhjiklmmonmklllkkjkkkmlljjiiiiklmnlllllkllkiiiihijjhkiiihhjkkkiihghiihijjljgghgfihgfedegfefeefffgfffffdcdcfdccccb`bbcba____`^__^__a^^____^_bb_^^`__^]_\Z[[]\\[\[[Z\_^]\\YYXXYYXWWWWVWWUVWUUUTTUVUUUTTSSSSQRRTPPOOOQPQRQQQPPPPOONPPPOOOONPOQOONONLKKKLMLLLNMMMNMMMMMKLLLKKLLLLLLKKJJHHIIIIJHGHHHIGIFFHIHIJHGFGGFFGGHHFFGIGFFFGGHGGFGGGHhgggfgfihfggggfgeghhhhhhjhhgfffeeghgghjjiimmljniidc]ZVT^kkljijjjjllnsssoppsqqqrqqttsssuuvuzwyyxsgF=AHNOQTSUTTUUTUUXXYWVWXVVVVUYXYZ\\ZY[Z\Z[\\ZZ[^\\[^\[\^__^\\\]``_^____^_``_^_^]]]^^^a`cccceceddcdddbabcbcdceffffefifggghhhhglllhiiiikihhigfeeghffeeeeffeefhhhhjiiijjkihikihhjhfeddgfjiiihgffgffgfghgihijjjklmmmnlkkjnjjkpjjimlmlnmmlklmmmjkkllkjjkljijnlomllommkijjjiilhghkhjijjljjkhikihghhijnkkjihhhhfihgeeeeefffeigfgjfhgffffgegcddedebbceabbc_a^^___`_a^_`b_`__`b^_]`_^^]]\\\\][[[_Z\Z\]^]][ZY[[ZZZXXXWVVVUUWUUUTTTUUUWTTRRRSRRSSPRPQQRQQRQQPPOPRPNNQPROONNNPPQPOOPMKJJLNLLLKLKLMMMMMMMMMMLLKKLLKLMKJJJKKKKIHHGGGHKIHIGGHIHJLHJGIFEEIIJGFFFGHFEEFGIGIHHHHHhhhhhhgggegfgghhhgihiiiijiilhhhhhhgfghiijkllliiiijfd_]akklmnopllmpporqqopqqqrsrqrstrtuuuuvvvxwxphE<@EMPQRRRRTUUSUUWZXYWWWVWYWUXYYZ][ZZZZZZ[[ZY[[\\\[^\\\\\]]]\\\\]]\]^^^^\`__^_]^^_____```ccccbacbcccbbdcdceddfgffffffffggggggiikfgfiihiijiggfffffeedeffffgghhhijkjijjiljihhhhijgfddfffgikihgffeeeghhhhjjjjjkmmmmrkijjjjkjjjjijmlllllmoomlljjjlkjijkkijkjjkkkjollkjjjiihhhggihhijjllllighhhgghiklkjiiihhgeggfeedeeeggefffggffgfffedcfaddeecbcbbaa_`__^_`_^_`b__`a`___`___\^^_`^\\\\]\[[[\ZYYZ[\^]\[YZ^ZWWWWXWVVUUTUUTTUTTTUUWTTRRSRRRRQPQPPQRPQSPPQRPPOPONNMOPNMOOOONMONNLLLLLLKKKKJJLLLMLLLKIKKLNKKKLKMLJIJJKKJKIIHHGGHHIIIIIIIIJIHHGFEEEEEEEEEFFFFEDFGHGIJHHHHiilijhligeigihhhkiiijjjjkjjihimikiggkihjkkklmjkjjijhfjmllnpomnppoqsrrruuurrstssstssstuxxyvvvxwwshD;>CKPPRQSQSTTTUVWZXXXXZXXXXXXYYZ\[ZZ]YZ[[[[Y][[[\\^]\\\\\]^\\\b]^]]^^^__a___^^`^^____`aacbbbabdcecbbccccbecehggfggfgkffffgiiihjgigjihhjiigdfeeeeeedehgffgikhhiijmikihjjkjijhghgfdefgghkkkiihfghhghiiijjijikmkmnnkjmlomlkkjjjijkmmlllmnmlllmmmkjiimmkllkjnkjjolmkkkihhgghghkjijkkkkijjiiihhhikkjjiihhhgfeggffeeeddfgedefggffgkhhfddefgeeebbdbcb`_b`ba``a__`bab`cab__]]_^^_^_`a``]\\\[[[]YYYYZ[]_\[ZZZYXZXXVVVVUWTVVVSRSUTUUXTUSSQQQRQPPRPPQRQSRPQRRPONNONNMNNMMPONMMNOOOMLMPNLLKLKJJKLKLLMKKKLKLKJKKJJKKJIIJJKKKKLJJHHHHIIILJGHHIJJKGHEEEFFFFEFFFFGHEFGHGGHGHGGiijigeb_begghhhijiijjkjkllljkkjijjihiiijjjkjkjkkkjjkkkklmoooljklpqrprstusqsttssttttsuvxvvuvvvvwvfC:=CIMOPPPRUUUTVYXZXXXXXXXXXXXZYX\ZZZZYYZZ[ZYZ[[[[\\\\[\\][]]\\]]]^^]]^^______^^]^a```_`abgcbbcccdbccbbccdeeeheffgfghggfeffggihgfffggihhhigffeeggfgggghhgghhghjiiihijijjjiijhhhggfgghhiiijiiiiijjihijiiiiijkllmmmljjjlkkkjjjjjjkmmkkkmnnmlmmmmjiiknmjjikjnijkmlllllihhhghhhjkjllkkkjjjiiiiihjiiihijhgheeegjhifeegdffeeeeghhhggggfeeeffeeecbddba`_a`baabba``a```aa`_`]^_^`^]___^^]\[\[[[]ZYYZ\[[^\Z[Z[ZXXWWUVWUTTTTRSRRRSTTTTTTSQPPQQOOOPQPPPPPPPPQQPNNNONOPNMNNNMNMNOMLNMMMNNMNNNLKKKKKKKKLKMKKLLKLJIIIJJIHJJKKKJJJIHHHIIJIJJHHHHHHHGGEEEFFFGGGFFGGFEFGGGGGHFFHjikifZVZ_hjimiiilkmjllnkomnkjjijlkkjjjjknlllnklkllmkkklmmmmnnjdjoqrrssttsrsttsstvuwuywywxwyvwwyveC9<BJMPPOPSUVVWXWWXXW[YXWWWXXZYZY\ZYY[ZXYYZ\ZZ[\\[[\]\[\]^[^^\\^^^^^]_^_`a___b_````c``````cdbcbbcdbfcbcdcefeehffffgghhgiehfefihggggggjiiiiffddehfgiijjhhhhhhhjjjiihijiinjjiihhiihjffhjiijmiiihilllikkijjjjkjklllmniijkkjjjjjijklmooollnpmlmpnnkjknnmjijjjnijknnnlmlhhghhiihhkkkkklmljjjkijihiiikijjjhiefegggghfffefffeedgkknghgjhhfehfffedddbaa``bbbbdceb``a`d```a_d^^]]^]]_`_^^]\\_[]Z\\[Z[\[Z][ZZZZZXXWWUUUUTTTUSTRRQRSTTTTTRQRSRROOOPPPPRPQQRPPPQMMMOOQPMMOONNONPOMLNMLMMLLMPNMKKJJKJKJJKKKLLKJJHIIIIJKILKJKLKKJIHIHJHGHJIGGIHGHGGJGGEFFFGGHFFIGFFFGGGHFGFFFkjjhcNKKaijijjiikkkjkkkkmkkkjjjkjjjkjlllmmllmllllmllmmnonmle[T]ipsrusrsrsstsssttuutuwvwwxwwvwwvteB8<CILSOOPSUWVYWWWWWW[ZXWWVWXYYZZZZYXZ_YYYZZZ[Z[Y[]]]]\\[[[\^]\]]^\]]_^^_____`_````a``bbbaaabbaaacbcddccdeffefhfhfhffgfeeffdefegihdegffggffgddehggjighihhhhihjkkhhhiiiiihihihhhhhhghhjiijjijkjjkjjjkkkkkkkklmlkkikjjikjjjjijhjjllmmmlllmlmnnnmkjmlmkkkmljjjjjklkkklihhiijjiikigkiklkjjijijihhiiihhhihgffefffffefjhgggfdefhiihgdijggggfddeeddbbdb`aabbcdcbbdbaa_`````^^]^_______^]\\\[[Y[^]\\\[ZZZZZYYYXXWXTUVUTTTTRRQQQRSSSSTTQQSSSRNNMOOOOROPPPPPPPLMMOOOONMONNMNNOONLMMMMMLLLLLLJJJKKJJIILMLLKKKJJJIHIJKIJKKKKJJIIIJHIHGGHHGGGFGIHGHGFFFFGHGGGGHHHHFGGFFEFFFFnkmj_F=JclnjkkiijinmmmlmpjkkjjiiijkkjkmklllmonnmnllnooopolkaJJUjpsqrssuvuttsttuvvvvu{wxwzy|xzxvudA7;DGKONOOSUWVVWWWXXX[ZZXXXXXYZ[ZZZ\[[ZZXW[ZZZZ[[[^^^^\\[Z[[^^\\^]]^]`^a^^^aa`a````b``aaa`abbbbaafbbeecbdgfffefffefgggeddeeddfehghddeefghgfhdffjhhhjhfgiihhkijlkhjhjjkjjiiiiijiihhilijjijkjjjjkmknnlkllllnmpnnlnkplkkkjjjkkkkkkmmnnnlooompnmmllkkjjjkjlnjlkkkkmpkjkkjiilkmkjihhliikjjkkkjlihhjjigghhhggffhfefjggiighhgfffghiigfigggjfdeddfddccdgcccbbbcaabccab_c`aaaab__^_^`^^]]]\]][ZZ\\]\_[\[[ZZYYZYYYXYUUUWUVUTSTQQRRSURRRSQQRSRQNQMOOQPROOPQQPPPNNNNNOOPMMMLMPPPPPNMMMMMLNLMLLLNKKJLKJILLMLLKLJKJIJIJJJKKMKJJIIKIKHHHGGGHIGGFFHGHGGFGHGHHHHHHHFFHGGGGJGFFHFlkjl]@>Jfollf`bdccgjhgmnmkgcdeddefhlkkkkhdfgghjnmllolkjijjc\D<UjqtsrrtvvuuusqqomllqsvwvwyyzxzzwucA7:DGJONNPTUWVVVUVXXXXXYYXXXWXZZ[[YZZZYYXX[ZZZY[]]]]\]\\\\\\]]\\]]\]]^^_]]^^_a`aba```aaaaacbbcdbabbcdccdfgfeefgfeeefgfeefegefeddefeddeffffeedefgggghhggiihghhhhhhhikiihiiiiiijijjiiihjkjlkiiijllikllllkmmnmmmljjjlllkkjjjklklkjmmnnnklmmmnpmmmllkjkkjjmljkllllnmkjlkjjjlikkjihhjkjjjhigkijihihfgggjhijjihhefeiiiihghhgghghkijhfghggifffdddccccdgdcbbbbdaaaaa``_`^aa```^__^]]^^_^\\\\\[Y\Z[Z\\\[ZZYYYZYXVVXTUUUUVXTSURSTRSSSRQQQQQQQONOMONONNOONQRQRONOONNONNMNONMMMMMONMMMMLLLKMLLKKLJIKKJILKLLKHIIJJJJJKJHIIJLJHIIIIIHHKHGGIHGGGGHGGGHGGGFGHHIHHHFGHGGGFGHGFEEmkolZ<8LigiYSWXZ`ce`^enll^YYZ]`bcdeimgd_\^behggfemnkkfbehebK9<Vjsokmquyssokmonnjfipuwwyyyyyxyyyua@68CFJNNNQSUTSTUUUWZX\XYYXXXWWYY[\[[ZXYYYZZZZ\YZ]_]\\^]Z[\\^]]\\]a\_]]]_^a_^_babbabb_ababccddcdbbbbedcdeegeeefggggfffeeefeeeeedfeiefddgfeffeddejhhhhijgkhighihggghilihiiihikjjikkjihhijikhikhhjkkkkilnmpppnnllmqmmlmllkjjjkklkkmmqnomlmollnnmommkkkmkkmljjllllllkjlmjjklmmlnighkkjjnjjillmjiggghghhjjijkihgghiihhhhigggggihghkhhhgfhfefddccccedfddbcccdaabac`aaa_aabab``a`^]^^^_]_]_\[Z\Y[[]\][ZYYY]YXVUVVUUVZWWVTUVRWSRSUSRPQPPQQPOONNPOPNOOOPOPQPOOPOOOONNMNNOMNMMNPMMMMLKLMKNLMLKJJJJJKKKKMKKIJIKKKKIIJJJHILJJKIJHHHHHHGGHHGIGGGHGGGGGIHIIHHHIIGHGGGGGGHFFFFkkllW98QfdTIRVXSVTQNXdijWLMPWRQPRU]gd^TN^TXWY_[ZfoifXTZ]][SB8>Vidafjrvqmb_dhec]X^evyyxyyyxyyyxytb?58BFKMNOPRSSSSUUVWWWWXXYXYXWXYYZ[[[YYZY[ZYZZYYZ\\\\\\][[\[\\]_\\\[\]]]^^_`````a_____`aabb`a`cdcccccddfeeeeeefffffffiegeeeeeccdcdedddcgfefhedeeghijiihfghggghhggghhhhhljjiijjjiihikihkkjjihkiiikkkllmmmoopnnklmlklllllnnmkmmllkllmmnonmmjlmnmmomkllnjllklkmllllmmmlkjkjjjjjjghhkljkkjjijkkkifgggeifggjijjjjjjiiilihgggggggfghiiggggffefeddcccdccccdbaddbca`a`aab_`baaabcc`a^]^^]\\\[YZ[ZY[[[[[ZZYZVXXXWVWWXWVWWWVVUVRSSSRRRSPQPQNPPPQNNNOONOMNNONOPONPPPPOOOONNMLMLMNNMMLMMMMLKLLLOKJJMKJJKJJJJKIIHHIKJJIKIIIILJIIIIHHIHGGGHFHGIGGFFFGHGGGGHHHHGEGGGGGFGFGHHGGGlkjkU78TeOBJ]_d\WQDHTijRCHQ[gZPHAJXa`LER^\YVNJHWihdODQV__\QE9@VjZSRhurnYP[^ea_TVZl{yzyzz{yzz|z{tb>36AEJMQQPQSSSSVUWVXYYX\XYXYWWXY[[ZZYYYXYZY\[ZZZ[\\]^^\[\_[[[^^\]]\[\a_^^_``a``a__`a_a`aacbdbecdceegefdcehgfeeehgggggedeffddcbdccfcddccceffffhfegkijihfggffgiihhhjiihhhhikijikijiiiijkkklllkilijjikmmooopponmmmlkmlmmnmmllmonnkklmmmmpnlllnsmlmolmlnlplllklnmnmmmlkkjnlljjkkkmjkljkklmkjkojiiiikgjhhhjjlkjjiiiijljihhlhjfhfghhggffgjfdeefeeechcdccebbdebaaabbaab``baa`_^```_^^^]\[\ZY[\ZZ]Z[[[Z\Y[XWWXWWXXXYYYWYXWWVUUSSRRRSQRQQPQQRQNNMOPOONNOPPPONMPPOPOONOOMMLNNMMOMNNMLLLLLLLLLLJJJJJNKKJJKKIJHHIKJJJKIHILJIIJIJHHHGGIGHHHGIGHGGGFGHHHHLHHIGGJIIGGFHGIHJKJHnmkiQ5:VPA?N_ife\OBC^lU@AI[igdVD=?ZeM<I_diebP@DVkfO<FS`mhgZM;AZmTAMflmWFQ\gqmlb_hy{zzzzzzyz{{{wsc=25@EJMORPPQQRTUSUVWYXXXXXWXWWWX[ZYYZYYYYYXXWYYZZ[]^\\\[[[Z[\]]][]^\\__^^`a`bbc`__```````a`aaaabcdeedddddgeddfeihhihgffeeeddfddcbcacbccccefffggeehiihffghggiiiiihhhhigghjkiihiiijiiijjjjkkkkiiiiijjkkmnnqponlkkjjkkllkjkklkkjllllkkklnolkmoollmmklllklkklllomnnnnmklikkkjkkkjjhkmjkllmmmkkijjjhggiiijjklkjnljjkklkkihhfgfhgghggghggffffeeeddcccccdecbdccba`bbbba``bbb`__```ab_^]^]\\Z\_[YZZZ[[Z[XZXXXXWVWXXYYXVVTUWUTSSSQQQRRRQPNQRQQOOOPOLPOQQOPOMNNNNOOOMOPNLKKNMMKLLNKLKLLLMLMMKLKJIKLNJKKJIIIJHIJKJJJIHIIIHIIJHGGGFGFFFGHHHGFHGGIFGGFGGHFHIHFHIHFFFEEIHHGHIonoiM4;VO=>VnkkieI:Jj]J<?WokjfcE7@[O?=RbnllcT@B`nR>;J_nmokhP>A]\OCIiqVCEUhzwvqovzz~{{{|||||{{|vsc;14?DIMNPOPRQRUUTUVWXXXYWWWZXWVVVWXYY\Z[YXXYX[[^ZZ\^\\\[[[[\\^]_\]]]]]]^_`aabbbaabaaab```c_aaaaddddeefehefeeeffihhhjeefedggfeccdcbbeccceceehhgeeefghffffhhgigggghhhhggghijiiijikkljlhijjlmjkjhhkilkmkkklmpppljjijkkllkkllmmmkllmljkpnnmlmonmmmmmllljklkkklmommmnmmmljjlonnkiikjljhjmkklnllkllmigiliijllljjmqklkkljkkggghiiiihjhgggfgggdcdhffdbbdddcccccebaaccbc`_`ac`a`b_``ca`^^^_]_]\\\ZZZYZ]Z[XZXXXYWXVWXYXWVUUUTSSRRSRQQQRRQQQRSRQPOROOLPPQONNPNPNONOOROOONMLLNMLJKLNMNOOMLLLLMKLJIILMMKJJJIIILIIJJHHHHHIIIHIIKHGGGFIFEFGGGFFFHGGGFGIFFGGHGGIHGIIGFFEFIHHHHIomoiJ4=VC4DcnmorfFDR`\D5JhnomicE6B[J>>[lnooeK7Eh]G;=VqpqppkS>@\`L;Ii]HAG`xuuvvvwz|}|}|}|||}~||ytd:02=CHMNOOORRRTTVVVWXXXWVVVXYWVUUXXWWYYXXXXYY[[[ZZ[[Z[[\[\[\^\\]]\\]_]]^_```bbca`bc```__``^bb```acccefeedeeeeffifghgeffeegffeeedbcccccedcddeffeeffegeffghgfghgiijhghegghijkkmjijjjiihijjlkijiihhhkkjjihmklllkkjjmkllllllnmllklmlkklllmlmpommpmonmlllllkllmmmmmmlkmmljkmlkkkjijjjiiijjkkmlkjkllliilijjkijikmmkklkmkjiggghgigffghgfffggfdddefeebabbcccdcba`aba`aba__`b_a_^^`aa`_^___]]]\[\[[ZZZZZ[XXXWWWVVUVXXXWYVVTSSQRRRRQPQPQRQQRRRQQORNMMPNONNNNNNNNMNNNONNMLMMMMLJKLMLLLMMMMLLKJJJJIJKKMJKIHIILJJKHGHIHGIHHHHIIHHHGFFFFGGGGFFFGFFFFGGGGGGHGGHHHHHHGGFFIGHJIKsoohG4>BA=JbmprqeIEZqY=DYgurtldC6E\F8CdjrpqcE>Ig`>4GiptrspoQ=@[aJ@Ii]B9Qrwyxz{|||{|}€}€€€}€~~}{vc:/1:DGKLMOOQRSRSVWVVVVVVVZVWWVVUVWVVWYYYWXY[YZZZY\[\\\[^]^\\[[\\]\[\]]]`_b`_aabaaea_acba`__ba`_^_fccefeecgedfggigggihhfefhgffhfddfdcccccdcccdeffghgghiijhgffgghiijghglhhjkkkjijkkkimmkjjkkjlhoiihkkjiijmkmnmmmkljilkllkkmollmpljjjjjkllnnmlkmmoqnonnmmlooommnnmlllllllmmlllmknnmjjiijokmkkklllkjjlmmjnjlmmmnjjlklmjjhjjjhhhigggifdeefhfeeeedcbadddccbbcbaaaa``ac___c_a_a^___`b^a___`][[\[\[\]]ZZYYZYWVVUUTUUUUWVUTUURRRSRUPRPPPRRRRSRSSRNMNONOOOOPNNNPNONNOMONLLMMLLLLLMLKLKLLLKKJIHIJJKJJIHIHHKLLJHHHGGGHGIIHHHIMJIHGFFGGGGGHFFGFFEFFHHGGGGGHHHHIIIHIGFGIGFGHHqppgE4E[G;Kapv{„oNQiiYLJ\hsqrsaA;GPEEF_hqqq`B:Nh^9=QgpqqqqmQ9?^dH:IfW9=]pwwwz}{{|}}}}}|}}}}zxa9/1:EHJLLOOPRQQRVVUUVVVWWVUVUUUUWVVVWXXYWWZZXYZ[X[YYXYZ\[[]\[[[\^\[\\]\^^_``aaaaa`__a`^____`a`___bdegfdddeefggggggeghheeghifeeeedddcedccddedeefhhggghiggghggghjiijfffhhilkkkiiijikijkklkjjihhhhihlkjhiijkkkkkkiigilkkkjklmmkqnkkkkjklllnnmmmmoppnnnnllllmnnmlmllmlllkkllllmmkkljhjkigjkkkklkkkjkjkkjjlkkkllkkkkkjljihhghhhghhhhhfgiggggfeededcbedcccbcdbbbbba`^```_`^^^^]__^`_^__`__\\\[Z\Y[\\ZYYWUXXVXVUUUUVVWUTTRRRRSSQQPQPPORSSSRRQPOMNOONNONNNNNLONNNNOONNLLQLKKJKLLKLLKMLLKKIIIIIIIIJHHJIHIIJJIHHGFGGGHIIIIIJIJHGFFGGIHHGHFGGFFFGIGGGGGFGGGGIHHHHGGHHFGHHHqqs`C>JXQLL`tz…†yPSlzhSTajopsp_:5L`L?D[honqX?:Ui[>=OemoprqlH5@`bF9IeRABZns{y~|{yuwy~~‚€€~|y_8.1;DIIKMNOOPQRRSTUUVUUXVVUVTSSSUVVVVXXYVVYZXXY[Z[XXWWY[ZYZZ[]]]^\\^]]]]_^``abbaab`__``__^```a```acgfffggffhhhghhifkhgeeeeedefegegecegffdefgeffkhggggkknijgghiiljkggfgghjjjjjkikkjjjjijlijiiiihmmljjjjggjmlnlljiiiijknmmmmlllkkklnljlnmooomnorpponnplnllmnnolnnnoolslnlklonmkkkihjiihjjklmmkklknkjjjkommlmmkklkkkljiiihgijihhihhhiigggggeeegdcceccccdddbbbafa`^`````_a_]]__^_`^b`a`_]`\[[[YZ[[YXXWW[XVVVVWTVVZWUUURSRRQRPQQQPPQRRSRRRQONMOONNNNNNMNPOONNNNNNNOKLLMKJKKKLMMLKKKKJIIIKIHHHGIIIIJJJIIJLHHGIHKJIIIIIILIJIIGFGIHJHHHFFIGFFGGGGGGFGGGFFHHFGHHHHJIJJIIrqqYC=Sk\RV_o€ƒgMVozƒgWasponmS57P\]METiomnQ<:[icCCMailmorY?9Be_E:LdUACWkpyz{|ohmq|€€€€}|y^7-09AFIJONQOOPSQRTUTSTTTUUUTTSSTUVWVVWWVVVYXXXXYXXXXXXY[ZZYZZZY[^\]^]]__^____`a`aa``^`a`````aaaaabcefffhkfgfffgfffeeffeeheeedededeffffccdefeeefhiihggjjjihghhhhkikhgggghiiijmjijkkkjiiiiijhjkjijljhhhiegikklmlljjiijkkknolllllnlllmlomkmlllnllmoponmllllmnmnmlklllllllmkklllkklhfgghhijlnmqlklllkjjjjllllmmkjllkjkljljjjijiiggghiiihghhgfffgccccccbbdcbbcc_`a`]__`_a___^]^_^^`^___^]]\\[ZZZZ[YWXXWWXWVUTTTTUTVVUTSRRQRQPOOPQPQRQRRQRSQOOMLMMMNPONNNNONNNONMNNOLLKKKKMKJKKKJKKLJIIIJJGHHHFIIIIJHIGHHHHHFGFGEIKIIHGGHIHHGGFGGHIHIGFGHGFGGGFFEFGGGFEFGGGHHHHGHHFIJsrp\ENdjkfaelxr\VYsƒƒzjcholk\H4;Vib[TQ_olmM:GahiSFK]dnml`N94IjaDCQbfPFRinwvvl`Z[o€€€€‚€y\5+.7BDGJNNNONPPPQUTTRSSSSSTSTTSUUUXXWYVVVUVUVVWYWXXYY[[ZZYY[ZYZZ[[]^]\\^^a``^_``__`aaa```cbbaaabcgeefffhgffgfffffgeefifeeeefdeeeeeedeeehffeeeffghhghhliiihhhhlkjijihhighijijjjiiikjiiihhhjkkkkljjihkgiilkjlklkkjjijilkknmllnnnnnlkmmmmkoklmnllmrppnnmllmlmmrmklmlmmmmlllllklkkkgghgghijnmmmlknllllkjkklnlplkjlkkjijikiihhjijhjhhikijhihiffffbfdeccbcccaabc`]_`_b```a`a_`]]^`^`^^^^^]][[\\[[ZZYXXXWXYWXTTSUUVUVVUTSRQRSQONOPRQSQPRTPRSQOOMLMONNOPNNNMMNOONMMNNOMMLMMMLJKLKJJJKLJJJLIJGIIIIIIIIIJKIHHHHJGHIIGHIIHGGHIIHHGGGGGIIIIIGGGHHGGGGIEGGIFGEEFHHHHJHHHGGIIssra_\iqnkmnpssi`Wex€†~ynegfjWG5>Zgjfc_^negKKOcskaWM]^cd_VM8BRbcJEYqi]TN]knsh`YQap~€€€€€€wZ3*-7CDFHNMMMMNOOPRRRQQRSRRRSSSSTUUWVUVVVXVYWWVWXWXYXXYYYZZXXXXZYY\[]][[\]]]^^^_^^___`a__aaaaa``acedegffgfeegfghgffeeffefeedeeegfffhgfffffefeegghhghhhhgijhjiijiikjhhgggggggjiiiihjjigghhhiikijkjjiggfhjklkklllkjijkklllllklljjjkkkllllkljlonmnnnnnnnoonnomllkllmknnoplllljgijjjjjhghihjmnllmllllmkkkkkkllmmljkjkhiiijjiihiiihhhihihihhihghhfcfbcaabceca`aaa_^^^__`_____`^]^^]^^]]]\\][[[Z[^ZYYZYYWXWVUTSSTSSTVUUUSSRRRQPMNPQQQPPPPOPRPOOLLLMMNONNMMMNNOONMMMMMNMLLLLLLKLLKIJKKJIIIGGGHHHHHIIIJJJIIHHGGGHHGGHIIGGGGFGGGHGGHGIGHHGGGFFEFGGGGFFEFFFHFEGHGFHHIJHHIIsssigjutsrrrxxylimqv€‚~~khgh\G;AdupoljjkiaZWbsqtlld^^_eXUMLO`rgQXgpsjc]Zlff]^Ubqx‚€€€€€€€€€wX2(,9?DEGHIKLMONMOQQQPPOPRQRSSTTTTUUVVVUUVTVYWWW[XXWWYYXXY]WVWVWYY\[\[[[]\\]___^]^`^^_a^^__`bacaaceegfefgfeegffghfeeeeefifeeefffffffeefeeeegggggkggggghgihghhiihhiighggffgiihihijhmjihhhkhiikihhhihfffgjkkklllmjjjjkklmnnlkkpjkjlkkllllmmkklklnnnmroonlmonmlllnlmlonmmlknjhiijihihhhiihikmnmmmolkkjkllooomnmnjkjkjjiikkjkikilihhkiiijgghhilggggddefcccca`ac`_`_^_`c`e___`^^]^^^]_^^\[\Z[\ZZZ[[\[[YXXXVTTSRVTSTVVUVTSQRTQQNNPQQQPPPQPOPRPONPLLMMNNMMMQPQPPMLLOLMMMLKKJKKLNKLJJKLJIHHHIIIHGGHHGHKKKIJHHHHGJGHGHIKHHHGFFGHGHIIIIHHGGGGGGGFFGGGGGFFFFGGGGGFGJKKIHHGJssqlnrtssrrrtvummoruyz{|sjgie`J;Mlrsrpqrtvkhouvwupnmkhgfc_`bjqmkkiqzvsqomlkie`eqy€~€€€€€€wU1'*6=CCFGHJKLLMLMNOPQONOQQRRSRRSUTTSSTWUVUUUUTTTUVWWYWVWVWWVXVVWYZ[[[[[]\\\\Z\\\]]\]]^]^^_`a```abdddfdegfeddefefgddcbgefffffhfeeeeeeeeeeediffffgggffffeffghiihghhhggggfffjghiijjgmjijhgghjjhggggggggjiijkkkljkkjiknkkmomlkikkjjkjknmkknmklnmlmpolnmmmllmnnlmnlkllmmmmljkjiiihgggfghigiijkmmmkollklmllopmnnlkjiijkiijkjjihhijihgiiiihhgghhieeefedcccbccbaab``__^_`_^a``__^]]]\\\]]]^\\[[[[ZYYXWWXXWWWWVWSSUUTUWWWVTSSRRRPNOOOPPPPPPQPOPQONPKNNNNNKMMMMNNNMMMMLLLLKKKKLKKKKKIJJLJIHHKIIHHGGHHHHHHJJIHHIIFHGHFGHHIHGFFFFGGHJHFIHHIGGGHGGFFFHHGGGGGGFGGGIGGKIIHHIIIstwvttvsvvvtsssooqutwy}xkebhb^NPWqzvuuuvwtsvxw{z{wusrrrqopqsuv|ttu~{xwwvvxzvsuwz€€€ƒ‚…xS.%(3<ABEFGHIJKLLLKKKMNNNOPOOONPSTTUURSUUUTUVUTTUTTTTUVUWXYWVVUUUWYXXY[[\[\\][[ZZ^_\\]]]_^^`a`a`cceccbbdgggdddfedcabbcgehgggggfffeddeegfeddeeeeffefgfddeeeegkjjhighgjhggigjhiiiijgliiiiiiijhgfighgigghhijkklnjilijjkjkonmmnkjkpjlkkmnljnlllklllnpnpnrmlklmplmlllmkllmmnjlkljlghhkhjikjkjjkmmmlnmmlomlmonmmnlmiijijkkkjjihhhihhmhhhkihhhgihifeegfcdbbeeebbbd`b``_____aab`a_]]]\]]\]\\\\ZZZZYYZYWWWWVVUWUWSTVTTUWWXUUSRSUQOOPPQQRQQPPPPPPPOOOMNMMMMMNNMMONNMONNLPKLLKKMKLKLKIHHJKJJHIIGHHHHIIIKHJIJJIIJHIFGGHGGFGIKGEFFGFGIIGFJIHHGGFFFFGFFFHGGGGGGGIGHHHHKIIIJIHIstutttutvuututspqruuvxyunbfgf_]]gvwwvuxyxuwxxxxxz{xvuuutuvvvwyywx|}}|{zz{{yyz}}‚€€€€€ƒ‚‚ƒ‚ƒ€xP+%&1:=@BEEFGHHIJKJIJJJKLMMMNNNNPSRQQRRSTSSRSTTTSSSUTSUWWWVVVWVUUUVUVWY[[[ZZZZ[ZZ]\Z\\\]]]^a````bcddbbcedbcdddedcabcbceegfghhhgfeeddedcddccdcbfggdefeddgeeefffgggggghgfffeffieiihgiiiijjjghggfffghhhgghhjnmlmijllkkjkkonnnmjijjjkjklmllnnnllllmnnmmmomnnkjplmllijjjkllnjmlljighhihiiihkjjkkklkmlmmmlmmmmmmllmijhjjlkjgjiihhhhgihhhiihhgfgihgffffeebbddcccccaba`__^_a_^^^`a][\Z]^[Z\]]\[\\\YXY[WYWWWWUVUWTSSSTUUVVUUURSRPPPPRQPPPOOOOORPQQPNMNMMMMONNMLMMMLMONJKKLHJJMKJIIJIIIIIIIGHGGIIIIKIHHHHHIJIGGGHGGFHHHGGGGFGGFGGJHIHGGGGGHIGFGEGFFFHGGGHHGFGGGIIHHHHKIIHGtuutwtutwvvtussrvvuuvxxurrrrrolruwzxz{|z|{{{{yxz~{|{{{{||}}||{{|}}}}ƒ€‚‚€€‚ƒ€€‚ƒ‚‚‚ƒ‚wM)"$-7:>ABCDFGHHKHGIIJIIKLMMMMPNOPQPOQSSSRRRRRRSSQQSSRTTTUUUVSRTTTTTUWYYYXXXXY\ZZZ\[]]]\\]^__`__bbbbbacddaacfffccbbbbbedjggggghffegcccccccbceagggdeeeddefgfefeeeggkkkfhefffeihhhihjjjiijjgigffggggffggkhhihlmljklklmlkoopmmklkljkjlmmmmmlkklolononnmpmnlkkommlljjjikllmklmojjjiikiiijjjjjljjllmmnmlmmmnmmlqmmkkkjklkjhjkliihhhkjjjihgggggghgfggfeeccfcbdfccccab`a``_^^a_`^\[\[_[YZ]\_]\[ZZYXXWWWXVTUUUTWUSTVWUUVVVSRRSSQPPPQSQPOONNOOPOPOONMLKMMNMMMLLKLOLMMMKNLKIJKMKIILJKIIHHHIHHGGHHHGIIIJHHHHIIGGGHHHFHHIGJGHGGFEEEGHIJHHGGHHIJGGGGFGFHHGHHHHGJGGHJIGGIHFGHHrrrssrsstvtsrrrssstwwxvtqrsuroqwwyywzyyz|{{{{yz||{{{|}{z|~~}}|}~}~~‚€€€‚‚‚‚‚„‚ƒƒ‚‚ƒ…uJ'!#+48<>?@ACDDDDDEIGFGIHGHJIJKKLNLLLLMMOQQPONNOOONNPRRRRRSVSRRTSTTTUWWWWWWWWWY[[Z[[[[[[\]^^^^^__````_abb`abbbbaacbabbdeedddfggefcfccdbccccdcaddcbcddgdddddcdddceefggeedffeehjhghhihiigfiggeffhjhhgehggeghimmnlkklllkklmlkjjlkjijjlmmlmmkkklklmmoomlnmmjkkllljjjjljnllkkkkkkkliijjjjijjjjkkjkllklllnnllmllqmlmkkkkkkjgkkkiighhhfffggghgfggffgigeeedccccddccbaaa^^_`]]]^]^]\[ZZ[XYY[Z[][[ZZZZXVVVVUUTVUTVTTTXVUUVUUSQRPQPPPQQRQPRPPOONMNNNNNKLKLLMLMMLKKKKKLLLJKKJJJKKJIHIIJIHIHGHHHIIIHGGHHHHGGGHHHGGFHHHGHFGGGGFDEEFEFGGJJHIFGIHFFGGIGFFFHHHHHHIGGGGHHJGFGFFFHEqqqqrssrqqrqoprrtsrssstsqrvvxwwvwwywzz{{|}|~{}}€~~~|}}~~€}€€~€‚‚‚„ƒƒƒƒ‚‚ƒ‚‚…ƒ†††„ƒƒ…„rG%"*67<;>=>?BBABBCEGGFEFEEEHHKIIJJJMLLKMMLMNNOOOLKLMOPQRQPQPPPQRSTUUVWUUVXVVVWXXY[\[ZZ[[\`]]^_^aaa`__cba_bcdcd`abaacddddddfghhgggfeccb``abbbbdcbbcdddddhcededcegeeffffefehhhiigghgghhffhfffggkhhhigihieghjmplnkjmlmnmmnlkjjlkjjjjlmomlkjkkklmpmonlknmlkklmmlkjjijjkllrmqkjjmjjkllojlijkkkgjmklkmllmolllnnqmmljkkkmlmllllllgjihihhhhhhjghgihghideegcacgdedcaaab^]^`^[^__^]\]_[]]\[[ZZ]YZ][[ZXWZVVUWWVVTTTTTTUUTTTTTTURRQPPPPRPPQOOMMMMMMONNLKLNNPKMMOKJJLLMLKKJJJJLKKIIIJJLJIHIGIHHHIIIGFGGIHFHGIGFGGFHHHIIFFFGFEEEEFEHGGHIIIFGGGGIIHGFFGFGGGHJJIGGGGGHGGGIGFFHFijjjklllllmnmmooooqqpoprsrsvrqrttuvwwwx|{zzzyyyzzz{}{z{||}}}~~~~~€„‚…ƒƒƒ„ƒƒ‚‚ƒƒ‚ƒ„†……€pD$"#-69==>>>??@@AAAABBCBCCBBCCDDEFGHIJJJJIIIIIJJIHIIKMMMMMNPOOOOQQQRRRSTTTTTTUWWWVY[YYYXYYZ[[\\\^^][]]^^__`a```abedbffhhknswxz{|{zslhcb```````aabccdcccddbeddbcfedfcddfeefhghhhhggfeheegfegffgjfghgefgffggkkljlkknmmmllpljjkkjjjjklllmlkkjkjklllmlkklklklooolkkjjjkkllllmlkjmijklkkkkjjkkljjjikknnmllkllmmmmmljiijkmljjklnkghjihijhgggfgggggggebdefbcddddecaa`b^^\`__^]]]^]\\[[[[ZZZ[\ZYZ[[YXVVUUUVVVWUVTSSSTSTTTTSUTRRQQQPORPPQPONMMPLKNPMLKKNMMKKKKKKJKKLLLKJJJJJJKIIIJLKKIHHGHGGGGGHGGHGHGGGGGFFGGFHHGGGFFFFEEFEDEEHFGGHHIEFHHIHGGFFEFFFFGGHGJGGGGHHHHFGHGEEEabceeeeeeeffffffghjklllkknppoopqvuuuuuvvwxxyyyyyzyxxyxz{{{~~‚~~€~€‚„€€€ƒ‚………„…‚kB'#+19<=>@A@@?@BBBBBABBCBBBBAABCCDEEFGHFEFHHGGGHGGGHIJKKLMMNNOPOOPPRRQPQSSRRSSVVWWVWXXWXZXXYZZY[_]\\]_````accdeefiklpv{‚ˆ’ ¤§®µµ´¨™†rjccbab`c_aaadecbbbbbbeddcddffgdeegeefkghgghgefdhddeddgfffkggggdcddfghkknklllnnnnllllkjkljijjkpkklnnmjjkjjlmolkjllnllnnmklijnjklpmlmooollkjknklllknkklkklkklnmmlmkmlnmmmnljilijjjkllkkjggkkkjjgghijjjggfggeefeeeeecdfgfca`b_^]a^\\\]\\]\[[^^^[ZZ][ZZ[[ZXVWWUVVUUVUTUUUTSTUUTUTSSSSRRQPPPRQSRROQNMOLLNNLLLLMLLKKJKLLJJKLLNJKKKIIIKILJJJIIIIIJHGEFHHHGHHGGGGHHHGGGFGHGGFGGFFFEEFFDFEGGJFHHJGFGHIGFFFGFFGHFGFIHJHGGKGHHIHGHHEEFbedgdcdgecfefffffdeffffghghhhgjjjijklnnoppqrrrrrstttvwwwxwy{xwwxwwy||{|}{{||}}~~}~~}}}}~{eE06=AEFGGHHHFEFGGFFFEEEEEFEDCBCCDCDDEEEEEDDDDCCDDDEEFGHHIHIJKLKJKLLMMLLMOOOOOPQRQQSTUUVVWXYYYY\_acdfhknqstvxyz„‰”™ ¨±ºÀÇÇÇÈÉÉÈÈȼ²–€tjf`__`__^acbaacaabbbcbbddfacdefefeeggggfffeedccdgddehedfhgdecddefghjkkjljlnlkmmkjkkjjiijkkkllllkjiiikjjlnnmkjkllkmnnmlljiljkkonnmnlmnlkkklkkkklmkkmlklllllkkjklllmmmmlkjiiiikjlkjkkkiilkjkihfgghhiefffgffecedeeeffdddb```]]^]]\\]\\[ZZ[\^\[ZZZZZZZ[YXXWVUWWUTTTTTTSTUTTTRSPTSRRRQQPOOPQQRQOONNNNOONMLLJMKKJJJJJJJJJKKJJJIJIIHIIIHIJIHIIIIHHFFGGGGHFGGGGGGGGHIHGHFGIGGFEFFFGGDEEHGGFGGHHHHHHGGGGGHGGGEFFFGGHHGGGGGHHGGFEEGcdfededfgfgfgffffeiflihfigegighijhiiijklllmnmlkkkkmllmnmmnppppqqqrtuwuuuxwvxzz{||||||}|}}}||||y^HIMTYZ[ZZXVWWVTRQOMMMKIIJIHGFFFEEEEFGGFEEEDCCBEDDDDDDEFFFFGHIHHIIIKJJJJKLLMMNOOPSQRTUUVXY\^`cglrx~ƒ‰•š¢ª¯®®¸ÂÇËÎÐÐÐÏÎÎÏÐÒÍÍËÐÌËÉÊÇŪ›„nea``__^abb`a``acccbaadefabceefdddgfefgffehcddcdeddefeefhfgghefgiikjkjlkklmknjiiijmjiilkhkklkjkkjihiijmmmllkklolmnqnmljjmjmkpprmnlllmlmllkmjjkljjnpmmlnlklmjklonmmlllkkijhhijjklmkkkkkkjjjkfffgihfffhhhfeefegfgffcdab`^]\]\\^\[[_\[YY\[[[[ZZ\ZZZZZXXZX[UXWVVWTTTTTUUTTUSSQTRQQRRQQOOQPPPQPONPNNOOMLLKJMKKJJIHIKKMJKKJJIIJJJIIIHHIHHGGGGGFGHGFFFFHGHHIGHGGHKIIHHGGGGGHEGFGFGEHGGGHGIGHGIIHFGHIIHHHHKFGEDFEGGGGGGGHGFGGFEFededebdeecddfefeeeefghgfghgfgfhfhhhhikkkkmmmkjkjjlmkllllmmoppooopotqrrsqsprooppoonmlmmmmmmmmmmgZRQV\]^`abccbbb`_^^^_^^[XWVUSRRPONNNMLLKKJHGFEDEEFEEEEEFGFEEEFFHFIHHHIHJJNMNOQSUXY[_chlqw|‚‡“𥰹ÁÂÄÇÊÌÎÐÐÏÎÏÍÏÑÎÍÍÎÎÎÍÍÍÎÎÏÎÍÍÍÍËÊÉÆÅ»¯“}tfd_^^_^```_`aabbaabaaaabbbbaabcdddeedddcacbbbcccdfgeddeeeeefiihiiiikkjijjihhhijkkijkjjkkmjjknljiijjjjkkkklomlllmnnmkkljmkmpqllllklllkkjljjklhkoommllmmmmjkkljkikjkjihiiihiikkkklmllkjjihggghhggfdddfefffeeeeeeaa_`_^]\\[[\\[[\\[ZY\[Y[][ZZYYYZZXXZVVTTTTSSSTSSSSUTQRRQQRQRSRURQOOPPQQPQOONNNMNMMLLJJJKIJIIIIIJIKJJJIIJKJIJKIIJIHHGGGFGGGFEEFFFFGHGGGGGGHGIJHGGGGGHEFFGFGEGHGFGGHFGIHGFEGIHGGFGGGFFEFHGGGGGFGFGHGFGGFGifhdebddedfdgfffeedfgidgfghhhghhhhihljjkkkkkjkkjjlonnmmmsmppopqptrvrssvvuuuvvuuusssqpomnqmjjika\W]cbacdeefgghihikkkihggghgedcddcb`]][XVTSRPNKJIHHIHHIHHIIHGHHHHHIIIJKMNORVZ_djpx‰‘™£¯»ÃÊÏÓÔÕÖÖÕÔÖרØÖÔÒ×ÒÑÓÔÒÐÒÔÓÒÒÓÒÒÎÓÑÖÕÔÔÔÑÑÑÑÐÏÏÏÅ©•mg`_a_a_`a`_^_^_`___`bcaaaa`bbddedddeca`faabedededdeddhefefjiiiiljljjjjijhghljkihikiiiiijiikmjmkljjjjklllmmmmknnplmmljmllmqlllkklllkmmmmnllmmlnnnlmlmmlklllmmlmmmkjimiiiiilkkjomllmkjihghhhgfgfeeejeghhffeeddba``^_]\[Z\][Y[\]^[Z\[[\\\YYZZY\YXXYVUTUSSSUTTUUSSSTSSQQQSRRRRRRQPPOPRQPPOOPONNOOPNOKJJKJIGHHIIKKJIIIIIJJJKKJHIJIIHGHGGIHIGGGIGFFGIFGFGHGHIHHHHIIIGHFGFEEGFGFDGHHHGIHFFFFGHHGGGFHGFFFHHHFFGHGGFFGHHHGIGfeeedcdcecddeeeffffhgifggfgigggggghhiijjkjiiihhgkkklnmnmmmooooqpqrrqsstssssruutttttsstuusqrsnlhefgedfghklljjkkkjklkkkkkkkkkkjiikjijihgecb``_][YWUTTUTRSRSTTTTUWY[^adhlt|„Œ” ©´»ÂÉÐÒÔÓÒÓÔÔÔÔÔÓÓÓÔÖØÖÓÖÓÔÓÓÔÔÔÔÒÒÓÓÓÓÒÑÑÑÎÎÏÕÓÔÕÔÑÒÒÑÑÏÍÍÍÌ͸¬’|rca^]]]\\[]`^]^^]\]^^^aa`_```abaacba```a`bbbccdccddcdeffgjihighiijjiijhfghjiihggijihjkiijkjiljkjkkjkllmnnqmlnomjjjjjknlkkkllkkmmkjkiklnnnnmmmmmkllmmllkllkkjkklijjjiiijhijjjnmlkkkihhhhhhjkligffgehigffefgdcba_]]]\]\^][\[\Z\\\[[[[ZYYYXXYYXXXXVVTSSSRSSSRRRRRSRQPQQRRRTSSQPPPOOONPPNOOOONOLLLKKKIJJIFHIHIJJIIIKIIIIJHIJHHIHIHHIHGGGGGGFFGFFGIGGGFGGHHHHHHHHHFHFFEEEFFFFFHIGGGIHGIFGGIHGGGHHGEEGFEFGFFFFEEFFFGFEHHeeeefdddedeeefhgggggggggheefgghgghhgjikjljihhikhkklmolllnoqopoqprrsrsssrrsstuuuvvuuuuuuuutssrrrqoihijklmonooonnnqqpnnprrqqrstsqpoopnmnomlmlmoliiijljihhijkmorx€ˆ™¦¯¸ÃÎÑÓ×ÚÚÚÛÜÙÖÙÛÚÙÛÜ×ÕØÝØÚØÝØØØÚÖ×ÖÜרØÙÖÖÔÓÕÚÕÔÔÕÓØ××ÖÖÖ×××ÖÕÔÔÓÓÕÖÔÒÐÓÅÄ¥|ic^\[\\\\\]]]\\]]^^^a_^^_^_`d`_`a`____``aabbbcddgcefgfggghhghijjlknhgfiijhhglghhijjjhiliiimjljijmkmmmnoonnnllkjjlkjknllkkklkqmkklllmonnmmlronllmmmmlkkikjjkllkkkjijijiiijlnmnkljihiijhiikkjgjgjfggffffffedca^_b^\\\]\\\[\[\[ZZ[\\Z[YYXWYZXYY\WWUTTTRRRSRSRQQTQRRRRRRRRRQPORPONNOPONNMMOMNLLKKKLJJJKIHIIHIJLHIKJHHHJHHHJIIIJIJHHHHGHHHGFGFFFFFGJGGGHGGGIGJHGFGHGEEFHGFFGHIHHGIGGGHHGGGGHGGFFDDEEFFGHFGFHFFFFFFFGFfhfeedeeedeefghgfefffgggfefgffghhffgghiilhiiijjhjjkllkklnopoonnnsqsqrqsssutvuwvvvuuwvvwxvuuttusqojiijjlnnnonoooopqppprrqqqqrrrrrrrrqrssssssttvwxz|ƒ‡Œ‘–œ¤®·ÁËÔרØÙÛÜÜÜÜÜÛÛÙÙÙÙÙÙÙÙÙרØÙÙÙÙØØÚÙÙר××Ö×ÖØØØ×ÖÕÕÖ×ÕÕÕÕÒÔÔÓÑÒÐ×Ô××××רÖÔÔÕÔÑÐÑËۦ‹ul_]YYYZ[ZYYYXXYX\^]\\\\[]^^__^_`^^^^^_`___`bbbbcdfeeefeeggikjihijhfefhgggggghmikjjihkgiikkigijjjknllmmmlkjkkkjjjijklljllkkkjjijklmonmlllnnnomqolnnkjlkkjiilkkkjijiiiijklkjkjlihhhghhhhikjhgfgeggeeffeeeecaaa`_`a]^]Z\[\Y\\\]ZYYXXXYXYYXWYXXUVWUTTRRRTRQQQOQPQRPPQPRQQPNNOONNNOOMMLLMNNLLKJLLJIIIJJIIIGIIIHIJHHHIJIHHIIIJIIIIGGGGGGGFGGFFFEEFGHGGGGGGHGGGGFGEEEFFFGGGGHIGHFIFFFFEGGGGGGGGGDDDEDEEFEFFFGFFFFFFFFgfffeffedefefgigffefgghgffjgihfgihliiiijlkkkjjijkkkkmlpmmosopnopsrrrrqssrstttuuuuuvvvwxxzuwvvvwqpkjklmnmopposqrqpqrrtrrrrsuuvvvvwwwxyyyz{}‚‡Œ‘—ž§±¸¿ÇÏÕÜÛÜÞÞÞßßßààààßßÞÞÞÞÞàÜÞÝÜÚßÛÛÝÞÞÞÜàÜàÛÛÜÞÚÚÛÝÜÛÙÝÚÛØÚÙÙ××ÖÙÕÖÖÕÕÔÒÕÒØÕØ×ÚØØ×ÚÚÙØÛÕÕÔÓÍȼ·š„sb\YXYXYXWWXXXWZZY[]][Z_]]Z[]_][[[\\^__^^^^_`bbeeeddfeegfkkjgghjieegggfefffhiijjihhkikkljiiihiijkllnnmllkmkkihghiklljnmljjkkjnmpmnmmlopomnpmnomnmjjjkkjijlkllmiklljijkklkkjkijiiimihghijihgggggeeefgfheecba_^^`]\^Z\[[Z\\`\YXWXXXYYYXWWVVWVXVUUTSTRSQQQQQRQPPOOQRRRRRRNPONNNNOMMKMMOMLLKJKKJIJKKLKIJJIIIIJHHGLKKJKIHHIIIJJHGFGGIGGGGGIFGFGGHGFGIGGHHGHHHGGFFEGFHGGGIHHHHGJGGFFFFFJIIGGGGFGDDEFFJFHFJHHGHGGGFFfffhfffffgfefggfefeefffgggggilhgghiiihijkijijijjkkjkmmnonooooooprrrrrqsrrrsuuuuuuuvvwwwvwvwzxvvrqlllmnnnoppprqrrsutstttttuuuvxyz|~€‚…ˆŒ‘—œ¢©³¾ÊÕÛÞßàáââáâáàÞÞÝÝÝßÞßàßÞßÞßáßÞßÝÞÞÝÚßÛÜÝÝÝÝÛÜÛàÛÝÞÞÛÛÛÜØÛÚÝÚÛØÚÛÔÔÓÑÑÑǽ½ºÅÏÍËÐÓÒÓÖÙØØØØÙØÚ×Ö×ÕÓÍÇÁ½¦—~g_WVTTUUUTSUXVTUVWVXYZZYWY[[XXYZ[[\^__```^^_`accccfddefhihffghieedeecddegfegjjffglijljihghhiiijklllkjkkkjkgggihhihhkokjkkkjmmlllllkokmlmommommkjhjmkllkkkkijikkkkjhjijijiijjiihjihhhikmiggfggefegfefdddcb_]__^\\Z\][Z[[[YXWXXXXXXWWWVVUVVUUUUUSRRSPRRQQSOPSPOPQQPPPPNMLNONMMMLKLLKKLJJIKJJHHHIIIIHHGGHIHGGGHHHHIHGGGGIGFFGGGGIFFGFFFFFFFGGGGGHGHIGGHJHFFFFGFFFFGHHHHHHGGFGFFGGFHJIHHGFFEDEEFFFEFGHGHHGFGGFFefhgfffghfffhgfffeeegfghjjkhiiihhiijlhiikjjjnjjknkklmnqptnnooonpqsttttvrsrsuuvwuzyyy|yyxxyyyxvvsrnmmooqopqutttstuuuvvvvvvxz}€ƒˆŽ”š¡ª²¾ËÓÚÞÜÝÞÞÞßààáááââââââââââááááááááááááááßáàßßÞßáàßÞáÞàßßßàÞàÝÜÜÜÛÛÛÝÜߨÛÎÍÈø®§ž‚s~”£¯¼ÈÎ×××ÖÕÕÞØÛÙàÚÛÛÛÔÖÐÉÄı§†k^TSRPPPQQQSTTTUVTVTUWYWXZZYXYXY[Z[]^\\\]]^]^_`aa`abddhgggfdcdebbccdddddddfhjffhkhghjimhkkkighkihjjjlljijhjjmhhimhjkkknmmmmlllonnnolmlmmmnnoollmmjklljiknklkkkklkkokljjknjiijhkilihjkihghgffeecgeffdbceb_^_`b\]]]]]\[Z[[[YXXZWXWWVVWWUVUTTUUUSRSSRQRPQTOORPPTPPPOOPOMMONNMLLNKKLKKKIIJKJJIJHHIKIHGEGGGGFFFGGKKKGFGGGIHFFIGIHHFFFEEEEFGGFFGGHJHHGFGIHGGFEFFEEFGGIIHGHIGGGHGHHHFGIIIIGEEEEGGGFFFGGIGIHGGIFGGffhfgffhfefffgffffffghhhhihhihiihjjjihihikkjkkkklllmoqpnnooooopssrsqrtssstuvvwvuxwxxyyyyxxxwwvvsqnnnooppqqrsstvwyz{{}€ƒ…ˆ’˜¡©³ÁËÖÝßßßàááâââáââââââââââââââãããââáàáâââááááââáßâáááááááàÝßÞáàßÞßÝÞÜÞàßÞÜÛÝÜÜÙ×¹ª›‘zuqdVVXr|ƒš¿ÄÈÏÖÓÓØØÙÚÚÚÚÚÚÖØØÒÌǾ´«ŽvdPMKKKMNOPQQQRRRQQRTSSUVUTUUUWVVXZYXZZZYZ\\\^a__`aa`bccbbbaa```a``aaaabcdffefjgfghiggffffgffghhhjghgjihfiiighhhhhilklmkiklllmknmmkmlmmlnmjkjjkmikkkiijklkjkkklkjlkjijmkjiiihhhjkkjiggfghgdefegfffedcdba`_^a]\[[[[\[Z[ZZYXXYVWUWVVWVTTTTTSRSSSSSVRQQQQPPQPPPPPRPOOPMMONNMMLLLLLKIIIJIJHHHHHHHHHGGGIHGGGFFFGHIHGHJGGHIGGGGHGGHHHGGFEFGFEFFFFGGGFGIHGHIGFFFFEGHIIGGGHGFFHGEFFFFFIHHGFEGFEFFFEFFFGGGGGGFFFFGgehghggffefgggeffffgggjhhihhiijhgiljjiihikllllmlmmonpqpooppooprsstvsrtxwwwwxxxxx|yxxz{zxwwyxwvurqoonoqrrrrswyz|~†‹‘—¡«³¾ÇÏÚÛÜÝÝÞßàáâãããââââââââââââââââãããããããâââââââââáâââáââââááááááàßàááááàààààààßßßßÜÝØÖ©’ƒrcXOGHN]nrlio}Œ´¾ÔÖØ×ÞÛßÜÝÜÛÛÛÛÝÔÕÊÆºµ˜cJFHJLKLLLLMLLNNOPONQTTTTTSSSTTWVVVYYYYY[]]\]]^_ab`_`abaa```^]]^^____aabceefddeihjfhegddgjefgggififjhggihkkkhgghikkjkkjkllmnlnnmmnlmmlmljmmmlnmljjjjkkljknlkkkkkljjjjjiiijjjhjjjjhhjfhhifefffgfffecdba`_^`^\[ZZZ\Z[[ZYXXXZZZVWVUUVTSTTTQQQRURRRRQQQRPQQQPPQPPPQOONNQOOOPKMLMKLJKJMJLHHHHHGHHHHGGHIGFFEFFHGHIGKHFFHHGGGHHGGHJGHGFFGGEEFFIFFFFGGHJHJHHGGFGGGGGGFGGHGGGGGFGFEEGGFFGGFGEEFEEEHGGGFFFHGFFEFGgegfgefdfeehghgffhhhgghhiiiiihhhhhiiihillklmlnnmllmlnnpoonpqooqprtttstuuvwwxxxxxyzxxyzyxwuuuwvurqoooqsuwz}€„Š–¥®ºÇÖÜÝÝÚרÙÜßßÞÝÝßâããäãââàÞÞßáââããããââãããããããããããâãããââââââââáââââââáááâááàáááßáàßÞÞÞÜÛÚÙØÒ®¥|w_RJ@>=IqnŠhefhlr…”§ÀÆÑÔÖÛÛÜÞÞËÏÍÏÒÐÑÍĽ³œ…gIC>AGILLNMKKKNMMMMMOOOOPOOOPQRSSSSSTTUUVWXYYYYYYY\_^]\\][[\\\\[[\^^_``^`acdcceieccccccgfeedegffgeeefgihhhhffgghhhjkjjkmlnllnnmmlmlnmmjiigijjjjkkikiklkmmlkklllkkjjjijiiiiihijjjihhgggfgehgffeeecbcba``a`^\[ZZZ[[Z[ZYXWWWWXVVUUTTTTTTSRRQQQQQQQQQPPPQQQPPOOOQMOPNMLLLLMKKLKJJIIIIHHGHHHGGHHHHGHHGHFFFFFHGGIGGGFEFGGGHFFFHHHFGFFGGGFFFFFEFGGGGGHHHGGFGFGHHHGFFJHGHIGFFEEFEDGEFEEDFGEEFDDDGGGHFFGGGFGGGIgfghhggeedceggggghihihghkkjikllhghjhmjjjjkmlklmlmmlmnnspqqpppoqppqrsstvuwvwxxxxyyywxzyxwxx|uwvvssppouy‡›¦³ÃÑÜßÝÞßàáâãâѱœŸ©¸½´®¹ÊÓàáããããÖÉÀÊÙÛââããããâãäãããããããããââãããããââââââââââââáááâááàáàßßÞÞÝÝÜ×̾±£“€o^PLFC5007DQ\cijnlihhr|”³ÁÍÐÚÙÜÝà×ÒÐÅÇËÑÜÏË¿¶¢–jI=8<CGMNNMLLMMKKKLMNNONONMMNOOOOOOPQVSSTUVWXXXWWYZ[[Z[\YYYZ[ZYXZ^^^_`^]^`aabaaaaaacccccdcbbcbdfffecgkiggfeffighiijkiiiiiklnkklkkkklljjjiiijhiklkjkkmkkilklpmmkjkljihhiijiijjmiihigfffgdfgghfecaaccdaca`^\[\[[[_Z[ZYXWWVVYVUUUUUSSSSSSQRQQQQQSQQPPOOPPQQOOORONNNMNLKKNKPLKJNIIHIJIIJHLGGGGFGHIHGFGGGHHGGGIGGGIGGGIHHGFFJHHHHGGFFFFFFFEEGHIFHGIHGGGGHGHHIGGGFHIIIHGFEEEFECFEEEEEEEEEGDIDFGGFFGHGGGHGHHgfffgggdedeeghggggihgfhgiiiihghggjjhijjjkklklllmnolmnmnnoponooppooqrrsttuxvvwxvvwxvuwwwvvwvuvttrrqqov…§ÆÕßßàâäååååääåååãßœ–y«¤™†’²ÊÝàãäã߿”ÊÕáãääãâãääããããããããâââãããããââââããâââââáááááßÞÛØØÕɽ«˜†wi\QHA::;?EBD3--.37;@Ojh}Ž|ptt†“¥ºÃÒ×ÜÝààßÒÈÆÇÊÏÉȾ´§˜pM@59=CMPQRQQQQPONOPPPQPOLNOOOOMONNOOOPPQQQQRSRRTUVWWWWWVVVVVVUUWXZ\[[[\\]`c_^___a``aa`aada`abcccdefeeffeeeffffggggghiiikkjjjjjjiijljhjhhghgikkkjkkmlkilklmmnjjiihhlijiiijihliihhfhkggdeeddfdba`a``_b`_^]\\[\ZZZYYYXVTUUUUTSTTTSTRRSQQQPQSQQRQPPPNNMMMMMNNPONMMLLKKKLKMLKKLJIIIIIHHHHHHGHJHHGFGGGIHHGFGGHHHGHGGGGIGGFFFFGGGEFFFGFEFGFFFGGFFGHHHGGFGGGGGGGGGGGGGHFFFFEDEDEEDCEEEFEEEDDDFFGGFHGGGFGGHHgeefjggdeegfggghkhihggihjiiighjgghghkiiillmknlllklllnmommnnmnorooopqrstttuuuwwvvvvvvwwwuuwxuvtsrrrqqv†ÕàææææææææææååååæäãÝΣˆƒ|mnb\f¦ÛâääãÖȬ›¨½ÇÞáääääääãääãããäãããããããããââââãâââââáááàßÞÛ×È®”{hWI@:511/-,,-138>;3-*,+,,/4=J`ž„|sm|‚¢¸×ØÙÜßßßÙ×ÌÅÆÆÈ̾¸« sR>359DUT][YVTTSRSTSQQQPQSSTQOOOOONNNNNOPOOOOOPQRRSTTVTSTUUUTSSTVWYYXXY[[^^\\]^^]\\a^^_accaabbccccccceeeeeddeeegghgdfghjiiijighhhjklikghfggjjjjkjknnliknllmnmmjjghhiikjiiihlinhgghfddcdecccbbbbe`a`b`_^^]_\\Y[YYYXVUTVUXUUSTUTSTRQPPQQQRQQQQQPOPNMMMMNORNPPPMNLKKJKKKNLJKJJKKJIIILIJIJHHIHHGGGFFFFHGGHIJHHHIHJGGGEEGFHHGGFFFEEEDDFFFFGGGGGHHHIIJGGGJHHHHGJGHGHFEFFEDDFDDDECFEGFGEFEEEFGGGFFFGGGJHHHfeefffedddffgghhhhiggghhiiiihhiggghijgiijjmkkkllllllllmlllmmnnnnoqqqrrrqsttsuuututuuvuwuvwxvwtturrqprz¦Øçìêéèçççççææååæåååâמ]j_jwtgg‚¹àåååäáܵœž”´ÛáäåäääääääãäääãääãããããããâããâââââßÛ򯃵”wbNB9520.//00110///22897566533235=@]zƒ‚w…‘¦¸ÊÙÜÜÞàÜÙØÕÐËÉÀº¹©œvSA357APUijlifca__][[[YWWXWVUTSRRRRQQPPPPOOOOOPPPNTPPPQPPOOPOMPRSTTSUVXYYYXXXWXYYZYY[`_]\\]^_c`_`aaaaabbbbbbcedcdeddeffffeggfefggggggffgghgggijjknmmjjlnllkjjjihhghgilihiglihhgffdeedccbbebaabaa`_`_^^]]\[ZYYXXZYVUUUUUUUSSSSTSRQQPPQRRQQQRTPOONNOMMMPNMMMMLLLKJKLLKKLKKKKKIIKJIJHHHHHHHHIGGHFFGFHGGHJIHHIIHGGHGFEFFGGGHFFFEEEEDEFFFGGGFGIIIIFGGGFGHHGGGGFGFFEEGEDDCDDDDDBDEFFFEEDDDEEEEFEGHHGGGHHffhggeeffefeigiihiiijjjiiikihhjghihijjjillmllkllnlolmmnlllnmonmnppqrqrsqrtuuutstuuvwwuxvvvxxwuwutssqnlk™ÙçìëééèççççææææææååâÖ‹yw}€¨rgj•Èääåääãȸ£•Œ©ÐÖãääääääääääããããããããããããããââááÞÖ·‚eP>5/-,--./0269766777657=EIJJHEB???>>?AGPbt¥—ty½ÍßÝÜÝßàààßÖËźµ³©¡yU?3329AQjnzwtvwutstrolhgea^\[YWVVUTTUTSSRQQRTQQRTSSQRQONOOONOPPQQRRSWXVVUVWVUUUWZWXXWY\]]\\]]^^`a^^^_acaaccccbbcddddfeheffeddehggfiggggffffgljkjikkjkllmlllihglhjhhgghigkgghlhfeddfcdaabea`_``b_`_^]^\[ZYYYXWXYWVVWUTUUTUSRSSRRQPPQQSQQQSPOOONMLNMLMMMMMOLPMMJNLMJKKLKMKLJIJLIHHHGGHHHFGGFHFGFFFGGGGHIIHIHHGEFFGHFGGGFFGFFFFFFFFIHHGHHGHFGJHGGIFHGHHGFFGGFEEFEDDECDEGFHDDEFEEDEEDDEFEEEFGGJHGFGGfgededeeeefeffgghgiihhhhhhihhhjghiiiijjikkkkljjkllljllollllmnnnnopooprqqrttuttttuuuvvuvvvwwxwtuuuusrpi\T‡ÔâìëêéèçççççççççæååàŸuNQUmvfJãäåååäÚÛÂ²Ž¸ÔãååääåååäÛÒÒÓÕØÚÜààáâããâáÞÛÅÁŽjQ<4.,))(*+,-149>BDFGGGGHHHTbdgdb]XVTRQQPNMOSdz‚œ™œƒyˆ›±Ã×ÝÛàààáááÚØÅ´®®ŸŸyTA5206;K_gxy{yxxyxwxxwwurrrqomifdb`^][ZYYXWWWWVUUSRSSSTRPOOPPPQPPQQRSSRRRQPQSSTSSTUVVVWXXYZZZZZZ[\\\\\\]^]^^^^__`abbbbbbccddccbccfgggfdedeeghhhhkjiijiiihigfhgfiggggghgfeedgheedceadaaabaa`___]^\]]][ZYXWYXXWWWVVVXTTTSSSSSRQRPQPOOPPPOOOOONMLKNNLMMONNMLLMLKKKKJJJKKKKKJKJIGHHHGGHGHFHGFHFFFFFFGGGHJIHHHGGFFFIHGGGGGGFFEEEFGFFGGGGHGGHGFGHGGIFHHGGGFFGGEEDDCDDCDEEEDEEEFFEDCEBEEEEFHFFFFGGGEFGeeedhdddhghefgghhiiighjihhhhhhjhhhiijjjikjpkljokkklkmmnmmlmmooqmopqooppqssrsstuutuxvuuxuvwvvwxxuvtutqoRBGÌÚêëìêéèççççççççæåÞ¹eb][ea_v›ÛáååååäàÖ½™‹‹¶Ò×àâåååååäÓÈÄÄÆÉÌÎÏÐÒÙÞáàßÚ¯g?5...00,*)**+-47EMX\_dgebfilqx~…ŒŠ…|twxsmppke\YVZu ‘Ž„…~¥ÀÝÙàààààááÝØÃ®¥Ÿ¢¤yS=41135AL_€~‡…~~|~€~{|~{xyzxwxyxxxwsomjedb_]][XWWVTUUTSRQQQRSTUTRSRQQPPPOPOPQPQPPPQSWVTVXWUVYZ\[[\\[Z[]^^]^___a___^^aabcfa`abbfcbdfeeefefghghggggghihhjfeeeeigfggffeeedceeedebcbdaaaa_]`_`_^_\\\^YZYXWYYYVVVYVTUUTTSTUVSRQRQRPOOQPTOOONMMMMMMLLMLNMMMMNMLLLKLKKKJJIIIIJIIGGHGGIHGGFGGGHFGFFGHHHGIIJKKHGGGGFFHHHHHGIIIIIGHFFGHGGHIHHHGGGGGGHHGGFGIGGGGFEDDCCCCDFEEEEEEFHEEDDEEFFEFFGEEFHHHEFFefedeccdhffegihhhhihgfijiijjighhhhihiijijjkkkjnikkkklllnnnnmnnnnoqpnoopqrrrstttwuuvvuvxuvxwvwvuuutttosU;?N{ŸÄçìíêéèèèèèèçççæÞÑ…rb€xrnkq{¨ÜäåæææåàßÒļ½¾¾ÌÝãåååæãÐÀ¿¾ÉÃÎÈÉÈÑÔÝÞÑÀ›qW86211244321/-.048@QOso}}|zw|€†‰‘–›—’Œr‚s‚mieb]QUZepv‚sjwƒš«ÅÙáàààààááÝÝ륣 uM<22245:AWptƒƒƒ‚€€‚‚€~~}|zyxxyzxuvwsonnjfecba_^]\[ZYYXWWWVUUUSQRRRQQPPPROOOOOPPRTTUSRTTTTUUUUWWXXYYYYZ\\[\]\[[[^^__`_`````ababaceeffgffeeeefhffeddddceffeeffccabcdeddcbbaa`a`_^^__^__^\[ZZWWWWXWVVUUVVTSRSRSURQRRRSQQRNOOPPOOOMMNMLLKKKKKLNMNMMMMLLLJKJJIJJIKIIJHHHGGGFGGGGGFGEEEEEFGHHHHJIJHHHGGGHFFFFGHHGGGGFHGHGGHIEFGHHGGGJGGGHHGGFFFGGGFFGEEDCDEEDFEEEEEFFHEEEEGEEDDEEFEEEHFHEFFeeededgggghhihhhhhhhiihijhhijhhhhhhhijkjikmkjkmjklmmllllmnpoonnnopqoooopprvuutuwwwwuuvxxxxwxxvvvxwwvwsV;6AOh‰»ãëîìêéèèèèèèèçßÕ„nO_z€’w‰¶ÝâææææåäãâááßʾÍàâãäååÐÌÉÊÌÍÏÍÌÔÚÞäâÌ•fN9:;;;889;>@>;876567@HYsv’ˆžŽ§ˆ±¸·¶³°•”ymgcb_dUanXqYSe_mgiu€•¡¹Ïßàáááááááßàĵµ¶« oI9445567:JWi‡‡„‚………„„‚‚ƒƒ„†ƒ„„„……ƒ‚ƒƒ‚€€~}}|{z{||xutsqolieba`][ZXWVVVVVTSSSSRQRQRPQPQSRRPQSRQRUTUUTSTVXWVWXXVUVX\ZYY^\\]]\]^^^``_`aaabcdeggfedddffeedcbceeddeedefcd``bdddddabab`a_^^a^_]`__[ZZXXVW[XXVXUUTTUVTTRRSSSSSPOOQSSROPPPOPMMNMLNKLKLKMMLLLLNLLLLJKIIILJJJKKKGIHHGGGGGGGGGIFDEEEEFHHJJIILIJHIHIHGFGFFGHGGGIHIHHHIHIFFGHHGGGGGGGHHHHEEFFGGEEFEEDDDEFEFEEEFFFFHFGFDGEEHGGGGFFFHGHEGGfeecdeeefhhggeighjihilihihhhjghkihhiikkmmmlkkkjjkmlolkkkllmnnnnopnononppqrwtutuvvuutuwxvxxwwwwwwwwvvvtW;:<GLcw¨ÛêïìêèèèèèèèçãଛiQh}{qrh}´×áæææææææææäÞ½¡¢¬Ëãååãââââáâãããäååâ·taEC@AA@?ELQWUTSQNLIFCBCESYdqz‚ƒ€‰Ž‰…~klbdfdcbbVHT`Uff{nnnq’¡¼ÍÝàáâááßááßÛ×Ñô¦˜gC>=DKJFCAILb{~‚„…„ƒƒ‚ƒƒ…„…„ƒ„…ƒ‚ƒƒƒƒ„„‚‚ƒ„„ƒ~}}~~{xyyxuvxtpnjgecb`_\Z[[ZXWWVVTRQPSTTSRRRRRQRQTSTQSSSRRRUUSSSTSSSTWXXXYZYXY[\]]]]]^_`aaba`aabbaabcbcabaaa``acdcaa^_aa``___a^`__]]]]^]]\[[ZZYXXXXYWXUUVTTTTTSRPPPQQQRPOOOOOOONNPONMMQMKKJKKJJJKLNLKLKLKKJKJIIIIIHHGHHIGGGGGFEGHGHFEEEEFEFGGIJIHHGHGGHHHHHFFFGGFGGGGIHHIHGGGFIHHHGGFGHHIHGHEEEFGGEEFFEDDDDDDDEFFFFFEHGGGGHFEHGGFFFFFHGFEEEeeeegeeeehihggighhjhhhiikhjhjiiikjmkjjlkklklmkjjjklmmlmmmlmnrnnoonnnqppqrsxuuuvvzuxvxwxx}xxxyw{www{xvvX;5<FGJWkßëíìëêééèèéèçåȯƒti~|vqoz‚Œ¯ÐáææççççççæãÁ°¥ª¸Öãääåæååååååååååà¢sjfaUKIFHU^sssehmqrs[cRRLFJCHOQKB@CHR^tu{|}€„}whiXMFDDJ[kmpidfmz…˜¯ÈÜàââáááááÛßÓÑ¿°š‹ZEJVg}c[RKHHV`l„‡‰„„…‡ƒƒ…†…†…†„ƒƒ††…††…ˆ…„„…„†‡ˆ†…„…ƒƒ…€~|{zz{zxxwutrqnjgeb_][ZXWVWVVUUTTSSTSTTTTTSSQPRSSRRRQPQRSSSUUTUWWVX[ZYZ\\\\]]^^^]]_ba_``aa___````cddccbb^````a_a_a^`_^\[\`_^\[[\YXXXVTVZWWVUTTTVTRRQPPPPQPQPONNNNMNMNROMMMMMKKJKKJIJJKLKKMLMLLJLJJJJJIHIIJHIGFFGFFEIHFGFDFEDEFFIGHKIIHHJGGHIHGGEFHGFFFFGGIHGGGGIGEIGGHIIHIIIIHGHEEEEEGEGGIFFDDDDEIFGGFFGFGHHGGGFHGGGFFEFFHHFFGFfeeffdffgkhhggggihihiiijighghiiijjkkkkkjkklmmkkjjjjjmlllllmnooonmmnoqqrqrssstuutvuwvvvxwwvwwxxyyxwxzvxY<;=HGJNYc‹ÍêììêéèéééèèãÞÜØ¦~eXpnjWPQ—ØäççèèççççåáÞÝÖÐÎÛÝáæççææææååäääÞªyt…}aTOLVaz˜†px„|eSPONYVSKDKOM<?=Q[q}‡’—œš–‘†ub\NF???NWm`acd]kmw—«ÊÞââàÝÝÞÛÜÕÔÉÅ«›‘j[YbromljYWOKJN_uy}„…„……………„†‡……‡‡†…††††‡‡…„†…††„„„„„ƒƒƒ„ƒ„„„ƒ‚€~}}|{zyyywuuuspmkhfddba_]\[[ZXVYXWVVTSRSUUTSRRRQQRRRRTSRQRSSSUWWWWXWWYZ[\\[[Z\^\[\_]\]^]]]]]]]]^_]\]^^^_^^]^^^]\Z[\\]\ZZZYXVUVTTTTUUVUSSRVTSTRSPOPQPOONNNNOMMMNNMMNLNMJIIKJIIJIJKJJKKKJJIJHHHHHHIIHHGGFFFGFFFGHFFFEEEEEFFFGGKHHHIIFHGGHGFFFFFFFFEFGGGGGGGGFGJFGHHHHIHIIIGGFEDEEEEEEEFEDDDDEEFGJHFFGHGGGGGGGGFGGFEFFFGGFFFifeeffjggghhiihihhhikjihhhhhhjiiijkjlkkjllmlkklkjjkkmnommmonpppmnnnprqqrsstttuxwwwvwywzwwx{xxy||{yxzvy[=6>IHJLKQZv˜ÃåëíëééééèèèççåÖ–VippiXM]ްØáæçèççççæåääßÝÁ¯¶ÈÒãåææææåÜÖÙâ×Â~ˆ¥}tqn^]nƒ³ª¤±¹w_OEMONONeUNTkLEYq†™¤»ÃËÈÈ®©€ukeJ><@PT`o\[_eo‚sq|‡žÃÔàÜÐÇÃÆÏÎÌĽ²ª›—}pnifaafa[\[F:9<AK\v}‡ƒ‰†‡†…„‡†ˆ‡ˆ‡‡‡‡††‡ˆ‡††††‰††…ˆ‡‡…†„„ƒˆ†‰††‡‰ˆ……‡…„ƒ‚€~~~}{zzzxwvvtpljgeca^\ZYYXVWXWVUUUTSTSRRSRQQQQRRRQQQSSSUXYZWVVWXXXWVXZ[[\\[\\]^[[[ZZZ[[\\]^_^^_``\[Y^__ZWX[XWVVUUTUSSUTTTSRRVTSRQRROOOPONNMNONMLMLKLNLKMNKLIKJIJLIJKJJKJKIHHJIFFGHIHIHJGGFIGHFEFEEEFGFGFFEFFFFFGHHIGHGKHHGFFFEFGGGGEEFHGIHIGFEGHGGHGGHIIHIJGHHGGGEEEHEEEGDEDDFEGGGIFEGHGIGGGFGHFGGGFGFFGGGGFhhfgfgggggfeiiikhhhhijihhhijijijiijjjkjjjjmkkkkllllmlmmmnnnnnmnnmmopqqrssststuwtwvvwvvxxyzzyyz||{}yzvy\=9?JINZNNTWmƒ®Ýêîêêéééééééèá×¹œ€m]^Ygiq—ÓÛæçççççèèççåâÁ´˜©¯ËãææææåÒÐÌ×ÕÑϨ‡ƒ€€}sx’¢ª¬¹Ê¶uaPNNF>>?TjYXVRDx¹µÈÉÖÔÒȲ–yuuYD>@S}riaXiw{€€twz‘¦ÀÍÛÔµ·«¨§¦¡››„{}nh`TNJLRKB50349@Tksƒ„…†Š†…ˆˆ‡…‡†††‡‡†††„…„††‡……„†…†…………ƒ‡„………††…„ƒƒƒƒƒƒ„…†ƒ‚€€}|zzz|yvvuusqpmjgeca`_^\\\ZYWVWXVTTTTTTSRRRRRRRSSSRRSSUVTSTTUXWWWWWXXWVVXYXXXZYXZ][ZZZZYYXYYZZZWWVWXVTSRSTSRRUSSTURRRRRRRQQNNNNOMLNKLLLLLKKKKKKLKKJIIIIIIIJLJJJIIIHHJKGEFGGGHGGFGEEEEEEEEDFIGEEDFEEEFHFFFFFEFFFFFGFFEEFGGFFEEEEFIGHFFFFFGGGGGGIHHGHGFFFFFFEDEEEEEDDCDFFFGEFGGGGGGGGHGGGGGFFFFFFGGIGEgeefffhgggffhhhhghjhghijiiiihiijkjjiiiiijkmjkkkljlllllrmnnnmmmponnrotsstxstsutwwwv{wwxzz{{z{}z|{zzy{y{^=8?JHR`iQMNQ^q¢ÝëíìêêééééééèåܽˆZFNVSSY`ŒÂÓåæçèèèèèçæåâÞÁ´²¹ÒáãååæÝÕÚáäåáݺ¬Ÿ•”—œ §«±»ËàαteSJJCFHLTd`i€JCo¦°¾ÂÎÑÖÔÖ«ŒteYK?:>Ydk`^c}”°šz€ut|‰œ¶ÉÝÄÅ«£•‹‘šˆ‚}{xvrogVGE>;95/.///49HWhƒ†ˆ‰ˆˆ‰††Š†‰‡†‡†…‡†‰‡ˆˆ‰ˆˆ†‹†ˆ…ŠŠŠ†ˆ„†…‰††††„„„‡‡ˆ…І…„†‡ˆ„†‡ˆ‡†††††€€~~~}{zxxxz|zxvuromigda^][YXYXWVVWUUTTUVTUSRSTTSSRQSTQSSUTTTTTTRSUTTUUVVWXZYXYZ[XXXXYYYZWVVUVUSSRSRRQQRRRQQQQRONMLNOMMMMMLLMJMMMLMKKLJJKKLJJHGHHHHIIIKJIIKHJHKJHFFGHGIGFGGGGFFEGFEEGGGFEFGEEEGGGFFEEEGGFFGGFFEEHGGFEEFEDFIGGGGFEFHGGHHGIGFGHGFFFFIFEEEEFDDDCCFEEFGGGGGGGFGGGHGGIIIIIFGFFFFFFFeeefffgggfgggghhhhhihhiijhhghkjjjkjiiiiikkkjkjllmmlmmllmopommmopppqoqrstvrtsuuvvxuwwxyyyyyz{}yyy{z{|x|]>:?JIR`\RMKNR`l“¾ÕìëêéééééééèåÛ¡REBLYZ\aa³ãåçèéééèçèèçåãâ๺Ä×áåææææçççåÛÔ×ÙÕÑØßßßÔËÒààÔ¥ytaZUdyvtvzeb]HQl—¨¯µ´³´¶¬£ydQC?69:GMWZfl}žŸ€pqqolt€–³»Â¹®˜Žw|‰}og`adjyf^QQ=1.,--.//135@HYnuƒƒ…†ˆ‡†‡†‡††‡†…†††…‡‡‡†‡„„…†„‡‡‡……„„…††‡‡†„„„„……†‹††„„…‡„††‡†††…„„„„‚„„„„‚€€~{|{zyxyxvusrrmhhgda_^^^]\[ZYXWWUUWXUSSRSTSSSTTTSRTSRQQQRRSSTWVYVVWWVUUUUUUWWWUUTTSQQQQPPPPQQPONOOONNNLOMLMMMNLMKJJJKKKKKLJIJKIHIHHHHGHIIIIIIJIIHGGHHFFFGHGFFEFFGEFFFGFEEEFFFFFEEDDEEEFEEEGFFFHGFFFEEFFFFFEFFFFGGGGFFFFFGHHFFFFGHHHHGFFFFEFEFEEFDCEEFFFEEFFGFFGHGGHGHJHGFFFFFEFFFGkfgffgfgjfhglggiihgiiiijjhhhhikkjjiigihilknkkkmlklmmmnomoppmmnoopppoqsstuttuvvzyyyyy|||z}|}{}~|~{|{v|]?8?IHSahRLILQU_`}¢ËåçèèçèèééèèäÕ™`ND\ilpx†½Ýãâåèèéèçèèèèçå㺴¬ºÎáäæçèèçäÔÙÙÚÛàâãããâááàß¹Ÿœ Ÿœ•”ŒŠ‰p`TKJVf‘Ї†…}ulaUaB?><=@DMRdt}„ƒzpiemtkbo|‚ˆ‡……ƒ|thdoudZUQW]ge]TP<1,,,-../01116<K^l‚†‡ˆ‰ˆˆ‡‡†ˆ‡Š†ˆˆ‰‡‹‡ˆ†Š†‰‰Š†ˆ‡‹‹‹‡‰†…‡‰ˆ…„…„…†‡†‹†‰ˆ‰…ˆ†ŠˆŠ‰‰‡ˆ„ˆ‡†ƒ‰……„‹†ˆ‡††‡†„„…„ƒ€~~|{||{zzyxxwutrpmigc`^[[[ZXXWWWVTTWVTSSTTSRQQRSRSRQSSRTUWSSSRRRRSSRRRRRQPPOPRRROOOOPNNOOPMMNLLLLONMLKKIIIJKKKKJJJIJIHIJHIIIGIILIHHJIHHIFGHKFEEFGFFFFFFHDEFFFHFHFFFFGGFEEGEEDEEEFGFEFHFEGEEFFFFFFFECEEFHGHEEFGGGHHFFFFGGGFHIGHFEEEEGEEEDEFEGFFFDEEFFFGFGGHHHHHGFGEFFFIGGGfffggigggggggghiijjiiiihjhhiihiihiihhihhjjjkkmmkllmmmnmlonnlmnoopnoortsstxtuuvvvwvxyz{|z||{|||{z}z|{x}^?;?JHTaXYXORS`r^Zw¶ÜãçæçèèéèèèâÚ«~i^e››¿¦É¾½Ýäèéèèèèèèçæãξ¶¢Ÿ¥¾ÝäçèèèåãääääååæçææäÔÆ³¦¨µÍÊÅ•€„ŠxlcROMW\YUQLIFEEKSansvvvvwrmt~ˆ–‹‹zookklnfcanstmutvnva`ce[WPNNYf`\VQD8420-.-./011136?JYjt‡ˆŠ‰ˆ‡…‡‡Š†ˆˆˆ‡‡‡‡†Š„‡††…‡‡‡‡ˆ†††‡ˆˆˆ‡……………†††…††‡…‡…††…„………„†ˆ…ƒ„……………‡„………„…„„„…†‡‡†…€€~}||{|}|zywwvspnligeccb`]\[[[YWWWVVUUTSSSSRSTTSTTSQSSRSQPPPOOPRPPPPNNOOOONNOPPPONNLMNLLKKJJJJJJKKJJIHIKJIIIIHHHHHHFHGHIIIHHHHGFGFGHGEEDEFEHFFFFFEEEEFFEFGFFGIHHFGHDDCEEFFFEEEEDEFEIFEEEEEEDDEDEFEFEFFFGGFFEFFFGGFFGGGGFFGDDEEEEEEDDFFFFEEFFFFFFGGHIHHGFFGFEFFGGHEgfegghhhjhjgghiiiiihhhijjihijihhgiliiiiihiiillmmnmqnmmoonmmnnosoppqqttsrrsstvuwwxwwy{{}{|€|}}{{||~|{}_?:@KJTbsfh]TUineTWg¯ÝåçççèèèèèéæãȰ|c|šŒ‚xov‰±×áæçèèéèèèçæäãáǪ˜·ÜãæçèçççæççææçççæÞ¸¢”žÄ¼ÉÇÙ~^lkszZecb\URHD:95?KÃÕÜÛÚÛÛÝÙȶ© ˜ortxyvsoiijrnqsuwvxrmppcWRMRXYWVXQQQOC;.,++,./0001149HWg}‚‹ˆ‡ˆ‰Š‰‰ŠŠŒŒ‰‹‹ŠŠŒˆ‰††‡‡‡Šˆˆ†Šˆˆ‰‰‡‡‡‰†ˆˆ‰‡‡‡Š†††‡‡†…‡‡Œ…†‡†…Œ†‡†††‡ˆ‡†††ˆˆ‰†ˆ‡‡‡ˆ‡†††‡‰‰ˆ‡†…„‚‚}~~}{zzyxz{ywvtsrpmjfc`_][XWWWWVTTTUWWXURSSSRSQQMNMNNMKLMNMMNMMMMMLLLMPOKLMMMKJIIIJIIJJKKIHHHIIIKIHHIHGGHGHHHHGGIIHHGFGGFFFEFDEEEEFFFEFEEDDEFFFFGGGGHGFFGEFFGFGFFDCEFEGEDFFEDEHEFEDDDDEEFFFFFFIFEEGEIGGFIGGGFEHFDDDDDEFDDEFFFEFGGFGGGGHHIIIGGFEFGFFGGGHHeffggiiihghhijiijjihhiiiiiiiijhhgiiiihjlllkjkjlmnmnomlmnnlnpooppqtqqqqsrssssuuvvwzzyzz{{|||~~}}}z{|y~`?<ALKUbpeiwbZbfXMRTdtÑ×äåèèèééééèæÏ«smieeejpŒ¶ÑæééééèèèèèèçãȬ¤•¬¾ÐåèèéèèèéèèèçççÒ¿‹‹Šº·¯ž‰‘„yŒc^\addc]VPLT_ˆµÓäææåæææãßÞÜÀ¬¤Š††’›š™“Œ…vvxxx||u{”s‚poXZZ[^`^VVOOSQN6-++,,-.03100135<FWms†…‰‰‰‰‰‰ŠŠŠ‰ˆˆ‰Šˆ‹‰‰ˆˆˆˆ‡‰ˆ‡††‡ˆ‰ˆˆ‡‡‡†‡†‰‡‡ˆˆ†ˆŠˆ‰‡†‡‡††ˆŠ‡††‡‡ˆ‡†‡ˆˆ‡‡‡‡‡ˆ†ˆ‡‡‡ˆˆ†…†††††‡……†‡††ƒ‚‚€~}}||{zyxwuvwuspoonjgdcb`_^]]^^[YXWWVUUUWQONNNKKKKLMKMMLKLLKJKJMOLLKJJJJHHIIJJHIIHHHFGGGGIHIHHHGGHFEEEEFGIIHGFFGEEDEDCDDEEDEFEDDDDEEEEEFEEEGGGFFFGEEDFDFEEDDEEDDCDEEEEGFCEFECDFDEFEEEEEEEEEEFGFHFGFFGFEHFEDDEEDDCDFEEEDGCFHGGGGHIIIHGFFFEDDEEHGGGefiggikihgghjjlijjjjjiijojjhhhhhhhhikjkljkmlnlqnnnnmnmmnnonopooqrqqqsrssstvutuxwwyyz{||||}~~}~ƒ{|}z~b@;@LKVcqprqoojaOLNOPYf޹ÍáæéééééééèèäݬaYdPMM_Hƒ·ÕäæèèèèéèèèèæäÔʲ›¢¯ÍâåèèèèéèèèèççÜÓµ–‹„ƒ‚‚‚’š®À¯¤¦£’†‰’—š›š™¢¯ÌÝáåææææææææåäãââÞÏ¿ÂÇÔÝÁ¼¹¶¯¦ š’ˆy€}phcaWXd`qombaQDA83/,+.0.-/03322111138GUg‚ˆ‹ŠŒŠŠ‹‹Š‰‰Š‰Šˆ‹‰‰ŠŒ‡ˆ‰‹‰ˆ‡ˆ‡ˆ‰Œ‡‡ˆ‰ˆŠŠ‰ˆˆˆˆ‰‹‰Š‹Š‡†‡‰†Œ‰‰‡‰ˆˆ‡‡ˆ‰ˆ‹‡ˆ‡Šˆ‰‡ˆˆ‹ˆ‹ˆˆ†ˆ†…†‡†††ˆ…„†‡‡‰……ƒ„„„„„ƒƒƒ„‚€‚‚}|zxusqsvuutsqnnlkieb`][YXVUTRQONMLKKKMMMLKJKLJIMIKKKKJIMIJIHHIIJIIIGGGFGEHHHIGHMHGGHFEEEEFFIHHGEEGDDDEFFDDDDDEFHEEDDEGEFEEEFFGGIFEFGEDDFFGDCDEEDDFDEEHEEEFDDDFDDDEFGEDEHFGEEEHFGGGGGFHFFFGFEDDCCDDDEFFEEEGFEFGHHGHIKIHGFGIFFEEFIGGGgggegihhhgghhhhgijkjhiikjghhhhhhhgijkhjmkjmllmmmmmnjnommmnnooopssqqpsrssstvuuuwxxxy}}|}}|~}~~}}}|}{~bA;@LKVepirdmijh\PPOOQZb€™»ÝåééééêêêêéåÞÎd\ep|nbgmÄ×àåèèéééèèèèççäÝÁ¥©©¿Þåèèèèèèèèçæäâßűž‹”¤¸ÅÐÏÐÖÝÜÜßÜÜÜÞßßßßàãæççççççççççççççççåãááäåäÍÉËÌË×ÖÙÊÁ»‰sqndYcd`]`dl|ifaWOC=83/001123567765420.24?FXlt‡‡ŒŠ‰‹‹Š‰‰ŠŠˆ‹‰‰Š‹‡‰‰Š‹‰ˆˆˆ‰Š‰‡ˆ‰ˆˆ‰ŠŠ‹ˆˆ‰‰‰‰‰†Š‡‡ˆ‰‡‰‡ˆ‡ˆˆˆ‡ˆˆˆˆ‡‡‡†ˆˆˆ‡‡‡‰‡‡ˆˆ†……†††††††††ˆ†…†……„„…„ƒ‚€‚ƒƒ‚ƒ†€}}|{zzyyxyzwuttttrppplhfda^[XVSQPNJKIIIKLKHJKJIIIIJJKIIIIIIGFFGGGHHFFFEEDFGHHGGIHGGHEEEEEEEIHGFEDDCDFEBDECDDDDDFEEDDEEEEEEEEFFFFEEHEDDDDDDCCDEEDCDDEEEEEEFBCDDDDCEEFFDDEEFDDDEEFGGGHEFEFFFFEEEBCEFGEDFEEEEGEEGHGGGGHHHFFFFFEGFFHGGGigggjgfhihhhhggiiiihghhkkiihihjihhijljjlmmmmmmmnnnnooomlmpsttpqstrrrrsttstxvux{z{xx{}|~}|}}~~}}}~~aC;@LJUdpkraonpo`VPNNPRSYpŒ´áçèèéêêêêééåÚ¡š˜˜–eb`bmÛáåçèéèèèéèèèèæåÑÀ¨š½ÞãåæççèèèèåÙÓÚâÉÑÌ̹ÜÑÞßààààáâãääåæææçççççççççèçççèçççççççææåæçæåãàááââââÜÙ²‹yonedeepp€nfg\[`ZgQKIF;68;<=>?>?@?=:4000235:JXi‡ŒŠŒŠŠŠ‰Š‹ŒŒ‰‰Š‹‰Œ‹‹ŠŠ‰Š‰‹ŠŠŠ‹‰‰ˆ‰‰Šˆ‡ˆ‹‰‰‰‰‰ŠˆˆŠŠˆ‡Š‰ŠŠ‹‹Š‰ŒˆŒ‡‹‹‹‰ŠˆŠˆŠ‡‰ˆˆˆ‹Š‰††‡ˆ‡‹ˆ‹ˆ†‡Š††…„„‡…………ƒƒ…„ƒ…„„…†‚~|}}{}~~}{zz{wttusrolhd^YTPLKHGHJKKIIIHGGHHHJIIIMHGHHEFGGFFFFFFEEEGGIHHHKGGHHEFDEFEDHGHFGDECBDFCCCCCEDCDGDEEEEEEDEFEDDDEGDDECCFDCCEDDDGEEDEFFEEEDEECDCEFFDEEEEEDEDGGFEEEEFHHHFGGHFGFGDDCCFFGEEEEFEEEEEFFFGHGHGHFGEEFEEGFHGFGhfghgggiiijjhghlhgiiijkkjjihhkiihghjjjjlmmlklmnpnmmmmmmmnoppqqqtrrrsrrttttuvvzzyzyz|}||z||||~€~~~~}„cE>@JJTcojpboiooij\VRPRSXZm{ŸÌÖçèéêêêêéçϳ‚lZJECOY\q‚ËÍ×ÜâæèèééééééééèÙô¦¿Ë×ãåçèééèåÐÊÇÇÇÈǽª´¿ÉÍÏÓ×ÛÝáææçèèèèèèèèèèèèèèçèèèççççççççççèèççççæççææäÙ¯‰…r}spljrtvwjmaVVXVRJKCEJMOQPOPQQTMA7401244479DJ\my†‡‰ŠŠŠ‹‹ŠŠ‡ŠŠŠŠŠ‰‹‰‰‰‰‰‰‰‰‹Š‰‰‰ˆˆ‰ˆ‰ˆ‰ˆ‰ˆˆ‡ˆ‡‰‰ŠˆˆˆŠ‰Šˆ‰ˆˆˆˆˆ‡‡ŠŠŠ‰‰ˆˆ‡Šˆˆˆˆˆ‰ˆˆ‡‡†ˆ†ˆ‡‡‡†ˆ‰†††‡‡††„‚„„„„„‚‚‚‚‚‚‚€€|}}~~~~~}}}||yxxwusrqonf_YQMGFEFGHIIIHGGGGGJHHHHGHGFFFFFGEEFEEEEEGEGHIJIGHHHDDCDDDDFGFEEDDCCDEECCCCCCCCDEEFECDDDHEDDGDDDCDFCBDFCBDDDDDDEEFCEEEFDEECCCCDFDFEEEEDEEEFFGEFEDFFGFFGFFGEDDDEDFFGEDFFFFFFEEFFFFFGFFGFFFEDDDEEFFGHhiihgghhhihhkkkihgiiiikjhiihhiiihghkkjmmmmmllnonnnomnnoppppqrrrsrrrrutwvvuuvxwwz||}||||}…|~€€€€‚eE?AIJT_rqqgphnllld^SOSVVWW^k¸ÔçéëêêêéèŹ]_<:-5GMVfœ¥»ÞåèèééééééééèäÞÊÃÇËÔÓßçèéèæØÇÉÍÔÚѸ¦Œˆqpt…›§³ÊàáççèèèèèèèèèèèèèèèèèèçççççççççèèèèççææææäãÛÜ£wpw~‚|vqda`gkTRNKLKIGHJLV[`csqkb^;0--043768:;=ASht††Ž‹‹‹‹‹‹‹‹ŠŒŒ‹ŠŠ‹Š‰‡ˆ‰‰‰‰Š‰ŠŠ‹ŠŒ‡‹‹ŠŠŠ‰‹‰‹‰‰ŠŠ‹Š‹‰‰ˆ‡ˆ‡ŠŠŒŠˆˆ‹ŠŠˆˆˆŠ‰Œ‰‹ˆŠˆˆ‡‰‡‡‡‡ˆ‰‡Šˆ‡‡‡………‰…„ƒ‡„ƒ„„„…‚‚ƒ„ƒƒ„…€€~~~€}}~}yyyztunj]OFEEFGGGGGFGIHIHKGIHGHJGGFGFFEEEFFFFDFGFFHHIHGJHGEDDECCDGGFEGEDCCCDDCCEDCCGDEFDEEDDDDDDDCDDEDDEEECCDDDEFFDCDGEFEEEGEDDFDEDGGFEFEFEHDEEEFGHEEEEGGIFFFFFFDDDDEEEHFEFGGGGIFGFGGIFFFFFHFFEEDDDDEGFIGggijhghggihhihighhhfiiijihjkijjjjjkmkknnmkmnmnpmnnnmnopqppqrrrsustsrttvuvvvwwwyz{|||||}~~~|€~~~€€~‚gF@BKMPT\dcejilkortwd]\W[kWR\d…ªËåêëêêééÙɰ rL1,7:[axs†n–¤ØÜááåéééêêêêêéèæåã×ÌÓÝåçèèçäâââãßÚ¹¼ŠŠbRNNT[j|ªÖæçèèéèèèèèèèèèèèèèèèççççççèèèèèèèçæææåäãÏË«‰nYVVf‡ˆˆyseYX\][QHEC=766<MTrijn€kiO5.'.11278?EECDHYbox}†‡ˆ‹‹Œ‹ŠŠŠŠ‰‹ŒŒ‹‹‰Š‰‰‰ŠŠŠ‰‹‡‹‹Š‰‰ˆŠŠŠŠŠˆˆˆ‰‰ŠŒŒ‰‰‰Š‹Š‹Š‰Šˆ‰‰‰‰ˆ‡‰ˆ‰ˆ‰‰ˆˆ‰ˆˆ‡ˆˆ‡†ˆ‡ˆ‰ˆ‡ˆ†‰‰‡‡ˆ………ˆ„ƒƒ…………†‡†ƒ…†…€€‚‚‚€€}~}|{{zzzzwwxpiYJFBBBCEEEEFEEGFFFGGGHJGFEGEFEEDEDEGFFEEFHGIIGGGFEEEECDDDEEEFFDCDCCBCCEDCBCDDEEEDDEEDDDECCDCCBDEDCBCCBCBCCCEFDEDDEDDCCDCDCDDEEEEEEEDEDEFFGFEEFFGFEFFFEECDEEEEDDEFGGFFEEEFEFGFEEDEEEEEEEEDDDDEFFFfghhhgjgghiikillmihgjiiikijiijjikjkkkmoommpmlnpnrpopppqqrsusrrtusuttwvyvywxzzz{{€~}}}€€~€‚€€‚‚hG@CMLLKQ]mhsstkqsqrrqg_bcYQRV]|¥ÈãèééêééæãѸm::ARdw‰›³ÏÚÜØ¾¹ÍÚáåçééêêééééèçàϘª½ÓäæèèçççæÝ¹’„jgVSJLKIMpˆÙåèèèéèèèèèèèééééèééèèèèèçèèèèèèèèççççäÓ¿£•„vd[SNPX`enjiaZ\_[UPKI99*),:Q_ke_ejYH2,-5=A?=AHVbZTRT_agnv„†‹Œ‹‹ŒŒŠŠŠŠŠ‹Œ‹ŒŒŒ‰‹ŠŠ‰Š‹ŠŠŠŒ‰Š‰ˆˆŠ‹‹‹‹‰ˆ‰‰ŠŒŠŒŒ‹‰ŠŠ‹‰Œ‹ŒŒŠ‰‰‹‰Œ‰ˆˆŠŠ‹‹‹ˆ‰ˆ‹ˆ‰ˆ‡ˆ‰‡‡‡‡†‡‡Š‡‹†††‡†ˆ††††‚„„‚‚‚‚€€€~~~}{||z|zuniVGA?@ABBCDDDEHGFEHGGHJGFEGEGDDDEEEGEEEEEEFHKGGGFEFFEDFEEDEEEDDDEDDBBBEDDDECCDDDDEFEECCCBCDCCCCCCCBBCDDDCCCFFDFCCCCDCCGCFCCDFEDDDEFEEDDEFFEFFFFEEDGFIEHEEFEEFEDEGGGFHEGGGFEFFEGDEEEDFEDDDDEEEFGFgfhgggggggiijiiiiihhijihjhjhijjijjmnnnnonlonnmonqppppppprtttsrtuttuuvvuuzwz~{||{}}~}~~~~~€€€~ƒiHABMPMNRSX]ejlkqsq€q€e_d]XPRSW\x•»ÞäèéêêêëèÌŸ‡˜«ÁϽÐÕÚÜÙ¹{[|š½ãçéêêééééééåÚ´“¡°Èáçééééçᮣ‚{uuw†™Š|~…ÖæèééèéééèéééééééééééèèèèçèèèèèèèèèèççãÜ£“ƒ{ebZVRTV[Vb`gdYMKKMOML?<,%%&-69:<AEI@522DhfbTMRalxjabfecdfpx}††‰Š‹ŒŒ‹Œ‹ŒŒŒ‹‹‰‰ŠŠ‰‰‰ŠŠŠ‰‰ŒŠŠ‹Š‰ˆ‰‰‰ˆŠ‰‹‹‹‹‡‰ŠŠŠŠ‰Œ‡ŒŠŠŠŠ‰‹Š‹‰ŠŠŠ‰‰ˆŠˆˆˆŠ‰Š‰‹‡ˆˆˆ‰‰‡ˆ‰‡ˆˆ‡…‡ˆˆ‡‡‡‡…‡‡‡‡†……ƒ„…ƒƒƒƒ„„ƒ‚€€~}~{|}{}~yum`M=<<>AAABDEDDBDDFGGGGFFEFFFDEFEGEFEDDGEEFGHGGHFFEFEDFFFEEFEDDDDCCCBBCECCEACFEDEEFDECCCBDDDCBCCCBBBBBBCCEEEEDDCBEEFCDECEBCCCCDEDDDDEDDDEEDDEEEDDEEEEEEEEDEFFGEEHFFEFEFFFFFFEDEDEGDDEDEEDDDEFGFFgfiggfhgggiimiiiijjhhjkkjjjjkjjjkknnonnnooonomqqqpppqqsrrsvttsssssvvwwwwzw||z|}}ƒ€€€€ƒ~€‚„jIBEMPNNNLPXhjtrsrqvrpmic_VQQRRUYp‹¬ÏØåèëëëéèãÞßàß׫ ‹’—˜’…dXF‰ÛãçééééééêééäÙ®œœ¦ÂÞäèèéèçäàâßÞÜâßààáââáåèéééééééééééééééééèèèèéèèèèèèèèèçççæåãâ´’Œ|qkhgk~mjmptwkZJ>:>EMKC7.+*$!#%&')-5>BBFQrŽyj]Z^tx{eggka_^ahoz‰ŒŽŽŽŒ‹ŒŒŒŒŽ‹‹‹Ž‹Œ‹ŽŠŠ‹Š‰‹Š‰Š‰‰Œ‹‹‹‹Š‰ŒŠŠ‹ŒŒŠŒŒ‹Œ‹‹‰‰ŠŒ‰‰‰‰‹‰ŠŠŠŠŒ‰‰ŠŠŠˆ†ˆ‰ˆŒ‰ˆˆ‹‰‰ˆˆˆŠŠŠ‡‡ˆ‰‰ˆ…†……†ˆ…„„…„„‚€~~€||yxhT=99=????@ABBBDEFEEEFEEEIIIGGEEFFFFDDDEEFFHGGHGFGGGEEFFFFFGEFDDCDCCBBDBCEDCDEDEEFEECECBCCCCBEDDDCBEBDCCCCDCCEBBBCCCDFDDCCCDCCCCDEDGFFEEEGEEFGEGEGFEEEEEEEFIGFFHGFFDEEFEFHFFFFEDDDDFEEFDDDEGFEFffhhgfgggghhjiihghhhhkjjjljjkikkkkmnnnooonnnnnppqqqprssssssstutstsuuwwwxxy|||~~}~}€€€€€€€€€‚€„kJCFOONNNNRQVZ`fmrsywxlhkwf^WQSTVZft¬ÃßçëêêëëëëêçÛyiKl‡—££¡—‹–¬Æáäæåçéééêêêèá´©žœº×áèêêééééèçèéééêéééêêêêêééééééééééééééèèèèèéééèèèèèèçæåäâáàÑᣇwzxy{zytuswyqfVNIGEDB8-*(''"!"%$$)/:db\f‰xjg``aacffc[XX]bgjrx|€„ˆ‰Š‹ŒŽŒ‹ŽŽŒŒŒŒŒ‹‹‹Œ‹‹Š‰‹‹ŠŒŒ‹Š‹Š‹‹ŒŒ‹ŠŒ‹ŒŠŠ‹Œ‰ŠŠŒ‹ŠŠ‰‹ˆŠŠ‰‰Š‡‹‰Š‰ŠŠŠˆŠŠ‰Š‹ŠŠˆ‰‰ˆ‰‰‡ˆˆ‰‡‰‰ˆ‡ˆˆˆ‰‰Šˆ‡ˆ…†ˆ‡‡‡††…„„„„„„€€€}|{xp[B:479;>??@@ACCBCCDEEDEFFEFEEFFEFFFDDDEHEFGGGHHEGFGFFFFDFGEDFDDBBBBBBECCDCCCDDEEEDDDDCCCCBBACCDDCBBBBBBCCCCBCCCDCBCEDDDDCDCBCBCDEDEEEEEDEFFGFEEEFFEDEEFEFGHGFFGGGFFEEFFEEDFHGGEDDDEHEEFEDDGFEEgfghgfhhgggijjihihhhhijjiiikjknmnmqnonoooonnoorqssrruuusttvtxuuuxuwx{wyz|}|||€€€€€„‚ƒ„……€€~‡mKDFOOOOOPPMNRZbpvqtjoieruongVSSTTV[cz‘¶ÞéìíîíìëëéÞ£mflޤÉàààßÞßßáâßÛÚàààäéééèåÏ»±ž›¡ÛÝãçèééêêêêêêêêêêéêêêêêêééêêêêééééééééééééééééèèèèæåáÝßÕÅ·²ª¢™‰ˆgnooruz|trty|~mgfbQC</)&#$$$ $)06CKUr|œŠˆx{b^QHMU\cRNQUZ^^ahmry†ŠŽŽŽŽŽŽŽ‘Ž‹ŠŒŽŒŒŒ‹‹‹Œ‹‹Ž‹‹ŽŒŒŒŒŠŠ‹ŒŽ‹Ž‹Œ‹ŠŠ‹ŽŠŠ‹‹‹‰Œˆ‰ŠŠ‹Š‰‰ŠŠŠ‹‹‹ŠŠŠ‰Š‡ˆ‰‡ˆˆˆ‰†‡‡‡ˆ‡‰‰†‡ˆ†…†ˆ…‡„†…‚‚ƒ€€‚‚ƒ~~}ujO;3469:<>??@@@ABBDDDDDEEEEFGFKGHFFECDEEEFGGHHIEHGGEFEFEEFDCFDCCCCCBBCCDDDDCDDDEDDDDCDDCCBCCCBFDCBCBCBCCEDCBCCDDBBCCCDDDCCCCDEEDFDDEFFFDDEEFEEDEEEFEEEEFFFFGGGGGIFGFIFGFFGFGFFFDECEDDDDCDEGFEDgfhihghjhkiiihggilihhhklijjjkklmmnqoomooprqqppppsrrstttstttstuvvvuyz{wzz||{z||~€€€€ƒ€‚ƒ‚‚€ˆoLEFQQPOOOOOPPSTe}twrskhw}}hb]WWUUUY]mz¤ßéïñïíìëëåÝÝʺ°ÃâêêëêèåáÜ¿€{Š«ÜåæèéèäâÙȺÂÝÝÞàåèéêêêêêêêêêêêêêêêêéêêêëëêêééééééèéêééééèèèèèæãÏ»§˜‘Œ‰†ƒsgVAHRejiijjrt~…{uokf_TE>70+(%&!! !#(+/2;BR]l~~~oiXI?:;<>??ADHNUZ`chnpuy}ƒ‡ŽŽŒŒŒŒŒ‹‹ŠŒŒ‹ŒŒŠŒ‰Œ‹ŒŒŒŒŒ‹ŒŠŒ‹Œ‹ŠŠŠŒ‹‹ŒŒŒ‹‹ŒŠ‹‹‹‹ŠŒˆŠŒŽŽŒ‹ŠˆˆˆŠ‹ŒŠŠŠ‰ˆˆ‰ˆ‡ˆˆˆˆ‰†‰‰‡‡‡ˆˆ‡ˆ‰‡†‡†ˆˆˆ……„†…„ƒƒ‚‚ƒ~€~zmX?00158:<==>>?@ABBBCGDDDFFGHFFFEEEGDDFEEEFFGFFEGEFEFEFEEFECDDCCBAB@BBCEDEDCDDDDEFDEDDCBBBBBAAABCBBBAACBCBBBBBBCBBBCCDEGDBDDDDEDEDDCDEDDDDDFFEDDEDEFEEFFFFFFGHHGGGGEHFHGFFFGFDEEDDDDDDDDDEFFEFjiijjhgjhiiiihghiijjkhlkjjkkmmmnmnpooorrqqrrrrqqsrutttttuvztuwxvyxyz|z|}~~||}€‚ƒ‚‚ƒƒ„‚‚ƒ‚€‡qMFGSQQPPPOOPOON]tŒvptsvz|~}uka[WUSSUVV_k“ÁÛïñòðîìëéçåãããçêêëëçफ़”dC„ž†{²ÝÝÛáåææåäãâåçáÛâàäçéêêêêêêêêêêêêêêêêêêêêêêééééééééééééèçæåãàÞ¿¥Žz…‡‰ŠŒ‹zqM7+/Iuvqg^dqsˆ}zvvpnol_TNF:1*(%$##$'*--2;@DHWdb[ULB85142669;>@GMQX\_cfjov‰”“”ŽŒŒŒŒŒŽŒ‘Œ‹ŒŒŒŒŽŽ‰ŠŽŽŒŒŒŒ‹ŒŠŒ‹ŒŒŒŒŒŒ‹‹Œ‹Œ‹‹‰ŠŠ‰ˆˆˆ‰ŠˆŽˆ‰‰ˆ‡ˆ‰‹‡ˆˆˆˆ‡†ŠŠ‰‡‡‡‡‡†„„ƒ‚‚…‚‚~{zbB0,-269:<<<<>>>@ABBCCCBEFFIFGFFDEEDDFFGDEFHFGFGGHHIFFDDEFDDCCBAACBEBCDCEGEGDEEEFDEEDCBAABA@@ABBB@AAADBBBCCCCCCDCCDEEEEEDDDDDDDDDDDCEDCECCFIDEDCCDFHFGFFFFFGGHHGHHHHIHGEFFFHEFFEEEDEEEDDDDEEFhghkhghkijiihhgjjjjjkhjjkklllmnmnooooppppppqrrrrssttuttuuvvuw{xvxxyz{{{|}~|}}~‚‚‚„ƒ‚‚‚‚„€€€ƒ…pOGHSSRRRSPOPPPPXZ[ajxvxz|xxxxwsgc]SUWVV]f†¤ÄçòôñîëëêëìììëêëëëéÛ´‹hu𣧢¡„ Ãâçééêêêêäת¾ÔâçêêêêêêêêéêêêêêêêêêêêéééééééééééééèãÞßܶ§šŽ‡vz~…ƒ‚}p`N4.%4KxŠ€{l^flrvvvtromouf[OC90121/.0//*')-0136?JA;74:??=:67688:;?CIOTX^cgjqxŒŒŒŒŽ‹‹ŒŒŽ‹ŽŒŒŒŽŒŒŒŽŒŒŽŠŒŒŒŒŽŒ‹ŒŒŠŒ‹ŒŒŒ‹‹‹ŒŒ‹‹‹‹‹‹‹‰‹Š‹ˆŒŠ‰ˆˆ‰Š‡‰‡ˆŠ‰ˆ‰Š‹‡‡‡‡ˆˆ‡‡†‡†‡†‡†………„„‚…‚…ƒ‚€}zjI0*(-269:;;:<=>@@@AABCCEEEFFEEEDEFEDFEDCDGFFEEFGEDDDGDDCEDDBBBBBBAAACCDEEEDDEFEEDCCCCCBABCAABCBCAAAAAABECCCADDDECCDGDCEGCCDEEFDEDDDEDCCCDFFDDDDDDDDDEEFHFEFEFFFHGFIHHGFFFFFEGHFFFDEEEDDGEEEFhhhhhhiihjihhggjkkkkkkmllllmmnqpporopqsstrtrvsttvttuxxxxwvvwyyxxxz|{|}}~~}}~€€ƒ‚‚„ƒƒƒ‚‚‚ƒ‚„„…„ƒ‚ƒ‡qOGISSTRRQPPPPPPQNOWiz†‚€|uqpxz~–vdVUUUUUZ_l„²áðóòñîììììììëëëëëæáâàÒÁyzzrj]J”£ÆßáàäèéêæÚ½®°¹ÕæèêêêêêêêêêêêêêêêêêêêêêêêêêêêéééèåÚ®”uz|€†€‡…„ycYC710;W}Œˆh`dt~‚„ˆumjmsw_YKA<?AA?:850'"!"""'+-*'(/EV_MB;9888789;>AFKQV[^cjt€ŒŒŒŽŒŽŽŽ‘ŒŽŽ‹‹‹ŽŽŒŒ‘ŒŽŒŒ‹‹‹ŒŒŽŽŒ‘‹‹ŠŠŠŠŠ‹ŒŒ‡ˆ‰ŒŠŠŠŒ‹‹‡†ˆ‰‰‰ˆŒ‡‡ˆŒˆŠ‰ˆ†‰…„„ƒƒ…‚ƒ€|nQ4)'(-6889;:;=>>>@@@CBFEEDGEEEEEFEEEGDDDDEDDEDEEEEEDGEDDEDDCBBBBBAAAEDEEFEFDDDDEDDCCECECABBBBBCBBABCDBBCCDCCEDDECCDDCDFDCCDDEDCDDDFDDDCCDDEEEDEEEEFEFEFFFEGGHEFFFFIHHFFHGFFFGFFFEEFEEEEFFFFFhlhhhhhhhjilknhikkkklmmnnpnnnmoppopopqrrsrssusttuuuuvtxwwwwxxwyzzz{y|}}~~|~~~€€‚ƒ‚‚‚ƒ„ƒ‚ƒ‚„ƒ‚‚ƒ†sOHITSSSRRQPRSX^UQSSaqknw{vqs…}vseVXUVYXX[]kwŸÒàôöòíìëììììììëëëëëêæÍœ‹nlgfi~˜¨ž…šÌÜèëèàÖÊÀÁÉáæëëëëëëëëëêêêêêéêêêêêêêêêêêêééèæß³qhPiux|}}|zzzz{xvtn]KUb‚’‹†r`Wiƒƒ‰wogool^TKHEFINSIB>8-#! ! !!"$"!&.BdVKB><<;:867788;>CHOX^bkp|Š’ŽŽŽŽŽ‹ŒŽŒŽŒŽ’ŽŽŽŒ‹Œ‹‹‹ŽŒŽŽŽŒŽŒŒŒ‹‹ŒŽŠ‹‹ŠŠŠŠŠŠŠ‰Šˆ‰ˆˆˆˆˆ‰ˆ‰ˆˆˆˆ†ˆ‰ˆ‡ˆˆŠ…Šˆˆ„………‰ƒƒ†€‚‚€pY;,$&*1688::;<===??>?@ABCDDDDDDBDDDDDDDBDFDDCCDDDDDDCCDEDCCBBBBBBAAABCCDDDFCDDDDDDBCEBCDBBBBBAAABBCCCGDBCEBCCBCECCCCCDDCCCCCDCDCDDDDDCCCCCDDEDFEFGEEEEEEFEFEEFFFFFGGFEFEFFGGFEFFEDEFFEEEFFFEiiiijjjiiiijjiijkkmklnonnnooonnqssspqqrtusrsuuwvuvyw{wxwywwy|y||{{||||}~€‚‚€†‚„„ƒ‚ƒƒƒƒ„ƒ†‚ƒ‡uQIITSTTRRRQTXbeXPQQUW\fz{ytt†z~tsw\ZUVWWWXZ^dl°ÕîñóñïíììììììììëëìëéåÜÒÉÈÆØ×ÚÔŸˆ_O„žäêéèçæäâäæèëëêëëëëëëêêêêêêêêêêêéééééèèççæãЦ…lu„’™—•˜™Š”´¹½ÄÎǺƒhr‰™‹‰|aR`v‰‡ŠyxtrsmbUN\TOQZUUGIA3($"#$$"! !%1@OKG?@?>>;:6556789<?CJQW]cmx†’Ž’ŽŽŽ‘’‘“ŽŽŽŽŽŽŒ’ŽŽŽŽ‘ŽŒ‹Š•‹‹‹‹ŒŠ‰‰ˆ‹‰ˆˆŒ‰‰‰‹ˆ‰‰ˆ‡‹‰‡‡‰ˆŠ‰Šˆˆ††………„„†‚„ƒƒvlE/%$'0488:::<==<=>>???@BCCCCDGDEEGEDDDEDDDDGEECCDFCCCDCDDDDCBBBBBB@CCCCDDEDGEHCBBBCECCCCCEBAABACCCCBCDCBCBCCCCCCDEDEEDDDCBCDDDDDDDDCCDDDDDEEEFEGFEEGEEEGFGEEFGGGFGGFFIDEFHFFFFEEDEEHFFFHFEEiijjjjjjiiijjjjjjkkjlppnnoooppprqoqqrqsttvssuuwvvvvvwwwvwwx{{yzz{|~~}|}~~€€€ƒ‚‚ƒ‚ƒ€ƒ…ƒƒ„ƒ†‚‚‡wRKJUXTVTSTU\agrWPRRTVYX_gp~†|upl^ZUUVVVXY[\bh‚˜¼åóõòïíìììííììììììììêçæäáÞÕǨ€Zmˆ´àéêëëëëêêëëëëêëëëëëêëëëëêêêêêéééèçæåäããàØÒÍÁ´¹½ÎáàÞÕÌÉŸ®¾ÖÑÐØàÔÆ›|†Š‰ˆ‰„gLYi€woorvzri]U\YUQOLF@??:6/(((((&&'(-7HQRQMIE@>86544766689<>ELT]dgq{Ž“‘ŽŽ‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽ‘ŽŽŽŽŽŒ‹‹‹Œ‹‹‹Œ‹Š‰ˆˆˆ‰‰‰ˆŠ‰‰‰ˆ†‰Š‰‡‰‰ˆ‡ˆ‡ˆ†ˆ‡‡‡‡†………„„ƒ„ƒƒyrP5%#$+1979::<;;=;<@?>??@AABBDCCDDDEDDDDDBCBEFDBCCDCCCCCCCDCBAA@CBBAABCDDEEDDCDCCCCCDCDCCBBBBBBABBCCCCCCCCCCCECCCDDDDFDDDCCCDEDDDCDECCDCDCCDEEDDGFEEEDDEFFEEEGGGFEEEFEFDEFEEEEEDEDEEFFFFFFEDhjkkjjjjihjkjkljjlllmnpprpqqqqpqpqurusutsttuvvvv{vvwxxwwxxyz}}}||}~~}€~€€…‚ƒƒ„‚‚‚‡„„ƒƒ„‚†„…ŠwSLKWVTVVTUWetsnXQRRSSRQS[j€‰{‚ur}]ZVUWVWXZX[[_cq„²áðóòòïíííííììììììììëëéÜ×—xŒ|ˆÂÅßäêëëëëëêêêëëêêêêêêêêêêéééèèèçççææäâÜÕÐËÆÅÈÔãäããäåæåäÝ×ÏÆÄÈÚææåæçäá²’ŠŠ‹ƒrNRjzxhcgqˆvgdd_[\^`J<=DACHC3/-,,,.015Hbkusoa\IA<76666766789;>CHOWYbio|Œ–Ž‘‘‘ŽŽ’‘‘’‘‘‘‘ŽŽ‘‘ŽŽŽ“ŽŽŽ“ŽŽŽ’ŽŽŽ“Ž–‹‘ŽŒ‹‹ŽŠŒŠŽ‰‰†‰ŠŒŒŒ‰Š‰Œ‰Š‰‹ˆ‰ˆ‰†ˆ…†……ƒ‡„„~w^<'#$(-98:;::;;>;;;<>?>???@@BBBDDDCCCCDDBCBCDCAFFFCCCDDFDDCCBAACCECDBCCCDFCDBECCDDCDBBBCCCBBBBAABCDCCCEECDCCCCCBCECCDDEDCGDDDFDCCDCCCDCDCDDEEHGGFFEDDFEEEEEEFFGHFEDFEFEEEDEHEFEEEFFFFEEGFEDijjljjjjjikmmllklmmmmmpopoqrrsqrqrurttutttuuvvvvvvwwxyxxxxz{||}|}~~€~|„‚‚ƒƒ‚‚‚ƒ‚‚ƒ‚ƒƒ„„…‹zTMMWVUVVTXZj‰nm^TTSSTSSUVdysz„utz\YVVWVZZ[\[[[^bny Ðßôöòîííííììììíííìììêáѽ°¤–““®ÙåéìëëêêëëëêêëêêêéééééèçææåååàÝÙÖÔÓÓÉÅÁÄȸ´ÀßââããåèééèçæáááåæçççèééæáÎÀ¤‰ƒwaD[†‰‹q_alpxg]adefekd[KLIHLRA79A@@?>>>F[j‘ŽŒ{qWKIGB=:88777777::;?DKRZZ^gu‚Š‘’Ž‘‘ŽŽ‘‘ŽŽŽŽŽŽŒŽŽŽŽŽŒ‘ŽŒŒŽ‰‹‹‹‰‰‰Œ‹Š‰‡ŠŠŠŒŠ‰‰‰Š‰‰‰Šˆˆ‡ˆ‡†…†…„ƒƒƒ‚ƒzjD*$!&+38;:::;9;:;<====>>>@@@AAABBABBBBBBBBCCCBCDCCCDDDGBCDCBAACCCDDBCCCCCCCBDCDDDCEABBAAAAAAABBABACBCDCCCBBBCDCCCCCCDFDBDCDBCCCCCCCDCCCCCCEDDDEEFFEEEEEEEEEDEEFFEEEFFFEEEEFDDDEEEFFHFDEEFEiiijjjkjmlonmlllmmnoqnppsssroppqqrutyuvuyvuuvwzwxw|xyyyy{y}}}}~}€~}}~…€€†ƒƒ€ƒ‚…‚ƒƒ‚‚‚ƒƒƒƒ„‚‰†‡……‹{UNNWVUVUTZ]ruoo`YVQSTUSQQYdjkq{tuw[YVXWWXWXXXXZZ\^em°ÕïòôñïîíììììììììëëêéæãâßÃ¥™›ÚäèêêéééééèèèèèççææææåäÞÙ×ÕÒÒÔØÎÔÀÏÒÍÆ¿Ã®¡¥²¸¾ÈÓÐÐáãæèèèçæçèçæææèéééçåÚ׬ˆ}_]j”¶’{e[cco\[_hsuqnl^SPRKOL@<BQ^[VRNOSUnwˆ„€qURUY^J?96777666899:=AHOPXbft|€‹‹“‘”‘’‘’’’•‘’’‘‘Ž‘‘“Ž’‘“’‘ŽŽ“ŽŽŽŽ‘‘ŽŒŒŽŽŒŒŠŒ‹‹Š‹‰Š‰‰‹Œ‹‰Š‹ˆ‰ˆ‹ˆ‰‰‰‡†‡ˆ…†ƒ}qN/&!#(28;:::;:;::::;=<<==>>??AAABBBAAABBBCDDDCBCBCDCCDGCCDCCBBECBCDCEDDDEDCCCCDDDDEBAA@@?@AAABBADBBBBCBCCBCCCDDDEDDCDDDCEDFBDCEDCCCCBCBCDDEEEDFFFFFEEEGEEFEDEEEEEEEFEEEEFGHGGDEEFEEFGFFFFEjjjjiikkkkllmmllmnnopnoqqqrrqpqsssssttstvvwvwxwwwwxxy{zyzy|||}}}~}~€€€€‚‚‚ƒƒ‚€‚ƒ‚‚ƒƒ„„„†ƒƒ†……„…Š}WQOVVVUUU[^qtrqqr^RTSY_\\\ZZZi…tvw\YVXWXXYYXYZZZ[\^cj‚™¼çôõñïììììëëëëëëëêêêêêèäÝÉÑÝáåæåääãâàßàÞÛÖÖÔÏËÈÄÂÂÆÊÒÓØÛÞäÜØØÝßßµ¬››Šœ™šœš–›¤»ÜæééèèééæâÙâçééééèèçà½©š‰•Ÿ›Œvlgjlgachovi^[YTPVLPNI@IU_k^ZWQNKPX`gpqidipbQ><887777687789;?CHSY_ips}…‹•’‘‘‘‘‘‘‘‘ŽŽŽŽ’‘’ŽŽŽŽŽ’ŽŽŽŽŽŽŽŒŒŒŠŠŠŠ‹Œ‹‰ŒŠŠŠ‰‰‰‰Š‰‰‰‰ŠŠˆŠ†ˆŠˆ………†‡ˆ„†ƒ€xX7*!"'/789:::::9:89:::;;=>>>>@A@@?AA@@ABAEDBDCDCBECBCECBCDEEBBCBCBCBBABBCDDDBAAABBBBA@@??@AABBAACBBCBCCCCBCDBBCCDEEDDEDBCCCBCDDFDACCCCDDDDDCDDEDFFFFEDFEEEDDECEGEEEFFFDDEFGDEDEDEDEEEEFFEEjjpiikolollllnnmnoppooopqqrsurssstutwvyvvxuvwxyw{yyx{{||||{|||~~~}}~‚€€‚€„ƒƒ‚ƒƒƒ„†………„ƒƒ‚„ˆ†………Œ[SRVWWVWX\`qsxsrueZUT]ewjZSSU]elxz[ZWWXYYYYXYYYYZZZ[`cqƒ³ãðòòñïíìêêêééééèèèèççæãáàâãââߨÔÏÌÉÄ¿¸±®¨£žž ¤¬¸ÆãßåææåäãáÞÞ;¬¤Ÿž¥««©¡›—’—Êâçççèçäл°¸âçèéééééæàÌÅ–œ’”¡¦˜ˆ‚€€wpopnc\[^d_\[[^_[Y\ac^WbMJEADJUmp”yulQ=;8887766666788:;?IPZgpux}„‘Ž•—“–‘••••”‘‘‘‘‘‘“““‘‘“’“‘“‘’’’’’‘‘’’’•‘ŽŽŒ‹Œ‘ŒŒ‹Œ‹‹‹‹‰ŠŠŠŠ‹ŠŠŒ‰‰ˆ‰ˆ‡†…‰‰‰„‡†ƒ{lB.#"%,668:;;:<9:9<::9=;>=====>?@?AABA@A?BDCDCBBBBBBBDCCEEBAACCCDCDBCBCB@ABCBBBAABBAA@A@B@AAEBCBCCCCBCDEECBBBBDDEEHEDDCBBCCCCCCCDBCCCCCDCDEDDEFEGFFGGGFFHEFFFFFFEEEEDCCEFFGEFEGEFEEEEEFEEEkkkikllknnmlnpoppppooqqprrsutrstuuuuuvwwwxvvxyyw{yxy{}|{{{|}}|}~~~}|€€€ƒ€ƒ‚ƒƒƒ„„…ˆ††…ƒƒ„†††††Ž‚^XUV\YYWXZ`pvvrqrmjbYbfp}_TXZ[YdudYYWWXYYXYY[[\ZZYYZ__`mz¡ÎÞòôñíëèæäãáàßÞÝØÓÎÌÉþ¼ºµ¯«¨£ž™–Їƒ|ywwx|Œ–£µÅßåææÜ˶§™šœ £©³¾ßááÞÐÉĬ¬¦¹àäåæãÕ»¤–ƒÚååééêêééáÕœ|‡ž³¾Ì«—•–‡{zyn]ZYacfiq|{wslgd[SNKFD>:=CWmpqu{mUF>;98876686677779:CJXdkqyvyx‰’’’‘’‘’”‘‘’‘“‘‘‘‘‘’ޑޑ’Ž‘“‘‘‘ŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹Š‰‰ˆ‰ŠŠˆˆ‰Š‰‹‰ŠŠŠŠˆ††††„‡‡ƒ}rN4$""(258889999888888::;<=<<===>>>>??@B@AAABABCCBBDDEDDDGBABCCBBBBBBBCD@@AAAAAAA@B@@@@@BAABECBABACCBBDDDBBCCCCDDDECDDCCCBBCDCCCDBBCCDCCDEECDEDDFGFGFEFEGDEEEGEDEFEDDCDEFFFEEDGFFEEEDDEDEEolmllmmmrnnmnoopqpqppqqqssuuvvuvxuywzxwwyxwx|y{{{z~|{|}~~~~~}€~~~€‚ƒ€‚‚ƒƒƒƒƒƒ„„„„„„„†‡††…†„…†‰‰‡Žƒa[XUWXXWWY^q}t{z{z}`bh€…eTYYVTXb_YYXXXXXZZXYWZYY[ZZ\[]^enޱÔíïðëãâàÖÌ·±¬¥Ÿš–’Œˆ„~zwtqnjigdba__`abdgovŠ™§ºÎÙâܺ«ˆ‰lq}”¯°ÈÎßàåÒᾯ¨¨³×ØÙæßÃŒ„ªäçèêêêêêçØ¡tlq‰¼±ÓÐ˳¨§§œ‰‡‰‰qbhoqtz‰ˆ†…ˆzyk[NJIBA;94CNaZTQck]OGC;8897679677879:@FVbhqz|~|{€„†““”š”––—’“’’’‘’“““‘•”•‘“’‘’’’”‘‘’‘‘‘’‘•‘‘‘’‘‘”““’‘Ž‘ŽŽŽŒŽ‹Š‹Ž‹ˆ‹ŠŠŠˆŠ‹‰‹Šˆ‰‰‡ˆ‰‹†Š††~z[9&"!%,4798:9;9879889::;<=<<=<<;<>>>?@@?ABAEBCCEABDECBBBBABCCEBDBBBAAFB@ABBBCBBDABA@@@ABABCEEDAABECABCCDBBCCCCCEEFGGDCCCCBDDDDCDCBCDDCCDDEDEDDEFFHFEFFEGDFEFEDDFEEEFCDEGGGDDDFFEFFEDDDEFDmmllmnnnroomosqqqqqqqrssssuuuuuvuuyvwyxwyyxzzy{zzz~|}~}~~}~}€~~~~~}€€ƒ‚ƒƒ‚ƒ„„……†…†…………………ƒ……†…‡…eb]YVVWXXYZh|rovbmulafiŠiWf{gVZ[YYY[XYXWXZ[\YYYX\[[^[\\^dj„™»ßçâ¶¥›”…€|yvrnlihfdba``_^^^]]]]]\[[\]^`ekpxƒ›¬»ËÚÕа“‡x†Œª¬®°¼ÓÑÐν³¨ª¯ÕßàåãÀ©˜¡«Ååêêêêêëêä´wkcu‹¤®ÍÕÏÈÆÄ¾·«ž™”‘’™—‹‡…„{wx|zzVLKKEIIHKUm}p^^ehe[OE><:976676678778?CR_hq{~|zyyz‚…‹“““’’—’’‘‘’“”’’’’’•““’’’’“‘’‘‘‘’’‘‘‘ŽŽŽ‘‘‘‘ŽŒ‹Ž‹ŒŽ‰‹ŠˆˆˆŒ‹ŠˆŠ‰ŒŠŠŠŠ‹‰ˆ‰‰Š…‡…„€~kB*#!$(268788998888889:;=;;<?<;;<=??>>>?@@@@ACCBABCCCBDBCBBBABBBABBAA@@AABBBCBBBAAAA@AABABCBABABBCCBBBBBBCEEDCBBBDEDCCCDEDDDDCCCCCCCDCBDCDCDDEEFFHEFGFFFDEDEDDDDEDCCCEEDDDDEEFFEDDDDDDFDDqqqssrrssqqrrsrssstqruwuuvvuwwxx{wyxwxxy{yyyzy{{|~|}|}~~€‚‚€‚|„„„„„„„‚……‡†††…†…„„…‰†……‡‡ˆ‰ˆŒ†pjgaZTUXXYW^gjoreenh]ck‚nhrwWXWWXXXXWXXXYXYXYXY\[[[ZZZ]]`es¢³ž‚|jfcbb_]]]]\]YZZZZZZ[[ZYYZ[Z[ZZZ[\\Z^`ehqy…‘¡³ÂÛÐßĵ«¡¡£¢Ÿ›ž¶ËËÌɼº½ÀÐÞåæçÚËÆÆÖáæèèçççççä¢sip~’Ÿ¯ÉÒÙÛâäãØÇ½·©£¥²´£œ•‡…urpt_TJB=?CO}xs‚Ÿš˜€uy}sh\JEB@=97666678777=AQ^hq}€‚‚ƒ‚~}‚ƒ‰“”–“˜’“”˜’–“’’’’”••““’““•’’““’’’˜‘‘‘’‘’’‘“‘•‘’‘–‘‘‘“’’“ŽŽŽŽ‘Ž‹‹ŒŽŽŒŒŒŒ‹‹ŠŒŠ‹‹ŒŒŒŒŒ‰‰Š‹‡‰„sK/%""&158899987878:989:;::;<<;<;<<<=>>?@@?@@CCBBBCCCCDAA@@AACBCBDA@@@@CB@@AAAABBBCBBDCCBDBBCBBBCCBBBAABCCCDDCCCBCCCCFDFEDDDCCCCDDCCCDDDDDDFEGEHGHGFGGFFDFEFEEEDDCCCDEDDCEEGFFEEEDDDDDCCDrrssssttsssstuttuwtrtvvuvvwvwwxxyxzyz{yy{zzzz{}|||||~~}||~~~‚~~ƒ‚~‚‚ƒƒƒ…„‡ƒ…‡…ƒƒ„„…ˆˆ†…‡†‡†‰‘ˆwxwobYSUYYX\^del~qpf^elƒ’s^yvWYXYYYXY[[[XYXZYYZZZZ[\[ZZ_\\`cmvyy}wne`[[[[[[][\\[ZZZZ[[[[YZ[\\\[[ZY[\\\^`bels~‰—¤¶ÈÔÞàÜ˼±¢›Š«Ò×ÞãÑÉËÍ×ÞåçèçåàååæççåÚÐØáàâ®yr{‹¥ ¯ÍÑÛáåçèçÝÕÇ·»¹¦¥§±³°¨¡|qbLL==;ARnŸ”¬®°¢“Œˆ‰†}umfa[QG>876778887<>KYfq}‚ƒ„„ƒ‚ƒ†‹‰Ž””’“–•’’’““”•””“““‘““’’“•’““’“‘’‘’‘’’’’“‘‘‘‘‘’‘‘’‘‘‘ŽŽŽ’ŽŽŽŽŽŽ‹ŒŒŒ‹Œ‹‰‹ˆŠŠŠ‰‰‰Š‰Šˆ‡†‚{W5(!!%,38788878888:9899:::;:;;;;<><;=>>=>??@@ACCCCBBBC@@@@@@@@BAAAAA@@AB@@@CCCBABBBAABBBCBBBCBBCBBBBAAACCBCDCBBBCDBBFCDCCBBBCCCBBBBCCECCDDFCGFFEFFFHFEECEEFEEFEDCCDDCDDCDDEEEEEGDCDEDCCDwuuvwxyyyxxxwvvvyyxwvxyyyyyy||}||{~}}|{|~}~~‚~|}€€~~~ƒƒ‚‚‚‚ƒ‚ƒ‚‚ƒƒƒƒ„„„†…‡†…††…†‡Š…‡ˆ‡ˆ‡†‡ˆ‰“—†‡†‚scYSUYXXZ\`ltwxpcel„†yx’yW[YYY[YZYXYXXXYYZ]\[[Z[ZZZ\[[\]_flqƒ‘—€e_\[ZZYZZ[[Z[[Z[\ZYZY[[[ZZZ[ZZ[[Z[]]]`chmv~Š•¤´ÁÛäÞéšžªÀÖàææåæççççèéêéèèèééééçÔÉÈÑÛÛÎ’t|“©Ò×ãåçèèééäᳫ§¤¥£¢¤«´¡«…–v|]RJB>@Lbx–¶¬•”“’“™žp‡‚€_F987888899=>DSgq€‚‡«œ•› ¦¯¡“ˆ‹‘Ž•““”–““”••™•—””’””—“““‘“”“’’’“““””””•”“‘’–’’‘’’’‘‘‘‘‘Ž”‘‘’ŽŽŒŽŽŽŽ‹‹‹ŒŠŽ‰‹ŠŒ‹‹‹‹ŠŽ‡Š~f?,"!$*38888879888::88;::;<:::<;;;;;<===>>@?A@CCAAAABB@@AAB@@@C@EACAAA@@@@ABCBEADABA@BBBDCEBCBABBBCBCAABDCDEEBDCCAABFCDCDBBBCCCBBBDCCCCDGEEEGFFFGGFGEDEEHFFEFFDDCDECCDEEDDEEFEEDDDDDCDDDvuvyxwyzxwwxxyyyy{xwxzzyz{{{||||}|}{}~~~~}}~€~~€€€ƒ‚€‚ƒ‚‚‚ƒƒƒƒ„„„…†…„†…‡Š†††††‡†…‡‹ˆ‡‡‡‡‡Œ—•Ž•˜‘Šs_UQTZY[[\_am|pgdk‚ƒzk‚›yW[ZZZZYZXYZXXXZZZ[]\\[\YZZ][[\]^`dhx»À¼‘mc\[\[ZZZZZZ[Z][YZ[[[[[[[[\[\ZYZZZZ\^_`dhmt}†™¤°¹Å·³µµÀàÜåçççéëëëëëëëêêêêëëëëéÝÊÁ¹ÈŲ̀”†‚“›ÕÞãèêéééêçÇ™Šˆ––”’Œ…ƒ„€wkb_ZMCFHSWep‰¤¨©§ª¡†|lo€rmE:776677884/8CTgoz}ˆ ¹¼ÃÆÈ»¼©œ’…”‹Ž’“—–””””“•••””’“•–“““““”“”•’’“‘’“’•’“““““‘‘‘‘’““‘‘‘‘‘ŽŽŒŽŠŽ‹‹‹ŒŠŒŠ‰‰ŒŠ‹‹ŒŠ†Š‚pK2#""(1467987887777979:989:99::;;;;;<=>=<??@@ACAB@@A@@@A?A????@@AA@@??@@BAAB@AAA@C?@BAABBBBCAABAAAAAABBCDCBBBDCD?@AABBBDBBDCEBBBBBDDDDDDDDEFFFFGDFGFGFFECEEEEFFEDDDEEEEEFFDFEEDDEEFFFFFwvxyzw|yyy{zzz|yyywz~}}{€€€€€~~€€„€‚€€ƒ‚‚‚ƒƒ‚ƒƒ‚‚„„„„ƒ‚ƒ„…„„…‡‡‡†††Š†ˆˆ‡†ˆˆˆ†…†‡‰‰ˆ‡‡ˆ‰Ž˜¢§¯±²™„lYRMRZYYZXXevael~€€{–xXYYYYZYZY[YYXXYZZZZX\[[YZ[[[\]]^_`cm’À»Ï{l]]\[[ZZZZZZZZZ[[ZZZYX[\ZZZZZYYXYZZZ[\^`cglrx‡š¤©¯®ËÔÞâãååæèèééêéééééêêêêêééÛÆÀ¿ÕÉÅÀ·« œ³×âèéêêêé騶‰oowŽœžž™‚ƒ_ZYXZ`jbXSSRSTQZew–ž»µ®‘ƒtcZTWZO@968666678.+-5AP_ny‚”¦ªµÇÁº¼¾¡§˜›”‡Š‘‘œ•—•—–•–—•—““–˜”ž—•••“—”“’“”š“’’•’–“””“’““—“”“‘’“‘’’‘‘”‘””•‘’‘‘‘‘Ž”ŽŒŽŽ‹ŒŽŽŒŒ‹‹‹Œ‰‹…zZ9%""%,4677798797779878989999:::;=;;;==>???@???@@??A@BA@AB?A?>?@AAA@??@@AAAD@DAA@C@@@@ACBAABAAAAABABACBACBBCCCCDBAABBDCCBBABBBBDCBCFFEEDDEFGFFFGEFFFFGFEEGFFFFFEDDDFEEEFEEFGFFEFEGFEEEEwwwwwwyxzzz{{{{yz{||}|}}~}~€€€€ƒ‚„‚€‚…„‚ƒ„ƒƒƒƒ„……†„‚ƒƒƒ„„„„ƒ…††…††‡ˆ‡†††††ˆˆ‡†ˆŠ‰‡‡Œ‰‰‰‰ˆ‡ˆ‰Žš§°¾È´¡zfUMKPXZ[ZX`e\Zdm|||…Šyafga[[YYYZ[ZZYYZ[ZZY]\[Z[[Z[__^^^_`fl‘¨Ä¢wd_\]\[[ZZZZZ][\\[[YY[[ZZ[[[YXYXWWWWXZYYZ\`cgkosy„‰–¦±»ÂÊÒÛßåæççæãâåéêééééêëéæäÜÔÒËÎÛÖÏÏÍÜçêëëëëéèη„{pƒ¢¥®´¾¥‹mQLIMQac]OPQTZQJRThy†€unhSFCA>;97777866660+/3=DOYdr†«³¾Ç¬¡•Œ”š¨£•ˆ‹“•–—————–—“•–••˜—–“”””““’“–•’’‘”“”““”“‘’’”““’“““‘“““’”‘‘‘‘‘’ŒŽŒ‰‹‹‹‹‰‰‰€kB)$"$(367789888776667788889899::;<;:=<<=====?>?>>?@@BA@A@?@??>@@@?@AA@@@ACD@@???@?@@@?BCA@BBA@AAA@@@ABABCBBBBCCCCCBBDBBDB@BCCCBCCBCBEEEDCCDDFGEDFFFGFIGGFFFFFCEDEEFFEDEEFFFFFDEEGEEGFFyyzxwxzx~|{{}}}}}}|}~€‚~€€ƒ€ƒ‡ƒƒ…‚………„ˆ„„„…†‹……„…†…†‹‰ˆ…††‡‡Š‡ˆ‡ˆ‰ˆ‰Œ‹Š‰ˆˆ‰‹‰‰Š‹Šˆ‹‹ŠŠŠŠŠ‰ŠŽš¯±ÖÈ» –Šr_QLIPYY\YYYWYdn€‚…‡†‰|{‚yh\\ZYYZZ[\[Z\\ZZY[ZZZ[[[[\\^^_^`_acky–º¥¤…g_\[[[[ZYZZ[ZZ[[[Y[YYYXYWYYVUUUTSSRSSTTTUYZ\]^`bdgmy†•¤°ÁÉÑÛåáÛÕÇÃÈÝèèéèèèéèèèççæßÚØÙÜßäæçééêêëêéÙ½§–’“¥®¿ÖÕÓ¯mOHDM]lo`SQRUUJOPSZaa_\YQC?<:87777787678633258:>EO^pŒ‘’‘ŽŠ…‘›£»³²¥¬˜†‹–˜š˜˜—————˜–—•˜———˜––”““”••––”˜”—“”””••––””““““”—’’’“‘‘’“’’”•’’Ž“‘”“”‘‘‘’ŽŒŽŽŒŒ‘ŒŒŒŒŠ…tL.&#"&15778897777776:888889999:99:;<<<<<====?>>>>??ABA@???B????@A@@B>@@@ABD@B???AAA@B?ABA@AA@@BAA@A@@BABDBBABBABBCAADBBCBABCCCBBBBDDDDECCCDDGFEDEEEEFHIGFFFFFFEEGEIGFEEFGFHEFEFEFFFFGFzzyyyyyx{{{|}}{{|z~|}}€~€€…ƒ‚ƒ„ƒ‚………ƒƒ„…‡…†‡……††‡ˆˆ‰ˆˆ†‡‡‡ˆˆ‡‰‡ˆŽŒŠŠ‰‹ŒŠŠ‹ŽŽ‹ŠŠ‹‹Œ‹‰‹‹‹‹‹‹™¥©¢—”‘’§‡j[NJINVYZYY[[douƒ‘…ˆyiz•sa`\[Z[Z[\ZZ[]\\\\[[[[\^\\\__^_``abbin† ¥½’rg]\\[YY[ZZZZZYYWWXWVUTTTSSRQQPQQRRTUVXWXZ[\]a`caeens‚ ²½ËÕÙÐË·«²ÔááåæáÞàãæèéêêéèæåæçççççæçéëêêèäßÛÍÈÁÉÏØÙ×̸›}ZX]Œ¾°Ÿ‡sia]LQRS[YYYYNF@>;:987787777876777798;?FO^jqvy}~€ƒœ©ªºÕ¶®©‘ˆ’‘––š™——–––˜”™––——•–—••””–˜••–”–’’“””””•–”’“”””•’’’“‘’”“’”“”“’‘“‘‘ŽŽŒŽŽŒŽŠ…{W4)#$%,47878:677777677889688889::9:;<;;<<<<<>???>=>@@@@@?>>=?>????@B??@@AA@@?>?AA?A???@@ABA@AAB@@@@@@AAABBAAAAA@BCAABBCCBCCCCCCBBBBBCECBCCCCDEEDFEEEFGGFFGFFFFEEEDFHFFFFFFFDFEEEEDFEEEyyyy}yy{z}||{z{{|~}}~~€€ƒƒ‚ƒƒ‚…ƒƒƒ…†‡…††Š……†ˆ‡ˆˆ‰ˆˆˆ‰ˆŒŒ‹‡ˆ‰‰ˆŠˆˆ‹‹ŠŠ‹‹ŒŽŽŒ’ŽŽŽ˜£šŽŒŽ‰‡š |cVNJHNTW[ZYZcqƒx†ˆˆynƒ‡‚od^\[[[[ZYZ[\\\\\[[[\]\]\\]\]``aa```ej|Œ¤¿šp_\[\[YXXWWWWWVUUUSRRPTQRQRRSTTTVXX\\[ZYZ\^adeda_]ahw…—©¹ÑË•œ§¹ÍÑÍ·°ÂÐÕâååååææçèèèçÛ×ÖÜæçèèèçææåÛÖÛãäåäâÑÓ|œÚàáàßδ—€oa\VVKMJGC?<<<9:998877789778788889;>CKWjp}‚ƒ”´§¼º¼¸µ«¢¨±Ÿ‰Œ’œ—–––—™–š—˜˜—–™–˜•”•–•”•—”–“—•™””””•—“–––”™”““”’“••“”•””““”’’‘’“‘‘’’ŽŽ““‘‘‘‘‘ŽŽ‘Ž‹Œ}h>-##$*27888:778:798779898;98899889;<;;<=<=>=====<<=?>=?>>>=??@@@??@??A??@@@@??@@@@@??A@@@AABBB@@?@AA@@@ABAA@AA@BBBBBBECBCDCECECBBCCEDCDDCFCDEFFFDDEFGHGFFGGFFGGGEEFFFFFGGGGGEDDGDGEEExyyzzyz{{{||{{{{|}~~~~~}€‚‚„ƒƒƒ„†„ƒ‡††…†…†‡‡††…‡…‡ˆŠ‡‡‡ˆ†‰ŠˆŠŠŠŠŒŒŒ‹‹‹ŒŽŒŽŽŒ‹‹ŽŽŽ‘˜š‡xˆ…™–Œr]TMJHKTW\ZYbqkn{Š‹‹x`js„}vvkda[[ZZ\\^]\]^^^\^]]]^\`]]```bbbadegs~–¹Ÿ–{d^XXXXWWUTUTSSRRPRQRSSTUVXXYZ[[[Z]^cgc^gq~Š‚{ywnadfq|Œ ªµ©•›—š“™Ì¼Ï«¬°µ¾ÉÕßáâäååä×ÉËÇÌÓÚåèêêééåàÜÙØÝåæåÙÓÕÙâççæäáÑÕ®¥™~pdXPID@><>89888777778888:999999;=DM\nw†}~ƒ‰œºÔ²³Åƹ–‰‰Š’™—›˜–—•———˜––—–––•••”””•””“•”–••–•“”“••–””••““’“’’““’”•“‘’”’‘’“’‘”“‘‘Ž‘“Ž‘‹‹sJ3#"#(0677577777788778888888888889;::;<<;<=====<<<<==>?>=>>?=??@>?A@?B??C@B@A@BBA@??AA@A@AAABA@@?@AA?@@AAACA@@??@BBBBCBCCCCDCCDCCCBECCCCDEDDDFGFEEEEGGGEDEGFEEDDDEFFGFEHEEEFFEDDDEEEHvyz{|{{{{|}|€{}||~‚~}~~~€€ƒƒ‚„ƒ†ƒ„…„„ˆ‡‡†ˆˆˆ‡‡†††‹‡‰‰Š‡‰‡ŠŠŠŒŽŽŒŽ‹ŒŽŒŽŽ’ŒŽŽ‘ŒŒ“’‘‘“›¦¡…x†{y“ºŸ‚jZRMJGLOU[Zamzyyx{Žwaew…€Žsd[\[Z\\^]\_^\\\\]]]]\`^^_```_`aabcdksª¥‡h_UUUUSSQUQSSTTUWWXYZZ\[YZ][Z[[[^dnvv_lƒ¡œ’y…‹m\Zairƒ˜¡œ«°¶¼ÄËÊËÉÅÀ¬©¨¦´§¦¬¼ÅÐßÜÛÖÔÁ»»ÃÓãåææçææßÑÎÏÙÖ×ÛÞâåçççæäÞÔɲ¹»¾³§•‚tgZOHB=:998889667988998887:9;>ELWku‡……‡…††”´±²®Æ¿Þ¸¨§¨—Ž’”—š™˜—™˜—––——˜˜˜˜™™˜˜””•™™š•˜•™–˜•——™˜—•”–™”•”•“™–˜•””“’’““’’““’‘‘”‘–‘““’’’Ž’ŽŒ„}Z9%""%+5786878787:888999887778988899:;:;;:;;<<;;;>=>>>=>>????A?@?@A@@B?>@???@@BB@@???@@A@BA@CA@?@@@BBBAAABA??A@@@ECCDDDECBBEBBDCCCBEDDCCCFEGEFFFEGEEEFGFEEFEEEEEDGFFFGFJGFEFFEDEEEFFFzyyzz|{z{{{{||}|}~~€€€€‚€ƒ‚„ƒ…ˆ…‚ˆ„„„‡‚‡‡‡†††††‰‰ˆ†‡‡‡ˆŠˆ‰‰Š‹‹ŠŒŒŒŒŒ‹‹ŒŒŒŽŽŽ’‘‘”ž§«•yvstŒ±¹•vfYRMJHJNU[^`_alvy’{n|‰…˜ƒ~nbb^]\\`]\]^]]]`]^^^]`_]___`__aabccejn‚›¡º’reXVTTTTUUVWY[[[[\\[]\\\[\^]][^_dp–ydx¥|_w˜‚x^Xccilw‹ŸŸ®°²¸×ÐâÜäãá˶¯®ºÄ¼¤¦©ÆŸ¢§º¹»¼´¥Âº¿ÄÊÑÛâáàÚäáߨÆÞäèèçççåã×ɺ²ª±³¶®´²¯£•„th]RIC=;99768887877777:89<>CIVgyŽŠ†€€…œ®Ë¾É»¾Ö¯Ÿ™“Œ“›™šš˜–˜˜˜˜˜—˜˜˜––––––•••˜–™•–”–——•˜–••”””””““”””””“‘‘’“’•’‘‘‘‘‘‘Ž‘‘‘‘‘ŽŽŽ‰ƒiB)#!#(356677777776888987887777899:9:99:::::;;;;;><<<<<>=>A??@?@@@@@@??@@@???@BA@@?@@@@??AA@BA@?>??@@C?BAA@@?@@@ABCCCCCCCBBCBCDDDCACDDDCCDEEEFFFFFFFDFGFFEGFDEEEEGFFFFEFFFFEDDDEEEFFF|zzyy|~{z|{z}~}|}€€€~~€€ƒ‚†……„„„†‡ˆ„ˆ„„…ˆƒ††ˆ‡‰‡ˆˆ‰‡‡‡ˆˆˆˆŠ‡‰‰‰‹ŒŒŒ‹‹‘’ŽŽ•“Ž“‘•žµ²²x}mp‰¬»«ˆqbXRMKHJMS[Z\^hlv”€››•~˜„~yqid^\\_]]]^_^]]]]^^]^`___``_`abaabccdhw„½›‚p\YXY[[[\]]][Z[[\[]Z[[\[[[[[\_dy‹“{„Œ¨p`k}†zhfhd`_cjs‹’šŸ¥°»ÉÒÛåååãâãäää忨ÊÀ¶¶»¾»º°µÃ¶§¦´¸¶³¾ÔâàãáãäæèçççæäâÒ³©¨¨«¬¬¬«««¶¶µ°«Ÿ“}k\PG@;77677778;778899<?CHUbt‹‰”Šˆ‡„ŠŒ¤Ì¾Ø¾¿Â¶½×’”—›«£žœ™›™š˜™™˜———š—••—•˜—™™™–›———™••––•˜––”””•”—”““•’’“““‘‘“’“–––’•’””•’‘–’”““ޓЉtK.%!"&16677778686788889788899888988999;9;:::;;<;><<<;<><<=>???A@?>@?????@???AAAAAAB@@???@A@AABB@D??@CBCA@@AAA@?BABCCCCBBBAEEDDEDDBBCCDDCBEEEFFFEFFGGFFEEEFGFEEFEHGGGFGGGHFEDDDFEFFHF|zzyz||z{{}||}}}}}€€€~€€‚‚ƒ†………„„„ƒ„ƒ……††…‚††‡‡ˆ‡‡‡†„‡‡ˆˆˆ‡‡‡ˆˆ‰ŒŠ‹Œ‹ŠŒŒŒŒŽŽŽ‘•ª·vjcq‹¬¹°™‚l`YSQLIJLRZ__`bp”‚{€}€šqu‚xxl``_^_``_^^]]^_`^^_``aaaadbcbbbccdedjs¸¢~e_\\]\\\\[[[\\]\^Z[[][\[[[\]]jtux}€ylqsvvs}~€vgd^ehnt~‰‘—¢¯¸ÂÒáäççèèçèéééèåãáâãÙÔËÆÆ»¯ ¡¨°¢–¥²ÀÞåèèèçæææãØÊ½¯¦ £¦¦¥¤£ ¢¥¥¥¥®¬¤›Š{m`ULE?<:9778797689<?CHS`q†–’‡†Œ–¢ºÄÅǽÐ׺¨ž™•“˜¨¸°£Ÿ››š™—˜–˜—˜——––•••—–––˜•—˜˜—˜”–—••–˜–“••”’—””“”’’’’’‘‘’’“‘””•‘‘‘’’““’‘‘‘“‘“‘Œ~X4(!"%,67778777889777777889677777677899:;:::::;:;<<=;<<<==>>>@?@???>>>???????>??@@?>@A????@@@@@@???@AA@?@@A?@@@AAAAABBBBAACCCCEDCCCCCCDEDFDDDEEEEEEFFFFEEEGHFEFEGHGGFFFFFEEDDCCDEEFE~|zz||}}~}~|}~~~‚~€€€€…‚ƒ‚„‡‡‡„ƒƒ…„††……„……„‡‡‡ˆ‰†††‡…ŠˆŠ‡ˆ‡†ˆ‰ˆŠŠ‰‰Š‹Ž‹‹ŒŽŽŽŽŽ‹ŒŽŽŽ“’’•›§§ªqabr–°·½«’zi`XTOMKJKR[Z\^lw{€†{ˆ|€Œwit’¨„wg_^]]^_`^^^````__``_`bbabbccbcbadea\blŠª§µ‹md^]\]^][[\[\\\\ZZZ[Z\[ZZ\^]^^`cfjt†ŽŒ‡vsrvšŸœ|bY[\]`cks}‹”¨¬¾ËÙÞååäááåèèèæåäãäåäãåàÍ´©£³¨ž’”ÇßçéèèçæåäÙÌÁµ¬¤ £¤¨£Ÿœ˜™š›Ÿ§¼¾¿¸°¥™„pbTJB<76668688;<=BGPZj~…”Ž”‹‰†•¨²¿²¨ÏÖ¾¾¿½¶´¡°ÐÀº¡œ˜˜˜–œ™œ——–šš›œš—™—˜——™Ÿ—˜—š–”•–—š–˜–˜˜—–™“•”””š•”’˜’““””•“’‘–“–•–“–”“’“”–‘’ŽŽƒk>+"!$)18998778887666677999778978777889;;:=:;<<;<<=<:;<<?==>=>>?A@A>@>??>???@?@?????@??????@@@@@@?BAA@@?BAA?@ABAAADCCBBBCBCBCCECDCBBCCDDDDDCDEEEEEEEHGFFGEHFEFHGFFHGFFHFEEEDDDGFFEGE{}{{|{||||}|}€}}~~€€€€€ƒ„‡ƒ„ƒƒƒ†‚…††‡…………††ˆŠ‡…†††…‡‡Š†‡ˆ‰‰‰ˆ‰Š‰ˆŠ‹Œ‹ŒŽ‹ŒŒŒŽŽŽŽŽŽŽ”™¦Ÿ o_aq¢º¸½É¨Šxjb[VQNKJIPY\^cfox†~ƒ|€œwav—€vie^]^^__`___^__`a`_`cbbcddbbbcdeb[]cj€–£¹’wlb_^^^^]\[\]]]]][\[\[Z[[\\]]^^abs•Œ|wxz~€•¨Š\Sbeb\[Y\ajt€Œš§¶ÊÒáÙÑßáææåãȰ¹ÅÎÖàãäáÌÆ¯ ¡›”ŒŠ©ÛãèéèçæåãÚËÁ¶®§¤žŸ £££¢ ›˜•“’‘‘—œ¦««ª®´¶¸˜}k]SJC<;:9899:=?BJRat‘›‘Œ†€„„‡œÑų®«ÉØÀž—Ÿ´ÓÏÔ½©¢œš˜˜—˜—˜–—˜˜———˜—˜˜˜™š———––—––•–•—–˜––––“”••“““”’“’““”“•“’’’–““‘”•“‘“Ž’’’‡vK2#""'-3588888886667998967778887777789<:::::9;;::;<;:;;====>>>@>>?>?>>@???@@??=>=??@?????@@@A@??@@@A@@?BAA?@BABABCCBBBBBBCCCCCBBBCCCBDDDDDDDFEFEFDDEFFFFFFFFFFFFFFGFEEEEGEEEEEFEDEF{||}}{~||||||}}~~~€ƒ„‚†ƒˆ‡ˆ„ˆƒ„„††…†‰†††‡‡Šˆ‹ˆ†…††‰…‡‡ŠŠ‰ˆŒ‰Œ‰’ŒŽŒ‹ŠŒŒ‹ŒŒŒ‘Ž‘‘‘’˜¦••h_`q¦ÜÃÂà‡yme]WTOLKJOWY_]ds‹•z€›w_s˜‡€”›—{h^^_a_^__```bbba```aacccdccccde`Z[^bgx‡¹™†xe_\]]]\\][[\\[Z[Z\[[]\\\\[\[]^o‚”ˆ~}•І–„r`comd[WUSTUZajtƒ¢¶ÄØÓáäççåᱤ’ÂÀØ»ÍÓÑÑÄǤ¦—–‰¢ÀåéééèçåãÙÏø°¨¤¦¡£¢«©¨§¦›—“‘’•—›œœ™—›Ÿ¡¢¥¥¢ž–n_SKD>:9888;=CKXdr‚‡‘‘”Їˆ„†ˆ¡°°±¦ÙÓÆÀ·¨±ÉËÝп²¡š˜˜˜˜—˜™•›™›™›™šš›œœšš™—˜——————––––””•—–˜•••”“““–”•”“”•”–”““””•’“““’•‘“‰€\9(#!%+45778778878678889666779777777779999999::::::;::;<=>>@@?==>@>@>>?@@??>>>>????AA@?A@A?@@??@??@CABABAAA@@@@ABCCCBBCBBDCCCEBCCECEDDDDDFDEFFFFEDDEFHGIEGGGFEEFFGGEEEEFEEEFEEEFDEF{z|||}|||}|||~}}~€~}€€€‚‚‚ƒ‚‚‚ƒ‡„…………††……†ƒ…††‡‡‡†‡‡‡‰…††ŠˆˆˆŒˆ‰‰ŠŠ‹‹‹‹‹‹‹‹ŒŒŽŒŒŒ‹ŒŠŒŒŽŽ‘’˜”t`_^p¨×ÃÇÚͶžŒ~qg^XSOMKHMSZ`bev”‚vw`t™‡u—Žr^ccb`_^`ca`baaaaabdcecddgdecfc`ZZ]beis{‘²®¥Žwk^^]]\\[[[[[\\[[\\\\\\[[[\]]elqxy{~„zz{‰rwx„s^ZYXTROQU_gt€‘ ²ÄÑãæççÜÆµÆÒÙÌÇÍÀ½¾Â¨¤™™ ¯Â×èëêéèçãÕÈ¿·±«¦¡¡¡Ÿš˜˜˜˜™“’’“”’Œ‹ŠŠŒŠ£Œ”ˆ{maWMF@=;<9=BLVev—Š…~…Š•¡ÃÓÆÆ¼²¦š¨³ÏäÞϼ«£›™œ™™—˜–˜———›–š˜š˜˜—™™˜—––—˜˜—————––•––•–••––”””””””””””•”“’’““’““’’’‘‘…lA*$!#(05667778767788787665677667666678899999:<:;;:::::<<===<<<>?>>>>>????@>>?@?>??A@@?@?A@?????>?@A@B@AAABA?@AADBBBDCCCCCCCCCBBCCBCCCCCCCDDFEEEDDDEGFHGEFEEFFFFFFFEFEEEDEEEFEEEEEF|{||}||~€|}}}}}~€~€‚ƒ„‚ƒƒ…„‡‡ˆ‡‡ˆ‰…†††„…††‡ˆ††‡‰ˆŠŠŠ‰‰‡ˆŠˆ‰ŠŒ‹‹‹ŒŠ‰‹ŽŽŽŒŽŒŒ‘‹ŒŒŒŽ‘ŽŽ‘•—wfa^^pœÒÆËÕã˳¡€qg^XRPLJHMPV]]ey|}Žvauš†uˆƒ“}adda```aaaaaa`acabbcdcccdddcdb`Y\^bdghls‰¥¡ÉŠn]\]]\[]]\]Z[[[[\^]\\\[Z\\\[[]dhxzuuy}ƒˆƒ¨“‹bZ`_YTPLNOV]ht‚‘¤¸ÍäæçæâÇÏÍæåäØáÎÀ¾¾²¢—ÁàæèéêééæäØÆ½´¯¬¨§¡¤Ÿž› ˜”’’“–•“’’Žˆˆ€€‚…‰Œ“¤¤¤Ÿ˜„ti^PC;57;CKYfq…’‹‘Ї†„††˜Á½»½À¾À«§«¬ÄÜßâÓÅ·¤žœ››™™™›œœœœ˜™™š™™—™——ž–™˜™˜Ÿ˜˜—•••–™••–˜˜——š–••———••”–”’“—”•“™“—“’‘“‘‰vJ.%#"&-676777777779777667566966665568899;99:::::99:;<<<<>=<<<=???>>=>>>@@@??A@???@A@??@@A@??A?@?DBBABAAA@ADAAAABABBCFCDCCCDDDBBCCCCDFDECCCDEEDCDFDEFFEEFGDEFGGHFFEEEEEEEEEGFFEEEFE{||{||||~€}}}}€~~~€‚~‚ƒƒ€‚‚‚ƒ…‰††‡„ƒƒ„†††‡‰‰ˆ†…†‡†‡ˆˆˆ‡ˆ†‰‹‹ˆ‰ŠŠŠ‹Š‹‰ŠŠ‹Š‹Œ‹‹‹‹‹ŒŒŽŒŒ‘•tnda`mŒªÇÅÆÒáϸ¥’ƒti_YUPMKIKPW`dgffu›sbw›†v‘…ˆ”jyƒugfdegecabbaaaabdddfeedddgc`\]`ffhghkp‚”±Ó°‚l_^]]\\_\]\^\\\\^_]^^]]\\]\]^blz„ŒŠˆˆ|}{{ufozph_WSMLKQVakyˆš«ÂØÝå䯯È×áØÓ×ݽ²´Ã¥˜¹âèêëëêéèåØÈ½µ°¨©¦§œš™šš™”’‘Ž‘‘”‘“’‘ˆƒ€xwxz|zyzzƒˆ—£¦´³±—„a@4047>CNWds~Œ™Š„}}‹—¨ÎÎȾ²¤¢¡µ×åêêßÀ®§¢Ÿ››š—˜™™˜˜›š˜™˜˜———™–™™™•™˜˜––—–—˜–•––•–—™–––—“—”–“”””’’’““““–“““‘ŒV3(#"$+5666787777876666766656655766679978899:;:99;::;<<<=>=<<===?>>=>??@@??AA@@@@?A@@@@>???@@???@AA@BBBBA@A@AABCDCCFECCBBCCDDACCCCCDCCCCCDCBDDCCDDEFFFEEEDDEEDDDFFFEFFFFFFFEFEEEED€}€||}€~~ƒ~~€€€€€ƒ‚‡€…ƒ‚‚ƒƒƒ…†††‚ƒ†……ˆ‡††††ŠŠ‰‡‰ˆ‡‡ˆˆ‹‹‹‰‰‰‰ŠŠŒ‰Œ‹‹‹ŠŽŒŒŽ‹ŒŒ‰‡‰‹‘“€|pegm~’ÃÃÁÄÚãÏÀ©•„sh`ZUQOLJLOU^^aarwrbxœ†w‘‚€˜‰…—‘skhmoiabcdeecbccddddeedfgc`\]bdggfihilz‰¬É „ufb_^[^]]]]\]]][_]\[\[][\[\^aqw˜›˜˜˜…xqorlrqw†‘—yaV[SNMJNR\dq£¹ÏÙáɲÀ¿¿»Áͺª¦¥¤¨ÀßçëëêêéçåÜξ´°®¥¥œ–—š˜—“‘”‘”“š‘‘ŽŒŠ…€}wywvutsuuw}‚Œ”£®¸ÐÂdž€<0/148;?FMYeq€…“‰‡‡ƒ‹Ž£¹¼¾½Á«³ž—³ÅÐáãäÕɽ¦Ÿœœ™˜ ™™™™šœ˜™š™˜š™›˜˜™›—š˜—˜—–——›–•——–—™šš˜——˜”—–••™”•”““’’–“˜“”’ƒi;+#"#(06667877777866666666676767666689888::<:9999:::::;===<<<=>B??>>>?????@@??@@@@@A@@???@??@@@@@ABBBBBCBAAAADCBBCDEDECDCCCDDDCDCCCDCBCEDCCCDEEEEHFGFHFFDDEDEFEFFFFGGFFHFEFFEEFIF||}{{{}~~~~ƒ€€€€‚‚€€„ƒƒƒ„„„„†„„‚ƒ†……ˆ…†…†††‡†‡†‡‡‡ˆ‰‰‰Š‰‰ˆ‰Š‰ˆ‰ˆŽ‹Š‹‹‹‹ŠŒŽ‹Š‰‰‡‡ˆ‰…‚‚„†„†ŠŠƒ~~|“¾¿šªÅÎÖÎÄ—ƒsi`[WSRMJKMT`baejecx†x‚›‹w‹¤ƒyp{naeeeeeebbcdefeeedefdb[\adhiiihhknw–´ž˜}mga^^^]]^^^]]\_]\[\\][\\\]_gmrwz}{x‚†ŠŽ‚˜„|bW`^[WPKNQX`kv‰›°ÈÍÓÂÂÃÀ·›Ÿ¢¨”œ¾ßçëíëééèåÛÑÁ³©«§¥¡–—š–˜”•‘Ž‘‘Œ‹ˆ…„|zwxwtsrmoqvz…™¤²¼»º¡W4/.2456:<?DOXet€‘”—‰„~~Š•¦ÅµŸššœŸ¼¯Ææðîëáï§¡žššš™šš™š˜˜–––˜˜˜˜™™˜–›˜™š˜———˜–––————————––—’—˜–•””•—”’’’•”•““’†vG1$""&,26677777778575676766767665766688888898889:99:::9==<><<=?>=>@>>??????????@AA@A@@???@??@@AAABCCBBBBBBAAAAABCCBDCDBCDCCCCCBCECCBBACCCDGCDEEEEEEFFFDFDDDEEEEEEFGFFFFGFFGFDFGFF‚~}~~~~~ƒ~€‚‚ƒ‚„„ƒ…ƒ…ˆƒ„„‡…„„‡…Š„„…‡‡ˆ…ˆ†‰††‡††ŠˆŒˆ‹Š‹‰Š‰ˆ‰‰ŠˆŠ‰ŽŒŠ‹‹ŠŒŠ‡‡‡‡…‚€{tpmjjw„„ŽŽ‘›«œ—¬¼”©½ÂãÕǬ“‚sjb]WTPMKLLTZ\_cdeyŽ…y€ƒœ†›’‡‡}vededcccbccdfecedddee_Z^aeggghhijjjmtŽª—•ˆvmca^^]]\^]\[\]][[[[[\[[\\^]^cht†˜šž•‘Œ‡ƒ‚|jgie`[WQOONU[eq’§½ÄÎÖáÔ¹¥œ¤¦•»ßçêëëêèæäÙÌÁ·°¨¤ Ÿžž™–—œ˜˜—•‘ŽŒŒ“ˆ†‡ƒƒzyyyusrqonty‹–£®¿·µ›~V70/13457889=AGO]jw„ˆ“‘“‹†…„‰ž±›»Á¿À·±ÊâåçæäÕͼ¦ šœœœ™œžŸš˜™˜™›™™™œ™š™™—™˜—•–—˜˜›–˜˜˜–––——˜˜—–––••——•”™•—•˜’•ŠƒZ8&""$)259898:8778876666666788687777778:898::988889;:98=<;::<=====>>>>?@?@@@??>?ACCCBA???@@BBA@BBDDDBDBBBDBBAAABCCCDDDCFEEBEDDBBCCCBBBDCCEDCDFEEEEEGFEFEEEDFFFFFDGFGFFFIFEFGHHFEE~}~|~~~}~~€~~€‚€‚ƒ‚ƒ„ƒ‚ƒƒ„…„…„‡…„ƒ……„„ƒƒ†††††………‡‡‡‡‡‡ˆ‡ˆ‰Šˆˆ‡ˆŠ‰Š‰ˆ‰ˆ‹ŽŠŠ‡……ƒƒ…„ƒ€}vpkgedbaabek‚z‡±²±®¬š£½–‘ŸŸ ÁãËé“„ume^YURPNMMRY^ffdp’~yƒ~ƒ›Ž‚Š„ˆ’„zŽot~laccdheeddehhheeg^[_aeeehghkiiiknt‡˜«¶¢…xkd`^^^_^\\\]_\[[[\[[\\\\\^`fnzŒ†„ƒ‚urƒ…‡…………‚}vri]XROMRV`ky‡š½ÕÝâоËà˽ÂÞçêêêêéçãØÊʺ·°«¤¡šš—–•–——˜˜‘‘’”•’ދЋŒ†„ƒƒzyyyutttsvz‰•¢ª³°®•zV60.1456678889:;BHS_l}ƒ’𑉅– ©ÃÅÂĵµÇÌÕèïìèàî¦ žœœ—™˜˜˜™š™™™››šš™™˜™—˜–™™—•–—˜•———————–—•———–––––––••˜”•”““•ŠgA)#!#&0556667877776665686777777677788888889;998888;;98;;;;;<<==?====>@?>>=>>???@A?@????@@@BC@@BAABCCBABBDABAACBBBCCBBBBCDCBBCBBACCCDDCCCCCDDDEEEEFFEEEDBCCDEEEEDEEGEFFHEFHGFGDEF~}~…„‚‚€‚€‚ƒ„…ƒ‚ƒŠƒ‡„…„‰…‡„ƒƒ…„ƒ„‰…‡……††……†Š‰‰ˆ‰‡‹‡ˆ‰Œˆ‡‡ˆˆ‡‰ŒŒŽ‡Šˆ‡†ƒ„…€|tnifdaaa`^]]^``bjƒ{¤ÃÂıŒ˜¼šŸ£¤´ÇÏÊÁª•…xne`[XUSPONRV[dbiotz—~ƒžŒ…œ}˜Š• ‰‰Žˆrbcddeeeddfiefffh_[_befghhhiiihjkoxšÅÖä¹–„oha_]^_]]^]^\\\]\\[\\\[\]_bgoqrrpkcq‘œŒ–†Š†‡‡’ŽfXUVROQQYbp}Ž¡´ÊÖåäãããääçêêêêéèçäÙÓËž¹²¬§š—””‘”•–™”–••–—ŒŠ‰ˆˆ†„ƒ‚‚‚}zywvuxz~‚‰•¡«³±¯”vS7/-0357677988978;>AKTany‡Š—”“‹……‚„‡œÅÀ¿¸°³ÐȽÎÜãæåå×ѽ¦Ÿœ››š™™™™˜œ›››šššš™š˜˜˜——œ—˜––—››œ™š——˜š—™—™–›˜™––——•™––––’vJ.%!"%-4765567977655676666779876779888998888889888::99<;<;><<==<====<=>>>==>??>?A@C@@@>@@AB@@ABABBCBAABBCBBBBBBBBBFBCBBBFCDBCBDCCCECCCCCDCGDDDEFGFFEGEDDDCGEFEFDDEGEFFHHGHIGHFEE~}€€~ƒ‚€€€€€€ƒ„„ƒƒƒƒ„„„„„ƒ„ƒ„ƒƒƒ„„…„ˆ††…………‡‰ˆ‰‡‡†‹†‡‡‹‡†ŠŠŠ‡‡…‚€~{yrmieddb`_]][[Z[[]]__birbn”°ÈŵŒ¢žš°®¯°±½ÑÌ«˜Š{qhb^\XUSPMNS[afhpts|¢‹…‰|žw‘¡†€‹—uceedffhdefigfgjga^`cihhhhijjiijlp~¢ÔëîêÏ·›xmc``_]]^]^\\\\\][[]^]`_`__bedddcfx„¥’|zŠz‡Š–¢€lVW\]VPONV^it‚‘¤¹ÇÙßãâçêêêêééèçæåßÝ×ÒÌÇÀ¸±¥ œ›š˜–••”“‘Ž’–“‹ˆŠ………„‚‚~~{{{zzyw|„Š•¡¬¶²“wU61-/24577778:999999<=CJVamz†“”š‡‚}}‚•ž¢¢¥±Ñýº¹ÀÔêñîìäÁ¬£žœšš››š™™œ››˜ššššš™™š™˜˜˜š˜˜™™˜˜•–—˜—˜›š———™•˜—–•••—”–—––•““‚X5(!!#*35666667666556766666688787578899;8987888778:9:8889:;;;==;=<<<<<=<===>?A>>@@@@@AA@@AB?@AAAABBBBBBDCBBCBBBBCAAACBBACCBBBACCCBCBBBCCCCCDCCFGFFFEEEDCCCEDFEECEFEEFFGGGGGGFFEE…€€€€€ƒ€‚€€€ƒ†ƒ„ƒ…ƒ„„ƒƒ†ƒ„„†„†……„ˆ‡‡‡†……‡‰‡ŠŠŠ„…ˆŠŠˆ‡‡‹…‡†Š…ƒ„„„„‚|zqkgcaababb`_^]]^[YZZ[]^``bi€^Wu›´È±Ž’¥¡«½ÄÉ®´ÄÜÓÁ¯œŒsieb_[YTPMOSYbafntx¡‹‚•}ƒ “ƒ…¡vcedceeeddegggiif`]adgjjjiijjijkls…´çñöøù÷é¬sgd`_]]]]]]\\\\\\^^]]]]\\\\\\[]et’–‘}~†Š“Œ§—‹lZ^`_ZTSQOSW^fuƒ”§µÊÏÔßççççèçççæåäãâââÝØÍüµ°«©ª£žš—”‘‘‘ŽŽ‡Š†‡‡ˆƒ„~}{zz{{|~…Œ—¥®º´²•zU91.0145799:889:;:::;<;9>BMTbr|Š‹•ŒŽ‰††…Š’™¬Ô¿Â¼¶¾¾ÊÔßââáÒÁ³¢››œœ™œ›ššœ›œš›™˜˜œš™šš——š™™—˜˜˜™šš—™˜›š›™—––•—–—˜™••••‡i>,#!"(/567686768886777656668767877899888887788878:::9;889;;;;<;>==<<=>>==?>??>>@?A@@@@AAABBB@AAFCDCBCADCDCCCDDBCACCEBBBCCDDDCCCCCCCBBCCCCDDCDFEFEEEFEECBCFFFFEDFEDDEGFEGGGGFEEE‚‚€€€‚‚…„‚„„„ƒ„„ƒƒ…‚„……ƒ……†„……†ˆ†…†ˆˆˆˆ‡‡…†ˆ‰Š‰ˆ‡ˆ‡…†„ƒ„}|{slhc`^\YXVY[]_`_^\[[ZZ[[[\]^^^`i|ZTXu ¯±š“•¥§©ÀåÔ¢§¯ÂáÙʲž€wokgda[VRNNPW`ehggw…}„Ž£’z•ƒ™¢xeihgefffggfghijd`]ddfgijjiiijjkov’Ìëóöûÿþøè»”€mfa__^^\\\\\]]]]^]]]^]]\]\[[\bgv}}{††‰ˆ‰‡†‹quvw{gZZYTONOW_itƒ¸ÎÓáâäåæææææååääããßÞÙÔÌÆÀ»¸¶¯¨¢™–’““‘Ž‹ˆˆ†‡‡ˆ‚€~}}||}~€Œ—¤®·³±—yZ>:89::::9889:;<?@BCB@?=;9<?ELXcp‡Ž—ˆƒ~…~Ž®¯ÆÅ¼½·®¬²ÅßëçÜÒºª£Ÿšš˜™™ššœ™œ™™˜—–™˜™—››™–›™™–—˜˜—šœ›˜˜—›–›™˜––•——˜˜—–••“ŒxK3$""&+15555555666555555666877777779:9889887876778998:899:9::;;;;=;<<==>=@>?>>>>???@@@AA@@@B@AABBDECCCDCCCBCBBAAABCCBBCDCCCCCCEEEDDBADCCBDEEEDDFEFDEEDCCCDEEEEEEDDGFFFEGHGFGGED…„„€‚‚‚„‰„†ƒƒ„„‚†„ƒƒƒƒƒ„…„………„„…ˆ…†…†††‡‡‡‡ˆˆ‡‡ˆˆˆ‰‰‰‰ˆ‡‡€~~{tmhc`^]]]\ZWUSVSYZ]a^][[ZZZ[\^\\\]]_hz[JMY{¡§œƒ‘–¡Ä×Þ¤›£ÎâÚ̶¢’…}wrnic^YTOOOV_`bcoy~}‘‡‹›–ƒ’Ž€|““~jkkgfedeeeghiikd_]eegghhjjjjjjkq|ŸÙìõøûþÿÿúï̧Œrjb`_^\]^]\]\]\\\]_]\]\\]\\\\^`hkptw|ƒƒ†‡ˆˆ˜™™}o^ZYZTPNKQU]ep{‡“£³ÁÒÙäåæææåääääääãããââââàÝÔÉÁ¸¯¦ œš•‘ŒŠ‹ˆ†‡‰…}~„•§°¸µ—~\C?@EFGFDA><;<>@BEJPSUPJE@<9:;@DNWcox…ˆ’‘’‰†„‚Š’¡ÉÁºº¸µ´¬ª¶ÁÎßßÞÍ÷¥Ÿœ›œ› œœ™™¡›œšŸ›œœœ›ž™™™›™˜˜œœ›œœš™™š›š˜™™š—œ™—–œ—™†^:&""$(/354455555666545567687667788898898:8887788:898;9:9:9::<:::=:;<<=>>@?@@@>=>>?@@@@@@@@BAAABBEEECCCBCBBCBBAA@BBBBBCDDFDCBCDDDDDBADCCCEBBDDEGFFDHHHDDDDEFEEEDDEEFFGGHGFFHGED‚ƒƒ‚„ƒƒƒƒƒ…‰ƒ…ƒ„„„‚„ƒƒƒƒ††…„†…„„„……††‡………‡‹ˆˆ‰‰ˆ†ˆ‰†„„„ƒ‚|zwnhd`^]\[[YZYZXWRTWWX[\\\\Z[\\ZZZZZ[YXW[az`KCQ_}«¡Œ“˜™´Óä§«³¹ÇäÖλ§šˆ|tmh`ZUPOOT]aejmoo–š©‘|’Š~–‹~s‚Œzliheefghiije_^dfjjnjjiijkjlsƒ°çðöùûþþÿÿüôÚÁ }peb__`_`^^^^]^]_^]]]\[\`]`^`^`bdgiy‡–’‘…‡ƒ|uilnh\XSOJLNUZdks~œ¬¾ÊØÞåæáÝØÝäååäääåååääããáÝÔÊÁ¸°ª£œ–‘Ž‹‰†„€{}~ƒ…’¢²º¸±¤dFEGPXYZVSMGDACEHJOUZ`[WONGB>:::<>FMXbo}…‘’—އƒ}~‚•¨»»¹°¤¡¢´³´»ËãçÞÁ°¨¡ž››š™š™›››š›››™š˜˜™™˜™›š™™™ššœœ››šššš™—™œš˜œ›˜–™—–’‹oB)$!#&-444455555555744566667667789998888788877888888889:99:::9::::;<=====>======>>>>AA@@??AAAABBCBDDCCCCBBBBBCBAABBBBBDCDECCCDEDDDBBEDCCCBBDDEEEFDDDEEDDEEEDEDDDDDFFFFGFFEFFEF‚ƒƒ‚…‚‚„†„‡…‰……„…†ˆ‡‡‡†…„…ˆ…„†‡††…‡‡‡†‡†‰†‡Š‰……‡‰…ƒ…‡„}xslgca_^]\[[ZZZZ[[^YWVWXY[^^_\\\\\_YZZZZ]XVUW_{fLBKdpˆ¥Œ‡Ÿ±¦ªÀ࿺ÐݼÆÛÜÙÁ±¥š“Œ„yrha[UQPOUZ^dehj€™ª¥~“Š~˜‰|}£›“vjffgjhiijjg`^dhiijjihjkkjmv‘Æìòøúüþÿÿÿÿýû÷鲇wie``_a__^_`^]^^]]]\\\]\\\\\\]\^ar…¤ž”‡~|{{}€ƒŠw[YXYQKLINSW]go}‡™ª»ÔÙäãÝ×Ñ×ãäåäääååååååäääãâÜÒɾ·°§Ÿ˜’Œ„‚€}|}~…Œ’™£´ÆÁ¾«™qYST\djnomibZQMKKOTZbhgc^WTOIC>:98:;AFOXes|‰‹”’”‹ˆ‡„Š‘¶Öî§¢¡Ç¹É¸¯ÂÝÝÝÕϼ¥ ¡œŸœ›ŸŸžœ›œ›žš™™™šœš œœš›››š›››š›™™ž˜•”{J-%!"%+3355555755544555566666667889888:8978777888888887:99::9::::::;==>===>>>>=====>ABAA@>@@@ACCDDDBABCBBCDBBBDCEBAABCDDCCCCEDFDDDBBFEECCCCCDDFEGCDDDEHDEDGDEDDDEDHFGFGFFEFFFE‚ƒƒ‚ƒ‚ƒ„„„„ƒ……†„…………‡ƒˆˆ„„…‡„†‡‡†ƒ……‡†‡†ˆ…†Š†ƒ„…ƒ‚{wqlgda^\\\\Z\[[ZZ[[\[[[ZZYZ[[\^^]]]`]\[XWWWWUTTRV]jiLEMˆ{q{’š·º½¬¡½ÂÊèåǶ¿ÔÜÛ˼±¨ ˜‚wld[WTRPSX_hhgwŽŽœ‰•‰€™‹…–…„vjlmkhhiikea`cgijkjiikllmo|žØîôùûýÿÿÿÿÿÿÿÿùê¿™†qhb`a`_`a`^_`_^^a]]]]^_]]]\\\]^jz†™‡‡Š‹‘†€p^bda\TMJGJLRX_fq|‰–ª¼ÎäßÛÐÀÉÜÛÙÛàâååæççççæååäÞÝÖÎǺ²©œ•މƒ‚ƒŽ˜£¯¸ÅÂÁ¡gdbgkjxpywuuuf\WT]gr}}yokd[PIC?<98::>AHO[htˆ’˜ˆƒ}†³ÊÙ¬ª¤Ÿ£¬³·²¢²¼Ôäãâ¹¥¡žœ›››œ ›œœœ œ™™™šššš››œšœœžš›œ›™š™™™œšš˜˜˜˜˜’…Y3(!!#)12655554345534555566666667777789776777877899889999999999999:;==>===>>><><==>>?@???>@?@AA@ABBAAACBBCCBBBBABABDCCCGDCDCCCFDCCCCCBCCCCDEDBDEDCCCCCEDEDDDDDDDDDFFFFFEEEFGFD†‚ƒ‚…ƒ„ƒƒ…ˆ……†††††Š†ˆ†‹†„………„†ˆ‡†…‰…†‡Š‰‰„ƒ„†ƒ{xojgdb`_^^\[\\\\__]\\\\\\[[\^^^]^\_^d]^__ZZXUVWTSTTSU\pcKJPxuj|Ÿ¯Ûȳ˜´ÇÐàåÕ››¥Àâèæ×ÍÄ»°¢•‡zne^ZUTRUW_eckyz{˜†‚š‡Ž“†…Š{onmjhhhhkcaadlloljjklmmnq‚°éòöùûýþÿÿÿÿÿÿÿÿûñѱ”vlcbaaaa`````__`^^_^^^`^]]]]^^biqyƒžŸŒ‹„€~ztttrla[SQMHFGLQX_hp}Œž¯ÅßØÔ¼¾ÈÐÎØÜÝÜàåæçææåäääãââáßÚÓÊ¿³«£™‘ŽŒ‘™¦µ¼Îºº “}j_ZZ]_efgijnsq~orkk|’ž§š‰ƒsnYOIE@;:989:<CIS^k|Œ“‘‰‡±©¦¡”±¡¶Ú¾ª¶ÀËÙÒ̪¤ ŸŸŸŸž›œœœœ› œ›œœ›ž›Ÿš™šœœžž š›š™™Ÿšššš˜›™–Šm:,"!"&,245554444463444466676667786788899777667788777889::::;889:9=<==>===@>>=====?>@?>>??B@@@@@AAABDBCCBBCBBBABCCBCCBCCEDEDDCECCCECCCDCCDDDDDDEECDCDDDDGEFDDDDDFDDEGEFECEHFEE‚‚‚‚†ƒ„†††††„‡††„„„†……„…†‡‰„†ˆˆ‡…†…‡‡„€€€€upjda_]\\\\\]]]`]]]]^_]]]]]]]^^_]\^]][\]^_YVSQPNQSTRTUUUW\f^IJQl‹|c_x¨»Ò¯˜«ÙÏÑâÝ¡—§»Îéèâà×μ«œ‰{nga\YWTUX]eimmoy}…Šˆ…šƒƒ˜‘…ƒxŒ{llnjiebagjknmmmlmmlptŒÆêô÷ùûýþþÿÿÿÿÿÿÿÿüöâÖ¨sfdba`aa`____`^__`_^^^^__^]_abfiu~Œ£‘€…ƒ„…‡ŒŠŒi_YYYXPJGEILSYajw„”¥ºÎËû¹ÍÖØÚÜÝÝ×ÝáåææäãäååäääããáÝØÐÉ¿µ«£ ž¤°º©š‰wmdbZZUXVUVUUWX[_fonxz–§§¨›—vj`YTOHC><999:;?CKTany„‰“—˜Š††ˆ‹ˆ…Œ¶²µÙجµ´¢ Ÿœœž Ÿ››žœ››››šœœœ™›šš›œœ›››œž›œšššš™š™š›š˜˜™–ŽzF2#""%)0234534444443844556866777888888977786566777777889::;9899:9;<<<<<<<=>>==?====>>?=??B>?@@@@CBBBAAABAAABCCBBBBBCCBBCDECEDDDCBCDCCCCDEEDDFEGEBBBDEEDEDDDCBDEDDDCEEEDDDDEDD‚„…„†††„…††‡Š„‡‡‹‹Œ†ˆˆ‰‡††††…†Šˆ‡†‰ƒƒ|xslfb_]\\\`\\[\]^^^`__`^^^]]^]^\__^]\\^^^]\[ZWSOLHEGIUVVWWZZ\^waIGOi•o[X[zš¡§›§ÓÆÈÝÜ©žÄ¯ ¸äíîîëâÚųž‹|qjd`\ZWWV\ddffpwŽ…„š…œ–¤ ‰¤›‘vnknkhbahilmnnllnnnrz˜Õëõøûýþþÿÿÿÿÿÿÿÿÿÿþü÷æ¸zlgb``a`_____^___`^^^^^^^]_`___eo€‹‘‡‰Ž“–˜—–’ta[Z][[SMIEHJOT]fq|‹œ°ÆÆÒÕãäääääåßÜßâÛÒ×ääääããããããââáââÛÓ¾ÀžªŸ”‰€wha]]abc_[UPNLMLOQRU\`gzƒ£¸¨©“€ogfgc\QJD@<:9888<=DKWcmyšŒ‰ƒ„ˆš¬Ï¿µ³²ª¥°À®‘œž¢œ¢¡ ›£œžžžžž¢œœœœ›Ÿœ››œžœœœžœ š›œ›šš™†[8%"!"'/233555443443445745666866798788887777677779787::999999::99;;<<<;><>=><<=====>>?=@?C>??@@?@AAAABAAAABDBAAAABCCCBCECDCEDDDFBBCCCDEEEFCDDDEEBBCDEFCEEECCDEEDDGDFEGDDDEDDD„‡…„„ƒ…††ˆ†……ƒ‡††‡ˆ………†ˆ††††……†‡ƒƒ€}zwlfc`][[[[Z\[\Z\\]]^^^`^^^]_^][\]]\_`]]\[ZYXUSRNKIDGFIKNUVXXXYY[_saGDKgnhbVVYt™›•ªÍÃÊܲ¥ÎÇ›š¾ÜîðîéÝßË´ Œsnjhe`[XVZafjmn|–~ˆ„šŒˆ‘Ÿ††œ’ˆ€vkllgbbgimmnlllmnot€§æï÷úüþþÿÿÿÿÿÿÿÿþÿÿÿÿÿùëÄžˆpibac`__`_____^___^^`_^^__^^^adqy~ˆƒŠ‹†|wroljdddgidaYSLGIIMRYakw…“¦¹ÈãääØÊÐ×ßåßàâÑÌÉääãØØÙÝâããããâàÛÝÿ¤•‡{smhd\UWX]edf`YTHFAEKMOPRUWbk{š¢•Š}igdiqeXQKGDB?<8999:>BLU`nw„‹’šŠ„~~…Š‘ »Õ±°³ÀËʇ “˜œž žœœœžŸœžœžœœœœ››œœœ›œ šœœœœ›š™›“‹k@'#!"$-22233334344444444566677687778878879767767777789988999:999:;<;;<<<==<<<<====>>?<>=>>>??@@AB@@@ABAAABBBBBA@AACCADDBCCDCCCBABCBBDBEDCCDEEEECCDDEDCDEDDDDDDDDECCCEDDEECDDˆ†…„‹†‰†…„…………‡…††‰…„…†‡‡‰‹„„‚€~wpid`\\[[ZZZ[\\]]`_^_`]`_^\]^a^`__\\]]\^^]]\[ZVSPKGAACGJLNPRUWY[WWX[^qaEEId›€{kWW]z™œ®¹Ã¶Æà»¶ÕÆ›“¬çíïí騨æÎµ¡ƒyurnjd]ZV[_djipy€€—†‚’‘~{~…‰ ŽŠ‰zlmmecbfikkomllnoovЏìòøûüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÕ²”vlcca____________^^_b___`^]___agjr|ƒ‰‹ƒn_a`beimt{}yf]SWPKJHKMT[en|‹›°ÄÜÕÍÁ¶ÐÙåÛßáÔËÓäæçÞÑÏÑ×ÝááàÝÔɰŽwokecaZQNLQZ^ffjbXOFB?CDGKNPRTX^lsz„~rkkmotgYTMMOGB=:9899;=DJT]iv}‰”Œ’ˆ†…„…—¸³²±ÂËÞÇ®«§™’•šœžžŸŸŸœ žŸžž ŸŸ ¡¢ ›ž›¡¡ Ÿ œœ ››™–yI,$""$+/3355554334546666665676666688777877777767666777888899:9<9:;<<<<<<<=;<???=??>>?>>>>??>A@ABC@C@@@@AAAAAABCAAACBABDDECDCCCCCBBBBDCDDFDDEEFFDEEBCCDEDDCEDDDFEEDECDDEFFCDD…„„„…„†„…„…‡‡‡……††††††…„ƒ‚~{{{qlf`][ZYXV[ZZ[\\\Z]]]\]_]]][[\]_^^^^]]]]]Z[\YVUTOLLIGFIPQPPPRUVYZYYTUVY\nbC<Fb˜†•j^_c| ¦®¸´Íá°Åפ“¯çîîëåÐÊèåл§–†|vpia[WY^dkmpvw€‰Œ‡{~s~„‹ˆ†wlmlgdcfjmmonnmqpqz•Éíôùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷áѧ€rfc`__a`______^^^_^^_a__```aabdfop€—‹nopx|€†‰Œsj_X^^VNKGIKRXaiu ²ÂÀ½ÃËÛãââáÛÕØæéëèÛÌÈÁÂÆÃÁ®žŽ€wplhgec_[VRMLKQUZbcm_[PGEDDADEJQPWXY^cmwz}{wttuseZTPRQHA>;97889:=@HOZeozŽŠƒ}}Ž•±±ÂÊÉʼǩœ—•““š£ žžœ››ž Ÿžœœœ Ÿšœœœ œ Ÿžœœ›››œœœ’ƒV1&!"#(.2342434234455555566566656687768977886:6756687777778:98:9::;:;=<======<?>>?>=>>><=<>=>?@?@@A@@@@@AA@@AACCABCBAABCEBCCCCCCCCDCCBDEDDDEEGFDEEEDCDEECBCDDCFDDCDDDCDDDCDD„„††‡‡Š†ˆ………††„†‰ˆˆ‡†††‚€}ylida^[ZZYXXXY\Z^\]^``_]\]]_\\\\]]^_^^]]_]\]]Z[XUPLIFKKNOPQTWUTUYVVWXXXUUUW\m^@9A^˜‡ž‘of`_j‚™²ÈÆÕãɰ·ÈªŸ´éðíâÓ¿ÒèêäÑ¿®£›”އvkb]XZ[`gfkq|…‰Œž…wnt~”Žzvmnlgeehjmnnpolqpr¤Ùñöúýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþü÷縌xie```ab```__]^^__^aa__`__``__`chwˆ™…‹’™Ÿ•‰ˆ…{rgefb]VRMIJJNTZaktƒž¯µÍÐâåæçèâÛáçèéçå×ɯ¥˜†|sliggggedb]VVXYWQNRQSW\a]_QPKEDBDFGHIJMSUY_dlyy€~xojeb^RODC>;988888:<?CNT^hr„‹‡Œ…††…“µ±Ä¿½³¨¤šž´¥™˜—™žŸ¢ ž Ÿ¡ žž Ÿ¡Ÿ¤Ÿœ Ÿ¡ŸžŸ ŸžœŠf9)"!"&+246663433434555556965676668766:98766776556677777777888:99:;:;;==;;=<<=?===>?>>A=><A>>?@@B@C?@@AAA@@@AABAABDBAABCEBBBBCECDDDEFBCCCCDDDDFGHEDDCCCCBBDDDDFEDDDCDCDDDEED……………ˆ‡††„„…‡‡ˆ‰ˆˆ„~ytmhe\[ZYXWUWXYYZ[\[^\^__^^]][]^\ZZZ][^_]]][[Z\\XTROMIKHKMPSSUUVVWWWXVVVVVVVVTX\`V83=Us‡•t`af»ÁÎßçÕ³²Î¢œ´ëóíÞÐÓåçåáãÙź°ª¥›‘†xof`ZXX^jlkrz{|œ’ˆt…£ˆvtmmmhdfkkonnnpppps†²æó÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúì܇rjdbba```__^^__`^```a`_^a````bcmuz‚‘‰…„‚‡„„ƒ‚„xunhaYRKIHLPU[emv€Žœª»Êàåæèéèèèçåäá׬“ƒtmhfdedffgjgdb\ZY^g_ZXWRIOOSXVTUXTPLGGBBCBAFKPUZ]biu„…†zvmd]SLEC?<:98878:;<ABILU[clw†Š‹‹‡Š™£˜‰„Ž’›ž´²³²«–™™¢¡ ¢ ŸžžŸ žžžžŸŸŸžŸžžžžœœœžŸ›œ’uD/#"!$(133332543433445565555666556776888777766667777667777888::9899:;<=:9;;<==;=<==>>>==;=>>>??@@@@@@ABAABDBABABABAABBBBBBDCBCBBBAABBCDDBCCCCDDDDCCCBCCBBDDDFDCDECCCCDDDEED†††„„†…†ˆ„„†ˆ‰Š‡‡„‚usjd`^\[ZZYXXWXXYZ[__`]^^a_`^]]^]]]]ZZ[][]]\]]Z\WUUTPLKJNSSUVWWWY[WXWXWYUXVVUVVZZZ[gW32:N\w”†w`_e‡®Èæé㹯°œž³ÝïîàÝáêçÕ¸ÖèÞÔʽµ¨šŒshaYWV^edghmw’¦³”ˆ|˜™xqmomjdgjkonoooppqvÄéõùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÔ·›ymcbaaa``_^__`__``___^]_^`a`_`achpwzyx{„„‡ŒŽ¡žš…ŠŠŠ}i^]RNKGJKQW[biq~‰™©·ÆÓäççèéèäϲ›„wida`ddccjeefgdc_`^cejgf`[RONKKNTWZ[ZXSNJCB<==DINTUW[`jpuƒ|obZMHD@=:999878<DIKKMPQQW_hr{…„މŒ‘‡„‚…Œ µ¶ØÙÖ¶¨£š™žžŸŸ ¡£ž¡ž¢ŸŸž¢¡ Ÿ¢Ÿž£œœœŸœœœ”…X6%"!#&033333543333444595445666556788777766766677776667778788:88999:;;;::;;==<;==>>>=>?><=>?>??@@CABAAAABBCCCDBCBAAAACBBBBBCBEABBBBCCBCDCDCBCCDDDDBBBCCCBDDEDCCCCCCDCFDDEHD††††††††ˆ…†‡„‚€}ytkb^ZYYYXYXXXXXY[[[[^\[\\__^]]]][ZZZZZZZ[[ZZYYYXWURPMOOORSSTUVWX[YYYWWWWWWUUVUTVVVVWZfZ5,5MRf}—–™Ÿ©zbca}¦Íèå½µ´¤¡¯ÐßîîèèíéÏÊÏêììéçÞÒǵ¡‘‚vjaZXV[dhpnm€ ”–ˆƒˆ„ƒvmnnhdgllonqpqpqty–Öìöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ñ⮈xgdbaa``_cab``aa`__^]`^b`__```beilrsƒ—“‘Œ‘“‰‰Š•yb]\XRMHHILPUZ`frz‰”¡¯ÂØÛæèè⸒tmgcd_\XSW]_baadfca`_adcdfg\ZXUQLRW_g_\Za]ZQKF@AAIJOVUTW\_clw|€pjZOLD@<=====?JT\bdfb[TIQW_dn}ƒ‘‹Œ„~|{€„‡£ÀÏÔÒÌÎÅŠ’”ššœœžŸ ž¢Ÿž›œž¡ Ÿžžœœœœœ››—Œe>'"!"$+245435444453545444564666666777776666778787876777667787899::::;:;;;;<<;=<;<<=>>>===>>>??@@C@@@BCAAB@BCDBBBAABBBBBCBABBCBAABCDDBBCBDCBEDDDEDBCCCCCCDDCBCCDBCCCCFCDEDD†‡‹†††‡‰‰…ˆ„urjc_\ZYXWXXY[Y\YYZ\\\\\]\[]]``_[\\^[\ZZYYZ^ZZ[[ZZWVTQPMMPQSSVV[ZZYYYYZ\WXWYWWUWVVVWWWWW[e^7-0@NWk‘ª¦±¡†sp_`| ÇÞ··²¨«®µÈâîìêìèÑÈÕèðòòòðìá×¼¦”„vib\XU\bfjhq„‡€„”Š¡‘{lonfeglmmnpoprru¡äï÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõ隃icaabaaaaa`aba`__^]^^`____`_^]^agn˜ª‡Š…‚ލ›–iZ\`[WPJJGIKOSY`fmyƒ‘ž¯ÄÒåçß·leb_^^XQNLRYYY^edcbaaace]UX`Zd\[\[`hg`XVY]ZdXTLHHHJKJIMPSZZ\`eluhpc_SGDDEFGGM`iorru€o_TP\YYZbmu}‚‡x‚€ƒ‚ˆš¢¸×ÊÚµ¤ ›žŸ›™˜š›œœ Ÿ§¡Ÿ£¢£žŸŸŸž¡ žžžžŸ›¡™“wF+#!"#)0346456545433445445777656666678868667976677777766678878;;;:;:>:;<<;;::;=<<=====<<=>?>?@BACA@AA@?ABBBCDCBACCCDCCCBAABCDBABCCDCCCCCDCBDFDCEDCCDDCBCDCBBCCDBDDEDEFFEFE„„†††‡…ƒ€yrkc_\ZXWUVWXWYZZXYYZ\]^]]]^\[[[[ZZY\]\[ZZZYYZZYXWWTTTSQSVRORRSTVWZYYXYYY[[WYXWUVVWWXWWWWY[]ga;..1<I]…’©¦™z_[]u’£³º¼¹¸· ±ÓæèæâÞÔÚæêîòóõôñïåÚ«—ƒwld\YV[`fkpspqŽŠŽ—™|lmngehlnropoorrw†¯èòøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷íβ“qiccbbaaa`aaa```__abdegikgcba__di{‡…„‰†~{ƒ‹ŒŽ„qonmjb[TOJEFGLOUZ`fpx„ŽŸ¬¿×Ô½…nhb`[]UUONMNOS]_eedcccceYSMT[[[Ydciij`RPNQ[^bUXQPNLLDCBHLPVVVXX\dfsghZUQNLIJVc‰ƒ~~€qcaa\TPNT\elrx}{yxz}ŒŸºÒÀ®²Àʲ–’”––—˜™š›œžžžœŸŸœž œžžŸŸžžœš›•…Q0&!!"&-23545344454344554456555567677665556566657777766767667778999:99<<<:;;;9<==>=<===<>>>=@@@@ACA@@?@@ABAABCAABBAABACBABBCCBBCCEDBCBBCCBBCDBCDDBCCDDCCCCCCDCCBDDDEEEEEDDƒ†…ƒ€}voh`^\ZXXVUUVVWYY[[\YXY[\__^\\\_\[[\ZZZ\]\\[XWWYZ[XWWXSQTSSVUUVWVUVYWYYZY[YYZZXYXVVVWZXWW[YXYZ]id>2-+/=Jat–«˜™ ¥fNQXkz”¼º¼ÍÜŸ¡ËàÜÑÓÚØéïñòôôõôóòðîèÈ®˜†ynf`]\`afjkgjv‰¤¤®—€mmnhgilnppppqtsxÀìôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõðà§}nfcaaaa`aaa``_`bdgjmtywob_^__`bhpw„‚€xhxz‹…‡…ˆ’”rf[]WTMGFGILOSY_emwŽ·Ó¼}oha_\]`]ZVROMTVX[^aafed[RNNTWXY^efgj_WNIFLOSWWVSVQMICBBGHNRQPPQSW\cconn`UPFNao‹tos€„srkfSIFIT[ekrqptopw}€ƒ˜Ÿ§°·µØÅÔº°ª•‘‘“™™šœ£žžžŸ œŸžŸžŸŸ¢¢¢žœ¡š™‰_8("!!$)23856344554464554445556666658665556666666777976566678788=:;;:;<<<<<;<;;<==?=>>?<B??>@@>>?CA@@@AA@AAAAAABCAAABACBABDCCABBDDDCCBECCBBBEBEDDDDDFDDCDDEDDDDCCCEFFEEEED‚‚€}woi`\YXWVUUVVTVWXYYZZZZYZZZZ\[[ZZZ[\[\[[[[\\\\YWWVYWVUUSSQSUTUWXUWXXWWYWXYYWWWWVWWXXXYYXXYWWXXYY[^hgA50+,.>Pf–˜˜¦ªqNGRO]h‚š·åÕ ¤ÅÌØÄ·ÈÛìòõöööõôóóòñìæÉ°›ˆ|qkfdbcfiokkswˆ§–{nnnhgjlopqqqrtt}™Óïöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûòâ¶}hebbbbdccabhnsy~„‹‘odcadca`cgpt}‚}ƒŽ“”˜—–ŒŽui\ca^WPJGFGGLOTX^eow…•©ºž‚ungeccdcdZTRQNIOTX[aie_\YVSPINUZadkcbUIHHIJPRRQTZSVQKKJMNNNLJKPPQSV\aepb[SGOPVYZ_j}xukjZQEALU]dn~smljptzzxx{€‚‡ ¾ÇÊÐÖÕ˘„“‹“’”———™šœŸŸžŸŸžœžžŸžž›ššš›‹kC.#!!#&02344344544444445546555456656655556666787776666666777789:9:;:<;;;;;;;;;;<<<<=<====?>?@?>?CB??>>>@@AAAAACBAABAABBBBBABABBCCCCCABCCBBBCBDCCCDDCCDBCCDCCBBBCCCDFEEEEDysg`][ZXVVWWVWVWW[[[Y^[[[ZXXZZZ[Z[ZZY]ZYZZ[\ZZYYYXXYZZUTSTRRSTVVW[WVWZYYYZXXXXXXWWWWVUVWYZYYXXYZZZ[]^fhD73.++2FT_l›¢¯lQRVSPS\|°É΢¥ÊÃÅ·²¸ÚíòõõõóññññòñïëæÊ±‹~uqnljjimkknowŒ’vopoijlnpqqqqsuu¦çó÷ûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöìÊ¥‰leabaccbbcp™Ÿ‰—‘Œ„xqiijc`^`bhry…Š– ¨¦¤š™“‰{qoohbUYPLJFFEHKOSY_hn|Š™§ ¥˜Šojgfdc`ZXSIGGIJRZ_jjldaYPKGMPU\af``VPRUQPTGLTTSTY[[[ZWTOFDCFMNOPSW[`jer[TPFIBIPVYXZULG?DP[djqvvmghqn{wssuy}ƒŒ’—±ÙÕØ·žžžœ“ŒŽ•””•™™™›Ÿž£ŸžžžŸŸžž žŸ›œƒQ3%!!"%023343745444446575555574456555657556788797755566788777:9;99:::9:<;:;=;=;<<<<<<>===>=>?B>@AB@B@??@@AACBBBABCBEDDBCCCCCCDBBCDDCAACEBCDDCDDDDFDEDDCCCDDCCBCCCDDFGGEFDja]ZYWWUVUVWWWWUWXYYYYYXYYYXYZZY[[[[[YYXYZ[XYWXZZZZ]YWVTSSUSUVWXWWWWWVWWXWWWXWWWWXWVVVVVXXYYYYYYYYZ\\]dhE952.+/6FS[bœŠaO…u\QLPV¿«Ž¡ÏÎм™®ØìñôôõñëíïðïìíîìèÍ´ ކ€}yxxwurmoosvqtsopqmmotrqqrqsvw†²êôøûÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûñÕÀ™ukeddcdbboˆŽ†‰…‰ƒ„…††sfb_bbjƒ—“™¦œŸ‘ŒŒŽŽŽrn^W][WQLFDDGHMQW\bitŽž¢³¦€vnjfc_\[PHIJKKRV^cfhedWOONNNSTUX]e[\[ZWUPKLLMQUa[ea`ZSNFC>DIJLNPQRW\bmadWOJCFIKBHJKKKMYediknromkmlh```fipuy~‡™²Æ¸«ª¼ÍÛ¶—’…Œ‹““”•–™›œœšžŸ šž žœœš™”Š`;&" "#+13333335542354444555664565555555555667676655567657777788899:<::;;:;;;;;<<<<<<?====<==>>?????>>@@?@AA@@AAABABBBABBAADCBBBABBBABCCCBDCBBBDDDCDDDBDCCDCDDDDCDCDDFEED[ZYXWWWVVUUWWXXY[Z\\\ZYY[Z[ZZYZY\[[Z\ZYYYZ\ZZVVX\]\[ZVVSTTUW\[[XXXXY[XXYYWWWWXYWWWWWWVUW[WZZZYYYYXXY[\biF;741.,/<MVYjˆfMx•s[MHLY|‘ˆžÏâãä ¤ÒìòõôóòïëêíëÚÔàìîè禛”‹‰‰‰…~xqnmkinpooooqrrrssrqsvx‹¿éõùûþÿÿÿþþýýýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷óçƒsedcbacpœ˜Š‰Œ’–˜‰€na_ccm{“›žŸš–‡„†Œ’œª©¥Œ{maaed_XXOKHDEGKOSW\bmw„‘Ÿ±¯½¦›‹xoha^ZWVTQMMNVZcdefaa\XY[URRHQ]]\[abb`]YVMGJMT\fihg_\ODA>CBEJKMMPSV]cbqcc][XQJFO]adffgeceovx{vomc^]]`ekosv~‰‘˜Ÿ¦«³×ÐÎżªƒ|€“–––˜›šŸ›Ÿž£ŸžžŸž œž—‘oC*#!!"'/443243444344333455456445667665556666867666555665999777789:;::;<;:;;;@;>==<<<>==<<<??>>>??@?>>?@AAAA@BCDEDCCBBAABDBDBBBDA@BDABCBCBBDCDBCCEDDDDDDDCCCCBCDDDEFFFEEDXWWWWWVVWWWXY[\]Z[[Z\[ZZZZYYZ[ZZZZZYZZZZXXWTTTUVXXXYZVVTUUWXYY\YY\YY[Z[[YXYYZYXWXXXXXXWWZXYYYXYYXWWWY[ajF<9853/,5CPY]m~}MmŒsOGEMVk† Íêîë¦Ííóöôòóï×ÞåÚ½¿ÇÕêíéÓ¾²§¢žŸŸžœ’ˆ~uronopoppqux{xvvuuux{”Ñì÷úüýþÿÿÿýúúûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ井kgccdkqˆ‡Š†‘ˆŠŒŽ’‹…zvqolppuzz{yxxx}€”œ¨ž˜Œ~{rvy|‚f\[YTNIBDEILPTY_ho|‡–¦°¿½ºŸ€keaagda[WZ^_aZUVY\fgicc[SPNOOSUVW\b_[WPLHMP\feghha\QIHGDAFFHKMPPRTW^ib|„Šwf`]_mrypoedhqwyyƒw{tlnlkkf^^aiqv|€‡™³º¿ÌàÅÐ|xsvzƒ‹’”•–˜™š›œœœŸŸš–„N/&!""%-443223344333375556446445566665666677657655666555677788899999:;;<;;;:;;<<<<<<==<;;;=??>>????>>???@@A@AABCCBBAAABBBABABCBABBBBBBBCBABBCCCCCDDEDDDDEFCBBBCDDEEEEEEEWXYWVVVXZ[Z[[\\\[\]]\\[[[YXZZ[\[\[[[[WVVWUUSSTVWYXXXZXVVXXXYZZ\[ZZZZa[[[ZYZYXXXXYYYY_XZY[Z\Y[WWWXXXXY[`lF;99:53-.9KUUa}VK~´ž•ogUJNUtÁåîêÂ×ñôöôòòêÄ·Ãʤ’©µÔìðëÙÊ¿¹¹º¾Ã»¯ …xrmnnoprv{~~}|{ywvzžáï÷ûüþþÿÿÿõêíîíêóüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷îΩogccdfkt{€Œ‡…Œ—ž–‡‰†|okdcddccgov†”¡¨¦œ’…‚…‘†‘€q][[^XSKGFEFIMPV\bit~Žœ¬ÃÀÐÁ³™tkiiiifddkdQNMPSZacfbj\VSQPOOORZ\g]^XTQOW]ab\Y]g\_]ZXSMHG@HKIJOORT[_k‚•¤‡yjdpwxqfd_mn|xwuwrznpd]OJNYcms|ˆŽ“ªÐÈç‹€zwvvvy~Œ”–˜˜™šŸžŸœ¢Ÿ š›‰\5("""$)3334633455433455556464456666756645666576556665865687997889<:<;;::;==<<<<<<<<?=<<<<=???=>?>>>@?@@BBBBABDBBBBAABCCCBBBBCBBCCGCCBBCDDCCCCCCCDDDCCCCECCCBBCDDEEEFFFEWWWWWYYYYYZ[\]\\\]]][ZZY[ZZ][ZZYZZXVUTTSTSSRSTVWXXXXY[XWWWWZ\\\[Z[ZZYYZ\YXXXXUXVYXYYYXZYYYYXXVWWXXXXY\_lG<::96420/;NRQK6G‘¡§—ƒ{eSKN[{¦Îìí×åíôööõóò楡¿¯’–©»ÉëññìâßÜÙÚÞÆÎüª™‰{topqst}ƒ…†„‚|zw{ƒªêòøûýþÿÿÿýé¶½¿ÀÓöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüó×Ãxoeddgimppq€Œ–Ÿ“‹‡„…Ž–€me`ddddq}‰œ ª¤¥”‹€…}ytqod[]a__ZYQKGCDFJMSX^dny†“¤´ÃÜÙÖ´œ‰upljggfigZRQOSSRRbbaaZZ[]YSPMQRW]^`aacdccbRSTW^]gbma^XRMINCJNPRSUWZo›”~stuvveWRNTZ\_[Zblgba`]XNFDDINU\epuzƒ™œ”‚}|{zvysrr{{ƒ‹Ž’”•—›› ž›Ÿš—Œg=-#"!$'.135443355333344545355566566656555555566656455666587899888999::::9:;;;<<<;<<<<<<=>>???>>>>>?@>@@AAACBAAABBBCBBCABBBDCCCBCACCCBCCCCCCCBBBCDDCDFDDDBCCCCCDDEEEFFGBWWXYZY]\\[\[_]^[\\[Z[Z[[^\Z[[\]YYWUUSSSTTTSSUVYX]XWWX[ZXYVVY_]aZZZZXXYYXXXXYXXXWYYYXXXZXWWXX[VWYZYZYY[_nH<::99873.0?QOD>Euœ›šz^JDSe…¥ÎçÖÛðöööõôóã †˜ª”š¶ÉÝïòóòñðïíéÙÉÇÒÍŲž~xsru|‡‘Ž‹…}{‹µìóùüýÿÿÿÿûæ“’¾‘mËôûþþýþÿÿÿÿÿÿÿÿÿýûúøüÿÿÿÿÿÿÿÿÿÿÿÿþû÷òä°‡vfdbbbaafw‹¨Œ…†’¥§¤sa_a`bdz€›°¬©—Žˆ‚|tpkhda^]^___]\]TNJDDDHKPTY_itŒ°ÄÛÖÜ̸›‰qldejg``aZVTRV]_ea]\bai]TQOQMSUWc_lkkfaZSRMSU[bgleka]VPMMRSSSTVV^…Äœƒv}wwq_WLGDBBBBCDEFFEFFDA??>>ADIQ]ir{…‰‡„~€}…~€|{{{ywst{Ž‘”•›™› žŸ ¡‚J1%"!$&/034445553345444545396566577677678856555566476678889899888999::;=;::=<==<;;<==??>>>>====>>>?@?@@AAABBABBAABCCCBBAABCEDEDDBBCCBDDDCDCDDECCCGDFFFDDCDDDDGDEDEEEEFDY[ZZZXYY\Z[[\]\ZZ\ZYZYZZ[\YZYYWSRPQQRQSUWYVUXYYYZXXZXZXWWUWXXXYVXYXXXXXYX[Z[XXXWXVYVXXWVWVWWXWXZZXVTTV\nG=<<?A@=7112<MHDIh‹¨œŽ•xFBN_i ÂÔåñ÷öõööô䢄†ŠœÍÞîòòòóôóòñëÕÊÁÓàÑÒ»¤“†|ƒ”¡¤§Ÿ—‹„‡“Åíõùüþÿÿÿÿý꫈ÁŒhÃôúüøùýÿÿÿÿÿÿÿÿÿûñèéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñå½™ƒkgcbbbdqƒŠ“„€†‹Žš™œwsnnlgdp{ƒ’‰†€{tnkhhhjeggknnlhec`_^XVMHEEGJMPW^go{ˆ˜©¼ÔÝáÕÀ® tkiga`dfca][[[TMRY^`cfc_ZSRRVMU]`hjlji`YVTVKOTZ^bjcf`\[XWUUTUT]x¢£yqrpljccXSLB@<;:98889::;<;;:<@@AGLWar{}€€~~}||{y}€|„ogefrvŽ’“”˜™šœš”ŠZ9'" "$+/354445433346443333666565667765666565555444767778767;:9889:989;:;::;;;;;;;;>=>==>>===>>?@??@?ABAAAABABBA@CBBBBCAAAABCCBDDCCBBCEDCDBCCDDCCDDDCDCCCCDDDDDDEEEEDGDZZ]]\Z\\^[[[\ZYYYZYZ\\[[\[YWUTROMOOQRRSWYZZX_XWXYYYXWWWWXYYY[W[XYXXXXWWXYY[ZXWXXZWYWXXWVXWWWYX[ZZWUTUTZpG==AIT`J<62/1=JNP]u„š–—™žE?X`gl™¹áíõõôõöô娃||…žËèðòðïòõóñðêÙÐÛèçÒØßÀªœ’Ž™ª¯³²®¦›”Œ‡‹ŸÔðöúüþÿÿÿÿÿôÉÇ͘iÓöøóæÔê÷øúûüþÿÿÿó¤›ÃïùÿÿÿÿÿÿÿÿÿÿÿÿÿýûöïÒ°qhdbacirzƒ‚‘‘‘—••Љ‘wgaflqsrokgdb`_cfhffm{‚vvnf_^^^ZVQLHDFGJOU[civŸ´ÎÛâ×ÕÓѵˆtlcfjlqom`XXKJJMPT_`j`o`_ZWQNUV`jknlrfc]WOIRPUY]`dgca_\[WVWZ`y¨kc_]\`ddcV]LH?:765956677:889::<<=@GU|ˆ”…„‚~€}‚~yˆyrnn`\_eegrz‚–––—™š›•”l@*#!!$)/3334444443444554446667767666765565856666677666666687789889::9::;9:<<<;;;;<>=@==>?>@=????@AAAAAABDCDABBDBCBBBBAAADBABCCDCEEHBCEFDDDCCDDDDDCDDFCBCCDFDDDDEEEFEGEZZ\[ZY[[ZYYWXXYZZZZZZZZ[YXUSRQPOOPRSTVX[[\YXXWWWYYXWXUVVYYZZYWWWWWVUXVWXXXXXXXXWWVWVYWWWYYXWXWWVUTSRST[qH<>ETecVB95110=Z\Zac…¹¨»˜KX‡vhjhu†´æóôóõöô範ksÈèðïäëðôñïðîéíïîåÐÛÙ×Å´¬§«±¿ÉÉÇÀ¸¢™˜¬âò÷úýþÿÿÿÿÿûòì×¥qàøõé‹„¡èíííõüÿÿýç’H\{¾òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûóÛÌ¡|rgeeghqw}~‚†‘–”•’ŠŽˆ}shllnnifedecbafsoqry‰™}sy…o`aa``]\UOJDFFJNSZ_gr}‹˜ÁÍØÖããßɲ’~upppommb]VSLKKLHOUZ_`b]e]WVUZ^ihijkllmaXVTTPQS\effeeeecciqx€[HHIIILQRSQNGB:6655556757884689:;<CRn™•–‰‚{vsolgedfe^QRSSUZ\aeimx}„Ž‘••–‘€K-&!!#'/43345554444453544465776666666865556556556666667766877887788799:;999:;:;=<<<;<<=>?>>>>@??@AA@@AABBAAAAAAACEBBBBAABBBBBACCDDCCCEDCECCBDDEEDCDFECCCDFFCEFDEEEEDEFZ[\[ZY[YXYZZZZ\[]Z\\ZXWVVUTQPQQRSSUWYZ[[[WWXYX[WVVVWWWVWZXWWWWWWWVUWXV[XXXXXXXVVWWXXXWWX\ZYUTVUTSRRRSV\rI=@O`jp^H=631.3EZ[lfo‰˜¡‘ch”¬mg_i~±àêñôöõ꿉kj{™¿ÞïìÙÇßîêçóõõõõîÑ·ÍçæãÏÃÄÈÔáæëæßϾ²§Ÿ¡ºéôøûýþÿÿÿÿÿýúøë·xäúòØtVƒÒγÀÚòþÿûãuSC`œíÿþüûùüÿÿÿÿÿÿÿÿÿÿþû÷òâ´‹xecdchtwzz€”–žŽŒ‰‰‹Œˆƒ}tkha`aaaa`ahs‰~{ƒ§—€†…ia__^^]^XSNHFEHLPT[cmwƒ’£³ÄÜâçæãϯ˜•‹‚|qnndcdeYTRQHGNPUY]^affdb_aggfggimmoda^[VPVZeefgikqyˆ˜›š‡raHB?>>>???@AA@>:765555576678225789;>HSet€‘|xpg[RPTSTTTC=;@QY\]`behmrx†•’ƒU2'"!"$,44346585444565444555677556655665555586856666777667878888::9888:99989;;;;<;;;<;?>???>@?>????@@@BBBAAA@ACBCBBBCBAAACDBBCBCCDCDDEDDDCBBEDCEFDFEDCCDFEFEHEEEEEFEED[\\[YXYYZ[ZZZZ[[YXYXWVUTTSRRRRTVWXZ\\\ZVWVVVVVVVVUWVWXVXWUVVWUXWWWWWWVZWWUVVWWWVWXXWWYXWXVUSTVTSRRRSTW\rJ>E[dmqoTB:63134BT`klj~¨–_m©—Žl^S\k›ßîòööðç¤oiu”¸áíëÓÂÍééìôùúú÷쾺Ãèììéåàçéëîïîíê×Ǻ®´ÇìõøûþÿÿÿÿÿÿÿÿýñÇzèûðÂhaešÂ…Y™çýþýè¥}iTèÿüôìîùÿÿÿÿÿÿÿÿÿÿÿÿÿüôçÀž„kgdixz†sq}ˆ„…ƒ{’ ”ƒ„|sg^bca```dmu}x…Œ„s€{morolg`^^\ZQJFEGJNSX^go|Š™¨ºÐØåæâĨ¥¢œ”…xsijlhcb_VKKMPQURT\^`bhgdfffdgijimlkkdadfffhkqx…•£»Åܹ–‚cXGC@>;<;<8:999866755557568821489=>EHKOZfq|sleMD<>@>=92,(8MY]^^_`bcehms|…„†`:.%$"$)0344555556555543555578657765655555667666677876666688878889888898999::9<;;<;;;;>===>=???A?>@??@AAAABDAAAAAAAABBBCCCCCCBCCDEEDDGEDCCCCCDDEDCEDDCCEEEEEDDDDDEFEED]\[YXZZ[\[_[[ZZXWWXVTTTSTUTTTUVY\]a\[YXVXWWWVWWUUVWWZUTUWVVVVWWWWVYWYWZVVUXWYYYXXZ[WWWXWVTSSTTTTVVUUUW[rK?Jalmwq\G=964204CUbmik˜aq‡‚™ÇjWISb•ÌãóôôîÁzjr¼çìçʰÁçïôöøøøô䮟ÈëññðïïïïïðòôöôíÝÏÅÃÖíõùûþÿÿÿÿÿÿÿÿþóÌ}àùï°\d`’¼ff…ëýþÿôÙÜÌeoçþøè·¬ÙúýÿÿÿÿÿÿÿÿÿÿÿþüøñÖµ“rikt~qopsy„‚rx—§¬…œƒk_b`_aa``eimqu~Љ|zy{€†‰|i^^^`][TQKFEFKOTZais}Œ›¾Ñåá×ÏÅ¿º¹¹—†{spocb\^TPRSUXSHMRV_bfeiefghhklmkiijihghlr…’¦½ÇÏÌÝÞà¶—zfPHA=<;<987777776565566687315=;EAUJKNU_mplkeF:31.,,+(#$/MY^b___aaadihilpnkD3*'&%(0375556766655555456576667766756666677688878766679788:88787788999;::::;<;;<?;<;?<==?>>?A?>>?>>@A@CCBBAA@AAAAABCABEBCCDBFFEDEDDFGCCDECDDGDDDEEFDDDDEGEDDEDDDDDFD]\\YZ[[\\[ZXXWVTTTUTTSSTVWWWXX[]]_][YXVUUVVTUUUTVVVVVSTUWUUTVVVWWWYVVVVVUUVWXXXXX[YWWUURWUTRTUUUUUUUWX[sM@LffiwseOC>985246AO]pokd^m˜Ÿµ‘dMKS[ŠÖéóô칃rpˆÁ×åÑÁ±´çóùøöõòèÁ’›ÑíóôóòòòðäÝïõùøóîâ×ÜâïöùüþþþÿÿÿÿÿÿþõÔ•Òôí²PTg¤Æ„xªïýþÿûõðßulçüóÜls®ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿýõÞÑ£|torwkehiu…‘}}š² y|ŠŒugklifdbacegiiw‡…Œz|}‡•’l\cbb_][XSMGFFINRV\blv„‘¢²ÅÚÙæåáââʱ¦›Œted^^`][^`]VLFLMQUZ_bfebefjklkknjlor{†˜°ÂÚßàÙÑßåäâàݳ”x\PFB=<:9767777665779:::855<=>BEB@=<HSZ`_^M:0''&)))$$$4M\_a``_a]__``accb]NB62,**/4755655645555554445556886645566676554767666666776778888968899999899:<;;;:;;<:<<==>>>??>>>@>>?@@@@BBAAAAABAABBBBBACCDBBCDDDDDCCCDEDCCDDCDDEEFDDFEEEEDDDDEEDCDD__`]]^]]\XWUTUVSSSSTTVYXX[__^\a]\\\ZXWUUWWWUVUWUZWUVVVVUXVUVVWWVVWYVVVVUUVWYYXYYXWWWYWVVWVXWWUUVWWZZZY\rNEN_fmyws]I@=985324?Ndyqpag £¬®È‚WOKP^“Öáîè¶‘wx¤»¶¬§Ìíõù÷óïçÅ©šÐêðñòóóòðÚÖêòõõõôñèæêñ÷úüþþÿÿÿÿÿÿÿþõÞu¨åß²W]¥ÝâxÆíþÿÿýûñÚflæýòÁX@ˆÞíïñôúÿÿÿÿÿÿÿÿÿÿÿûøòã¶{jecbcdkv‡‹‹…¡Ÿ™yp‚ˆ…‡‡{slc`ababaclx‰‹‡„‡ŠŒ“˜‘ukigc`^]_YUPIHDGIMQV^fn{‡—§»ÐÚæçèåßÅ»·³š‰wkhgfeddegZQNLKILPVYeeefgilmlkkmx€‘¦¸ÉÌßäååååäæçæåãßǯ”wfMD><:89877689:;:@@A;;974349>?811=DGFC@90'!!(.*)**-9R]`aaa`b___^^^`ba`VUIB91.04677645556665644434756776865556666656766676668777887889978899:999999:<<;::;;;<=?>??=>>>??@>>?A@@ACAAACBCBCBDBDBAACCDCBBCDEDEDEDFEDDDDEDDDDEFEEEEEEDDDDEFEEDDD__^\]]YUUSRQRSSQQRTUWZZXY^^]\[[\ZXXYWWUUUUUTTTUUUTSSSQUTWVVVWXXWVVYWVWYYXXY[[[YXWWWWXXWVVVWWWWWYXXWWWY]rOAJ[csz|zoRC=;:;7455AScomjn¤¨ºªÍš|]RLU`‹ÈÜæËž‘”š¨®³¹Êéôøùöîdz¡¨«¾ÑÞàìñôóòðßÊ×çéêïôõòðñôøûýþÿÿÿÿÿÿÿÿþ÷Þr{¥Î£`uÖèâªtëÿÿÿÿøâ˜Vyæýð°L?^²Ð°©ÉôÿÿÿÿÿÿÿÿÿÿÿÿÿüôèÁ„mhddehkv‚„Š…‡š‘ƒ‡~tgfbbbabho{‰‚‡ˆ‹Œ‹~wyzsoic`_^]TNIFGGJLSY`gs~ŽŸ±ÂÕæééåÏÎÙʯ‘‘‚|vsnjijf^XUQMKBTSZefgfjimprv{…’¡´ÂÛÎÏäææçèèçèéèçæåãá׳˜xZOB?:;;;<=>9::8879<:6432-02.*&#).///.-)&%0/,('-5BW^aa_______`\\\[[[XVQNJA<;;::9765665544445555555565676555666667777677777788997888878988999::::::;;=;;;;;<===>?>>>>>???@@??AAAACCAAABAA@@@AAABBBBABBDEDCDDDDDDDEDCDDDFGFEEEEEDDDEGFEDFEDb_^YYWUTTRPQRSRSSTUWY\^ZZ\\\\[]ZZXVVUUVUUUUUVTUTSRRSTTTUWVWWXYYZZYYW\\^]\Z]YYYYY[YZXXYZZZYYXXXYYXXWVV[_uP@DP`jzzˆz[E=<<::7736DRc{zx’±©¥¶Î¨¯u\UPWc¼Ðçëç´ÆÝîò÷÷øò浕£´ÍççâãíôøöõóêÉÂÄÄÍàîîòóôõøûýÿÿÿÿÿÿÿÿÿþùãs_a„™ƒ²ÛÑǃ{áûýÿúè¦ccëþïžE0I”u`p—íÿÿÿÿÿÿÿÿÿÿÿÿÿþüøñÓ¬Žrjeddegmpu~ˆ“˜™˜—“Љš˜sebea`adir~‡‡‘Š›”“‰„„„…„se`]^]]WVOIHFHKPT\dmx‡—ªÂÖåèèßÜáß¹–”–œ¢–‹qnm`\ZYSOQWY\dfffgis|‹š©½ÂÄÃÒÝËÏâååçèééééçâååæçäݸŒiRD>@AB@?;70*()07DB@<<=/*% "#$'('%#)36+')/7EW`ab`c_``_^a\\\][\\\WYSRRPGB?<:8766554464555654456545756556666778:7889998:8:8789998788889::99<<;<;<=<;<;;<===>>>>=>?>@?>??@@AABBA?@AAA@@ACAAAABCCCBDCCCDEDDCCCDDCEDDEGFFEEFFEFCFFGEEEFE\YXUTRRQRRRSTUUUWXXXZ]ZYYZZYZZZXXWVUUUVTUUUTSSRRRRSSTSUVVVWX[\[[ZZYX[][[ZYYXX]YYYYZXYZZYYYYZXWYZYYXXY[_wO@AFPZiw~„^G><<<<:8566AM`vx‚€„¤É²–˜ƒxe[TZe¤Íìïé×ÈÅÁÙñö÷ø÷òëʰ°´Çßñôïìñ÷ûúø÷í¹±µ¯¢Ëíôõõøûýÿÿÿÿÿÿÿÿÿÿüé¤`\kyvou’œ’‡Åõûÿô»q\m³ðÿî“@59EKA;{çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôØÂœzqigfggjlu|„‰ˆ‹Š‰“˜¨‰xd`gebadhp€}„„‚ŠŽˆ†„‚…‡‹˜wf_[^_^^]ZRMIDGJNSZ`kv…“©½Óåèçåå弩 ±¼Éظ ~whea_^^_befhjknqz†£ÃÊÙßÝÕÎÖÙÕÐäåæçèêëêéæãÓäæèçÝ̹ºs^GA?;840-' *.9GGH>@4' !&&'$$)4/(',07EW___^]]]\\\\\]Z[Z[\ZXXVTSQQNJGB?;976455545566667765435554566888887776888877:8889<99899888:999:;;<<<;;;;;=>>=<=>>>>===>????@AAA?CA@@AA@AAAAABABBBBBBBBCCBCDCCDDDCDDDEEFEEFFDFEECFDDDEDFDVVVTSSTTSTUUVWWXZ\]Y[XWXYYYYYZ\YZXVUWVZTTTTQQQQRUUXUUUXXWWWY^^^\[[\\[YXYXYZWXYYYZZYZZ[ZY[[ZZXXY[]ZYZ[\_vOA?BHP[kwu^G>>@@@<:8546>I[q~„¤¬°ª«˜¥žšlTY`¤ÎëìæÄÁÄâíóôöóèÆ·±Í·ÑÚïôöõõøüûúùî˪²ªšˆ’ªêòõöùüýÿÿÿÿÿÿÿÿÿÿÿö輯Ïݧju†~a[»òûÿî—gf¬é÷ÿí‹C7550-4kæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷êß®†wkhfeddhq|~~ƒˆ“—››™‘{hhlid`dhtxxutx‡Š‰‰‹‹ŒŠ“ˆi`]a_^_`\YTPLGHIMRYajt‚”§¼Ôåæåáܺ¿ÊרäâÜİ –„wrruxwvutuv|ƒ ³ÛãååäåæåäåãææçèééêëêéæãÚââáÔÓª¤’}R?71.-,++&!!,/:ACC@;0! "$$'&)+,%&*15EX]_^^^\]\]\\\][\[Z\]Z\[\\ZVUVWNHB>;:766656666766666756565776667987799888877:888997778:::99::99:;=>;;::<;<>>>=<?@@@>>>>>>?@@BAABCAAABA@AAACACBCA@AABBBBCCCDEFDCCCDFEFFGEEFFFFGGFGDDEEDFDURTUTSUUUVWZ[[ZZ[]]XXVWVXWYXYYYYZXVUUUUTUSUOQRSSUUUUUVW[XVXZ^^]]\[[[ZXYZYYWUXYYYYZZZYYZYYZ\\\\\\^ZZ[[\auPA@@DFWckv]F?@ABA><;8654:AWt|‚Œ“¥¸«š ¥¢ƒjWW]zžÈåÜ»ªÁáêëî鯛ž®±µÏÎíò÷ööúýýýúóèËÉ»¢„¦èñôöùýþÿÿÿÿÿÿÿÿÿÿÿÿüõñðî㵞·Õ—Og¹ôûúßvU“åúþÿë†H>7/+/5kåýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüóæ¼–…rliefginty~ˆ•‘‘ŒŽ‹”}|~mcegjlkkjjrwwxyzzzyyqd`^^^^a^]]]ZWOIIIMRX^hr‚’¦»ÐåãÜÒ¶»ÛÔÜææã×ÉĶ¥‹„‡ ŸŸ¡ Ÿ ¤²ÄÌÒáéëèåæçäæçèèéêêêëëëêèãÒÍÖÙćk]PB5.)''*+--("%%'*,-...# $"$')+%"(,3FZ]^^]\Z][]]\[ZYZZZ[[[[Z[[ZYYXXTROIC@=;9855567766654555576667867777798888877:888::8:8889999<;:9:;>=;<=<=<;>=>@>>?>>>>>??????AA@BAAAAAAAAAAAABAAAAABCBCBCCDDEDCCCCEEDDEEDEFEEEFEDDDDDEDEDVTTUUVWXZZYYZ]`\\]]Z]WWXZXYY]ZZYZXWUVVVTTTUUTTTUUVXWWWWZYYZ[]]\[\ZYYYYZZ\YXYZZYYXYWXXZ[\^_``___^^[\^_bdwRBABDESba^ZE@AGFF@><;86437B[wƒ…‰œ¥²³¸˜qTT]{œ¿Ñ°•™Ÿµ¹ÌÚ·¬ ÀÓåôöùøøúýýýúöïÚØÙ¶–¦æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿýûûûöòñðíÓ˜…ÏúüôÓ]M²Øï÷ýèZmE.28;måýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøïа–wogfcdgkmw‹”‘‡Š‰Œ’•˜š€uieca`_``adgilnmkkjhc`^]]^^^]^^]_ZXSNLILOU\fr‘¦¼Ðåà˵µÈÕÛååæáÝÛÖ³’‹ˆ”®¯ÚÙ×ÝÜÕÖßæèëéççæßÛçéèèæäåæäâáàȯ˜„wjWE:3201-,+*.-1.+#!%! -%""%*(!%1I[^^_\\[]]`]][YXXY\\`__[]\\[]\]YYXXUPGA=:88877778654658676668767778798;8:788:9989888:988889:<::;<<<;=<<<<<>=?===?===???????@AA@@BBBAAAB@CAAABBDAABCBBCABBCCCDBBBCDCCEDGFGFEEEEEDDDEDEDFDUTVWXZ[\\][[[]\[ZYYXWUWZZYYXYWXWXWVUTTUTUTTSTTVWXXYYYYYZ[\\_[Z[[ZYYZZZZZ\\\\[ZZYZZZY[\^a_^___^^^^_acdegxSBBDEFTtfXNC@AEGFB?>==9654:A[€€‚ƒvލ¯²ª|£›\LUc—¡¯ 𥮲ºÊÀª“¤µÈåð÷úûúúúûüýýùôïâ×ÕÑ®˜´æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüóäáóþýó´UBrÁðûå}UVQLMHCqäüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøåÖ¦ujhhouvx…ˆ‘‘“—™”‹‹‰ƒ~uuvkcbaaacdeeefeeeeb`_]]^^_^`_^][\aXSNJLNT[fq’¥¹ÎäÖ¸¹ÀÉÍÓâäåßå˼—}|†•·ÀÇÍÜÞÞååãæêëêèæ×ÝæêèåÞÆ¼ÉÒ©‘~k\NE<60,)224630,'&$$# - %'#"0M[]_\Z[\^\_[\ZYXXY\\]]]Z[[\[\\\ZYXXYVRMHD?<:9876655555656776777777767777888::998:788999:9::::9:<;<<;<<====><<<=====>>=???>?@AB@@BBBABBA@AAAABBBBBBCABBA@BBBACBBACEDDDEFFFFFFEEFFECDEDDEEWXY\^``^_\\[Z\]YWWWWVW[YYZZXZVVVUTTUTUWUWWWVYYYZZ[\Z^\`_^[[[ZZZ[ZYY[\\^^^__^]\_]]___``cbb__aabbchhhiiijyTBCN\NUrkZHB?AEHNE@??=<98557C`‚„Šƒ£Ò©¥‡ª“_JQhŒŽ‰™¬¯«±Æáë³Ôâñõøúûûûúùúüüü÷ðîëãáÕÁ¿ÅéðõøûýþÿÿÿÿÿÿÿÿÿÿÿüúüþÿÿÿÿÿÿþüøóùÿþôÕWFAc‡ëúãyX‘`KZlXuÝðïíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùê¶‹{nozƒ‹…„‡˜¤œ¡‹‰‹‰Œ’•ƒsba_`bbb`__`___`___^\]]]]^^^]\\^][YRNMNTZdq’¦¼ÎàÈÀÀʼ³ÁÜâãÈ»§£whddirˆ›¦ÀÍãßãÙÕßäèéçåÛââãߨ·•Œ…s`M<40///.-*)54@770*" - - !(% 1P[]\ZZ`^^^^]^[\Z]^_\\\][[\\\^^`_`\^^^ZYSRLE@=;97755566656687677877877788:899;::99888:99::99:;:<<;<>??<>>>=?=>=?=>==>>=????CAAA@@BBBA@@@@BCCCCBDCCBBAAA@@BBBBDCDDDDGDEEEEFFGEEFFEFDCCDDGF[\^aab][[YZZZ[ZXUTWVWXYYZZWVVTVUUTUVVUVUWYYWYZZ[\\\[\[]]\ZZYYW[Z[Z[[\]^]___`_]__`bbaabaab_acdffefghijkjzUCGTVUapmgG?>?BGLMD@?==;:876>C\‚ƒ„…‹” ŸqŸ[K^u™~Œ¤ª°ÁÐÍÉËÎäõûûûûüûûùøùûüûóåâìíéã×ÑÝîñõøûþþÿÿÿÿÿÿÿÿÿÿÿúöùüþÿÿÿÿÿÿÿÿÿÿÿÿûãfYEƒæùáw[‚qp vTzÖÇ¡±çûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú랊x|…€†~ƒˆŒ’…|‡’•˜Ž‘†ƒwgkliedba`___``____```^]]_^_]\]^]][[VRPPTYer’¥ºÊÔÉ«–¤³¨œŽƒwkaY[]djr{”±¿Î÷±°Íãæã×ÏÍɽ±oWC;4-''(16@H@6.(+.01-'! - - - ''(!3Q[[\[Z^^`a_]\[\Y^[\\\[[[[[\\^^`]_[]^^ZYWWVNJGC?;855876656567787776798789977788999:97:::::99:::;<<<<<<<<<><<<==>==<====>?>>???>@@A@@@@@@ABDCBBBCBBBA@BA@?ACBBBABBDDDCDDEEFFEDFFEEEDCCDDFD_cjcd^[Z[Z^\^XWSRUWVZZZYXVUTTTVUXVUVVWWXZ[\[[\_^c_^_^^_\[[ZXYZ\\]^]\^]abc````accdbfaaa`abcbdeghinmljkkjxUIJYioxysjHA??AGKKGA@??=<;;::;BZn{Ž…|ˆ™š›]Rp Ênow‹¥ªÚÆÂÇÍãòöúûûûûúúúùúûûùîÙäîñòñîëìðòõùûþÿÿÿÿÿÿÿÿÿÿÿþõåôüþÿÿÿÿÿÿÿÿÿÿÿÿÿöç»šŽ˜íúáu\¦€ƒ|c€Õ²šv·îùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÒ°–ƒ~{{yyxwƒ•§š˜‹ˆƒƒ~}ztec`a`b`_```___^^^^^]_]^^]^]]]\\]XUTRWZeo€¥¿É×Äšs|~„†pjea[YWZY\_fo~Љ†‰ŸÅÙÙÖÀ”~ocSA3'" !#'-5>I`^X</$! #$" - - %)%!4SZ[\_[__cab^\[\Z^[]\_[ZZ^[\]]^`\^_``b[_[YXTTTNG@9668775555688886777887999:;899;:9:;9:9=:::>;;;>=?<=<<==>=<<<==@==<<=?>?>>>?????@B@@AE@CBBCDBCBDBCBABBAA?@BDCFAECDCEDDDEEFEEDFFEEEDDCCDEE`gc_]ZZZZZZZUOMLPVXWZYXWVTUTTTTUWWVVXYZ\^a__]]^^`_`a^]^\[Y[YZ[]^^^^^^]_abbcdddcccbabcdddeefhijkkllkjjkjw`TY`sˆƒxoM?BEHJPYJB@@?>==>?ACCBQar†}ytyž¯pZŽÑÄqfio€³¾ÅÒÐìó÷úûüûúùùúûûûûùëèëïòóôôîëîðöùüþÿÿÿÿÿÿÿÿÿÿþüíáîûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôíèçõüã{_·ÅÈÊyR‹ÛΩ‡ŒãóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöÞŦ‡…wtqpnmnqq~™‘•ކŒ‡‰‹”Ž‹viiffdb``c`a_^^]_b^___^]^]]]]\\\]]\ZXWX\fq€£¶¼ÆŸyutqnywsmibaa`_]\]]`deegiu’€r^L>1,(&$**3:@ELQTU\dL:+!! - - - - $#""6TZ[[[[^^^[[ZZYZY][[Z_[ZY[[\^]^]]\\]]]\\[ZZWUTSNH=76:9888667877767877777789879899988998:::::99:<<>;=<<?==<<<==<===;<<>===?>>@?????@@AAACBBBBABBBABAAAAAA@AAAAABBBCCDEDGEEEEEDEDEGECCCCCDD``_[YX\\\YYSMECLNY[YZYXVVVVVWXZW[YWZ^^^_a___aaa__`_^\]^\]\\\]]_``____^_dhffedeffedcfhijijkkknnolqlnjjkjnfkxy’¯“}rQFLQY[XULC@@C?=>AFPMH@@N]n{znužµ\°¶Ÿys_`ez«Ï¿¿Ûîô÷øúùøøùúûûûúøôðíìðóö÷ñÜéðöúüÿÿÿÿÿÿÿÿÿÿÿþùáÎÚíõûýÿþýþÿÿÿÿÿÿÿÿÿüú÷õúýꉂÃÜåØuS™à䮘Îèîòõúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûïä³~omfgddhm{Ÿ•ŒŒ”˜œ›š“’€yrpmfb_`a`a^^^^a`^]]_]]^^]]^\\]]\\[YXX[^hs€¢¶·Šxsmy‹“…uv{wqla\[YWUSPOUZVRH<2)#!( &,19>OXk`e\a^[O@3'!! - - - - $$!$<V[^[]^]]_Z\YZZZX\\]^_[[[]\\]^^_]]\^^^]]]]\]YZUTKC649;;:9977777;7777787879887989999:9;;;:99<;=;<;>;==<==<<<>>==>=@=<=?<?@?>=>?@>???A@@ACBCBDAABBBCAAAAABBBBBABBCCBCCDCCCFIGHEFDDEFCDCECCD[[ZYWUVVUSMFNBJOTZYYYYXUWWX[ZYXX\^_a__`aa__`bb`_^\]]\Y]\^__^_`abbbbbbcfiihhhffffghiiiijllllllmmlpjlkjll{wˆ‚€‹•§ ‡pUKYdqƒjWLCA@A@@?CHS^SHC?JWj}wr‰¯q]¨š¤¤¢Ÿv__\oˆ¤¹ÆÕêîôö÷÷ö÷øùûûûúø÷÷ðéæðöøîÜçðöúüÿÿÿÿÿÿÿÿÿÿÿýúáϼÑë÷ûÿýûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþðºÔîóÝlS£Û̳zg¯Õ»°ÊðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúëŠunhgefgs|‚‰‡Ž“‘ŠŒxga]`a``________]^]]]]^^]\\[]]]^^^^]]`hqŸ²©•Ї•¬Ä¶©–žw~_]ZWJGC>8420*&)!#&)+2;>CFGLca_WROLE;4+" ! - !## (EX\][]]^ZZZZXXVZY][\\][[\]]^]]\[[][[Z^\]Z[[[YXXLB:1;CDDA>;888887777888778888798999999999999::;;;::;;<<=<;<<<<<==<====<<>>>=>>>?????@?@ACCBABAA@ABAAABAAAAA@BABBBABDDDBCDFFFFEEEDDECBBFDDCYYZXUTTSPNFJMRWW[YXZZ[\\[Z[Z[[^`abcdcadcd_bahb`]\ZY\[\^_b``accfbcdfhkinhlijgghiiklnllllnpnsnpnompppkkmnu{™®„s’¹£•v[\n}‰Žz[LCBBBA@@DIZ``RF=<J`qƒwth^Yž™®¸Òªde_^s†¤ÕÛçéíòõóðòóôöùüùöøúóÙÙëôõßËãðöúüÿÿÿÿÿÿÿÿÿÿÿþýöëØÏáí÷ÿ÷íòöúýþÿÿÿÿÿÿÿÿÿÿÿøñîíõôÒcV¶‘nNW“°†‚’¿íøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûòÖ¸š|rjgfegnsz‰‘’Ž’“–› ¢¢¡“Œlb_a`_``_]_____]]^]]]\[[]]\\^_^]\\ZY[^gp}Š˜¦Ÿ¢¨¯¾áãÝ®”…zf\JD=73+)''&&'),06:@AGPWFDFIOTPKIE@71+( ## - - - - -"!&$&!!,M[]]^_]_Z\ZZXVWZ[][_]`\\\\]^]`\]]][\[^\\[`[^XVOE:5:GMSROG@=<<;977878777;8888799:9;:99:9::99:9;<;:<<<<<<<<>==<<<<<>=?<==?=?>>>??@??@@ACACCCBAA@@@@AAAADAABBBBBAACACCEDBCFDDDFEEEEEECBBEDDDXYWVVTUSSPSXZ[YZ[[[\\\[ZZZZZ]^_`cdcba`aab_`a_^]ZZZ]_`abbbbcccdedffhihihhihhhjlllnonnnnnnljmlmmmmmmmjkoo}© ˆqž¥Ž~idu•‹„^KCBFABAADHWd`\M@>>KZkzvh[U}œ³½Ð·›jeomj„§âÚÐçîòçââßÞî÷üøôøüõÛÊéòðÍÄáð÷úüþÿÿÿÿÿÿÿÿÿÿÿÿýúòäÜåòÿðßåíôúýÿÿÿÿÿÿÿÿÿÿÿÿÿýûý÷¼i]º±rEDN˜ÂwKf„ÙðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøæØ¬ˆ{mighjos…Š”–˜™š£”›•’‡ƒwic_``aa`^_a`a__`b``^]]_Z]]]\`[YXWUSTUY_fny„›Ÿ¯ªÏÑÚǯ‚k`WMD=1-)+'()-04;=@BDEEBFIMF>50258:85672-+)$#)# - - !#&%&" 1T\\^^\\[YZZXWXX[[[[]\\\\[\]\[[Z]\][\\]]\[[[YSJA:4=HMRRSRRKFD?=:9877777787888788889999999899:9;::;<<<;;<<<<<=<=?=<<==<====>>>=>>??@AAAAABCCBA@@@@@@@BAA?BAAAAAAABABBCCBDCDDCDDDCDDCCCBCCEEWWVWYYXY[^`\[ZZ\]^_`a]^[Z[\^``abfdd``abbf__ZYZZ\^`deffjihccddfhhjjjhliiijkmnnpsttrsoqmkkkknopmnmnnnkmnoz‡ ´ˆrŒ…€spzˆ‹ŽŒ`JCBAABEBBGTah_TE?<?JXitka]q˜ÒÒÖ²³ihvz„~¤ËÌéðóëçáÑÕéõú÷ó÷úôÜÛëðè¼®ãð÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿþýú÷òíõýñÚØÚèñùÿÿÿÿÿÿÿÿÿÿÿÿÿþýþùǑʷ^TZW—k`fÂÖéóûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøê½–ƒrlhhkp€ŸŸ§¤¤Ÿ˜„|vrlfb`aaba``a`^^^^aaa_]]]\YYXXYWUROMKMNQUY^djt‰’§«¤™vXJ;620.-,.037<ACEDCCCA><;77870'$$&+--.//.,3*)'((# - - - !$%&&&"! 6V]__]\ZYZYXWWZY`]]^__^]]]`\\[^]]\]\_]^^^\[XXM@529GOTSVXZXXRPHB=;:8877978788;7888999<::99999;:;:>;><;;;;==<<>>==><==<<?>=>>>>>>???AABA@@ACCBBBC???@@BBBBBAFAA@@@BABBBBBBCCEEEDCDFDDDDCDCEDXYZ[\\\\]_^]\[\^_a^]\\\\\\_aaaabbaa`aabb`\_V[\_bcfghhggghbefghhhjjjiijklmnooqqrrrpnlpkllmnoopmmmmmljkjnwƒˆ„|{}}€•„t}ŒŽ‡eJDAAAACCBEO[[_PHB?>>IT_pf]i¥¯®’fs¼€«tv•ÍãòöóñíÞÒêóùöòô÷óçåìè˨°äðøüýÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿúôøûòÖ¹¶ÀçôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüîØÀÃÅka\`¡¯kg–¬®©¹ÜíûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúíÊ©‘yqjlp}˜”œ‰„|xtnligdcbbabbab````^]]]^_]\\[XURONLJHIIIKMNRUX]aiklnmjgb]PC?333579<@DGJKLCEB>;8631/-,+*(# ()*'*++++*'$'# - - - -!#%&'(%"!!:W\^_]]YZUTSTTVY]\]^___]]]_[\Z[Y\\]\]]][\[YWMA8/9EKRSTWYZZXVUTLFB?=:9897767788899:9::;::9::<:9:;;;<;::;:::==<;<<<<<<=<>===>>>>>>??A@A@@@BCDAAA@>??@@BCBAAAAABBAAAAAABCCADCDDDDCCCCCBDCCCCCYZ\\\]^\__`^^^a`_^^]]^bccagddacaabcbabdaa``acdegllkkkggghiijkjjjnllnnopprrwwwrtpnmkloqrqvswponnmlmllljju}…–„ƒ…‚zw”™ƒ„“’bJFFBCBBBBDIPTQLGEA><>EL[mc^lz‹³{o}ʱ§ªlho—ÌÙíðòîêíñóõôòîíîðñêÏ´®µêòøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúúùï̸²Íéöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôõòå€_R^¨©l‚ÚèÜ’cŒÑãøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÜÁŸ€upsŒ–ƒ|uolheccbaabb_bbbba___^^__\[ZYTRPMLKKHKHIJIIKMMOQSRQOMKHEB@>><;;;=AFILJIHHEA?<9642/,*(&%##" &&'%'(+**)%$&" - -&')*((%"!$?[]__^\ZYRQRSTUY]^caaaa_`__Z\]]Y`]_^_^`_`[ZQE:25CMRTUX[[[[\[ZXVURJC?<:98777778;999:::;<::::::99:::<;::;<<;=<;;;<=<;===A=?>??>>?>>?@@B@EDDDDB@@?>@@C@AAAACAAACAAAAAEBBCGCDDFECCCCEDDCDCBCGCZ[]_^^^]_```^^_Z_^____aabbcdcaaabcefcbbabbcegjjhjijijhjkkkkljjijmnoppqrtsttrunnmmknoqsrrrprpnnlmlkheddhty|†€†Š‰‚Œ‹Š¦šŒ€cQGDBCCCBBCFILMIFECA@?>@DUmd\W[Êp‚Æ®®©¡i]bh†¦ÈçìæäìôóñòñÝ×ßñò踧¡¿îóùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøíÔÅÍäíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíhX_µl›èòä†QLzµÙ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùçÚ°‚v{~zyunljigedecbab``caaa`_``b]_\YTQOLKJIHIIKLKKLOLNMMJFB?<987<9;=BGHJMOONILIFB=:7410/-)(&%$#"! "#$"$$$$$% ,$%)!"'!&)**)(%!!'H^]aab_[ZPMGHKRY\^abb_a_^[ZZ[ZZZ\]^[_^^\\[SIG<?BMUVWY[ZY[]][ZYYXTTMHD?=<978888999999989:9:::::9:;;:::9::;;<<<=<<<<<<====?>>????>>??AA@AAABBB@@@>@@@@@@AAAABBC@BBBBCBBBDDDDDFGHCCDDCBBCBCCC\^b`___`ccc`^^^^__abdccbhbbbbabbddedccbceffimjkhkhhjnnnnolkkilqqprtuutwvvtvttonoqtxsusrrutuoojjheefeeghrtx“”™ƒ‡ŒŒ‹‹ ¨”‚jVGDDCCDBBBDEGGFGHFCCB>;<DYqdXSq‡›u†ÄŸ³¹£ma[\b|•¸Ü¾µÔîððóñåÈÖìñ麟¥ÌîôøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïéÞáæïöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøç©t‹Ãm©íòá}HENu§ÏòùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøëÄ£Žzynlgeeeedccdccbb`abaa```_^ZWTROLKKJHIIHJKLOTINKIEA?<;:::<>@AEIKMMLMMKHFDA<952/,*)(('%#"!!!! ! "# ! - -,F:**6=C%-%'.00.*(%!!+O]_agcd_\TLBCFPW\^bbf`a^]]_Z]ZZ[]\^^_^b^`ZPKILRRZ[_`aa`\\]_\ZZ\[XXZSQHB>;:9889<;;999989:99;9999:;<;::;;<=<==<<>=?======>@?AAB??????@@@AABBBB@@A@@@?@BACAAACCDBBBBBDCBCEDEDCEEDBCDCCCBABCDD`aaaa`bcbcba`_`bcefgcbbcbbbabcccdefdddeefihjjihgmikklkmmmmmmopqrsvvuussstrrourttvvursqqpomljhffdeehhjno{pjop•™‚‡Š|l|š£ Œp[HDCCBBBCEDDEGFNOPNLIC?<?CSl`R\e^ZƱÓÁ©ŽlUZ_p€š¦±»ØíïôöîÏÆÜñíĪÒîôøüþÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüñâ¿ÂÌßòüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúê¿´›qµðòævf…^HfŒÄóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÕº‚xnkifeefcddgddc`bc`]]]WTRPOKKJJJKLLMNNKOOOLGC==8;9<>@BGJOQRSRRJIFCA@;8530.,*)'&&%$##"""!! !! ! *GHJ51Jf@'2-# %/=A;4+&$!/S\`aabcc^WM<=>IT[\aab``__`_[\[\\\[\]^^b^\WSPRRUWZ[]]^_____]]\\\\ZXYUTPKGD?<::99999::989::99;::::::::::;<<:;<;;<===<<==>????>?????@@@@@@AABBB@?@??@@@@ABABBBBDDBBBBCCCDDCDDDFEBBDCBCCBBBCDEccccfccddccdeegeedhecbfcccdeefgeeefffffghigghigjnmqlllmmnnspqswutuvuurqsvssuwu}utttuvpqqpjhgfedegkqrturzmhhhv…œŠtfjб®£eODCCAABCCCCCDGQ^]\WRKE@=<BUjXRUNT|ϯÖо hYZ\jz“¶¶ÉîòöùôÕÎÑïð׺À×ðõøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøï×ÁÅËâêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóóè¯x¾òïÛspriKImŸíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷æÕ«Š{nkhgffcdcdddbad^\VSQONLLJKKMOONOOLMIGGFEA@??@ABEIJKIQHQNLIECA?;8641/,+)('&&%$$#$##!"""!!! "$:WjP<Hfh@%"/A?5(#).-)'*=WWY6+&#!4X]dababaaYOB:=CUY\bce`a`b__^^]__^]a^^_b^[YZWVX[[\\^]]^^^^^]^]^_\\ZZ[\WYVSIB>;;:9899:98<<;9:::;;::::::::;<;:;:;><;<??>>??????B@@??>>??@@A@ADBAAA@?@BAA@DACCBBCCCBCBCCFDECCCEEDBACCBBBDBCDCEfgededdedcdedbbbbbbbccdccccbdddeeeghhhjkkljiknnonnnmmlnppoqqrstusrsrrrrsvuvwvvwstttvsomkiffeeeikmqppqqrzkWZ^djkl|ydkzš¿«˜{XICBAAABBCCCCHUeuj`YRMFA>?@KWY_YS|××ØÂ¬§®¢k[[ajv“ºÅíôøûöãÉíóîçØâñõùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôéסÀëûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøßÆËôíÄo~¶nVWZ‰èþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷꺕…tnjhfffcbdd`^[WSOMLNLMMOPQRR`POLJFCF?CBBCDEHLNONMKJIGEC@=:8531/-+*)('&&&%%$$$$$$$$$$$#!!! " &2?5%$1Oo`SNaek>-;SYjD*0<FPA/7LSWI5("#:Y^_____]\ZRKIGMWXYZZ[[^___`^^^___^__`ab]\[ZZ[[[[[[[Y[[[ZZ[^]^^]\\\[[\XWUSOLID@>;:9::989999989999:<;;::::<<;<;;;<<<>@>@>>?>@??????=>???@A@@A@@@@@@??@AAAABBBBBBBBBBCCDDDCCCDCCBBBAABBBBCDCFkggghhhfgcefhbbbbbccecgdcccdeddddfijkjmklnpqrnonnnppppqqtqstwsrrqqtssuvwvwvuvwtxtrqpljgeefjlorttqtoqonokSRZlh]eu‰yvxˆŸ°§ŸmQIBBBAABCCBBHWpzwc]\[NE@=;>J^bph€ÑÑź°ÁËȾ^cgjrˆÓîõûøìÅÄîø÷öôòôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷é˰¨×õûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôèóûî¬lŽ–msšp{åúöóøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùíɧ‘zrkiffd`][XVTSPNMNOPRSTSSTTSPNHKFFGIJJJJKHMKKHEB?><;97630.,,+*)(''''&'%%&%%$#$$%'*,*&#!! " !##%(,18=(.D_`a=)7gd[SMdnR;?ZSgjT8=Qf]a7>KTPB0& %AZ^```__\[ZVVTUXYXYXYYZ\\\]a_cacbebcbebb]^]a\]\[[][ZY_\[YZ[^]_^\\]\^\[ZZXVTTQOHA=;;;::99999:9999:;;::::::<;:;;;>;=<==<>>>?>@?>>@>?>>???BA@@@@@@AA@@CCCBBAABBBCCDBBBCDEDCDDCECCBBBABBCDCBDCDfffffgfedccba```bcddccgdddddddfghiknlkmmnppnoooooqoooopqsqrtrnnnqquwxz{zzvttuxtrqpnkjhkfiknrrrpppooonmlpkIMWgg_[o¡ž v|‚’±§ƒiRIDCBBBBCBBJYo†veca]WLD@=>>HQ_q†Äšx™ÂÌçáᾤžoQMQw´ÔòûúòÍÃðúûüùö÷÷úýþÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúèÑÅÌñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýþÿïªkœdpxovÞîäÜæõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ× ƒwmhfa]ZVURRRRRTXZ[VTQNNMNQMLLKIOKLKMKHECA@><;963210/.,+()))(('''&&&&&&&'&%%$'+7?@C0$"!!!!! "#$%),**8A@CGJVcQ9)(Dadh_P,:cbB,GfW?=NQTakJ8Mg_]L9=HIK4&")H\^_]^\_ZZZXWWXYZZYYZ[[[Z[[]\^``bbaabdcd^]]`\]]]Z[\[Y\[ZYZ\[[[[ZZ[[\\\^YYWVTRQPJFC@?<;:::9999:;:9:::::9:<;;;;;;;<====>>??>=>???>>>=>>>>@@@A@?@AAA@@@AAABAA@A@@@CCBBCDDECCCBDCCCCDBBBCCCBECCgedfjghdccb```^aceeedehggfefhikmmkknopqqppqnopqpqooooppqsqsqpnnqtv}|€z{||{{tvtqpomjjhjklrrxsuqoomlkkkkinjHKUvoeckœ£¨Šzwx—®›‰jRJEDBBACBBKYqzxjilvaQIDAA<<DNg„xq‰§ÜÖÕݽµ®«zJ7:Ix§ÒðôñØÅðúûüùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùóôöûÿÿÿÿÿýúôîòöøúöòøþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿó³‹sZx®msÚܨ€¦ßôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷çÔ®‹yie_]ZXVUUVWWWYdYWUPOOONNMKJJIJJIHD@>;:86420.--,+*))('''''()&&&&&%&''(*+)'&+;OZgQ7&"!"""!!#*05:C<6=X]aholijlB/=aSJZpO/=`[.%BgV:CWb`^S?:ahJA:3355/'!.N^_c_^\_\\ZZ[\[``````_]][[\^\]^_acaabdde^a_`\^]]\\\\[[[ZZZYYZZZ[ZZZ\]]ZYYYWWVVUUTRJC?=<::9999;;::::::<::;::;:::<<>=@=@??@>>>?>>>==<<=>>@??@@?@@A@?@A@?AABBBC@BACBBBCCDCCCFCDCCBADDBBBACABCDgeeeeedddbc`a`bcfigeffgggghjkllmmnoonqooolrnopooonoprsrnsppopqsxyyyyzzzxwwutsqqnollllooppsrqpnnommlllkmqiGIQgsmi— ¸°£Šxz‰ž¶¦nRKFDCCECBIUcpnmnpvfVKGEB?>=CKd|kds¬ÕÓÜ»ÎÈÔŠPA3>Kq¡Ðíñéæñùûüùõööúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷翵ÔöÿÿÿÿÿÿÿþýüûøéÑÕðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Ð fWŠisϨi]]¡êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúòå¼}mgba`^\\ZYWUYXXTTONLJIHHJFCA@=;:964210/-,+*))(('''''''''&&&&&&&'&)-4;==3+:Q`sjd:'()-1-('':NQTX\FK]jkkjdfikG@RXIDT^L;DVP(#OgH7Oma_QG69]jP4.*40+# 2T^_`_^\^]\[\`\[_]]]^^^^][[\\\]]___`aaa`d^`__]]]]_][[Z[[[ZYYZ\[ZZXZZ\ZZXXXYWXXWVWXVUPKG@?=;999;;::;<;:<::;;;;:;;;<<;==>=?B>>>>>>>==<=>>>>>?>??A?@@@@@@@BAABBA@AABBBCCCDBBBBBBBBBBCCBBABCBABAhfedccddecddcdgihijgghihhhklopqnonsmkmnnnnsooonoopqsvssrqqqrxz{xxywx{{{wxtsrqppoomrooosrqqrnlmmmkmpqqpsqhFGL^trk„’œ¶¡›Š‰–°Å©‘pWNGEDDCDGQWansvvwh\QJHGCA>=@GR\dou¬Æ½¨ÈÂÛ•z`G7?Mx²Òêêèò÷úýùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûì²|Ž·òÿÿÿþýþÿÿþþýóÕÁ¾äíõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûõÛЃYy†lu£„^Wb{Ç÷÷öôñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøëœ‰vqnjfb][ZWRTZTQNKFCBBA?><;975430.--,**)(''''''''&&&''''&&&&&'''',<LWbSA>F^rtuc=./;HJI9,3O^mWdNFWnaZdr\SgjHCVe@7YhE7LhB#*Yd@CbebM?3(3Xb]:,,4-(7X^_b`^_b^___^][^]^_c_a_]\\\\\a_c^^_ccbcd``_`^^]]_`\^]^\\Z[Y[\_\]]][\YYZ^XYW[Y[Z^^^`c[TKEA?;;::::;;<=;<;<;;<<;<<><<<==@=??>>>>@>A=>=?>@??>?@AAA@@@@@BBBABAAAA@@BAACCCCCBDBBAABCBBBCBDABBBBCCfdfbccccddddffhiklkhhhggiilnpqpnnmmjlnpspoqpqrqppqruurrrttuvxz{yyyxwytutspppooonolnmmmnlnnnkkknmnpqrrrrveDEHWny‡~„…}š •™§´¾®•w\OGEEDEGNSZlyvsqkbUPLJIFC@>BAKU\er~•ÃÈÊÀÚ¾”cF=GPu¥Àêðóöúþúö÷÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÌ†om¥îþÿÿýûýÿÿÿÿÿîϹÉÒÙìùýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøß©•œy]¶x]ƒš„¬éêÒÄÖñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùì˲ŸŽ…{smdYVVNMLKIGB=;9985320//.-,+*))('''''''''''('&&'')'())(''&''3O^nlnUFWoruv_?3BUZk\R<8VodQG>CabY\_`PPeeFNfa;D__>A\d:65a`=BeeN:;,.1LhcB/-/+#;[]`a__`b_^^_[[Z\\]^^^__^^^^^]^^_]]]]]]]__`]]]]]^]]\]\\\[Z[[[[\[\[]\[XYZZXXXYYZZ^_ceee^[SKF>=;;:;<<<;<<;;;<>;:;<>==?=<=>>>>=>>>====<===>>>?A??@@@@@@@?@@CBAAAA@BA@BFCABBBBBCAABBBABBCABABCCBeefedegeddiejjjjllkihhgjlnsopnmmlllmpprqqqtvvwxwusssuuzzzvxxx{z~}}xxssrronnqnpoppolkkmkmmlklmooprusvrrubDCGO_m~…Љuw†“¤¤ ž¦®ºÐ²y[OGFEEHKOWjuyqnnmaURQNKFB@><>EKU\]dƒ‘´½¬ÁņWKFHNl‰Àèð÷úüùõö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ¾tdjšîþþÿûøûÿÿÿÿÿõãìíä½Õëòöûÿþüþÿÿÿÿÿÿÿÿÿÿÿÿÿüúàͯwe¶rmŦx“ÛšŒ“ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùïÜ̾µ–„}r^PJFDCA?=:5210.-,*)((((('''&&&&'&&&('&&''('&'''(+./-*(&'):Zrtwqeehnyuw_@8[Sdhr]A>`o^C13BeiQHbfLP_^JR`]JLbW=@bb6*AjV:GhgE=::ICHhi?2.1' #B]acc_`aabb`_]]]`]_]]^_`aacab```aa`^_``\]^`]_^_^_]]]a]\\]]]\^]]\^^]][[[Z[ZYYZ[]^adiilhhedZSID@><;<=<;<<<<<<<<<<<>>>??===@>>=@>>========>??@??@@@@@@@@@AABBBACA@ABBBBBBAABBBAABDCCABCCABBBCCBgggghihgfdffijjkklkjkklpqspnolllnnopqrrrssvxuvwytsssvxyvzwz{{z{yyxvutponnmnmmmmkkjnmmllkllllopsuttutsrswaGFGNUc„•Šqss‰µ©£¥¨¬²¼À°£yXNIGFHJOVepspprtucWUTNJFDA>><>BLY\ez¶´Õ½Ý¼·¢hKHGišÍíøúûøõööúþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòªhcj“îÿþþùôúÿÿÿÿÿüøûýðÔÊÎÛæöÿüùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöâ¢w¢»u{ÕËvÔžqM„çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøóíåЬ¡£†cMB>:7542/-+)))(&&%&&&&&&'(&%&&'&&&&'&%%%&'''*,6=AD;1+'(+>dqvvvmhkmqtu^?;IOXbieDDdpT4,,FhfHPg_JWmT@Sm_TYeD0Eh\3.TZLLIdiVS?BNPVgQ7-*'"'K_abc_aaa``______]]]^_acabaaa`aaaa_]][\\]]_]__^^^]]]^^]]\[[[\]]\^^^^\[[Z[[\]\]`behiijhhggb]YQHD@>;;;<<<<<;;;;;<;========?>>==?><<<<==<=>????????@@@@@AACCCAAAA@@BAAABABAAABAABBBBBBDCBBBBBBBikohjjllkiiinkklpppprsrrrponopqotsstux{vxvxwuttsstvwxz}v{{{{{z{zywvuupnnpnnpqmnjjjmnonommnprvsxttstssrty`IIFMRZjy¢ƒ{oœ§Ã°¨«°·½²Ÿr[RJIIINS`lvqqu‡„pb^YRMJGFB>;9:>JS[_}‘±´³µ¯Ë²˜cFBQÀàöøú÷õö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþñ gahíþýüøôøüþÿÿÿþüýÿøïÛ¾ÉÒàðððøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüóÞäßÒyyЪo€Ö^F}ç÷õóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùöðçä¡eI:520.,*('%%&&%%$$$$$%%&&&'%&&''&&&&&%%%%'').;IUb_[B/((-Bfquxvxvsqpqr[>9AORirdGHgnJ/',MicGRiZ[^jNOSinomQ6,NXX.2Oa?.@dgiN@CeijWA-$""-S`ebcabbbab`ecb__^^^a_cbbbbceeeba_^^\\]^`^``a`b^^^]]^_^^`[`]]]]]^_`]]]][_]^^`bgijknmoomkqjkd`WNGB?>>=<<<=>=<><=>===<<=?@>>A===>;<<<<=>>>????>@A?A@?@AAAAA@@BAAA@BBAACBCBDCCAABBAEBBCCBAAABBCjmojkjkjkllkkkklooosrrnnmnnnooqqrqstw{wvtsttssssuxvwy{yvxyyxxuuuvuuqpmnnpooromlhjkmnnnnortuvurtssssqrrtxeRLGLPW`m€‹ƒq{›§Ç»²ª®µÅŠzaTKJILQ\ivvwwŒlc_TNLJIHC@<8:;G]_cyµ½¼«šÁÞ±vL<GZŠÝñöù÷õö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿýï¦hagŽíýüùöóöùüÿÿÿÿÿÿÿÿýòÚÌ÷¶¸äóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöñî݆r„‘m…×ÄcJ{ÛëááñýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóéÉ¢§˜eB1,)(&&%$$####$%##$$$%&&&&&&&(''&&%%%$$$%&*.>O\knseT6))/Fhqstttth\lqrX=3DR^oqbHMjlG,)/Tk`JXlVM]iD1FfglS<,&=R:&*H<,(1H\K;52JbSH0!3Wbcbbaabbbc`bbb`aa_^_^aabdcccddab`_`^]_``______]^[]]_\\\\\``^]]]__^]]Z[[_\^_chijjkkklmlkkkjigg[TOEB?>===<=<<<<<<=>=>>>??>=<<==><<;<=<=>????>?@A>??@BAAAAA@@A@?AA@?AAABBABBA@BDAAABBBBBBAABBBlmomklnnooopoorooopnmljmonqqvqrsvrxwwurrqrsttszwwxwxzz{vvw|wvwwvystpoopponoommonoooqstwxyvvtttttussssru{j\RIMPU\brŽŠ™~x£ÍÃ͝«·»¶²‰fVMJJOXfy}{“®²vg_VOMKKIFC@;97<L_\`|“§®µ¾ßÚŠT=;F]¤äòú÷óõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿüöß³k_fíýûøõôö÷ûÿÿÿÿÿÿÿÿÿúõä̼¦¿ãñúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûúìšjq\T•Ö®cgƒ©¥“ãúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøð彟™–Z;,&$$#""""#""#####%$%%'&%%%%%%%%%%$%%$$%%',<TbpkvuxZ<+*0Iksmipur`ZirpS<>T\fks]HQmkC*&2\d]Sg]NMaO516IYN>,!0+$!!$% $/1.(&798. #7\adcbaebbbccbbfbccccddfffcdccceada^_abbaca`_a`a]_]`__``_b`___`a^_]]^]\\\^^ccgjnkpmqorrssqqqmjhddaTJDA?==<<;;<<===>==?????????=>==<<<==>?AAABAA@???@ABA@ACAAAC@BA@@@A@@AABBAABBAABBEBBBCAAAABmmmnonpqppppqrpokikkllnponpprrrrrqrrssrrsstttt{zzxy|yxvtvvwvwxtsqooonmnmnoonnnnooprtvxwwxvvuuvuttssstsv}l_XQPPTY^cr„‰ƒ€‹œ¶ÂÄÆ¸®¯²´ÂÇ“hVKKLUbirz}º±‚m`WOMKKKHEC@=;;;HWWYn¡·áÌáç£oE<<PuÛíûöñôöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿùßÄ‘l_f•ìýú÷ööõõúÿÿÿÿÿÿÿÿÿÿÿùéÞÌÈÛîôøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò¶jR@]Ù˜dª®~QsÌöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷îÔ±™‹xM4&$#""!!!!!""#####$$$%&&%'%%%%%%%%$$$$$%&'0L^sb^itu]<+*1LllfjnmqZQhroPFJ[ffiqXMTljA('3UjSINU@=<;)'/10( " %=\_aca`a`abbbbbdcbbbabcdcdcccccb`aa_^aaa`dca_^\]]^^`b_^^^_^_``__]^]]\\\\]^_bdilmkmlmmoopppppolljgdbWOJEA>=<;;<<<==>==>?>>===>>=>==<<<==??A@@@B>?>??AAA@@AB?@@@@@@@A@CAAAABBA@BDBBBABABAC@ACBBmmnnoqstvwzuxrplfhjkomrqqpsqtqqpppoqqtyuuvzyxx|{wxxvtrsxxytrqponoooomnmmoooqooqwwxy{x~xxxwuzzyyxuutxuyymb`[TLRX\`bkw…‘‰ˆ•±µÆÄ¸°®®³áØÐ“fVLKPZbkrz¦¶ŒtbWNMMNLJHFCB><9<FMJLu·ÁÄâéÊ[>9AeÈìû÷òõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿöÅkc`kîüúø÷öõõùüþÿÿÿÿÿÿÿÿÿüùê×ÔÉ×åïòöùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿøåžVYr¾ÝŒe¥ÞÂtHdÔöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþú÷į̀’‡vF/$##!!!!!!!!"######$$%'%$$$%%%%%%%%%$%$$&'5Tk`OQ`tuU<**2OnudVbsiOQgsmLFWnj^lnRESnc?('/OIB@?6*('"'C\_a```bcccecechdcbfceeeeddfcdcccc`_`caaadba^_\^_bcca`_e^`___b``_c`_^_`aa`bfeponkmllmnoqptpqonormkgf^[OFA=<;<==========>??=<<=>>===><==B?@?A@C@>>??AAB@AABAA@A@??AA@AABBABBDCBBCCBBDBBBCAABCCnoopruvvwwutpllklkklmmosppqqqqqopqstvwwwwwyyyz{{zvuusppopponnnnnnmmlmmnmoqqpqprsx|zzzxxxxxwuyzywwuututyunaa^[TTUZb]YfyŽž”™ž«ÂÂÌ¿³¬«ÃßÖÊgXKNV[\esƒ”Ÿ˜zbVNMLMKKJHFDB@=<9<?BFgåëÞ§vT:B\Äëû÷óöøûýþÿÿþÿÿÿÿÿÿÿÿÿþó·h\Zap¨îüúùøöõõ÷øüÿÿÿÿÿÿÿÿÿÿÿü÷ìÒ¾Éßåìòöùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿøà¢…âß‚iÏìÜxJiÙ÷üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúöêÄ¢‘ƒk?-$##"""!!!"$""""###$$$$$$$$%$'%%%&%'%%$$%'2XQNECdseM6)*2PpteV[]`PSltkHGejkkmfLOPSZ5&&)/3.)'$"!)H]`````bababaabdddbccdeeeedcba^d__```baaab`a\]]`abbb``_a__^_^_^___`__`abaddefojjjmklooonnnnpoonomljgb_ZRIE?><;:<<===<===??>=<=>>=======>>@??@@@???>??@@@?@A@?????AAABACBBBCCBBBBBBBCBBBBAABCApquvxv}xytpnmnplnmqpponoppqrpqqrvy{zy{xxz{|{{zytrqonnnnnpnnnnoolmlmmnpstuw|zzzzyy{x}xxxzyyyywwvzttuyum`caeaYSY`\UWk”Ÿ˜“‘–ª¼ËÕ»«ª³ÉâѧŒn[OR[WZnyŒªž‚bUOOOOMLKJIIFFA?96869DcŽÆãÍÁ”pOD\¿ëûöñóôùýþÿþþÿÿÿÿÿÿÿÿÿÿôgWXap»ãóòðòõö÷÷øûþÿÿÿÿÿÿÿÿÿÿýüöìÓÀ¾ÄÕÙë÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöííîíÞ{oâðãviwéöûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö꿟’€a;*$$#""""!!""""""#####$#$$$##$%%%%%%%$%%$$%.AO?9JfffD0(*3RpteKObQHWpuiDF[rqnb`C6;B<,$""##! #,,,M]_aab`ebcbfcbcdegefdjfedeedaa^d_`adaca`ac``\_`hbeeebc`b_``a_aaa```a_ccefkkkinjmknlkmmmonmmqppppnollfgbaVMFB><<=<>=><<==>>>@@?><===>=?>@@@?A@@@@?A?>?A@@?@@A???CBCABABBCCCDDBCCDDECDBBBCACCDAqsuvvtsrqpokmpokmmqpponmprrsssuvwzyyyyzyy|yxwwvtroonmllmmlmmmllllklmoprsvvwx}zz}{xyvxxywxxyvwxwvuttttuuup`cc{–rV[a\WVXl„™°’–ªÇÈ˸¯±³Â׿țv`ORTWer‡œ|cVOOLMNLLMMKIHFB?;753>Jp¤¨©¤¡ƒk[aÆëúôçáë÷ýýþþÿÿÿÿÿÿÿÿÿÿÿõÀmXX^k”ÐʾËÙðö÷÷÷úýþÿÿÿÿÿÿÿÿÿÿÿÿüïÕ·ÃÂÅãôûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõâvußîÑle¥èõøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö黓Y7)%$#"#"#!!""""""#####$##$$##$$%&$$$$$$%$$$)024ATjkP7-'*3SpsfH?AAE^ouY>@OV_VOC3.,+*'""""!! (2-$3R]^aaaabbbageeeddcagdffecdca__^``abdaa____]]]_aaa``bbc__^a_`^aa`^`aa`cdgjjkkjkijjkkllmkljmlmkllmmollgfca_XSMEB?><>=======>>>=>=====><?>?=>?@AA>@?????@?@@@@@@?>@@@@AAAABDCDECCCDDDCBABBBBCCCAxuyrppopsoolnnnoqpqqsqqqrsxxyvzzyyzyyyzyzzyxwsqpollmllompmnmlkkmmnqqrtwy|z~~}{{~{}yxx|wzy~vuuuvsuwtssuuqade•‘’d^^`VPPZt¡§•—«¾ÆÈ»±®°·¾ÉÉ£}`OPTZgžŒ{dVNNLLLMMMMNLLJHD@;66:DWx‰ŒŽ‘•pkmÍëùóãÒÈïüýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Öy[Z\^dvyxªçóøøùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿúõðåɶÑíøÿÿÿþýþÿÿÿÿÿÿÿÿÿÿÿÿÿöã€{àé¥cÙççäåñûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõè·”R5($$#""""!"""""""$#$#$#"##$$#&$%%$$$%$%%$$$%'+6I_nW@2)&)4SqrhF858C]qbP:28>>:5/*&%%$$"!!!! !!#-44*"<X]_aacbbafdieggfcebgeffgcea`]^^bbcccaa__]]^abbadbbaddd`__`aa`aadafaabffjjmnmkmjljonnmnkmkollmmknmnlliihgdd_^RJDA?>=<==>==@=====>>>=B>@?@>>>>?A?@??>B?@@A@@??@A??@A@AAAABCDDGEDDEDDCBABCCBCDFAtqplkknpnkmlmlnopqqqrrrrrtttttuvwwxyxwwztpqrrpokllnpmlmlmkkjkkmoootvwy|~}|~}}|}~~||}{{ywuutrrowvwwvrtpuurbcd~ ›rgb[WOLUf~šš™•“—¨¿ÇÒ¿³³²ºÐÒß©x_PQS_px}{eXNMMLLMPNNMMLMKIGC@9;>Oeyƒ„zjotÂëùòÙ‡ èüýÿÿÿÿÿÿÿÿÿÿÿÿÿùêr_YPRWWWi‰áñøùúûüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÛÍÊêöÿÿÿýúýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷í–§áåb˜àÝ©€—Èóûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôç¶N2'$#""""""$"$#%"""""######$$$$$%&'((('%%$$&%&1?VoZH<0,'*6VptkF2//=ZTS>/-+,-+)(&%%$$$#""!! !!##&,' '.54."%B\]_aabaaaedhedbc```bdfec`a^]]]^bccdcaa^_\^_``aaccba``a^ada___a`aafbbdffjjkklkkijijihgjjkkkjjhhimklmkjjgggfca[UMIDA>><=====<==>><<<<====>>>>>>??@>>>???@A??=?AA@@AAAAAABBCCDDEDEDCBABBBCCCCCDBonmnnnmnmnpppqqpqtwr|vutqsssrrsuwvwwwustqpppponknmronmmmmlllmnooptz|~{€}~~~|~~‚~~{{{{yvolsqsxwuuvsssuuqccdr¦Çœ›x]SLKQ_q†›—°œ’’˜«ÂÊж®®²¿ÓÜÚ¢zcPPVcot{gZOJJKLMNNNNMNOOOPRQF<=H]xƒ‰uaTf¶èøïÎxtÉøûþÿÿÿÿÿÿÿÿÿÿÿÿüñÄ™wWNLOQTe§çõøúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïïñô÷ôñððøÿÿÿÿÿÿÿÿÿÿÿÿÿûöîØëâ„c°Ý¯l_]—áóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþøò嵚zK1&$##""#"""""""""##""######%$$$&*.461*&%$$$&+8Oh_MC:72)*5YnwdE0++6;<9.*(((''&&%$%%$$#"!!!!! !!'&5*##%0;8600#,376/%&H\baddd`aaedidbad^]]aekeccc`b]_`cdddca`^^^c`fbdddbbaaaddddcaa`a`cceejjjhkkmmokmjnklkigkillljiihhmklmjjnnmmmhihf[SLFAA>>=>==<?>;<<<>?>==>B==>>>>>@?@>???AAABAA@C@@ABBBBABDCECCCCDDBBBBBDCEEECBBmnmkmmoprtssrqrrsvtruvvtrsqortsrrrssqopqomnmmnnlllnmmmmonmnnopstvw{}z}}}~~}}~}~}ywvvroqlquuvwxvwvuustvzfcdq«¹«¨‡bVOIO\mx†¢« –—™«¿½º³µ´¯¼ØÓЫƒfPTZblmj]RMJKMLONNNNNOPU`xl]L?ERf|…“~_SU˜ãóé»xq½ñùýþÿÿÿÿÿÿÿÿÿÿÿþúðÌ \NORX_ÜòøùúüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûøöòäÒźãóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûøóà|fÅÉ\P_l¥ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ïÚ²˜‰uH/&%##""#$""""!!""##""""####$$%&-5CMKJ0'%$$$(0Gee[PFJU8+*2Pka[<.*)+.-,*(''('''&&%%%$$##"#"!! (5*%$ %/11000C9A3/%%/1441&(L\bacba`abcbcca`ZVZ]ceddbaa__^`bbbcba``^^`aaccc`bacbbbcdb`aaa``accefgfhhkimlnijjjjjjigkijiihfdigllllkjkkjiigfed`\XPHE@?>>==<<==<<=>>>>>??==>>>>>>=@>@??@@@@@A@@@@ACDCBBBBCCCCCBBBBBCCBBCCBCCBBlnnoquz|{wuvuttsussruuvrqsrprrqqqrronnnmmmomllnnmmonmmnoppqtwyyyzx‚~~ƒ~}‚€~~xvtpnkrzyyxzwwvwxxvzvvwkddr¯ÉÈÎh]QIMXkq{ˆ «²¥š–°Á·´º»®©¸ÈÐ݆eS[[cjj`TOLJKLNNNNNOOQ[i°…cODJVk…„cT^ƒÅåʦ„t¥êöýþþÿÿÿÿÿÿÿÿÿÿÿþúê bY[eršÇéûúùúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüûôÛϨ»ãòûýÿÿÿÿÿÿÿÿÿÿÿÿÿþüöá‚kÉÓwFM†hêüþÿÿÿÿÿÿÿÿÿÿÿÿÿþýöíÕ®˜„pE.%%$#####""#"!!""#"""""####$$&+6KcdfJ6(%%%$*<YjpcY_iY<+)/HJKA3+)(((((''('''&&&&%%$$$###""!! "!75/,**,6<=;23::93''19762' ,O\abfbabgbdbbbbXRTY`defcbaa``abbcbd`]^__`chggcbaebddcbdcaaabc`abeehgihjjjjlkniniiijjlllijjjhfehillnnmlmlkhggffieea]UMGB@?>====>>==?>?>====>>B>>>=>@>@?>???@AAAA@@BDDCCCBBCECDCBBDBBBAABCCCGCBBqrw|}~}zyvuurspussquqppprpnooqlnnnlkkljlmlmllmnmmonmmnppquxz|zxxy||~~~}~€€}{xtqpmqqu|{{{yyyz|{zxxwwxzjcev²»¶½¶rbXPPTcpw|ާ¬¾©˜›¡«´±ÙƵ§¤±ÊËÊ¥{op]^ckcWOLJJMPONORPPTb~Ÿ¨¦€cOCKUhvi]a}œ¢œ“މ”åôýþþþÿÿÿÿÿÿÿÿÿÿþýð©xk°Ìéóúÿüúúùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿ÷åÐÆÆÝñöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøè’•ÑÈgINUUtçùüÿÿÿÿÿÿÿÿÿÿÿÿþýüõëÏ—„jB,&%$$##$""""""""""""#""$%&(''+0Ga]\RG2'%%%&0F`xrlmmeZ7*(*494/,(((((('''''('((&&%%$#$$$$#""!! ")=J:5426???9//72.%*39754("2Uc`aaabccaba`_VNV\^dd``a`_``bbbaba`^^^_`bcdcccbadbcabbbbbabbcbbceghgihhiijijiiiiijjiihiihhhhhhhiijiijjjhgfffgggffecaYUNEC?>=====>=>>>>=>=>>>>>>=>?>=?>??@A@?@AAABDDDDCDFCCCCCCBABAAAAABDCCCDCB}|„„…}|zyxwttswqtttuuqnnoqonoormmlllllnkkkklpllllmpmnoqtxz}~~}|}~~}~}|}€€ƒƒ‚{{okmpt|~}‚€|zy|z}hceyªË·Â¼}ld[RR_lsw~œ°¾£œœ£ÆÑ³¥Ÿœ¬»ÃË™‡ƒc]^gkUMLJJPQRPOPSSYu˜¼Ü¶—y]JBHRcma\cx|‡‚……áòýþþÿÿÿÿÿþÿÿÿÿÿÿýóÂŒ«åîðõúüÿüúòéêêïôúÿÿÿÿÿÿÿÿÿþüþÿÿÿÿÿÿÿû÷åËÅÆÞï÷ÿþüüüþÿÿÿÿÿÿÿÿÿûõÑÀÁ»g?FQ]qåóóò÷ýþÿÿÿÿÿÿÿÿÿýüôê̬”…`?*%%####$"""$"!!$#"####$'+0561.7Zgi[JD.'%%%'6Toxzn{ieG1(''())(''&'''*((('()(''%%%%'%$%$%#"""#!!!!! #*4NKACECCDD98)%" -5:775*%:[```bbedcad^ZSNS`acdc_aaa^`bfdeed_a^`adffdgddcddededdb`bedeefeeeggjhjiijkiihhiiimjjiihkhggjjjhhijjlihjjhggihlimlmklgfc^QGBA>====@>>>>==>>>??>>>>?>>>A@@@C@@??@AADDDDECDEEBCDDCAABADAAADCCCCCCB~ƒ€||xxvurssspstsqrqmlounooprmmnlnmlmmljjkplmnnlmmrtvx{|}~}}}}||}~}yvsqks{}~zzyz{|€€ƒ„ƒ€€€~~~~ia^m¥º¸»ÂˆqhbYU^kptw{‹£¥¦™—¦Á¿²¨¨ ™ž¦²«·ÂZ\eaRMKJIKQTUSPQSf‡³ÛÙÅ©pSGBEJNSWcu|†x…’‡‰äðýþÿÿÿÿþþþÿÿÿÿÿÿþùëâèóùúüýþÿüù繟¯ÙêôÿÿÿÿÿÿÿÿÿýúüÿÿÿÿÿÿÿÿÿúóçÓÑèôÿüøùùüÿÿÿÿÿÿÿÿÿÿþ÷òì×l\wxz§âæØáè÷ýÿÿÿÿÿÿÿÿÿþüóèȪ“Y;(%$$####"""""""#"#$##$'1;HULE;=Zsl]TF8)&%%):`mwrn`VE5,&%%&('&'''''(((((((((''&%%%%&%%$$##"!"!"$')&" #%)+5HJEG<763/+&"/67788-!*F]_``ccba``\WTPW^`dbbb^^]_^acdbba^]__abddfcgddceccbdecbccdadcccdefgkhjijkjiihijjjjhihgfffgfgfighhjjlijjjihghijhlikkkihgdaUKGA?=<<?>>===>>>=?A?@?>>?????@>??@>?@AAABCCCCCBBABABCCCAABAAABBCECCCB€ƒ~||}wxxxxwtutytsnmmmloomorqsnommnppppqmnmpppppqsvy|}‚‚}~~}~}zmhsqw†}ƒywut{z‡‚ƒ‚‰…‡‚…†‡†‚l`[bй¼½Ã•™xc][`onqt|Œ£¡Ÿ™™œ«½¯ªªš‘¸²ÎÇ r[dcRNKJIGQY\]UPTp¡ÏÕÕ¿·˜†eK?>@@GOcq|†ˆ‘ˆÞîýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõøúúùúûýÿü÷àŽ†…—ªËî÷ÿÿÿÿÿÿþûùûýþÿÿÿÿþÿÿýúôïðñøÿöíóùüÿÿÿÿÿÿÿÿÿÿÿýúôà}g¡ÚáæèÕœ°æöÿÿÿÿÿÿÿÿÿýüòæÆ©•}T7'%%%$##""%###"%&%####%/=OchlS?<]hsplYE.'%&*>[ne[PF=0+'%$$%&&&(''''''((((('&&&'&&%%%&%%%$#""!"%-=2.,+$$&(*,3FFG=3/+'"#19?;;:6(1P^``aca``e[TORXaaccbab^__bbcchca^\]ccddfegehghdecedddcbgcdeedhcjfhhjjkilkkjjkkkkjjhjihhhhhghiihkjjillmjjjkimmmllkonpnrmmhgZPGB@>=?>>>>>??>>???@?>@@A@@??>>?A?DBCAABCBFDBBBBBAAA@@@@CAAABBBCCCBB}|{}wxxxwvvsrrqpmmmmlnnnopqolllnoppqnnmnnpqrrwyz{~‚}~€~~~}}||}}}|yxqknhpvw~|{vrwqw|ƒ‚‚ƒ‚………‚†‡ˆ…‡ƒ‚uj]_dn{¡Ä”’‚m`]_gnpsuy†š®¤¢œš¢¦§Íº§™’—žÂݾ‡qifRNKOSUXami`UUwÇÒÐË»½¡}ZCDHEAN_nЇ}‡‘‰Ììþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüú÷ùúüÿüöË€}Ž›Ãîÿÿÿÿÿþüúøùúýÿÿÿþþþÿÿÿþýûùüýíàìøüÿÿÿÿÿÿÿÿÿÿÿÿÿùè’m»Ùð÷íºb`s³ïÿÿÿÿÿÿÿÿþýüòåĪ™€Q5'&&$$$##"##$%)-/0($##,:Odcqk`=8K^bqnoL2'%%)6VMI@72,)&&%%%%%&%&'&&'('''((''(&&&&%&%%&&*/*%$##$+3:9;@0&-=1***-1/-' #/:>:;>8'7W^`aba__[WPRQYa`aa``_a^``aac__^]\]^cddcfdgcededcbdfffdcgddeededfdfgiihgigghjjkjjihgiefefegfghiihfjijikjjkjikklllmmmnllljhgbZRMGC@?==<<=?>>???>?>>???>@A?>??A>@@BAABBBDDDCBCBAA@@???@@@AAAACCBBB‚}~|||}~xwrppqpporpoprnoopomkknqoqprnmnpqswz|†„‚ƒ„‚ƒ‚~„}|{{{{{zxtmgnw{~z{vqmjqvy€ˆƒƒ€‚†„„ƒˆˆ‰‰Š„„|ye][`t—“‘•»‘zpcc`bmlnsy‚£ª¢–•š¤¾Ê¯¢˜“—·ÏÝ™‹‰‰[OMS`klmt|k[Vy¼ÝÕËÄæ“‚iVURKJP^n|‡ˆˆ‘“Ž»ëýþþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿþýúøùúüþùñË’††Žœ«Ö÷ûÿÿÿýûúùùúüÿÿÿýüüüþÿÿþýüýúáÉÖéñ÷ûÿÿÿÿÿÿÿÿÿÿÿüñ·wŠºÑïè¤aQn’ìúöò÷ýþÿþýüûñäĬ—‚N4&&%$$%#$###%,5DM<,$#%0Ide^inQ96>HOitnT2($%(.584,)(&$%&%'%%%%%'&&&''''''''(('&&&&'%%&).10+(%#(0:AEKC6'/88($##"!".7==<=</>X]ada___TMNVZbafa`_baa`ecdce^\]]]cchefcfffffggcdddeeeidgejhlefelddehhggkjojnklllgggiiifgfhgjikjjfkjkinkkkliolmlklmloopmjjlig^ZRHC@>>=>??>>>>>>>>??>>>B@?>A@B@C?BBAAABBBCBAABAB@B???@@@ABAABBBDB}|||}}~||wrqppoqppoqqqsqnopplkknooooopopqsv{ƒƒ‚‚‚€€€€€}~}{{|zytoqorzy|wusnols{|‚‚€€†‚ƒƒˆ‡‰‡ˆ„…~{ne[\[|¨†~´˜—–rbadhkmqtx† ¡¢Ÿ›–’¸··¯¥“¡¯¯§±ÁœgTNXpv~uxz|fYr°ÊàÑËĪ—•wZcfTX]py|xˆ“‘’²éüýþþþþÿÿÿÿþÿÿÿÿÿÿÿÿþýûùùùûýöíʤ‰‰—žÅî÷ÿÿÿüùúúùùüÿÿÿüúúùüÿÿÿÿÿýúÞ̸ÀÑë÷ÿÿÿÿÿÿÿÿÿÿÿÿúä¥n‡¢àܬifyŽÜòéâíøýÿþýûùðãÄ«–€K2&%%%%%#$$$%+6JbWI/%%'8\deeiXD86=BQ`jmJ0'$%%),+)('&%%%%%%%%%%&&%&&'&'(()('())())'&&&*-:F81.*+,9AEPFC0&+8-%!'3;;==9+BZ^c`_^]SJPU[_`a`^^]]^badbba_\\^^^abccfbfdccddddddeeecccedigfeeddcdcighgjiihhiihgeeeffhggffgihkjjgihjjmkjjkijjkkkkjjiiklkkjigda`VMGA@>>?>>>@>>>>??@>>?@?>>>????@??@@AAAACAAAB@??????AA@@ABAAAABA€}|€{xusqsqqruqrrrssrpnnopnrsspoppqtwyzƒ„„„‚†„‰€€ƒ€€ƒƒ‰}~}}|yworz|}zxwtsnqv|€~„‚ƒ‚…ƒ‚†„†…ˆˆ‰‰Œ„†~xrcZY`ily¯£¡–tjjdakmptx„Œ¶¨œ“›£¬¾·°•ŸªÆÅÀ†_V\q€|yz|~pfl¦¿ÏÒÔÙ³‘‚ms‹„h[[lkvxˆ”•“èüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿþþüùúúüýõìÕ²š“¤ºººÈìöÿÿÿýúúúúúüþþÿýúùùüÿÿÿÿÿþýöèÆªÂæöÿýûýÿüúýÿÿÿÿÿõ⯎²Ê½ºn†˜çëÅ£ÉëöþþýûùðãÁ«”|G1'&%%%%%&*-+/@bgjF1%%)?dpliYJ>:AYWYemW=,&%%$%&&&&&&%%%%%%$&%&&(&&&%&&((((((*+/3.)''*.9IC=>@-+1KJIFE6)$#$$!!-:=?>7-I^da^^^RKNZ]caf``^^]_`bbgbc_^[\_aafcdcedgdccedeeefjfededghihfggbacghiikilihghhifgeeglfigjjkjoijiijkjonnmmlnkmkojkijjkijlmlkijkkgh[QHB?>>>>@???????@@A@B>====?>A@A??@CCD@CCDBB@??@>??AAA@AAAADBBB~}}|}{vurrrrrtttqrrrprqqqnnooooppqqrtx{~€‚‚‚‚‚€€€€€~€€€|~~|{wwwwwvvvwvvvvx{||€‚€€‚…„„…††ˆˆŒ…„€}voe[\\cs¥«¥¥™„„¡magnqrvxŽ¢·©›‘Œ’𮯵©£™™›ÉÝÆ®ˆeelooqru€vjk›´Îý¾»¥„qt–‹o`gpuv‰”–“©èüýýýþþþÿÿÿÿÿÿÿÿÿÿÿþÿÿüúûûüýóéÛÅ ž±¶»ÂÊêõÿÿÿýúúúûûüüþÿýúùøûÿÿÿÿÿÿÿýøèǺçôþúöûÿúôúÿÿÿÿÿþøëãß˦º~m¬Ãç覓¥Åïüýþüúð㿨’vF2))(%$')/5649HfugC/%&+AjptePF?ADRllnXE4'%%%$%&%%%%&%%$%&$$%&%%%&&&&''''(((-57;93+'-4<JJDA?7/*667660+%!"! '7=??51N`^^\ZRKQV[]]^]\]^^]_abbbb_\[Z]_``cbccdcgccbcaeceffgfbdehhikfbbbddefijjjlhgfffheffgjhfihihiikijjkkkjlkmgiinkmijhkjjjiijmnjiiiihhgc\TMEB>???????>>>???>>=>@>===??@@????@@ABBAAA@@?=>>??A@BAA@AAAC~~}yxyxvtw||{zyyyqrtuuutwpmmposqqqqvx{€†‚ƒ‚‚ƒ„„ƒ†……‚„€‚€‚}~wvvvuy{~}}{{}|†ƒ…~ƒ†ƒ†„Ї‰†‰‰Ž„ƒ‚ƒ}xuf\W[l˜¢º°º¢™œ}hcdqputvƒ”©µ¡•Œƒ ®»¼¼©œ›ÐÏÏɽ“|refcelz‡okƒ¥ŸžÑ²©‡zwŒ•‰nabqr‹”—“¥æûüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûüýýþóéåç§¡¸âɲÑçóÿÿÿýûûüüüüüþÿýûùøúüþÿÿÿÿÿþýöïñóöùöôôôðíóùüÿÿÿÿþùõïÓ¨³uj•Þìé¶ˆŠ›æùûþüúðâ¾§’oH40/'#%,4:EVB;Kjvg@-$$+CjxuaRGIOOQZbSC5*%$%%%%%%%%&$$%$$$$$%%%%%%&&&)+)''),18<CE7-,3?JKKHF:1*(.5.*(&%!!" !5>A;35T^_]XPLP[]^^]\[[]]]^ccccb_\[\]a__`dbbbebhcccffedefggfchfkhhhffghhgjimmmmmhffeehfihhgffhhljlinmqmqkkjmkmikjnklillljiihijnokmllllllhkb\OFA@@??@@A@@@@@B??>B@@?>>??C@???@@ABBBAA@@@>?@@??AABBBAABAA{zzxyzzy{||yxxwuursutttrqlmlnopnsuy€~€‚€ƒ€€ƒƒ†ƒ‚€€}~~~{zwvvvuy|}~~||{}|‚„€€ƒ„ƒ„………††ˆ…†ƒ‚‚‚€~zth\[^x¥°¼¼½¾‘kbbjsttvx†™ž¦ š†‡—¨¾ÂĨ™ §µÞÍ̧€g[]_afproox…’§œ—“‰Œy}Šš€l\fj}••“¡åúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûýÿþþóèé쨟Áéäá×ßñÿÿÿþüüýýüüüýÿþüúøùùüÿÿÿÿÿÿÿþýüû÷óòñíÖ¸Ýíóùÿÿÿÿÿÿÿöå’xir…ãïëÍ¥~Ñöùýüúðã¼£‘qL=50)'(7JLPPPDLjvf?-$&+AjutfY\cF8<C?;2)'$$&%%%%%&%%%%$$$$$$$$$%%%&&,31.+).06<CNG;0.;JJOKH;1+&&&&%%%$#! " """ +=?62:V\[[WNRV[^]Z^[[[\\^_a`^]]ZZZ\^a````ab```bbceedddeedbcbgggggggfhhhhjjjjjihgfdedededefgghfhhhijklkjjjjjjjhigijlhiijjhghijnljjihffggffg`]SIFBBA@@@@??@@A@>>???A>?>??@???BAA@@AA@@AB@?@@??@@AAAABBB@|zzzz{}~}|€zyvuuvvvv{uuonllmnruw}€‚€ƒƒƒ„‚‚‡‡‡„ƒ€€}}~‚„~{yyxwuvxz€…„€‚ƒ…†‡‚„„‰„„„„…†‡ˆ„†‚…†‡„„„„~zf\Za{Œ©Ê²¡ª…ghderqww{…Ž¡¹šˆˆˆ‹˜²ÑƲ¢—”™·Õ×Öšq`X[[]gxiddi¤ˆnryuxƒ•¤•x]]`s’Ô÷ûþþÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþýþÿÿþòæèꙌÂîíìâÛîûýÿþþþþþþýýþÿÿþûùùùüÿÿÿÿÿÿÿÿþþýùöòîìηÏìöúÿÿÿÿÿÿÿúé¨cNFwàæèÏЗçúûýûúï亡ŽsN@;90+2CSYSeL@Ikvf@-&'/=[utrfhM:0,--)&$$$$&%$%&&%%&&%$$$$$#$$$$%%&*1;<50259=AILT>3/JIJ@830+&$$$##$##"! ! #(,.1/+%%;?21?Y\\ZWXZ[^\\Y^[^]accbe`^\[Z\[\`cceabaa`caddeefddeecbccdggfgighhllljlkljjhgfeehegefceglhieighhijokkimmmiiiihijlhlinighmjjkmhiijdd_\_aggdbVLGCA@@CA@@?@A@>?A???>>>???A>?@@@?@A@@@BBA@@@@@@@D@@ADCEA}|}~~~~{|zzxvuxzvusrplllqrux{€€€€‚€€€‚‚‚‚‚~}|}}~€|xwuvuyy{}}|~€€€}€ƒƒ„……ƒ„„ƒƒ„ƒ~€€„…‡…‡‰ˆƒ}si^__w¥¯±«²´œpfcaipvyy|‡™›œ—³–‚‹³È¾²¢’—©Áƹ—ogkaYi}ubchv”«e_fkiex‘›¥†dZQk”›Ïôúÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñäæãˆ‰Åóõöêßë÷ûÿÿÿÿÿÿÿþþÿÿÿÿýúúùüÿÿÿÿÿÿÿÿÿÿÿüùòêëिèöûÿÿÿÿÿÿÿýöÓ€EPpÚØÕÏ˺ÈðþýýûùïḞ‹jNBA>766Ee\RMHAEgvhC3+37?PashhN9.&'(&$$$$$%&%$%&&$%$$$$$$%$$%%%%'(,1<KB>@BFJJIGEB?//4952.+)&%$$%$#%!!!! "',/26===6.$ 90)2G[]][ZY[\[ZZY^[]]`aa`_]\[[[]]_aabb`b```a`dcddddddbacceffggfihijjikikmljiggffegcfegefhhfgfffhhiihgggiihhhffegjihhhighihhhgfedca\^[_behgeccYQLECAB?@A@@@@@@@?>>>>>>?>?>?>@B?@@@@@@@@?@@@@@@@@@@BBBA€…†‡ƒ†€~|}{zw|xurqonprv{~€‚ƒ„ˆƒ‚‚‚ƒ‡ƒƒ€€ƒƒƒ|wss}{{ƒ}{zxwutuy||}}~€‚€‚ƒ„ƒƒ„‡„…‚‚‚†‚‚|{~}}€„‡‹‹‘‘‘‹Š|j`\b–œµÈ¤vlkdfuswy~ˆ““–§¢‹ŽµÐÀ¸œˆ¤Æ»Ê”uogju{vdfs«zb^]\\]m‰™ž tYO^…ž™™¾ìõþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýðâæß•vÈóòñæÎâóùþÿÿÿÿýûôíîðõúúúúúüþÿÿÿÿÿÿÿÿÿÿþüøôòéÀ´Îéôüùöûÿÿÿþýä±€cœáÙÆÌßëïöþýýûùîÜ´›ˆdMDEI>99GZaOI?:BcmiM:;DDCEMRWG6*"$(&$$$$%%%%%%%%%$%$$$$$#$$%&'(*,/4?NHEGHJRIFFG?>3,+-,*(&&%%$$$$$#$!!!! +6;=@@@IDD9.!))%4O^`a\[Z][ZZ[Z^^aagac_^\]]]`baeaaababafbcbecbdccidbdfdihhgijiimkjimijknhhhighfhdfglfiggfhhlhjilhffjikhigkghffijjihihjfggfefd_]]^ceghnmlljiiaYOIDB@@@A@@@A@AAA?@>>>C?@???BA?@A@A@BAC?@@@@A@A@@@BACAƒ„„„…‚‚}}|||wtuvspqqsuz~€‚ƒƒ…ƒƒ‚‚ƒ‚‚|}}~}‚~|yuvmt{zyyyyxy{xwy|~}}}~€‚ƒ‚‚‚€‚ƒƒƒ‚€€€}~€~{€…‰ŠŒŽ‘Š…€vlaacs¢º¸·Ÿ–‚sjdjpvyz}„†’¬«—ŒŠž²Æ¾«ŒŠˆš¹»¿soonoi`o~„Š‚zpib\[Zhz£ƒ`DXw˜˜š§æñþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüîàæäµÌôðìâÉÚïöýþÿÿÿû÷èÙÉÞêõøûûûüýþÿÿÿÿÿÿÿÿÿÿÿÿþùñѺ²Çê÷òíöÿÿÿÿÿùßÈÌÚìçÐÊßòûüþýýûøí×°›„cJCHCA;:CYSG@78=`sk[RLWgE7551,'!#%&$#$#$%%$%%%%&$$$%&%%&&),,,37:>BLJFHNJJHGB>83/)('('''&%%%$$$"""!!! 46===?ADFD?6.%"#$7V]`^\ZWWWYZZ[^_````_]^[\]`bbbe```a`aaccccdbcfdcdefhfdhfhhhkjijjjiiiihhggfgeddddfikefggfghkgkjlhgghghgidjggggggffggfecdcbb]Y]\afgghhnlkiklkjc\SIFBA@?????>@@@A?=>>???@>>??????@@@@@?@?@@@@@AA@AAA@‹ŒŒ„†‚‚ƒƒƒ{xwutrrsw{ƒƒ„ƒ‰‡†‡Š…‡„„‚„€€|}|||||zvsuuwzywxxx{zyz{zz|€‚~€‚…„ƒ‚„ˆ„††‡‚€…††~‚†ŒŒŽ‘‰‰€{kb[_~§¦»¹º©ž”|pffstxz|ˆ£Á˜‘†¸Õ¿¸œ‰‚•¯±¢vtpifab„‡…€~}wm`]Z_m‡˜¨‘hAIi“™Ÿ¡ãïþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúàÎ×Þµ¾ÞöðêáÆÖîöþÿÿÿÿöêÀ“”œ¿æìò÷ýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúæÈ»«áëßÙôÿÿÿÿÿüùôïóøïßæêóüýþýýúøíÓ°›‡bIA=;=;;?JOE=639\r{nfjmZJ/%! !#$&%$%##%%%%%%%&%%$%&'*.19869?BMMKMMMNKH?;5.-,*('(''&%&'%$%$$%"""!!!!! +8=A@AACDE@@:6+$ !$:X]^]ZXXXX[\``dcb`_^^^^]cbhdfcda````abddedibddfffhmjkihhmhhijiiijkkhggifdfgdcdffgikfiijiijkglllikijghfiijiihhgggggkeddeda\X[acjijkkkpmmkonppohcTJDB@????@???@@>????@????@???@?C@@@@@B@?@B@ABBAAAA@І‡„…~}{{yysvuxz}ƒ„…†††…ˆ‹‡†‡……ƒƒƒƒ~}}}||{zywuvyzzwvwxxy{|{|}||€}€ƒ€ƒƒ‚ƒ„‚‚€‚€~~‚‚…„„„†ˆˆ‰ŒŒŒŒŠ…wl`_av…¿¾¿Â”ngcktxyzz“•’¤…“¦·Â¹´˜|xˆ™yr}labd€ˆ‹Ž‡ƒ‚zjgigg|›¦xEIZ‹››×îþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôƼÁ¹´Æí÷ðèÚÇÒíöÿÿÿÿÿðÇ–ƒ{’®ÅÜôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøäÓËÐʼ×ñþÿÿÿÿÿÿÿÿÿÿöíêëôüýþýýú÷ìаœ‰aI=47=<;>DDD;556UptzceTJ8($!#&(%"%&&&%%$%%%%%%%%%$$).389;DMLJJIKPQSA8894/,*)''''''&%%%%%%%%###"!! !+48;<=ACE@??;9, $&=ZZ\\XXWXY[]abaa_]\]][]_abdddccaaba`bcebccdbddfdghhfghgfffghkiihihgfedeffhecddfgghkfhiihikhgijiihfgfeeefhhhfghedeec`_^]Za[_bfhijlnlkmlmlnnooolgbWMHBA@???>???????@??????????@@@@@?@@BAAAAAAB@@@@@AŠŠŠ„…€~{z|yyy{„…†‰ŽŒ‹ŒŠˆ‡‰‡‰ƒ„„ƒ€}}~}€xywvv{trqruwy~{{~}}}€€ƒƒ„‚†‚‚……„ƒ€€‚‚ƒ„†Š‰Š‹ŠŽŒ’“””–‘Šˆ€yja\avŠ Ä¼Ãµ°…omfiwuywx†‹‘¡š‹Š—¦¹³«„yroƒ‹spuvkfm€ƒ…Š‘†‚ˆ‡qqrspz‹ž£ˆTBO‚š¤žËíþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøìº¬±¬£¨ÐïîíàÏÞïöýþÿüøÜ¯‰{‰œŸ£³ÁÞöúÿÿÿÿÿÿÿÿÿÿÿþýýþþÿûøëÞÑ··Ýëôøýþÿÿÿÿÿÿÿúôôóøýþþýýú÷ëͯž‰`H;56==>>@B>:5/2OduiaN?6+'$%(/82+)((*.1*%$%&&&&&&%%'.:CFJFUKMIDGJD?93/-+)('&&&&&%%%$$$$$$$$$"#"" !"##"$"##*05::?BFBEDC95#"!')BYZ[[ZZZ\\^_caa_^\[\````bbfdjeeabaabfdecbbcbddfdkhgghhjfefhhlhfgjhfdbcffggfffdgghglhhikhhhhfjkkhffgfeefghghgkgdceca__\Z[_bghjmrmpoqpqnoororpronjh[OGB@???@??A@??>????????>>>@@A@B@@@DBBAAAB@@@@@BA……ƒ€€|z{|~€…ŽŽŽŒŒ‹Ž‹‰‰ˆˆŒˆ†…ƒƒ‚„€}{{yxwruvvvqnmmsxz{}~€~~~}}~€‚„€€€‚ƒƒ‚‚‚‚€ƒ„†‡‡‡‰Š‹‰Œ‹‘ŽŽ‹Š…wmc`^p‡£·®®·€kffmvxxvvx|‹´¢’“ ²ˆ|uoigqvx{ooy‚†ƒ‚€€…uxІ~†‰“¢‘ePFn™žžÈìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõç›Í·‡ˆ£ÏíòêâéðöûýÿøéËŸƒŒ¸Ïµ£«Èæöÿÿÿÿÿÿÿÿÿÿÿýúûüþÿÿÿüöé;ÀÊÛòúýÿÿÿÿÿÿÿýüûûýþþþþýùöëÈ®Ÿ‡^F;77<?C>?<974--A^VQD60(((.39A><73326>:/)&(*-001,&'*9OMMCBBACF9433/,*''&&%%%%%%$$$$#######"!! !####%$####""!"#(,-.4<;:993.)"##',HYXYZZZ[[\_````]\\]]^]_```baaba`babcdccccccbdcdeghgggggeefhhgedcbafbcdfgfffeedgfgfffijjggefffggeeeefeefggghffccbba\X`\^`dhijkmomoopoooonpmroppomjg]TLDA>????@@@@@@??@@?=?>>??>?>?@A@AABAA@A?@@@@@@‡‚€€€€~|‚†‘’““‘‘Œˆ‹‡††‡„ƒ‚‚„~|{wuvuswsrmknorx{~~~ƒ‚…~„€‚„‚€€€€‚‡…ˆ„ƒƒƒƒŠŠŠŠ‡ˆŠ‹‹‹Ž‘ŽŽŽ•ŠŠ‚}nbZ^u…›¢³¾§°ymojjurtssu}™°ª—ŒŒ‘ °‰‰†smos{{qoxƒ†…‚}ƒ}€¡‘—’‘šœyWGa—ª ÆìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÓ|€§ÂŠk}©ÓìêéìðñóùÿòÞ»œš¶ÞÓͯŸ´ÛëúýÿÿÿÿÿÿÿÿÿüùùúüÿÿÿýüöðÚ½ÆÄëóôôúÿÿþÿÿþýýýþÿÿþþýùõêÆŸˆ]F=88;C@><8860*(09<8.*%$&+8?NLKHC@<=?BM7.-/7@A@?2'(2EJKA9<@620,*))'&$%$$$%$%$$$$##%######! !!""%$$$$$$$$$$###"!"%&'*/220,(&""#$'0MXXZ__``ecc`aa`]`\]^a^gbdcc`ba``cafdedgccccdhcfdffgggggcdfmhfcb`^`ebdfkhifgfifgfggggjjogmegffghdegjefefgjglfea``a]XZ_`fgijoknnrmooqpuopooprprtwrqmmcYMEA@????@A@?@??A@??@?@?A????@CAAACABBB@@??@@@~€€€~€€ƒ†‹‘““‘“‘ŒŒˆ†‚ƒƒ‚‚ƒ~~}{|vwysqonriotuwy|}~~}~~€€€ƒ€€€€‚€~€€‚ƒƒ„…„„„…†‡ŠŠŒˆˆŒŠŠ‹‹ŒŠŒŒŽŽŽŽ‹‡‚|na_^p’£¹¾¯«„w‹vgkuvsrsy…˜¥«ž‹Œ‰°—~}€„ˆ…‡xhksƒƒtuy€~ƒ’›••“”–iC[’¬ŸÎìýþÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþí©ioŒ˜fW·åëïïïãæõÿìIJ±±¯Èȱ¶Ãª™±åõúÿÿÿÿÿÿÿÿþú÷÷øûÿÿÿÿÿÿþõÛÊÉÝê×Ýôÿÿþÿÿÿÿÿÿÿÿÿþþýøó鯬Ÿˆ^F>8559>:82.+)$ %)))'$#!'.8ELTQQMIHHECB==<?KPUST4)/9FE=41.,*(''&&&%%$%$$$$$###"""#"""""!!! !""$##$$$$%&%%$$$$$%%$$$%$%%&'&%%$$#$$)5OWYZ``aaa`a``^^\\]_`a^^^____`baacafcdddcccddccddgfhhheb_effgfa]X_bbcfghggfggggffhgijigohgehbcdgcehiegfggjgfbaa^\_[]^acgjjkkkllllomnnnnpopqqqstvrqnnic[RHD@@>??AA@>????>>???>?>??@@AAAAAAAAA@?>>@?A€…‚‚ƒ†‰—‘‘‘•”˜‘‘‘‹‹Œ‡„‚€€ƒ}|||z}}|tqkhoqu{y~z{|€~€}~}}~€€€‚‚‚€€€ƒ„…ƒƒƒ„…Š……†ˆ‹‹Œˆˆ‰‹‹ŒŒŒŒ‘ŒŒ‘Ž‘‘‘„|lbZ^}¢¤Â¾ÄŠt‹‰ogiuqrsqw~¢³½‰†…†”±¶™‘’‘‰…jbn~„„jkq…’£™›˜”“¡™†VV„¯ ×îýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýê\Yrc^j¦ÔçïêææÜðÿí·Êá˵¿Î¨§ÆÇª›Äèóÿÿÿÿÿÿþþýú÷÷÷ûþÿÿÿÿÿÿúöçÞæéÌÉíöøûýÿÿÿÿÿÿÿÿþþýøóèÆ«ž†^G?910036/+&" !$##$%*29AKOUWVUTKMEBDEGKM]PME635@G90,(&&%%$$$$%##$$$$##"""!!!"!!!!!! !!"$#%%%%%%%%&&$%&%%$%%$$$%%##""""##$$%%$+:QZ\\aceefcd`_``]_`cbb`a^^_a``bcccbfffeeefffccccehhjjjdaaggghi_Z\`deejiihhghhjgfehimjigohffhbbbfdegiffgihjfe`^][\_adceglkoopkjkqpppppqpoprqwtttwsuqsonhfUJB@?B?ABA?>?@@A>A??@@??@CABAAABAAB@@?>>>?@€…†ˆŒŽ‘‘ŽŠŠ‰‰†ƒ~‚€~}}~€|{{zxurokrjqzz{z{{z{||{{{|yyz|}}}~}€ƒ…„ƒ‚‚‰…†††‡Š‡ˆ‡ˆ†‹…††††ˆ‰‹‹‹‹‘‘ŽŽˆ€xnaabn~™ÃމŒˆqhflpturrv}“¹¹²˜†ƒ²¯«£˜˜›žž‡qihrunadi„ŠŽ’˜¡˜Œ’Ÿ‰hpw”¡æðýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ãwRT\oxsoŠ ÊãîåÄœ¹ëÿîÞæêÛ±´¼¨¼áÜ»«ªÈìÿÿÿÿÿþýýüùö÷÷úýþÿÿÿÿÿÿÿûöòì¸Èâêò÷ûÿÿÿÿÿÿÿÿþþþøóèŪ…]H?90'(*('$$)**+,379<BIRbYZSMLEHLLLMLGA=:1-7L9.*'&%%$$#$&$$##$$$###!! !!"###%%#%%$$%'%%%%$$%&%$$%%&%%%$$$$$$$$&$%%$-AU\\]beffeba```__`````__^^_a``bbbcbcdeeeafffbbcdehhijhdccgihif\`dcddegfjggfihjggdgimkigojgfdbbbgfedihggigfcc[YW\^abcdgjjklkkhjillljmmmlnrqrrstsrqrprrnkhaVJFAC?@@@@AA@@A>@@@@@?@@@@A?@@BAAA@@?>>??@‡ŠŽ‘‘“‘‘•ŽŽŽ‹‹‹‰‡„‚‚ƒ€~}~|}xwsnmemsy~„~||y{{}||{{xwy{}€~€‚‚‚‚€„†…†ƒ‚ƒ…‚‰…†‡‡†‰ŠŠ‡‰†…‡„„†Š‰‹Š“•–’–Žˆ„€pf_cv—•œy”ªŽwpllqprqotƒ Á³¤–†„£Çº½›œ¬¼Ÿ‹wknunc^cj|††“”—ž‹‹”¢}gnšžáòýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúï¯dQN[h…”¡¿ÅËäéÔ¾“¢èýòâèë˪ªÄ¥ÆÖäÓÄ¿Þ÷ûÿÿÿþüüüù÷÷÷ùüýÿÿÿÿÿÿÿýûøôéÕÍÅßòùÿÿÿÿÿÿÿÿÿþþøóèé~[G=7.&!!"$" +2877:>;78;AHOTVQMVFVGEED@<5/,)+=8-($$%%$$%$$$$$$$$###"! !""#$$$$$%%%%%%%%%%%$%$$$%%$%%%&%%$%$$#$$$$&$$&$/GY^baffgedbbcfcdccceaa`___`bbabhbcccdgfdbefgggeienikiggkkliigcahijfhfhfjgfghijkljjjmmmlnkhfddcdgfefkihimgcccXVY^bgghfmlqlmklijkoookmnrmmorrrrspoqrsrqqnojhYLEBA@@@@@AB@A@C@?@@@A@?@@?BAAAAAA@??A@?@”’’’’’’’‘’“’‰‰ˆ†„†ƒ††…ƒƒƒ|{zyxsomjnosxz|~€ƒ~}||y{{|}|{{yz{||}}}{{|€~€€‚‚‚€€‚‚†…ƒ‚ƒ‚…„……„„‰‰‰‡†„„……„††ˆ‰Šˆ‹‹““•“’‘ŽŠ…~tifauŠ«¹}¦§upplqrpnny†œµ³¶›ˆ‚‘¦¸¨™¾«¢Šqmroe[`kw~†w”Ž“‡ˆ‘ke|œäóþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøè`T]gˆµÌÅàÞàæ½‡åûöòðìÀ¡”—®¼ÕãçäÃÒðøÿÿÿýûûüú÷÷÷ùúýÿÿÿÿÿÿÿÿÿþüöäÈÅÏìöÿÿÿÿÿÿÿÿÿÿþøòèç›wT?:60(%"%'%!%6FEDFGEC;5<<CGSVNMIE@:751-+('&'(*,($$$$$$#$%#""""!"""""" !"#$$#%$$$&%%%$%$%%%%%%%%%%%&%$%$%%%%&%%%$$$#%&%$%%4N]`abgedccbbccddcdddccba``aaaabebcddbbbdbeedcddeefgkihhhhiiigedhiideefeffgjhkkiihjjllkjkiggfeedefgghihhgc_[ZW[^acfghflillkijijkmmljlmmmmnqqqnomopqqpoooonkgZOICB@ABABA@??@@??@@@A????@AA@@@@@???AA@™™¡˜˜“••˜’”‘‘‰†ƒ…‰ˆˆ†‰„ƒ|ywtokjgpz{ƒ„†€‚ƒ~||||}{{~}|~}|}|{{|}…„ƒ‚†€…‚………„„‚†„…„‡„Šˆ‰‡…„„…ˆˆ‰‡‰‹ˆŽ‹’“˜–™•—’‰…wnad|ž“}“Ѽ³³°kholnmpx†ž¸¶¯˜‚…Žš§›”Œ„}}rnptlY]mvzƒuy‰Ž––›ƒyspcm£æõþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôà~_cp“Á®Ä»²Äßßâ¬w}Äòóóðê¾—’}ƒ¡¼ÌáÊÉÈäòÿÿÿýûûûùøøøùùûýþÿÿÿÿÿÿÿÿþûøòìïòùÿÿÿÿÿÿÿÿÿÿþøòéÀ¥™oM>9870**-,+#.HINRYYZL@@?@CGOLGFB;5/+)'&$##$$$%$#"$$$$$###"""!!!!!! !!!"""$$##$$$$$%%%%%%%&$$%%%&%'$%%%&&%%%%%&%%%%%&&%&%%%&9Taccbgcbbbbdefefehffffcc`bbdbeciccdfaaadcfedccdeeihjijjlkkjmjjijhgehiieeejhgimigimlllqjkhggjedcffhigghhi`ZXZ\cdfeijkilhkklkllllollkonnmonqqunplrqsqqqqswsrmj^RIDBA@??@@@?CAB?@@AA??@??@BA@@@@??>?@@•–––—“””“ŽŽŒŒ‰ˆ††‡‰Š‰‡…€|wspnhotx}}~€€~€|}}z|}}}}||{~||z{{~~~~€~„…„…ƒƒƒ†………†…†‡‰‰‰ˆˆˆˆ‡‰‰‘’”–•––—–”ˆ‚wkfbtƒŒÏÑ®©¤Ÿnehqqnoq|е«±†|}‚‚„…r`r†sfs€t^]tlhlrs{‚ˆ’—‡}lge{¯ëöþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð»qkw”ƹ¯š–ªÖàã˜sYo¡æïôðêÀ¢–hxˆ• ¢¡¢¡½ìÿÿÿýúúúùøøøøøùúýÿÿÿÿÿÿÿÿÿÿþú÷÷÷ûÿÿÿÿÿÿÿÿÿÿÿøñä¾£“gG8;@<86310,'):KLRXXXXTA<?PHKB>>?90,''$$#####$%#"""#%#"##"""!!!! ! "#$$$#"%&$#$$%%%%$$%&%%%%%&&%&%%$%%%&&%%%%%%%%&%%&%%%%%&(?[bccba^aaaaeeeefchffcdccabccbcaccddeaa`aaa`abdeeeihkgjkkkkjkihghgfefffdefigghlghiiiijiihffffcedfghihfeb_ZZZ^adfffjmkgkhkhhhjklmnllknlmmmlnoonnlqptrrrtuvutpmh_TMDB?@?@@@@@@@@A?B???A@??AA@@@@????@?“•—–—–—‘ŽŽŒŠˆˆ‰ˆ‰‹Š‰‡†„~{yplkls}€†††~}}}€ƒ€€}||}~~€|}{z{~|{{}|„‡€}€€‚}€€‚ƒ‚„ƒ…„‡€‚„„…†Šˆ‹ˆŠ‰ˆŠ‡‡ˆŠŒ‘‘•”™™›œœœœ“‰‚sj_bjm„ʾ¬¸Îªvpfflgjns|‡— ¬ˆ|xxz€Švccl…tqwk`zkelgjsx‚†¦Œ€wtz‹®èòüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþî¨kuƒ««‘|zÍÎÛ˜jUVàêðíéѳŸ‚pr|}‰—®–Õúýÿýüúùùøøøøøúûýÿÿÿÿÿÿÿÿÿÿÿýûûûýÿÿÿÿÿÿÿÿÿþþ÷ðâ½¢aA:>@A994522*##5FPVYVTVYLA=?EF=56882+'$$#!###$###"!!"""""#"""" !! !""#"$#"#%#$%$$$%%%%$$$%%&&%%%&&&%%%%&%%%%%%%&&'&&&$%&%$%&%&+H]eeeb``ddgffefefchggffdddgdeeeddddedacaa_]_abgghfpjlimmljkjnilhkgghgfffffjhhhkhijkiijlihgeffdeefgljkhea\Z^`feffhinmlllikgkjijklsnpoopqmqnoonnnorptsvt{wwuxtsqpeWLEA@@B@AA@@A@A@B?@A@@??AAA@B??>?@@?“••–“ŽŽŽ‹‹Š‰ˆˆ‡†‡‰‡„ƒƒ~|{vuylsx~‚„†………€}}€~|{||~}~}}|{xyz|{|y}|}~}|}|~}‚€€€ƒ€€€€„„…†ˆ‡‡‡‡†ˆˆˆ‡Š‹Ž’’’“™™š››šœ–“†{sie_gz¥¿Ç½´qgcflmmpu}„’¨Œyvx}rhcn„slt‚ƒwu…}ofaiqu|€…Š‹ŠŒ‹¨ÐîùûýþþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþìždoŒ“†sliwŒ°Ç»¢yY[‡àæëéçåÑ´‘qq|mct‹|‹Çòúÿþýúøøùøøøøúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöîß¼¢Ž]A7<A=964340+#!+BTQaUPMJKD?9<>>72175.(&$$""#"""#""""!"!""""""!! !! !!"###$##$$$$#$&%$%&&&%%%%%%&&&&%%%&&%&&&$$%%%%%%%%%%&%%%%%%%%'.N_^_``abcdddddddfcdddcddeeeeecdeeeefeba_\[^`abgffgggjfliiikhjihhkfffgddceejdihihjjjjiihggjgfgeddghljiif^][addefgijmiiggggghfjjkjsnmkomnmonoonnprrqrsvuzwwvvuvurrdYPEC@A?A@AAA@@?A>A@???@@AA@@@?>?@@?““”’’’’””Žˆ…‚…ˆ‰ˆ‰ˆ†…‚}|zxqntx|‚‡††………„„…„ƒ~}}}}{{{|}€~~zywwy||}}}||~}}~~€€†‚‚„€€ƒƒ‡„„…ŠŒ†………†‡ˆŠŒŽ”••–™™›œœœ›œ˜™”ˆ†vn`ap‹ž¬··¹¤ˆvpffhfnrw|†”€ywwxwsnqtsqot~…~Ž—zi^gqux}„Œ”—’ »Úîñôùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþí™`kx~ypbZeo|¥Å¶¢Š`a‚ÕÖáæëêèÓ´Œrka^arwpo¤äôÿÿþûøøùùøùùúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõìÚº¢]A6:::97544/*#!%0XVRQMIDDF@=;;>?82361*%$$$##"""!""!!!!!!!!! !!!""#"$$$$$$&%&$$$$$$%&%%&'&&%%%&&&&%%%%%&'''&&$$%%%%%'%%%%%&%%%%%%%'2S_a___adhhhhieecfcedecdehgfefdgedeifeb`ZV[dcedgfjgjjjkljjjlhkkkijehghgfeigjjkjnmmjqkigfefhhfghgfmkmjjggaabhfggghkjmjihhhhihijkmnunnmpnpmsqrrqptturst{zzwywywtturqh`OFBA@@ABAAAB@AAA??>?@AA@??@????@?“Ž‘‘‘“”•ŽŠ‚ƒƒƒƒ‚€€{wurwnv|€„„……„………„ƒ‚€}}z}|z{{{zyyzxxvxz||{z{|}~~}~|}~€}€‚„‚„ƒ…………†‡ˆ……†ˆ‰Š‹ŒŽ‘“”••—–—•˜˜œ››™˜—•‰€vjeaqƒ˜º¹À’‡rjfegnptw‡„ywxuvytsturu~‹v}’™‡o]kwtr}„‘Œ‘Ÿ™’˜³ÈÉÍàõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíž[[edc]SXgcb¢Â±Ÿc`z¥µ¿ãîîìêÓ¦zf^X`ffdgzµîÿÿÿüøùùùùùùúúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýôêÖº¢—^@5;:;98774/)%"%37<>AA@=79><<<>@=885-'"#$$##"""""! !!"###%%%$$$&%%$$$$$$$$$$$$%%&%%%%%%%&%&%%%%%&&&&%$$%%%%%%%%%&&%%%$%%&&&&)6S____aceghfdgcfccadedddefgecdcddddffdaYQX``accgffghhhhhiiihfimjggeedeeefffeekjmjjjkgffeefdfeghgghhhhhffbdfggghhihikkjihilojjlmnqpnnnpnnntqrsrqtttstuxxxwwvzxvuusqle]SIDAABAAA@A@@@@@@@??@@A>?@?@A@@?“’“–”–“ƒ‡‚‚~~€}|{wtqtw|ˆŠŒŒŒ‡……†…†ƒƒ‚€}||}€}}z|{yyzyzz{{{}{zz{|}~}€~~~€€€‚ƒ……Š„„ƒ†‡††……‰†…ˆ’“””•’“•”˜•——˜˜™š›Ÿ ŸŸ—‘‹…vi_bs~š°¸Á©´{rgejituy~~|zxwttxxwvrpwˆ‹}t{•›umqolm~‰“‘“—–•ž¹ÀÊÎïüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿï¦YOSUSOOVfX[ŸÂ¯’e`p’ŒÅîëèæáe\]dgl_[l”éúüþûùùùùùùùúúûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóêØº£^@5::;;:::2.(("#&)+.9<;6127;==>B?=74*$""####! ! ! !""#$%&&%$%%%%%$%%%$$$$$$%##$%%%&'%$%&&'''&%%$%%%&&&%%%$$&%%%&%%%%%%%$$%%%%%%*:U^_adeffgfeegeebbceddehffeedhdedffgdd[SS_``bhdgeegjijgnmmhhhijihhehdddffhgiikkmjlkmgjegfhghiiihgjhjhihljiikioiikqhkkllllrnklnopprnpoporqustuvtxxysvuzy}x{xzx}xysqoogbTHB@@@AABB@A?@@@@?@C@B@@@??ABAA“ŽŽŒ‘——“’ŽŠ„‡€€}||}xxy~………ˆ‰ŠŠ‰‡‡‡‡…„ƒƒ€}}}}}|zyyyyyyxxzyzz|}~z{z{{~~~}~~~€‚€‚„†††„„„††…ƒƒ„Šˆˆ‰‘“”““••“•––•—™™™›œœ›œœ—’Š€tid_nƒ˜º³°›’vkddhquw|{{zyussvw}xqovƒŸmu—••pnkfazŽ”’Ž™˜š¡¨±¨›¸éùüþþÿÿÿÿÿÿÿÿÿÿÿÿÿð¾ZJIIIMQ]g\ZšÃª—d\_qr~«êèãÝÖµ‹h`^egaYVVxÚòùýûúúùùúùùúúúúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûóêÙ·¤Ÿ_@5:<:7662,)'$! .7<;4/1368::8754-&#""#"!!" ! !"###$%&&(&'&&%%%%%%%%$$$$$$$#$%%%%&&%%&&&&&&&%%%%%%%%%&%%%$%%%%%%%&%%%%%%%$%%%&,?Y^bdeggfgdgeeffadeeeeggdfeeddddeghhaZQRW`bbceeffggghjhjjigiijjighegacfffffijjjnjjjhdfeeehffddegfhggfggkjjiifmjkllhiilhlmqmmnmlnnnnponorquttuutxwwttuvvvv{wzyxwwtrppkfaVKFBBBAAA@@?@@@@@@@@A@@@??A@A@˜“’’’•›‘‘‰†…ˆ‡†‚‚~||~†‹Ž’ŒŽ‹ŠŠ‡ˆ‰‰„‚‚‚‚‚‚}{zzyxxxyyyyzyz|~‚{{{}}‚€€…ƒƒ€‚„†††…‰†…††††…„†ˆ‡ŒŒ’‘‘”•˜˜—”””•–•–šš››œ›¤ž Ÿœœ–”ˆƒuj_ar|–§«ÍÊË“tpeejirvz{yxtsstvy|soq{ƒgnˆ•’Ž|ofgw‰™Ž‹©˜›žœš™¤Îï÷þþþÿÿÿÿÿÿÿÿÿÿÿÿóÕcCABELUifb[–«¥“w^QVngd äêêâÖ´˜|fdea\VRSc¨èõûûûúúúúúúúûûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóéÖ´¤¢aB9<;9653.($!"08=74121221221.+(#!!!"!! "#$$%&'''&&&&&&&'&%%%%%%$$$$$$$$%%%&&%%%%&&&(&%%%%%%&&%%%%%%%%&%&%%%%%%%'&%%&&&%%%/E]bhhghighhhfffgagekegggeieecfghhigi_TPR\bafffeghjglijikjjiojmhffidgbchjffgiijknjjhffffgfhglgfglkkgfefglkljhillmlmililhqmqmpnnnsmporonossuvvtutyw|twvwvvu|y|yyxxsuuunomjZMEBAAA@@C?@@A@AAABB@A@>@BAA@š™”ŽŽ‹ˆ‰…†…†„„‚€„ŠŠŽ‹‰ˆˆ‡‡†††…‚ƒ‚‚€}zzzyxxxxyyyzz{|~~||}~~~~€€‚ƒƒ‚‚‚„†…„……†……ƒ…†‡…†ˆ‰ŠŒ’•••””‘““•––—™—š™š›žŸŸ —”Œˆwkf`m}ªÈ¶ÌŸ„vlddgqswxyxutsrtwwvshm}x_h‚Œ‹Š‡ztlvƒ†Š‘ŸŸ“’•˜•Š›¶ÖòþþþþÿÿÿÿþÿÿÿÿÿÿöçrI@BFOZm“t]‰«›ŒuVOPjgsçëðç»°¢’qj`[ROMY…ÞðúûüûúúúúûûûüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüóèÒ²£žcE;;9410/,&$ %2:62232/*%$$&(%$#"!"!!!! !""#$&&&&('('''(&&&%&&%%%%%$%$$%$%$&%%&&&$$$&'''&&&&%%%%&&&%%%%%%%%%%%%%%%&&''&%%%%%%&2L_ehkieiggffcffhbeegegeddecdddefgihg\NGU_bafddehjhgkiihhgiikijfecdbcbfiifghihjknijgfefegggggghiihhffefggehijmmmmklhjiiimmnlllnnmllmqooorssrrqtqttttuuwwwvxwwuvvtqstroqsmf[NHAA@@????@@BAAA@@?@@@@BBA@¤œšŽŠŠ‰‰‹‡ˆ‡†ƒƒ„ˆ‰Š‹ŒŽ‘‹‰‡ˆ‡ˆ‰†††„…ˆ‚„‚€€~{{z||}z~|{z{|~}€…€‚ƒ„€€ƒ‚‚…ƒ†…‡……„‡‰†……ŠŠŠ‹“’’•’š•–•––—“”•˜™™™šš ž¢Ÿ¦œœœ™–ˆzqaanxœÌ¸Ä¦Ž†|qfekkvwzwwtspquyvuicfnYax‘“œ„†wwy{ƒŽ›‘‹’‘‘†«ÇïüýþþþÿÿÿþþÿÿÿÿþùéœSDCJU\s•r©™†sMGLfks—Üãîß™©¦ˆxbZQMLPoÀëùûýûúúûûüüüüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòçβ¢šeG=;4/,-,*('#$3200133.% ""!!" !!!!! """"#$$$%(&&''''(&''''&&&&&&%&%%%&$$%&%$&&&&&&%'%'&&&%&&&&%&'(&'%&&%%%%%%%%$%%&'))(&%%%%%%'6UafgijfiggejhigiiigifgffggfgddefgihfRGLZ`bchfgehgggjijghiokpileeefeeeihhhmhihnnnijhgghfhgfhljkijjjhhenhgfgillmnnlmmmjlknnqpsomkklmnqqtqqrtrsquvvsxuvv{zyyzwyuwrrsstqquttrp^OEBAA?@?CA?AAAE@@@?ACAABB@—•‘ŒŠŠ†‡†„‚‚‚…†ˆŠŒŒ‹‹Š‰ˆˆˆˆˆˆ……†……„‚ƒ€€}}{{{|}{y}||}||}}€„€ƒ„‚‚€€„‚†„„ƒ„„‹†‰ˆ††‹‹‹ŒŒ’““‘‘’•‘“”•““””’“”š›œžž¥›Ÿ ›˜“Œ„ykc[j…¥¬³±»ÅvjbchruvwtrsoptvvtkcdjU\l‚’˜’ˆ‰†}kq{…‹‹‹Ž˜¤”†œ¼ìúüþþþÿÿÿþþþÿÿÿþýöØ€MJMS[|ÀŸu‚¨—lGFH\trŒ°ãèÔƒo¢È§•zdZROMN[šäöûþüùúûüýýüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòæÊ³£šeI>:0,(()+-+*%')((-/.-%!!! ! !!"""##$$$$$$%%&&&'''(((()(()))))(&%%%%%%$%%&&%&&'&%&%&%%%&&&(&&&&&('&'%&%%%$%%%%%%&),171.)&%%%%&'<]begggffeeefefegggeiffcfbffedfffgiiXEIW]`bdefgchfhhjiighijiiihedddafgghhhhhihhiiihhhhheggiiiiigggghgfnihghkkhmjjjjiijklmmnnnnmklmnoppqqqqqqrqrrstyuuttuwwwuusqnorqsqrsrtspj]PIBA@??@B@AAAAA@@@BBBB@AA˜ŒŒˆŠ„ƒƒ„ƒ‰Š‹’Ž‹Œ‘ŒŠŠ‹Š††…„†ƒ„„ƒƒ€~}}~}}{}|}~~}„ƒƒƒ„…†€~‚‚‚„‚†…‡‡‰ŠŠˆ‹‰‰‰ŒŒ’Ž“–’’’’‘‘‘•‘‘“˜“•”•’“•œ›œœžž ž¥¤¤¡¡ ¡—“Œ†rgZ\rŒ—³·¹Áº”xqeejiortqsqoruttndefTTaŒ›•‘Š–Ž…ojpv}ŠŒ—˜ š—xz“¦çøûÿþþþÿÿþþÿÿÿÿþýüå½rQMOZ‡»ÈŽ€‘–}dNDDVwq}ºÝ¼qf˜¨~dZYWRMSyÅñùþüûûüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûñåǶ§šdI=8/)&&(+030/&!&+./-&#""" "#"#$$$%&''&&%%&'''''''(((()(*+-/.+)'%$%&%%%%&&&&&&&&%%&&'(&&&&&&&'&%&%&'&&&%%$%%%%%&'-4<@E3,&'%%&')A^ceifhghejefeheifffifffgffeeejghij`SAL[c`edofgehhjikjlkkkkjpiiegdd`hhhgmhhhihjiiihhiihdiinjjjjfefijmfnijijjjgmiiilijkonmmmmnmmnnoppwqspppurrqqrwxyvvtutwwyuwqqlmoqqqqtrwttpo`QGB@??@@@ABBBAAAAAAAB@BAŽŒ‹ˆ‡†‰‚„ƒ„„‰‹ŒŒ‹‰‰‰ŒŒ‹Š‰‰ˆ‡†††††„ƒ‚‚~~~~|z~}~~~~|~„†ƒ‚‚€‚‚‚ƒ„„‚†…††‰††‡‡ˆ‰ŠŒŒ’Ž“’’’’‘‘‘’’“—’“’’“”•–˜š›œœžž ¢¡¡ž ˜”‡xoe_Zdr˜É½Ùº©’uibchosrqrrpqrssqiggVSVi}Œ•Š—‡pe^dm„Š’Ÿž“ƒry‰ŸÕ÷ûÿÿþþþÿÿÿÿÿÿÿÿþý÷ÐPGO^˜ØÏ˜ƒ‹‘~jVKBQwqjuŒ±¦og¢¯˜ƒzc[YZ[QKe¬éöýýýýüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúñ䯳“aH<7.'$'),024.+!%(+-/(%$#""""#$%%&(&'''''&&())('%''(((()*-05;:=3*(%%&%%%%&'&'''&(&%&(&&'&&'&&&&&&&&&%&&*,(&&&*-,-,)4@=<84*%%%%&'+F`ccfffffeeceefeedffgeffedfefegfhid[QCP^``cdeeedgghhjjlhjkigjifdcdc`degghfffihhggfffgfhehhjjjffeefiihgjiiijghghhiijjkkljmkllmlnpoppqvqpoopqppqsswwvtttttuutsplkkmmmmnoprrrssol^QJBA>@C?@@@@@AA@@AAA@@?“މ‡„‚†ŠŠŠ…‡Š‹Š‹’’’‘“ŽŠŠŒŠ‹ˆˆ†………ƒ„€€„~€€€†ƒ‚ƒ„…†…‡„…„„…††ŠŠ‰‡‰‡Œ’‘‘’’’’’’‘•“•—’““–“•–———•–—››¡›Ÿž¢¡¦¡¢¡¡™•Š|qg[[h€ ¹µµ¸½zrefhgmnopoopsvrkieXQP]n}ŠˆrbTWe|‚…‘¢ŒxitŠÙ÷ûÿþþþþÿÿÿÿÿÿÿþþþùèŠMEMmªÚÖ¨{}¥‰qeUHLhd^u§ˆp§¤•ƒub[Y`[XIV”àóüýþþýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúð䯱«‹^F;8+%#'+-131,)#!%()*('&%&%$%''''((&')'&&&&+12,)((())((*.4;?>IE:+(%%%%&'&&&&&'&&'''&&&&'&''(&%&%&&&&&&)-0,''*/6;81/=A742-(&&%%%'.K`ddfeddheefjjifeeffkghgfejgffhfhfeWNKV_ecffhiifggkillljjiifngfcbbcbedffijjfhhhhhggghfhgghljkfgeiiiiihmjmjihkhjilllkpkmmlkonqmoprrrrvqqnvrrrvu{w|vvstvwvwtuqplnkqmmmmmnoqssrqlpaSGCAA@???A??BA?@BAA?@@‰ˆ……„‡‰‹‹Šˆ‹Ž‘ŒŒ‰ŒŽŽŽ‘ŠŒŒŽ‡‹Œ‹‰ˆˆ‡ˆ†‚…ƒƒ€€€€€€€~~„…………ƒ†‚„†††…†‡‡‰‡‘‘‘‘‘–’’’””•––––”—˜™› ›œ››œœ››˜œš–‹‡ƒ{tjc\k|‘¯µ¤²œ‘ymcbfnqqrpoprvrnjf[RMRZn‚ƒ„‡€zweNOTclrz†ƒo^l‰—Þ÷ûÿþþþÿÿÿÿÿþþþþþÿûë‘LB[~¶ÚÔÉg|•~xfNM_a`WWov…Ÿ£’†vaWQTYPGOr½ïúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðãÆ¯ªƒYA9/&%%+//11/+&"$$((''(((&')))(((('''&'''',;3/+*-013/,05;C<;GM;,(%%%&&&)'''&''&''&&&&&&&''''%&&&),*().7@4)-06?@F60BL@74+'&&&&&)2Q`bbcdccdfeiiighfefffgfcfeghffhfeaWMOQZbddffghhegghglkjjiiifeefbb`bbabdfggjeedhfgfgeffghhfhhgefdighghhhiljjihfiiklljkjjjkjklmkqproqtqoonnpqrtttuvrwuvzxwvtpkkjlkllnnllmmnlnnmklj]RKCA?@A@@@@BB@@@@@?@?‡…ƒ†ˆ‹“Ž‘ŽŽ‘’“‘Ž‹‹‹ŽŒŒ‹‹ŒŠ‰Š‡ˆ‡††…ƒƒƒ‡‚†€‚‚ƒ…‚€€‚~€€‚ƒ…‚‰‡‡…‡ƒ†††††‡‡…‹‰‰ˆŽ‘–‘•’‘–•—–——›••—š—šŸŸ¡šœœ˜–—˜š‘‡…xn][fq•»¶Ñ±½ž…vefjgrqqooqutsngaTKKPcx…ƒŽzygIDIRZ_l}zg]fzŠËõùþþÿþþþÿþþþÿþþþÿúéKHeŽÃÛÝΩmi}‡ˆ„`OZt`LKYz ¯¸€w`PLKLLFH_§ëúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðâŬ¨zS=1*&&*20/01-*%#! !"#%&())))**))))(('(('''('''.79/--38??;23<IC98FM>+(&%&&&&'('''''&&*''&&&&&'&'&%%&'+13,,6HE=/28=?JI;1IKIB:+'&'&&&*6WagcddhdddeghgggffffgfeegedfffhedYOMS[ccdeiiiikghhjgmkjijiiihdfbeccbaacfiijffehgjgggijihmhjihgidifjjjikillojhhljnmmlniijmlllnnrprqqpoosnppsrvssuyxywyy|wuqljkjokklplllmmnlomkjlki`SHB@@@@@BAB@@@A@??A?††ˆ‹Ž“‘’ŽŽŽŽŽŽŠ‹‰Œ‹‰‹‹Šˆˆ††‡††…ƒƒ‚‚€‚‚‚€€€€€€€€€€‚„ƒ…††„‡‚‚‚‡„„„……ˆ‰‰‰ŒŽŽŽŽ‘””–”–—–“–˜˜™™™™™™••––’“”•—”’’’‘‘‹†€vh`XfŸ¼»¬¾Æ¦€mcbempoonorxxvmkYNMLXgs€†‡~tfI@?GNRVj~gWfmz´ðøþþÿþþþÿþýþÿþýþþùç‹LMm™ÎÝàÜ·|ns‚€xwf\h`LD_…¾°—‡zv^NDDDCADN‡åúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùïãé¦qM8,)'+/00//-*(%%%$"#&'''()*))*++,+***))'(''())((&/6:/238<<G?7:BH@97GK<)(&&&&''&''''''&&'''('&&('&&&%&'*5D:15=DE8.6?<9>E5,8FFO<,)&&&&%,;YbaacdedeeffhegjfdfeeeeedccdffhdYNQS\bbcefiiighhhhihjjihiiidgde`eea`cceghhhfeefgggggihiijhjdgjidighiigihmkmihhlklkkeeeghjjjknoonnklmnlmmpqrrsuuuvvvvxwxtpljhhikjkkkjlklmllmmlkllkh^RKBA@@AA@@???A@??@?‰Š–‘”“’’“‘‘“—“”ŽŽŽŒŒŠŒ‹‹ˆˆ‡†‡‡ˆ‡…„ƒ‚ƒƒ}}~~‚†€ƒ„…„…‡‡‡‚‚„‡‡ˆ„…†‡ˆ‰ˆ’‘ŽŽŽŽ‘’’‘’˜——•–•”–˜š ™ššš”–“““”“’’‘’•“˜“˜‡ƒugYZo„›¼¹ÙÂÈ™€vdfhmponnovzwrm]UUNOWas„…‡pcF<:@EHL]oifilqŸàòýþÿÿþþýüúûûüýýþ÷âMQs¢ÙßãàÚ£ro‡zz„||eNSkŸÍµ©€xu]LBAAB??EnÞ÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúïã¾§ hF3,*,2471//,*((+*+*()**,+*)**,041-+*)))'''')+,+**09925<C:;JM><FI=78GJ9)'&&''&&'('''''(''(('&&%&&&'&&&)0@FE49EM@60?A;7=7/,09EJ@,*&&&'%-@\addcdgfiggfifjhfggelghdfbcceei^ROW^jeeehfliihljkklkkkkllihhidddfc`bgflkkggefefggghhkiiimhkghijfihkjjhijnlmllillmikedcijmlqpoommmllmompnrrsrrtwwwy}{xzrljkhhilihhkjmkklomnnnmqoooocUICAA@A@@@B@A???@@Š‹‘Љˆ‰Š”‘“–”““‹‰ŽŒŽ’ŽŒ‹‹Œ‡ˆ†‡…†Š‡…††„…‚€€€€||z}}~|‚~{€€€€‚€‚‚„ƒ„…†‰ˆ„†‡‡ˆ‰‡ˆˆ‰‰ŽŽ‘Ž‘’’’“””‘—”•“–˜˜š›™˜——•”“•’’’“‘’“’‘’’–‘–‘І~tfa[dq“µÍù£–‚hacjppnlotz}um`ZXXNKT`o{s`SC;9BFIJWpnr}†~´ëýþÿÿþýüùö÷øúüýþõÇqKRt¨ÞáåäàÜ™ix’z’ŒymhÁ¸¨˜…zsi\J????<<>\ÀðúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúïṤ™a?3-./062/--+))(**))****+**)+.4;78.+*)**)'*+05692-7D;3:FA59IL@@GH;::GI7('&&''('&''''''(('''&&&&''((((',8BM>16IJ:74=C94413444CI8*(%&&'%0G\`a_bdeeiffdeeecdcccfefba_cbfeaYXW^ccdddefggjhkjkiiiihihhgfedbddebcdffkigdfffffgghhhhhjghhgfgggefghhhgjlmjjjigmkifdacdjiklnnplkjjhkmmnplqoooqswvxy{{{wslhfgfhihgfdggiijlljklnmnopppn`SLDB@@@??@????@@?‘Žƒ€ˆ‰Ž•”—“‘’’‹‰ŽŽ“‘“‘Ž‹Œˆ‰‡Š‡†††……„„ƒƒƒ…~~||}‚‚‚‚ƒ‚ƒ€€€‚„…Іˆ…‡‰‰‰‰ˆˆˆ‰‡Š‡ˆ‰“ŽŽŒ“—”••™”––——˜–œ—˜šœ••–—“‘‘”“˜’”’’“”‘“’–••“”‘‘‰…vnZXfz£×¼³«Ò pgbkponnnszz|oc^\\OEMWdqkWK?:;BHTQUbp}—’Ž‚œëûúúûüûúóíïòô÷ùüî¸_IRt¨àâæåäÜËŒq€ƒ{~˜–¦µ²²¢‘}rkdVH?>?><9:N›ç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùîÛ¶¢[>5--.0//-,+**)))))***++++*)-4>?@60,***,,+.29<B>:4AEC?AD@26IL@>IG77;IB4(''('''''''('''&''''&&&&'(,..,*/;KL:/4IH639GB51115>EA?=2)'&(&&%3N]`cbbcgfieideeeeeddcgfea``cdgfb\^`idedhdffjjkjljkiihmhiggggdccggfcdfjjlhgeihjfggiiihhhkgihgehgggfggilkkllijjifnkhcbbcfkkllonplljlkpornqpqopqttvw}zz{}vqifeeehfeeddeghhijmjkjnnnowqtnl`TICAB@A@@?@???@>“•Žƒ†ŠŽ’”•”““Ž’ŒŽ““ŽŒ‹Šˆˆ†ˆ‰ˆ‡‡††ƒ„€€€~|{z||~€€€|‚€€€€‚‚„……†‡ƒ†ˆŠ‹Š‹‰‰‰†‡†ˆ‰Š‹ŠŠŠŠ‹‹‘’••˜””•˜˜˜–™——““’’”“‘ŽŽŽ’”‘‘Ž””•“‘Š€s`[Ug|¤¶“°¶xi`kqkmnnot{}pf_aj\FIPZd\QG<=@DKVY\aqŽ™‡’àøöô÷úùõéÝàèïòööáLFPo¥áãçççãÒ¯‚ƒsqu‰¦“¹©¨—ƒqh`YPF@AEA=;9I|åóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøí×´ †U;2,,*,+++++*())*+)**++-++**08<=842/.-295/5=;;;=73CNB9DN@2;JC9<JC4497;0(''((&'(('&'&'('''''''')+28:>6,/:II8*4JH22>>B=6424C?>74-&&%&&&&7T^`cbbcgffeddda``abbaabbaaaceefc_aceddcedeeghihmjiiiilhieffedcehifdfgjjhggdeffeffgfgghhhggfgfihhghjjiigkkifhghgjid_bdfhkllkomljkkkjonnnnnmnpqtvvv|zzzupkgfgeabbbacefhghigmiihjjlmnoomkc\TLCB?@@@?@>@>>=š“‘Ž’’“’•••”•‘“‘—‘‘Ž’ŽŽŽ‡‡‡ˆˆ‹‡‰‡‡ƒ„~}||z|€€„€€‚€‚‚‚‚ƒ‡…‹ˆ‡‡‰ŠŠ‰‰‹ŒŒ‹‹‹‹‹‰ˆˆˆˆŠŒŠŽ‘–˜™™™—œ›ž—š™˜‘‘‘’‘—‘ŒŽ““”’’Ž‘‘‘’”•›šœ‘”„{lbXZhu‘µ·µº~j^jqmmnklqw|sjefqtJGMTZXNE??BKRX_dkv…ŠŒ‘ŒˆÀôóóòñòîÒ½¿ËèîïéµgGENj¤àäèèèæãЯ“•xdgo‘™ž“‰‚xg^WPJDCDEC>=;DhÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷ìÓ²žP70+*+****+*))))**)****++*++2?D?5455418AB5;DH>8744EOC:GOA:=ED7:A@0-131+(('''&&(''''&(((&&'((('+2==GIF20<GE5)5LF/1AMHCB@0.6;70*&%%%&&&%:Y^acbdcgeecbceaa_^`baccbbcdeflfecgefdgdfdiefgijmkjinnmgidfedcdhkgffhikhffiddegggfhfiiighgjijhjhllmlljigkiggkkklkeb`eghhknqmollknjqmoonmmnppsrwvxw{{ysqlhghgf_ccbbfffghhnmmjkillkmnmllkcd_UHCAB@??@?@>?>’‘Ž‘‘‘‘•‘“’’‘ŠŒŒŽŽŽ‹Ž‡ˆ†‰ŽŠˆ‹„ˆ…„‚~}€€‚ƒƒƒƒƒƒ„‚‚‚‚‚ƒƒ…†……†…‹‰ˆ‡Š†ˆ‡ˆ‹‹†…„„…ˆŠˆ‡ˆˆŠ‰ŠŠŽ‘“‘’•”—™—’‘‘ŽŽŽ‘‹ŽŽ‹‘Œ‘’“”””“†€wof_Wbov}¬½‹k]iqmmnkknuvvlimrrRJUWWTPKDACIQ[how‡ˆ‡ƒv|¬ïñóíçêæ¶£˜µßçäÆ[DDKh£ÞäêêêèæãÇ›…„l]^`jsttqngZVPJDB@DIE?=;AVŸìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöëÒ°ŸvL3.+*)*****)*,-.--,+*++,++--7HED<8;A81<LC39FFDA<:7GO@8DPF<>E9/194,++++)('''''&('''''''().211-)2=::@PH78?B?3)7ME//?FOAD6**++,*'&%%&%%$%>\_ccceacccaa``_\Z]_bbbbdefffeeedccccbfceddeggjjkkjjjjjhebfeeegjkggghihffdddccddfeeeeefffehiihhhkiliigfegghijigdb`ccgjiimpojniljjknlmklkmnprsqwtxxwurnmjhefcf`cfeefggdccgfhhiijilmmllmkbcg\RJBB@?>@>?>>>ŽŽ‘‘“–“•“˜““‘“ŒŒŽŽ‹ŽŠ‹‹ŽŒ‹ŒŒ‡ˆ„‚‚…€‚€€€‡„‹Š‰‡ˆƒ†ƒƒƒ…„Š‹Œˆˆˆ‰†‹‰ˆˆ‹ˆ‰‰Š‡‡‡††‡‰ŠŠŽ‹ŒŽ“’‘’••‘—•––—•”ŽŽŽŒ‘ŒŠ“‘’”’œ”˜’’ŒŒƒpeXVbov§¯¥o\fqmmkjjjnqpnowooWN[jVUSTIB@FO]jvˆ‰‡„~or¦íîïìêì㺔”«ÛàЧzUECIf£ÞæìíîëéåÛ£ut}dVVZ_cddc`UOIC@?@DHHA>;>L„èýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùõëÒ° qH0+)()))****+-1450-++,-.../1;LKB>>IF=4DKG48AMJNC=:JO>7CJO@C:2+*,,)'''''(''''''(''''((),07?A;2-8CD7:NM87CK<1)<IC2.6GID9/'%$$'&&%$#"!!!&Cacdidedfbc```a\Z\abfdcbfgjhjgidcdecdbfdfgkjjijjmmnkkhgedcfehgkllghgkhgfeegdcbcdfeeegghfeegjlhoklimmlffehfkklheb`bjimlnkonmmnkljonqnpnolpnrstqwuxvsqnnnjlhjfgcefhimihgeefgjhljmkkklkjkidddd^THC@??@>A>>>ŽŒŽŽŽ”’‘‘‘”’’ŒŽŽŽŒŒŒŒ‹‹‹‹‹ŒŒŒŒŠ†…ƒƒƒƒ‚‚‚‚…„†…‰††„…ƒ„„„„‡ˆ‰‰‡‡ˆ…‡ˆˆ‡ŠŠŠ‰Š‡‰††ˆˆˆ‰‰‹ŒŽ‹ŒŽŽ’’’’’‘“”“ŒŒŒŽŠŠˆŠŠŒŽŠŒ‘“””–‘ŽŒ‹ŽŒ”Œ†€xpe_X^hŽÃµt\crkmjihinnoqstjc\W]aXTROKB<BMWhny|||{wcm ëëêìííèѧ“¶ÙÔÆ‘lUDCG_¢áèîðñïìèÞ¬oehk[RSUWYYXVPMDA=>?BECB>;=DsäûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýýùõëЯ mE.+))))**+**,09D;31.147:9963<OJ>@?IL=5EOD489@EKJ=<GN:/5>;;61-())(''''''(''((''(()*/3468::JFF7/;IA0:KK68FJ:2)=QD6-+061-*'&%&&%%#"! !(Ldceecdccaa__\ZX\_bcddddhhifeedbbbecdcddfgiiihjjjkkkjedaccefghihhffddddddegeeddeffeffceddehjjhkjjjiihdddggkjifb]cfiijkmjlnkikkjhnlmlmlojmnqssqwtsqokigjjjiigfcefhhkjjhhghhhhijkjkkkkkkhcfhgf\RJA@??>?>>=ŽŽ‘‘—‘’‘Ž‹‹Œ‹ŠŠŠ‘ŒŽŠˆ‡†ˆ‰†ˆƒ„ƒ…†‡Š‰‰‰‰‰……„‰††‡ˆ‡ˆ‰‰Š‡‡ˆˆˆˆˆˆŠŠŒ‹ŠˆŠ††ˆŒ‹‹‹ŽŠŠ“‘“ŽŽŽŒ‹Š‹‡‡‰ŽŽ‘•™ž–—Ž‹‹“–’‰†ƒ€skYW^r—²n\_slmkiihhjmsqpg`a[_eYUVRI?:<CN[f|Ž„v€tl©ìîïëçêêÚ¹½ÆÉÈ©„mTD@DXŠÎÞìïòðîêß´ldfjaXQOOPQTUMKA?<<>@BCB?::?fÉùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùôêЯ fB-*)))**+*)*-2DHF5349<CDMD;4>OJ:9ALJ=6FOB777:BLE>=EB6,,/00.+)''''''&('&'((((&'()-17=EGH>@FPH<7EK<-9NK39JJ<2)@GG4-'')''&'''(#"! ".Tcbdebgccbc`a\X[adheffihliiffddbddeeheefiilkmjjkllmjjecbedhiojkjkfidcceccdgddddcbcddecfcdejjkgmlpjiifddejjljieabkjmjplmllkjimlijmkomnloloptstrwtsnlhgfklokmjieeglikkmknnnnnlllnmmkolmjheknrlg_QEA?>>?>>=Ž‹’‘ŒŽŒŒŒŒŒŒŠŠ‰‰‰ˆ‰Š‡‡‡‡‡‡ˆ†„„‚„„…„†‡‡†…‚„„„„†‡‰ŠŠŠŠŠŠŠ‡‚ˆ†ˆ‰‰ˆ‰ŠŒ‰‡ˆ…‡‡Š‹‹ŠŠŠˆŠ‹Œ“ŒŽŽŽŒŒŠŒŒŒŒŽ‹‹‰‰„‡†‘‘”•”’‘މŒŒŽŽŽŽ‹Šˆƒ{pd^Vcomh]^uonkklihikopog^`WYZYQLIF?9;?HQ`‡™—ŽŽ•š²îñóêáæêâ¿Î»³©¡ŒoSEBCSlÚêïóòðëâ¾vTXgdaUNLJIJIHC><:;;==?A=99:Z¼óûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùóêί¡`=,*)))+**)**.5FRF6:>?CDDLL>4>OK89CMG?9HOA7<ECA>=74471+***++*))('((''')''((,-/20.6?==AMG:>?NF=:HL<.<O@07LHB3(093.*&'''''&$#! $5Wbbccbbaccb``^``dgedfghhhhiefdcaeefehffgijjijkknhehifcddeehjojjhkeecccfbcccddb`\badccbbcdfjhggijjgihgeffjiiiebeijimkmllljhjjomjjmknmlllkopqoppsuojifggkllkliigghiiiikkmmnonmnnnmmlmmljhcjpqnli]PH@@?>>??•‘Ž”’‘‘’”Ž“ŽŽŒŽŽŒ‹ŠŠ‰‹‹Œ‰ˆ†„„……ŠŠ‰ˆ‡ˆ‡††‡ˆ‹†‡ˆŒŠŽŠ‹‰ˆ‰ˆˆˆ‰ŽŒŒŒ‹‰‰‡‹†‡ˆ‰ŠŠ‹ŠŒŒ‹ŒŒ’ŽŽŽ‘ŽŽŠŠ‰Ž’Ž’‹‹ˆˆ‡‡‰’’–––”””˜’‘ŽŽŽŒŠ‹‘“‡ˆ~ypj[Y_fl^]umolklihgjnnmh^XQUeYMHFC?;:=DGPsª£¦´ ÎóóóéßàâØ»¡ž¡¥¨wQICBNb€·ßèðððëäÇ€XOVg`ZUMIFFGB=;9:;:;;<<:988PŸìùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòé˯¡\8,)))*******/8IRG7=GPA>CNOB8>MK69HLI@@KJ@9BCH@<3/-,-+)(((()***)))(((('''),16=<86ADC@BPJ63?LC=?LK;5AG=/5CK@5)(**('%%&'%$" ';Zadedccaddeabcdbjgeeiimmnghffcccffhghhhhkjkikklheekheehfkgjjojkhjeddddgdedeeib__abdeechdghkhjghhgfjghgkjjhoidcikommlokiiijkkoljjlkomrlnmsptprouqlijhjgmmnkkkklmhrkjjkltqwxxussropqvpoopnlptnrmm_QEA?>>?>‘’‘ŽŽ‹ŒŠŽ‹‹ŒŒŠŒŒ‹Œ†…†ŠŠ‹Œˆ…„‚†ˆ‰ˆ‡‡ˆˆˆ‡ˆ‰‰ˆˆ‰‰ˆˆ‰ŒŠŒŒ‹‰‰‰‰‰‰‹Š‰ŠŒ‹ˆ‡„…†‡†‡‡‰ˆŠ‰Š‰Œ‹ŒŒŒŒŽŒŽ‹Šˆ‰‰ŽŠŠ†ˆˆ‰Š’’””•—“‘‘Ž‘ŠŽŽŠ…‡‰‹Œ“އ†‚}xrhaX[_Z]vmmmlkjhgikonj`RMOUVIBAA?::;=@Ib„§´¸´±ºìùöòèÝÛÑͧ””‘¯Ö½ƒYSHAL[w—¾áíîðëåЋ`NIR\`bZNIA?<:87677668:9876IŠå÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ðèɰ Z6+)))*******0;IRF7?OL=??NPA4AMH58MMLFCBE9/4;752.-+-**)(()*162/+(((++++**079<:H99CM>2@OI34DE?=ALLD;BC6(-6460)((''&%&'#! *C]`bcbab`a`dccccceedcdeeegfedddccefhehgghjjjhiiifffgfeeffgfkjkjiggddeedccdefeb_acccdcddedgikfffgffffghhljjhjhhfijmqnkljjijkkjnjjjmkomolmmpppoqnnlifgghgghjijjkllhhhkjjkoorstuuwurrsvqpppmopsnnmke[OG@?=>>‘””“““Œ‹Š‹‹’‹ŒŠ‹‡‡‰Œ‡„„…‰Ž•ދЉ‹‹‹ŠŠ‹ŽŽŽ‹‹Œ‹‹ŒŒŒŒ‰ŠŒŠ‰ŠŒ‰‡†…‡‹‡Š‰ŒŒŽ“ŽŠŠŠ‰ˆ‡‡‡ŠŽŒŽŽŽŠ‰‰‹Œ‘Ž“•••—–’’“•Ї„…‰ŽŠŒ‡ˆtm[W[V]ulromlnhffglok`TKMOMD>>@A<99;>CTlœÏ¸¶µ¹ÛòòñçÝ×Ç®’ŠŠ”ÃÝÒ“eZLEIXq‘©ËæêîêçÖ–kOEEV‰|n^SK?;86665543344675DzÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðèɰžU5+*))*)*****1<IRE7APJ:9?LN@5CKF48CPLLDC:2,+-.-+**)+*))((*/;AA2,()+.01/++6BH==<<>NK;/>PG04HN<6=NNKDF9/&&))))((%%%&#"!! !!!".I_aedeac`abeeedgggehcfefdieccefgdefhghhmmlklknijhiggehfhhihljmijfgceegfhdfhkcabfefdeddefelllkmglfffjkmimlljlhhhijmoqlkjojmmpknikkmmnnsoqpppsrrlmjgfhhlhigljmlklljmkkjqjnnpqttvw{zzuvsrqvvuttpppqjk^NB@=>=•’‘’‘‘ŽŒ‹ŒŒ‹‹‹‹Œ‰‹‹‹‹Œ‰‰‰ˆˆ‰ŠŠ‰Œ„ˆ‹Œ‘‹ŒŒ‹Š‹‹ŒŒ‹‹ŒŒ‹Š‹ŠŒ‹‹ˆŒ‹‹Š‹ˆˆŒŠˆ‰‰ŠŒˆ‡‡ˆ‡‡‰‰ŒŒŒŽŽŽŒ‹†‹ˆ‰‡ˆ…†‰ŒŽ‹‹ŠŒ‹ˆŒŒŽŽŽ‹ŽŽŽ‹†‡…‡†ˆ‹ŠŠŠ‰ŠŠŠ‹Š‰„€zsg`WSWflrpomnigeejmme_TLJIC<=>><9:;>CIb޳¸´ÊÈÇéíëß¿¥žœ”ŠŽ™½ÝÕ£€sSHLUm¤¶ÓåëéçÜvVCBP{šqpmh\RH?<97542244555AlÝòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüöïèʱ“Q3+********++2?KSD7BQJ99ANL?5CPB138;A=951/++,+++*,-.0-)))/5ALD4.+159@;<3-;LE3108@MI:0BPC/5LM943>LA?7/,''((''('&%$" !!!"#2O]aaab_a``afddeeefddbddcaabbbeffddehfhhjgijjjjjhfecgeeeghhhkjmhgddcdeeeecgje_bdeeedddffffhgggifhfffiiihliihhgikkklkiikjjjllkijijkmnnnnnnlnnnkjffchgiikfffhhhhiijijiiijjmmqqrsuuwxyvvttrsssrrppkpkkkZKE>><–“—‘’’’ŽŒ‹‹ŒŒŒ“‹ŒŒ‘ŽŽŽ‰‹‰ŽŒ‘ЉЋޑ‘’ŽŒŽŽŽ–ŒŒ‹‹Œ’‹ŒŒŒŒ‹‹‡†ˆŠŠŠ‹Š‰‡‡‡†‰ŒŒ‘‹Š‰‹‹‹ˆ‡††ˆŒŒ‹‹Š‹‘‘‘’”’™Œ‹ŠŠ‹Œ•ŽŽ‰††ˆŠ‰Š‡†‡ŠˆŠ“’“Œ‰†…ƒwr]QQ\crqponkjgeilmmd`VLJB=<><:8;<?DM]„¶¤œž¢³ßéâÃ¡Ž„šœ¢©£·ÛÖ¤’wZQNUj‡Ÿ®Êâêèæà¥‚_FAGljo‡¨Ÿ–}hUFA:752222555?aÌïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüöïçʉN2+,++++*++**3CKSC7CQJ79DOM=7EE?/-02420-+*)))))*+/5861+**2?OQH5017=CIPA71EKB/))1DLG81EP@.7ML6.,2862-*)'(&'%$##" !!!"#$6U[_ccdabbebgehhhejegcdcc^]_fehfffgfhhlkphhjqlmjgeefhfjgkklkkknihdcdhefggfihedfffffdefkhhhjghglfifehlhjhlhhghjmmpmpjiikkpjkjjjlilmmornooonsmmjheefiijkkfhehhkiiioikiljmkplqqsttuwx|xzyyvvtwvvrqopnomlZLA==’‘‘‘Ž‹ŒŠŒŒŒŒ‹ŒŒŒŒŠŠ‰‰‰‰Š‹ŠŒŠŽ‘ŽŠ‘‘‘ŽŒŠˆ‰Š‹ŒŒ‹‡ˆ‡ˆ‡Œˆ‰„†‡ˆŠŠ‰‰ˆ‡‡ˆ‰ŠŠŠ‹ŠŠ‹ŒŒŒŒ‹‹Š‡‰ˆ‰‹‹‰‰‡‡‡‰ˆ‡ˆŒŽ’”•˜’ŽŠŠˆŠ‹Œ‹‹Šˆ‡ˆ†††ˆŠˆ……†…ˆ‰‹Ž‹Œ‹ˆ†„vj\OQZiqqsomkjhhlpojkm_TK@?>=99:<CHQay‘’“–‘•ÌàÓ¹¤ˆ†›˜¨ ©Õ¹›‹}eWTUe~š¤ºÞéçåÞ²ŽmLB?OZzŸ·Æ¶¨…j\PHA;5433556=X®íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïæÄ¨€K2+++++**+-+*4FJRB7FRI67FFK8/5<5-,+-.--,***))))+,5BAF9--+7JOVF56;<@@JNC83EOA/,(1HLB82HQ>-2J=2,'*-,*)((''%%&" !""""$$&<Y]^^^a_bbebfeghgegeea`^^[\^ecgedcfghhiiiiikihhhffgggfhgigjijjjigcddfdfghhhgedeedcedfgihhijdddfffdfikfgeggggknnnpljiijkkkkkiiikjmlmpomonppommjhfghjjjihfffhhhhihjhjjjilkplpopqsvwvwtssuvvtutsqpmnmnmkdWIC=”“”‘‘ŽŽŒŽŽŽŽŽŒŠ‹‰Ž‰‹Ž‹”“•’Ž’‘–‘“’˜Œ‹Š‰‹’ŒŠ‹Ž‹‹ŠŠˆ‹‰‡‡‡‡‡‡‡‰‰‡‡‡‰‹ŠŽ’Š‹ŠŠŒ‹Šˆˆ‡‡ˆ‰Š“‹ŽŽŽŠŠŠŠ‹‘’”™™˜’‹ŠŠŒ‘ŒŽ‰ˆ‡†‡ˆŠŽŽŽ†„„…††‡Š‹ŒŒ’Ž‘‹‹†‚vm[MR[erqponkhimoronuzocSCCD<:<>ALYjyƒ†„„“¾ÒÊò™‰Ž’˜¨šœ¤¤’†|k\TW_v•¤Íæåäß¿›UB=Fh½Ù×ܶšƒth_ND:534685;Q“êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõîæÀ§vH1++****++,,,5IOQA8JLH45<C=4../.+))********+))),.=KTM>/..;OUTF25>G<:LOB96GO?.+'1JL>74KI<./45.*&%((''&''&#" ! !!!""#"$$##%)D^^_`^d_abdcffiiihjde`_^`Z\^ecgfeefhljniljjigedffhggiiljmmnjliihjfgegdjjjkkfeeedddggghkhjjjffeheeejikhiiigikoopnqkjijjkmmlkjjjnlnlmppmonsrpopjjimkkjniifllkhjjjgmilmmlppqmtqrrttyvxttstuvuztsrpnonpmmifWI?ŽŽŠ‹‹ŽŽŽŒŒŒŠŠˆˆ‡‰ˆ‹Œ‹ŽŒŒ“‘‘‘‘‘Š„‹ŠŠŒŽŠ‹‹‰‰‰ˆ†‡‡Œˆ‰Š‡†‡„†…ˆ‰‰ŠŠŠŒŒŠŠŠ‹ŒŒŠ‰‰‰‡‰Š‹‹‹‹‰ˆŒ‹ŒŽŽ–‘Ž‹ŠˆŠ‹ŒŒ‹ˆ…ˆ…†ˆŒ‡ƒ‚‚‚…‡ˆˆ‰ŠŠŠŽŽ‹‹‹Šˆ„|shZMQZirrtpnjiklomo|Žªr[GD><=@AJavˆ…„ˆˆ„—ÉâÔâË©“‹‰‹ŒŽ—š‹‡Œu`XM_h…§«ÃãããßĦ‰]F?Zƒ´ÜÛÜÛÁž˜dTE<866875;J†èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüôí忦pE1+*****-+,,,4KKN=5=I>133220,,-+******-++**+))*,/?RRRB232?PNQC/3;;;AMM?8:IN=-+(3LD:53:A4(+-+))''''('&#"! !!"""""""#$#%&-Ja^^^^`_aacdeggggffcd^__XT]^acdceegjlhiilkjggceggehgijihlkjihfffggffgekihefeffedeeghgfgghjifffheefggjihfggjmonmlljjijkknjhhehjoknmorqlllmlljjhjjjkkkkihflhgdffgfihkkkkonnnqqqqrrxuuutsttsrqpolmnnnmlljgaTEŽ“‹Š‹’ŽŒŒŽˆŒˆ‹ŠŽ‘ŽŽŽ’’‘“•‘Š…‡‰‹ŒŠŠ‹‰ŠŒ‰ˆˆ†…„‡ŽŠŠŠ‡†‡„ŠˆŒŒˆ‡ˆŠŠŠ‰Š“‹ŒŠŠŠŠŒŒŒŠˆŠ‹Œ—”ŠŠ‰ˆ†Š‹ŒŒ‰‡…ƒ…††‡ˆŠŠŠ…ƒƒ†‰ŒŠŽ‰‰ˆ‰‹ŽŽ”ŒŠ‹ŽŽ‰„xnZMR\gtsqnjihkooq™°¹–…aKB=?DAH\€‡’ˆ…ÏÖÔÜà±”Œˆ‰‰ˆ‡’”’f\^chxž¬¼àáãßɧ’aJMj§ÝÞßÝÙ»£¡¦ƒlVH>:99877;G|çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¿¢jA0+++**+***+,4EME:7886/,,+,,+**+******++*)**)**,0BSWUG54:GMRQ@..:==DNL;8=KO;,,(7EH6//10,('((')'(&%$"! !""!""""##"$####$'2O`b^abbbdcgefgigkfhbbdh^UU\^acgcefkjlhlknkihgehhhhhgiiijlkkgffihigffjhmihhhfjffdeehhhfhgffhhighfghkhjjkfihnmrnlllllkjlmiggighjontpqpqlkkkkmiijmlpknkkhiklhgchggejhlkplqnuuvsqrwwxsrsustrqpqpplnnpnnnolkhfSŒŒŽŒŒ‹ŒŒŽ‘ŒŠŒŒ‰ˆˆŒŒŽŽŽŽ‘’’’’ŽŒŠ‡ŽŠ‹ŒŒ‹Š‹ŒŠ‰ŒŠ‰ˆ‡ˆˆˆ‡‡ˆ‰†…†…ˆ‡ˆˆˆ†…„……‡‡ˆ‰ŠŠŠ‰‰‰‰‹Š‹ŒŒŒ‹ˆ‹Œ‹ˆ‡‡‡ˆ‰‹‡…ƒ€‚€‚ƒ†‰†„…ƒ„ƒƒ„†‰Œ‡‡ˆ…‰‡Š‹ŒŒŒˆŒ‰ŒŽˆ€vi[OS\ittrnkijmrsƒ£Áº®}QFBA@@EWm„–¥’”ž½×ÙÙÕ»™ŽŒ‰…šž¡Ÿ™€mqˆykt•«´ÐßâÞÃ¥’gVS‡ÏàáâÝÙ¶À¨†oWJA=::988;Duæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¼ f>/*++******,+/=9;4..0/,,,,++,,++**+*,,,+*+,+**,.1ESSUK:<CEIQP?.5;<;AOK87?NM:++'/<6.,)*+**((('''%#"! !!!!!!"$#"""##$#$$$#(6S`^^abbbcddeffhfdbc`eie\ZW^_abbbefijjhhhhhebhfgghihffgijkifdffggheffjhlffeggffeceeijgegdeehhgfgfhhihhfffiimmnkmijjklifhfgghgijpopponpikkkkigilkkmklkkhjkhdddikhfhhiijilnrttsrrvtsqrssrsrqmmkmlmnnoqspmmli^Ž‹ŠŠŒŒŒ‹ŒŒ‘‘ŽŒŒ’”“›•˜’‘‘”•™•–‰‹ˆ‹‘Ž’‘‹ŒŒŠŽŽŽŽŠ‰ˆ‡‡†‡ˆˆˆ‰‰‰†ƒƒŠŠ‹‰Š‹ˆˆˆ‰‡‡ˆŽŒŒ–—˜‹‰ˆˆŠ‹‹‘Œ“Љ†ƒ€~„…†‡‡†„ƒ„ƒˆˆˆ‰Ž†ƒ†‡ˆ‰‰ŽŽŒ‹‹Œ‹ŽŽŽŽ…um^SU\fqpoliikot…µ¼°”qWHILLGCXi˜¬Ê© ¬¯¶ÍÞÈŸ˜—”†Š˜«Î§„v}¥¾vf€ª®ÂÛàܽ ’r]n¢ÜãããÝÚ³«©©‹s]MC@=;::9<Bnçüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôíå¹¢c<.+****-+*+,+,210.,-,,+++++++,--+*+*++++,--++)),3IOPRPBABDHPN>8;CD:?QJ56ARF8**(*-,*(((())(&%$$"! !##""$"""#$%$$$#"%$&$$$)9W]^_dbcdefheffibaaeelhe^___addheeffhjijggccdiggfjjkklmlmmgddihkhifggjjlfgfjgffeehfligelgffkgffhhjjjgffjiikqnolnijkojihhghilkppppqnllpomlmjigmkklonmlkkojfdiijiihigghlllmqrututwsttsststrqnolpmnorsttvvuuvjŽŠŠŠ‹ŒŒŒ‹ŒŽŽŽŽ‘‘‘‘’”””““““”••“”‹‹‘‘’‘Ž‹‹ŒŒŒŒŽŒŠ‰†‰‰‰ˆ††…„ƒ‚‚ƒ„‹ˆ‰‰‰‰‰ˆ‡ƒ‰…†‡Œ‘ŽŒŒŠ‰ˆ‰Š‹Š‰ˆ†„ƒ‚„„€€‚ƒˆ†ˆŠ‰‰‡ƒ„…ˆŠ‰ˆ‡„„…‰ˆ‹‹ŒŽŽ‹Žˆ„}vqaSU[irqlihjmt„—ª²²gSL]whQOZj{²²²Ÿ’”¬Áäצ–›¡¦©“…–±·¶ž“ÈÉ‚blŠ›°ÕßÖ²›”}w|¹âæåäÞа¯¶«wcPEA><;:9;@gåúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôìä¹£`;.+**++++++++,.-,-+,,,+,+++,-283,++,,,,036;4-,*-6KMPRQGEEGGOL>4@MD6APA24A;=3*)&()*)))((((%#! """"!######$$##$##$#"$###$#*<X\_`aacedffccb`]acefecb___abeeeeeeeegggfdaefgfhghiiiiiihgeeeihjhgfggijgdgfgfggghhffggfffggffffhjjijgedhikkllkiiikljfffggikllmmoonllkllmljjiimjklonnolklifeijjggggggehhjkklnppqqpsutssspmmkmmnnprtuutvssrqnŽ‹’ŽŒŒ’”Ž‘’•‘˜’’‘’“™—œ—š™™—›–”“““”–—‘™’•’ŽŽŒŒ•‘‘‹‹Ž‹’“’Œ†„‚‚‚„‡†Œ‡‰‰‰ˆˆˆˆˆ‡ˆˆ‘“‘ŽŽŽŒŒŒŒŽŠ‰ƒ‚‚‚‚‚„‚…€~~€‚…‹‰ˆ‡‰‰‰‡†‡ˆ‹Œ‹……‚„ˆ‹Œš‘•ŽŒŽŠŒƒ~zvcUU]fqmjhgls}†—¤’cLZv‰‘lYblzŠ”žœ›Š˜¥½åΫ¥¨Ÿ–¥–—¸×ÖÑ›™ÁÏŠ_fƒžÄÜ£˜Žƒ¢ÞäèæãÝǰ¯±{bSGD>===9:?aÚøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôí帟]9.*,+,+++,+,--+*+,,-,,,,,++-.8>>.,----.3;AA?/,*/9LJCMTPOA=HNI=8LMEDHH@.,230-)(''')('((%$#" !!""####$$$%$$$%#$###%#%#$#$#,AX]ccbadedefcd^[^ehjeedccb`eeeefeeehegffdcdjjiekhhikkkijhfegeihmijhjiiheegfghjhniihgfggjgigihjjjjmkjgdfkkpknlkimlljiefgjjmlroompopkmlmmpjhjjkmjllpponnmmighlkkhlgghhhiiihiilmqrrpttuvvpolmlqnpqrsvtssxwvqooŽŒŒŽŽŽŽ’ŽŽ‘‘“”•‘‘‘““’’“™—œ—˜˜˜–•““’‘’’•‘”’•‘“ŽŽ‹Œ”‰†ƒƒ„…†‡‡ˆˆŠˆ‰Š‰‰‰‰‡†‡‡ˆˆˆˆ‹‰ŒŒ’Œ‡„ƒƒ‚€ƒƒ…†‚€€|€‚ƒ…ˆˆ‰‰††††‡ˆ‰ˆ††……ƒ…„…‰ŒŒ‘ŽŒŽŒ‹ŠŒ†ƒ€ztbRTZgrlihkry†“ “bSb‰¥›‡mlozz{‚‰‡ƒ”ºÉÇ²Â§š†–¢¼ÔÝÚ§š¼³–eYo„•·´ª£¤¥§©¯âçêæâݽ ¨¸©‡t`UJF@??>;<@]ÇõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúôíÞµšY6-)+++++*++,---++-,,,,,,,+,.0:H:1476752:C>?7.-*0;LGIHRTK;<KJE<8DSC?>?5****+*)))'(()%&#"!! !!!""#####$&%$$$$$$####""#####$#/I\^`_aaefedb_\X_ehkc^]]baabdeedffedffgggcfghgfeffijkjjjifedeeiglikghgffeegfhiihiegggfgehggehjkijjkkjghikkmkmgkillkhjfhijjmmmmnmmklkkilkjfhjkklkkjqpqkmnmijkkkmghgfehcefiegfllmmqnnnomlkllllooruuuussponponmŒ‹Œ‘Ž“““Ž‘—”˜˜™”•””•–——““•™™™˜—š•–•š’“’—”•”—••‘’—’“Š‰ŠŒŠ‰‰‰…‡†‰††ˆ‰Œ“’‘‘ŠŒ…†……†Š‹‘”—‰†ƒ‚€€‚„Žƒ‚€‚„‰ˆ‰ŠŠ‰‰…‹‰ŽŠŒ‡……†ƒƒ„…‡Š‹Š‹Œ‘ŽŽ‹‹‹Œ‰Š…}ubUTZdnhhjpz|‚ˆoVf §«vrqqprs|zxz–ɽ»³¬¨œ–‹Ž—¦¯ÏÖÓ³œ¦»•lVct†¢º £³Ã£¹äçéæâÜ´Ÿ¢¤¦‚paWNJEB@><??YºòûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÚ³‘V5-+*+++*++,,++,+,,,,--,,,+,/6DH;8:?HB;9EF>>3..*0?LE;BRRI8:LQA74@FG=73,(''(()))('&%#! !!!!""#$$#$%%%$%%%%$$$##$$#%###$#$#2R_aaaffefhdc_Z\ehle]Z[^abffggffgfjgfhlihfniiedehfmlnjihieefkfihlkkhkffeffkhmijhiefhgglgghjfhlqkjimjjijlmmnjmijiolkhjkllkkmnmlqmmnoonnpkjfjkppqlmkqpqmmmmmmnqkoklgfghcfeigkkkmpqropppkkknnqrttuw|vzstoompnnlŽ‹‹ŒŽŽ’‹“””—••”–••””••”–š™˜˜™——˜•––˜’’“–••“”’‘“’‘ŠŒŽ‘Œ‹Šˆ†††‡‰ˆ…†‡‰ˆˆ‰Œ‘”’ˆ…ƒ‚…†ŒŒŠ‰‰‰…„„„ƒ„†……„„||yy{††‡‰ˆˆ…‡††…Šˆˆ‡‡………………†‰‹‹‰‰ŠŽŒ‘ŽŽŒ‹ŒŒŠ‰ˆ‡‡}rcSSYdjihm‚€}|{Šnf†¬¢”slkpt~„€ns–ÇÆµª®™›•|…ˆ“”°ÑÍÅ¥ŒlSYdw‹«É¾ŸžÂæçèåâÖ¯“§¨–}rbZOLHEB@=>>V¬ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúóëÔ²‡R3-**,+,+,++,,,+,,,-,.,,--028=BG=9<>BH;8AND>4,-*2C@B89LO@16KB;2+/421/,+)('('('&%#! !!!""""####$$%%%%%%%$$$#"""""#######$7X`bbccbededb_`bfif_]\_abcdefffggegghijihghfeeefhgjjifgfhbefgfhhiffgheedfghfiikeeegihgkfgiifilkkjilkjikllikjjiiiiihgiilomnnqlkmjgeimkjigffjkonojkkqoollmnmnpnkljkhgcbbccefhilpmkmllkjfhhlpqrssuwwuurolkkjiig‘‹Ž“““”•—œ••”˜•–““”•—ššš—–––—ž˜˜˜˜˜™•••››œšš‘‘‘“”•’“‘ŒŒŽŽŽ‰ˆ‡‡…„…‡‡Š‰‹ŠŽŽ““—–”‘Œ‰„‚ƒ„†‹‰ŠŠ†„……†ŠŒŒŒˆ‰…„‚}||tr|ƒ‡Š‡Š‰‡‡†‡ˆ‰ˆ‡…†††‡ŠŠŽ‡ŒŒ‹ŒŽŽ‘ŽŽ‹‹ŒŠŠ€xcUTW_ifjzŠ}{‰“muŽ™‘…rjluŽ—Ÿ{q“Å»±¬ª›†ˆŽ‰„ˆ—’¤ÑÕÍ®‰˜oTNZjz€·É»”ŠÉçèèåáÒ¬ž¢‘{ub\TOKHEC???RœëøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòêÒ²M2,+*+,,++++,,-+-,,,,,,,.049?FKB=@E@B@;8@INA5,.,3IJ?55HD8.2;;3-)()))((*))&'%$$#! !!!"""""###%%%%&&%%'&%%$%$#$##"$"#######&<\bcdeedffffffgfjhd^^`cdfdieefghieihqjjiiggfghlhkgihhfgggfggggjikefggfiiigifkilegfgikgkhghlkkkjjkilkkjnjklmkkkojmiihlkmllnomlkqiddiihgffiinnnnnmllpopmlmqnpnllmkjgicbbbbeflkmmllljijieggjoqswvvwvvwqniihhiigŽ‹Œ‹‘‘’’•——˜™––”””–’•–—™˜š—•••–—š˜˜”˜–——–”˜–›—–“’’‘‘‘ŽŒ‹‹‰ˆ…ˆ…‡ˆ‡†ˆˆ‰‰ŠŠŽ’””’’‰‰Š‰‰‰ƒ„†‡‡†„ƒ‚‚‚‚„‡‡‰ŒŠ‰ˆ…„€{xyvz„…‡†‡‡ˆ†……‡ƒƒƒ‚ƒ„…‡ˆ‰Œ‡Š‹ŽŽŽŽŽŒŒ‰Š‹‰ŠŠ‹Š‰ƒ|ubSPS^fis|~„‘”}eg}Љrhht’¨ ‡†¿¼¯²¬œŽ…ž–‡”‘©ÏÒÐ¸Š‚spXOQfsy‹ºË¬€…ÑçèèäáΪ˜ŸœŒzuc]VROHFB???Pç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñéÏ«wH0,+++,,,+++,,,+,--,,-,,04;CBLJ@AHA<<;;8AB?@3,+*3LJ;2,5<3),0/.,***)))('&%$#! !!""!!##$###$$$$$%%%%&'&&%%$#$##$"!""######$(A`acdefeededfffffed`adcefefedcddfehijgjhhfhggghghggfheedcbcdfgiilfgggfgggggghge`gggghgiiiikjkkkjjhlkljlijmllllojjiihljlkmkkiihhdccdefffejloonmkgmnpppklmommmlklljedccdddfhlkjhhihhgeeeffjnopssvrqmmmhcddjjji“Œ‹‹Œ•’”˜—žŸ –—”””–™›™š——•••””–—žœŸ˜˜—œ›š¤———–•’‘‘“‘”ޑދ‰‡……ˆ‡ŒŽŽŽŽ•””’Œ‰‰‹ŒŒŠˆƒŠŠŒƒ€‚ƒ…ŠŒ‰‰‰‰‡†ƒ‚‚€{}ƒ…‡††„ˆˆ‡ˆˆ†‹‡‡ƒ‚‚ƒ…ЇЉ‹ŒŽ“’”ŽŽŽŒŒ‰‰ŠŠŠ‹ŠŒˆ‡‡†}s`RPS[glvy‹‘“‚u]Ucyxhiq‘ž³‰Šœ¼¹©ª¬—‘’™¨µ”“¥ÕÞÌÆŒ€vqaQPblq½´yÉæèéãÞ¦¦©Ÿ‰zwb^VXOID@>??M‡ãöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñèË¥pD/+++,,,,,,,+,,++,------2<F?@NK@EF?;99;>@BD:1++*4HL70).0.)(**)*)*()('%#" ! !#"!"""!##$$##$&$%%'&&%%&&%%%%%%#$#$##""###""%*HbedhfhfhfiflllfedeffdcehefddeechekkkkkghhhhliiikhhgheccdcdehjkjmgigkfmllkmgifddhflkjkljliokoonklgllllpiklolnnnjlkkhkkpmnklihgfccdeeegjjlmqonkklnpspomnnpmmmlkkjjdedhhihllmjifghhfeeeeihkooottwrqlljecbdkknmŽŠ‹‹ŒŽ”•˜™›œ››˜•””––˜˜›•••˜“••”“••˜šœ›™—˜–™š›——”––•‘‘ŽŽŽŒŠ††…ˆ‡ŒŒ’ŽŽŠˆˆˆŠ‹ŠŠŒ†‚€€€~~ƒ†ˆ‰Š‰ˆˆˆ†‡‚†‚‚ƒƒƒ„„„…„ƒ„ƒˆ‡‡Œ‡…†‡‡‚‚€‚†ˆ‰ŒŽ‹‹ŽŽ’ŒŽŒ‹‹‹‰‹‹ŠŠ‹ˆ‰ˆ‡‰†wk^QMO[gjn†¢’u`NJLk¨›€khnw‰‹Š’¢¹ž –˜¨»Ã²—‘ŽœÚͳ¥Œ€xxw^Q^lq±šu~ÂäèêãѶž•¤¡‰|n_XSQNH@?<=>JàõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñæÈ¨jA.+,,,,,,,..,-,,++,.-./.8GE<@MD<GME>:59DAA92/++)/@72.)+,+)*+*)*)((&%"! !"!!#""##$$$$$$$$$$$$$&%%$%&&%%%%#""$#"""#""$$##%-Mcdbhffffefehhgdccdbdeddfdfba`dddejjihgefegiiijjjiidcccadbegjkkiifgfeefgkhhffdegigkijjjijjkkoiiijilmkkliklmkllljhgiilkmlmihfgfgaccccfjkmlmqplimmoqqqpmmnpkkkmjigfdechghimnkgfefggefcefhhkpoovqrpommjd^belkmnŒŽŽ‘••™šš›˜•˜žžšž˜š”•–™–›•–•˜•˜ššš–ž–ššœ™š˜ž™œ”““•ŽŽŒŒ‹‹Œ’’˜Œ‹‰‰…†ˆ‡ˆ‹”ŽŒ‰„€}{~ƒ‰‹ŒŠŠ‹ˆ‡‡ˆ………†ˆ‹ŠŠˆˆŠ…‡…‡‡ˆ‡†††……‡ˆ„ƒ‚‡‹Œ‹‹”‘ŽŒŽ‹ŠŠŠ‹‘ŒŠ‰‰Šˆˆˆˆ„um]NJLV`n…‘Œ{[KAFb¤³Šploonw‘›™™“‹˜”Ÿ½àÛɯªš—³·®«“{¢‰iZ]lrÅ•u{µáããÒË©•¥Ã¢‰whZROMJF>?;<=H{ÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøðåÇ©f?.+,.,+,,,-,,,,,,,,,,,-/<JE;@GC=KOLC@44:;62.+*+*,01/,)))))((*('&$##! !!""""#$##$%%%%$$&$$$$$&&%%%%(''%%###$#""##%#$###&1Scgeiiiffeiejggdccfghegdidfaaajglfjgffgfhefjmlplmjkefcdddehkmllkjfhggeikliiffefhigkiljjkkktmnjjjlmoljjllklnlnloighjlqknkmhiglfgcdddegjmmonqmjltstrtrqqqnpkmmnihfddeeijnlrlkffehgjfhceekghkpowvusqrske`chlllmŽŽŽ’’‘˜–™š™˜šœ›šš™™–—•–™—”•”––—”™šš˜˜•—–š™™˜™—š™š–”’•‘‘ŒŽ”ˆˆˆ‰‰ˆ‡‡……†Šˆ‚€~~|€…‡Š‡†‡‡†…††‡ƒ†…‡ˆŠŽŠˆˆˆ‡‡†……„………„„„„ƒ…†ˆ††…†‡‹Š‹‰ŒŽŒŠŠ‰‹ŠŠˆŠ‹‹ŠŠŠŠŠ‰ˆˆ‡ˆ…†‰€yri[MJLZn„‰†iLDE^ž›•vmom^^}—•‹‡zo|¦ÐìêÙǾ§¦®³®° ƒ~°vZ]ku¬©•uu§ÍÞɺާ¸Ã£ƒoaTNIHGB=<;;<GtÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûøðäÆªc=-,,-.,-,,,,,,,,,,,+,-00?LG<AJ>4>LC@8/011/.,,,+)+--,+))+))''&%#! ! !""$#$$$$$$$%$%%$#&$$$$$&%&&%$$#$%$#####""""#"####(6WcgdhcccfefeddeedcehgedccbcbbbifhffeedffffgjkkjjiggdedcbdfiklggeffffgdjiiggffdghigjjlkkmlllmljkkmmojiikklmlknjjiijkkkkkjigggecgcdafkjjlmnmnlllspqqqooopmpkkkmgfedceeikmnrifbdfffgeddedgghilnooquwxsngbhmmllkŽŽ‘’’‘˜˜˜œ¢¢£œš™™˜˜™˜š———™šš—™—šš›˜›–œœ™™˜œ ™Ÿ——•–••••’‘‘”‘‘‘‰†‰™†‰ƒƒƒ‚„„ƒ‚„…ˆ…‰‡†……„„„„ˆ‹‹‰‹‰ˆ‰‰‰‰ˆ†…††……„„„„…‡Šˆ‰‡‡‰Œ‹‘•Œ‹Š‰‰‰‹ŒŒ‹ŒŽ‹Ž‰‰‰‰ˆ‰ˆˆ„„ƒ‚{xtpbUOPbƒ‚[GL[„¬œ}syhOUpƒƒxspjj„ªÚïððìàÄ·°±¨”…‰œžƒYXkx‰¸©–toµ²³ÂÁ¾¾½À™}j\QLJJHB=<;;=FoÙòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýúöïãê^;-,,,,,,++,,-----,-,,.00CKGFFB945:;61,,,,,-++++***+****)(%$#" """ ""!"#$%%%%%$%%'$%%$$%$%$&%'''&%$%$$$$#####""######$$*<\dhehdefeeffgdeehdeghdcbcbeeeejhkffeedjggfiillmihgggggfffgjjlgieffjfggljijighfjiiilmmlmmololqonooppkjjoksmlknllkkjmkjjiiihhecbhdeeikjjlnrnnmpmtqurrowrtppmnkllldefjhnmxosgcbcglhllkihghgkillllpszutljmqppnmkŽŽŽ‘““””“™œšœ›››™™—šžš—–”—‘˜—––™šš—™“™š—™š›˜–“—••’––——”’’‘‘”Œ‹‹ŒŽ‹„€}}y€‚‡‡‰Š†…‡…„„„„ƒ‚…†ˆ‰Š‹Œ‹Š‰‰ˆ‡†ˆˆˆˆ†…„‚‚ƒ…………ˆ‰‰ŠŠ‰‰‡‡ˆ‹ŒŽ‹ŽŽŒŠ‰‡ˆ‰‰ŠŒ‹ŒŒ‰Šˆ‰‰Šˆ†ƒ‚‚‚‚ƒ~„~yiXTUhzz~~bRPXl‡¢„rjcLN`w{}rkfX_{¨áîõúôêÜõœ˜‹”›Ÿ}WUjt€œ¥–sm|™©ÃÝßÝÎÄ´xfZPMKJHC?=<<=FjÞñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõíâÀ¢Y9-,+,,-+++,,--,,-+,,-...=OEB<:4,/11/-+,,,+-++,,,*,*)**'&$"! !"!""" !!""##$$$$%%%$%%%$%%%%%$$$%%')(&%$$#$$$###"!""#######$,C\cdceeedddfedccccbfdcba`bbegffgghefdedgfffjihfffheggfdfeghjiigfcfffghhhhijighdggjkklljmnnkklpmmnokkkkkljjjjijkjjjjmmhfghhdedbbcbegjkjjmnonmmnnnnooooonoonmmjjhheffghnnqokda_dimghhjjhghceeeehlmlnoolmppoppol‘‘•”›–—“œ›™›œ›š˜˜˜šœš™˜˜˜˜’”•–•••š››™š˜ŸŸžœ››žš˜•””—“•“˜—š˜—“–’’’“‘‘•‘Ž‹ˆ…ƒ€{z{€ˆŠ’ˆ††…‡„ƒƒ‚ƒ„†‹ŠŠ‰‰ˆˆ‰‡‡ˆŽŽ‡…ƒƒ††‹‹’‹ŒŒ‹Šˆ‡‡Š‹ŽŒŽŽ•‘ŠŒ†‡‰ŠŠŽ‹ŒŽ‹‰‰ŠŠ†„‚…ƒ„†Œ†ƒn\UWbjryeUSU]j~ngaIISerrlgcUSr¨ÚîöûøôåÍÅÅ¥’”Ÿž‰w_Shoyžž–tk{ŸÄßßßÕØÀ©ŽraYPNLMFD@><==FhÖðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôí὚V7-.-,--,,+++,----,,,--..9?A;62/+++,,,+++++,+++,**)(&%#"" !!!!##"#"#%%%$%&%$%%%%%%%&'''%%$$$$'++)&&$%%%$%#""""$$$#$###$#/J\cgffffcdehdccddbbfdd`_affffiggfieffeekhhhkiffjfhgghkfggjknijgfdgghhljkjijkiiiklmnpllkmnllnopntmoijknkkikjjijjmkqnnjffffiffcaaeelinjiksqpnqrroqoqoooonppnnojliiklklnonrlga_`ghmiikmljhhbedbcginnomrnqqrqqqql‘‘‘”””“’’’’“•–————˜–—˜˜——–”‘‘’’“–”••˜˜ššš™œœ›š›››—™””“”’‘”“–•—’–““””““‹ˆ†‡„„„ƒ‚‚…‹ŠŠŠ‡…€ƒƒ…„„„…†ˆŠ‹ŠŒ‹ˆˆ‡‡†††‡‡†…ƒ€}ƒ„‡ˆ‹‹‹‹Œ‡ˆ‰‹Š‰†Š‰ŽŒ‹ŒŠŠ‡‰ŒŠŠŽŒ‹‡‰‰ŒŠˆ‡†ƒƒ‚ƒƒ„‡‰ŠŒŒ‰…‚l[TS]gih[USPXcjjc`H?IVdqlebW]j•Õí÷üüûîßØÊ¯› ½Ã•€{dfhfe‚¡‘sk³àãàÚÔ˾¬v^XPMKIFCA?<<<EdÏðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùóìá¼–S5------,,+++,.---,,-----/3220.-++,,,+++,++++++*)&$#"! !!!!!!!!###"##$#$$%$$$$$%%%&()('&%%$$$&(,.*%%$%#$####""#$$$"###"$#3P^cgdffgccfeddcbba`aa_^`bb`ecefdcccehgejhhhhgffeehhhggghhjjjghgfcghihhiigjkkfijklmomkmmmmllmopklljejjlkjhihhijjkjmomhecdceegaaadekilkklooplmlonnmnmmlmnnlnkjjjkjkkknpolkgc^_begjijnormiibdccdggonnlkkmnmlmklk•”“‘’—’’’”•˜––—™˜™—›•’“’‘’•—”–•––œ–™šš™›¥œžœœ˜š”“”𔕕–—˜™™˜—“™”•Ї„ƒƒ†ˆŠ’’’“’‹ŠˆŠ‡…ƒƒ„Љ‰ˆŒŽŽ”ŽŒŒˆˆŠŠ‡‰‡†ƒ‚€€„…ŒŽŠŠ‹Œˆˆ‰ŽŠ‹ŠŽŒ’’’‹‹‹ŠŠŠŽ‹Š‰ŠˆŒ‰‡†„‚ƒ…‰Ž’‘‰‰}hWOOZic]XRPNXfb`ZG>AKUbkgdgtyŠºçòöùûõïéÛµ ·Ô®z~k`ab`uŒnkŠÅÓàÜ×ÉÅ·¯˜}fWNKIIIIA@=>=DaÄïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôìâºO3,+.----,--,--.-,,,-,++++-,,+++++,,,++++++++*'%$"! !!!""###"####$$$##$%%$$$$%&'')-./*'&&%$&')0-)$%$%$%#$##"$$%$%###$##$8V_dgfhggcdefefbaa``c_]_`bbbedecbbbcfiigjjlhgfhfgghhkhjhmhkjjhiihgghlhnjnkqkljnmpmnlkkpkjklloppjmkiijkkjiikilmmjklmllgedhdeeibccjhljokmmmnplmnqnnnommlpmnmnkkjjjjllmrqqmkdb]``bdnjkoqqpjkffccejjpnsljiwnomnjpj‘“’““”“‘‘’’”•–•––™’”“’‘’”’Ž”–”“••––––šš›™™™˜—™™˜”™’”••–—˜›Ÿœœš›“‹‡ƒ‚~ƒ„‰–“Œ‹Š‰‡ˆ‰ˆ………‰‰ˆ‡ŒŠ†‡Š‰‡…‚‚ƒ‚‚€€„…‰‹Žˆˆ‡Š‰ˆˆŠŒ‹ŠŠŒ‹‹‰Œ‹ŽŽŠŠ‡‰‰‰‰Š„……‡ˆ…„…‚„ˆ‹ŽŽŽŒˆŠ‰‰ˆˆ€}pbSPQ[ca\TPNOYb[TE?ABKXl}vu~Œ‘§ÑëðöúúûòãÀºÃÁž}z{sqoh^lˆyhm•ÆÌÊž¹³¬§¡‡lYLJGILF@>===E^ÂîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøôìจL3,,.------,,-.--,,-.,+,.,-,++++++-,-+,*+*)'%$#! !!!!!#"$"$#######$$$$%$$%%$$&)((*,150+)'''''(*+,)$%###$#$"""$$$%%#####$&<\adfffegcebfdcba``_^\`aaaccdba_a`cdfhigjiiggeffgiifhfhhhhgfhgffhghhihkiiijgkjmmmkljjjjgkjllljjiijjjkllmjhijlmlklmlihdeedddcbacchhhhkkljlkkkmnmmllplkkmmlknhjiiiklmmoomje_ZVYY\`nkloolpkjgfccbeikmmliikmkijjihšššš›”———‘“–™———ššš”’Ž‘•™˜–•”“––™™˜š¡š ˜——˜—›˜——˜˜˜˜™œ¢£¢›˜’‡‚‚~‚†Œ•‘”’ŽŽŒŒ‹ŒŒ‘Ž‰ŠˆŽ‰Œ‰Ž’ŽŽ’Їˆ…ƒƒ‚‚†‚……†‡‡…††ŠŠ‰‰‹’Љˆ‹Ž‹Š‹‹ŒŒŒ‘‘ŠŠ‡‰‡‡ˆŠ…†„†„‚ƒ…†Š‹ŽŽŽŒ‹‹‹Š‡…€}wpdWQPZnaYROQV[YSDDECF[q†’€‚Šœ¬ÃÜêñöùüõêØÐϽ™{zƒƒ‡xkalneo™ÉÊÎÆ²¬§¨¨œ“o[KGFIGD?=<=>D`ÁîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøóëÚ±~I4.----.-,-..-..--,--,,,,,,++,++,+,+**)('%$#"!! "!!"""##"###"$##$###$$%$$$%%&()((+,+,8RB4+*)**))+***)$%$&$%###$%)(((&###%#%(A`ceffgfgcecfdccdbb_``fdecfgga``dceefgigoijjkgihkjjglikijhggkkkfkiokkimikkjgljqnnkllljiiklpnmjkippppqnpljhllqmnnrmkhhgheeefdecghhhjhmklllijkomnlmlpkrkmmlknjjjljmmqpqnohc\WLJKPamkonnnpkkhiefceceglhgjrkjijjji—˜˜˜–”””“““˜––———•”•‘‘“••”••”““”›š›œ›ž˜—–˜”›˜šššššš š–—˜–‘’Œ„„†‰‘‘ŒŒŒ‹ŒŠŠŠŽŒŠŠ‰ŠŠŠ‰Žˆ†„ˆ‚‚€‚‚ƒƒ‚„…†„††‡†ˆˆˆˆŠŠŠ‰ŠŠ‰ˆ‰‰‹ŒŒ‹Œ‰‰ˆŠ†……‡‡†‚…„†€€…†Š‹‹‹Š‰‡…ˆˆˆ‡…‚€}xufXSS_l`TPPRZYRGFHHGa”…Ÿ¸ÄÎÚëð÷þøðêàÖ¬„|‰–«¼Ÿofk]q›ÇÉÈÇž›“…r^KE@ACA><<=>Ea½îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýýøòêÖªwE30.-----------..-,--.--,,,,,+++,,,*)&%$"! !!!""##"##############$$%$$%%&&/:63<K9./=IF4,./01/-,*)(&$$#$$$#$$')-04;+$###"%*Gabffdgggceafcdedbb```dccba``]`acdfefdggihgddeggiegghhihhhhijihgjjnkjimijiigjjlkkiihjijklmoplijjnlllllliigmlnlnnmjihgcieebcccdffgehhmjmijikkkkllllmjjjjijhijjjljmnqpmjhd]VH;AEVenkllmlmkkfedebaacbdafijhiiiiih–—š–”–˜˜™•’“••œ›˜“’‘’š™˜••••–•”••œ¢œŸœž™œ——–››Ÿœž ›š˜••“—–—›˜–—––•ŽŒ‹‹Š‹Œ‹‹•“’–‘ŽŒ‹‹’ŽŒŽ•‘Ї‡…‰‚‚ƒ„„…‰„„†‹‡ˆ‡Š‰‰‹ŠŒŽ‰‹‰‹Šˆˆ‰‰‰‹“Ž’ˆŒ‰ŠŠŽ‰ˆˆˆƒ‰„„……„Œ‹Š‰ˆˆ‰‰‰ˆˆ‰‹ˆ…ƒ‰ƒ€~{i\TS\hYRPMR[QIGGEHcœ“‹u‚µÇÌÒàëôúù÷ôìÙ£‹t‘ÕÐË‹}tns|›ÄÅÆ¸ªš™˜˜‹tcMD==?><;=AAF`¸îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷òéÒªqC1.----.-,-/-,-...--,,-.,,,,,,*(('&&%$""! !!!!""!!!#!"##"##"#$$$$$$$$$$%%$$$%&&(.:NOSYQ@13CSA438:=??70,+''%##$$$$%(-8FHJ>0&$#$$&-M_cdefjghdeageidebc`achcb``aabdcffjgedkhlhgdifkghfgilikjkilmmjjhkjnjlknikjlinjmjiiihnmmknmrolkonoknmllmkmlmnqmplkihglgjeebbchhhgggllmknilkmkskmkjkqkkkkklghjmmllqrrnlhf]XJ;6;Ncgolmmllnjkgfdeaccddfefjkklikihi—•™”—™˜˜™—”‘‘’’“””••’••˜–”•–›žœ™š›—””•——šœ›š—ž˜˜—˜—𡡣𛕓’’‘‹‰ˆŠŠŒ”””ŒŽ‰‹Œ“““ŽŒŒŒŒŒ‹‰†…ƒ‚‚ƒƒ†ˆ†††„„„‹‡‡ˆŠŠ‰‡‹„‹‰‰…‡Š‡ƒˆ…‡‡‰ŠŠŠ‰‰‰ˆŠ‰‰‰ˆˆˆ†ˆƒ„„†‡‡†Œ‰‰Šˆ‡†„‰†‡‰‰ˆ†„„„„„yj[TQZ^UPLLMPKGFCF_˜ˆ€”±ÔèÝÜãñ÷úýùôß±Œ‡‡¹Îº”“³°®¦ž±Å»¬¢™”Ž‹ƒ|udNE<<<;:;=>?E_¾îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ñè̪nB1/---------,,,,---.,--,,,,+*))%%"!!!! !!! !#!!!!!!!!! "##"#$###$$$$$%&''&&%$&'.28=OpR\[]A27HP><:CKOQIC6-+'&&$#(+)(16=HFOI?6(%###'0Q[^`ggecbad`fdebcaaacfga_]acdeddfgjhgdjffefdeekggghhhgggiikkkljhhhihhiihjijijiihhhhhhgijnmmmmlllkkkikmlilmoomkkjihhgiffccadehhhhhgkkkjiikkllmjjhhhhgijjljehhiilmnnnljg]RH=63Lfeilkopmkjijgfccbegggffhiijihjghi––™˜ž›œ—™••‘Ž“’—•™““”—“•–——™•–—œ›š™˜–”•—˜œš™››œœœŸ ¡Ÿ§ž£¢¢™—•”’‘‘“ŽŽ•–––—’ŒŽŽ’Ž••˜’“ŒŠŠ‰‹ŽŠ‰†…„„…ˆ‰‹‡‡†‰‰ˆ‡Œ‰‰Œ‘Š‹‰Œ‡ŠŠ‹……††…ˆˆ‰ˆŒŒŒŠ‰ˆŠ‰‹ŠŽ‰‡‡ˆ†‡††‡ŒŒ‹ŒŒŠ‰ˆ‡†‡‡Š‡†‰‰‰‰‰Ž‰‹‡†€}jZROSZQKKJNLKIDCTqu{€‘ŒáäæÝàñöùýûùè͹§š‘™°¶°–“¯¿¿×«¶½¨–’Ї€|seOE>=;:::<>?EaÅîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷ñèǬk@1------,/-.,--.---.--,-++*)%#"! !"$""""""""!""""!"##$#$$###$%%%%&,124,()+2?I^`bnV\naC69BN:8>RX_WWJ;.+('&%(/117GCOKHLM@B(%$$#(4TZ^_idccbadbeegccdeefffb_`ggggggkilijekfgdjfggklllkhghhimkokmklmmhkhhiihnnnjlikhjhhgghijnmllrllkijlgokkkpounmjkiihohlfgbedihiijjkjlkkkjioooklkkgihgfhiiijfihjkqqqnpklaVF>51Bfkiiompookihjhggffkknkihkirmoijhjk–—˜˜˜˜—””‘‘’’’’•••“““““•”–—–••˜˜—–•™•˜›››žžŸŸ¡£¢¢ŸžŸŸšœœœ™˜—––——˜š–“’’‘’ŽŽ‘‘“’’’’“‡‰ŠŠŠŠˆˆ…†ˆˆ‡ˆ‰ˆ††‡‡ˆ‰ŠŠŠ‹Žˆˆˆˆ‡‰‰‰‚…„……†‡‡‡††‡‡‡‡Šˆ‡‡†…„„…„ˆ‰‰ˆ‹Š‰ˆŒŠ‰‡‡‡‡ˆˆˆˆŠŠŠŠŠ‹Š‰‡‡ƒ~xgXQKQTJFHOMQIDABVjs|ˆ’‘¦ÈáÜØàñôùþüûñäßÎ³š“®´¶˜Ž©´º¿¦¢®°£˜‹…yuobPE?=;;;;;=?FbÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýü÷ñèÅ®i>0-----.,---,,,-,,,--++*'%$"! !"""""#"##""""##$$#"#"$%$$$%&'%%/CAA=48@?ANV_dfk[ZbdC458868AU^]WSO<.,)))+/;FEEG@GNCG=51'$###)8UY[]^^_]__`aedcaddeefdfbbbedfgghkgihgefffejffhiiihkiiijkmiiiihlmifhihhiinnmiighgjghhikkkkkllkkkjihhhjhjloonoljiiiihhhedbefiiijjjkjlkkgkijhjiijhehggeihhigefgkmmnnlkjdXF63.@^goljnlnoniihiigeghjknlifjfkklijhih——™–––˜”•’’“”•–š——–•“““•”˜˜––••––•–™™Ÿ¢£¤ ª ¨§¥¡žŸ›œ¦¥£¢¢¡›œ—š“‘‘•”•––™œ››“—“‘‘’”“‡‰‹‹Š‹‰‘ŠŠ‰ŽˆŠ†††‡Š–’‹ŒŒŒŽ‰‰‰‰‡†‡ˆˆ‰………„………ˆ‰ˆŠ…„„„„…‡ˆ‰ŠŒ‹‹ˆŒŠŠˆŒŠ‹‹ŒŠŠ‰ŽŠ‰ˆ‹…}ygYOIHJDEIKJIE@BHMQeˆ‘»ÃÊÌÓïôùýýýöðçÔ«¡ž²¹Ÿ‹’²¶¼œ™¦²¢”ŒŠxtsh`PF?=;<<;;=>FbÉïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûöðçįf<0--.-..----++--++**)('%#!! !#"$$#######""""###$#$$$$%$$$%&('&'5P]QNKLS_bggjfpjZYaRA6/.048CZ_`XUJ<0.-/4@GNNYNT>B>61.+(&###$+<WY][\]a]_^_adcbdiijgjeecdchefglkkhkiifhfkjjgfgkjllmnnjkknilkjilkhgjjrkmjonmjjjjjjjkjkmqkljqlkjkjjgjknjnlsoqljjjjjjighfdeiglklkmmmlpkjiljkklihhghhgjghhigeefhonrrrllhdK92/6Lipponmmnmnkplmiggjkolmlijigllpkjiih–––”–•’‘‘““•™—š™˜“Ž–““’•”—›—•”“–——š›œž ¢¢¡ ¡ ŸŸŸžœŸ ŸŸžžžžžŸš›˜˜–•–•““””’•›™›™—•’‘Ž‘‘’ŒŒˆŒŒ‹‰‰‰‰Š‡……………ˆŒŒŒŒ‹‡†………„„†…†‡‰Œ‰†††…†…„‡‰ˆ†…‚‚‚ƒ‡Š‹‹‹‹Š‰Š‰‰ˆŠ‰‰†ˆˆŒŠ‹ŠŒŒŠˆ‡ˆ‰‰‰ˆ†‚~ysgYNDDDDFKJJHDBGFHQh‚Œ–¨¶Ê½ÉïõùýþþûøçÁ§§£¢¤¤‰{”“„•¤¡¡’…~xsmh`XOF?=;=@?>=>GcÍïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûõðç¯b:/--.-,---,-+,/+)('%#"! ! !"""#$$%$$##########$$$&%%%$$$&*-/,-Fk[XTRU^]\eVgegiSOHC<70,/47BLSQNHB;29?@BGRNKJA:///,'&%$$#"$$,BVXYYZ[[\]]_`bbdggfggfddbddheggkjiggfhdfeggiggejjjihfgghihghfjhhhhgijlijikiiiiiiijjkkmmlikkmkkjkjjhijjjnklllijljjjkifeegijgihkklmmllkjjkiihhhgfhjhfffhfgeddhklmmllljeN;98=E[oopoonmnnlikklhhhjkjjmlkiiilkkjiiihš”––•’“•™™˜——–”“‘“˜”““”•————š“––—œ¢¢¢¢¢ £¢¥ ¤¡ ž§¢£œžššš™š¡¢¢™›˜œ•–—˜™™”’‘‘’‘’”މЉˆ†…†ˆ‰ŒŒ”“ŒŒŒ‹‡‰„ƒƒ‚ƒ‡…‰‡ŒŒ†‡‡‡††‡†ˆˆ†‡ƒ‚„ˆˆŽŽ‘ޒЉˆ‡Šˆˆˆ‰ŠŒ‹‹’Œˆˆˆ‰„ƒ€zwhXKBBDEIJLJJHFEBJXnƒ˜¦¶É¹ÁíõùüýþüúïÍÁ»¡˜•‡w~yry”–˜–’ƒwtqhe\VMF@=<>?>==?HdÐðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõðæÀ¨^9/-,-----.+,)(''$""!! ! !!"!!"""###$$$$#"####$$%%%&&%(&%%$%(4A73@Z\]ac`[VQ]hYf\WPD?=:761./36<ETRHA?9;LQUOMQRCE5-)%%%$%$###$'%.FVXZYYZ[[]]aaabegigngfefdhghhhjpjmggefghfgglgihkhgfeefeijjjjjiiijjjjjmknmmijihinikknntnnjmlonnkklnmnoonnnnmkjkkllmihghfjjommiqknnrlnmpmokkhmhfggggghhighfgdmnnmnkmkkS>7:BM[qqrqvqrmplkilllkkjlkijqmmjklpklihilg“‘Ž‘‘”–——˜—–••““”•—˜••˜–––”––—”—˜œž£©¥¢¢ Ÿ¡¢¥¡ ŸŸ Ÿšžž¢¢£œœœ›žžžž›™™–š’–”™•˜“””’‘’’ކ‡……„†‡ŠŠ‹ŠŒŽŒ‡‡‡‡‡ƒ…„ƒ‚ƒƒ†„†‡ˆ‡‡‡†‡‡…†‚‚ƒ…†‡……‡ŠŒŽŽŽŽŠŒˆ‡…ˆ†‰‹ŒŽŠ‰‰Š’’‹‡††„ƒƒ€€|yvfWLACEKKLJJJHGFFO\|ª³¹¿¸½êóùüýþýüøïâÍ¿³ —Œ‹sziefy‹Œ‹ƒ{sp`[UQKE@?@BEA??@HfÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõð໡[8/--.-,,-+*'%#" !!!""#"""###$$$%$$$##$$$$$$$$%%$&&()*+6BEDHUWpnnno_UZ\SLLKE@>:977531347:JUUGGGECQ\ZZH?9/+''#$#####$##$$%2LUVVVYX[Z\]^_abffhfggfcdeefhjjjkjhdheffffgffgihgefceeedigiijgjiijiikkmjkijijhhgmjllooolkikkooliklnmmnnmmkjgkkjdhggffeggjkkkmilkmlmllllllkjijiijggghgghfffffjiigmjmjS>:8DP_moqqovqqmojkhhhjjjjljjjlkjjlmomnhijmf‘’––—™ššŸ——–—•”•˜—›˜˜˜š™—–˜–•–˜šž¤¡¤¦©¢¥¡££¥¢¥¡§ žžŸžžž¤¤¥¤¥Ÿ¥Ÿ£££œ›š™˜™˜™”–“˜˜˜™™——”’“—–™‘ŽŠ‰ˆ‡†‡‹ŽŠ‹ŽŽ‡Ž…‡†††‡†„‚‡ƒŠƒ‡†‹‹‹Š‰ˆ†††‚ƒ…‡‰‹’‘‘‘‘ŽŠ‹‡‡ˆ‹‘ŒŽŠˆˆˆˆ–‘’ˆ†ƒƒ€€€|xxgXKCELKNNOLJIGFIWy¦ÄÂÀ¸¯ËêõûüþþýøôîäØÎ°˜Œ}wcZ_j~ŒŒ‚qn\UQOIC>?GMHDDBAHjÕðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîß·šW6.---,)('%$"! "!!!""#"""""##$$$$%$#%&$$$$$%$$%%$$%%&&*-7?AFTie`WXa_^\aiNB@BBA?;::7777789::<F][USSD<J_QGA<1*'%%&"$###$$%$%$#%6MSTTUZY][____eegfjhiihceegfiillligghfhghiighilfddeeeegghhiijhjjkkllmlmkmmliiijjmmnmpnolljkkqlkkqmnmnoppqkkilljfjgeddegjqmllnlnmrnmklmrmlmmjkjjhffghffjfffnilikhmkpYB44@Qbvnvttrvqupoljiijoopkmlliokjjqqsnohjkog‘‘’•™š™™™™˜˜˜–––—˜˜˜š›ššš™˜”––—™œžŸŸ¡¡¤££¡¢¢£¥£¢¢ Ÿžžœ žŸŸŸŸŸžšœš™™œ™›š›™™˜š””•””ššššœœœ››˜—••’‘ŽŽŽŠŠ‰‰Œ‹‰‹ŠŒŽŒ‹†………†††‡…„‚‚ƒŠ…††‡††‚„‡ƒ€€€‚ƒ‡ŠŽŽŽŒŒŽŽ‡Š‰‰ŠŒŠŠ‰ˆ††„…„ˆŠ‰‰ˆƒ€€‚‚€|~xwsfYODGLNPONLJIFHSq£©¯¸Èº²ÑñúüþþþùôóïçØÈ²•…zfc^gu‡ƒ{tkaUQMJFD=APPQRTNDJmÝñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúôíݹ’S4-+)'&$#! !!!!!""#""""##$$$%%%%&%&$$&%%%%&%&%&(+.14@OLSX][[\Z]fedLEA;7568656865569@<=>BJQ]d][P@;XOA70+''$%##"#####$$#%%%&8NMOSSVWYZ\\_`bdedeeebgdfgggiiiihfffgfffhigfhifddbeeddffhfgghhjgljkgmjjjkjiijkjjlmnkllnjjkkkkhjjjjllnoollkjgggggggddeeilllmnnklnmmlkkkkkmnjgihhgfdfhhhdbfggghhiimhZF;2?L^nmnooqqqqrpomlkklmllkjjkjihkjqpqmlilnng“”—›¡›ŸŸž™š™™¤œ¢œššŸ£››Ÿ™š™žž¥ ŸŸ ¢££¡¡£¤¤£¤££Ÿžž›© ¢Ÿžžž››œžŸœ¢œžž š˜™›››žœ›››¡ž¦ž˜˜••––“‘‘•’”Œ‹‰Š‹‹ŠŽ‹’Ž‹‰†ˆ…ˆŠˆ‡„ƒ„…†‹ŒŒ‡‰†‡‚…‚€‚‚ƒ‡Ž‘™’’Ž’ŽŒŠŠ‹Ž‹‰‰‡‡…„ƒ†…‰ˆ†……„ƒ‚‚‚ƒ…‚ƒ|~zywtj_NFGPORONMMIHNf‰ £®ÂÔ´¼ã÷úýþþ÷ðóõðêæßŶ”ydr‹ˆ€vldZSOJHFF=CMddghbLLpÜòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÛ¼‰M.(&$#"" !"!!"####"#$#%""#$%%%'&%%%%'$$%%'))('''(0:FTW[TMTo\UXs[dPE<40.--021/./23477<>=ERQ]\]^aZQ<:HF5*)%&'''$$$%$&$$###%$$'9MPNRSUVYY]\badbfeeeddfehiliihiheeigiffejgfgheddedhdcdhhhhiglkjimjkjnlrkkiijkmpjmlokkkljollkjjojjjpmqppklllgghlkpecefhnmmlommkklnlllokjjmjgflhhhhefhjeabmikhiiijq`O;6;I\nosnqoqpssspooppopplmlpmmihiomqqppqqrnmi—˜š›œžš˜›œŸ£¡›¡ žŸ¢™™™ŸœœœŸ¡ ¡££££ŸŸ¢ Ÿ¥ ¢—™•œš›œ¢ Ÿž¡¢£ŸŸŸžžžœššœœŸœœšššŸŸ œ—–••–—–’‘‘ŒŒŠŠ‹ˆ†‰ŠŠŠ‰ˆ‰†‡ƒ‚‚‚ƒ‚„…„…‡‰Šˆˆ„„€~…‡‰Œ’’’’ŒŽ’މ‰‡Š††„„‡†………„…„ƒ€‚‚ƒ|||||zyj\PDHLOPNNMJHIZy†‘§½Â¯®ÕðøüýþõìóúöòðìåΤ¡Çƺsz‡~qg\XROIGDA<BJ`lt{gQNrÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùóìÙ¼E(#!! !!"!! !"!!!""#########$#$$%'&&&&&%%%%%%%'.6=3/49:>N_WQYVSQm\PHOYH92-*''',0.,+*.2479:?BFLTUZOOPQSB45G6*,-,-5A0&$##""""""###$)=JPNSSTWXY[\```_aba_cdddihhiigfeeefeddefggghhceeeefdefihiefgggkijjkiiikkkhjmmmlijkokkkkjllifhikjjjlkljkhgghghhkihcehiklmmmmllkkklkkklkjhhdeflhheheeddbcdihjggfkmcRD8>FXmklnllmnnooomoqpponmlllmmmeikmnqqqpqmqnlh šœŸŸžž™œ¤¥¦£¡¡¡¡¡žŸž£¢¢œ ¤¡§¦¦§§¡¡¡¢žœ ¥ŸŸ £¡ œ£¢£¡ ¡¡¢¥¢¢¢¢£¤¦¦Ÿ£œœ¡žžžžžžžž¤ ¡›œ–—–š–™”“‘’ŽŒ‘Šˆ†ˆŠŠŠŠŠŠ‰‰€}ƒ„ˆˆ‰„†…†‡‹ˆ„…}ƒˆŒ‘•’“’’Ž‘‘’’“’““ŒŽˆˆ‡‹‡‰„…„‡††……††„„ƒˆƒ„…‚ƒ„€}{vj[NDGONOONLJKN`m€ ºÉ·Ÿ¹âñùûýøòõùøøôñìݪ´ÒâØÎr‚|mbXVQOJHB?;>H]m‡€vVQuÝôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòë׺w?%! !!"!!!!!"!#######$&%$#$$$$$%&&&&'&&&&%%%&%)>`L?DTQMSe^_WmQ<IpYM;861,&%$$%'++*)((-38=BCFMRP]\]KD@<72//.+,05;=HD6+%#%"#"!"##$#$+@JQNSSSUXX[[c_^_a``_dcceiiiiiffeeegecdhghgjgfdihlehhjjjjjfffiglkkjnjkkkkkkllrmkkpmnkqkjkljfegilllmmklmnhggjhjilgdbfimllmomnlqlnkkkrlmkmhjbcglijihfkdfeeelijgghlf^H=>HWlmnkqlkmonmmmnsssppnmlmmnmlmpnppsqrqsrrnkh—˜Ÿ ŸžžŸ¢¤¥¥£¥£¡¢œœœœ›˜š™›ž¢ œœ›žœžžŸ¡¡ ¡¡ žŸŸ Ÿ ¡¢££¤¢ ¡¡ œœ›¢œœœ›œŸžžžžžžžž›œ••””•˜’ŒŒ‹ŠŠˆ†‡‡‡Š†„ƒƒ‚~‚ƒ†††……ƒ„„„…‡‡‡„„„Š’“’‘”‘’’”—“ŽŒŒŒˆ„„……‡…ƒƒ„……†‡†ƒ…ƒ‚‚‚„„ƒ‚‚‚ƒƒ„|yvi[PDHOOQQPLJLPZf‰´»Â«š¼éöúüú÷øøûý÷òéà®´ÛÞãÕª’„|i_WSONID?=;=DThwyYRxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøòëÕ¼q<#!!!!"""$"!""#&%%%$$$%$$#$$$%%&&'&&&%%%%%$$$$-F]X[]Tt[__]TLML97?F7*)('%%$$$%'((('()2<GQNLMSQNLID>60-+*)+*26>HFEEE;1&"""""""#!"#%.BIMNPPSTUVZZ[\]__^^^dbcdeeefecedfeffeeffffgfedhggehghhhhgggfhgiikkkjkjkjkklllikkmmnkkkllmhfehilklllklmkhhhihhigddcimmjljllnlmjnlllmlljiic_dfhgggiddcdeeehggghhj]O@ADUejklkmllnnfhjmnpppppnnmlinlllnmqppoqorsrmlh˜šŸ¡ ¤¡¥¤©©¨¤££¤£¥žœ™˜’“•›¢ ¡œž ¤¢¢¤¥¤©¤¨¢¡¡¡Ÿ¤ Ÿ ¡§¢¢ ¥ Ÿ¢ŸŸ¤£¢Ÿ¢¢¢›œž£¢¢¡¡ŸŸŸŸšœ—–•”•˜‘ŽŽŒ‹‹ˆ‰Ž†Š…„ƒƒƒƒ‚ƒ…ŒˆŠ††ƒƒ„Іˆˆˆƒƒ†‹•“™–•“”’’’”––“‹‹‹†‚ƒ„ƒƒ‚…‰†††‰ƒ„‚ƒƒˆ‚„…‡†Œ‹Š‰‡…ˆ„{zk]NEHPOPOOLKLO[u–¯¸¿›Ìïöûúù÷ôøýú÷îÚ¾¶ÊÕÝßÞ×´Žr[SOONFB><;<AL^o€~ZSxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷ðéÓ¾m9#""""##"######$$$$$$%&%$%&%%&(&(''&&%%%%%%$$$0Qv]j[WbYaicVKW=-,-,(%$$$$%$$$$%&&'''*<I]RdYVSRCA82-)''*,-/3BGPLDDUFB.'!#"!!!!"!""&2CJNORPSSTVYZ[Z]]]\\^cbddeeffedjgfffffffimijffeihhgfhfhmllgigkhhjpkljmnmkomlkmjkjmnnnnmpmlhfgiillrlllpjjefhkkkhgfegppolllmlnmonomooplmjke_blhjklgjeecefifigggmhiVGCFRffjjnmmklnnbbippqrrpsoqoqlnnnnomrpppqoqrroljš››œŸžžž¡¤ ›¥¢£¦¤Ÿ›—•”“‘‹ŒŠ–—¤¡¡Ÿž¡¡¡£¢¢¢££¢ ž›š™š›£ž ŸŸŸ¡ ¡žŸž¡ŸŸ¡Ÿžžžž›———˜™™™™š›ž™˜—˜™”’”ŽŽŽŽŒŒ‹‰ˆ‡…ƒ‚„‡†…†ˆ‹‹‹…††…‚ƒ„…„„‚‡…‰Ž’•’”“’’”ŽŒŒ‰‡†„‚|~€‚ƒ…‡‡…………ƒƒƒ„„„ƒ„…‡ˆ‹ˆ‰ˆ†„…„‚€}xj[PEIOOOPNLJMTh†›®«¢ ØóùúûöðöýüüñßÎÈÄûÕÞåݳ†dOLJHDA><;<>HVm{~[RxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïçѽh8$"""""##$####$$%&%$%%%%%%%&&&&&'''&&%%%%%%%$%3Ye_bWVo]igq^G:-)%&&%%$#$$$##$&'+-+&(-AMMNSXJ@80,('&(%,149;<HWQU=6760)$!! !! "!!!"(8FIJKMMPSTXWVYZZZ[[\^cbbbcccceefgggghfehkkigefdjihfghgfffhggfkijkkkliiiiionllkkjjmmnlllkkliiiijmlmiiiiefcghiigeeehlkjjhmkllnkmlmlnjjjkifbdffgikjdfedcegfdffghgbZLJHSaefihnlmjih]Sbkoppnrprnnnnlmmnnnmmmmlnopqronnœž¥ž£¡Ÿ›¤¢¥¦¦™“‘Œ’’’ŽŽ˜ª©¨žŸ ¡¡§§§¢££¢Ÿšš˜™›ž£ ¡ Ÿ¡¢££¥ŸŸŸŸ š”’’’’—•——˜—˜¢œ ™˜—•”““–ŒŒŒŽ’Š‹‡…†€ƒŒŒ‘ˆ‹Œ‹Š„„„‰†…†ƒ„ˆ‘““—’’“”‘–“•”™““’“ŽŽ‹Œ‰‡„……ƒ~}{}‚ˆ‰‰‡‹ˆˆ……„†„„„‡ˆŒ‹Š‰ŠŠŠ†…„…ƒ†ƒ„…†{k^QGIPOQOOLMYczŒž°¥Ž—Ãíöùû÷òóôùýôëäÝÆÃÄÏÛßÜÛ©nOIHGDA=;;:=DRl}}[QyÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïæÍ»d7%#""#$#"##$$$%%%%'%&%%&'&''&&''&&&&'''%%&'&(&6`seqVUTUiefe?-(&$%$$###$#"#%-75787('.SRSEDA=1($"#$)-17>E?>FPRQ?/+*'%#! " !!!)<HIKKOMPRUVVUYYXXY[^]cbbbdbbeghggghkhgfkkkigfggjjkhiilghfiffgkkmjjkliiijlonmllkjjonnllkjjllljqlmlnjiiifffihihfefhllkkljmlmmnkmlpknnnimhfekijiihhffedfjgfdggjhh]TNOVbejihglmmii]SXlquutsrrrssssnpopnpnnlklponoroomœžž ˜ž›Ÿ¡¢¡Ÿ›“’’““–—•‘’’™›šžŸžžžžžž£ Ÿžœ››››Ÿ¢š™—˜™¡¡ ¤œ››œœœ˜——–•—™—›˜œ™—š™œœš˜••””“”ŽŒ‹‹‹Œ‹‹‡†‡…‚ƒƒ‡‹‰†ŒŽŠ†…‚……………„‰Ž‘•”“”’’‘’’‘Œ‹ŠŠ‰ˆ†„‚€}}{€†ˆˆˆ†ˆˆˆ„„„„„„…‡ˆ‰‰ˆˆ‡†…„„ƒƒƒ„„ƒƒ…~{wkaTGIMOQONPhpz‹”¡¦Šw™ãð÷ûøõðëõÿøðæÔµ´ÄÕÙÙÚÙÕyQLGEC@=;:;=ENb}tZOwÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöîäʹ`5%$###$%%&$$%&%%%%&&&&&'&''('&&&&&&&&&&%%'&&&(6f`WSECEOhZTB/)%&#$$#!##$$&*29@EIO8+*.;;72.*'%$"&(2BCF=WMC>QB:2+'"!!! +?IIIILMQPRSRQSTVWZ\]]dbbbcadihihgijlgggjjkhgghhjjiiihhfgfgghhkkkijjjiiilnnnmkkgjjllnokgiimmljollllhiihggfhgfdeejmmmllkilllknjklkkkkmilgggggjjighfffghgggcfffc`ZVRZbdgghhflmkecT]hnsvqrppqrppqsnoopnplllllonnnponm ¢¢¡ Ÿ Ÿ££¤Ÿ›šœžŸžžœ›¢š™›Ÿª¢Ÿ¢ž¤¡¦œœ£››™™——––™™œž¥¤¤›œ›žžž˜›œœœ™˜—˜œ™šœš›Ÿš›—š—˜’”ŽŽŒ‹ŠŒŒŒ‹Š‹‡Š„ƒ„„†‰‰ŽŠ‡‡†‚ƒ‚†„„†‰Œ““—”••˜”–‘“ŽŒŽ‘‹Š‰‡ˆ‰……€€~~€…‡Š‡ˆ‰‰ˆˆ…„†ˆˆ‰‰ŠŒ‹‹ˆˆ‰ŒŠŠŠ‹ƒ„…ˆ„ƒ€~{zpeRFHMNOOTfƒ†˜¢“ksãòùùùôîöýúöìϺ©¼ØÕÛÚÚÙ†p]QGD@=;:;?FLXfpVNqØðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõíãÉ·]3%%#$$$$%%$%%%&&%&&&&&%&&'(''&'&'&(&''&&&&%%&(6OWGC817LON=/(%$&$$###$&(,1:JIZNMG;2)*60)&#"!"%'-6?IKFALW@?93,$" " .BIHIJLMQORQOOPRUX_]b`ebddccihmllnommimgnkjjkhijmmnjkiigghklmkkjplpkiijkpnuoqkkjjkolnmkikiqnqmomlllhkjjhighfecfjsnponmnlllllnirkiikklijiogiinllgifefliihmdfff`_ZY]hgigjjjkllje^_nostƒqwvvqrszttsrpppqmnllmommntqun››œ™™˜œžžœšš››Ÿœ›› ›š››œ—š—™š œžŸžœ››š—”–—˜˜—••–———————–”“””””•–œ™™˜›œœ›š›œš———˜–“ŒŒ‹‹Š‰‡‡‡ˆ‡†……„ƒ€ƒ†…€~‚‚‚„„‰‘‘”••”••—’’ŒŠŒ‹‹‹Š‡‡„…†ƒ~|‚‚‚‚ƒ„†ˆŠ‡‡‡†ƒƒƒ†‡Š‹Œ‘Љˆ‰ŠŠŠŠ†Š„……‡„„‚€€}wl_SGIMNOTe€Œš¥§ˆn\f€¹ëöúý÷ñ÷üüüòÞθ«¸ËÜÚØÙµ£œŒtNF>=<>BHKNPMIHjÓìùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõíâȳZ3&$#$&&%%&&&%%&%&('&&'&&&&('''&&&%&&'&&%&%%%&(1A:0-+**182+(%%%%%%#&(,2:AEGKMLLHC99'%%$#!""*/37<LMWGDDB=9.'%"! !1CIHIJKKMOMIKMRUVX^]___`bacciimklmmmmhihmkkkjgklnmojjjjhiilmlllkkjjhhgjlooppmijjlmmlniihihklllonkkkgjijggghddchnmnnoppomlkllljlhiiihhgkihfhilmhcdefhhhhhhdedc`\U\dfffejilniggghimortrpvtvrstttsqqponnmnllmolmnommmœ™š˜™šž£žž››£¡§šš›››™˜›››žŸœšœžžœœœœŸžœšœž™•–—˜™™˜–”••——–—””•–“••œ–™–œ Ÿ žœ››œ™˜———š““’Œ‰ˆ‡††Š…„……„‚€‚‚‚~}‚„…‡Š’š”—–•–––˜’–ŽŽ‹ŒŒŒ‹Œ‰ˆˆ‰„„€|‚†Žˆ‰‰ŠŠ‹ˆˆ††…‰ˆŽŒ”””‘‹ŠŠŒ’Љ‰‡†‡„…‚ƒƒ…|vneTHHKMU`yŠ«ª©‡hZVj’Ðò÷ûù÷õôùýöîÞ˳§¾ÙÛÜÞßáâáÌŠWG==?DNOIFA?Cd¿ÝñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõìâÄ®W2&%%%&&%(&'&%%&&&''&&&('((((((('&&'''&&''&(&&')**(&&&&''&%%$&.3+()-4>AQR[WUQKIF71,%$$#! #-:BMKGNTNF6-)&$! #6EJIJJLKKKKHHNTUVX^]``cccbfcijnkpoqnmjllljoonoposmojnkmklioosnolnjkhhimotonmjjllnmmmmjijllllnlpmkjjjjjjiogiddejmoooosrunlkmllkojkllijhligfgjkigcdffhjjkijfhded[Ycchhhgjkpjdflmtrqqrrqpuuvtttvvvrvrqnonnonmplkmolmmš—˜—˜›œ›ššœ››Ÿ ž˜š’–“›•š ¡ ž›žžž ¡Ÿ›ž Ÿžž›š™™š›žœ›—––˜–”“”—•””–˜••–™šššœ›œ››————“–•“’‹ŠŠ‰ˆˆƒ‚ƒƒƒ„†ƒ‚‚|||||~‚…‡‹ŽŽ‘‘‘‘Ž‰Š‹Ž‘Šˆ…ƒ€€€‚ƒ†‰‹Œˆˆ‡‡†‡…‡‡‡‡Š‹ŒŒŽŒ‘“‹‹ŒŒ‘Œ‹Š‰ˆ††ƒƒ‚ƒƒ|yqgWIIMR[j„¹¢‹s[VWw¯Ýôùûüôìõþû÷êÒ¼´ªÇÚßäèèèëëÝ“WF>@FO]NC?<C_µÒèõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøôëÛÁ¨U0&%$%$%%&&&&&%&'''&&&&''''''(('''''%'&&&&&&&&'(('&%$$$%&$##$-=969=AF@EQVJ?<:5/*'+*29.*&#*AETINJNGA3('%$"!!%;GGIJJKKKJIHLPRSXYZ[]^`abbbciillnmonkilkkknnoooonmnjjijjjkponlpjjgkiijmonkokkkkklmniklllkilllklmkhhfhhjiigfdghikmllmppomkjlmmkkjjiigjhjhfehkgcc`figeeegfggfbee_]_`cdfgjmmehklmspppqqrnqquuutttwqsrqoooonnkmmmlollk™™™˜˜››¤š™šžŸ›™š›ššœž £¤©¤¤¡¢¢¢ £££¢£¤¥¡¤ žž› ›žœŸ š™˜˜˜˜–•••–•”––œ••–›š™™œžŸšŸ—–••‘Ž‘“Š‹ˆ‰€‚‚‚„‡ƒˆ{|yx{€‚†‹Œ‘ޓޓ‘”““‘Ž‹‰Š‹”Žˆ†ƒ‚€ƒ„†Œ‹‘Œ‰‰ˆ‡†‡‡‰‹ŒŽ‘“ŽŽ”’’‘ŽŽŒ‹‡†…ˆ…ƒƒƒƒ‚ƒƒvjVIIMT]pˆ›œ˜lVT]‰Ìî÷ùüøõùýüûóêäÙËÂÆÊÙåâßáãâÚˆSHEMRZVC=<C]©ÓåóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÒÁ¢R/&%%%%&&&&'&&&&'''&&&&(('()')(&''''&'&&&'''&&&'%%%%$$$$###$+5FJJHOSB?FTFA3.*&#"%-7ED::@'1IKLLIG9-($#%'&$" !)?HHJMJKKJIIKQPRSYZ[[^]`afejiijooompljiklonqosptqpmnnmjjlssqnomqjjijknmrooonnonpkkmoilmqnqlnmnkllkiifjhpjjfeglmopqnqmqpnlklpmmllkmjjjjjlhggmihbdegijegfhgffedhde`cccbfgkkkhonposqwrttsqvtzyxvzuxwwsqpssronmpnpppmll™˜™˜——›››•™žž ŸŸŸŸ¡¡¢¤©¦¤¢ ¡¥ žžŸ¡¥ ŸŸŸž››š™™™š™˜—–•’‘ŽŒ‹Œ”‘‘‘’’’••–—››š––’“”‘ŽŒŒ‰ˆ†|€‚ƒƒƒ‚‚~}{{xz|ƒˆŠŽ‘ŽŽŽ“”Œ‹ŠŠŠŠ‰‡…„†‚}€€ƒ…‡ˆŠ‹‹ˆ‡††ˆˆ‡ˆˆŒŽ‹ŽŽŒŽ‘’’ŽŒ‰ŒŒ‰††…ˆˆ†„†††…ƒ~rfVIJNT\w‘œ¯£}ZTXb–ãñøüüýýüýþûöñêâÊ®·ËàÙÊ¿ËÞàÀˆgb]^aWF>:B[ ÖãñúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÐÁ™N.'&&%%&&&&&&&&&'''&&&''&(*(')('''('''&&&&&&%%$$$$%$$#"#$).15C\RRHZL>BE<1+%$"#"&)9KNXJG;+1NA;61,(&!##0F?;-# #-CIIJJKIHHGIKOPSSVXY[\\`bccefhkmllmljjklmoooopprpnjihkklmqspmmlkiiiklmmmmmkllmmqkkkkilmonpoolkijijjifjihgffgimmnmnnrknnnjlllkkklkkkjiihhghhhedbefghiddcbbbbecb`^[[[\^beefhhkmnoqnrqpnrprrrqsrqpooprqnomrponomnmnlmm™šš—–š¢ž£ž¦ ¢ ¡¤¦¦§¤ ¡¢¡ ŸŸ¡¡ ¡ ŸŸš™™šš™——˜—”•‘“ދЋŒ“ŒŽ’•””–Ÿššš›™™“”ŽŽŽ’‘‘ŒŠ‡ƒ~€Š‚†‚‚€}|}~}~„‹‹’”‘’‘’”‹ŠŠŠŒ‹ˆˆ†„…†ƒ‚‚„…ЉŒŒ†„„…‰‰‹“““ŽŽŽ‘‘“’’ŽŒ‹‰‰‡‡‰†ˆ‡Š‹ŠŠ‹†…{qfTHINTm“ °ÆŒ`TRWp¯äòúûýú÷øùùúõðëàÀ®¼ØÐÿ»ÛÞ϶²“}m`N@?CZ—»àîõúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷òéοK.((&&&(((&%&%&&'''(')&''((((((('(('''(*'&&&&$$$$#$$$###-<@ADIYVQJIF4,+)&! !$+/8IRUWVH7,131+%$##$#%/:FE?8)" ! %2FHKIIIIGGHKKOPSSUVXZ\\`addggilnmmmononssspqoqrwoljlkoopoqqolmlklljlmsssllllnrprqqjkipnrqtrtlkiijkjmjpjighgjlpmonnornsmooommklkomqjkjmhkijihddclhjiiccaa`_`dcd^ZYYZ]^abcejillopvptrqmqptqprtqnmkklnrrrnrqoosnpnrqqm–—˜—™›š™š›œœš˜•”“•—•Œ‹‰ŒŽŽŽ‹‰ˆ†ˆ‰‰‰Š‹ŽŽ’”’‘’“••”’’’‘ŽŽ‹‹‹ŠŠŠŠŒ”””‘“•”‘ŽŽŽŒŒŠ‡‡ˆ†…‚€€€‚‚‚€~|}}~€†‹Ž‘Ž‘ŽŽŒŠŠ‰‰‰ˆ„„ƒƒƒƒ‚„†‡ˆ‹‹‡†………ˆŠŒ‘ŽŽŽŽ“’‘”ŒŒ‰‰ˆ‰‡ˆ‰‰‡‰ˆ‰ŠŠ‡‡†……~wl`UKMOd–¡¯±žkURRcºì÷úý÷òóôøýøóñ騫 ²ÁÀÁÉØÙÛÚàÙÓ¤ygVJHPZŽ«Þëñ÷üþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷òè˼‰G.('&&&''(&&&&&&&'((''&'((())**++++****))(((('&%%$$*/+&,;AVP]NTQO=2+&$#"!""(-4<BFO[SNKI0$&(&!"#(,/17DISLD5," '7FHIIGFFEGIKKNOQRUUXY\\`adefgjmljjkjhmmqppnonqqrlkhklooonqpplmmjijknoronklomnopolkjjjnoqpspokkhiljjlkjhgfgdjnmlllmonllkolnooijinjigiihhgghgfbcdlggheba_``__cbc]ZWYZ^_aaceffhjklllhenmmnpppimpnlkeeegiijkkklkkpnopon—™š—œœœœ›˜”‘Œ‡‰‹„|upliikmnnnmlheginponlknquz‚‰ŒŽ‘”””‘‘’“ŒŽ“‹‰ˆˆŠ‹‹ŒŽ‘’’‘Œ‘Œ”””Œ‰‰ˆˆ‡ƒ‚ƒ‚ƒ‚‚‚€~~~}~ƒ‡Ž‘ŽŽŽ“ŽŒ‘ŒŒ‹Œ‹‰‰Š†ƒ~€„ƒ…„„„„…ˆ‡‹‹Ž‰†‡‰‹’’“’–‘”ޔޒ•’’“—”“’”ŒŠ‰ˆ‹ŒŒŒŽ‹Š‰ˆˆˆˆ„ƒyumfZONZ~¡¦¡yZRRVk’Îñ÷ûù÷õôøüú÷ôìßž˜¨¿ÁÓÔØÜàæêÒÈ€taRW_k‡°ÞåäìóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöñèȾ‚E.('(('(''''&&&(((()''''()**.02356531/////.-,+*)+,.3>G*0CGNPPL>50,'"!! %(-4?TJXWWVQIB6)"!! !*7?NIHPXYYB5*#! *?FHHFEFFGJJMNQPQTVVXX[^dbcfghkmljjjjklnvpsnpotuulmnpppprrwppppkhirntptonmnmmowqokklomsoupsonlpjjkkkmiheefggklmlmlmnomkkomnoplmlmiigjhggiijgedefkgfecbdad^^`cab]]ZY]bbdabeifgilhe_[dlnuqupqginqljc\^acdeihlklkqpppnn•›š—–“Іytpmjgfeedcbabcdfhgfk[^TZVdcccbab^_behmtz…‡ŠŽŒ‰Šˆ‡††ƒ‚ƒ‚„†ˆ‡†ƒ‚„†‡‰‹Œ‹Š‰ˆ‡…ƒ‚~|||‚‚€~}{}}ˆŽŽŽŠŽŒ‹‹ˆˆˆ‰Š†‚€~‚‚„„„†††‡‡‰‰‰ŠŒŠ‰Š‹‘ŽŽ”Ž‘””’”••–’ŒŒŠ‰‰ŠŒŒŒŒŠ‰‰‰‰‰‡ƒƒ}zvqo\LTh}ˆ“¤{`ROSXw©Úôúûüøôøûûû÷ñäĦ——¬ÅÊÑÕÝãçßЬƒvqrurrˆÀØÎÃÊéõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöðçÇÀ{B-''''&((('&&&'')(((''')*.2469@=<<<==<998521/3<::=?DK@,.WCIA;4,)$####&*-3;GGHPFKPIB7,)'# !%)/8AKLAIWIA<4+#! !.DEHGFFGHIJKMNQPRUVWXX]_`adfijkmkhjjkloppppmonmnnmmmmmoprtsoomljjjmmsponpqolmomlljlnmlrooonlmkjiijjihfecfgijklmllknmoomlolnqnmmmniiggffgjf\Xfeefjggcccd`]Waec`_\]\\_bcd`beedc`]VSQ\hlntopmjfikkkic_]^_abedliihlmnpol“‹ƒ|vqkeb_`abaa_bdeeeeimoptvmifeccbaaba``b^^^^aaejpw~ŠŒ““”Œ‹‰‹‹‹‰‡~|z|~€}~€ƒ‡ˆ‰‡……„……}}}~}€~||~ƒ‰Œ”’—”ŒŒŒŒŒŒŒ‹ˆ‰ˆ‡…ƒ~‚‚„ƒ„†Š‰‰‰‰‹Ž’‘’‘‘‘‘‘”””—™˜˜—–‘Ž‹‹‘‘’”–•”””’’ŠŠŽ‰‡„ƒ‚‚‚‚wlZNWcoxxwZROPR^‡Èæöøûúùúüùöö÷îÚ·Ÿœ¢¨¦µÓÛâå͹ ‰€‰”’‹w†·âÀ«ÍéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïçÇÀw@-''''&)))'(&&''()()''),5@BCA?AB==?D><:><:::9;CHIZYYL:*,;C4)'%" !%)07;>BLPYTRAC:1+%"! !*7<@DIJPHOB91+'! ! ! !!"1EGGHHHHKJLLNNQQRVXZ\\_``bfjpppkjjmmprtuupqrspropnqrrrsqttsoqmlllnrrspqntqsmlkkkmlpllmroqmllpkjhlijhheffijoksmnmommlonnmpnronnxmnoonnijilZQZfhjjjggdhce`[[ceb__]`^__dddcccc_]ZSOMTdgssuoqjhellkjjdc^^^abgflijjmlkoqmqieba``_^^`deeffghkomlnpryu}yzqqqrheeedcdedcbaa`^\abjp‚‡ŒŽ‹‰…„‚~{yvsqrstuuutrrrsvz|„„ƒ„‚…}|z|z|x~z€}€„Š’’’‘‹ŒŒŽŽ‹Š‰‰ˆˆ…„‚„|~~‚…„€€„„‡ˆ‹‹Š‰‹Š‹ŒŽŽ‘ŽŽ’‹Ž‘“”—›™–˜˜–Š‘‘’••””——•’’‘’Œ‰ŠŠ‰‡‡ˆŠŒ†}tl]PUX_d^SPMPPWaÙîöùûýýþ÷ðöüòäÏÀ¨¤Ÿ®ØÝÝͳ¬Œ‰˜½Ñ”h{ª°³®ßêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïæÆ¾q=,'''''''(''&&'((((((*-8DJOGCCCB>?@?<;<@@ADFMQVPIMNLL3&'('$$"##%'+0<MI[NMJMD:50+'$!! "$'*+8C[NNDDA>6,'"! "$8EGFGGHHKKKLONPQTWZ]]]`bdehlmkkijjmnrrsrqooproonnnpppoopqrsmmkmnoqrnpnnoqmmllmlllklkmnonnjmlkghhkijdddfgjlmknmomommmmmnlmmmmmnnmpqmljhijXIWijkjfgffba^_\]adg`\[[[\__``___`^YYWLDQ_adfhkkhdddca_^^_\Z[[\^cehijkkdWPjk[[\]``ddbbbcefoitnrpupxrsstvxurrqmjhjgpomgkfihh_\\^afs}„‡Š‹ˆ€tpliggiijlnnnnmmnnsvy~‚††‡„„|~}~}}||}€„ˆ…†‡ˆˆŒ’Ž“’ЉЉˆ„…‚„‚ƒ……‡…ŽŒ‹ŒŒ‘ŽŽ’Ž‘’’’‹“˜——™–›˜—’‘•˜—›š›™™™ž™›–˜”““’‹ŠŒ‘’“”–ŽŽ‚€yr_SPPRSOMNOOPVi£Þï÷úüýþ÷ðó÷õòíá³³¦’´ÏÙãàߪ“°×Û¡mf…¥«¨ÌäîöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôíàÆ¹j9+(''&'))(''&(()()((*.8HQWQKHGDABBA>>>FHJQPOQRV^HMIF:,# !!!!"$(.8BGMNJSVKG:5.'$!! !#',3=+/@GNSE;40,%" ! !! !"'?EHFFGHIKLOOPPRSUX\\^_bdghjknkjknkonystppnnoqoonnotpoooqrrsnooooqrsnrstqwmnkllmmnlllppttvjmkjgihljjehggjmnqppnononqnnnnlmkklmmppuomkhhm]LMblnllhiefa`abZ_bgb]YXY^\d__]]]a`\WZSGI]^bbcdheecda`]YYZYYWUWZZacikoprfEFalXY[^^`aaa_acdegijijjihjljjlmmmkhhgfefgghigjfhhfddeimu€€‚‚‚~zvqkc`_bbddfgijihjhlmprvx€€†‡‡‡ƒ|}}~}~~€ƒ…†Šˆ††‡Š‹Ž‘’’‘ŽŽŽ‰‡„„‚‚ƒƒ‚‚„ƒƒ„ƒ…‰‹ŠŒ‹‹‹‘‘‘ŽŒŒŽŒ’Œ‡ŒŽ’”——••–—›˜—•–ššš™™šš››ž˜˜——–—˜“‘‘’“•”…ƒ€yoaROLMMMPOMNP]tåôøûüý÷ðñó÷üôêÒÍØÑÁŸ’Ÿ¯ÄÍÝÉÆÈÝÙ®ukoz€’²Ýàï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþýüúóìÜÆ²c5+)'&&&)((''&&'(((((+/@Ra[TPLKGCCEGFFLZURS^OFJND61-+)%! $+3<>LNWMTFLE>3(%#"!""&*2=?N4)2[C>;8-$" #+AEFGGGJLLMPPPQSUXZ\\_`ceghijojklnjmnrqqnpmnnpmmmnrqnmknrqpnmnmnnpqpnqrrrvnlkmmnkngmmponlkiiiighhllhcggikmlllnmmlonmlnnnkjhllmnppqmnihggQSXemlkjhjbcaaa[V]_\UXVXXZ[\\ZYYZ]_[SSONL[[^__`bbcde_SHB=>?EMKKQSY^ekooqdVOioYZ[[[]bafedbbcccdeeedehhhfdfihfcafdccegijjkmosvy~‚†ŠŽ€ƒ}wrolie``baabcbcegiiiknqptvy{€†‡ˆ‚€‚………„„„ŠŠˆ‰Š‹Ž”•–••”•’ŽŒŒ‹‡†‚‚‚ƒƒ†ƒƒƒƒ…ˆ‰Š‰‹ŽŒ‹‘’˜’•ŽŠ‹‘‘Ž–“™•—–”•˜˜›šœ——™£Ÿ¢ž¡œžš™˜™—š–”•––•”™”–•“‘‘މ‡}wcTMJJMMNMMNSb¾ëôùûýú÷õóõ÷ôñîëé⺤‰†œ²¼Üµ±ËÜÙ¼ˆkk‰}£Ûãï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóëÚǪ^3*(*'&')('''(*()('()+/E_abWTPQLGMRSTUUUnUMPTBA81+%#"! !#" !)7?>GPXOE>6/+'$" !"%/?DFGHN1*5H>/%#! ! $0AFFGHILLNOTRRV]]\\^`ecgflkmjonnnojrrqppoqosotllmnoqnmklnqnmmpqppqonorrtsvnnmpnskomooonlkjjijmkjhlifefillnkkjmlllolkkpopjjhmnnnpprnojkg_YZdpllmnijedbc_Z\e^ZXZYYX[\`ZXYYX\[ZRSNJPZZ`_^^ccidhVF90+)+.39AOQSYcitpqfeappohfeb``aa`aba``abcddeeefffffghfdgjnqtx|~~~ƒ‡Š‹Š€ƒwojfcaa``__`bbbaccceilmmnqqruxy{~€†‡‰ˆ„ƒ††„ƒ„†Š‰‰ŠŽŽŽ‘“”––“ŽŽŽŠ‰‡†‚‚€ƒ„ƒ‚‚‚‚‚‚‚…†‡ˆˆ‰Œ‹Ž‘’‘‘“Œ‰‡‹ŽŽ‘•—–•••”—–•–˜™™™š–˜™Ÿœ™ž››™™—˜–˜š™˜˜—™””‘”””’‘ŽŠƒ}wdSMHJLKKLMORiÇï÷úüýþøóóóôôöøñã·œŠ‹ŽŽ‘–“£ÌÑ×¾žvatƒ|–Íçï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúòëØÄ£X2*''''(('((((((((()+,8Kbs_YSQSTUY`iijLAOVAF=40)%""""#)-,*""0KA?ISJC5-)##"""#%0:?EGNKEFJ+$2J0"! &6DHGGHIKKNPQQUX\\]^adddeeggijjjmjokrppooopoponnnlopqmljllpkmnomlmnmppqqrrpmnnnnnjllopnmmkigjkkkjhgffgikkgjikjnkmlljkkllmjkhjjnmmmmnnjjfb]enmkkhnijfecb]`dc^ZWXXXX\_XSUXWUXWZQRMOSVXYZ]^^^^^ZJ:+*((''(.3AQSV_flnohgfhjŠˆ†ƒ€{vvvqnonnortuwz|zyzz{}€‚‚‚‡Œ’—‰‘‘’‰•“’މzslhd`]\\\]^^_aabbdefhlmplzoppprvvx|…ˆ‹‰‡„††‡„„…‡ŠŽ‘”–••‘‘‘“˜”—””ŒŒ‹†„‚ƒ…‰‰‹ƒ„‚ƒƒ…ˆˆ‰ŠŒ––—”™”–‘’‰†Š‘Ž’•››Ÿ™™•™ššœ™š˜žœœ›ž¡£œ››žœ£™œ›š¡ŸžžŸ™šš˜š˜˜”•ŒˆzeSKHHKJKKLNVt¢×ó÷ûüþûù÷õôòõøñã½›“‘††ƒ‚‹©Ë̽¦ƒus||’½åî÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñéÕÀœR0*''''(((())(+(*(),47@Uqr^WTXghhkmc]T?=@8,++-)&"!"#&/6;>*$#7PFIM>1*$" "%.39FIIKKLXC91%"/*$ ! !);GIIHIIKLNPUUZ[``cdefffijjikkkjmnonrpqosqsttooosstqpmmklmpprpsopnnouttt{rnloppmnjlnvnmnokjinmokliedhkulkkmklkolnmllmllkljkijlroqmnnrkmgghqonjjjnijfhfhhied][[\[[[\[TPQUVUYXXRQRUTVVXY]]^^]]]G0(())((&&+6GVY]dlmnkigli‡‰ƒ„„ƒ†‰ˆˆ‰ˆ‰ˆ‰‹ŒŒ‰‡ˆˆˆˆˆˆ‹ŽŽ‡‹‰‡†ƒ‚€~zvspmid`]\[ZYZ[\\^_`aaaaacefnloolkkopqrvw|…‹‰…‚‚‚‚„„†ˆ””•”””““‘“•”“’‹‹‹ˆ„„ƒƒƒ…†††…ƒ‚‚ƒ„…‡ŠŠŠŠ’“–“’’‘‰‹Œ‹“˜–•š˜šš™•˜––“›™™™Ÿœœ›Ÿœ››œœšš›ž¡¡¡žžŸš››šš›šš˜—•“‘ˆ€weUNFGIKNLLRY}¸àôùûþþþû÷óïôøòçÖµŸ £˜ˆƒ†Ž¬ÏÄÂz{„‰“ÀãíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùñèÓ”O/*'')(()*))))))))*/:BOaroZSY^inif_[KJ4(((&%,356.+058GMT7,($/R;1-*'""!"$+06DHKJXK]NB:3,$" !"! ! ",@GHHHIILNORWY[]abcegghhiijikkklnnnnnoonpqqkspponnqponnknoropopoooooooutsmnjpqnmmknookmmlkjhkkjhhifejlpmlkkkkjkkkjmlllljkjjjloonmjnlkjjilnolnhjiiiifgfgfga_[\]\\[Z\\RHTVVTXXXRRRSSRRVWVSSSTTSG8+)&&'&)''5@[\`ehhijieedŠƒ€‡‡‡‰Šˆ‰‡‘Š‘“‘ŽŽŠ’’’’–‘“Šƒ|zwqmmlfcba`^]Z\]ZWXY\^]bbibfdeacegnkooprsuwxz||‚„ŠŠ‡„€‚ƒƒ„„‡—”Ÿ™œ——˜˜™››š””“—ŽŒŒ‹‰‡„††‰„‰‰‰……„„„‹‡‰ŠŽŒ‘”™•—“”Ž‹ŽŒ‘’“ŒŒ–˜˜˜›š™˜›š›™Ÿ—–—œ›œœ £¢¢¡¢¡¥ŸŸ›™š›ž£¢ª¡Ÿžžž ¡§›œ™˜˜˜™“’ŒŠyhYKEGLMMMOR^ÐéöùüýþýûøôóñðïêâÝÛǵ›ˆƒ‚‹›¶ÏÂÀ€‚¨Êçí÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûùðçÓÄK.*(())())*))*,//-+8DNXgup[X^cujQPUC4-'#!""'2CTB:>HEDOXG61-$(++'#! ")-3?ABJXNGIMQS9*%! !! ! ! #0CFFFGJKNQTW]^^`begilkklnnmnolmnoqsqpowqrqqqttvpnnuoonnntqtqrpsoqprptrxspnnmroomsrsnomrlklmlqjjhhgfinnsnmmqpplmjjlrmmllklinloopnlknkkjjlrssmpjjhkikgghjhg_]\_^]]^_bZRRXVVTYWWTUURPOPWVTQPONOUJB4*%%%%&''4C`cdglgfgjeec~yyz}€€€ƒ‡ytrw}€„~}|‚Œˆ…Š”‰zpmjgea\\\[[\\a^^]_b_\\[\_^]^baaa`acilnpsxyyxwz}}~}}€…‡ˆ‡ƒ‚‚„‚†ˆ‘–––––——™™™˜˜–•’’‹ŠŠ‹‡‡…†…††‡„„„†††…††‡ˆŒŒŒ‘’••’’‹ŒŒŒŒŽŽŽŒ‘•—™™š››ššœ››šœ™š›œžž¡¢¤Ÿ¢¢¡¡¡ŸŸš›š¡¡¡¢žŸ›ŸžŸ ¡¢¡››•˜›šš˜”’ŒŠ†~wfTKEIMMLMMVe•áï÷úüþþþüùòêïôõôìçã϶˜Œ’™šœ¨½µŒ‚ŽÈ׿í÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþþûùðçÓÆ‡I-*)(*)()*+,/39A<4=JW^dstpc]bgeeB.-,)$$"""$+@OMLMMG_PWJ@AB)"#$$!! !-;?B@<DONM<<CJ:+%! ! ! "%5FFEFGKMRUY]^_acgiklljlnnnnonmpqqrrrqpqqpmqrsrplmmnlnnoqspqproroonrqtsqnnnmmmknnponknlljlmnkjgfeedjknosnoopqplkjllqijjkklimmqromllmijilmmnlkjihhihhfillg`[^^^^^^_c`YWUVUVVWWWUXZTPOMNNNOMKJIILE>4**))()+;Jadhkiecaeeecrqtutqnptlfb`djggijggnuvx|ƒ„‚rgecb_a[ZYY[\\^`bc`ddeed^^_^][]]`bfjowy{~~}€€~€~|~„††‡„…‚‚ƒ…‹”£˜œ˜–––˜——––‘”‹‹ˆˆˆˆ‰‹†Š††„‡†‹‰‘“””““”••••’“‹’ŽŽ“”› œŸœžœ¡šœ›Ÿž££¤£¤¤¤¡§£¥ ¥¤¤¡¡Ÿ œžŸ¢¨¡¢¡ ¡¡¤¡¢¡ š›ššœœ–—–›‘ˆ…zycSJFHLLNLNWk§áðøúýþþýûøõö÷ùûøõñæÉ¤œž¥¬‘Ž£²†}~Œ®ÖáîøÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøïåÒÈF,*)))))*.258AUQKMRasjkynh]dXQD0'$##"#%'%%/XO_VRJHLNE;0*)&! "#" !+BFXNLKMB91+/:4*$ ! ! ! #)>EGFGKOT[]_adcggmmplllqprqsoprvssuyyxtsrvtssupolpnnnnnstxpsrrrronorsupmmnnqnomqouqnknllllmokiffcdjupsqtssprqqlllqmplkjkklkpmuqoonnmiklqmlmliihggkjiinmmb[\cccaaaaa_[`XWX\XWWWX^XVRPMLLMLKJJGGHJBC=88965;GTaemigcaaeffeiijkjid`\YUQRSVY``i]ccegvqrppeeeeedceihgdaabbaa_acbbb````a`_`bgmrxwx|€ƒ~~€}|z~|‚…„……„„…†Ž——˜™™™˜—–—˜˜˜—“““‘ŽŽŒ‹Š‹ŠŠŠˆ††††ƒ†…‰‰’”•••”‘““’‘ŽŒŽŽ’”—™š™™šŸœœœ šš›žŸ¤££¡ ¡££¥ŸŸžžž¡ŸŸž¡£¤¤£¢¢¡¡ž£¤£ ¡š˜––—›–‘–—–“Ž‹†|xoaTMFIMLLMP^v°æôøûýþþýþÿýúüÿþýùòã°¯«™‡Š’’€{wƒ‘¯Òîøÿÿÿÿÿÿÿÿÿÿÿÿþþþû÷îåÐÊyB+*))(),6@DEJMW^[Zcusqr|ob`]UJB4.'%$&(.42-*3WMTZNGEC8.)%&''"$'17)!#/CEGK:0//*##$$$" '/@EHIMQVY]_bcffhjmnmlmnooqqrqsuusuwxxwusrurqomiiipnnnmmrtrosusrsoopsupknpnknlmmporqokllmnmmojjfeairropppprppormlmmlnmjikilknmmmpjiihhlpqkljifiggedckjonc\^aaababbb_\ZZZYXZXX[ZY]WWTRMLKMILPIDDEDBBABIKLNNVadghefccbdbfhgimgj`]WTOLLMQSYacicbeipwqlquusrrnmnppyrsciccacaaaacfhijmkjllmosvy}|€ˆ‚~€€|z{~€Š‰‰‰‰Œ•“žœ›™›šššš¡˜›——“”’‘“”””Œ’‹Šˆ†…††‡†‡Š‘’–––—–š”˜’’‘‘“’•˜˜žš›››œ¡œ ŸŸŸŸŸ£¤¥¥¦£¤¢££¥Ÿ¢ Ÿ ¡¢§§ª¤§£££¦¦©§¨©©žŸš—•——›—’‘•—•‘ŽŽŽzvpeVLEGKLNNScƒÀêóùûýýþþÿþýþÿÿþüúïàÚØ¾™‡‹‰†{w}ŒœÚíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷íãÎËr>+,**(),GQYGSV]`biorvxwlbPJLLI;4.+**.5=FL2*3YTRNI7-*(%#*8/**.8EC1%$-ZE;4,'%#! !! " ! " !$-8AGJOSX\_bdgijlrrronmuqrpstttyvzxwx{yytrrutunjhgkpooprqqqqrvuttssvttqonsqqqrrxnpospoopmlmqmnijfegrqvwwvvsrqrprnpprnplhhjklkplllpkmhhjpqrknjfeihgcbemjrd\\bbabfbeef^[\b\\YYYYY]\]Y[XXRPMLLQNJFFGEBBBHJQNZTebkjjcfffffgmlmld^YSQNMLOUWYZ[]ehilxtƒutl€~}uwpqmnopsvlda_`bbbcdeghikmpqomnnqvxz}~€€~}~~{|}‚‰‰‹‘”–˜œœšš›šššš™˜—•”“‘‘‘’’“•ŽŽŒ‰ˆˆ‡†…„„‚†ˆ‹Ž’““”•••–———”“‘‘‘’““–˜˜˜š—™š›œœšžžŸž ¡¢££¤¦¤£¢£¢¦ ¢¤£¡¢¤§¦¦¤¤¢¤¥¦¦¨¦§¢¡Ÿš™–˜˜œ•”“—š—ŽŒ…‚{yfVKCGMMMPUkÅîöùüýþÿÿÿÿÿÿÿÿÿÿöìèåÜ»›‘’˜¢‹z}‹žâðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöìáËÇl<*)(('),?dQIU]]haxmojeb^N9ALNMD:99>CBTMZP5(1693.*'%$#")39;<INTLX=*&)/4-%#! !!! $)1<*! ! ! !%)7DHMSW[_dfgijknpqrqppnurrqtsuvvwxwwvvvtprrrrpmkimsqopqporqrsrortttsrsoonpqpnqrqnqlnopnnmmmmjjhechkrrtssqrrrqqorlnoonnkjiknnloiklkkifjkljjjjkcaijb^chkkdY^bbbccebfib[^^a\\[YYYWYY][[YXXTPONMMLKMPJEDDGHJKOSY__`bccccbgjigjbZTPNLLMPVU[[[XY\`gtspmjkjlnopqqonjihggda^]^``cehjkpruxzwtssttv|~€„ƒ‚€€€€~€‚‚‚ƒˆ†‰”–›¢ ›››œš›šœœœ•’’‘’‘˜“”‘•Œˆ‰†…†‡‡‰Š‹Ž’’˜™˜•–“”•œ—˜”’‘”““”––œœ˜›˜ž˜™›››ž¢¡¢ ¤¦«ªª¥¦¥£¢©¤¦¤£¥ª¬®©¨¦§¤¤¦«ª¨©§¨¢ŸŸžŸ šœœœ˜™šš˜˜”•ŒŒŽˆ†~ydTICFMLOPWsžÕñöúüþþÿÿÿÿÿÿÿÿÿû÷ôòéÕ§™£´¡–~}† æöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúöìßËÂg:**(''(+;dKIaZW\`gnaXY[]C7=Q_XNJJDLJJQLPW1'*0+((#"""#'/C]MFLUPHHG,$#%$" ! ",5$!"+9SE/! ! !! #)3@JPX_acgppoqrrtvyzytvvwuuuuuwx{{{xyxwvsrqrrpononvsspsqxrrswusrrszyyrstuoopuqrpnnqkloqopppnnjjfcfnotqxssrrrsqsoqmnnpnmkljoorlnookljhkplnnnmmd_bid^_fime_]fa_cieeemf`ac_`]^\ZZ\\\Z\\_Z^ZZYZTRPPPQOOJPMKHHJJQRVX\_abcceiijih]WNNNOPRSTUUUUVWX_dffc[ZY\^adeggghfhccbaaa]^`ehjmopruy~|zwttsux|~ƒ}~€‚……ˆ††‡‰Ž‘–˜›››šœš›˜™™š—–•”‘’’‘’Ž‹‰‰…„……†‰Š’””••––••’”•“’’’’’•••—˜š›™š˜™—ž™š›››œœœ ¤¥¥£££¤¥¤¢£¤§¨§¨ª¬«©©¦§£¦©ª©ª¨¨¦§¢¢£¢Ÿž™œŸš™———˜–•’‘‘Ž‹ˆyqbTLCGMNOSY{³ÜóøûþþÿÿÿÿÿÿÿÿÿÿÿüùîݺºÌÄžŒ„‚‚åúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõìÜʾd8)*)'((*:dJFNTNIPVWURRW^G5Hbbf[RKBG`QYFF;+$#$%,0*$*/24>MUZHNLI;94*# !" ! !"""/A0%"&:CCF* #'4?NY^dfimprrsusrvyxwwtvsuvvvvwwwzvxwspsvtrssrppmnourrpppqqssqmqqrsrqqrpnolmmqqonokkklnonnmnnnhgehjmnrqrrsusrtpoopmnnmmmijjmmmlmjmkjimrnkjgfea\aei_`chid]__`_`debegea``c__^]]]\[Z[[[\_[[[ZYYXWWSRQNNHMKJGJNORSTXY__``cfffihibWUVVUVYYZXWUVY\`eeec`[XXY[\_aeihihgddeffgggjmosntsuy|~}|yvvw{€}~‚„~~„„‡†‡ˆ‹‹’™™ŸœŸ››œ›œ˜™™š–”’‘’–’’”‘–“‹‡‡ˆƒ„†‡Š‘““˜˜œ™™™š–••—”““”•™—œ››˜™›Ÿ™œ››šžœž££¢Ÿ¦§¨ª©££££££¥¥¥¦¤¬²«ª©©¬¦¦§«³ª©¬§§¦§§¢¢¡¡ ŸŸŸžœ››™—••‘‹‰ƒ€yqf]MDGONPS^ˆÇåõøüýþÿÿÿÿÿÿÿÿÿÿþüõëäáèìͯ¢œ”œÏðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõëØÉ¹^6(*(&'');@?>>715=TTTIKSYONUbggh^VHDJN?3.*&"""*::547@UOKNUSMA80)('$ ! !! #))&0EA*$#+[MK7% "#(3@P`enoqrxyxw{wvw}zy|uvuyyxyz{{zzvysppsuuuztuqpqtttttqvutvwrpppqtsrrrpnnqnmmrpnnokklmnqmnnomohfhonqqxqsruutsvrvrumpnrmmmmmpqqmmjniikronkkkm`Y[ieg`cfpga`abcbabddhedceac_a^`^^^^_^]a\^_`]\\a`_XXTUQMKLB:AJOUTYYXY__^^dffghhyvf_ZWWXXXY\XW[_djkkhec`^]]\^_bejhhiihjmnprtsrrrrosty}}~€}{zy{}~~€€~€‚ƒ„……†ˆŽ•›šœ››šš››œ—˜–˜—˜—”’’’“’’“ŽŒ‰ˆˆ…‚„„ˆŠŒ“”—™™šš›š˜–‘“““”–———œ™™˜™š›™œœœžž¢¡¡Ÿ¤¥¦¥¥¢£¡¡¡¤¤££¤¥¬ªª©¨¨¬¦©¬®«ª¨§§¬ª©¨¦¤¢ž¡¡Ÿžœš™–•Šˆ†ƒ‚ztgYNDGNOPWa×íöúüýþÿÿÿÿÿÿÿÿÿÿÿûöñïòôìãÙ;¸ÀåôÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÖɰW3(''&&&(3@3,*(''9SUVIGVhjllkijccRK::2+(%$#$%,3GXOEEFPZINB?8.($#"! "(<CB@CC4#%/]QJ5' !(-:FR_eknrtuwwwwwwx|{yyuwtuuxyyzwttuvsroqquxwvvtpmqrqptsrqwuuuspportuqrnmlnooonmrqpoommklmmnjgjkhchjnnqrrrqpqqrqrpppolpmmiiinqlimljgghklmknhhh`WZ`efgaeghc`^abbba`efgc``baa`___`_^^^^^`\_`a__^_^^WWTSSQOJ;<ALOPQWVXYYY__gghhih…vs^^]]]]XZYY\biokkjnoqmjgedbbdgjlqptuvxzzzrqrrrvwz|~‡~~~~}‚‚‡‚ƒ‡„ƒ…†ˆ‹œž š™˜˜™š›››œ˜š™žžž™š”“”™–—“”ŠŠŒ‡‡†ƒ„†‰—™›šš•—›Ÿ ›ž›œ–”•››››ššœšš™™šš™¢Ÿ¡ž¢¢¢¢££§¦¤¥¨¥¦¤¦¤©¤¦¦¬¨ª©®®®ª¨©¬«±°±®®ª©©¬ª³«©©©§¦¤£ ŸŸŸ¡¤›—™‘‘Š‹…ƒ}viZKBFPNPVfžÝîöùüþÿÿÿÿÿÿÿÿÿÿÿýûúøùúôîçÜÓËÉÙóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÔʦR1()&)%&&()(%#"!%5Vj\KR[eemnic`]ND6)'&$###',3<C]VcT\GOLI;/*'$! ! $8aA<KYQJ%'6aZc9(!! ! " $)/6BTXbgllprxwwwywzzz{{zyvzwvuzz|xutxvxsssuuyy{vvqootrrrvpprxwxvspqpwuuqsomloospsoqrqpunpopllihgkhefnnrrssvrrquqsqtpoopoppojkkplijsljhikpmljnkrd[V\dgghglgfa_`ecebbaehna]bfgfedefff^`___`_ccefgaa]^WZYXUUSH=>FMPRTVVZYXY_ciijijjkhdZ\QQQRTTUY_bpollkpsronlkkifgimoqqtz{{ywxqrrssvyƒ€}~~ƒ‚‚ƒƒƒ‚ƒ……††…†‡‹“—™šš™š•˜—šœ›˜˜—™™™™™˜˜””””•’ˆ‡†…ƒ‡…†‡‹Ž“™™™–˜–™œŸœ›››››™˜—š››š›œœ™™–š›š œž¡¡££¤¤¥¤¤¤¤¤¦£££¤¥§§§§ª««ª«©©©©¨¬®®®²³®¬««««³¬ª©¥§§¥¢¢£¢¡¢ž›—˜“‘Œ‹ˆ‡ˆ€yhWLBHKOQ\m¤àð÷úýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷êÛãä×ÝóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÓÊšN/&'&&%%$$%$$#""%3W\cbbdpgkfcWTG92'%$$$&)/8<NDCLIJLJF8.+)&""!!! /DbAH`[]@&+APQJ:% #%-28?JX]aflllprsrwvvvy|{yzuvvzxvu{yxuuuwttstvwwzyxvqmpqqprsokpswwvvrpqqwtqnonmkoprmrprrqnnmmlmkhdgfjehjmosrtuurropoppqpomnlljkklllijknmjhknonmjjhe\XW`hgiiikcbbaabbdbbdhkh\`bcddddccbb_`b__`aabcbaa_][XXXXXY\PDJQSUUUWXZ[[\cfhijkjjlid`]XUSRVWXZ^bhnlpovwywwwwrrppmrpusv{{|}xxwvuuvyˆƒ‚€€€€ˆ‡‡„…†‰ˆ‡††‡‰ŠŽ™•ž—˜™š™›•˜˜š›™ššš››œ˜˜˜™“”“‘†„ƒƒ„†‡ŠŽ”–žš¡šž™›œž›¢œž››œ¢¡¡žœŸ ¡¡¡Ÿšš›¡œ Ÿ¢¢¦¦¦¤£¤¤§¥¦£¢£¥¨««°¯°¬««¬«ªªª«°¯¯·±°¯µ¬¯«´°´¬¬««¬§ª¤¤¤¥Ÿžš˜˜˜‘”‘‘’ŒŠ€ygZNEGONS^v³ãñøûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûòèîóòñøÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÑÉJ-''&%$%$%$"#"""$1UTWZ_bef]TME?90'$$%&.9;>ABNGDUNIA71'$"!!!! ! !!! !'7OfANbHF7%0LQF>-" ! "(.7@FLRackllmnnttttwwvx~{|yxtuw{vvv|{xuvuytvuuvyx~ywrposqqrrpmpwu{wwtssvvwsonnnlloprrsstqropmmmpiedkgjipppq|svvtqpnqpppppposmkikmomllljolkkonommjkhbYY^jiikojkbbccemffedeoieegggcddedda`_`a`_aa`bfddb`]ZZ]YYZ_`^\ZX\YYYZZ]]`clghjplniŠŠ…zurnc^^__`ceijmnux|}}}zvuupnpptuxz||~yyyxxy{ƒƒƒ‚~€€€‚†‡‡…†‡‰‰ˆ‡ˆ‰“”—–—”””—•™”˜˜šš˜ššš›——•”’’Šˆ„„„‡ˆŠŒ’—˜™™š››˜š™™˜šš›™›šœ¡œœž Ÿ ¡œ–™™ ¡¡¢¤£¤£££¢¢¦Ÿ¢£¥¥¨ª¯««¨§¨««¬¬¯®®®¯¯¯¯¯¯¬««¬ª¬¬««¬¨ª¥¥¤¤¢ œ›šš’“’ŽŽŒŠƒ|uk_PDGKPTe‚·èõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòèÎÇ…F-&&&&$$$$$##"###1URRY`^ZWTJ?<93,*)26:@A`PVDO>530,(&%#!" ! !! #%%" "%# $/DXS?He@>-#,T@6,# !'+28AKOSX]cgjkmnppqpstusuxwwuttuvwvssttsuuuuvttsvwyvxusprtrpqrqoqtuv{utsuuvtsqpqpmmmoppqqqpnnlonmmohfeeejlooprsssqpoonrsqonnnnmmjhlonnmllkkjllmmmkkiii[T]eggjkjgc^cfeeedcadefffcddeccbeec``_ab`_aa`_edda_^]\\X[\bf`\WWXXYXZ[]]aehijkmlmf“†~|}ttiljiiijkkmr{x„ƒ„}}vvuqruuz|~}}}~€‚|~‚„‡‰…†‚‚ƒƒŠ‹ˆˆˆˆ‰Š‰‰‰’–—˜™˜˜•–”›–˜˜˜˜›š™ œœ›œ—š“’’–Ž‘‹‰ˆ††ˆŠ”” ŸœŸžž™™—–˜œ››œ¡ ŸŸ¡¢¤ žŸ¡ ¡Ÿ£¡¢žœ›ž ¥¡§¦¨¥¦¥¦¢¡£¦§¨¨¯¯¯ª³±¯«©¨§ª¯±¯¯®°²¸¯°°°¯¯¯¯¬®¯¯¯®¯´®©«¦¦¥¨§§¡¡–““”މ„}n^PEGNPVjŒÄìôùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðçÌÅ|A,''&&%$$$##""###09;;<@BBDC@:76102<IKLJJTUF851+)&$#"!!!"!!!!!! !! "3H1#!)./!(3WZZ:795,"!',+'! !%-4<GNTW^`hgmnonqnuprpxvuuuuttuttuyuuv|utsuuyuvwwu{z{xysrrvtrqtppruu}v|vutxuvttprrtmoopppqrooonmsnpmoiifehrtuqpqxsrpsosstssollqmmkilsornnnolliposmnklij\TZfejiplkc_^dddefbb`eehgjeddiccclecabcda`_da_^deca__aaca^aieb]XX[[[[[Z]_cegjompllg„„}}y~rqlllkjkmosttwx|ƒ|{wvwvvxyy{{{}}|…‰Š†ˆ„ƒ€‚ƒ„Š‹Šˆ‰Š‹Ž‰‰ŠŒ”˜˜˜—––•–“••›—˜™™—šš›™›šš˜•‘ŽŽŒŠ‡ˆ‡‡ˆŽ‘”——˜ž›œœœœœ›š˜˜™™™›žŸŸ¡¡¢¢¥¡¡¢ ŸžœŸž ¡ Ÿ ¡¤¢¦¦¨¦¥£¥Ÿ¢¤¥¥¨©©©«««¨«ªªª««°¬¯®°±²®¯¯¯¯°ª®«®¯±©¯¬¬¯¯©©§¦¦¦§¦¢¡ ž™–•”“’ˆ„ymaRFHMRXp–ÈîöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüõïçËÃu=,(*,))%####""""#,<.(.2336988:;97BUJa_`HMA8/*(##"""!!!!!!! !$'3KH7$"*H)!%4<J9(''$! "!! !#*/7>EQUY[^dfhjmjlmonomqorrrprssrqosuurvvvuutttsrssttttzxusrrrrrponrtwwxvusuuvuvtsqqpnmpqooqtrlllnmnnpkjffdjnrsutrpqqqonnpoqqpomlomnknqqppnoomkkjpmllnjkg[PZfefkijjb\`_`adbb_aadehggeedeccccbcab\_`_^_`_^dfca`_`aa`aceca]\ZZYYWWV]beehkoklljgz~‚~…}€tqqqonnopttwuxwz€ƒ~}{|€ƒ~€}|}‡€‡‡ŒŠ‰ˆˆƒƒƒƒ…‰ˆŒŠŠŽ‹‹Š‰“—¡ ¡˜––™–œ›š—œœœ›ŸŸ œŸ›˜—••ŽŒŒŒŠ‡††‰Š‘—š£Ÿ›œ ¡ž£››š›œœ™žžž¡¥¥¥¥¥¥¥§¨¡Ÿ Ÿ¥¢¥££¤¥¤¤¤¦§¬§«¦¤¤¦§§©°¬«ª¬«©¨«±®¯«¯±°¸²´²³±±°¯®±©®®µ¯±¬¯°°¯±°°¯¯¦§¥¥¥£ŸžžŸ™™˜—‘Œ†…|l`QFHMPXv¡ÓðöûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîçËÂm8+*0-,(&###$##""##"!"%*,--05?LHUOMUMLJJI8-($""!! !!! !!! ! !!!"*59?QY1%#,5&!"#&(&" $)1;BJNV[_bghnnpkmkllqnoopptttppswrqqrtvwwvwv{vysrrssttut{vursrqrrnorzx}wxustxyyuvvutunmmqpoousslmlnoqnpkjeehrsysvvvqtqqpoourvppnonrooovvuqsrqolkooqmnlnim^UWdhkimhgd]\abeedccafggghhgghehdfdicdcc^a_^^^_``ccdbb`dbdchfeb`^^_^[_WWW^cjiijolllkgqrwxz}zuvvtssutrttvsvw|€€€€ƒ„}}~€€€€‚ƒ‡‰Šˆˆƒƒ€ƒ…‡Š‰ŠŠ‹‹‹Œ‹‹‰“˜š™š™—–””•˜˜˜˜˜™››œšš˜——•’ŒŒ‹Š‰‰ˆ†„…†‹”˜ Ÿ››œŸž œ›››š›››™Ÿ ¢£¥§§ª§§§§¥ŸžžŸ ¡£¤¥§¥¤¥¥¦£¤¥§§§§¨©«¬°¬«©ªª©¨«¬±¬¯«¯°±±±±²±¯¯²«®¯®««¬¯¯°¯¯®¯¨©§¦¦¦§¤¡¡ Ÿšš™˜”ˆ†}sjaTIKOSZ{ª×òøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíçÊÀe4++..)&&$$##"$$$$# !',,++-<KGGTWUTDF82,'%#"!! !!!!!!! !!!#%')/7Ga`aF)%%2?% ""! #&.4<CIRW[\`cfikkkljkkkjpmoqqrttsqruqpswtstttssssrrosqtttxutttuqtrqnnlquyxwtuuuvwvxrrrrqoknnonpqursklloqqmqigdhkstwssqqqqppppppppppnmmrmooooqpppqnllnmmkkjje]SX`dhhgggfa]\adccccbafjffggfbededddhbccb]a_`a_\`adab`a_baccghea`^^]\[_XXX_cghiikkihigmqtuuuuvxyyyy{zyztvtvy€‚„Љˆ‡……†€ƒƒ…‚ƒƒ…ˆŠŠ†ˆƒ„ƒƒ…‰ŠŽ‹‹‹ŽŽŠŒš™ ™œ——–—˜Ÿ•›››™™˜žžŸ™™–•’‘Ž‹Šˆˆ‡†ˆŠŽ“—¡Ÿ¥ Ÿ ¥Ÿ Ÿ ¡›Ÿ›ž›ššžŸ¡¡¨©©©ª©ªªª¤£ ¤¡ ¡¡¡¢£§¤¨§ª¥¦¥§¥¥§©«¬®³¯°®®®®¯¯¯¯°°®®°±·°±±±²³¯²°±®´®±±º°®¬±±²¯³±´¯°¯¯¯®««¬®¬ª§¥£¡¡¡¡ •’Šˆˆ~zpdXMKLQ^…¼ßó÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíã˺_4+,/+&%&$$$$$&*-($##&)./**2PN<BdJ:50*&$#"!!"!! !!!!!!!!!! ! %-8LC>ENJJU?'"$<0$ ! #(.6@GRUWZ_abefhooolljnmolpqrqvwwuttuqoqvwwxxsursturqpst|utuuutsvquspmnpwwzyxtvv{xxwxpprtpmnrqppttutskknpqrnriggnpuu}ttstqqppquqrppppnmmrqsoopqqqqqnmnrnolkkl`UT`cghiikjk`__efiekccemgefmfefmchghehabba_a_caa``bdacadfgfedgeea`_`_c^_[[^gglkkkliggmhsstusrsvz|}}~~|wwtttw{€‚…‡‰ˆˆ…„„ƒƒ„…ƒ…†ˆŠ‰†‡………„„„„ˆˆ‰ŠŠ‹‹‰‹‹’•–”“““—•˜™—”–”–”—™žž›—–“‘ŽŒ‹ŠŠˆˆ‡††Œ“–›Ÿžž žŸŸŸžŸžš›™œ›šžž ¡¢Ÿ£¤¤¢¤¤¢Ÿ¢¤£¢¡££¤¤¥¤¥¦¤¢¥¦§¤¦©¯®®°°³®¯®°±±°¯®±²¶°¯«¬«¯®°¯®¬¯®±²´±¯°°°¯±±²±±²²²²±²²±°®¬«©§¥£ ž˜–’Œ†‚~uk[MMNV`ˆÅãôùûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûóìßÍ´^5/-0)&$%%$"%&0:62230,,0)&)6NS=<<9/(&$$##!"!!!! !!!!&+)"""!!#/=ENSRQF?Ci:'""""!&+3:BINVWY\`bdeiijlnkhhimmmlqqqrqpooqppoprssvxussrrpooppsuuutssqtrsqokmnpqwvxrtsustuutqnqrqnqrruqpsrqnnknspppmliijptuvwsssqnppqrrqronnonnnpooopppnmkklmoljomjgbXVVbgggijjjc^`aefdbcdfimeffgeffebddfedbbba___``a`aabaa`cdeeffgdb^``_____]`dfhklmnmggghexuroqty~ˆ††„ƒ|{wxxy~‡‡ŽŽŽ‹‰‰‰„‚‚„†ˆˆŠŠŠŠ‹‰ˆ‰†ˆ‰Šˆ†††‡ŠŠ‘‹Œ‰‘•”””“““”˜– —•–˜—œ–œšŸ™–”ŽŽŠ‹ŠŠ‰‰‰‘–—œŸ¦¡¡¡¢ ¤ ŸžŸžœ››œœ¡œžŸ¡ £ ¢£¤£¢ ¢£¤¥§§§¥¨¤¦¦¦£¢¢¥¥¦§§«¯¯µµ¶±µ¯°¯µ°¯®µµ¶³µ´µ±¶µ¶±³®®¯¶µµ¯°°°²µ³·²±°°³¹²´µµµ¸´½¼¼¼¼»»´¶±±²³¬¬¤§ ŸžŸ˜•”“‹‰xi[OKMSb’ÓèõùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúòêÜÐ^7000(&&(('&'3@FKKNJF.,'"#(1NC=>D0$"""####"!!!!!!!#",45,)($"'?][^`bYD<;;6$! !%,4=HKUY[\abcflehjommjighlsnwpsqxpqnroroopsstr{usstuvpoosquuvuursrussqmkmqttxwwttsxsuutqopwsrrxtsssqwqppqnsqoorkiilovu}vwvwqpputtrrqronnooqqtppnsoponjilrnjinjic]QQ[jhhhligd__adjeddedjjmdjhgeifddfcideedbabd_a`babacbfggeehkghca_ccc_`_caghijnnnnmhjjjf{wqqrx|„‡ˆˆˆ…|wxy~‚ˆ‰‰‹ˆˆ…„‚†‰ŒŽŽŒ‹ˆƒ‡†ˆ‰‰ˆ‡††‡ˆ‰ˆˆ‹‹”’‘••••••–––•——™˜š”••“Ž‹‹‰‡††††‹“—šœŸ£¡ŸŸ Ÿ£¡ žžŸ›œ››œœœœŸœžŸ ž Ÿž¡¢££¦ª©§¦¦¥¢£¡£¢¢¢¥¦§¥«°¯¯±´±®°°°±±°¯±±°±´³³°²±±°³°²µ³²µ°°¯±³µ±²³²²µ¶´±²³´´´´··¼º»¸»µµ²³¶³¯®¦¥¤¢ Ÿ›—’’“‹‚ui[MKKVf–ÜíöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñèÚЧ_:0+*(),151.9EQ[XVXY;+% (*-02=0&#"! !!"""! !"##"$&=e?8=M)"+NJBZMPbA3.*% ")/6<DORWZ]]^aadigdilljifggkmnnqoppoklmmnqnootssrspqppoqppnrruuvqvssrrsrqnlqwvuutssurtsrpompssqrusooorqqmplnnpnnmkeiknpvuwuusqmqptssrrsolnnoqrstqonoopnmjlmlkiinfc\PHXdcfghhif_``beebeedcghgejjfcjeegfchcbadbaab___bdbacbddeaglhdcc_]^^`_``cbfhklmnllkfieif‚~{zy|€„Љ’ˆ‡‚~~‚‰‰‰ŠŽŠ•ŒŒ‹ˆˆ‰‹””“–ŽŠ‰ˆˆ‰ŠŠ‡‰‰‰‚Š‘‘‘‘’’––š—™—š™šœšž˜š”•ŒŒ‹‹‹‰‡††‡‡‡Œ“—¡£¦¡£¡ žŸ£¢¢¡ žŸž žŸ¥ŸšžŸ ¥¤¥¢§§§¦«ªª§¨¦¥¤§¤¤¥§¥©ª®°³³µ®®¯±±²°¶²¹²±°°±²²´²¸··±°±´²·³²²¶±±°¶³µ²´´¶³¼µ³±¶¶¸´¶¶º·¼º¿¹¼¸¹¸·¸¹³°¯¸®«§¥ ž™™•‘‰„woYJHKWm£âï÷úüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøðçÙÑž^8-**+/<KDAGOYgb]VX?+$ "(&&&'&%#"" !!!!! !#+7*(4IgJIKE,%/XOE]B<81)"! $(/8@KQUX]_c_`^bbgfghpmnihhmnoppoupqppkmnnmrqrotsstuprpqpsrrssrwwwvwtssvvuppryxytvuwvuvxronmntrqrtsronnqprnqmpntmqligkowvvvzvyrrosrttvvutnnropqwwwqoonnqpopqnmhhineeRGQfedeffgfcddbcdfeedfglhggjhefjeffgehabbdcbaaacafcccgdcdeeokfcd`^\``f_`adflhppplkkjgiiig„ƒƒƒƒ„†‰‰ŠŒŒ‡‡„„ƒ…‰‹Œ‹‹Šˆ‡‰ŠŠ‹ŒŒ‘“Ž’ŒŠŠŠŒŠŠŠ‰†…ƒ~{„ŽŽ‘‘’’–—š–––š—™™˜—•“‘Ž‹‹‰‰‡‡‡†…†‡Š‹Ž“™¡¤¢¡ žŸžžŸ¡ ¢›œŸŸ žžžŸœœ›žž ž¥£§¦§¥§¨«§¦¦¦¥§§§¦§¨«°²³´¯ª¯¬®°°±¯³±²®±³´³²²³³·±±±°®®¯²±±®®®·³´°³³´²³°±±·µ¶µ¶·¸···º¹º·¶¶¸µ¸µ¶··±¯¬«§¤Ÿœ—“Œ†}rf[NNP^t¨áñ÷úýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøïæØÕ”Y5-*4<GSUXZ[]jffZNC/)&$$$$$$#"##! !""""%2338CTeRfU?,#.^A51,)'$!! #&-29@FPTZ[`abb__`bbgfhikkjhiimllkonolllqmmnnnspposrrnnnpopnnnstsprsssttsqssqprsuwuttsrovuqmmlnoqpsssppponpppmnmonmmkhijprwtuvvtsoomrrtsvtttqoroqrtsqpppnlljnnmnidghihUGRcbdeefeedb`aaccdbddfhiiiijgghhdecfddbcdddc_abccdbddddeeeeniea^Z^]`a`^`aglihlkkjknifhhii‘’‹Œˆ‰ŠŽ‹ŠŠŠŒŽŽŽŽŠ‰Š‡‡Š‹Ž–‘’“‘˜ŽŽ”ŽŒŒ‰ˆ~zzwƒ’“”“–••—š˜ž™›šš™Ÿ”Ž‹‹ŒŒŒ…ƒ††‡ˆŒ’›œ¢¡¥£¡¡¡Ÿ¡¢£¢¦ ¢žž£¤¥¡¦¦¦ŸŸ œ¡ ¡¡ Ÿ¢¡¦§§¨¬¨¨©¬²ªª©«ª«®´²¼´³°¯¬°¯¯°³¯¸°²²·³´³¸²³²¸²´°°¯¶±±®¯²·´´°¶´·²²²´±¸·¹¸¸¸¼½½½½½¾¹»¸¸¸¸¶¶¸¼µ³³¶¯°¦£œœŽƒ{xvbSNP_z´ãñøûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷îåØ×–V6/6FUhih[\\`fibWNB8+ """""!##)+)# !"""##'8@GMKdTHVlA,$,20*%#! "(/6=CNRUY_```dabbcdffghmklhhhmmmnnmpoonokrpsorntqururrnqquopnnptsvpppqrvvvqqpprxrusrszrqswrnllnurttxtqooptnpppppopppliimqzuxuuuytrpqorstsvvwsrrrqtrwrsrsomlklmoqkhhkik[LNaedfjihefedcgghefbceiiijlligjhgehefcccfggdddeeeehikeedkijhnhe`]]daeba`bcmihhokjilljjjikj’”•–“‹‡ˆ‰Ž‰‹Ž‹‰Š‹‰†‰ŠŒ‘‘ޑދŒŒ…ƒ}v|†‘’’“”““‘••˜—˜˜››™™“Œ‹‹Šˆ†……ƒ‚„‡‰‹”–˜Ÿ¢¡ œ ž ¢¢¢£¡¡Ÿ ¡¡¢¢ £¤£ ¡¢¢ŸŸŸ¡¡¢¢¡ ¡¢¨¨©©«ªªª¬©ª«ª©©ªª«¯´´²³¯¯®®«¬¬®®²´²¯¯°±²³°±²±±±±²¯¯®®¯¶±°±´µµ³°³²±±±±³±¹·ºº¸¶¹½ºº¾¾½·¸¸¹º¹·¸¸¸··¸´±®§¤œ—‘Їƒ~wcQPPd³æôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöíãØØ—R87CXmkn`VVX\dc_UQOP4"!"####&)07640)-32/-,26:OOPNCcNKKE?(!$(%#"! !&+17>EJPU[^a`_``_`bbfgijhhhiigggknmklmljnnoknoonqmspqoononqrpnmnppppqqqnqrrppponrsurplrqposztonkotrqssstspqppnooononnmliijpssssstuttrnoosusqqqrqqpqqrsqmppponklmookgijkj^OPUdighighe_[edfcdddbdgjkkkjghhhfgeeddddafcddbaeaefggkffehfhjiee_____bcbacdefhikijfhhjdiikh›› –—Œ‡‡ŠŽŽ””••”‘ŒŠŠ‹Š‰ŠŽŽ‘‘””””–ŽŽ‘„}x€‹›”•–—”“’“–—œ—š˜ ˜•‹‹ŒŒ‹‰‡„ƒ„ƒƒˆ‹“™™ž¦¦§¡žžŸ ¢¢¤¤©§¨¤¤¤¨¥ª£¤¤££¤£ª£¥¢£¥¦§¨¢¢¤¥¨³©®«¬ª¯«¬«¯ª««¬«®¯´µ·µ´¯°±²°¯¯´³¹´±¯²²´²³°®²³°±±±¯±²²¯³°¶²¶±···²²±³³³±±±³´¹¸º¹¹¶·¸¹¹¾½¾¹¹»¿¼½½¼ººº¼·³²´§¨ž—˜š—”Ž‹€vbSOQeƒ¼èóùûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúõìäÙ׈M68IgnrnZQOUY]aXRQTJ<"!%(*--/5?CFDAFKLiI;>VLHSXYPMdLTFB0$ #(.6<BFOR]^^afccac`bbbcjhmjhhfikijjjknjllkjnnpknnpnqlssunqopnuqoooospprurqnursqsoorvsysqprqprvtrnmnssrrusrsvqoppospoopoonlilpxuussrvwwtqqrrxutpnpsonptrzrnovrrponssulhhklmcYJS]iikkkfh`[^fegcfggcfinjhijhhhigkegdefhcfcmdccegihhhlgghkfhjkdebb^^^aceghfjgkklijfhfjiihkgœœ™•’Œ‰‹•••””’Œ‹ŒŽŽ‘““Ž‘’’‘•••Ž„z~€†’‘‘˜”••–”•“”——˜˜—•’ŒŠ‰ˆ†„‚‚‚ƒ‚„…Œ‘–™›ž ¤¡¡¡ žŸ ¤¥¤¥§¡§¤¦¦¨¤££¢¡¡¡££££¤£§©¦¥¥£¥§¨©ªª«ª¬ª¬««««ª¬¬¬¬²³´°¯®±°²±±±²³³³²±±°±°°°¯®°³°°¯¯®¯¯¯¯®²±²¯²²³°°¯¯±±²²³µ¶¶¶¶µ´¶¶¹¹¹¸º¹º»¾¼¼º»»¼»¸¶³°¦¤££¤¡˜”†~ueWSRiнéôøûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôìäÙÜJ3;NfllkVINV[]SOMJHE1"!".951028@K[IGSnPMeZNEQmjihjQME741,#" "&+07@BGKOUX[]acdccac``_ccfhiihfgijhgeghnjiekjlmmjnmmlrmoppnqoonnnpqppqqrtrqqoooonnnruvssstttrtvvrsnoorqsrrrrrvqpoppppppppkgijoutrssrrssrrqprsssqoprpnoqrrsooqsqplnnnnkfijjicXQKYdiiijhfbZ_cffecfhgdgijghiihhijeeegbfedbccddddehhgffhgijfbijfaccb\]^aeeffdihlkkiiddcfgghieŸš˜”’‘“’’–“’’––—”””•••—™““’–“”“““–•”’“‹ˆƒ}‚ˆ‹”‘‘“—˜™–—”–”™˜š•“Œ‹Š‰ˆˆƒ‚‚…†‹Ž–˜Ÿž¢¢¢¢¤¡¡¡¢¥§ ¥¥¤¥¥§¨¨ª§¨££¡ ¢££¤¥¤¤§¨°¨¨§¦¨°««««¬¬¬ª°®¬¬¬®°·³¼³¶±°±´´·´´µµ³¼²°±³³²¬®¯±³³·¯¯¯¯®¯¯µ¸º±µ°³´¶±¯¯±°··¸¶¼¼¼½½µ´¶¹¹¾»»¼¼»¿¾Á½½½ÀÀÁº··¸²®®±²ª£¡žš‹ƒ}gWPRkÅëóùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùòëäÞ݃H>AUgkmaSKMT^UIHG@:3(&'1@BA848?FVSMHW[VTcbcCHVTUKOYA4+%#" "(.4;AEKRTVY___bjihcbccba_ddfhlhhfjilgffggmiifjikljjnmmmqoopqqqnpppptqpqtturppqpnmpmmqxuusqt|vxvzvwssoqrtuvsxswtvqppqswwwoplhfinsstrsrrsxruqpqtsyqposqposrvsspprvponnoqmjiopqg]QNVfhlikiifaaccfeddfgggmihgiiiinilgjefefecacdecgfefhfeeggkgddifdchcb^\^bdggkjjjmloiifefhhkhif——˜’’‘’’“•—“‘’‘““–œ›šœš–”‘‘“’”““““”””•‘‘’‡‡ƒ‰’‘‘“”—™˜”•“•“–˜‰‰‹‹Šˆ‡‡‰„ƒ€~‚„‰Š•šžŸ £¢¢¡£ž¡¡£¦£ Ÿ ¤¦¨¨¨ªª¦¡œŸ ¤¥¤¡¢¢¦¥§¨©©ªª©ª¯¬«««©§©ª¯®«¬²³¶²²³´²³´¶··´´³´²²±°¯¯®©®®®°²³²²¯°±±°±°²´³°¯®®®©¯°·¶¸µ»º»¹¸´µ·º¹ººº»»¼À¾¿½¾¾¼¹¸···¶µ·º¹¸·²®§¥£ š“Œ„{fUSSn•ÈíõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøñêàÝÛ‡H>Lc_\\_UKLOPPFA=3.(''1@N\K>6;CJKNKI_XRVXgCJUST;1/+(#"! "(,28>CGJNTVY[]`cddghecb`bbb_deggfdedghlgfffghiifhhhhhhkllppooooomlmlopppqrsprpqrqnokmnquuuttttuuuutturrorrtuutusrrqpppqssurnlihfmtrrsrrrssrrqnqrttqnpmooonrsuospqqpknmopqlkjllh_SLXegijijghecbbbfeefghhhihffheihlfhffddfd`cadgecefffgffeggkeddhbdec_c^^``acccbjkljkjjgghiijhig˜—˜——“”“”•”•–’”’“”—¡¥Ÿ—™“””˜•——šš›——••’ŠŠ“™‘‘‘˜’––˜˜—˜™™šŽŒˆƒ‰‹ŠŽ‰‡‡ˆ†Šƒ„ƒƒ„‡‹’“œ› ¡¦¡¨¢££¤£¨¢¥£¢ Ÿ¢¨©¯©¨«¢žŸ¥§¯¥©«¨§§©ª«²±°°°¬¬®ª¨©ª«®²µ´´´½´·²´³¶´¹¶¹¸¸´¶¶·²²²³°±°°¯±±³³³³³´´²¸¸¸´µ³±°°°°°°®±±±¯·´¸·¸µ¼º¿¹¹º»ºº¹¹ºº¼¿½À¾Â¿Ã¼º¸··¶º¾¼Â¿Æ¿Á»¹µ²¬«¥ ˜’†weVQTsÏîõúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøðéÝÝ×€GAXVNLM^[KJMNJF?:2(&&,8G^WR>7;FGHGGJfjY@<PpPQ<2,'$#"" %+06=EJPRSSYX[_eeecefhedcbbgbbcjklfdddefglilefghhlfmggfjhkklnpppppmllpmooopsrtuutsqrnnnrswwztstvuuw{vttzrtpwswvwxxstpoprprqqpomjikmvsstwtustsrqqquttrompoooqrytuorqyqplnntpqlolmidULTehlkkkkkleghjiginhjjnjgfegljjikeieddccc_bchhfgljkhihkeigjeffgffcc_b_``aacbbdklpklmnijjmmkihi–”—–•““’’“”“’’’”•˜ œ››˜™˜™“•–—–—˜™™˜—•’Š‹‹Ž•”‘‘’˜’–—˜–˜——‹‚€‡Œ‹‰Š††ƒ„ƒ„„ƒˆŒ”–˜œžŸ¡¡¢¡ž ¥£¥¢¥¢¢¢£¥©ª«¨¥¡Ÿ¢¦§¨©¦©©¬ªª«®¯²±°®ª®««««¬²···¶µ´´³²³´µ´´µ¹°²²²³´±±°±°°¯¯¯°²±°±°²³³³¹··µµ¶°®±±±°¯¯°°°¶³µ´¶´µ¶¹¸¸¸·´¹¹ºº¼½½¼¼¼¼»¼¸¸µ³±¹½¾½½¾À¿¿¼¼»¶²°¨¡™ŽwhYWVv£Ñðøúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ïèÜÜØw@7GQF>O^WKJOHDB>;5+',2@NOQB4348:?BDGjgC+2AEJ7(&$#!! ! #'-2:AGKMORTUVZ\_cfeeehlfbdecbdcddjhfcdcbafegggeeeigkfedddffkjlmqqolkhiillnmpprrtqtrpnnnnorssssqtvrouwvsrqtrrqqrvuuutqpnnnoomlnnljjjnrqprtuuvssqqqsusrtnmmqqppsuvttprppookmmnnmjmlngWLVcfjlmlklme`jkigiiifijjkebfhhhiiheeccccc`_cdefghiijfigkfgfgeeddcbba_`_`_aacddekllkmlljiikmjhhišš›šŸ””‘‘’˜™Ÿœš™˜›˜—˜š—ššš™˜™š›œ›Ÿ˜••’ŒŒŽ”•”““‘–”˜——™œž ›”†|ˆˆ’ŒŒ‹‰…†ƒ„ƒ……†Š““š› Ÿ¢¤¤£¢Ÿ¤£¥¤¨¤¤¥¥¤¦¥¬©¯¦£ £¦«¨ª©«ª¯¬¯³²¶²³³³´´°²±µ°¯±º·º¸ºµ¸³´²¸··µ»¶¹¯±±³³¶±³°²±³°³³³°®¯³³¶¶·³¹¸¸¸¸µ°¯¶±¸³±±±°±±¶¶º¹º³µµ¸¹¹¸··º»¿»Æ¼Â½¼»¹»À¸¸³±¹ÃÃÃÄÄÃÁÁÂÃÅ¿¼¼¼µ¶ª£—Ž…~l^VX{×ñöúüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöîçØÙÚm95@??CTTTMHGA@?=<4-07?ILNE90)(*19=?DoQ/%&+/-'#!!"!! " !%*/5<BJJRSTVZYYZabdilhhhmgedddfghhgfjgddecacgfiffhkjjgkfieeefejjmmqonjhgiklnppvqrstrtqpqstuuuutttqwrqqvvvqopwssrtsvv{ttsropnnmlkonjjlnspooqtuuyssrtswtrssrqrrqrrvwxstqtoonnottvmmlrlq\NScgkkompkngdclkihkjjjmnricelhiikiiedcfb``_`cegejilhiiiijjjgiihddbcaa^^_b`cdkhigpmnmolkjihiggini™˜™™˜•“‘‘“—šŸŸ š—›’•”–•••™•™œœœ›š—‘ŽŒŽŽ”““’‘‘‘•”——™›™šŠ}|ƒŠŠ‹‰………„‚ƒƒ„ˆ‹Ž’•˜›œœžžžžžžŸž¢£¤¥¦¤¤¤¤¤¤£¬¨¦¢¡ ¤§§¦¦¦©©ª¨««®¯±³²±³±²´´´´±±±±±³µ¶´´µ¶´´³²²³²¸µ¶³²®¯°²³²¯¯°°°°¯±±²¯®¬³µ¶µ·³µ´·²´³±°±±´³²°±°±²¶·¹¶¹³³³´²³³¸¹»½½»º¼À¿½º¹º¹·¶µ»ÀÃÂÂÁÄÅÅÄÃÃÄÀ¿¾¼·µ¯§ž˜’‰€p`[Y|®Øò÷úüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöíæÛàÒc839;>EQZXOD@638<82/<AMJIE?9.+%&+7;<=@B*$#$$$"!! #)/5:?CHONNTXYZZY[]cggkkhhggcdedeghiigfjeeeedddffffghhffffceeeffekjlllmjhhhjlnorrsorsrqporusstotrsrqpqoqrqnnnoptsrqtvvsrqrvqmnnnmmmookimopooprtuutttqtsussoqqqooosrrqpntqolonprqoolmmnl\NUbdgjikkjhe`efkjiijijjoolggjlgiiihidcbb_`_beeggehilfikiijihggedadaaa`\^]b_edffhhokkkjjjkjhighjmhœ™›—™”’’’•œŸ§¢£œ›–š›œ—™”“–™—œœŸ›™—“‹ˆŒ’š“””•Ž“’™™šœ£›’t‚…“Š‹‰ˆ…ˆƒ„‚ƒ„…ˆŒ“˜š›œŸ¤ž ž žž ¢¤¦¦«¨©ª«ªª¥¨¦¬¦¤£¤¥¦§ª§®ª°©ª¬±±´¯¹¸···²¼´·µ»´¸²²²¹¸¸´´µ¶µµµ¹··µ¹µµµ´¶¹±±±±³º´µ°°°µ³´´´³´µ¸··³µ´¸´¶²±±±²µµµ²´²²·»ºº¹¹¶¸´µ¶¶·¾½¼½¾»À¾Å¿À¹¼·µºÂÄÆÄÉÃÅÅÊÆÌËËÊÉÉÉÂÀ»¼¶±©£›–‡qaWYºÝòöúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõìæÖßÈ[637;>FP\dJ:0).654/08D?:;<<:86'%)31025/%"!! !!!! !!!!%*07?FKPQRV[\\]_afddekhjjnikfdcldehmlpjjhkefhkedeijmikiheeffdfeffhdljpkkihihjlnppvutqwsqonovvussstrvqnnoqwrsponqsysrswvvsupoppqxnomrstnmmrqqpppvtyxwvutztvsrpsruoos{rqommsqnmpqvsronoppr_PP_cginjkkjgabihjjmjlkkjwnhgklmjjhihieebbbabfelhjjlllioklhhhfglddde`bba`a_bbgfhhmjpkkkkkjjoklikkmh˜—š••”“’”–œŸŸžœ—›—––˜——••–––—˜—˜Šˆ‡Œ’“••’‘Ž“’˜š™ ‰~zq{†…†…„„‚‚‚…ƒ†‡Š“™›œœœœžŸšŸš ¤¦¨¦©©§ªª¨¦¤¢¦¥¥¥¥¥¦¥¥¦©¤¨©ªª¯³µ±®¯°³µ´²²³³³´²¶²³³´°°°±¯¶±µ¶¸·¸µ¶³¶¶¶¶µ±±°±²²²´¯±²´³³²³°´³´±±²µ´³³´²²±²³³³µ±´µ·¾ºµ´²¹·¶¶¶³¶·¾¹¹¸··ººººº¸»´¹¾ÂÂÃÂÅÃÄÆÈÇÇÇÊÊÉÉǾ»»»¹¶¬¤ž˜€o^Z[ƒ¾ÞòøúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõëåÚݪR56=@BFQ]O<-!%*42.+,.,*-08D9@2)&(()('%%#!! $(.5<CINRWUUW\^```bedeffggiiigfdecfdhjkkkkhfedgkfcehkkjjjifcfdcbdfikhdljighghjklnoooooppppomopqrrssssrqqolpsurrqportvstvtrsstnmmpuqlomsqnkopstrnpprqqqqqusttssrorqpmqusspnnnoponqtsqponnop`QTZ_dgjkkjkieeghhjkjhhgjkjieejlkjjggfedebbbddggiilmkijiiiiffefgfcbbbab_a]`_aaffghjihhiiihhhijjhihih™˜š›š˜—œŸŸ›œœ››Ÿšœ™™™š–––——•І‡‰Œ”’–••‘‘ŽŽ‘“š¤•~o|w|ŠˆŠ‰ˆƒ„‚‚€…†‰–—š¡¡¢£¢›œž›ž¡£¤«§¦©©ª°§¤¢££«¥¦§«¨ª¨ªªªªª©±®µ°¸³°¯´²²±µ°±²µ´¶¶·³·®²±³³·µ»¸··¾µ½¸À¹¼µµ´³³³²·´´³¸··´µ¶·³³³³±²²´µ¶··´´±·³³´´´··¹¹º¶¶µººº¶··¼¼¾½½µ´¶¼¼½¹ººº»ÂÂÇÄÅÂÉÇÍÌÌÉÍËÑËÎÇÆÂ¾ÀÆÄľ·¯¯¡™„o^Y^‰Äáó÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôêâÖÜ‹G5<HKMRUL?0#!#*1-+)(&$$%,69:4-'$#$$#""""! "#*07AFNOVX_[Z[\^cchefekkjihjoijeffghihmlnmnigedehfddgirihimgeeeddbcfmjhhkhgfhfikoputtoqpqqppooursrqs{stsrrrrttzswttttu{vxvtttstpqorqnnnosomnvswtuuutuqqqprutttwrqnxppqvuspnotnmnoqwtspposoqdUQ[adhjlnlljhhijkimljjjinllcbfnmpjjgfeccfdedigkhjippnjlmmikfgdiedbcbbbbbabccddefhhkjihliigghkkjhjhhiššššž™——•˜˜ž˜™™›œšš›š˜™”–’Œˆ‡ŠŽ‘’’ŒŽ‘“•’ ‡|vjv‰†……€ƒƒƒƒˆ‰‘—›š žœ™™–™›Ÿ¢£¥«¦¨«ª©¦¡ ž¢£¬¦§¨©¨©§§¥¥¦ª©°¯¯¯®®®±¯¯®¯°´´·µ³±±®²°³´µµ¸º··¶µ¸¶¶´µ±µ·¶´´²µ¶´³·¶·²²°³²²±²°±²µ¶µ²µµ´²±±´¶¶¶¸·¹µº¶¶´·µ¹¶·¸»º¸´³´´¸¸¹·¾ÀÁÀÂÂÃÃÂÂÈÇÉÉÌÆÉËËÈÆÃÂÂÂÂÅÇż·°¥Ÿ•Œ~ob__‰ËäôùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýóéáÓÎq:3CEKQ[M>2&$$%+)('&%#""$(+.,*'""!#""""!!" !!! !#(-4:@FMSTVX[^\]___bbccdekijkjlhdcafeffggllmkifgcegffehghidfedddddbdceghhiikgffggjknpqrpnpqommmoptrsrsuurrrtsssuvvsttusuvuuvwussssqponkmmnpmjnruuvssqploqqprrrqqqroommmrtttsnnnnmnpqrvtqmnllleYST`ijmnqnkjjjjjjjggfgeijkma[dhkkjiiddccdddeehhigjjjjkllklikefddacbbabbbaa``aefedhikmihjihhhhjjighgim¢¡¢ ›š—–—˜šžœ¤ž¡Ÿ›š›œ™™––‹‡ƒ‡’’’‘‘‘‘—šœ›–yixox‡Š†…„…ƒ…€††ˆ‹Ž•—œœœ¡¡œœ™™——¢¥©¦«§®¬¬¥¢¡¡¡§¤¬§¦§«ªª¦¦¤¥§ª«±¯®µ°³¯´¯°¬²³¹¹¹¶·³²±²²²³µ¸»¶¸·¶¶¸µ¾·¹¹¹··¸º·¸¸¸¶´µ¼µ·±²±´´µ¶µ´´µ¶µºº»·¸···½µÂ¶»¸¹ºº¶¸¹»ºº¸»¹¼»º³®°´µ·¹º¾ËÁÃÃÇÂÄÃÁÂÈÇËÊÎÆÉÉÉÉÅÅÊËÍËÈÈÈÁ¿¼º°¡šŒre]aÐåõøûýþÿÿÿÿÿÿÿÿÿÿÿÿþüòéßÓº\319BGUNC3)%$$%&%%$$##%"#%&''%$!!!""#"!!!!!%*07>GKPT\]\]c`_^ba`_cbecdelkojigfcaafflfggmmoigfghjggeffhhiejfgccdghiiijjikjkhiiikpnrrrqonqonmnlprttutzuvtwtvtxuzvwsuwyuxx}wxxwrqrvrsnmlqnrmjlpsuvzuyrqnoquuussrqquqppqqxvztrqrqqnnpusvrpqrlnidST\jkoppommtjlklkjjkjihmnrbX^mkokjigceedfjjjkmllkjkmkkllklikeheeeedeabcfa`_`bifedhimlihijihjklihgkklj£¤¤¤žš™——˜šœœœœœžœ›šššš—”‘‡Š‘‘‘ŽŒ‘’••”–š‡}tdr{‡†…„€€€~‚€†ˆ‹”—˜šš˜œœœœ˜›šššž¡¤§¦¥¦£¤¤¤ ¡¡¢£¤¥¥¦¦¥¤¤¤¤¥¥¦§««¬¬®©¬¬¬¬¬¬®®³µ·¶¶µ³°°®²²´¶·»·´´²µµ·µ´²²°¸¶·¸¸··¶¶¶¶¸µ¯®®²±´³²°³²´µµµ¹¸º¶¶µ··¹µµ¶¸¸¸·¸¶¹º¹···º¸µ²²²±²µµ¸º½ÀÁÂÃÄÁ¾ÆÂÂÃÆÅÉÈÇÁÀ»ÉÉÈÆÉÊËÌÅÁÀÁÁ¿¼¶²«£—teccÓéõùûýþÿÿÿÿÿÿÿÿÿÿÿþüòèÝÕ¦R.-05:>D3'%#$'$$$$#####""####""!! !"!! !%(/5;BHQRSX]^a``bca_ba``bbcdeflnlihecbccdeedghjjhefffffggefdgfedgfecefihiklkjhkkkihhjmnnqsqpoormmmlmprrrusrpututtturrrrruxyswwvwyztorqpmljmlqllimquvvvussrpoqrssvvutsrrrqqrswvusrqqrqjoputvpqljiifUIXajkmllllkjikimjjhiijinpfZ^dijkijhgccceghfiklklkkllklnlllgfeffeabcbabcca`_abiedbfgnkjihhhhjgkhhhfbji©©¨¡œ›™ž›¤œ›œ¢¡¡¤¦ž¢—•‘‘’”‘’’’ŽŽŽ‘“˜—ŸŸŸzkght‹„„„„‚‚‚†Œ‘•œ›Ÿ™šœ¡¡££¤ Ÿ¤¢ª¤¥¥¦¥§£©£¤¡¦¢¤¤¥¥¥¤¦§¨££¥¦§¨«³«««¯®¬®¬¬¬¯³¼µ·¶µµ´´µ²·¶º¹·····µ¼¶¸´´´´´¸·À·¹¸·¶¼¶¶µ´´´´º´·²±³¹¹¹¹¹·¸¸¹¹¹¹¹¹¹¶¶¶¸¸¼º½¾À½»º¿¸»´°±²²º¹¹¹¼¾ÃÀÆÅÅÁÀÁÇÂÈÈÊÅÎÄÆÁÁÄÉÉÒËÐÉËÅÁÂÂÄÇÃÁ½¾µ°¦ž’‡xkcg˜ÜèõøûýÿÿÿÿÿÿÿÿÿÿÿýúðçÛÔ•J+))*/30'$###$######""###"!"""#!!!!"" #'-3:@FPSWYZ]__`bcbddebbbcbcfifglsnlhhddcdehiigghjgffgflgiijeecgddfleefkkriklnmlllllihikmtnqqqopotllkkmpqvtuqpqvtws}twrssttxxyvvwxxzvsrrpokijmlqllmuuzyyuvvwrpqttttywyxxttsrrvvwxwvvvvrqoprwuvpqkkikWJRgclnqnoosllhojmjjijjnkqjb^gejilijggefffhkjjlnkolmmnoolkklhjfgfea``_`bbdbbbiejddfljomqnlklikkkkmhecig¡žžœ››žœ›››œ£¡¡¢žš”ŒŒ‘‘’Ž“”––‚r^joy‡††ƒƒ‚‚€ƒ„„‡‹‘’—™œŸšœ ¢¢¡¢› ¢¤¦¤¤¤¤¤§¢¡¡¤Ÿ¦¡¡ ¡Ÿ¤¤¦¢§£¦¨ªª««©ª««««©««¬°´´´³¯®®°°²³´±·¸ººµ²²±³³³²¸³³±´µ¹¸·¶µ³´²²³´³µ·¸·¸²³±²³¸»¹¸¹¶··¸¸¸¶¸´····¶·½º¾¾¿¿¿º¹´³¯°±³³¹µ¹»¿ÃÂÀÀ½¿¿ÀÂÂÂÇÇÇÅÁ¼ÃÁÇËÊÉÉÈÆÃÁ¿ÄÅÇÉÇÅÄÀ¾¼¸²ª •Ž|lii—ÔéõùüþÿÿÿÿÿÿÿÿÿÿüùïæÙÕ…C*(''''&%$$##"###$##"!"!""!""!!"!" #&+/6<BHKPV[\]^_`abdb`edeccccdhmigjponjfc`dcefghhihfeeeigggdcaieecccfgfdgijiiijkkjjjmolhjjlmnmonooommjkjmnoorupmpqqqssstsrrmtuwuvvwyywzrsqpnmjklmmlimpvvxwxuutvqrsuwvtwvusuuutttuvussprqqoomprutspqklfXJS`adnpnnopmgjikjjjifghmihb``gekjlhgffdeeijjjlpmkojjikonjkjmfeeffcaa___babbccdeeaehklmkpomllgjigehhgdee¡¡¢žŸ ¡¢Ÿ¡¡ ¥¤¥¢£›™’Œ•””’”Ž‘˜˜™›œykf\lƒ‚†‚‚‚‚„‚€€„Љ’”œ˜Ÿ›¡ ¡ ¡£¢¢ ¢¦¤¨¤¥§¬¤§¢¤¤¤¤¦¡¢ ££¤¥¦¦§¨®¬¬¬«°¨©§§«¬ª«®°¸¸¸°¯¯²³µ±¾º¾¶³´¶µµµ¶²¸³µ²¼·¹·¶µ´²¶³·¶¶µ¸¶¶¶·´´¶º¸½¹¸¸¹¶¾»¿¸»·¸·¼¹ºººº½¼¿¿Ã¿Æ¹¸´²³·¶´¶¹ºÀÀÁÁ¿¾¿ÃÁÁÁÆÅÍÂÁ¿¼ÀÃÇÎÌÐÉο¼¿ÀÄÎÈÊÈÈÅÊËÎÅĽ·«¢›}pjn›ÜêõøüýþÿÿÿÿÿÿÿÿüøïåÙÖz=)&%%%&$$$$####$##""""#!"" !!!!!!! $)/38>EIORTZ````abgcgcbbefffffihmmmnonlkjdbbdchhkjokmhfddfhgfbacjfecbdkgeglkjgghqllikkmmljnownqoqnnnokkklmpppprqmntqrqttwssrqqutxvwu~}{xyrxpnnmlqrrnllssww{wyyytuuvvxw{uxvuswwwvww{vtsuutpoprpwtytsoqmo\OP`abipomnoollmnojkjifgimjeb_cgekllggffgjkmmqmnmmlollijmnkkjoihgjebbd`b`baccfflegffinmqmoopllhiigfggkfgh¡ ŸŸŸœžŸŸ ¢¢¡¢¢Ÿ–‹Ž‘“‘ŽŒ’“”•…‚pZefsˆ†……‚€„ƒ„ƒ‚ƒ…‡‹Œ‘”–™›˜Ÿ››œŸ¡ Ÿž¡ Ÿ¡¤¤¢¤£¥¨¦£¦ £ £¢¢ ¡ž¢¢¥¨¨¨©ª¬¬©¦§§¦£¥¤¦¨«§««¯°¯«ªª¬©¬®³²¶°µ¸¶±´´µ¯®®°°´³³³´´²°±¯¯¯²±µ´´´¶´´¯¶µ¸»¹··µ¸·¸·¹¹¸·¼´µ¶ºººº»»½¾¼¼¾¿¼·¶³´µµµ¶¸»½¿¿¿¿¿¼¾¾ÃÂÁÁÆÆ¿·¿»ÂÇÇÌÊÊÇû®½¾ÅÈÉÇÈÄÅÅÊËÌÈǽ´¯§›rlk˜Þìõùûýþÿÿÿÿÿÿÿû÷îåØÓo9(&%%%%$$$$######""#"##!"!!"!!! !"(,27?EIMOQUX]ecbccddfccaccdcfeggijjkmnojheeadhediigfiiigedeegbf_bgeccadffdgmjffefhiiljkkkjjjonnnnmmjklkijlnpomopqpoppoqqsrpooorsutuuuuz|zzuqqpmhlmpspmmptuvvvsusqpuwvvxxzuxutsttvvwwvttsvusoqrrqqqqqpmki_RU]_ahppjkkllljjjiffefdhkml\Taggglhgggehjklnnolnonlnklhjklklhhihhfccbb``_``bbddeeggjmmmnllllljghhhhgeefgh££¦¡¡ §§¦ ¤¤¤¡¢Ÿ›’Ž•‘‘’—‹Ž”•–˜˜ykeYfxŠˆ…ƒ€€…„„„„‡‹’’˜—™™œ› ›Ÿ ¢¡¡¡¨¡¢¡£¢£¢«¥¥¤¤¤¥¤¤£¤£¢ ¡¡§§§§ª±ª¬ª¨©¨¦¥¢§§§¨ª¬±²²®¬¬¬¬®±´²·°¯³··º´´¯°°´±µµºµ·³±¯°¯´¯±²¶´¸´¶µ´µ¶¸¾½»¹¹·¼¸¹¹º¹··½´·¸À¾Ã¿Â½À¼»»º¹¹¹»»º··»¿¿¿¾Æ¾¿¾ÂÁÁÁÂÄÈÇÆ½¸¼ÀÃËÆÆÇÇÄÀ¼¶»ÁÄÌÉËËÑÎÌÍÎÌÎÊÍÇÉÀÀµ©œ}ogkßêöùüýÿÿÿÿÿÿÿúõíäØÏf5('&&%%$%$$$####%""""##!#"!!! !%)06=DKQUYXWX^`ijiegghffffcefheggkkmmoosnoifdddkffgjhffjklfeegghceffdbbbcgfeemkiffefgjimmnkliomqnnmonmihhhlvpqpnosqqqsppqtstpmmmottvvvuyv{ywutuvollqqqnlmpsywyuvtxqoqvwxwzz{vxyysutzyyvtrws|usqusvrtqpppjhaWT]`agropikltlllligefefgklraV[kkoklgffghmlqoroompppnrmmlmmnkqhkihhfdfcc`b__adcddhfgkonqnomlmqmjhjinjihigge¢£££¢¢£¡¡ ¤ Ÿž˜“‘‹Ž‘“ŽŽŽŽ“”‰p\ddp…‚Ї†…€€€‚‚„…‰Š‘’•————š›œžž ¢ž ¡¡ ¡¡¢¢¢£¦¥¥¡¤£ £ Ÿ Ÿ¢£¤¤¦§§©ª¤££¤ ¡¡£¥§¨®±ª§¨©ª¨«¬¬«¯³±°°¬¯±·¸¶³µ°°°°°²±°¯¯¯°®¯¯¯°°°±°°°±µ¸¹¹º·¸¹¸¶·µ¹·¹³µ´µµ¸º¿½¿½¼¹¹º·³²²ºº¹·¸·¼Á¿¼¾¿¾º¿½¼º¿ÀÄÇ¿º¹µ½ÀÅÊÉÅÄÁ¾¸¸ºÁËËÉÉÉËÌÎÏÑÔÐÌÌÊËÈÈÄÃÀ³¦˜‰}ollšÞíöúüþÿÿÿÿÿÿúôìãØÄ\3('(&%$$%$$$#####!""""!!"#%(.3:AGPTY[^]]]]aceghcfggdfffdgjfegillmmopnjieeeddeegjjhggjjgdeeedcceeeaccdeddeffdffghfgiimmjhljolpmlljfedffjmppqqqqoortrnpsrrrmmloppqvutryuxvtqqpoknpqkjilnrtuuutwrroruvyvttttsxuutttvuurrrstttspvttqpooqlfcZWY`ejoomljllnkkjihgdeeghjlaT]fjiiihefgjjlmmnommlllnmmlljkijjjgigghedfcb___`bcddbieiopnmmononpmkgggnihghfgc¦¥©£¤¤¤£¥ ¥Ÿ –‘‘‘‘“’—”~reWetz‹‡ˆˆˆƒ€€~‚ˆ‹’”““–›˜œ™žš›œ¢ ¤¢¢¥¡ ¤¡¢£¤¥§¦©¡¥ £ Ÿž£¡¤£¦¤¦¦§¨©¥¦¦¦Ÿ¡ ¡¢£¦ª´¯±ª§¦©ª¬¬²°¯¯µ¯®®¯°²²ºµ´³´´´³·±²²±±²¯±·°±°´°²²³²±´º·»ºº··¹¾¸¹¹¹¹¹³ºº»¼¾¼¿ÀÁ¼¼º¾·³´·µº¹¹¸¸¼Â¾½½¾¾¿¼»¾ÆÂÉ»µ·¸¼ÈÄÉÉÊÃľ»¿ÆÅÏÉÇÊÍÍÌÍÜÓ×ÒÔÏÐÐÑÌÌÇʾ´¨›‘‚tkn âìõøûýÿÿÿÿÿùóëâÔµS0'''$$$#%$$$$###$"###" "%*08@GNTX[__dbbbbefeeghikhgfjgfgkifgkmrnpmrnkiiddejfhgjjnhjhkgfgjedeffifffgggdccdeeeffjggglmmjghmlonpkkkjdadeflmorwtvpnowtqnpswqqlmmurxqutrqzvyurpoomntqqkjjpqvvwuvuxrqqvvvwwsrtyxxustwxzwvsvr{ttssrvtxrooplkebWW^ciuppkllqmnkljjhkjiiijocXXhijjjhhhghpqspsoqmnllkonmmmlljrjhgjfgffeeba`a_cdkfeehgkppmmmunnnommjkjmigfgghg¥¥¤¡¤¤££¡Ÿš–”ŽŽ‹Œ‹’–Šsbd\lƒ‚Š…‚}~~€‚„ŠŽ’‘”••–—•š˜——š››œŸŸž¥Ÿ¡¡¤¢¤¦¤¤¦¤£ žœ› Ÿ ¡£¢¤¤¥¢¡¡¡ ŸžŸ›¢¢££¥§±¯««§¦¦«¯®±±±®¬°°±³µ²²³²²³´³±°®¯®²¯°¬±¯³®±²³³µ¸¸·¶´µµ¸¹º¸¹¸¶µ´³¹¶º»»»»¸···¸µ²´µ¶´µ²¸·¼À¿¿½½»½¾¼º¾¿Á½¹®´·¾ÁÂÂÂÁ¿»¹ºÁÇÈÈÊÉÉÊËËÍÎÓÐÑÎÏÏÐÑÑÎÌÇÇÄÂÀµ«¢•†uqs áìôøûþÿÿÿÿùòêâУL/'''%%$#%$$$###""!""!! "$*/5;CLPXXZ]``aaaaadeffgiiijhggggghhhhhknnlolkiihebdfgehhiiheeeddgkiefihfeeggdbbbccddghgegeghhfffghmknolijjjbdffgkmpssrtnpqqqpmqqqmnlmnnooppprrttttroooonpnlhjkoqrprsttuqrswvvtsrrrtuvsttwxwuwrsssssotsusstqplhhdWQ\bfkoooimlnmkiiiigggjjigcZ[^gjjjigggijqsropnmjjjlihhjjmllkkkjgheeecabbb___edghhghhmoomlkmmnnnnlhjkkkgefeec¦£¢££¢£¡ž—“‘‘‹Œ–—™ueVbktЇ‰…}„~~„‰ˆ‘•–—™š™™•š™˜—™šžž žžŸ¥ ¤¢¥¢©£¡£¨£¢ ¡žœ››¡£©¤¨¤¦¤©¡¥¢¦žžŸ£¥¨©¬§±¯®®®®²°±±·´¸ª²¯³±±±·±±²¶²´³³®°°±¯²®°ªª®³¯²²¶µ¹³¹¸¸·¶´¹µ¼¼½¸¸¶µ´³µ¹º¿¿¿ºº¹¾µ³±¯³¸·¹¶·¹Á¾ÄÀÁ¾¾¿Ä¼½¼»¿ÇÁüµ¸¾¿ÆÃÌÄż¹¼¿ÂÈÆÍÉÊËÌÎÒÒÑÐÕÔÔÏØÖÕÒÔÐÓÌËÍÐÅÀ¾º©‹|tw£ÝéôøüýÿÿÿøðéâÑ’E.''%$$%%%$%####"" !%*18?GMWZ]aefgghbbaddggjimnojnhjikhhgfhkkllmloigfddcdddgfjhkihfgeffjihfjjlfeemeaaabedhhkjjijjjjjfhfjjmlrljhmklkkknnoptrqqtprrsoporrrqpoonnorppqtstuuuursssttliilosruppr{tvrutwwwsttuuuuztww|wwvxrstvststtzsrrtojiiXNVgglnqnomumolkhmjkfgijilg^\^bgilihhllmmrqrssmkighkkkhiinnplmmmjnffcbbebb_`bedghoikkooqmlklmpnolkkkkkheehege¢ Ÿœ›–“ŽŽŽ‘ŽŒŒŽ‘“ŠudbXhx|‰†ƒ}~~}~~€‚ƒ…‡‰Œ‘•˜š˜˜™—˜”””––™šœžžŸ ¢ £¡¡¡¢ ¡¢£ ¡ œ›ž £¥¤¢¢¢¥¤¤¢¢¡Ÿš ¢¦¨©ª©§ªª««¬°¯°°±±¯®ª¬°°®®¬®°°²²²°´¬««¯¯®¬ª®¦«¯°¯²³µµµ³¹´´±³´·µµµµµ·´³²µ·º»½½¿¶·¶µ¯¯¬°³¶¶·µº¿¿¿¿¼½º¾À¾·º¹¾ÁÁÀ»½ÃÅÆÆ¾¿¿¼µº¼ÄÊÇÆÉÇÊÌÐÒÑÐÐÐÑÑÑÏÙÖÔÐÐÎÏÌÎÏÎÈÆÅ·®¦’xtÜêõúüþÿÿ÷ïè×Ð>,&&%$$$$$$$####" !&*/4;CIQTX[^cifeeeha``ddhhiinmnjjggfffgfgikfllmjiggcbbbabcgfgffghcddggifgfiljhgggcccbafeghhhiekijfffeekmmlkhjimmmlmmnloppqpnpoqqsmpppopoononoqqopqsknnoourqoqnlgjkpsrrtpoottsrrqwusqqqrprsstwyyvutxqssrruxvttqrqplkjZMU_dhmonkkknmifhhhggfjkkhhd`^cgghhfgimnnopnnkjjkigfmnkhhgfffgjiihhgdabc`]c_bedddehhkknnnlkklmnnoikggegffeee`\£ ¡ ¡—•’ŽŒŽ‘˜™š…€hYado‡‡†‡†€}|~ƒƒƒƒƒ†‡‹‹Œ‘–˜œ™˜›—™Ž‘–™šš› žŸ £¡£¡¨¡¢ŸŸŸ£¡ŸŸŸžž¡¦¦¥¤£¢§¢©¥¥¤¨ Ÿ¡ §§«©§§«¬«µ³¸²´¯´®¬¬©³±°®±°±°¶µ´±³ª«¬©§©¬¯±°´³¹µ¹´´´¸¶¶³µ´ºµ¶µ¸µ¹º»»¼º¿º»½¿¶º´±¯°±¶µ¶¸»»Â¾Ç¿Á¿Ã¿¾¼Á¿Æ¿¾ÀÃÄÇÂÉÆÈ¿À»·¼ÇÈÊÇÆÉÍÎÎÑÙÔÕÕÕÖ××רÚÖÕÒÒÑÔÔÔÑÏÑÒÑÑÅÅ´§’ttŸØæõøûýþöïçÑËu:+&&$$$$$$$%"####'+19@EKS\^`aafniifgfgfghjinnmkpoojiggefgihhhlkklmhfghbbbcbbdffifhgheddghjiiiijkjihgggcdcffkkmijkljkfhgjkmopkjjlmrpsoqqqopppppqroqpsotttosopppqvsvttrtnnnnouqpowmkippvs|utouqrssssrwtsqqpwpsrttxxyvuswpsqprvwwtsquqpnq^RR\`eksnmlpkmjgfhhgghgokkhib`bhjlihefinotrroplkikjhhnlkjigdefgjjjijebbeb`_b`eejgiijkonnonkjkrnnmpjlhhhhffhld]_ Ÿœ˜”ŒŒŒŒŽŽ’•”’yjdVguz†„‚€}{{z~€~‚ƒ…‡‰‹Œ‘”—˜šœ›–•“‘‰—œ›™š›¡ž ¡ŸŸžžœš Ÿ £©§¥¤¤¤¢£¡¡¢¢¤£¡ ¡¨©©©©§¨¨©¨¬´±²²°¬¬©¬ªª¨©ª¬«¬«°°°±¯®®©¨¦©§¨¤©«¯±±±³´¶³³°³´¶¶¶³²²´µµ´¶µ¸¹ºººº»··µ¹·º°°°²³µµ¸¹½¿¿¾¿¼À¿¿¼¼¼½¼Â¿¿»¾¿ÂÂÃÂÃÅÀ¹´¹ÂÄÅÇÇÉÌÍÍÑÓÕÓÓÒÓÓÕÓØÙÙÕÕÓÓÓÔÐÒÒÒÒÒÒÏÇļ²¢‘€vq—ÎæôùûýöîæÎÅi7*%%$$#$$##" #%*/5;AJNQV[^aaafljgieffgeghklmmlkkkkhgfffhhiijhmpmlleehc`bbcdefgffefgffedfehhhhhggeiheegbdcefjkjhijjkhdggjiihjjklnoooonpononnpnnoonnnolnnooomorrssqutrponnmopvpqplfiipsssvttpuqqqrqsrsrrqqqpoprssuvzsuqrppprtwtssrqpnmn`PT[^bjmnnlloklhgehdggghomjfb]ciikiggehknpqqpoomkjjiiijjjnc\^`ffkiikkbcce`````ffffjlllnoomljkllkjjkhiihghehkhc`c ™™’Ž‘“Ž’“•—•š‰„l\``l†„††€}{yz{|ƒ‚ˆˆˆ‹Ž“”˜—œ›š›œ••’’“––¡œ›šž¢¢§£¢žžœœœ¢¡¢¡§§¨¨¨§¦¢¥¡¡ ¡¡¡¤£¥¤©©ª©©©±§ª«±®µ°²²¯©©ª¬«®©ª¯ª¬¯®¯«ª¬±¨¨ª«ªª®²²¶¶¶´·µ»²µ°·´µ¶¹²³³¶´¸¶¹¸»»¾¹ºº»¸¹µ¾·¹´´³·µº¹ÀÀÁÁÁ¾¾½¿¿ÁÀÀ¿Â¼ÁÀ¿¾ÁÀÈÆÅÂĽº·³¸ÅÂÃÅÊÉÍÎÓÑØØÙÓÒÔÚÖÙÛÛØÜÖÙÙÚÕ××רÙÙÚÕÕÌÊÄÄ´ª‘qn›Õäôøûôíå̶^2)%%$$#$#" "'+18?HMQTY[abiijlmlkhifffgillpppnnkjjmgfgmgnmmjlkpookkeddaadfihlffggeegkggdfgmiggkgiiifdefcddegjjjijkkigejhohiijloosoonqpuppnmmppuonmpoplnnoooooqustqwsqnopqrssvprlgelnwwwuwvuuvqsstsxxyrpqwqqopr{utu{svqttsswwwsqqrqpppbWVYZbiwpqnllolkkkgggggmlqlib_`iikihgjklmqqrononkkkkiijkiidZSR`eekihhlagfeeeb`bghgionmlqqtmkjpmmlmloijilghhmihaaf•ŽŒ‹ŽŽŽŽŒ’“–’“}oeVdowˆ……„{zy{~}}€ƒ…‡‡‹‘‘‘”––––“••••””–œ™˜˜˜››žŸ¡¡ š™˜š™™™›™›šžŸ¡¢¢¢¥¤¦¡¡Ÿ¦¡ ž¡œŸŸ¡¡¥¥¦¤©¨¦¤©¨«§ª««ªª©¥ª«««©ªª®ªª«¯«°§ª«°¬®ª«¬¬¬¯±³´µ²³³³³µ²±±³³³³³²²²µ´··¹¹»º¸´µ³¹¸º´³³¶µµ³´µ»º¾¾½¹»»¾½½½¿¿¿¿¾¼À½¿¿ÁÁÃÀÀÀ½¶µ¶»¾ÂÂÃÄÉÊÎÏÑÑÑÐÐÐÓÔÕÕÙÚÛØÚÖÚÖ×ÓÔÕÙÙÙ×××ÓÌËÇļ°žŽ{vu™ËåõúóìäǧW/(%%$##!! "&*/4:@HPUYY[]_cdhijkkjiiidghijkklllmlkjihdfhlfikkjklmkjjjfddccgjjhkffggeeeeedcfhgggffeddfdddfbeeghhhiijhgfeejhhhjilnoonnnmoppnnlmnqrpllmmmljmnoppopqsstpppqnoppqrsvqlfffmpuvusvrtssrrssswuuqqqpppnqs{vutssrqrrsttssqppnkjjbXVTY[fonllmmlllmmjfgeghmlmjc]`dfgffghkmmnqnmlkkkhkkkihghhh]J<Taffkihfeabababbdffbhlmklmrnmmlkklmimlniihkfhlmgg\el’Œ‹‹•Ž‘‘“’”™”p_]Xg|Œ‹Š‚€~}{zz}}€€ƒ‡‡‰‹•“˜–›–••˜–—––—š›š™˜ž£ ¥ ¢™š˜™™˜œ £¡¤¤¥¤©¤§¡¢ž¦Ÿž¡¡¢¡¤¦¦ª¤©§¦§©©°¨««¬¬°¬©ª¬««¬¬®¯®ªª«¯¬°«¯®®°±±µ²···´µµµ³¶µ¸¸¸±´³¶´³²¶µµ¶¸¸¾½¾º¸´¹¸¸¸º´·····¶º»Å¹½¼»»¾½Â½À¾ÅÄÿ¾¾À¿ÄÃÂÃÆ½¿½¼¾ÁÃÆÅÅÆÌÉÓÏÔÐÒÓÔÔÔÔÙÙÛ×ÚÚÚØÙØØ×רÛÜÜÜÜ×ÜÕÓÒÓÌÊü¯¤“„xv›ÏâôïëÜæQ.'&&#" !$).39AGLPW[`beghfkhmnolnkmikkljnmmkkkkmrkiighjjjiikmjloplnijfedgfnlnijghgfdeghccdffeffehhhefdcceefimlklliieefeekhghjirrrommpmsrqnomnouppprmplklrnoqstvsxxzprrqqrrsquswmheiltrtutrwwxtrrrsuswtvvwtrrrput|wxttrxruswutttpqnkjldZUYY[bnnnktoonmllkigggllmlme_^dgkgihnnommnqnokkjjjjkooohgggO=CXbghkkkfhcb`fccdhhfemmnkonqnppqmsmnlmmnmnjkjklmgehmmŒ‰‰‰ŒŒŽŠ‘‘‘“‚xiV`dp…ƒƒ‚€y}~|||}„…†‡‰‹Ž“”˜–˜––––———˜›™šž›š™žžžœœ™™™šš™œ ¡£¡¤¥¤¤¤¢¢ŸŸžžšž›¡ ¡¡¦ª©§ª¥¦¦©«©¨§§¨§®©¬««ª«©®°¬«ª©¨«««¬®¯¯®¯±±²±´´µ²²¯³³¶¶¶µ³±³²¶´´³¶¶·¸¸¸¼»¹¶µµ¸¹¹¸¹µµµ·´¶¶ºº¹¸¼º»»¿½½½À»¼¼»»¾¾ÀÀ¿¼¾½º´³µ¿ÃÃÂÃÃÅÇÇÇËÍÐÏÓÌÌÍÔÖÙÙØÕØ×××ÙØØ×ÙÛÜÜÜÜÜÖÛÔÕÔÒÎÍÈÁº´ª™‡~v–ÈáëèÒ¸¦M.&#" !!&(-08>DJNVXZ_ccdeefffgmkkkkimjjhkkmkkjkjknkiighklmjgjllkmqnkheeeffefiiigfedcdbeheacbeeeeeedcgeeeddgiikiedeeedbbbefhgggkjjkklmlmmmlnlmnopompormoilmrnprrsrrvxsoqonlonsrqqkdfflqqrtttsttussssstqsrttuuqnootsttuutrrptsuuqoopnmkjh\YVZ\dknqomonmjkiiihgggmonke_`bflkfhinmlkmmmmlijjjjkkngihheVEJT^gijjjjggb``dcdfhiffghhioonloomjkllkmmmnkic]dhigfghhŽˆ‰ŠŠ‹ŽŒ‘‘’‘—•“wi_Ucv{Œ„„ƒ‚€~~}~||}~‚†„‰ˆ‰‹Œ’“™•—˜œ–—–›™žš™™ššžœœ¡ŸŸ œ›š›œŸž£¡¦£¤¤¨££¢§¢¤Ÿ ŸŸžŸ¢¡¦¦«ªªª§©©¬ª©§§§©§¯®©´°¬®ª´®±«ª¨¨©¬®²³²²²°´²·³µ´´´¹³´²¸´···´³³¶²·¶µµ¶¶ÁÀÀº¼»¹¸½¸¸¸º¹ºµ¶µ···¸Á»»¸¼»Â¾¿¿¿ÀÀÁÁ¼»¼¾¾ÁÀÀ¾À¶²´µºÂÀÃÄÈÄÉÇÎÎÍÍÑÏÓÎÐÑÖÖÚÚÙ×ÚÚÚÚÚÛÛÜÜÜÜÜÜÜÜÜÛÛÛÕÙØØÏËÉȾ±ž|t•ÄÚå̱F)#" #&+07>DKPUX[^cfigfgghiffgnjjkkjmlonolmlolnmljiihhjkolmmmlooonmfcdfgighgkhifedcchggedacaddddieddhfhhkjijljjdfdjedcddfhjjjjlkkklknnnlkmplmnrnmlppsnnnoosostsrpqvroptommsorrumfdhkqssswtvt{uutwststtvstuvsqmoqttssyvwrsqsrxronrolkjheYYY_fqrropponljlipikiijmopf`^cehjkfhkvmljsnomoimjlkokliihj^QLW^ggokkjmgla`cicffjgfghgfhqnnlonmmmmokrnomhd^`chkffgkgЉ‹ŒŠˆ‚o\a]l‚„†…‚€}||~~{{|}ƒˆ‡…‰‰ŒŽ‘’”–”—˜—”–•›–™šš™™šœ››œŸžœ™™šš˜œ›œœžž¡¡¡¢¤¤£¡ ŸŸŸž›ž¢£££¦¦§¥¥¦¨¦©©«©¨§§§¨¨ª©«ª©©««¬«³¬«ª¥¥¦©«¯±±°°²®¯²²µ°³±³¯±±´³´²±±±²´²³°µµ¶¶¹¹¹¶¹»»····¶·¹ºµ´´¸¸º¹»··¸¼¼¼¼½¼»¹··»º¼½½¼¿¾¿¾¶®³µº¾½½ÃÄÇÄÈÈÈÈÈÉËËÍÊÐÕÔÓÓÒÕר×××ÚÛÜÝÝÜÜÛÛÙÛÛÛÚÚÕØØØÔÓÑÏ˾³‰zo޾¼¶ˆc:%%%)-39@FKQU\\]aegjiiiiihhggghghgkhmmnkllkjjjjjkhjkihjjllmjmlmmkge`ceegggidffifeedccccad`baededdbcceeiikkjlkgfcddcacbdeikkjjjjjjilklkkknnommnnmmmnmmlmnnnonppomponloqnknnonpnmhgeknqutsttttttttutsstsusttuommqtqoprrrqoqqtrqmnnoljhheZSXZemnonnoonnkijihhhimoolg]^_dhiijfilolkkkknlmkljkilkmiig_WSP[cffjkkkjgcZbhhcddfdghhggimkllllmmmmnkkknlgccefghffede’‘–‘~udWakt‰ƒ…„…}||~€~‚ƒŠ‰‰Š‘’’••›•˜—˜™šš›››šžšœŸžŸ›£ššššžšœœŸ£¡¥¢££¦¥¤¡¢ž žœœž£¤«¬¬¦¨§¨¨©«««¬¬©«§§©¬¬®ª¬¬¯¯¯¯³¬ªª«¨®®¬´³¸²³²²¯¯²²¶²´´´µµ²¶³´´´³¹²µµ¶¶¶¶º¹¸¸¸¹¸ºÂ¶¾·¹µµ·ºµ·¸¾¹º¹¾·¶¹À¿¾¾½¶µ¶·º¾½À¿¿¼Æ½¿·±´º»ÁÀ¿¾ÅÄÈÅÎÈÈÈÈÈÈÌÐÎÕÔÕÔÓÑÔÕ××ÖØÛÛÜÜÜÜÛÛÛÛÜÛÛÛÛÜÜÜÝÜÜØØÐʺ«™†tnއcK0,-17=BISW[]`cgikllmqmmnnghhkhhiiikjrnpopkkjmjjijllllkkjmkmmnnnlleccieeefgidffhihhgceedddficfegcbbeeefijojjjjhfddcbcfffinmolojojjjlmoqqovornnpqosprmompnnopoponnpnkmrnkmrooqtlhghisrrsusxw{ttsstwsrsutvsuutmmouqpooqupmourxronqotjhhgYSTYbqrrnmnvppljijhiggjppqjc`acjjjiiioooprlmjnlqmoknjrlllnaWVVYfghhkjmlkfa`jiibaeggnjjjjmrllklkulnlrjhjrjgfggjjmhhddcŽŽŽŠ†tgcXgx|ˆ…‚€~~|||~€‚ƒ…†Š‹ŒŒ‘’’“”””““”—˜˜˜˜™™˜˜™œœœš™™š—˜˜˜—›šž£¢¡ £¢£¤¤ Ÿ›œšŸŸ¡¥ª«¬¬¨§¥§¦§§¨§¦§««ª¨§¤§©¬¬«¬¬¯°®®ª©§¨¨®±³²¯´²±®®¯²²²²´´³°°±·´³³³³¸²¶¶·¶¶·¶³¸¶·¶¶µ¶¶µ²µ³µµ´µ·º½¹¹¸¹·¸º½¹·´±¯´µº»½½¾¼½½½¶¶²´¹»¼¾¼¾¿ÄÄÅÂÁÂÂÁÈÅÉÌÍÍÑÏÎËÒÐÐÐÒÑØÚÛÜÜÛÛÚÚÙÛÜÜÛÛÛÜÝÝÝÝÝÝÛÙÖ˾´¦–‚zsnhbC=9=AGNSXZ]^`bdghjlmopmkggggggeeehhjjmoojkiiijhhgiklhkjjjmlmlmokhgbdfgeechhhdffffdaa`aaccdef_aaa`cbcefgjkifffjifddbeeedhjnmmllijjjjmponnnmlqnoppnnnmkmlonprpppnpqqnnqnkmoporuqjheikqqsssqttutspsttpqutssrqomloqplqrqqqppqrrqoponmkigeXNTXbllmmmmnnmmkjikfhejlppke`^bdghhiijllomkgijnlpmoijimmlmeWPMW^eiiiiimmkcccffg^beefggfcltohhhkjjkmhhghhkggfgijkiheaba“‘”‚}j[adn†…„„„~~||}‚ƒ‡ˆˆ‰ŽŽ““”’’’”“““–•œ˜™™˜˜˜™›œžž ˜—˜š˜›˜™š£¡ ¤«££¡¨£££¤ŸŸ ¡¥¦ª©®®®§¨§§©©ª¨§¦°©ª¨§¨©ª¯³²²®¯®«ªª©«¶¨®®¯±°²°¶°¯¯···±´³·³³²···´·³´µ¸µ»º¾¹½¶µµº¶·µµµ¹µ´²µµµµºµ¸¹½º¼¸¹·»º¿µ²±¯µ»»Ä»½½¾¼Å¼¿¶··¸¹½½¿¾ÂÂÈÆËÂÆÂÂÂÉÈËËÖÏÓÎÌËÑÏÏÒÖÕÚÙÛÛÛÙØÙÛÛÛÛÛÛÛÜÜÜÜÜÜÜÛÚÙÑËõ«—ŠyreWNORVY\_cigecddhimlnnqljikjnihefgkikklntilhpkmiijmkkjjjnmqlmllkjfgggfgffchhhhifeccabcgggdfed`````dclmmgmjfedfjhffffjghhnnnnommjllmmnoroonnlrpqqrnpmllpoopssuprqysropnmlnoqsvqojijppqqwwvssuvtrsutsoqrssvqrlkntrpqstrqrpsqyqpqqnmkiggZQQV_mmsoqnqonmnlllkghjtmplg_^afgffhjnlqmolkhhjmmpmoiijlnqkcQIQ_ekinmqknkkeeehgfccddeiggfpominikjiilgigjhpfkghhlihebaeb‘‘“ŒŠxmfZeovƒ}z}}|{{{ƒ€‚„†ŠŒ‹‹Ž‘’’”‘’‘‘’“•–›—˜™™˜šœ››œ›˜—–––š˜™—œ¢ ¥¨¤£¢¢¡¡ŸŸœžŸ¡¢¥§ªª©¨§§£¨¤©¬©©¨¨¦§£©¤¦©««®°±°¬¬«ª©©¨©ª©©®«¬«°®°¯¬¯¯¶³³±±¯¯°²²²³´´¶³¶¶¶´¹¹¹¸¸µµ´´³´´´²²²³²µµ¶¶¶µµµ¹¸º¸·¶¹·´®¯µ¼»»ºº¼»¼»»º¹¶·¸ºº½½½¾ÁÃÃÂÁ¼ÅÁÃÃËÉÉÊËËËËËËÌËÐÕÖ֨רØ×ÕØÙÛÛÛÚÚÛÛÜÜÜÜÜÜÛØÖÑÊÊû´«œ’|qmhggiklmmljfffijllmmnkjhkkkkgegiiijhlmmiihhhijkmmkjhjjnnnlljjgfcgefefcecheeccccccacdghgeddca```adfmiigfeedfgggfghhihijlklonnmkkjiglmnmljmlllprrnnklmpnptsponrppoommkllnorspljgkmqsrsvwvssusqsvusrprqrssqnjosqnpptwrpoorrqppopkjiidYPTV^flmmkklnmljlllkihknnnlh^YaggggfjlmmllmkjijjlmokkijllmlgWLQ\dijjmmlkjhgeefc`edeeeeeeefplhfghighggghfihhegegfeefdcbba˜˜–‡…na`]i~‚„ƒ}~|||}}}{{~ƒƒ††‰ˆŒŽ““”’—–•Ž’˜”š’—•›˜š˜š˜žŸžœ œœ—˜—™™™šŸ¥¤£¡©§¦¥££¢¢¢Ÿ žž¡¢¦¦¦¨«¨§¥¦¢©©«¬¬¬©ª¦¨§§¥¦¨¬²°¯¯ªªªª©©¨ªªª®¬®¬¯¯²±³³´²·³´±²¯³³³³²´¸·¶¶¾¹»¸¾¸¸¸¹´¶¶¶·¹´´²³²¶µµ·½·»º¹µ»º½»Â¹½³°°±µÂ½¿»Á½½¿¿¾½¸¶¸ºº¼¾ÄÁÇÁÀÂÅÀ¿¿ÅÄÅÄÌÈÈËÍÍÎËÐÐÐÏÔÕÙÙÚ×ÕÕÕÕÚÚÚÚÚÚÛÛÛÛÛÜÜÜÛÖÔÐÌËËÃÈÀÁ±«§¦’‚€€}{„€}ytpnoqpoonnnnnmlkjjgghijjniqlnkjimhjlnmommkonnmmlljkgfefhkffeieidedgbecccdehiihhdbaa`abginiifffedlffffipkkkkknkllklmmnjijrqrlljmlmnvsrnmmpnpnrrspnnrqtnmmllmlnmtplihjmqvssuyxxrsrqquuvtwssruswonpxspnqrxuspnpqrrqropkiij[RRW^jkqnnkllqmlklkjihiqmmkj`Y^ggjijkqoonlmmmmnppooolkjjmonl[NJUaonnmnmmlkgghleabdfhiiehgegpkfefgkgihhghhigeejgjfgfhcbbacŽ‹|qhYcfp€}yzzywxxyz~€ƒ…†‡‰‰Ž‘’“‘’‘‹””’’‘—–˜˜˜——˜™™š™˜˜——––˜™›œ £¤££¢¡¡¢¢ œžžŸžžŸ£¤¥¦¦¦¦¢¤£££¨ª«§©§§¦©£¨¨¨¦§¨¬®¬«®§©©©¨©¨ªªª««¬¬«®¯²¯²³³²³±´¯±°³±²°³´µ²µ¶¸¸¹·¸´¶µµ´µ²²°°°³²²²´´¶¹º´¹¸¹µ»ººº¹´±ª°³¶¸Á½¾º¼½º··µ´³¶¸»»¾ÀÂÁÁÁÁÁÁ¾¾¿ÃÂÄÅÆÅÈÃÅÆÆÇÇÉÎÏÒÒÒÒÑÌÎÏÖÔ×ÖÖÔÚÙÛÛÛÛÚÛÜÛÖÍÉÈÈÄÃÃÿ½´®¦¤–‰‡……‚…ˆ‡ƒ}zzyyywusqstronkkjihijlkkimmmmkihhmonmmmmkprokjhhhhgggijhfffeddbbaaacdddefedfedbaaa_abgljhgdebddddffijkkkjighikkjjmplhkmoqollimlooppolmnpnpoqqsnnnnopmmmnnnkmmmlifkppqrqsuyxsnpppqqprrssrrrrmjqwvrpmooqqqoqtrppqnlkiigZOTU]djklllklllkklmigghikkjga[`jeeihknnnpiiinqqpqqqnnlkhlnnp_LJK[fnonmmmmmlgijfacdhihhhec_gkkigefgkffgidgfgeffidfffdcabbaf‰pba\gw|‚ƒ}}~yzxxwvy|……ˆ‰Œ‹‹˜”•‘”’’Ž“““’‘–——˜——žš˜š˜——™™œœ›œ¡ž¦¥§¨¨¢¤Ÿ ŸžœŸ¢¢¢¡¦¤¬¤¥¥§¥¤¥ªª«¨°¬²§¦§§¨©©©©¨ª««°¯¯«®©ª¬°§©¨«ª²²²«®®®°´°µ´´²¶²³¯³°³³µµ·´¶µµµ¸¸¹·¹·¼µ·´¶²³±°°¸³¶³·µ¸·¹·¹¸ººººÀ¶´²°°¸´¼¸À¼½º¹¹»¶¶´²µ¹»Â¿ÄÄÅÂÄÁÆÁÀÁÇÂÄÃÈÆËÊÉÃÆÅÇÈÊÊÍÍÑÑÑÏÎÍÍÏ×ÔÚØØÔÚÚÛÛÚÚÚÚÚÔÐÈÅÇÌÄÅÄýº¶¸©¥™•“‘Œ‰‰ŽŽ„‡…„…†……†„}{}{xwvtrrqqrqpooqrsurpoomspooopssrqpjhimmmihikihhigedecb``bgghhgefffeddfdccddghhhkfefgdfdfiojjkmkihkkmkjjmlkjmmnnnmplmlvomnooqopqstuqrsuonnsnoosrrrrnoigiopwrrqttzspoopppqsrstttrrlinyxvpppopoppqvttponokkkk[PS\]ekrnooplmllkklnhghjlrkjb]_hheehipnnmpiiioovqyuuqqnmmuppgYFCQhlnoqnmnmmmmnjfdihojmmmfddjjjiifmjkkkkjglkkhihjdgfidcdebbdkYabn~~~}|{zyyxuusx|€ƒ†ˆ‰‰‹ŒŒ‹‘’‘‘‘‘‘“”’“““’•–˜š™˜—–——™•––™šœœœœŸž¡¡ ŸŸœ™™™ž›žžŸŸ¢¢£¤¤¤¤¤¤¤¥¦©¨ª¦§§§¦¥¥§¨ª«ª§§§ª¬¬«ª§ªªª¨«¬ª§§¦ª«¯®¯¬¬¬¯±³¯¯¯°°µ³´¯±°³³µµ¶³²¯´´´³¹··¶·´µ³²¯®ª¯°°°°±¶³¸··µµ¶¹¹·¶³®±±³´¶´¶¸»»»º·¶µ²´´µ·º¼Á¾¾½À¿¿½ÁÁÂÃÄÂÂÁÃÃÅÇÅÃÄÅÈÉÊÊËÊËËÊÈÊÍÍÎÏÐÓÒÕÔ×רÙÚÖÔÓÐÉÇ¿ÂÃÄÄľ½¼ºµ¶«¦œ™”’‘Ї„†ˆ‰‹‰ŠŠ‡„ƒ‚‚}{ywvwwwxrpruvwvtsspnrqqoppppqophjkmlmijjjhijihfceec_bdgiifhegfffefecbbeghhhiihilfdddhjkkjihhhhkkjijjjjjjlmnmljklllonllpoqmrusrqprrromlmnooopromigehkoqrppoussoopppqqpmqssstqmhntyuspqqpmopqrrrsoopmikn\OU[_cjoqnoonllllkkkighiklqkc\afgeedgiomlkjiifjknpuuuqonpqrpqbPBM_iponjflkmjmjihgeghiijlifffghiigfihhgjiighhhhihfdeeeea]cccdXcru…€}}~|zyyz{zyuuy‚‰‡‹ŠŒŒ‘‘“””‘’˜˜˜“•”˜“•””–˜›Ÿšœ—•––˜™–™™Ÿž¢Ÿ¡ ¦ ¤¡žžœ›˜˜šžžŸž¡¡¨¦¬¤££©©ª¤ªª©¨«¬¬¤¥¤¥§«ª¯«§¯¬¬±««¨ªª¬¬¬©¨¨ª«±²´³µ®²®²²´®±±²±µ³¶¶¶´¹¹¹¹º²±³»´µ¶»¶¶¸¹¸·²²°°¯±±°²¶´ºµ¶µ»¹¿¶´³°±·¶½´·µµ¹½¼Á··µ´´··¼º¾¼À¾¿½ÂÀÀ½ÅÄÅÃÆÃÄÄÅÁÀÁÂÄÊÈËÉÎÎÎÈÉÉÊÇÊÌÎÎÎÏÕÕÕÔÙ××ÖÙÓÏÍÊÉÊÂÂÃÈÃýÀºº´´«§Ÿ•–‘Œ‰†‡ŠŠ‹Œ‘Ž“…‹„ˆƒ„‚€‚„ƒƒwtv|€~}|}tsrrtuuyusssoonnnqnnnommnpqpigfihhfeglknhijkjmmmjigffkknjjlponigfghlknmnmmmmkjjhikkllllppqqqjlnrqpmlnqqrrsuyrqsxwvollonqopprmjfceilsqursqwrqnoprrvqpopruttnlmwuytsruptopqrsxrtppnmno^QR\`gkuprrsnmmpoplmjgfhlqosf_`fhmhfellnlkkjhhfkkkntuvqpqvuvpqXIHWdpopkhflknmmiihhhhiliiigggjmopjhhjhhiijkikiiimhfghhmd``bcidj||}~~|zyyyxxwwuxv{€ƒ…†‡‰‰ŒŒ‘ŽŽ’’‘”””‘•’“’’’•—šž›–•’“””““”š™›žž¡¡ ¡›——•™˜šœœœœ ¡§§¥¢¢¡¢£¦¤¤¥¦£¥¢ ž¤¢§©«ªªªª§¬«¬¬«¨¬©ªª©§§§¨©¯²²±°°¯¯³¬ª««¯¯´±±±µµ¸¶¶³³°³µ¶´µ¸¶²²±°¯¯¯±²¯®±¯±±²¶´µ±¶µ·¸¶±±°±²¶´´³³³·»º¹·±³³´µ¹¹º¹¾¼¾¾¾ºº»¾½½½ÀÁÀ¿À¼½½½¼ÄÅËÈÇÆÆÃÂÂÄÄÇÇÇÈÏËÎÏÐÑÑÐÑÑÒÑÐÍÈÃÿÃÁÂÂļºµ´¯®©¨œ™‘Ž‹‹ˆ‰‰‹†‹‰Œ‹††‚ƒƒ‚‚ƒ„ƒƒ|xz€€||uvwy|{zyyxxtqqrpoooonnmoqponjhfihhfhjjkjijjiiihjjjjhgijklmnonmhhikkkjnnlhjjllkijkllmmmlppppmjmooonlnorsrrsssptwwxsnliomqopnlhfefhknrpqqqqqnrnnnoooqpnqruokioruuttsruqsqrtsssopppmmn^QSY_ekoppponllkmmpkjigfkopng^bhhihgffmonlkiihhefejlpspoqrttup]KJM_lonliigllllleddefggggijihkmmlljjjjhklljkihgiikeggghgccfdced…|{||zyyzxzvtwy}†‡‡‰ŒŒŒ’ŒŽ’“—“•“”‘–’•—™šš˜›™”•’”””•–”œ™š¡ž¢žœ¡›—––˜žžŸ¢¡¡¡§£¦¤£¢¨¡¢¤¬¤¥§ª¥§¡Ÿ ¦§¯ª¬®«ª«±¬¬¬«¬¬«ª§¦ª°®³³´²²±±¯³²´®®²³´²´µ·¸¹¸¹¶¶³³³¹¶¸µ·µ´®®®®³³³²´°±°²³µ²ºµ¶³¸¶´´´°±³·¶µµµ¶º»»¹¸¶µµµ¶¹¸¿¹º»¿½ÂÂÁ¼¿½Á¿ÂÂÂÀ¿¿Ä¼¾¾¾¿ÅÅËÇÅÅÅÃÅÅÄÅÈÇÇÈÏÍÏÎÎÏÓÑÖÕÕÏÏÈÃÀ¿¿ÆÁÃÂÇÁÁ¼¹¶¶¯®©©š‘”ŽŽŽ’Œ‹‹Œ‰‘‹Š†ˆ„…„„…‰……}y|‚„ŠŠŠ†…~|~„ƒ€…ƒ€€wuuysstursssszusrrljkokikqmklklojmjijllpmjikkkmrqrpmllnponnmmlkjknmllnnprrqqqprxommoornmorsvtutwssu||{urmlmnorpumjedhnooosssqpprnrnnproppsssswlgkvvzvwtssvrsrtsvuupqpppraUPT\dlrprnlonmnkmmqlkijjnpujb_diljhggioookjjjjlfggjkomlovvwwucRKMZpqtmllkhlmoljffefghgfhoklmqqqmnoolkhppokpighkjkfhhigfdhhijjf~}|zzzyxxwyxzwy|€„…‡‰‹ŒŒŽŽŽŽŽ‘‘’’“‘”“–™™™™˜—”“‘’’’••••›˜š››››››œš–˜˜™œžœŸ ¡¡¡¥£££¡££¨ ¢£¤ ¥©¦£¦¡£¥§©¯¨¬«¬ª¬®®¬®ª«ª«¨§¦§§«®°°±±±¯²±¯²¬¯¯²³±±²µ·¸¹¸¶¶´¶°²³µ´´±°«©¬¬³±³³³¯°±²²´²²²²®®¯±²´°³µµµµ¶¶·¶²¯«±±¶µ¶¸¹¶¸¸¼½»ºº¹¹¹¾¼¾½½»½º¿½¾º½¾¿ÀÅÆÆÆÃ¿ÁÂÃÁÅÆÅÄÈÄÑÌÏÌÌÊÍÎÑÎÍÈÈÿ»½¾ÂÂÁ¿À¼¾¼¹¶³¬ª¦£š‘“‹ŒŒŽŽ‘ŒŒ‹‹ŠŒˆ†……ƒ„‚††‚{x„†ˆ‰ˆ‰‡„€ƒ††……„……„‚|wwxxuvxwtuuuuzwvtsnnoooopppooonojjjkklloomjklmnqsqoonppppomnnnmmlnkmmopppqppopooopqqrqlorstsstuusvxvsrqoknqqqrpokhchmmmoptssqpoompoqsrmnmsqpnmhlpttvuwrssursttqqnnmnnmj`SRPW_lurpnkloopmkmmnkkgklnplebafmliffikkkkiigjkkgggijiflpsww|iWPL[inpmjiihilnqkhfffgehhhiiinrpoqkhfnnjhonkjjhhkhfffibcdeceeighe{||yzzzyxx{z{|€ƒŠˆŒŽŽ‘ŽŒ‹Œ‘““““”“–•š˜˜š™ž—•”“”ššš™™—›š›šž™™ššœœ˜——œ› œž §¡¡¡¢¡¡£§¢§¥©¤¥¦¦¦§¤££¥¦¬«µ©¯©¬¬¬ª±¯¯®¯°¯¬«§§§¨ª²°´³±±°±²°¯²±²²²²·¶¶¶·¶¹¹¹µµ´¸´¶¶¹µ¸«¨§§«°®®´²¶²²¯´µµ³´µµ±±±²¯³³´µ¼¶¸µ¹¸½¶¶¬¨¬³±·¶»»»»º»À»¹º¼»¿»½¼Á½¾¼Á¿¿¾¾¿ÄÂÂÀÅÅÆÃÂÁÃÂÇÃÇÃÂÄÈÄÒÐÐÌËÊÏÎØÌËÇÈÀ½ºÁ¾ÄÀÀÀÀÁÁ»»´²®¦¤œ˜“’‹ŒŽ’ŽŽŽ“ŽŒ…ƒ„†…ˆ‡Ž‚}{z‹‰ŠŠŠŠ†……‡‰ŠŠ‰ˆŠ||‚||~}{{{zzxzz~vuuvwxwvwxtutzopnmloooosqpqqpooqonnmnrrusspqqtqspoopptuuptqqqsquwxusrppqrutyxxwzz|wurrooprqpqrpoigiomqorq{sssvrrnuttstorrsqslimqswvyvvvvttuvuxqqopnmmocWNQT[hwvvpokmoonnnnlmkkkpormiefeijmgdfnmmkkiljkljgghmhghmnruyl`ROWnrtolkokpppprjihihljomnjkisrqqrhcdmoiiokihhfkihedek``ehdififleyyywz{zvxx{|~€„…†‡Š‹ŒŒŒŒ‹‹‹’“”•–—™™˜•˜™˜“••—š˜˜–•——™™ššš————–—–——œ›Ÿœ¡£¥¡¡¡¡ ¢£§ ¦¥¨¥¤¤¤££¡£¥¨©©©©©ª©¬¯¬«¯ª©§¦¥«¦§¦ª®¯°®«¬©¯¬®²°²²µ´µ¶¸³µ´´³³²´´¶·µ³©Ÿ©¥«°°¯®®¯¯®®¯®®´²±¯°°±¯°°´¶···²´³¸¶µ²³§«®²±µ´¼º¹·º¼»·¹¸»»»»½¹ºººº½¾¿»¾ÀÂÂýÁ¿¾¾¿ÀÃÃÃÂÃÀÃÄÄÅÉÇÈÊÊÉÉÈÆÂÂÂÀ»½º¾½½½½¾¿º¹¸¶±±ª§¢¡™–““‹Œ’’‘‘“‹‹ƒ„„………†€y{{‚‰ˆ‰‹ŠŠ‡ˆ………‰‰ˆˆ‰ˆˆ„„‚ƒ‚}€†„‚‚‚~{yw{{~wwvwzwuuvxtttsoooomnopqrrqpooppnlnknnrsvvrqrssqqqqsrrtssqqprrrruvvwrqqprrstzxywyzztuqqmptsqrrtomhlqqnnmrqtssssqpnrrsprppqsnmimvuuvuwuuvsptvvtspoooonpdXRMU[gssttpnkopnmmklklkmmoopiheffhildfgmonkjhjjklkgikgeghmloncWPJXgntrklmkjpooolhijjhijopnkjjssoliccdomkjnhhhgcjhhffe\Vbedbcdeffd{{zzzzyz}}~€††‹‹ŒŒŽŒŒŽ‹ŒŽ‘‘Ž•••—››š™—œ˜˜˜˜–›š™—•—›š›œœœ›—™—˜•——š™ž Ÿ¬¤¥¡ ¢ ¥£¨£¦¦©©ª¤£¤¤¤¦©®ªª©®ª¬¬¬¬¬¬®±ª§§¨©ª©©©®®¯®±¯®««¯¯°¯¯¯³±·µ¹´¶µ¹²¶³³²²²¶´·°®ª£¤ªª³³³¯±¬¯µ°¨¨®´¯®±°±°³°º¶¼·¶³¶·¹²±±±³µµµ²¹·¼º¾·»º»¸¹¹À¼¼»¾¸»ºÀº¼½¿¿ÃÁÁÂÃÀÿ¾¾À¿ÅÂÃÃÃÃÇÅÈÆÊÇÈÉËËÌÆÃÁÁÀÀ¼¾½Ä¾¿½½½¿º¸¶·°¯¬§¢žš–•”––œ’””˜’•”ŠŒ‡††‡‡‰€{}‚„‹Œ‹‹‡…„‡‹‹‹‹†…††…‹ƒ‚‚‡‰‹ŠŠ„‚~}|„|}}|{{zwuvxw{utqrqtpprzvtuyturypnlmoutuuwsqtyvtstsutyyxssqrruvwwwvvtrqrsvuxw|yzx}yyuurrsuuwv|sunlnttwrtprrttvvvqsrxtspwtystmllsvwwzuxtrrqqsvxtttuptrrh\SOR[fvvuttomnuqnntklknoppqnnijjlikkkjjjtopkiiklkjlilhfemjmlsdREALclvtolplkjqnumhfkimiijpnnonpspmidabgppnnnmnljikhiij^SUefecbcdeedxwxyzzz|~ƒ…‡Š‹ŽŽŽŒ‹‰‰Š‘Ž‘”•˜™™™™˜———•–––—˜–˜˜˜˜š™š——–—•™–•’–—™™ŸŸ Ÿžž Ÿ¢ ¡££¥¦¥¤¤¡££¦¦¨©©¦¨¨®ª«ª¬¬¬ª¬®«©§ª¬«©©©®¨¯¬¬«¬«ªª¬¬®¬±²¶µ¶´µ³³²°®®±²³³®¥ªª«¬¬¯®¯¬°©«®©¥£ž©®´¬«¬²±±¯±±±±±²²±°«©°¯´²²²¶µ»¹¾·····¹¹¹¹¹¸¾·¹¶¾º»»¿ÁÁÁÁ¿¾¾¾¾¾¼Á¾ÁÁÁ¿ÄÃÃÄÇÆÆÅÅÅÆÆÅÂÁÀ½»»º¿½¾¾½»¾½»·¸´³®ª¥¡œš—•”””˜˜›’““’ŽŽŒŠŠ‡ˆŠˆ†‚}‚‡‰Œ‹‹ŠŠ„‡†ˆŠ‹Š†ƒ…†‡‡Š„„…ˆŠˆˆ†ƒ~}{|z~||||xwuwxytsrrstqsuyyxwxttqqqokoqtttsrpsvwwvttuttwxussqstuvvvwtuqqqstvwxxxyzyyvvvuqtttuvvvqnjnsvwtrrqrruvvvuqrsvvsputsonjnpvywtttsssssrtuspqppoomh^TMTYeqqssqoknqsronnjijoqrrqnlfmnljjhkjklnnliijlihhjihfffjjlkh\C2DUgorslimmkjqmlhgfiijhijollimupmmge^dkljlnmlmlliheilgXSXfefgddefecvvwz{{‚ƒ„†‰Š“ŽŽŽŒŒ‹‘Ž–‘“ŽŽ’•™˜Ÿœ ˜›—–—˜——–˜™˜™ššœš™˜›–˜˜˜•••š™™œ ¢¡ Ÿ¢ž ¢Ÿ¡¡¦¤ª¨¨£¢¢¤¨¯ª©§¥¨¨®©«¯¯¯¯¯¬®®®ª°±²±¯¬±«¬«¬©ª«¬®²®³°¹³º¸·´¶±®¬ª¬®°µµ¶°¬±±²°²²³¯²¬°©«£¡£¢¤«²²²²³²µ®¬®²°¶µ´³±®«©¯µ´´³»²¶·¹¸¾·¸·¹¹¼º½¸º¸¾¹½¼½º¿»ÁÁÂÁÁ¾¾¾ÀÀÁÁÁÁÁÁÈÂÆÄÃÄÌÅÆÆÇÇÆÆÆÀ¿¼ºº½»À¾½½¼»¾¼»·¸³³®¯¯¯¦£Ÿ¡œ›˜˜š¢™›–—“‘ŽŠŒ‰‰…„†‹Œ‰‘Ž“‰Š‰Š‹‘Ž…ƒ„‹ŠŠˆ‹Š‹‹ŒŠ‰ŠŽ‡†~~‰€‚~~~‚y{uvwxxywwv{yzz|€{{vyyzuttuvwxwsqrvw|yyz||}x|xuuwv|ywwzxxuuuvwxyz|‚|}}}zzxwvuuzz{wzvwokjusyvsrsvzv|{|xxtvuyuttytunlntv~||vwv{stv|xwurrrpomnjdVNR[ftuytuomntqrrpnnnmmpqwuunkioooknjklrlpmkhjlmhhimheeiinlmkjL55I]knsnkkrommqkigffhjkghjnlllnnlkkgfffknjkkklolsjgfijcUS\ghhgfeihjfvvy|}~~‚„‡ˆŠŒŽŽŽŽŽŽ‹‹‘‘Ž‘’”—˜—˜˜—–•”•”—™—–••™™šœœš™˜—••”•–—”––™›œžŸž›ŸŸœžžŸŸŸ ¢ Ÿ¢£¨ª¬ª«¦¥¥¨©ª¨ª¨¨©¬«ªª¬©©©ªª°¬ª¬ªª¨«¬®±®°°³³µ±°°°¬«¨«¬¯±±¯°®¯¯°°¯®¬®®±«ª¤ š¢¦ª®®±®²²±°¯««ª°°ª§¦¥£©³±´³¹±µ¸¹µ¸··¶¹º¹··¸¹¸¸·¸¹»»»»¼º»¼¼½¾¾¿½À¿À½ÃÁÁÀÀ¾ÁÄÃÂÅÅÿ¾¾¾¾¿·¸·º¸¸¸º¸¸¸¹·¼µµ±²®ª©¥¤Ÿžœ™šš™™˜––‘’ŒŒ‹‰‹Œ‡‡…Š‹‹‰‹‹‘ˆˆ‰‘І„…†„ЇŒŒ‰‡†‡‡†‚€|}|€ƒƒ€€€}yzwwwvvxwwx{{|}}{|zxvxvvvuuvwy|xtttvw|z{||}{wwwwwyy{{yxyyxvvvwyyz|}}}}{xwxxvwxz{|utrpkkkvttutsvwxv{xxxxtvuvtuwuqqlotwyz{{uuuvuwxzzxwtrsoojiiXNUZesrtusokortqrqqmnknpqrttrkkkosnkmjlmqlmklilmlfiihcbakmmlmkR97;Tjlnmjmlmnnnmhfeffijhehjkkjjkklmlhffhjljlfjjlkjhgfhi`T\geaghfdhhiixy|„‚‚‚†ˆŒ‹ŒŽŽŽŽŒŽ•‘‘’”“˜™˜—™™—––”““™”›˜˜–—•š™œ›™ž–””””••——›š™›Ÿž¢œš›Ÿ¡œŸžŸž ¥ž ¡¡£©©°¯¯ªªªª§¨©¬¬¬¬¨±«ª«³¬¬©¯ª¬ª¯µ¬«¬«ª¬±¯®¯±±µ´³³¸±³®¬«««²¯³±³²³®²³´°¯¬«¯®³§¥¢Ÿ£®®¸±¯°±²·µº¯²®°°¥ ¤§§¨¨¯±²³³´µ¹´µ¸¹·»»½¶¿¸¶·¹º»º¹¸¸º¾½Á»¾¹º»½½ÂÀļÀ¿ÀÀÄÃÂÀÀ¾ÀÁÂÂÊý»¼¿¼½·»·À¿¿»À¸º¸º·¼¼¼µ¸®±««¦§ £¢¢¡¢›™™™–•’Œ‹Š‰‰’‹ŒŒ‹Ž˜‘Ž“‹Œ‰ˆ•“”’І††…‰‡‹ŠŠ…†††ƒ€€~‚ƒ„‡‰„‚~}}~}{{|||~ƒ‚ƒ‚{wxyxyyyy€‚|}xwxy{}{~€|zxwxz}}}}{zz||wyxzy}|‚ƒ~‚|zyx{z{}{}trrrmnqxxxvvv}||y|yyyztwvxwxvurqqvxƒ{|z|wz{}|||zwvrspnjk\QS\eqtzuupnnsruutsrmnnosxrtrpoqoonmmnlopqpqkllpmlmojfcaemmlmp\E27Jgnsnlknmloqnmfefjjnjhgikqkkjkjlllgghmknlkjiimjhgjhmh`_jgeenhddghkk|~‚‚„…‡‰‹‹‹Œ‘Ž‘‘’“’“•••””’‘‘’““’““–•˜––”š˜œššš™“‘‘“”–“–—š›››œ›œ™›œœœœ›žžžž Ÿ¤§«¬°°±««¨©¦¦§¨¦¨©©©ª§«®¬©«¦©ªªª¬®««¬¬¬®¯¯°°°°±¯±°±±¯¬¬«¬¬¬¯¯²²²¬¬°°®«¬«®®ª¡££§¯®¯²²²²´´´´³®®ž–Ž¡¡¨¨ª«±²´´´´µ³¶¸·µ»º¸µ´³¶¶¶´¶µº¹º»¼¼º·º¹¹¹»º½½½¼À¿ÀÀľ¾¾¾¼¿¿ÂÂÁ½¼¹¹¹»º¼·····¸¸¸¸¸¸¸·¼··´´¯®¬«¦£ ¡¡¡ ›™•—••Œ‹Š‰‰‰‹‡Š‹‘˜”Œ‹ˆ‹ŠŒŽ•–“ŽŠ‰‡†…ˆ‡‹‹Š‹‰‰ˆ‡…„‚‚ƒ‡ˆ‡†„ƒ€€€~|‚„………ƒ{{{{{||||~~}|z{{|}{z}zzz{||}}}}}}{}}{xxwz{~~€~~~zzz{yzz{y|zwrqmqqrsvvwwvv{||yyxxuuuwvvvxttrsuy|~€|{zxwz{|xywxwvopqpk[PV]dmsuurtmpqrssspmmmnorturpnqlqoolmmnlmjjklkmnokllljdZejmnno`M;0Gbjqolkjiimoolf_ehiiihiikkljihiikjldfhlkmmlkkjhghhhglgghd`ffggddhike€„††‹‹ŠŠŒŒŒŒŽŽ–‘’’‘–”˜˜—–™”˜”’‘‘”˜”””˜—˜•œ››™››œš™“’‘—–—•œšžž œœžœž›š›£œž¢¤¤¬©²¯´°²««ªª«¬¦©§ª©¨©«¬¬««¨¨©¯¬¬¯¯°®°²µ±´°·³¶°±°µ²¶¯®®¯±®®¬¯¯¸³²±±°¯®°±¬§§¨ª°±³³²²µ´·´´´µ¯’ˆƒ‚“¤©´°°°°±³¶¶¶µ······¾¸¶´³´½µ·¶º¸»ºÁ»º¹·¸¿»¼¼¿¾¾¾¾¼À¿¿ÀÄ¿Á½¾¾¿ÀÅ¿¿½»¸¹¸»»½º¼¼»¸º··¹½¹»¹¼·ºµµ®®¬¤££¤¢¨£¦šœœ“”ŽŒŠŠŒŽŽ’™–“”Ž“Šˆ‡‹Ž‘Ž•““‹ˆ‰…ˆ‰‹‹‹Ž‰‰ˆ…„„Š„……‡„‹‹Šˆˆ‡…‚„ƒˆ†…‚‚‚‡Ž‡Œ…‡€€…ƒƒ‡Œ‡„ƒƒ~‚Š€~‚||}~~€€}}}€‚ƒ„{}|zzzz~~„~~}{{z}|}||}xtrqqru{zyyxyyy{||y~zz{|{|||wytttwy‚‚ƒ‡zxy}||yyy}vuoprv`QS]blr{vwstryv}wxrpmnotsxvxpnnrrsomlnnqpqmmlnowonmpljfcdpooothX@2>[htpnnoihiqqqlbafgkhjhokpllihhijojkddflkpornmigfihjjllkjcbgghfbegilh‚‚…‡ˆ‰ŠŠ‹ŒŒŒŽ‘ŽŽŽŽ’’‘‘’’““•––––”“‘’‘‘“•—•”“—”••š™š˜š™˜–””“‘”“••œšžžžžœœšš››œŸŸŸž£«§¦«©ª«¯±®«©§§¦¦£©¦ª¨©©©©ª¨¬«©©¨¨©¦¬©¯¬®¯²·³±±°°°°°°¯°°±®¬ª««®¦®¯¯°°¯®¯°®«¯®«±²±°°²²²²±±±¯ª§¡‹yƒ’¢©®¯¯¯°±±³µµ¶¶µ´´´µ¸³·¶¶³´µµµ´µ¹¸»º»»»¶¸¸¹º¼½½½½º¾½À¿¿½À½½º¾¾¿Á¾¹¹µ·¶¶¶¼¼»¹»ººµµµ¸º¹¶·¸·´¶µµª¦¦¡¡œŸŸŸŸž™™—–“’ŒŽ”˜˜”‘‘Ž‹‡‰ˆŠŒŽŽŽŽŒˆˆƒ…†‰Š‹‹‹Š‹‰Š††………„ƒ„‡„††ŠŠ‰ˆ„€‚ƒ‡ˆ„ƒ‚‚†‰‰†…‚€ƒ†…Š–˜™Œ„€}†…€|~‚€ƒ‚}~‚‰…‚ƒ|{{{{|}~~ƒ€~|}||{||~€|y}vsmqrvy{{zy{|{{}~|z}|{zzy{zyxxtuuy{~€‚ƒ}wyz}}|zyz|uuopscTU[agpvvvxrsttuvuwqqmpquvwsojmopnnlmnnnpppmmmoqqnnmmkfdimnoopj`D2@Ucmplheiijkqvrkccfeeeghgghhgffgjkifi`dehilmoqmigghijkllkiechihfgkiiii‰‹ŽŽŽŽŽ‘•‘•“”’’•“”“––˜––’“—•••›–™•––—–š™ššžžž˜—•“““”˜’š˜š¡žžžŸœœžœžœ¨ Ÿ¡¦§©¨¨§«««ª¯¬«ª¨¨©©¨¨§ª©®«°¨ª©¬«¨¨©¨ª©°¯°µ´²±±±±°³²²¯¯°³¬«ªª®°®±°µ°±°°¯®®®¯²²²±¸³·±´°²²³²²±±³³–‹‚u}„‘´³²±µ´µ±¸µµµº¶¹µ½·¸µ·¶·µ¸¶¶·ººº¸¾½½¼½¹º¹ºº¾¿Á¾¾¼¿½ÅÀÁÀ޽¾Ä¾¾½½¸¸¶¶¶µ·¾»ºº½¸¸¹ºº¹º»·Á¹º¶¸´µª¨¨©§¦¡¢¡¤›››——˜™••“““š™š—š“‘‘ŒŒŒŒŽŽŽŽ‘ŒŽŽˆŠ††ˆŽŠŒ‹ŠŠ…‡……ƒ‚‚ƒ‡‡ˆ†‹‹‹…„„…†‹…„ƒŠˆˆˆ„ƒ‚ˆ‡†‹œ—§—“„€†…†€‚€ƒ‚‚ˆ…†‚‚‚‚†‰ˆƒ‚€€€…†…ƒƒˆ€}}~€€~‚}}urstx~~{{{€€€€€~}}}}‚|}{ywvvy{€}ƒ…~z{}}}~€|{|utstfZWY_iozvxwywvv{vvuwqrqtu~}}rkkqqtnnnrnnntqqpqrqqroomlfbhtttuvohJ49P`orwkffijmmqrsjdcffffhgffigcdghmjifhfeekimmnpsggilhomrlmihinmnnmlmlppŠ‹‹ŒŒŒ‹‹Ž‘‘’’‘“”“““”–˜’‘Ž““”••“•”–•–––—šš—•”“““’”•—“™˜š›œœœœš›™œœžœž¡¤¨§§§¦¥¦¥ª¦©©ªª§¤¨£¨¥§§§§©¨¨¨¨¨««¨§§§ªª®°®¯°±±³¯°¯¯³³²®°±²¬¬«ª¬¯®°¯°¬¬¬®ª¯¯±²³´²°°³¯³¯¯««©¦}yr‚˜¥«¯²±±±´´µ±±±±²µµ¶´´³²³·¶¶³¶µ¸¸¹¹¹·¸¸¹¹½¹¹¸¸¸¹·º¼¼»½½À½Á¿Á¾¾¿Á½»µµ´µ´¶²µ·½»¹·¶µ¸·¸··¶¸·¶µµ´²¯¬§ª©¨¦¦£¢¢¥››››•–—˜—–•“’”š––—“ޑދЋŒˆˆ‰Š‹‹ŠŒ‹‹Š‰†…„‡……„ƒ€ƒƒ………ƒƒ„ƒ‚„„…†‰‚……‹‰ˆ„ƒ‚‚‚ƒƒ€…Š’••”„„„…ƒƒ€€ƒ…††††‚€€‚„‡‰…ƒ†‡‚~€…ˆ†‚„‡€€€€€}|{wrtu{€}y{||}€~}~~}||{zywvw}€|~~|{z||}{}|{yyy{uvwiYXX^fpvwvwwxywvxwvuvprsvx|}{nmmqrsnooonnnumnlnprqqoolf`gmrrssvmN4=I]nqtoihglmnornkffdeca`dhff]TVXfiihieedccgionnmjehjihkmmlljjkljlmnnnlno‘ŒŒ‹Œ’Ž”˜‘‘’‘’’‘”˜“—““”™‘‘Ž‘””™•––—•š—™™››š•“’”“’““–™–˜–š—››žœŸ›šš›œ œžœ¢ª©°©«¨®¥§¤ª§©©©§§§¨¥¨¨©©ª««§©©©¨«ª¨©¯±®³³²³´³³¯±¯´³³³¶±±²¬¯¯³³²²²®°«¯¬®°°´³µ²°±²²³±³¯±¬š•rww„œŸª«±¯µ²²²´´´´´³³³·····´´µ¸¹º³½¶»¹¹¸¼··¸ºº½¹»¼½»º¹¹»½»À¿Ä½Á¿Ã¿¿ÀÀ¼ºµµ´··¸µµ¸½¸¸µ´¶··¹···º¶´´¶±¯¬¬¯±°¯§¦¦¦¥§Ÿ¤››˜™˜ š™™˜—–•œ–’Ž‘‘˜”•“˜–ŒŠ‹ŒŽŽ““Š‹’Š‹†…„ˆ††…†‡‰…Š…‡‚„ƒƒ‚‰ˆ‡†ˆˆŠˆŠŠƒ‚‚‚ƒ†‚„€…‡Ž•ŒŒ„ˆˆ‰ˆˆ‡††‡‰ŽŠŽ…‚‚‚…„‡‡††‡……„„†‡‰‡†‡‡€…†ˆ‡‡†Š}}xvwy{ˆ‚}}~~ƒƒƒ~|}ƒ‚†}|zyxy|…€‚€}}†ƒ}{yz{{w{obUZ\dn{|{z{y{xwwzxzvvruw|}}zyooprtsrrtuolptmpnnpwqrrsideprwvxwx\>8GZqtwsommmsrrpsmhc_ae`\_bdcfUQNYdikgjefeddfipooliijjihlmnmokllllnmwvvprnŽŒŒŒŽŽŽ‘‘’’“––”“’’‘Ž’”’’“–––”––˜—–“’’‘’’“”–™™——•––›™œš›šš˜›šœŸžœž¢§¨©ª¦§¥¥¦¦¥©¦¥¢¤¤¥¢¢£¨©©¨©©ª§§§¨¨ª¦ª¬®°¯¬®¯®¬®®¯®®¯´±±°®®®®««ªª©¬®¯«²®¬¬¬¯¯°¯³³²±°°³´³¯®ª§¦Œ{qdu€Ž¡£¦¨ª¬³²²¯¶³³²´´³±³²³°¶µµµ´³³³¶¶»½ºµµ¶·¸ººº·¶·¹¶»»»»»º½¿¿½Â¿À¾¼º¹¹¸¶³±²¯¸µ·¸·´¸³µ¸¸·¶µ··º´µ³²««¯®«®¦§£¦£¢œ›š˜˜™™™›œ™––––”’Œ””•’Ž‹ŽŒ‹ŠŒ‰Š‹ŒŠŒŒ‹ŠŠ‡†…†††‡‡‡ˆ„…„„ƒ…†„‚…„††ˆ‡‡‡‡‡†ƒ€‚ƒ…~~~„‡ˆˆˆŠˆ‡…††‰‰ˆ‡††‰‹Š‰…€‚‚€…„…†‡‡‰†…ƒ„„‡ˆˆ„ƒ€€…„„„‡‡Š€|ytwxz|€€~~{}~ƒƒ…}{{}}zyzƒ€ƒ}|}€ƒ‚€}}}{yyuwuncZT\amwy{zz{wyxyzzwxuwrvxyz}vsnruvutrtvtqtwploknpwqrshcdhpstuxzqRC>WoqtvrpnpquwrpnhaX_^a``adfec^Y]`gjkggeeeeefiomokjjigiillmnolmoooolqsvool”’ŽŽŽ•Ž’‘’Ž‘““—˜˜••”•“’””••™—˜–›––˜‘”‘’“•””–œ›š˜–—™›˜œšœ›š™¡ ŸŸž ¤¥§§¨¨¬¦§§§¦«§©¥¤¥¤¤¥¤¤§®©ª¬¬¨©§ª®©ª¬³²´®®¬´¬¬®°°³²²±´±´®¯°°¯¯©«ªª©±°®¯«´®¬¬¬®¶±±°³²°²µ¶¶¶·¯° ž}jf_q‰”¨©®¯°³²²²¶¶·µ¸µ»´¸±´³µ°µ´¶´´³¸¸¹¸¼º»µ·¶½¸ºº»··¶»¹¾¼¿»»º¼¾¿½Â¿Ä½º¸¸·µ³±²µ²·´»¹·µ¹´···¶µµ··¹´¶²²®¬¯®®®®§©©¨¢£œœŸ¡š›› ›››œ––“˜””Œ–––––’Ž‹“ŒŠŠŒ‹Œ“ŽŒŒŽŽ‰Œ†…†‡‰‰‰…‡„…†‹…„„ˆ‰Œˆ‹ˆˆ‡‡…ƒ‚„„…€€ƒ…‡Š‡ˆ‡‡‡ˆŒŒŠ‰‰‰‹‰‡‚‚ƒ…‡†………†‰‡Œ††ƒˆ„ˆˆŽƒ„~†………Œ‹‹‚„{xxˆ‚ƒ‡ˆˆˆ‰ˆˆ~~‚ƒ„„}‚~~|‚‚€ˆƒ€€€ƒ|}~…„„}€zxvyof\VYbnz|~~{|z€zzz{wuxy{z{z}vttwv{uttzvuwvolnnopvstj^_gmvtzy€uiPEOnsvvvvxsuv|xsomf_^`_dfjjiijjjjkksmlhghiffggipoqlnkmgnjoooopmnoroppqrxpplŽ’”””“’’‘’Ž’•————™˜™——–”‘Ž“‘”–•”•–˜—š˜™–˜™™˜˜˜›˜ššž ¢¤¤¥¥¥¤¤£££¢¤£¥¥¥¤£¡££¥¦¦§§¦§¦§¦§¦ª©«ª¬±°®«¬¬ª®¯°°°°¯±±²§¨©§££¥¬©ª¨±¬®¯¬¬ª§®¯¯°°±±°µ´³²°©”…p[acy’™¡§«¬®®²±±²µµµµ¸µµ´´²³²±°µ³³´´³··¸¸·¶µµ¶µ¸¸·¶µµ·¶º¸º»ºººº¼·¹¹º»¼»¸´µ²²°°±²²´³µ²²²²²³³·¶¶´¶µ¶³²±±®®®¬ªª§¦¤¦¦¦Ÿž›žŸŸŸ¡›œ ››™—“–“•”’‹Ž‘–“”‘“’‘ŽŒŒ‹Š‡ŒŽŒ“ŽŽ‹‹ŠŠˆˆ‡‰ˆŒ„†ˆ‰Šˆ†„„†……‡‡„„„ˆ‰Š††‡‰ˆŠ‰…„ƒƒ„ƒƒ€ƒ†‡‡‡ˆ…ˆˆˆˆ‹ŒŒ‹‹ˆ†‡Š‰Œˆ……ƒ…†ˆ‡‡‡…‚†…ˆˆˆ‚ƒ}ƒ„„…………Š}x{|„ˆ‡„„„ˆ‰ˆ„……„‚ƒƒ€€ƒ}~}~ƒ€€‚{~€„„ƒ€€zyy{{yurh]U\am}}~~|{z{{}zxwxwwuyyyyzz{xvtututuvzwv|unkoqrrtrqf`]ksttyywndQVbotuvwwwtuvwwsolba```dkhfhhifilmnpnmfecghhhhgijkhjihffgjjllljmnmmnopqsqqlŽ“’“”ŒŒŒŽ’‘‘‘””›““‘—‘’‘‘‘““”””–œœ›˜™™œ˜™“”’———•››››œ››˜œ™™”“–œœ¢žŸ¥¥ª«ª§§¤©¥§¤¤¢§£¥¤¤¢¢£¤£«ª¨¨¨©©¨¦©§¯«®¬´°µ¯®¬±¯¯²°¯°¶°¯¯²±°¦¦¦¥¤¦§«©¯¬²µ¯®¬©¨©¯¯´±°°º²²²²³¶±©‡q`Q^j|›Ÿ¨¨®³¯²²²°¯³º¶¸¸¸¸·´³²µ²±°µ´¸´¹·»º»¶¶µ·¶··¹¹¹µµµ¸·»¸¸¸»º½¼½¶¶¶·¹¼º¸´¶±±°³³³²¶¶·²µ³¶³´³¸¶º´¼µ´³¯±±±¶®¬««¨§¦¦¦¦Ÿž›£ Ÿ ¢¢¤Ÿ œž˜•”–”•“•”—“———”•‘‘ŒŒ‰ŠŒ’“ŽŽ‹ŒŠ‰ˆ‹‹Šˆ‡†‰ˆ†ƒ‚ƒ‡‡††‡„…†Œ‰Š‡‡‡ˆˆŒ‡††‡……‡†…††‡‹ˆ‡ˆˆŒ‰ŒŒ“‘‹Š‰ˆˆŒŒ‹‹Œ…††‰‡‹‡‰ˆ„‰ˆŒ‰‡ƒ‚‚‰ˆˆ‡Œ…‡†Š€|}‚„މ‹ŠŒ‹Œ‡ˆƒ„…‰†ˆ„€‚ƒ‚‚€€‚‰‚€………‚€‚‡…†€}{z}z{uqaXZemwƒ~{zy}~~ywvxvvx~}}|z~yzvutwv|x|xx}umkssvuwqpcbhyzzx~yxj`^cktuxvzyzvywwxmjafa``ffffjjihipzovoogdekhhiogghjjkfddfflloklkrmlllntrwwxn’”މ‹Œ‹’’”““’’“‘’““““”•–”——™˜–“‘ŽŽ‘’’’—•–•–””•——˜——•”‘“• ŸžŸ¥¦¨¥¨§§¤¤¢¡¡¡ £¢£¢¤¡¢¢££££¥¥¨¤¦§§¦¨§¯«¬¯®¯¬ª®®®¯±¯«®¬®®ª©£¢¡¥¥§¨©ªª«¬«§¦¤¤¤¨ª®¯±²²¯®®¬œ“zc[McwŠž¢¦ª«®®®±±²°²´´´³³³°²±³°³²±°µµµµ·¶¸¶¶²µµ····¶µ¹µµ´´µ¸··³¶¸¹¸·±³³µµµ±±°°®°³´³¶±±¯¯¯´²²²´´´´´´³²²²²±¯««¨ª©¥¢£¡ œ¡£ Ÿ¡¡ ž ˜›”””•“’’‘‘““““‘••”’‹Œ‰Š‰Š‹Œ‹Š‰‰‰ˆˆŠ†‡ˆŠ‡ˆ‰ˆ‡…‚~‚…„ƒ††Š†‰‡‡‡ˆ…‡„††††‡ˆˆ‡††ˆˆ‰‰ˆ„‡‡‡ˆŠ‰‰‡‡‡‡ˆ‹‰ŠŒŽ‰Š‚……‡‡‰‰‰‰ƒ‰ˆŒ‹ˆ‚††‡‡……„…†„}~€†‰ŠˆŠ†‹ŽŒ‡‡€…ˆˆ†…„ƒ„……ƒƒ……ƒ‚‚‚‚„ƒ„ˆƒ„‚~|y{y{qaU\dkwz€ƒ~~z{z{zyvvuxswy||~|x}yzvvtvwyvvv|…‚vnkstusqka[ivz{zz}xpfddjptuvwyy{uwxvqnjg`a`_`edfchgfelsrmkhkhgfhhjkiefiijkdb^ggllljkhiimmmlrqrrsp’’–’ŒŒ‹Ž’’–•–’““”““‘–‘“’•”˜““”•”•–•‘ŽŽ’•–—”——š–˜˜™•˜—›——•””™˜žŸ¤£§¬«ª©ª£¥¡¢ ¡£¤¥§¢¤¢£¢«£¦£¦¥¨¥¥§©©«°¬µ±¯±¬³³³°´´³®®¬¯¯°°¬¨§¤£¤©¦ªªª«««¨¤¡Ÿ¢£¨±´´µ³¶±µ®®Ž~iV[Vgƒ”«««®³²´¯¯°³³´¶·¶¹´µ²³²´³³²·²³³¹µ·µ½¸»¶·µº¶·¸¹¶¶¶º¶¸µµ¶º·····»·¸³µµµ´³´´°°®¯¯¯²¶´¶°±¯´´´³¶µµ¶·µ¸·ºµ³´¸°«®®®§¤¤¦ ¡ŸŸ ¡¡ §¢¢¡¡˜š•š“–”–“•“—–”““š•”‹ŒŽŽŒ‹Š‰ŠˆŠ†ˆ‰ŽŠ‰‡‰„‡‚ƒ†Š‰‰ŠŒˆ‹ˆ‰†ˆ…‰ˆˆ†Š‰‹ˆ‡ˆŒŠŠŠ‹…‡‡‡ˆŒ‰‰ˆŒ†‡‡‹‹’‘‘‰‰ˆˆˆŠŠŠŠ‰Šˆ‘ŠŠƒ…Œ‹‹…„ƒƒ„„€†…Ž‰ŠˆŠŒŽ’ˆ‡„ŠˆŒ‡…‚„…‰‰Œ…ˆ†Ž‡‡„……‹„ƒ„‹Š‰…ˆ„‡ƒ‚€~~|zgWX`ipy|€ƒ~€€||{z|~xyzz{ƒ„}{|yywzyyyzyu†ŒuomstxrraXbuz{|{~umgimstxvyy}z{x‚xulhgf[Y[]_effblfffrqoheenljimmnjgeefghkfbbiikllklhggmlqppqrrsp’ŽŒŽ‹‹ŒŽ’“”’‘‘•‘’’’’’’“‘”’••’ŽŒ”••–””–••”•••“—––––•••™˜ž› ¡£§§¦¦¦¥¦¢¢ŸŸŸ¢¦§§¥¢¢¡¡¡¢¢¢£££¨¦§©©©««¬¬««¬®®®®°¯¨«««¬¬¬ª©¦¦¦§§¤£¢¡¡¡ žœš™˜ž ¦ª´²³²°¯¯¬—}h\LYauŽšª¬¯°±°°¯±±³´··¶¶¸²²°²±²±²²²²³³³²µµµµ´´µ¶·´µ´µ³·µ¶´³³¶¶¶´¶·µ³¶··³°®¬®µ°±¬°°¶²²®¯¯³²³³µ´µ±¶´¶µµ´´´·¯ª®¬¤¤¡¡šŸ¡¢¢¡žž¡›™”“’’””•“”’”•—”“”•‘މŒŒŒŒŒŒŒŠŠŠˆˆ†Š„ˆŠŠŠ‹‹ŠŠ‰……€€‚‡ŠŠŠ‰‰‰‡†††‡†‚‚ƒ†‡‰‰‰†‰ŠŠˆˆˆ‹‚†††‡ˆˆˆˆˆ…‡‡‹Œ‹ŠŠˆˆ†‡ˆ‹Œ‘ŒˆŠˆ‰‡Š‰‰ƒƒ„‹‹…‚€}}„†Šˆ‰ˆŒŽŒ‹’‰‡…‰ˆ‡‡…‚†ˆˆˆˆ†‰ˆŠˆ‡„‡‡ˆ………‡ˆˆ…„…‚‚|~~€‚u__`hox|~€}~~}{zz}zyx{|€€{zyz{xvyyzzz{xv„‡€trpqrqmbVcrx|{|{wpkgntrsuuyyyyywwvshc_[SZZ^`_]`acefgijhcccefijkkkhgfgfhihggfkkkkkkkhgbkknnnlsqso“““ŽŽ‹”“———’’‘‘’”’•”–””’’’’‘”“•’ŽŽ•–•””””™”––––œ–˜•––šš›•™šŸ §¦ª«ª§ª¨©¦§¢£¡¤¥¥¥¬¥£¢¢£¤¢¥¤ª¦¬¦©©¬¬°®²¬°««¬±¬¬«¯¯·¯±¬ª²¬®«©¨¦¦§§¢Ÿ ¡¡¡œ–“‘•šž¤¬¬¯°´²º²¯±³•†kWRIWg{œ¢±«³²·±µ±²±´´·´¾·µ¶·²´´´´µ²²´·¶º¶¶µµ¶·µ¸´µµ½µ·µ·³¸µ·´¶µ¹¶·µ¶µ¶³·¶¶¯®««²°·°°°±°µ²¶²µ¯³°·´µµ¶µ¶µ¶´··¶µ³´¶¯±®¦¨¢ ŸžŸ¥£¦¡¡ £Ÿ ™˜“’“‘˜–˜”—––—•˜˜˜’Ž”‹Š‹Š‰‰‹ŠŠ‡Œ‹‘’ŽŽŒ…………‚„‰‘‹Œ‡ˆ………†…„„‹‹‹‹Œ‰ŠŒŽŽˆŠ„…†ŒŒ“ŒˆŽ‡ŠŠ”Œ‰Š‹Œ††ˆŒ’Œ‹‰Šˆ‡‹‰ˆ††‡Œƒ€…}|~ƒ‹Š‘ŒŒŽŽ“ŒŽŒ‘ˆŒ‰‰ˆ‡ˆ‰‰ŠŒŽˆ‰ŠŠ‹Š‹‹Œˆˆˆ‰†ˆˆˆ‡„„…‚ƒ‚‚€„|qkgjsz†‚‚€ƒƒƒ€‚{}|}~€€…€{zzzxw}{{z{yxv‰‚}twvzqnf]asx~€€xtmjjsrrtvuyyy{€xxurf_[WWZ\`__]^`bbghjgdbcchfijijlgjjjhljjgiiqppmpmmihcjlmnnovrtn“ˆ‹ŒŒŒ’“’Ž‘’“’’’•“’’““‘“’’‘“–—˜””“•”“’—–———–•“–˜™•––™› ££¤¤¤¤¤¥¦§¢£Ÿ¢¡¤¤¥¤¤¤¢ ¡ŸŸ ££¥¥¥¥¨©¬¬¯¬«¨¨¨¨©¬«««¯¯®«±«¬ªªª«§¥¤¤£££¤¤ œœœ™•†…„†‰–ž¤¨¯±²²²³¬©¨Šq^HPQdt‚” §©«°²²²²°°¯´µµ´·¸µ²±²²²´³µ°´¶····´³³±´³¸²µ±µ²¶´µ³µ´µ´¶µµµµ³´²´²±¯¨¨©®®°®±¯±²°°°±²°±¯°°·´µ³³²µ´´³±®¯¯´µµ°°®®¨§¦¥¢¡ ¢£¤£¤ Ÿž–•“’‘‘“”””—––˜–ŽŽ‹Ž’Œ’‘‘ŠŠŠŽŽŠŠ‰‰…‡‡‰ˆŒ‹Œ‹‡†ƒ…ˆ†‚‡ŒŒ‹‡ƒƒƒ…„„ƒ„…‡‡Š‰‹†ˆˆŠ‰‰‰ˆ‰„†‡ŽŽ‹ˆˆ†‰Œ‘’Œ‡„‰‡††‡ˆ‰ŠŒˆˆˆˆˆ‡ƒƒ…‡‡‡Š‚z‚}|{}~ƒ†ŠŠŽŽŽŽŠŠ‰ˆˆŠ‰Šˆˆˆˆ‡†„‡†ŠŒŒŽŠŒ†ˆ‡‰‹‰†ˆˆ‰‰‡„ƒƒƒ‚‚wsnpt{„…ƒƒ€‚„„€€|||~€€€~xzz{yxw{{zy{xwwz{wrwwyqocinw}~~~ttjmoqqtuuuwv{|€xwtoc^WZ^^^_``\^^_^cfgeeabbdefegjifghhhiihghipnmklmnia_jnmmoooopm“ŽŽŒŠ‹Ž‘”‘’’“‘‘’‘•“’“““—–—‘‘”‘’•—›˜š——˜–“’”˜˜˜™••—œ˜™–˜˜ž¢ ¢¤£¤¤£¤§£ª¢¢¢¥¤§£¥¢ ¡¦¨¨©©§¨¨©¨®¯®®««§¨§®¨«¬®¬´¬«¬±¯¯°ªª¦££¤¤¤£¢ Ÿ›—‘Šƒ|xwƒ’¢¦®®µ¶¸²±³¸§¦~eVFOZk‡ž¤®¬±®¯°¼±´°°³»¸½µ·µ³²´²·´¸´µ¶¶µ¸·ºµ³²±³µ´¹²´´»µ¶µ¸¶¶´¸¶º¶¶µ´´¶´º±®«¬°°º®°®µ±²±¯¯¯¯´´´´´³·´¸²²²·³´±°¯³²µµµ°µ¯¦¥¥¥¢££¥¦ª¤¤¢£Ÿ¢žœœž“’•”“’’“—˜˜–•‘’ޓޒ’—’’‹ŽŠŽŠ‹ˆ‰ˆˆŽŒ‘ŽŽŠ‹††……‰‹‹‹‹’ˆ…„‚„†„…ƒ‡†‹‹‹ŠŠ‡ˆŠ‹‹‹‹ˆˆ‰ŠŽ‹‰ˆ‡ˆŽ™‹ˆ…‡‡‡ˆ‹‹Š‹ŽŽŠ‰ˆ‚……ˆˆ„~}ƒ}}}~‚‹Š‘’•ŽŽŽŠŒ‰‰‹Š‹ˆ†‡Š‰Ž‘•Ž‰Š‡Šˆˆˆ‹‰Š‘‰Œ„ˆƒ‡ƒvwsz}…†Š„Šƒ‚‚„„…{~€ƒ‡‡ˆƒ~}{}{|xx{‚{zz{wvw{phq|vytprwy€„€€~tssvwwuzz{x{}ƒz{qmb^\_cgghb``a`cbbekfeabccchfdfigdefgkhhhlipmmlkmodY]kmnnqotpqk‹ŠŠ‹ŠŠŽ‘’‘‘ŽŽ’‘‘‘‘’’’’’•—–“‘ŽŽŽŽ’’•——–—–””““”•˜˜—••–™š™˜š•˜šž¡œ œ¥¢¢¢¢¢¢¢¤¢££¢ ¢¢¡¢¦¦¨©©¨¨¦©©«¬«©§¦¦¤£¦§©¨¨§¦§ª¬®¬¯¨©¤£Ÿ›š˜–—‡xsnmmzƒ’¢§©®±´´´±®¯ŽsXTJ[iz“š£ª°¯¯¯¯¯°°²¶¶¶¶±³³³²´°µ³´´µµ·²´´²²²±²´µ²²²´µ¸¶µ³¸¶¶´·¶¹¶¶µ³³³´°¬¨¬®¯¯°®¯®®¬¯ª¬¬¯¯²±±²·´´±±±²²±¯±±±²µ³²§ ¢¡£¡¤¤¥¦¤¡ ŸŸŸž›™—–’’”–”‘’“”“”“ŠŽŽŽŽ‘‘ŽŠŽ‹‹‰ˆ‡ˆˆ‹ŠŠ‰ŠŒŽˆ†„†‡ˆŒŠ‰‰Šˆ‡†‚~‚…„„„ƒ„†‰‰‰‡Š‡ŠŠŠˆ‰‰‹‡‰Š‰‰ˆ„……ˆ‰ŒŠŠ„‡ˆˆˆŠ‹‡„‡ˆ‰Š‰‡‡†‡‚…†…„†€€~z|~ƒ†‹‹ŽŒŒ‰ˆˆŒˆˆˆ‹ŒŒŒŒ‰‰ˆ‹Œ“‘ŽŠŠ‡‰ˆˆ‡‹ŠŠŠŒˆ…€€€…„}ttu}‚ƒƒ„ƒƒ‚ƒ„…€{~„…‡„„€}y|z{zxvz{{{{zzwvwmcer{vwwvtz}€}}|ytsrvxxvyyyw{‚ƒ…~yupkcegffghgcbaaacceghgdabccb`\_`__aafeeefekiijlglona]]lmnoqmnmke‹ŠŠŠ‘’‘‘’’’’•“““—“™••‘’Œ“Ž••–•™˜™•—’‘’“”™–˜““”–—Ÿ›™˜œœœ£ Ÿ¡§¦§¢¥¦¥¢¨¥§£¤ ¡¢¢¦¦®ª¯«««®©««°«ª£¡¡¢£§¦¥¥¦¦§®¬²±¯¨ª£¤š›––“ˆumfbbew‡“¦¨¯±±¹µ¶¬¬|gSUUay†˜§§®¬¯®´±³³³²¶µµµº¶·³´´¶³µ³¶µ¶´´´¸±µ²±³¸´µ´µ²´³·¶º¶µ³ºµ¶´·¶¹·¼³±²µª«¯²±±®¬°¯´®«°¯°°¯²¸±²²¸³´³·±µ±±±µ²·³µ±²®©¤¤££§¥«ª©¤£¡¢Ÿ¡Ÿžš™–•’’’›››””––“”Ž‹ŒŽŽ“’‘ŽŒŒ‹ŠŠŠ†ˆŠ‘‰‰ŠŽ‡…†ˆŠŠ‰ˆˆˆ‡…„ƒ‚‚……„ƒ‚‡Š‰ˆ‡‹Š‰ˆˆ‰ŒˆŽ‘‰‰‰‰…„†ˆŠ‘Œ‹‹Š‰Šˆ‹‘‰…„††Š‰ˆˆˆ‡†…‹‰‡†…‚}|}„ŒŠ‹Œ’Ž“ŒˆŒ‰ˆˆ‡•Ž“‘‘’ˆ‹Š”ˆ‡€ƒ‚…zstzˆƒŠ‚‡ƒ‚ƒ‰ˆ‰„‡}‚„ˆ‡„†}yw{{{yz€~{|‚|xwodbat{|‚zzz~€€‡zyxxw}}|}~{~~~ƒ‡{vvojikorqqmkgeefedcjhigdbbdhc_\]^bbbaeefdiiihhilinmmfbdlnxrqmnljcŠŠ‹‹‘ŽŽŽŽŽ‘‘“’’‘’’’‘ŒŒŒŽŽŽŽŽ•’••–••”’Ž‘’“””“’“–——ž›š™ž ŸŸŸ £¢¢¢¡¡¡¢¢¤¤£¢¢ ¡££¤§¨¬©ª«¬©©ªª§¦¤¤¥¢¡ ££¡ ¤¦©¨¨©©ª©¨¨£Ÿš•…vmf_\Z`cw‰™¤¨«¬±°¯°£›k_O\fs‹›ž¢¨«¬¬¬°°²´´´µµ²±±±³³´·²°²²³³³³´±±°²±²³µ³³´´²²²´³³³´³µ²´³³´¹µ»²¯®¬¦©©¯³®«ª¥¬«°¬©®«°®¯±±´´°¯®°°±±±°±±±²²²·±°¬¬¬¨¥¢¤¤¤¤¦¦¤¢¡¡¢žœ™›™š””’”•š™š—–’‘“Љˆ‹Œ““ŽŠŒ‹ŒŠ‹Š‹Š‰‡ŠŒŽŒ‹‡ˆ†‰‰ˆ„‡ˆ‰‡Š‰ˆ„~~~‚‚ƒ‚…‡ˆ†…„‹‰‰†ˆ‰‰‡†„‰‰„‰†‡‰‰‹‰‡‡ˆ‰‰‰ˆŠŠ‰„ƒ‚‡…†‡†……ƒ†ˆˆ‰†€…{}~…Š‹ˆŠŒ‘‰‹ŠŠŠŠˆˆ†Žˆ‡‡ŽŠŽŒ‹‘ŽŒŒˆ‡‡‹Žˆ‰ŠŽŽ‰„ƒ‚‚€zuqzƒƒ‚‚‚ƒƒ…ƒ„‚€~ƒ†ˆ„ƒƒ}vyy|y|{||}}{{xvhWJ]w{~€z}~~~€€}wwvxz~~€~{|~„Œ„|vpmimnrvxzupnkiggfedigiifdefffb]]]]^babaccjiiiiihhhhihhhlnpqqkmlhbŒŒ“ŽŽŽŽ”ŽŒŽŽ–“”’‘‘•Œ‘ޑޔ”˜–š””“’‘—–œ”’’‘“š˜šœœžžž¡ ž¡¡¢¡ ¤¢¢ ¡£¬¦©¥¤¡¢¤¥¨®ª´±²¨¨¨©¦¨¢£Ÿž ¡£ª¢¢££¤©¥®©©«¬«©©«›ŽŠˆƒ|vlb\VTV^fz”Ÿ¯®¯®³°²³´›r^[T`q€•šœ¢ª«¬«²¬®«°°·´µ³¸²±°´³¶³²±±±·´¸´·²³²³±³±·µ¸³³µµ³¹µ¶³´³µµ¶³µ³¶µºº¼¯¬ª©ªµ¯¬«¬«¬ª°«ª°®®°²³µ¯°®®¯³¯®±¹±²±·²³«¬©¦¥¥¦©¥¨¤¢¢¥¡¢›šœ›™™™–œš¡šœ™˜•“‘Œ‹Œ™’’‘Ž‹’‘‹ŽŠŽŽŽ’‹‰‡ˆ‡Š‰‰‡Žˆˆ‡Š‰{{|€„ƒƒƒ‚ƒƒ‚†…Œ†…ƒŒŠˆˆŽˆ‰……‡Žˆ‰…‰‰‰ˆ‡†……ˆ‡‰‰ŒŠŠ‹†‡†‡…†‡‹„„„ŒŠˆ†……€~~€„‹’‰‰ŠŽ‹Œˆ‰‰ˆŠ‡Œ˜‰‘’’ŽŒ‡„‡‘ŒŒ‰„ƒƒ„†„€xtw„ƒ„…†„……ˆ…‰„…€„ˆ‡‰‰Š€zxzz}zz{€„€}…~}yv^KGYw|}~†~ƒ~zvuu{{ƒ†€€€‚„‡ztoihrx„†€}wssuttmiiiiiijglhthgee\Z^ccdbedkjjjkiiiijkjpmmmnorqslgbŒŒŒŠ‘ŽŒŒŽŽ‹ŽŽ‘‹‹ŽŽ‹‰•”—“”‘”’““–˜•‘‘•—˜˜˜šžžž¡œ ž¡¢¢žžž¥¢¢ ¡£¥£¥¥££¥§©ª«««ª¬««§¨¦¥£¡ž› ¤¤¤¥££¤¦©§¦¥¦¥£¢¡ž™“Œ„|wrlfbZWQUVcn€”¡¬¯¯¬¬¬ziV]_oƒŠ—› ¢¤ª©ª««©««°°±²³°°¬±²¶±°°±²´²³±°¯³²²°±²·´¹±³µµ³³´³³³³µ²³±´´¶²±²®¦¥¥ª«°¯«ª¨¨§©ª©¨©©¯¯¬®¬¬«®®®«®²´°°°²±²®¬ª¬«ª§§¦©£££££¡Ÿ¡››šœœš˜˜˜œ›ž›™—™™•‘Œ’”‘‘ŒŒ‹‹Œ‹ŽŒ‹‡†‡ŽŒŠ…†„†‡†‡‡†…„„„ˆˆ‡|x„……ƒƒ„„ƒ…†Œ‹‹„…ƒ„‚‡ˆ…„„‚…‡Š‹Ž‡‡†‰ˆˆ‚‚‚‚‚ˆ‡‰‰‰‰ˆ‡Š††‚„…†ˆ‹„……ˆ‰‰†…€~~†Š‹ŠŒŽŽ††‡‡ˆ‡‡‰ˆˆˆ‡‡ŽŽŒŠˆŒŽŽŒ‹ŠŠ‰†‚…ˆŽŒ‹‹Š…‚~……„„~w{|„„„……„…†‡……ƒ‚{€„€„‡ˆ†ƒ~|z{||yzx€€~}}}zrRC;Yw{|~€€~|~}~{ztvvz{€|}‡Œ…zvqmkvˆŠˆ…ƒ‚|zywrrqomjfhgihggfea[Z]^_abddloomlgjkjjkhkmmqnnonmjhcŒŒŠ‘ŒŽ‘‘’Ž‘‘‘‘‘‘‘‘“ŠŠˆ‹‘–•˜’•‘““›››“‘’”™™Ÿž¡¢¢¢¢Ÿ ¡Ÿ¢ ¤¢§¢¤¡¦¥¥¡¡¢§¦¦§««¬ª®¬°¯°«¬ª©§¨¨§Ÿœž¡¡¨§«¥¨¨ª©¨§¦¦¦¦¦£¤ŸšŠƒ}toid_ZUQOQXgv‰¡¦³¯±®®°²¡œlaT\gwŽ— ¢«©©¨«ª©¬««¯¯¶°³³´°²¬°°·³¶±°±²²µ²²²²¯´³´°·²·´¸±´´·³´´¹´³³´´¸¸¸´·¶¶¬¨§ª«²±±®®©©©©¨¨©«ª««¯¯¯¬®¬«¯®¯²¬®®³±µ°°¯µ±±®¯±±¬¬¬§ª£§¦¦¢ Ÿ¡Ÿ ¡¡››™››œœ š˜— ˜—‘Ž““—’’‹Œ‰ˆ‹‘Œ’Œ‹†ˆ‡ŽŠ‡†‰„‡†‡†‰‰Š„‚ƒˆˆˆ~‡†‰……ƒ‰‰‰……‡ŒŒ„…„††„‚‚„„‰‡ŒŒŽˆŠŠ‘ˆˆƒƒ„‰ŠŒ‰‰‡…†Š‡†„ƒ„‡ˆ‰†‡†‡ˆ‰ˆ…~~€…Ž’Ž’ˆŠ†ˆˆ‡ˆ‰ˆŠ‰Š‹ŒŠ‰‹’ŽŒŒ‹†„„‰Š–ŽŠŠ‰†€†‡‰‚~~†……†‹†ˆ‡†ˆ‰†‡‚€„ƒ„‡Š…‚~~}ƒ|yy{€‚ƒ„~}}ymM76[t‚{~€„€}|zyxxy~|€~}|~ƒ‡Œ„~{xuv~ˆ˜“’‘“’††„€}{rnnnlkigeeb_[[[_^bcdbnoxmlkkklkkhiknnmnqpnijdŒ‹ŒŠŠŠŒŒŽŽ’‘ŽŒŒŒ‘‘ŽŽŒ‰ˆ†‹Ž‘’•”’“““’‘Ž‘“”•˜šŸžžž¡¡ Ÿžœ¡ž¢ ¢¡¢¡ ¢¡¤¢¢¢¨¨§§©©ªªª©¨§§§¥¤¥¥£Ÿžšž ¥¥¨§§¦¨©©¨¥£¤¤¢žŸ’Š}tnfc`]XURPNTXl~§©®®¬¬©¨•ˆt`^Vdq•œ¦¦¦©¨¨¨«¦ª¬¬¬®¯¯¯¯««¬°°°°µ±³²²±¯²®¯¯°°³±±±³³²±´²±±³´¶´³³³³¸¶µ³¶¬©¢¨§¬ª¦£¤£©©¨¨ªª«¬®®¬ª««¬®¨ª«¬ª®°®®¬®°±®®¯¬®¦«¦©¤¦¦¦ Ÿž ›š˜ššž ™˜˜—’‘ŽŽŽŽ““•’Ї„†ˆŠ‹‹‹Š„†ˆŽŒŠ‡‡‡‰„„„…†‡‡‡‚„„„„…ƒ„…‡ˆ‰………ˆ‡‡††‡‹ŒŠ„„„…ƒ€‚€„…†„‡†ˆ…ŠŠŒ‡„ƒ…ŠŽ†„‚…†Šˆˆ…„„…І‡€€€~~„‹‘ŒŽŒŒ‡Š†‡…‰‰‰ˆ‰‰ŒŽŒˆ‹ŽŒ‹ˆˆ‰Š‹…€„†ŠŒ‹ŠŠŠŠŽˆ„€†‡‰‚ƒ„…†ˆ‰‡‰ˆ‰ŠŠ‡‡‚}€……„„ƒ€~|}|zv{|€‚‚ƒ‚}}wqM:4Tov{}€}||€|{zzz|{~}~x|†‡…ƒ…†‚~~…’•”””’‘‹‰†…ƒ‚{tssvxqjieca`ZZXZZZZ_agkkkkjjklmkiijmnoorujfhdŽ‹ŽŠŠ‹ŒŽ‘—’’’•””Ž•“Ž‘Šˆˆ‡ˆ’‘•“•‘‘–’‘‘˜–™š›ž¨žŸ£¢¡ŸŸž¡¡¡¡¤¢¥¡¡¢¥¤¤¦§¨¨§¨¦ª«®®¨©§®§§¥¥¢¡žžŸ¦¦§§¦¨§§¦¥¥¤¤¡¢š‹oeb_^]WTQOLLU`rŠ—ªª¯¯³µ¶£Ÿ„uiZ\^h{Š¡©©ª«®©ª«¬©°¯±³²³®¯ª¯°°¯¯°´´´´µ²²±²®¯±´³³±³±°°´³²²¶¶¹¶¸´¹¸¸µ¶³µ¬§§©¬µ®°¬ª¥¡¡§¢¨¨¨¨¯®³±¬ª«««¯®¨«©«¬³®°´°±¬¯³®®¯¹¯©¬§§¦ª¦§¢£Ÿ ¡¥ŸŸ›œ›š¡ ¡˜˜˜˜‘“ŽŽŽ“—“•’ˆ„ƒ„‡‹Š‹Šˆ‰ŠŠŽŠˆ†Žˆ‰†………‡‰†ˆŠ„…„‡…Šˆ‹‹Š‡ŒŒŽˆ‹‡‡‡‹‰„†……‡‰ƒƒƒŠ‡‡†‰‰‹ŠŠŒŽ…ƒ…„ƒ†ŠŒ†„…Œ‹‹‰…„ƒƒ„‰†‡ƒ‚€ŠŒ•’—”ŽŒ‰‰ˆ‹‘Š‹‹‹ŽŽŽŒŽŽŠŠ‡‰ˆ‹…€€…ˆ’Ž‹’Š‹Šˆ……†‡ˆ‰‰‰Š‹Œ‹‹ŠŽŠˆ‚†„Œ……‚{{{‚|yz‚…ƒ†ƒƒ‚‚}ytX><Pisy|}€€€€‚~€|€~ƒ}{€ˆ„‡ˆ…†‹’ ›››˜œ“ŽŽ†„wtu„{rplkaa]ZXZZ[Y^`dipkljjkqmnijjpqqotogemeŠ‹‹Š‹ŽŽ‘“’‘‘‘‘’ŽŽ†††‰‰Ž“‘’‘‹‰‘”–•™šžžŸž¡¤£¢¢¡¢¡¢£££¥¦¨¨¨¨©¦§§§§©§¨££££ ¤ ¡ ¢¤¥§¨¦¦£¤£¢¡ œŸžœœ“Œ€qeWVWYWVQPMQNYcyŽœ§©ª¬°¬ªšŒwb_Uaju€Ž›¡§¨©«««©«¥¬©¯¬¯±±®±«®«¬¬«¯¯°¯±°°¯®©®°±³³³±³°°¯®±²¶µ·´´´¶¶¸µ³°¬§©©«®¯¬ª«£¢ ¢¢¥§©ª¬¬¬ªª¨¬ªªª¨¥§¨¬«®°°°´³¯¬¯®®®®¯¯¨§§¨¦§¥¥££¡£ŸŸž››šœ›š™””’‘ŽŽ‘‘—“Œ‹†„ƒ‡‹ŠŠ‹Š‰‹‹ŒŠˆ‡‰ˆˆ††„‡ˆŠˆ„„…„†††††ƒ…†Š‹Žˆ‡„‡ˆˆˆ…‡‰‡††…ƒƒ††……†‰‹‹ˆ…„„ƒ‚„„„ˆ‰Š‰‡†…ˆˆ‰ˆ‡„‚|‚……‡†„}€‚ƒƒ‰Œ‘‘ŽŽŽ‹Š‰‰‰Œ‹ŒŒŒ‹Œ‹ŒŠŒ‹‰††††††‚€€‰ŒŒŒŒ‰ŒŠŠ‰‹†‰…‡ˆ†„ˆ‰‹ŒŒ‹Œ‹‹‹‹‹ˆˆƒ„ƒ†‰Š…„yu{}~|‚{{|€‚‚‡ƒ‚€~{wfREUcltwz}}}||||~~€}~~{‚‚ƒƒ…‰‘“’Ž‹•œžž™˜—’މ†zxw…„}wtsi`_^\YZ[[[^_beefhhjjkkmikkmlllnjfaa`‹ŒŒŒŒŒŽ‘‘•“’“‘Ž“”ŽŒ‹ŒŒ“‘“’ŽŒ‹‰Œ”“˜˜™œŸ ¢ŸŸŸŸ¤ ¢ ££¤£¦£¤£¥¦§¤ªª¬ª®®¥©¨ª¨¦§¥¦¡¢¡¤£¤¤¨¤¥¦§¦¦£¤¤£œ ™‰‚th]QPQSZSQONOS`ož£²¨®®¬ª~kZZXan|‘–¢£¨¬°¬®ªªª¬¬©°¯²²±°°®¯¯±«¬¯°³®µ°±¯µ¬®·¶¶´¶²³°°°°°µ³¼µ¼´µ´¸·¹²®¬ªª²ª°¯®®®ª«¥¦¢£¤¦ª±°±®¬¬«®§««ª¨¥§¨¬²´´±´°®¬´¯±±°°±©ª§©¥§¥¨¢¤£¤ ¡ž›œœ ¡žž˜™”•‘”‘––—”˜‘ŒŒ†…‚†‹‰‰‰Ž‘Љˆˆˆ‰‡†ˆ‹ŽŒŒ„ƒƒ…„‰ˆ†‡‡‡†…‰‹‡‡„‰ˆŠ‡„„‡‡Š‡†…ƒ‡…„„†‹“Ї…††ˆˆ‡„„ˆ‰‰‰Œ…‡‡‹‡ˆ‚|~†„‹‡†~€„‡Š‰Œ‘‘•ŽŽŽ’ŽŽŽŽŽ•ŒŒ‹‹Ž’‘І†ˆˆ‹†ˆ‡†‡”ŒŽŽŒŒŒŒ…ˆ‹ˆŠ‰™ŽŽŒŒ‹Š‹‹“ŒŠ†‡†Ž‰Š…„ysw‚}}}}…‚‡‡‡„††‡€‚{zob]\agpuy‚€|~|€‚„„…†€€ƒƒƒ„…ƒ‡›šš”‘‘˜›¤¤¦£¤¢™–”•މˆ€€~‡…zwqjda_a\\\\]^`accchhnkjjmllmpmomqieccc‹ŠŠ‹Œ’‘’’‘ŽŽ‹ŒŽŒŒ‹‹ŠŽ“’‘‘ŒŒŠŒŽ“”™™›œž šœšžŸŸ Ÿ¡ ¡¡¤£¤¤¦¢¤¥©ª©¨ª¬¨¥¥¦ª§§¦¦£¥ ¡¡¥¤£¤¤¤¥¢¥¨£¡ œ˜™›‘Š~qg[WOOOSSQNNNTWgv‰ž¤¨¨¨¨£„pdT^cnx€Š—¡£¥©®®ª©©¨¨««¬ª°°®«¨¯¯«¬ªªª®®¯¬®«ªª®®®¯¯²³³²±°°®°±µ³·µ´´´³´²°¬«§«±ª¬«ª©©§¨¥¥¢£¨©¬¬¬«¬«ªª§ªª«©§¥§¨¬¬¯°¯®®¬³®±¯¯®°©©§ª¢¦¤¢ žŸœžœœšœ¡¡¢šž—•‘“‘“’“”“•”˜ŽŒ†…‚……†‡‹ˆ‰‰‹ŽŽ‹‹‹‡†……‡‹‰ˆ†…„„ƒƒƒ„‚„‡‡ƒ……Љ‡ƒƒ„Ї†††……„††‡‡†‚‡ƒƒƒˆ‹‰ˆ…ƒ†‡‡††ƒ†‰Š‰‹ŒŒ…„ƒ†ˆ…|~€‡„„ƒ€}€‡Œ‹‹ŒŽŽŽ‘‘‹ŒŒŒ‰ŒŽ’‘‡††ˆ‡…‚ˆ…ˆ‹ŽŒ‹‰ŽŠ‡‡††…††ˆˆ‹Œ’Œ‹Œ‹‹‹‹ŽŒŒŠ‰ˆ‡ˆŽŠ‰ˆzpw}z}~~„‚‡…†…„ƒ‚~}{wphecinwy~}{|{€ƒƒ„…†€€€‚ƒ‡‰„‚—œ ™—•𢢣¢¡›™–”•’Žˆˆƒƒ‚†‡„}yqmhea`]^___`ab_`_bcilkimmmnpjkjjheccaމˆ‹““™’‘‘’ŒŒŒŒŽŒŽŽŽ“Ž‘••”’’‘‘“”›ššœŸœžžœžžžŸ¡ ¡¡¢¡££¨¨©¦§¤¦¥©©©©§§§¥¦¥ª§§¦¥¦¥¥¥¦¬¤¢£¦£«¢¦£¡Ÿœž–™‰qf]RONNPPXRPLNV^m¨¨¬ª¬©°—xe_X`lx‹”™¥£¦¥«®¬ª©¨¨¯ª¬ª°¬«ª©°¯¯°¬ª¬®®®®¯¬°«¬±±±¯±¯³²³²±±²²²³µ³¸´´µº´·°®««ª®®±ª¬¬«ªª««¨©¢¥§¬¬¬¬®±«®ª«««ªª¨§¨ª¨¬«²µ¯«¬²³³±±±²®°ª©«£¦ ŸŸ¡Ÿ¡Ÿ›œ›¡Ÿ¨¢£˜ž–”“™“”“˜—˜“–——ŽŠ†…„‰ŠŠ‰‹‹‹‹‹Ž‰††‡…‰Š’‰††‡…‡†………‚„‰„‰‡Š…„І…†Š†…………ˆ‡‡„ˆƒ„„‹ˆ‡†…ƒ‰‰Š†……‰ŠŽŒ’Œ…ƒ‚……ƒ~‚ˆ„‡€~~‚†”••“‘‘’ŽŽŽ‘‘‘‘“˜‘Š‹Œ‹’‘ˆ†…†ƒƒ‡ˆ‘ŒŠ‰‹‰ˆˆ‡‰†…‡‰ˆ‹•‘”ŽŒ‹•‹‹ŒŽŒŒŽŠ‹€tt„~||‚‡…„„‡‡ˆ‰Šƒƒ~€~tmjkpxz}}|{||†…„„…†‡‚‚€ˆ†Žˆ„‰—™¢ ¡›š¢Ÿ§¤¥¢ ¢ ž•𓕉ˆ‡‰…yzoifgfgddddacaa^bbhjommmropljhhhhehc‡‰ŒŽ’’’’Ž‘‰Œ‰ŠŠŠ‹ŽŽŽŽ‘“•‘‘‘‘‘‘’”•—™™œœœžž››œžžžž ¡¡¢£¤£¥¥¥££¢¢¤¤££¢¤¤¤¤¦¥§¢¥¦§©¦§¦££¢¤£¡œ›››˜˜™‘Š~qdWSNPNQURPONNNYduˆ”£¨©ª§¤¢|n\_]kxƒ’–›Ÿ££¥¤¥¥§£¦¥¨©ªª©©«¬¦«ª¯¯®«ªª©©©ª«««««®²®°¯°¯³°°²²´¶µ³²³´µ´®¬®ªªª®¬«ªª¨©©ªª¨¥¤£¥¥¨««¬®¦¦¤¤¤§¦©®¨¦¬©«©©¥««®©¬««ªª©¨¤¡žžžžžœœ™›œ¡¡£Ÿœ˜—“““•“““”•–“–’‘Œ†††ˆˆ‰Š‹ŒŒŒŒ‹ŽŒŠ†………†‰‰‰„ƒ……†ƒ‚„……€ƒ‡……†‡†…………‚ˆ†„ƒˆ‚…„„„‡‚‚‡……„……‰Š‹ŒŒ‹€……ƒ€‚ƒƒƒ€{}‡Œ’‘’‘‘ŽŒ‹Œ‘‘“”˜‘‹‹ŠŠ‰‹‹Ž“Šˆ…†‚ƒƒˆ‹ŠŠ‡ˆ†‰‹‰‡‡††‡ˆ‡Œ‘”‹‹Ž‹ŠŠŽ‹‰Š‹Ž‰yxz€}}}ƒ†‡…„…„…ƒ„ƒ€€~}ztqrt{|}~~{}}…Ї…„ƒ„ƒˆ†‰ˆŠ”œžŸ¡žœžŸ¡¢££¢¡¢£Ÿ—š““‹ŒŽŽŠ…{smhklgedddaa`a\\]ffommnmmmmledddcedŽ‘ŽŽŽ‘•މŒ‰Ž‹‘Ž“•”–‘‘’—”•’“”š’“’˜–œœ¢Ÿ ›œœ››œ¡¡¡¡¢¢¥¢¤¥©£¥¡ £¢¡¢¦¡¢¢¤¤¨¤§¤§¨©ª©§§¦¥£§¦¥šš›™ ˜™Š†th\PMJLOPWSRMLLN]j}”›¦¥®¯š’}nfZ^cp†‹›ŸŸ¨¨©£¥¥¥¤§¥««¬¬«°ª¬«¬¨ªªµ¯®®¯¯¯¯§©©©ª±¯´®°°³²±±±°²±´³µµ¶¶º¶¸³¶³¹³³®¯«®¯²ª®ª«ª©©¬ª¯©¦¤¤¦¨¨¨«±¶®¯¨¨£¦¤¬¬ª¦¦¬«¯«ª©¨¨¬«°«¬©°ªª«¬¦¢ œ¡ž ¡¢žœŸ ¥¢¤š˜˜”—”™•——Ÿ–—–—“•†‰‰Œ‹—’ŒŽŽ‰ˆˆ‹‹‹‰Ž‹‹…ƒ„‹†…‚‚‡ƒƒ‚„ƒ……†‚‚†…„…‰†„………„ƒ‡…ƒƒˆ‡‡‡„††‡††…„ƒ‡‡‰ŠŽŒŠ‹ƒƒ‚‹„ƒƒ‚ƒ……†€}}ƒ““““—‘‘‹Š’‘‘—˜š‘ŠŠ‹‹‹Œ’“•‹…†ƒƒ†ŽŽŽŠŠ‡‰‡ŠŠ‰Œ‡ˆ‰‹Œ’‘“””‹Œ‹”ŽŒ‰‘Œ‹‹ŠŒ•‘…|xz~†€††ŠˆŠ‡ˆ…‹†‰ƒ‚ˆ‰Œ€~}}{yz|~€…‰ˆ‹……„„…‰ŠŒŒ‘‘’–Ÿ¡¢¤£¬Ÿ¡¡¨¨©¥¨¢ š™”””™’’𔓋ކ†ypospjhieiba`_]X]dfpntpmntmnfgbcbedŒŠŒŽŽŽŽŽŽŒŠ‰‰ŠŠŠŒŒŽŽŽ““—””’”˜•“““——›Ÿœ››œ™š™› ¡¡£££¢£¥¢ŸœžžŸ ¡¡ ž¢¡¢¢¥£¤¥¥££¤ª¦§¤££ ›–˜”–•’ŽqfXRMLKNPQSQPMJNQar†›Ÿ£¤¤¢žq`^^hox…•™œŸ ¢£¢ ¢¤££¤¤¨¦¨¨©¨¨¥«©©§©©ª«¬¬®¬«©¨§¨©ªª¯®²®°°±±²²±¯±±²²´µ¶¹·´µ³³³²¬ª°¬±«©¦§¥§©©§§§¦£¥¦¨©©©¬®ª¨§¨¤¥¤«¬¦¥¥©«©§¦¦©¨¨©¨¨¨¦©§¨§§£ ž™Ÿ¡ ŸŸŸŸžž¡¥£ ž™—””’”“”“—––’‘’‘‘‡‰Š‹ŒŒŽŒ‹‹ŒŒ†‰ˆˆ‡ˆ‰Š‹‡‡††ƒƒ~ƒƒ‚‚ƒ‚‚‚‚„„ƒ„……„ƒ„††„„†€„ƒ‡‰Œˆˆ„…€ƒ‚†ˆˆ‡ŠŒŠ…„ƒƒ‚†ƒ‚‚……€}}}ƒ‡Ž’’““•ŽŽŒŠŽŽ‘–𖑉ˆˆ‹‹ŒŒŒŒ‹Š†„ƒ‡‰Ž‹‡Š†††‰ˆˆˆŒ†‰ŽŽŽ’ŽŠŠ‹ŽŽ‹‰‰ˆ‰‡‹‹ŒŒˆ}y~‚€‚…†Š†ˆˆˆ…‡††ƒ„…ˆ‡†z||}€‚‚ƒ…‡ˆ‰Š‡†††ƒ†ˆ‹ŒŒ‘„ˆŽœ¡¥¦£§™¢ŸŸ¡¥£¤¡ ›š••”••””—•“Œ‹‹‡~wtuvqkiefa^\_aaadglntuonoljfg```bcŠŒŒ‘Œ‹Œ‘ŠŠ‰ŒŒŒŒ•”ޑޑ™”—˜—––””•——žžžŸŸŸž›œœœ› Ÿ¤¡¡£¦¥¨¢£Ÿœœœž¢¢¤¡¢¡¡¢¢¢¡¡¥¢¥¤¤¢¨¤¬§§¢¥ œœ›–™™š‰‚sf[PMKMPRTUUQOKIPVh}§¦ª±ŸŸ„shZ^al}„‹’œ›ž ¥¡¢ ŸŸ¡¤©ª«©¯§ª¨«¦¨¦ª¨©©¬±ª¬®«¬ª®«°©®¬°±²²²±µ±²±±°´³¶´¹¹¸·¶´ºµ·±°«««±¯²±ªªªª¦¦©«ª«¨«¦©¦ª¯¬±¯³¬®©ª§©¦¨¨®««¨«§¨¦¥¥¥¦©§¨©««¨¬§©¦¨¢ žŸŸŸž¢ ¡¡ž§¦¥¤£›—•••“—•–”—––’”““’‘ŽŽ‹‘‹Š‹Œ‹‹‘ŠŠ‹‹ˆ‡†‡‰‰‹‰ˆ‡‡„‚†…‰‚€…‚†‚‚ƒ‡ƒ†††…‰ˆŽ„‚‚…„„‡‹‹‰ˆ………‡ƒƒƒƒ…ˆŠ‡‰‰Š„„‚‹‚†„ƒƒƒ…†|}ƒ‹Ž–—˜—–“”””ŽŽ’’’’‘’••”Žˆ‡ˆ‹‹ŽŒ‹Œ‡ƒƒ‰‰ŒŽ‘ІЉІ‹‰Šˆ‡Š‹™’’‘ˆ‡ŒŽŒŠ‰ŽŠŒ‰Ž‹ŒŒ†‡„„‚†………†‹ˆŠ‰‹…‰††„‰ˆ‡‡}ƒƒ‡‡…„…‡Œ‰‹‹Š‡‡‡Œ‹‹Š–‘‘‘‘ƒ‡œœ¡¦«¤££¤ŸžŸ©¤¨ ¡ ¢–‘“™š–š–•‘Ž„{zqkik`[Z`cjjhhkoxsqqqkgegbaaabŒŠŒŒŒŠ‰ˆˆˆ‰‰‰‰ŒŒŽŽ’ŠŠ‹Ž‘“’’”•–‘–“–———œšœžž™œœœœŸŸ£¡¢¤¥¤Ÿœ›˜™–œŸ¡¢£¡£¡ £¡¢¢£¢¦¢¢¢¥¤¤¤¢¡ œ™•””‰~qfYTKMLPUTSQQPNNMV_q†“¤¦¦¥¦—‰yhaYcjw‡‹’˜œ £¤¡ŸžžŸ¡¤©¨¨¨¨§¨§§¦¦¦ª¨¨©«ª©ª¨«©¬«¬ª¯¬«®¯²±±±²°°°²±±²²³³³·µµµ±¬ª¨«¬®®®®ª¤¤£ª¨¨§¨¨ª¦ª¦§§§§¬®¬¬«¨¦¦¦§¦©©©©ª§ª§¨¢£¤¦§§§§¦«ª¨§§¨¥§¡ Ÿ›žžœœ¢ž¦¢¡™™–•”–•–••”—’‘‘‘Ž’ŒŒ‘‘ŠŠ†ŠŠŒ‹‹‹Š‡‰ŠŠˆ‡†‰ŠŽˆŠ‰‰‡†…„‚ƒƒ…ƒ‚‚ƒ„‚‚‚ƒ‚„…†‚€……ˆ‰‡‡†‡‡†…„ƒ‚ƒ…‡Šˆ‡†‡ˆˆ€‚„‚„ƒƒƒƒ‚€zz{„Ž’“”––•••““ŽŽŽ’‘“““ŽŠ‰„‡ˆŒŒŒŒŽŠŠŠˆƒ‚„Š‹‹ŠŽ‘ŠŠ‡‡†††Œ‹Š‰‹‡Š‰ŽŽŒ‰„ˆŒŒŒ‹Š‰‰Ž‰‰‰Š‰Œ‹„~ƒ………„‚€†…††‡‡‰ˆŠ„„„…„†‡Œ…„~~ƒ…„…††ˆ‰Šˆˆ‡‡„ˆ‡‰‰‹Œ‘‘•–ƒ}z˜ž¢§¦¥¤¤¢ŸŸ£¡ ¡ž¡˜•“›™™••–”ŽŽˆ‚z}|yutrn^[Zadfghhlowqqplgfddddcb`ŽŒŒ‰Š‰ˆˆŠ‡ˆŠ‘‘’Ž’“ŒŠŠŽ‘““’”““”•–˜•›˜™™›š Ÿ¡žžŸœ›¡Ÿ££«¤§ž˜˜ššž¡¤¥¥¤¤££¢£££¡£¢¤£¦¨©¥©£¨¢¢ Ÿ™–––Š…ui\PLJJNQYWXROMLMP]j{”š¨¦«©©m^YXbr}‘””š™ŸŸ¥¥¥ žžž £¦«¨¬¨¦§¬©ªª«©©ª¨¨©¨«©°¬®¬°¬«°²¸±±±²±´°µ²²²³³´²·³³±¯««ª±®¯®´¬§¤¤¥«¨§¦¨¨©§«§«§¨¦®¬°ª¦£¦¤¦©¨®©©ªª©¯¨§¤ªª«©¨©ª¯«¬¨¯ª¨¤¦ ¨¡¡›£žœœž£¡¦ ¢žž–›“–—š™™”–“”‘‘Ž’’Œ“’‰‰‡‹‰‹‹‰‰ˆ‹‹Š‰‰Œ‹‹‹‡……‡‡ˆ‰‰„†€ƒ‚‡ƒ‰…„…Š‚ƒ…ƒ‚ƒ‡…ŠŠ‡‡‡…Ї‡„ƒ…‹Š‹ŒŒ‰ˆ†ˆ†„„„ˆ……„‰†ˆ€~|‚Š˜•˜–•””––“‘‘‘“’—““ŽŒˆˆˆˆŠŒŒ‰ˆ†‚‡’Ž‘‘’‹“‘‡…‡ŒŒ‹ˆŽŽŽŽ‘ˆ……Œ‹“Œˆ‡‡‰ŽŠŠŠŒŒ‹ƒ‚‰‹’†„…††‡†††‰ŠŠ‰…„…†…‡†Œ†…ƒ†„„†‰…†‰‹Ž‡‰††„ˆ‡Š‰Œ“–Ÿ˜yfw’œ¦¥¤¤³ª¨¡¢¢¦¢¢¡¡¡¢››š›œž–••˜–•Љƒ‚€~~}xra]afffghioowwyojfefigedfb‹ŒŠ‰†…„ˆ‡‡…ˆ‰‹ŒŽŽŒŠŠŽ“’”“““•———˜˜™šœ›žžœžžžž›œ £¤£Ÿž™—–›¡¤¤¥¥¤¥£¢ ¡¡¡ ¥¢¤¤¥£¢¢ ›››š•Ž‹vi\VKKJNQSWWUSNMKQVgu†™ž¦¥¤¡œ†seUZ[hvŒ‘–••ššžŸŸŸŸœžž¡¢¥¦¨¨©§§¨¬««¨ª¤¤¥¨§¨ªª§¥¤¨¨©ª¬¬¬«ª¬®°±²¯±°±°°µ²²±³°²²³¯®ª«©«ª°®¬©§¦¥¤¤¤¦§§¦§§©¨©¦¦¥¦¦§¦¦¥¤ ¢¥¥¤©«ª¨§§¨¨¨¤¤£¦¦ª¥¥¥©°®¬¬©«¬¥¢¢ ¡ž››››œž ¢¡ œ›˜˜˜——›“””™—–”–“”ޑދŒ‹‰Š†‰‰‹‹‹ˆˆ†‹‹‹‹Œ‹ŒŒŒ‹ŒŒŠ……„†…†‡ˆ„„‚‚‚‚‚‚ƒƒ„†‡‡†‰‚€…ƒ„„…ƒˆ‰†‚„…Ї…„…‡†…„„ƒ‚‰†‡ˆ†„…‡ˆ„…†‰„|~}„‹’“”˜”–’”–“’’‘““”’“ŽŒ†‰ˆ‹Œ‰‰‡‰ˆˆ‚€~…ŠŽŒŒ‹ŒŒ‹‘ˆˆˆ‹ˆŽ‹ŒŽŒŠˆ‚„…ŒŠ‹‡†„‡‡ŽŠŠŠŽ‰‡†‰Š‹ˆ‡‡‡ˆ‰‰ˆ‡‡‰ŒŠ†‡††„„ƒ‡†ƒ‚…‚‚‚„…††ˆŒŠ‡†‚‚‚…„††‡ˆŒŒ”˜™™‘qtz•› ž£¢ª©§¤¤¢¥£¢œœœš™˜›——–”“–™–“‘Šˆˆ‡†…ƒreiolgghjkoovwyngbefgggedbŽŽŽ‰‡†„…‰ˆˆ‰‰‰ŒŽŽŒŒŽ’’“‘“““”˜—š›››¢›žžžŸŸŸ¥¥¤ž£žŸ¡¦ ¢¡¡žœš™›£Ÿ§¦¦§ª¦©£¢¡¢¡¢¢¬¤§£¥¤©£¤ ™›››Š„sg^TPKIIMVXXXYTRMKT\n†’¤¤¬§§•zjaUY`k{…‘“˜šš››šŸŸžžž¢Ÿ¨¤©¥§¨ª§§¨®¬«¨©¤§¦¨©¨¨¨¦¥¤¦¨««³®¯ª¬¬±°°±³°±°·±º±µ²´²´²¶±²«®®«±®®§¦¦§¥£¤¦§¨¨¨§ª¨§¦¤ª©§¥ª¡žž¡¤©§««ª§¦¦©¨¨§¦¤§§ª¥¨¥¨¬¬¦¤¤¤££Ÿ¢¡ ž¢¡§§§ ž›››››››œ””•˜˜–––“•Ž‘ŽŒ‘‹Š‰Š‹Œ‰Ž‘‹‹‹‹‹ŒŽŒ’Œ‹ŽŠ‰…‡‡†‡‡‰Œ„‡†††‡†…†‹‹ŠˆŒ‡ˆ‚€€†ƒ……ˆ‡ˆ„„„…‹…„„ˆ‰†…ƒƒ‚Љ‰‡ŽŠˆ‡†Š€~€…‘˜••”™––’”“‘‘˜’“’’’””˜’“ŽŒŠ‰‹Ž‰‰‰Š‰‰€~‚‹‰“‹‰‰Œ‘‰‹ŠŽŽŒŒ‹ŠŽ‘‰…‚„ˆŠ‹‡‡‡ˆŠŽ’ˆ‡‡Š‹‰‹ˆŠŠ‹‹Š‹Œ†ƒ†‰…ƒ‚ˆ„ƒƒ……‹ˆ‡†‹‹ŒŠˆ‡†‚„‚‡…‹‹ŒŒ’’¢›’Œ†‰˜š¤£¤¢ª©¬ªªª«¥£ žœœ ››˜™–•“–—™––‘‘’Ž‹‹‚vtxwvljlooorxxzldaehnjmhee‰ˆ‡‡ˆˆˆˆ†ˆ‰Š‹ŒŒŠ‹‹‹Œ‘‘‘‘”•——šš››››žžž ¡ ¢ž Ÿ ¡¡ Ÿ šžŸŸ¢Ÿ¡¡¤¦¥££¡£¢ž¡£¥¢¢¡¡¡ ŸŸœš™‘ŽtgZTKPLMMOQTVWWTRRPZdw‰—¥¤¤¢¡~qab[bis€ˆ”˜œœšœ˜›œœŸŸ¢¢¤¤§§ª§§¨¨¨¨¨§¤§§¨¥¦¥¨¥¥¦¨©««¬«¨¬¬°¯®¬³¯°°±²±±²³µ±²±±°¨¬«®«¨¨¨§¥¥¦§§¤¢¦¤¥¥§§§¨§§§¢£¢¡ ››œ¢¤§¦¦¦§¦¦¦¤¢¢ž¡¡¦¦ª£¥¥¨«¬§¨§¦¢£¡ ŸžŸ Ÿ£¤¤£¤›š˜™˜™˜™™›”––˜˜•”““ŽŽŽŠ‘‹‹ˆ‰‰‹Š‹‰ŒŠˆ‹‹‹‹Š‰‘‹ˆ‡††„‡‡‡‰Š‹ˆƒ†„……ˆˆ‰‰ˆ‡‡‡Œ†‡ƒ„…†‡‡‡……„……„ƒƒ„‡†ˆ†…‚‚‚„ƒ…‡Š‹Š‰ˆ†……z|…Š“—••”™—•’‘‘“’‘’’”‘ŽŽŽŒ‰ŒŽŽŒŒ‰‹Œ‰„~‚‡ŠŠ‰‰‡…‰Œ’’Œ‹‰Š‹Š‹Œ‹‹‰ŠŠ‡‡†…‚†‹‹ˆ‡„ˆˆŠŽ‹Œ‘І‡†ˆˆŠ‰Šˆ‹‹‹‹ŠŠ…………†‰…„‚‚‚‚…„Љ‡†Š‹Œ‰ˆˆ†ƒ‚ƒ„‰‰‹Œ’”Ÿ ›—––˜šŸŸŸ¢¥©ª©©©¥¥¤¡žžœ››œ˜––•’““•—•”’‘‘‹…z{~xonnooquvvqid`ciiijjigˆ‰‡ŒŠŠŠ‹Œ’ŽŽŒŽ‘–Ž‘’”™–›™ž›œž›¤Ÿ ¥¤¤ ¢¢£¤¤¢£ Ÿž ¢¥Ÿ¨£¢¢¢¢£¦¦¢££¤ ¢ ¥¥¥¤ª¡¢¡¡ š™Œ‡vk]PJFJNPQSSSTUUSQRUam™œ©¦ Ÿ„sk`fkox…Ž˜™œœ£Ÿ¡œœœœ ž£Ÿ¢¢§¦®§¦§¨¨¨©¬««§©¤¥¤§¦¨¨®«««¬¬¬«²¯µ¯¯´¯°°³±µ±²³»°´°°®¬¬¬®«ªªª§§§¨§¨¨¤§¤ª¨«§ª¨°§¨¢ª¢£Ÿž¡£¬¨«©«§ªª«¤¢£¤££¤§¦«¦¦¥¨¨«¦©¨§¢¤ žœ¡ ©¤¤£«¬¬¢£š›—ž™™™œ™š›œ˜œ—•–—‘“’—ŽŽŽ‹ŽŽŽŒ‘Œ‹Š‹ŒŠŠ‹‘ŒŠ‡‡‡††‰‡ŠˆŽ‰†ƒ‹‡ˆ†ŒŒˆˆ‡‹…†‚ƒ„Š‰ŠŠŠ††‡‡‡‹…„‚ƒ…Іˆ‡‡„††‡ƒ……†ˆ‹‰Š„‚~zx|ƒŽ•”˜——•˜˜›”•’–’‘‘˜’•ŽŽ‘‘“““‹‰ƒƒŠˆŠ‡……†‰‹’šŒŽŽ‹Ž‹‹‰ˆˆˆ………‡‰Šˆ‡‡‹ŠŒ”Œ‘Œ‰‡ˆ‡‹ˆ‹Š‘“ŽŒ‘‘‘‹‰ˆ‡†…†Š……ƒ„„„„‰‡‹‹‹‹ŠŠŒ‰‰†‡†ˆ‚‚ƒ‰‰Œ“•žŸª¢¦¤£¤¤Ÿžžžž¡¤ªª««¯¨©¨¨¡¡žœœšŸ˜š”•”“”““——˜˜™‘‹‰†…~~ttttqsx„unii`bflhhimgˆ‡‡†ˆ‰‹Š‹‹ŽŽŒ‹ŠŒŒŒŽŽ‘’“”—˜™šœœœœœ›žŸŸ£¡¢ ¡¡¡¡¢Ÿžž ¡Ÿ¢¤£¢¢¢£§£¡ ŸŸ¡¡£¢£££ Ÿœ™˜”ƒxj]UIGEJRRSQPRSSSRRVZiwˆš £¤˜|ifamx‚ŒŽ’–š›››žžžœ›šœžŸŸž¢¢¥¦¬ª¦§¨¨¨¨§ª«§£¤¡¥¥¦¦§¨«ª«©©§¬ª«««¬®®³±±±³³²®¬«¬¬¬¬¬¬®ª©¨©¥¥¤¤¤¤£¢¢¥¤§§«§©©©¨¨¢¢žžœŸ ¤¥¦¦¦§¨¥¥¥¥¡¢ž Ÿ¤§¨§«¥¥¥¦¥¥¢¢¢£¡¡ž ©¤¥¥ª£¢ ˜™˜œ™˜—™™˜——˜–”“‘—ŽŽ’’’‹‹‹‘ŽŒŒŒŒŒ‹‹ˆˆˆ‰‹‹‹ˆ…ƒ††ˆ‡‰ˆ‡…„ƒ††‡‡Œ‹ŒŒˆˆ‡ˆ†…ƒƒ…ˆˆŠ†„ƒ„…‡†…‚‚ƒ…‡„ƒƒƒƒ„~€„„„„„„„|wxx‡Ž‘””’˜”–˜˜•”‘’’““’‘‘ŽŽŽ‹ŒŽŽŽŽŽ‹‰‡€„†‰ˆ††„†ˆŠ‹’“Œ‡ŠŒ‹ŠŽŒ‹‡‡…„„ˆ‹Š‰‰ˆˆˆ‹‹‹ŒŒŒŒ‹‰‰ˆˆ†††‹ŠŒŒŠŒŒˆˆˆˆˆ‡…‡…„ƒ„‚…†‡††‡ˆ…‡ˆ‰‡…………†‚ƒ†‡‹Œ“–ŸŸ£¢¥££ ¡ Ÿ¡£¦¥¥¥¦§§§§¢¢ Ÿ›šš ˜˜••–•“”““““‘ŽŠ‰‡~~utsrruyzsmfiaddddfhhhЇ‡‡Š‹‹‹‘ŽŒŒ’ŒŽŽ’’“”““—˜˜˜œ›ŸŸŸœŸŸŸŸŸ¡¤£§¡Ÿ £¢¢¢¢¡¨¡¡¡¢ ¢£¤¢§£¤¢ ¡¡¡¡Ÿ£¢¥¥¥¤£ž ™˜ŽŒyo`RLEEFIQV[TQRVVWQQYap„’¨¨©£¦‘„teedožŸŸ ¡¢¡§› œ›››¡ ¡Ÿ£žœŸ¡¤ª¨¬®©¬«°©¨§ª§¦¤§¥«««¦¯¨¬ª°ª©¨«¬®ª±²³¯²±®³²²²³°¯«©ª®¯®°®¯°°¨ª¥¥¥¦¥¨¤££¤¥«ª¬¬«ª¢£Ÿ £¤¦§¨¨§«¨«¥§¡£¢¢¡¨ª¨«§«§¨¦§££¤ª¢¢¡¡£¥£ª§«ª«¢§žœš—¡™˜–›¡—”“”‘—Ž‘˜’””“‘‘‘“““‘ŽŒ‘‹Š‹Œ’‹Šˆ„‚‡†‹ˆ‰…„„„„‡‡ŒŒŒŒŒŒ‹Š‹Š‰‰‹…„€‚Œ†„ƒƒƒ††‰„†„ƒƒƒ€‚‰………ˆƒ„}ywy~‡Œ•‘“’˜”•–˜–—“•’›–—‘Ž’Ž”‹Œ”Šˆ††Œ‰‰‰…‰‰ˆ‰‘”މЖ“Œˆ‰…„†ŽŽŽ‰‰ˆŒŠŽŒŒŒ“ŒŠ‰ˆˆŠ…‡‡Œ‹‘ŒŽŽŒˆŠŠ‘ŒŒ†‹†„„…ƒŠ‡ˆˆˆ‡‹‰‰ŠŒ†……ˆ‡‡‡†‡ˆ†‹™™ ¦¢¦¥§¨©¥§¥¥¥¤¢¨¥¦¤©¨¨©©¥ª¢£ž››¡›œ›š••“““‘‘““”‰‰w|zyuxxxqliiiiffeeglh‹ˆˆˆŠ‹ŒŒŽŽ‹ŽŽ‹Ž’Œ‹ŠŒ‘‘’“””–•˜™œœŸ›œœœœŸŸ ¡¤¤£¢Ÿžžœž£¢£ £££££¡ Ÿ Ÿ ¡¢ š—”’†~n`VJGEHIMQUZXSRRQPQS^fx—¤¥¦ œŒ{qehl{Œ“Ÿ£¥§¨¦¤¤¡¡œœšŸŸŸ¡¡¡Ÿžž ¢¥¦¨¨¨¥¨¨«¬¬ª¨§§£¤£¦¦§§§¦§¨¨¦ª§©«¬¬©°±±®®¯®³±±±¯¯¦ªª¬¬¬¬«¥¥¢£¤£¢¢¡£¤¥¦¬¤¤¥¤£££¢Ÿž£¥§¨¦§¨¨¦¨§¨¦§¢¢¡¢£ªª©§§¦©¨©¦¦££¦¦¢¡¡£¤¥£¦¦¨¥¤ ž™˜——˜˜˜—˜˜š›™•’‘‘‘’“’’’‘‘“”’‘’ŒŒŽ‰ŒŒŽ‹ŠŒŒ‹‹Žˆ‰ƒ‚‚†„……ƒ~‚„……ŽŒŒŒŠŠ‰ŠŠŒ‹‰‹ˆ‡‡†„„‚‚~ƒ…†…„„„ƒ‚‚‚‚‚……ƒ‚‚ƒ€}xxx†‘‘‘’’”“””—–—““’–”‘ŒŒŽ‹ŠŠˆŠ‹ŒŒ‹ˆˆ‰‰‰‰‡‰†‡‡‰‹‘ދЉŒ’‘’Љ†„‚…‡‰†…„†…ˆŠŒ‹‰‹ŒŒ‹‰ˆˆ†‡†‡‡Œ‹ŒŽ‹Œˆˆ‡‹Š†‰ˆ†‚ƒƒ†…ˆ‰ˆ„‰ˆŠ„‹†…„„„…„‡Š‡‚Œ—™œœ¡¢£¢¥ž§¢¦£¤ ¢ ¡¡ ŸŸ¢££¢£££¡ž›ššœš™–˜˜—”‘‹xƒˆˆˆ…€~w{yxvyxqklljhhieacefg‹‹‹‹‹Ž’ŒŽŠ‘ŽŒŽ’‹‰‰‹“”™™˜——˜œ¢žŸ¡œŸ¢Ÿ£¢§£¥¢¡¡ Ÿ ›£¡¦¢¢¡¡ ¤¢¤¤¨££ ¡¡¦¦§§¨¢¥¤¤Ÿ šš•~seWNDEGIMQUX\YWSVONRWdr€•Ÿ¨«œ–†yqhlt˜›¤§¬¬±©«§ª¡£žœ££¥¥¥¦¥¥¥¦¨§ª¦¨©©§¬¨²¬¬«ª§§¥¤¥¦§ª©©§¬©«§¯©«¯¬©®±¶µµ±°³±´®®®°«²¯¯®°«ªª«¥¥¤ª££¡¥¤£¤¦§¬¤¦¤¤£©¡£ ¡¤««¬ª®¦ª©ªª¬©ª¦¦¥££¥§¬ªª§ª©®¨©¦§¥¤¥§¡¡¡©¥¦¥©¨®¤¢žœ˜žœœš›š›¡˜›™—’’•“˜—–––’“’’’™–—’“ŽŒ‘Œ‹ŒŒ‹ˆ‰ƒ‚ƒ‡††ƒ€€‡‡ŠŠ‘Œ‰Šˆ‘ŽŽ‹†‡„……„…†‡‡‚€€ˆ†Š…‡ƒƒƒ‚ƒ„„†„„ƒ‚ƒ~{{{ˆŽ—‘“’’–’™˜˜”œ˜—–š•˜“Ž”’‘Ž‘‹‹ŠŒ‹‹‘“’“ŠŒ‰Ž‡‡ˆŒŒ’ŽŒ‹‘”””‘Œ‹…„…‹Œ…†…ˆ†‰‹ŒŒŒŒŒŒŒŒˆˆˆŽŽŒŽŽŒ‹‹ˆŠŠˆ†ŒŒ†‰ˆˆ‡Š‰Ž‡‰ˆ‹‹‹…†„„„†‡ˆ‡†”œœ›¢¢£¤¥¦§¥¥¤¥¤¦¢ ¤ŸœŸ¡¦¢£¤¨¢¤Ÿ›Ÿž¡œžœ¥žž——Žrq|Šˆˆ„~|{{||€upillomslfdcchi‰‰Š‹’‹Š‰‹Š‹ŒŽŒŒŠŠŠ“•™—˜˜ššœœžžžŸ Ÿ¡¡¡Ÿžžžœ ›¡ ¡Ÿ££¤¡¢¡¡¡£¡£ž¡¡¥¢¡Ÿžž›™•ˆrf\OKEHJORUY[][YTQPMU[k{Œ £¦¤¥—‹€tspy‰•›¡¥ª¯¯¨§¤£ ŸžžŸ£¦©©¦¥¤§¨©¨¨¨¨¥¨¨©§¨§¦££¤¥¥¦¨¨¨¦£¥¥«©©§ªª¯¯««ª®®²²´°°±®©©©¬«¬¬¯°¯ª«¦¤£££¤¢¢¡£¢¤¦§§§¤¥¥¥¢¡ž¢Ÿ¦¬«©ª«¯§ª©ª©©¦¦¤¦¦¦¥¨ª«¦¦¦©©©¨§¦§©¥¥¤¢¢£¨¦¥£¨¦¦¢ ˜šœ›››š™—–”“’””””’‘ŽŽŽ’’“”š–”Š‹ŠŒŽ‹Œ‰‹‹Š‹ˆ‰ƒ„…ˆ†€|‚‡ˆ‰ŒŒŽŠŒ‰‰‰ŒŒŠŠ‰Š…ƒ„…„‚ƒ~€„†„‚ƒƒƒ€ƒ…††…ƒ„‚}zz{€…Œ‘‘’’‘‘”’”––•›˜˜˜™••‘‹”•“ŒŒŠ‹‹Œ‘’‘ŠŒ‰‰ˆˆ‰Šˆ‰Œ‹ŽŽŒŒ…†‡‹‹„……†„Š‹‹‹‹‡ŒŠ‹ŒŒ‡ˆ‡ˆ‰‰‰Œ‹ŒŒ†‰‰‰‰‹†ˆˆˆ†Ž‹‹‡ˆˆ‰‡‰Š‰†‰‡‹Š‹„ƒ‚‚‚‡‰‹…‡‡—œœ››¡¡£ ¡¡ ¥££¢££¡ž ŸžŸœž¤¡¢¢¡ Ÿœœ›œšŸžžž—˜{k‚Љ‰†‚z||}€~qpiikkknmkhfaab‰‰ŽŽŽŠˆ‰Œ‹Œ’‹‹Œ“”™™š˜š››œŸž¡§¤¤¢¡Ÿ¡ žž žžŸ Ÿ£¡¤¤ª¢¦¢¨¤¤¤£¤¦¢¤¢£ Ÿœœ‘~wh[TKGEIOSYZ[]_][SPOPYbsˆ“¦¢¬¥¦’†~usv~”œ¢¢¨©¯¯¯§¥£¡ŸŸŸ¥¢¨¨®ªª¨©ª¯³«©ªª«¬¬¬¦££§¤§¥©¨ª¦¥¤§§ª©«ªª±°µ«®¯®¯²²´µ¸°²¬¬¬¬±³°±²¯©¬¦¤¢¥¦¦¥¥¢¥¥¦¨ª©©©ª¨¦Ÿž £¦±«®¬¬«²¨ª©©©¥¦¨¬¨«§¬ª¯¤§§¯©ª§¦¥¥¤¥¥¥¥¦§¯§ª©¨¦§¡ Ÿžžž¢¡››™—’•”””œ——‘ŽŽ•–Ÿ”š•”ŒŒŽ‘š’‹ŽŽŽŒ‹‹‹Š‡Ž†‰€†€ˆŠ“‘Œ‹ŒŒŠŠŒ‰Š„‡†‡„„€}}‚„„ƒˆ‚‚‚…„‡‡‰†ˆ‚||{{€‡ˆ’‘“™‘–’˜••–™™š˜™šŸ™œ’“““’‘“™——’•ŒŽ‹›•™‘’Œˆˆ‘Ž’Ž•‘‘’ŒŽ‰ˆŠ’ŒŠƒ†‡ŒŽ‘Œ‹Œ‹Œ‹‹‹Œ‰Šˆ‰‹ŒŒ‹ŽŒŒŒ‡‡‡‰‰Œˆ‡‡Œ‰“ŒŽŽŽˆˆ‡Š‡ŒŠŒ…ƒ‚†„ˆˆŠˆŒ’˜¡œœ› ¡¤¡£££¢¥¤¦¦¦¦¦¡¡¡¢ž›¤¤¤¡©¤«£¢œ›œ¡ž¤žŸš™’‡†ˆŒ††|~~|ophfhjlomronc`_‰Š‹‹‹‹Žˆ‰Š‹‡‰‹ŒŒŽ‘”––™™™—››››œœžœ¤ ¡¢ŸŸœšš›Ÿœ›žŸŸ£ ¤¡¢ž Ÿ¢¤¤¤¤¤§ ¡¢¡Ÿ˜——‹sf]QNKJHLPTZZZ[]\ZTNQS`k|˜¢¡¢ {ry}†“˜œœœŸ ¢£§¨©¥¤¢¡ ££¥¦§§©ª«¯®¯©«©¨££¢¢¢§¤¦§ª¢¤¤§§©¥§©ª§¬¬¬°±²²²²²°«¬ª®±««ªª«ª§¨§§££¡¤¥¥¥¤¢¥¦©«ªª§¦£žž›Ÿ¡¨«««««ªª±¨ªªª§§¥¨«¬§¨§¬©¨¤§¨¨©¨¦¥£¤¤¤¢¥¥§¨°§§¦¥¢¡ ŸžœžžŸ › —–’’“‘””—•’ŠŽ“”••“’ދޑ“”‹‡‡ˆŠˆ‰‡Œˆ‰…†…އ„€ƒƒ„†‰‹ŽŽŽ‹Œ‹‹‹‹‰‰‹‡‰ˆ‡ƒ‚†‡†…‚~~|}~ƒ……„„‚‚‚†„„…„……‚}xxv~…ˆŠŽ‘‘’—•–—˜”–—————–“’‘’’“”•–˜ŒŒŒŒ“”“ŽŒŒ‡ŒŽŠ‘ŒŽŽŽ‘ŒŒŒ‰‡„ˆ‰ŒŒ‹ŒˆŠ‹Œ‹‰ƒ‹‰‰ˆ‹ŒŒŒŠ‹Œˆ‰ˆ‡‡ŠŠ‰†‰‰ŽŽŒ‰Ž‰ˆ„‡‡ˆ†ˆˆ‡„ƒ€…„‰‡‡ˆ––™›šœœ›œž™¡¢¢§§¨©¦£¢¡¡žŸŸšœ £¡¢£¢žœœ›žžž›š•ŠŒŽ‘Œˆˆ‚€‚}uljee`klmmqsqfb^‹‹ŠŽŠŒŒ‰‰Œ’‘‘‘”˜˜žšŸ™š™œœ ¡¢¢¢£ ¢žœš™šœŸ¡ž£¢£££¤¥¥¦¢Ÿ£¤¨¨¨¦¨¡¡ žžž””„yj]TKKJJMPWV[[\\\YURPTYgu†™ž«¤¨›™Œ‚~w{‡—›¤ ¡¡¢¨¨«¥§¢¡¡¥¢¨¨¦¥¥¨¯°·±µ°²¯µ´µ®µ°©©¤§¨©©¨¦©§«¥§¨¯¨¨¦¦§ª©®®®¯´°·±µ´´±°¯¬¬³±³¯³ª¬¨©¦«§¨¢¦¤¤¥©¥©¨©ª±°©¨¢ ¡¢¤§³²²ªªª««°«®¬¬©«ª¬¬¬©¬©§¦§¨¨¬¦¦¥¨§§¦¦¥«¨²§¨¤¤£¢ ¤¡ Ÿ¢œ›ž–—‘““—•˜™š•’’˜–š•’‘ŒŽ™˜˜‰††ˆˆŒ‡ŽŒŽŽ‰‰‡ˆ…„‰ŠŒ‹‘‘’ŽŽŒŒ‹ŒŒŒŠˆ‡‹‡Žˆ‡‚†„……„‚‚}}„…‰†ˆ…Š…†…‡†…„…{wx}†„Œ‘”’“”˜—™˜™•————–•‘’’”“›—˜™™’’ŒŽ’Ž‘‘’‘““–Ž’‹‹‘‘‘”““’’•ŽŒ‹‰‡†Œ‰ŽŽŠŒ‡Œ–Šˆ…ЉŒ’Ž’‰ŽŒŠŒˆˆŠŽŽŒŽ‘ŽŠŽ‰ˆ‡Œ‰‹†‡‡‡‡‡ƒ†„‰‰‡‹“•™šž¡Ÿœ ˜›Ÿ¤¡©¦£§ª¤¥£§¡¢¢¢¡ œ Ÿ£¢¦¤©¢£œŸ››™•••“™‘ŒŒ‘ˆ‡ƒ…xpjhedekklnvssjfe‹‹‹Œ‰‰‰ŒŒŠ‹’“—˜˜™˜——˜—›œ ™›š›œ¡žžžšššššœžžŸžŸ ¢¡£Ÿ žžžž¢Ÿ ¡£¢£££¡Ÿ›˜–†{nbVQIKLORUWVWYYXXUSRPZaqŽ¡ ž˜Š…Š‘˜›œœœ›š™Ÿ¢£¤¤¦¤£¡ ŸŸžžŸ§ª°²´¯°°±°³²´®¬¦¤¡§©©§§¥¥¥¥¥§¨©¦¨¦¦¦¨©¯¯¯¯°®¶±µ´±®®¬¬®®¯¯¯¬ª©¨¥¥¦¦¤£¢¤£¥¥¥¦¦¦©¬¬ª©£¢žž¢¤¨«¯±±ªª¦¨©«¨ª©©§ª©«¨¨¨«©¨¤§¨¨¦¦§¨§¦¤¨¦¦¥¥¦©¨©¤¢ ž ž¤¢¢¡¡ ¡šš™ž–•’“”••™™š”’“˜–˜’“ŒŽŽŒŽŽŒ‹ƒ}€ƒ‡‰‹ˆŒŒŽŒŠ‰‡‡‡…„ŠŒŒŒ‘ŒŒ‹Šˆ‡†‰‹Œ‰Œ‰ˆ‡ˆ‡Šˆˆ‚‚€…ƒ‚‚‚‚€ƒ„††„…„‡„……………ƒyzv|†…†ˆ‘’“‘••˜™™˜˜”•——“”•”‘’”•˜––“’’’ŽŽ‘ŽŒ‘’’”Ž‹‹‹ŽŒ‘‘“‰‹‡‡‡ŠŠ‘Љˆ‹‹ŽŠˆ…ˆˆŒŒŽŽ‹‰‹Š‹ˆˆ‰Š‹Ž‹Œ‹‹ŽˆŽ†ˆˆ‰‰‹†‡ƒƒ‚‚‚ƒ‚‚‚‡Œ“•™˜žžŸ›žœœšš—œž¡ –¢¤¤¤¢¢ ¢¢¡Ÿ ›››ŸŸ ¢¡¢ ž™››ž˜’”“”‘‘ˆ†„{pmgfdgjlmmpppmjhg \ No newline at end of file diff --git a/tutorial/tracking/model-based/edges/teabox.ppm b/tutorial/tracking/model-based/edges/teabox.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b20630a2d581c73b63e358e21c3892c4dbc9897f --- /dev/null +++ b/tutorial/tracking/model-based/edges/teabox.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +320 240 +255 +GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^NNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVVNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEENNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNEEE============EEEEEEEEEEEEGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^VVVNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEGGGGGGGGGEEEEEENNNNNNNNNNNNNNNNNNNNNNNNNNNEEE===333333333333333333333333333333333333===EEEGGGVVVVVVNNNEEEEEEGGGGGGNNNVVVNNNNNNVVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^NNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEENNNNNNNNNVVVVVVNNNVVVNNNGGG333===EEEGGGGGGEEE===333---""""""---===GGGVVVNNN333333===333EEEVVVNNNEEEVVV^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^VVVNNNNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVNNNNNNVVVVVVNNNNNNEEEEEEGGGNNNNNNNNNNNNNNNEEE333"""---EEENNNGGG333------333===VVVVVVGGGGGGVVVfff^^^fff^^^fffffffffffffff^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVVVVVVVNNNVVVVVVNNNGGGVVVVVVVVVVVVVVVVVVVVVGGG333""""""333EEE333"""---"""---===VVV^^^VVVGGGVVVffffff^^^fffffffffffffffffffffNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fffffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEVVVNNNVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNGGG===---"""""""""""""""---EEEVVVNNNGGGEEE^^^^^^ffffffffffffffffffffffff^^^NNNVVVNNNVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVNNNNNNGGGNNNVVVVVVNNNNNN===---""""""""""""---===EEE======NNN^^^ffffffffffffffffffffffffppp^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffffffffff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEVVVVVVVVVVVVVVVVVVNNNEEEEEEEEEGGGGGGEEE---""""""333---""""""333---""""""---333EEEGGGNNNfffffffffffffffffffffffffffpppffffffNNNNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEVVVVVV^^^VVV^^^VVVNNNEEE===333===333""""""---333---===333===---"""------""""""333EEEGGGVVV^^^fffffffffffffffffffffpppfffpppfffppp^^^NNNNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEE^^^VVVVVV^^^VVV^^^VVVEEE333------"""""""""""""""""""""---EEEEEE======GGGVVV^^^ffffffffffffpppfffpppfffpppffffffGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^^^^^^^VVVVVVVVVEEE333---"""""""""""""""333333---""""""333NNN^^^ffffffpppfffpppfffppppppppp^^^NNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^VVV^^^^^^VVVVVVGGG===---"""""""""---""""""333EEE333"""---VVVpppfffpppfffppppppfffpppffffffGGGNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEE^^^VVV^^^^^^^^^^^^VVVVVVGGG===""""""""""""EEEEEE---"""333EEEGGG---"""333^^^fffpppfffppppppppppppfffppp^^^GGGNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^^^^^^^VVVNNN===------======"""""""""---===VVVGGG------======"""GGGfffpppfffpppfffpppppppppppppppfffGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^fff^^^^^^VVVVVV==="""333GGGGGG===333333EEEVVV^^^GGG---333---^^^pppfffpppppppppfffpppppppppffffffGGGGGGVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffpppfffffffffppppppfffpppfffpppffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffff^^^fffffffffffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEGGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^NNN---"""======EEE======EEENNN^^^^^^===GGGfffppppppfffppppppppppppppppppppp^^^GGGGGGNNNVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffffffffpppfffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fffffffff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^VVVEEE333333======EEEEEEGGGVVV^^^^^^333333^^^pppppppppppppppppppppppppppppppppfffEEEGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^ffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffffffffffpppfffffffffpppffffffffffffpppfffpppfffppppppfffpppfffpppffffffffffffffffffffffffffffff^^^ffffffpppffffffpppffffffffffffffffffffffffpppffffffffffff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEGGGGGGGGGGGGGGG^^^fff^^^ffffffffffff^^^fff^^^fff^^^VVVGGGEEE======EEEEEEGGGVVVVVV^^^NNNEEE"""===---"""NNNppppppfffpppppppppppppppppppppppppppfffEEEGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^fffffffff^^^^^^ffffffffffff^^^fffffffffffffffpppfffpppfffpppfffffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffffffffffpppfffpppffffffffffffffffffffffffpppffffffpppfffffffffffffff^^^^^^ffffffffffff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGfffffffff^^^fff^^^fffffffffffffffffffff^^^VVVNNNGGGGGGNNNVVV^^^^^^fff^^^^^^GGG---333VVVNNNEEE===EEENNNpppfffppppppppppppppppppppppppppppppwwwfffEEEEEENNNNNNNNNVVVNNNVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffpppfffpppffffffpppffffffffffffpppfffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGG^^^fffffffffffffffffffff^^^fffffffffffffff^^^fff^^^^^^fff^^^fffffffffffffff^^^NNNGGGVVVffffff^^^^^^ffffffppppppppppppppppppppppppppppppwwwwwwwwwfffEEEEEEGGGNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^fffffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffff^^^ffffff^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffffppppppppppppppppppppppppppppppppppppppppppwwwpppwwwpppwwwfffEEEEEENNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEfffffffffVVV^^^ffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwppp======GGGNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppffffffpppfffffffffffffffppppppfffpppffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffGGGNNNffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppfffpppfffpppfffVVVVVVfffpppppppppwwwppppppwwwpppwwwwwwpppwwwwwwwwwpppfff======GGGNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffpppffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffppppppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffffffffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffppp^^^===VVVfff^^^^^^^^^^^^fffffffff^^^^^^^^^fff^^^ffffff^^^^^^fffffffffpppffffffffffffGGGGGGpppppppppwwwppppppppppppffffffwwwwwwwwwwwwwwwwwwfff======GGGGGGNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ffffff^^^fffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEffffff^^^===VVVVVVNNNVVVVVVNNNNNNfffVVVNNNVVVNNNGGGVVVfffNNNVVVVVVVVVNNN^^^fffVVVNNN^^^VVV===GGG^^^VVVppppppVVV^^^^^^^^^VVVpppwwwwwwwwwwwwwwwwwwfff======GGGNNNNNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffppppppfffpppfffpppfffppppppfffppppppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffffffffpppffffffffffffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEGGGEEEEEEEEEGGGGGGppppppVVV===NNNEEEVVVfff^^^GGGNNNVVVEEEVVVfff^^^EEEGGGNNNGGG^^^fffVVVEEE^^^NNNEEE^^^ffffffEEENNNVVVGGGfffVVVGGGfffpppfffpppwwwwwwwwwwwwwwwwwwwwwppp333===EEEGGGNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffffffffff^^^fffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppffffffffffffpppfffppppppfffffffffpppffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEGGGEEEpppfffVVV===GGG===fffpppfffGGG^^^NNNGGGfffpppfffEEEGGGEEENNNppppppVVVEEE^^^EEEGGGpppppppppEEEGGGVVVEEE^^^EEEVVVwwwwwwwwwwwwwwwwwwwwwfff333333EEEGGGNNNNNNNNNNNNVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fffffffff^^^ffffff^^^^^^fff^^^^^^fff^^^ffffff^^^fffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffppppppfffpppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppffffffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEGGGppppppNNNEEENNNGGGfffwwwNNNfffVVVVVVppppppfff===NNNEEENNNfffpppGGGGGG^^^===VVVppppppfffEEENNNNNNEEEVVVEEE^^^wwwwwwwwwwwwwwwwwwwwwppp333333GGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffpppfffpppfffpppfffppppppffffffpppfffpppfffffffffffffffpppffffffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGppppppNNNNNNfffVVVfffpppNNNwwwwww^^^pppfffVVV===VVVVVVNNNfffpppEEENNNfffGGGNNNfffppp^^^===NNNNNNGGG^^^GGG^^^pppwwwppp^^^wwwfff333333EEEGGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffpppfffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffppppppfffpppfffppppppfffpppffffffpppffffffpppfffffffffffffffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEpppppp^^^fffppppppppppppffffffwwwwwwffffffNNN===^^^fffffffff^^^NNN^^^ppp^^^^^^^^^^^^NNNEEE^^^VVVVVVpppVVVVVVffffffVVV^^^wwwwwwfff---333EEEEEEGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppppppppfffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGpppppppppppppppppppppwwwfffpppwwwwwwpppfff^^^VVVGGGpppwwwppppppwwwpppwwwwwwppppppfffffffffppppppppppppwwwppppppppppppfffwwwwww^^^------===EEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^fff^^^^^^ffffff^^^ffffff^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffppppppffffffppppppfffpppfffpppfffpppfffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGppppppwwwpppwwwwwwppppppppppppwwwwwwpppfffppp^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww^^^------======EEEEEEGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffffpppffffffpppfffpppfffpppfffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppfffpppffffffffffffppppppffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGpppppppppppppppppppppppppppwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww’jj¼@@äú��ú��ú��ú��†††^^^""""""333======EEEEEEEEEEEEGGGGGGGGGEEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppfffffffffpppfffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwú��ú��ú��ú��ú��ú��ú��††††††VVV"""---===============EEE===EEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffpppwww‘‘‘––––––†††www^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^ffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffffffffff^^^fff^^^fffpppfffpppfffpppfffpppffffffffffffpppfffpppffffffffffffpppfffffffffppppppffffffpppffffffpppfffpppffffffpppffffffpppfffpppffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffpppffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEGGGEEEEEEEEE^^^^^^fff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffpppfffppppppppppppppppppppppppppppppwwwã´88Œccú��ú��ú��ú��wwwwwwwwwwwwwwwVVVEEEGGGGGGNNNNNNNNNGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVVVVVVVVVV^^^^^^fffpppwww†††‘‘‘‘‘‘–––ªªª°°°¶¶¶¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇǰ°°‘‘‘ppp^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fffffffffpppfffpppfffffffffffffffpppffffffffffffpppffffffpppfffpppfffpppfffpppfffpppffffffppppppfffpppfffpppfffpppfffffffffpppfffpppfffffffffpppfffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppffffffffffffpppffffffpppppppppfffpppppppppú��ú��ú��ú��pppppppppfffppppppfffpppfffVVVVVVú��ú��^^^^^^fff^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGNNNNNNVVV^^^fffwww‘‘‘ ªªª°°°¶¶¶ÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇÇÇÇǰ°°†††ppp^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffpppffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffffffffpppfffpppffffffffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^VVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppppppppppppppú��ú��ú��ú��wwwppppppwwwpppppppppppppppffffffú��ú��pppfffpppfffppppppfffpppfffpppppppppfffpppfffffffffffffff^^^^^^VVV^^^VVV^^^^^^^^^fffppp††† ªªª°°°ÁÁÁÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ××××××ÎÎÎ×××ÎÎÎ××××××××××××ÎÎÎ×××ÎÎÎÎÎÎÎÎÎÎÎÎ××××××ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇǰ°°†††fff^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffffpppffffffpppfffpppfffppppppfffpppfffffffffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffpppfffpppffffffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffff^^^ffffffffffffpppfffffffffffffffpppfffppppppfffppppppppppppú��ú��ú��ú��pppwwwpppwwwwwwwwwpppwwwppppppfffú��ú��fffpppppppppppppppppppppppppppppppppwwwpppwwwwwwpppwwwwww†††––– ªªª°°°¶¶¶ÇÇÇÎÎÎ×××ßßß×××ßßß×××ßßß×××××××××××××××××××××ßßß××××××××××××××××××××××××××××××ÎÎÎ××××××ÎÎÎ×××ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÎÎÎÁÁÁªªª^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffffffffpppfffpppfffpppfffppppppfffpppfffpppfffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffffffff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffpppffffffppppppfffpppfffpppppppppú��ú��ú��ú��pppwwwwwwwwwwwwwwwwwwwwwpppppppppú��ú��pppppppppppppppppppppwwwpppwwwwww‘‘‘–––ªªª°°°ÁÁÁÁÁÁÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß××××××××××××××××××ÎÎÎÎÎÎÁÁÁ¶¶¶––– ÁÁÁÇÇÇÎÎÎ×××××××××××××××××××××ÎÎÎÇÇǶ¶¶ pppVVVNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^ffffffffffffffffffpppffffffffffffffffffpppffffffpppffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppfffffffffpppfffpppffffffpppfffpppffffffffffffffffffffffffpppffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffppppppfffppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwwwwwwwwwwwwwpppfffú��ú��pppppppppwwwwww†††‘‘‘ ªªª¶¶¶ÇÇÇÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß×××ßßßßßßßßßßßß×××××תªª†††fffVVVNNNfffppp ¶¶¶ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÁÁÁªªªwwwVVVGGGGGGNNNNNNGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffpppfffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffff^^^ffffff^^^fffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppppppppppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ÇÇÇ×××ßßß×××ÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎßßßßßßßßßßßßÎÎÎ×××ßßßßßßßßßßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß××××××ÇÇǶ¶¶–––pppVVVGGG===EEE^^^pppffffffppp†††°°°ÎÎÎ××××××ßßßÎÎÎÇÇÇÎÎÎÎÎÎÇÇǰ°°†††NNNEEEEEEGGGNNNGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffpppffffffffffffffffffpppffffff^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffff^^^fffffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppú��ú��ú��ú��wwwpppwwwpppwwwwwwú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ßßßßßßçççßßß¶¶¶†††–––www‘‘‘ÎÎÎßßßßßßÇÇÇ ÁÁÁ×××çççßßßßßßçççßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÎÎζ¶¶ fffNNNEEE===333=========------333===NNNwww‘‘‘wwwwww†††ªªªÎÎÎ×××ßßß×××ÎÎÎÇÇÇÇÇÇÁÁÁ°°°–––NNN======GGGVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffpppppppppú��ú��ú��ú��wwwpppwwwpppwwwpppwwwwwwpppwwwpppú��ú��ÇÇÇççççççççççççççççççßßßßßßçççÇÇÇppppppfff‘‘‘×××çççßßßÎÎΪªª–––ÎÎÎßßßßßßßßßçççßßßçççßßßßßßçççßßßçççßßßçççßßßßßßßßßÁÁÁ wwwNNN===333333---333333333333333333==================GGG^^^www†††††††††¶¶¶××××××ßßß××××××ÎÎÎÁÁÁ°°°–––NNN===333GGG^^^ppppppfffffffff^^^^^^^^^VVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEE^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffpppfffpppfffppppppú��ú��ú��ú��pppwwwpppwwwpppwwwpppwwwwwwppppppú��ú��VVVÁÁÁçççççççççßßßçççççççççßßßßßß‘‘‘VVV^^^ffffffÁÁÁßßßçççßßßÎÎΪªª†††ÇÇÇßßßßßßßßßççç×××ÎÎÎÇÇÇÎÎÎ××××××ßßßßßßßßß¶¶¶wwwGGG333---------------===GGGNNNNNNVVVNNN^^^pppppppppfff^^^^^^^^^VVVVVVppp‘‘‘‘‘‘†††ªªªÎÎÎßßßßßßßßß×××ÎÎΰ°°ªªª–––VVV===333===NNNpppwwwwwwwwwwwwwwwpppwwwpppfffpppffffff^^^^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffppppppppppppfffú��ú��ú��ú��pppwwwpppwwwwwwpppwwwwwwpppwwwpppú��ú��===NNN–––×××ççççççççççççççççççßßß°°°^^^wwwwww†††ÎÎÎßßßçççßßßßßßÎÎÎÎÎÎÁÁÁ×××çççßßß×××ÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎ×××××ב‘‘NNN===333333333===333333333===GGGfff††††††‘‘‘–––ªªª ‘‘‘wwwpppfff^^^fffVVVVVVfffpppppp†††°°°×××ßßßßßßßßßßßß×××°°°ªªª–––GGG333333===EEE^^^wwwwwwwwwwwwwwwpppppppppfffffffff^^^^^^VVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^fff^^^fffffffff^^^fffffffffffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��pppwwwwwwwwwpppwwwwwwwwwú��ú��===EEEGGGwwwÎÎÎïïïçççççççççççççççÇÇÇ‘‘‘^^^pppppp–––ÇÇÇççççççßßßçççßßßÎÎΪªª°°°ßßßçççßßßßßßçççßßßçççßßßÇÇÇfffVVVGGGEEEGGGVVV^^^^^^VVV^^^NNNGGGGGGNNN^^^fff^^^fffppppppppppppfffNNNGGGNNNfffpppfffppp‘‘‘°°°×××ßßßßßßßßßßßß×××ÎÎΰ°°†††GGGGGGVVVNNNGGGNNNfff††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVNNNVVVVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppffffffpppfffpppfffppppppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��wwwpppwwwwwwwwwwwwwwwwwwú��ú��===EEEGGGNNNfff ßßßçççççççççççççççßßßÎÎÎpppffffffNNNwwwÎÎÎßßßçççççççççßßßßßß×××ÇÇÇÎÎÎßßßçççßßßçççßßßßßßÇÇÇ‘‘‘ppp^^^VVVppp–––‘‘‘–––pppGGGNNNNNNVVVNNNVVVEEEVVVwww–––ªªª°°° www^^^GGGEEEGGG^^^^^^^^^pppppp¶¶¶××××××ÎÎÎÎÎÎÎÎÎÇÇǰ°°–––ffffffffffff^^^VVVGGGGGG^^^ppp†††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffffffffff^^^ffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffppppppfffpppfffppppppfffpppppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGGVVVNNNVVVÎÎÎçççççççççççççççßßß×××–––VVVVVVVVVpppÁÁÁßßßßßßçççççççççßßßÎÎΪªª¶¶¶ßßßßßßçççÎÎÎ××××××ÎÎΖ––‘‘‘††††††–––°°°ÁÁÁÁÁÁfffNNNGGGEEEGGG^^^^^^GGG†††¶¶¶ÇÇÇÎÎÎÎÎÎ ppp^^^===GGGppp^^^fff‘‘‘†††wwwwww‘‘‘¶¶¶×××ÁÁÁªªª––––––‘‘‘†††pppfffNNNGGGEEE333333333EEE^^^www††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppfffffffffffffff^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGG^^^NNNGGGVVVpppªªªßßßçççççççççççççççÇÇÇfffGGG^^^fffwwwÇÇÇççççççççççççççççççßßßÎÎΰ°°ÇÇÇßßßßßßçççççç××××××××××××ßßßßßß×××ßßß°°°†††wwwfffVVVNNNwww––– –––‘‘‘pppNNNEEE===EEENNNfffwww‘‘‘fffpppfff––– ‘‘‘pppwwwfffVVV^^^fffVVVGGG------------333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppwwwpppwwwwwwpppwwwwwwwwwwwwwwwwwwppp===EEENNN^^^^^^VVVVVVfff^^^†††ÎÎÎçççßßßçççççççççÎÎΖ––fff‘‘‘––– –––ÎÎÎçççßßßççççççççç×××ÎÎΪªªªªªÎÎÎççççççßßßçççßßßçççßßßçççÎÎΪªªªªª¶¶¶ÎÎΪªªwwwwwwpppVVVVVVVVVNNNGGG===EEE ªªª ªªª‘‘‘‘‘‘‘‘‘wwwpppppppppffffffpppppppppwwwffffffVVVNNNVVV^^^VVVGGGEEE===---------333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwpppppppppffffff^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppffffffVVVNNNVVVppp¶¶¶ßßßççççççççççççßßß¶¶¶–––fffVVVVVVfff°°°ßßßççççççççççççççç×××¶¶¶ªªª°°°ßßßççççççççççççççççççÎÎΖ––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppwwwwwwwww ÎÎÎçççßßßçççßßßßßß×××ÁÁÁªªªªªª¶¶¶ªªª ‘‘‘††††††wwwppp^^^VVV^^^fffVVVNNNGGG===------------333333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppfffffffffffffff^^^^^^VVVVVVVVVNNNNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppfffffffffNNNNNNNNNfff‘‘‘ÎÎÎççççççççççççççç××׆††^^^ppp ßßßçççççççççççççççççç×××¶¶¶ªªªÎÎÎçççßßßççççççßßß××××××¶¶¶°°°°°°ÇÇÇ××××××ßßßßßßßßßßßßßßßßßßçççççççççççççççççççççççççççßßßßßßßßßçççÎÎÎ×××××××××ÎÎÎ pppfff^^^ffffffffffff^^^VVVGGG===333333333=========333333333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwppppppffffff^^^NNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffff^^^fffffffffffffffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppEEEEEENNNfffpppffffffffffffVVVNNNNNN^^^www¶¶¶ßßßççççççççç××ב‘‘VVV===EEENNNwww¶¶¶°°°×××çççççççççççççççßßßÎÎζ¶¶ÎÎÎ×××çççççççççÎÎÎÇÇÇÎÎÎÁÁÁ ––– °°°ÁÁÁ×××ßßßçççççççççççççççççççççßßßççççççßßßççççççççççççççççççßßßçççßßßßßß–––wwwwwwwwwppppppffffffVVVNNNGGGGGGGGGGGGVVVVVV^^^VVV===---333333333===GGGfff†††‘‘‘†††‘‘‘†††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppppp^^^NNNGGGEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffpppffffffffffffffffffpppffffffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppEEEEEEGGGNNNffffffpppppppppffffffVVV^^^NNNfffªªªßßßççççççßßßÎÎΪªªGGG===NNNwww‘‘‘ªªªÇÇÇÇÇÇ×××ççççççççççççççççççßßßÁÁÁÇÇÇßßßçççßßßßßßßßß¶¶¶‘‘‘fffNNNGGGVVV†††ßßßççççççççççççççççççççççççççççççççççççççççççççççççßßßçççßßßÇÇÇ pppVVVVVVppppppfff^^^VVVVVVGGGEEE333333GGGffffffpppNNN333---===333EEEGGGNNNNNNfffppp†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwfffGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffpppfffppppppfffpppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppGGGGGGNNNNNNNNN^^^pppppppppwwwppp^^^VVVNNNNNN^^^‘‘‘ÎÎÎßßßççççççßßß¶¶¶¶¶¶ÇÇǰ°°°°°°°°°°°VVV–––ßßßçççççççççççççççÎÎÎ ªªª×××ççççççßßßÇÇǰ°°ªªª°°°¶¶¶°°°¶¶¶ßßßçççççççççççççççççççççççççççççççççßßßçççççççççççççççßßßßßß fff^^^^^^^^^fffppp^^^GGGEEEGGGGGG333---"""333333=========EEEppp^^^VVVfffppp^^^fff^^^fffppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwpppVVV======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwGGGGGGGGGNNNNNNNNNVVVfffpppppppppfffppp^^^VVVNNNVVVppp ×××çççïïïççççççßßßwww^^^–––¶¶¶ÁÁÁ¶¶¶ÁÁÁ×××ßßßßßßßßßççççççßßß¶¶¶ ßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßßßßÎÎÎÁÁÁªªª†††ppppppwwwwwwpppwwwppp^^^NNNGGG===---"""""""""""""""333NNN^^^‘‘‘ppp^^^VVVVVV^^^VVVNNN^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwfffEEE333333======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGGGGGGNNNNNNNNNNNNNNNVVVwwwppppppwwwwwwwww^^^VVVVVVNNN^^^wwwÇÇÇçççïïïçççççççççÎÎÎÎÎÎçççççççççÎÎΰ°°fff†††‘‘‘ÎÎÎßßßçççßßß×××ÎÎÎßßßßßßßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßÇÇÇ–––†††††††††wwwNNN===VVVppp^^^pppwwwwwwwwwfff^^^NNN===---""""""""""""---333EEENNNpppfffVVVEEE=========EEEEEENNNVVVffffffwww‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwGGG333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffpppfffpppfffppppppfffppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwGGGNNNNNNNNNNNNNNNNNNNNNVVV^^^wwwwwwwwwwwwwwwfffNNNVVVNNNfff–––×××ïïïïïïçççïïïçççççççççççç×××¶¶¶ ‘‘‘‘‘‘pppªªª×××ççççççççç×××¶¶¶ÁÁÁßßßççççççççççççïïïçççççççççççççççççççççççççççççççççßßßÁÁÁ ‘‘‘†††pppNNN333333^^^†††^^^pppwwwpppfffpppVVVEEE===333333333---"""------333===333333GGGEEE333333======EEEGGGNNN^^^fffppp†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††www^^^------333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwww††††††GGGNNNVVVNNNNNNNNNVVV^^^NNNVVV^^^pppwwwpppwwwpppVVVVVVVVVVVV^^^www¶¶¶ïïïïïïçççïïïçççïïïçççççççççßßß¶¶¶ ––– ¶¶¶ ppp°°°çççççç×××ÎÎÎßßßççççççïïïçççççççççïïïççççççççççççççççççççççççßßßÁÁÁpppfff†††††††††††††††––– ‘‘‘^^^www‘‘‘†††pppVVV†††pppppppppVVVNNNGGGNNNNNNEEE333""""""""""""""""""===NNNEEE=========333333======EEENNN^^^ppp‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††fff==="""---333=====================EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNNNNVVVVVVVVVppp^^^NNNNNNVVVVVVfffwwwpppVVVVVVNNNVVVVVV^^^fff‘‘‘×××ïïïïïïïïïçççïïïçççïïïçççççççççßßßÇÇÇ ¶¶¶çççççççççççççççççççççççççççïïïçççççççççççççççççççççßßß××××××ÇÇÇÎÎÎÎÎÎÎÎÎçççßßßÎÎÎÁÁÁÁÁÁ××××××ßßßÇÇÇ‘‘‘††††††wwwNNNpppwwwfffwwwwww^^^^^^VVVVVVGGGEEE======---------------GGG^^^^^^NNNEEE333333333333333===EEEGGGVVVfffppp†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††wwwEEE""""""333===333==================EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffpppfffppppppfffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNVVVNNNVVV^^^wwwfffVVVNNNNNNNNNVVVppppppwwwwwwVVVVVVVVVVVVVVVVVVVVV^^^wwwÁÁÁçççïïïïïïçççïïïçççïïïçççççççççÎÎÎÇÇÇ ßßßççççççççççççççççççççççççççççççßßßßßß×××ßßß×××ÎÎÎÎÎÎÇÇÇÁÁÁ°°°°°°ÎÎÎ××××××ßßßççççççßßßßßßççççççççççççßßßÇÇÇ‘‘‘fffVVV–––^^^ffffff^^^fffffffffVVVGGGGGGGGG===GGGGGGGGGGGG^^^†††^^^NNNNNNEEE333===333333======EEENNNVVVfff†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††VVV---"""---=================================EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=========EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffpppffffffpppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††VVVNNNVVVVVVVVVfffppppppfffVVVVVVfff^^^VVVVVVpppwwwVVVVVVVVVVVVVVVVVVVVV^^^VVVfff‘‘‘×××ïïïïïïïïïçççççççççççççççççççççßßß×××ßßßßßßßßß××××××ÎÎÎÇÇÇÁÁÁ¶¶¶°°°°°°¶¶¶ÎÎÎßßßßßßßßß×××ßßßÇÇǪªª––– –––––– ×××çççççççççÎÎÎÇÇÇßßßççççççççç×××¶¶¶‘‘‘––––––wwwpppfffpppfff^^^VVVVVVNNNVVVGGG^^^^^^VVVGGGGGGNNNfffwwwpppfffGGG===333333===333333======GGGVVVfffwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††ppp333"""---333===333===333========================EEE=========EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppfffpppfffpppfffpppppppppfffppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††VVVVVVVVVVVVVVVfffwwwpppwwwppp^^^ppppppNNNVVVVVVfffVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^¶¶¶çççïïïçççßßß×××ÎÎÎÇÇǶ¶¶°°°ªªª –––‘‘‘†††pppppppppfffpppwww‘‘‘°°°ÎÎÎßßßÎÎÎ †††††† ¶¶¶×××ßßßÎÎζ¶¶ªªªÇÇÇ×××ßßß¶¶¶‘‘‘–––ßßßçççççççççßßß www–––ÁÁÁÁÁÁ –––‘‘‘^^^ffffffwwwppp^^^NNNGGGEEE===GGG^^^^^^pppNNNEEE333===333333===333======NNNfffpppwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††wwwEEE""""""333333======333===333===========================EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEE===EEEEEE======EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppfffpppppppppppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††fff^^^VVVVVVVVV^^^pppppp^^^ppp^^^pppwwwfffwwwVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^fff‘‘‘ÇÇǪªª†††pppfffffffff^^^^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVVffffff–––¶¶¶ÎÎÎÎÎÎ ––– ªªªªªªÇÇÇÎÎζ¶¶°°°ÇÇÇßßßçççÁÁÁ°°°ÇÇÇççççççççççççÇÇÇfffppp–––¶¶¶ÎÎÎÎÎÎÎÎÎÁÁÁªªª––– –––†††ppppppVVVGGGEEENNN^^^www†††pppppp^^^NNNEEE===333===333333333===GGG^^^wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘††††††††††††VVV---"""---333===333===333===333===========================EEE===EEE===EEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEppppppwwwpppwwwppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘www^^^VVVVVVVVVVVVfffwwwppp^^^wwwppp†††VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^pppwwwfff^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^ppp ¶¶¶××××××¶¶¶ –––ÁÁÁßßßßßß××××××ßßßççççççßßßçççççç×××ÎÎÎ×××ÎÎÎwww†††–––ÁÁÁ×××ççççççßßß×××°°°ªªªªªªªªªªªª pppNNNEEE===VVV‘‘‘ªªªªªª‘‘‘‘‘‘††††††ffffffGGG===333=========EEEVVVwww†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††fff333""""""===333===333===333===333=================================EEEEEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEE======EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††––– †††VVVNNNVVVVVVVVV^^^www^^^www†††VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^^^^^^^fff¶¶¶¶¶¶www^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^^^^pppwww‘‘‘ °°°°°°¶¶¶×××ßßßççççççççççççççççççççççççççççççÇÇÇÁÁÁÎÎΰ°°–––‘‘‘ÁÁÁßßßçççççççççÎÎΆ††www‘‘‘–––‘‘‘wwwppp^^^^^^VVVGGGNNNVVVppp‘‘‘ªªªªªª‘‘‘pppffffffNNN===333333333333---EEE^^^www‘‘‘°°°ÁÁÁ¶¶¶¶¶¶–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††EEE""""""333333333===333333===333===333=================================EEE===EEE===============EEE=========EEEEEE===EEE======EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwpppwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘ ¶¶¶ÁÁÁ–––wwwNNNNNNVVVVVVVVV^^^^^^ppppppfff^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^fff†††°°°‘‘‘fff^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffppp–––ªªªÁÁÁÎÎÎßßßßßß××××××çççççççççççççççßßß×××ÎÎÎ×××××××××ççççççççççççÇÇdž††††† ÁÁÁÁÁÁ†††VVVGGGVVVfffVVVNNNVVVGGGVVVfffpppfffNNNEEE======333===333333333---333EEENNNppp––– ªªª‘‘‘‘‘‘ªªªªªª–––‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††VVV---"""---333===333===333333===333===333===333==============================EEE======EEE============EEE======EEE=========EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††† –––‘‘‘†††–––fffNNNGGGVVVVVVVVV^^^wwwwww†††wwwfff^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^ppp‘‘‘ªªª^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNVVVNNNVVVVVV^^^^^^^^^^^^^^^ppp†††°°°ÇÇÇÎÎΰ°° ÁÁÁÎÎÎ×××ÇÇÇÎÎÎßßßççççççßßßççççççßßßßßßßßßççççççßßß×××ÎÎÎ×××ßßßÎÎΪªªppp–––ÇÇǶ¶¶–––pppVVVVVVVVVNNNGGGEEE======333===333333===333333=========GGGfffwwwwww °°°ÁÁÁ°°° –––‘‘‘‘‘‘––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††ppp333""""""333333===333===333333===333===333===333============================================================EEE======EEE=========EEE===EEEEEE=========EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘ †††^^^GGGGGGNNNVVV^^^pppwww^^^wwwfffVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fffwww –––pppVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^ffffff^^^†††‘‘‘www^^^fffwww–––ªªª––– ªªª¶¶¶ÇÇǶ¶¶°°°ªªª°°°¶¶¶ÇÇÇÎÎÎßßß×××ÎÎÎÁÁÁÇÇÇßßßççççççßßß×××ÎÎÎßßß××××××ßßßçççßßß×××ÇÇǪªª †††pppVVVGGGEEE===333333===333333======333=========GGGfff†††ªªª¶¶¶°°°ÇÇǪªª–––‘‘‘‘‘‘––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwEEE""""""333===333333333===333===333===333333===333================================================EEE=========EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ªªªªªªwwwppp–––°°°pppVVVNNNGGGNNNVVVVVV^^^www†††‘‘‘www‘‘‘wwwfff^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^fffppp‘‘‘ªªªwww^^^VVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^fff‘‘‘www‘‘‘†††fff†††ppp^^^^^^fffwww‘‘‘ ªªªÁÁÁ×××ßßß×××ÇÇÇÇÇÇÎÎÎÇÇÇÁÁÁªªª°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇÇÎÎÎßßßßßß×××ççççççßßßçççÇÇǰ°°ªªªªªª°°°°°°ªªª †††wwwVVVGGG===333===333333===333333===EEEGGGfff†††††††††‘‘‘¶¶¶ÇÇÇÁÁÁ¶¶¶°°°–––‘‘‘–––––– –––––––––––––––‘‘‘–––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††VVV---"""---333333===333333===333===333333===333===333===============================================================EEE===EEE=========EEE===EEE======EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ ªªªffffff ¶¶¶–––fffVVVNNNGGGNNNVVV^^^fffppp†††www^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ppp fff^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^fffpppwwwwwwwwwppp††††††fff^^^^^^ppp‘‘‘ªªªÁÁÁ×××ççççççßßßççççççßßßçççßßß×××ÎÎζ¶¶ ªªªªªª––– ÎÎÎççççççßßßççç×××ÁÁÁªªª––– ªªª ––– °°°ªªª‘‘‘www^^^GGGEEE333333===333======GGG^^^‘‘‘††††††‘‘‘ ÁÁÁ¶¶¶ÎÎζ¶¶ªªª ¶¶¶°°° ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppp333""""""333===333===333333333333===333333===333333333===333===========================================================================EEE============EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘––––––ffffff¶¶¶¶¶¶ÁÁÁ†††pppVVVNNNNNNGGGVVVVVVfff†††www†††fff‘‘‘††††††fff^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff‘‘‘ªªªfff^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^fff‘‘‘‘‘‘‘‘‘ppp^^^fffVVVVVVVVVfff–––ÁÁÁÎÎÎ×××ßßßçççÎÎΪªªÁÁÁÎÎÎ××××××ÁÁÁ –––‘‘‘ ßßßççççççßßß×××ÁÁÁªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘––– ªªª †††ppp^^^GGGEEE=========EEEGGGfff†††‘‘‘††††††‘‘‘¶¶¶°°°¶¶¶ÎÎΪªª ÇÇÇÇÇǶ¶¶ ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘EEE""""""333333333333===333===333===333333===333===333333======333==================================================================EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘–––ppp^^^fff¶¶¶ÇÇÇ×××°°°‘‘‘ppp^^^NNNNNNGGGNNN^^^^^^†††www†††fff‘‘‘‘‘‘^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fffVVV^^^^^^fffwww ªªª^^^^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^^^^pppwwwwww††††††VVV^^^NNNNNNVVV^^^www–––ÁÁÁßßßßßß×××ÁÁÁÎÎÎßßßÎÎÎÁÁÁÁÁÁªªª–––¶¶¶×××çççççççççÎÎÎÁÁÁ°°°ªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘†††www^^^NNNEEE===EEENNNfff†††‘‘‘††††††°°°ÁÁÁÁÁÁ°°°ªªª¶¶¶×××ÎÎζ¶¶ ––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††VVV---"""---333333===333333333===333333333333333333333===333======333======333=============================================EEE==================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘wwwffffff‘‘‘ÁÁÁÇÇÇ×××¶¶¶–––ppp^^^NNNNNNGGGNNN^^^^^^pppwwwppp‘‘‘‘‘‘www†††fffffffff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fffffffffppp‘‘‘ÁÁÁfff^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVV^^^fff‘‘‘‘‘‘wwwpppwwwpppfffVVVNNNNNNVVVfffªªªÎÎÎßßßÁÁÁÇÇÇÇÇÇÎÎÎÁÁÁªªªªªª¶¶¶ççççççïïïççç×××ÁÁÁ°°°ªªª –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwwwwwwwwwwww‘‘‘ªªª¶¶¶°°°www===333===EEENNNfff†††‘‘‘††††††–––¶¶¶ÁÁÁ¶¶¶ªªª ÎÎÎßßß×××ÁÁÁªªª–––––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ppp333""""""333333333===333===333333333333===333333333333===333333======333=======================================EEE===EEE===EEE==================EEE===EEEEEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††wwwwww††††††‘‘‘‘‘‘¶¶¶–––ÁÁÁÎÎÎÁÁÁ–––www^^^VVVNNNGGGNNNVVV^^^ffffff‘‘‘–––†††‘‘‘^^^fff^^^fff^^^fff^^^fff^^^fffVVV^^^^^^fffffffffpppwww ‘‘‘pppfffVVV^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^fffpppwww††††††††††††††††††ppp^^^^^^NNNNNNNNN^^^ppp–––¶¶¶ÇÇÇÎÎÎÁÁÁ–––ªªª‘‘‘¶¶¶çççççççççççç×××ÁÁÁªªª –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††wwwwwwpppppppppppp–––°°°¶¶¶ VVV333333333333===EEENNNppp†††‘‘‘††††††‘‘‘°°° ªªª°°°¶¶¶ßßßççç×××ÁÁÁªªª––– ––– ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘EEE""""""---333===333===333333333===333333333333===333333======333======333=============================================EEE===EEE======EEE===EEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††wwwwwwffffff^^^^^^^^^^^^www°°°¶¶¶ ªªª‘‘‘ °°°ÎÎζ¶¶–––www^^^VVVVVVNNNNNNVVV^^^fffwww†††‘‘‘†††‘‘‘‘‘‘www^^^^^^fff^^^ffffffffffffVVV^^^fffffffffffffffpppwwwªªªÁÁÁ‘‘‘ppp^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^^^^pppwwwpppwww‘‘‘††††††www^^^NNNNNNVVVfffªªªÇÇÇ×××××××××ÎÎÎççççççççççççßßßÇÇǶ¶¶°°°ªªª––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††wwwwwwwwwpppwww–––ªªª°°°‘‘‘NNN333333333333===333======EEEVVVppp†††–––††††††‘‘‘°°°ÁÁÁ¶¶¶ÁÁÁÇÇÇßßßççç×××ÁÁÁªªª––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘VVV------333333333333===333333333333333===333333===333===333===333===333===333==========================================EEE======EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEwww††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwpppffffff^^^^^^^^^^^^VVV^^^VVV^^^^^^ppp^^^‘‘‘ÁÁÁ ––– °°°¶¶¶¶¶¶ÎÎÎÁÁÁ–––fff^^^VVVNNNNNNNNN^^^fffpppwww–––†††–––†††‘‘‘††††††fff^^^ffffff^^^fffffffff^^^^^^ffffffffffffffffffwwwÁÁÁïïïçççÇÇdž††^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^–––www†††–––‘‘‘^^^^^^VVVNNNNNN^^^ppp†††°°°ÇÇÇ×××çççççççççççççççßßßßßß×××ÎÎÎÁÁÁªªª ––––––‘‘‘‘‘‘‘‘‘††††††††††††wwwwwwwww–––ªªª°°°‘‘‘VVV333---333333===333======333======GGGVVVwww†††‘‘‘††††††‘‘‘–––ÁÁÁÁÁÁ¶¶¶ÁÁÁßßßßßß×××¶¶¶ªªª––– ––– ––– ––– –––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘––––––‘‘‘ppp333""""""333333333===333333333333333===333===333333===333===333333===333=========333=======================================EEE======EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVVVVVVVV^^^^^^VVVVVVVVV^^^VVV^^^^^^pppNNN^^^––– ‘‘‘–––°°°××× °°°×××ÇÇÇ pppfff^^^VVVNNNNNN^^^^^^ppp††††††–––††††††††††††ffffff^^^fffffffffffffff^^^^^^ffffffffffffpppfff†††ßßßïïïýýýýýýßßß www^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^^^^wwwwww††††††††††††pppVVVVVVNNNNNNVVVppp††† ¶¶¶×××ßßßßßßçççßßßßßßßßßßßßßßß×××ÎÎÎÎÎÎÁÁÁ°°° –––‘‘‘‘‘‘††††††–––°°°°°°–––VVVEEE===EEE============EEEEEEGGGGGG======EEEGGG^^^www‘‘‘†††††† °°°ÁÁÁ¶¶¶°°°°°°ÎÎÎßßßÎÎζ¶¶ªªª–––––– –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘†††EEE""""""---333333333333333333333333333===333333===333===333===333333===333333======333=======================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVVNNNVVV^^^VVV^^^VVV^^^VVVVVV^^^NNNVVVfffVVVGGGfff ††† ªªªÎÎΰ°°ÁÁÁ¶¶¶×××ÎÎΪªª–––†††wwwfffVVVNNNNNN^^^^^^fffwww––– †††††††††††††††ppp^^^ffffffffffff^^^ffffffpppfffffffffppp ïïïýýýýýýýýýýýýïïïÎÎΆ††fff^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^ppp†††–––†††wwwwwwwwwVVVVVVGGGNNNVVV^^^www‘‘‘°°°ÎÎÎßßßßßß×××ßßßßßßßßßçççßßßßßßßßßßßßÎÎÎÁÁÁ¶¶¶ –––‘‘‘†††www†††‘‘‘°°°¶¶¶ªªªfffNNNNNN^^^^^^VVVGGGGGGGGGNNNVVV^^^VVVGGGEEE=========GGG^^^www†††‘‘‘††††††–––ÁÁÁ°°°ªªª°°°¶¶¶¶¶¶××××××ÁÁÁªªª––– ––– ––– ––– ––– ––––––––– ––– ––– –––––––––––––––––––––‘‘‘VVV---"""---333333333333333333333333333333333333333===333===333===333333======333333======333====================================EEE======EEE===EEEEEEEEEEEE===EEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNfffVVVGGG†††ppp‘‘‘¶¶¶¶¶¶°°°ÇÇÇßßß¶¶¶°°°ÎÎÎßßßÁÁÁ°°°–––†††ppp^^^NNNNNNVVVffffff†††††††††‘‘‘‘‘‘†††fffffffffffffff^^^^^^fffffffffppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^www†††–––††††††wwwffffffVVVNNNGGGGGGVVV^^^www‘‘‘¶¶¶××××××¶¶¶ÎÎÎ××××××ßßßççççççßßßçççßßßßßß×××ÇÇÇÁÁÁªªª–––†††††††††ªªª¶¶¶ÁÁÁªªª^^^^^^fffpppppppppppp^^^^^^www‘‘‘†††wwwfffNNNEEE============GGG^^^†††–––††††††°°°¶¶¶ –––°°°ÁÁÁ°°°ÁÁÁ×××ÎÎÎ ––– ––– ––– ––––––––– –––––– ––– ––– –––––– ––––––––––––ppp333""""""333333333333333333333333333333333===333333===333===333333===333333===333===333===333=============================================EEE===EEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††wwwffffff^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVNNNNNNGGGVVVNNNVVVVVVfffNNNNNNfff^^^ °°° ÎÎÎ×××ÁÁÁªªªªªªÎÎÎçççßßßÎÎΰ°°†††ppp^^^^^^VVVVVV^^^fffppp†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘pppffffff^^^fffppppppfffpppfff×××ýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^fff‘‘‘†††††††††‘‘‘†††www^^^VVVVVVNNNGGGGGGVVVfff ÁÁÁÇÇÇÎÎÎ×××ßßßßßß×××ßßßßßßßßßßßßßßßßßßçççßßß×××ÎÎÎÇÇǰ°° –––fff^^^^^^VVVNNNNNNNNNNNN^^^fffppp‘‘‘ªªª †††ppp^^^VVVGGG============EEENNNfffwww‘‘‘‘‘‘††††††††††††ªªªÁÁÁ¶¶¶°°° ªªª ––– ––– ––– ––– –––––– ––– ––– –––––– –––––––––––––––†††EEE""""""---333333333333333333333333333333333333===333===333333===333333===333=========333======333=================================EEE=========EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVNNNGGGGGGGGGGGGNNNNNNVVVVVVVVVfffNNNGGGppppppVVV^^^‘‘‘ ÁÁÁ¶¶¶ÇÇÇÁÁÁ°°° çççïïïßßß×××¶¶¶†††wwwppp^^^^^^VVV^^^fffppp†††‘‘‘†††‘‘‘†††ffffff^^^ffffffpppfffpppppp–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^ppp††††††pppfffffffffppppppVVVNNNGGGGGGNNN^^^ppp‘‘‘°°°××××××ÁÁÁ×××ßßßßßßÎÎÎ×××ççç×××××××××ßßßßßß×××¶¶¶–––pppfffVVVNNNVVV^^^fffVVVGGGEEEGGGGGGNNNVVVfff‘‘‘ffffffpppVVVGGGGGGEEE===333===EEENNN^^^www†††‘‘‘††††††°°°¶¶¶¶¶¶ÇÇǶ¶¶ ‘‘‘––– ––– ––– ––– ––– ––– ––– ––– ––– –––‘‘‘VVV------333333333333333333333333333333333===333333333===333333333333333333333===333======333=============================================EEE===EEE======EEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††pppppp^^^VVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVVVVVVVVNNNNNNGGGGGGNNNNNNNNNNNNVVVVVVVVVVVVNNNfffNNNEEEwww‘‘‘^^^fff–––¶¶¶ÇÇÇÎÎζ¶¶¶¶¶ªªªççççççÎÎÎ×××ßßßÁÁÁ–––‘‘‘ppp^^^VVV^^^ffffffwww†††‘‘‘ppp††††††pppfffffffffpppfffppppppwww¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fffppp‘‘‘www‘‘‘††††††fff^^^^^^NNNGGGGGGVVV^^^www–––°°°ÁÁÁ×××ßßßßßß×××ßßßçççßßßÎÎζ¶¶ªªª–––pppffffff^^^VVVVVVNNNNNNNNN^^^^^^VVVGGGGGGEEEEEEGGGNNNNNN^^^pppwwwwwwwwwppp^^^VVVGGGEEE======333===EEEGGG^^^ppp‘‘‘†††††† ¶¶¶ÁÁÁ¶¶¶ªªª –––––– ––– ––– ––– ––– ––– ––– –––––– ––– –––ppp---""""""---333333333333333333333333333333333333===333333===333===333333===333333===333333===================================================EEE======EEE======EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^^^^VVVVVVVVVNNNNNNNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV^^^GGG333VVV†††††††††ffffffªªª×××ßßß°°°°°°ªªªçççççç×××çççÎÎÎßßßÇÇǶ¶¶°°°–––wwwfffVVVVVVffffffppp †††–––ppppppfff^^^fffppppppfffpppÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwwwfff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^pppwww†††‘‘‘‘‘‘fffVVVNNNGGGNNNVVVfffwww‘‘‘°°°ÎÎÎßßßçççççççççÎÎΪªªwwwffffff^^^fffffffff^^^^^^^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNGGGGGGEEEEEEGGGNNNVVVfffwwwwwwwww^^^NNNGGG======333======EEEGGGNNNVVV^^^www††††††‘‘‘‘‘‘††††††–––ªªª¶¶¶ÁÁÁ ––– ––– ––– ––– ––– ––– †††EEE""""""---333333333333333333333333333333333333333333===333333333333333333333===333333======333333=============================================EEE======EEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNN^^^GGG333GGGfff––– ‘‘‘ffffff ßßß¶¶¶ªªªªªªÇÇÇçççççççççÎÎÎ×××ïïïïïïßßßÇÇǪªªpppVVVVVV^^^pppppp‘‘‘††††††‘‘‘fffffffffpppfffppppppppp†††çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××–––fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff–––‘‘‘†††††††††‘‘‘‘‘‘^^^VVVVVVGGGGGGNNNVVV^^^www†††ªªªÇÇÇßßßßßß fff^^^^^^VVVNNNVVVVVV^^^fff^^^^^^fff^^^^^^VVVVVVVVVVVV^^^VVV^^^VVVNNNEEEEEEGGGNNNNNNVVV^^^ppppppfffVVVGGG===EEEEEEGGG^^^fffppp^^^NNNVVV^^^ppp†††www‘‘‘°°°ÎÎÎÎÎΰ°°‘‘‘––––––––– ––– ––– ––– ––– ––––––‘‘‘VVV"""""""""333333333333333333333333333333333333333===333333===333===333===333333333===333333================================================EEE===EEE======EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††wwwpppfff^^^VVVVVVNNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^NNN333333GGG –––‘‘‘VVV^^^°°°¶¶¶ÇÇÇ ×××ßßß××××××ïïïïïïïïïïïïïïï×××°°°†††ppp^^^VVV^^^ffffffwww‘‘‘–––†††pppfffffffffpppppppppppp ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff^^^^^^^^^^^^^^^^^^^^^^^^fffpppfff^^^^^^^^^fff†††wwwwww††††††fffVVVNNNGGGGGGNNNVVV^^^ppp†††ªªªÇÇÇ ppp^^^^^^VVVVVVNNNNNNVVV^^^^^^^^^^^^VVVNNNVVVVVV^^^ffffffNNNGGGVVVVVVNNNNNNNNNGGGEEEGGGNNNNNNVVV^^^^^^fff^^^NNNGGGVVVwwwwwwwwwffffffNNNNNNVVVpppwwwpppwwwwww °°°°°°ÁÁÁÁÁÁªªª–––‘‘‘–––––– ––– –––––– ––– ––––––ppp---""""""---333333333333333333333333333333333333333333===333333333333333===333===333333===333===333=============================================EEE===EEE===EEEEEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwppp^^^VVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV333---333NNN‘‘‘ªªª^^^NNNNNNfff ÎÎÎ ÇÇÇÇÇǶ¶¶çççïïïïïïýýýïïïïïïïïïßßß°°°†††pppfffffffffpppfffwww–––pppffffffppppppppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××× ppp^^^^^^^^^^^^fffwww††††††‘‘‘ffffff^^^^^^fffwww†††––– –––‘‘‘pppfff^^^VVVGGGGGGGGGNNNVVV^^^ppp‘‘‘ªªª‘‘‘ppp^^^^^^^^^VVVNNNGGGNNNVVVfff^^^^^^NNNGGGVVV^^^^^^^^^NNNGGGNNNNNNNNNVVVNNNNNNVVVNNNGGGGGGGGGNNNVVVVVVfff^^^NNNNNNNNNVVVffffffVVVGGGGGGVVVfffwwwffffffwwwwwwwww†††ªªªÇÇÇ××תªª‘‘‘–––‘‘‘‘‘‘––––––––– ––– ––– ––– ––––––===""""""---333333333333333333333333333333333333333333333333333333===333333333333333===333===333==========================================EEE===EEE============EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV===------===NNNfff†††VVV^^^GGG^^^ ‘‘‘ÎÎÎ××× ßßßïïïýýýççççççïïïßßßçççßßß¶¶¶–––†††wwwppppppfffppppppppppppppppppppppppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎffffff^^^fff‘‘‘‘‘‘†††††††††‘‘‘fff^^^fff––– –––‘‘‘†††‘‘‘––––––fff^^^VVVNNNGGGEEEGGGNNNVVVfffwww–––ªªªªªª‘‘‘wwwfff^^^VVVGGGNNNNNNVVVfff^^^^^^VVVNNNNNNNNNVVV^^^VVV^^^VVVNNNGGGNNN^^^^^^^^^VVVGGGEEEGGGGGGNNNNNNVVV^^^^^^VVVNNNNNNGGGNNNNNN^^^ffffffpppppppppfffVVV^^^fffwwwwww–––°°°ªªªÁÁÁÇÇǪªª‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘NNN"""""""""333333333333333333333333333333333333333333333333333333333333333333333333333===333333============333=============================================EEE===EEEEEEEEEEEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV======333---EEEVVVpppVVV†††‘‘‘VVVNNNVVV‘‘‘×××ççç°°°ßßßïïïïïïïïïÇÇÇÎÎΪªªÁÁÁßßßçççÁÁÁ°°°ªªª°°°ªªª†††wwwppppppfffpppwwwwwwwwwpppwww†††ßßßýýýýýýýýýýýýïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß pppffffffppp††††††‘‘‘‘‘‘†††wwwpppppppppfffpppppp††† ‘‘‘wwwVVVVVVNNNGGGGGGGGGNNN^^^ppp ¶¶¶¶¶¶ wwwffffff^^^^^^^^^^^^NNNVVV^^^fff^^^VVVNNNNNNNNNVVV^^^^^^NNNNNNVVV^^^^^^^^^VVVNNNNNNGGGEEEGGGNNNNNNVVV^^^www‘‘‘ppp^^^ppppppfff^^^wwwwwwwwwwwwpppppp^^^VVV^^^fffwww††† ÁÁÁÇÇÇ pppwww†††‘‘‘––––––––– ––– –––ppp---""""""---333333333333333333333333333333333333333333333333333333333333333333333333333===333===333=========================================================EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEENNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV======333333333GGGGGGEEE‘‘‘–––†††^^^GGG^^^ªªªßßß×××ïïïýýýïïïççç ªªª‘‘‘¶¶¶ßßßïïïççççççßßßÎÎÎÇÇÇÁÁÁ–––pppppp††††††www ïïïýýýýýýýýýýýýÇÇǪªª ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇdž††fff^^^ffffffppp‘‘‘–––†††‘‘‘–––fff^^^^^^ppp††† ªªª–––wwwpppppp^^^^^^^^^VVVVVVGGGGGGGGGGGGVVV^^^www‘‘‘°°°ÎÎÎÎÎΪªªpppffffff^^^VVVVVVNNNVVV^^^^^^VVV^^^VVVNNNNNNVVV^^^^^^ffffffNNNVVVVVV^^^fff^^^VVVNNNGGGNNNNNNVVV^^^ †††wwwwwwfffNNNNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEGGGVVVfffwww‘‘‘‘‘‘wwwwwwwwwwwwwww†††––––––––– ––– ===""""""---333333333333333333333333333333333333333===333333333333333333333333333333===333===333======333================================================EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV===EEEGGG===333333GGGGGGppp––––––‘‘‘EEEVVVppp ÎÎÎïïïýýýïïïïïï‘‘‘‘‘‘×××ïïïïïïïïïïïïçççÎÎÎ××××××ÎÎΪªª††††††‘‘‘ªªªªªª–––††††††°°°ïïïýýýýýýýýýýýýÎÎζ¶¶wwwßßßýýýçççïïïýýýýýýýýýýýýçççÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªppp^^^^^^fff†††‘‘‘–––‘‘‘ffffffwwwwwwfffffffffffffffpppwwwpppfff^^^VVVNNNGGGGGGGGGNNNVVVpppªªªÇÇÇ×××ÇÇǰ°°‘‘‘pppfff^^^ffffffVVVVVVGGGVVV^^^^^^^^^VVVVVVNNNVVVfffffffff^^^VVVGGGVVVVVV^^^^^^^^^VVVVVVVVVVVV‘‘‘fff^^^fff^^^NNNGGG===========================EEEGGGfff†††wwwwwwppp^^^fffppp‘‘‘‘‘‘––––––‘‘‘GGG------333333333333333333333333333333333333333333333333333===333333333===333333333333======333================================================EEE===EEEEEEEEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVV^^^===NNNfffNNN===---333NNN^^^ppp VVV‘‘‘ffffff†††×××ïïïýýýççç fff†††ÎÎÎïïï×××çççïïïïïïïïïïïïÎÎÎ××××××ÁÁÁ¶¶¶ÇÇÇ×××ÎÎÎÁÁÁ –––ÎÎÎïïïýýýýýýýýýýýýýýýççç‘‘‘ççççççwww¶¶¶ÎÎÎßßßýýýýýý°°°GGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††ppp^^^fffwww†††–––‘‘‘††††††wwwffffff^^^^^^^^^^^^wwwwww††††††www^^^^^^^^^VVVNNNGGGGGGGGGVVVfffwww–––¶¶¶ÎÎÎßßßßßß°°°†††wwwpppppp^^^VVVNNNNNNGGGNNN^^^^^^^^^^^^VVVfffffffffffffff^^^VVVNNNVVVffffffffffffwww†††^^^GGGEEEEEEGGGGGGEEE===333333333333333333333333======VVV†††ppp^^^^^^VVVVVVGGGGGGVVVVVVffffffwww†††‘‘‘‘‘‘fff---""""""333333333333333333333333333333333333333333333333333333333333333333333===333===333333===333===333===333====================================EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNNNNVVVNNNVVVNNNVVV^^^EEE^^^ppp^^^EEE333333===NNNfffppp‘‘‘fff‘‘‘–––pppVVVpppÇÇÇïïïïïïÎÎÎpppÎÎÎçççÁÁÁ×××çççýýýýýýççç°°°×××ççççççççççççïïïïïïçççÇÇǶ¶¶ßßßýýýýýýýýýýýýýýýýýýïïï ïïïÎÎÎ^^^–––wwwïïïýýý××× fffýýýïïïÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfffwwwwwwppp†††††† †††ppp^^^^^^^^^^^^pppppp†††wwwwwwwwwwwwppp^^^^^^VVVGGGGGGGGGNNNVVVpppªªªÁÁÁßßß×××¶¶¶°°° pppppp^^^VVVNNNNNNNNNNNNVVVVVVfff^^^fffffffffpppfffffffff^^^fffwww–––¶¶¶ÇÇÇÎÎΪªªpppNNNEEE=========333===333333333333333===---======EEEGGGNNNfffpppfffEEE===333333------NNN^^^^^^^^^^^^fffpppwwwppp333""""""---333333333333333333333333333333===333333===333333333333333===333===333333333===333333===333===333==========================================EEE===EEEEEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNVVVNNNVVVVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVVVVVVV^^^GGG^^^ppppppGGGEEE333333===GGGpppfff^^^–––ªªªªªª^^^NNN^^^ÇÇÇïïïÎÎÎwwwÁÁÁÇÇǰ°°ÎÎÎýýýïïïïïïÁÁÁ–––ßßßïïïïïïïïïçççßßßýýýïïïïïïßßßçççýýýýýýýýýýýýýýýýýýýýý°°°ÎÎÎÎÎÎVVV °°°–––ïïïýýýýýýßßßpppïïïßßßVVVÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††pppfff^^^fff†††‘‘‘ www†††wwwpppfff^^^^^^ffffff††††††‘‘‘^^^^^^^^^VVVNNNGGGGGGGGGVVV^^^ppp†††°°°ÇÇÇßßßßßßßßß°°° †††fff^^^^^^^^^fffNNNGGGNNNNNN^^^^^^ffffffffffffpppppp†††ªªªÇÇÇßßßßßßßßßßßßßßß××× fffNNN======333333333===333=========333333===EEE======NNNNNNGGG333"""------"""333VVV^^^^^^^^^^^^^^^^^^^^^^^^NNN===333---333333333333333333333333333333333333===333333333333333===333333333333===333333======333===333===333==========================================EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^VVVVVVNNNNNNNNNNNNNNNNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^EEEVVVwww^^^EEE======333===NNNpppppp °°°ÁÁÁ–––^^^NNNfff¶¶¶×××ÇÇÇ ¶¶¶ÎÎÎïïïýýýïïï°°°ªªªÇÇÇ×××çççýýýïïïçççÎÎÎÎÎÎßßßïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýªªªwww wwwÎÎÎÇÇÇïïïýýýïïï pppïïïÇÇÇEEEwww––––––ýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfff^^^fffwww†††‘‘‘‘‘‘‘‘‘†††††††††fff^^^^^^^^^ppp†††††††††‘‘‘†††ppp^^^^^^VVVNNNGGGGGGGGGVVVfffwww ÁÁÁßßßççç×××××× ‘‘‘†††pppfff^^^VVVNNNNNNVVVfff^^^fffwww–––°°°ÁÁÁÎÎÎ×××çççßßßçççççççççßßßçççÎÎÎ fffEEE============333333333======333------""""""------""""""333------===VVV^^^^^^^^^^^^^^^VVV^^^VVVVVVNNNGGGGGG======333333333333333333333333333333333333333333===333333===333===333===333===333===333333=========333==========================================EEE===EEE===EEE======EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^EEEGGGVVVwwwfffEEE=========333===GGGppp‘‘‘°°° ‘‘‘VVVfffªªªßßßçççÁÁÁÎÎÎïïïýýýïïïÇÇǶ¶¶ÁÁÁßßßýýýïïïýýýýýýïïï¶¶¶°°°–––ªªªïïïïïïýýýýýýýýýýýýýýýýýýýýý××׆†† www‘‘‘ppp×××ýýýÇÇÇfffªªªýýý¶¶¶=========NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇdž††pppfff^^^fff††††††‘‘‘––––––pppffffff^^^pppwwwwww†††††††††††††††fff^^^^^^^^^VVVNNNGGGGGGNNN^^^www‘‘‘¶¶¶ßßßßßßßßß¶¶¶¶¶¶ÎÎÎ××תªª†††pppfffffffffppppppwww†††ªªªÎÎÎßßßßßß×××ßßßßßßççççççççççççßßßßßßßßß×××°°°‘‘‘GGG===333------"""333EEEEEE333"""""""""------"""---===VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVNNNNNNEEE======333333333333333333333333333333333333333===333333333===333===333===333======333===333================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEENNNNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^EEEEEEGGGfff^^^EEEEEEEEE======333333EEEppp–––°°° ªªª†††VVV^^^ ÎÎΪªªªªªÇÇÇßßßÇÇÇ‘‘‘ªªªÁÁÁçççïïïýýýýýýýýýýýý×××ÎÎΖ–––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎÇÇÇppp×××ýýý wwwçççýýý¶¶¶NNN===333NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß wwwfffffffffppp‘‘‘‘‘‘†††‘‘‘††††††wwwfff^^^fffffffffppppppppppppppp^^^VVV^^^^^^VVVVVVVVVGGGNNNNNNVVVppp‘‘‘¶¶¶ßßß×××¶¶¶ÎÎÎßßßßßß×××ÇÇÇ ††† ªªª°°°¶¶¶ÁÁÁÇÇÇßßßççççççßßßßßßççççççßßßççççççßßßÎÎΰ°° pppNNN===333------------"""""""""---"""""""""""""""""""""===VVV^^^VVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNGGGEEE===333333===333333333333333333333===333333======333===333===333===333===333===================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^ffffffEEEGGGNNNpppNNNEEEEEEGGGEEE======333333EEEwww°°°ªªª‘‘‘wwwNNNwww‘‘‘ªªª °°°ÇÇǰ°°°°°ÎÎÎïïïýýýýýýýýýýýýýýýïïïççç×××ÇÇǰ°°ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççýýýýýý†††NNN–––ïïï°°°^^^NNNVVV^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××׆††ppppppwww‘‘‘––––––‘‘‘†††††††††www^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^NNNNNNNNN^^^ppp‘‘‘¶¶¶×××ÁÁÁÁÁÁÁÁÁ×××ßßßÇÇǪªªppppppwwwªªª¶¶¶×××ßßßßßßßßßçççççç×××çççßßß¶¶¶ªªª‘‘‘fffGGG===333------333333---""""""""""""EEEVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVNNNGGGEEE===333333333333333333===333333===333333333===333======333===333======333===================================================EEE===EEE===EEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^VVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffGGGVVVfffpppVVVEEE===GGGEEEEEE============GGGppp††††††–––‘‘‘wwwVVV †††www ¶¶¶ÇÇÇÇÇÇßßßýýýýýýýýýýýýïïïýýýýýýççççççïïïçççßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýý×××wwwïï着ªwwwwww†††fffÇÇÇ–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýèÇÇËccÔ''êõ÷îÕ%%§TT––––––‘‘‘†††ppppppfff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVNNNNNNVVVppp‘‘‘¶¶¶ÎÎΰ°°‘‘‘–––†††ppp^^^VVV^^^^^^www ªªª ¶¶¶ßßßÎÎΰ°°‘‘‘pppGGG333---"""333GGGGGG333"""---------EEEVVVVVV^^^^^^^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVVNNNNNNEEE===333333333333333333===333333===333===333===333============333=========================================================EEE===EEE===EEEEEEEEE===EEE===EEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^VVV^^^VVVNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppfffffffffffffffppp‘‘‘^^^EEEGGGNNNNNNEEE=========EEEEEEEEEVVVwwwwww†††–––wwwÁÁÁ†††fffppp–––ÁÁÁÇÇÇçççýýýýýýýýýïïïýýýýýýýýýççççççïïïïïïççççççýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýý°°°–––ÎÎΪªªppp××× °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��Ù--“ŽŽ†††‘‘‘‘‘‘‘‘‘pppffffff^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^VVVVVV^^^ppp‘‘‘°°°°°°pppwwwppppppfff^^^^^^VVV^^^VVV^^^ppp^^^GGG333---------===GGGVVVVVVVVVNNN333""""""EEEVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVNNNEEE333======333333===333333===333===333===333======333===333===============================================================EEE===EEEEEEEEE===EEEEEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNNNNGGGNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffpppfffppppppfffpppfffpppfffpppwww‘‘‘ –––^^^VVVwwwNNNEEEEEE===EEEGGGVVVGGGEEEVVVwwwwwwwww °°°‘‘‘^^^^^^†††ÁÁÁßßßçççïïïïïïýýýïïïýýýïïïýýýççççççýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎïïï ¶¶¶www°°° ßßßýýýýýýýýýýýýýýýýýýýýýýýýú��ùû‰‰á³¨¨šVVéú��ú��ú��ºBB‘‘‘‘‘‘‘‘‘‘‘‘––––––†††^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^ppp†††ªªª–––‘‘‘¶¶¶ÎÎÎ ‘‘‘†††fffNNNGGG===333------------333===EEEEEEGGGVVVNNNGGGEEE333""""""""""""""""""NNNVVV^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVNNN======GGGGGGEEE===333===333333===333===333===333======333=========333=========================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^^^^fffffffffffffffffffffpppfffppppppppppppffffffppppppfffpppfffpppfffªªªwww‘‘‘‘‘‘wwwppp†††‘‘‘NNNEEEEEEEEEEEENNN^^^VVVEEEEEEVVVppp^^^fff°°°ÇÇǰ°°fffpppwww†††ÇÇÇ×××ïïïççç×××ßßßýýýïïïýýýççç×××ïïïÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççýýýßßßßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýý––––––‘‘‘GGGwww‘‘‘VVV°°°çççýýýýýýýýýýýýýýýýýýýýýúZZüïïýýýýýýïïï××ײllú��ú��ú��æ‘‘‘––– –––‘‘‘wwwfffú��ú��^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNVVV^^^ppp‘‘‘ªªª¶¶¶¶¶¶NNNEEE===---------333======EEEEEE======EEE333---333333333333------""""""""""""""""""---VVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVVVVVGGG======GGGNNNVVVNNNGGGEEE===333===333333333===333===333======333===333=========================================================EEE===EEEEEE===EEE======EEE===EEEEEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^VVV^^^ffffffffffffffffffffffffffffffpppppppppppppppppppppfffpppppppppppppppfffpppffffffpppwww†††www‘‘‘www‘‘‘†††VVVEEEEEE===EEEGGGVVVNNNEEE===EEENNNfff^^^†††ªªª†††ppp¶¶¶–––‘‘‘wwwÇÇÇçççïïïççççççïïïýýýçççïïïçççÎÎΪªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýççç××תªªVVV†††–––ÇÇÇ‘‘‘ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçÝÝú��ú��ú��ö‘‘‘‘‘‘wwwwwwffffff^^^^^^ú��ú��^^^^^^^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNGGGEEE333===EEEEEEEEEEEEEEE===333333------"""""""""---"""------""""""""""""---""""""---VVV^^^^^^VVVVVVNNNVVVVVV^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVNNN======GGGNNNVVVVVVVVVVVVNNNGGGEEE======333===333===333============333===============================================================EEEEEEEEE=========EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fffffffffffffffffffffpppfffffffffppppppppppppppppppppppppfffppppppppppppppppppffffffffffff^^^www†††‘‘‘†††††† †††^^^EEEEEEEEEEEEEEEGGGGGGEEEEEEEEE===GGG^^^VVVfff–––www¶¶¶°°°^^^fff ×××ÎÎÎçççïïïïïï×××ßßßïï着ª¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××www–––†††ÁÁÁçççfffVVVªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýûÞÞú��ú��ú��ôwwwpppffffff^^^fff^^^^^^ú��ú��^^^^^^^^^VVVVVVNNNNNNGGGGGGGGGGGGNNNGGGNNNGGGEEEEEE=========EEEEEEGGGGGGGGGGGGEEE===333333------""""""""""""""""""""""""""""""---------333"""""""""------""""""===VVV^^^^^^^^^NNNEEEGGGVVV^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVVVVVGGGGGGGGGVVV^^^^^^VVV^^^VVV^^^VVVNNNGGGEEE===333===333======333===333======333=========333==============================EEE=========EEE===EEE===EEE=========EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwpppwwwpppppppppfffffffffffffffpppppppppfffppp‘‘‘†††ppp––– fffGGGEEEEEEEEEEEEEEEEEENNNNNNNNNEEE===GGG^^^VVVVVVfffÁÁÁÇÇǰ°°†††VVV^^^ªªªÁÁÁïïïïïïßßßÎÎÎïïïÁÁÁÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎΆ††×××çççpppfffNNN–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýúwwú��ú��ú��ò†††pppffffff^^^^^^fff^^^ú��ú��VVVVVVNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGEEE======EEEEEEEEEGGGNNNGGGGGGEEE======333------"""""""""""""""""""""""""""""""""""""""NNNEEEGGGNNN"""---333""""""""""""EEEGGG333"""EEE^^^^^^^^^^^^NNNEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVVNNNVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVNNNNNNEEE======333======333=====================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fffffffffffffffpppfffpppppppppfffpppppppppppppppppppppppppppwwwwwwpppwwwpppppppppfffffffffffffffpppppppppppppppVVV^^^fffpppppp°°°–––NNNEEEEEE===EEEEEEEEE^^^ppp^^^NNNGGG===EEEVVV^^^pppÎÎÎÇÇǪªª¶¶¶ ^^^fffwwwªªªçççýýýïïïÁÁÁïïïïïïçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΰ°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççÎÎÎwww†††ppppppïïïýýýýýýýýýýýýýýýýýýýýýýýýü´´ùú��ú��ú��ùIIßßß wwwfffffffff^^^^^^ú��ú��GGGNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEEEEEEEGGGGGGGGGEEEEEEEEE===333333---------""""""---"""""""""""""""---""""""""""""""""""""""""---===GGG---GGG^^^NNNfffGGGEEEVVVVVV333GGGNNN===GGGGGG---"""GGG^^^^^^^^^^^^VVVGGGNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNGGGEEE======333======333===333=========333================================================EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE^^^fffffffff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffppppppppppppfffpppppppppfffpppppppppppppppwwwwwwwwwwwwwwwwwwpppppppppfffffffffpppppppppfffpppfffffffffGGG^^^ffffff wwwªªªVVVEEEEEE===EEEEEE^^^^^^fffVVVEEE===EEENNNwww–––ÁÁÁ×××ÎÎΪªª†††GGGNNNªªªçççïïïÇÇÇïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎwwwpppßßß¶¶¶×××ýýýýýýýýýýýýýýýû°°ùú��ú��ú��ú��û¯¯ýýýçççÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGNNNGGGGGGGGGGGGGGGEEE======333333------------"""---""""""---"""""""""---"""---===GGGEEE"""""""""""""""---333333===GGGVVVVVV^^^333EEEVVV^^^EEEGGGNNN333^^^EEENNNVVVNNNGGG^^^GGG===EEE333""""""NNN^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVV^^^^^^^^^^^^fff^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVNNNNNNGGGEEE======333===333=========333======================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEfff^^^^^^fff^^^fff^^^fffffffffffffffffffffppppppfffppppppfffpppfffppppppfffpppppppppppppppppppppwwwwwwwwwwwwwwwppppppppppppfffpppppppppppppppfffpppfffppppppfffGGGVVVwwwfff‘‘‘°°°–––‘‘‘°°°‘‘‘VVVGGGEEEEEEEEEVVVfffpppwww^^^GGGEEE======GGGffffff†††ÇÇÇÁÁÁÇÇǰ°°VVV===NNNªªªççççççïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°ýýýýýýýýýýýýýýýýýýçççÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶fffppp ^^^pppçççýýýýýýýýýû¦¦ùú��ú��ú��ú��úttýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGEEEEEE=========333------------"""---""""""---""""""---"""""""""---===GGGEEE===VVVpppNNN---333===---===VVVVVVGGGVVVffffff^^^fffGGGVVVEEEVVVEEENNN======NNNGGGfffNNN===GGG^^^333---------VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNEEE==============================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEE^^^fff^^^fff^^^ffffffffffffffffffffffffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwppppppppppppfffppppppfffffffffpppfffpppfffppppppppppppEEEGGGppp†††www–––––––––ªªª¶¶¶–––^^^GGGEEEEEENNN^^^wwwpppfffVVVGGGGGGEEEEEEEEENNN^^^www ¶¶¶ÁÁÁ°°°^^^GGGNNN–––ßßßïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××pppýýýýýýýýýýýýýýýýýýßßß×××ÎÎÎçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ppp–––‘‘‘ÁÁÁÁÁÁªªªûœœùú��ú��ú��ùû‰‰ýýýýýýýýýýýýýýýýýýççç¶¶¶ ú��ú��GGGGGGGGGEEE333333333------------"""---""""""---""""""---""""""---"""---------"""333VVVpppfffVVVpppwwwGGGEEEVVVfffGGGGGGfffEEEEEE^^^NNN^^^NNN^^^GGG^^^GGGVVVEEE^^^333NNNGGGVVVNNN===333EEE^^^===---"""---VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^^^^fff^^^^^^GGGEEE===========================================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEE===EEEffffffffffffffffffffffffpppfffpppppppppppppppfffppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfffpppfffffffffppppppfffpppppppppwwwpppppppppfffGGGGGGVVVwww‘‘‘wwwwwwªªªªªªªªª°°°¶¶¶–––VVVGGGGGGGGGVVVppppppwwwpppVVVNNNGGGEEE======EEENNNfff ¶¶¶ÁÁÁ°°°‘‘‘NNNNNNªªªïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇfffwwwýýýýýýïïïýýýýýýýýýýýýýýýçççÇÇÇ×××ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎζ¶¶ ªªª‘‘‘ªªª¥::ùú��ú��ú��ùû››ýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªú��ú��===333333------"""---""""""""""""---""""""---""""""---""""""------===GGGGGG333---333fffwwwppppppppppppNNN===NNNfffNNNVVV^^^---===^^^NNN^^^VVV^^^NNNfff^^^NNN===VVV333VVVEEENNNfffGGGGGG^^^NNN333"""===^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVV^^^^^^ffffffpppfffffffffVVVGGGEEE==============================================================================EEEEEE===EEE===EEE===EEEEEEEEE===EEEEEE===EEEEEE===EEEEEEfffpppfffpppfffpppfffpppfffppppppfffpppfffppppppppppppwwwwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppwwwpppfffppppppppppppfffppppppppppppppppppppppppwwwpppppppppNNNGGGNNN^^^‘‘‘www†††¶¶¶ÁÁÁ°°°°°°¶¶¶ fffNNNGGGVVVpppwwwªªªfffVVVNNNGGGEEE=========VVVfff °°°°°°ÎÎÎfffEEEVVV×××ïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ^^^wwwïïïýýýïïïýýýýýýýýýýýýýýýýýýïïïÇÇǰ°°ÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶pppfff°°°ÃLLú��ú��ú��ú��ùû§§ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°ú��ú��---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===VVVppppppGGG---===fffppppppfff^^^pppGGGEEEVVVpppNNN^^^VVV---EEE^^^VVVVVVVVVGGG===VVVNNN------==="""333"""333EEE---===GGG333GGG^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^fffpppfffpppppppppfffffffffVVVGGGEEE======================================================EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEE===EEEfffpppppppppppppppppppppffffffpppppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwppppppwwwppppppppppppfffpppfffpppfffpppppppppwwwwwwwwwwwwwwwwwwwwwppppppwwwppp^^^VVVNNNVVVfffwww††††††ªªªÁÁÁÁÁÁ°°°°°°ÁÁÁÁÁÁfffNNNNNN^^^ppp°°°VVVNNNGGGGGGEEEEEE======NNNNNN ÇÇÇççç–––EEEEEE ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß‘‘‘fffïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýý×××ÎÎÎßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßpppVVVÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýý××× ú��ú��"""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVV^^^^^^pppGGG---EEEfff^^^ppp^^^VVVpppGGGVVVfffpppNNN^^^NNN---EEENNNGGG===333------""""""GGG^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^fffpppfffpppfffppppppppppppffffff^^^NNNGGG======================================================EEE======EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEE===EEEEEE===EEEpppppppppwwwwwwpppppppppfffpppfffpppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppp^^^^^^VVVVVV^^^VVV–––‘‘‘–––¶¶¶ÇÇǰ°°°°°×××°°°pppNNNVVVVVVwww–––‘‘‘VVVNNNGGGGGGGGGGGGEEE=========GGG†††ßßßÁÁÁpppEEE†††ýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎ^^^VVV‘‘‘ïïïïïïïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÎÎÎßßßïïïýýýýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁßßßú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýýÎÎΖ––ú��ú��""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""---NNNEEENNNfffEEE---===pppVVVVVVNNN^^^pppGGGfffffffffEEEGGG===""""""---""""""""""""NNN^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffffffffpppfffpppfffpppfffppppppfffffffffVVVGGGEEE===========================================================================EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEEppppppppppppppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwpppppppppffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwppppppppp^^^www†††VVV^^^NNNVVV†††ªªª‘‘‘–––ÁÁÁÁÁÁ°°°°°°ÁÁÁ¶¶¶NNNVVVfff‘‘‘†††VVVNNNNNNGGGNNNGGGGGGGGG===333===NNN‘‘‘–––‘‘‘fff–––ïïïççç×××ýýýýýýýýýýýýýýýýýýýýýýýýßßßfffVVVppp ×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎÁÁÁßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççwwwßßß‘‘‘–––çççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––fff333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===^^^VVV---"""EEEpppVVVEEEEEE^^^^^^===EEEGGG===------""""""""""""""""""333"""---VVV^^^^^^^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffffpppfffffffffpppfffpppfffpppffffffpppffffff^^^^^^VVVGGGEEE==========================================EEE============EEE======EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEppppppfffpppfffpppfffpppppppppppppppppppppppppppwwwpppwwwpppppppppppppppfffppppppfffpppffffffpppppppppwwwwwwwwwwwwwwwwwwppppppppppppwwwpppppppppppp^^^pppªªªfffNNNNNNppp‘‘‘–––––––––¶¶¶ÇÇǰ°°°°°ÎÎÎÎÎÎwwwVVVNNNpppwww^^^GGGGGGNNNGGGNNNGGGNNNGGGGGG===EEEfff†††www^^^–––ïïïßß߆††çççýýýýýýýýýýýýýýýýýýýýýïïï–––^^^NNNNNN^^^ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁßßßwwwÁÁÁªªªpppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ–––fff---"""""""""""""""""""""""""""""""""""""""""""""---------"""""""""---NNN^^^EEE333---EEEpppNNN333333GGGEEE---------"""""""""""""""""""""""""""""""""""""""------------333---333^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffpppfffpppffffffffffffpppffffffffffffpppfffpppfff^^^^^^NNNEEE=========================================================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppppppppwwwpppwwwppppppppppppwwwpppppppppppppppppppppppppppffffffpppfffpppfffpppppppppppppppwwwwwwwwwwwwwwwppppppppppppwwwwwwpppwwwwwwpppffffff¶¶¶¶¶¶wwwVVVGGG^^^www–––ªªª––– ¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇdž††VVVVVVffffffNNNGGGNNNNNNGGGNNNNNN^^^†††^^^GGGVVVwww†††VVVpppßßßÇÇÇwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççNNN^^^‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýçççpppÇÇÇfff^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁ‘‘‘VVV---""""""""""""""""""""""""""""""""""""""""""---GGGVVV==="""""""""EEEfffVVVNNNGGG---===VVVEEE---------"""---""""""---"""""""""""""""""""""""""""333"""---333333333======""""""333333---===VVVfff^^^^^^^^^^^^fffVVVVVV^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffffffffffffffffffffffpppfffpppffffffffffff^^^VVVGGGEEE======================================================EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEwwwwwwwwwwwwwwwppppppppppppppppppppppppfffpppfffpppfffffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwfffppp¶¶¶¶¶¶–––^^^VVVVVVppp °°° ªªªÎÎΪªª ÁÁÁÁÁÁppp^^^fffNNNGGGGGGNNNNNNNNNNNNwww¶¶¶ ^^^EEENNNppp^^^ppp‘‘‘††††††¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýïïï–––ªªª×××ïïïýýýýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýïïï°°°ÁÁÁVVVNNN^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ†††NNN---"""""""""""""""""""""""""""""""""---------EEE^^^NNN===""""""---VVVwwwpppfffEEE------------""""""---"""---"""---""""""""""""""""""""""""""""""""""""===EEE======EEE===------""""""333333---"""GGG^^^^^^^^^fff^^^^^^NNNVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^VVVGGGEEE==========================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEwwwwwwwwwpppwwwppppppwwwpppfffpppppppppppppppfffpppppppppfffpppfffpppppppppwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwww†††^^^^^^ªªª¶¶¶ªªªfffVVVfffppp––– ––––––¶¶¶°°°ªªª–––ªªª¶¶¶ªªª^^^^^^NNNGGGGGGNNNVVVNNN^^^ªªª×××¶¶¶†††NNNEEEGGGNNNfff‘‘‘°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýýÁÁÁ‘‘‘ÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßßýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßfff–––¶¶¶ßßß°°°×××ýýýýýýýýýýýýýýýýýýççç¶¶¶†††GGG---""""""""""""""""""""""""""""""333GGGVVVEEEGGGppp^^^GGG---"""333^^^fffVVVEEE---""""""""""""---"""------"""---""""""""""""""""""""""""""""""------"""""""""---===EEE===333---"""---======333---NNN^^^^^^^^^^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffff^^^NNNEEE=============================================EEE===EEEEEEEEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEwwwwwwwwwwwwpppppppppfffppppppfffpppffffffpppppppppfffpppppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwpppwwwwww††††††www^^^^^^††† –––fff^^^fffpppwww‘‘‘ªªª ––– ÁÁÁ ‘‘‘ªªªÎÎΆ††wwwNNNNNN^^^fffppp^^^fffÇÇÇÎÎÎÁÁÁ–––pppGGGGGGGGGfff†††‘‘‘ªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýïïï°°°†††ªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýïïï‘‘‘¶¶¶çççÎÎÎVVV†††ýýýïïïýýýýýýýýýýýýççç¶¶¶‘‘‘EEE""""""""""""""""""---===333"""---GGG^^^pppGGG===NNNppp^^^---"""---EEE===---""""""""""""---""""""---""""""---""""""---""""""---------""""""---===EEE===---333---"""---""""""=========333VVV^^^^^^^^^VVVNNNVVV^^^^^^^^^^^^^^^^^^fffVVV^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffpppfffpppfffppppppffffff^^^VVVGGGEEE====================================EEE===EEE===EEEEEEEEEEEEEEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwpppppppppppppppppppppppppppppppppfffpppppppppwwwwww†††wwwwwwppppppwwwwwwwwwppppppwwwwww†††††††††††††††ppp^^^^^^––––––†††fffffffffpppwww‘‘‘ ‘‘‘ªªª°°°ªªª–––ªªª°°°¶¶¶fffVVVpppwwwwwwpppfff¶¶¶ÎÎÎÎÎÎ †††fffVVVfffppp‘‘‘ ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ °°°×××ýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶×××ýýýýýýýýýýýýýýýýýýýýý××׆††ÁÁÁÁÁÁppp†††çççÇÇÇçççýýýýýýýýýççç¶¶¶†††===""""""""""""------EEE^^^==="""333^^^fffVVV===EEENNNfffNNN---""""""---""""""---"""""""""""""""---""""""---"""------------"""---=========---333EEEEEE333"""---""""""333======333^^^^^^^^^NNNNNNVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppffffffffffffpppfffffffff^^^NNNEEE========================EEE==================EEE===EEE===EEE=========EEE======EEE===EEEwwwwwwwwwwwwpppwwwpppwwwpppppppppppppppfffppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwww††††††††††††wwwppp^^^VVV†††ªªªªªª‘‘‘†††^^^ppppppwww‘‘‘ªªª–––††† ¶¶¶°°°–––°°°ÎÎΰ°°ppppppfffpppwwwfff °°°ÁÁÁªªª†††www‘‘‘fffppp‘‘‘–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ÁÁÁ¶¶¶×××ýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýßßßßßßýýýïïïýýýïïïýýýýýýýýýýýýïïï××תªªppp¶¶¶ççç ïïïýýýýýýççç°°°===---"""---333======NNNppp333"""===pppfffGGGGGGGGGfffNNN==="""""""""""""""---"""""""""""""""---""""""---"""------333===------===GGGEEE333---333333---""""""""""""---======EEEVVVVVVNNNNNN^^^^^^VVV^^^VVV^^^^^^fff^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffpppfff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffffffffffffffpppffffffffffff^^^VVVGGG==================EEE===============EEE=========EEEEEE===EEE=========EEE===EEE===EEEwwwwwwwwwwwwwwwwwwppppppwwwppppppfffpppppppppwww††††††wwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††ppp^^^^^^‘‘‘¶¶¶°°°ªªªppp^^^ppppppwww‘‘‘ªªª‘‘‘†††–––ÁÁÁ¶¶¶––– ÎÎÎÇÇÇwww^^^VVVfffwwwfffwww–––†††www–––VVVppp––––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççççç–––×××ççç×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïßßßÁÁÁïïïýýýýýýýýýýýýïïïVVV°°°çççÁÁÁ‘‘‘çççýýýýýýçç窪ªEEE333---333NNNNNNGGGVVVppp333"""333fffppp^^^GGG===333333""""""""""""""""""""""""""""""""""""""""""---333333---333EEEGGG===333GGGEEE===---"""""""""""""""""""""""""""---"""===333GGGVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffpppfffffffffffffffpppfffffffffpppfffffffff^^^fff^^^^^^GGGEEEEEE===EEE==============================EEE======EEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwwwwppppppfffwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwww††††††††††††††††††www^^^fff––– ¶¶¶†††ffffffpppwww‘‘‘‘‘‘ †††–––ÁÁÁ¶¶¶‘‘‘‘‘‘¶¶¶¶¶¶wwwffffffwww^^^ppp ppp^^^ffffff‘‘‘–––^^^VVV––––––×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßß߆††×××ýýý×××çççýýýýýýýýýýýýïïïýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïççç°°°çççýýýýýýýýýýýýýýý¶¶¶^^^°°°ÎÎÎÎÎÎ×××ýýýýýýýýýßßߪªªwwwGGGEEE======^^^GGGEEENNNpppEEE333EEENNN^^^NNN---""""""""""""""""""""""""""""""""""""""""""---"""---===EEEEEEGGGGGGGGGEEE333---333---"""""""""""""""""""""""""""---333=========---------NNN^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffffffffffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fffffffff^^^VVVEEEEEE===EEE===EEE========================EEE======EEE======EEE======EEE===EEE†††wwwwwwpppppppppwww††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘††††††www^^^^^^–––°°°°°°–––wwwfffpppppp†††°°°‘‘‘††† ÁÁÁ°°°‘‘‘†††°°°ªªªpppffffffpppwwwfff^^^^^^ wwwGGG–––ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßßßß¶¶¶ßßßïïïÎÎÎçççýýýýýýýýýïïïÇÇǰ°°ßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇǶ¶¶çççßßßýýýýýýýýýïïïßßßçççßßßßßßýýýýýýýýýýýýßßß pppEEEEEE======NNNEEE333GGGppp^^^VVVNNN333---"""""""""""""""""""""""""""---""""""---"""---333333EEEEEEGGGGGGGGGEEE===333------""""""---"""""""""""""""""""""""""""333======EEEEEE===333"""---VVV^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^VVV^^^ffffffpppfffpppffffff^^^GGGEEE=======================================EEE===EEE======EEE===EEE===EEE†††††††††wwwwwwwwwwww††††††††††††††††††††††††wwwwwwwwwwwwpppwwwpppwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††www^^^fff†††¶¶¶ÁÁÁ°°°pppfffpppwwwwww†††‘‘‘–––†††ªªª¶¶¶ªªªwwwwww‘‘‘pppwww^^^www†††††††††fffpppppp‘‘‘‘‘‘GGGppp–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°°°°çççççç×××ßßßýýýýýýýýýÎÎΆ††††† ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××ÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýý××תªªpppEEE333======EEE===333EEEpppfffNNN===""""""------"""------"""""""""---""""""---333EEEEEEGGGGGGGGGGGGEEE333333---""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""333======EEEEEE===333""""""333VVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^^^^^^^ffffff^^^^^^^^^fff^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^VVV^^^fffpppfffppppppfffpppppppppfff^^^NNNEEE=======================================EEE===EEEEEE===EEE===EEE===†††www†††‘‘‘‘‘‘†††‘‘‘††††††††††††wwwwwwwwwpppppppppppppppwwwwww††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††www^^^^^^†††ªªª°°°–––pppfffpppwwwpppªªª ‘‘‘‘‘‘ªªª†††wwwppppppwwwwwwppp††††††www‘‘‘†††‘‘‘–––^^^VVV °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç熆†¶¶¶ªªªçççßßßïïïïïïýýýççç°°°‘‘‘¶¶¶ÇÇǪªªÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªpppEEE333=========333---333GGGEEE---"""---===EEE======333EEE------333333333"""333GGGEEEEEE===333------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------333333333"""""""""===VVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^ffffffffffff^^^fffffffffpppffffffffffff^^^ffffff^^^fffffffff^^^^^^VVV^^^^^^fffffffffppppppfffppppppfffppppppppppppfffNNNGGG=========EEE===========================EEE===EEE===EEE======EEE††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††wwwwwwwwwpppfffppppppwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††wwwfff^^^‘‘‘¶¶¶¶¶¶fffppppppppp–––°°°†††††††††°°°†††††††††fffwwwpppwww––––––‘‘‘–––†††NNN–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁ^^^†††wwwfffÁÁÁïïïçççßßßýýýÎÎÎÁÁÁ¶¶¶ÇÇǪªªÁÁÁ ßßßýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýïïï×××ßßßÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïï××× pppEEE333333===------"""""""""""""""333EEENNNNNNGGGGGGEEEEEEEEENNNNNNEEE333EEE===------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---EEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffffVVV^^^^^^ffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffff^^^^^^VVV^^^^^^ffffffpppfffpppfffpppppppppppppppppppppppppppfff^^^GGGEEE======EEE=====================EEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwwwwwwwfffppppppwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††wwwfff^^^†††°°°†††–––wwwfffppppppppp°°°ªªª††††††°°°°°°–––ªªª‘‘‘ppppppfff^^^www†††‘‘‘–––‘‘‘–––pppÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï–––NNNfff–––ÁÁÁßßßÎÎÎ ïïïççççççÁÁÁ°°°°°°ßßßÁÁÁ¶¶¶ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ×××çççýýýýýýýýýýýýýýýýýýïïï××× pppEEE333""""""""""""---333333======GGGVVVVVVNNNGGGGGGEEEEEE===---333===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^ffffff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffpppfffffffff^^^fffffffffffffff^^^^^^VVVVVVfff^^^ffffffpppfffffffffpppfffpppfffpppppppppppppppppppppfff^^^GGGEEEEEE===EEE===EEE======EEE======EEE===EEE===EEE=========‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppfff‘‘‘‘‘‘ªªªªªª‘‘‘pppppppppppp†††°°°ªªª†††‘‘‘ªªª‘‘‘ †††wwwppppppVVVpppwww‘‘‘‘‘‘‘‘‘wwwfffÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïppp^^^‘‘‘°°°ÁÁÁ×××ßßß çççïïïïï着ª‘‘‘‘‘‘¶¶¶ßßßÎÎÎ×××ýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïï×××çççýýýýýýýýýýýýýýýýýýïïï××× ^^^===333---"""---333GGGGGGNNNEEE===EEEGGGGGGGGG===333------"""""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^^^^ffffffffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppffffff^^^^^^ffffffffffff^^^VVV^^^^^^ffffffffffffffffffpppffffffpppfffpppppppppfffppppppppppppppppppfffNNNEEEEEE===EEE===EEE======EEE=========EEE============EEE‘‘‘–––‘‘‘–––‘‘‘†††‘‘‘†††††††††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††‘‘‘‘‘‘–––––––––‘‘‘fff^^^wwwªªª¶¶¶°°°†††fffffffffppp†††ªªª–––wwwwww†††pppfffwwwpppwww^^^pppfffpppwww†††‘‘‘pppwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎppp‘‘‘°°°–––¶¶¶×××^^^¶¶¶ïïïççç¶¶¶†††ppp‘‘‘ ïïïýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––NNN======333333---""""""EEENNNVVVVVVGGG===EEEEEE===333---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""---""""""""""""---333VVVfff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^ffffffpppfffpppfffpppfffffffffffffff^^^fffffffffffffffffffffffffff^^^ffffffffffff^^^^^^VVV^^^^^^ffffffpppfffffffffffffffppppppfffpppfffpppfffpppppppppppppppwwwppppppfffVVVEEEEEE===EEE===EEE=========EEE======EEE============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwwwwwwwwww†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––– ––––––†††ppp^^^°°°¶¶¶ªªªpppffffffpppwww–––wwwwwwwwwpppfffwwwpppwww†††ppp^^^pppwww†††‘‘‘‘‘‘‘‘‘°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁffffffppp–––ÁÁÁ‘‘‘VVV°°°ßßßçççßßß¶¶¶wwwppp^^^wwwÎÎÎýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ–––NNN=========333333""""""===NNNVVVGGGGGGEEE======333333333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""EEE^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^VVV^^^^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffpppfffffffffffffffffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppVVVGGGEEE===EEE===EEE===EEE======EEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘wwwwwwpppwww†††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– –––‘‘‘ffffff°°°¶¶¶ wwwffffffpppwwwwwwwwwpppwwwwwwpppwww†††www–––wwwfffpppwww†††‘‘‘–––‘‘‘ªªªÁÁÁÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇNNN^^^VVVNNN^^^¶¶¶–––^^^††† ßßßïïïßßß‘‘‘^^^^^^fff^^^†††ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ NNN333======333333---"""---333======333============333---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""""""""---""""""""""""""""""---"""EEEVVV^^^^^^fffffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^fffVVVVVV^^^^^^fff^^^fffffffffpppfffffffffffffff^^^fff^^^ffffffffffffpppfff^^^ffffff^^^fffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppfff^^^GGGEEE===EEE===EEE===EEE======EEE======EEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwww†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– –––‘‘‘fff^^^ªªª°°°°°°wwwffffffpppwwwwwwwwwppppppwwwpppwww†††fff‘‘‘‘‘‘wwwfffppp‘‘‘†††–––––– çççýýýýýýýýýýýýýýýýýýýýýýýý×××NNNGGGGGGVVV^^^www°°°†††VVVfffpppÇÇÇßßß××× fff^^^^^^VVV^^^×××ýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ NNN======333333------===333333333333333333---""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""---"""---GGGffffffffffff^^^ffffff^^^fffffffff^^^fff^^^fffffffffVVVNNN^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppfffffffffppppppfffppppppfffpppfffpppppppppwwwpppwwwpppwwwpppwwwwwwwwwpppppppppfff^^^NNNEEEEEE============EEE===EEE=========EEE –––††††††††††††††††††††††††‘‘‘†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––‘‘‘ppp^^^°°°ÁÁÁ–––wwwffffffpppwwwwwwppppppwwwpppfffppp^^^††††††‘‘‘wwwppp†††–––‘‘‘‘‘‘‘‘‘‘‘‘ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïfffEEEGGGfff†††www NNNVVVppp¶¶¶ïïïÁÁÁªªª–––www^^^NNNNNN ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ NNN===333------""""""---333333333---""""""""""""""""""""""""""""""""""""""""""---"""---""""""---"""""""""---""""""""""""""""""---"""""""""---""""""---""""""""""""---""""""""""""---"""---"""""""""---VVV^^^ffffffffffff^^^fffffffff^^^ffffff^^^fff^^^ffffffNNNNNN^^^fff^^^ffffffffffffffffffpppffffff^^^^^^fffffffffffffffpppffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffffffffppppppfffpppfffppppppppppppppppppwwwpppwwwwwwwwwwwwpppwwwppppppwwwppppppfffNNNEEE======EEE===EEEEEE======EEEEEE===‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††††††††††††††wwwwwwwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––‘‘‘fff^^^†††ªªª¶¶¶¶¶¶wwwffffffppppppppppppppppppffffffVVVppp‘‘‘‘‘‘‘‘‘pppwww‘‘‘‘‘‘–––wwwªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁNNNNNNppp¶¶¶–––pppGGGNNNppp‘‘‘××× °°°†††^^^NNNNNNpppßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ VVV===---"""---------"""---------""""""""""""""""""""""""""""""""""""---"""---"""------------"""""""""""""""""""""---""""""""""""---""""""---""""""---""""""""""""""""""---333===---"""""""""333^^^ffffff^^^fff^^^fffffffffffffff^^^ffffff^^^ffffff^^^GGG^^^^^^fffffffffffffffpppffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppppppppwwwwwwpppfffpppppppppppppppfffNNNEEE======EEE===EEE===EEE===EEE===‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––– –––†††wwwfff^^^www¶¶¶ÁÁÁªªªwwwffffffppppppppppppppppppfffNNNVVV‘‘‘†††‘‘‘fffVVVwww†††–––†††ppp‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýçççfffGGG××׆††‘‘‘wwwNNNGGGpppfff‘‘‘††† www^^^VVVVVVNNNÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶ NNN===---"""---333---""""""------""""""""""""""""""---""""""---"""------"""---"""------333======---""""""---""""""---""""""---"""---""""""---""""""---"""------"""---333333333===333---""""""---===^^^^^^ffffff^^^ffffff^^^fff^^^ffffff^^^ffffffffffffNNNGGG^^^^^^ffffff^^^fffffffffffffffffffff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffffffffpppffffffffffffpppffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppwwwppppppfffpppfffpppfffpppppppppfffNNNEEEEEE=========EEE===EEE======†††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––– –––––––––‘‘‘†††fff^^^wwwªªª¶¶¶ªªªffffffpppppppppppppppfffVVVNNNVVV†††wwwpppGGGNNNfffwwwwww^^^†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïpppNNN ×××¶¶¶fff†††wwwNNN^^^VVVVVV†††ªªª–––wwwVVVNNNNNNGGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶‘‘‘GGG333"""------333---"""""""""---"""---"""------""""""---"""------333------333===---======EEEEEE""""""---""""""---""""""---""""""""""""---""""""---------333===---333EEEEEE===EEE===---"""---"""EEE^^^^^^^^^^^^ffffffffffff^^^ffffff^^^ffffff^^^fffVVVNNNVVVffffff^^^ffffffffffffpppffffffffffff^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffff^^^ffffffffffffpppffffffpppfffpppffffffpppfffpppppppppppppppfffpppppppppwwwwwwpppwwwwwwpppfffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††www††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††fffVVV†††°°°ÁÁÁ¶¶¶ffffffpppfffpppwwwppp^^^NNNNNNfffpppEEEEEEGGGVVVppp^^^pppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççfff^^^¶¶¶ßßßÎÎÎwwwpppVVVVVVªªª°°°†††wwwVVVEEEEEEEEE^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°†††EEE------333---------"""---"""------"""---------------"""------"""---------333333======EEE===EEE======EEE---"""""""""---"""---""""""---""""""---""""""---333EEE333EEE===333======333333EEEEEE---""""""---GGG^^^fffffffff^^^fffffffffffffff^^^fff^^^^^^fffVVVNNN^^^^^^fffffffffffffffpppffffffffffffffffff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffpppfff^^^ffffffppppppfffpppffffffpppppppppppppppwwwwwwwwwwwwpppfffffffffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE=========†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘fffVVVwwwÁÁÁ¶¶¶°°°www^^^ppppppfffwwwwwwfff^^^NNNGGG^^^pppVVV======GGGNNNfff†††–––ïïïýýýýýýýýýïïïïïïýýýýýý×××VVV^^^ÁÁÁßßßçççÇÇÇppp†††www––––––°°° wwwfffNNNEEE======GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°www===------333------------"""---------------333===333---------------===333333EEE===EEE333GGGEEEEEE=========""""""---"""---""""""---"""---"""---"""---------===EEE333GGG333======333333========="""---"""---NNN^^^^^^^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffffpppffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffff^^^ffffffppppppffffffppppppfffpppfffpppwwwwwwwwwwwwpppffffffffffffffffffffffffffffffpppfffppppppfffNNNEEE===EEE============–––‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††wwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘www^^^VVVwwwªªªªªª–––ffffffpppfffpppwwwfff^^^pppGGGNNN^^^GGG===EEENNN^^^ppp†††‘‘‘†††ßßßïïïýýýïïïÎÎÎ×××çççïïï GGG^^^ÁÁÁçççßßßßßß°°°†††pppppp‘‘‘ªªª–––fffVVVGGGEEEEEE===EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªfff333---------------"""---------------"""---======333333333===333EEE===333===GGG===EEE===EEE===EEE---333333---""""""---"""---"""---""""""---"""333======---EEE===---GGG333EEEEEE===333===333---"""""""""333VVV^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^fff^^^fffffffffffffffffffffffffffpppfffffffffffffffppp^^^^^^ffffffpppfffpppfffpppfffpppfffpppppppppwwwwwwwwwwwwfffffffff^^^^^^^^^fff^^^fffpppfffffffffpppfffppp^^^NNNEEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††fffVVVfff‘‘‘ªªª^^^pppfffpppfffppppppppppppNNNVVVVVVNNNEEEEEENNNpppwww†††wwwÇÇÇïïïççççç窪ª ßßßÇÇÇpppEEEVVVÁÁÁççççççççç××ב‘‘www^^^VVVfffpppfffVVVGGGEEEEEEEEE======pppïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß ^^^333---"""------"""333------------------333GGGEEE=========EEE333EEEEEE===EEEEEE===GGG======---333---"""---"""---"""---""""""---"""333===333333======GGG===EEE333333EEE---EEEEEE==="""---"""---"""""""""333^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fffffffff^^^fff^^^fff^^^fffffffffpppffffffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffpppfffpppffffffpppfffpppfffpppppppppwwwppppppfffffffff^^^^^^ffffffffffff^^^ffffffffffffpppfffffffff^^^NNNEEE============†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘wwwfffVVVppp°°°ffffffpppfffffffffpppppp^^^^^^^^^NNNNNNGGG===GGGfffwwwfffÇÇÇïïïççççççÁÁÁ°°°ÎÎÎ ^^^EEEGGG¶¶¶çççïïïïïïßßß‘‘‘^^^fffNNNNNNNNNVVVGGGEEE======EEEEEE===NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªªNNN------------"""---333EEE333333===EEE===333GGG===EEEEEE===GGG333===EEEEEE===EEE---333333---"""---"""---"""---"""---"""---333=========GGGEEE333EEE333GGG333GGG333333GGG333---------""""""""""""""""""===^^^^^^fff^^^^^^^^^VVVVVV^^^fffffffffffffff^^^^^^fff^^^ffffffffffffffffffpppfff^^^^^^fffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fffpppfffpppffffff^^^ffffffffffff^^^ffffffpppffffffffffffpppfffpppfffppppppppppppppppppfffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffNNNEEE=========‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††wwwfffVVVfff^^^fffpppfffffffffpppfff^^^VVVVVVVVVGGGEEE===EEENNN‘‘‘––– –––×××ïïïßßßçççÇÇǶ¶¶ ^^^EEEGGGwwwÎÎÎïïïïïïççç–––VVV^^^^^^NNNGGGGGGEEE==================EEEÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG------"""---------EEEGGG===EEEEEEEEEEEE===GGG===GGGEEEEEEEEE===EEE===333333------------"""---"""---"""---"""---333333333EEE===GGG===EEE===EEEEEE===EEE333GGG===---------"""""""""""""""""""""GGG^^^^^^^^^fff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffffffffpppfff^^^pppfffpppfffffffffpppffffffpppfffpppfffppppppppppppfffffffffpppfffffffffffffffpppppppppppppppppppppppppppffffffppppppfffNNNEEE======‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††wwwfffVVV^^^fffpppfffpppffffffppp^^^NNNNNNGGGEEEEEE======EEEfff°°°¶¶¶¶¶¶çççïïïßßßÎÎΰ°°‘‘‘ ÎÎÎpppNNNGGGfff ßßßçççççç°°°VVVGGGffffffNNNEEE===333333333333333333EEE°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG---"""------------EEEGGG===GGG===EEEEEE===GGG333GGGGGGEEE===---333------------"""------===------------------=========EEEEEE333GGG333EEE===EEEGGGEEE333---------"""---""""""""""""""""""""""""NNN^^^fff^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^ffffffpppffffff^^^fff^^^ffffff^^^^^^fff^^^^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppfffffffffffffffffffffpppfffffffffffffffpppppppppwwwpppppppppppppppppppppffffffNNNEEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††fffVVVVVVfffppppppfff^^^fffpppVVVNNNGGG===============NNN ªªª°°°ÎÎÎçççÁÁÁ–––––––––ªªª×××–––^^^GGG^^^–––ÁÁÁççççççÁÁÁfffEEEVVVwwwVVVGGG===333333333333===–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––EEE------------------EEEGGG===GGG===GGGEEE===EEE333333===333---------------333333------EEEGGG333333===EEE===333GGG---333EEEEEE===GGG333GGG333333===333---"""---""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffff^^^ffffffffffffpppffffffffffffffffffffffffpppfff^^^fffffffff^^^fff^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppffffffffffffffffffffffffpppfffppppppfffpppffffffffffffffffffffffffffffffffffffpppfffpppppppppwwwwwwwwwwwwppppppwwwppppppffffffGGGEEE‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††fffNNN^^^ppppppfffffffffpppfffpppVVVEEEEEE333===EEEVVV†††‘‘‘ªªªÎÎζ¶¶†††‘‘‘ °°°‘‘‘pppVVVVVV†††ªªª×××çççÇÇdž††GGGGGGppp¶¶¶ÇÇÇ pppNNNEEE333333333===wwwïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΑ‘‘===------------------GGGGGGEEEGGG333EEE===333333------------------"""---333GGGEEE---333GGGNNN333======GGG======EEE------GGG======EEE---===---"""------"""---"""""""""""""""""""""""""""""""""333VVV^^^^^^^^^fff^^^fffffffff^^^^^^^^^VVV^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffffppppppffffffpppfffpppfffpppfffppppppfffppppppffffffffffffffffffffffffffffffffffffpppfffpppppppppppppppwwwppppppwwwwwwppppppfffpppfff^^^GGG†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††fffNNNVVVpppppppppfffpppppp†††ªªªwwwGGGEEE===EEEfff†††††††††°°°×××××× †††††††††–––†††^^^VVVpppªªªÎÎÎßßßÎÎΖ––NNNVVV°°°ßßß×××°°°–––wwwGGG===333===333^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††===------------------GGGEEE333===333---------------------------"""------===NNNGGG333===NNNGGG333======GGG===EEEEEE------EEE333333333------"""---"""""""""""""""""""""""""""""""""""""""""""""EEE^^^^^^^^^^^^^^^ffffffffffff^^^fffVVVVVV^^^^^^ffffffffffffpppffffffffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppfffpppfffffffffffffffffffffpppppppppffffffpppffffffpppfffpppffffffffffffffffffffffffpppppppppppppppwwwpppppppppppppppfffppppppfffpppfff^^^†††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††fffVVVVVVppppppffffffppp‘‘‘¶¶¶ ^^^GGGEEEEEE^^^‘‘‘°°°–––ªªªÁÁÁ×××°°°‘‘‘‘‘‘†††–––°°°‘‘‘ppp–––ppp–––ÁÁÁßßßÎÎΖ––^^^www×××ßßß×××°°°°°°wwwNNNEEE=========VVVïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ333---------"""------333333---------------------------------------------===NNNNNN===EEEGGGGGG333======GGG===EEEEEE------333------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""EEEVVV^^^^^^ffffff^^^fff^^^^^^ffffffVVV^^^^^^fff^^^ffffffffffffffffff^^^ffffffffffffppp^^^ffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppfffpppffffffpppffffffffffffppppppfffpppfffpppfffffffffpppfffffffffffffffffffffffffffffffffppppppppppppppppppppppppppppppfffpppppppppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††pppVVVVVVpppffffffppp†††ªªª VVV^^^wwwVVVfff ††† ÎÎζ¶¶ ––– ‘‘‘¶¶¶ÁÁÁ‘‘‘ÇÇÇpppwww–––×××¶¶¶–––°°°ßßßçççÎÎΰ°°°°°VVVEEE=========NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ333---------------------------------------333===------------======------EEEGGGNNNGGGEEEGGGEEE===GGGEEEEEE---===333"""---"""---"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffffpppfffffffffpppffffffpppfff^^^ffffffpppfffppppppfffpppfffffffffpppfffpppppppppfffffffffffffffffffffffffffffffffppppppppppppwwwpppfffpppppppppppppppwwwpppppp†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††pppVVVVVVpppffffffwww†††‘‘‘^^^wwwªªªwwwppppppwwwªªªÁÁÁ¶¶¶ªªª–––††† ÁÁÁ××× °°°^^^°°°ªªª°°° ÇÇÇçççßßßÇÇÇ ªªªwww^^^GGGEEE======GGG×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇwww333---------------------------------------======333===============------EEEEEEGGGNNN===GGGEEE===EEE===333---"""------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---NNN^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffpppfffpppffffffpppffffffppppppfffpppfffpppffffffffffff^^^fffffffffppppppfffpppffffffppppppppppppwwwppppppfffppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††††††††pppVVVVVVffffffwww†††ppp–––†††pppppp‘‘‘pppªªª¶¶¶ªªª‘‘‘‘‘‘‘‘‘–––ÎÎζ¶¶‘‘‘wwwNNNfff ÁÁÁ–––×××çççßßßÁÁÁ –––www^^^GGGGGGEEE===GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁfff333---------------------------------333333EEEEEE===EEE======GGG===------EEE===EEEEEE333EEE333------------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---VVV^^^fff^^^fff^^^^^^ffffff^^^^^^^^^fff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffppp^^^fffffffffffffffffffffpppffffffpppfffffffffpppfffffffffpppfffpppfffpppffffffffffffffffffppppppfffppppppfffpppfffpppfffffffff^^^^^^^^^fffpppfffpppffffffffffffpppppppppwwwppppppfffffffff‘‘‘†††‘‘‘‘‘‘†††‘‘‘–––––– ‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††www†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††pppVVVNNNfffwww‘‘‘^^^www‘‘‘fffppp†††ªªª†††ªªª°°°ªªª‘‘‘‘‘‘ ‘‘‘–––×××ÇÇÇpppVVVVVVppp °°°×××çççßßß¶¶¶ ‘‘‘www^^^VVVGGGEEE===EEE¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶^^^------------------------------------===EEEGGGEEEEEE=========EEE333------GGG333---333------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""333^^^fffffffff^^^fffffffff^^^^^^^^^ffffff^^^fff^^^fffpppffffffffffffffffffffffff^^^^^^^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffpppffffffpppffffffpppfffpppfffffffffpppffffffffffff^^^^^^fffffffffpppffffffpppppppppfffpppfffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppwwwpppfffffffffffffff†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††fffNNNNNN^^^www‘‘‘fffGGGVVVªªªwwwfffwww‘‘‘ –––––––––ÁÁÁÁÁÁ –––ÁÁÁªªª†††pppVVVpppªªª wwwÇÇÇçççÎÎΪªªªªª‘‘‘pppVVVNNNGGGEEE===EEEªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------333EEE===EEEEEEGGG===333===333---------333---------"""------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""EEE^^^ffffff^^^fffffffff^^^fffffffff^^^fff^^^^^^ffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffff^^^ffffffpppffffffpppfffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffff^^^fff^^^pppfffppppppfffpppppppppppppppfffpppffffff^^^ffffffpppfffffffffffffff^^^ffffffpppppppppppppppfff^^^fffppp†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––––––––––––––––––‘‘‘––––––––––––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††wwwfffNNNNNNwww†††GGGNNN–––†††ppp^^^fff‘‘‘wwwppp–––ßßßçççÎÎΰ°°°°°ªªª‘‘‘‘‘‘‘‘‘VVVppp ppp°°°ÁÁÁÁÁÁ°°°¶¶¶‘‘‘^^^NNNGGGEEE======EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------===EEEEEE===333EEE333------------------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""GGGfff^^^^^^ffffff^^^ffffff^^^fff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^^^^fffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffppppppffffffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffpppfffpppppppppppppppppppppffffffffffffffffffppppppfff^^^ffffffffffff^^^ffffffppppppppppppffffffpppfff‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– ––––––––––––––––––‘‘‘‘‘‘––––––––––––––– ––– ––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††www††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘†††††††††††††††††††††www^^^VVVpppwwwVVVVVVppp†††pppVVVNNNpppwwwfffVVV‘‘‘ßßßýýýïïïÎÎζ¶¶–––‘‘‘–––†††VVVfff†††–––ppp†††ÁÁÁ××××××¶¶¶^^^NNNGGGEEE======EEE ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªGGG---------------------------------333EEE===333------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---NNN^^^ffffff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffffffffpppfffpppfffffffff^^^^^^^^^ffffffpppfffppppppfffpppfffpppfffpppfffpppfffffffffffffffpppfff^^^^^^ffffffffffffffffff^^^ffffffpppppppppppppppppp‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––––––‘‘‘–––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††wwwVVVNNNfff^^^NNNNNN^^^^^^NNNEEENNNfff^^^^^^×××ýýýýýýççç××תªª¶¶¶ wwwffffffppp‘‘‘fff ßßß×××ÇÇǶ¶¶^^^GGGGGGGGGEEE======‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªEEE------------------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------"""""""""------"""""""""""""""""""""""""""""""""---VVVfff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffff^^^ffffff^^^ffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppppppppffffff^^^^^^ffffffppppppffffff^^^ffffffpppfffpppfffpppfff‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––––– ––––––––––––––––––‘‘‘––– ––––––‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††pppVVVNNN^^^^^^NNNNNN^^^GGGEEEGGG^^^†††www‘‘‘¶¶¶çççïïïýýýçççÇÇÇÁÁÁ†††wwwppp^^^wwwfff°°°ÇÇÇÁÁÁ°°°ªªª‘‘‘^^^GGGGGGGGG======EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß–––EEE---------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------===333---"""------------"""""""""""""""""""""---"""""""""333^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppffffffpppfffpppffffffffffff^^^^^^fff^^^fffpppffffffffffffpppfffpppfffpppfffffffffpppffffffpppffffffVVVNNNVVVfffpppfffpppfffffffff^^^fffffffffpppfffffffff––––––‘‘‘–––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––––––––––––––– ––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††ppp^^^VVV^^^NNNNNNVVVNNNEEEGGGwww‘‘‘www‘‘‘¶¶¶ÎÎÎçççýýýýýýçççÁÁÁ†††ªªªÁÁÁwwwpppppp°°°ÇÇǰ°°––––––†††fffGGGEEE=========EEE†††ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß‘‘‘===------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333EEEEEEEEE---EEE===333333333333---"""""""""""""""---333===333"""""""""===^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffpppffffffpppfffpppffffffffffffpppfffffffffppppppfffpppfffpppfffpppffffffpppfffpppfffffffff^^^^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffffffffppppppffffff^^^GGG===NNNfffpppfffpppffffff^^^^^^^^^fff^^^fffpppffffff‘‘‘–––––––––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––––––––––––––––––––––– ––– –––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††wwwVVVVVVVVVNNNGGGGGGGGGEEEfff††††††ÇÇÇßßßßßßïïïýýýýýýÎÎΖ––‘‘‘ ¶¶¶–––°°°¶¶¶ªªª¶¶¶ªªª–––†††pppGGG============EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===---------------------------------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------"""---333EEENNN^^^^^^NNN333GGG===EEENNNNNNEEE---""""""------333EEEEEEGGG==="""""""""EEEVVV^^^fff^^^^^^fff^^^fff^^^ffffff^^^^^^ffffffffffffffffffffffff^^^ffffffffffffffffffpppfffffffffffffffffffffpppfffffffffffffffffffffppppppfffffffffffffffppppppfffffffffffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffpppfff^^^GGG===EEEffffffppppppffffffffffffffffffffffffpppfffffffff‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––– ––– ––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘††††††††††††wwwVVVNNNGGGGGGGGGGGGEEEGGG^^^www‘‘‘°°°ÎÎÎ×××ïïïýýýýýýççç××תªª ¶¶¶‘‘‘ªªª¶¶¶ ªªª–––‘‘‘pppfffGGG============EEE‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGGGGGEEEGGGNNN^^^fff^^^^^^GGG333333333GGG^^^VVVEEE------333===GGGGGGEEEEEE333---"""""""""GGGVVV^^^^^^^^^^^^^^^^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppfffffffffffffffpppfffffffffpppffffffpppfffppppppfffffffffffffff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffffffffppppppffffffGGG333===^^^pppfffpppfffpppffffffffffffpppfffffffffpppffffff–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––– ––– ––– ––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††pppVVVGGGEEEGGGGGGGGGEEEEEENNN ÁÁÁÁÁÁïïïýýýýýýýýý×××°°° ‘‘‘†††††† –––wwwwwwfffVVVGGGEEE=========EEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††333---------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333333EEE^^^VVVVVVVVV^^^^^^^^^VVVEEE===333---333EEENNNGGG======GGGGGGNNNGGG===---"""---"""""""""---GGGVVVVVVVVVVVV^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffppppppfffpppfffpppffffffpppfffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppffffffNNN===EEEVVVppppppppppppffffffffffffffffffpppffffffpppfffffffff‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––––– ªªª ––– ––– ––– ––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††‘‘‘‘‘‘†††wwwpppVVVEEEGGGGGGGGGGGGGGGGGGfff°°°¶¶¶¶¶¶ßßßýýýýýýýýýïïïßßßÇÇÇ–––‘‘‘www^^^fff††††††pppVVVNNNGGG===EEEEEEEEEEEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎwww333------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---333===NNNNNNVVVfff^^^fffNNNGGGGGGEEE======333333333===EEENNNNNNEEEGGGNNNGGG333---"""""""""""""""""""""---NNNNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffpppffffff^^^fffffffffffffff^^^fff^^^ffffffffffffffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppfffppppppffffffffffffffffff^^^ffffffpppffffffpppfffpppfffpppfffffffffffffffffffff^^^ffffffffffffpppVVV===EEE^^^pppppppppppppppffffffpppfffpppffffffpppfffppppppfffppp‘‘‘‘‘‘––––––––––––––––––––––––––– –––––––––––– ––– ––– –––––– –––––– ––––––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††wwwpppVVVGGGGGGNNNNNNGGGGGG^^^–––°°°ÇÇǶ¶¶ïïïýýýýýýïïïïïïçççÎÎΪªªpppfff†††ppp^^^NNNGGGGGG===NNNVVVVVVGGGªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇppp---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""333===GGGGGGVVVVVV^^^^^^VVVGGG===333333333333333333======EEEGGGVVV^^^NNNEEEGGG===---"""""""""""""""""""""""""""---NNNNNNVVVVVVVVV^^^^^^^^^^^^^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppffffffffffff^^^fffpppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffffffffpppVVV======VVVpppfffpppppppppppppppfffpppfffpppfffffffffppppppfffpppfff–––––– ––– ––– ––– ––– –––––– –––––– ––– ––– ––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwpppVVVGGGNNNGGGNNNGGGNNNppp–––ÁÁÁªªª×××ýýýýýýïïïýýýïïïïïïßßߪªªÎÎζ¶¶wwwfffVVVNNNGGGEEE===NNNwwwwwwNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ^^^"""""""""""""""""""""""""""""""""""""""""""""""""""---""""""---""""""EEE======EEENNNVVVVVVNNN^^^VVVEEEEEE------"""------------333======GGGNNNVVVNNNGGGEEE333333---333======"""""""""""""""""""""333GGGNNNNNNVVVVVV^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^fffffffff^^^fffffffffffffff^^^ffffffpppfffffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffffffffffffff^^^fffpppfffpppfffpppfffpppfffpppffffffffffffffffff^^^ffffffffffffffffffGGG===VVVfffpppfffpppfffpppfffppppppfffpppfffpppfffpppppppppppppppfff–––––– ªªª ––– ––––––––– –––––– ––– ––– ––– ––– ––––––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††wwwwwwVVVGGGNNNNNNNNNGGGNNNpppªªª¶¶¶ ßßßýýýýýýïïïýýýïïïßßß¶¶¶×××ßßß¶¶¶‘‘‘fffNNNNNNGGG======GGGfffNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN"""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVVVVVVVV^^^^^^^^^NNN===333333""""""""""""""""""---"""333GGGNNNNNNNNNGGGEEE333---------333EEEEEEEEEEEE---""""""""""""""""""===GGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^fff^^^ffffff^^^°22ú��ú��ú��ú��fffffffff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffpppfffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffNNNEEENNNffffffpppfffppp^^^pppppppppppppppfffpppfffpppfffppppppppppppppp–––––– ––– ––– ªªª–––‘‘‘‘‘‘‘‘‘†††‘‘‘ ––––––––– ––– ––––––‘‘‘–––––––––––– ––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††wwwVVVGGGGGGNNNNNNGGG^^^†††ªªª–––¶¶¶ïïïýýýïïïýýýýýýçççÎÎÎÁÁÁÇÇÇßßßÎÎÎNNNGGGEEE======EEE^^^wwwVVVªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN""""""""""""""""""""""""""""""""""""""""""---""""""---""""""""""""---^^^fffVVV^^^fff^^^===---""""""""""""""""""------333"""===NNNGGGGGG===""""""------======EEENNNEEE------"""""""""""""""""""""EEEGGGGGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^ffffffRR÷ú��ú��ú��ú��fffffffffffffffffffffffffffpppffffffffffffpppfffffffffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffVVVNNNNNNfffffffffpppfff^^^^^^ppppppppppppppppppfffppppppffffffpppfffpppfff ––– –––––––––––––––––––––––––––––– ––– –––––––––––––––––– –––––– –––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††††††††††††††ppp^^^GGGNNNNNNNNNppp–––‘‘‘www×××ýýýïïïïïïýýýïïïßßß°°°ÇÇÇ×××××תªªVVVGGGEEE======EEENNNpppNNNªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÁÁÁGGG"""""""""""""""""""""""""""---""""""---""""""---"""""""""---""""""333VVVNNN===EEEVVVEEE---"""""""""""""""---333GGGGGGEEE---333333---""""""---333EEEEEEGGGEEE===333"""""""""""""""""""""EEEGGGGGGNNNNNNNNNNNNVVVVVV^^^^^^^^^fff^^^fffhddÞú��ú��ú��ú��ú��fffffffffffffffffffffffffffpppfffpppfffpppppppppffffffpppfffpppffffffpppfffpppfffffffffffffff^^^ffffffppppppffffffpppfffpppfffffffffpppfffffffffffffffffffffffffff^^^fff^^^VVVVVVfffffffffffffff^^^^^^fffwwwppppppppppppppppppppppppfffpppfffppppppppp–––––––––––– –––––– –––––– –––––– ––– ––– ––– ––– ––– ––––––––––––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘––––––––– ––– ––––––––––––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www^^^GGGGGGNNNppp–––ªªªwwwVVV‘‘‘çççýýýïïïýýýýýýçççÎÎΰ°°ÎÎÎ×××ÎÎÎÁÁÁªªªVVVEEE===EEEGGGGGGGGG–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶GGG""""""""""""---"""""""""---""""""---"""---""""""---"""---"""""""""---333---"""------""""""---"""---333===GGGNNNGGGGGG===---"""""""""---===EEEGGGGGG===333---""""""""""""""""""---EEEú��ú��GGGGGGGGGNNNNNNVVV^^^^^^^^^^^^ffffffµ77ú��çú��ú��ú��ú��fffppppppfffffffffppppppfffpppfffffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffffffffffffff^^^pppfffpppppppppfffpppffffffpppffffffffffffffffff^^^fffffffffffffff^^^^^^VVVffffffffffffffffffpppppppppppppppppppppppppppppppppfffpppfffpppfffpppfff–––––––––––– –––––– ––––––––––––––– –––––––––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––– –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††wwwwww†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††fffGGGNNNVVV††† †††^^^VVV¶¶¶ïïïýýýïïïýýýýýýßßßÇÇÇÁÁÁÎÎÎßßßßßßçççÇÇÇ^^^EEEGGGVVVEEE===ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE"""""""""""""""---"""---""""""---""""""---"""---"""""""""---"""---"""""""""""""""""""""---EEEEEEEEEEEEGGGGGG===333"""---333===333---EEEGGGGGGEEE333""""""""""""""""""""""""333EEEú��ú��GGGGGGNNNNNNVVVVVVVVV^^^^^^ffffffSS÷ø��‰MMú��ú��ú��ú��pppfffpppffffffpppfffppppppfffffffffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffpppffffffffffffpppfffppppppffffffpppfffpppfffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^fffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppppppppfffppppppfff–––––––––––– ––– ––– ªªª ––– ––– ––– ––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††www†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††wwwfffGGGNNN^^^–––°°°pppNNNppp×××ýýýýýýýýýýýýýýýïïï×××¶¶¶ÎÎÎÎÎÎÁÁÁ×××°°°†††www^^^EEEEEEwwwÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªª===""""""---"""---"""""""""---""""""------"""---"""---"""---""""""""""""""""""""""""---======NNNGGGNNN333333---""""""---333NNNNNNEEE---===333---""""""---EEE333"""""""""""""""""""""===GGGú��ú��GGGGGGGGGNNNVVVVVVVVV^^^^^^fffhddÞú��¾00fffú��ú��ú��ú��ppppppfffpppfffppppppffffffffffffppppppffffffppppppffffffppppppppppppffffffpppffffffffffffffffffpppfffppppppfffpppffffffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffppppppppppppfffppppppppppppfffpppppppppfffppppppfffpppppp‘‘‘–––––– ––––––––––––‘‘‘‘‘‘††††††wwwwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††www^^^NNNVVV–––ªªª†††NNNVVV‘‘‘çççýýýïïïïïïýýýýýýçççÁÁÁ ¶¶¶ÇÇÇ××××××ßßßÁÁÁwwwNNNNNNwwwÇÇÇçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""---""""""---"""---"""---"""---------333------------------"""------333---EEEGGGNNNGGG===---"""""""""---===EEENNNNNNGGG==="""""""""---======GGGNNNEEE"""""""""""""""""""""EEEEEEú��ú��GGGGGGNNNNNNNNNVVVVVV^^^^^^fff°22ú��ækbbfffú��ú��ú��ú��pppfffpppfffppppppppppppfffpppppppppfffpppfffppppppfffpppfffpppppppppfffffffffpppffffffffffffpppfffpppfffpppfffppppppfffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVVVVV^^^^^^ffffffffffff^^^fffpppppppppfffpppfff^^^^^^ffffffffffffpppfffppp‘‘‘‘‘‘†††wwwpppffffff^^^^^^fff^^^ffffffpppfff^^^VVV^^^^^^^^^^^^^^^^^^fffpppwww†††‘‘‘‘‘‘†††††††††††††††††††††††††††wwwwwwwww†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwfffNNNfffVVVNNN^^^°°°ïïïýýýýýýýýýýýýïïï××תªª °°°ÎÎÎßßßßßß°°°wwwÇÇÇÁÁÁ×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––==="""---"""---"""---"""---"""---"""333=====================333333333EEEEEEGGGEEE---EEE===333"""""""""---333===GGGNNNEEEEEE333"""""""""---333EEEGGGGGGGGG==="""""""""""""""""""""---EEEEEEú��ú��GGGGGGNNNNNNVVVVVV^^^^^^^^^SSöø��ŠMMppppppú��ú��ú��ú��pppppppppppppppppppppfffpppfffppppppppppppffffffpppfffpppppppppfffpppfffffffffffffff^^^ffffffpppfffpppfffppppppfffppppppfffpppfffffffff^^^^^^fffffffff^^^fff^^^^^^^^^^^^VVV^^^^^^fff^^^fff^^^VVVNNNVVVfffppppppfffffffffppp^^^^^^^^^^^^fffffffffpppffffff^^^^^^^^^^^^^^^^^^ffffffppppppppppppwwwwwwpppppppppfffffffffffffff^^^^^^VVVfffwww‘‘‘†††wwwppppppfffpppppppppppppppppp†††wwwwwwwww†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††pppNNNVVVVVVNNNNNNNNNfffÎÎÎýýýýýýýýýïïïýýýïïïÎÎΰ°°ªªª–––ÎÎÎ×××ÇÇÇ‘‘‘¶¶¶¶¶¶ppp ªªªßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––333---"""---"""---"""---"""------===GGGGGGEEEEEE=========EEEEEEGGGNNNNNNGGGGGG==="""""""""""""""333EEEGGGNNNGGGEEE333---"""""""""------===NNNEEE===333"""""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^^^^fffàú��»++pppppppppú��ú��ú��ú��fffppppppfffppppppfffppppppppppppppppppfffpppfffpppfffpppppppppffffffffffffffffffffffffppppppfffppppppfffpppfffpppfffppppppfffpppfffVVVNNNffffffffffff^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^VVVVVVNNNNNN^^^ffffff^^^^^^^^^^^^VVV^^^VVVVVV^^^fffppppppVVVVVVVVVVVV^^^^^^^^^^^^fff^^^fffffffff^^^fffffffffffffff^^^fffffffffffffffpppppppppwwwpppfff^^^^^^^^^fff^^^fffffffffpppppp†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––– ––– ––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppVVVGGGNNNGGGNNNNNNßßßïïïýýýïïïïïïýýýïïïßßß×××°°°‘‘‘°°°ÎÎζ¶¶ÎÎÎÇÇÇpppwwwÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††---"""---""""""---"""---"""------GGG^^^NNNNNNGGGGGGGGGNNNVVVNNNNNNGGGEEE------""""""""""""333EEEGGGNNNEEE===333"""""""""---===EEE===---GGG333---""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^ffffffú��èlbbfffppppppú��ú��ú��ú��ppppppfffppppppppppppfffppppppppppppppppppfffpppfffpppfffpppffffffpppfffffffffffffffffffffpppfffpppfffpppfffffffffpppfffppppppffffffVVV^^^pppffffff^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^NNNNNNNNN^^^VVV^^^ffffffNNNEEE333333EEEGGGNNN^^^fffppp^^^^^^wwwwwwpppfffpppfffffffffffffffpppppppppppppppppppppwww††††††‘‘‘†††wwwppp^^^^^^^^^VVV^^^^^^^^^fff^^^pppppppppppppppwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––––––––– ––– ––– –––––––––––––––––––––––––––‘‘‘‘‘‘wwwVVVGGGGGGGGGNNNVVV–––ïïïýýýýýýýýýïïïïïïýýýçç窪ª††††††‘‘‘‘‘‘ÁÁÁÇÇÇ‘‘‘fffªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇwww------"""---"""---"""---"""---333VVVfffVVVVVVVVVfff^^^NNNGGGEEE333---"""""""""---333"""---GGGEEEEEE333"""""""""---333EEEEEEGGGEEE333---333""""""""""""""""""""""""""""""EEEEEEGGGú��ú��NNNVVVVVV^^^^^^ffffffffffffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffpppfffppppppfffppppppppppppfffppppppfffppppppfffffffffpppffffffffffffpppffffffpppfffpppffffffpppffffffppppppfffpppfff^^^fffpppfffpppfffffffff^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVV^^^VVV^^^EEE"""---"""---333NNNVVVfffpppffffff††††††††††††††††††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘††††††wwwwwwfffffffffVVV^^^VVVVVVVVV^^^^^^^^^^^^^^^ffffffpppfffppppppwwwwww††††††††††††‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘–––––––––––––––––––––––– ––––––––– ––– ––––––––––––‘‘‘wwwVVVGGGGGGNNNGGG^^^ÁÁÁïïïýýýýýýýýýïïïïïïïïïÎÎÎÁÁÁªªª†††–––ÇÇǶ¶¶†††°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎfff---"""---"""---------------===GGGppp^^^VVVfff^^^VVVGGG===""""""---======333===GGGEEE---"""===---"""""""""333===GGGGGGGGGNNN===---""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��VVV^^^^^^ffffffffffffpppfffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ppppppppppppppppppppppppppppppfffppppppfffpppfffpppfffpppfffffffffffffffpppfffppppppffffffpppfffpppffffffpppfffpppfffffffffppppppfffffffffffffffffffff^^^^^^^^^^^^^^^VVVNNNNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNEEE---""""""------NNN^^^ffffffffffffwwwpppwwwwwwwwwwwwfffppppppppppppwww†††wwwfff^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^ffffffpppwwwwwwwwwwww††††††††††††‘‘‘–––––––––––––––––––––‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––– ––– ––– ––– ––– ––––––––––––‘‘‘‘‘‘pppVVVGGGGGGNNNNNNpppÎÎÎýýýýýýýýýýýýïïïïïïýýýïïïßßß°°°––– –––°°°†††ÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff------"""------333===EEEEEEVVVfffwwwfff^^^^^^GGG---""""""""""""===NNNNNNGGGNNNGGG===---""""""""""""---EEEEEEEEEEEE========="""""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��^^^^^^fffffffffppppppppppppppppppppppppppppppú��ú��ú��ú��pppfffpppppppppppppppfffpppppppppfffpppfffpppppppppfffpppfffpppffffffffffffpppppppppppppppfffpppfffffffffpppfffppppppfffpppfffpppffffffffffffffffffffffff^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGG===333333---===VVVffffff^^^^^^ffffffffffff^^^VVVNNNNNNNNN^^^^^^^^^fffpppppppppfffpppffffffpppfff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppwwwwwwwww†††††††††‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– ªªª ––––––––––––‘‘‘–––‘‘‘†††fffVVVGGGGGGGGGNNN†††ßßßïïïýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁªªª††††††www–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇVVV------"""333GGGGGGNNN^^^^^^wwwppppppVVVNNNEEE333---"""333===---EEENNNNNN===333------"""---333333"""===EEE===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""===EEEGGGVVVú��ú��ffffffppppppppppppppppppppppppwwwwwwwwwppppppú��ú��ú��ú��fffpppppppppppppppppppppppppppppppppppppppfffppppppfffppppppffffffffffffppppppppppppppppppfffppppppfffffffffpppfffpppfffffffffppppppfffffffffffffffppp^^^^^^^^^^^^^^^fffVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNGGGNNNGGGGGGEEEEEEEEEGGGNNNVVV^^^ffffff^^^fffffffff^^^VVVNNNNNNNNNVVVVVVVVVffffffppppppfffwwwpppppppppfffpppfff^^^^^^^^^fffffffffpppppppppppppppwwwwwwwww†††‘‘‘‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ªªª ªªª ªªªªªª ªªªªªªªªªªªª ––– –––––––––––––––‘‘‘‘‘‘†††wwwNNNGGGGGGNNNVVV–––çççýýýýýýýýýýýýýýýýýýýýýïïïïïïÎÎΖ–– –––‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÇÇÇNNN---"""---333VVVNNNVVV^^^ppp^^^^^^NNN===NNNGGGEEEEEEEEEGGGNNN333---333""""""""""""EEEEEEGGGNNNGGG---"""---"""""""""""""""---""""""===333"""""""""""""""""""""333GGGNNN^^^fffú��ú��ppppppppppppppppppppppppwwwwwwwwwppppppppppppú��ú��ú��ú��pppppppppppppppppppppwwwppppppfffppppppppppppfffppppppfffpppfffffffffpppppppppppppppppppppfffpppfffpppfffpppfffpppfffffffffpppffffffffffff^^^^^^ffffff^^^^^^fff^^^fff^^^^^^^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGGGGGNNNVVV^^^^^^fff^^^ffffffVVVNNNNNNVVVVVVVVVVVVVVV^^^fff^^^VVVVVV^^^^^^fffffffff^^^fff^^^^^^ffffffppppppwwwwwwwwwpppwwwwwwwww†††††††††‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ––– ––––––––––––‘‘‘‘‘‘†††pppVVVGGGGGGNNN^^^¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎ××תªª‘‘‘‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÁÁÁGGG------"""333GGGGGGEEEEEENNNNNNNNNNNNGGG^^^fffNNNEEENNNGGG===""""""---333---===EEENNNNNNEEE333333""""""""""""""""""""""""333333"""===EEE333""""""""""""""""""333GGG^^^fffppppppwwwpppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppppppppfffppppppffffffpppfffppppppffffffffffffpppppppppwwwppppppppppppfffpppfffppppppfffffffffppppppffffff^^^^^^fff^^^ffffff^^^^^^^^^^^^ffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGEEEEEENNNVVVVVVVVV^^^^^^ffffffppp^^^VVVVVVVVVVVVVVVfffffffffpppfff^^^^^^^^^ffffffpppppppppwwwppppppppppppwwwwww††††††‘‘‘––––––––––––––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘––– ––––––‘‘‘‘‘‘–––––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘††††††pppVVVGGGGGGNNNfffÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýççç×××ÁÁÁÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE---""""""---333""""""333VVVNNNVVVfffffffff^^^NNN===---"""""""""333EEEVVVGGGGGGNNN===333"""""""""""""""""""""EEE===GGGEEE"""GGGNNN333"""""""""""""""""""""---===NNNfffppppppwwwwwwpppwwwwwwwwwwwwpppwwwwwwwwwpppwwwpppppppppwwwwwwppppppppppppppppppwwwpppppppppppppppfffpppppppppfffpppppppppfffpppffffffffffffppppppppppppppppppppppppfffppppppfffffffffpppffffffpppffffffppp^^^VVV^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^VVVVVVNNNNNN===GGGNNNNNNVVVVVV^^^^^^ffffffffffffVVVVVVNNNVVVVVV^^^pppfffpppppppppppppppfffpppppppppwwwwwwwwwppppppwwwwww†††††††††‘‘‘‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––––––––––– ––– –––––– ––– ––– ––– ––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªª°°°°°°ªªªªªªªªª –––‘‘‘‘‘‘‘‘‘††††††wwwVVVGGGNNNNNNwww×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççççççßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß°°°==="""""""""""""""""""""---VVV^^^^^^fff^^^VVVEEE333""""""---333===GGGGGGGGGGGG333---"""""""""""""""""""""""""""---NNNGGGVVVEEE---GGGGGG---""""""""""""333===GGG^^^ffffffpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppwwwpppppppppppppppfffppppppppppppppppppfffpppfff^^^ffffffppppppppppppppppppfffpppppppppffffffpppffffffpppfffpppfffpppffffffVVV^^^fffffffff^^^^^^fff^^^fffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffpppfff††††††wwwwwwppp^^^fff^^^ffffffwwwwwwwwwwwwpppppppppwwwwwwwwwwww†††††††††††††††††††††‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘–––––– ––––––––––––––– ––– ––– –––––– ªªª ªªªªªªªªª°°°ªªª ªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘†††www^^^GGGGGGVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""""""""""""""---EEEGGGNNNGGGGGG===333---======EEENNNGGG===------""""""""""""""""""""""""""""""333""""""---"""EEENNNEEEEEE---"""===---"""""""""---===EEENNNVVVffffffppppppppppppwwwpppwwwwwwpppwwwpppwwwppppppwwwpppwwwwwwwwwwwwpppppppppppppppppppppwwwpppwwwpppppppppppppppfffpppppppppfffppppppffffffffffffpppppppppppppppppppppppppppfffpppffffffppppppffffffpppfffpppffffffVVV^^^^^^fffppp^^^^^^ffffff^^^^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVfff^^^VVVVVVVVVVVV^^^^^^ffffffpppfffwwwppppppfffpppppppppwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘†††††††††‘‘‘––––––––––––‘‘‘–––––––––––– ––– ––––––†††‘‘‘††††††††††††‘‘‘–––––– ––– ––––––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª°°°°°°ªªªªªª ªªª –––––––––‘‘‘‘‘‘www^^^GGGNNNVVV çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––333------""""""""""""""""""---"""333333333EEEEEEEEEGGGVVVGGGEEE---"""""""""""""""""""""""""""""""""---333GGG333"""333"""---333"""""""""""""""""""""---===GGGNNN^^^ffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppwwwwwwpppwwwwwwpppwwwpppfffppppppppppppfffpppfffpppfffffffffwwwppppppppppppppppppppppppfffpppppppppppppppppppppfffpppfffpppfff^^^VVVfffffffff^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVV^^^VVVVVV^^^fffpppfffffffffpppwwwwwwwwwwwwwwwpppwwwppppppwww††††††††††††††††††††††††‘‘‘†††‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘††††††††††††††††††––– ––– –––––– ––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªªªªªªªª ––– ‘‘‘†††www^^^NNNNNN^^^°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘---------""""""""""""""""""""""""------===EEENNNGGGEEE333---"""""""""""""""""""""""""""""""""333333EEENNNNNN"""------""""""""""""""""""333EEEGGGVVV^^^^^^fffpppffffffpppfffpppppppppppppppppppppwwwpppwwwppppppppppppwwwpppwwwppppppppppppfffpppwwwwwwpppwwwwwwwwwppppppppppppfffpppppppppfffpppppppppffffffpppwwwppppppppppppppppppppppppfffppppppfffpppppppppfffpppfffpppfff^^^VVV^^^fffffffff^^^^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^VVVVVV^^^fffffffffffffffwwwwwwppppppwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘ ––– ––– ––– ––– ªªª ªªª ªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª ––––––‘‘‘†††fffNNNNNNfffÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333---""""""---"""333EEE======------"""===EEE===333"""""""""""""""""""""""""""333"""""""""EEENNNVVVGGGEEEEEE"""""""""""""""""""""---===EEENNNVVV^^^^^^ffffffpppffffffpppppppppppppppppppppppppppppppppwwwppppppppppppppppppwwwppppppppppppfffpppwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppffffffppppppwwwppppppppppppppppppfffppppppppppppppppppffffffpppfffpppfff^^^NNN^^^ffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffppppppffffffwwwpppwww†††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘††††††††††††††††††––– ––– ––– ––– ––– ––– ªªªªªª ªªª ªªª ªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°ªªªªªª ––––––‘‘‘fffNNNGGGpppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333------=========NNNVVVVVVEEE""""""---------"""""""""""""""""""""---"""===NNNEEE==="""NNNNNNGGGEEE---"""""""""""""""---===GGGNNNVVV^^^^^^^^^ffffffpppfffffffffpppppppppffffffpppfffppppppppppppppppppppppppppppppwwwpppppppppwwwppppppwwwpppwwwppppppppppppfffppppppppppppppppppfffppppppfffffffffppppppwwwppppppppppppppppppppppppfffpppppppppfffppppppfffpppfffffffffNNNVVV^^^fffffffff^^^^^^fff^^^^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^fff^^^^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffpppffffffffffff†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††www†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘†††††††††††††††††††††‘‘‘––– ––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°ªªª°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªª –––†††wwwfffVVVNNN×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp333333NNNVVVVVV^^^fff^^^GGG---""""""""""""""""""""""""""""""""""""333EEEGGGVVVVVV==="""EEE---""""""""""""""""""333===GGGVVVVVV^^^^^^^^^^^^fffffffffffffffpppfffpppfffppppppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwpppppppppfffppppppppppppppppppfffppppppfffpppffffffpppwwwpppwwwpppppppppppppppwwwpppppppppppppppppppppffffffpppfffffffffVVVNNN^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^ffffffffffffpppfffffffff‘‘‘––––––††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘††††††††††††††††††‘‘‘––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª¶¶¶°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°° ––––––‘‘‘†††pppVVVNNN†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß×××ppp333NNNpppfffNNNVVV^^^VVVNNNEEE"""---"""333=========EEE======EEENNNNNNVVVGGGGGG---""""""""""""""""""---333EEEGGGVVV^^^^^^^^^^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppffffffppppppppppppwwwppppppppppppppppppfffppppppppppppppppppppppppfffffffff^^^NNN^^^fffffffff^^^^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffff^^^VVV^^^^^^fffffffffffffffffffffffffff––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘––––––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘†††††††††††††††–––––– ªªª ªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªªªªª ‘‘‘†††wwwVVVVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××fff===VVVfff^^^GGGVVVNNNGGGEEE333"""333===333===GGGNNNGGGVVVVVVVVVGGG^^^VVVVVVEEE333---""""""""""""333===EEEGGGVVV^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffpppfffppppppfffpppppppppfffppppppppppppppppppfffpppfffpppwwwppppppwwwpppwwwppppppppppppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppppppppppppppfffpppffffffppp^^^VVVVVVfffffffff^^^^^^^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffff^^^^^^^^^^^^fff^^^ffffffpppfffffffffffffff––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘††††††‘‘‘††††††††††††‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªª°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ªªªªªª–––‘‘‘wwwVVVVVV–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××^^^GGGVVVVVVVVVGGGNNNGGGEEE---"""333GGGNNN======GGGGGGVVVfffGGG^^^GGGGGG===---"""""""""""""""---===EEEGGGNNNVVV^^^^^^^^^ffffff^^^^^^^^^fffffffff^^^fffffffffffffffffffffffffffffffffpppppppppfffpppfffpppppppppppppppppppppfffpppwwwppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppffffffpppppppppppppppppppppppppppppppppfffppppppppppppppppppfffppppppfffpppfffNNNVVVffffffpppfffffffffffffffffffffffffffffffffpppfff^^^^^^fff^^^ffffff^^^fffffffffffffff^^^fff^^^fff^^^VVV^^^^^^^^^fffpppffffffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––††††††‘‘‘†††††††††–––––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªª°°°¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°° †††www^^^^^^ çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß×××VVVEEEEEEGGGVVVGGGGGGEEE===333---===GGGGGG333---======VVVGGG---===333"""""""""""""""""""""333===GGGGGGNNNVVVVVV^^^ffffffffffff^^^ffffff^^^ffffff^^^^^^fffffffffffffffffffff^^^fffffffffpppffffffffffffppppppppppppppppppppppppppppppppppppppppppwwwpppppppppppppppwwwpppppppppfffpppfffpppfffppppppppppppwwwpppppppppppppppppppppppppppppppppppppppfffppppppfffpppfffVVVVVVffffffpppfffffffffffffffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^ffffffffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘ ––––––––––––––––––––––––––––––†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––†††††††††††††††††††††‘‘‘–––––– ––– ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶°°° ‘‘‘^^^^^^°°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎGGG333===GGGVVVGGG333333333333===EEEEEE===333"""---333===333""""""""""""""""""""""""---===EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffpppfffffffffpppppppppppppppppppppppppppppppppwwwpppppppppppppppfffwwwppppppwwwpppppppppppppppfffppppppfffpppppppppwwwwwwpppwwwppppppppppppppppppppppppppppppfffppppppppppppppp^^^NNNfffffffffffffff^^^ffffffffffffpppfffffffffffffffffffff^^^^^^^^^fffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^^^^fffffffffpppffffffpppffffffppp––––––––––––‘‘‘––– ––– ––––––––––––‘‘‘––––––‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘––––––†††wwwwww††††††††††††–––––– ––– ––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁ¶¶¶ÁÁÁ°°°ªªª–––^^^fff¶¶¶çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßß¶¶¶EEE===EEENNNNNN---"""------------"""333===333"""""""""""""""""""""""""""""""""333EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffffffffff^^^fffffffffpppffffff^^^ffffffffffffffffff^^^^^^^^^fffffffffffffffffffffppppppfffppppppfffppppppppppppppppppppppppppppppppppppwwwpppwwwpppppppppfffpppfffppppppppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfff^^^VVV^^^ffffffpppffffffffffffpppfffffffffffffffpppfffffffff^^^fff^^^fffffffffpppfffffffffffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffff––– ––––––––––––––– ––– ––––––––––––†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––wwwwww†††††††††‘‘‘–––––– ––– –––––– ªªªªªªªªª ªªª ªªªªªª ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ°°°ªªª–––^^^fffÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××–––333EEEGGGNNN333"""""""""---""""""""""""---""""""""""""""""""""""""""""""---===EEENNNVVVVVV^^^^^^^^^^^^^^^fffffffffffffff^^^ffffffffffffpppffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffppppppppppppfffpppppppppppppppwwwpppppppppwwwwwwpppwwwpppwwwwwwpppppppppppppppfffpppfffppppppwwwppppppfffppppppppppppppppppfffpppwwwpppppppppfffppppppppppppffffffVVVVVVfffppppppfffffffffffffffffffffppp^^^^^^ffffffffffff^^^fffffffffffffffpppffffffpppffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffpppfffffffffffffffffffffªªª –––––– –––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––pppppp†††††††††‘‘‘‘‘‘–––––– ––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ¶¶¶ªªª–––†††fffpppÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp------333333"""""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ppppppffffff^^^fff^^^fffffffff^^^fffffffffffffff^^^ffffff^^^fffffffffffffffpppffffffpppppppppfffppppppfffpppppppppppppppwwwwwwppppppppppppwwwpppwwwwwwpppppppppffffffpppfffppppppwwwpppppppppppppppwwwpppwwwpppppppppwwwppppppppppppppppppwwwpppffffffVVVVVVfffpppfffpppfffpppfffpppffffffpppfff^^^ffffffffffff^^^fffffffffpppfffpppfffpppfffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppfffffffff ––– ––– –––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––fffpppwww†††††††††‘‘‘––– ––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÎÎÎÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ°°° †††pppwwwÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýççç×××^^^---"""---""""""""""""""""""""""""""""""""""""""""""""""""333===EEEGGGVVV^^^^^^^^^^^^^^^fffffffff^^^fffpppfffpppfff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^^^^fffffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppfffpppppppppppppppppppppppppppwwwwwwwwwwwwwwwppppppfffppppppfffppppppwwwwwwwwwppppppwwwwwwpppwwwpppwwwpppwwwpppwwwpppppppppppppppppppppfff^^^NNN^^^fffppppppffffffpppfffffffffffffff^^^^^^fffpppfffffffffffffffpppfffpppffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffpppppppppffffffffffffffffff ––––––†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ffffffwww†††††††††‘‘‘‘‘‘–––––––––––– ªªª ªªª ªªªªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÇÇÇÇÇÇÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÏ££àbbí11õø÷óæ))¾DD}eewwwÎÎÎïïïýýýýýýýýýýýýýýýýýýçççÎÎÎNNN---""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^fff^^^ffffff^^^fff^^^fffffffffpppfffpppfff^^^fffffffffffffffffffffffffff^^^^^^fff^^^fff^^^ffffffffffffffffffpppffffffpppfffpppfffffffffppppppppppppppppppppppppppppppfffpppppppppwwwwwwwwwwwwppppppfffppppppfffpppwwwpppwwwppppppwwwwwwwwwwwwppppppwwwwwwpppwwwpppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffpppffffff^^^fffppp^^^^^^ffffffffffffffffffppppppfffppppppffffffpppfffffffffffffff^^^^^^^^^^^^^^^fffffffffpppffffffpppfffffffffffffff^^^ –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ppp^^^www†††††††††‘‘‘‘‘‘‘‘‘–––––– ªªª ªªª ªªª ªªªªªª°°°ªªª ªªªªªªªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ñ “]]wwwÎÎÎïïïýýýýýýýýýýýýïïïçççÇÇÇEEE"""""""""""""""""""""""""""""""""""""""---333EEEGGGVVVVVV^^^^^^^^^ffffffffffffffffffffffffffffffpppfffpppfff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fffffffffpppffffffpppfffpppfffffffffffffffpppppppppppppppfffppppppfffpppppppppwwwpppwwwppppppfffppppppffffffpppwwwpppwwwpppppppppwwwppppppwwwpppwwwpppwwwppppppwwwppppppwwwpppfffppp^^^VVV^^^fffpppffffffpppffffffffffffffffff^^^VVVfffffffffffffffppppppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^^^^fff^^^ffffffpppfffppppppfffpppfffpppfffffffff –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††ppp^^^ppp†††www†††‘‘‘‘‘‘–––––––––––––––––– ––– ªªª –––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎð++àxxÕªªÏÇÇØÏÏ×¢¢í00ú��ú��ú��åwwwÎÎÎïïïýýýýýýýýýïïïççç°°°===---"""""""""""""""""""""""""""""""""---===EEENNNVVV^^^^^^^^^^^^ffffffffffffffffffffffff^^^ffffffpppfffffffff^^^fff^^^ffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^fffffffffffffffffffffffffffpppffffffffffffffffffpppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffpppppppppwwwpppppppppwwwppppppwwwpppwwwwwwwwwppppppwwwppppppwwwpppppppppfffVVVVVVfffpppfffppppppfffffffffffffffffffffVVVfffffffffffffffpppppppppffffffffffffffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffppppppffffffffffff^^^fff –––‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††ppp^^^ppp†††††††††‘‘‘––––––––––––‘‘‘‘‘‘–––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªª°°°ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××ÎÎÎ××××××ÎÎÎÎÎÎÒ··ú��ú��ú��÷ªªªÎÎÎïïïýýýýýýïïï×××ú��ú��"""""""""""""""""""""""""""---333===GGGVVV^^^^^^ffffffffffff^^^ffffffppppppffffff^^^ffffffffffffpppffffff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffffffffpppffffffppppppfffppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppffffffpppwwwpppppppppppppppwwwppppppppppppppppppwwwpppwwwpppppppppwwwpppppppppfff^^^NNN^^^fffpppfffffffffffffffffffffffffff^^^^^^fffffffffpppppppppffffffpppffffffpppffffff^^^ffffffffffff^^^^^^^^^^^^ffffffffffffpppfffpppfffpppffffffffffffffffff^^^ –––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††www^^^fffwwwwwwwww†††‘‘‘‘‘‘––––––––––––‘‘‘†††––––––––– ––– ––––––––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ°°°¶¶¶ÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ×××××××××ÎÎÎÎÎÎÚÂÂú��ú��ú��ð%%¶¶¶ªªªwwwÇÇÇïïïýýýïïï×××ú��ú��"""""""""""""""""""""---===EEENNNVVV^^^ffffffffffffffffffffffffppppppfffffffffffffffffffffppppppfff^^^fff^^^fffffffff^^^fffffffff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppppppppfffppppppfffpppfffpppfffpppfffpppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppwwwwwwppppppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffffffffffffffpppffffff^^^ffffffffffffppppppppppppfffffffffffffff^^^VVV^^^fffffffff^^^fff^^^^^^fff^^^fffpppfffpppffffffpppfffpppfffffffffffffff^^^‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^fffwww†††wwwwww†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘––––––––– –––––––––––– ªªª ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××××××××××××××××××ÔÔÛ··ïBBú��ú��÷ Ù™™ÇÇǶ¶¶ ÁÁÁïïïïïïÎÎÎú��ú��"""""""""""""""---333EEEGGGVVVVVV^^^fffffffffpppfffffffffffffffpppfffpppfffffffffffffffpppfffpppfffffffff^^^fffffffffffffff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffpppfffffffff^^^fffffffffpppppppppfffppppppfffpppppppppfffpppfffppppppppppppwwwppppppppppppppppppffffffppppppwwwpppwwwpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwppppppfffffffffVVVVVV^^^pppppppppfffpppffffffffffffffffff^^^fffffffffpppfffpppffffffffffffpppfffffffffNNNGGGffffffffffff^^^^^^fff^^^ffffffpppfffpppppppppfffpppfffpppfffffffffpppfff^^^†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^^^^www†††wwwwww††††††‘‘‘‘‘‘‘‘‘–––––––––––––––––– –––––– ––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎ×××××××××××××××ú��ú��ú��ú��ùëYYÚ¼¼×××ÎÎÎÇÇÇÁÁÁªªª†††ÁÁÁßßßÁÁÁú��ú��"""---333===GGGNNNVVV^^^^^^ffffff^^^fffffffffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffffffffffffffffffff^^^fff^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppfffpppppppppfffffffffpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwpppwwwppppppppppppfffpppfffVVVVVVfffpppfffpppffffffffffffffffffpppfff^^^^^^pppfffpppfffpppfffpppfffpppfffpppffffffVVVNNNVVVfffpppffffff^^^fff^^^fffffffffffffffpppfffpppfffpppfffpppffffff^^^fffffffff†††‘‘‘†††‘‘‘‘‘‘†††fff^^^ppp†††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ×××ÎÎÎ×××ú��ú��ú��ú��ú��øèhh×ÖÖ××××××ÎÎÎÇÇǰ°°†††ppp°°°†††ú��ú��333333EEENNNVVV^^^^^^fffpppfffffffffffffffffffffppppppfffpppffffffpppffffffffffffpppfffpppffffff^^^^^^ffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^ffffffffffffffffff^^^^^^fff^^^ffffffpppfffpppfffpppfffppppppfffppppppfffpppfffppppppfffpppfffpppfffppppppfffpppfffffffffpppwwwpppwwwppppppwwwppppppwwwpppwwwfffppppppppppppppppppppppppfffpppffffffVVVVVV^^^pppfffppppppffffffffffffppppppfff^^^^^^fffffffffppppppfffpppfffpppfffpppffffff^^^VVVVVVfffffffffppp^^^^^^fff^^^ffffffffffffpppffffffpppfffpppfffpppfff^^^fffffffff^^^‘‘‘‘‘‘‘‘‘††††††ppp^^^ppp†††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––––––––– ––– ªªªªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ××××××××××ÑÑÞ©©ó((ú��ú��ú��æxxßßß××××××ÎÎÎú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffppppppffffffffffffffffffffffffpppfffffffffffffffpppfffpppfffpppfffffffff^^^ffffff^^^ffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^fffffffffffffffffffffffffff^^^fffpppfffpppffffffpppppppppfffpppppppppfffpppfffppppppppppppppppppppppppfffpppppppppffffffppppppppppppwwwpppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffVVVNNN^^^pppppppppfffpppfffpppffffffpppfff^^^^^^fffffffffpppffffffffffffppppppffffffpppfffNNNVVV^^^fffpppfffffffffffffff^^^^^^fffffffffpppfffffffffpppffffffffffffffffffffffff^^^‘‘‘‘‘‘†††www^^^fffwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––– ‘‘‘––––––––– ªªª ––– ªªªªªª ªªªªªªªªªªªªªªª ªªª ªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶°°°°°°°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÇÇÇÎÎÎÎÎÎ××××××××××××××××××ݬ¬ú��ú��ú��ö!!××××××ÎÎÎÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��fffpppfffpppfffpppffffffffffffpppfffpppfffffffffpppfffffffffpppfffpppffffffffffff^^^ffffff^^^fff^^^^^^^^^fffffffff^^^^^^^^^^^^ffffffffffffffffff^^^fffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffppppppppppppppppppffffffpppfffppppppffffffppppppwwwwwwpppppppppwwwppppppppppppppppppppppppppppppppppppppppppfffffffffVVVVVV^^^fffpppfffpppffffffpppffffffpppfff^^^^^^fffffffffpppfffpppfffpppfffpppffffffpppfffVVVNNN^^^pppfffpppfffffffff^^^^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^^^^†††www^^^fffwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÇÇÇÎÎÎ×××××רÏÏú��ú��ú��ø×××ÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ªªª‘‘‘ú��ú��††††††wwwwwwwwwpppppppppfffpppfffpppppppppfffppppppppppppppppppfffffffffffffffffffff^^^fff^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffffffffff^^^^^^ffffffpppfffffffffffffffpppffffffppppppffffffpppfffpppfffpppppppppppppppfffpppppppppppppppfffffffffpppppppppwwwppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfffpppVVVVVVVVVfffpppfffpppfffpppfffffffffffffff^^^^^^fff^^^ppppppfffffffffppppppppppppfffpppppp^^^GGGVVVfffppppppfffpppfffffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^pppwwwwwwwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– ––– ªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÞ¥¥ú��ú��ú��ôÇÇÇÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶°°°ªªª–––ú��ú��†††††††††††††††††††††††††††wwwwwwpppwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffpppfffpppfff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffpppfffpppffffffffffffpppfffpppfffpppppppppfffppppppppppppppppppppppppfffpppfffpppfffffffffpppppppppppppppfffpppppppppfffwwwppppppfffwwwppppppppppppppppppppppppfffpppVVVVVV^^^fffpppppppppfffpppfffffffffpppppp^^^^^^ffffffffffffpppffffffffffffpppwwwppppppppppppNNNGGGfffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^^^^^^^wwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁî++ÛvvΨ¨ÈÁÁÐÂÂÚ““ôú��ú��ú��Ý__ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª–––ú��ú��†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwppppppppppppppppppppppppfffffffffffffffffffffffffffpppfffffffffppppppppppppffffffpppfffpppffffffpppfffppppppppppppppppppfffpppfffppppppppppppppppppwwwwwwfffpppppppppffffffffffffppppppppppppppppppfffppppppppppppfffppppppwwwppppppppppppppppppppppppppp^^^NNN^^^fffppppppfffpppfffpppfffffffffpppfff^^^ffffffffffffpppfffffffffffffffpppfffppppppwww^^^GGG^^^pppfffffffffpppffffff^^^ffffffffffffppppppffffffffffffpppfffffffffffffff^^^ffffffffffffwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––––––––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁú��ú��ú��ú��ú��ú��ú��ú��ú��è<<·±±ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwppppppwwwpppwwwpppwwwpppfffpppfffpppppppppfffppppppfffpppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwwwwwwwwwwppppppppppppppppppffffffppppppppppppppppppppppppppppppppppppfffppppppwwwpppwwwppppppwwwppppppfffppp^^^NNNVVVfffwwwpppfffppppppfffpppfffppppppffffffffffffffffffffffffffffffpppffffffffffffpppwwwfffNNN^^^pppfffpppfffppppppffffffffffffffffffpppppppppffffffpppfffffffffffffffffffff^^^fff^^^ffffffwwwwwwwwwwwwwwwwww†††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––––– ––––––––––––––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°ªªª ªªª°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁË››ßXXï))öù÷ òåEEщ‰¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††www†††‘‘‘†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwpppppppppfffppppppwwwpppppppppfffpppppppppppppppppppppppppppwwwpppppppppppppppwwwppppppppppppwwwwwwwwwwwwppppppppppppppppppfffpppppppppppppppppppppppppppppppppppppppfffppppppwwwwwwpppppppppwwwpppppppppppp^^^NNNNNNfffppppppppppppfffpppfffpppfffpppffffffffffffffffffppppppffffffpppfffffffffffffffppp^^^GGGVVVfffppppppfffppppppffffffffffffppppppfffppppppfff^^^fffppppppfffffffffffffffVVV^^^^^^^^^fff^^^wwwwwwwww†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––––––––––––––‘‘‘––––––––––––––––––––––––‘‘‘–––––– ––– ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°ªªªªªª°°°°°°ªªª°°°ªªª °°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°ªªªªªª –––––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwpppwwwwwwpppwwwwwwwwwwwwwwwpppwwwpppppppppwwwppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwpppppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwppppppppppppwwwppppppwwwwwwwwwpppppppppwwwppppppppppppwwwwwwppppppwwwppppppppppppffffffVVVNNNfffwwwppppppfffpppppppppfffppppppppppppfffpppffffffpppfffpppfffffffffffffffffffffffffffGGGEEE^^^ppppppfffpppfffffffffffffffffffffpppfffpppffffff^^^pppffffffppppppfffffffffVVV^^^ffffff^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘––––––––––––––– –––––––––‘‘‘–––––––––––– –––––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°ªªª °°°°°°ªªª°°°°°°°°°ªªªªªªªªªªªª ªªªªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°ªªª ––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppfffpppwwwpppwwwwwwwwwwwwwwwpppwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwppppppfffVVVVVVfffpppppppppppppppppppppfffpppppppppppppppfffppppppfffppppppfffpppfffffffff^^^fffppppppVVV===GGGfffpppfffpppppppppfff^^^fffffffffpppfffffffffpppfffffffffffffffffffffffffff^^^^^^^^^ffffffffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘––––––––– ––– –––––– ––– ––– ªªª°°°°°°ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªªªªª ªªª°°°°°°°°°°°°¶¶¶°°°ªªª–––†††–––ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ªªªªªªªªª –––‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^pppwwwpppppppppwwwpppppppppppppppppppppppppppfffppppppffffffpppppppppffffffffffffppp^^^EEEEEEfffpppfffffffffpppfff^^^ffffffpppffffffffffffpppfff^^^fffppppppfffffffffffffffffffff^^^fff^^^ffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––‘‘‘‘‘‘––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°–––†††‘‘‘ªªª°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘–––‘‘‘†††www††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^ppppppwwwpppwwwwwwppppppppppppwwwpppfffpppfffpppfffpppppppppppppppfffpppffffffppppppfffGGG===^^^pppfffffffffppppppfff^^^ffffffffffffffffffffffffffffffffffffppppppfffffffffpppfffffffffffffffffffff†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––––––––– –––––– ªªªªªªªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª †††www†††–––ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªªªªª ªªª ––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††–––‘‘‘†††††††††††††††††††††††††††wwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppfffpppppppppppp^^^fffppppppwwwVVV===VVVppppppfffpppppppppfff^^^fff^^^^^^^^^^^^NNN^^^fffffffff^^^^^^pppfffffffffffffffppppppffffffpppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°° †††fffwww‘‘‘ °°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ––– –––––– –––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwww†††www†††wwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwppppppwwwppppppfffppppppfff^^^ppppppwwwpppGGGNNNpppwwwppppppwwwpppppp^^^^^^^^^^^^fffffffff^^^fffffffffffffffffffffppppppffffffpppfffpppppppppfffppppppppp†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––––––––– ––– ªªªªªª ªªª ªªªªªª ªªª ªªªªªªªªªªªª°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°° ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°†††fff^^^–––ªªª°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°ªªªªªª ªªª ––– ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††††††††††††††††††††wwwwwwwwwppp^^^VVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwppppppppppppppp^^^fffwwwwwwwwwfffVVVppppppwwwwwwpppwwwwwwfff^^^^^^^^^ffffffffffffppppppppp^^^fffffffffffffffffffffffffffffffffpppfffpppppppppppp‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––––––––––––––‘‘‘––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°ªªªªªª‘‘‘ppp^^^fff‘‘‘ ªªª°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªª ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwfffVVVfffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††pppppppppppp^^^fffwwwwwwwwwpppffffffwwwpppwwwwwwwwwwwwffffff^^^^^^^^^ffffff^^^fffpppfffffffffffffffffffffffffff^^^^^^fffpppfffffffffpppfffpppppp‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘–––––– ªªª ªªª ªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªª ªªª ––––––‘‘‘–––ªªªªªª°°°°°°°°°°°°–––wwwVVVVVVwww ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªªªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††www^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfff^^^wwwwwwfffpppppppppwwwwwwwwwwwwpppfffVVVVVV^^^^^^VVV^^^fffffffff^^^fffffffffpppfffffffffffffffpppfffpppffffffffffffppppppppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘–––––––––‘‘‘–––––––––––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª –––‘‘‘†††––– °°°°°°°°°°°°ªªªVVVNNNfff††† ªªªªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††wwwwww†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwppp†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwpppfffwwwwwwpppppppppwwwwwwwwwwwwwwwppp^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffffffff^^^ffffffffffffpppffffffppp^^^ffffffppppppppp††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––––––––––– ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªª –––––––––†††wwwffffffwww‘‘‘ªªªªªª°°°°°°ªªª–––fffVVV^^^––– ªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªªªªª –––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††pppwww†††††††††wwwwwwwwwwwwwwwwwwfff^^^wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffpppfffffffff^^^fff^^^ffffff^^^fff^^^VVV^^^^^^^^^ffffffffffffffffffppp^^^fffppppppffffff†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª–––‘‘‘†††ppp^^^VVV^^^www ªªªªªª°°°°°° www^^^^^^www‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª ªªª ––– –––‘‘‘––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††wwwwww†††††††††††††††wwwwwwwwwwwwwwwVVVNNNwwwwwwwwwwwwwwwwww†††wwwfffppppppwwwpppppppppfffffffffffffffffffff^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppppppppfff††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– ªªª ªªªªªªªªªªªªªªª ªªª ––– ªªª ªªªªªª †††pppfff^^^NNNVVVfff††† °°°ªªª°°° †††fffVVVppp‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªª°°°ªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††www††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††wwwwwwwwwwwwwwwwwwGGGGGGwwwwwwwwwwwwwwwwwwwwwwww†††wwwpppwww†††‘‘‘††††††wwwwwwwwwffffffffffff^^^^^^VVV^^^^^^^^^fffppppppfffpppffffffpppfffpppffffff‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– ––– ––– ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª †††wwwfff^^^VVVVVVNNNNNNppp‘‘‘ªªª°°°°°° †††ppp^^^fff††† ªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶ªªª °°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªªªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††wwwwwwpppGGGEEEpppwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘–––––––––‘‘‘††††††wwwwwwwwwpppfff^^^VVVVVVVVV^^^^^^fffffffffpppffffffppppppppppppfff†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––– †††www^^^NNNVVVVVVNNNNNNVVV ªªªªªªªªª‘‘‘ppp^^^^^^www‘‘‘ ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°ªªªªªªªªªªªª°°°ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°ªªª°°°°°°ªªª°°°°°°ªªªªªª ªªª –––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††wwwwwwwww†††www^^^NNNfffpppwww‘‘‘–––‘‘‘‘‘‘––– ––––––‘‘‘††††††wwwwwwfff^^^^^^VVV^^^VVV^^^ffffffffffffpppfffpppfffppp^^^^^^††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ––– ªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––––––‘‘‘www^^^VVVNNNNNNNNNNNNNNNfff‘‘‘ ªªªªªª–––^^^^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°ªªªªªª ––– ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††wwwwww††††††wwwffffffpppwwwwww††††††††††††––– –––––– ––––––‘‘‘††††††††††††wwwfff^^^^^^^^^^^^^^^^^^^^^ffffffpppppppppffffffffffff‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––– ––– ªªª ªªªªªªªªªªªª ªªª –––––––––‘‘‘www^^^NNNNNNNNNNNNNNNNNNVVV–––ªªªªªª–––†††fff^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ––– ––– –––––– –––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††www††††††‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www††††††††††††wwwwwwwwwwww†††††††††††††††‘‘‘––– ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppppppfffffffff^^^^^^VVV^^^fffpppfffpppppp^^^^^^^^^††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––––– ªªª –––––––––‘‘‘www^^^NNNGGGNNNNNNNNNNNNNNN^^^‘‘‘ ªªª †††ppp^^^fff‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªª ªªª ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www††††††–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘††††††‘‘‘††††††‘‘‘†††††††††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘––––––ªªª –––––––––‘‘‘–––‘‘‘††††††wwwwwwffffff^^^^^^^^^ffffffpppppppppffffff^^^^^^‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––––– ªªª ªªª ––––––†††www^^^NNNGGGNNNVVVNNNNNNNNNVVVwww––– ªªª‘‘‘ppp^^^^^^www‘‘‘‘‘‘––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªª ªªªªªª ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––– ––––––––––––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘www††† ªªª ––––––––––––‘‘‘‘‘‘‘‘‘wwwwwwppp^^^^^^fffffffffwwwpppffffffffffff^^^†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––– ªªª ªªª ––––––†††wwwfffNNNGGGNNNVVVVVVNNNNNNNNNfff‘‘‘ –––^^^VVVfff†††‘‘‘––––––––– ––– ªªªªªª ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ªªª ªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––––––––––––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘––––––www††† ªªªªªª ªªª –––––––––––––––‘‘‘––––––‘‘‘‘‘‘†††‘‘‘†††wwwppppppffffffppppppwwwfff^^^fffffffff†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ªªª ªªª –––‘‘‘wwwfffNNNGGGNNNNNNNNNVVVNNNNNN^^^www–––ªªª †††ppp^^^fffwww‘‘‘‘‘‘––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªª ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª ––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††www‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘––– ––– ––– ªªªªªªªªª ªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppppppppwwwpppfff^^^fffffffff††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘wwwfffNNNGGGGGGNNNNNNNNNVVVNNNVVVppp‘‘‘ ‘‘‘wwwfffppp†††––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª ªªªªªª ªªª ªªª ªªªªªªªªª ªªªªªªªªª ––– –––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘––– ªªª ªªªªªªªªª ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwpppwwwwwwpppfffffffffffffff†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––‘‘‘fffNNNGGGGGGNNNVVVVVVVVVNNNNNN^^^–––ªªª †††pppfff––– ªªªªªªªªª ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªªªªªªªªªªª°°°ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªª ªªª ªªª ––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††–––––– ªªª ––– ––– ––––––––––––‘‘‘www††††††wwwwwwwwwwwwpppfffpppffffff^^^^^^††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––––– ––– ––– ––– ––– ––––––‘‘‘pppVVVGGGGGGNNNVVVVVVVVVVVVNNNVVVppp‘‘‘ ªªª‘‘‘ppp‘‘‘––– °°°°°° ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªªªªªªªªªªª°°°ªªªªªª ªªª ªªªªªªªªªªªª °°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ––– ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘†††‘‘‘†††‘‘‘–––‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘–––––– ªªª –––––– ªªª ––– –––††††††††††††wwwpppffffffpppffffff^^^†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––––––––––– ––– ––––––––– ––– ––– ªªªªªª ––––––†††pppVVVGGGGGGNNNVVVVVVVVVVVVVVVNNN^^^––– †††www†††––– ªªª ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª ªªª ªªªªªª ªªªªªª ªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ªªª ––– –––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††www††††††††††††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††††††††–––––– –––––– ––– ªªª ªªª –––––– –––‘‘‘‘‘‘‘‘‘‘‘‘†††wwwffffffffffffppppppfff \ No newline at end of file diff --git a/tutorial/tracking/model-based/edges/teabox.wrl b/tutorial/tracking/model-based/edges/teabox.wrl new file mode 100644 index 0000000000000000000000000000000000000000..a452ea0b7c54d955211974b893b38cf1e3df1eed --- /dev/null +++ b/tutorial/tracking/model-based/edges/teabox.wrl @@ -0,0 +1,33 @@ +#VRML V2.0 utf8 + +DEF fst_0 Group { +children [ + +# Object "teabox" +Shape { + +geometry DEF cube IndexedFaceSet { + +coord Coordinate { +point [ +0 0 0 , +0 0 -0.08, +0.165 0 -0.08, +0.165 0 0 , +0.165 0.068 0 , +0.165 0.068 -0.08, +0 0.068 -0.08, +0 0.068 0 ] +} + +coordIndex [ + 0,1,2,3,-1, + 1,6,5,2,-1, + 4,5,6,7,-1, + 0,3,4,7,-1, + 5,4,3,2,-1, + 0,7,6,1,-1]} +} + +] +} diff --git a/tutorial/tracking/model-based/edges/teabox.xml b/tutorial/tracking/model-based/edges/teabox.xml index c8a31267bb11f0e8ca0e943562af1376510849df..386970455ef26a3e67b3a97283d3335ef2543a44 100644 --- a/tutorial/tracking/model-based/edges/teabox.xml +++ b/tutorial/tracking/model-based/edges/teabox.xml @@ -18,16 +18,18 @@ <step>4</step> <nb_sample>250</nb_sample> </sample> - <face> - <near_clipping>0.1</near_clipping> - <far_clipping>100</far_clipping> - <fov_clipping>1</fov_clipping> - </face> <camera> <u0>325.66776</u0> <v0>243.69727</v0> <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> diff --git a/tutorial/tracking/model-based/edges/tutorial-mb-edge-tracker.cpp b/tutorial/tracking/model-based/edges/tutorial-mb-edge-tracker.cpp index 1ee84758d6387c6f7a9f405a26de3fe6f2230120..6d1794f166c5a41572431d4930e2860e6f9dcf43 100644 --- a/tutorial/tracking/model-based/edges/tutorial-mb-edge-tracker.cpp +++ b/tutorial/tracking/model-based/edges/tutorial-mb-edge-tracker.cpp @@ -1,67 +1,160 @@ -/*! \example tutorial-mb-edge-tracker.cpp */ +//! \example tutorial-mb-edge-tracker.cpp #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpImageIo.h> +#include <visp/vpIoTools.h> +//! [Include] #include <visp/vpMbEdgeTracker.h> +//! [Include] +#include <visp/vpVideoReader.h> -int main() +int main(int argc, char** argv) { - vpImage<unsigned char> I; - vpCameraParameters cam; - vpHomogeneousMatrix cMo; +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) || defined(VISP_HAVE_FFMPEG) + try { + std::string videoname = "teabox.mpg"; - vpImageIo::read(I, "teabox.pgm"); + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--name") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + std::string parentname = vpIoTools::getParent(videoname); + std::string objectname = vpIoTools::getNameWE(videoname); + + if(! parentname.empty()) + objectname = parentname + "/" + objectname; + + std::cout << "Video name: " << videoname << std::endl; + std::cout << "Tracker requested config files: " << objectname + << ".[init," +#ifdef VISP_HAVE_XML2 + << "xml," +#endif + << "cao or wrl]" << std::endl; + std::cout << "Tracker optional config files: " << objectname << ".[ppm]" << std::endl; + + //! [Image] + vpImage<unsigned char> I; + vpCameraParameters cam; + //! [Image] + //! [cMo] + vpHomogeneousMatrix cMo; + //! [cMo] + + vpVideoReader g; + g.setFileName(videoname); + g.open(I); #if defined(VISP_HAVE_X11) - vpDisplayX display(I,100,100,"Model-based edge tracker");; + vpDisplayX display; #elif defined(VISP_HAVE_GDI) - vpDisplayGDI display(I,100,100,"Model-based edge tracker");; + vpDisplayGDI display; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV display; #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; + return 0; #endif - vpMbEdgeTracker tracker; + display.init(I, 100, 100,"Model-based edge tracker"); + + //! [Constructor] + vpMbEdgeTracker tracker; + //! [Constructor] + bool usexml = false; + //! [Load xml] #ifdef VISP_HAVE_XML2 - tracker.loadConfigFile("teabox.xml"); -#else - vpMe me; - me.setMaskSize(5); - me.setMaskNumber(180); - me.setRange(8); - me.setThreshold(10000); - me.setMu1(0.5); - me.setMu2(0.5); - me.setSampleStep(4); - me.setNbTotalSample(250); - tracker.setMovingEdge(me); - cam.initPersProjWithoutDistortion(839, 839, 325, 243); - tracker.setCameraParameters(cam); - tracker.setNearClippingDistance(0.1); - tracker.setFarClippingDistance(100.0); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + if(vpIoTools::checkFilename(objectname + ".xml")) { + tracker.loadConfigFile(objectname + ".xml"); + usexml = true; + } #endif - tracker.setDisplayFeatures(true); - tracker.setOgreVisibilityTest(false); - tracker.loadModel("teabox.cao"); - tracker.initClick(I, "teabox.init"); - - while(1){ - vpDisplay::display(I); - tracker.track(I); - tracker.getPose(cMo); - tracker.getCameraParameters(cam); - tracker.display(I, cMo, cam, vpColor::red, 2); - vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); - vpDisplay::flush(I); + //! [Load xml] + if (! usexml) { + //! [Set parameters] + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(8); + me.setThreshold(10000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + tracker.setMovingEdge(me); + cam.initPersProjWithoutDistortion(839, 839, 325, 243); + tracker.setCameraParameters(cam); + //! [Set angles] + tracker.setAngleAppear( vpMath::rad(70) ); + tracker.setAngleDisappear( vpMath::rad(80) ); + //! [Set angles] + //! [Set clipping distance] + tracker.setNearClippingDistance(0.1); + tracker.setFarClippingDistance(100.0); + //! [Set clipping distance] + //! [Set clipping fov] + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + //! [Set clipping fov] + //! [Set parameters] + } + //! [Set ogre] + tracker.setOgreVisibilityTest(false); + //! [Set ogre] + //! [Load cao] + if(vpIoTools::checkFilename(objectname + ".cao")) + tracker.loadModel(objectname + ".cao"); + //! [Load cao] + //! [Load wrl] + else if(vpIoTools::checkFilename(objectname + ".wrl")) + tracker.loadModel(objectname + ".wrl"); + //! [Load wrl] + //! [Set display] + tracker.setDisplayFeatures(true); + //! [Set display] + //! [Init] + tracker.initClick(I, objectname + ".init", true); + //! [Init] - if (vpDisplay::getClick(I, false)) - break; - vpTime::wait(40); - } + while(! g.end()){ + g.acquire(I); + vpDisplay::display(I); + //! [Track] + tracker.track(I); + //! [Track] + //! [Get pose] + tracker.getPose(cMo); + //! [Get pose] + //! [Display] + tracker.getCameraParameters(cam); + tracker.display(I, cMo, cam, vpColor::red, 2); + //! [Display] + vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); + vpDisplay::displayText(I, 10, 10, "A click to exit...", vpColor::red); + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) + break; + } + vpDisplay::getClick(I); + //! [Cleanup] #ifdef VISP_HAVE_XML2 - vpXmlParser::cleanup(); + vpXmlParser::cleanup(); +#endif +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + SoDB::finish(); #endif -#ifdef VISP_HAVE_COIN - SoDB::finish(); + //! [Cleanup] + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV or ffmpeg and rebuild ViSP to use this example." << std::endl; #endif } diff --git a/tutorial/tracking/model-based/hybrid/CMakeLists.txt b/tutorial/tracking/model-based/hybrid/CMakeLists.txt index 220abfa3947394f5854ba8ed827ea4ec79587116..c126946c59cac6a851e526c00b571b9d36e13b71 100644 --- a/tutorial/tracking/model-based/hybrid/CMakeLists.txt +++ b/tutorial/tracking/model-based/hybrid/CMakeLists.txt @@ -3,24 +3,25 @@ project(tutorial-tracking-mb-hybrid) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-mb-hybrid-tracker tutorial-mb-hybrid-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-mb-hybrid-tracker.cpp) -# copy the data -get_target_property(target_location tutorial-mb-hybrid-tracker LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.pgm" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-mb-hybrid-tracker - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.mpg" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.ppm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-mb-hybrid-tracker.cpp ${data}) endforeach() diff --git a/tutorial/tracking/model-based/hybrid/teabox-triangle.cao b/tutorial/tracking/model-based/hybrid/teabox-triangle.cao index afe1b25ad9811c80bd91183562d26ec933210995..e8053825aa74ded63dfec8a4fd1ad56c5f11ab03 100644 --- a/tutorial/tracking/model-based/hybrid/teabox-triangle.cao +++ b/tutorial/tracking/model-based/hybrid/teabox-triangle.cao @@ -1,27 +1,33 @@ V1 -8 -0 0 0 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z 0 0 -0.08 0.165 0 -0.08 0.165 0 0 0.165 0.068 0 0.165 0.068 -0.08 0 0.068 -0.08 -0 0.068 0 -0 -0 -12 -3 0 1 2 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +12 # Number of faces +3 0 1 2 # Face 0: [number of points] [index of the 3D points]... 3 0 2 3 3 0 3 7 3 3 4 7 3 4 5 6 -3 5 6 7 -3 6 2 1 -3 6 5 2 +3 4 6 7 +3 1 6 5 +3 1 5 2 3 5 3 2 3 5 4 3 -3 7 6 0 -3 6 1 0 -0 - +3 7 6 1 +3 7 1 0 # Face 11 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/tracking/model-based/hybrid/teabox.cao b/tutorial/tracking/model-based/hybrid/teabox.cao index 4e6dd5c45d46f8971d2800f333a116dacd0e7078..83c1fdf783e68e78dfa381d4c326f07d88fa8d3f 100644 --- a/tutorial/tracking/model-based/hybrid/teabox.cao +++ b/tutorial/tracking/model-based/hybrid/teabox.cao @@ -1,20 +1,27 @@ V1 -8 -0 0 0 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z 0 0 -0.08 0.165 0 -0.08 0.165 0 0 0.165 0.068 0 0.165 0.068 -0.08 0 0.068 -0.08 -0 0.068 0 -0 -0 -6 -4 0 1 2 3 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +6 # Number of faces +4 0 1 2 3 # Face 0: [number of points] [index of the 3D points]... 4 1 6 5 2 4 4 5 6 7 4 0 3 4 7 4 5 4 3 2 -4 0 7 6 1 -0 +4 0 7 6 1 # Face 5 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/tracking/model-based/hybrid/teabox.init b/tutorial/tracking/model-based/hybrid/teabox.init index f326e929605734e745c9e9dff9c19939d67d9f75..f109800a0eab96b97050f4872839f09b0a0c2bd3 100644 --- a/tutorial/tracking/model-based/hybrid/teabox.init +++ b/tutorial/tracking/model-based/hybrid/teabox.init @@ -1,6 +1,5 @@ -4 -0.000 0 0 -0.165 0 0 -0.165 0 -0.08 -0.165 0.068 -0.08 - +4 # Number of points +0 0 0 # Point 0 +0.165 0 0 # Point 3 +0.165 0 -0.08 # Point 2 +0.165 0.068 -0.08 # Point 5 diff --git a/tutorial/tracking/model-based/hybrid/teabox.mpg b/tutorial/tracking/model-based/hybrid/teabox.mpg new file mode 100644 index 0000000000000000000000000000000000000000..50fb8ad2d9e245e2bcc06b94d22763a07d9b6191 Binary files /dev/null and b/tutorial/tracking/model-based/hybrid/teabox.mpg differ diff --git a/tutorial/tracking/model-based/hybrid/teabox.pgm b/tutorial/tracking/model-based/hybrid/teabox.pgm deleted file mode 100644 index 41833d705da80194d014d5e4a442d1a060ddd435..0000000000000000000000000000000000000000 --- a/tutorial/tracking/model-based/hybrid/teabox.pgm +++ /dev/null @@ -1,73 +0,0 @@ -P5 -640 480 -255 -IKJKKJJJLKJKKJJKKKKKKLLMMMNNOOQONNOPPQPQQQQQTSSSTSUTWUTTWVWWXXYZYYZZ[\[\]]]\`^^_```^^`aababa`__ZUQPPQQSRQRUUVVUUWVWWXVUUVWWUVVVVXVVVYWVVVVWWUUVUUWWWVXWX[YXZ\[[\\[[[[[[XXYXWYXXXXYYWXWWXZZZZ\[]\\]_]]^_^_`c__bcacbaa``a`_`^^b`__aaaaa`b```a```____aababacc`ababaabbaaaabc```a___`bcbbbdddcccfbc``___^_aacbb``^]^^_d```b_`baaeee``acbcccbaeffeddeda`aa`abfdbbbcdddcdddfheffggfddefdcbb`abbbe`_`a`ccaaaaba``_accddeddcccgbc`aa`abbcca`bb````bbbaa__ac`_``^_^`]_`_^^_^\\\\\\YZXZ[[Z]Z[XYZYXXYYXZXXYZ[]\[[[\`\[[\[[[\YXYYW\WUUWVWWYVUVYXXXZWVVVUUUSTWTRRVTVUTSSSURRRSSTSVSSSRRQRTSSSRSUTVSTRQQQPPPQPOOPMNLKMOMNMLLLLKLMLLKJLKKKJLKKJKKMLKJIIIIIHHGGGHHIGGGFEGGGFEEFEKMKKLKKJKLKLLLKKLLLMLLMMMMNNOOPOPRPQQOPQQQQQRSSSSSUTUUUUWVVWXXYZZZZZ[\]]]_^]^__a`a`__`aaaababa_]VQQPQQRSRSTUUVUTWVWWXVVWWVWWWWXWXUVWVUVUWVWXWWVUVXVUVWVW[ZZZ[Z[YZZ[\ZZYXYZXWWVWZWXWVWWWVXXZY\\\\\]^\]_^a^^^__bbabcaaadba``_^___^`aaaa``_`aaa`_``__``abbabbbbaaa```baa`abba`_`___`ab`abccbbccbbca`_`a`abaccb``^^a^`aa`__^_aaabbb```abc_aabddceedbbaaa`_`bddcbccegecddeigfeefddccdddedc`bbbdd`````acb_bbbbcccbcddddedcccbbaabbbcccd`baaaaa`cb_aa```a```__^^]^^_b___\^_][\]\YYXXXXXYYXWYWXWY[ZYXWXYZ\\_^^]\]\\\\[[ZZZZYYXXWWVWWWWYVUTW[XXXWVVUUVUUUVUTSVTUVWWSTSRRRSSSSSSSSRQQRTQRTQTTUTRSRQQRPPPQOOONMLKLMMLMKKKLLLNNMLKLMLLKJKJJJKIJKKJJJHHIHGGGFGGIGHHGEFFFGEEEELLLLOMNMNNNNPLLLNMLMMMNNNNOOPOOOSQPPQPSSSSSRSTVVWUUUUVVVWWWWXXZZ[[[[]\]]]^_^___``acbbaaabccbaa`_VRRQRQQQTTWVVVUTVWXWXVVWXWVYXWXW\WVWVVWVXWWWVVVVUUUUUVUW[[\[[[ZYZ[[[[ZZXXXXV[WWWWWWXYWXXXY\[\\\[^^]^^^^^]]``_^_accbabbaaabd_`_`__`baa```abbaa`__``bcccfba`caaa````bac``bbbb^_``_bbb``bdcabdbbbca``cbb`aafffad`__^_aac_^^`bbabba`aaabd``abciceefcaab`__`acbacdddeedfeefdejegeeddccefdddfbbbc__````bbcccecdccbdefdddfcdbbbdbcdgccdeddc`ac```aaaabaa```b`__``a^_`___\^^^\[\\ZYXYXYY[YXXYWXX[\\XWXYZZZ\^^__\^]\\\[\YYZ[ZYZYXWVWXXXYWWVVWWXYYXXWWXWXUXWXTUTTUUTSSRRSRUSSSTRSSSQRRTRQQPPRSSSURQQRQQQQOONMNOONLLLMLNMLLLLNLLLMMMMKJJJJJKIHIKIJIIGGHHHHGGHIHHIIGGGFEEFHELLLMNMNONMNNNMMMMMNNNNNPNNNNOPPOOOOONNPQTTSSTVUVVWWWWVWXWWXYXXZZZ[[\\\]]^^^^^^`aaabbaaaaacddcca`YSSPRQRPRUUVUUUUVVWWWVVWWXXXYWVVWVWWWWWVWXWVVVVWUUUVVWWWY[\\[[ZY[[[][YZXXWWVZWWWWVWYYWWXZ[ZY[\[[]_]\^^^^^^_`__`ba`aaa`aaba`_`____`aaa`acbbbbaa`aaabbcccba``_``aaaab```aaaeb_`a```___`abbbababccaaacebaabbbbbbc`a`_`ab___aca_cbbbbcbadcadccecddedbbb```_`cabdddeeecfeeedddefededcchedccb`baa`abaaaca`abdbbbbab`bbdccceaaaecccccdgdddecba`````aa`_````aa``_^a^___]]\^^]\\[ZZYYXXYZ[YXXWWXYZ[[WXXYYZZ\^]]\\]]\[\ZZYZZZZYYYWWVWWXWWWVUWXXXXYYYYYWVWUVUUTVUUTTSSRRSRQSUUVSRSSRQQRSRQQQPQSRTSQQRPOPPPOPNNONMMKLLMKKJLJKKLLLNMLLMKKJIJKJIHIIIJGHFGGGFHGGIIHHHGFGEFEFFHENNONOPOOOPPOONONPOQOPNMNOMJKLKJIHIJKLLPQVTTSVVUVVWWWXWWXWWXY\Y[Z[Z\\^^]^^^`___caaacbaacbbbacddb`[VSQRQQQRVUUTTWWXVXXYYYWXXZYYX_WVVWWXWWWVXVWWVWXUVXXXWZYY[_\\[[[\[[[][ZYYXZX[YZZYWVYYXXY][ZZ\ZZ[]]\[^]]^a____````__aaaaacbaa`_`__`abbbbcfdfdgfeccbdcfdbacab```aaebca`_caabba_`abbaaaaaabebcacbdcbadcabcdecccbaabb`cbbccbaa`adbbbbbcadcbdccfgheddccedd```bbdccefefffedffffdddddccddddhcc`eabbbbbabbaa`adbcbddccccdcdeeedddcccccedccddeaa``a``aa__``caaa_a`_`___^]^^^]]]^[Z[_YXXYZ[Z[XXX[Z^Z[[[\]Z]Z\\\\[[\^`[\ZZZZZZYXXXXWWXXZZZWVVVX[XXY\[]YWXYVWWXVWVWTSSTRRSSQRSTTRRTSURRRTRQQRPQQQQQQQPOOSPPQQNNNNNMLLLMLKKLKKJKLMMNMMLLJKJIIKJHGGHJIIGGGJGGGFGIIJHHGIIIFGFHGNNNMNQPPOOOPPPPOONOPPPHHA==>@CBABCCDFGJMOPPPQRSWWXXXYXXYWWWWXXXYYYZ[\]\\\]^```a``aacb`bdccccdgee]VSRQQQQSVUWUSWVWWXXYYYWWWWWWXXXWWVVWWWXVXXXXWWXWVWWWWXYZZ\\[[\Z[[[[ZZZXYYYWYYYZYXXYYYZZZZZZ\YZ\\^\[^]]___`aaaaa`a_`aa`_bbaa```___`cbcbbccdddeedcbbbdecbc```bcaabbbb`_`abbbaaabbaaaabbbddbbabadeaababeccceddcbcca`d_aaaba``aabbdabbbdeedbbfffeeedcefd`aabcccdeeeeefheeeedcddeccdddddcbcaeabbbfabbbbbbaaaaacecbcddcefebccccdedddbccdddaaaaaa`ba`_``abaeaa`a`___^]^_^][Z^Z[[[[YXYYYZYXXXXYZZ[[[\[XYY[[\\\^]`^\[ZZ[YXXXXYYZWXXXYXXWWWWWWXXXZZXWWXXWWUWVVUVTSRSSRRRRSSTRRRTRQQRSRRRQPOPPPQQQQPOOPPPQQONOOONMLJLKKJJIKKKLLNMLLKJJJIIIIIHGGHHIIJHFIHHGGGHGHIHHHHIFEFIGOOQONQQQRPOPPQRQQOPOLD@;53257889;?A?=?GDBDHKLMOQRTXWYYYYXVSSUWZXVVZ[\\][Z\^_a`bacaabdbbdccccdfge_VURQRTSSTUUUVVVVWYXZZ[XWVVWWXXWWWVWZXXWVVVXYXXWWWWXYXXY[Z\\[[\[Z[\[ZZZZZ[]Y]ZYXWY\Z[[\[[ZZZ\Z[\[[]]^]]_a`aafddb`_``bac`cbaac`_`a````bdbddddcccdebbbdddccadddcbaaacaa``bbabbdcdb``abbbcbcdccdaecaaaaddccbdeecdedaaddda```````abaabcdfeebbcgggffffdgeeeeeefefeddegeefeegggdddddabcdbbbbccfabbbbabddddeaaabbceffgddcifecccfeeddddbfcddccdccccbbba`_`abbbaaba`_b_`^^^_][\^[\\\\ZZ[XXZYYZYYZZZ[\[Z[Y[[^[\\\]]^_^_[ZZXXYXXXWWWWXWYWXWWWVVWWWX[YWVXWXXYUWWUUWWWSSTTQRTWTTRQRTRRQQQQRUQPPOOPQRQQQPPQPPOPPOONNQOONMLKJKJLKKKLLMNNLOJLJIIIJHHJHIHHHIIIIKGGHIHGHHHHHIGFGHFONQQPPRSRSQQQRRRONFC=8644010333368887666789:<>CHLORUW\Z[PEDELSNIMRV\\WTRW\]aa``_caabddccbbccdeff^VSQQSTTTTUUUUVVWXWXY\ZYWVWXXXWVXWXYXWWVVUVYWVWWWWXXXZYXY[[Z[\[ZZ][Z[\ZZ[\ZXZZZXYYZZ\]\^\\\^[Y[[[[]]^^__^^``ceca`__^_```c`aca_```c````abcddedddccbbbcdcbc`accebaa````_`bbaaabcbb``acabcbcecbcabbcdb`bbcecceggfddbbdebcbaac``a`abbbceeeeabcefghgffddddddcddddecccdddeeefdefeheecbddccccccccbaaaaabccdcbb`bcdedcdedceeecccddecddccedcccecccedcb_`aaccea`aab`````____^]^^][\^\\[[YXXZXXY[[Z[\\\[ZYXZZYY\]\_^]\Z[[[^YZYXX[XXWWVVWWWWWWWWWWXXZWWVXWVUUUWWUUUUWSTUTQRTSRSSRQRTRQQQQQRQQQPOQQSTQQQRQQPOOPPPNMQPONMLKJJIJKKLKKLONKKJJKKJIJIIIHHHHGIJIHHHGGHJHJHHHIIHHGFEQPPQRRSSRRRRSSTQOD:415578633320334422.*)()*+-04:@FNRUYY[J;6=GII>>ITZ\TKLUY]`bbbccacbecccccbcdefe^VRQQRSTVTRSTUUUTVXVVYZYWWZYYXWXXWWYWVVVVUVWWVYXYYYYXXZXXYYZ[]][Z[[[[[Z[[\YYY[ZX\[[Z`^\]^]a^[Z[\^\]]]]]_caaacda```a_^`a`c`aa``aaaa``__abeeeeedccccbbcccbd``cbba``````_`bda`_abbccba`bddcbcecedddfeebbcabeffffffcccecacddcc_`cccdddddefecbdefghggghhdddfehefdeddccdefgefeedceeeddgddceeecdda``aaabccbbccbeeddccdeccddedccccecebbdhcccdbbcccdca``aaabb```aa`_`aab``^]]]^]\\\[\[ZYYYXXXW[Z[]Z[[\ZYYYXXX\\]]]\[[[[Z^YYXYXXXWVUUVVWWVXXXXWXZYYWWVXUTTUUWVUUVWXUUTTRRSSRRRSQQRQRSQSSRQSQPQVSTRQPQRQQQQQPPPQQQQRONMKJJKNLKKKKKMOMNJJJIIIJKIIHGGHFJHHIIIJGHGGGGHHHHHIGGFRRRRRSTSSRSSSTSPD:506:@FEDCA=86432.)%" #(,5?ENRWX[G656:>:7:?OYXRJ@JUZ^`cbaaabbbbcddcccegfd^URQQTSSSSSSTUUUUUUSUWWXVVVUWXWYXVWYWVUUUVVWVVXWXYZVXXXYXXYY[^][ZYYYYZZZ[\\[[[ZXZZ[Z[\\[\[]\\[]_^[]]^^]_bbabcdbaaa`__`a^`_`_`___bc`___aabdeeecccbgbbcdaa`^`bba`__`a```a````^abbabca`bdcbbbcbcddeedeccccbcefhfdecccdccbcccb__`bbdecegehfeeefefgfeeegcdffdddfdefedeffihegfedcfeeeddbbcedecdecaa`bcbbcbbbbbeedbcddfeedceefeeddbcabddbcdbbbabbcbaa`aa_bb`aabbda`aaa_^]\[]]]\]][\ZZZXXXXXWYZ[ZYZZZYYZZXXXZ[\\][]^[Z[][YYZXVVUVSVUVWWWWWWXXWWVWXWVVUUTTTUUTWUYWWUTSSSSRQQRRRQQRTSRSRRQRQQRRRRQQQQQRRPPQQPOOONNNNNNKJJLLKKKLLKLLKKJKKHHIGIIIJIHHFGGHKJIIHIFFFGHGFGGIGFFSSTSSSVVUUVTSTSI=64:@GNONOOMKHD;42+%#+4;EQWXYE60122257<IWZUI@@LY]_bdbacecccfefdddfgfe^USRRSSSSTWWVVWVUUTTYWVVVVVVVVUVUWXXWVZUWUVVZXXX[ZZY\XWXXXYYZ\][[YZYZ[]\\]\[[\]YZZ\[[\]\^^]\\\^^]]]^_^^`aabcabdbabe`a````_a``__`c``___a`acfegbbbbcabca``abbbca__bcc`dbbaaaa`abeabbbbcccceedcbdeegdededcbcegffffeccfedbdbb````abdeehgfgggifgffgeeiehedfhddcegffeefgghhgjfgdcdfefeedhdedddddddcbdbabddfcfciedcbeeeeeccefffgedddbccddecbccacccba``baaaaaabbaab_aa`]\\\[_]]\[\[[YZ[\\YXWVWZZ[YZZ\ZZ[[YXYZ[^\][^]\\\\]]^ZXVUTVTVUVWYXVWXXZVWWWWWVVVWUVUUTSTUWXVUUTSSSRRRRRSTRSSTRUSRRRRURSRSSSSSRRQQPRPPOPPOOOOPMLKJJLKKKJKLKIJKJKIHHHHIHIIJHIHGGJJKIIIIGEEHGGGHGIGIESSTUTTTVUSVTTTLC>9@IMRSTTSSTOMIC<4-%$,4=IVTUB60/.-.36<FU[XPGDDO[_bccddfdcbgeeeeefgff^RSSSRTWVUUUUUUTUVTUUUWVVVVVVVUVVWYXWWWUVUVVXYXXYZYYYWXWXZYXYZZ[ZXYYZ[\ZZZYYZZYY[[\\\[\\\\]\]]]]]]]]__````bccbdbaba_``_``___`__`c]^]]_a``afefbb`bccbdaada`aaccaabbb`dbbbaaaaaabaaabdcbcddccfdheddbfdedcccdedefeedccecbbbc_``aabcddfffgfffffgggfeedefedddccccefedghiihggfffddeeeeddfdcbcdecdedddabbdddcdcfeeeffeedddbeeegfeedcbccdddbb_`ab`bba_`aaaaaac`a`_]]^^]\\\\\\[\\]\\\[ZZ[[ZYVVUZZ[YYYYZ[][ZZZ[[\\\[^\[[\ZZ[ZYXVUSSSUUVWWWWWWVWVWVWXXWWWWUUTTSSTTTTTVYVUSRSQQQRUSRRRRRSSSSRRRRRQQPPPQRQPQNOOONNMNMMOMLLKJILKKKKKKJIIIJKHHIIHHFGGHHHIGFHIIIIHHIEEHFFFHGIFFFTUUUUUUUUUVTWUJC?FMRVWWVUVVUTSRLG:3( &-6BMVK<101.)).49CU\\[RGBH[`ceeddfegggghghgfhgg]TVTTTUVWVUUUVUTTUUUUUWUVVWWXUVWXWXXWWXWWVWXXXXXXYYX[XXW[YXXXXXYYYYZ\\]\\[[[]YZ[\\\]\\\]]]]^^]`]\\_^^^`__`baaadcab``__ccc`_^````b_^]\]ba`beefffbbbdbdabcbaabcccddcbbdbdbbaaabceedbbcddgddbacddcccdffeccddddeeddddddccdgdccbaaabdedhffeefifkhihggggffhdccccddegefkikihgggeddeffeeedfdcccdddcefedddceeddefgeeeddeecdfedddefefdedgfffhcb_^acbbbc```cba`ac_^^]\]^\[\_]^]\\\]_^`]\\`]\ZZXXXY[Z[[XXZ\][\[[\\_]\[^\[[^ZZZXXWVYUTUUUUWWXZXXVYVXWVWVWZXXVTTVSTSSSSSVUUUVSSSRRTSRRSRTSSSSSSRRQRQPPQPQQOPPOOOPNNMNMLLLLMLLJMLNJJJKJJIIIJGIIIILHFFIIHIIGHIIHIJJIEFGGGGHIIFGFUVUTUWVUUUVUUUMHLPSXWVWXWWWXYZRQD=1*" '/:EBB2(+2*&')05DV_`^\OEIYadeeedeefgfigfhiihhh_TTTUUVUUVVVVUUVUTUUVVWUVVWVYVWXXWY[[ZYXYXXYYYXXXXXXXWWWWXWXWXYYYYZZ[[[[\[[ZZYZ[\\\]]\]]]^]^^]]]]]]]^]^^^_b_aabeaa``_^`aaba_`a``b`_^^^``acbbbbbbbabbbbbbbbbcbbcccccceabaabbbccdccccbcccbbbbbbbdadgfedcdgffdedcccb`acdddccbbabbddddeeefffhggghdhggfeeeedcddefffffggghggfifddeghgeeedbccceddegfeeedbegdcegffdddceheedddddcdeeccddddeedb`aadecbca`_a_`dab`_^]]`^[[\]\]`_^]]__`\]]]\[[ZYXYZ\ZZ[XYYZ][\[Z\]]^\[]\[[[[Z[YXWVVUUUUUUWVXZVVUUUWXWWVWXXVUTSTTTRRRSRTTTSTTSSRRTSRRRQRRRTRRRRRQQQPPOOOPOOOQNPOLNMMMMLLLLLKJKKKJJKKIIJIHIHHIIIIHGGHJJJIHHIILIHIHHGGFFFGFEEFFWVVUVWWWVVVVVRQQQUZYYWZYYWWWXWSJB80)$")3<@0'('&$!"(-6EZaac\WHJZaffehggffhgghhjiiijh`TRTUVZWZXVVXUUVUUVVWVWUVWWWXWYYYXZZYYYY[\\[[ZXXWWYWWWYVVWVXWY[Y\[[YXYZ[^\]ZZ[\\_[\]]\^\\]\\^\\]\]a__^^``_a`baabb`^`^``aaad`aa__caa`_`aa`bcbaaaabbccbccddeffhdecbccceccaacdcedddfddbbccbeddabcddeegdceeeffdcbbdbaabddddccdddcdhdcddeeeefiihghiiihgieffgggfgffffgffhihfeegdcehgheeedccccdfegfdggfeddeedgfgfddddfeeeddfeecddddhdcccdddf`abdedccbaaa```aaa`^^]`^[\[^\\^b^]]c_`]_]\\[[[YYYZZYZ[ZZXXZZ\[Z\]]^^]_\\[[[ZYZXWVWWVUVUUUVWZVXWVUWWWWZXXWUTWSRTTRUTTTUUWTUUUSTRRRSRQQSQQQRRRQQQRQQQOOOOONNNNOPMNMQMNMLLLLNJKJLLLKKJIJHIJHIJLIKIHHGHIJIHHHHHIIHHIGHFFFGGEEEFVVVYYYWWUVVUSQRTTXXWWWYYZWVVUSJA92)# (1*$$*%#!%).9I]^aVSNHH[dfffgfggghgghijjjiig`SSTUVVVWXVVVTUUUVUUUWWVWXXYYXYYXXYYYZZZZ\\\ZYYYYWWXWVWVWXWWWYZYYWYXYZZ[\]]Z[[[ZYY[]\\\[\]\[[[\_]]]^______`^`aaa`__`_babaab``a`_a`aca`aabbccbaaaabdedcdeddeeedebbacccdcbccccddeeebccdddcddcbcddedcdcegfffedcbaaaaaceeedcddeeedddefdddeeegjgfghikhggffeffedgffceefffffeeeeedegffeffdcccddecdfggfeeeedecddegfccdfddieddcddeeddeccccgddeabbcedccdacb`__adbaa```^[[[]]]]^^^]_^`\_^\[[]ZZYZZZ[[ZZZWXYZ[[[[^]]\\\[[[[[ZYZWVUUUVUUVVUVVVVWVWSWYWWXXWVUUUSSSSRRSSSTSWTUUTTTSSQSQQPQPPORRQQPPPPQQPOOMNNMMNNNMNMNMNPNLLKJJJJKKKJJJJIIKJHJJJJIHHHHGHIIIHGHHIIHGGGGEFGGFFEEEWWWWXVUVVWXTSQSTVW[VYWYYZWVPLG@6/(" ##"(*/;OW\PG@<?G_eeegighiiihhhiijllkh`SRTWUVVWWWWWXWTSTUTTUVXZX\[]YZXXZ^ZZZ\ZZ[[ZYY]ZZYXWWXXWXXYWXYZZ[[[\]ZY[[\]\\\[[\ZZ[]\][\\\[[\\]\\]^`___bbb_abb```___`aaaaa``aa``aaaabcbgdcdddddeedfeedddegeddebbbfcdccbbcdddeeffdddgffeeeedcddeicddhgggheebbbdaabefigeeffefggfffedddddefggfghiiigfghffgcdheecdfifgefdeeefhfffffffddfddeeddefedegeiddedddfiecdfccefffcedeedddegccddddddcbcccbbbbca^_bcccdba`_^b\^^``_^`^b^`_`]ZZ[]YYYZ\[[\^ZZYYX[[][[\]]`\\[\[[ZZYZWUTTUXUUVXVWUUVVVWVWWWWYXXWWUUSTSTRSSSSSTWUTUTUWUTSSSSQRPRRQRQQPOPQOPSPONNMLMMLLMPMKKMOOLNLKJIJNKIJNLLJJIJIJJJJIJIIJHHHIIGHIIJIHGGGGFFGGFIFGGWXWVVVVWWWVSPNOORSTUWWWWUTMG?:0)# !#$$#'*-=CLD><5?AQbdefiihhiihhjihijkkkiaSRRVTUUVUVUUVUTSSSTTUVYYXZZZYZWY[[ZZZZZZZZZYZZ[[[[ZYYYXXXWWXXZZZY\][Z[[[[[[[Z]_\\Z[[[[[\Z[[ZY]^]Z^^_^_`_]__a_`````_^^_`aaabbba`abbabcccedeeedddeeefefddefhdddcbcdcaeccabcddefeeeeeffdegffeecfefebdehggggceabcbbccefhifefiffhifdeedddgfgggfeghighjggghfhddecdcdeddeeddfdeeffgigifedddeeeefeeeadefeecegfceeeeeeeaceegdcdddfeeeedddfdcefcbbbcecaaabaaabccddbaa_^^\]^``_]^^_]^^]\[^\]ZWZYYY[\[ZYYXWXYZZZZZ[[Z[XYYZZZYYWUSTUWTUUWWVUUVVVVXWVVWXYXWWUVSSSSSSTSSTUUUTTTTTSRQRRRRQPQSRQQPPPPPONSQONOMMNMKLLLKKKKKLMMMLJJJLKJIKLKJIHHHIKJJJJIIIHHHHJIIIIIIHGGIGFFGGGHFFFYXXWWWYXXXVSMIGKPQSSXWUSPJD90)" - - $%&$ "&',596556<DRadihhhhhkkkiiiiikkklnhaRQRVTVVWWXUTTUTTTTUVVVXYZ[YYYZYYZZYYZZZ\YZZZZ[ZZYYYZ[ZYYXXXXYYZ[Z\[[[\[[]^]]___^][ZZ\Y[][\[\[^]^\^^aaaa``_`a_aab`b^]^^`dbbb``bbedfabccdfeeegddddefddedejfgdfdcbdebaeeebbcddefieeehhjfefgffeceeffedeifgghedbcddeheffkgffehfgihfedffdcdceeeeehhkjkiggefffdeddfddebcedbcfddegghiggfeifdegfefeddcdehdffgfmdgfefeeeccdddcccddeffhgkceedcfdcbbbcccacadaaafddddb``^]^^_]^`_^^]_\]\\[Z[[\[YZYZZ[[[\^YWWWZZYYY[[YY[XZZ^ZZY]WVVWVXUTUVVTUVVUUXXWVYVWWXVWVUTTRSTTRRSUUTTTTTSRRQQQQUUTSTSSQQPPOMMMNQQRNONNNMLNLLKJKKKLLKLLKLKNKJIIKLIIJLIJJJJHHHIHGHHIIHHIIJHHHJIGGGGJILGIFYWXYXXYYY[WSLEEGFGIKORLHB=2(" """ - -#.,,'!""$%,189>AEJR_dggghhhgjjjiijjjkkllkhbQQSTTVVWWVUUTUUTWWWVVVWWWXXY[[YYZYYYZZYYYZ[[ZZ[ZYXXYYZYYYXXYYZZZY[ZZ\\^^]^^]]^]]]\[[[[\[[[[[Z^\\[^^a`ab`_``a_`_``_^^^^^ccba`_aacbbbccdeffecccddddeddeecfegbedccddbbdccbbbdeedeeffhhhhhggdfgegfgffdceeeffeedegggggfgkhhghhgggfggeeeedeedeeeegfffhhhffghiffddedeebcdddbfdeegghigfeefgedefffedddddeeeeggfeghfffeebcdefdbcdeeeffffcdddbccccbdb`a_``aaa`dfffcbab^\]^_]_ca__]_Z\[[ZZ[ZYZYYY[][\\\]ZYWXYYXXZZZYYYXYY[\ZXWVWWXVVTTUWUUUVUUTTUWUVVWWWVVVUUURSTSRSTTUSSTTTRSUQPRQTTSRRRQQPOOONMNNOOONNMMNMKLKJJJKJJKKKLKKJJLMKIIKJIIIIIJJJKIHHIHHHHHHHGHIKIIJIHHIHGGGGFFFYYYYYY[ZZZYUKDDB@?@CFED<4* #(+4:>843/& - -%07+(#!"%*.7>ELQWZbejgggjhhgiiijkjjkklnlkhdPPRUTXXWVUUUVVUTWWXYWWWWWWXXZ\YXXYYY[\YXY\[]ZYYYZ[YYZZYYYZYYY[[\[ZZZ[[]`]^___^]]]^\[[_\[ZYZZ\]]__^^a`aabbb`aba_a``_a___cbbaabb`cbebeeeeggfccdgdddhdedefgfheeefefdddccbcddhdegfefhhhhhgfffhffggfieddfddfeeifhgfgggghkihgghihgfifeehfiehefddeheeehhiffhjigfddefecacffgffffffhgffeddeeeeeeeekghggfeflfjhgfiggeeeeddfgedbdefedfifgffdgcdbfcbbbbba`````accdcccc`]]]^``bab`_^_]\[[ZZ[ZY[YZZ[][\\[][[YXY[WYXXXWWXYZYZ\[YXXZXWVVUUVVWXVXUVUUVVTTUWXXVVUTUVUVSSSSTTSSTTSTSVSQPSRUSSRRSURQPQOPPOMNNONMMLLMLOLMLKKLJJKKKKJJJLMMKJJIIJJJIJJJJJIHHHIJHIHIGGHKJJIIHGHIHGFGGGFZ[ZYZZ[ZZZZVNDA<:9:<<<6/%&*5.00B?=>>:8( - &*0+)%$$&.5@GMX[]^ceghfhhghhhijjkklmnnnmlkhdOOQSTUTVUUVVVVWVVVWWWWVXYYXYYZZXXXXY\ZXXYZ[[ZZZZYZZZ][YYYZZZZZZYY[\[[\]]]]]]^^^^^]]\[[ZZ]\[\^^]]]^__``ab``a````_^^_a_``abbbbbaaaabbegefgffddegcddeeddeffffeeeeddcccdfeedddcffffgihihhhhhhgbfjgffdeefccgeefddehiiiiiijhhhiiihfieedhgfefdddedeeeegfhhgiihhgfdfeebcdegfdfigghjgeeeeffceffffeefffffffffihgffffeedeefhfeecddeedefffefbcdcbddbabbbbaa`_aaccbabca___^^_`bbbb_]^]\[[[[[[\\YZZ[\\\\]][ZZYYXXXXXVVTXWXXY[[XXXYXWVVUVVXYWVUUVVVVUTTUUUVVUTTUUSSRRSTUTUSRSSSRRRQOPQRSRQRSTRRQPNNLOMNMNNNNLKLLKKKMKJJJIJKKKKJKLJMKJKIHIKIHHHHHIIHGIIJIHHHHHHIIJJIIHHHJGFGGHF[[\\]]][][ZXQF?9544563,!'*31--.370/6>/)(& %0.)" %)/9FWLXY[\cdhgjjjhhiijjkkkllmmnmmnnicNNPRUUVYXYVWVVWYUVWWWYZ[XXX[ZYYYYYYYYYXXY[[ZZZZYYYZ[\]Z\ZZ[]Z[ZYY\[[\_^_^^]]]^]^_]]]\[[Z\\\]^^]]]____^`c`a`__\`_\\^aabbbbccccabbbbcedefgfgeefgggeffgdgfifgddeedeccdhhhhhfedfffeiihhhiiilhhghhiffdhfedcffffeefhhhhihjiihjkkhgghffehfgffedeedfeefjghgghhhjihffeeeeeefgefgghiigggfffgghfgfgfeeffgffghgigggfffffdefhgffhfgfgedefffefbdcbceffcdbbbcbb`abcccbaa``_`^^_`fbbb^]`^\\\[[[[\][YY[[[\]]]^][ZXXYXXXXZVWXXXXZZZYXYXXVVVVWZXWUTUWWVVUTTUVTUVUTUVVTTRSRVUUTRQSRSQSQQPPQRQPPTTSRTQPPOOONRNNNNOMLLLQKJMKIHIIJKLLLKLMMMKJJJIHHHHHHIIKIHHIIKHHIIIKHIILIHIKHIHGGIHHF\Z[[[[[[^\[ZSG?84001/,# #&($ #(%"%*#! '*&!#%,<DSIEGGOW[^bffgiiikkkklllllmkmmllmjbMMOQTUVVVVVVVVXWVWYXWXZXXXXYZYYXYYYXXXWYZYYZYZ\ZYYZ[[[Z[Z[[[Z[\ZZ[[[\_]__]^]\]]^^]\]]]^]\]]]]]]]]^___^__`a____`_\]^``aa`_baccccbbccfddffffeeefffeefedgffddcdefdcbbcdefffeeegggffehhhmiikghegfffeeffgiiigeefggghggiijhhgjkkhgffffdeeedgfffededeefgghhggghkgffbeeeeeeeeefhiihgffffdffgefgffedefggghggggghffghgeeeeeeggfeeeeeggfecdbbccecccbbbcdc_aaabbbbbaa`a`_^`_ab_`a_^_`\[\]\[\\\[ZYZ\\\[\[[][ZYYZYWWXWWXYYYYZYYYXYVWWWWWXXVWTUUWVVVUTTUUTTUTTTTTTTSSSSSTRRQSRRQSPPPQPPPQNPQQRQPPOONONNNNNMNNOLKKKKMKIIHJJJIIHIJJJJJJIIGHGHHHHIIIIIHHHHHIGIHHGHHIIHGHGGFFFFEFF`_^\]\\\^\[[UG?82.-,*#!'")!&%#&((/;NE:*$*4@MU[^eelihikjklmkklpppmommibLJOPTUTTUVVVVVXXWWXXXXWWWWXXXXXXXWWXYXXXYYZ\Z[[ZZ\[]]]Z[\][_\^\[\^]^__^_^\\__^]_^]\\\\^a^^^_]]^aa`^^^^_`aa_]^`____`aacab`ccgffddbcdfddeffeejeggheeefeffheeegeecccbccdgeeeeejhigffhhgiiikkmhhfeefgihhgghjgfffggijhiiighgjkjhgffejefeedgddddeefgdefgijhggghffffffeedfhhhhhiggjfffffgfffffhfedefggigfddgggggkhhggggddfhedefefgkfedhdgcdeaccbbccdd`aacb``ea```aa^^^_ac`^`a`^]\[^^]]^]\[]ZZ[[[[]Z[^[Z[\ZZWXXXWXXYY[ZYY][ZZZWWWXWXVVUUVWWWVVUXVTTSTTSTTTSTUSSRSSRTRSSRQSQPQTQOPQQRQQPPPROPOONNNOMMORPMMOLKKJKLJJKMIKIIIKIJJKJHHGGGHHHIJJIJHHHJJIHHIIIKJJJIIIGHGFEFFHH]\]]]^^^^\[\SI@92.+*$ - #'" $',..021/&%09DSY_eiiijjkmllllpmmnpnniaKJPQTTSSTUUVWWXWWWWXWXUWWXVXXXXXXXXXYXXXYYZ\ZZZ[ZZ[\[[[\]^[[\]]]]\\]`^]]]]\]^^]]]]]\\\_^]^__^^^^^____^^`ca`_^`__a`ca`c`aaccefecccddddeeeeeeeeffeeddedfggfedeeecccccceeeejefeefhhfghhhhijehihgfeghhhhgfggggggfgjihijiggghiiihggceefdedgcedfgfffdeehiiiijhggffffgfeffhhhhgjgfgggffgggfcddfffdddffffedefggggihhghhfeeeedeffffghfedddfcccbfcabbbceaaabba`b```aba^_^_`ceaa`_^\\\\[[[^[[[]ZZZZZ[\[Z\[[\][ZWXYXXXXXYZYZYZ[ZXXWWWXVVVVUVWWVVWVVXUTTSSSRSUSRSRSSRQRRRRQPQQRRQPRRQQOMOPPPPOOOONOPOOOONNOPOONLLLKIJJKKJIJJIIIIJKJJIHGGGHHHHHJHIHGGGHIHHKJIIIJKIHHHGFFFFGGG]^^]]^^]^\^ZRKB:2-*& - - - !&*$"%*3::4'#"#-;NYcimjnlllmlmmpnppponlaJJOSTVSSTTUVVVVWWWWXYYWWWYYYXXWWXYYYYYY[Y[Z[ZZ[]]^\\[\]`]^[[[\\\]]\\\\\\]^]]]^^]\]]]]]]^]_`a_a]]]^_^^^_`a`````____```cafccddeebhcgdhefeeeeeeefehecdffffeeeeeffghdcccddeeeeeeeehggggjhihjghgfgkehijihhggggjhgffhiijjiiighiiiighhhhhgeeggifjihgiggfhinkkjkgjhgghgggihjiihghfehhihiiiilccegffeeeefeeefgfkghiiiihhhigfeefgfeegfiffdddgccbabcbbadceaabbcb`dababab``__`bba`_^^\`\][[\^\[[]\[ZYZ\\[Z]\_\^\^WZYXXYX\[[Y[ZZ[\YXYYXXXXVVVXWYWXWXXYUUTSSURTRQRUSSSRQQPPQQQQPUQRRRRRPNOOPQPPOOOPNOPQOMMNNMPOPPMMLLMNLLKKKJJIJKJIIIIJIHHHIIHHHIIIHFFHHIGIJJIIIKJJIHGFFGGGGGG]]^]]^_]][ZXTNF<5-*$ #$)'!#*2<<?0%! - -$<Sfikjmlllllmnnmnooonn`JJNTUURSTTUUWUUXVVUUVWXWYXWWVVVWWXXZZZYYXZZ[\\[]]]\\[\]]\\[[Z]]]\\]]\\\\\]^]\]]]]]]]]]^^^__```]]^^a_^^``a`````__^`a`_`abbddeeebccdcddeefhgfffgeeddddcgeeeegfkgfgccddeefedddddfhghgfhhhhhggfghiefihihihgghhhgfffgghghhghijjiigggghiiffgghfgihfihgfhjjjjkkhijhggghijhijjjhgfeghhhijihjeefggfhedffgfffgegghjikjjhffffefgfeeddcheedddcabbaab`a`dbb`acbaaaaab`baa_````aaa`_]]\]\\\\\\]\\\]\[Z[\^[Z[\\\\[ZXXYXWYX[Z[XYYZZZ[YXXWXXWYVVWWVVVVXVVTTUTVTQQRRRSTRRQQQPPQRRQPOORQRSSOOOPPQOPPPOONOPNMMONNOONMNNMLMMMLKKKJJLJJJIIIIIIJJGGGIHHHHHHIFIGHHHHHIKIIIIIJHFGGGFGEGH]^^^]^^^`\ZYYTLB80*$ - #1-)'(&!$)2:EJ<.*( -0Wgikjmlommmooonnnmnnl_JIOUUUSRSSUUTTTWVVVZVXWXXWVWWWWWXXYZZZYYYYZZ\^\`]]\]\__^\\[[\]]`\]]^^^\\]]^^]\\]]^]]]__]_``a``aa^^``_^_```abaa___ab```adbgedeeciefdbegefggggggfgdddcdheeeedeghhhccdeeeeddeefdggfhgfihhhlhghjihgffhihhhgghfgggggghihggkkjjmmnjjggikighkjjhfgjhiggghklkjkkkkjkjjghiiiikhhigffiiijkjjiiiijigfgeehgghihjihgiiijjjiihffghgffifddifeeeecbabcaaaccdccaabbaaa`bebccdae`a``ac`_^^]^^^\\\\]]]`^][[[\\[[Z\^^^\[Y\Y[WXY[[\ZZY[[[[ZXYXWWVVVVWWVUXVYVVSVSSTTSSRRRTSRRSQRSRQQRRPPQRRRRSNQQQQQPSSSOONNNNLMNNNQNLLPOQNOOPLJJLIJJJIJJJIIIIJJJJJIHHHHHIHGHGGGHIHGIIHIIIIIHJGGGIIJI^`^^^``_`]\\[[SNB6.& - $2@J8,&!%/4=ESI6*.$ - -8Zjjjjjjonnnnooponnook_KMORTTTUUTUUXTTUVWYXVVWWWWVWXXYYZZZZZYXYYYYZ\\\\\\\\]__]\\Z[]]]`[][[Y[\\\]_^^\[\\]a^^___``aa``__^_`_a`_`_aaaa`_``aa`a`adbdfddddedecceeffgghhigeecdgfffeedfeefgigddcehfcdeeeedgggkhfghhhihgghhhigegfgggfhjgjighihhihgfghjijjjiighijjijigjjhgghgefihiihjkkkiijjkfhiihikgghggfijjljhjkjijjihggggggghhghhgfgfiijiiigggiigghhiffffeeehdbbbbaaabccbaaaabba`aaa`aaaaaaaa``ba`_``^]][\\\]]^_`^\\_]]\\\[\\]\[ZZXZWY[[YYZYXYYZYZZYXWYVUTTUXUTWVUTUSSSTUTSRRSSSRQOPQRRSQRRRPPTSSRQQOOOPOPSSSQOPNNNMMNNNMMLLJLMNNMLLKJKJHJKJJIHIIIHIIIIIIHIIHHIIHHHHJHHIIIIHHILIIIHHHHHHHHG^^^^^```a_^^^]ZUN@5-%(.)!!%$-DXPE0$)-=BQRJ-') 'C]ljjlllnnooqpqpoonook^JFNQUTUXTRUTTTTUVXWWVVWWVVWXX[[\YZY[YXXXY[YZ[\\\\^[\]^][[\[[]^^`[][[[[[^____\[\]\]^^_^_^`_`aa`ab`___`c_aacaaaa``aaa```bccbdeegggeicdefgghegjigfeefegeeeffffjhggfeffefhcdeeeedfghihgfhihhhjgghiikhliggjjjifgiihhijihhgghjjjjiihijijmmjhiijlhfffedgghiijjkjiimkmhjijhhihhhhifiiijjkkkiimmlhhgigfghgfgljkfhfjiiiiifgihhggffgfffeeeeedbbbcbaaacbbabbbba``````a``bdbaaa`bcc_a__\`[[[\]_^a``^]]^]\]]]`]^]^[]XZZYYZYWYXWXXY[\[ZXWWVUUTTTUTXUTSUSUSTTTRSRTTTSQPORRSSRRRQPPQQSSQRRQQQNOPOOQPPNLMMNNNPLLLLKKLMMMLLKJJIIIJJJIHHHJHHHIIIIHIJHHIJIJHJIHHJIIIHIKJIIKLKIJJIHHI__`__b```aaa``]\TLC:2)*67:-#""" #,**=WUVC3 )4BFQ:&## -5Qkkkllllnoooopppopoook`IHLQUUUVTTTTTUVVVWVWUUXWVVWXXZ\[WZY[XXXYX[YZ\\^]]\\]^]]\[[[]]]a^[[[[[[ZZZ\[[[\\\\]^^_^^_bacb``cb`aa``a_aabaaaabbbba``adcccbdeffeedddefhghffhigigeefgfffeffgihhhghfffeecceddeefjhjhghhifgghggfiiihijhghjijhfjlijjjijihhhihiiiiijiiknmjhkkkkhgddfeefghhijjijjjjmhgghhiiihghighiijkmmmjijjighhhfgggggfghgeffgjiihhhgghgffefgfeeeeeeedbbcccaabcccbaabb``````caaaaaaa``````^___\\\[[]\^^^_^^]]_^^]]^^]]]\YZYYXXXZYYYXWXXZZ[VYXWTTTTSSSSTTTTSTSSSTUSRRRRSSSRRRRSSSTRSQQQRRSSQQQQPONNNNNOQQMMMMMMMMLLMMMMLLLLLKKJHIJIIJJHHGHGGHHIIIJJIJHHIIHHGHHHIHHIIIIJJJKJJJHIJHGHF``b`_aaaba``cba][VSIA4'#,9CLD9.))((*-1<N\aSC/*28;0%# - - %HeknnoonoonooopqoopqoolaIFLRUUTUUWTUUUUVVZWWUUUVWWWWYZZZYYZ\YZXZY\YY[[]^]^]___]^[\\a]]^\\[[]\\[[\][\\]]]\\]__a`_`aaa``accbbbcdbaabbbbbbccdbaabbbcgbceghlgffffgggggggggfiikghhggfffglihhhhjghfdccddcdefihhhhhhihghihhfiiihgghhhiiijjjjijljiijjjiiikjjjjlnkklmjhilmlghecdeeegiiijniliijmhggijjkkhhghggijjkjjjjijjhijhhgggggffhhgfffegihhgffegffdegghegdeefdedccbbbbedgdea``a_`a```ababab`a`b`b``____\\[[[]]^]^^^^]]___]]^_]\\]YZZZYZY[YYYXXZY\ZZXYVWTSTUTSRRSTTSTTSSSSSSRSSUTVTTRVRSSRRRRSRTRRRTTURSOPOOONNNOQNONNMPNMMLLKLMMLLLLJJKIHHHHJHGGJHGGHHJJKIIJJHIHJHGGHIGGIHKIJIJJKKJJJJMHGHJF``aaaaaabbbacba_^[YWKA0#19LLOC<61.-28@LYgbQA) - - -)2<2# - -4[ilnonnnppnnmpoooooponlaHFJQSUTTUVTUVUTVVVVVTUUUVWWXYYYZYYXYXXXXXYXZ\\^]\]]]]]\\[\]^^]][Z[]]\]]]^^Z]\]\\[\]^^^\__`ab_``dgcacdcdbabcbbbbbbcaaabcddccdehjigfddeffhhgigghdhfgghjhigfggjjhjihhffgeddddedefihhhgghjkihhhhgijhgggiiiihhjljijjkkiijjkmjhhhijkllkklmjjikjigiffdeeefhiiikiihiiighhiijmkhhgihhiijiijiihijjjjjighhhggighgeffffggggfffffgeeeefefdddeeddcccbccddddcabab`aca``_c``_``a`aa`____^^]\[ZZ\_]\^_^[\\^]]\\]]]\\ZXY[YYYYYXYYXWYYYYYVVUTSTUTSRQRSUVVVUTTSRRRRSSSTTTTRQQRQQQRRRQRRQSRRRQRNNNOMMLMMNNNMMMNNNNMLLLKJLMLLLLJJIHIIHGGGHHGGGGKIIIIIIHHGGGGHHHFFHHHIIJJLKKJIIJIFGHHGa`aa`abcbaabcbaab``\WK<'$1:GMKJDE;79<DQYdedQ@ )27% - %@^mlpnmnlpponnoprpqpqonkaGCJPSUUVVVUUUUTVVWVVVWWXXWWXY[YZZ[YXXXY[XZZZ\\\\\\]\]]\\\]^_^]][[[\^\a^^^`\\]^^_\\]]]]^`_bbcbbbdeeccccegccbbbbbgbcaaaabdedcgfihggifeeffhgggfghhhfhgggfghhhhjhgikigfffffeccfefgihhhhhhjjkhgghhijgfgilkkjiikjjkojjkijklkllhihjjmmrllmmjkkljiijjjfedhgjiijmjijihihihjjiijjiiihiiijjjlihhgikijjliijkhggffffggdeeghgfegffefeddgefdedeffdbdfdhdeeddcceccadcb```d`a`_aa```_____^_^_[^Y[[[]]]][[\]]\\\\\[[\YXYYYYXX[Z[[ZX\ZZZYVVUVVVSSRTQQSTTTVUUVSRQQRSRRRRSTSSSSRRRQRSRWQQPQRPQQNNOPNNNMLMNONOOPONNOOOLKLLMMMLKJJKIIHHHHHHHGGIIKIJJIIHHIGGGFFFFFFGHIIIJJIIJKHIJIGFIHHaaaaabbccaaabbcdba``[UC.%&2=ABBBA@>:;BLVace`L2#&- 1OllmmmnomppppopppppppoojcGHJOQTXWYVVVUUTVVWWVVWWWWWYXXYYZ[ZZYXXYYXYYZ\[Y\\]^]_]_]\]^_^]]\\\\]]a_]\]]]\\\\]]]]]]__]`bbbbdcccccbcedccbccccdcbaaaaaddddgeiggggffeffggggfffeheffgegghhhhhgghkkffeeeeedeffggkiggghhjjjiihighigghikjkkjijjjkjijkkklnklkfihjklmmllkjjjkkjjjiiigfehgjiilkiiihihgghhfjjjkihhhifghijiihhhiiiijjiiiiihgfegfgeddfgggffgfeefddddcdddbcdddddddgbbcdeddeccaaaaaaaa`abaa`_`aaa_]^^^]\[ZZ[[[]\\[Z[\]^^^\\[[Z[YYXYXWWWXXXVWXXXXWVVUUUTSQQQTQRRRSTVTTTTSQQSSRQQRQRRRRRSQRPPPORQQPQTRQQOOONNNNMKMOOPNNOPONPONLLLLNNNLKJIIJHGHHGHHJIGHHJIIJIIHHHHHGFFFEGKHHIIIIJIIIKHIIIIIIIHbbbbbcecdbbaacfcccfc_YK8*&)4@==?=@?=;@FN[adf]C( &FdmnmmnpompoproqqqqqqqpplcGBIMPRUWWVWXUVUVVWWXVWWVVVWXXYYZZYYZXXYYZYYZZZZ\\\^]^^^]]^^__\[]\]^_^`^^^^^]\a]^^^]\]]^___baadcccddeeddddcddeedddbbcbedcchegejijhgfiiiffgigffiehfeegfjghhjhhhhhkjffeeeeeeefifgijfgfgijjmkoiihhiihjijjiknlokkkkjkllkmnkjiiijmkkmmmlkkjmkjjmlkihijghhkiijjiihhgggghiijkmjkjiijhghhiiiiiliiikkjjkjjiigffgghdeeffggggggjgeddddcdccbbcdcdccdgdccgfhdddccbaaafaa`___aa`_`db`^^^^^\\[[\[\]\\[\^\\]\^][[ZZ[[YXXXVWVVYWVWXZXVVVUVVVSRQURTRRQQQTTSSVSTRQRRSPQSRRQQPPPQQOOPQSSVQRQQQQONMMNOOMMMMMMNNMNOOPOMLLLLLLLKJKIHJIHHHHGHIJGHIJIHIIIIIHHHGGGGGGHIHJHHIJHIIKKJILIJIJIccccdccccbbbbefccdeea\QB6,.4:;=?@ECADHLS[bcgZ<& - - 7_gnopoooporpppoqpppqsppqmcHAGLPRTUUVWVTTTVVWWXWWWVWVUYZYZYYYYZXYZZZZZZ[ZZ[]]^^^^]^^^]__]\\]^a_]^^^]^`][[[\]]]\]]]___aabbbbeccdfdddedddcddddddcbcdcbdeeeihhggfgiiffghhgeeefffdddfefhggggggijghfffffgffgfffeefefhijjjjihghigghiijjjlkjiiijjiijkkmjijljkkjjmnnmnkjjjkkklkjijjghhhhjjjhihhggfgihijlljjjjijihhhhhhhgggiijklkkkkiiggfgdeddefdfhghggggfefgeddccbbccccgdbdedddddddfcdbbaba``__^^^_a`````_^c``^\\[[[[]]]]]^[[\\^][[YZZZYYXXWWWWYWVWWWVUVVVUTTRSQRTSRQQRRRRRRQQRSQQRRRRRRQPPOPPPPPPQRQQSQQPQQQONMNPNNNMMMMMNLNMNNONMLLKLKLLKJJIIJIHGHHGHIHHIJIJHHIIJJIIHGGFFFGFJGHHHHHHHIHFGGHGHHIJddecdddcccdccefddegffa]NA8478:==BDIHMMSV^_b`V>(! - - - -*HbnnqooopoopqqpqrqpqqqqqpmdG@GKPSSTUUUUSTTVWXYYWVWVVWWZYZYYYYYYY[\[[\[ZZ[[[\_^_^^^`^^^a_^^a^^__]_^[^__]]\[[\_]]]^^a__`bbbbbbbbccddddeeeffeedddebbeeccdieihhghffhhgggiiihgfffheedddegggjhgfhhhhghigghiiigjffefgggijiikhgghhghiiilmkljijjkllpihjjjjklljkhhjmonlmkjkkkkjkkkjjmghhhillkjjighhgghhjkmmkllklkjkhfhihlhiiiijjklmmmjijifhggecdfeefjhhhfgfffffeeddbacdccdfbbcedfffdddcbaabb``a`_^]^_`a`c_``_`a_`]]\[[[^^^]^^[[[\^`\[YYZZZYXYXXWXWVVYXXXXVXVUTTSSRQSTSSPTSRQPQQQRRQQRRTRSRQQPPPQQQRQRQPPSQQPPPPNNMNNMNQNNNMNOMNMMNOORLLLMLMMMJIIJIJHIHIIJIHHMJIHJHIJLJJJJHIGFGHHJFIGGHIGGHHHHGGGLGIIdddcddddddeeeeedeefeed_YPGB>=9:;?BEHIKOVY[\]\JD?0" 0*% $=WpnnonnnpppnqsssrqqrroqqqndH@GJPQQRRTTTSTSUVWYYWVWVUWWXXYYYYYZZYZ[\\\]\[[]\[\\^^^_^]____^^^]]]^]^``___]\]]\\\\]]^_`a```abcbbbbccccddddddfgfdeeecdddddddeiggeffghijgigfghffeeffdcddfdgegigfhgghhhifggggiggfeeddddgiiihhgfggfeggimmjklkjkmlllkjjjjkjkkkkhfjlmnmolklnklkjkkjjihilijkljjjiiiihhgimlnmkkkkjkiihhhhhihhhilkjknlkljjjhghifeddefddghikggfefeeeddddcdcacbbbccfcdeedcccaa`ac`aaa__^_^```c^_^____`]\\[[\^]`^^]\[[\\\\\ZYZZZYYXWWWWWWXWVXYWUVVVSSSTURRRRRPRRQOOOQSRRQQRRRQQPQRPPQQQPPPPOOOPSPQPPPNNNNNOOONNMMMMMMMNONNMMMLLLMKKJJGHGHHIIIHIMHHIHHHHHIIIIJJIHHHFGGGGGGGGGIFGGGGGGGGGHIIfddefddefeffjefegegfgdfa_VTKE?<<>@BFFKMTW\^_`YXTM3 #AS>,! 4ToonnnooorrspqrpsrrqrrruttqeF?EJORQRTWVVUSTUVWXZWVWXXXXXXYYYYYYZYZ[]\\\][[\][\]]]]_^]a_^^^^^^]]^]___`^_\]]^]\]\^\^`bbba`accaaabgdddededddfffdefgggggffefghggffggijhhhgffgggifefddfefegdegihifhhhhhgigfgiihfdebdeefgiiiggfgeeeihhjmjimmjmmmlnlmlkjkjlkjiihjklmmmnmmljjjjljmjihljijiijigghjjijjjknmmjjkkjljfhmjkhjhiiilkjkllknjhiihhhffedddcehgjggggghfgfgdedcccbccbcdcfdfdddbcdaa`______```]]^``b___`ab`_^`\\[_]]]\\]^^^^]^\\[\Z[XWWWWXXXWYWWVWZVVWVZUTTTTSRSTSRSRPOOPRRQQRRSRRRRPQQQQQPPOMNOONOOPOPOPPOPNMMPPOONNOMMMONNNLMMMMMNLMLKKJJLHLKKIIHIJHHIHGGHHJIKKKIJHGGGFFGGGFHHGIHHGGGFGGFFHKIgeeeeddefeefgffffeffheedb^[XQKHFEFIKMOTX]`becba`VJ6%6VTVC6/*+-3:NlnnnmnqqpporpqqrtrqrssstuuseE>DJNPPRUVVWWTVWVVVWWXXVUXXYVXXXWZYYYYZ[[[\\Y[\\Z\_^\]`^]_`^_^]^a^]^\_^```a`^^^^[\[[\^aabbbbbbcbaaacddddcdeedeefdeffeffggecffgghgffhhjghgggffhghgfeddddedddfggggghhhihghfggiihfeeffffgghkigfeedfefiggjijllkmmmlmlkkkkljjjihiijkmmmmmmllkjjjkjjkjiigikjhjihhjkjjkkkkkkjiijkjjkiijjigjhhkikjjjllkjhhiiggggggjgedeeeeefffegffffddccbbcdeeedbbcdedfbba_`__^^]_ca_^\\\_`````_aab`^]]\\_]]\\\]^]\\\\\\[ZYXXXXXXVUXWYUVVWZVVVUUUVWUUTSSTSQRRQQQRRRQPQSSQPOROPPPPRPPOOOOOOOOQPSPOOOONNNOOOONNNNNLNONNLLMOMNMKLLLKJIKGHHIJIHIIIIIJFFGGJGHHHHGGFEFFGHGGFGGFHHGFGGGGFFGHIIgfhggffgjedefghghhgghgffged_^[XURRSUW\`agehhhhiebVQ<+3N^f_ZTOMKQVZimsorooqrpsrqrrsustsstvvwvxteE=CINOPUUUUTTTVUVWVVWYWVUYYZWXXXYYYZZ_Z[Z]\[[[[\Z\]_\]]^]__^^_]]]]]^^__`__``_^__]^]]\`abbbbbcbbdccccdeccddeeegffffefedeeeffffihihgghijiiggghgggjgeffffeeddegijghhhhiinhkhijjhjfeeefighhhiifeeeeffeefgiilkllmmnmnmkjkllkjjihljjlmmppqmolmkmkmkkkmjjiijmhkkikpmnllkmlljiijjnkjjliiihhihgiiijihjmjkiijlgggjhhhgfeeeeeeifgfhfhffedcdcccfdddeccddcccaaa_`___````a_^^`_^__`b`__``a^]]^\`]\]]]^]]\[\]\[[YYXXZYXVVVXXYVUUZWVVVUUUVVUTUTUTSQTRSQQQPPQPPSRQPPRQQQQRRPPPQOPPPPPPPPPNMMMMLLLLLNOOONPLLMONLLLLKMNLLLMLLJJHHHIIIIKIJHHJFFFGIIIIJHHFFFHIIHHGFFFGGFGFHGGHIGIIHHffgigfghhffhhighhgghhiihhgebab_\]^__`abdffhiihggc]XSB4=J[khgc`_\`chknprnooppposqqtsusrsstuuvvvvveF=AIMOQSRUTTTTVUUUUWWVVVVUUUVXWY[[[ZYZZZZ\^[[ZZ[Z[[\[\\]^^]]\\]^__]^\_`______``^\]^]\``bbcbbccddccdcccbcddfegffffeefffjgeeeeeghihhjhiiihffffffggefgefffefeehihghhiiijjfhiiiihjfeeefffhjiiheeffffffefhhijkkllmmnnnllkkkjjlkhjiklmmonmklllkkjkkkmlljjiiiiklmnlllllkllkiiiihijjhkiiihhjkkkiihghiihijjljgghgfihgfedegfefeefffgfffffdcdcfdccccb`bbcba____`^__^__a^^____^_bb_^^`__^]_\Z[[]\\[\[[Z\_^]\\YYXXYYXWWWWVWWUVWUUUTTUVUUUTTSSSSQRRTPPOOOQPQRQQQPPPPOONPPPOOOONPOQOONONLKKKLMLLLNMMMNMMMMMKLLLKKLLLLLLKKJJHHIIIIJHGHHHIGIFFHIHIJHGFGGFFGGHHFFGIGFFFGGHGGFGGGHhgggfgfihfggggfgeghhhhhhjhhgfffeeghgghjjiimmljniidc]ZVT^kkljijjjjllnsssoppsqqqrqqttsssuuvuzwyyxsgF=AHNOQTSUTTUUTUUXXYWVWXVVVVUYXYZ\\ZY[Z\Z[\\ZZ[^\\[^\[\^__^\\\]``_^____^_``_^_^]]]^^^a`cccceceddcdddbabcbcdceffffefifggghhhhglllhiiiikihhigfeeghffeeeeffeefhhhhjiiijjkihikihhjhfeddgfjiiihgffgffgfghgihijjjklmmmnlkkjnjjkpjjimlmlnmmlklmmmjkkllkjjkljijnlomllommkijjjiilhghkhjijjljjkhikihghhijnkkjihhhhfihgeeeeefffeigfgjfhgffffgegcddedebbceabbc_a^^___`_a^_`b_`__`b^_]`_^^]]\\\\][[[_Z\Z\]^]][ZY[[ZZZXXXWVVVUUWUUUTTTUUUWTTRRRSRRSSPRPQQRQQRQQPPOPRPNNQPROONNNPPQPOOPMKJJLNLLLKLKLMMMMMMMMMMLLKKLLKLMKJJJKKKKIHHGGGHKIHIGGHIHJLHJGIFEEIIJGFFFGHFEEFGIGIHHHHHhhhhhhgggegfgghhhgihiiiijiilhhhhhhgfghiijkllliiiijfd_]akklmnopllmpporqqopqqqrsrqrstrtuuuuvvvxwxphE<@EMPQRRRRTUUSUUWZXYWWWVWYWUXYYZ][ZZZZZZ[[ZY[[\\\[^\\\\\]]]\\\\]]\]^^^^\`__^_]^^_____```ccccbacbcccbbdcdceddfgffffffffggggggiikfgfiihiijiggfffffeedeffffgghhhijkjijjiljihhhhijgfddfffgikihgffeeeghhhhjjjjjkmmmmrkijjjjkjjjjijmlllllmoomlljjjlkjijkkijkjjkkkjollkjjjiihhhggihhijjllllighhhgghiklkjiiihhgeggfeedeeeggefffggffgfffedcfaddeecbcbbaa_`__^_`_^_`b__`a`___`___\^^_`^\\\\]\[[[\ZYYZ[\^]\[YZ^ZWWWWXWVVUUTUUTTUTTTUUWTTRRSRRRRQPQPPQRPQSPPQRPPOPONNMOPNMOOOONMONNLLLLLLKKKKJJLLLMLLLKIKKLNKKKLKMLJIJJKKJKIIHHGGHHIIIIIIIIJIHHGFEEEEEEEEEFFFFEDFGHGIJHHHHiilijhligeigihhhkiiijjjjkjjihimikiggkihjkkklmjkjjijhfjmllnpomnppoqsrrruuurrstssstssstuxxyvvvxwwshD;>CKPPRQSQSTTTUVWZXXXXZXXXXXXYYZ\[ZZ]YZ[[[[Y][[[\\^]\\\\\]^\\\b]^]]^^^__a___^^`^^____`aacbbbabdcecbbccccbecehggfggfgkffffgiiihjgigjihhjiigdfeeeeeedehgffgikhhiijmikihjjkjijhghgfdefgghkkkiihfghhghiiijjijikmkmnnkjmlomlkkjjjijkmmlllmnmlllmmmkjiimmkllkjnkjjolmkkkihhgghghkjijkkkkijjiiihhhikkjjiihhhgfeggffeeeddfgedefggffgkhhfddefgeeebbdbcb`_b`ba``a__`bab`cab__]]_^^_^_`a``]\\\[[[]YYYYZ[]_\[ZZZYXZXXVVVVUWTVVVSRSUTUUXTUSSQQQRQPPRPPQRQSRPQRRPONNONNMNNMMPONMMNOOOMLMPNLLKLKJJKLKLLMKKKLKLKJKKJJKKJIIJJKKKKLJJHHHHIIILJGHHIJJKGHEEEFFFFEFFFFGHEFGHGGHGHGGiijigeb_begghhhijiijjkjkllljkkjijjihiiijjjkjkjkkkjjkkkklmoooljklpqrprstusqsttssttttsuvxvvuvvvvwvfC:=CIMOPPPRUUUTVYXZXXXXXXXXXXXZYX\ZZZZYYZZ[ZYZ[[[[\\\\[\\][]]\\]]]^^]]^^______^^]^a```_`abgcbbcccdbccbbccdeeeheffgfghggfeffggihgfffggihhhigffeeggfgggghhgghhghjiiihijijjjiijhhhggfgghhiiijiiiiijjihijiiiiijkllmmmljjjlkkkjjjjjjkmmkkkmnnmlmmmmjiiknmjjikjnijkmlllllihhhghhhjkjllkkkjjjiiiiihjiiihijhgheeegjhifeegdffeeeeghhhggggfeeeffeeecbddba`_a`baabba``a```aa`_`]^_^`^]___^^]\[\[[[]ZYYZ\[[^\Z[Z[ZXXWWUVWUTTTTRSRRRSTTTTTTSQPPQQOOOPQPPPPPPPPQQPNNNONOPNMNNNMNMNOMLNMMMNNMNNNLKKKKKKKKLKMKKLLKLJIIIJJIHJJKKKJJJIHHHIIJIJJHHHHHHHGGEEEFFFGGGFFGGFEFGGGGGHFFHjikifZVZ_hjimiiilkmjllnkomnkjjijlkkjjjjknlllnklkllmkkklmmmmnnjdjoqrrssttsrsttsstvuwuywywxwyvwwyveC9<BJMPPOPSUVVWXWWXXW[YXWWWXXZYZY\ZYY[ZXYYZ\ZZ[\\[[\]\[\]^[^^\\^^^^^]_^_`a___b_````c``````cdbcbbcdbfcbcdcefeehffffgghhgiehfefihggggggjiiiiffddehfgiijjhhhhhhhjjjiihijiinjjiihhiihjffhjiijmiiihilllikkijjjjkjklllmniijkkjjjjjijklmooollnpmlmpnnkjknnmjijjjnijknnnlmlhhghhiihhkkkkklmljjjkijihiiikijjjhiefegggghfffefffeedgkknghgjhhfehfffedddbaa``bbbbdceb``a`d```a_d^^]]^]]_`_^^]\\_[]Z\\[Z[\[Z][ZZZZZXXWWUUUUTTTUSTRRQRSTTTTTRQRSRROOOPPPPRPQQRPPPQMMMOOQPMMOONNONPOMLNMLMMLLMPNMKKJJKJKJJKKKLLKJJHIIIIJKILKJKLKKJIHIHJHGHJIGGIHGHGGJGGEFFFGGHFFIGFFFGGGHFGFFFkjjhcNKKaijijjiikkkjkkkkmkkkjjjkjjjkjlllmmllmllllmllmmnonmle[T]ipsrusrsrsstsssttuutuwvwwxwwvwwvteB8<CILSOOPSUWVYWWWWWW[ZXWWVWXYYZZZZYXZ_YYYZZZ[Z[Y[]]]]\\[[[\^]\]]^\]]_^^_____`_````a``bbbaaabbaaacbcddccdeffefhfhfhffgfeeffdefegihdegffggffgddehggjighihhhhihjkkhhhiiiiihihihhhhhhghhjiijjijkjjkjjjkkkkkkkklmlkkikjjikjjjjijhjjllmmmlllmlmnnnmkjmlmkkkmljjjjjklkkklihhiijjiikigkiklkjjijijihhiiihhhihgffefffffefjhgggfdefhiihgdijggggfddeeddbbdb`aabbcdcbbdbaa_`````^^]^_______^]\\\[[Y[^]\\\[ZZZZZYYYXXWXTUVUTTTTRRQQQRSSSSTTQQSSSRNNMOOOOROPPPPPPPLMMOOOONMONNMNNOONLMMMMMLLLLLLJJJKKJJIILMLLKKKJJJIHIJKIJKKKKJJIIIJHIHGGHHGGGFGIHGHGFFFFGHGGGGHHHHFGGFFEFFFFnkmj_F=JclnjkkiijinmmmlmpjkkjjiiijkkjkmklllmonnmnllnooopolkaJJUjpsqrssuvuttsttuvvvvu{wxwzy|xzxvudA7;DGKONOOSUWVVWWWXXX[ZZXXXXXYZ[ZZZ\[[ZZXW[ZZZZ[[[^^^^\\[Z[[^^\\^]]^]`^a^^^aa`a````b``aaa`abbbbaafbbeecbdgfffefffefgggeddeeddfehghddeefghgfhdffjhhhjhfgiihhkijlkhjhjjkjjiiiiijiihhilijjijkjjjjkmknnlkllllnmpnnlnkplkkkjjjkkkkkkmmnnnlooompnmmllkkjjjkjlnjlkkkkmpkjkkjiilkmkjihhliikjjkkkjlihhjjigghhhggffhfefjggiighhgfffghiigfigggjfdeddfddccdgcccbbbcaabccab_c`aaaab__^_^`^^]]]\]][ZZ\\]\_[\[[ZZYYZYYYXYUUUWUVUTSTQQRRSURRRSQQRSRQNQMOOQPROOPQQPPPNNNNNOOPMMMLMPPPPPNMMMMMLNLMLLLNKKJLKJILLMLLKLJKJIJIJJJKKMKJJIIKIKHHHGGGHIGGFFHGHGGFGHGHHHHHHHFFHGGGGJGFFHFlkjl]@>Jfollf`bdccgjhgmnmkgcdeddefhlkkkkhdfgghjnmllolkjijjc\D<UjqtsrrtvvuuusqqomllqsvwvwyyzxzzwucA7:DGJONNPTUWVVVUVXXXXXYYXXXWXZZ[[YZZZYYXX[ZZZY[]]]]\]\\\\\\]]\\]]\]]^^_]]^^_a`aba```aaaaacbbcdbabbcdccdfgfeefgfeeefgfeefegefeddefeddeffffeedefgggghhggiihghhhhhhhikiihiiiiiijijjiiihjkjlkiiijllikllllkmmnmmmljjjlllkkjjjklklkjmmnnnklmmmnpmmmllkjkkjjmljkllllnmkjlkjjjlikkjihhjkjjjhigkijihihfgggjhijjihhefeiiiihghhgghghkijhfghggifffdddccccdgdcbbbbdaaaaa``_`^aa```^__^]]^^_^\\\\\[Y\Z[Z\\\[ZZYYYZYXVVXTUUUUVXTSURSTRSSSRQQQQQQQONOMONONNOONQRQRONOONNONNMNONMMMMMONMMMMLLLKMLLKKLJIKKJILKLLKHIIJJJJJKJHIIJLJHIIIIIHHKHGGIHGGGGHGGGHGGGFGHHIHHHFGHGGGFGHGFEEmkolZ<8LigiYSWXZ`ce`^enll^YYZ]`bcdeimgd_\^behggfemnkkfbehebK9<Vjsokmquyssokmonnjfipuwwyyyyyxyyyua@68CFJNNNQSUTSTUUUWZX\XYYXXXWWYY[\[[ZXYYYZZZZ\YZ]_]\\^]Z[\\^]]\\]a\_]]]_^a_^_babbabb_ababccddcdbbbbedcdeegeeefggggfffeeefeeeeedfeiefddgfeffeddejhhhhijgkhighihggghilihiiihikjjikkjihhijikhikhhjkkkkilnmpppnnllmqmmlmllkjjjkklkkmmqnomlmollnnmommkkkmkkmljjllllllkjlmjjklmmlnighkkjjnjjillmjiggghghhjjijkihgghiihhhhigggggihghkhhhgfhfefddccccedfddbcccdaabac`aaa_aabab``a`^]^^^_]_]_\[Z\Y[[]\][ZYYY]YXVUVVUUVZWWVTUVRWSRSUSRPQPPQQPOONNPOPNOOOPOPQPOOPOOOONNMNNOMNMMNPMMMMLKLMKNLMLKJJJJJKKKKMKKIJIKKKKIIJJJHILJJKIJHHHHHHGGHHGIGGGHGGGGGIHIIHHHIIGHGGGGGGHFFFFkkllW98QfdTIRVXSVTQNXdijWLMPWRQPRU]gd^TN^TXWY_[ZfoifXTZ]][SB8>Vidafjrvqmb_dhec]X^evyyxyyyxyyyxytb?58BFKMNOPRSSSSUUVWWWWXXYXYXWXYYZ[[[YYZY[ZYZZYYZ\\\\\\][[\[\\]_\\\[\]]]^^_`````a_____`aabb`a`cdcccccddfeeeeeefffffffiegeeeeeccdcdedddcgfefhedeeghijiihfghggghhggghhhhhljjiijjjiihikihkkjjihkiiikkkllmmmoopnnklmlklllllnnmkmmllkllmmnonmmjlmnmmomkllnjllklkmllllmmmlkjkjjjjjjghhkljkkjjijkkkifgggeifggjijjjjjjiiilihgggggggfghiiggggffefeddcccdccccdbaddbca`a`aab_`baaabcc`a^]^^]\\\[YZ[ZY[[[[[ZZYZVXXXWVWWXWVWWWVVUVRSSSRRRSPQPQNPPPQNNNOONOMNNONOPONPPPPOOOONNMLMLMNNMMLMMMMLKLLLOKJJMKJJKJJJJKIIHHIKJJIKIIIILJIIIIHHIHGGGHFHGIGGFFFGHGGGGHHHHGEGGGGGFGFGHHGGGlkjkU78TeOBJ]_d\WQDHTijRCHQ[gZPHAJXa`LER^\YVNJHWihdODQV__\QE9@VjZSRhurnYP[^ea_TVZl{yzyzz{yzz|z{tb>36AEJMQQPQSSSSVUWVXYYX\XYXYWWXY[[ZZYYYXYZY\[ZZZ[\\]^^\[\_[[[^^\]]\[\a_^^_``a``a__`a_a`aacbdbecdceegefdcehgfeeehgggggedeffddcbdccfcddccceffffhfegkijihfggffgiihhhjiihhhhikijikijiiiijkkklllkilijjikmmooopponmmmlkmlmmnmmllmonnkklmmmmpnlllnsmlmolmlnlplllklnmnmmmlkkjnlljjkkkmjkljkklmkjkojiiiikgjhhhjjlkjjiiiijljihhlhjfhfghhggffgjfdeefeeechcdccebbdebaaabbaab``baa`_^```_^^^]\[\ZY[\ZZ]Z[[[Z\Y[XWWXWWXXXYYYWYXWWVUUSSRRRSQRQQPQQRQNNMOPOONNOPPPONMPPOPOONOOMMLNNMMOMNNMLLLLLLLLLLJJJJJNKKJJKKIJHHIKJJJKIHILJIIJIJHHHGGIGHHHGIGHGGGFGHHHHLHHIGGJIIGGFHGIHJKJHnmkiQ5:VPA?N_ife\OBC^lU@AI[igdVD=?ZeM<I_diebP@DVkfO<FS`mhgZM;AZmTAMflmWFQ\gqmlb_hy{zzzzzzyz{{{wsc=25@EJMORPPQQRTUSUVWYXXXXXWXWWWX[ZYYZYYYYYXXWYYZZ[]^\\\[[[Z[\]]][]^\\__^^`a`bbc`__```````a`aaaabcdeedddddgeddfeihhihgffeeeddfddcbcacbccccefffggeehiihffghggiiiiihhhhigghjkiihiiijiiijjjjkkkkiiiiijjkkmnnqponlkkjjkkllkjkklkkjllllkkklnolkmoollmmklllklkklllomnnnnmklikkkjkkkjjhkmjkllmmmkkijjjhggiiijjklkjnljjkklkkihhfgfhgghggghggffffeeeddcccccdecbdccba`bbbba``bbb`__```ab_^]^]\\Z\_[YZZZ[[Z[XZXXXXWVWXXYYXVVTUWUTSSSQQQRRRQPNQRQQOOOPOLPOQQOPOMNNNNOOOMOPNLKKNMMKLLNKLKLLLMLMMKLKJIKLNJKKJIIIJHIJKJJJIHIIIHIIJHGGGFGFFFGHHHGFHGGIFGGFGGHFHIHFHIHFFFEEIHHGHIonoiM4;VO=>VnkkieI:Jj]J<?WokjfcE7@[O?=RbnllcT@B`nR>;J_nmokhP>A]\OCIiqVCEUhzwvqovzz~{{{|||||{{|vsc;14?DIMNPOPRQRUUTUVWXXXYWWWZXWVVVWXYY\Z[YXXYX[[^ZZ\^\\\[[[[\\^]_\]]]]]]^_`aabbbaabaaab```c_aaaaddddeefehefeeeffihhhjeefedggfeccdcbbeccceceehhgeeefghffffhhgigggghhhhggghijiiijikkljlhijjlmjkjhhkilkmkkklmpppljjijkkllkkllmmmkllmljkpnnmlmonmmmmmllljklkkklmommmnmmmljjlonnkiikjljhjmkklnllkllmigiliijllljjmqklkkljkkggghiiiihjhgggfgggdcdhffdbbdddcccccebaaccbc`_`ac`a`b_``ca`^^^_]_]\\\ZZZYZ]Z[XZXXXYWXVWXYXWVUUUTSSRRSRQQQRRQQQRSRQPOROOLPPQONNPNPNONOOROOONMLLNMLJKLNMNOOMLLLLMKLJIILMMKJJJIIILIIJJHHHHHIIIHIIKHGGGFIFEFGGGFFFHGGGFGIFFGGHGGIHGIIGFFEFIHHHHIomoiJ4=VC4DcnmorfFDR`\D5JhnomicE6B[J>>[lnooeK7Eh]G;=VqpqppkS>@\`L;Ii]HAG`xuuvvvwz|}|}|}|||}~||ytd:02=CHMNOOORRRTTVVVWXXXWVVVXYWVUUXXWWYYXXXXYY[[[ZZ[[Z[[\[\[\^\\]]\\]_]]^_```bbca`bc```__``^bb```acccefeedeeeeffifghgeffeegffeeedbcccccedcddeffeeffegeffghgfghgiijhghegghijkkmjijjjiihijjlkijiihhhkkjjihmklllkkjjmkllllllnmllklmlkklllmlmpommpmonmlllllkllmmmmmmlkmmljkmlkkkjijjjiiijjkkmlkjkllliilijjkijikmmkklkmkjiggghgigffghgfffggfdddefeebabbcccdcba`aba`aba__`b_a_^^`aa`_^___]]]\[\[[ZZZZZ[XXXWWWVVUVXXXWYVVTSSQRRRRQPQPQRQQRRRQQORNMMPNONNNNNNNNMNNNONNMLMMMMLJKLMLLLMMMMLLKJJJJIJKKMJKIHIILJJKHGHIHGIHHHHIIHHHGFFFFGGGGFFFGFFFFGGGGGGHGGHHHHHHGGFFIGHJIKsoohG4>BA=JbmprqeIEZqY=DYgurtldC6E\F8CdjrpqcE>Ig`>4GiptrspoQ=@[aJ@Ii]B9Qrwyxz{|||{|}€}€€€}€~~}{vc:/1:DGKLMOOQRSRSVWVVVVVVVZVWWVVUVWVVWYYYWXY[YZZZY\[\\\[^]^\\[[\\]\[\]]]`_b`_aabaaea_acba`__ba`_^_fccefeecgedfggigggihhfefhgffhfddfdcccccdcccdeffghgghiijhgffgghiijghglhhjkkkjijkkkimmkjjkkjlhoiihkkjiijmkmnmmmkljilkllkkmollmpljjjjjkllnnmlkmmoqnonnmmlooommnnmlllllllmmlllmknnmjjiijokmkkklllkjjlmmjnjlmmmnjjlklmjjhjjjhhhigggifdeefhfeeeedcbadddccbbcbaaaa``ac___c_a_a^___`b^a___`][[\[\[\]]ZZYYZYWVVUUTUUUUWVUTUURRRSRUPRPPPRRRRSRSSRNMNONOOOOPNNNPNONNOMONLLMMLLLLLMLKLKLLLKKJIHIJJKJJIHIHHKLLJHHHGGGHGIIHHHIMJIHGFFGGGGGHFFGFFEFFHHGGGGGHHHHIIIHIGFGIGFGHHqppgE4E[G;Kapv{„oNQiiYLJ\hsqrsaA;GPEEF_hqqq`B:Nh^9=QgpqqqqmQ9?^dH:IfW9=]pwwwz}{{|}}}}}|}}}}zxa9/1:EHJLLOOPRQQRVVUUVVVWWVUVUUUUWVVVWXXYWWZZXYZ[X[YYXYZ\[[]\[[[\^\[\\]\^^_``aaaaa`__a`^____`a`___bdegfdddeefggggggeghheeghifeeeedddcedccddedeefhhggghiggghggghjiijfffhhilkkkiiijikijkklkjjihhhhihlkjhiijkkkkkkiigilkkkjklmmkqnkkkkjklllnnmmmmoppnnnnllllmnnmlmllmlllkkllllmmkkljhjkigjkkkklkkkjkjkkjjlkkkllkkkkkjljihhghhhghhhhhfgiggggfeededcbedcccbcdbbbbba`^```_`^^^^]__^`_^__`__\\\[Z\Y[\\ZYYWUXXVXVUUUUVVWUTTRRRRSSQQPQPPORSSSRRQPOMNOONNONNNNNLONNNNOONNLLQLKKJKLLKLLKMLLKKIIIIIIIIJHHJIHIIJJIHHGFGGGHIIIIIJIJHGFFGGIHHGHFGGFFFGIGGGGGFGGGGIHHHHGGHHFGHHHqqs`C>JXQLL`tz…†yPSlzhSTajopsp_:5L`L?D[honqX?:Ui[>=OemoprqlH5@`bF9IeRABZns{y~|{yuwy~~‚€€~|y_8.1;DIIKMNOOPQRRSTUUVUUXVVUVTSSSUVVVVXXYVVYZXXY[Z[XXWWY[ZYZZ[]]]^\\^]]]]_^``abbaab`__``__^```a```acgfffggffhhhghhifkhgeeeeedefegegecegffdefgeffkhggggkknijgghiiljkggfgghjjjjjkikkjjjjijlijiiiihmmljjjjggjmlnlljiiiijknmmmmlllkkklnljlnmooomnorpponnplnllmnnolnnnoolslnlklonmkkkihjiihjjklmmkklknkjjjkommlmmkklkkkljiiihgijihhihhhiigggggeeegdcceccccdddbbbafa`^`````_a_]]__^_`^b`a`_]`\[[[YZ[[YXXWW[XVVVVWTVVZWUUURSRRQRPQQQPPQRRSRRRQONMOONNNNNNMNPOONNNNNNNOKLLMKJKKKLMMLKKKKJIIIKIHHHGIIIIJJJIIJLHHGIHKJIIIIIILIJIIGFGIHJHHHFFIGFFGGGGGGFGGGFFHHFGHHHHJIJJIIrqqYC=Sk\RV_o€ƒgMVozƒgWasponmS57P\]METiomnQ<:[icCCMailmorY?9Be_E:LdUACWkpyz{|ohmq|€€€€}|y^7-09AFIJONQOOPSQRTUTSTTTUUUTTSSTUVWVVWWVVVYXXXXYXXXXXXY[ZZYZZZY[^\]^]]__^____`a`aa``^`a`````aaaaabcefffhkfgfffgfffeeffeeheeedededeffffccdefeeefhiihggjjjihghhhhkikhgggghiiijmjijkkkjiiiiijhjkjijljhhhiegikklmlljjiijkkknolllllnlllmlomkmlllnllmoponmllllmnmnmlklllllllmkklllkklhfgghhijlnmqlklllkjjjjllllmmkjllkjkljljjjijiiggghiiihghhgfffgccccccbbdcbbcc_`a`]__`_a___^]^_^^`^___^]]\\[ZZZZ[YWXXWWXWVUTTTTUTVVUTSRRQRQPOOPQPQRQRRQRSQOOMLMMMNPONNNNONNNONMNNOLLKKKKMKJKKKJKKLJIIIJJGHHHFIIIIJHIGHHHHHFGFGEIKIIHGGHIHHGGFGGHIHIGFGHGFGGGFFEFGGGFEFGGGHHHHGHHFIJsrp\ENdjkfaelxr\VYsƒƒzjcholk\H4;Vib[TQ_olmM:GahiSFK]dnml`N94IjaDCQbfPFRinwvvl`Z[o€€€€‚€y\5+.7BDGJNNNONPPPQUTTRSSSSSTSTTSUUUXXWYVVVUVUVVWYWXXYY[[ZZYY[ZYZZ[[]^]\\^^a``^_``__`aaa```cbbaaabcgeefffhgffgfffffgeefifeeeefdeeeeeedeeehffeeeffghhghhliiihhhhlkjijihhighijijjjiiikjiiihhhjkkkkljjihkgiilkjlklkkjjijilkknmllnnnnnlkmmmmkoklmnllmrppnnmllmlmmrmklmlmmmmlllllklkkkgghgghijnmmmlknllllkjkklnlplkjlkkjijikiihhjijhjhhikijhihiffffbfdeccbcccaabc`]_`_b```a`a_`]]^`^`^^^^^]][[\\[[ZZYXXXWXYWXTTSUUVUVVUTSRQRSQONOPRQSQPRTPRSQOOMLMONNOPNNNMMNOONMMNNOMMLMMMLJKLKJJJKLJJJLIJGIIIIIIIIIJKIHHHHJGHIIGHIIHGGHIIHHGGGGGIIIIIGGGHHGGGGIEGGIFGEEFHHHHJHHHGGIIssra_\iqnkmnpssi`Wex€†~ynegfjWG5>Zgjfc_^negKKOcskaWM]^cd_VM8BRbcJEYqi]TN]knsh`YQap~€€€€€€wZ3*-7CDFHNMMMMNOOPRRRQQRSRRRSSSSTUUWVUVVVXVYWWVWXWXYXXYYYZZXXXXZYY\[]][[\]]]^^^_^^___`a__aaaaa``acedegffgfeegfghgffeeffefeedeeegfffhgfffffefeegghhghhhhgijhjiijiikjhhgggggggjiiiihjjigghhhiikijkjjiggfhjklkklllkjijkklllllklljjjkkkllllkljlonmnnnnnnnoonnomllkllmknnoplllljgijjjjjhghihjmnllmllllmkkkkkkllmmljkjkhiiijjiihiiihhhihihihhihghhfcfbcaabceca`aaa_^^^__`_____`^]^^]^^]]]\\][[[Z[^ZYYZYYWXWVUTSSTSSTVUUUSSRRRQPMNPQQQPPPPOPRPOOLLLMMNONNMMMNNOONMMMMMNMLLLLLLKLLKIJKKJIIIGGGHHHHHIIIJJJIIHHGGGHHGGHIIGGGGFGGGHGGHGIGHHGGGFFEFGGGGFFEFFFHFEGHGFHHIJHHIIsssigjutsrrrxxylimqv€‚~~khgh\G;AdupoljjkiaZWbsqtlld^^_eXUMLO`rgQXgpsjc]Zlff]^Ubqx‚€€€€€€€€€wX2(,9?DEGHIKLMONMOQQQPPOPRQRSSTTTTUUVVVUUVTVYWWW[XXWWYYXXY]WVWVWYY\[\[[[]\\]___^]^`^^_a^^__`bacaaceegfefgfeegffghfeeeeefifeeefffffffeefeeeegggggkggggghgihghhiihhiighggffgiihihijhmjihhhkhiikihhhihfffgjkkklllmjjjjkklmnnlkkpjkjlkkllllmmkklklnnnmroonlmonmlllnlmlonmmlknjhiijihihhhiihikmnmmmolkkjkllooomnmnjkjkjjiikkjkikilihhkiiijgghhilggggddefcccca`ac`_`_^_`c`e___`^^]^^^]_^^\[\Z[\ZZZ[[\[[YXXXVTTSRVTSTVVUVTSQRTQQNNPQQQPPPQPOPRPONPLLMMNNMMMQPQPPMLLOLMMMLKKJKKLNKLJJKLJIHHHIIIHGGHHGHKKKIJHHHHGJGHGHIKHHHGFFGHGHIIIIHHGGGGGGGFFGGGGGFFFFGGGGGFGJKKIHHGJssqlnrtssrrrtvummoruyz{|sjgie`J;Mlrsrpqrtvkhouvwupnmkhgfc_`bjqmkkiqzvsqomlkie`eqy€~€€€€€€wU1'*6=CCFGHJKLLMLMNOPQONOQQRRSRRSUTTSSTWUVUUUUTTTUVWWYWVWVWWVXVVWYZ[[[[[]\\\\Z\\\]]\]]^]^^_`a```abdddfdegfeddefefgddcbgefffffhfeeeeeeeeeeediffffgggffffeffghiihghhhggggfffjghiijjgmjijhgghjjhggggggggjiijkkkljkkjiknkkmomlkikkjjkjknmkknmklnmlmpolnmmmllmnnlmnlkllmmmmljkjiiihgggfghigiijkmmmkollklmllopmnnlkjiijkiijkjjihhijihgiiiihhgghhieeefedcccbccbaab``__^_`_^a``__^]]]\\\]]]^\\[[[[ZYYXWWXXWWWWVWSSUUTUWWWVTSSRRRPNOOOPPPPPPQPOPQONPKNNNNNKMMMMNNNMMMMLLLLKKKKLKKKKKIJJLJIHHKIIHHGGHHHHHHJJIHHIIFHGHFGHHIHGFFFFGGHJHFIHHIGGGHGGFFFHHGGGGGGFGGGIGGKIIHHIIIstwvttvsvvvtsssooqutwy}xkebhb^NPWqzvuuuvwtsvxw{z{wusrrrqopqsuv|ttu~{xwwvvxzvsuwz€€€ƒ‚…xS.%(3<ABEFGHIJKLLLKKKMNNNOPOOONPSTTUURSUUUTUVUTTUTTTTUVUWXYWVVUUUWYXXY[[\[\\][[ZZ^_\\]]]_^^`a`a`cceccbbdgggdddfedcabbcgehgggggfffeddeegfeddeeeeffefgfddeeeegkjjhighgjhggigjhiiiijgliiiiiiijhgfighgigghhijkklnjilijjkjkonmmnkjkpjlkkmnljnlllklllnpnpnrmlklmplmlllmkllmmnjlkljlghhkhjikjkjjkmmmlnmmlomlmonmmnlmiijijkkkjjihhhihhmhhhkihhhgihifeegfcdbbeeebbbd`b``_____aab`a_]]]\]]\]\\\\ZZZZYYZYWWWWVVUWUWSTVTTUWWXUUSRSUQOOPPQQRQQPPPPPPPOOOMNMMMMMNNMMONNMONNLPKLLKKMKLKLKIHHJKJJHIIGHHHHIIIKHJIJJIIJHIFGGHGGFGIKGEFFGFGIIGFJIHHGGFFFFGFFFHGGGGGGGIGHHHHKIIIJIHIstutttutvuututspqruuvxyunbfgf_]]gvwwvuxyxuwxxxxxz{xvuuutuvvvwyywx|}}|{zz{{yyz}}‚€€€€€ƒ‚‚ƒ‚ƒ€xP+%&1:=@BEEFGHHIJKJIJJJKLMMMNNNNPSRQQRRSTSSRSTTTSSSUTSUWWWVVVWVUUUVUVWY[[[ZZZZ[ZZ]\Z\\\]]]^a````bcddbbcedbcdddedcabcbceegfghhhgfeeddedcddccdcbfggdefeddgeeefffgggggghgfffeffieiihgiiiijjjghggfffghhhgghhjnmlmijllkkjkkonnnmjijjjkjklmllnnnllllmnnmmmomnnkjplmllijjjkllnjmlljighhihiiihkjjkkklkmlmmmlmmmmmmllmijhjjlkjgjiihhhhgihhhiihhgfgihgffffeebbddcccccaba`__^_a_^^^`a][\Z]^[Z\]]\[\\\YXY[WYWWWWUVUWTSSSTUUVVUUURSRPPPPRQPPPOOOOORPQQPNMNMMMMONNMLMMMLMONJKKLHJJMKJIIJIIIIIIIGHGGIIIIKIHHHHHIJIGGGHGGFHHHGGGGFGGFGGJHIHGGGGGHIGFGEGFFFHGGGHHGFGGGIIHHHHKIIHGtuutwtutwvvtussrvvuuvxxurrrrrolruwzxz{|z|{{{{yxz~{|{{{{||}}||{{|}}}}ƒ€‚‚€€‚ƒ€€‚ƒ‚‚‚ƒ‚wM)"$-7:>ABCDFGHHKHGIIJIIKLMMMMPNOPQPOQSSSRRRRRRSSQQSSRTTTUUUVSRTTTTTUWYYYXXXXY\ZZZ\[]]]\\]^__`__bbbbbacddaacfffccbbbbbedjggggghffegcccccccbceagggdeeeddefgfefeeeggkkkfhefffeihhhihjjjiijjgigffggggffggkhhihlmljklklmlkoopmmklkljkjlmmmmmlkklolononnmpmnlkkommlljjjikllmklmojjjiikiiijjjjjljjllmmnmlmmmnmmlqmmkkkjklkjhjkliihhhkjjjihgggggghgfggfeeccfcbdfccccab`a``_^^a_`^\[\[_[YZ]\_]\[ZZYXXWWWXVTUUUTWUSTVWUUVVVSRRSSQPPPQSQPOONNOOPOPOONMLKMMNMMMLLKLOLMMMKNLKIJKMKIILJKIIHHHIHHGGHHHGIIIJHHHHIIGGGHHHFHHIGJGHGGFEEEGHIJHHGGHHIJGGGGFGFHHGHHHHGJGGHJIGGIHFGHHrrrssrsstvtsrrrssstwwxvtqrsuroqwwyywzyyz|{{{{yz||{{{|}{z|~~}}|}~}~~‚€€€‚‚‚‚‚„‚ƒƒ‚‚ƒ…uJ'!#+48<>?@ACDDDDDEIGFGIHGHJIJKKLNLLLLMMOQQPONNOOONNPRRRRRSVSRRTSTTTUWWWWWWWWWY[[Z[[[[[[\]^^^^^__````_abb`abbbbaacbabbdeedddfggefcfccdbccccdcaddcbcddgdddddcdddceefggeedffeehjhghhihiigfiggeffhjhhgehggeghimmnlkklllkklmlkjjlkjijjlmmlmmkkklklmmoomlnmmjkkllljjjjljnllkkkkkkkliijjjjijjjjkkjkllklllnnllmllqmlmkkkkkkjgkkkiighhhfffggghgfggffgigeeedccccddccbaaa^^_`]]]^]^]\[ZZ[XYY[Z[][[ZZZZXVVVVUUTVUTVTTTXVUUVUUSQRPQPPPQQRQPRPPOONMNNNNNKLKLLMLMMLKKKKKLLLJKKJJJKKJIHIIJIHIHGHHHIIIHGGHHHHGGGHHHGGFHHHGHFGGGGFDEEFEFGGJJHIFGIHFFGGIGFFFHHHHHHIGGGGHHJGFGFFFHEqqqqrssrqqrqoprrtsrssstsqrvvxwwvwwywzz{{|}|~{}}€~~~|}}~~€}€€~€‚‚‚„ƒƒƒƒ‚‚ƒ‚‚…ƒ†††„ƒƒ…„rG%"*67<;>=>?BBABBCEGGFEFEEEHHKIIJJJMLLKMMLMNNOOOLKLMOPQRQPQPPPQRSTUUVWUUVXVVVWXXY[\[ZZ[[\`]]^_^aaa`__cba_bcdcd`abaacddddddfghhgggfeccb``abbbbdcbbcdddddhcededcegeeffffefehhhiigghgghhffhfffggkhhhigihieghjmplnkjmlmnmmnlkjjlkjjjjlmomlkjkkklmpmonlknmlkklmmlkjjijjkllrmqkjjmjjkllojlijkkkgjmklkmllmolllnnqmmljkkkmlmllllllgjihihhhhhhjghgihghideegcacgdedcaaab^]^`^[^__^]\]_[]]\[[ZZ]YZ][[ZXWZVVUWWVVTTTTTTUUTTTTTTURRQPPPPRPPQOOMMMMMMONNLKLNNPKMMOKJJLLMLKKJJJJLKKIIIJJLJIHIGIHHHIIIGFGGIHFHGIGFGGFHHHIIFFFGFEEEEFEHGGHIIIFGGGGIIHGFFGFGGGHJJIGGGGGHGGGIGFFHFijjjklllllmnmmooooqqpoprsrsvrqrttuvwwwx|{zzzyyyzzz{}{z{||}}}~~~~~€„‚…ƒƒƒ„ƒƒ‚‚ƒƒ‚ƒ„†……€pD$"#-69==>>>??@@AAAABBCBCCBBCCDDEFGHIJJJJIIIIIJJIHIIKMMMMMNPOOOOQQQRRRSTTTTTTUWWWVY[YYYXYYZ[[\\\^^][]]^^__`a```abedbffhhknswxz{|{zslhcb```````aabccdcccddbeddbcfedfcddfeefhghhhhggfeheegfegffgjfghgefgffggkkljlkknmmmllpljjkkjjjjklllmlkkjkjklllmlkklklklooolkkjjjkkllllmlkjmijklkkkkjjkkljjjikknnmllkllmmmmmljiijkmljjklnkghjihijhgggfgggggggebdefbcddddecaa`b^^\`__^]]]^]\\[[[[ZZZ[\ZYZ[[YXVVUUUVVVWUVTSSSTSTTTTSUTRRQQQPORPPQPONMMPLKNPMLKKNMMKKKKKKJKKLLLKJJJJJJKIIIJLKKIHHGHGGGGGHGGHGHGGGGGFFGGFHHGGGFFFFEEFEDEEHFGGHHIEFHHIHGGFFEFFFFGGHGJGGGGHHHHFGHGEEEabceeeeeeeffffffghjklllkknppoopqvuuuuuvvwxxyyyyyzyxxyxz{{{~~‚~~€~€‚„€€€ƒ‚………„…‚kB'#+19<=>@A@@?@BBBBBABBCBBBBAABCCDEEFGHFEFHHGGGHGGGHIJKKLMMNNOPOOPPRRQPQSSRRSSVVWWVWXXWXZXXYZZY[_]\\]_````accdeefiklpv{‚ˆ’ ¤§®µµ´¨™†rjccbab`c_aaadecbbbbbbeddcddffgdeegeefkghgghgefdhddeddgfffkggggdcddfghkknklllnnnnllllkjkljijjkpkklnnmjjkjjlmolkjllnllnnmklijnjklpmlmooollkjknklllknkklkklkklnmmlmkmlnmmmnljilijjjkllkkjggkkkjjgghijjjggfggeefeeeeecdfgfca`b_^]a^\\\]\\]\[[^^^[ZZ][ZZ[[ZXVWWUVVUUVUTUUUTSTUUTUTSSSSRRQPPPRQSRROQNMOLLNNLLLLMLLKKJKLLJJKLLNJKKKIIIKILJJJIIIIIJHGEFHHHGHHGGGGHHHGGGFGHGGFGGFFFEEFFDFEGGJFHHJGFGHIGFFFGFFGHFGFIHJHGGKGHHIHGHHEEFbedgdcdgecfefffffdeffffghghhhgjjjijklnnoppqrrrrrstttvwwwxwy{xwwxwwy||{|}{{||}}~~}~~}}}}~{eE06=AEFGGHHHFEFGGFFFEEEEEFEDCBCCDCDDEEEEEDDDDCCDDDEEFGHHIHIJKLKJKLLMMLLMOOOOOPQRQQSTUUVVWXYYYY\_acdfhknqstvxyz„‰”™ ¨±ºÀÇÇÇÈÉÉÈÈȼ²–€tjf`__`__^acbaacaabbbcbbddfacdefefeeggggfffeedccdgddehedfhgdecddefghjkkjljlnlkmmkjkkjjiijkkkllllkjiiikjjlnnmkjkllkmnnmlljiljkkonnmnlmnlkkklkkkklmkkmlklllllkkjklllmmmmlkjiiiikjlkjkkkiilkjkihfgghhiefffgffecedeeeffdddb```]]^]]\\]\\[ZZ[\^\[ZZZZZZZ[YXXWVUWWUTTTTTTSTUTTTRSPTSRRRQQPOOPQQRQOONNNNOONMLLJMKKJJJJJJJJJKKJJJIJIIHIIIHIJIHIIIIHHFFGGGGHFGGGGGGGGHIHGHFGIGGFEFFFGGDEEHGGFGGHHHHHHGGGGGHGGGEFFFGGHHGGGGGHHGGFEEGcdfededfgfgfgffffeiflihfigegighijhiiijklllmnmlkkkkmllmnmmnppppqqqrtuwuuuxwvxzz{||||||}|}}}||||y^HIMTYZ[ZZXVWWVTRQOMMMKIIJIHGFFFEEEEFGGFEEEDCCBEDDDDDDEFFFFGHIHHIIIKJJJJKLLMMNOOPSQRTUUVXY\^`cglrx~ƒ‰•š¢ª¯®®¸ÂÇËÎÐÐÐÏÎÎÏÐÒÍÍËÐÌËÉÊÇŪ›„nea``__^abb`a``acccbaadefabceefdddgfefgffehcddcdeddefeefhfgghefgiikjkjlkklmknjiiijmjiilkhkklkjkkjihiijmmmllkklolmnqnmljjmjmkpprmnlllmlmllkmjjkljjnpmmlnlklmjklonmmlllkkijhhijjklmkkkkkkjjjkfffgihfffhhhfeefegfgffcdab`^]\]\\^\[[_\[YY\[[[[ZZ\ZZZZZXXZX[UXWVVWTTTTTUUTTUSSQTRQQRRQQOOQPPPQPONPNNOOMLLKJMKKJJIHIKKMJKKJJIIJJJIIIHHIHHGGGGGFGHGFFFFHGHHIGHGGHKIIHHGGGGGHEGFGFGEHGGGHGIGHGIIHFGHIIHHHHKFGEDFEGGGGGGGHGFGGFEFededebdeecddfefeeeefghgfghgfgfhfhhhhikkkkmmmkjkjjlmkllllmmoppooopotqrrsqsprooppoonmlmmmmmmmmmmgZRQV\]^`abccbbb`_^^^_^^[XWVUSRRPONNNMLLKKJHGFEDEEFEEEEEFGFEEEFFHFIHHHIHJJNMNOQSUXY[_chlqw|‚‡“𥰹ÁÂÄÇÊÌÎÐÐÏÎÏÍÏÑÎÍÍÎÎÎÍÍÍÎÎÏÎÍÍÍÍËÊÉÆÅ»¯“}tfd_^^_^```_`aabbaabaaaabbbbaabcdddeedddcacbbbcccdfgeddeeeeefiihiiiikkjijjihhhijkkijkjjkkmjjknljiijjjjkkkklomlllmnnmkkljmkmpqllllklllkkjljjklhkoommllmmmmjkkljkikjkjihiiihiikkkklmllkjjihggghhggfdddfefffeeeeeeaa_`_^]\\[[\\[[\\[ZY\[Y[][ZZYYYZZXXZVVTTTTSSSTSSSSUTQRRQQRQRSRURQOOPPQQPQOONNNMNMMLLJJJKIJIIIIIJIKJJJIIJKJIJKIIJIHHGGGFGGGFEEFFFFGHGGGGGGHGIJHGGGGGHEFFGFGEGHGFGGHFGIHGFEGIHGGFGGGFFEFHGGGGGFGFGHGFGGFGifhdebddedfdgfffeedfgidgfghhhghhhhihljjkkkkkjkkjjlonnmmmsmppopqptrvrssvvuuuvvuuusssqpomnqmjjika\W]cbacdeefgghihikkkihggghgedcddcb`]][XVTSRPNKJIHHIHHIHHIIHGHHHHHIIIJKMNORVZ_djpx‰‘™£¯»ÃÊÏÓÔÕÖÖÕÔÖרØÖÔÒ×ÒÑÓÔÒÐÒÔÓÒÒÓÒÒÎÓÑÖÕÔÔÔÑÑÑÑÐÏÏÏÅ©•mg`_a_a_`a`_^_^_`___`bcaaaa`bbddedddeca`faabedededdeddhefefjiiiiljljjjjijhghljkihikiiiiijiikmjmkljjjjklllmmmmknnplmmljmllmqlllkklllkmmmmnllmmlnnnlmlmmlklllmmlmmmkjimiiiiilkkjomllmkjihghhhgfgfeeejeghhffeeddba``^_]\[Z\][Y[\]^[Z\[[\\\YYZZY\YXXYVUTUSSSUTTUUSSSTSSQQQSRRRRRRQPPOPRQPPOOPONNOOPNOKJJKJIGHHIIKKJIIIIIJJJKKJHIJIIHGHGGIHIGGGIGFFGIFGFGHGHIHHHHIIIGHFGFEEGFGFDGHHHGIHFFFFGHHGGGFHGFFFHHHFFGHGGFFGHHHGIGfeeedcdcecddeeeffffhgifggfgigggggghhiijjkjiiihhgkkklnmnmmmooooqpqrrqsstssssruutttttsstuusqrsnlhefgedfghklljjkkkjklkkkkkkkkkkjiikjijihgecb``_][YWUTTUTRSRSTTTTUWY[^adhlt|„Œ” ©´»ÂÉÐÒÔÓÒÓÔÔÔÔÔÓÓÓÔÖØÖÓÖÓÔÓÓÔÔÔÔÒÒÓÓÓÓÒÑÑÑÎÎÏÕÓÔÕÔÑÒÒÑÑÏÍÍÍÌ͸¬’|rca^]]]\\[]`^]^^]\]^^^aa`_```abaacba```a`bbbccdccddcdeffgjihighiijjiijhfghjiihggijihjkiijkjiljkjkkjkllmnnqmlnomjjjjjknlkkkllkkmmkjkiklnnnnmmmmmkllmmllkllkkjkklijjjiiijhijjjnmlkkkihhhhhhjkligffgehigffefgdcba_]]]\]\^][\[\Z\\\[[[[ZYYYXXYYXXXXVVTSSSRSSSRRRRRSRQPQQRRRTSSQPPPOOONPPNOOOONOLLLKKKIJJIFHIHIJJIIIKIIIIJHIJHHIHIHHIHGGGGGGFFGFFGIGGGFGGHHHHHHHHHFHFFEEEFFFFFHIGGGIHGIFGGIHGGGHHGEEGFEFGFFFFEEFFFGFEHHeeeefdddedeeefhgggggggggheefgghgghhgjikjljihhikhkklmolllnoqopoqprrsrsssrrsstuuuvvuuuuuuuutssrrrqoihijklmonooonnnqqpnnprrqqrstsqpoopnmnomlmlmoliiijljihhijkmorx€ˆ™¦¯¸ÃÎÑÓ×ÚÚÚÛÜÙÖÙÛÚÙÛÜ×ÕØÝØÚØÝØØØÚÖ×ÖÜרØÙÖÖÔÓÕÚÕÔÔÕÓØ××ÖÖÖ×××ÖÕÔÔÓÓÕÖÔÒÐÓÅÄ¥|ic^\[\\\\\]]]\\]]^^^a_^^_^_`d`_`a`____``aabbbcddgcefgfggghhghijjlknhgfiijhhglghhijjjhiliiimjljijmkmmmnoonnnllkjjlkjknllkkklkqmkklllmonnmmlronllmmmmlkkikjjkllkkkjijijiiijlnmnkljihiijhiikkjgjgjfggffffffedca^_b^\\\]\\\[\[\[ZZ[\\Z[YYXWYZXYY\WWUTTTRRRSRSRQQTQRRRRRRRRRQPORPONNOPONNMMOMNLLKKKLJJJKIHIIHIJLHIKJHHHJHHHJIIIJIJHHHHGHHHGFGFFFFFGJGGGHGGGIGJHGFGHGEEFHGFFGHIHHGIGGGHHGGGGHGGFFDDEEFFGHFGFHFFFFFFFGFfhfeedeeedeefghgfefffgggfefgffghhffgghiilhiiijjhjjkllkklnopoonnnsqsqrqsssutvuwvvvuuwvvwxvuuttusqojiijjlnnnonoooopqppprrqqqqrrrrrrrrqrssssssttvwxz|ƒ‡Œ‘–œ¤®·ÁËÔרØÙÛÜÜÜÜÜÛÛÙÙÙÙÙÙÙÙÙרØÙÙÙÙØØÚÙÙר××Ö×ÖØØØ×ÖÕÕÖ×ÕÕÕÕÒÔÔÓÑÒÐ×Ô××××רÖÔÔÕÔÑÐÑËۦ‹ul_]YYYZ[ZYYYXXYX\^]\\\\[]^^__^_`^^^^^_`___`bbbbcdfeeefeeggikjihijhfefhgggggghmikjjihkgiikkigijjjknllmmmlkjkkkjjjijklljllkkkjjijklmonmlllnnnomqolnnkjlkkjiilkkkjijiiiijklkjkjlihhhghhhhikjhgfgeggeeffeeeecaaa`_`a]^]Z\[\Y\\\]ZYYXXXYXYYXWYXXUVWUTTRRRTRQQQOQPQRPPQPRQQPNNOONNNOOMMLLMNNLLKJLLJIIIJJIIIGIIIHIJHHHIJIHHIIIJIIIIGGGGGGGFGGFFFEEFGHGGGGGGHGGGGFGEEEFFFGGGGHIGHFIFFFFEGGGGGGGGGDDDEDEEFEFFFGFFFFFFFFgfffeffedefefgigffefgghgffjgihfgihliiiijlkkkjjijkkkkmlpmmosopnopsrrrrqssrstttuuuuuvvvwxxzuwvvvwqpkjklmnmopposqrqpqrrtrrrrsuuvvvvwwwxyyyz{}‚‡Œ‘—ž§±¸¿ÇÏÕÜÛÜÞÞÞßßßààààßßÞÞÞÞÞàÜÞÝÜÚßÛÛÝÞÞÞÜàÜàÛÛÜÞÚÚÛÝÜÛÙÝÚÛØÚÙÙ××ÖÙÕÖÖÕÕÔÒÕÒØÕØ×ÚØØ×ÚÚÙØÛÕÕÔÓÍȼ·š„sb\YXYXYXWWXXXWZZY[]][Z_]]Z[]_][[[\\^__^^^^_`bbeeeddfeegfkkjgghjieegggfefffhiijjihhkikkljiiihiijkllnnmllkmkkihghiklljnmljjkkjnmpmnmmlopomnpmnomnmjjjkkjijlkllmiklljijkklkkjkijiiimihghijihgggggeeefgfheecba_^^`]\^Z\[[Z\\`\YXWXXXYYYXWWVVWVXVUUTSTRSQQQQQRQPPOOQRRRRRRNPONNNNOMMKMMOMLLKJKKJIJKKLKIJJIIIIJHHGLKKJKIHHIIIJJHGFGGIGGGGGIFGFGGHGFGIGGHHGHHHGGFFEGFHGGGIHHHHGJGGFFFFFJIIGGGGFGDDEFFJFHFJHHGHGGGFFfffhfffffgfefggfefeefffgggggilhgghiiihijkijijijjkkjkmmnonooooooprrrrrqsrrrsuuuuuuuvvwwwvwvwzxvvrqlllmnnnoppprqrrsutstttttuuuvxyz|~€‚…ˆŒ‘—œ¢©³¾ÊÕÛÞßàáââáâáàÞÞÝÝÝßÞßàßÞßÞßáßÞßÝÞÞÝÚßÛÜÝÝÝÝÛÜÛàÛÝÞÞÛÛÛÜØÛÚÝÚÛØÚÛÔÔÓÑÑÑǽ½ºÅÏÍËÐÓÒÓÖÙØØØØÙØÚ×Ö×ÕÓÍÇÁ½¦—~g_WVTTUUUTSUXVTUVWVXYZZYWY[[XXYZ[[\^__```^^_`accccfddefhihffghieedeecddegfegjjffglijljihghhiiijklllkjkkkjkgggihhihhkokjkkkjmmlllllkokmlmommommkjhjmkllkkkkijikkkkjhjijijiijjiihjihhhikmiggfggefegfefdddcb_]__^\\Z\][Z[[[YXWXXXXXXWWWVVUVVUUUUUSRRSPRRQQSOPSPOPQQPPPPNMLNONMMMLKLLKKLJJIKJJHHHIIIIHHGGHIHGGGHHHHIHGGGGIGFFGGGGIFFGFFFFFFFGGGGGHGHIGGHJHFFFFGFFFFGHHHHHHGGFGFFGGFHJIHHGFFEDEEFFFEFGHGHHGFGGFFefhgfffghfffhgfffeeegfghjjkhiiihhiijlhiikjjjnjjknkklmnqptnnooonpqsttttvrsrsuuvwuzyyy|yyxxyyyxvvsrnmmooqopqutttstuuuvvvvvvxz}€ƒˆŽ”š¡ª²¾ËÓÚÞÜÝÞÞÞßààáááââââââââââááááááááááááááßáàßßÞßáàßÞáÞàßßßàÞàÝÜÜÜÛÛÛÝÜߨÛÎÍÈø®§ž‚s~”£¯¼ÈÎ×××ÖÕÕÞØÛÙàÚÛÛÛÔÖÐÉÄı§†k^TSRPPPQQQSTTTUVTVTUWYWXZZYXYXY[Z[]^\\\]]^]^_`aa`abddhgggfdcdebbccdddddddfhjffhkhghjimhkkkighkihjjjlljijhjjmhhimhjkkknmmmmlllonnnolmlmmmnnoollmmjklljiknklkkkklkkokljjknjiijhkilihjkihghgffeecgeffdbceb_^_`b\]]]]]\[Z[[[YXXZWXWWVVWWUVUTTUUUSRSSRQRPQTOORPPTPPPOOPOMMONNMLLNKKLKKKIIJKJJIJHHIKIHGEGGGGFFFGGKKKGFGGGIHFFIGIHHFFFEEEEFGGFFGGHJHHGFGIHGGFEFFEEFGGIIHGHIGGGHGHHHFGIIIIGEEEEGGGFFFGGIGIHGGIFGGffhfgffhfefffgffffffghhhhihhihiihjjjihihikkjkkkklllmoqpnnooooopssrsqrtssstuvvwvuxwxxyyyyxxxwwvvsqnnnooppqqrsstvwyz{{}€ƒ…ˆ’˜¡©³ÁËÖÝßßßàááâââáââââââââââââââãããââáàáâââááááââáßâáááááááàÝßÞáàßÞßÝÞÜÞàßÞÜÛÝÜÜÙ×¹ª›‘zuqdVVXr|ƒš¿ÄÈÏÖÓÓØØÙÚÚÚÚÚÚÖØØÒÌǾ´«ŽvdPMKKKMNOPQQQRRRQQRTSSUVUTUUUWVVXZYXZZZYZ\\\^a__`aa`bccbbbaa```a``aaaabcdffefjgfghiggffffgffghhhjghgjihfiiighhhhhilklmkiklllmknmmkmlmmlnmjkjjkmikkkiijklkjkkklkjlkjijmkjiiihhhjkkjiggfghgdefegfffedcdba`_^a]\[[[[\[Z[ZZYXXYVWUWVVWVTTTTTSRSSSSSVRQQQQPPQPPPPPRPOOPMMONNMMLLLLLKIIIJIJHHHHHHHHHGGGIHGGGFFFGHIHGHJGGHIGGGGHGGHHHGGFEFGFEFFFFGGGFGIHGHIGFFFFEGHIIGGGHGFFHGEFFFFFIHHGFEGFEFFFEFFFGGGGGGFFFFGgehghggffefgggeffffgggjhhihhiijhgiljjiihikllllmlmmonpqpooppooprsstvsrtxwwwwxxxxx|yxxz{zxwwyxwvurqoonoqrrrrswyz|~†‹‘—¡«³¾ÇÏÚÛÜÝÝÞßàáâãããââââââââââââââââãããããããâââââââââáâââáââââááááááàßàááááàààààààßßßßÜÝØÖ©’ƒrcXOGHN]nrlio}Œ´¾ÔÖØ×ÞÛßÜÝÜÛÛÛÛÝÔÕÊÆºµ˜cJFHJLKLLLLMLLNNOPONQTTTTTSSSTTWVVVYYYYY[]]\]]^_ab`_`abaa```^]]^^____aabceefddeihjfhegddgjefgggififjhggihkkkhgghikkjkkjkllmnlnnmmnlmmlmljmmmlnmljjjjkkljknlkkkkkljjjjjiiijjjhjjjjhhjfhhifefffgfffecdba`_^`^\[ZZZ\Z[[ZYXXXZZZVWVUUVTSTTTQQQRURRRRQQQRPQQQPPQPPPQOONNQOOOPKMLMKLJKJMJLHHHHHGHHHHGGHIGFFEFFHGHIGKHFFHHGGGHHGGHJGHGFFGGEEFFIFFFFGGHJHJHHGGFGGGGGGFGGHGGGGGFGFEEGGFFGGFGEEFEEEHGGGFFFHGFFEFGgegfgefdfeehghgffhhhgghhiiiiihhhhhiiihillklmlnnmllmlnnpoonpqooqprtttstuuvwwxxxxxyzxxyzyxwuuuwvurqoooqsuwz}€„Š–¥®ºÇÖÜÝÝÚרÙÜßßÞÝÝßâããäãââàÞÞßáââããããââãããããããããããâãããââââââââáââââââáááâááàáááßáàßÞÞÞÜÛÚÙØÒ®¥|w_RJ@>=IqnŠhefhlr…”§ÀÆÑÔÖÛÛÜÞÞËÏÍÏÒÐÑÍĽ³œ…gIC>AGILLNMKKKNMMMMMOOOOPOOOPQRSSSSSTTUUVWXYYYYYYY\_^]\\][[\\\\[[\^^_``^`acdcceieccccccgfeedegffgeeefgihhhhffgghhhjkjjkmlnllnnmmlmlnmmjiigijjjjkkikiklkmmlkklllkkjjjijiiiiihijjjihhgggfgehgffeeecbcba``a`^\[ZZZ[[Z[ZYXWWWWXVVUUTTTTTTSRRQQQQQQQQQPPPQQQPPOOOQMOPNMLLLLMKKLKJJIIIIHHGHHHGGHHHHGHHGHFFFFFHGGIGGGFEFGGGHFFFHHHFGFFGGGFFFFFEFGGGGGHHHGGFGFGHHHGFFJHGHIGFFEEFEDGEFEEDFGEEFDDDGGGHFFGGGFGGGIgfghhggeedceggggghihihghkkjikllhghjhmjjjjkmlklmlmmlmnnspqqpppoqppqrsstvuwvwxxxxyyywxzyxwxx|uwvvssppouy‡›¦³ÃÑÜßÝÞßàáâãâѱœŸ©¸½´®¹ÊÓàáããããÖÉÀÊÙÛââããããâãäãããããããããââãããããââââââââââââáááâááàáàßßÞÞÝÝÜ×̾±£“€o^PLFC5007DQ\cijnlihhr|”³ÁÍÐÚÙÜÝà×ÒÐÅÇËÑÜÏË¿¶¢–jI=8<CGMNNMLLMMKKKLMNNONONMMNOOOOOOPQVSSTUVWXXXWWYZ[[Z[\YYYZ[ZYXZ^^^_`^]^`aabaaaaaacccccdcbbcbdfffecgkiggfeffighiijkiiiiiklnkklkkkklljjjiiijhiklkjkkmkkilklpmmkjkljihhiijiijjmiihigfffgdfgghfecaaccdaca`^\[\[[[_Z[ZYXWWVVYVUUUUUSSSSSSQRQQQQQSQQPPOOPPQQOOORONNNMNLKKNKPLKJNIIHIJIIJHLGGGGFGHIHGFGGGHHGGGIGGGIGGGIHHGFFJHHHHGGFFFFFFFEEGHIFHGIHGGGGHGHHIGGGFHIIIHGFEEEFECFEEEEEEEEEGDIDFGGFFGHGGGHGHHgfffgggdedeeghggggihgfhgiiiihghggjjhijjjkklklllmnolmnmnnoponooppooqrrsttuxvvwxvvwxvuwwwvvwvuvttrrqqov…§ÆÕßßàâäååååääåååãßœ–y«¤™†’²ÊÝàãäã߿”ÊÕáãääãâãääããããããããâââãããããââââããâââââáááááßÞÛØØÕɽ«˜†wi\QHA::;?EBD3--.37;@Ojh}Ž|ptt†“¥ºÃÒ×ÜÝààßÒÈÆÇÊÏÉȾ´§˜pM@59=CMPQRQQQQPONOPPPQPOLNOOOOMONNOOOPPQQQQRSRRTUVWWWWWVVVVVVUUWXZ\[[[\\]`c_^___a``aa`aada`abcccdefeeffeeeffffggggghiiikkjjjjjjiijljhjhhghgikkkjkkmlkilklmmnjjiihhlijiiijihliihhfhkggdeeddfdba`a``_b`_^]\\[\ZZZYYYXVTUUUUTSTTTSTRRSQQQPQSQQRQPPPNNMMMMMNNPONMMLLKKKLKMLKKLJIIIIIHHHHHHGHJHHGFGGGIHHGFGGHHHGHGGGGIGGFFFFGGGEFFFGFEFGFFFGGFFGHHHGGFGGGGGGGGGGGGGHFFFFEDEDEEDCEEEFEEEDDDFFGGFHGGGFGGHHgeefjggdeegfggghkhihggihjiiighjgghghkiiillmknlllklllnmommnnmnorooopqrstttuuuwwvvvvvvwwwuuwxuvtsrrrqqv†ÕàææææææææææååååæäãÝΣˆƒ|mnb\f¦ÛâääãÖȬ›¨½ÇÞáääääääãääãããäãããããããããââââãâââââáááàßÞÛ×È®”{hWI@:511/-,,-138>;3-*,+,,/4=J`ž„|sm|‚¢¸×ØÙÜßßßÙ×ÌÅÆÆÈ̾¸« sR>359DUT][YVTTSRSTSQQQPQSSTQOOOOONNNNNOPOOOOOPQRRSTTVTSTUUUTSSTVWYYXXY[[^^\\]^^]\\a^^_accaabbccccccceeeeeddeeegghgdfghjiiijighhhjklikghfggjjjjkjknnliknllmnmmjjghhiikjiiihlinhgghfddcdecccbbbbe`a`b`_^^]_\\Y[YYYXVUTVUXUUSTUTSTRQPPQQQRQQQQQPOPNMMMMNORNPPPMNLKKJKKKNLJKJJKKJIIILIJIJHHIHHGGGFFFFHGGHIJHHHIHJGGGEEGFHHGGFFFEEEDDFFFFGGGGGHHHIIJGGGJHHHHGJGHGHFEFFEDDFDDDECFEGFGEFEEEFGGGFFFGGGJHHHfeefffedddffgghhhhiggghhiiiihhiggghijgiijjmkkkllllllllmlllmmnnnnoqqqrrrqsttsuuututuuvuwuvwxvwtturrqprz¦Øçìêéèçççççææååæåååâמ]j_jwtgg‚¹àåååäáܵœž”´ÛáäåäääääääãäääãääãããããããâããâââââßÛ򯃵”wbNB9520.//00110///22897566533235=@]zƒ‚w…‘¦¸ÊÙÜÜÞàÜÙØÕÐËÉÀº¹©œvSA357APUijlifca__][[[YWWXWVUTSRRRRQQPPPPOOOOOPPPNTPPPQPPOOPOMPRSTTSUVXYYYXXXWXYYZYY[`_]\\]^_c`_`aaaaabbbbbbcedcdeddeffffeggfefggggggffgghgggijjknmmjjlnllkjjjihhghgilihiglihhgffdeedccbbebaabaa`_`_^^]]\[ZYYXXZYVUUUUUUUSSSSTSRQQPPQRRQQQRTPOONNOMMMPNMMMMLLLKJKLLKKLKKKKKIIKJIJHHHHHHHHIGGHFFGFHGGHJIHHIIHGGHGFEFFGGGHFFFEEEEDEFFFGGGFGIIIIFGGGFGHHGGGGFGFFEEGEDDCDDDDDBDEFFFEEDDDEEEEFEGHHGGGHHffhggeeffefeigiihiiijjjiiikihhjghihijjjillmllkllnlolmmnlllnmonmnppqrqrsqrtuuutstuuvwwuxvvvxxwuwutssqnlk™ÙçìëééèççççææææææååâÖ‹yw}€¨rgj•Èääåääãȸ£•Œ©ÐÖãääääääääääããããããããããããããââááÞÖ·‚eP>5/-,--./0269766777657=EIJJHEB???>>?AGPbt¥—ty½ÍßÝÜÝßàààßÖËźµ³©¡yU?3329AQjnzwtvwutstrolhgea^\[YWVVUTTUTSSRQQRTQQRTSSQRQONOOONOPPQQRRSWXVVUVWVUUUWZWXXWY\]]\\]]^^`a^^^_acaaccccbbcddddfeheffeddehggfiggggffffgljkjikkjkllmlllihglhjhhgghigkgghlhfeddfcdaabea`_``b_`_^]^\[ZYYYXWXYWVVWUTUUTUSRSSRRQPPQQSQQQSPOOONMLNMLMMMMMOLPMMJNLMJKKLKMKLJIJLIHHHGGHHHFGGFHFGFFFGGGGHIIHIHHGEFFGHFGGGFFGFFFFFFFFIHHGHHGHFGJHGGIFHGHHGFFGGFEEFEDDECDEGFHDDEFEEDEEDDEFEEEFGGJHGFGGfgededeeeefeffgghgiihhhhhhihhhjghiiiijjikkkkljjkllljllollllmnnnnopooprqqrttuttttuuuvvuvvvwwxwtuuuusrpi\T‡ÔâìëêéèçççççççççæååàŸuNQUmvfJãäåååäÚÛÂ²Ž¸ÔãååääåååäÛÒÒÓÕØÚÜààáâããâáÞÛÅÁŽjQ<4.,))(*+,-149>BDFGGGGHHHTbdgdb]XVTRQQPNMOSdz‚œ™œƒyˆ›±Ã×ÝÛàààáááÚØÅ´®®ŸŸyTA5206;K_gxy{yxxyxwxxwwurrrqomifdb`^][ZYYXWWWWVUUSRSSSTRPOOPPPQPPQQRSSRRRQPQSSTSSTUVVVWXXYZZZZZZ[\\\\\\]^]^^^^__`abbbbbbccddccbccfgggfdedeeghhhhkjiijiiihigfhgfiggggghgfeedgheedceadaaabaa`___]^\]]][ZYXWYXXWWWVVVXTTTSSSSSRQRPQPOOPPPOOOOONMLKNNLMMONNMLLMLKKKKJJJKKKKKJKJIGHHHGGHGHFHGFHFFFFFFGGGHJIHHHGGFFFIHGGGGGGFFEEEFGFFGGGGHGGHGFGHGGIFHHGGGFFGGEEDDCDDCDEEEDEEEFFEDCEBEEEEFHFFFFGGGEFGeeedhdddhghefgghhiiighjihhhhhhjhhhiijjjikjpkljokkklkmmnmmlmmooqmopqooppqssrsstuutuxvuuxuvwvvwxxuvtutqoRBGÌÚêëìêéèççççççççæåÞ¹eb][ea_v›ÛáååååäàÖ½™‹‹¶Ò×àâåååååäÓÈÄÄÆÉÌÎÏÐÒÙÞáàßÚ¯g?5...00,*)**+-47EMX\_dgebfilqx~…ŒŠ…|twxsmppke\YVZu ‘Ž„…~¥ÀÝÙàààààááÝØÃ®¥Ÿ¢¤yS=41135AL_€~‡…~~|~€~{|~{xyzxwxyxxxwsomjedb_]][XWWVTUUTSRQQQRSTUTRSRQQPPPOPOPQPQPPPQSWVTVXWUVYZ\[[\\[Z[]^^]^___a___^^aabcfa`abbfcbdfeeefefghghggggghihhjfeeeeigfggffeeedceeedebcbdaaaa_]`_`_^_\\\^YZYXWYYYVVVYVTUUTTSTUVSRQRQRPOOQPTOOONMMMMMMLLMLNMMMMNMLLLKLKKKJJIIIIJIIGGHGGIHGGFGGGHFGFFGHHHGIIJKKHGGGGFFHHHHHGIIIIIGHFFGHGGHIHHHGGGGGGHHGGFGIGGGGFEDDCCCCDFEEEEEEFHEEDDEEFFEFFGEEFHHHEFFefedeccdhffegihhhhihgfijiijjighhhhihiijijjkkkjnikkkklllnnnnmnnnnoqpnoopqrrrstttwuuvvuvxuvxwvwvuuutttosU;?N{ŸÄçìíêéèèèèèèçççæÞÑ…rb€xrnkq{¨ÜäåæææåàßÒļ½¾¾ÌÝãåååæãÐÀ¿¾ÉÃÎÈÉÈÑÔÝÞÑÀ›qW86211244321/-.048@QOso}}|zw|€†‰‘–›—’Œr‚s‚mieb]QUZepv‚sjwƒš«ÅÙáàààààááÝÝ륣 uM<22245:AWptƒƒƒ‚€€‚‚€~~}|zyxxyzxuvwsonnjfecba_^]\[ZYYXWWWVUUUSQRRRQQPPPROOOOOPPRTTUSRTTTTUUUUWWXXYYYYZ\\[\]\[[[^^__`_`````ababaceeffgffeeeefhffeddddceffeeffccabcdeddcbbaa`a`_^^__^__^\[ZZWWWWXWVVUUVVTSRSRSURQRRRSQQRNOOPPOOOMMNMLLKKKKKLNMNMMMMLLLJKJJIJJIKIIJHHHGGGFGGGGGFGEEEEEFGHHHHJIJHHHGGGHFFFFGHHGGGGFHGHGGHIEFGHHGGGJGGGHHGGFFFGGGFFGEEDCDEEDFEEEEEFFHEEEEGEEDDEEFEEEHFHEFFeeededgggghhihhhhhhhiihijhhijhhhhhhhijkjikmkjkmjklmmllllmnpoonnnopqoooopprvuutuwwwwuuvxxxxwxxvvvxwwvwsV;6AOh‰»ãëîìêéèèèèèèèçßÕ„nO_z€’w‰¶ÝâææææåäãâááßʾÍàâãäååÐÌÉÊÌÍÏÍÌÔÚÞäâÌ•fN9:;;;889;>@>;876567@HYsv’ˆžŽ§ˆ±¸·¶³°•”ymgcb_dUanXqYSe_mgiu€•¡¹Ïßàáááááááßàĵµ¶« oI9445567:JWi‡‡„‚………„„‚‚ƒƒ„†ƒ„„„……ƒ‚ƒƒ‚€€~}}|{z{||xutsqolieba`][ZXWVVVVVTSSSSRQRQRPQPQSRRPQSRQRUTUUTSTVXWVWXXVUVX\ZYY^\\]]\]^^^``_`aaabcdeggfedddffeedcbceeddeedefcd``bdddddabab`a_^^a^_]`__[ZZXXVW[XXVXUUTTUVTTRRSSSSSPOOQSSROPPPOPMMNMLNKLKLKMMLLLLNLLLLJKIIILJJJKKKGIHHGGGGGGGGGIFDEEEEFHHJJIILIJHIHIHGFGFFGHGGGIHIHHHIHIFFGHHGGGGGGGHHHHEEFFGGEEFEEDDDEFEFEEEFFFFHFGFDGEEHGGGGFFFHGHEGGfeecdeeefhhggeighjihilihihhhjghkihhiikkmmmlkkkjjkmlolkkkllmnnnnopnononppqrwtutuvvuutuwxvxxwwwwwwwwvvvtW;:<GLcw¨ÛêïìêèèèèèèèçãଛiQh}{qrh}´×áæææææææææäÞ½¡¢¬Ëãååãââââáâãããäååâ·taEC@AA@?ELQWUTSQNLIFCBCESYdqz‚ƒ€‰Ž‰…~klbdfdcbbVHT`Uff{nnnq’¡¼ÍÝàáâááßááßÛ×Ñô¦˜gC>=DKJFCAILb{~‚„…„ƒƒ‚ƒƒ…„…„ƒ„…ƒ‚ƒƒƒƒ„„‚‚ƒ„„ƒ~}}~~{xyyxuvxtpnjgecb`_\Z[[ZXWWVVTRQPSTTSRRRRRQRQTSTQSSSRRRUUSSSTSSSTWXXXYZYXY[\]]]]]^_`aaba`aabbaabcbcabaaa``acdcaa^_aa``___a^`__]]]]^]]\[[ZZYXXXXYWXUUVTTTTTSRPPPQQQRPOOOOOOONNPONMMQMKKJKKJJJKLNLKLKLKKJKJIIIIIHHGHHIGGGGGFEGHGHFEEEEFEFGGIJIHHGHGGHHHHHFFFGGFGGGGIHHIHGGGFIHHHGGFGHHIHGHEEEFGGEEFFEDDDDDDDEFFFFFEHGGGGHFEHGGFFFFFHGFEEEeeeegeeeehihggighhjhhhiikhjhjiiikjmkjjlkklklmkjjjklmmlmmmlmnrnnoonnnqppqrsxuuuvvzuxvxwxx}xxxyw{www{xvvX;5<FGJWkßëíìëêééèèéèçåȯƒti~|vqoz‚Œ¯ÐáææççççççæãÁ°¥ª¸Öãääåæååååååååååà¢sjfaUKIFHU^sssehmqrs[cRRLFJCHOQKB@CHR^tu{|}€„}whiXMFDDJ[kmpidfmz…˜¯ÈÜàââáááááÛßÓÑ¿°š‹ZEJVg}c[RKHHV`l„‡‰„„…‡ƒƒ…†…†…†„ƒƒ††…††…ˆ…„„…„†‡ˆ†…„…ƒƒ…€~|{zz{zxxwutrqnjgeb_][ZXWVWVVUUTTSSTSTTTTTSSQPRSSRRRQPQRSSSUUTUWWVX[ZYZ\\\\]]^^^]]_ba_``aa___````cddccbb^````a_a_a^`_^\[\`_^\[[\YXXXVTVZWWVUTTTVTRRQPPPPQPQPONNNNMNMNROMMMMMKKJKKJIJJKLKKMLMLLJLJJJJJIHIIJHIGFFGFFEIHFGFDFEDEFFIGHKIIHHJGGHIHGGEFHGFFFFGGIHGGGGIGEIGGHIIHIIIIHGHEEEEEGEGGIFFDDDDEIFGGFFGFGHHGGGFHGGGFFEFFHHFFGFfeeffdffgkhhggggihihiiijighghiiijjkkkkkjkklmmkkjjjjjmlllllmnooonmmnoqqrqrssstuutvuwvvvxwwvwwxxyyxwxzvxY<;=HGJNYc‹ÍêììêéèéééèèãÞÜØ¦~eXpnjWPQ—ØäççèèççççåáÞÝÖÐÎÛÝáæççææææååäääÞªyt…}aTOLVaz˜†px„|eSPONYVSKDKOM<?=Q[q}‡’—œš–‘†ub\NF???NWm`acd]kmw—«ÊÞââàÝÝÞÛÜÕÔÉÅ«›‘j[YbromljYWOKJN_uy}„…„……………„†‡……‡‡†…††††‡‡…„†…††„„„„„ƒƒƒ„ƒ„„„ƒ‚€~}}|{zyyywuuuspmkhfddba_]\[[ZXVYXWVVTSRSUUTSRRRQQRRRRTSRQRSSSUWWWWXWWYZ[\\[[Z\^\[\_]\]^]]]]]]]]^_]\]^^^_^^]^^^]\Z[\\]\ZZZYXVUVTTTTUUVUSSRVTSTRSPOPQPOONNNNOMMMNNMMNLNMJIIKJIIJIJKJJKKKJJIJHHHHHHIIHHGGFFFGFFFGHFFFEEEEEFFFGGKHHHIIFHGGHGFFFFFFFFEFGGGGGGGGFGJFGHHHHIHIIIGGFEDEEEEEEEFEDDDDEEFGJHFFGHGGGGGGGGFGGFEFFFGGFFFifeeffjggghhiihihhhikjihhhhhhjiiijkjlkkjllmlkklkjjkkmnommmonpppmnnnprqqrsstttuxwwwvwywzwwx{xxy||{yxzvy[=6>IHJLKQZv˜ÃåëíëééééèèèççåÖ–VippiXM]ްØáæçèççççæåääßÝÁ¯¶ÈÒãåææææåÜÖÙâ×Â~ˆ¥}tqn^]nƒ³ª¤±¹w_OEMONONeUNTkLEYq†™¤»ÃËÈÈ®©€ukeJ><@PT`o\[_eo‚sq|‡žÃÔàÜÐÇÃÆÏÎÌĽ²ª›—}pnifaafa[\[F:9<AK\v}‡ƒ‰†‡†…„‡†ˆ‡ˆ‡‡‡‡††‡ˆ‡††††‰††…ˆ‡‡…†„„ƒˆ†‰††‡‰ˆ……‡…„ƒ‚€~~~}{zzzxwvvtpljgeca^\ZYYXVWXWVUUUTSTSRRSRQQQQRRRQQQSSSUXYZWVVWXXXWVXZ[[\\[\\]^[[[ZZZ[[\\]^_^^_``\[Y^__ZWX[XWVVUUTUSSUTTTSRRVTSRQRROOOPONNMNONMLMLKLNLKMNKLIKJIJLIJKJJKJKIHHJIFFGHIHIHJGGFIGHFEFEEEFGFGFFEFFFFFGHHIGHGKHHGFFFEFGGGGEEFHGIHIGFEGHGGHGGHIIHIJGHHGGGEEEHEEEGDEDDFEGGGIFEGHGIGGGFGHFGGGFGFFGGGGFhhfgfgggggfeiiikhhhhijihhhijijijiijjjkjjjjmkkkkllllmlmmmnnnnnmnnmmopqqrssststuwtwvvwvvxxyzzyyz||{}yzvy\=9?JINZNNTWmƒ®Ýêîêêéééééééèá×¹œ€m]^Ygiq—ÓÛæçççççèèççåâÁ´˜©¯ËãææææåÒÐÌ×ÕÑϨ‡ƒ€€}sx’¢ª¬¹Ê¶uaPNNF>>?TjYXVRDx¹µÈÉÖÔÒȲ–yuuYD>@S}riaXiw{€€twz‘¦ÀÍÛÔµ·«¨§¦¡››„{}nh`TNJLRKB50349@Tksƒ„…†Š†…ˆˆ‡…‡†††‡‡†††„…„††‡……„†…†…………ƒ‡„………††…„ƒƒƒƒƒƒ„…†ƒ‚€€}|zzz|yvvuusqpmjgeca`_^\\\ZYWVWXVTTTTTTSRRRRRRRSSSRRSSUVTSTTUXWWWWWXXWVVXYXXXZYXZ][ZZZZYYXYYZZZWWVWXVTSRSTSRRUSSTURRRRRRRQQNNNNOMLNKLLLLLKKKKKKLKKJIIIIIIIJLJJJIIIHHJKGEFGGGHGGFGEEEEEEEEDFIGEEDFEEEFHFFFFFEFFFFFGFFEEFGGFFEEEEFIGHFFFFFGGGGGGIHHGHGFFFFFFEDEEEEEDDCDFFFGEFGGGGGGGGHGGGGGFFFFFFGGIGEgeefffhgggffhhhhghjhghijiiiihiijkjjiiiiijkmjkkkljlllllrmnnnmmmponnrotsstxstsutwwwv{wwxzz{{z{}z|{zzy{y{^=8?JHR`iQMNQ^q¢ÝëíìêêééééééèåܽˆZFNVSSY`ŒÂÓåæçèèèèèçæåâÞÁ´²¹ÒáãååæÝÕÚáäåáݺ¬Ÿ•”—œ §«±»ËàαteSJJCFHLTd`i€JCo¦°¾ÂÎÑÖÔÖ«ŒteYK?:>Ydk`^c}”°šz€ut|‰œ¶ÉÝÄÅ«£•‹‘šˆ‚}{xvrogVGE>;95/.///49HWhƒ†ˆ‰ˆˆ‰††Š†‰‡†‡†…‡†‰‡ˆˆ‰ˆˆ†‹†ˆ…ŠŠŠ†ˆ„†…‰††††„„„‡‡ˆ…І…„†‡ˆ„†‡ˆ‡†††††€€~~~}{zxxxz|zxvuromigda^][YXYXWVVWUUTTUVTUSRSTTSSRQSTQSSUTTTTTTRSUTTUUVVWXZYXYZ[XXXXYYYZWVVUVUSSRSRRQQRRRQQQQRONMLNOMMMMMLLMJMMMLMKKLJJKKLJJHGHHHHIIIKJIIKHJHKJHFFGHGIGFGGGGFFEGFEEGGGFEFGEEEGGGFFEEEGGFFGGFFEEHGGFEEFEDFIGGGGFEFHGGHHGIGFGHGFFFFIFEEEEFDDDCCFEEFGGGGGGGFGGGHGGIIIIIFGFFFFFFFeeefffgggfgggghhhhhihhiijhhghkjjjkjiiiiikkkjkjllmmlmmllmopommmopppqoqrstvrtsuuvvxuwwxyyyyyz{}yyy{z{|x|]>:?JIR`\RMKNR`l“¾ÕìëêéééééééèåÛ¡REBLYZ\aa³ãåçèéééèçèèçåãâ๺Ä×áåææææçççåÛÔ×ÙÕÑØßßßÔËÒààÔ¥ytaZUdyvtvzeb]HQl—¨¯µ´³´¶¬£ydQC?69:GMWZfl}žŸ€pqqolt€–³»Â¹®˜Žw|‰}og`adjyf^QQ=1.,--.//135@HYnuƒƒ…†ˆ‡†‡†‡††‡†…†††…‡‡‡†‡„„…†„‡‡‡……„„…††‡‡†„„„„……†‹††„„…‡„††‡†††…„„„„‚„„„„‚€€~{|{zyxyxvusrrmhhgda_^^^]\[ZYXWWUUWXUSSRSTSSSTTTSRTSRQQQRRSSTWVYVVWWVUUUUUUWWWUUTTSQQQQPPPPQQPONOOONNNLOMLMMMNLMKJJJKKKKKLJIJKIHIHHHHGHIIIIIIJIIHGGHHFFFGHGFFEFFGEFFFGFEEEFFFFFEEDDEEEFEEEGFFFHGFFFEEFFFFFEFFFFGGGGFFFFFGHHFFFFGHHHHGFFFFEFEFEEFDCEEFFFEEFFGFFGHGGHGHJHGFFFFFEFFFGkfgffgfgjfhglggiihgiiiijjhhhhikkjjiigihilknkkkmlklmmmnomoppmmnoopppoqsstuttuvvzyyyyy|||z}|}{}~|~{|{v|]?8?IHSahRLILQU_`}¢ËåçèèçèèééèèäÕ™`ND\ilpx†½Ýãâåèèéèçèèèèçå㺴¬ºÎáäæçèèçäÔÙÙÚÛàâãããâááàß¹Ÿœ Ÿœ•”ŒŠ‰p`TKJVf‘Ї†…}ulaUaB?><=@DMRdt}„ƒzpiemtkbo|‚ˆ‡……ƒ|thdoudZUQW]ge]TP<1,,,-../01116<K^l‚†‡ˆ‰ˆˆ‡‡†ˆ‡Š†ˆˆ‰‡‹‡ˆ†Š†‰‰Š†ˆ‡‹‹‹‡‰†…‡‰ˆ…„…„…†‡†‹†‰ˆ‰…ˆ†ŠˆŠ‰‰‡ˆ„ˆ‡†ƒ‰……„‹†ˆ‡††‡†„„…„ƒ€~~|{||{zzyxxwutrpmigc`^[[[ZXXWWWVTTWVTSSTTSRQQRSRSRQSSRTUWSSSRRRRSSRRRRRQPPOPRRROOOOPNNOOPMMNLLLLONMLKKIIIJKKKKJJJIJIHIJHIIIGIILIHHJIHHIFGHKFEEFGFFFFFFHDEFFFHFHFFFFGGFEEGEEDEEEFGFEFHFEGEEFFFFFFFECEEFHGHEEFGGGHHFFFFGGGFHIGHFEEEEGEEEDEFEGFFFDEEFFFGFGGHHHHHGFGEFFFIGGGfffggigggggggghiijjiiiihjhhiihiihiihhihhjjjkkmmkllmmmnmlonnlmnoopnoortsstxtuuvvvwvxyz{|z||{|||{z}z|{x}^?;?JHTaXYXORS`r^Zw¶ÜãçæçèèéèèèâÚ«~i^e››¿¦É¾½Ýäèéèèèèèèçæãξ¶¢Ÿ¥¾ÝäçèèèåãääääååæçææäÔÆ³¦¨µÍÊÅ•€„ŠxlcROMW\YUQLIFEEKSansvvvvwrmt~ˆ–‹‹zookklnfcanstmutvnva`ce[WPNNYf`\VQD8420-.-./011136?JYjt‡ˆŠ‰ˆ‡…‡‡Š†ˆˆˆ‡‡‡‡†Š„‡††…‡‡‡‡ˆ†††‡ˆˆˆ‡……………†††…††‡…‡…††…„………„†ˆ…ƒ„……………‡„………„…„„„…†‡‡†…€€~}||{|}|zywwvspnligeccb`]\[[[YWWWVVUUTSSSSRSTTSTTSQSSRSQPPPOOPRPPPPNNOOOONNOPPPONNLMNLLKKJJJJJJKKJJIHIKJIIIIHHHHHHFHGHIIIHHHHGFGFGHGEEDEFEHFFFFFEEEEFFEFGFFGIHHFGHDDCEEFFFEEEEDEFEIFEEEEEEDDEDEFEFEFFFGGFFEFFFGGFFGGGGFFGDDEEEEEEDDFFFFEEFFFFFFGGHIHHGFFGFEFFGGHEgfegghhhjhjgghiiiiihhhijjihijihhgiliiiiihiiillmmnmqnmmoonmmnnosoppqqttsrrsstvuwwxwwy{{}{|€|}}{{||~|{}_?:@KJTbsfh]TUineTWg¯ÝåçççèèèèèéæãȰ|c|šŒ‚xov‰±×áæçèèéèèèçæäãáǪ˜·ÜãæçèçççæççææçççæÞ¸¢”žÄ¼ÉÇÙ~^lkszZecb\URHD:95?KÃÕÜÛÚÛÛÝÙȶ© ˜ortxyvsoiijrnqsuwvxrmppcWRMRXYWVXQQQOC;.,++,./0001149HWg}‚‹ˆ‡ˆ‰Š‰‰ŠŠŒŒ‰‹‹ŠŠŒˆ‰††‡‡‡Šˆˆ†Šˆˆ‰‰‡‡‡‰†ˆˆ‰‡‡‡Š†††‡‡†…‡‡Œ…†‡†…Œ†‡†††‡ˆ‡†††ˆˆ‰†ˆ‡‡‡ˆ‡†††‡‰‰ˆ‡†…„‚‚}~~}{zzyxz{ywvtsrpmjfc`_][XWWWWVTTTUWWXURSSSRSQQMNMNNMKLMNMMNMMMMMLLLMPOKLMMMKJIIIJIIJJKKIHHHIIIKIHHIHGGHGHHHHGGIIHHGFGGFFFEFDEEEEFFFEFEEDDEFFFFGGGGHGFFGEFFGFGFFDCEFEGEDFFEDEHEFEDDDDEEFFFFFFIFEEGEIGGFIGGGFEHFDDDDDEFDDEFFFEFGGFGGGGHHIIIGGFEFGFFGGGHHeffggiiihghhijiijjihhiiiiiiiijhhgiiiihjlllkjkjlmnmnomlmnnlnpooppqtqqqqsrssssuuvvwzzyzz{{|||~~}}}z{|y~`?<ALKUbpeiwbZbfXMRTdtÑ×äåèèèééééèæÏ«smieeejpŒ¶ÑæééééèèèèèèçãȬ¤•¬¾ÐåèèéèèèéèèèçççÒ¿‹‹Šº·¯ž‰‘„yŒc^\addc]VPLT_ˆµÓäææåæææãßÞÜÀ¬¤Š††’›š™“Œ…vvxxx||u{”s‚poXZZ[^`^VVOOSQN6-++,,-.03100135<FWms†…‰‰‰‰‰‰ŠŠŠ‰ˆˆ‰Šˆ‹‰‰ˆˆˆˆ‡‰ˆ‡††‡ˆ‰ˆˆ‡‡‡†‡†‰‡‡ˆˆ†ˆŠˆ‰‡†‡‡††ˆŠ‡††‡‡ˆ‡†‡ˆˆ‡‡‡‡‡ˆ†ˆ‡‡‡ˆˆ†…†††††‡……†‡††ƒ‚‚€~}}||{zyxwuvwuspoonjgdcb`_^]]^^[YXWWVUUUWQONNNKKKKLMKMMLKLLKJKJMOLLKJJJJHHIIJJHIIHHHFGGGGIHIHHHGGHFEEEEFGIIHGFFGEEDEDCDDEEDEFEDDDDEEEEEFEEEGGGFFFGEEDFDFEEDDEEDDCDEEEEGFCEFECDFDEFEEEEEEEEEEFGFHFGFFGFEHFEDDEEDDCDFEEEDGCFHGGGGHIIIHGFFFEDDEEHGGGefiggikihgghjjlijjjjjiijojjhhhhhhhhikjkljkmlnlqnnnnmnmmnnonopooqrqqqsrssstvutuxwwyyz{||||}~~}~ƒ{|}z~b@;@LKVcqprqoojaOLNOPYf޹ÍáæéééééééèèäݬaYdPMM_Hƒ·ÕäæèèèèéèèèèæäÔʲ›¢¯ÍâåèèèèéèèèèççÜÓµ–‹„ƒ‚‚‚’š®À¯¤¦£’†‰’—š›š™¢¯ÌÝáåææææææææåäãââÞÏ¿ÂÇÔÝÁ¼¹¶¯¦ š’ˆy€}phcaWXd`qombaQDA83/,+.0.-/03322111138GUg‚ˆ‹ŠŒŠŠ‹‹Š‰‰Š‰Šˆ‹‰‰ŠŒ‡ˆ‰‹‰ˆ‡ˆ‡ˆ‰Œ‡‡ˆ‰ˆŠŠ‰ˆˆˆˆ‰‹‰Š‹Š‡†‡‰†Œ‰‰‡‰ˆˆ‡‡ˆ‰ˆ‹‡ˆ‡Šˆ‰‡ˆˆ‹ˆ‹ˆˆ†ˆ†…†‡†††ˆ…„†‡‡‰……ƒ„„„„„ƒƒƒ„‚€‚‚}|zxusqsvuutsqnnlkieb`][YXVUTRQONMLKKKMMMLKJKLJIMIKKKKJIMIJIHHIIJIIIGGGFGEHHHIGHMHGGHFEEEEFFIHHGEEGDDDEFFDDDDDEFHEEDDEGEFEEEFFGGIFEFGEDDFFGDCDEEDDFDEEHEEEFDDDFDDDEFGEDEHFGEEEHFGGGGGFHFFFGFEDDCCDDDEFFEEEGFEFGHHGHIKIHGFGIFFEEFIGGGgggegihhhgghhhhgijkjhiikjghhhhhhhgijkhjmkjmllmmmmmnjnommmnnooopssqqpsrssstvuuuwxxxy}}|}}|~}~~}}}|}{~bA;@LKVepirdmijh\PPOOQZb€™»ÝåééééêêêêéåÞÎd\ep|nbgmÄ×àåèèéééèèèèççäÝÁ¥©©¿Þåèèèèèèèèçæäâßűž‹”¤¸ÅÐÏÐÖÝÜÜßÜÜÜÞßßßßàãæççççççççççççççççåãááäåäÍÉËÌË×ÖÙÊÁ»‰sqndYcd`]`dl|ifaWOC=83/001123567765420.24?FXlt‡‡ŒŠ‰‹‹Š‰‰ŠŠˆ‹‰‰Š‹‡‰‰Š‹‰ˆˆˆ‰Š‰‡ˆ‰ˆˆ‰ŠŠ‹ˆˆ‰‰‰‰‰†Š‡‡ˆ‰‡‰‡ˆ‡ˆˆˆ‡ˆˆˆˆ‡‡‡†ˆˆˆ‡‡‡‰‡‡ˆˆ†……†††††††††ˆ†…†……„„…„ƒ‚€‚ƒƒ‚ƒ†€}}|{zzyyxyzwuttttrppplhfda^[XVSQPNJKIIIKLKHJKJIIIIJJKIIIIIIGFFGGGHHFFFEEDFGHHGGIHGGHEEEEEEEIHGFEDDCDFEBDECDDDDDFEEDDEEEEEEEEFFFFEEHEDDDDDDCCDEEDCDDEEEEEEFBCDDDDCEEFFDDEEFDDDEEFGGGHEFEFFFFEEEBCEFGEDFEEEEGEEGHGGGGHHHFFFFFEGFFHGGGigggjgfhihhhhggiiiihghhkkiihihjihhijljjlmmmmmmmnnnnooomlmpsttpqstrrrrsttstxvux{z{xx{}|~}|}}~~}}}~~aC;@LJUdpkraonpo`VPNNPRSYpŒ´áçèèéêêêêééåÚ¡š˜˜–eb`bmÛáåçèéèèèéèèèèæåÑÀ¨š½ÞãåæççèèèèåÙÓÚâÉÑÌ̹ÜÑÞßààààáâãääåæææçççççççççèçççèçççççççææåæçæåãàááââââÜÙ²‹yonedeepp€nfg\[`ZgQKIF;68;<=>?>?@?=:4000235:JXi‡ŒŠŒŠŠŠ‰Š‹ŒŒ‰‰Š‹‰Œ‹‹ŠŠ‰Š‰‹ŠŠŠ‹‰‰ˆ‰‰Šˆ‡ˆ‹‰‰‰‰‰ŠˆˆŠŠˆ‡Š‰ŠŠ‹‹Š‰ŒˆŒ‡‹‹‹‰ŠˆŠˆŠ‡‰ˆˆˆ‹Š‰††‡ˆ‡‹ˆ‹ˆ†‡Š††…„„‡…………ƒƒ…„ƒ…„„…†‚~|}}{}~~}{zz{wttusrolhd^YTPLKHGHJKKIIIHGGHHHJIIIMHGHHEFGGFFFFFFEEEGGIHHHKGGHHEFDEFEDHGHFGDECBDFCCCCCEDCDGDEEEEEEDEFEDDDEGDDECCFDCCEDDDGEEDEFFEEEDEECDCEFFDEEEEEDEDGGFEEEEFHHHFGGHFGFGDDCCFFGEEEEFEEEEEFFFGHGHGHFGEEFEEGFHGFGhfghgggiiijjhghlhgiiijkkjjihhkiihghjjjjlmmlklmnpnmmmmmmmnoppqqqtrrrsrrttttuvvzzyzyz|}||z||||~€~~~~}„cE>@JJTcojpboiooij\VRPRSXZm{ŸÌÖçèéêêêêéçϳ‚lZJECOY\q‚ËÍ×ÜâæèèééééééééèÙô¦¿Ë×ãåçèééèåÐÊÇÇÇÈǽª´¿ÉÍÏÓ×ÛÝáææçèèèèèèèèèèèèèèçèèèççççççççççèèççççæççææäÙ¯‰…r}spljrtvwjmaVVXVRJKCEJMOQPOPQQTMA7401244479DJ\my†‡‰ŠŠŠ‹‹ŠŠ‡ŠŠŠŠŠ‰‹‰‰‰‰‰‰‰‰‹Š‰‰‰ˆˆ‰ˆ‰ˆ‰ˆ‰ˆˆ‡ˆ‡‰‰ŠˆˆˆŠ‰Šˆ‰ˆˆˆˆˆ‡‡ŠŠŠ‰‰ˆˆ‡Šˆˆˆˆˆ‰ˆˆ‡‡†ˆ†ˆ‡‡‡†ˆ‰†††‡‡††„‚„„„„„‚‚‚‚‚‚‚€€|}}~~~~~}}}||yxxwusrqonf_YQMGFEFGHIIIHGGGGGJHHHHGHGFFFFFGEEFEEEEEGEGHIJIGHHHDDCDDDDFGFEEDDCCDEECCCCCCCCDEEFECDDDHEDDGDDDCDFCBDFCBDDDDDDEEFCEEEFDEECCCCDFDFEEEEDEEEFFGEFEDFFGFFGFFGEDDDEDFFGEDFFFFFFEEFFFFFGFFGFFFEDDDEEFFGHhiihgghhhihhkkkihgiiiikjhiihhiiihghkkjmmmmmllnonnnomnnoppppqrrrsrrrrutwvvuuvxwwz||}||||}…|~€€€€‚eE?AIJT_rqqgphnllld^SOSVVWW^k¸ÔçéëêêêéèŹ]_<:-5GMVfœ¥»ÞåèèééééééééèäÞÊÃÇËÔÓßçèéèæØÇÉÍÔÚѸ¦Œˆqpt…›§³ÊàáççèèèèèèèèèèèèèèèèèèçççççççççèèèèççææææäãÛÜ£wpw~‚|vqda`gkTRNKLKIGHJLV[`csqkb^;0--043768:;=ASht††Ž‹‹‹‹‹‹‹‹ŠŒŒ‹ŠŠ‹Š‰‡ˆ‰‰‰‰Š‰ŠŠ‹ŠŒ‡‹‹ŠŠŠ‰‹‰‹‰‰ŠŠ‹Š‹‰‰ˆ‡ˆ‡ŠŠŒŠˆˆ‹ŠŠˆˆˆŠ‰Œ‰‹ˆŠˆˆ‡‰‡‡‡‡ˆ‰‡Šˆ‡‡‡………‰…„ƒ‡„ƒ„„„…‚‚ƒ„ƒƒ„…€€~~~€}}~}yyyztunj]OFEEFGGGGGFGIHIHKGIHGHJGGFGFFEEEFFFFDFGFFHHIHGJHGEDDECCDGGFEGEDCCCDDCCEDCCGDEFDEEDDDDDDDCDDEDDEEECCDDDEFFDCDGEFEEEGEDDFDEDGGFEFEFEHDEEEFGHEEEEGGIFFFFFFDDDDEEEHFEFGGGGIFGFGGIFFFFFHFFEEDDDDEGFIGggijhghggihhihighhhfiiijihjkijjjjjkmkknnmkmnmnpmnnnmnopqppqrrrsustsrttvuvvvwwwyz{|||||}~~~|€~~~€€~‚gF@BKMPT\dcejilkortwd]\W[kWR\d…ªËåêëêêééÙɰ rL1,7:[axs†n–¤ØÜááåéééêêêêêéèæåã×ÌÓÝåçèèçäâââãßÚ¹¼ŠŠbRNNT[j|ªÖæçèèéèèèèèèèèèèèèèèèççççççèèèèèèèçæææåäãÏË«‰nYVVf‡ˆˆyseYX\][QHEC=766<MTrijn€kiO5.'.11278?EECDHYbox}†‡ˆ‹‹Œ‹ŠŠŠŠ‰‹ŒŒ‹‹‰Š‰‰‰ŠŠŠ‰‹‡‹‹Š‰‰ˆŠŠŠŠŠˆˆˆ‰‰ŠŒŒ‰‰‰Š‹Š‹Š‰Šˆ‰‰‰‰ˆ‡‰ˆ‰ˆ‰‰ˆˆ‰ˆˆ‡ˆˆ‡†ˆ‡ˆ‰ˆ‡ˆ†‰‰‡‡ˆ………ˆ„ƒƒ…………†‡†ƒ…†…€€‚‚‚€€}~}|{{zzzzwwxpiYJFBBBCEEEEFEEGFFFGGGHJGFEGEFEEDEDEGFFEEFHGIIGGGFEEEECDDDEEEFFDCDCCBCCEDCBCDDEEEDDEEDDDECCDCCBDEDCBCCBCBCCCEFDEDDEDDCCDCDCDDEEEEEEEDEDEFFGFEEFFGFEFFFEECDEEEEDDEFGGFFEEEFEFGFEEDEEEEEEEEDDDDEFFFfghhhgjgghiikillmihgjiiikijiijjikjkkkmoommpmlnpnrpopppqqrsusrrtusuttwvyvywxzzz{{€~}}}€€~€‚€€‚‚hG@CMLLKQ]mhsstkqsqrrqg_bcYQRV]|¥ÈãèééêééæãѸm::ARdw‰›³ÏÚÜØ¾¹ÍÚáåçééêêééééèçàϘª½ÓäæèèçççæÝ¹’„jgVSJLKIMpˆÙåèèèéèèèèèèèééééèééèèèèèçèèèèèèèèççççäÓ¿£•„vd[SNPX`enjiaZ\_[UPKI99*),:Q_ke_ejYH2,-5=A?=AHVbZTRT_agnv„†‹Œ‹‹ŒŒŠŠŠŠŠ‹Œ‹ŒŒŒ‰‹ŠŠ‰Š‹ŠŠŠŒ‰Š‰ˆˆŠ‹‹‹‹‰ˆ‰‰ŠŒŠŒŒ‹‰ŠŠ‹‰Œ‹ŒŒŠ‰‰‹‰Œ‰ˆˆŠŠ‹‹‹ˆ‰ˆ‹ˆ‰ˆ‡ˆ‰‡‡‡‡†‡‡Š‡‹†††‡†ˆ††††‚„„‚‚‚‚€€€~~~}{||z|zuniVGA?@ABBCDDDEHGFEHGGHJGFEGEGDDDEEEGEEEEEEFHKGGGFEFFEDFEEDEEEDDDEDDBBBEDDDECCDDDDEFEECCCBCDCCCCCCCBBCDDDCCCFFDFCCCCDCCGCFCCDFEDDDEFEEDDEFFEFFFFEEDGFIEHEEFEEFEDEGGGFHEGGGFEFFEGDEEEDFEDDDDEEEFGFgfhgggggggiijiiiiihhijihjhjhijjijjmnnnnonlonnmonqppppppprtttsrtuttuuvvuuzwz~{||{}}~}~~~~~€€€~ƒiHABMPMNRSX]ejlkqsq€q€e_d]XPRSW\x•»ÞäèéêêêëèÌŸ‡˜«ÁϽÐÕÚÜÙ¹{[|š½ãçéêêééééééåÚ´“¡°Èáçééééçᮣ‚{uuw†™Š|~…ÖæèééèéééèéééééééééééèèèèçèèèèèèèèèèççãÜ£“ƒ{ebZVRTV[Vb`gdYMKKMOML?<,%%&-69:<AEI@522DhfbTMRalxjabfecdfpx}††‰Š‹ŒŒ‹Œ‹ŒŒŒ‹‹‰‰ŠŠ‰‰‰ŠŠŠ‰‰ŒŠŠ‹Š‰ˆ‰‰‰ˆŠ‰‹‹‹‹‡‰ŠŠŠŠ‰Œ‡ŒŠŠŠŠ‰‹Š‹‰ŠŠŠ‰‰ˆŠˆˆˆŠ‰Š‰‹‡ˆˆˆ‰‰‡ˆ‰‡ˆˆ‡…‡ˆˆ‡‡‡‡…‡‡‡‡†……ƒ„…ƒƒƒƒ„„ƒ‚€€~}~{|}{}~yum`M=<<>AAABDEDDBDDFGGGGFFEFFFDEFEGEFEDDGEEFGHGGHFFEFEDFFFEEFEDDDDCCCBBCECCEACFEDEEFDECCCBDDDCBCCCBBBBBBCCEEEEDDCBEEFCDECEBCCCCDEDDDDEDDDEEDDEEEDDEEEEEEEEDEFFGEEHFFEFEFFFFFFEDEDEGDDEDEEDDDEFGFFgfiggfhgggiimiiiijjhhjkkjjjjkjjjkknnonnnooonomqqqpppqqsrrsvttsssssvvwwwwzw||z|}}ƒ€€€€ƒ~€‚„jIBEMPNNNLPXhjtrsrqvrpmic_VQQRRUYp‹¬ÏØåèëëëéèãÞßàß׫ ‹’—˜’…dXF‰ÛãçééééééêééäÙ®œœ¦ÂÞäèèéèçäàâßÞÜâßààáââáåèéééééééééééééééééèèèèéèèèèèèèèèçççæåãâ´’Œ|qkhgk~mjmptwkZJ>:>EMKC7.+*$!#%&')-5>BBFQrŽyj]Z^tx{eggka_^ahoz‰ŒŽŽŽŒ‹ŒŒŒŒŽ‹‹‹Ž‹Œ‹ŽŠŠ‹Š‰‹Š‰Š‰‰Œ‹‹‹‹Š‰ŒŠŠ‹ŒŒŠŒŒ‹Œ‹‹‰‰ŠŒ‰‰‰‰‹‰ŠŠŠŠŒ‰‰ŠŠŠˆ†ˆ‰ˆŒ‰ˆˆ‹‰‰ˆˆˆŠŠŠ‡‡ˆ‰‰ˆ…†……†ˆ…„„…„„‚€~~€||yxhT=99=????@ABBBDEFEEEFEEEIIIGGEEFFFFDDDEEFFHGGHGFGGGEEFFFFFGEFDDCDCCBBDBCEDCDEDEEFEECECBCCCCBEDDDCBEBDCCCCDCCEBBBCCCDFDDCCCDCCCCDEDGFFEEEGEEFGEGEGFEEEEEEEFIGFFHGFFDEEFEFHFFFFEDDDDFEEFDDDEGFEFffhhgfgggghhjiihghhhhkjjjljjkikkkkmnnnooonnnnnppqqqprssssssstutstsuuwwwxxy|||~~}~}€€€€€€€€€‚€„kJCFOONNNNRQVZ`fmrsywxlhkwf^WQSTVZft¬ÃßçëêêëëëëêçÛyiKl‡—££¡—‹–¬Æáäæåçéééêêêèá´©žœº×áèêêééééèçèéééêéééêêêêêééééééééééééééèèèèèéééèèèèèèçæåäâáàÑᣇwzxy{zytuswyqfVNIGEDB8-*(''"!"%$$)/:db\f‰xjg``aacffc[XX]bgjrx|€„ˆ‰Š‹ŒŽŒ‹ŽŽŒŒŒŒŒ‹‹‹Œ‹‹Š‰‹‹ŠŒŒ‹Š‹Š‹‹ŒŒ‹ŠŒ‹ŒŠŠ‹Œ‰ŠŠŒ‹ŠŠ‰‹ˆŠŠ‰‰Š‡‹‰Š‰ŠŠŠˆŠŠ‰Š‹ŠŠˆ‰‰ˆ‰‰‡ˆˆ‰‡‰‰ˆ‡ˆˆˆ‰‰Šˆ‡ˆ…†ˆ‡‡‡††…„„„„„„€€€}|{xp[B:479;>??@@ACCBCCDEEDEFFEFEEFFEFFFDDDEHEFGGGHHEGFGFFFFDFGEDFDDBBBBBBECCDCCCDDEEEDDDDCCCCBBACCDDCBBBBBBCCCCBCCCDCBCEDDDDCDCBCBCDEDEEEEEDEFFGFEEEFFEDEEFEFGHGFFGGGFFEEFFEEDFHGGEDDDEHEEFEDDGFEEgfghgfhhgggijjihihhhhijjiiikjknmnmqnonoooonnoorqssrruuusttvtxuuuxuwx{wyz|}|||€€€€€„‚ƒ„……€€~‡mKDFOOOOOPPMNRZbpvqtjoieruongVSSTTV[cz‘¶ÞéìíîíìëëéÞ£mflޤÉàààßÞßßáâßÛÚàààäéééèåÏ»±ž›¡ÛÝãçèééêêêêêêêêêêéêêêêêêééêêêêééééééééééééééééèèèèæåáÝßÕÅ·²ª¢™‰ˆgnooruz|trty|~mgfbQC</)&#$$$ $)06CKUr|œŠˆx{b^QHMU\cRNQUZ^^ahmry†ŠŽŽŽŽŽŽŽ‘Ž‹ŠŒŽŒŒŒ‹‹‹Œ‹‹Ž‹‹ŽŒŒŒŒŠŠ‹ŒŽ‹Ž‹Œ‹ŠŠ‹ŽŠŠ‹‹‹‰Œˆ‰ŠŠ‹Š‰‰ŠŠŠ‹‹‹ŠŠŠ‰Š‡ˆ‰‡ˆˆˆ‰†‡‡‡ˆ‡‰‰†‡ˆ†…†ˆ…‡„†…‚‚ƒ€€‚‚ƒ~~}ujO;3469:<>??@@@ABBDDDDDEEEEFGFKGHFFECDEEEFGGHHIEHGGEFEFEEFDCFDCCCCCBBCCDDDDCDDDEDDDDCDDCCBCCCBFDCBCBCBCCEDCBCCDDBBCCCDDDCCCCDEEDFDDEFFFDDEEFEEDEEEFEEEEFFFFGGGGGIFGFIFGFFGFGFFFDECEDDDDCDEGFEDgfhihghjhkiiihggilihhhklijjjkklmmnqoomooprqqppppsrrstttstttstuvvvuyz{wzz||{z||~€€€€ƒ€‚ƒ‚‚€ˆoLEFQQPOOOOOPPSTe}twrskhw}}hb]WWUUUY]mz¤ßéïñïíìëëåÝÝʺ°ÃâêêëêèåáÜ¿€{Š«ÜåæèéèäâÙȺÂÝÝÞàåèéêêêêêêêêêêêêêêêêéêêêëëêêééééééèéêééééèèèèèæãÏ»§˜‘Œ‰†ƒsgVAHRejiijjrt~…{uokf_TE>70+(%&!! !#(+/2;BR]l~~~oiXI?:;<>??ADHNUZ`chnpuy}ƒ‡ŽŽŒŒŒŒŒ‹‹ŠŒŒ‹ŒŒŠŒ‰Œ‹ŒŒŒŒŒ‹ŒŠŒ‹Œ‹ŠŠŠŒ‹‹ŒŒŒ‹‹ŒŠ‹‹‹‹ŠŒˆŠŒŽŽŒ‹ŠˆˆˆŠ‹ŒŠŠŠ‰ˆˆ‰ˆ‡ˆˆˆˆ‰†‰‰‡‡‡ˆˆ‡ˆ‰‡†‡†ˆˆˆ……„†…„ƒƒ‚‚ƒ~€~zmX?00158:<==>>?@ABBBCGDDDFFGHFFFEEEGDDFEEEFFGFFEGEFEFEFEEFECDDCCBAB@BBCEDEDCDDDDEFDEDDCBBBBBAAABCBBBAACBCBBBBBBCBBBCCDEGDBDDDDEDEDDCDEDDDDDFFEDDEDEFEEFFFFFFGHHGGGGEHFHGFFFGFDEEDDDDDDDDDEFFEFjiijjhgjhiiiihghiijjkhlkjjkkmmmnmnpooorrqqrrrrqqsrutttttuvztuwxvyxyz|z|}~~||}€‚ƒ‚‚ƒƒ„‚‚ƒ‚€‡qMFGSQQPPPOOPOON]tŒvptsvz|~}uka[WUSSUVV_k“ÁÛïñòðîìëéçåãããçêêëëçफ़”dC„ž†{²ÝÝÛáåææåäãâåçáÛâàäçéêêêêêêêêêêêêêêêêêêêêêêééééééééééééèçæåãàÞ¿¥Žz…‡‰ŠŒ‹zqM7+/Iuvqg^dqsˆ}zvvpnol_TNF:1*(%$##$'*--2;@DHWdb[ULB85142669;>@GMQX\_cfjov‰”“”ŽŒŒŒŒŒŽŒ‘Œ‹ŒŒŒŒŽŽ‰ŠŽŽŒŒŒŒ‹ŒŠŒ‹ŒŒŒŒŒŒ‹‹Œ‹Œ‹‹‰ŠŠ‰ˆˆˆ‰ŠˆŽˆ‰‰ˆ‡ˆ‰‹‡ˆˆˆˆ‡†ŠŠ‰‡‡‡‡‡†„„ƒ‚‚…‚‚~{zbB0,-269:<<<<>>>@ABBCCCBEFFIFGFFDEEDDFFGDEFHFGFGGHHIFFDDEFDDCCBAACBEBCDCEGEGDEEEFDEEDCBAABA@@ABBB@AAADBBBCCCCCCDCCDEEEEEDDDDDDDDDDDCEDCECCFIDEDCCDFHFGFFFFFGGHHGHHHHIHGEFFFHEFFEEEDEEEDDDDEEFhghkhghkijiihhgjjjjjkhjjkklllmnmnooooppppppqrrrrssttuttuuvvuw{xvxxyz{{{|}~|}}~‚‚‚„ƒ‚‚‚‚„€€€ƒ…pOGHSSRRRSPOPPPPXZ[ajxvxz|xxxxwsgc]SUWVV]f†¤ÄçòôñîëëêëìììëêëëëéÛ´‹hu𣧢¡„ Ãâçééêêêêäת¾ÔâçêêêêêêêêéêêêêêêêêêêêéééééééééééééèãÞßܶ§šŽ‡vz~…ƒ‚}p`N4.%4KxŠ€{l^flrvvvtromouf[OC90121/.0//*')-0136?JA;74:??=:67688:;?CIOTX^cgjqxŒŒŒŒŽ‹‹ŒŒŽ‹ŽŒŒŒŽŒŒŒŽŒŒŽŠŒŒŒŒŽŒ‹ŒŒŠŒ‹ŒŒŒ‹‹‹ŒŒ‹‹‹‹‹‹‹‰‹Š‹ˆŒŠ‰ˆˆ‰Š‡‰‡ˆŠ‰ˆ‰Š‹‡‡‡‡ˆˆ‡‡†‡†‡†‡†………„„‚…‚…ƒ‚€}zjI0*(-269:;;:<=>@@@AABCCEEEFFEEEDEFEDFEDCDGFFEEFGEDDDGDDCEDDBBBBBBAAACCDEEEDDEFEEDCCCCCBABCAABCBCAAAAAABECCCADDDECCDGDCEGCCDEEFDEDDDEDCCCDFFDDDDDDDDDEEFHFEFEFFFHGFIHHGFFFFFEGHFFFDEEEDDGEEEFhhhhhhiihjihhggjkkkkkkmllllmmnqpporopqsstrtrvsttvttuxxxxwvvwyyxxxz|{|}}~~}}~€€ƒ‚‚„ƒƒƒ‚‚‚ƒ‚„„…„ƒ‚ƒ‡qOGISSTRRQPPPPPPQNOWiz†‚€|uqpxz~–vdVUUUUUZ_l„²áðóòñîììììììëëëëëæáâàÒÁyzzrj]J”£ÆßáàäèéêæÚ½®°¹ÕæèêêêêêêêêêêêêêêêêêêêêêêêêêêêéééèåÚ®”uz|€†€‡…„ycYC710;W}Œˆh`dt~‚„ˆumjmsw_YKA<?AA?:850'"!"""'+-*'(/EV_MB;9888789;>AFKQV[^cjt€ŒŒŒŽŒŽŽŽ‘ŒŽŽ‹‹‹ŽŽŒŒ‘ŒŽŒŒ‹‹‹ŒŒŽŽŒ‘‹‹ŠŠŠŠŠ‹ŒŒ‡ˆ‰ŒŠŠŠŒ‹‹‡†ˆ‰‰‰ˆŒ‡‡ˆŒˆŠ‰ˆ†‰…„„ƒƒ…‚ƒ€|nQ4)'(-6889;:;=>>>@@@CBFEEDGEEEEEFEEEGDDDDEDDEDEEEEEDGEDDEDDCBBBBBAAAEDEEFEFDDDDEDDCCECECABBBBBCBBABCDBBCCDCCEDDECCDDCDFDCCDDEDCDDDFDDDCCDDEEEDEEEEFEFEFFFEGGHEFFFFIHHFFHGFFFGFFFEEFEEEEFFFFFhlhhhhhhhjilknhikkkklmmnnpnnnmoppopopqrrsrssusttuuuuvtxwwwwxxwyzzz{y|}}~~|~~~€€‚ƒ‚‚‚ƒ„ƒ‚ƒ‚„ƒ‚‚ƒ†sOHITSSSRRQPRSX^UQSSaqknw{vqs…}vseVXUVYXX[]kwŸÒàôöòíìëììììììëëëëëêæÍœ‹nlgfi~˜¨ž…šÌÜèëèàÖÊÀÁÉáæëëëëëëëëëêêêêêéêêêêêêêêêêêêééèæß³qhPiux|}}|zzzz{xvtn]KUb‚’‹†r`Wiƒƒ‰wogool^TKHEFINSIB>8-#! ! !!"$"!&.BdVKB><<;:867788;>CHOX^bkp|Š’ŽŽŽŽŽ‹ŒŽŒŽŒŽ’ŽŽŽŒ‹Œ‹‹‹ŽŒŽŽŽŒŽŒŒŒ‹‹ŒŽŠ‹‹ŠŠŠŠŠŠŠ‰Šˆ‰ˆˆˆˆˆ‰ˆ‰ˆˆˆˆ†ˆ‰ˆ‡ˆˆŠ…Šˆˆ„………‰ƒƒ†€‚‚€pY;,$&*1688::;<===??>?@ABCDDDDDDBDDDDDDDBDFDDCCDDDDDDCCDEDCCBBBBBBAAABCCDDDFCDDDDDDBCEBCDBBBBBAAABBCCCGDBCEBCCBCECCCCCDDCCCCCDCDCDDDDDCCCCCDDEDFEFGEEEEEEFEFEEFFFFFGGFEFEFFGGFEFFEDEFFEEEFFFEiiiijjjiiiijjiijkkmklnonnnooonnqssspqqrtusrsuuwvuvyw{wxwywwy|y||{{||||}~€‚‚€†‚„„ƒ‚ƒƒƒƒ„ƒ†‚ƒ‡uQIITSTTRRRQTXbeXPQQUW\fz{ytt†z~tsw\ZUVWWWXZ^dl°ÕîñóñïíììììììììëëìëéåÜÒÉÈÆØ×ÚÔŸˆ_O„žäêéèçæäâäæèëëêëëëëëëêêêêêêêêêêêéééééèèççæãЦ…lu„’™—•˜™Š”´¹½ÄÎǺƒhr‰™‹‰|aR`v‰‡ŠyxtrsmbUN\TOQZUUGIA3($"#$$"! !%1@OKG?@?>>;:6556789<?CJQW]cmx†’Ž’ŽŽŽ‘’‘“ŽŽŽŽŽŽŒ’ŽŽŽŽ‘ŽŒ‹Š•‹‹‹‹ŒŠ‰‰ˆ‹‰ˆˆŒ‰‰‰‹ˆ‰‰ˆ‡‹‰‡‡‰ˆŠ‰Šˆˆ††………„„†‚„ƒƒvlE/%$'0488:::<==<=>>???@BCCCCDGDEEGEDDDEDDDDGEECCDFCCCDCDDDDCBBBBBB@CCCCDDEDGEHCBBBCECCCCCEBAABACCCCBCDCBCBCCCCCCDEDEEDDDCBCDDDDDDDDCCDDDDDEEEFEGFEEGEEEGFGEEFGGGFGGFFIDEFHFFFFEEDEEHFFFHFEEiijjjjjjiiijjjjjjkkjlppnnoooppprqoqqrqsttvssuuwvvvvvwwwvwwx{{yzz{|~~}|}~~€€€ƒ‚‚ƒ‚ƒ€ƒ…ƒƒ„ƒ†‚‚‡wRKJUXTVTSTU\agrWPRRTVYX_gp~†|upl^ZUUVVVXY[\bh‚˜¼åóõòïíìììííììììììììêçæäáÞÕǨ€Zmˆ´àéêëëëëêêëëëëêëëëëëêëëëëêêêêêéééèçæåäããàØÒÍÁ´¹½ÎáàÞÕÌÉŸ®¾ÖÑÐØàÔÆ›|†Š‰ˆ‰„gLYi€woorvzri]U\YUQOLF@??:6/(((((&&'(-7HQRQMIE@>86544766689<>ELT]dgq{Ž“‘ŽŽ‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽ‘ŽŽŽŽŽŒ‹‹‹Œ‹‹‹Œ‹Š‰ˆˆˆ‰‰‰ˆŠ‰‰‰ˆ†‰Š‰‡‰‰ˆ‡ˆ‡ˆ†ˆ‡‡‡‡†………„„ƒ„ƒƒyrP5%#$+1979::<;;=;<@?>??@AABBDCCDDDEDDDDDBCBEFDBCCDCCCCCCCDCBAA@CBBAABCDDEEDDCDCCCCCDCDCCBBBBBBABBCCCCCCCCCCCECCCDDDDFDDDCCCDEDDDCDECCDCDCCDEEDDGFEEEDDEFFEEEGGGFEEEFEFDEFEEEEEDEDEEFFFFFFEDhjkkjjjjihjkjkljjlllmnpprpqqqqpqpqurusutsttuvvvv{vvwxxwwxxyz}}}||}~~}€~€€…‚ƒƒ„‚‚‚‡„„ƒƒ„‚†„…ŠwSLKWVTVVTUWetsnXQRRSSRQS[j€‰{‚ur}]ZVUWVWXZX[[_cq„²áðóòòïíííííììììììììëëéÜ×—xŒ|ˆÂÅßäêëëëëëêêêëëêêêêêêêêêêéééèèèçççææäâÜÕÐËÆÅÈÔãäããäåæåäÝ×ÏÆÄÈÚææåæçäá²’ŠŠ‹ƒrNRjzxhcgqˆvgdd_[\^`J<=DACHC3/-,,,.015Hbkusoa\IA<76666766789;>CHOWYbio|Œ–Ž‘‘‘ŽŽ’‘‘’‘‘‘‘ŽŽ‘‘ŽŽŽ“ŽŽŽ“ŽŽŽ’ŽŽŽ“Ž–‹‘ŽŒ‹‹ŽŠŒŠŽ‰‰†‰ŠŒŒŒ‰Š‰Œ‰Š‰‹ˆ‰ˆ‰†ˆ…†……ƒ‡„„~w^<'#$(-98:;::;;>;;;<>?>???@@BBBDDDCCCCDDBCBCDCAFFFCCCDDFDDCCBAACCECDBCCCDFCDBECCDDCDBBBCCCBBBBAABCDCCCEECDCCCCCBCECCDDEDCGDDDFDCCDCCCDCDCDDEEHGGFFEDDFEEEEEEFFGHFEDFEFEEEDEHEFEEEFFFFEEGFEDijjljjjjjikmmllklmmmmmpopoqrrsqrqrurttutttuuvvvvvvwwxyxxxxz{||}|}~~€~|„‚‚ƒƒ‚‚‚ƒ‚‚ƒ‚ƒƒ„„…‹zTMMWVUVVTXZj‰nm^TTSSTSSUVdysz„utz\YVVWVZZ[\[[[^bny Ðßôöòîííííììììíííìììêáѽ°¤–““®ÙåéìëëêêëëëêêëêêêéééééèçææåååàÝÙÖÔÓÓÉÅÁÄȸ´ÀßââããåèééèçæáááåæçççèééæáÎÀ¤‰ƒwaD[†‰‹q_alpxg]adefekd[KLIHLRA79A@@?>>>F[j‘ŽŒ{qWKIGB=:88777777::;?DKRZZ^gu‚Š‘’Ž‘‘ŽŽ‘‘ŽŽŽŽŽŽŒŽŽŽŽŽŒ‘ŽŒŒŽ‰‹‹‹‰‰‰Œ‹Š‰‡ŠŠŠŒŠ‰‰‰Š‰‰‰Šˆˆ‡ˆ‡†…†…„ƒƒƒ‚ƒzjD*$!&+38;:::;9;:;<====>>>@@@AAABBABBBBBBBBCCCBCDCCCDDDGBCDCBAACCCDDBCCCCCCCBDCDDDCEABBAAAAAAABBABACBCDCCCBBBCDCCCCCCDFDBDCDBCCCCCCCDCCCCCCEDDDEEFFEEEEEEEEEDEEFFEEEFFFEEEEFDDDEEEFFHFDEEFEiiijjjkjmlonmlllmmnoqnppsssroppqqrutyuvuyvuuvwzwxw|xyyyy{y}}}}~}€~}}~…€€†ƒƒ€ƒ‚…‚ƒƒ‚‚‚ƒƒƒƒ„‚‰†‡……‹{UNNWVUVUTZ]ruoo`YVQSTUSQQYdjkq{tuw[YVXWWXWXXXXZZ\^em°ÕïòôñïîíììììììììëëêéæãâßÃ¥™›ÚäèêêéééééèèèèèççææææåäÞÙ×ÕÒÒÔØÎÔÀÏÒÍÆ¿Ã®¡¥²¸¾ÈÓÐÐáãæèèèçæçèçæææèéééçåÚ׬ˆ}_]j”¶’{e[cco\[_hsuqnl^SPRKOL@<BQ^[VRNOSUnwˆ„€qURUY^J?96777666899:=AHOPXbft|€‹‹“‘”‘’‘’’’•‘’’‘‘Ž‘‘“Ž’‘“’‘ŽŽ“ŽŽŽŽ‘‘ŽŒŒŽŽŒŒŠŒ‹‹Š‹‰Š‰‰‹Œ‹‰Š‹ˆ‰ˆ‹ˆ‰‰‰‡†‡ˆ…†ƒ}qN/&!#(28;:::;:;::::;=<<==>>??AAABBBAAABBBCDDDCBCBCDCCDGCCDCCBBECBCDCEDDDEDCCCCDDDDEBAA@@?@AAABBADBBBBCBCCBCCCDDDEDDCDDDCEDFBDCEDCCCCBCBCDDEEEDFFFFFEEEGEEFEDEEEEEEEFEEEEFGHGGDEEFEEFGFFFFEjjjjiikkkkllmmllmnnopnoqqqrrqpqsssssttstvvwvwxwwwwxxy{zyzy|||}}}~}~€€€€‚‚‚ƒƒ‚€‚ƒ‚‚ƒƒ„„„†ƒƒ†……„…Š}WQOVVVUUU[^qtrqqr^RTSY_\\\ZZZi…tvw\YVXWXXYYXYZZZ[\^cj‚™¼çôõñïììììëëëëëëëêêêêêèäÝÉÑÝáåæåääãâàßàÞÛÖÖÔÏËÈÄÂÂÆÊÒÓØÛÞäÜØØÝßßµ¬››Šœ™šœš–›¤»ÜæééèèééæâÙâçééééèèçà½©š‰•Ÿ›Œvlgjlgachovi^[YTPVLPNI@IU_k^ZWQNKPX`gpqidipbQ><887777687789;?CHSY_ips}…‹•’‘‘‘‘‘‘‘‘ŽŽŽŽ’‘’ŽŽŽŽŽ’ŽŽŽŽŽŽŽŒŒŒŠŠŠŠ‹Œ‹‰ŒŠŠŠ‰‰‰‰Š‰‰‰‰ŠŠˆŠ†ˆŠˆ………†‡ˆ„†ƒ€xX7*!"'/789:::::9:89:::;;=>>>>@A@@?AA@@ABAEDBDCDCBECBCECBCDEEBBCBCBCBBABBCDDDBAAABBBBA@@??@AABBAACBBCBCCCCBCDBBCCDEEDDEDBCCCBCDDFDACCCCDDDDDCDDEDFFFFEDFEEEDDECEGEEEFFFDDEFGDEDEDEDEEEEFFEEjjpiikolollllnnmnoppooopqqrsurssstutwvyvvxuvwxyw{yyx{{||||{|||~~~}}~‚€€‚€„ƒƒ‚ƒƒƒ„†………„ƒƒ‚„ˆ†………Œ[SRVWWVWX\`qsxsrueZUT]ewjZSSU]elxz[ZWWXYYYYXYYYYZZZ[`cqƒ³ãðòòñïíìêêêééééèèèèççæãáàâãââߨÔÏÌÉÄ¿¸±®¨£žž ¤¬¸ÆãßåææåäãáÞÞ;¬¤Ÿž¥««©¡›—’—Êâçççèçäл°¸âçèéééééæàÌÅ–œ’”¡¦˜ˆ‚€€wpopnc\[^d_\[[^_[Y\ac^WbMJEADJUmp”yulQ=;8887766666788:;?IPZgpux}„‘Ž•—“–‘••••”‘‘‘‘‘‘“““‘‘“’“‘“‘’’’’’‘‘’’’•‘ŽŽŒ‹Œ‘ŒŒ‹Œ‹‹‹‹‰ŠŠŠŠ‹ŠŠŒ‰‰ˆ‰ˆ‡†…‰‰‰„‡†ƒ{lB.#"%,668:;;:<9:9<::9=;>=====>?@?AABA@A?BDCDCBBBBBBBDCCEEBAACCCDCDBCBCB@ABCBBBAABBAA@A@B@AAEBCBCCCCBCDEECBBBBDDEEHEDDCBBCCCCCCCDBCCCCCDCDEDDEFEGFFGGGFFHEFFFFFFEEEEDCCEFFGEFEGEFEEEEEFEEEkkkikllknnmlnpoppppooqqprrsutrstuuuuuvwwwxvvxyyw{yxy{}|{{{|}}|}~~~}|€€€ƒ€ƒ‚ƒƒƒ„„…ˆ††…ƒƒ„†††††Ž‚^XUV\YYWXZ`pvvrqrmjbYbfp}_TXZ[YdudYYWWXYYXYY[[\ZZYYZ__`mz¡ÎÞòôñíëèæäãáàßÞÝØÓÎÌÉþ¼ºµ¯«¨£ž™–Їƒ|ywwx|Œ–£µÅßåææÜ˶§™šœ £©³¾ßááÞÐÉĬ¬¦¹àäåæãÕ»¤–ƒÚååééêêééáÕœ|‡ž³¾Ì«—•–‡{zyn]ZYacfiq|{wslgd[SNKFD>:=CWmpqu{mUF>;98876686677779:CJXdkqyvyx‰’’’‘’‘’”‘‘’‘“‘‘‘‘‘’ޑޑ’Ž‘“‘‘‘ŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹Š‰‰ˆ‰ŠŠˆˆ‰Š‰‹‰ŠŠŠŠˆ††††„‡‡ƒ}rN4$""(258889999888888::;<=<<===>>>>??@B@AAABABCCBBDDEDDDGBABCCBBBBBBBCD@@AAAAAAA@B@@@@@BAABECBABACCBBDDDBBCCCCDDDECDDCCCBBCDCCCDBBCCDCCDEECDEDDFGFGFEFEGDEEEGEDEFEDDCDEFFFEEDGFFEEEDDEDEEolmllmmmrnnmnoopqpqppqqqssuuvvuvxuywzxwwyxwx|y{{{z~|{|}~~~~~}€~~~€‚ƒ€‚‚ƒƒƒƒƒƒ„„„„„„„†‡††…†„…†‰‰‡Žƒa[XUWXXWWY^q}t{z{z}`bh€…eTYYVTXb_YYXXXXXZZXYWZYY[ZZ\[]^enޱÔíïðëãâàÖÌ·±¬¥Ÿš–’Œˆ„~zwtqnjigdba__`abdgovŠ™§ºÎÙâܺ«ˆ‰lq}”¯°ÈÎßàåÒᾯ¨¨³×ØÙæßÃŒ„ªäçèêêêêêçØ¡tlq‰¼±ÓÐ˳¨§§œ‰‡‰‰qbhoqtz‰ˆ†…ˆzyk[NJIBA;94CNaZTQck]OGC;8897679677879:@FVbhqz|~|{€„†““”š”––—’“’’’‘’“““‘•”•‘“’‘’’’”‘‘’‘‘‘’‘•‘‘‘’‘‘”““’‘Ž‘ŽŽŽŒŽ‹Š‹Ž‹ˆ‹ŠŠŠˆŠ‹‰‹Šˆ‰‰‡ˆ‰‹†Š††~z[9&"!%,4798:9;9879889::;<=<<=<<;<>>>?@@?ABAEBCCEABDECBBBBABCCEBDBBBAAFB@ABBBCBBDABA@@@ABABCEEDAABECABCCDBBCCCCCEEFGGDCCCCBDDDDCDCBCDDCCDDEDEDDEFFHFEFFEGDFEFEDDFEEEFCDEGGGDDDFFEFFEDDDEFDmmllmnnnroomosqqqqqqqrssssuuuuuvuuyvwyxwyyxzzy{zzz~|}~}~~}~}€~~~~~}€€ƒ‚ƒƒ‚ƒ„„……†…†…………………ƒ……†…‡…eb]YVVWXXYZh|rovbmulafiŠiWf{gVZ[YYY[XYXWXZ[\YYYX\[[^[\\^dj„™»ßçâ¶¥›”…€|yvrnlihfdba``_^^^]]]]]\[[\]^`ekpxƒ›¬»ËÚÕа“‡x†Œª¬®°¼ÓÑÐν³¨ª¯ÕßàåãÀ©˜¡«Ååêêêêêëêä´wkcu‹¤®ÍÕÏÈÆÄ¾·«ž™”‘’™—‹‡…„{wx|zzVLKKEIIHKUm}p^^ehe[OE><:976676678778?CR_hq{~|zyyz‚…‹“““’’—’’‘‘’“”’’’’’•““’’’’“‘’‘‘‘’’‘‘‘ŽŽŽ‘‘‘‘ŽŒ‹Ž‹ŒŽ‰‹ŠˆˆˆŒ‹ŠˆŠ‰ŒŠŠŠŠ‹‰ˆ‰‰Š…‡…„€~kB*#!$(268788998888889:;=;;<?<;;<=??>>>?@@@@ACCBABCCCBDBCBBBABBBABBAA@@AABBBCBBBAAAA@AABABCBABABBCCBBBBBBCEEDCBBBDEDCCCDEDDDDCCCCCCCDCBDCDCDDEEFFHEFGFFFDEDEDDDDEDCCCEEDDDDEEFFEDDDDDDFDDqqqssrrssqqrrsrssstqruwuuvvuwwxx{wyxwxxy{yyyzy{{|~|}|}~~€‚‚€‚|„„„„„„„‚……‡†††…†…„„…‰†……‡‡ˆ‰ˆŒ†pjgaZTUXXYW^gjoreenh]ck‚nhrwWXWWXXXXWXXXYXYXYXY\[[[ZZZ]]`es¢³ž‚|jfcbb_]]]]\]YZZZZZZ[[ZYYZ[Z[ZZZ[\\Z^`ehqy…‘¡³ÂÛÐßĵ«¡¡£¢Ÿ›ž¶ËËÌɼº½ÀÐÞåæçÚËÆÆÖáæèèçççççä¢sip~’Ÿ¯ÉÒÙÛâäãØÇ½·©£¥²´£œ•‡…urpt_TJB=?CO}xs‚Ÿš˜€uy}sh\JEB@=97666678777=AQ^hq}€‚‚ƒ‚~}‚ƒ‰“”–“˜’“”˜’–“’’’’”••““’““•’’““’’’˜‘‘‘’‘’’‘“‘•‘’‘–‘‘‘“’’“ŽŽŽŽ‘Ž‹‹ŒŽŽŒŒŒŒ‹‹ŠŒŠ‹‹ŒŒŒŒŒ‰‰Š‹‡‰„sK/%""&158899987878:989:;::;<<;<;<<<=>>?@@?@@CCBBBCCCCDAA@@AACBCBDA@@@@CB@@AAAABBBCBBDCCBDBBCBBBCCBBBAABCCCDDCCCBCCCCFDFEDDDCCCCDDCCCDDDDDDFEGEHGHGFGGFFDFEFEEEDDCCCDEDDCEEGFFEEEDDDDDCCDrrssssttsssstuttuwtrtvvuvvwvwwxxyxzyz{yy{zzzz{}|||||~~}||~~~‚~~ƒ‚~‚‚ƒƒƒ…„‡ƒ…‡…ƒƒ„„…ˆˆ†…‡†‡†‰‘ˆwxwobYSUYYX\^del~qpf^elƒ’s^yvWYXYYYXY[[[XYXZYYZZZZ[\[ZZ_\\`cmvyy}wne`[[[[[[][\\[ZZZZ[[[[YZ[\\\[[ZY[\\\^`bels~‰—¤¶ÈÔÞàÜ˼±¢›Š«Ò×ÞãÑÉËÍ×ÞåçèçåàååæççåÚÐØáàâ®yr{‹¥ ¯ÍÑÛáåçèçÝÕÇ·»¹¦¥§±³°¨¡|qbLL==;ARnŸ”¬®°¢“Œˆ‰†}umfa[QG>876778887<>KYfq}‚ƒ„„ƒ‚ƒ†‹‰Ž””’“–•’’’““”•””“““‘““’’“•’““’“‘’‘’‘’’’’“‘‘‘‘‘’‘‘’‘‘‘ŽŽŽ’ŽŽŽŽŽŽ‹ŒŒŒ‹Œ‹‰‹ˆŠŠŠ‰‰‰Š‰Šˆ‡†‚{W5(!!%,38788878888:9899:::;:;;;;<><;=>>=>??@@ACCCCBBBC@@@@@@@@BAAAAA@@AB@@@CCCBABBBAABBBCBBBCBBCBBBBAAACCBCDCBBBCDBBFCDCCBBBCCCBBBBCCECCDDFCGFFEFFFHFEECEEFEEFEDCCDDCDDCDDEEEEEGDCDEDCCDwuuvwxyyyxxxwvvvyyxwvxyyyyyy||}||{~}}|{|~}~~‚~|}€€~~~ƒƒ‚‚‚‚ƒ‚ƒ‚‚ƒƒƒƒ„„„†…‡†…††…†‡Š…‡ˆ‡ˆ‡†‡ˆ‰“—†‡†‚scYSUYXXZ\`ltwxpcel„†yx’yW[YYY[YZYXYXXXYYZ]\[[Z[ZZZ\[[\]_flqƒ‘—€e_\[ZZYZZ[[Z[[Z[\ZYZY[[[ZZZ[ZZ[[Z[]]]`chmv~Š•¤´ÁÛäÞéšžªÀÖàææåæççççèéêéèèèééééçÔÉÈÑÛÛÎ’t|“©Ò×ãåçèèééäᳫ§¤¥£¢¤«´¡«…–v|]RJB>@Lbx–¶¬•”“’“™žp‡‚€_F987888899=>DSgq€‚‡«œ•› ¦¯¡“ˆ‹‘Ž•““”–““”••™•—””’””—“““‘“”“’’’“““””””•”“‘’–’’‘’’’‘‘‘‘‘Ž”‘‘’ŽŽŒŽŽŽŽ‹‹‹ŒŠŽ‰‹ŠŒ‹‹‹‹ŠŽ‡Š~f?,"!$*38888879888::88;::;<:::<;;;;;<===>>@?A@CCAAAABB@@AAB@@@C@EACAAA@@@@ABCBEADABA@BBBDCEBCBABBBCBCAABDCDEEBDCCAABFCDCDBBBCCCBBBDCCCCDGEEEGFFFGGFGEDEEHFFEFFDDCDECCDEEDDEEFEEDDDDDCDDDvuvyxwyzxwwxxyyyy{xwxzzyz{{{||||}|}{}~~~~}}~€~~€€€ƒ‚€‚ƒ‚‚‚ƒƒƒƒ„„„…†…„†…‡Š†††††‡†…‡‹ˆ‡‡‡‡‡Œ—•Ž•˜‘Šs_UQTZY[[\_am|pgdk‚ƒzk‚›yW[ZZZZYZXYZXXXZZZ[]\\[\YZZ][[\]^`dhx»À¼‘mc\[\[ZZZZZZ[Z][YZ[[[[[[[[\[\ZYZZZZ\^_`dhmt}†™¤°¹Å·³µµÀàÜåçççéëëëëëëëêêêêëëëëéÝÊÁ¹ÈŲ̀”†‚“›ÕÞãèêéééêçÇ™Šˆ––”’Œ…ƒ„€wkb_ZMCFHSWep‰¤¨©§ª¡†|lo€rmE:776677884/8CTgoz}ˆ ¹¼ÃÆÈ»¼©œ’…”‹Ž’“—–””””“•••””’“•–“““““”“”•’’“‘’“’•’“““““‘‘‘‘’““‘‘‘‘‘ŽŽŒŽŠŽ‹‹‹ŒŠŒŠ‰‰ŒŠ‹‹ŒŠ†Š‚pK2#""(1467987887777979:989:99::;;;;;<=>=<??@@ACAB@@A@@@A?A????@@AA@@??@@BAAB@AAA@C?@BAABBBBCAABAAAAAABBCDCBBBDCD?@AABBBDBBDCEBBBBBDDDDDDDDEFFFFGDFGFGFFECEEEEFFEDDDEEEEEFFDFEEDDEEFFFFFwvxyzw|yyy{zzz|yyywz~}}{€€€€€~~€€„€‚€€ƒ‚‚‚ƒƒ‚ƒƒ‚‚„„„„ƒ‚ƒ„…„„…‡‡‡†††Š†ˆˆ‡†ˆˆˆ†…†‡‰‰ˆ‡‡ˆ‰Ž˜¢§¯±²™„lYRMRZYYZXXevael~€€{–xXYYYYZYZY[YYXXYZZZZX\[[YZ[[[\]]^_`cm’À»Ï{l]]\[[ZZZZZZZZZ[[ZZZYX[\ZZZZZYYXYZZZ[\^`cglrx‡š¤©¯®ËÔÞâãååæèèééêéééééêêêêêééÛÆÀ¿ÕÉÅÀ·« œ³×âèéêêêé騶‰oowŽœžž™‚ƒ_ZYXZ`jbXSSRSTQZew–ž»µ®‘ƒtcZTWZO@968666678.+-5AP_ny‚”¦ªµÇÁº¼¾¡§˜›”‡Š‘‘œ•—•—–•–—•—““–˜”ž—•••“—”“’“”š“’’•’–“””“’““—“”“‘’“‘’’‘‘”‘””•‘’‘‘‘‘Ž”ŽŒŽŽ‹ŒŽŽŒŒ‹‹‹Œ‰‹…zZ9%""%,4677798797779878989999:::;=;;;==>???@???@@??A@BA@AB?A?>?@AAA@??@@AAAD@DAA@C@@@@ACBAABAAAAABABACBACBBCCCCDBAABBDCCBBABBBBDCBCFFEEDDEFGFFFGEFFFFGFEEGFFFFFEDDDFEEEFEEFGFFEFEGFEEEEwwwwwwyxzzz{{{{yz{||}|}}~}~€€€€ƒ‚„‚€‚…„‚ƒ„ƒƒƒƒ„……†„‚ƒƒƒ„„„„ƒ…††…††‡ˆ‡†††††ˆˆ‡†ˆŠ‰‡‡Œ‰‰‰‰ˆ‡ˆ‰Žš§°¾È´¡zfUMKPXZ[ZX`e\Zdm|||…Šyafga[[YYYZ[ZZYYZ[ZZY]\[Z[[Z[__^^^_`fl‘¨Ä¢wd_\]\[[ZZZZZ][\\[[YY[[ZZ[[[YXYXWWWWXZYYZ\`cgkosy„‰–¦±»ÂÊÒÛßåæççæãâåéêééééêëéæäÜÔÒËÎÛÖÏÏÍÜçêëëëëéèη„{pƒ¢¥®´¾¥‹mQLIMQac]OPQTZQJRThy†€unhSFCA>;97777866660+/3=DOYdr†«³¾Ç¬¡•Œ”š¨£•ˆ‹“•–—————–—“•–••˜—–“”””““’“–•’’‘”“”““”“‘’’”““’“““‘“““’”‘‘‘‘‘’ŒŽŒ‰‹‹‹‹‰‰‰€kB)$"$(367789888776667788889899::;<;:=<<=====?>?>>?@@BA@A@?@??>@@@?@AA@@@ACD@@???@?@@@?BCA@BBA@AAA@@@ABABCBBBBCCCCCBBDBBDB@BCCCBCCBCBEEEDCCDDFGEDFFFGFIGGFFFFFCEDEEFFEDEEFFFFFDEEGEEGFFyyzxwxzx~|{{}}}}}}|}~€‚~€€ƒ€ƒ‡ƒƒ…‚………„ˆ„„„…†‹……„…†…†‹‰ˆ…††‡‡Š‡ˆ‡ˆ‰ˆ‰Œ‹Š‰ˆˆ‰‹‰‰Š‹Šˆ‹‹ŠŠŠŠŠ‰ŠŽš¯±ÖÈ» –Šr_QLIPYY\YYYWYdn€‚…‡†‰|{‚yh\\ZYYZZ[\[Z\\ZZY[ZZZ[[[[\\^^_^`_acky–º¥¤…g_\[[[[ZYZZ[ZZ[[[Y[YYYXYWYYVUUUTSSRSSTTTUYZ\]^`bdgmy†•¤°ÁÉÑÛåáÛÕÇÃÈÝèèéèèèéèèèççæßÚØÙÜßäæçééêêëêéÙ½§–’“¥®¿ÖÕÓ¯mOHDM]lo`SQRUUJOPSZaa_\YQC?<:87777787678633258:>EO^pŒ‘’‘ŽŠ…‘›£»³²¥¬˜†‹–˜š˜˜—————˜–—•˜———˜––”““”••––”˜”—“”””••––””““““”—’’’“‘‘’“’’”•’’Ž“‘”“”‘‘‘’ŽŒŽŽŒŒ‘ŒŒŒŒŠ…tL.&#"&15778897777776:888889999:99:;<<<<<====?>>>>??ABA@???B????@A@@B>@@@ABD@B???AAA@B?ABA@AA@@BAA@A@@BABDBBABBABBCAADBBCBABCCCBBBBDDDDECCCDDGFEDEEEEFHIGFFFFFFEEGEIGFEEFGFHEFEFEFFFFGFzzyyyyyx{{{|}}{{|z~|}}€~€€…ƒ‚ƒ„ƒ‚………ƒƒ„…‡…†‡……††‡ˆˆ‰ˆˆ†‡‡‡ˆˆ‡‰‡ˆŽŒŠŠ‰‹ŒŠŠ‹ŽŽ‹ŠŠ‹‹Œ‹‰‹‹‹‹‹‹™¥©¢—”‘’§‡j[NJINVYZYY[[douƒ‘…ˆyiz•sa`\[Z[Z[\ZZ[]\\\\[[[[\^\\\__^_``abbin† ¥½’rg]\\[YY[ZZZZZYYWWXWVUTTTSSRQQPQQRRTUVXWXZ[\]a`caeens‚ ²½ËÕÙÐË·«²ÔááåæáÞàãæèéêêéèæåæçççççæçéëêêèäßÛÍÈÁÉÏØÙ×̸›}ZX]Œ¾°Ÿ‡sia]LQRS[YYYYNF@>;:987787777876777798;?FO^jqvy}~€ƒœ©ªºÕ¶®©‘ˆ’‘––š™——–––˜”™––——•–—••””–˜••–”–’’“””””•–”’“”””•’’’“‘’”“’”“”“’‘“‘‘ŽŽŒŽŽŒŽŠ…{W4)#$%,47878:677777677889688889::9:;<;;<<<<<>???>=>@@@@@?>>=?>????@B??@@AA@@?>?AA?A???@@ABA@AAB@@@@@@AAABBAAAAA@BCAABBCCBCCCCCCBBBBBCECBCCCCDEEDFEEEFGGFFGFFFFEEEDFHFFFFFFFDFEEEEDFEEEyyyy}yy{z}||{z{{|~}}~~€€ƒƒ‚ƒƒ‚…ƒƒƒ…†‡…††Š……†ˆ‡ˆˆ‰ˆˆˆ‰ˆŒŒ‹‡ˆ‰‰ˆŠˆˆ‹‹ŠŠ‹‹ŒŽŽŒ’ŽŽŽ˜£šŽŒŽ‰‡š |cVNJHNTW[ZYZcqƒx†ˆˆynƒ‡‚od^\[[[[ZYZ[\\\\\[[[\]\]\\]\]``aa```ej|Œ¤¿šp_\[\[YXXWWWWWVUUUSRRPTQRQRRSTTTVXX\\[ZYZ\^adeda_]ahw…—©¹ÑË•œ§¹ÍÑÍ·°ÂÐÕâååååææçèèèçÛ×ÖÜæçèèèçææåÛÖÛãäåäâÑÓ|œÚàáàßδ—€oa\VVKMJGC?<<<9:998877789778788889;>CKWjp}‚ƒ”´§¼º¼¸µ«¢¨±Ÿ‰Œ’œ—–––—™–š—˜˜—–™–˜•”•–•”•—”–“—•™””””•—“–––”™”““”’“••“”•””““”’’‘’“‘‘’’ŽŽ““‘‘‘‘‘ŽŽ‘Ž‹Œ}h>-##$*27888:778:798779898;98899889;<;;<=<=>=====<<=?>=?>>>=??@@@??@??A??@@@@??@@@@@??A@@@AABBB@@?@AA@@@ABAA@AA@BBBBBBECBCDCECECBBCCEDCDDCFCDEFFFDDEFGHGFFGGFFGGGEEFFFFFGGGGGEDDGDGEEExyyzzyz{{{||{{{{|}~~~~~}€‚‚„ƒƒƒ„†„ƒ‡††…†…†‡‡††…‡…‡ˆŠ‡‡‡ˆ†‰ŠˆŠŠŠŠŒŒŒ‹‹‹ŒŽŒŽŽŒ‹‹ŽŽŽ‘˜š‡xˆ…™–Œr]TMJHKTW\ZYbqkn{Š‹‹x`js„}vvkda[[ZZ\\^]\]^^^\^]]]^\`]]```bbbadegs~–¹Ÿ–{d^XXXXWWUTUTSSRRPRQRSSTUVXXYZ[[[Z]^cgc^gq~Š‚{ywnadfq|Œ ªµ©•›—š“™Ì¼Ï«¬°µ¾ÉÕßáâäååä×ÉËÇÌÓÚåèêêééåàÜÙØÝåæåÙÓÕÙâççæäáÑÕ®¥™~pdXPID@><>89888777778888:999999;=DM\nw†}~ƒ‰œºÔ²³Åƹ–‰‰Š’™—›˜–—•———˜––—–––•••”””•””“•”–••–•“”“••–””••““’“’’““’”•“‘’”’‘’“’‘”“‘‘Ž‘“Ž‘‹‹sJ3#"#(0677577777788778888888888889;::;<<;<=====<<<<==>?>=>>?=??@>?A@?B??C@B@A@BBA@??AA@A@AAABA@@?@AA?@@AAACA@@??@BBBBCBCCCCDCCDCCCBECCCCDEDDDFGFEEEEGGGEDEGFEEDDDEFFGFEHEEEFFEDDDEEEHvyz{|{{{{|}|€{}||~‚~}~~~€€ƒƒ‚„ƒ†ƒ„…„„ˆ‡‡†ˆˆˆ‡‡†††‹‡‰‰Š‡‰‡ŠŠŠŒŽŽŒŽ‹ŒŽŒŽŽ’ŒŽŽ‘ŒŒ“’‘‘“›¦¡…x†{y“ºŸ‚jZRMJGLOU[Zamzyyx{Žwaew…€Žsd[\[Z\\^]\_^\\\\]]]]\`^^_```_`aabcdksª¥‡h_UUUUSSQUQSSTTUWWXYZZ\[YZ][Z[[[^dnvv_lƒ¡œ’y…‹m\Zairƒ˜¡œ«°¶¼ÄËÊËÉÅÀ¬©¨¦´§¦¬¼ÅÐßÜÛÖÔÁ»»ÃÓãåææçææßÑÎÏÙÖ×ÛÞâåçççæäÞÔɲ¹»¾³§•‚tgZOHB=:998889667988998887:9;>ELWku‡……‡…††”´±²®Æ¿Þ¸¨§¨—Ž’”—š™˜—™˜—––——˜˜˜˜™™˜˜””•™™š•˜•™–˜•——™˜—•”–™”•”•“™–˜•””“’’““’’““’‘‘”‘–‘““’’’Ž’ŽŒ„}Z9%""%+5786878787:888999887778988899:;:;;:;;<<;;;>=>>>=>>????A?@?@A@@B?>@???@@BB@@???@@A@BA@CA@?@@@BBBAAABA??A@@@ECCDDDECBBEBBDCCCBEDDCCCFEGEFFFEGEEEFGFEEFEEEEEDGFFFGFJGFEFFEDEEEFFFzyyzz|{z{{{{||}|}~~€€€€‚€ƒ‚„ƒ…ˆ…‚ˆ„„„‡‚‡‡‡†††††‰‰ˆ†‡‡‡ˆŠˆ‰‰Š‹‹ŠŒŒŒŒŒ‹‹ŒŒŒŽŽŽ’‘‘”ž§«•yvstŒ±¹•vfYRMJHJNU[^`_alvy’{n|‰…˜ƒ~nbb^]\\`]\]^]]]`]^^^]`_]___`__aabccejn‚›¡º’reXVTTTTUUVWY[[[[\\[]\\\[\^]][^_dp–ydx¥|_w˜‚x^Xccilw‹ŸŸ®°²¸×ÐâÜäãá˶¯®ºÄ¼¤¦©ÆŸ¢§º¹»¼´¥Âº¿ÄÊÑÛâáàÚäáߨÆÞäèèçççåã×ɺ²ª±³¶®´²¯£•„th]RIC=;99768887877777:89<>CIVgyŽŠ†€€…œ®Ë¾É»¾Ö¯Ÿ™“Œ“›™šš˜–˜˜˜˜˜—˜˜˜––––––•••˜–™•–”–——•˜–••”””””““”””””“‘‘’“’•’‘‘‘‘‘‘Ž‘‘‘‘‘ŽŽŽ‰ƒiB)#!#(356677777776888987887777899:9:99:::::;;;;;><<<<<>=>A??@?@@@@@@??@@@???@BA@@?@@@@??AA@BA@?>??@@C?BAA@@?@@@ABCCCCCCCBBCBCDDDCACDDDCCDEEEFFFFFFFDFGFFEGFDEEEEGFFFFEFFFFEDDDEEEFFF|zzyy|~{z|{z}~}|}€€€~~€€ƒ‚†……„„„†‡ˆ„ˆ„„…ˆƒ††ˆ‡‰‡ˆˆ‰‡‡‡ˆˆˆˆŠ‡‰‰‰‹ŒŒŒ‹‹‘’ŽŽ•“Ž“‘•žµ²²x}mp‰¬»«ˆqbXRMKHJMS[Z\^hlv”€››•~˜„~yqid^\\_]]]^_^]]]]^^]^`___``_`abaabccdhw„½›‚p\YXY[[[\]]][Z[[\[]Z[[\[[[[[\_dy‹“{„Œ¨p`k}†zhfhd`_cjs‹’šŸ¥°»ÉÒÛåååãâãäää忨ÊÀ¶¶»¾»º°µÃ¶§¦´¸¶³¾ÔâàãáãäæèçççæäâÒ³©¨¨«¬¬¬«««¶¶µ°«Ÿ“}k\PG@;77677778;778899<?CHUbt‹‰”Šˆ‡„ŠŒ¤Ì¾Ø¾¿Â¶½×’”—›«£žœ™›™š˜™™˜———š—••—•˜—™™™–›———™••––•˜––”””•”—”““•’’“““‘‘“’“–––’•’””•’‘–’”““ޓЉtK.%!"&16677778686788889788899888988999;9;:::;;<;><<<;<><<=>???A@?>@?????@???AAAAAAB@@???@A@AABB@D??@CBCA@@AAA@?BABCCCCBBBAEEDDEDDBBCCDDCBEEEFFFEFFGGFFEEEFGFEEFEHGGGFGGGHFEDDDFEFFHF|zzyz||z{{}||}}}}}€€€~€€‚‚ƒ†………„„„ƒ„ƒ……††…‚††‡‡ˆ‡‡‡†„‡‡ˆˆˆ‡‡‡ˆˆ‰ŒŠ‹Œ‹ŠŒŒŒŒŽŽŽ‘•ª·vjcq‹¬¹°™‚l`YSQLIJLRZ__`bp”‚{€}€šqu‚xxl``_^_``_^^]]^_`^^_``aaaadbcbbbccdedjs¸¢~e_\\]\\\\[[[\\]\^Z[[][\[[[\]]jtux}€ylqsvvs}~€vgd^ehnt~‰‘—¢¯¸ÂÒáäççèèçèéééèåãáâãÙÔËÆÆ»¯ ¡¨°¢–¥²ÀÞåèèèçæææãØÊ½¯¦ £¦¦¥¤£ ¢¥¥¥¥®¬¤›Š{m`ULE?<:9778797689<?CHS`q†–’‡†Œ–¢ºÄÅǽÐ׺¨ž™•“˜¨¸°£Ÿ››š™—˜–˜—˜——––•••—–––˜•—˜˜—˜”–—••–˜–“••”’—””“”’’’’’‘‘’’“‘””•‘‘‘’’““’‘‘‘“‘“‘Œ~X4(!"%,67778777889777777889677777677899:;:::::;:;<<=;<<<==>>>@?@???>>>???????>??@@?>@A????@@@@@@???@AA@?@@A?@@@AAAAABBBBAACCCCEDCCCCCCDEDFDDDEEEEEEFFFFEEEGHFEFEGHGGFFFFFEEDDCCDEEFE~|zz||}}~}~|}~~~‚~€€€€…‚ƒ‚„‡‡‡„ƒƒ…„††……„……„‡‡‡ˆ‰†††‡…ŠˆŠ‡ˆ‡†ˆ‰ˆŠŠ‰‰Š‹Ž‹‹ŒŽŽŽŽŽ‹ŒŽŽŽ“’’•›§§ªqabr–°·½«’zi`XTOMKJKR[Z\^lw{€†{ˆ|€Œwit’¨„wg_^]]^_`^^^````__``_`bbabbccbcbadea\blŠª§µ‹md^]\]^][[\[\\\\ZZZ[Z\[ZZ\^]^^`cfjt†ŽŒ‡vsrvšŸœ|bY[\]`cks}‹”¨¬¾ËÙÞååäááåèèèæåäãäåäãåàÍ´©£³¨ž’”ÇßçéèèçæåäÙÌÁµ¬¤ £¤¨£Ÿœ˜™š›Ÿ§¼¾¿¸°¥™„pbTJB<76668688;<=BGPZj~…”Ž”‹‰†•¨²¿²¨ÏÖ¾¾¿½¶´¡°ÐÀº¡œ˜˜˜–œ™œ——–šš›œš—™—˜——™Ÿ—˜—š–”•–—š–˜–˜˜—–™“•”””š•”’˜’““””•“’‘–“–•–“–”“’“”–‘’ŽŽƒk>+"!$)18998778887666677999778978777889;;:=:;<<;<<=<:;<<?==>=>>?A@A>@>??>???@?@?????@??????@@@@@@?BAA@@?BAA?@ABAAADCCBBBCBCBCCECDCBBCCDDDDDCDEEEEEEEHGFFGEHFEFHGFFHGFFHFEEEDDDGFFEGE{}{{|{||||}|}€}}~~€€€€€ƒ„‡ƒ„ƒƒƒ†‚…††‡…………††ˆŠ‡…†††…‡‡Š†‡ˆ‰‰‰ˆ‰Š‰ˆŠ‹Œ‹ŒŽ‹ŒŒŒŽŽŽŽŽŽŽ”™¦Ÿ o_aq¢º¸½É¨Šxjb[VQNKJIPY\^cfox†~ƒ|€œwav—€vie^]^^__`___^__`a`_`cbbcddbbbcdeb[]cj€–£¹’wlb_^^^^]\[\]]]]][\[\[Z[[\\]]^^abs•Œ|wxz~€•¨Š\Sbeb\[Y\ajt€Œš§¶ÊÒáÙÑßáææåãȰ¹ÅÎÖàãäáÌÆ¯ ¡›”ŒŠ©ÛãèéèçæåãÚËÁ¶®§¤žŸ £££¢ ›˜•“’‘‘—œ¦««ª®´¶¸˜}k]SJC<;:9899:=?BJRat‘›‘Œ†€„„‡œÑų®«ÉØÀž—Ÿ´ÓÏÔ½©¢œš˜˜—˜—˜–—˜˜———˜—˜˜˜™š———––—––•–•—–˜––––“”••“““”’“’““”“•“’’’–““‘”•“‘“Ž’’’‡vK2#""'-3588888886667998967778887777789<:::::9;;::;<;:;;====>>>@>>?>?>>@???@@??=>=??@?????@@@A@??@@@A@@?BAA?@BABABCCBBBBBBCCCCCBBBCCCBDDDDDDDFEFEFDDEFFFFFFFFFFFFFFGFEEEEGEEEEEFEDEF{||}}{~||||||}}~~~€ƒ„‚†ƒˆ‡ˆ„ˆƒ„„††…†‰†††‡‡Šˆ‹ˆ†…††‰…‡‡ŠŠ‰ˆŒ‰Œ‰’ŒŽŒ‹ŠŒŒ‹ŒŒŒ‘Ž‘‘‘’˜¦••h_`q¦ÜÃÂà‡yme]WTOLKJOWY_]ds‹•z€›w_s˜‡€”›—{h^^_a_^__```bbba```aacccdccccde`Z[^bgx‡¹™†xe_\]]]\\][[\\[Z[Z\[[]\\\\[\[]^o‚”ˆ~}•І–„r`comd[WUSTUZajtƒ¢¶ÄØÓáäççåᱤ’ÂÀØ»ÍÓÑÑÄǤ¦—–‰¢ÀåéééèçåãÙÏø°¨¤¦¡£¢«©¨§¦›—“‘’•—›œœ™—›Ÿ¡¢¥¥¢ž–n_SKD>:9888;=CKXdr‚‡‘‘”Їˆ„†ˆ¡°°±¦ÙÓÆÀ·¨±ÉËÝп²¡š˜˜˜˜—˜™•›™›™›™šš›œœšš™—˜——————––––””•—–˜•••”“““–”•”“”•”–”““””•’“““’•‘“‰€\9(#!%+45778778878678889666779777777779999999::::::;::;<=>>@@?==>@>@>>?@@??>>>>????AA@?A@A?@@??@??@CABABAAA@@@@ABCCCBBCBBDCCCEBCCECEDDDDDFDEFFFFEDDEFHGIEGGGFEEFFGGEEEEFEEEFEEEFDEF{z|||}|||}|||~}}~€~}€€€‚‚‚ƒ‚‚‚ƒ‡„…………††……†ƒ…††‡‡‡†‡‡‡‰…††ŠˆˆˆŒˆ‰‰ŠŠ‹‹‹‹‹‹‹‹ŒŒŽŒŒŒ‹ŒŠŒŒŽŽ‘’˜”t`_^p¨×ÃÇÚͶžŒ~qg^XSOMKHMSZ`bev”‚vw`t™‡u—Žr^ccb`_^`ca`baaaaabdcecddgdecfc`ZZ]beis{‘²®¥Žwk^^]]\\[[[[[\\[[\\\\\\[[[\]]elqxy{~„zz{‰rwx„s^ZYXTROQU_gt€‘ ²ÄÑãæççÜÆµÆÒÙÌÇÍÀ½¾Â¨¤™™ ¯Â×èëêéèçãÕÈ¿·±«¦¡¡¡Ÿš˜˜˜˜™“’’“”’Œ‹ŠŠŒŠ£Œ”ˆ{maWMF@=;<9=BLVev—Š…~…Š•¡ÃÓÆÆ¼²¦š¨³ÏäÞϼ«£›™œ™™—˜–˜———›–š˜š˜˜—™™˜—––—˜˜—————––•––•–••––”””””””””””•”“’’““’““’’’‘‘…lA*$!#(05667778767788787665677667666678899999:<:;;:::::<<===<<<>?>>>>>????@>>?@?>??A@@?@?A@?????>?@A@B@AAABA?@AADBBBDCCCCCCCCCBBCCBCCCCCCCDDFEEEDDDEGFHGEFEEFFFFFFFEFEEEDEEEFEEEEEF|{||}||~€|}}}}}~€~€‚ƒ„‚ƒƒ…„‡‡ˆ‡‡ˆ‰…†††„…††‡ˆ††‡‰ˆŠŠŠ‰‰‡ˆŠˆ‰ŠŒ‹‹‹ŒŠ‰‹ŽŽŽŒŽŒŒ‘‹ŒŒŒŽ‘ŽŽ‘•—wfa^^pœÒÆËÕã˳¡€qg^XRPLJHMPV]]ey|}Žvauš†uˆƒ“}adda```aaaaaa`acabbcdcccdddcdb`Y\^bdghls‰¥¡ÉŠn]\]]\[]]\]Z[[[[\^]\\\[Z\\\[[]dhxzuuy}ƒˆƒ¨“‹bZ`_YTPLNOV]ht‚‘¤¸ÍäæçæâÇÏÍæåäØáÎÀ¾¾²¢—ÁàæèéêééæäØÆ½´¯¬¨§¡¤Ÿž› ˜”’’“–•“’’Žˆˆ€€‚…‰Œ“¤¤¤Ÿ˜„ti^PC;57;CKYfq…’‹‘Ї†„††˜Á½»½À¾À«§«¬ÄÜßâÓÅ·¤žœ››™™™›œœœœ˜™™š™™—™——ž–™˜™˜Ÿ˜˜—•••–™••–˜˜——š–••———••”–”’“—”•“™“—“’‘“‘‰vJ.%#"&-676777777779777667566966665568899;99:::::99:;<<<<>=<<<=???>>=>>>@@@??A@???@A@??@@A@??A?@?DBBABAAA@ADAAAABABBCFCDCCCDDDBBCCCCDFDECCCDEEDCDFDEFFEEFGDEFGGHFFEEEEEEEEEGFFEEEFE{||{||||~€}}}}€~~~€‚~‚ƒƒ€‚‚‚ƒ…‰††‡„ƒƒ„†††‡‰‰ˆ†…†‡†‡ˆˆˆ‡ˆ†‰‹‹ˆ‰ŠŠŠ‹Š‹‰ŠŠ‹Š‹Œ‹‹‹‹‹ŒŒŽŒŒ‘•tnda`mŒªÇÅÆÒáϸ¥’ƒti_YUPMKIKPW`dgffu›sbw›†v‘…ˆ”jyƒugfdegecabbaaaabdddfeedddgc`\]`ffhghkp‚”±Ó°‚l_^]]\\_\]\^\\\\^_]^^]]\\]\]^blz„ŒŠˆˆ|}{{ufozph_WSMLKQVakyˆš«ÂØÝå䯯È×áØÓ×ݽ²´Ã¥˜¹âèêëëêéèåØÈ½µ°¨©¦§œš™šš™”’‘Ž‘‘”‘“’‘ˆƒ€xwxz|zyzzƒˆ—£¦´³±—„a@4047>CNWds~Œ™Š„}}‹—¨ÎÎȾ²¤¢¡µ×åêêßÀ®§¢Ÿ››š—˜™™˜˜›š˜™˜˜———™–™™™•™˜˜––—–—˜–•––•–—™–––—“—”–“”””’’’““““–“““‘ŒV3(#"$+5666787777876666766656655766679978899:;:99;::;<<<=>=<<===?>>=>??@@??AA@@@@?A@@@@>???@@???@AA@BBBBA@A@AABCDCCFECCBBCCDDACCCCCDCCCCCDCBDDCCDDEFFFEEEDDEEDDDFFFEFFFFFFFEFEEEED€}€||}€~~ƒ~~€€€€€ƒ‚‡€…ƒ‚‚ƒƒƒ…†††‚ƒ†……ˆ‡††††ŠŠ‰‡‰ˆ‡‡ˆˆ‹‹‹‰‰‰‰ŠŠŒ‰Œ‹‹‹ŠŽŒŒŽ‹ŒŒ‰‡‰‹‘“€|pegm~’ÃÃÁÄÚãÏÀ©•„sh`ZUQOLJLOU^^aarwrbxœ†w‘‚€˜‰…—‘skhmoiabcdeecbccddddeedfgc`\]bdggfihilz‰¬É „ufb_^[^]]]]\]]][_]\[\[][\[\^aqw˜›˜˜˜…xqorlrqw†‘—yaV[SNMJNR\dq£¹ÏÙáɲÀ¿¿»Áͺª¦¥¤¨ÀßçëëêêéçåÜξ´°®¥¥œ–—š˜—“‘”‘”“š‘‘ŽŒŠ…€}wywvutsuuw}‚Œ”£®¸ÐÂdž€<0/148;?FMYeq€…“‰‡‡ƒ‹Ž£¹¼¾½Á«³ž—³ÅÐáãäÕɽ¦Ÿœœ™˜ ™™™™šœ˜™š™˜š™›˜˜™›—š˜—˜—–——›–•——–—™šš˜——˜”—–••™”•”““’’–“˜“”’ƒi;+#"#(06667877777866666666676767666689888::<:9999:::::;===<<<=>B??>>>?????@@??@@@@@A@@???@??@@@@@ABBBBBCBAAAADCBBCDEDECDCCCDDDCDCCCDCBCEDCCCDEEEEHFGFHFFDDEDEFEFFFFGGFFHFEFFEEFIF||}{{{}~~~~ƒ€€€€‚‚€€„ƒƒƒ„„„„†„„‚ƒ†……ˆ…†…†††‡†‡†‡‡‡ˆ‰‰‰Š‰‰ˆ‰Š‰ˆ‰ˆŽ‹Š‹‹‹‹ŠŒŽ‹Š‰‰‡‡ˆ‰…‚‚„†„†ŠŠƒ~~|“¾¿šªÅÎÖÎÄ—ƒsi`[WSRMJKMT`baejecx†x‚›‹w‹¤ƒyp{naeeeeeebbcdefeeedefdb[\adhiiihhknw–´ž˜}mga^^^]]^^^]]\_]\[\\][\\\]_gmrwz}{x‚†ŠŽ‚˜„|bW`^[WPKNQX`kv‰›°ÈÍÓÂÂÃÀ·›Ÿ¢¨”œ¾ßçëíëééèåÛÑÁ³©«§¥¡–—š–˜”•‘Ž‘‘Œ‹ˆ…„|zwxwtsrmoqvz…™¤²¼»º¡W4/.2456:<?DOXet€‘”—‰„~~Š•¦ÅµŸššœŸ¼¯Ææðîëáï§¡žššš™šš™š˜˜–––˜˜˜˜™™˜–›˜™š˜———˜–––————————––—’—˜–•””•—”’’’•”•““’†vG1$""&,26677777778575676766767665766688888898889:99:::9==<><<=?>=>@>>??????????@AA@A@@???@??@@AAABCCBBBBBBAAAAABCCBDCDBCDCCCCCBCECCBBACCCDGCDEEEEEEFFFDFDDDEEEEEEFGFFFFGFFGFDFGFF‚~}~~~~~ƒ~€‚‚ƒ‚„„ƒ…ƒ…ˆƒ„„‡…„„‡…Š„„…‡‡ˆ…ˆ†‰††‡††ŠˆŒˆ‹Š‹‰Š‰ˆ‰‰ŠˆŠ‰ŽŒŠ‹‹ŠŒŠ‡‡‡‡…‚€{tpmjjw„„ŽŽ‘›«œ—¬¼”©½ÂãÕǬ“‚sjb]WTPMKLLTZ\_cdeyŽ…y€ƒœ†›’‡‡}vededcccbccdfecedddee_Z^aeggghhijjjmtŽª—•ˆvmca^^]]\^]\[\]][[[[[\[[\\^]^cht†˜šž•‘Œ‡ƒ‚|jgie`[WQOONU[eq’§½ÄÎÖáÔ¹¥œ¤¦•»ßçêëëêèæäÙÌÁ·°¨¤ Ÿžž™–—œ˜˜—•‘ŽŒŒ“ˆ†‡ƒƒzyyyusrqonty‹–£®¿·µ›~V70/13457889=AGO]jw„ˆ“‘“‹†…„‰ž±›»Á¿À·±ÊâåçæäÕͼ¦ šœœœ™œžŸš˜™˜™›™™™œ™š™™—™˜—•–—˜˜›–˜˜˜–––——˜˜—–––••——•”™•—•˜’•ŠƒZ8&""$)259898:8778876666666788687777778:898::988889;:98=<;::<=====>>>>?@?@@@??>?ACCCBA???@@BBA@BBDDDBDBBBDBBAAABCCCDDDCFEEBEDDBBCCCBBBDCCEDCDFEEEEEGFEFEEEDFFFFFDGFGFFFIFEFGHHFEE~}~|~~~}~~€~~€‚€‚ƒ‚ƒ„ƒ‚ƒƒ„…„…„‡…„ƒ……„„ƒƒ†††††………‡‡‡‡‡‡ˆ‡ˆ‰Šˆˆ‡ˆŠ‰Š‰ˆ‰ˆ‹ŽŠŠ‡……ƒƒ…„ƒ€}vpkgedbaabek‚z‡±²±®¬š£½–‘ŸŸ ÁãËé“„ume^YURPNMMRY^ffdp’~yƒ~ƒ›Ž‚Š„ˆ’„zŽot~laccdheeddehhheeg^[_aeeehghkiiiknt‡˜«¶¢…xkd`^^^_^\\\]_\[[[\[[\\\\\^`fnzŒ†„ƒ‚urƒ…‡…………‚}vri]XROMRV`ky‡š½ÕÝâоËà˽ÂÞçêêêêéçãØÊʺ·°«¤¡šš—–•–——˜˜‘‘’”•’ދЋŒ†„ƒƒzyyyutttsvz‰•¢ª³°®•zV60.1456678889:;BHS_l}ƒ’𑉅– ©ÃÅÂĵµÇÌÕèïìèàî¦ žœœ—™˜˜˜™š™™™››šš™™˜™—˜–™™—•–—˜•———————–—•———–––––––••˜”•”““•ŠgA)#!#&0556667877776665686777777677788888889;998888;;98;;;;;<<==?====>@?>>=>>???@A?@????@@@BC@@BAABCCBABBDABAACBBBCCBBBBCDCBBCBBACCCDDCCCCCDDDEEEEFFEEEDBCCDEEEEDEEGEFFHEFHGFGDEF~}~…„‚‚€‚€‚ƒ„…ƒ‚ƒŠƒ‡„…„‰…‡„ƒƒ…„ƒ„‰…‡……††……†Š‰‰ˆ‰‡‹‡ˆ‰Œˆ‡‡ˆˆ‡‰ŒŒŽ‡Šˆ‡†ƒ„…€|tnifdaaa`^]]^``bjƒ{¤ÃÂıŒ˜¼šŸ£¤´ÇÏÊÁª•…xne`[XUSPONRV[dbiotz—~ƒžŒ…œ}˜Š• ‰‰Žˆrbcddeeeddfiefffh_[_befghhhiiihjkoxšÅÖä¹–„oha_]^_]]^]^\\\]\\[\\\[\]_bgoqrrpkcq‘œŒ–†Š†‡‡’ŽfXUVROQQYbp}Ž¡´ÊÖåäãããääçêêêêéèçäÙÓËž¹²¬§š—””‘”•–™”–••–—ŒŠ‰ˆˆ†„ƒ‚‚‚}zywvuxz~‚‰•¡«³±¯”vS7/-0357677988978;>AKTany‡Š—”“‹……‚„‡œÅÀ¿¸°³ÐȽÎÜãæåå×ѽ¦Ÿœ››š™™™™˜œ›››šššš™š˜˜˜——œ—˜––—››œ™š——˜š—™—™–›˜™––——•™––––’vJ.%!"%-4765567977655676666779876779888998888889888::99<;<;><<==<====<=>>>==>??>?A@C@@@>@@AB@@ABABBCBAABBCBBBBBBBBBFBCBBBFCDBCBDCCCECCCCCDCGDDDEFGFFEGEDDDCGEFEFDDEGEFFHHGHIGHFEE~}€€~ƒ‚€€€€€€ƒ„„ƒƒƒƒ„„„„„ƒ„ƒ„ƒƒƒ„„…„ˆ††…………‡‰ˆ‰‡‡†‹†‡‡‹‡†ŠŠŠ‡‡…‚€~{yrmieddb`_]][[Z[[]]__birbn”°ÈŵŒ¢žš°®¯°±½ÑÌ«˜Š{qhb^\XUSPMNS[afhpts|¢‹…‰|žw‘¡†€‹—uceedffhdefigfgjga^`cihhhhijjiijlp~¢ÔëîêÏ·›xmc``_]]^]^\\\\\][[]^]`_`__bedddcfx„¥’|zŠz‡Š–¢€lVW\]VPONV^it‚‘¤¹ÇÙßãâçêêêêééèçæåßÝ×ÒÌÇÀ¸±¥ œ›š˜–••”“‘Ž’–“‹ˆŠ………„‚‚~~{{{zzyw|„Š•¡¬¶²“wU61-/24577778:999999<=CJVamz†“”š‡‚}}‚•ž¢¢¥±Ñýº¹ÀÔêñîìäÁ¬£žœšš››š™™œ››˜ššššš™™š™˜˜˜š˜˜™™˜˜•–—˜—˜›š———™•˜—–•••—”–—––•““‚X5(!!#*35666667666556766666688787578899;8987888778:9:8889:;;;==;=<<<<<=<===>?A>>@@@@@AA@@AB?@AAAABBBBBBDCBBCBBBBCAAACBBACCBBBACCCBCBBBCCCCCDCCFGFFFEEEDCCCEDFEECEFEEFFGGGGGGFFEE…€€€€€ƒ€‚€€€ƒ†ƒ„ƒ…ƒ„„ƒƒ†ƒ„„†„†……„ˆ‡‡‡†……‡‰‡ŠŠŠ„…ˆŠŠˆ‡‡‹…‡†Š…ƒ„„„„‚|zqkgcaababb`_^]]^[YZZ[]^``bi€^Wu›´È±Ž’¥¡«½ÄÉ®´ÄÜÓÁ¯œŒsieb_[YTPMOSYbafntx¡‹‚•}ƒ “ƒ…¡vcedceeeddegggiif`]adgjjjiijjijkls…´çñöøù÷é¬sgd`_]]]]]]\\\\\\^^]]]]\\\\\\[]et’–‘}~†Š“Œ§—‹lZ^`_ZTSQOSW^fuƒ”§µÊÏÔßççççèçççæåäãâââÝØÍüµ°«©ª£žš—”‘‘‘ŽŽ‡Š†‡‡ˆƒ„~}{zz{{|~…Œ—¥®º´²•zU91.0145799:889:;:::;<;9>BMTbr|Š‹•ŒŽ‰††…Š’™¬Ô¿Â¼¶¾¾ÊÔßââáÒÁ³¢››œœ™œ›ššœ›œš›™˜˜œš™šš——š™™—˜˜˜™šš—™˜›š›™—––•—–—˜™••••‡i>,#!"(/567686768886777656668767877899888887788878:::9;889;;;;<;>==<<=>>==?>??>>@?A@@@@AAABBB@AAFCDCBCADCDCCCDDBCACCEBBBCCDDDCCCCCCCBBCCCCDDCDFEFEEEFEECBCFFFFEDFEDDEGFEGGGGFEEE‚‚€€€‚‚…„‚„„„ƒ„„ƒƒ…‚„……ƒ……†„……†ˆ†…†ˆˆˆˆ‡‡…†ˆ‰Š‰ˆ‡ˆ‡…†„ƒ„}|{slhc`^\YXVY[]_`_^\[[ZZ[[[\]^^^`i|ZTXu ¯±š“•¥§©ÀåÔ¢§¯ÂáÙʲž€wokgda[VRNNPW`ehggw…}„Ž£’z•ƒ™¢xeihgefffggfghijd`]ddfgijjiiijjkov’Ìëóöûÿþøè»”€mfa__^^\\\\\]]]]^]]]^]]\]\[[\bgv}}{††‰ˆ‰‡†‹quvw{gZZYTONOW_itƒ¸ÎÓáâäåæææææååääããßÞÙÔÌÆÀ»¸¶¯¨¢™–’““‘Ž‹ˆˆ†‡‡ˆ‚€~}}||}~€Œ—¤®·³±—yZ>:89::::9889:;<?@BCB@?=;9<?ELXcp‡Ž—ˆƒ~…~Ž®¯ÆÅ¼½·®¬²ÅßëçÜÒºª£Ÿšš˜™™ššœ™œ™™˜—–™˜™—››™–›™™–—˜˜—šœ›˜˜—›–›™˜––•——˜˜—–••“ŒxK3$""&+15555555666555555666877777779:9889887876778998:899:9::;;;;=;<<==>=@>?>>>>???@@@AA@@@B@AABBDECCCDCCCBCBBAAABCCBBCDCCCCCCEEEDDBADCCBDEEEDDFEFDEEDCCCDEEEEEEDDGFFFEGHGFGGED…„„€‚‚‚„‰„†ƒƒ„„‚†„ƒƒƒƒƒ„…„………„„…ˆ…†…†††‡‡‡‡ˆˆ‡‡ˆˆˆ‰‰‰‰ˆ‡‡€~~{tmhc`^]]]\ZWUSVSYZ]a^][[ZZZ[\^\\\]]_hz[JMY{¡§œƒ‘–¡Ä×Þ¤›£ÎâÚ̶¢’…}wrnic^YTOOOV_`bcoy~}‘‡‹›–ƒ’Ž€|““~jkkgfedeeeghiikd_]eegghhjjjjjjkq|ŸÙìõøûþÿÿúï̧Œrjb`_^\]^]\]\]\\\]_]\]\\]\\\\^`hkptw|ƒƒ†‡ˆˆ˜™™}o^ZYZTPNKQU]ep{‡“£³ÁÒÙäåæææåääääääãããââââàÝÔÉÁ¸¯¦ œš•‘ŒŠ‹ˆ†‡‰…}~„•§°¸µ—~\C?@EFGFDA><;<>@BEJPSUPJE@<9:;@DNWcox…ˆ’‘’‰†„‚Š’¡ÉÁºº¸µ´¬ª¶ÁÎßßÞÍ÷¥Ÿœ›œ› œœ™™¡›œšŸ›œœœ›ž™™™›™˜˜œœ›œœš™™š›š˜™™š—œ™—–œ—™†^:&""$(/354455555666545567687667788898898:8887788:898;9:9:9::<:::=:;<<=>>@?@@@>=>>?@@@@@@@@BAAABBEEECCCBCBBCBBAA@BBBBBCDDFDCBCDDDDDBADCCCEBBDDEGFFDHHHDDDDEFEEEDDEEFFGGHGFFHGED‚ƒƒ‚„ƒƒƒƒƒ…‰ƒ…ƒ„„„‚„ƒƒƒƒ††…„†…„„„……††‡………‡‹ˆˆ‰‰ˆ†ˆ‰†„„„ƒ‚|zwnhd`^]\[[YZYZXWRTWWX[\\\\Z[\\ZZZZZ[YXW[az`KCQ_}«¡Œ“˜™´Óä§«³¹ÇäÖλ§šˆ|tmh`ZUPOOT]aejmoo–š©‘|’Š~–‹~s‚Œzliheefghiije_^dfjjnjjiijkjlsƒ°çðöùûþþÿÿüôÚÁ }peb__`_`^^^^]^]_^]]]\[\`]`^`^`bdgiy‡–’‘…‡ƒ|uilnh\XSOJLNUZdks~œ¬¾ÊØÞåæáÝØÝäååäääåååääããáÝÔÊÁ¸°ª£œ–‘Ž‹‰†„€{}~ƒ…’¢²º¸±¤dFEGPXYZVSMGDACEHJOUZ`[WONGB>:::<>FMXbo}…‘’—އƒ}~‚•¨»»¹°¤¡¢´³´»ËãçÞÁ°¨¡ž››š™š™›››š›››™š˜˜™™˜™›š™™™ššœœ››šššš™—™œš˜œ›˜–™—–’‹oB)$!#&-444455555555744566667667789998888788877888888889:99:::9::::;<=====>======>>>>AA@@??AAAABBCBDDCCCCBBBBBCBAABBBBBDCDECCCDEDDDBBEDCCCBBDDEEEFDDDEEDDEEEDEDDDDDFFFFGFFEFFEF‚ƒƒ‚…‚‚„†„‡…‰……„…†ˆ‡‡‡†…„…ˆ…„†‡††…‡‡‡†‡†‰†‡Š‰……‡‰…ƒ…‡„}xslgca_^]\[[ZZZZ[[^YWVWXY[^^_\\\\\_YZZZZ]XVUW_{fLBKdpˆ¥Œ‡Ÿ±¦ªÀ࿺ÐݼÆÛÜÙÁ±¥š“Œ„yrha[UQPOUZ^dehj€™ª¥~“Š~˜‰|}£›“vjffgjhiijjg`^dhiijjihjkkjmv‘Æìòøúüþÿÿÿÿýû÷鲇wie``_a__^_`^]^^]]]\\\]\\\\\\]\^ar…¤ž”‡~|{{}€ƒŠw[YXYQKLINSW]go}‡™ª»ÔÙäãÝ×Ñ×ãäåäääååååååäääãâÜÒɾ·°§Ÿ˜’Œ„‚€}|}~…Œ’™£´ÆÁ¾«™qYST\djnomibZQMKKOTZbhgc^WTOIC>:98:;AFOXes|‰‹”’”‹ˆ‡„Š‘¶Öî§¢¡Ç¹É¸¯ÂÝÝÝÕϼ¥ ¡œŸœ›ŸŸžœ›œ›žš™™™šœš œœš›››š›››š›™™ž˜•”{J-%!"%+3355555755544555566666667889888:8978777888888887:99::9::::::;==>===>>>>=====>ABAA@>@@@ACCDDDBABCBBCDBBBDCEBAABCDDCCCCEDFDDDBBFEECCCCCDDFEGCDDDEHDEDGDEDDDEDHFGFGFFEFFFE‚ƒƒ‚ƒ‚ƒ„„„„ƒ……†„…………‡ƒˆˆ„„…‡„†‡‡†ƒ……‡†‡†ˆ…†Š†ƒ„…ƒ‚{wqlgda^\\\\Z\[[ZZ[[\[[[ZZYZ[[\^^]]]`]\[XWWWWUTTRV]jiLEMˆ{q{’š·º½¬¡½ÂÊèåǶ¿ÔÜÛ˼±¨ ˜‚wld[WTRPSX_hhgwŽŽœ‰•‰€™‹…–…„vjlmkhhiikea`cgijkjiikllmo|žØîôùûýÿÿÿÿÿÿÿÿùê¿™†qhb`a`_`a`^_`_^^a]]]]^_]]]\\\]^jz†™‡‡Š‹‘†€p^bda\TMJGJLRX_fq|‰–ª¼ÎäßÛÐÀÉÜÛÙÛàâååæççççæååäÞÝÖÎǺ²©œ•މƒ‚ƒŽ˜£¯¸ÅÂÁ¡gdbgkjxpywuuuf\WT]gr}}yokd[PIC?<98::>AHO[htˆ’˜ˆƒ}†³ÊÙ¬ª¤Ÿ£¬³·²¢²¼Ôäãâ¹¥¡žœ›››œ ›œœœ œ™™™šššš››œšœœžš›œ›™š™™™œšš˜˜˜˜˜’…Y3(!!#)12655554345534555566666667777789776777877899889999999999999:;==>===>>><><==>>?@???>@?@AA@ABBAAACBBCCBBBBABABDCCCGDCDCCCFDCCCCCBCCCCDEDBDEDCCCCCEDEDDDDDDDDDFFFFFEEEFGFD†‚ƒ‚…ƒ„ƒƒ…ˆ……†††††Š†ˆ†‹†„………„†ˆ‡†…‰…†‡Š‰‰„ƒ„†ƒ{xojgdb`_^^\[\\\\__]\\\\\\[[\^^^]^\_^d]^__ZZXUVWTSTTSU\pcKJPxuj|Ÿ¯Ûȳ˜´ÇÐàåÕ››¥Àâèæ×ÍÄ»°¢•‡zne^ZUTRUW_eckyz{˜†‚š‡Ž“†…Š{onmjhhhhkcaadlloljjklmmnq‚°éòöùûýþÿÿÿÿÿÿÿÿûñѱ”vlcbaaaa`````__`^^_^^^`^]]]]^^biqyƒžŸŒ‹„€~ztttrla[SQMHFGLQX_hp}Œž¯ÅßØÔ¼¾ÈÐÎØÜÝÜàåæçææåäääãââáßÚÓÊ¿³«£™‘ŽŒ‘™¦µ¼Îºº “}j_ZZ]_efgijnsq~orkk|’ž§š‰ƒsnYOIE@;:989:<CIS^k|Œ“‘‰‡±©¦¡”±¡¶Ú¾ª¶ÀËÙÒ̪¤ ŸŸŸŸž›œœœœ› œ›œœ›ž›Ÿš™šœœžž š›š™™Ÿšššš˜›™–Šm:,"!"&,245554444463444466676667786788899777667788777889::::;889:9=<==>===@>>=====?>@?>>??B@@@@@AAABDBCCBBCBBBABCCBCCBCCEDEDDCECCCECCCDCCDDDDDDEECDCDDDDGEFDDDDDFDDEGEFECEHFEE‚‚‚‚†ƒ„†††††„‡††„„„†……„…†‡‰„†ˆˆ‡…†…‡‡„€€€€upjda_]\\\\\]]]`]]]]^_]]]]]]]^^_]\^]][\]^_YVSQPNQSTRTUUUW\f^IJQl‹|c_x¨»Ò¯˜«ÙÏÑâÝ¡—§»Îéèâà×μ«œ‰{nga\YWTUX]eimmoy}…Šˆ…šƒƒ˜‘…ƒxŒ{llnjiebagjknmmmlmmlptŒÆêô÷ùûýþþÿÿÿÿÿÿÿÿüöâÖ¨sfdba`aa`____`^__`_^^^^__^]_abfiu~Œ£‘€…ƒ„…‡ŒŠŒi_YYYXPJGEILSYajw„”¥ºÎËû¹ÍÖØÚÜÝÝ×ÝáåææäãäååäääããáÝØÐÉ¿µ«£ ž¤°º©š‰wmdbZZUXVUVUUWX[_fonxz–§§¨›—vj`YTOHC><999:;?CKTany„‰“—˜Š††ˆ‹ˆ…Œ¶²µÙجµ´¢ Ÿœœž Ÿ››žœ››››šœœœ™›šš›œœ›››œž›œšššš™š™š›š˜˜™–ŽzF2#""%)0234534444443844556866777888888977786566777777889::;9899:9;<<<<<<<=>>==?====>>?=??B>?@@@@CBBBAAABAAABCCBBBBBCCBBCDECEDDDCBCDCCCCDEEDDFEGEBBBDEEDEDDDCBDEDDDCEEEDDDDEDD‚„…„†††„…††‡Š„‡‡‹‹Œ†ˆˆ‰‡††††…†Šˆ‡†‰ƒƒ|xslfb_]\\\`\\[\]^^^`__`^^^]]^]^\__^]\\^^^]\[ZWSOLHEGIUVVWWZZ\^waIGOi•o[X[zš¡§›§ÓÆÈÝÜ©žÄ¯ ¸äíîîëâÚųž‹|qjd`\ZWWV\ddffpwŽ…„š…œ–¤ ‰¤›‘vnknkhbahilmnnllnnnrz˜Õëõøûýþþÿÿÿÿÿÿÿÿÿÿþü÷æ¸zlgb``a`_____^___`^^^^^^^]_`___eo€‹‘‡‰Ž“–˜—–’ta[Z][[SMIEHJOT]fq|‹œ°ÆÆÒÕãäääääåßÜßâÛÒ×ääääããããããââáââÛÓ¾ÀžªŸ”‰€wha]]abc_[UPNLMLOQRU\`gzƒ£¸¨©“€ogfgc\QJD@<:9888<=DKWcmyšŒ‰ƒ„ˆš¬Ï¿µ³²ª¥°À®‘œž¢œ¢¡ ›£œžžžžž¢œœœœ›Ÿœ››œžœœœžœ š›œ›šš™†[8%"!"'/233555443443445745666866798788887777677779787::999999::99;;<<<;><>=><<=====>>?=@?C>??@@?@AAAABAAAABDBAAAABCCCBCECDCEDDDFBBCCCDEEEFCDDDEEBBCDEFCEEECCDEEDDGDFEGDDDEDDD„‡…„„ƒ…††ˆ†……ƒ‡††‡ˆ………†ˆ††††……†‡ƒƒ€}zwlfc`][[[[Z\[\Z\\]]^^^`^^^]_^][\]]\_`]]\[ZYXUSRNKIDGFIKNUVXXXYY[_saGDKgnhbVVYt™›•ªÍÃÊܲ¥ÎÇ›š¾ÜîðîéÝßË´ Œsnjhe`[XVZafjmn|–~ˆ„šŒˆ‘Ÿ††œ’ˆ€vkllgbbgimmnlllmnot€§æï÷úüþþÿÿÿÿÿÿÿÿþÿÿÿÿÿùëÄžˆpibac`__`_____^___^^`_^^__^^^adqy~ˆƒŠ‹†|wroljdddgidaYSLGIIMRYakw…“¦¹ÈãääØÊÐ×ßåßàâÑÌÉääãØØÙÝâããããâàÛÝÿ¤•‡{smhd\UWX]edf`YTHFAEKMOPRUWbk{š¢•Š}igdiqeXQKGDB?<8999:>BLU`nw„‹’šŠ„~~…Š‘ »Õ±°³ÀËʇ “˜œž žœœœžŸœžœžœœœœ››œœœ›œ šœœœœ›š™›“‹k@'#!"$-22233334344444444566677687778878879767767777789988999:999:;<;;<<<==<<<<====>>?<>=>>>??@@AB@@@ABAAABBBBBA@AACCADDBCCDCCCBABCBBDBEDCCDEEEECCDDEDCDEDDDDDDDDECCCEDDEECDDˆ†…„‹†‰†…„…………‡…††‰…„…†‡‡‰‹„„‚€~wpid`\\[[ZZZ[\\]]`_^_`]`_^\]^a^`__\\]]\^^]]\[ZVSPKGAACGJLNPRUWY[WWX[^qaEEId›€{kWW]z™œ®¹Ã¶Æà»¶ÕÆ›“¬çíïí騨æÎµ¡ƒyurnjd]ZV[_djipy€€—†‚’‘~{~…‰ ŽŠ‰zlmmecbfikkomllnoovЏìòøûüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÕ²”vlcca____________^^_b___`^]___agjr|ƒ‰‹ƒn_a`beimt{}yf]SWPKJHKMT[en|‹›°ÄÜÕÍÁ¶ÐÙåÛßáÔËÓäæçÞÑÏÑ×ÝááàÝÔɰŽwokecaZQNLQZ^ffjbXOFB?CDGKNPRTX^lsz„~rkkmotgYTMMOGB=:9899;=DJT]iv}‰”Œ’ˆ†…„…—¸³²±ÂËÞÇ®«§™’•šœžžŸŸŸœ žŸžž ŸŸ ¡¢ ›ž›¡¡ Ÿ œœ ››™–yI,$""$+/3355554334546666665676666688777877777767666777888899:9<9:;<<<<<<<=;<???=??>>?>>>>??>A@ABC@C@@@@AAAAAABCAAACBABDDECDCCCCCBBBBDCDDFDDEEFFDEEBCCDEDDCEDDDFEEDECDDEFFCDD…„„„…„†„…„…‡‡‡……††††††…„ƒ‚~{{{qlf`][ZYXV[ZZ[\\\Z]]]\]_]]][[\]_^^^^]]]]]Z[\YVUTOLLIGFIPQPPPRUVYZYYTUVY\nbC<Fb˜†•j^_c| ¦®¸´Íá°Åפ“¯çîîëåÐÊèåл§–†|vpia[WY^dkmpvw€‰Œ‡{~s~„‹ˆ†wlmlgdcfjmmonnmqpqz•Éíôùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷áѧ€rfc`__a`______^^^_^^_a__```aabdfop€—‹nopx|€†‰Œsj_X^^VNKGIKRXaiu ²ÂÀ½ÃËÛãââáÛÕØæéëèÛÌÈÁÂÆÃÁ®žŽ€wplhgec_[VRMLKQUZbcm_[PGEDDADEJQPWXY^cmwz}{wttuseZTPRQHA>;97889:=@HOZeozŽŠƒ}}Ž•±±ÂÊÉʼǩœ—•““š£ žžœ››ž Ÿžœœœ Ÿšœœœ œ Ÿžœœ›››œœœ’ƒV1&!"#(.2342434234455555566566656687768977886:6756687777778:98:9::;:;=<======<?>>?>=>>><=<>=>?@?@@A@@@@@AA@@AACCABCBAABCEBCCCCCCCCDCCBDEDDDEEGFDEEEDCDEECBCDDCFDDCDDDCDDDCDD„„††‡‡Š†ˆ………††„†‰ˆˆ‡†††‚€}ylida^[ZZYXXXY\Z^\]^``_]\]]_\\\\]]^_^^]]_]\]]Z[XUPLIFKKNOPQTWUTUYVVWXXXUUUW\m^@9A^˜‡ž‘of`_j‚™²ÈÆÕãɰ·ÈªŸ´éðíâÓ¿ÒèêäÑ¿®£›”އvkb]XZ[`gfkq|…‰Œž…wnt~”Žzvmnlgeehjmnnpolqpr¤Ùñöúýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþü÷縌xie```ab```__]^^__^aa__`__``__`chwˆ™…‹’™Ÿ•‰ˆ…{rgefb]VRMIJJNTZaktƒž¯µÍÐâåæçèâÛáçèéçå×ɯ¥˜†|sliggggedb]VVXYWQNRQSW\a]_QPKEDBDFGHIJMSUY_dlyy€~xojeb^RODC>;988888:<?CNT^hr„‹‡Œ…††…“µ±Ä¿½³¨¤šž´¥™˜—™žŸ¢ ž Ÿ¡ žž Ÿ¡Ÿ¤Ÿœ Ÿ¡ŸžŸ ŸžœŠf9)"!"&+246663433434555556965676668766:98766776556677777777888:99:;:;;==;;=<<=?===>?>>A=><A>>?@@B@C?@@AAA@@@AABAABDBAABCEBBBBCECDDDEFBCCCCDDDDFGHEDDCCCCBBDDDDFEDDDCDCDDDEED……………ˆ‡††„„…‡‡ˆ‰ˆˆ„~ytmhe\[ZYXWUWXYYZ[\[^\^__^^]][]^\ZZZ][^_]]][[Z\\XTROMIKHKMPSSUUVVWWWXVVVVVVVVTX\`V83=Us‡•t`af»ÁÎßçÕ³²Î¢œ´ëóíÞÐÓåçåáãÙź°ª¥›‘†xof`ZXX^jlkrz{|œ’ˆt…£ˆvtmmmhdfkkonnnpppps†²æó÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúì܇rjdbba```__^^__`^```a`_^a````bcmuz‚‘‰…„‚‡„„ƒ‚„xunhaYRKIHLPU[emv€Žœª»Êàåæèéèèèçåäá׬“ƒtmhfdedffgjgdb\ZY^g_ZXWRIOOSXVTUXTPLGGBBCBAFKPUZ]biu„…†zvmd]SLEC?<:98878:;<ABILU[clw†Š‹‹‡Š™£˜‰„Ž’›ž´²³²«–™™¢¡ ¢ ŸžžŸ žžžžŸŸŸžŸžžžžœœœžŸ›œ’uD/#"!$(133332543433445565555666556776888777766667777667777888::9899:;<=:9;;<==;=<==>>>==;=>>>??@@@@@@ABAABDBABABABAABBBBBBDCBCBBBAABBCDDBCCCCDDDDCCCBCCBBDDDFDCDECCCCDDDEED†††„„†…†ˆ„„†ˆ‰Š‡‡„‚usjd`^\[ZZYXXWXXYZ[__`]^^a_`^]]^]]]]ZZ[][]]\]]Z\WUUTPLKJNSSUVWWWY[WXWXWYUXVVUVVZZZ[gW32:N\w”†w`_e‡®Èæé㹯°œž³ÝïîàÝáêçÕ¸ÖèÞÔʽµ¨šŒshaYWV^edghmw’¦³”ˆ|˜™xqmomjdgjkonoooppqvÄéõùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÔ·›ymcbaaa``_^__`__``___^]_^`a`_`achpwzyx{„„‡ŒŽ¡žš…ŠŠŠ}i^]RNKGJKQW[biq~‰™©·ÆÓäççèéèäϲ›„wida`ddccjeefgdc_`^cejgf`[RONKKNTWZ[ZXSNJCB<==DINTUW[`jpuƒ|obZMHD@=:999878<DIKKMPQQW_hr{…„މŒ‘‡„‚…Œ µ¶ØÙÖ¶¨£š™žžŸŸ ¡£ž¡ž¢ŸŸž¢¡ Ÿ¢Ÿž£œœœŸœœœ”…X6%"!#&033333543333444595445666556788777766766677776667778788:88999:;;;::;;==<;==>>>=>?><=>?>??@@CABAAAABBCCCDBCBAAAACBBBBBCBEABBBBCCBCDCDCBCCDDDDBBBCCCBDDEDCCCCCCDCFDDEHD††††††††ˆ…†‡„‚€}ytkb^ZYYYXYXXXXXY[[[[^\[\\__^]]]][ZZZZZZZ[[ZZYYYXWURPMOOORSSTUVWX[YYYWWWWWWUUVUTVVVVWZfZ5,5MRf}—–™Ÿ©zbca}¦Íèå½µ´¤¡¯ÐßîîèèíéÏÊÏêììéçÞÒǵ¡‘‚vjaZXV[dhpnm€ ”–ˆƒˆ„ƒvmnnhdgllonqpqpqty–Öìöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ñ⮈xgdbaa``_cab``aa`__^]`^b`__```beilrsƒ—“‘Œ‘“‰‰Š•yb]\XRMHHILPUZ`frz‰”¡¯ÂØÛæèè⸒tmgcd_\XSW]_baadfca`_adcdfg\ZXUQLRW_g_\Za]ZQKF@AAIJOVUTW\_clw|€pjZOLD@<=====?JT\bdfb[TIQW_dn}ƒ‘‹Œ„~|{€„‡£ÀÏÔÒÌÎÅŠ’”ššœœžŸ ž¢Ÿž›œž¡ Ÿžžœœœœœ››—Œe>'"!"$+245435444453545444564666666777776666778787876777667787899::::;:;;;;<<;=<;<<=>>>===>>>??@@C@@@BCAAB@BCDBBBAABBBBBCBABBCBAABCDDBBCBDCBEDDDEDBCCCCCCDDCBCCDBCCCCFCDEDD†‡‹†††‡‰‰…ˆ„urjc_\ZYXWXXY[Y\YYZ\\\\\]\[]]``_[\\^[\ZZYYZ^ZZ[[ZZWVTQPMMPQSSVV[ZZYYYYZ\WXWYWWUWVVVWWWWW[e^7-0@NWk‘ª¦±¡†sp_`| ÇÞ··²¨«®µÈâîìêìèÑÈÕèðòòòðìá×¼¦”„vib\XU\bfjhq„‡€„”Š¡‘{lonfeglmmnpoprru¡äï÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõ隃icaabaaaaa`aba`__^]^^`____`_^]^agn˜ª‡Š…‚ލ›–iZ\`[WPJJGIKOSY`fmyƒ‘ž¯ÄÒåçß·leb_^^XQNLRYYY^edcbaaace]UX`Zd\[\[`hg`XVY]ZdXTLHHHJKJIMPSZZ\`eluhpc_SGDDEFGGM`iorru€o_TP\YYZbmu}‚‡x‚€ƒ‚ˆš¢¸×ÊÚµ¤ ›žŸ›™˜š›œœ Ÿ§¡Ÿ£¢£žŸŸŸž¡ žžžžŸ›¡™“wF+#!"#)0346456545433445445777656666678868667976677777766678878;;;:;:>:;<<;;::;=<<=====<<=>?>?@BACA@AA@?ABBBCDCBACCCDCCCBAABCDBABCCDCCCCCDCBDFDCEDCCDDCBCDCBBCCDBDDEDEFFEFE„„†††‡…ƒ€yrkc_\ZXWUVWXWYZZXYYZ\]^]]]^\[[[[ZZY\]\[ZZZYYZZYXWWTTTSQSVRORRSTVWZYYXYYY[[WYXWUVVWWXWWWWY[]ga;..1<I]…’©¦™z_[]u’£³º¼¹¸· ±ÓæèæâÞÔÚæêîòóõôñïåÚ«—ƒwld\YV[`fkpspqŽŠŽ—™|lmngehlnropoorrw†¯èòøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷íβ“qiccbbaaa`aaa```__abdegikgcba__di{‡…„‰†~{ƒ‹ŒŽ„qonmjb[TOJEFGLOUZ`fpx„ŽŸ¬¿×Ô½…nhb`[]UUONMNOS]_eedcccceYSMT[[[Ydciij`RPNQ[^bUXQPNLLDCBHLPVVVXX\dfsghZUQNLIJVc‰ƒ~~€qcaa\TPNT\elrx}{yxz}ŒŸºÒÀ®²Àʲ–’”––—˜™š›œžžžœŸŸœž œžžŸŸžžœš›•…Q0&!!"&-23545344454344554456555567677665556566657777766767667778999:99<<<:;;;9<==>=<===<>>>=@@@@ACA@@?@@ABAABCAABBAABACBABBCCBBCCEDBCBBCCBBCDBCDDBCCDDCCCCCCDCCBDDDEEEEEDDƒ†…ƒ€}voh`^\ZXXVUUVVWYY[[\YXY[\__^\\\_\[[\ZZZ\]\\[XWWYZ[XWWXSQTSSVUUVWVUVYWYYZY[YYZZXYXVVVWZXWW[YXYZ]id>2-+/=Jat–«˜™ ¥fNQXkz”¼º¼ÍÜŸ¡ËàÜÑÓÚØéïñòôôõôóòðîèÈ®˜†ynf`]\`afjkgjv‰¤¤®—€mmnhgilnppppqtsxÀìôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõðà§}nfcaaaa`aaa``_`bdgjmtywob_^__`bhpw„‚€xhxz‹…‡…ˆ’”rf[]WTMGFGILOSY_emwŽ·Ó¼}oha_\]`]ZVROMTVX[^aafed[RNNTWXY^efgj_WNIFLOSWWVSVQMICBBGHNRQPPQSW\cconn`UPFNao‹tos€„srkfSIFIT[ekrqptopw}€ƒ˜Ÿ§°·µØÅÔº°ª•‘‘“™™šœ£žžžŸ œŸžŸžŸŸ¢¢¢žœ¡š™‰_8("!!$)23856344554464554445556666658665556666666777976566678788=:;;:;<<<<<;<;;<==?=>>?<B??>@@>>?CA@@@AA@AAAAAABCAAABACBABDCCABBDDDCCBECCBBBEBEDDDDDFDDCDDEDDDDCCCEFFEEEED‚‚€}woi`\YXWVUUVVTVWXYYZZZZYZZZZ\[[ZZZ[\[\[[[[\\\\YWWVYWVUUSSQSUTUWXUWXXWWYWXYYWWWWVWWXXXYYXXYWWXXYY[^hgA50+,.>Pf–˜˜¦ªqNGRO]h‚š·åÕ ¤ÅÌØÄ·ÈÛìòõöööõôóóòñìæÉ°›ˆ|qkfdbcfiokkswˆ§–{nnnhgjlopqqqrtt}™Óïöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûòâ¶}hebbbbdccabhnsy~„‹‘odcadca`cgpt}‚}ƒŽ“”˜—–ŒŽui\ca^WPJGFGGLOTX^eow…•©ºž‚ungeccdcdZTRQNIOTX[aie_\YVSPINUZadkcbUIHHIJPRRQTZSVQKKJMNNNLJKPPQSV\aepb[SGOPVYZ_j}xukjZQEALU]dn~smljptzzxx{€‚‡ ¾ÇÊÐÖÕ˘„“‹“’”———™šœŸŸžŸŸžœžžŸžž›ššš›‹kC.#!!#&02344344544444445546555456656655556666787776666666777789:9:;:<;;;;;;;;;;<<<<=<====?>?@?>?CB??>>>@@AAAAACBAABAABBBBBABABBCCCCCABCCBBBCBDCCCDDCCDBCCDCCBBBCCCDFEEEEDysg`][ZXVVWWVWVWW[[[Y^[[[ZXXZZZ[Z[ZZY]ZYZZ[\ZZYYYXXYZZUTSTRRSTVVW[WVWZYYYZXXXXXXWWWWVUVWYZYYXXYZZZ[]^fhD73.++2FT_l›¢¯lQRVSPS\|°É΢¥ÊÃÅ·²¸ÚíòõõõóññññòñïëæÊ±‹~uqnljjimkknowŒ’vopoijlnpqqqqsuu¦çó÷ûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöìÊ¥‰leabaccbbcp™Ÿ‰—‘Œ„xqiijc`^`bhry…Š– ¨¦¤š™“‰{qoohbUYPLJFFEHKOSY_hn|Š™§ ¥˜Šojgfdc`ZXSIGGIJRZ_jjldaYPKGMPU\af``VPRUQPTGLTTSTY[[[ZWTOFDCFMNOPSW[`jer[TPFIBIPVYXZULG?DP[djqvvmghqn{wssuy}ƒŒ’—±ÙÕØ·žžžœ“ŒŽ•””•™™™›Ÿž£ŸžžžŸŸžž žŸ›œƒQ3%!!"%023343745444446575555574456555657556788797755566788777:9;99:::9:<;:;=;=;<<<<<<>===>=>?B>@AB@B@??@@AACBBBABCBEDDBCCCCCCDBBCDDCAACEBCDDCDDDDFDEDDCCCDDCCBCCCDDFGGEFDja]ZYWWUVUVWWWWUWXYYYYYXYYYXYZZY[[[[[YYXYZ[XYWXZZZZ]YWVTSSUSUVWXWWWWWVWWXWWWXWWWWXWVVVVVXXYYYYYYYYZ\\]dhE952.+/6FS[bœŠaO…u\QLPV¿«Ž¡ÏÎм™®ØìñôôõñëíïðïìíîìèÍ´ ކ€}yxxwurmoosvqtsopqmmotrqqrqsvw†²êôøûÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûñÕÀ™ukeddcdbboˆŽ†‰…‰ƒ„…††sfb_bbjƒ—“™¦œŸ‘ŒŒŽŽŽrn^W][WQLFDDGHMQW\bitŽž¢³¦€vnjfc_\[PHIJKKRV^cfhedWOONNNSTUX]e[\[ZWUPKLLMQUa[ea`ZSNFC>DIJLNPQRW\bmadWOJCFIKBHJKKKMYediknromkmlh```fipuy~‡™²Æ¸«ª¼ÍÛ¶—’…Œ‹““”•–™›œœšžŸ šž žœœš™”Š`;&" "#+13333335542354444555664565555555555667676655567657777788899:<::;;:;;;;;<<<<<<?====<==>>?????>>@@?@AA@@AAABABBBABBAADCBBBABBBABCCCBDCBBBDDDCDDDBDCCDCDDDDCDCDDFEED[ZYXWWWVVUUWWXXY[Z\\\ZYY[Z[ZZYZY\[[Z\ZYYYZ\ZZVVX\]\[ZVVSTTUW\[[XXXXY[XXYYWWWWXYWWWWWWVUW[WZZZYYYYXXY[\biF;741.,/<MVYjˆfMx•s[MHLY|‘ˆžÏâãä ¤ÒìòõôóòïëêíëÚÔàìîè禛”‹‰‰‰…~xqnmkinpooooqrrrssrqsvx‹¿éõùûþÿÿÿþþýýýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷óçƒsedcbacpœ˜Š‰Œ’–˜‰€na_ccm{“›žŸš–‡„†Œ’œª©¥Œ{maaed_XXOKHDEGKOSW\bmw„‘Ÿ±¯½¦›‹xoha^ZWVTQMMNVZcdefaa\XY[URRHQ]]\[abb`]YVMGJMT\fihg_\ODA>CBEJKMMPSV]cbqcc][XQJFO]adffgeceovx{vomc^]]`ekosv~‰‘˜Ÿ¦«³×ÐÎżªƒ|€“–––˜›šŸ›Ÿž£ŸžžŸž œž—‘oC*#!!"'/443243444344333455456445667665556666867666555665999777789:;::;<;:;;;@;>==<<<>==<<<??>>>??@?>>?@AAAA@BCDEDCCBBAABDBDBBBDA@BDABCBCBBDCDBCCEDDDDDDDCCCCBCDDDEFFFEEDXWWWWWVVWWWXY[\]Z[[Z\[ZZZZYYZ[ZZZZZYZZZZXXWTTTUVXXXYZVVTUUWXYY\YY\YY[Z[[YXYYZYXWXXXXXXWWZXYYYXYYXWWWY[ajF<9853/,5CPY]m~}MmŒsOGEMVk† Íêîë¦Ííóöôòóï×ÞåÚ½¿ÇÕêíéÓ¾²§¢žŸŸžœ’ˆ~uronopoppqux{xvvuuux{”Ñì÷úüýþÿÿÿýúúûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ井kgccdkqˆ‡Š†‘ˆŠŒŽ’‹…zvqolppuzz{yxxx}€”œ¨ž˜Œ~{rvy|‚f\[YTNIBDEILPTY_ho|‡–¦°¿½ºŸ€keaagda[WZ^_aZUVY\fgicc[SPNOOSUVW\b_[WPLHMP\feghha\QIHGDAFFHKMPPRTW^ib|„Šwf`]_mrypoedhqwyyƒw{tlnlkkf^^aiqv|€‡™³º¿ÌàÅÐ|xsvzƒ‹’”•–˜™š›œœœŸŸš–„N/&!""%-443223344333375556446445566665666677657655666555677788899999:;;<;;;:;;<<<<<<==<;;;=??>>????>>???@@A@AABCCBBAAABBBABABCBABBBBBBBCBABBCCCCCDDEDDDDEFCBBBCDDEEEEEEEWXYWVVVXZ[Z[[\\\[\]]\\[[[YXZZ[\[\[[[[WVVWUUSSTVWYXXXZXVVXXXYZZ\[ZZZZa[[[ZYZYXXXXYYYY_XZY[Z\Y[WWWXXXXY[`lF;99:53-.9KUUa}VK~´ž•ogUJNUtÁåîêÂ×ñôöôòòêÄ·Ãʤ’©µÔìðëÙÊ¿¹¹º¾Ã»¯ …xrmnnoprv{~~}|{ywvzžáï÷ûüþþÿÿÿõêíîíêóüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷îΩogccdfkt{€Œ‡…Œ—ž–‡‰†|okdcddccgov†”¡¨¦œ’…‚…‘†‘€q][[^XSKGFEFIMPV\bit~Žœ¬ÃÀÐÁ³™tkiiiifddkdQNMPSZacfbj\VSQPOOORZ\g]^XTQOW]ab\Y]g\_]ZXSMHG@HKIJOORT[_k‚•¤‡yjdpwxqfd_mn|xwuwrznpd]OJNYcms|ˆŽ“ªÐÈç‹€zwvvvy~Œ”–˜˜™šŸžŸœ¢Ÿ š›‰\5("""$)3334633455433455556464456666756645666576556665865687997889<:<;;::;==<<<<<<<<?=<<<<=???=>?>>>@?@@BBBBABDBBBBAABCCCBBBBCBBCCGCCBBCDDCCCCCCCDDDCCCCECCCBBCDDEEEFFFEWWWWWYYYYYZ[\]\\\]]][ZZY[ZZ][ZZYZZXVUTTSTSSRSTVWXXXXY[XWWWWZ\\\[Z[ZZYYZ\YXXXXUXVYXYYYXZYYYYXXVWWXXXXY\_lG<::96420/;NRQK6G‘¡§—ƒ{eSKN[{¦Îìí×åíôööõóò楡¿¯’–©»ÉëññìâßÜÙÚÞÆÎüª™‰{topqst}ƒ…†„‚|zw{ƒªêòøûýþÿÿÿýé¶½¿ÀÓöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüó×Ãxoeddgimppq€Œ–Ÿ“‹‡„…Ž–€me`ddddq}‰œ ª¤¥”‹€…}ytqod[]a__ZYQKGCDFJMSX^dny†“¤´ÃÜÙÖ´œ‰upljggfigZRQOSSRRbbaaZZ[]YSPMQRW]^`aacdccbRSTW^]gbma^XRMINCJNPRSUWZo›”~stuvveWRNTZ\_[Zblgba`]XNFDDINU\epuzƒ™œ”‚}|{zvysrr{{ƒ‹Ž’”•—›› ž›Ÿš—Œg=-#"!$'.135443355333344545355566566656555555566656455666587899888999::::9:;;;<<<;<<<<<<=>>???>>>>>?@>@@AAACBAAABBBCBBCABBBDCCCBCACCCBCCCCCCCBBBCDDCDFDDDBCCCCCDDEEEFFGBWWXYZY]\\[\[_]^[\\[Z[Z[[^\Z[[\]YYWUUSSSTTTSSUVYX]XWWX[ZXYVVY_]aZZZZXXYYXXXXYXXXWYYYXXXZXWWXX[VWYZYZYY[_nH<::99873.0?QOD>Euœ›šz^JDSe…¥ÎçÖÛðöööõôóã †˜ª”š¶ÉÝïòóòñðïíéÙÉÇÒÍŲž~xsru|‡‘Ž‹…}{‹µìóùüýÿÿÿÿûæ“’¾‘mËôûþþýþÿÿÿÿÿÿÿÿÿýûúøüÿÿÿÿÿÿÿÿÿÿÿÿþû÷òä°‡vfdbbbaafw‹¨Œ…†’¥§¤sa_a`bdz€›°¬©—Žˆ‚|tpkhda^]^___]\]TNJDDDHKPTY_itŒ°ÄÛÖÜ̸›‰qldejg``aZVTRV]_ea]\bai]TQOQMSUWc_lkkfaZSRMSU[bgleka]VPMMRSSSTVV^…Äœƒv}wwq_WLGDBBBBCDEFFEFFDA??>>ADIQ]ir{…‰‡„~€}…~€|{{{ywst{Ž‘”•›™› žŸ ¡‚J1%"!$&/034445553345444545396566577677678856555566476678889899888999::;=;::=<==<;;<==??>>>>====>>>?@?@@AAABBABBAABCCCBBAABCEDEDDBBCCBDDDCDCDDECCCGDFFFDDCDDDDGDEDEEEEFDY[ZZZXYY\Z[[\]\ZZ\ZYZYZZ[\YZYYWSRPQQRQSUWYVUXYYYZXXZXZXWWUWXXXYVXYXXXXXYX[Z[XXXWXVYVXXWVWVWWXWXZZXVTTV\nG=<<?A@=7112<MHDIh‹¨œŽ•xFBN_i ÂÔåñ÷öõööô䢄†ŠœÍÞîòòòóôóòñëÕÊÁÓàÑÒ»¤“†|ƒ”¡¤§Ÿ—‹„‡“Åíõùüþÿÿÿÿý꫈ÁŒhÃôúüøùýÿÿÿÿÿÿÿÿÿûñèéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñå½™ƒkgcbbbdqƒŠ“„€†‹Žš™œwsnnlgdp{ƒ’‰†€{tnkhhhjeggknnlhec`_^XVMHEEGJMPW^go{ˆ˜©¼ÔÝáÕÀ® tkiga`dfca][[[TMRY^`cfc_ZSRRVMU]`hjlji`YVTVKOTZ^bjcf`\[XWUUTUT]x¢£yqrpljccXSLB@<;:98889::;<;;:<@@AGLWar{}€€~~}||{y}€|„ogefrvŽ’“”˜™šœš”ŠZ9'" "$+/354445433346443333666565667765666565555444767778767;:9889:989;:;::;;;;;;;;>=>==>>===>>?@??@?ABAAAABABBA@CBBBBCAAAABCCBDDCCBBCEDCDBCCDDCCDDDCDCCCCDDDDDDEEEEDGDZZ]]\Z\\^[[[\ZYYYZYZ\\[[\[YWUTROMOOQRRSWYZZX_XWXYYYXWWWWXYYY[W[XYXXXXWWXYY[ZXWXXZWYWXXWVXWWWYX[ZZWUTUTZpG==AIT`J<62/1=JNP]u„š–—™žE?X`gl™¹áíõõôõöô娃||…žËèðòðïòõóñðêÙÐÛèçÒØßÀªœ’Ž™ª¯³²®¦›”Œ‡‹ŸÔðöúüþÿÿÿÿÿôÉÇ͘iÓöøóæÔê÷øúûüþÿÿÿó¤›ÃïùÿÿÿÿÿÿÿÿÿÿÿÿÿýûöïÒ°qhdbacirzƒ‚‘‘‘—••Љ‘wgaflqsrokgdb`_cfhffm{‚vvnf_^^^ZVQLHDFGJOU[civŸ´ÎÛâ×ÕÓѵˆtlcfjlqom`XXKJJMPT_`j`o`_ZWQNUV`jknlrfc]WOIRPUY]`dgca_\[WVWZ`y¨kc_]\`ddcV]LH?:765956677:889::<<=@GU|ˆ”…„‚~€}‚~yˆyrnn`\_eegrz‚–––—™š›•”l@*#!!$)/3334444443444554446667767666765565856666677666666687789889::9::;9:<<<;;;;<>=@==>?>@=????@AAAAAABDCDABBDBCBBBBAAADBABCCDCEEHBCEFDDDCCDDDDDCDDFCBCCDFDDDDEEEFEGEZZ\[ZY[[ZYYWXXYZZZZZZZZ[YXUSRQPOOPRSTVX[[\YXXWWWYYXWXUVVYYZZYWWWWWVUXVWXXXXXXXXWWVWVYWWWYYXWXWWVUTSRST[qH<>ETecVB95110=Z\Zac…¹¨»˜KX‡vhjhu†´æóôóõöô範ksÈèðïäëðôñïðîéíïîåÐÛÙ×Å´¬§«±¿ÉÉÇÀ¸¢™˜¬âò÷úýþÿÿÿÿÿûòì×¥qàøõé‹„¡èíííõüÿÿýç’H\{¾òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûóÛÌ¡|rgeeghqw}~‚†‘–”•’ŠŽˆ}shllnnifedecbafsoqry‰™}sy…o`aa``]\UOJDFFJNSZ_gr}‹˜ÁÍØÖããßɲ’~upppommb]VSLKKLHOUZ_`b]e]WVUZ^ihijkllmaXVTTPQS\effeeeecciqx€[HHIIILQRSQNGB:6655556757884689:;<CRn™•–‰‚{vsolgedfe^QRSSUZ\aeimx}„Ž‘••–‘€K-&!!#'/43345554444453544465776666666865556556556666667766877887788799:;999:;:;=<<<;<<=>?>>>>@??@AA@@AABBAAAAAAACEBBBBAABBBBBACCDDCCCEDCECCBDDEEDCDFECCCDFFCEFDEEEEDEFZ[\[ZY[YXYZZZZ\[]Z\\ZXWVVUTQPQQRSSUWYZ[[[WWXYX[WVVVWWWVWZXWWWWWWWVUWXV[XXXXXXXVVWWXXXWWX\ZYUTVUTSRRRSV\rI=@O`jp^H=631.3EZ[lfo‰˜¡‘ch”¬mg_i~±àêñôöõ꿉kj{™¿ÞïìÙÇßîêçóõõõõîÑ·ÍçæãÏÃÄÈÔáæëæßϾ²§Ÿ¡ºéôøûýþÿÿÿÿÿýúøë·xäúòØtVƒÒγÀÚòþÿûãuSC`œíÿþüûùüÿÿÿÿÿÿÿÿÿÿþû÷òâ´‹xecdchtwzz€”–žŽŒ‰‰‹Œˆƒ}tkha`aaaa`ahs‰~{ƒ§—€†…ia__^^]^XSNHFEHLPT[cmwƒ’£³ÄÜâçæãϯ˜•‹‚|qnndcdeYTRQHGNPUY]^affdb_aggfggimmoda^[VPVZeefgikqyˆ˜›š‡raHB?>>>???@AA@>:765555576678225789;>HSet€‘|xpg[RPTSTTTC=;@QY\]`behmrx†•’ƒU2'"!"$,44346585444565444555677556655665555586856666777667878888::9888:99989;;;;<;;;<;?>???>@?>????@@@BBBAAA@ACBCBBBCBAAACDBBCBCCDCDDEDDDCBBEDCEFDFEDCCDFEFEHEEEEEFEED[\\[YXYYZ[ZZZZ[[YXYXWVUTTSRRRRTVWXZ\\\ZVWVVVVVVVVUWVWXVXWUVVWUXWWWWWWVZWWUVVWWWVWXXWWYXWXVUSTVTSRRRSTW\rJ>E[dmqoTB:63134BT`klj~¨–_m©—Žl^S\k›ßîòööðç¤oiu”¸áíëÓÂÍééìôùúú÷쾺Ãèììéåàçéëîïîíê×Ǻ®´ÇìõøûþÿÿÿÿÿÿÿÿýñÇzèûðÂhaešÂ…Y™çýþýè¥}iTèÿüôìîùÿÿÿÿÿÿÿÿÿÿÿÿÿüôçÀž„kgdixz†sq}ˆ„…ƒ{’ ”ƒ„|sg^bca```dmu}x…Œ„s€{morolg`^^\ZQJFEGJNSX^go|Š™¨ºÐØåæâĨ¥¢œ”…xsijlhcb_VKKMPQURT\^`bhgdfffdgijimlkkdadfffhkqx…•£»Åܹ–‚cXGC@>;<;<8:999866755557568821489=>EHKOZfq|sleMD<>@>=92,(8MY]^^_`bcehms|…„†`:.%$"$)0344555556555543555578657765655555667666677876666688878889888898999::9<;;<;;;;>===>=???A?>@??@AAAABDAAAAAAAABBBCCCCCCBCCDEEDDGEDCCCCCDDEDCEDDCCEEEEEDDDDDEFEED]\[YXZZ[\[_[[ZZXWWXVTTTSTUTTTUVY\]a\[YXVXWWWVWWUUVWWZUTUWVVVVWWWWVYWYWZVVUXWYYYXXZ[WWWXWVTSSTTTTVVUUUW[rK?Jalmwq\G=964204CUbmik˜aq‡‚™ÇjWISb•ÌãóôôîÁzjr¼çìçʰÁçïôöøøøô䮟ÈëññðïïïïïðòôöôíÝÏÅÃÖíõùûþÿÿÿÿÿÿÿÿþóÌ}àùï°\d`’¼ff…ëýþÿôÙÜÌeoçþøè·¬ÙúýÿÿÿÿÿÿÿÿÿÿÿþüøñÖµ“rikt~qopsy„‚rx—§¬…œƒk_b`_aa``eimqu~Љ|zy{€†‰|i^^^`][TQKFEFKOTZais}Œ›¾Ñåá×ÏÅ¿º¹¹—†{spocb\^TPRSUXSHMRV_bfeiefghhklmkiijihghlr…’¦½ÇÏÌÝÞà¶—zfPHA=<;<987777776565566687315=;EAUJKNU_mplkeF:31.,,+(#$/MY^b___aaadihilpnkD3*'&%(0375556766655555456576667766756666677688878766679788:88787788999;::::;<;;<?;<;?<==?>>?A?>>?>>@A@CCBBAA@AAAAABCABEBCCDBFFEDEDDFGCCDECDDGDDDEEFDDDDEGEDDEDDDDDFD]\\YZ[[\\[ZXXWVTTTUTTSSTVWWWXX[]]_][YXVUUVVTUUUTVVVVVSTUWUUTVVVWWWYVVVVVUUVWXXXXX[YWWUURWUTRTUUUUUUUWX[sM@LffiwseOC>985246AO]pokd^m˜Ÿµ‘dMKS[ŠÖéóô칃rpˆÁ×åÑÁ±´çóùøöõòèÁ’›ÑíóôóòòòðäÝïõùøóîâ×ÜâïöùüþþþÿÿÿÿÿÿþõÔ•Òôí²PTg¤Æ„xªïýþÿûõðßulçüóÜls®ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿýõÞÑ£|torwkehiu…‘}}š² y|ŠŒugklifdbacegiiw‡…Œz|}‡•’l\cbb_][XSMGFFINRV\blv„‘¢²ÅÚÙæåáââʱ¦›Œted^^`][^`]VLFLMQUZ_bfebefjklkknjlor{†˜°ÂÚßàÙÑßåäâàݳ”x\PFB=<:9767777665779:::855<=>BEB@=<HSZ`_^M:0''&)))$$$4M\_a``_a]__``accb]NB62,**/4755655645555554445556886645566676554767666666776778888968899999899:<;;;:;;<:<<==>>>??>>>@>>?@@@@BBAAAAABAABBBBBACCDBBCDDDDDCCCDEDCCDDCDDEEFDDFEEEEDDDDEEDCDD__`]]^]]\XWUTUVSSSSTTVYXX[__^\a]\\\ZXWUUWWWUVUWUZWUVVVVUXVUVVWWVVWYVVVVUUVWYYXYYXWWWYWVVWVXWWUUVWWZZZY\rNEN_fmyws]I@=985324?Ndyqpag £¬®È‚WOKP^“Öáîè¶‘wx¤»¶¬§Ìíõù÷óïçÅ©šÐêðñòóóòðÚÖêòõõõôñèæêñ÷úüþþÿÿÿÿÿÿÿþõÞu¨åß²W]¥ÝâxÆíþÿÿýûñÚflæýòÁX@ˆÞíïñôúÿÿÿÿÿÿÿÿÿÿÿûøòã¶{jecbcdkv‡‹‹…¡Ÿ™yp‚ˆ…‡‡{slc`ababaclx‰‹‡„‡ŠŒ“˜‘ukigc`^]_YUPIHDGIMQV^fn{‡—§»ÐÚæçèåßÅ»·³š‰wkhgfeddegZQNLKILPVYeeefgilmlkkmx€‘¦¸ÉÌßäååååäæçæåãßǯ”wfMD><:89877689:;:@@A;;974349>?811=DGFC@90'!!(.*)**-9R]`aaa`b___^^^`ba`VUIB91.04677645556665644434756776865556666656766676668777887889978899:999999:<<;::;;;<=?>??=>>>??@>>?A@@ACAAACBCBCBDBDBAACCDCBBCDEDEDEDFEDDDDEDDDDEFEEEEEEDDDDEFEEDDD__^\]]YUUSRQRSSQQRTUWZZXY^^]\[[\ZXXYWWUUUUUTTTUUUTSSSQUTWVVVWXXWVVYWVWYYXXY[[[YXWWWWXXWVVVWWWWWYXXWWWY]rOAJ[csz|zoRC=;:;7455AScomjn¤¨ºªÍš|]RLU`‹ÈÜæËž‘”š¨®³¹Êéôøùöîdz¡¨«¾ÑÞàìñôóòðßÊ×çéêïôõòðñôøûýþÿÿÿÿÿÿÿÿþ÷Þr{¥Î£`uÖèâªtëÿÿÿÿøâ˜Vyæýð°L?^²Ð°©ÉôÿÿÿÿÿÿÿÿÿÿÿÿÿüôèÁ„mhddehkv‚„Š…‡š‘ƒ‡~tgfbbbabho{‰‚‡ˆ‹Œ‹~wyzsoic`_^]TNIFGGJLSY`gs~ŽŸ±ÂÕæééåÏÎÙʯ‘‘‚|vsnjijf^XUQMKBTSZefgfjimprv{…’¡´ÂÛÎÏäææçèèçèéèçæåãá׳˜xZOB?:;;;<=>9::8879<:6432-02.*&#).///.-)&%0/,('-5BW^aa_______`\\\[[[XVQNJA<;;::9765665544445555555565676555666667777677777788997888878988999::::::;;=;;;;;<===>?>>>>>???@@??AAAACCAAABAA@@@AAABBBBABBDEDCDDDDDDDEDCDDDFGFEEEEEDDDEGFEDFEDb_^YYWUTTRPQRSRSSTUWY\^ZZ\\\\[]ZZXVVUUVUUUUUVTUTSRRSTTTUWVWWXYYZZYYW\\^]\Z]YYYYY[YZXXYZZZYYXXXYYXXWVV[_uP@DP`jzzˆz[E=<<::7736DRc{zx’±©¥¶Î¨¯u\UPWc¼Ðçëç´ÆÝîò÷÷øò浕£´ÍççâãíôøöõóêÉÂÄÄÍàîîòóôõøûýÿÿÿÿÿÿÿÿÿþùãs_a„™ƒ²ÛÑǃ{áûýÿúè¦ccëþïžE0I”u`p—íÿÿÿÿÿÿÿÿÿÿÿÿÿþüøñÓ¬Žrjeddegmpu~ˆ“˜™˜—“Љš˜sebea`adir~‡‡‘Š›”“‰„„„…„se`]^]]WVOIHFHKPT\dmx‡—ªÂÖåèèßÜáß¹–”–œ¢–‹qnm`\ZYSOQWY\dfffgis|‹š©½ÂÄÃÒÝËÏâååçèééééçâååæçäݸŒiRD>@AB@?;70*()07DB@<<=/*% "#$'('%#)36+')/7EW`ab`c_``_^a\\\][\\\WYSRRPGB?<:8766554464555654456545756556666778:7889998:8:8789998788889::99<<;<;<=<;<;;<===>>>>=>?>@?>??@@AABBA?@AAA@@ACAAAABCCCBDCCCDEDDCCCDDCEDDEGFFEEFFEFCFFGEEEFE\YXUTRRQRRRSTUUUWXXXZ]ZYYZZYZZZXXWVUUUVTUUUTSSRRRRSSTSUVVVWX[\[[ZZYX[][[ZYYXX]YYYYZXYZZYYYYZXWYZYYXXY[_wO@AFPZiw~„^G><<<<:8566AM`vx‚€„¤É²–˜ƒxe[TZe¤Íìïé×ÈÅÁÙñö÷ø÷òëʰ°´Çßñôïìñ÷ûúø÷í¹±µ¯¢Ëíôõõøûýÿÿÿÿÿÿÿÿÿÿüé¤`\kyvou’œ’‡Åõûÿô»q\m³ðÿî“@59EKA;{çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôØÂœzqigfggjlu|„‰ˆ‹Š‰“˜¨‰xd`gebadhp€}„„‚ŠŽˆ†„‚…‡‹˜wf_[^_^^]ZRMIDGJNSZ`kv…“©½Óåèçåå弩 ±¼Éظ ~whea_^^_befhjknqz†£ÃÊÙßÝÕÎÖÙÕÐäåæçèêëêéæãÓäæèçÝ̹ºs^GA?;840-' *.9GGH>@4' !&&'$$)4/(',07EW___^]]]\\\\\]Z[Z[\ZXXVTSQQNJGB?;976455545566667765435554566888887776888877:8889<99899888:999:;;<<<;;;;;=>>=<=>>>>===>????@AAA?CA@@AA@AAAAABABBBBBBBBCCBCDCCDDDCDDDEEFEEFFDFEECFDDDEDFDVVVTSSTTSTUUVWWXZ\]Y[XWXYYYYYZ\YZXVUWVZTTTTQQQQRUUXUUUXXWWWY^^^\[[\\[YXYXYZWXYYYZZYZZ[ZY[[ZZXXY[]ZYZ[\_vOA?BHP[kwu^G>>@@@<:8546>I[q~„¤¬°ª«˜¥žšlTY`¤ÎëìæÄÁÄâíóôöóèÆ·±Í·ÑÚïôöõõøüûúùî˪²ªšˆ’ªêòõöùüýÿÿÿÿÿÿÿÿÿÿÿö輯Ïݧju†~a[»òûÿî—gf¬é÷ÿí‹C7550-4kæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷êß®†wkhfeddhq|~~ƒˆ“—››™‘{hhlid`dhtxxutx‡Š‰‰‹‹ŒŠ“ˆi`]a_^_`\YTPLGHIMRYajt‚”§¼Ôåæåáܺ¿ÊרäâÜİ –„wrruxwvutuv|ƒ ³ÛãååäåæåäåãææçèééêëêéæãÚââáÔÓª¤’}R?71.-,++&!!,/:ACC@;0! "$$'&)+,%&*15EX]_^^^\]\]\\\][\[Z\]Z\[\\ZVUVWNHB>;:766656666766666756565776667987799888877:888997778:::99::99:;=>;;::<;<>>>=<?@@@>>>>>>?@@BAABCAAABA@AAACACBCA@AABBBBCCCDEFDCCCDFEFFGEEFFFFGGFGDDEEDFDURTUTSUUUVWZ[[ZZ[]]XXVWVXWYXYYYYZXVUUUUTUSUOQRSSUUUUUVW[XVXZ^^]]\[[[ZXYZYYWUXYYYYZZZYYZYYZ\\\\\\^ZZ[[\auPA@@DFWckv]F?@ABA><;8654:AWt|‚Œ“¥¸«š ¥¢ƒjWW]zžÈåÜ»ªÁáêëî鯛ž®±µÏÎíò÷ööúýýýúóèËÉ»¢„¦èñôöùýþÿÿÿÿÿÿÿÿÿÿÿÿüõñðî㵞·Õ—Og¹ôûúßvU“åúþÿë†H>7/+/5kåýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüóæ¼–…rliefginty~ˆ•‘‘ŒŽ‹”}|~mcegjlkkjjrwwxyzzzyyqd`^^^^a^]]]ZWOIIIMRX^hr‚’¦»ÐåãÜÒ¶»ÛÔÜææã×ÉĶ¥‹„‡ ŸŸ¡ Ÿ ¤²ÄÌÒáéëèåæçäæçèèéêêêëëëêèãÒÍÖÙćk]PB5.)''*+--("%%'*,-...# $"$')+%"(,3FZ]^^]\Z][]]\[ZYZZZ[[[[Z[[ZYYXXTROIC@=;9855567766654555576667867777798888877:888::8:8889999<;:9:;>=;<=<=<;>=>@>>?>>>>>??????AA@BAAAAAAAAAAAABAAAAABCBCBCCDDEDCCCCEEDDEEDEFEEEFEDDDDDEDEDVTTUUVWXZZYYZ]`\\]]Z]WWXZXYY]ZZYZXWUVVVTTTUUTTTUUVXWWWWZYYZ[]]\[\ZYYYYZZ\YXYZZYYXYWXXZ[\^_``___^^[\^_bdwRBABDESba^ZE@AGFF@><;86437B[wƒ…‰œ¥²³¸˜qTT]{œ¿Ñ°•™Ÿµ¹ÌÚ·¬ ÀÓåôöùøøúýýýúöïÚØÙ¶–¦æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿýûûûöòñðíÓ˜…ÏúüôÓ]M²Øï÷ýèZmE.28;måýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøïа–wogfcdgkmw‹”‘‡Š‰Œ’•˜š€uieca`_``adgilnmkkjhc`^]]^^^]^^]_ZXSNLILOU\fr‘¦¼Ðåà˵µÈÕÛååæáÝÛÖ³’‹ˆ”®¯ÚÙ×ÝÜÕÖßæèëéççæßÛçéèèæäåæäâáàȯ˜„wjWE:3201-,+*.-1.+#!%! -%""%*(!%1I[^^_\\[]]`]][YXXY\\`__[]\\[]\]YYXXUPGA=:88877778654658676668767778798;8:788:9989888:988889:<::;<<<;=<<<<<>=?===?===???????@AA@@BBBAAAB@CAAABBDAABCBBCABBCCCDBBBCDCCEDGFGFEEEEEDDDEDEDFDUTVWXZ[\\][[[]\[ZYYXWUWZZYYXYWXWXWVUTTUTUTTSTTVWXXYYYYYZ[\\_[Z[[ZYYZZZZZ\\\\[ZZYZZZY[\^a_^___^^^^_acdegxSBBDEFTtfXNC@AEGFB?>==9654:A[€€‚ƒvލ¯²ª|£›\LUc—¡¯ 𥮲ºÊÀª“¤µÈåð÷úûúúúûüýýùôïâ×ÕÑ®˜´æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüóäáóþýó´UBrÁðûå}UVQLMHCqäüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøåÖ¦ujhhouvx…ˆ‘‘“—™”‹‹‰ƒ~uuvkcbaaacdeeefeeeeb`_]]^^_^`_^][\aXSNJLNT[fq’¥¹ÎäÖ¸¹ÀÉÍÓâäåßå˼—}|†•·ÀÇÍÜÞÞååãæêëêèæ×ÝæêèåÞÆ¼ÉÒ©‘~k\NE<60,)224630,'&$$# - %'#"0M[]_\Z[\^\_[\ZYXXY\\]]]Z[[\[\\\ZYXXYVRMHD?<:9876655555656776777777767777888::998:788999:9::::9:<;<<;<<====><<<=====>>=???>?@AB@@BBBABBA@AAAABBBBBBCABBA@BBBACBBACEDDDEFFFFFFEEFFECDEDDEEWXY\^``^_\\[Z\]YWWWWVW[YYZZXZVVVUTTUTUWUWWWVYYYZZ[\Z^\`_^[[[ZZZ[ZYY[\\^^^__^]\_]]___``cbb__aabbchhhiiijyTBCN\NUrkZHB?AEHNE@??=<98557C`‚„Šƒ£Ò©¥‡ª“_JQhŒŽ‰™¬¯«±Æáë³Ôâñõøúûûûúùúüüü÷ðîëãáÕÁ¿ÅéðõøûýþÿÿÿÿÿÿÿÿÿÿÿüúüþÿÿÿÿÿÿþüøóùÿþôÕWFAc‡ëúãyX‘`KZlXuÝðïíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùê¶‹{nozƒ‹…„‡˜¤œ¡‹‰‹‰Œ’•ƒsba_`bbb`__`___`___^\]]]]^^^]\\^][YRNMNTZdq’¦¼ÎàÈÀÀʼ³ÁÜâãÈ»§£whddirˆ›¦ÀÍãßãÙÕßäèéçåÛââãߨ·•Œ…s`M<40///.-*)54@770*" - - !(% 1P[]\ZZ`^^^^]^[\Z]^_\\\][[\\\^^`_`\^^^ZYSRLE@=;97755566656687677877877788:899;::99888:99::99:;:<<;<>??<>>>=?=>=?=>==>>=????CAAA@@BBBA@@@@BCCCCBDCCBBAAA@@BBBBDCDDDDGDEEEEFFGEEFFEFDCCDDGF[\^aab][[YZZZ[ZXUTWVWXYYZZWVVTVUUTUVVUVUWYYWYZZ[\\\[\[]]\ZZYYW[Z[Z[[\]^]___`_]__`bbaabaab_acdffefghijkjzUCGTVUapmgG?>?BGLMD@?==;:876>C\‚ƒ„…‹” ŸqŸ[K^u™~Œ¤ª°ÁÐÍÉËÎäõûûûûüûûùøùûüûóåâìíéã×ÑÝîñõøûþþÿÿÿÿÿÿÿÿÿÿÿúöùüþÿÿÿÿÿÿÿÿÿÿÿÿûãfYEƒæùáw[‚qp vTzÖÇ¡±çûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú랊x|…€†~ƒˆŒ’…|‡’•˜Ž‘†ƒwgkliedba`___``____```^]]_^_]\]^]][[VRPPTYer’¥ºÊÔÉ«–¤³¨œŽƒwkaY[]djr{”±¿Î÷±°Íãæã×ÏÍɽ±oWC;4-''(16@H@6.(+.01-'! - - - ''(!3Q[[\[Z^^`a_]\[\Y^[\\\[[[[[\\^^`]_[]^^ZYWWVNJGC?;855876656567787776798789977788999:97:::::99:::;<<<<<<<<<><<<==>==<====>?>>???>@@A@@@@@@ABDCBBBCBBBA@BA@?ACBBBABBDDDCDDEEFFEDFFEEEDCCDDFD_cjcd^[Z[Z^\^XWSRUWVZZZYXVUTTTVUXVUVVWWXZ[\[[\_^c_^_^^_\[[ZXYZ\\]^]\^]abc````accdbfaaa`abcbdeghinmljkkjxUIJYioxysjHA??AGKKGA@??=<;;::;BZn{Ž…|ˆ™š›]Rp Ênow‹¥ªÚÆÂÇÍãòöúûûûûúúúùúûûùîÙäîñòñîëìðòõùûþÿÿÿÿÿÿÿÿÿÿÿþõåôüþÿÿÿÿÿÿÿÿÿÿÿÿÿöç»šŽ˜íúáu\¦€ƒ|c€Õ²šv·îùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÒ°–ƒ~{{yyxwƒ•§š˜‹ˆƒƒ~}ztec`a`b`_```___^^^^^]_]^^]^]]]\\]XUTRWZeo€¥¿É×Äšs|~„†pjea[YWZY\_fo~Љ†‰ŸÅÙÙÖÀ”~ocSA3'" !#'-5>I`^X</$! #$" - - %)%!4SZ[\_[__cab^\[\Z^[]\_[ZZ^[\]]^`\^_``b[_[YXTTTNG@9668775555688886777887999:;899;:9:;9:9=:::>;;;>=?<=<<==>=<<<==@==<<=?>?>>>?????@B@@AE@CBBCDBCBDBCBABBAA?@BDCFAECDCEDDDEEFEEDFFEEEDDCCDEE`gc_]ZZZZZZZUOMLPVXWZYXWVTUTTTTUWWVVXYZ\^a__]]^^`_`a^]^\[Y[YZ[]^^^^^^]_abbcdddcccbabcdddeefhijkkllkjjkjw`TY`sˆƒxoM?BEHJPYJB@@?>==>?ACCBQar†}ytyž¯pZŽÑÄqfio€³¾ÅÒÐìó÷úûüûúùùúûûûûùëèëïòóôôîëîðöùüþÿÿÿÿÿÿÿÿÿÿþüíáîûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôíèçõüã{_·ÅÈÊyR‹ÛΩ‡ŒãóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöÞŦ‡…wtqpnmnqq~™‘•ކŒ‡‰‹”Ž‹viiffdb``c`a_^^]_b^___^]^]]]]\\\]]\ZXWX\fq€£¶¼ÆŸyutqnywsmibaa`_]\]]`deegiu’€r^L>1,(&$**3:@ELQTU\dL:+!! - - - - $#""6TZ[[[[^^^[[ZZYZY][[Z_[ZY[[\^]^]]\\]]]\\[ZZWUTSNH=76:9888667877767877777789879899988998:::::99:<<>;=<<?==<<<==<===;<<>===?>>@?????@@AAACBBBBABBBABAAAAAA@AAAAABBBCCDEDGEEEEEDEDEGECCCCCDD``_[YX\\\YYSMECLNY[YZYXVVVVVWXZW[YWZ^^^_a___aaa__`_^\]^\]\\\]]_``____^_dhffedeffedcfhijijkkknnolqlnjjkjnfkxy’¯“}rQFLQY[XULC@@C?=>AFPMH@@N]n{znužµ\°¶Ÿys_`ez«Ï¿¿Ûîô÷øúùøøùúûûûúøôðíìðóö÷ñÜéðöúüÿÿÿÿÿÿÿÿÿÿÿþùáÎÚíõûýÿþýþÿÿÿÿÿÿÿÿÿüú÷õúýꉂÃÜåØuS™à䮘Îèîòõúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûïä³~omfgddhm{Ÿ•ŒŒ”˜œ›š“’€yrpmfb_`a`a^^^^a`^]]_]]^^]]^\\]]\\[YXX[^hs€¢¶·Šxsmy‹“…uv{wqla\[YWUSPOUZVRH<2)#!( &,19>OXk`e\a^[O@3'!! - - - - $$!$<V[^[]^]]_Z\YZZZX\\]^_[[[]\\]^^_]]\^^^]]]]\]YZUTKC649;;:9977777;7777787879887989999:9;;;:99<;=;<;>;==<==<<<>>==>=@=<=?<?@?>=>?@>???A@@ACBCBDAABBBCAAAAABBBBBABBCCBCCDCCCFIGHEFDDEFCDCECCD[[ZYWUVVUSMFNBJOTZYYYYXUWWX[ZYXX\^_a__`aa__`bb`_^\]]\Y]\^__^_`abbbbbbcfiihhhffffghiiiijllllllmmlpjlkjll{wˆ‚€‹•§ ‡pUKYdqƒjWLCA@A@@?CHS^SHC?JWj}wr‰¯q]¨š¤¤¢Ÿv__\oˆ¤¹ÆÕêîôö÷÷ö÷øùûûûúø÷÷ðéæðöøîÜçðöúüÿÿÿÿÿÿÿÿÿÿÿýúáϼÑë÷ûÿýûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþðºÔîóÝlS£Û̳zg¯Õ»°ÊðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúëŠunhgefgs|‚‰‡Ž“‘ŠŒxga]`a``________]^]]]]^^]\\[]]]^^^^]]`hqŸ²©•Ї•¬Ä¶©–žw~_]ZWJGC>8420*&)!#&)+2;>CFGLca_WROLE;4+" ! - !## (EX\][]]^ZZZZXXVZY][\\][[\]]^]]\[[][[Z^\]Z[[[YXXLB:1;CDDA>;888887777888778888798999999999999::;;;::;;<<=<;<<<<<==<====<<>>>=>>>?????@?@ACCBABAA@ABAAABAAAAA@BABBBABDDDBCDFFFFEEEDDECBBFDDCYYZXUTTSPNFJMRWW[YXZZ[\\[Z[Z[[^`abcdcadcd_bahb`]\ZY\[\^_b``accfbcdfhkinhlijgghiiklnllllnpnsnpnompppkkmnu{™®„s’¹£•v[\n}‰Žz[LCBBBA@@DIZ``RF=<J`qƒwth^Yž™®¸Òªde_^s†¤ÕÛçéíòõóðòóôöùüùöøúóÙÙëôõßËãðöúüÿÿÿÿÿÿÿÿÿÿÿþýöëØÏáí÷ÿ÷íòöúýþÿÿÿÿÿÿÿÿÿÿÿøñîíõôÒcV¶‘nNW“°†‚’¿íøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûòÖ¸š|rjgfegnsz‰‘’Ž’“–› ¢¢¡“Œlb_a`_``_]_____]]^]]]\[[]]\\^_^]\\ZY[^gp}Š˜¦Ÿ¢¨¯¾áãÝ®”…zf\JD=73+)''&&'),06:@AGPWFDFIOTPKIE@71+( ## - - - - -"!&$&!!,M[]]^_]_Z\ZZXVWZ[][_]`\\\\]^]`\]]][\[^\\[`[^XVOE:5:GMSROG@=<<;977878777;8888799:9;:99:9::99:9;<;:<<<<<<<<>==<<<<<>=?<==?=?>>>??@??@@ACACCCBAA@@@@AAAADAABBBBBAACACCEDBCFDDDFEEEEEECBBEDDDXYWVVTUSSPSXZ[YZ[[[\\\[ZZZZZ]^_`cdcba`aab_`a_^]ZZZ]_`abbbbcccdedffhihihhihhhjlllnonnnnnnljmlmmmmmmmjkoo}© ˆqž¥Ž~idu•‹„^KCBFABAADHWd`\M@>>KZkzvh[U}œ³½Ð·›jeomj„§âÚÐçîòçââßÞî÷üøôøüõÛÊéòðÍÄáð÷úüþÿÿÿÿÿÿÿÿÿÿÿÿýúòäÜåòÿðßåíôúýÿÿÿÿÿÿÿÿÿÿÿÿÿýûý÷¼i]º±rEDN˜ÂwKf„ÙðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøæØ¬ˆ{mighjos…Š”–˜™š£”›•’‡ƒwic_``aa`^_a`a__`b``^]]_Z]]]\`[YXWUSTUY_fny„›Ÿ¯ªÏÑÚǯ‚k`WMD=1-)+'()-04;=@BDEEBFIMF>50258:85672-+)$#)# - - !#&%&" 1T\\^^\\[YZZXWXX[[[[]\\\\[\]\[[Z]\][\\]]\[[[YSJA:4=HMRRSRRKFD?=:9877777787888788889999999899:9;::;<<<;;<<<<<=<=?=<<==<====>>>=>>??@AAAAABCCBA@@@@@@@BAA?BAAAAAAABABBCCBDCDDCDDDCDDCCCBCCEEWWVWYYXY[^`\[ZZ\]^_`a]^[Z[\^``abfdd``abbf__ZYZZ\^`deffjihccddfhhjjjhliiijkmnnpsttrsoqmkkkknopmnmnnnkmnoz‡ ´ˆrŒ…€spzˆ‹ŽŒ`JCBAABEBBGTah_TE?<?JXitka]q˜ÒÒÖ²³ihvz„~¤ËÌéðóëçáÑÕéõú÷ó÷úôÜÛëðè¼®ãð÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿþýú÷òíõýñÚØÚèñùÿÿÿÿÿÿÿÿÿÿÿÿÿþýþùǑʷ^TZW—k`fÂÖéóûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøê½–ƒrlhhkp€ŸŸ§¤¤Ÿ˜„|vrlfb`aaba``a`^^^^aaa_]]]\YYXXYWUROMKMNQUY^djt‰’§«¤™vXJ;620.-,.037<ACEDCCCA><;77870'$$&+--.//.,3*)'((# - - - !$%&&&"! 6V]__]\ZYZYXWWZY`]]^__^]]]`\\[^]]\]\_]^^^\[XXM@529GOTSVXZXXRPHB=;:8877978788;7888999<::99999;:;:>;><;;;;==<<>>==><==<<?>=>>>>>>???AABA@@ACCBBBC???@@BBBBBAFAA@@@BABBBBBBCCEEEDCDFDDDDCDCEDXYZ[\\\\]_^]\[\^_a^]\\\\\\_aaaabbaa`aabb`\_V[\_bcfghhggghbefghhhjjjiijklmnooqqrrrpnlpkllmnoopmmmmmljkjnwƒˆ„|{}}€•„t}ŒŽ‡eJDAAAACCBEO[[_PHB?>>IT_pf]i¥¯®’fs¼€«tv•ÍãòöóñíÞÒêóùöòô÷óçåìè˨°äðøüýÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿúôøûòÖ¹¶ÀçôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüîØÀÃÅka\`¡¯kg–¬®©¹ÜíûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúíÊ©‘yqjlp}˜”œ‰„|xtnligdcbbabbab````^]]]^_]\\[XURONLJHIIIKMNRUX]aiklnmjgb]PC?333579<@DGJKLCEB>;8631/-,+*(# ()*'*++++*'$'# - - - -!#%&'(%"!!:W\^_]]YZUTSTTVY]\]^___]]]_[\Z[Y\\]\]]][\[YWMA8/9EKRSTWYZZXVUTLFB?=:9897767788899:9::;::9::<:9:;;;<;::;:::==<;<<<<<<=<>===>>>>>>??A@A@@@BCDAAA@>??@@BCBAAAAABBAAAAAABCCADCDDDDCCCCCBDCCCCCYZ\\\]^\__`^^^a`_^^]]^bccagddacaabcbabdaa``acdegllkkkggghiijkjjjnllnnopprrwwwrtpnmkloqrqvswponnmlmllljju}…–„ƒ…‚zw”™ƒ„“’bJFFBCBBBBDIPTQLGEA><>EL[mc^lz‹³{o}ʱ§ªlho—ÌÙíðòîêíñóõôòîíîðñêÏ´®µêòøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúúùï̸²Íéöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôõòå€_R^¨©l‚ÚèÜ’cŒÑãøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÜÁŸ€upsŒ–ƒ|uolheccbaabb_bbbba___^^__\[ZYTRPMLKKHKHIJIIKMMOQSRQOMKHEB@>><;;;=AFILJIHHEA?<9642/,*(&%##" &&'%'(+**)%$&" - -&')*((%"!$?[]__^\ZYRQRSTUY]^caaaa_`__Z\]]Y`]_^_^`_`[ZQE:25CMRTUX[[[[\[ZXVURJC?<:98777778;999:::;<::::::99:::<;::;<<;=<;;;<=<;===A=?>??>>?>>?@@B@EDDDDB@@?>@@C@AAAACAAACAAAAAEBBCGCDDFECCCCEDDCDCBCGCZ[]_^^^]_```^^_Z_^____aabbcdcaaabcefcbbabbcegjjhjijijhjkkkkljjijmnoppqrtsttrunnmmknoqsrrrprpnnlmlkheddhty|†€†Š‰‚Œ‹Š¦šŒ€cQGDBCCCBBCFILMIFECA@?>@DUmd\W[Êp‚Æ®®©¡i]bh†¦ÈçìæäìôóñòñÝ×ßñò踧¡¿îóùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøíÔÅÍäíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíhX_µl›èòä†QLzµÙ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùçÚ°‚v{~zyunljigedecbab``caaa`_``b]_\YTQOLKJIHIIKLKKLOLNMMJFB?<987<9;=BGHJMOONILIFB=:7410/-)(&%$#"! "#$"$$$$$% ,$%)!"'!&)**)(%!!'H^]aab_[ZPMGHKRY\^abb_a_^[ZZ[ZZZ\]^[_^^\\[SIG<?BMUVWY[ZY[]][ZYYXTTMHD?=<978888999999989:9:::::9:;;:::9::;;<<<=<<<<<<====?>>????>>??AA@AAABBB@@@>@@@@@@AAAABBC@BBBBCBBBDDDDDFGHCCDDCBBCBCCC\^b`___`ccc`^^^^__abdccbhbbbbabbddedccbceffimjkhkhhjnnnnolkkilqqprtuutwvvtvttonoqtxsusrrutuoojjheefeeghrtx“”™ƒ‡ŒŒ‹‹ ¨”‚jVGDDCCDBBBDEGGFGHFCCB>;<DYqdXSq‡›u†ÄŸ³¹£ma[\b|•¸Ü¾µÔîððóñåÈÖìñ麟¥ÌîôøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïéÞáæïöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøç©t‹Ãm©íòá}HENu§ÏòùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøëÄ£Žzynlgeeeedccdccbb`abaa```_^ZWTROLKKJHIIHJKLOTINKIEA?<;:::<>@AEIKMMLMMKHFDA<952/,*)(('%#"!!!! ! "# ! - -,F:**6=C%-%'.00.*(%!!+O]_agcd_\TLBCFPW\^bbf`a^]]_Z]ZZ[]\^^_^b^`ZPKILRRZ[_`aa`\\]_\ZZ\[XXZSQHB>;:9889<;;999989:99;9999:;<;::;;<=<==<<>=?======>@?AAB??????@@@AABBBB@@A@@@?@BACAAACCDBBBBBDCBCEDEDCEEDBCDCCCBABCDD`aaaa`bcbcba`_`bcefgcbbcbbbabcccdefdddeefihjjihgmikklkmmmmmmopqrsvvuussstrrourttvvursqqpomljhffdeehhjno{pjop•™‚‡Š|l|š£ Œp[HDCCBBBCEDDEGFNOPNLIC?<?CSl`R\e^ZƱÓÁ©ŽlUZ_p€š¦±»ØíïôöîÏÆÜñíĪÒîôøüþÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüñâ¿ÂÌßòüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúê¿´›qµðòævf…^HfŒÄóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÕº‚xnkifeefcddgddc`bc`]]]WTRPOKKJJJKLLMNNKOOOLGC==8;9<>@BGJOQRSRRJIFCA@;8530.,*)'&&%$##"""!! !! ! *GHJ51Jf@'2-# %/=A;4+&$!/S\`aabcc^WM<=>IT[\aab``__`_[\[\\\[\]^^b^\WSPRRUWZ[]]^_____]]\\\\ZXYUTPKGD?<::99999::989::99;::::::::::;<<:;<;;<===<<==>????>?????@@@@@@AABBB@?@??@@@@ABABBBBDDBBBBCCCDDCDDDFEBBDCBCCBBBCDEccccfccddccdeegeedhecbfcccdeefgeeefffffghigghigjnmqlllmmnnspqswutuvuurqsvssuwu}utttuvpqqpjhgfedegkqrturzmhhhv…œŠtfjб®£eODCCAABCCCCCDGQ^]\WRKE@=<BUjXRUNT|ϯÖо hYZ\jz“¶¶ÉîòöùôÕÎÑïð׺À×ðõøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøï×ÁÅËâêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóóè¯x¾òïÛspriKImŸíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷æÕ«Š{nkhgffcdcdddbad^\VSQONLLJKKMOONOOLMIGGFEA@??@ABEIJKIQHQNLIECA?;8641/,+)('&&%$$#$##!"""!!! "$:WjP<Hfh@%"/A?5(#).-)'*=WWY6+&#!4X]dababaaYOB:=CUY\bce`a`b__^^]__^]a^^_b^[YZWVX[[\\^]]^^^^^]^]^_\\ZZ[\WYVSIB>;;:9899:98<<;9:::;;::::::::;<;:;:;><;<??>>??????B@@??>>??@@A@ADBAAA@?@BAA@DACCBBCCCBCBCCFDECCCEEDBACCBBBDBCDCEfgededdedcdedbbbbbbbccdccccbdddeeeghhhjkkljiknnonnnmmlnppoqqrstusrsrrrrsvuvwvvwstttvsomkiffeeeikmqppqqrzkWZ^djkl|ydkzš¿«˜{XICBAAABBCCCCHUeuj`YRMFA>?@KWY_YS|××ØÂ¬§®¢k[[ajv“ºÅíôøûöãÉíóîçØâñõùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôéסÀëûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøßÆËôíÄo~¶nVWZ‰èþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷꺕…tnjhfffcbdd`^[WSOMLNLMMOPQRR`POLJFCF?CBBCDEHLNONMKJIGEC@=:8531/-+*)('&&&%%$$$$$$$$$$$#!!! " &2?5%$1Oo`SNaek>-;SYjD*0<FPA/7LSWI5("#:Y^_____]\ZRKIGMWXYZZ[[^___`^^^___^__`ab]\[ZZ[[[[[[[Y[[[ZZ[^]^^]\\\[[\XWUSOLID@>;:9::989999989999:<;;::::<<;<;;;<<<>@>@>>?>@??????=>???@A@@A@@@@@@??@AAAABBBBBBBBBBCCDDDCCCDCCBBBAABBBBCDCFkggghhhfgcefhbbbbbccecgdcccdeddddfijkjmklnpqrnonnnppppqqtqstwsrrqqtssuvwvwvuvwtxtrqpljgeefjlorttqtoqonokSRZlh]eu‰yvxˆŸ°§ŸmQIBBBAABCCBBHWpzwc]\[NE@=;>J^bph€ÑÑź°ÁËȾ^cgjrˆÓîõûøìÅÄîø÷öôòôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷é˰¨×õûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôèóûî¬lŽ–msšp{åúöóøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùíɧ‘zrkiffd`][XVTSPNMNOPRSTSSTTSPNHKFFGIJJJJKHMKKHEB?><;97630.,,+*)(''''&'%%&%%$#$$%'*,*&#!! " !##%(,18=(.D_`a=)7gd[SMdnR;?ZSgjT8=Qf]a7>KTPB0& %AZ^```__\[ZVVTUXYXYXYYZ\\\]a_cacbebcbebb]^]a\]\[[][ZY_\[YZ[^]_^\\]\^\[ZZXVTTQOHA=;;;::99999:9999:;;::::::<;:;;;>;=<==<>>>?>@?>>@>?>>???BA@@@@@@AA@@CCCBBAABBBCCDBBBCDEDCDDCECCBBBABBCDCBDCDfffffgfedccba```bcddccgdddddddfghiknlkmmnppnoooooqoooopqsqrtrnnnqquwxz{zzvttuxtrqpnkjhkfiknrrrpppooonmlpkIMWgg_[o¡ž v|‚’±§ƒiRIDCBBBBCBBJYo†veca]WLD@=>>HQ_q†Äšx™ÂÌçáᾤžoQMQw´ÔòûúòÍÃðúûüùö÷÷úýþÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúèÑÅÌñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýþÿïªkœdpxovÞîäÜæõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ× ƒwmhfa]ZVURRRRRTXZ[VTQNNMNQMLLKIOKLKMKHECA@><;963210/.,+()))(('''&&&&&&&'&%%$'+7?@C0$"!!!!! "#$%),**8A@CGJVcQ9)(Dadh_P,:cbB,GfW?=NQTakJ8Mg_]L9=HIK4&")H\^_]^\_ZZZXWWXYZZYYZ[[[Z[[]\^``bbaabdcd^]]`\]]]Z[\[Y\[ZYZ\[[[[ZZ[[\\\^YYWVTRQPJFC@?<;:::9999:;:9:::::9:<;;;;;;;<====>>??>=>???>>>=>>>>@@@A@?@AAA@@@AAABAA@A@@@CCBBCDDECCCBDCCCCDBBBCCCBECCgedfjghdccb```^aceeedehggfefhikmmkknopqqppqnopqpqooooppqsqsqpnnqtv}|€z{||{{tvtqpomjjhjklrrxsuqoomlkkkkinjHKUvoeckœ£¨Šzwx—®›‰jRJEDBBACBBKYqzxjilvaQIDAA<<DNg„xq‰§ÜÖÕݽµ®«zJ7:Ix§ÒðôñØÅðúûüùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùóôöûÿÿÿÿÿýúôîòöøúöòøþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿó³‹sZx®msÚܨ€¦ßôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷çÔ®‹yie_]ZXVUUVWWWYdYWUPOOONNMKJJIJJIHD@>;:86420.--,+*))('''''()&&&&&%&''(*+)'&+;OZgQ7&"!"""!!#*05:C<6=X]aholijlB/=aSJZpO/=`[.%BgV:CWb`^S?:ahJA:3355/'!.N^_c_^\_\\ZZ[\[``````_]][[\^\]^_acaabdde^a_`\^]]\\\\[[[ZZZYYZZZ[ZZZ\]]ZYYYWWVVUUTRJC?=<::9999;;::::::<::;::;:::<<>=@=@??@>>>?>>>==<<=>>@??@@?@@A@?@A@?AABBBC@BACBBBCCDCCCFCDCCBADDBBBACABCDgeeeeedddbc`a`bcfigeffgggghjkllmmnoonqooolrnopooonoprsrnsppopqsxyyyyzzzxwwutsqqnollllooppsrqpnnommlllkmqiGIQgsmi— ¸°£Šxz‰ž¶¦nRKFDCCECBIUcpnmnpvfVKGEB?>=CKd|kds¬ÕÓÜ»ÎÈÔŠPA3>Kq¡Ðíñéæñùûüùõööúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷翵ÔöÿÿÿÿÿÿÿþýüûøéÑÕðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Ð fWŠisϨi]]¡êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúòå¼}mgba`^\\ZYWUYXXTTONLJIHHJFCA@=;:964210/-,+*))(('''''''''&&&&&&&'&)-4;==3+:Q`sjd:'()-1-('':NQTX\FK]jkkjdfikG@RXIDT^L;DVP(#OgH7Oma_QG69]jP4.*40+# 2T^_`_^\^]\[\`\[_]]]^^^^][[\\\]]___`aaa`d^`__]]]]_][[Z[[[ZYYZ\[ZZXZZ\ZZXXXYWXXWVWXVUPKG@?=;999;;::;<;:<::;;;;:;;;<<;==>=?B>>>>>>>==<=>>>>>?>??A?@@@@@@@BAABBA@AABBBCCCDBBBBBBBBBBCCBBABCBABAhfedccddecddcdgihijgghihhhklopqnonsmkmnnnnsooonoopqsvssrqqqrxz{xxywx{{{wxtsrqppoomrooosrqqrnlmmmkmpqqpsqhFGL^trk„’œ¶¡›Š‰–°Å©‘pWNGEDDCDGQWansvvwh\QJHGCA>=@GR\dou¬Æ½¨ÈÂÛ•z`G7?Mx²Òêêèò÷úýùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûì²|Ž·òÿÿÿþýþÿÿþþýóÕÁ¾äíõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûõÛЃYy†lu£„^Wb{Ç÷÷öôñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøëœ‰vqnjfb][ZWRTZTQNKFCBBA?><;975430.--,**)(''''''''&&&''''&&&&&'''',<LWbSA>F^rtuc=./;HJI9,3O^mWdNFWnaZdr\SgjHCVe@7YhE7LhB#*Yd@CbebM?3(3Xb]:,,4-(7X^_b`^_b^___^][^]^_c_a_]\\\\\a_c^^_ccbcd``_`^^]]_`\^]^\\Z[Y[\_\]]][\YYZ^XYW[Y[Z^^^`c[TKEA?;;::::;;<=;<;<;;<<;<<><<<==@=??>>>>@>A=>=?>@??>?@AAA@@@@@BBBABAAAA@@BAACCCCCBDBBAABCBBBCBDABBBBCCfdfbccccddddffhiklkhhhggiilnpqpnnmmjlnpspoqpqrqppqruurrrttuvxz{yyyxwytutspppooonolnmmmnlnnnkkknmnpqrrrrveDEHWny‡~„…}š •™§´¾®•w\OGEEDEGNSZlyvsqkbUPLJIFC@>BAKU\er~•ÃÈÊÀÚ¾”cF=GPu¥Àêðóöúþúö÷÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÌ†om¥îþÿÿýûýÿÿÿÿÿîϹÉÒÙìùýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøß©•œy]¶x]ƒš„¬éêÒÄÖñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùì˲ŸŽ…{smdYVVNMLKIGB=;9985320//.-,+*))('''''''''''('&&'')'())(''&''3O^nlnUFWoruv_?3BUZk\R<8VodQG>CabY\_`PPeeFNfa;D__>A\d:65a`=BeeN:;,.1LhcB/-/+#;[]`a__`b_^^_[[Z\\]^^^__^^^^^]^^_]]]]]]]__`]]]]]^]]\]\\\[Z[[[[\[\[]\[XYZZXXXYYZZ^_ceee^[SKF>=;;:;<<<;<<;;;<>;:;<>==?=<=>>>>=>>>====<===>>>?A??@@@@@@@?@@CBAAAA@BA@BFCABBBBBCAABBBABBCABABCCBeefedegeddiejjjjllkihhgjlnsopnmmlllmpprqqqtvvwxwusssuuzzzvxxx{z~}}xxssrronnqnpoppolkkmkmmlklmooprusvrrubDCGO_m~…Љuw†“¤¤ ž¦®ºÐ²y[OGFEEHKOWjuyqnnmaURQNKFB@><>EKU\]dƒ‘´½¬ÁņWKFHNl‰Àèð÷úüùõö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ¾tdjšîþþÿûøûÿÿÿÿÿõãìíä½Õëòöûÿþüþÿÿÿÿÿÿÿÿÿÿÿÿÿüúàͯwe¶rmŦx“ÛšŒ“ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùïÜ̾µ–„}r^PJFDCA?=:5210.-,*)((((('''&&&&'&&&('&&''('&'''(+./-*(&'):Zrtwqeehnyuw_@8[Sdhr]A>`o^C13BeiQHbfLP_^JR`]JLbW=@bb6*AjV:GhgE=::ICHhi?2.1' #B]acc_`aabb`_]]]`]_]]^_`aacab```aa`^_``\]^`]_^_^_]]]a]\\]]]\^]]\^^]][[[Z[ZYYZ[]^adiilhhedZSID@><;<=<;<<<<<<<<<<<>>>??===@>>=@>>========>??@??@@@@@@@@@AABBBACA@ABBBBBBAABBBAABDCCABCCABBBCCBgggghihgfdffijjkklkjkklpqspnolllnnopqrrrssvxuvwytsssvxyvzwz{{z{yyxvutponnmnmmmmkkjnmmllkllllopsuttutsrswaGFGNUc„•Šqss‰µ©£¥¨¬²¼À°£yXNIGFHJOVepspprtucWUTNJFDA>><>BLY\ez¶´Õ½Ý¼·¢hKHGišÍíøúûøõööúþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòªhcj“îÿþþùôúÿÿÿÿÿüøûýðÔÊÎÛæöÿüùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöâ¢w¢»u{ÕËvÔžqM„çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøóíåЬ¡£†cMB>:7542/-+)))(&&%&&&&&&'(&%&&'&&&&'&%%%&'''*,6=AD;1+'(+>dqvvvmhkmqtu^?;IOXbieDDdpT4,,FhfHPg_JWmT@Sm_TYeD0Eh\3.TZLLIdiVS?BNPVgQ7-*'"'K_abc_aaa``______]]]^_acabaaa`aaaa_]][\\]]_]__^^^]]]^^]]\[[[\]]\^^^^\[[Z[[\]\]`behiijhhggb]YQHD@>;;;<<<<<;;;;;<;========?>>==?><<<<==<=>????????@@@@@AACCCAAAA@@BAAABABAAABAABBBBBBDCBBBBBBBikohjjllkiiinkklpppprsrrrponopqotsstux{vxvxwuttsstvwxz}v{{{{{z{zywvuupnnpnnpqmnjjjmnonommnprvsxttstssrty`IIFMRZjy¢ƒ{oœ§Ã°¨«°·½²Ÿr[RJIIINS`lvqqu‡„pb^YRMJGFB>;9:>JS[_}‘±´³µ¯Ë²˜cFBQÀàöøú÷õö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþñ gahíþýüøôøüþÿÿÿþüýÿøïÛ¾ÉÒàðððøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüóÞäßÒyyЪo€Ö^F}ç÷õóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùöðçä¡eI:520.,*('%%&&%%$$$$$%%&&&'%&&''&&&&&%%%%'').;IUb_[B/((-Bfquxvxvsqpqr[>9AORirdGHgnJ/',MicGRiZ[^jNOSinomQ6,NXX.2Oa?.@dgiN@CeijWA-$""-S`ebcabbbab`ecb__^^^a_cbbbbceeeba_^^\\]^`^``a`b^^^]]^_^^`[`]]]]]^_`]]]][_]^^`bgijknmoomkqjkd`WNGB?>>=<<<=>=<><=>===<<=?@>>A===>;<<<<=>>>????>@A?A@?@AAAAA@@BAAA@BBAACBCBDCCAABBAEBBCCBAAABBCjmojkjkjkllkkkklooosrrnnmnnnooqqrqstw{wvtsttssssuxvwy{yvxyyxxuuuvuuqpmnnpooromlhjkmnnnnortuvurtssssqrrtxeRLGLPW`m€‹ƒq{›§Ç»²ª®µÅŠzaTKJILQ\ivvwwŒlc_TNLJIHC@<8:;G]_cyµ½¼«šÁÞ±vL<GZŠÝñöù÷õö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿýï¦hagŽíýüùöóöùüÿÿÿÿÿÿÿÿýòÚÌ÷¶¸äóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöñî݆r„‘m…×ÄcJ{ÛëááñýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóéÉ¢§˜eB1,)(&&%$$####$%##$$$%&&&&&&&(''&&%%%$$$%&*.>O\knseT6))/Fhqstttth\lqrX=3DR^oqbHMjlG,)/Tk`JXlVM]iD1FfglS<,&=R:&*H<,(1H\K;52JbSH0!3Wbcbbaabbbc`bbb`aa_^_^aabdcccddab`_`^]_``______]^[]]_\\\\\``^]]]__^]]Z[[_\^_chijjkkklmlkkkjigg[TOEB?>===<=<<<<<<=>=>>>??>=<<==><<;<=<=>????>?@A>??@BAAAAA@@A@?AA@?AAABBABBA@BDAAABBBBBBAABBBlmomklnnooopoorooopnmljmonqqvqrsvrxwwurrqrsttszwwxwxzz{vvw|wvwwvystpoopponoommonoooqstwxyvvtttttussssru{j\RIMPU\brŽŠ™~x£ÍÃ͝«·»¶²‰fVMJJOXfy}{“®²vg_VOMKKIFC@;97<L_\`|“§®µ¾ßÚŠT=;F]¤äòú÷óõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿüöß³k_fíýûøõôö÷ûÿÿÿÿÿÿÿÿÿúõä̼¦¿ãñúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûúìšjq\T•Ö®cgƒ©¥“ãúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøð彟™–Z;,&$$#""""#""#####%$%%'&%%%%%%%%%%$%%$$%%',<TbpkvuxZ<+*0Iksmipur`ZirpS<>T\fks]HQmkC*&2\d]Sg]NMaO516IYN>,!0+$!!$% $/1.(&798. #7\adcbaebbbccbbfbccccddfffcdccceada^_abbaca`_a`a]_]`__``_b`___`a^_]]^]\\\^^ccgjnkpmqorrssqqqmjhddaTJDA?==<<;;<<===>==?????????=>==<<<==>?AAABAA@???@ABA@ACAAAC@BA@@@A@@AABBAABBAABBEBBBCAAAABmmmnonpqppppqrpokikkllnponpprrrrrqrrssrrsstttt{zzxy|yxvtvvwvwxtsqooonmnmnoonnnnooprtvxwwxvvuuvuttssstsv}l_XQPPTY^cr„‰ƒ€‹œ¶ÂÄÆ¸®¯²´ÂÇ“hVKKLUbirz}º±‚m`WOMKKKHEC@=;;;HWWYn¡·áÌáç£oE<<PuÛíûöñôöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿùßÄ‘l_f•ìýú÷ööõõúÿÿÿÿÿÿÿÿÿÿÿùéÞÌÈÛîôøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò¶jR@]Ù˜dª®~QsÌöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷îÔ±™‹xM4&$#""!!!!!""#####$$$%&&%'%%%%%%%%$$$$$%&'0L^sb^itu]<+*1LllfjnmqZQhroPFJ[ffiqXMTljA('3UjSINU@=<;)'/10( " %=\_aca`a`abbbbbdcbbbabcdcdcccccb`aa_^aaa`dca_^\]]^^`b_^^^_^_``__]^]]\\\\]^_bdilmkmlmmoopppppolljgdbWOJEA>=<;;<<<==>==>?>>===>>=>==<<<==??A@@@B>?>??AAA@@AB?@@@@@@@A@CAAAABBA@BDBBBABABAC@ACBBmmnnoqstvwzuxrplfhjkomrqqpsqtqqpppoqqtyuuvzyxx|{wxxvtrsxxytrqponoooomnmmoooqooqwwxy{x~xxxwuzzyyxuutxuyymb`[TLRX\`bkw…‘‰ˆ•±µÆÄ¸°®®³áØÐ“fVLKPZbkrz¦¶ŒtbWNMMNLJHFCB><9<FMJLu·ÁÄâéÊ[>9AeÈìû÷òõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿöÅkc`kîüúø÷öõõùüþÿÿÿÿÿÿÿÿÿüùê×ÔÉ×åïòöùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿøåžVYr¾ÝŒe¥ÞÂtHdÔöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþú÷į̀’‡vF/$##!!!!!!!!"######$$%'%$$$%%%%%%%%%$%$$&'5Tk`OQ`tuU<**2OnudVbsiOQgsmLFWnj^lnRESnc?('/OIB@?6*('"'C\_a```bcccecechdcbfceeeeddfcdcccc`_`caaadba^_\^_bcca`_e^`___b``_c`_^_`aa`bfeponkmllmnoqptpqonormkgf^[OFA=<;<==========>??=<<=>>===><==B?@?A@C@>>??AAB@AABAA@A@??AA@AABBABBDCBBCCBBDBBBCAABCCnoopruvvwwutpllklkklmmosppqqqqqopqstvwwwwwyyyz{{zvuusppopponnnnnnmmlmmnmoqqpqprsx|zzzxxxxxwuyzywwuututyunaa^[TTUZb]YfyŽž”™ž«ÂÂÌ¿³¬«ÃßÖÊgXKNV[\esƒ”Ÿ˜zbVNMLMKKJHFDB@=<9<?BFgåëÞ§vT:B\Äëû÷óöøûýþÿÿþÿÿÿÿÿÿÿÿÿþó·h\Zap¨îüúùøöõõ÷øüÿÿÿÿÿÿÿÿÿÿÿü÷ìÒ¾Éßåìòöùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿøà¢…âß‚iÏìÜxJiÙ÷üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúöêÄ¢‘ƒk?-$##"""!!!"$""""###$$$$$$$$%$'%%%&%'%%$$%'2XQNECdseM6)*2PpteV[]`PSltkHGejkkmfLOPSZ5&&)/3.)'$"!)H]`````bababaabdddbccdeeeedcba^d__```baaab`a\]]`abbb``_a__^_^_^___`__`abaddefojjjmklooonnnnpoonomljgb_ZRIE?><;:<<===<===??>=<=>>=======>>@??@@@???>??@@@?@A@?????AAABACBBBCCBBBBBBBCBBBBAABCApquvxv}xytpnmnplnmqpponoppqrpqqrvy{zy{xxz{|{{zytrqonnnnnpnnnnoolmlmmnpstuw|zzzzyy{x}xxxzyyyywwvzttuyum`caeaYSY`\UWk”Ÿ˜“‘–ª¼ËÕ»«ª³ÉâѧŒn[OR[WZnyŒªž‚bUOOOOMLKJIIFFA?96869DcŽÆãÍÁ”pOD\¿ëûöñóôùýþÿþþÿÿÿÿÿÿÿÿÿÿôgWXap»ãóòðòõö÷÷øûþÿÿÿÿÿÿÿÿÿÿýüöìÓÀ¾ÄÕÙë÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöííîíÞ{oâðãviwéöûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö꿟’€a;*$$#""""!!""""""#####$#$$$##$%%%%%%%$%%$$%.AO?9JfffD0(*3RpteKObQHWpuiDF[rqnb`C6;B<,$""##! #,,,M]_aab`ebcbfcbcdegefdjfedeedaa^d_`adaca`ac``\_`hbeeebc`b_``a_aaa```a_ccefkkkinjmknlkmmmonmmqppppnollfgbaVMFB><<=<>=><<==>>>@@?><===>=?>@@@?A@@@@?A?>?A@@?@@A???CBCABABBCCCDDBCCDDECDBBBCACCDAqsuvvtsrqpokmpokmmqpponmprrsssuvwzyyyyzyy|yxwwvtroonmllmmlmmmllllklmoprsvvwx}zz}{xyvxxywxxyvwxwvuttttuuup`cc{–rV[a\WVXl„™°’–ªÇÈ˸¯±³Â׿țv`ORTWer‡œ|cVOOLMNLLMMKIHFB?;753>Jp¤¨©¤¡ƒk[aÆëúôçáë÷ýýþþÿÿÿÿÿÿÿÿÿÿÿõÀmXX^k”ÐʾËÙðö÷÷÷úýþÿÿÿÿÿÿÿÿÿÿÿÿüïÕ·ÃÂÅãôûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõâvußîÑle¥èõøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö黓Y7)%$#"#"#!!""""""#####$##$$##$$%&$$$$$$%$$$)024ATjkP7-'*3SpsfH?AAE^ouY>@OV_VOC3.,+*'""""!! (2-$3R]^aaaabbbageeeddcagdffecdca__^``abdaa____]]]_aaa``bbc__^a_`^aa`^`aa`cdgjjkkjkijjkkllmkljmlmkllmmollgfca_XSMEB?><>=======>>>=>=====><?>?=>?@AA>@?????@?@@@@@@?>@@@@AAAABDCDECCCDDDCBABBBBCCCAxuyrppopsoolnnnoqpqqsqqqrsxxyvzzyyzyyyzyzzyxwsqpollmllompmnmlkkmmnqqrtwy|z~~}{{~{}yxx|wzy~vuuuvsuwtssuuqade•‘’d^^`VPPZt¡§•—«¾ÆÈ»±®°·¾ÉÉ£}`OPTZgžŒ{dVNNLLLMMMMNLLJHD@;66:DWx‰ŒŽ‘•pkmÍëùóãÒÈïüýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Öy[Z\^dvyxªçóøøùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿúõðåɶÑíøÿÿÿþýþÿÿÿÿÿÿÿÿÿÿÿÿÿöã€{àé¥cÙççäåñûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõè·”R5($$#""""!"""""""$#$#$#"##$$#&$%%$$$%$%%$$$%'+6I_nW@2)&)4SqrhF858C]qbP:28>>:5/*&%%$$"!!!! !!#-44*"<X]_aacbbafdieggfcebgeffgcea`]^^bbcccaa__]]^abbadbbaddd`__`aa`aadafaabffjjmnmkmjljonnmnkmkollmmknmnlliihgdd_^RJDA?>=<==>==@=====>>>=B>@?@>>>>?A?@??>B?@@A@@??@A??@A@AAAABCDDGEDDEDDCBABCCBCDFAtqplkknpnkmlmlnopqqqrrrrrtttttuvwwxyxwwztpqrrpokllnpmlmlmkkjkkmoootvwy|~}|~}}|}~~||}{{ywuutrrowvwwvrtpuurbcd~ ›rgb[WOLUf~šš™•“—¨¿ÇÒ¿³³²ºÐÒß©x_PQS_px}{eXNMMLLMPNNMMLMKIGC@9;>Oeyƒ„zjotÂëùòÙ‡ èüýÿÿÿÿÿÿÿÿÿÿÿÿÿùêr_YPRWWWi‰áñøùúûüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÛÍÊêöÿÿÿýúýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷í–§áåb˜àÝ©€—Èóûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôç¶N2'$#""""""$"$#%"""""######$$$$$%&'((('%%$$&%&1?VoZH<0,'*6VptkF2//=ZTS>/-+,-+)(&%%$$$#""!! !!##&,' '.54."%B\]_aabaaaedhedbc```bdfec`a^]]]^bccdcaa^_\^_``aaccba``a^ada___a`aafbbdffjjkklkkijijihgjjkkkjjhhimklmkjjgggfca[UMIDA>><=====<==>><<<<====>>>>>>??@>>>???@A??=?AA@@AAAAAABBCCDDEDEDCBABBBCCCCCDBonmnnnmnmnpppqqpqtwr|vutqsssrrsuwvwwwustqpppponknmronmmmmlllmnooptz|~{€}~~~|~~‚~~{{{{yvolsqsxwuuvsssuuqccdr¦Çœ›x]SLKQ_q†›—°œ’’˜«ÂÊж®®²¿ÓÜÚ¢zcPPVcot{gZOJJKLMNNNNMNOOOPRQF<=H]xƒ‰uaTf¶èøïÎxtÉøûþÿÿÿÿÿÿÿÿÿÿÿÿüñÄ™wWNLOQTe§çõøúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïïñô÷ôñððøÿÿÿÿÿÿÿÿÿÿÿÿÿûöîØëâ„c°Ý¯l_]—áóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþøò嵚zK1&$##""#"""""""""##""######%$$$&*.461*&%$$$&+8Oh_MC:72)*5YnwdE0++6;<9.*(((''&&%$%%$$#"!!!!! !!'&5*##%0;8600#,376/%&H\baddd`aaedidbad^]]aekeccc`b]_`cdddca`^^^c`fbdddbbaaaddddcaa`a`cceejjjhkkmmokmjnklkigkillljiihhmklmjjnnmmmhihf[SLFAA>>=>==<?>;<<<>?>==>B==>>>>>@?@>???AAABAA@C@@ABBBBABDCECCCCDDBBBBBDCEEECBBmnmkmmoprtssrqrrsvtruvvtrsqortsrrrssqopqomnmmnnlllnmmmmonmnnopstvw{}z}}}~~}}~}~}ywvvroqlquuvwxvwvuustvzfcdq«¹«¨‡bVOIO\mx†¢« –—™«¿½º³µ´¯¼ØÓЫƒfPTZblmj]RMJKMLONNNNNOPU`xl]L?ERf|…“~_SU˜ãóé»xq½ñùýþÿÿÿÿÿÿÿÿÿÿÿþúðÌ \NORX_ÜòøùúüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûøöòäÒźãóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûøóà|fÅÉ\P_l¥ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ïÚ²˜‰uH/&%##""#$""""!!""##""""####$$%&-5CMKJ0'%$$$(0Gee[PFJU8+*2Pka[<.*)+.-,*(''('''&&%%%$$##"#"!! (5*%$ %/11000C9A3/%%/1441&(L\bacba`abcbcca`ZVZ]ceddbaa__^`bbbcba``^^`aaccc`bacbbbcdb`aaa``accefgfhhkimlnijjjjjjigkijiihfdigllllkjkkjiigfed`\XPHE@?>>==<<==<<=>>>>>??==>>>>>>=@>@??@@@@@A@@@@ACDCBBBBCCCCCBBBBBCCBBCCBCCBBlnnoquz|{wuvuttsussruuvrqsrprrqqqrronnnmmmomllnnmmonmmnoppqtwyyyzx‚~~ƒ~}‚€~~xvtpnkrzyyxzwwvwxxvzvvwkddr¯ÉÈÎh]QIMXkq{ˆ «²¥š–°Á·´º»®©¸ÈÐ݆eS[[cjj`TOLJKLNNNNNOOQ[i°…cODJVk…„cT^ƒÅåʦ„t¥êöýþþÿÿÿÿÿÿÿÿÿÿÿþúê bY[eršÇéûúùúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüûôÛϨ»ãòûýÿÿÿÿÿÿÿÿÿÿÿÿÿþüöá‚kÉÓwFM†hêüþÿÿÿÿÿÿÿÿÿÿÿÿÿþýöíÕ®˜„pE.%%$#####""#"!!""#"""""####$$&+6KcdfJ6(%%%$*<YjpcY_iY<+)/HJKA3+)(((((''('''&&&&%%$$$###""!! "!75/,**,6<=;23::93''19762' ,O\abfbabgbdbbbbXRTY`defcbaa``abbcbd`]^__`chggcbaebddcbdcaaabc`abeehgihjjjjlkniniiijjlllijjjhfehillnnmlmlkhggffieea]UMGB@?>====>>==?>?>====>>B>>>=>@>@?>???@AAAA@@BDDCCCBBCECDCBBDBBBAABCCCGCBBqrw|}~}zyvuurspussquqppprpnooqlnnnlkkljlmlmllmnmmonmmnppquxz|zxxy||~~~}~€€}{xtqpmqqu|{{{yyyz|{zxxwwxzjcev²»¶½¶rbXPPTcpw|ާ¬¾©˜›¡«´±ÙƵ§¤±ÊËÊ¥{op]^ckcWOLJJMPONORPPTb~Ÿ¨¦€cOCKUhvi]a}œ¢œ“މ”åôýþþþÿÿÿÿÿÿÿÿÿÿþýð©xk°Ìéóúÿüúúùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿ÷åÐÆÆÝñöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøè’•ÑÈgINUUtçùüÿÿÿÿÿÿÿÿÿÿÿÿþýüõëÏ—„jB,&%$$##$""""""""""""#""$%&(''+0Ga]\RG2'%%%&0F`xrlmmeZ7*(*494/,(((((('''''('((&&%%$#$$$$#""!! ")=J:5426???9//72.%*39754("2Uc`aaabccaba`_VNV\^dd``a`_``bbbaba`^^^_`bcdcccbadbcabbbbbabbcbbceghgihhiijijiiiiijjiihiihhhhhhhiijiijjjhgfffgggffecaYUNEC?>=====>=>>>>=>=>>>>>>=>?>=?>??@A@?@AAABDDDDCDFCCCCCCBABAAAAABDCCCDCB}|„„…}|zyxwttswqtttuuqnnoqonoormmlllllnkkkklpllllmpmnoqtxz}~~}|}~~}~}|}€€ƒƒ‚{{okmpt|~}‚€|zy|z}hceyªË·Â¼}ld[RR_lsw~œ°¾£œœ£ÆÑ³¥Ÿœ¬»ÃË™‡ƒc]^gkUMLJJPQRPOPSSYu˜¼Ü¶—y]JBHRcma\cx|‡‚……áòýþþÿÿÿÿÿþÿÿÿÿÿÿýóÂŒ«åîðõúüÿüúòéêêïôúÿÿÿÿÿÿÿÿÿþüþÿÿÿÿÿÿÿû÷åËÅÆÞï÷ÿþüüüþÿÿÿÿÿÿÿÿÿûõÑÀÁ»g?FQ]qåóóò÷ýþÿÿÿÿÿÿÿÿÿýüôê̬”…`?*%%####$"""$"!!$#"####$'+0561.7Zgi[JD.'%%%'6Toxzn{ieG1(''())(''&'''*((('()(''%%%%'%$%$%#"""#!!!!! #*4NKACECCDD98)%" -5:775*%:[```bbedcad^ZSNS`acdc_aaa^`bfdeed_a^`adffdgddcddededdb`bedeefeeeggjhjiijkiihhiiimjjiihkhggjjjhhijjlihjjhggihlimlmklgfc^QGBA>====@>>>>==>>>??>>>>?>>>A@@@C@@??@AADDDDECDEEBCDDCAABADAAADCCCCCCB~ƒ€||xxvurssspstsqrqmlounooprmmnlnmlmmljjkplmnnlmmrtvx{|}~}}}}||}~}yvsqks{}~zzyz{|€€ƒ„ƒ€€€~~~~ia^m¥º¸»ÂˆqhbYU^kptw{‹£¥¦™—¦Á¿²¨¨ ™ž¦²«·ÂZ\eaRMKJIKQTUSPQSf‡³ÛÙÅ©pSGBEJNSWcu|†x…’‡‰äðýþÿÿÿÿþþþÿÿÿÿÿÿþùëâèóùúüýþÿüù繟¯ÙêôÿÿÿÿÿÿÿÿÿýúüÿÿÿÿÿÿÿÿÿúóçÓÑèôÿüøùùüÿÿÿÿÿÿÿÿÿÿþ÷òì×l\wxz§âæØáè÷ýÿÿÿÿÿÿÿÿÿþüóèȪ“Y;(%$$####"""""""#"#$##$'1;HULE;=Zsl]TF8)&%%):`mwrn`VE5,&%%&('&'''''(((((((((''&%%%%&%%$$##"!"!"$')&" #%)+5HJEG<763/+&"/67788-!*F]_``ccba``\WTPW^`dbbb^^]_^acdbba^]__abddfcgddceccbdecbccdadcccdefgkhjijkjiihijjjjhihgfffgfgfighhjjlijjjihghijhlikkkihgdaUKGA?=<<?>>===>>>=?A?@?>>?????@>??@>?@AAABCCCCCBBABABCCCAABAAABBCECCCB€ƒ~||}wxxxxwtutytsnmmmloomorqsnommnppppqmnmpppppqsvy|}‚‚}~~}~}zmhsqw†}ƒywut{z‡‚ƒ‚‰…‡‚…†‡†‚l`[bй¼½Ã•™xc][`onqt|Œ£¡Ÿ™™œ«½¯ªªš‘¸²ÎÇ r[dcRNKJIGQY\]UPTp¡ÏÕÕ¿·˜†eK?>@@GOcq|†ˆ‘ˆÞîýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõøúúùúûýÿü÷àŽ†…—ªËî÷ÿÿÿÿÿÿþûùûýþÿÿÿÿþÿÿýúôïðñøÿöíóùüÿÿÿÿÿÿÿÿÿÿÿýúôà}g¡ÚáæèÕœ°æöÿÿÿÿÿÿÿÿÿýüòæÆ©•}T7'%%%$##""%###"%&%####%/=OchlS?<]hsplYE.'%&*>[ne[PF=0+'%$$%&&&(''''''((((('&&&'&&%%%&%%%$#""!"%-=2.,+$$&(*,3FFG=3/+'"#19?;;:6(1P^``aca``e[TORXaaccbab^__bbcchca^\]ccddfegehghdecedddcbgcdeedhcjfhhjjkilkkjjkkkkjjhjihhhhhghiihkjjillmjjjkimmmllkonpnrmmhgZPGB@>=?>>>>>??>>???@?>@@A@@??>>?A?DBCAABCBFDBBBBBAAA@@@@CAAABBBCCCBB}|{}wxxxwvvsrrqpmmmmlnnnopqolllnoppqnnmnnpqrrwyz{~‚}~€~~~}}||}}}|yxqknhpvw~|{vrwqw|ƒ‚‚ƒ‚………‚†‡ˆ…‡ƒ‚uj]_dn{¡Ä”’‚m`]_gnpsuy†š®¤¢œš¢¦§Íº§™’—žÂݾ‡qifRNKOSUXami`UUwÇÒÐË»½¡}ZCDHEAN_nЇ}‡‘‰Ììþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüú÷ùúüÿüöË€}Ž›Ãîÿÿÿÿÿþüúøùúýÿÿÿþþþÿÿÿþýûùüýíàìøüÿÿÿÿÿÿÿÿÿÿÿÿÿùè’m»Ùð÷íºb`s³ïÿÿÿÿÿÿÿÿþýüòåĪ™€Q5'&&$$$##"##$%)-/0($##,:Odcqk`=8K^bqnoL2'%%)6VMI@72,)&&%%%%%&%&'&&'('''((''(&&&&%&%%&&*/*%$##$+3:9;@0&-=1***-1/-' #/:>:;>8'7W^`aba__[WPRQYa`aa``_a^``aac__^]\]^cddcfdgcededcbdfffdcgddeededfdfgiihgigghjjkjjihgiefefegfghiihfjijikjjkjikklllmmmnllljhgbZRMGC@?==<<=?>>???>?>>???>@A?>??A>@@BAABBBDDDCBCBAA@@???@@@AAAACCBBB‚}~|||}~xwrppqpporpoprnoopomkknqoqprnmnpqswz|†„‚ƒ„‚ƒ‚~„}|{{{{{zxtmgnw{~z{vqmjqvy€ˆƒƒ€‚†„„ƒˆˆ‰‰Š„„|ye][`t—“‘•»‘zpcc`bmlnsy‚£ª¢–•š¤¾Ê¯¢˜“—·ÏÝ™‹‰‰[OMS`klmt|k[Vy¼ÝÕËÄæ“‚iVURKJP^n|‡ˆˆ‘“Ž»ëýþþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿþýúøùúüþùñË’††Žœ«Ö÷ûÿÿÿýûúùùúüÿÿÿýüüüþÿÿþýüýúáÉÖéñ÷ûÿÿÿÿÿÿÿÿÿÿÿüñ·wŠºÑïè¤aQn’ìúöò÷ýþÿþýüûñäĬ—‚N4&&%$$%#$###%,5DM<,$#%0Ide^inQ96>HOitnT2($%(.584,)(&$%&%'%%%%%'&&&''''''''(('&&&&'%%&).10+(%#(0:AEKC6'/88($##"!".7==<=</>X]ada___TMNVZbafa`_baa`ecdce^\]]]cchefcfffffggcdddeeeidgejhlefelddehhggkjojnklllgggiiifgfhgjikjjfkjkinkkkliolmlklmloopmjjlig^ZRHC@>>=>??>>>>>>>>??>>>B@?>A@B@C?BBAAABBBCBAABAB@B???@@@ABAABBBDB}|||}}~||wrqppoqppoqqqsqnopplkknooooopopqsv{ƒƒ‚‚‚€€€€€}~}{{|zytoqorzy|wusnols{|‚‚€€†‚ƒƒˆ‡‰‡ˆ„…~{ne[\[|¨†~´˜—–rbadhkmqtx† ¡¢Ÿ›–’¸··¯¥“¡¯¯§±ÁœgTNXpv~uxz|fYr°ÊàÑËĪ—•wZcfTX]py|xˆ“‘’²éüýþþþþÿÿÿÿþÿÿÿÿÿÿÿÿþýûùùùûýöíʤ‰‰—žÅî÷ÿÿÿüùúúùùüÿÿÿüúúùüÿÿÿÿÿýúÞ̸ÀÑë÷ÿÿÿÿÿÿÿÿÿÿÿÿúä¥n‡¢àܬifyŽÜòéâíøýÿþýûùðãÄ«–€K2&%%%%%#$$$%+6JbWI/%%'8\deeiXD86=BQ`jmJ0'$%%),+)('&%%%%%%%%%%&&%&&'&'(()('())())'&&&*-:F81.*+,9AEPFC0&+8-%!'3;;==9+BZ^c`_^]SJPU[_`a`^^]]^badbba_\\^^^abccfbfdccddddddeeecccedigfeeddcdcighgjiihhiihgeeeffhggffgihkjjgihjjmkjjkijjkkkkjjiiklkkjigda`VMGA@>>?>>>@>>>>??@>>?@?>>>????@??@@AAAACAAAB@??????AA@@ABAAAABA€}|€{xusqsqqruqrrrssrpnnopnrsspoppqtwyzƒ„„„‚†„‰€€ƒ€€ƒƒ‰}~}}|yworz|}zxwtsnqv|€~„‚ƒ‚…ƒ‚†„†…ˆˆ‰‰Œ„†~xrcZY`ily¯£¡–tjjdakmptx„Œ¶¨œ“›£¬¾·°•ŸªÆÅÀ†_V\q€|yz|~pfl¦¿ÏÒÔÙ³‘‚ms‹„h[[lkvxˆ”•“èüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿþþüùúúüýõìÕ²š“¤ºººÈìöÿÿÿýúúúúúüþþÿýúùùüÿÿÿÿÿþýöèÆªÂæöÿýûýÿüúýÿÿÿÿÿõ⯎²Ê½ºn†˜çëÅ£ÉëöþþýûùðãÁ«”|G1'&%%%%%&*-+/@bgjF1%%)?dpliYJ>:AYWYemW=,&%%$%&&&&&&%%%%%%$&%&&(&&&%&&((((((*+/3.)''*.9IC=>@-+1KJIFE6)$#$$!!-:=?>7-I^da^^^RKNZ]caf``^^]_`bbgbc_^[\_aafcdcedgdccedeeefjfededghihfggbacghiikilihghhifgeeglfigjjkjoijiijkjonnmmlnkmkojkijjkijlmlkijkkgh[QHB?>>>>@???????@@A@B>====?>A@A??@CCD@CCDBB@??@>??AAA@AAAADBBB~}}|}{vurrrrrtttqrrrprqqqnnooooppqqrtx{~€‚‚‚‚‚€€€€€~€€€|~~|{wwwwwvvvwvvvvx{||€‚€€‚…„„…††ˆˆŒ…„€}voe[\\cs¥«¥¥™„„¡magnqrvxŽ¢·©›‘Œ’𮯵©£™™›ÉÝÆ®ˆeelooqru€vjk›´Îý¾»¥„qt–‹o`gpuv‰”–“©èüýýýþþþÿÿÿÿÿÿÿÿÿÿÿþÿÿüúûûüýóéÛÅ ž±¶»ÂÊêõÿÿÿýúúúûûüüþÿýúùøûÿÿÿÿÿÿÿýøèǺçôþúöûÿúôúÿÿÿÿÿþøëãß˦º~m¬Ãç覓¥Åïüýþüúð㿨’vF2))(%$')/5649HfugC/%&+AjptePF?ADRllnXE4'%%%$%&%%%%&%%$%&$$%&%%%&&&&''''(((-57;93+'-4<JJDA?7/*667660+%!"! '7=??51N`^^\ZRKQV[]]^]\]^^]_abbbb_\[Z]_``cbccdcgccbcaeceffgfbdehhikfbbbddefijjjlhgfffheffgjhfihihiikijjkkkjlkmgiinkmijhkjjjiijmnjiiiihhgc\TMEB>???????>>>???>>=>@>===??@@????@@ABBAAA@@?=>>??A@BAA@AAAC~~}yxyxvtw||{zyyyqrtuuutwpmmposqqqqvx{€†‚ƒ‚‚ƒ„„ƒ†……‚„€‚€‚}~wvvvuy{~}}{{}|†ƒ…~ƒ†ƒ†„Ї‰†‰‰Ž„ƒ‚ƒ}xuf\W[l˜¢º°º¢™œ}hcdqputvƒ”©µ¡•Œƒ ®»¼¼©œ›ÐÏÏɽ“|refcelz‡okƒ¥ŸžÑ²©‡zwŒ•‰nabqr‹”—“¥æûüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûüýýþóéåç§¡¸âɲÑçóÿÿÿýûûüüüüüþÿýûùøúüþÿÿÿÿÿþýöïñóöùöôôôðíóùüÿÿÿÿþùõïÓ¨³uj•Þìé¶ˆŠ›æùûþüúðâ¾§’oH40/'#%,4:EVB;Kjvg@-$$+CjxuaRGIOOQZbSC5*%$%%%%%%%%&$$%$$$$$%%%%%%&&&)+)''),18<CE7-,3?JKKHF:1*(.5.*(&%!!" !5>A;35T^_]XPLP[]^^]\[[]]]^ccccb_\[\]a__`dbbbebhcccffedefggfchfkhhhffghhgjimmmmmhffeehfihhgffhhljlinmqmqkkjmkmikjnklillljiihijnokmllllllhkb\OFA@@??@@A@@@@@B??>B@@?>>??C@???@@ABBBAA@@@>?@@??AABBBAABAA{zzxyzzy{||yxxwuursutttrqlmlnopnsuy€~€‚€ƒ€€ƒƒ†ƒ‚€€}~~~{zwvvvuy|}~~||{}|‚„€€ƒ„ƒ„………††ˆ…†ƒ‚‚‚€~zth\[^x¥°¼¼½¾‘kbbjsttvx†™ž¦ š†‡—¨¾ÂĨ™ §µÞÍ̧€g[]_afproox…’§œ—“‰Œy}Šš€l\fj}••“¡åúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûýÿþþóèé쨟Áéäá×ßñÿÿÿþüüýýüüüýÿþüúøùùüÿÿÿÿÿÿÿþýüû÷óòñíÖ¸Ýíóùÿÿÿÿÿÿÿöå’xir…ãïëÍ¥~Ñöùýüúðã¼£‘qL=50)'(7JLPPPDLjvf?-$&+AjutfY\cF8<C?;2)'$$&%%%%%&%%%%$$$$$$$$$%%%&&,31.+).06<CNG;0.;JJOKH;1+&&&&%%%$#! " """ +=?62:V\[[WNRV[^]Z^[[[\\^_a`^]]ZZZ\^a````ab```bbceedddeedbcbgggggggfhhhhjjjjjihgfdedededefgghfhhhijklkjjjjjjjhigijlhiijjhghijnljjihffggffg`]SIFBBA@@@@??@@A@>>???A>?>??@???BAA@@AA@@AB@?@@??@@AAAABBB@|zzzz{}~}|€zyvuuvvvv{uuonllmnruw}€‚€ƒƒƒ„‚‚‡‡‡„ƒ€€}}~‚„~{yyxwuvxz€…„€‚ƒ…†‡‚„„‰„„„„…†‡ˆ„†‚…†‡„„„„~zf\Za{Œ©Ê²¡ª…ghderqww{…Ž¡¹šˆˆˆ‹˜²ÑƲ¢—”™·Õ×Öšq`X[[]gxiddi¤ˆnryuxƒ•¤•x]]`s’Ô÷ûþþÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþýþÿÿþòæèꙌÂîíìâÛîûýÿþþþþþþýýþÿÿþûùùùüÿÿÿÿÿÿÿÿþþýùöòîìηÏìöúÿÿÿÿÿÿÿúé¨cNFwàæèÏЗçúûýûúï亡ŽsN@;90+2CSYSeL@Ikvf@-&'/=[utrfhM:0,--)&$$$$&%$%&&%%&&%$$$$$#$$$$%%&*1;<50259=AILT>3/JIJ@830+&$$$##$##"! ! #(,.1/+%%;?21?Y\\ZWXZ[^\\Y^[^]accbe`^\[Z\[\`cceabaa`caddeefddeecbccdggfgighhllljlkljjhgfeehegefceglhieighhijokkimmmiiiihijlhlinighmjjkmhiijdd_\_aggdbVLGCA@@CA@@?@A@>?A???>>>???A>?@@@?@A@@@BBA@@@@@@@D@@ADCEA}|}~~~~{|zzxvuxzvusrplllqrux{€€€€‚€€€‚‚‚‚‚~}|}}~€|xwuvuyy{}}|~€€€}€ƒƒ„……ƒ„„ƒƒ„ƒ~€€„…‡…‡‰ˆƒ}si^__w¥¯±«²´œpfcaipvyy|‡™›œ—³–‚‹³È¾²¢’—©Áƹ—ogkaYi}ubchv”«e_fkiex‘›¥†dZQk”›Ïôúÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñäæãˆ‰Åóõöêßë÷ûÿÿÿÿÿÿÿþþÿÿÿÿýúúùüÿÿÿÿÿÿÿÿÿÿÿüùòêëिèöûÿÿÿÿÿÿÿýöÓ€EPpÚØÕÏ˺ÈðþýýûùïḞ‹jNBA>766Ee\RMHAEgvhC3+37?PashhN9.&'(&$$$$$%&%$%&&$%$$$$$$%$$%%%%'(,1<KB>@BFJJIGEB?//4952.+)&%$$%$#%!!!! "',/26===6.$ 90)2G[]][ZY[\[ZZY^[]]`aa`_]\[[[]]_aabb`b```a`dcddddddbacceffggfihijjikikmljiggffegcfegefhhfgfffhhiihgggiihhhffegjihhhighihhhgfedca\^[_behgeccYQLECAB?@A@@@@@@@?>>>>>>?>?>?>@B?@@@@@@@@?@@@@@@@@@@BBBA€…†‡ƒ†€~|}{zw|xurqonprv{~€‚ƒ„ˆƒ‚‚‚ƒ‡ƒƒ€€ƒƒƒ|wss}{{ƒ}{zxwutuy||}}~€‚€‚ƒ„ƒƒ„‡„…‚‚‚†‚‚|{~}}€„‡‹‹‘‘‘‹Š|j`\b–œµÈ¤vlkdfuswy~ˆ““–§¢‹ŽµÐÀ¸œˆ¤Æ»Ê”uogju{vdfs«zb^]\\]m‰™ž tYO^…ž™™¾ìõþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýðâæß•vÈóòñæÎâóùþÿÿÿÿýûôíîðõúúúúúüþÿÿÿÿÿÿÿÿÿÿþüøôòéÀ´Îéôüùöûÿÿÿþýä±€cœáÙÆÌßëïöþýýûùîÜ´›ˆdMDEI>99GZaOI?:BcmiM:;DDCEMRWG6*"$(&$$$$%%%%%%%%%$%$$$$$#$$%&'(*,/4?NHEGHJRIFFG?>3,+-,*(&&%%$$$$$#$!!!! +6;=@@@IDD9.!))%4O^`a\[Z][ZZ[Z^^aagac_^\]]]`baeaaababafbcbecbdccidbdfdihhgijiimkjimijknhhhighfhdfglfiggfhhlhjilhffjikhigkghffijjihihjfggfefd_]]^ceghnmlljiiaYOIDB@@@A@@@A@AAA?@>>>C?@???BA?@A@A@BAC?@@@@A@A@@@BACAƒ„„„…‚‚}}|||wtuvspqqsuz~€‚ƒƒ…ƒƒ‚‚ƒ‚‚|}}~}‚~|yuvmt{zyyyyxy{xwy|~}}}~€‚ƒ‚‚‚€‚ƒƒƒ‚€€€}~€~{€…‰ŠŒŽ‘Š…€vlaacs¢º¸·Ÿ–‚sjdjpvyz}„†’¬«—ŒŠž²Æ¾«ŒŠˆš¹»¿soonoi`o~„Š‚zpib\[Zhz£ƒ`DXw˜˜š§æñþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüîàæäµÌôðìâÉÚïöýþÿÿÿû÷èÙÉÞêõøûûûüýþÿÿÿÿÿÿÿÿÿÿÿÿþùñѺ²Çê÷òíöÿÿÿÿÿùßÈÌÚìçÐÊßòûüþýýûøí×°›„cJCHCA;:CYSG@78=`sk[RLWgE7551,'!#%&$#$#$%%$%%%%&$$$%&%%&&),,,37:>BLJFHNJJHGB>83/)('('''&%%%$$$"""!!! 46===?ADFD?6.%"#$7V]`^\ZWWWYZZ[^_````_]^[\]`bbbe```a`aaccccdbcfdcdefhfdhfhhhkjijjjiiiihhggfgeddddfikefggfghkgkjlhgghghgidjggggggffggfecdcbb]Y]\afgghhnlkiklkjc\SIFBA@?????>@@@A?=>>???@>>??????@@@@@?@?@@@@@AA@AAA@‹ŒŒ„†‚‚ƒƒƒ{xwutrrsw{ƒƒ„ƒ‰‡†‡Š…‡„„‚„€€|}|||||zvsuuwzywxxx{zyz{zz|€‚~€‚…„ƒ‚„ˆ„††‡‚€…††~‚†ŒŒŽ‘‰‰€{kb[_~§¦»¹º©ž”|pffstxz|ˆ£Á˜‘†¸Õ¿¸œ‰‚•¯±¢vtpifab„‡…€~}wm`]Z_m‡˜¨‘hAIi“™Ÿ¡ãïþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúàÎ×Þµ¾ÞöðêáÆÖîöþÿÿÿÿöêÀ“”œ¿æìò÷ýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúæÈ»«áëßÙôÿÿÿÿÿüùôïóøïßæêóüýþýýúøíÓ°›‡bIA=;=;;?JOE=639\r{nfjmZJ/%! !#$&%$%##%%%%%%%&%%$%&'*.19869?BMMKMMMNKH?;5.-,*('(''&%&'%$%$$%"""!!!!! +8=A@AACDE@@:6+$ !$:X]^]ZXXXX[\``dcb`_^^^^]cbhdfcda````abddedibddfffhmjkihhmhhijiiijkkhggifdfgdcdffgikfiijiijkglllikijghfiijiihhgggggkeddeda\X[acjijkkkpmmkonppohcTJDB@????@???@@>????@????@???@?C@@@@@B@?@B@ABBAAAA@І‡„…~}{{yysvuxz}ƒ„…†††…ˆ‹‡†‡……ƒƒƒƒ~}}}||{zywuvyzzwvwxxy{|{|}||€}€ƒ€ƒƒ‚ƒ„‚‚€‚€~~‚‚…„„„†ˆˆ‰ŒŒŒŒŠ…wl`_av…¿¾¿Â”ngcktxyzz“•’¤…“¦·Â¹´˜|xˆ™yr}labd€ˆ‹Ž‡ƒ‚zjgigg|›¦xEIZ‹››×îþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôƼÁ¹´Æí÷ðèÚÇÒíöÿÿÿÿÿðÇ–ƒ{’®ÅÜôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøäÓËÐʼ×ñþÿÿÿÿÿÿÿÿÿÿöíêëôüýþýýú÷ìаœ‰aI=47=<;>DDD;556UptzceTJ8($!#&(%"%&&&%%$%%%%%%%%%$$).389;DMLJJIKPQSA8894/,*)''''''&%%%%%%%%###"!! !+48;<=ACE@??;9, $&=ZZ\\XXWXY[]abaa_]\]][]_abdddccaaba`bcebccdbddfdghhfghgfffghkiihihgfedeffhecddfgghkfhiihikhgijiihfgfeeefhhhfghedeec`_^]Za[_bfhijlnlkmlmlnnooolgbWMHBA@???>???????@??????????@@@@@?@@BAAAAAAB@@@@@AŠŠŠ„…€~{z|yyy{„…†‰ŽŒ‹ŒŠˆ‡‰‡‰ƒ„„ƒ€}}~}€xywvv{trqruwy~{{~}}}€€ƒƒ„‚†‚‚……„ƒ€€‚‚ƒ„†Š‰Š‹ŠŽŒ’“””–‘Šˆ€yja\avŠ Ä¼Ãµ°…omfiwuywx†‹‘¡š‹Š—¦¹³«„yroƒ‹spuvkfm€ƒ…Š‘†‚ˆ‡qqrspz‹ž£ˆTBO‚š¤žËíþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøìº¬±¬£¨ÐïîíàÏÞïöýþÿüøÜ¯‰{‰œŸ£³ÁÞöúÿÿÿÿÿÿÿÿÿÿÿþýýþþÿûøëÞÑ··Ýëôøýþÿÿÿÿÿÿÿúôôóøýþþýýú÷ëͯž‰`H;56==>>@B>:5/2OduiaN?6+'$%(/82+)((*.1*%$%&&&&&&%%'.:CFJFUKMIDGJD?93/-+)('&&&&&%%%$$$$$$$$$"#"" !"##"$"##*05::?BFBEDC95#"!')BYZ[[ZZZ\\^_caa_^\[\````bbfdjeeabaabfdecbbcbddfdkhgghhjfefhhlhfgjhfdbcffggfffdgghglhhikhhhhfjkkhffgfeefghghgkgdceca__\Z[_bghjmrmpoqpqnoororpronjh[OGB@???@??A@??>????????>>>@@A@B@@@DBBAAAB@@@@@BA……ƒ€€|z{|~€…ŽŽŽŒŒ‹Ž‹‰‰ˆˆŒˆ†…ƒƒ‚„€}{{yxwruvvvqnmmsxz{}~€~~~}}~€‚„€€€‚ƒƒ‚‚‚‚€ƒ„†‡‡‡‰Š‹‰Œ‹‘ŽŽ‹Š…wmc`^p‡£·®®·€kffmvxxvvx|‹´¢’“ ²ˆ|uoigqvx{ooy‚†ƒ‚€€…uxІ~†‰“¢‘ePFn™žžÈìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõç›Í·‡ˆ£ÏíòêâéðöûýÿøéËŸƒŒ¸Ïµ£«Èæöÿÿÿÿÿÿÿÿÿÿÿýúûüþÿÿÿüöé;ÀÊÛòúýÿÿÿÿÿÿÿýüûûýþþþþýùöëÈ®Ÿ‡^F;77<?C>?<974--A^VQD60(((.39A><73326>:/)&(*-001,&'*9OMMCBBACF9433/,*''&&%%%%%%$$$$#######"!! !####%$####""!"#(,-.4<;:993.)"##',HYXYZZZ[[\_````]\\]]^]_```baaba`babcdccccccbdcdeghgggggeefhhgedcbafbcdfgfffeedgfgfffijjggefffggeeeefeefggghffccbba\X`\^`dhijkmomoopoooonpmroppomjg]TLDA>????@@@@@@??@@?=?>>??>?>?@A@AABAA@A?@@@@@@‡‚€€€€~|‚†‘’““‘‘Œˆ‹‡††‡„ƒ‚‚„~|{wuvuswsrmknorx{~~~ƒ‚…~„€‚„‚€€€€‚‡…ˆ„ƒƒƒƒŠŠŠŠ‡ˆŠ‹‹‹Ž‘ŽŽŽ•ŠŠ‚}nbZ^u…›¢³¾§°ymojjurtssu}™°ª—ŒŒ‘ °‰‰†smos{{qoxƒ†…‚}ƒ}€¡‘—’‘šœyWGa—ª ÆìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÓ|€§ÂŠk}©ÓìêéìðñóùÿòÞ»œš¶ÞÓͯŸ´ÛëúýÿÿÿÿÿÿÿÿÿüùùúüÿÿÿýüöðÚ½ÆÄëóôôúÿÿþÿÿþýýýþÿÿþþýùõêÆŸˆ]F=88;C@><8860*(09<8.*%$&+8?NLKHC@<=?BM7.-/7@A@?2'(2EJKA9<@620,*))'&$%$$$%$%$$$$##%######! !!""%$$$$$$$$$$###"!"%&'*/220,(&""#$'0MXXZ__``ecc`aa`]`\]^a^gbdcc`ba``cafdedgccccdhcfdffgggggcdfmhfcb`^`ebdfkhifgfifgfggggjjogmegffghdegjefefgjglfea``a]XZ_`fgijoknnrmooqpuopooprprtwrqmmcYMEA@????@A@?@??A@??@?@?A????@CAAACABBB@@??@@@~€€€~€€ƒ†‹‘““‘“‘ŒŒˆ†‚ƒƒ‚‚ƒ~~}{|vwysqonriotuwy|}~~}~~€€€ƒ€€€€‚€~€€‚ƒƒ„…„„„…†‡ŠŠŒˆˆŒŠŠ‹‹ŒŠŒŒŽŽŽŽ‹‡‚|na_^p’£¹¾¯«„w‹vgkuvsrsy…˜¥«ž‹Œ‰°—~}€„ˆ…‡xhksƒƒtuy€~ƒ’›••“”–iC[’¬ŸÎìýþÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþí©ioŒ˜fW·åëïïïãæõÿìIJ±±¯Èȱ¶Ãª™±åõúÿÿÿÿÿÿÿÿþú÷÷øûÿÿÿÿÿÿþõÛÊÉÝê×Ýôÿÿþÿÿÿÿÿÿÿÿÿþþýøó鯬Ÿˆ^F>8559>:82.+)$ %)))'$#!'.8ELTQQMIHHECB==<?KPUST4)/9FE=41.,*(''&&&%%$%$$$$$###"""#"""""!!! !""$##$$$$%&%%$$$$$%%$$$%$%%&'&%%$$#$$)5OWYZ``aaa`a``^^\\]_`a^^^____`baacafcdddcccddccddgfhhheb_effgfa]X_bbcfghggfggggffhgijigohgehbcdgcehiegfggjgfbaa^\_[]^acgjjkkkllllomnnnnpopqqqstvrqnnic[RHD@@>??AA@>????>>???>?>??@@AAAAAAAAA@?>>@?A€…‚‚ƒ†‰—‘‘‘•”˜‘‘‘‹‹Œ‡„‚€€ƒ}|||z}}|tqkhoqu{y~z{|€~€}~}}~€€€‚‚‚€€€ƒ„…ƒƒƒ„…Š……†ˆ‹‹Œˆˆ‰‹‹ŒŒŒŒ‘ŒŒ‘Ž‘‘‘„|lbZ^}¢¤Â¾ÄŠt‹‰ogiuqrsqw~¢³½‰†…†”±¶™‘’‘‰…jbn~„„jkq…’£™›˜”“¡™†VV„¯ ×îýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýê\Yrc^j¦ÔçïêææÜðÿí·Êá˵¿Î¨§ÆÇª›Äèóÿÿÿÿÿÿþþýú÷÷÷ûþÿÿÿÿÿÿúöçÞæéÌÉíöøûýÿÿÿÿÿÿÿÿþþýøóèÆ«ž†^G?910036/+&" !$##$%*29AKOUWVUTKMEBDEGKM]PME635@G90,(&&%%$$$$%##$$$$##"""!!!"!!!!!! !!"$#%%%%%%%%&&$%&%%$%%$$$%%##""""##$$%%$+:QZ\\aceefcd`_``]_`cbb`a^^_a``bcccbfffeeefffccccehhjjjdaaggghi_Z\`deejiihhghhjgfehimjigohffhbbbfdegiffgihjfe`^][\_adceglkoopkjkqpppppqpoprqwtttwsuqsonhfUJB@?B?ABA?>?@@A>A??@@??@CABAAABAAB@@?>>>?@€…†ˆŒŽ‘‘ŽŠŠ‰‰†ƒ~‚€~}}~€|{{zxurokrjqzz{z{{z{||{{{|yyz|}}}~}€ƒ…„ƒ‚‚‰…†††‡Š‡ˆ‡ˆ†‹…††††ˆ‰‹‹‹‹‘‘ŽŽˆ€xnaabn~™ÃމŒˆqhflpturrv}“¹¹²˜†ƒ²¯«£˜˜›žž‡qihrunadi„ŠŽ’˜¡˜Œ’Ÿ‰hpw”¡æðýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ãwRT\oxsoŠ ÊãîåÄœ¹ëÿîÞæêÛ±´¼¨¼áÜ»«ªÈìÿÿÿÿÿþýýüùö÷÷úýþÿÿÿÿÿÿÿûöòì¸Èâêò÷ûÿÿÿÿÿÿÿÿþþþøóèŪ…]H?90'(*('$$)**+,379<BIRbYZSMLEHLLLMLGA=:1-7L9.*'&%%$$#$&$$##$$$###!! !!"###%%#%%$$%'%%%%$$%&%$$%%&%%%$$$$$$$$&$%%$-AU\\]beffeba```__`````__^^_a``bbbcbcdeeeafffbbcdehhijhdccgihif\`dcddegfjggfihjggdgimkigojgfdbbbgfedihggigfcc[YW\^abcdgjjklkkhjillljmmmlnrqrrstsrqrprrnkhaVJFAC?@@@@AA@@A>@@@@@?@@@@A?@@BAAA@@?>>??@‡ŠŽ‘‘“‘‘•ŽŽŽ‹‹‹‰‡„‚‚ƒ€~}~|}xwsnmemsy~„~||y{{}||{{xwy{}€~€‚‚‚‚€„†…†ƒ‚ƒ…‚‰…†‡‡†‰ŠŠ‡‰†…‡„„†Š‰‹Š“•–’–Žˆ„€pf_cv—•œy”ªŽwpllqprqotƒ Á³¤–†„£Çº½›œ¬¼Ÿ‹wknunc^cj|††“”—ž‹‹”¢}gnšžáòýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúï¯dQN[h…”¡¿ÅËäéÔ¾“¢èýòâèë˪ªÄ¥ÆÖäÓÄ¿Þ÷ûÿÿÿþüüüù÷÷÷ùüýÿÿÿÿÿÿÿýûøôéÕÍÅßòùÿÿÿÿÿÿÿÿÿþþøóèé~[G=7.&!!"$" +2877:>;78;AHOTVQMVFVGEED@<5/,)+=8-($$%%$$%$$$$$$$$###"! !""#$$$$$%%%%%%%%%%%$%$$$%%$%%%&%%$%$$#$$$$&$$&$/GY^baffgedbbcfcdccceaa`___`bbabhbcccdgfdbefgggeienikiggkkliigcahijfhfhfjgfghijkljjjmmmlnkhfddcdgfefkihimgcccXVY^bgghfmlqlmklijkoookmnrmmorrrrspoqrsrqqnojhYLEBA@@@@@AB@A@C@?@@@A@?@@?BAAAAAA@??A@?@”’’’’’’’‘’“’‰‰ˆ†„†ƒ††…ƒƒƒ|{zyxsomjnosxz|~€ƒ~}||y{{|}|{{yz{||}}}{{|€~€€‚‚‚€€‚‚†…ƒ‚ƒ‚…„……„„‰‰‰‡†„„……„††ˆ‰Šˆ‹‹““•“’‘ŽŠ…~tifauŠ«¹}¦§upplqrpnny†œµ³¶›ˆ‚‘¦¸¨™¾«¢Šqmroe[`kw~†w”Ž“‡ˆ‘ke|œäóþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøè`T]gˆµÌÅàÞàæ½‡åûöòðìÀ¡”—®¼ÕãçäÃÒðøÿÿÿýûûüú÷÷÷ùúýÿÿÿÿÿÿÿÿÿþüöäÈÅÏìöÿÿÿÿÿÿÿÿÿÿþøòèç›wT?:60(%"%'%!%6FEDFGEC;5<<CGSVNMIE@:751-+('&'(*,($$$$$$#$%#""""!"""""" !"#$$#%$$$&%%%$%$%%%%%%%%%%%&%$%$%%%%&%%%$$$#%&%$%%4N]`abgedccbbccddcdddccba``aaaabebcddbbbdbeedcddeefgkihhhhiiigedhiideefeffgjhkkiihjjllkjkiggfeedefgghihhgc_[ZW[^acfghflillkijijkmmljlmmmmnqqqnomopqqpoooonkgZOICB@ABABA@??@@??@@@A????@AA@@@@@???AA@™™¡˜˜“••˜’”‘‘‰†ƒ…‰ˆˆ†‰„ƒ|ywtokjgpz{ƒ„†€‚ƒ~||||}{{~}|~}|}|{{|}…„ƒ‚†€…‚………„„‚†„…„‡„Šˆ‰‡…„„…ˆˆ‰‡‰‹ˆŽ‹’“˜–™•—’‰…wnad|ž“}“Ѽ³³°kholnmpx†ž¸¶¯˜‚…Žš§›”Œ„}}rnptlY]mvzƒuy‰Ž––›ƒyspcm£æõþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôà~_cp“Á®Ä»²Äßßâ¬w}Äòóóðê¾—’}ƒ¡¼ÌáÊÉÈäòÿÿÿýûûûùøøøùùûýþÿÿÿÿÿÿÿÿþûøòìïòùÿÿÿÿÿÿÿÿÿÿþøòéÀ¥™oM>9870**-,+#.HINRYYZL@@?@CGOLGFB;5/+)'&$##$$$%$#"$$$$$###"""!!!!!! !!!"""$$##$$$$$%%%%%%%&$$%%%&%'$%%%&&%%%%%&%%%%%&&%&%%%&9Taccbgcbbbbdefefehffffcc`bbdbeciccdfaaadcfedccdeeihjijjlkkjmjjijhgehiieeejhgimigimlllqjkhggjedcffhigghhi`ZXZ\cdfeijkilhkklkllllollkonnmonqqunplrqsqqqqswsrmj^RIDBA@??@@@?CAB?@@AA??@??@BA@@@@??>?@@•–––—“””“ŽŽŒŒ‰ˆ††‡‰Š‰‡…€|wspnhotx}}~€€~€|}}z|}}}}||{~||z{{~~~~€~„…„…ƒƒƒ†………†…†‡‰‰‰ˆˆˆˆ‡‰‰‘’”–•––—–”ˆ‚wkfbtƒŒÏÑ®©¤Ÿnehqqnoq|е«±†|}‚‚„…r`r†sfs€t^]tlhlrs{‚ˆ’—‡}lge{¯ëöþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð»qkw”ƹ¯š–ªÖàã˜sYo¡æïôðêÀ¢–hxˆ• ¢¡¢¡½ìÿÿÿýúúúùøøøøøùúýÿÿÿÿÿÿÿÿÿÿþú÷÷÷ûÿÿÿÿÿÿÿÿÿÿÿøñä¾£“gG8;@<86310,'):KLRXXXXTA<?PHKB>>?90,''$$#####$%#"""#%#"##"""!!!! ! "#$$$#"%&$#$$%%%%$$%&%%%%%&&%&%%$%%%&&%%%%%%%%&%%&%%%%%&(?[bccba^aaaaeeeefchffcdccabccbcaccddeaa`aaa`abdeeeihkgjkkkkjkihghgfefffdefigghlghiiiijiihffffcedfghihfeb_ZZZ^adfffjmkgkhkhhhjklmnllknlmmmlnoonnlqptrrrtuvutpmh_TMDB?@?@@@@@@@@A?B???A@??AA@@@@????@?“•—–—–—‘ŽŽŒŠˆˆ‰ˆ‰‹Š‰‡†„~{yplkls}€†††~}}}€ƒ€€}||}~~€|}{z{~|{{}|„‡€}€€‚}€€‚ƒ‚„ƒ…„‡€‚„„…†Šˆ‹ˆŠ‰ˆŠ‡‡ˆŠŒ‘‘•”™™›œœœœ“‰‚sj_bjm„ʾ¬¸Îªvpfflgjns|‡— ¬ˆ|xxz€Švccl…tqwk`zkelgjsx‚†¦Œ€wtz‹®èòüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþî¨kuƒ««‘|zÍÎÛ˜jUVàêðíéѳŸ‚pr|}‰—®–Õúýÿýüúùùøøøøøúûýÿÿÿÿÿÿÿÿÿÿÿýûûûýÿÿÿÿÿÿÿÿÿþþ÷ðâ½¢aA:>@A994522*##5FPVYVTVYLA=?EF=56882+'$$#!###$###"!!"""""#"""" !! !""#"$#"#%#$%$$$%%%%$$$%%&&%%%&&&%%%%&%%%%%%%&&'&&&$%&%$%&%&+H]eeeb``ddgffefefchggffdddgdeeeddddedacaa_]_abgghfpjlimmljkjnilhkgghgfffffjhhhkhijkiijlihgeffdeefgljkhea\Z^`feffhinmlllikgkjijklsnpoopqmqnoonnnorptsvt{wwuxtsqpeWLEA@@B@AA@@A@A@B?@A@@??AAA@B??>?@@?“••–“ŽŽŽ‹‹Š‰ˆˆ‡†‡‰‡„ƒƒ~|{vuylsx~‚„†………€}}€~|{||~}~}}|{xyz|{|y}|}~}|}|~}‚€€€ƒ€€€€„„…†ˆ‡‡‡‡†ˆˆˆ‡Š‹Ž’’’“™™š››šœ–“†{sie_gz¥¿Ç½´qgcflmmpu}„’¨Œyvx}rhcn„slt‚ƒwu…}ofaiqu|€…Š‹ŠŒ‹¨ÐîùûýþþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþìždoŒ“†sliwŒ°Ç»¢yY[‡àæëéçåÑ´‘qq|mct‹|‹Çòúÿþýúøøùøøøøúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöîß¼¢Ž]A7<A=964340+#!+BTQaUPMJKD?9<>>72175.(&$$""#"""#""""!"!""""""!! !! !!"###$##$$$$#$&%$%&&&%%%%%%&&&&%%%&&%&&&$$%%%%%%%%%%&%%%%%%%%'.N_^_``abcdddddddfcdddcddeeeeecdeeeefeba_\[^`abgffgggjfliiikhjihhkfffgddceejdihihjjjjiihggjgfgeddghljiif^][addefgijmiiggggghfjjkjsnmkomnmonoonnprrqrsvuzwwvvuvurrdYPEC@A?A@AAA@@?A>A@???@@AA@@@?>?@@?““”’’’’””Žˆ…‚…ˆ‰ˆ‰ˆ†…‚}|zxqntx|‚‡††………„„…„ƒ~}}}}{{{|}€~~zywwy||}}}||~}}~~€€†‚‚„€€ƒƒ‡„„…ŠŒ†………†‡ˆŠŒŽ”••–™™›œœœ›œ˜™”ˆ†vn`ap‹ž¬··¹¤ˆvpffhfnrw|†”€ywwxwsnqtsqot~…~Ž—zi^gqux}„Œ”—’ »Úîñôùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþí™`kx~ypbZeo|¥Å¶¢Š`a‚ÕÖáæëêèÓ´Œrka^arwpo¤äôÿÿþûøøùùøùùúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõìÚº¢]A6:::97544/*#!%0XVRQMIDDF@=;;>?82361*%$$$##"""!""!!!!!!!!! !!!""#"$$$$$$&%&$$$$$$%&%%&'&&%%%&&&&%%%%%&'''&&$$%%%%%'%%%%%&%%%%%%%'2S_a___adhhhhieecfcedecdehgfefdgedeifeb`ZV[dcedgfjgjjjkljjjlhkkkijehghgfeigjjkjnmmjqkigfefhhfghgfmkmjjggaabhfggghkjmjihhhhihijkmnunnmpnpmsqrrqptturst{zzwywywtturqh`OFBA@@ABAAAB@AAA??>?@AA@??@????@?“Ž‘‘‘“”•ŽŠ‚ƒƒƒƒ‚€€{wurwnv|€„„……„………„ƒ‚€}}z}|z{{{zyyzxxvxz||{z{|}~~}~|}~€}€‚„‚„ƒ…………†‡ˆ……†ˆ‰Š‹ŒŽ‘“”••—–—•˜˜œ››™˜—•‰€vjeaqƒ˜º¹À’‡rjfegnptw‡„ywxuvytsturu~‹v}’™‡o]kwtr}„‘Œ‘Ÿ™’˜³ÈÉÍàõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíž[[edc]SXgcb¢Â±Ÿc`z¥µ¿ãîîìêÓ¦zf^X`ffdgzµîÿÿÿüøùùùùùùúúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýôêÖº¢—^@5;:;98774/)%"%37<>AA@=79><<<>@=885-'"#$$##"""""! !!"###%%%$$$&%%$$$$$$$$$$$$%%&%%%%%%%&%&%%%%%&&&&%$$%%%%%%%%%&&%%%$%%&&&&)6S____aceghfdgcfccadedddefgecdcddddffdaYQX``accgffghhhhhiiihfimjggeedeeefffeekjmjjjkgffeefdfeghgghhhhhffbdfggghhihikkjihilojjlmnqpnnnpnnntqrsrqtttstuxxxwwvzxvuusqle]SIDAABAAA@A@@@@@@@??@@A>?@?@A@@?“’“–”–“ƒ‡‚‚~~€}|{wtqtw|ˆŠŒŒŒ‡……†…†ƒƒ‚€}||}€}}z|{yyzyzz{{{}{zz{|}~}€~~~€€€‚ƒ……Š„„ƒ†‡††……‰†…ˆ’“””•’“•”˜•——˜˜™š›Ÿ ŸŸ—‘‹…vi_bs~š°¸Á©´{rgejituy~~|zxwttxxwvrpwˆ‹}t{•›umqolm~‰“‘“—–•ž¹ÀÊÎïüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿï¦YOSUSOOVfX[ŸÂ¯’e`p’ŒÅîëèæáe\]dgl_[l”éúüþûùùùùùùùúúûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóêØº£^@5::;;:::2.(("#&)+.9<;6127;==>B?=74*$""####! ! ! !""#$%&&%$%%%%%$%%%$$$$$$%##$%%%&'%$%&&'''&%%$%%%&&&%%%$$&%%%&%%%%%%%$$%%%%%%*:U^_adeffgfeegeebbceddehffeedhdedffgdd[SS_``bhdgeegjijgnmmhhhijihhehdddffhgiikkmjlkmgjegfhghiiihgjhjhihljiikioiikqhkkllllrnklnopprnpoporqustuvtxxysvuzy}x{xzx}xysqoogbTHB@@@AABB@A?@@@@?@C@B@@@??ABAA“ŽŽŒ‘——“’ŽŠ„‡€€}||}xxy~………ˆ‰ŠŠ‰‡‡‡‡…„ƒƒ€}}}}}|zyyyyyyxxzyzz|}~z{z{{~~~}~~~€‚€‚„†††„„„††…ƒƒ„Šˆˆ‰‘“”““••“•––•—™™™›œœ›œœ—’Š€tid_nƒ˜º³°›’vkddhquw|{{zyussvw}xqovƒŸmu—••pnkfazŽ”’Ž™˜š¡¨±¨›¸éùüþþÿÿÿÿÿÿÿÿÿÿÿÿÿð¾ZJIIIMQ]g\ZšÃª—d\_qr~«êèãÝÖµ‹h`^egaYVVxÚòùýûúúùùúùùúúúúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûóêÙ·¤Ÿ_@5:<:7662,)'$! .7<;4/1368::8754-&#""#"!!" ! !"###$%&&(&'&&%%%%%%%%$$$$$$$#$%%%%&&%%&&&&&&&%%%%%%%%%&%%%$%%%%%%%&%%%%%%%$%%%&,?Y^bdeggfgdgeeffadeeeeggdfeeddddeghhaZQRW`bbceeffggghjhjjigiijjighegacfffffijjjnjjjhdfeeehffddegfhggfggkjjiifmjkllhiilhlmqmmnmlnnnnponorquttuutxwwttuvvvv{wzyxwwtrppkfaVKFBBBAAA@@?@@@@@@@@A@@@??A@A@˜“’’’•›‘‘‰†…ˆ‡†‚‚~||~†‹Ž’ŒŽ‹ŠŠ‡ˆ‰‰„‚‚‚‚‚‚}{zzyxxxyyyyzyz|~‚{{{}}‚€€…ƒƒ€‚„†††…‰†…††††…„†ˆ‡ŒŒ’‘‘”•˜˜—”””•–•–šš››œ›¤ž Ÿœœ–”ˆƒuj_ar|–§«ÍÊË“tpeejirvz{yxtsstvy|soq{ƒgnˆ•’Ž|ofgw‰™Ž‹©˜›žœš™¤Îï÷þþþÿÿÿÿÿÿÿÿÿÿÿÿóÕcCABELUifb[–«¥“w^QVngd äêêâÖ´˜|fdea\VRSc¨èõûûûúúúúúúúûûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóéÖ´¤¢aB9<;9653.($!"08=74121221221.+(#!!!"!! "#$$%&'''&&&&&&&'&%%%%%%$$$$$$$$%%%&&%%%%&&&(&%%%%%%&&%%%%%%%%&%&%%%%%%%'&%%&&&%%%/E]bhhghighhhfffgagekegggeieecfghhigi_TPR\bafffeghjglijikjjiojmhffidgbchjffgiijknjjhffffgfhglgfglkkgfefglkljhillmlmililhqmqmpnnnsmporonossuvvtutyw|twvwvvu|y|yyxxsuuunomjZMEBAAA@@C?@@A@AAABB@A@>@BAA@š™”ŽŽ‹ˆ‰…†…†„„‚€„ŠŠŽ‹‰ˆˆ‡‡†††…‚ƒ‚‚€}zzzyxxxxyyyzz{|~~||}~~~~€€‚ƒƒ‚‚‚„†…„……†……ƒ…†‡…†ˆ‰ŠŒ’•••””‘““•––—™—š™š›žŸŸ —”Œˆwkf`m}ªÈ¶ÌŸ„vlddgqswxyxutsrtwwvshm}x_h‚Œ‹Š‡ztlvƒ†Š‘ŸŸ“’•˜•Š›¶ÖòþþþþÿÿÿÿþÿÿÿÿÿÿöçrI@BFOZm“t]‰«›ŒuVOPjgsçëðç»°¢’qj`[ROMY…ÞðúûüûúúúúûûûüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüóèÒ²£žcE;;9410/,&$ %2:62232/*%$$&(%$#"!"!!!! !""#$&&&&('('''(&&&%&&%%%%%$%$$%$%$&%%&&&$$$&'''&&&&%%%%&&&%%%%%%%%%%%%%%%&&''&%%%%%%&2L_ehkieiggffcffhbeegegeddecdddefgihg\NGU_bafddehjhgkiihhgiikijfecdbcbfiifghihjknijgfefegggggghiihhffefggehijmmmmklhjiiimmnlllnnmllmqooorssrrqtqttttuuwwwvxwwuvvtqstroqsmf[NHAA@@????@@BAAA@@?@@@@BBA@¤œšŽŠŠ‰‰‹‡ˆ‡†ƒƒ„ˆ‰Š‹ŒŽ‘‹‰‡ˆ‡ˆ‰†††„…ˆ‚„‚€€~{{z||}z~|{z{|~}€…€‚ƒ„€€ƒ‚‚…ƒ†…‡……„‡‰†……ŠŠŠ‹“’’•’š•–•––—“”•˜™™™šš ž¢Ÿ¦œœœ™–ˆzqaanxœÌ¸Ä¦Ž†|qfekkvwzwwtspquyvuicfnYax‘“œ„†wwy{ƒŽ›‘‹’‘‘†«ÇïüýþþþÿÿÿþþÿÿÿÿþùéœSDCJU\s•r©™†sMGLfks—Üãîß™©¦ˆxbZQMLPoÀëùûýûúúûûüüüüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòçβ¢šeG=;4/,-,*('#$3200133.% ""!!" !!!!! """"#$$$%(&&''''(&''''&&&&&&%&%%%&$$%&%$&&&&&&%'%'&&&%&&&&%&'(&'%&&%%%%%%%%$%%&'))(&%%%%%%'6UafgijfiggejhigiiigifgffggfgddefgihfRGLZ`bchfgehgggjijghiokpileeefeeeihhhmhihnnnijhgghfhgfhljkijjjhhenhgfgillmnnlmmmjlknnqpsomkklmnqqtqqrtrsquvvsxuvv{zyyzwyuwrrsstqquttrp^OEBAA?@?CA?AAAE@@@?ACAABB@—•‘ŒŠŠ†‡†„‚‚‚…†ˆŠŒŒ‹‹Š‰ˆˆˆˆˆˆ……†……„‚ƒ€€}}{{{|}{y}||}||}}€„€ƒ„‚‚€€„‚†„„ƒ„„‹†‰ˆ††‹‹‹ŒŒ’““‘‘’•‘“”•““””’“”š›œžž¥›Ÿ ›˜“Œ„ykc[j…¥¬³±»ÅvjbchruvwtrsoptvvtkcdjU\l‚’˜’ˆ‰†}kq{…‹‹‹Ž˜¤”†œ¼ìúüþþþÿÿÿþþþÿÿÿþýöØ€MJMS[|ÀŸu‚¨—lGFH\trŒ°ãèÔƒo¢È§•zdZROMN[šäöûþüùúûüýýüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòæÊ³£šeI>:0,(()+-+*%')((-/.-%!!! ! !!"""##$$$$$$%%&&&'''(((()(()))))(&%%%%%%$%%&&%&&'&%&%&%%%&&&(&&&&&('&'%&%%%$%%%%%%&),171.)&%%%%&'<]begggffeeefefegggeiffcfbffedfffgiiXEIW]`bdefgchfhhjiighijiiihedddafgghhhhhihhiiihhhhheggiiiiigggghgfnihghkkhmjjjjiijklmmnnnnmklmnoppqqqqqqrqrrstyuuttuwwwuusqnorqsqrsrtspj]PIBA@??@B@AAAAA@@@BBBB@AA˜ŒŒˆŠ„ƒƒ„ƒ‰Š‹’Ž‹Œ‘ŒŠŠ‹Š††…„†ƒ„„ƒƒ€~}}~}}{}|}~~}„ƒƒƒ„…†€~‚‚‚„‚†…‡‡‰ŠŠˆ‹‰‰‰ŒŒ’Ž“–’’’’‘‘‘•‘‘“˜“•”•’“•œ›œœžž ž¥¤¤¡¡ ¡—“Œ†rgZ\rŒ—³·¹Áº”xqeejiortqsqoruttndefTTaŒ›•‘Š–Ž…ojpv}ŠŒ—˜ š—xz“¦çøûÿþþþÿÿþþÿÿÿÿþýüå½rQMOZ‡»ÈŽ€‘–}dNDDVwq}ºÝ¼qf˜¨~dZYWRMSyÅñùþüûûüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûñåǶ§šdI=8/)&&(+030/&!&+./-&#""" "#"#$$$%&''&&%%&'''''''(((()(*+-/.+)'%$%&%%%%&&&&&&&&%%&&'(&&&&&&&'&%&%&'&&&%%$%%%%%&'-4<@E3,&'%%&')A^ceifhghejefeheifffifffgffeeejghij`SAL[c`edofgehhjikjlkkkkjpiiegdd`hhhgmhhhihjiiihhiihdiinjjjjfefijmfnijijjjgmiiilijkonmmmmnmmnnoppwqspppurrqqrwxyvvtutwwyuwqqlmoqqqqtrwttpo`QGB@??@@@ABBBAAAAAAAB@BAŽŒ‹ˆ‡†‰‚„ƒ„„‰‹ŒŒ‹‰‰‰ŒŒ‹Š‰‰ˆ‡†††††„ƒ‚‚~~~~|z~}~~~~|~„†ƒ‚‚€‚‚‚ƒ„„‚†…††‰††‡‡ˆ‰ŠŒŒ’Ž“’’’’‘‘‘’’“—’“’’“”•–˜š›œœžž ¢¡¡ž ˜”‡xoe_Zdr˜É½Ùº©’uibchosrqrrpqrssqiggVSVi}Œ•Š—‡pe^dm„Š’Ÿž“ƒry‰ŸÕ÷ûÿÿþþþÿÿÿÿÿÿÿÿþý÷ÐPGO^˜ØÏ˜ƒ‹‘~jVKBQwqjuŒ±¦og¢¯˜ƒzc[YZ[QKe¬éöýýýýüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúñ䯳“aH<7.'$'),024.+!%(+-/(%$#""""#$%%&(&'''''&&())('%''(((()*-05;:=3*(%%&%%%%&'&'''&(&%&(&&'&&'&&&&&&&&&%&&*,(&&&*-,-,)4@=<84*%%%%&'+F`ccfffffeeceefeedffgeffedfefegfhid[QCP^``cdeeedgghhjjlhjkigjifdcdc`degghfffihhggfffgfhehhjjjffeefiihgjiiijghghhiijjkkljmkllmlnpoppqvqpoopqppqsswwvtttttuutsplkkmmmmnoprrrssol^QJBA>@C?@@@@@AA@@AAA@@?“މ‡„‚†ŠŠŠ…‡Š‹Š‹’’’‘“ŽŠŠŒŠ‹ˆˆ†………ƒ„€€„~€€€†ƒ‚ƒ„…†…‡„…„„…††ŠŠ‰‡‰‡Œ’‘‘’’’’’’‘•“•—’““–“•–———•–—››¡›Ÿž¢¡¦¡¢¡¡™•Š|qg[[h€ ¹µµ¸½zrefhgmnopoopsvrkieXQP]n}ŠˆrbTWe|‚…‘¢ŒxitŠÙ÷ûÿþþþþÿÿÿÿÿÿÿþþþùèŠMEMmªÚÖ¨{}¥‰qeUHLhd^u§ˆp§¤•ƒub[Y`[XIV”àóüýþþýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúð䯱«‹^F;8+%#'+-131,)#!%()*('&%&%$%''''((&')'&&&&+12,)((())((*.4;?>IE:+(%%%%&'&&&&&'&&'''&&&&'&''(&%&%&&&&&&)-0,''*/6;81/=A742-(&&%%%'.K`ddfeddheefjjifeeffkghgfejgffhfhfeWNKV_ecffhiifggkillljjiifngfcbbcbedffijjfhhhhhggghfhgghljkfgeiiiiihmjmjihkhjilllkpkmmlkonqmoprrrrvqqnvrrrvu{w|vvstvwvwtuqplnkqmmmmmnoqssrqlpaSGCAA@???A??BA?@BAA?@@‰ˆ……„‡‰‹‹Šˆ‹Ž‘ŒŒ‰ŒŽŽŽ‘ŠŒŒŽ‡‹Œ‹‰ˆˆ‡ˆ†‚…ƒƒ€€€€€€€~~„…………ƒ†‚„†††…†‡‡‰‡‘‘‘‘‘–’’’””•––––”—˜™› ›œ››œœ››˜œš–‹‡ƒ{tjc\k|‘¯µ¤²œ‘ymcbfnqqrpoprvrnjf[RMRZn‚ƒ„‡€zweNOTclrz†ƒo^l‰—Þ÷ûÿþþþÿÿÿÿÿþþþþþÿûë‘LB[~¶ÚÔÉg|•~xfNM_a`WWov…Ÿ£’†vaWQTYPGOr½ïúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðãÆ¯ªƒYA9/&%%+//11/+&"$$((''(((&')))(((('''&'''',;3/+*-013/,05;C<;GM;,(%%%&&&)'''&''&''&&&&&&&''''%&&&),*().7@4)-06?@F60BL@74+'&&&&&)2Q`bbcdccdfeiiighfefffgfcfeghffhfeaWMOQZbddffghhegghglkjjiiifeefbb`bbabdfggjeedhfgfgeffghhfhhgefdighghhhiljjihfiiklljkjjjkjklmkqproqtqoonnpqrtttuvrwuvzxwvtpkkjlkllnnllmmnlnnmklj]RKCA?@A@@@@BB@@@@@?@?‡…ƒ†ˆ‹“Ž‘ŽŽ‘’“‘Ž‹‹‹ŽŒŒ‹‹ŒŠ‰Š‡ˆ‡††…ƒƒƒ‡‚†€‚‚ƒ…‚€€‚~€€‚ƒ…‚‰‡‡…‡ƒ†††††‡‡…‹‰‰ˆŽ‘–‘•’‘–•—–——›••—š—šŸŸ¡šœœ˜–—˜š‘‡…xn][fq•»¶Ñ±½ž…vefjgrqqooqutsngaTKKPcx…ƒŽzygIDIRZ_l}zg]fzŠËõùþþÿþþþÿþþþÿþþþÿúéKHeŽÃÛÝΩmi}‡ˆ„`OZt`LKYz ¯¸€w`PLKLLFH_§ëúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðâŬ¨zS=1*&&*20/01-*%#! !"#%&())))**))))(('(('''('''.79/--38??;23<IC98FM>+(&%&&&&'('''''&&*''&&&&&'&'&%%&'+13,,6HE=/28=?JI;1IKIB:+'&'&&&*6WagcddhdddeghgggffffgfeegedfffhedYOMS[ccdeiiiikghhjgmkjijiiihdfbeccbaacfiijffehgjgggijihmhjihgidifjjjikillojhhljnmmlniijmlllnnrprqqpoosnppsrvssuyxywyy|wuqljkjokklplllmmnlomkjlki`SHB@@@@@BAB@@@A@??A?††ˆ‹Ž“‘’ŽŽŽŽŽŽŠ‹‰Œ‹‰‹‹Šˆˆ††‡††…ƒƒ‚‚€‚‚‚€€€€€€€€€€‚„ƒ…††„‡‚‚‚‡„„„……ˆ‰‰‰ŒŽŽŽŽ‘””–”–—–“–˜˜™™™™™™••––’“”•—”’’’‘‘‹†€vh`XfŸ¼»¬¾Æ¦€mcbempoonorxxvmkYNMLXgs€†‡~tfI@?GNRVj~gWfmz´ðøþþÿþþþÿþýþÿþýþþùç‹LMm™ÎÝàÜ·|ns‚€xwf\h`LD_…¾°—‡zv^NDDDCADN‡åúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùïãé¦qM8,)'+/00//-*(%%%$"#&'''()*))*++,+***))'(''())((&/6:/238<<G?7:BH@97GK<)(&&&&''&''''''&&'''('&&('&&&%&'*5D:15=DE8.6?<9>E5,8FFO<,)&&&&%,;YbaacdedeeffhegjfdfeeeeedccdffhdYNQS\bbcefiiighhhhihjjihiiidgde`eea`cceghhhfeefgggggihiijhjdgjidighiigihmkmihhlklkkeeeghjjjknoonnklmnlmmpqrrsuuuvvvvxwxtpljhhikjkkkjlklmllmmlkllkh^RKBA@@AA@@???A@??@?‰Š–‘”“’’“‘‘“—“”ŽŽŽŒŒŠŒ‹‹ˆˆ‡†‡‡ˆ‡…„ƒ‚ƒƒ}}~~‚†€ƒ„…„…‡‡‡‚‚„‡‡ˆ„…†‡ˆ‰ˆ’‘ŽŽŽŽ‘’’‘’˜——•–•”–˜š ™ššš”–“““”“’’‘’•“˜“˜‡ƒugYZo„›¼¹ÙÂÈ™€vdfhmponnovzwrm]UUNOWas„…‡pcF<:@EHL]oifilqŸàòýþÿÿþþýüúûûüýýþ÷âMQs¢ÙßãàÚ£ro‡zz„||eNSkŸÍµ©€xu]LBAAB??EnÞ÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúïã¾§ hF3,*,2471//,*((+*+*()**,+*)**,041-+*)))'''')+,+**09925<C:;JM><FI=78GJ9)'&&''&&'('''''(''(('&&%&&&'&&&)0@FE49EM@60?A;7=7/,09EJ@,*&&&'%-@\addcdgfiggfifjhfggelghdfbcceei^ROW^jeeehfliihljkklkkkkllihhidddfc`bgflkkggefefggghhkiiimhkghijfihkjjhijnlmllillmikedcijmlqpoommmllmompnrrsrrtwwwy}{xzrljkhhilihhkjmkklomnnnmqoooocUICAA@A@@@B@A???@@Š‹‘Љˆ‰Š”‘“–”““‹‰ŽŒŽ’ŽŒ‹‹Œ‡ˆ†‡…†Š‡…††„…‚€€€€||z}}~|‚~{€€€€‚€‚‚„ƒ„…†‰ˆ„†‡‡ˆ‰‡ˆˆ‰‰ŽŽ‘Ž‘’’’“””‘—”•“–˜˜š›™˜——•”“•’’’“‘’“’‘’’–‘–‘І~tfa[dq“µÍù£–‚hacjppnlotz}um`ZXXNKT`o{s`SC;9BFIJWpnr}†~´ëýþÿÿþýüùö÷øúüýþõÇqKRt¨ÞáåäàÜ™ix’z’ŒymhÁ¸¨˜…zsi\J????<<>\ÀðúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúïṤ™a?3-./062/--+))(**))****+**)+.4;78.+*)**)'*+05692-7D;3:FA59IL@@GH;::GI7('&&''('&''''''(('''&&&&''((((',8BM>16IJ:74=C94413444CI8*(%&&'%0G\`a_bdeeiffdeeecdcccfefba_cbfeaYXW^ccdddefggjhkjkiiiihihhgfedbddebcdffkigdfffffgghhhhhjghhgfgggefghhhgjlmjjjigmkifdacdjiklnnplkjjhkmmnplqoooqswvxy{{{wslhfgfhihgfdggiijlljklnmnopppn`SLDB@@@??@????@@?‘Žƒ€ˆ‰Ž•”—“‘’’‹‰ŽŽ“‘“‘Ž‹Œˆ‰‡Š‡†††……„„ƒƒƒ…~~||}‚‚‚‚ƒ‚ƒ€€€‚„…Іˆ…‡‰‰‰‰ˆˆˆ‰‡Š‡ˆ‰“ŽŽŒ“—”••™”––——˜–œ—˜šœ••–—“‘‘”“˜’”’’“”‘“’–••“”‘‘‰…vnZXfz£×¼³«Ò pgbkponnnszz|oc^\\OEMWdqkWK?:;BHTQUbp}—’Ž‚œëûúúûüûúóíïòô÷ùüî¸_IRt¨àâæåäÜËŒq€ƒ{~˜–¦µ²²¢‘}rkdVH?>?><9:N›ç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùîÛ¶¢[>5--.0//-,+**)))))***++++*)-4>?@60,***,,+.29<B>:4AEC?AD@26IL@>IG77;IB4(''('''''''('''&''''&&&&'(,..,*/;KL:/4IH639GB51115>EA?=2)'&(&&%3N]`cbbcgfieideeeeeddcgfea``cdgfb\^`idedhdffjjkjljkiihmhiggggdccggfcdfjjlhgeihjfggiiihhhkgihgehgggfggilkkllijjifnkhcbbcfkkllonplljlkpornqpqopqttvw}zz{}vqifeeehfeeddeghhijmjkjnnnowqtnl`TICAB@A@@?@???@>“•Žƒ†ŠŽ’”•”““Ž’ŒŽ““ŽŒ‹Šˆˆ†ˆ‰ˆ‡‡††ƒ„€€€~|{z||~€€€|‚€€€€‚‚„……†‡ƒ†ˆŠ‹Š‹‰‰‰†‡†ˆ‰Š‹ŠŠŠŠ‹‹‘’••˜””•˜˜˜–™——““’’”“‘ŽŽŽ’”‘‘Ž””•“‘Š€s`[Ug|¤¶“°¶xi`kqkmnnot{}pf_aj\FIPZd\QG<=@DKVY\aqŽ™‡’àøöô÷úùõéÝàèïòööáLFPo¥áãçççãÒ¯‚ƒsqu‰¦“¹©¨—ƒqh`YPF@AEA=;9I|åóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøí×´ †U;2,,*,+++++*())*+)**++-++**08<=842/.-295/5=;;;=73CNB9DN@2;JC9<JC4497;0(''((&'(('&'&'('''''''')+28:>6,/:II8*4JH22>>B=6424C?>74-&&%&&&&7T^`cbbcgffeddda``abbaabbaaaceefc_aceddcedeeghihmjiiiilhieffedcehifdfgjjhggdeffeffgfgghhhggfgfihhghjjiigkkifhghgjid_bdfhkllkomljkkkjonnnnnmnpqtvvv|zzzupkgfgeabbbacefhghigmiihjjlmnoomkc\TLCB?@@@?@>@>>=š“‘Ž’’“’•••”•‘“‘—‘‘Ž’ŽŽŽ‡‡‡ˆˆ‹‡‰‡‡ƒ„~}||z|€€„€€‚€‚‚‚‚ƒ‡…‹ˆ‡‡‰ŠŠ‰‰‹ŒŒ‹‹‹‹‹‰ˆˆˆˆŠŒŠŽ‘–˜™™™—œ›ž—š™˜‘‘‘’‘—‘ŒŽ““”’’Ž‘‘‘’”•›šœ‘”„{lbXZhu‘µ·µº~j^jqmmnklqw|sjefqtJGMTZXNE??BKRX_dkv…ŠŒ‘ŒˆÀôóóòñòîÒ½¿ËèîïéµgGENj¤àäèèèæãЯ“•xdgo‘™ž“‰‚xg^WPJDCDEC>=;DhÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷ìÓ²žP70+*+****+*))))**)****++*++2?D?5455418AB5;DH>8744EOC:GOA:=ED7:A@0-131+(('''&&(''''&(((&&'((('+2==GIF20<GE5)5LF/1AMHCB@0.6;70*&%%%&&&%:Y^acbdcgeecbceaa_^`baccbbcdeflfecgefdgdfdiefgijmkjinnmgidfedcdhkgffhikhffiddegggfhfiiighgjijhjhllmlljigkiggkkklkeb`eghhknqmollknjqmoonmmnppsrwvxw{{ysqlhghgf_ccbbfffghhnmmjkillkmnmllkcd_UHCAB@??@?@>?>’‘Ž‘‘‘‘•‘“’’‘ŠŒŒŽŽŽ‹Ž‡ˆ†‰ŽŠˆ‹„ˆ…„‚~}€€‚ƒƒƒƒƒƒ„‚‚‚‚‚ƒƒ…†……†…‹‰ˆ‡Š†ˆ‡ˆ‹‹†…„„…ˆŠˆ‡ˆˆŠ‰ŠŠŽ‘“‘’•”—™—’‘‘ŽŽŽ‘‹ŽŽ‹‘Œ‘’“”””“†€wof_Wbov}¬½‹k]iqmmnkknuvvlimrrRJUWWTPKDACIQ[how‡ˆ‡ƒv|¬ïñóíçêæ¶£˜µßçäÆ[DDKh£ÞäêêêèæãÇ›…„l]^`jsttqngZVPJDB@DIE?=;AVŸìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöëÒ°ŸvL3.+*)*****)*,-.--,+*++,++--7HED<8;A81<LC39FFDA<:7GO@8DPF<>E9/194,++++)('''''&('''''''().211-)2=::@PH78?B?3)7ME//?FOAD6**++,*'&%%&%%$%>\_ccceacccaa``_\Z]_bbbbdefffeeedccccbfceddeggjjkkjjjjjhebfeeegjkggghihffdddccddfeeeeefffehiihhhkiliigfegghijigdb`ccgjiimpojniljjknlmklkmnprsqwtxxwurnmjhefcf`cfeefggdccgfhhiijilmmllmkbcg\RJBB@?>@>?>>>ŽŽ‘‘“–“•“˜““‘“ŒŒŽŽ‹ŽŠ‹‹ŽŒ‹ŒŒ‡ˆ„‚‚…€‚€€€‡„‹Š‰‡ˆƒ†ƒƒƒ…„Š‹Œˆˆˆ‰†‹‰ˆˆ‹ˆ‰‰Š‡‡‡††‡‰ŠŠŽ‹ŒŽ“’‘’••‘—•––—•”ŽŽŽŒ‘ŒŠ“‘’”’œ”˜’’ŒŒƒpeXVbov§¯¥o\fqmmkjjjnqpnowooWN[jVUSTIB@FO]jvˆ‰‡„~or¦íîïìêì㺔”«ÛàЧzUECIf£ÞæìíîëéåÛ£ut}dVVZ_cddc`UOIC@?@DHHA>;>L„èýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùõëÒ° qH0+)()))****+-1450-++,-.../1;LKB>>IF=4DKG48AMJNC=:JO>7CJO@C:2+*,,)'''''(''''''(''''((),07?A;2-8CD7:NM87CK<1)<IC2.6GID9/'%$$'&&%$#"!!!&Cacdidedfbc```a\Z\abfdcbfgjhjgidcdecdbfdfgkjjijjmmnkkhgedcfehgkllghgkhgfeegdcbcdfeeegghfeegjlhoklimmlffehfkklheb`bjimlnkonmmnkljonqnpnolpnrstqwuxvsqnnnjlhjfgcefhimihgeefgjhljmkkklkjkidddd^THC@??@>A>>>ŽŒŽŽŽ”’‘‘‘”’’ŒŽŽŽŒŒŒŒ‹‹‹‹‹ŒŒŒŒŠ†…ƒƒƒƒ‚‚‚‚…„†…‰††„…ƒ„„„„‡ˆ‰‰‡‡ˆ…‡ˆˆ‡ŠŠŠ‰Š‡‰††ˆˆˆ‰‰‹ŒŽ‹ŒŽŽ’’’’’‘“”“ŒŒŒŽŠŠˆŠŠŒŽŠŒ‘“””–‘ŽŒ‹ŽŒ”Œ†€xpe_X^hŽÃµt\crkmjihinnoqstjc\W]aXTROKB<BMWhny|||{wcm ëëêìííèѧ“¶ÙÔÆ‘lUDCG_¢áèîðñïìèÞ¬oehk[RSUWYYXVPMDA=>?BECB>;=DsäûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýýùõëЯ mE.+))))**+**,09D;31.147:9963<OJ>@?IL=5EOD489@EKJ=<GN:/5>;;61-())(''''''(''((''(()*/3468::JFF7/;IA0:KK68FJ:2)=QD6-+061-*'&%&&%%#"! !(Ldceecdccaa__\ZX\_bcddddhhifeedbbbecdcddfgiiihjjjkkkjedaccefghihhffddddddegeeddeffeffceddehjjhkjjjiihdddggkjifb]cfiijkmjlnkikkjhnlmlmlojmnqssqwtsqokigjjjiigfcefhhkjjhhghhhhijkjkkkkkkhcfhgf\RJA@??>?>>=ŽŽ‘‘—‘’‘Ž‹‹Œ‹ŠŠŠ‘ŒŽŠˆ‡†ˆ‰†ˆƒ„ƒ…†‡Š‰‰‰‰‰……„‰††‡ˆ‡ˆ‰‰Š‡‡ˆˆˆˆˆˆŠŠŒ‹ŠˆŠ††ˆŒ‹‹‹ŽŠŠ“‘“ŽŽŽŒ‹Š‹‡‡‰ŽŽ‘•™ž–—Ž‹‹“–’‰†ƒ€skYW^r—²n\_slmkiihhjmsqpg`a[_eYUVRI?:<CN[f|Ž„v€tl©ìîïëçêêÚ¹½ÆÉÈ©„mTD@DXŠÎÞìïòðîêß´ldfjaXQOOPQTUMKA?<<>@BCB?::?fÉùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùôêЯ fB-*)))**+*)*-2DHF5349<CDMD;4>OJ:9ALJ=6FOB777:BLE>=EB6,,/00.+)''''''&('&'((((&'()-17=EGH>@FPH<7EK<-9NK39JJ<2)@GG4-'')''&'''(#"! ".Tcbdebgccbc`a\X[adheffihliiffddbddeeheefiilkmjjkllmjjecbedhiojkjkfidcceccdgddddcbcddecfcdejjkgmlpjiifddejjljieabkjmjplmllkjimlijmkomnloloptstrwtsnlhgfklokmjieeglikkmknnnnnlllnmmkolmjheknrlg_QEA?>>?>>=Ž‹’‘ŒŽŒŒŒŒŒŒŠŠ‰‰‰ˆ‰Š‡‡‡‡‡‡ˆ†„„‚„„…„†‡‡†…‚„„„„†‡‰ŠŠŠŠŠŠŠ‡‚ˆ†ˆ‰‰ˆ‰ŠŒ‰‡ˆ…‡‡Š‹‹ŠŠŠˆŠ‹Œ“ŒŽŽŽŒŒŠŒŒŒŒŽ‹‹‰‰„‡†‘‘”•”’‘މŒŒŽŽŽŽ‹Šˆƒ{pd^Vcomh]^uonkklihikopog^`WYZYQLIF?9;?HQ`‡™—ŽŽ•š²îñóêáæêâ¿Î»³©¡ŒoSEBCSlÚêïóòðëâ¾vTXgdaUNLJIJIHC><:;;==?A=99:Z¼óûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùóêί¡`=,*)))+**)**.5FRF6:>?CDDLL>4>OK89CMG?9HOA7<ECA>=74471+***++*))('((''')''((,-/20.6?==AMG:>?NF=:HL<.<O@07LHB3(093.*&'''''&$#! $5Wbbccbbaccb``^``dgedfghhhhiefdcaeefehffgijjijkknhehifcddeehjojjhkeecccfbcccddb`\badccbbcdfjhggijjgihgeffjiiiebeijimkmllljhjjomjjmknmlllkopqoppsuojifggkllkliigghiiiikkmmnonmnnnmmlmmljhcjpqnli]PH@@?>>??•‘Ž”’‘‘’”Ž“ŽŽŒŽŽŒ‹ŠŠ‰‹‹Œ‰ˆ†„„……ŠŠ‰ˆ‡ˆ‡††‡ˆ‹†‡ˆŒŠŽŠ‹‰ˆ‰ˆˆˆ‰ŽŒŒŒ‹‰‰‡‹†‡ˆ‰ŠŠ‹ŠŒŒ‹ŒŒ’ŽŽŽ‘ŽŽŠŠ‰Ž’Ž’‹‹ˆˆ‡‡‰’’–––”””˜’‘ŽŽŽŒŠ‹‘“‡ˆ~ypj[Y_fl^]umolklihgjnnmh^XQUeYMHFC?;:=DGPsª£¦´ ÎóóóéßàâØ»¡ž¡¥¨wQICBNb€·ßèðððëäÇ€XOVg`ZUMIFFGB=;9:;:;;<<:988PŸìùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòé˯¡\8,)))*******/8IRG7=GPA>CNOB8>MK69HLI@@KJ@9BCH@<3/-,-+)(((()***)))(((('''),16=<86ADC@BPJ63?LC=?LK;5AG=/5CK@5)(**('%%&'%$" ';Zadedccaddeabcdbjgeeiimmnghffcccffhghhhhkjkikklheekheehfkgjjojkhjeddddgdedeeib__abdeechdghkhjghhgfjghgkjjhoidcikommlokiiijkkoljjlkomrlnmsptprouqlijhjgmmnkkkklmhrkjjkltqwxxussropqvpoopnlptnrmm_QEA?>>?>‘’‘ŽŽ‹ŒŠŽ‹‹ŒŒŠŒŒ‹Œ†…†ŠŠ‹Œˆ…„‚†ˆ‰ˆ‡‡ˆˆˆ‡ˆ‰‰ˆˆ‰‰ˆˆ‰ŒŠŒŒ‹‰‰‰‰‰‰‹Š‰ŠŒ‹ˆ‡„…†‡†‡‡‰ˆŠ‰Š‰Œ‹ŒŒŒŒŽŒŽ‹Šˆ‰‰ŽŠŠ†ˆˆ‰Š’’””•—“‘‘Ž‘ŠŽŽŠ…‡‰‹Œ“އ†‚}xrhaX[_Z]vmmmlkjhgikonj`RMOUVIBAA?::;=@Ib„§´¸´±ºìùöòèÝÛÑͧ””‘¯Ö½ƒYSHAL[w—¾áíîðëåЋ`NIR\`bZNIA?<:87677668:9876IŠå÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ðèɰ Z6+)))*******0;IRF7?OL=??NPA4AMH58MMLFCBE9/4;752.-+-**)(()*162/+(((++++**079<:H99CM>2@OI34DE?=ALLD;BC6(-6460)((''&%&'#! *C]`bcbab`a`dccccceedcdeeegfedddccefhehgghjjjhiiifffgfeeffgfkjkjiggddeedccdefeb_acccdcddedgikfffgffffghhljjhjhhfijmqnkljjijkkjnjjjmkomolmmpppoqnnlifgghgghjijjkllhhhkjjkoorstuuwurrsvqpppmopsnnmke[OG@?=>>‘””“““Œ‹Š‹‹’‹ŒŠ‹‡‡‰Œ‡„„…‰Ž•ދЉ‹‹‹ŠŠ‹ŽŽŽ‹‹Œ‹‹ŒŒŒŒ‰ŠŒŠ‰ŠŒ‰‡†…‡‹‡Š‰ŒŒŽ“ŽŠŠŠ‰ˆ‡‡‡ŠŽŒŽŽŽŠ‰‰‹Œ‘Ž“•••—–’’“•Ї„…‰ŽŠŒ‡ˆtm[W[V]ulromlnhffglok`TKMOMD>>@A<99;>CTlœÏ¸¶µ¹ÛòòñçÝ×Ç®’ŠŠ”ÃÝÒ“eZLEIXq‘©ËæêîêçÖ–kOEEV‰|n^SK?;86665543344675DzÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðèɰžU5+*))*)*****1<IRE7APJ:9?LN@5CKF48CPLLDC:2,+-.-+**)+*))((*/;AA2,()+.01/++6BH==<<>NK;/>PG04HN<6=NNKDF9/&&))))((%%%&#"!! !!!".I_aedeac`abeeedgggehcfefdieccefgdefhghhmmlklknijhiggehfhhihljmijfgceegfhdfhkcabfefdeddefelllkmglfffjkmimlljlhhhijmoqlkjojmmpknikkmmnnsoqpppsrrlmjgfhhlhigljmlklljmkkjqjnnpqttvw{zzuvsrqvvuttpppqjk^NB@=>=•’‘’‘‘ŽŒ‹ŒŒ‹‹‹‹Œ‰‹‹‹‹Œ‰‰‰ˆˆ‰ŠŠ‰Œ„ˆ‹Œ‘‹ŒŒ‹Š‹‹ŒŒ‹‹ŒŒ‹Š‹ŠŒ‹‹ˆŒ‹‹Š‹ˆˆŒŠˆ‰‰ŠŒˆ‡‡ˆ‡‡‰‰ŒŒŒŽŽŽŒ‹†‹ˆ‰‡ˆ…†‰ŒŽ‹‹ŠŒ‹ˆŒŒŽŽŽ‹ŽŽŽ‹†‡…‡†ˆ‹ŠŠŠ‰ŠŠŠ‹Š‰„€zsg`WSWflrpomnigeejmme_TLJIC<=>><9:;>CIb޳¸´ÊÈÇéíëß¿¥žœ”ŠŽ™½ÝÕ£€sSHLUm¤¶ÓåëéçÜvVCBP{šqpmh\RH?<97542244555AlÝòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüöïèʱ“Q3+********++2?KSD7BQJ99ANL?5CPB138;A=951/++,+++*,-.0-)))/5ALD4.+159@;<3-;LE3108@MI:0BPC/5LM943>LA?7/,''((''('&%$" !!!"#2O]aaab_a``afddeeefddbddcaabbbeffddehfhhjgijjjjjhfecgeeeghhhkjmhgddcdeeeecgje_bdeeedddffffhgggifhfffiiihliihhgikkklkiikjjjllkijijkmnnnnnnlnnnkjffchgiikfffhhhhiijijiiijjmmqqrsuuwxyvvttrsssrrppkpkkkZKE>><–“—‘’’’ŽŒ‹‹ŒŒŒ“‹ŒŒ‘ŽŽŽ‰‹‰ŽŒ‘ЉЋޑ‘’ŽŒŽŽŽ–ŒŒ‹‹Œ’‹ŒŒŒŒ‹‹‡†ˆŠŠŠ‹Š‰‡‡‡†‰ŒŒ‘‹Š‰‹‹‹ˆ‡††ˆŒŒ‹‹Š‹‘‘‘’”’™Œ‹ŠŠ‹Œ•ŽŽ‰††ˆŠ‰Š‡†‡ŠˆŠ“’“Œ‰†…ƒwr]QQ\crqponkjgeilmmd`VLJB=<><:8;<?DM]„¶¤œž¢³ßéâÃ¡Ž„šœ¢©£·ÛÖ¤’wZQNUj‡Ÿ®Êâêèæà¥‚_FAGljo‡¨Ÿ–}hUFA:752222555?aÌïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüöïçʉN2+,++++*++**3CKSC7CQJ79DOM=7EE?/-02420-+*)))))*+/5861+**2?OQH5017=CIPA71EKB/))1DLG81EP@.7ML6.,2862-*)'(&'%$##" !!!"#$6U[_ccdabbebgehhhejegcdcc^]_fehfffgfhhlkphhjqlmjgeefhfjgkklkkknihdcdhefggfihedfffffdefkhhhjghglfifehlhjhlhhghjmmpmpjiikkpjkjjjlilmmornooonsmmjheefiijkkfhehhkiiioikiljmkplqqsttuwx|xzyyvvtwvvrqopnomlZLA==’‘‘‘Ž‹ŒŠŒŒŒŒ‹ŒŒŒŒŠŠ‰‰‰‰Š‹ŠŒŠŽ‘ŽŠ‘‘‘ŽŒŠˆ‰Š‹ŒŒ‹‡ˆ‡ˆ‡Œˆ‰„†‡ˆŠŠ‰‰ˆ‡‡ˆ‰ŠŠŠ‹ŠŠ‹ŒŒŒŒ‹‹Š‡‰ˆ‰‹‹‰‰‡‡‡‰ˆ‡ˆŒŽ’”•˜’ŽŠŠˆŠ‹Œ‹‹Šˆ‡ˆ†††ˆŠˆ……†…ˆ‰‹Ž‹Œ‹ˆ†„vj\OQZiqqsomkjhhlpojkm_TK@?>=99:<CHQay‘’“–‘•ÌàÓ¹¤ˆ†›˜¨ ©Õ¹›‹}eWTUe~š¤ºÞéçåÞ²ŽmLB?OZzŸ·Æ¶¨…j\PHA;5433556=X®íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïæÄ¨€K2+++++**+-+*4FJRB7FRI67FFK8/5<5-,+-.--,***))))+,5BAF9--+7JOVF56;<@@JNC83EOA/,(1HLB82HQ>-2J=2,'*-,*)((''%%&" !""""$$&<Y]^^^a_bbebfeghgegeea`^^[\^ecgedcfghhiiiiikihhhffgggfhgigjijjjigcddfdfghhhgedeedcedfgihhijdddfffdfikfgeggggknnnpljiijkkkkkiiikjmlmpomonppommjhfghjjjihfffhhhhihjhjjjilkplpopqsvwvwtssuvvtutsqpmnmnmkdWIC=”“”‘‘ŽŽŒŽŽŽŽŽŒŠ‹‰Ž‰‹Ž‹”“•’Ž’‘–‘“’˜Œ‹Š‰‹’ŒŠ‹Ž‹‹ŠŠˆ‹‰‡‡‡‡‡‡‡‰‰‡‡‡‰‹ŠŽ’Š‹ŠŠŒ‹Šˆˆ‡‡ˆ‰Š“‹ŽŽŽŠŠŠŠ‹‘’”™™˜’‹ŠŠŒ‘ŒŽ‰ˆ‡†‡ˆŠŽŽŽ†„„…††‡Š‹ŒŒ’Ž‘‹‹†‚vm[MR[erqponkhimoronuzocSCCD<:<>ALYjyƒ†„„“¾ÒÊò™‰Ž’˜¨šœ¤¤’†|k\TW_v•¤Íæåäß¿›UB=Fh½Ù×ܶšƒth_ND:534685;Q“êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõîæÀ§vH1++****++,,,5IOQA8JLH45<C=4../.+))********+))),.=KTM>/..;OUTF25>G<:LOB96GO?.+'1JL>74KI<./45.*&%((''&''&#" ! !!!""#"$$##%)D^^_`^d_abdcffiiihjde`_^`Z\^ecgfeefhljniljjigedffhggiiljmmnjliihjfgegdjjjkkfeeedddggghkhjjjffeheeejikhiiigikoopnqkjijjkmmlkjjjnlnlmppmonsrpopjjimkkjniifllkhjjjgmilmmlppqmtqrrttyvxttstuvuztsrpnonpmmifWI?ŽŽŠ‹‹ŽŽŽŒŒŒŠŠˆˆ‡‰ˆ‹Œ‹ŽŒŒ“‘‘‘‘‘Š„‹ŠŠŒŽŠ‹‹‰‰‰ˆ†‡‡Œˆ‰Š‡†‡„†…ˆ‰‰ŠŠŠŒŒŠŠŠ‹ŒŒŠ‰‰‰‡‰Š‹‹‹‹‰ˆŒ‹ŒŽŽ–‘Ž‹ŠˆŠ‹ŒŒ‹ˆ…ˆ…†ˆŒ‡ƒ‚‚‚…‡ˆˆ‰ŠŠŠŽŽ‹‹‹Šˆ„|shZMQZirrtpnjiklomo|Žªr[GD><=@AJavˆ…„ˆˆ„—ÉâÔâË©“‹‰‹ŒŽ—š‹‡Œu`XM_h…§«ÃãããßĦ‰]F?Zƒ´ÜÛÜÛÁž˜dTE<866875;J†èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüôí忦pE1+*****-+,,,4KKN=5=I>133220,,-+******-++**+))*,/?RRRB232?PNQC/3;;;AMM?8:IN=-+(3LD:53:A4(+-+))''''('&#"! !!"""""""#$#%&-Ja^^^^`_aacdeggggffcd^__XT]^acdceegjlhiilkjggceggehgijihlkjihfffggffgekihefeffedeeghgfgghjifffheefggjihfggjmonmlljjijkknjhhehjoknmorqlllmlljjhjjjkkkkihflhgdffgfihkkkkonnnqqqqrrxuuutsttsrqpolmnnnmlljgaTEŽ“‹Š‹’ŽŒŒŽˆŒˆ‹ŠŽ‘ŽŽŽ’’‘“•‘Š…‡‰‹ŒŠŠ‹‰ŠŒ‰ˆˆ†…„‡ŽŠŠŠ‡†‡„ŠˆŒŒˆ‡ˆŠŠŠ‰Š“‹ŒŠŠŠŠŒŒŒŠˆŠ‹Œ—”ŠŠ‰ˆ†Š‹ŒŒ‰‡…ƒ…††‡ˆŠŠŠ…ƒƒ†‰ŒŠŽ‰‰ˆ‰‹ŽŽ”ŒŠ‹ŽŽ‰„xnZMR\gtsqnjihkooq™°¹–…aKB=?DAH\€‡’ˆ…ÏÖÔÜà±”Œˆ‰‰ˆ‡’”’f\^chxž¬¼àáãßɧ’aJMj§ÝÞßÝÙ»£¡¦ƒlVH>:99877;G|çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¿¢jA0+++**+***+,4EME:7886/,,+,,+**+******++*)**)**,0BSWUG54:GMRQ@..:==DNL;8=KO;,,(7EH6//10,('((')'(&%$"! !""!""""##"$####$'2O`b^abbbdcgefgigkfhbbdh^UU\^acgcefkjlhlknkihgehhhhhgiiijlkkgffihigffjhmihhhfjffdeehhhfhgffhhighfghkhjjkfihnmrnlllllkjlmiggighjontpqpqlkkkkmiijmlpknkkhiklhgchggejhlkplqnuuvsqrwwxsrsustrqpqpplnnpnnnolkhfSŒŒŽŒŒ‹ŒŒŽ‘ŒŠŒŒ‰ˆˆŒŒŽŽŽŽ‘’’’’ŽŒŠ‡ŽŠ‹ŒŒ‹Š‹ŒŠ‰ŒŠ‰ˆ‡ˆˆˆ‡‡ˆ‰†…†…ˆ‡ˆˆˆ†…„……‡‡ˆ‰ŠŠŠ‰‰‰‰‹Š‹ŒŒŒ‹ˆ‹Œ‹ˆ‡‡‡ˆ‰‹‡…ƒ€‚€‚ƒ†‰†„…ƒ„ƒƒ„†‰Œ‡‡ˆ…‰‡Š‹ŒŒŒˆŒ‰ŒŽˆ€vi[OS\ittrnkijmrsƒ£Áº®}QFBA@@EWm„–¥’”ž½×ÙÙÕ»™ŽŒ‰…šž¡Ÿ™€mqˆykt•«´ÐßâÞÃ¥’gVS‡ÏàáâÝÙ¶À¨†oWJA=::988;Duæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¼ f>/*++******,+/=9;4..0/,,,,++,,++**+*,,,+*+,+**,.1ESSUK:<CEIQP?.5;<;AOK87?NM:++'/<6.,)*+**((('''%#"! !!!!!!"$#"""##$#$$$#(6S`^^abbbcddeffhfdbc`eie\ZW^_abbbefijjhhhhhebhfgghihffgijkifdffggheffjhlffeggffeceeijgegdeehhgfgfhhihhfffiimmnkmijjklifhfgghgijpopponpikkkkigilkkmklkkhjkhdddikhfhhiijilnrttsrrvtsqrssrsrqmmkmlmnnoqspmmli^Ž‹ŠŠŒŒŒ‹ŒŒ‘‘ŽŒŒ’”“›•˜’‘‘”•™•–‰‹ˆ‹‘Ž’‘‹ŒŒŠŽŽŽŽŠ‰ˆ‡‡†‡ˆˆˆ‰‰‰†ƒƒŠŠ‹‰Š‹ˆˆˆ‰‡‡ˆŽŒŒ–—˜‹‰ˆˆŠ‹‹‘Œ“Љ†ƒ€~„…†‡‡†„ƒ„ƒˆˆˆ‰Ž†ƒ†‡ˆ‰‰ŽŽŒ‹‹Œ‹ŽŽŽŽ…um^SU\fqpoliikot…µ¼°”qWHILLGCXi˜¬Ê© ¬¯¶ÍÞÈŸ˜—”†Š˜«Î§„v}¥¾vf€ª®ÂÛàܽ ’r]n¢ÜãããÝÚ³«©©‹s]MC@=;::9<Bnçüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôíå¹¢c<.+****-+*+,+,210.,-,,+++++++,--+*+*++++,--++)),3IOPRPBABDHPN>8;CD:?QJ56ARF8**(*-,*(((())(&%$$"! !##""$"""#$%$$$#"%$&$$$)9W]^_dbcdefheffibaaeelhe^___addheeffhjijggccdiggfjjkklmlmmgddihkhifggjjlfgfjgffeehfligelgffkgffhhjjjgffjiikqnolnijkojihhghilkppppqnllpomlmjigmkklonmlkkojfdiijiihigghlllmqrututwsttsststrqnolpmnorsttvvuuvjŽŠŠŠ‹ŒŒŒ‹ŒŽŽŽŽ‘‘‘‘’”””““““”••“”‹‹‘‘’‘Ž‹‹ŒŒŒŒŽŒŠ‰†‰‰‰ˆ††…„ƒ‚‚ƒ„‹ˆ‰‰‰‰‰ˆ‡ƒ‰…†‡Œ‘ŽŒŒŠ‰ˆ‰Š‹Š‰ˆ†„ƒ‚„„€€‚ƒˆ†ˆŠ‰‰‡ƒ„…ˆŠ‰ˆ‡„„…‰ˆ‹‹ŒŽŽ‹Žˆ„}vqaSU[irqlihjmt„—ª²²gSL]whQOZj{²²²Ÿ’”¬Áäצ–›¡¦©“…–±·¶ž“ÈÉ‚blŠ›°ÕßÖ²›”}w|¹âæåäÞа¯¶«wcPEA><;:9;@gåúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôìä¹£`;.+**++++++++,.-,-+,,,+,+++,-283,++,,,,036;4-,*-6KMPRQGEEGGOL>4@MD6APA24A;=3*)&()*)))((((%#! """"!######$$##$##$#"$###$#*<X\_`aacedffccb`]acefecb___abeeeeeeeegggfdaefgfhghiiiiiihgeeeihjhgfggijgdgfgfggghhffggfffggffffhjjijgedhikkllkiiikljfffggikllmmoonllkllmljjiimjklonnolklifeijjggggggehhjkklnppqqpsutssspmmkmmnnprtuutvssrqnŽ‹’ŽŒŒ’”Ž‘’•‘˜’’‘’“™—œ—š™™—›–”“““”–—‘™’•’ŽŽŒŒ•‘‘‹‹Ž‹’“’Œ†„‚‚‚„‡†Œ‡‰‰‰ˆˆˆˆˆ‡ˆˆ‘“‘ŽŽŽŒŒŒŒŽŠ‰ƒ‚‚‚‚‚„‚…€~~€‚…‹‰ˆ‡‰‰‰‡†‡ˆ‹Œ‹……‚„ˆ‹Œš‘•ŽŒŽŠŒƒ~zvcUU]fqmjhgls}†—¤’cLZv‰‘lYblzŠ”žœ›Š˜¥½åΫ¥¨Ÿ–¥–—¸×ÖÑ›™ÁÏŠ_fƒžÄÜ£˜Žƒ¢ÞäèæãÝǰ¯±{bSGD>===9:?aÚøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôí帟]9.*,+,+++,+,--+*+,,-,,,,,++-.8>>.,----.3;AA?/,*/9LJCMTPOA=HNI=8LMEDHH@.,230-)(''')('((%$#" !!""####$$$%$$$%#$###%#%#$#$#,AX]ccbadedefcd^[^ehjeedccb`eeeefeeehegffdcdjjiekhhikkkijhfegeihmijhjiiheegfghjhniihgfggjgigihjjjjmkjgdfkkpknlkimlljiefgjjmlroompopkmlmmpjhjjkmjllpponnmmighlkkhlgghhhiiihiilmqrrpttuvvpolmlqnpqrsvtssxwvqooŽŒŒŽŽŽŽ’ŽŽ‘‘“”•‘‘‘““’’“™—œ—˜˜˜–•““’‘’’•‘”’•‘“ŽŽ‹Œ”‰†ƒƒ„…†‡‡ˆˆŠˆ‰Š‰‰‰‰‡†‡‡ˆˆˆˆ‹‰ŒŒ’Œ‡„ƒƒ‚€ƒƒ…†‚€€|€‚ƒ…ˆˆ‰‰††††‡ˆ‰ˆ††……ƒ…„…‰ŒŒ‘ŽŒŽŒ‹ŠŒ†ƒ€ztbRTZgrlihkry†“ “bSb‰¥›‡mlozz{‚‰‡ƒ”ºÉÇ²Â§š†–¢¼ÔÝÚ§š¼³–eYo„•·´ª£¤¥§©¯âçêæâݽ ¨¸©‡t`UJF@??>;<@]ÇõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúôíÞµšY6-)+++++*++,---++-,,,,,,,+,.0:H:1476752:C>?7.-*0;LGIHRTK;<KJE<8DSC?>?5****+*)))'(()%&#"!! !!!""#####$&%$$$$$$####""#####$#/I\^`_aaefedb_\X_ehkc^]]baabdeedffedffgggcfghgfeffijkjjjifedeeiglikghgffeegfhiihiegggfgehggehjkijjkkjghikkmkmgkillkhjfhijjmmmmnmmklkkilkjfhjkklkkjqpqkmnmijkkkmghgfehcefiegfllmmqnnnomlkllllooruuuussponponmŒ‹Œ‘Ž“““Ž‘—”˜˜™”•””•–——““•™™™˜—š•–•š’“’—”•”—••‘’—’“Š‰ŠŒŠ‰‰‰…‡†‰††ˆ‰Œ“’‘‘ŠŒ…†……†Š‹‘”—‰†ƒ‚€€‚„Žƒ‚€‚„‰ˆ‰ŠŠ‰‰…‹‰ŽŠŒ‡……†ƒƒ„…‡Š‹Š‹Œ‘ŽŽ‹‹‹Œ‰Š…}ubUTZdnhhjpz|‚ˆoVf §«vrqqprs|zxz–ɽ»³¬¨œ–‹Ž—¦¯ÏÖÓ³œ¦»•lVct†¢º £³Ã£¹äçéæâÜ´Ÿ¢¤¦‚paWNJEB@><??YºòûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÚ³‘V5-+*+++*++,,++,+,,,,--,,,+,/6DH;8:?HB;9EF>>3..*0?LE;BRRI8:LQA74@FG=73,(''(()))('&%#! !!!!""#$$#$%%%$%%%%$$$##$$#%###$#$#2R_aaaffefhdc_Z\ehle]Z[^abffggffgfjgfhlihfniiedehfmlnjihieefkfihlkkhkffeffkhmijhiefhgglgghjfhlqkjimjjijlmmnjmijiolkhjkllkkmnmlqmmnoonnpkjfjkppqlmkqpqmmmmmmnqkoklgfghcfeigkkkmpqropppkkknnqrttuw|vzstoompnnlŽ‹‹ŒŽŽ’‹“””—••”–••””••”–š™˜˜™——˜•––˜’’“–••“”’‘“’‘ŠŒŽ‘Œ‹Šˆ†††‡‰ˆ…†‡‰ˆˆ‰Œ‘”’ˆ…ƒ‚…†ŒŒŠ‰‰‰…„„„ƒ„†……„„||yy{††‡‰ˆˆ…‡††…Šˆˆ‡‡………………†‰‹‹‰‰ŠŽŒ‘ŽŽŒ‹ŒŒŠ‰ˆ‡‡}rcSSYdjihm‚€}|{Šnf†¬¢”slkpt~„€ns–ÇÆµª®™›•|…ˆ“”°ÑÍÅ¥ŒlSYdw‹«É¾ŸžÂæçèåâÖ¯“§¨–}rbZOLHEB@=>>V¬ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúóëÔ²‡R3-**,+,+,++,,,+,,,-,.,,--028=BG=9<>BH;8AND>4,-*2C@B89LO@16KB;2+/421/,+)('('('&%#! !!!""""####$$%%%%%%%$$$#"""""#######$7X`bbccbededb_`bfif_]\_abcdefffggegghijihghfeeefhgjjifgfhbefgfhhiffgheedfghfiikeeegihgkfgiifilkkjilkjikllikjjiiiiihgiilomnnqlkmjgeimkjigffjkonojkkqoollmnmnpnkljkhgcbbccefhilpmkmllkjfhhlpqrssuwwuurolkkjiig‘‹Ž“““”•—œ••”˜•–““”•—ššš—–––—ž˜˜˜˜˜™•••››œšš‘‘‘“”•’“‘ŒŒŽŽŽ‰ˆ‡‡…„…‡‡Š‰‹ŠŽŽ““—–”‘Œ‰„‚ƒ„†‹‰ŠŠ†„……†ŠŒŒŒˆ‰…„‚}||tr|ƒ‡Š‡Š‰‡‡†‡ˆ‰ˆ‡…†††‡ŠŠŽ‡ŒŒ‹ŒŽŽ‘ŽŽ‹‹ŒŠŠ€xcUTW_ifjzŠ}{‰“muŽ™‘…rjluŽ—Ÿ{q“Å»±¬ª›†ˆŽ‰„ˆ—’¤ÑÕÍ®‰˜oTNZjz€·É»”ŠÉçèèåáÒ¬ž¢‘{ub\TOKHEC???RœëøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòêÒ²M2,+*+,,++++,,-+-,,,,,,,.049?FKB=@E@B@;8@INA5,.,3IJ?55HD8.2;;3-)()))((*))&'%$$#! !!!"""""###%%%%&&%%'&%%$%$#$##"$"#######&<\bcdeedffffffgfjhd^^`cdfdieefghieihqjjiiggfghlhkgihhfgggfggggjikefggfiiigifkilegfgikgkhghlkkkjjkilkkjnjklmkkkojmiihlkmllnomlkqiddiihgffiinnnnnmllpopmlmqnpnllmkjgicbbbbeflkmmllljijieggjoqswvvwvvwqniihhiigŽ‹Œ‹‘‘’’•——˜™––”””–’•–—™˜š—•••–—š˜˜”˜–——–”˜–›—–“’’‘‘‘ŽŒ‹‹‰ˆ…ˆ…‡ˆ‡†ˆˆ‰‰ŠŠŽ’””’’‰‰Š‰‰‰ƒ„†‡‡†„ƒ‚‚‚‚„‡‡‰ŒŠ‰ˆ…„€{xyvz„…‡†‡‡ˆ†……‡ƒƒƒ‚ƒ„…‡ˆ‰Œ‡Š‹ŽŽŽŽŽŒŒ‰Š‹‰ŠŠ‹Š‰ƒ|ubSPS^fis|~„‘”}eg}Љrhht’¨ ‡†¿¼¯²¬œŽ…ž–‡”‘©ÏÒÐ¸Š‚spXOQfsy‹ºË¬€…ÑçèèäáΪ˜ŸœŒzuc]VROHFB???Pç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñéÏ«wH0,+++,,,+++,,,+,--,,-,,04;CBLJ@AHA<<;;8AB?@3,+*3LJ;2,5<3),0/.,***)))('&%$#! !!""!!##$###$$$$$%%%%&'&&%%$#$##$"!""######$(A`acdefeededfffffed`adcefefedcddfehijgjhhfhggghghggfheedcbcdfgiilfgggfgggggghge`gggghgiiiikjkkkjjhlkljlijmllllojjiihljlkmkkiihhdccdefffejloonmkgmnpppklmommmlklljedccdddfhlkjhhihhgeeeffjnopssvrqmmmhcddjjji“Œ‹‹Œ•’”˜—žŸ –—”””–™›™š——•••””–—žœŸ˜˜—œ›š¤———–•’‘‘“‘”ޑދ‰‡……ˆ‡ŒŽŽŽŽ•””’Œ‰‰‹ŒŒŠˆƒŠŠŒƒ€‚ƒ…ŠŒ‰‰‰‰‡†ƒ‚‚€{}ƒ…‡††„ˆˆ‡ˆˆ†‹‡‡ƒ‚‚ƒ…ЇЉ‹ŒŽ“’”ŽŽŽŒŒ‰‰ŠŠŠ‹ŠŒˆ‡‡†}s`RPS[glvy‹‘“‚u]Ucyxhiq‘ž³‰Šœ¼¹©ª¬—‘’™¨µ”“¥ÕÞÌÆŒ€vqaQPblq½´yÉæèéãÞ¦¦©Ÿ‰zwb^VXOID@>??M‡ãöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñèË¥pD/+++,,,,,,,+,,++,------2<F?@NK@EF?;99;>@BD:1++*4HL70).0.)(**)*)*()('%#" ! !#"!"""!##$$##$&$%%'&&%%&&%%%%%%#$#$##""###""%*HbedhfhfhfiflllfedeffdcehefddeechekkkkkghhhhliiikhhgheccdcdehjkjmgigkfmllkmgifddhflkjkljliokoonklgllllpiklolnnnjlkkhkkpmnklihgfccdeeegjjlmqonkklnpspomnnpmmmlkkjjdedhhihllmjifghhfeeeeihkooottwrqlljecbdkknmŽŠ‹‹ŒŽ”•˜™›œ››˜•””––˜˜›•••˜“••”“••˜šœ›™—˜–™š›——”––•‘‘ŽŽŽŒŠ††…ˆ‡ŒŒ’ŽŽŠˆˆˆŠ‹ŠŠŒ†‚€€€~~ƒ†ˆ‰Š‰ˆˆˆ†‡‚†‚‚ƒƒƒ„„„…„ƒ„ƒˆ‡‡Œ‡…†‡‡‚‚€‚†ˆ‰ŒŽ‹‹ŽŽ’ŒŽŒ‹‹‹‰‹‹ŠŠ‹ˆ‰ˆ‡‰†wk^QMO[gjn†¢’u`NJLk¨›€khnw‰‹Š’¢¹ž –˜¨»Ã²—‘ŽœÚͳ¥Œ€xxw^Q^lq±šu~ÂäèêãѶž•¤¡‰|n_XSQNH@?<=>JàõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñæÈ¨jA.+,,,,,,,..,-,,++,.-./.8GE<@MD<GME>:59DAA92/++)/@72.)+,+)*+*)*)((&%"! !"!!#""##$$$$$$$$$$$$$&%%$%&&%%%%#""$#"""#""$$##%-Mcdbhffffefehhgdccdbdeddfdfba`dddejjihgefegiiijjjiidcccadbegjkkiifgfeefgkhhffdegigkijjjijjkkoiiijilmkkliklmkllljhgiilkmlmihfgfgaccccfjkmlmqplimmoqqqpmmnpkkkmjigfdechghimnkgfefggefcefhhkpoovqrpommjd^belkmnŒŽŽ‘••™šš›˜•˜žžšž˜š”•–™–›•–•˜•˜ššš–ž–ššœ™š˜ž™œ”““•ŽŽŒŒ‹‹Œ’’˜Œ‹‰‰…†ˆ‡ˆ‹”ŽŒ‰„€}{~ƒ‰‹ŒŠŠ‹ˆ‡‡ˆ………†ˆ‹ŠŠˆˆŠ…‡…‡‡ˆ‡†††……‡ˆ„ƒ‚‡‹Œ‹‹”‘ŽŒŽ‹ŠŠŠ‹‘ŒŠ‰‰Šˆˆˆˆ„um]NJLV`n…‘Œ{[KAFb¤³Šploonw‘›™™“‹˜”Ÿ½àÛɯªš—³·®«“{¢‰iZ]lrÅ•u{µáããÒË©•¥Ã¢‰whZROMJF>?;<=H{ÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøðåÇ©f?.+,.,+,,,-,,,,,,,,,,,-/<JE;@GC=KOLC@44:;62.+*+*,01/,)))))((*('&$##! !!""""#$##$%%%%$$&$$$$$&&%%%%(''%%###$#""##%#$###&1Scgeiiiffeiejggdccfghegdidfaaajglfjgffgfhefjmlplmjkefcdddehkmllkjfhggeikliiffefhigkiljjkkktmnjjjlmoljjllklnlnloighjlqknkmhiglfgcdddegjmmonqmjltstrtrqqqnpkmmnihfddeeijnlrlkffehgjfhceekghkpowvusqrske`chlllmŽŽŽ’’‘˜–™š™˜šœ›šš™™–—•–™—”•”––—”™šš˜˜•—–š™™˜™—š™š–”’•‘‘ŒŽ”ˆˆˆ‰‰ˆ‡‡……†Šˆ‚€~~|€…‡Š‡†‡‡†…††‡ƒ†…‡ˆŠŽŠˆˆˆ‡‡†……„………„„„„ƒ…†ˆ††…†‡‹Š‹‰ŒŽŒŠŠ‰‹ŠŠˆŠ‹‹ŠŠŠŠŠ‰ˆˆ‡ˆ…†‰€yri[MJLZn„‰†iLDE^ž›•vmom^^}—•‹‡zo|¦ÐìêÙǾ§¦®³®° ƒ~°vZ]ku¬©•uu§ÍÞɺާ¸Ã£ƒoaTNIHGB=<;;<GtÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûøðäÆªc=-,,-.,-,,,,,,,,,,,+,-00?LG<AJ>4>LC@8/011/.,,,+)+--,+))+))''&%#! ! !""$#$$$$$$$%$%%$#&$$$$$&%&&%$$#$%$#####""""#"####(6WcgdhcccfefeddeedcehgedccbcbbbifhffeedffffgjkkjjiggdedcbdfiklggeffffgdjiiggffdghigjjlkkmlllmljkkmmojiikklmlknjjiijkkkkkjigggecgcdafkjjlmnmnlllspqqqooopmpkkkmgfedceeikmnrifbdfffgeddedgghilnooquwxsngbhmmllkŽŽ‘’’‘˜˜˜œ¢¢£œš™™˜˜™˜š———™šš—™—šš›˜›–œœ™™˜œ ™Ÿ——•–••••’‘‘”‘‘‘‰†‰™†‰ƒƒƒ‚„„ƒ‚„…ˆ…‰‡†……„„„„ˆ‹‹‰‹‰ˆ‰‰‰‰ˆ†…††……„„„„…‡Šˆ‰‡‡‰Œ‹‘•Œ‹Š‰‰‰‹ŒŒ‹ŒŽ‹Ž‰‰‰‰ˆ‰ˆˆ„„ƒ‚{xtpbUOPbƒ‚[GL[„¬œ}syhOUpƒƒxspjj„ªÚïððìàÄ·°±¨”…‰œžƒYXkx‰¸©–toµ²³ÂÁ¾¾½À™}j\QLJJHB=<;;=FoÙòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýúöïãê^;-,,,,,,++,,-----,-,,.00CKGFFB945:;61,,,,,-++++***+****)(%$#" """ ""!"#$%%%%%$%%'$%%$$%$%$&%'''&%$%$$$$#####""######$$*<\dhehdefeeffgdeehdeghdcbcbeeeejhkffeedjggfiillmihgggggfffgjjlgieffjfggljijighfjiiilmmlmmololqonooppkjjoksmlknllkkjmkjjiiihhecbhdeeikjjlnrnnmpmtqurrowrtppmnkllldefjhnmxosgcbcglhllkihghgkillllpszutljmqppnmkŽŽŽ‘““””“™œšœ›››™™—šžš—–”—‘˜—––™šš—™“™š—™š›˜–“—••’––——”’’‘‘”Œ‹‹ŒŽ‹„€}}y€‚‡‡‰Š†…‡…„„„„ƒ‚…†ˆ‰Š‹Œ‹Š‰‰ˆ‡†ˆˆˆˆ†…„‚‚ƒ…………ˆ‰‰ŠŠ‰‰‡‡ˆ‹ŒŽ‹ŽŽŒŠ‰‡ˆ‰‰ŠŒ‹ŒŒ‰Šˆ‰‰Šˆ†ƒ‚‚‚‚ƒ~„~yiXTUhzz~~bRPXl‡¢„rjcLN`w{}rkfX_{¨áîõúôêÜõœ˜‹”›Ÿ}WUjt€œ¥–sm|™©ÃÝßÝÎÄ´xfZPMKJHC?=<<=FjÞñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõíâÀ¢Y9-,+,,-+++,,--,,-+,,-...=OEB<:4,/11/-+,,,+-++,,,*,*)**'&$"! !"!""" !!""##$$$$%%%$%%%$%%%%%$$$%%')(&%$$#$$$###"!""#######$,C\cdceeedddfedccccbfdcba`bbegffgghefdedgfffjihfffheggfdfeghjiigfcfffghhhhijighdggjkklljmnnkklpmmnokkkkkljjjjijkjjjjmmhfghhdedbbcbegjkjjmnonmmnnnnooooonoonmmjjhheffghnnqokda_dimghhjjhghceeeehlmlnoolmppoppol‘‘•”›–—“œ›™›œ›š˜˜˜šœš™˜˜˜˜’”•–•••š››™š˜ŸŸžœ››žš˜•””—“•“˜—š˜—“–’’’“‘‘•‘Ž‹ˆ…ƒ€{z{€ˆŠ’ˆ††…‡„ƒƒ‚ƒ„†‹ŠŠ‰‰ˆˆ‰‡‡ˆŽŽ‡…ƒƒ††‹‹’‹ŒŒ‹Šˆ‡‡Š‹ŽŒŽŽ•‘ŠŒ†‡‰ŠŠŽ‹ŒŽ‹‰‰ŠŠ†„‚…ƒ„†Œ†ƒn\UWbjryeUSU]j~ngaIISerrlgcUSr¨ÚîöûøôåÍÅÅ¥’”Ÿž‰w_Shoyžž–tk{ŸÄßßßÕØÀ©ŽraYPNLMFD@><==FhÖðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôí὚V7-.-,--,,+++,----,,,--..9?A;62/+++,,,+++++,+++,**)(&%#"" !!!!##"#"#%%%$%&%$%%%%%%%&'''%%$$$$'++)&&$%%%$%#""""$$$#$###$#/J\cgffffcdehdccddbbfdd`_affffiggfieffeekhhhkiffjfhgghkfggjknijgfdgghhljkjijkiiiklmnpllkmnllnopntmoijknkkikjjijjmkqnnjffffiffcaaeelinjiksqpnqrroqoqoooonppnnojliiklklnonrlga_`ghmiikmljhhbedbcginnomrnqqrqqqql‘‘‘”””“’’’’“•–————˜–—˜˜——–”‘‘’’“–”••˜˜ššš™œœ›š›››—™””“”’‘”“–•—’–““””““‹ˆ†‡„„„ƒ‚‚…‹ŠŠŠ‡…€ƒƒ…„„„…†ˆŠ‹ŠŒ‹ˆˆ‡‡†††‡‡†…ƒ€}ƒ„‡ˆ‹‹‹‹Œ‡ˆ‰‹Š‰†Š‰ŽŒ‹ŒŠŠ‡‰ŒŠŠŽŒ‹‡‰‰ŒŠˆ‡†ƒƒ‚ƒƒ„‡‰ŠŒŒ‰…‚l[TS]gih[USPXcjjc`H?IVdqlebW]j•Õí÷üüûîßØÊ¯› ½Ã•€{dfhfe‚¡‘sk³àãàÚÔ˾¬v^XPMKIFCA?<<<EdÏðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùóìá¼–S5------,,+++,.---,,-----/3220.-++,,,+++,++++++*)&$#"! !!!!!!!!###"##$#$$%$$$$$%%%&()('&%%$$$&(,.*%%$%#$####""#$$$"###"$#3P^cgdffgccfeddcbba`aa_^`bb`ecefdcccehgejhhhhgffeehhhggghhjjjghgfcghihhiigjkkfijklmomkmmmmllmopklljejjlkjhihhijjkjmomhecdceegaaadekilkklooplmlonnmnmmlmnnlnkjjjkjkkknpolkgc^_begjijnormiibdccdggonnlkkmnmlmklk•”“‘’—’’’”•˜––—™˜™—›•’“’‘’•—”–•––œ–™šš™›¥œžœœ˜š”“”𔕕–—˜™™˜—“™”•Ї„ƒƒ†ˆŠ’’’“’‹ŠˆŠ‡…ƒƒ„Љ‰ˆŒŽŽ”ŽŒŒˆˆŠŠ‡‰‡†ƒ‚€€„…ŒŽŠŠ‹Œˆˆ‰ŽŠ‹ŠŽŒ’’’‹‹‹ŠŠŠŽ‹Š‰ŠˆŒ‰‡†„‚ƒ…‰Ž’‘‰‰}hWOOZic]XRPNXfb`ZG>AKUbkgdgtyŠºçòöùûõïéÛµ ·Ô®z~k`ab`uŒnkŠÅÓàÜ×ÉÅ·¯˜}fWNKIIIIA@=>=DaÄïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôìâºO3,+.----,--,--.-,,,-,++++-,,+++++,,,++++++++*'%$"! !!!""###"####$$$##$%%$$$$%&'')-./*'&&%$&')0-)$%$%$%#$##"$$%$%###$##$8V_dgfhggcdefefbaa``c_]_`bbbedecbbbcfiigjjlhgfhfgghhkhjhmhkjjhiihgghlhnjnkqkljnmpmnlkkpkjklloppjmkiijkkjiikilmmjklmllgedhdeeibccjhljokmmmnplmnqnnnommlpmnmnkkjjjjllmrqqmkdb]``bdnjkoqqpjkffccejjpnsljiwnomnjpj‘“’““”“‘‘’’”•–•––™’”“’‘’”’Ž”–”“••––––šš›™™™˜—™™˜”™’”••–—˜›Ÿœœš›“‹‡ƒ‚~ƒ„‰–“Œ‹Š‰‡ˆ‰ˆ………‰‰ˆ‡ŒŠ†‡Š‰‡…‚‚ƒ‚‚€€„…‰‹Žˆˆ‡Š‰ˆˆŠŒ‹ŠŠŒ‹‹‰Œ‹ŽŽŠŠ‡‰‰‰‰Š„……‡ˆ…„…‚„ˆ‹ŽŽŽŒˆŠ‰‰ˆˆ€}pbSPQ[ca\TPNOYb[TE?ABKXl}vu~Œ‘§ÑëðöúúûòãÀºÃÁž}z{sqoh^lˆyhm•ÆÌÊž¹³¬§¡‡lYLJGILF@>===E^ÂîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøôìจL3,,.------,,-.--,,-.,+,.,-,++++++-,-+,*+*)'%$#! !!!!!#"$"$#######$$$$%$$%%$$&)((*,150+)'''''(*+,)$%###$#$"""$$$%%#####$&<\adfffegcebfdcba``_^\`aaaccdba_a`cdfhigjiiggeffgiifhfhhhhgfhgffhghhihkiiijgkjmmmkljjjjgkjllljjiijjjkllmjhijlmlklmlihdeedddcbacchhhhkkljlkkkmnmmllplkkmmlknhjiiiklmmoomje_ZVYY\`nkloolpkjgfccbeikmmliikmkijjihšššš›”———‘“–™———ššš”’Ž‘•™˜–•”“––™™˜š¡š ˜——˜—›˜——˜˜˜˜™œ¢£¢›˜’‡‚‚~‚†Œ•‘”’ŽŽŒŒ‹ŒŒ‘Ž‰ŠˆŽ‰Œ‰Ž’ŽŽ’Їˆ…ƒƒ‚‚†‚……†‡‡…††ŠŠ‰‰‹’Љˆ‹Ž‹Š‹‹ŒŒŒ‘‘ŠŠ‡‰‡‡ˆŠ…†„†„‚ƒ…†Š‹ŽŽŽŒ‹‹‹Š‡…€}wpdWQPZnaYROQV[YSDDECF[q†’€‚Šœ¬ÃÜêñöùüõêØÐϽ™{zƒƒ‡xkalneo™ÉÊÎÆ²¬§¨¨œ“o[KGFIGD?=<=>D`ÁîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøóëÚ±~I4.----.-,-..-..--,--,,,,,,++,++,+,+**)('%$#"!! "!!"""##"###"$##$###$$%$$$%%&()((+,+,8RB4+*)**))+***)$%$&$%###$%)(((&###%#%(A`ceffgfgcecfdccdbb_``fdecfgga``dceefgigoijjkgihkjjglikijhggkkkfkiokkimikkjgljqnnkllljiiklpnmjkippppqnpljhllqmnnrmkhhgheeefdecghhhjhmklllijkomnlmlpkrkmmlknjjjljmmqpqnohc\WLJKPamkonnnpkkhiefceceglhgjrkjijjji—˜˜˜–”””“““˜––———•”•‘‘“••”••”““”›š›œ›ž˜—–˜”›˜šššššš š–—˜–‘’Œ„„†‰‘‘ŒŒŒ‹ŒŠŠŠŽŒŠŠ‰ŠŠŠ‰Žˆ†„ˆ‚‚€‚‚ƒƒ‚„…†„††‡†ˆˆˆˆŠŠŠ‰ŠŠ‰ˆ‰‰‹ŒŒ‹Œ‰‰ˆŠ†……‡‡†‚…„†€€…†Š‹‹‹Š‰‡…ˆˆˆ‡…‚€}xufXSS_l`TPPRZYRGFHHGa”…Ÿ¸ÄÎÚëð÷þøðêàÖ¬„|‰–«¼Ÿofk]q›ÇÉÈÇž›“…r^KE@ACA><<=>Ea½îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýýøòêÖªwE30.-----------..-,--.--,,,,,+++,,,*)&%$"! !!!""##"##############$$%$$%%&&/:63<K9./=IF4,./01/-,*)(&$$#$$$#$$')-04;+$###"%*Gabffdgggceafcdedbb```dccba``]`acdfefdggihgddeggiegghhihhhhijihgjjnkjimijiigjjlkkiihjijklmoplijjnlllllliigmlnlnnmjihgcieebcccdffgehhmjmijikkkkllllmjjjjijhijjjljmnqpmjhd]VH;AEVenkllmlmkkfedebaacbdafijhiiiiih–—š–”–˜˜™•’“••œ›˜“’‘’š™˜••••–•”••œ¢œŸœž™œ——–››Ÿœž ›š˜••“—–—›˜–—––•ŽŒ‹‹Š‹Œ‹‹•“’–‘ŽŒ‹‹’ŽŒŽ•‘Ї‡…‰‚‚ƒ„„…‰„„†‹‡ˆ‡Š‰‰‹ŠŒŽ‰‹‰‹Šˆˆ‰‰‰‹“Ž’ˆŒ‰ŠŠŽ‰ˆˆˆƒ‰„„……„Œ‹Š‰ˆˆ‰‰‰ˆˆ‰‹ˆ…ƒ‰ƒ€~{i\TS\hYRPMR[QIGGEHcœ“‹u‚µÇÌÒàëôúù÷ôìÙ£‹t‘ÕÐË‹}tns|›ÄÅÆ¸ªš™˜˜‹tcMD==?><;=AAF`¸îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷òéÒªqC1.----.-,-/-,-...--,,-.,,,,,,*(('&&%$""! !!!!""!!!#!"##"##"#$$$$$$$$$$%%$$$%&&(.:NOSYQ@13CSA438:=??70,+''%##$$$$%(-8FHJ>0&$#$$&-M_cdefjghdeageidebc`achcb``aabdcffjgedkhlhgdifkghfgilikjkilmmjjhkjnjlknikjlinjmjiiihnmmknmrolkonoknmllmkmlmnqmplkihglgjeebbchhhgggllmknilkmkskmkjkqkkkkklghjmmllqrrnlhf]XJ;6;Ncgolmmllnjkgfdeaccddfefjkklikihi—•™”—™˜˜™—”‘‘’’“””••’••˜–”•–›žœ™š›—””•——šœ›š—ž˜˜—˜—𡡣𛕓’’‘‹‰ˆŠŠŒ”””ŒŽ‰‹Œ“““ŽŒŒŒŒŒ‹‰†…ƒ‚‚ƒƒ†ˆ†††„„„‹‡‡ˆŠŠ‰‡‹„‹‰‰…‡Š‡ƒˆ…‡‡‰ŠŠŠ‰‰‰ˆŠ‰‰‰ˆˆˆ†ˆƒ„„†‡‡†Œ‰‰Šˆ‡†„‰†‡‰‰ˆ†„„„„„yj[TQZ^UPLLMPKGFCF_˜ˆ€”±ÔèÝÜãñ÷úýùôß±Œ‡‡¹Îº”“³°®¦ž±Å»¬¢™”Ž‹ƒ|udNE<<<;:;=>?E_¾îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ñè̪nB1/---------,,,,---.,--,,,,+*))%%"!!!! !!! !#!!!!!!!!! "##"#$###$$$$$%&''&&%$&'.28=OpR\[]A27HP><:CKOQIC6-+'&&$#(+)(16=HFOI?6(%###'0Q[^`ggecbad`fdebcaaacfga_]acdeddfgjhgdjffefdeekggghhhgggiikkkljhhhihhiihjijijiihhhhhhgijnmmmmlllkkkikmlilmoomkkjihhgiffccadehhhhhgkkkjiikkllmjjhhhhgijjljehhiilmnnnljg]RH=63Lfeilkopmkjijgfccbegggffhiijihjghi––™˜ž›œ—™••‘Ž“’—•™““”—“•–——™•–—œ›š™˜–”•—˜œš™››œœœŸ ¡Ÿ§ž£¢¢™—•”’‘‘“ŽŽ•–––—’ŒŽŽ’Ž••˜’“ŒŠŠ‰‹ŽŠ‰†…„„…ˆ‰‹‡‡†‰‰ˆ‡Œ‰‰Œ‘Š‹‰Œ‡ŠŠ‹……††…ˆˆ‰ˆŒŒŒŠ‰ˆŠ‰‹ŠŽ‰‡‡ˆ†‡††‡ŒŒ‹ŒŒŠ‰ˆ‡†‡‡Š‡†‰‰‰‰‰Ž‰‹‡†€}jZROSZQKKJNLKIDCTqu{€‘ŒáäæÝàñöùýûùè͹§š‘™°¶°–“¯¿¿×«¶½¨–’Ї€|seOE>=;:::<>?EaÅîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷ñèǬk@1------,/-.,--.---.--,-++*)%#"! !"$""""""""!""""!"##$#$$###$%%%%&,124,()+2?I^`bnV\naC69BN:8>RX_WWJ;.+('&%(/117GCOKHLM@B(%$$#(4TZ^_idccbadbeegccdeefffb_`ggggggkilijekfgdjfggklllkhghhimkokmklmmhkhhiihnnnjlikhjhhgghijnmllrllkijlgokkkpounmjkiihohlfgbedihiijjkjlkkkjioooklkkgihgfhiiijfihjkqqqnpklaVF>51Bfkiiompookihjhggffkknkihkirmoijhjk–—˜˜˜˜—””‘‘’’’’•••“““““•”–—–••˜˜—–•™•˜›››žžŸŸ¡£¢¢ŸžŸŸšœœœ™˜—––——˜š–“’’‘’ŽŽ‘‘“’’’’“‡‰ŠŠŠŠˆˆ…†ˆˆ‡ˆ‰ˆ††‡‡ˆ‰ŠŠŠ‹Žˆˆˆˆ‡‰‰‰‚…„……†‡‡‡††‡‡‡‡Šˆ‡‡†…„„…„ˆ‰‰ˆ‹Š‰ˆŒŠ‰‡‡‡‡ˆˆˆˆŠŠŠŠŠ‹Š‰‡‡ƒ~xgXQKQTJFHOMQIDABVjs|ˆ’‘¦ÈáÜØàñôùþüûñäßÎ³š“®´¶˜Ž©´º¿¦¢®°£˜‹…yuobPE?=;;;;;=?FbÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýü÷ñèÅ®i>0-----.,---,,,-,,,--++*'%$"! !"""""#"##""""##$$#"#"$%$$$%&'%%/CAA=48@?ANV_dfk[ZbdC458868AU^]WSO<.,)))+/;FEEG@GNCG=51'$###)8UY[]^^_]__`aedcaddeefdfbbbedfgghkgihgefffejffhiiihkiiijkmiiiihlmifhihhiinnmiighgjghhikkkkkllkkkjihhhjhjloonoljiiiihhhedbefiiijjjkjlkkgkijhjiijhehggeihhigefgkmmnnlkjdXF63.@^goljnlnoniihiigeghjknlifjfkklijhih——™–––˜”•’’“”•–š——–•“““•”˜˜––••––•–™™Ÿ¢£¤ ª ¨§¥¡žŸ›œ¦¥£¢¢¡›œ—š“‘‘•”•––™œ››“—“‘‘’”“‡‰‹‹Š‹‰‘ŠŠ‰ŽˆŠ†††‡Š–’‹ŒŒŒŽ‰‰‰‰‡†‡ˆˆ‰………„………ˆ‰ˆŠ…„„„„…‡ˆ‰ŠŒ‹‹ˆŒŠŠˆŒŠ‹‹ŒŠŠ‰ŽŠ‰ˆ‹…}ygYOIHJDEIKJIE@BHMQeˆ‘»ÃÊÌÓïôùýýýöðçÔ«¡ž²¹Ÿ‹’²¶¼œ™¦²¢”ŒŠxtsh`PF?=;<<;;=>FbÉïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûöðçįf<0--.-..----++--++**)('%#!! !#"$$#######""""###$#$$$$%$$$%&('&'5P]QNKLS_bggjfpjZYaRA6/.048CZ_`XUJ<0.-/4@GNNYNT>B>61.+(&###$+<WY][\]a]_^_adcbdiijgjeecdchefglkkhkiifhfkjjgfgkjllmnnjkknilkjilkhgjjrkmjonmjjjjjjjkjkmqkljqlkjkjjgjknjnlsoqljjjjjjighfdeiglklkmmmlpkjiljkklihhghhgjghhigeefhonrrrllhdK92/6Lipponmmnmnkplmiggjkolmlijigllpkjiih–––”–•’‘‘““•™—š™˜“Ž–““’•”—›—•”“–——š›œž ¢¢¡ ¡ ŸŸŸžœŸ ŸŸžžžžžŸš›˜˜–•–•““””’•›™›™—•’‘Ž‘‘’ŒŒˆŒŒ‹‰‰‰‰Š‡……………ˆŒŒŒŒ‹‡†………„„†…†‡‰Œ‰†††…†…„‡‰ˆ†…‚‚‚ƒ‡Š‹‹‹‹Š‰Š‰‰ˆŠ‰‰†ˆˆŒŠ‹ŠŒŒŠˆ‡ˆ‰‰‰ˆ†‚~ysgYNDDDDFKJJHDBGFHQh‚Œ–¨¶Ê½ÉïõùýþþûøçÁ§§£¢¤¤‰{”“„•¤¡¡’…~xsmh`XOF?=;=@?>=>GcÍïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûõðç¯b:/--.-,---,-+,/+)('%#"! ! !"""#$$%$$##########$$$&%%%$$$&*-/,-Fk[XTRU^]\eVgegiSOHC<70,/47BLSQNHB;29?@BGRNKJA:///,'&%$$#"$$,BVXYYZ[[\]]_`bbdggfggfddbddheggkjiggfhdfeggiggejjjihfgghihghfjhhhhgijlijikiiiiiiijjkkmmlikkmkkjkjjhijjjnklllijljjjkifeegijgihkklmmllkjjkiihhhgfhjhfffhfgeddhklmmllljeN;98=E[oopoonmnnlikklhhhjkjjmlkiiilkkjiiihš”––•’“•™™˜——–”“‘“˜”““”•————š“––—œ¢¢¢¢¢ £¢¥ ¤¡ ž§¢£œžššš™š¡¢¢™›˜œ•–—˜™™”’‘‘’‘’”މЉˆ†…†ˆ‰ŒŒ”“ŒŒŒ‹‡‰„ƒƒ‚ƒ‡…‰‡ŒŒ†‡‡‡††‡†ˆˆ†‡ƒ‚„ˆˆŽŽ‘ޒЉˆ‡Šˆˆˆ‰ŠŒ‹‹’Œˆˆˆ‰„ƒ€zwhXKBBDEIJLJJHFEBJXnƒ˜¦¶É¹ÁíõùüýþüúïÍÁ»¡˜•‡w~yry”–˜–’ƒwtqhe\VMF@=<>?>==?HdÐðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõðæÀ¨^9/-,-----.+,)(''$""!! ! !!"!!"""###$$$$#"####$$%%%&&%(&%%$%(4A73@Z\]ac`[VQ]hYf\WPD?=:761./36<ETRHA?9;LQUOMQRCE5-)%%%$%$###$'%.FVXZYYZ[[]]aaabegigngfefdhghhhjpjmggefghfgglgihkhgfeefeijjjjjiiijjjjjmknmmijihinikknntnnjmlonnkklnmnoonnnnmkjkkllmihghfjjommiqknnrlnmpmokkhmhfggggghhighfgdmnnmnkmkkS>7:BM[qqrqvqrmplkilllkkjlkijqmmjklpklihilg“‘Ž‘‘”–——˜—–••““”•—˜••˜–––”––—”—˜œž£©¥¢¢ Ÿ¡¢¥¡ ŸŸ Ÿšžž¢¢£œœœ›žžžž›™™–š’–”™•˜“””’‘’’ކ‡……„†‡ŠŠ‹ŠŒŽŒ‡‡‡‡‡ƒ…„ƒ‚ƒƒ†„†‡ˆ‡‡‡†‡‡…†‚‚ƒ…†‡……‡ŠŒŽŽŽŽŠŒˆ‡…ˆ†‰‹ŒŽŠ‰‰Š’’‹‡††„ƒƒ€€|yvfWLACEKKLJJJHGFFO\|ª³¹¿¸½êóùüýþýüøïâÍ¿³ —Œ‹sziefy‹Œ‹ƒ{sp`[UQKE@?@BEA??@HfÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõð໡[8/--.-,,-+*'%#" !!!""#"""###$$$%$$$##$$$$$$$$%%$&&()*+6BEDHUWpnnno_UZ\SLLKE@>:977531347:JUUGGGECQ\ZZH?9/+''#$#####$##$$%2LUVVVYX[Z\]^_abffhfggfcdeefhjjjkjhdheffffgffgihgefceeedigiijgjiijiikkmjkijijhhgmjllooolkikkooliklnmmnnmmkjgkkjdhggffeggjkkkmilkmlmllllllkjijiijggghgghfffffjiigmjmjS>:8DP_moqqovqqmojkhhhjjjjljjjlkjjlmomnhijmf‘’––—™ššŸ——–—•”•˜—›˜˜˜š™—–˜–•–˜šž¤¡¤¦©¢¥¡££¥¢¥¡§ žžŸžžž¤¤¥¤¥Ÿ¥Ÿ£££œ›š™˜™˜™”–“˜˜˜™™——”’“—–™‘ŽŠ‰ˆ‡†‡‹ŽŠ‹ŽŽ‡Ž…‡†††‡†„‚‡ƒŠƒ‡†‹‹‹Š‰ˆ†††‚ƒ…‡‰‹’‘‘‘‘ŽŠ‹‡‡ˆ‹‘ŒŽŠˆˆˆˆ–‘’ˆ†ƒƒ€€€|xxgXKCELKNNOLJIGFIWy¦ÄÂÀ¸¯ËêõûüþþýøôîäØÎ°˜Œ}wcZ_j~ŒŒ‚qn\UQOIC>?GMHDDBAHjÕðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîß·šW6.---,)('%$"! "!!!""#"""""##$$$$%$#%&$$$$$%$$%%$$%%&&*-7?AFTie`WXa_^\aiNB@BBA?;::7777789::<F][USSD<J_QGA<1*'%%&"$###$$%$%$#%6MSTTUZY][____eegfjhiihceegfiillligghfhghiighilfddeeeegghhiijhjjkkllmlmkmmliiijjmmnmpnolljkkqlkkqmnmnoppqkkilljfjgeddegjqmllnlnmrnmklmrmlmmjkjjhffghffjfffnilikhmkpYB44@Qbvnvttrvqupoljiijoopkmlliokjjqqsnohjkog‘‘’•™š™™™™˜˜˜–––—˜˜˜š›ššš™˜”––—™œžŸŸ¡¡¤££¡¢¢£¥£¢¢ Ÿžžœ žŸŸŸŸŸžšœš™™œ™›š›™™˜š””•””ššššœœœ››˜—••’‘ŽŽŽŠŠ‰‰Œ‹‰‹ŠŒŽŒ‹†………†††‡…„‚‚ƒŠ…††‡††‚„‡ƒ€€€‚ƒ‡ŠŽŽŽŒŒŽŽ‡Š‰‰ŠŒŠŠ‰ˆ††„…„ˆŠ‰‰ˆƒ€€‚‚€|~xwsfYODGLNPONLJIFHSq£©¯¸Èº²ÑñúüþþþùôóïçØÈ²•…zfc^gu‡ƒ{tkaUQMJFD=APPQRTNDJmÝñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúôíݹ’S4-+)'&$#! !!!!!""#""""##$$$%%%%&%&$$&%%%%&%&%&(+.14@OLSX][[\Z]fedLEA;7568656865569@<=>BJQ]d][P@;XOA70+''$%##"#####$$#%%%&8NMOSSVWYZ\\_`bdedeeebgdfgggiiiihfffgfffhigfhifddbeeddffhfgghhjgljkgmjjjkjiijkjjlmnkllnjjkkkkhjjjjllnoollkjgggggggddeeilllmnnklnmmlkkkkkmnjgihhgfdfhhhdbfggghhiimhZF;2?L^nmnooqqqqrpomlkklmllkjjkjihkjqpqmlilnng“”—›¡›ŸŸž™š™™¤œ¢œššŸ£››Ÿ™š™žž¥ ŸŸ ¢££¡¡£¤¤£¤££Ÿžž›© ¢Ÿžžž››œžŸœ¢œžž š˜™›››žœ›››¡ž¦ž˜˜••––“‘‘•’”Œ‹‰Š‹‹ŠŽ‹’Ž‹‰†ˆ…ˆŠˆ‡„ƒ„…†‹ŒŒ‡‰†‡‚…‚€‚‚ƒ‡Ž‘™’’Ž’ŽŒŠŠ‹Ž‹‰‰‡‡…„ƒ†…‰ˆ†……„ƒ‚‚‚ƒ…‚ƒ|~zywtj_NFGPORONMMIHNf‰ £®ÂÔ´¼ã÷úýþþ÷ðóõðêæßŶ”ydr‹ˆ€vldZSOJHFF=CMddghbLLpÜòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÛ¼‰M.(&$#"" !"!!"####"#$#%""#$%%%'&%%%%'$$%%'))('''(0:FTW[TMTo\UXs[dPE<40.--021/./23477<>=ERQ]\]^aZQ<:HF5*)%&'''$$$%$&$$###%$$'9MPNRSUVYY]\badbfeeeddfehiliihiheeigiffejgfgheddedhdcdhhhhiglkjimjkjnlrkkiijkmpjmlokkkljollkjjojjjpmqppklllgghlkpecefhnmmlommkklnlllokjjmjgflhhhhefhjeabmikhiiijq`O;6;I\nosnqoqpssspooppopplmlpmmihiomqqppqqrnmi—˜š›œžš˜›œŸ£¡›¡ žŸ¢™™™ŸœœœŸ¡ ¡££££ŸŸ¢ Ÿ¥ ¢—™•œš›œ¢ Ÿž¡¢£ŸŸŸžžžœššœœŸœœšššŸŸ œ—–••–—–’‘‘ŒŒŠŠ‹ˆ†‰ŠŠŠ‰ˆ‰†‡ƒ‚‚‚ƒ‚„…„…‡‰Šˆˆ„„€~…‡‰Œ’’’’ŒŽ’މ‰‡Š††„„‡†………„…„ƒ€‚‚ƒ|||||zyj\PDHLOPNNMJHIZy†‘§½Â¯®ÕðøüýþõìóúöòðìåΤ¡Çƺsz‡~qg\XROIGDA<BJ`lt{gQNrÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùóìÙ¼E(#!! !!"!! !"!!!""#########$#$$%'&&&&&%%%%%%%'.6=3/49:>N_WQYVSQm\PHOYH92-*''',0.,+*.2479:?BFLTUZOOPQSB45G6*,-,-5A0&$##""""""###$)=JPNSSTWXY[\```_aba_cdddihhiigfeeefeddefggghhceeeefdefihiefgggkijjkiiikkkhjmmmlijkokkkkjllifhikjjjlkljkhgghghhkihcehiklmmmmllkkklkkklkjhhdeflhheheeddbcdihjggfkmcRD8>FXmklnllmnnooomoqpponmlllmmmeikmnqqqpqmqnlh šœŸŸžž™œ¤¥¦£¡¡¡¡¡žŸž£¢¢œ ¤¡§¦¦§§¡¡¡¢žœ ¥ŸŸ £¡ œ£¢£¡ ¡¡¢¥¢¢¢¢£¤¦¦Ÿ£œœ¡žžžžžžžž¤ ¡›œ–—–š–™”“‘’ŽŒ‘Šˆ†ˆŠŠŠŠŠŠ‰‰€}ƒ„ˆˆ‰„†…†‡‹ˆ„…}ƒˆŒ‘•’“’’Ž‘‘’’“’““ŒŽˆˆ‡‹‡‰„…„‡††……††„„ƒˆƒ„…‚ƒ„€}{vj[NDGONOONLJKN`m€ ºÉ·Ÿ¹âñùûýøòõùøøôñìݪ´ÒâØÎr‚|mbXVQOJHB?;>H]m‡€vVQuÝôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòë׺w?%! !!"!!!!!"!#######$&%$#$$$$$%&&&&'&&&&%%%&%)>`L?DTQMSe^_WmQ<IpYM;861,&%$$%'++*)((-38=BCFMRP]\]KD@<72//.+,05;=HD6+%#%"#"!"##$#$+@JQNSSSUXX[[c_^_a``_dcceiiiiiffeeegecdhghgjgfdihlehhjjjjjfffiglkkjnjkkkkkkllrmkkpmnkqkjkljfegilllmmklmnhggjhjilgdbfimllmomnlqlnkkkrlmkmhjbcglijihfkdfeeelijgghlf^H=>HWlmnkqlkmonmmmnsssppnmlmmnmlmpnppsqrqsrrnkh—˜Ÿ ŸžžŸ¢¤¥¥£¥£¡¢œœœœ›˜š™›ž¢ œœ›žœžžŸ¡¡ ¡¡ žŸŸ Ÿ ¡¢££¤¢ ¡¡ œœ›¢œœœ›œŸžžžžžžžž›œ••””•˜’ŒŒ‹ŠŠˆ†‡‡‡Š†„ƒƒ‚~‚ƒ†††……ƒ„„„…‡‡‡„„„Š’“’‘”‘’’”—“ŽŒŒŒˆ„„……‡…ƒƒ„……†‡†ƒ…ƒ‚‚‚„„ƒ‚‚‚ƒƒ„|yvi[PDHOOQQPLJLPZf‰´»Â«š¼éöúüú÷øøûý÷òéà®´ÛÞãÕª’„|i_WSONID?=;=DThwyYRxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøòëÕ¼q<#!!!!"""$"!""#&%%%$$$%$$#$$$%%&&'&&&%%%%%$$$$-F]X[]Tt[__]TLML97?F7*)('%%$$$%'((('()2<GQNLMSQNLID>60-+*)+*26>HFEEE;1&"""""""#!"#%.BIMNPPSTUVZZ[\]__^^^dbcdeeefecedfeffeeffffgfedhggehghhhhgggfhgiikkkjkjkjkklllikkmmnkkkllmhfehilklllklmkhhhihhigddcimmjljllnlmjnlllmlljiic_dfhgggiddcdeeehggghhj]O@ADUejklkmllnnfhjmnpppppnnmlinlllnmqppoqorsrmlh˜šŸ¡ ¤¡¥¤©©¨¤££¤£¥žœ™˜’“•›¢ ¡œž ¤¢¢¤¥¤©¤¨¢¡¡¡Ÿ¤ Ÿ ¡§¢¢ ¥ Ÿ¢ŸŸ¤£¢Ÿ¢¢¢›œž£¢¢¡¡ŸŸŸŸšœ—–•”•˜‘ŽŽŒ‹‹ˆ‰Ž†Š…„ƒƒƒƒ‚ƒ…ŒˆŠ††ƒƒ„Іˆˆˆƒƒ†‹•“™–•“”’’’”––“‹‹‹†‚ƒ„ƒƒ‚…‰†††‰ƒ„‚ƒƒˆ‚„…‡†Œ‹Š‰‡…ˆ„{zk]NEHPOPOOLKLO[u–¯¸¿›Ìïöûúù÷ôøýú÷îÚ¾¶ÊÕÝßÞ×´Žr[SOONFB><;<AL^o€~ZSxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷ðéÓ¾m9#""""##"######$$$$$$%&%$%&%%&(&(''&&%%%%%%$$$0Qv]j[WbYaicVKW=-,-,(%$$$$%$$$$%&&'''*<I]RdYVSRCA82-)''*,-/3BGPLDDUFB.'!#"!!!!"!""&2CJNORPSSTVYZ[Z]]]\\^cbddeeffedjgfffffffimijffeihhgfhfhmllgigkhhjpkljmnmkomlkmjkjmnnnnmpmlhfgiillrlllpjjefhkkkhgfegppolllmlnmonomooplmjke_blhjklgjeecefifigggmhiVGCFRffjjnmmklnnbbippqrrpsoqoqlnnnnomrpppqoqrroljš››œŸžžž¡¤ ›¥¢£¦¤Ÿ›—•”“‘‹ŒŠ–—¤¡¡Ÿž¡¡¡£¢¢¢££¢ ž›š™š›£ž ŸŸŸ¡ ¡žŸž¡ŸŸ¡Ÿžžžž›———˜™™™™š›ž™˜—˜™”’”ŽŽŽŽŒŒ‹‰ˆ‡…ƒ‚„‡†…†ˆ‹‹‹…††…‚ƒ„…„„‚‡…‰Ž’•’”“’’”ŽŒŒ‰‡†„‚|~€‚ƒ…‡‡…………ƒƒƒ„„„ƒ„…‡ˆ‹ˆ‰ˆ†„…„‚€}xj[PEIOOOPNLJMTh†›®«¢ ØóùúûöðöýüüñßÎÈÄûÕÞåݳ†dOLJHDA><;<>HVm{~[RxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïçѽh8$"""""##$####$$%&%$%%%%%%%&&&&&'''&&%%%%%%%$%3Ye_bWVo]igq^G:-)%&&%%$#$$$##$&'+-+&(-AMMNSXJ@80,('&(%,149;<HWQU=6760)$!! !! "!!!"(8FIJKMMPSTXWVYZZZ[[\^cbbbcccceefgggghfehkkigefdjihfghgfffhggfkijkkkliiiiionllkkjjmmnlllkkliiiijmlmiiiiefcghiigeeehlkjjhmkllnkmlmlnjjjkifbdffgikjdfedcegfdffghgbZLJHSaefihnlmjih]Sbkoppnrprnnnnlmmnnnmmmmlnopqronnœž¥ž£¡Ÿ›¤¢¥¦¦™“‘Œ’’’ŽŽ˜ª©¨žŸ ¡¡§§§¢££¢Ÿšš˜™›ž£ ¡ Ÿ¡¢££¥ŸŸŸŸ š”’’’’—•——˜—˜¢œ ™˜—•”““–ŒŒŒŽ’Š‹‡…†€ƒŒŒ‘ˆ‹Œ‹Š„„„‰†…†ƒ„ˆ‘““—’’“”‘–“•”™““’“ŽŽ‹Œ‰‡„……ƒ~}{}‚ˆ‰‰‡‹ˆˆ……„†„„„‡ˆŒ‹Š‰ŠŠŠ†…„…ƒ†ƒ„…†{k^QGIPOQOOLMYczŒž°¥Ž—Ãíöùû÷òóôùýôëäÝÆÃÄÏÛßÜÛ©nOIHGDA=;;:=DRl}}[QyÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïæÍ»d7%#""#$#"##$$$%%%%'%&%%&'&''&&''&&&&'''%%&'&(&6`seqVUTUiefe?-(&$%$$###$#"#%-75787('.SRSEDA=1($"#$)-17>E?>FPRQ?/+*'%#! " !!!)<HIKKOMPRUVVUYYXXY[^]cbbbdbbeghggghkhgfkkkigfggjjkhiilghfiffgkkmjjkliiijlonmllkjjonnllkjjllljqlmlnjiiifffihihfefhllkkljmlmmnkmlpknnnimhfekijiihhffedfjgfdggjhh]TNOVbejihglmmii]SXlquutsrrrssssnpopnpnnlklponoroomœžž ˜ž›Ÿ¡¢¡Ÿ›“’’““–—•‘’’™›šžŸžžžžžž£ Ÿžœ››››Ÿ¢š™—˜™¡¡ ¤œ››œœœ˜——–•—™—›˜œ™—š™œœš˜••””“”ŽŒ‹‹‹Œ‹‹‡†‡…‚ƒƒ‡‹‰†ŒŽŠ†…‚……………„‰Ž‘•”“”’’‘’’‘Œ‹ŠŠ‰ˆ†„‚€}}{€†ˆˆˆ†ˆˆˆ„„„„„„…‡ˆ‰‰ˆˆ‡†…„„ƒƒƒ„„ƒƒ…~{wkaTGIMOQONPhpz‹”¡¦Šw™ãð÷ûøõðëõÿøðæÔµ´ÄÕÙÙÚÙÕyQLGEC@=;:;=ENb}tZOwÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöîäʹ`5%$###$%%&$$%&%%%%&&&&&'&''('&&&&&&&&&&%%'&&&(6f`WSECEOhZTB/)%&#$$#!##$$&*29@EIO8+*.;;72.*'%$"&(2BCF=WMC>QB:2+'"!!! +?IIIILMQPRSRQSTVWZ\]]dbbbcadihihgijlgggjjkhgghhjjiiihhfgfgghhkkkijjjiiilnnnmkkgjjllnokgiimmljollllhiihggfhgfdeejmmmllkilllknjklkkkkmilgggggjjighfffghgggcfffc`ZVRZbdgghhflmkecT]hnsvqrppqrppqsnoopnplllllonnnponm ¢¢¡ Ÿ Ÿ££¤Ÿ›šœžŸžžœ›¢š™›Ÿª¢Ÿ¢ž¤¡¦œœ£››™™——––™™œž¥¤¤›œ›žžž˜›œœœ™˜—˜œ™šœš›Ÿš›—š—˜’”ŽŽŒ‹ŠŒŒŒ‹Š‹‡Š„ƒ„„†‰‰ŽŠ‡‡†‚ƒ‚†„„†‰Œ““—”••˜”–‘“ŽŒŽ‘‹Š‰‡ˆ‰……€€~~€…‡Š‡ˆ‰‰ˆˆ…„†ˆˆ‰‰ŠŒ‹‹ˆˆ‰ŒŠŠŠ‹ƒ„…ˆ„ƒ€~{zpeRFHMNOOTfƒ†˜¢“ksãòùùùôîöýúöìϺ©¼ØÕÛÚÚÙ†p]QGD@=;:;?FLXfpVNqØðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõíãÉ·]3%%#$$$$%%$%%%&&%&&&&&%&&'(''&'&'&(&''&&&&%%&(6OWGC817LON=/(%$&$$###$&(,1:JIZNMG;2)*60)&#"!"%'-6?IKFALW@?93,$" " .BIHIJLMQORQOOPRUX_]b`ebddccihmllnommimgnkjjkhijmmnjkiigghklmkkjplpkiijkpnuoqkkjjkolnmkikiqnqmomlllhkjjhighfecfjsnponmnlllllnirkiikklijiogiinllgifefliihmdfff`_ZY]hgigjjjkllje^_nostƒqwvvqrszttsrpppqmnllmommntqun››œ™™˜œžžœšš››Ÿœ›› ›š››œ—š—™š œžŸžœ››š—”–—˜˜—••–———————–”“””””•–œ™™˜›œœ›š›œš———˜–“ŒŒ‹‹Š‰‡‡‡ˆ‡†……„ƒ€ƒ†…€~‚‚‚„„‰‘‘”••”••—’’ŒŠŒ‹‹‹Š‡‡„…†ƒ~|‚‚‚‚ƒ„†ˆŠ‡‡‡†ƒƒƒ†‡Š‹Œ‘Љˆ‰ŠŠŠŠ†Š„……‡„„‚€€}wl_SGIMNOTe€Œš¥§ˆn\f€¹ëöúý÷ñ÷üüüòÞθ«¸ËÜÚØÙµ£œŒtNF>=<>BHKNPMIHjÓìùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõíâȳZ3&$#$&&%%&&&%%&%&('&&'&&&&('''&&&%&&'&&%&%%%&(1A:0-+**182+(%%%%%%#&(,2:AEGKMLLHC99'%%$#!""*/37<LMWGDDB=9.'%"! !1CIHIJKKMOMIKMRUVX^]___`bacciimklmmmmhihmkkkjgklnmojjjjhiilmlllkkjjhhgjlooppmijjlmmlniihihklllonkkkgjijggghddchnmnnoppomlkllljlhiiihhgkihfhilmhcdefhhhhhhdedc`\U\dfffejilniggghimortrpvtvrstttsqqponnmnllmolmnommmœ™š˜™šž£žž››£¡§šš›››™˜›››žŸœšœžžœœœœŸžœšœž™•–—˜™™˜–”••——–—””•–“••œ–™–œ Ÿ žœ››œ™˜———š““’Œ‰ˆ‡††Š…„……„‚€‚‚‚~}‚„…‡Š’š”—–•–––˜’–ŽŽ‹ŒŒŒ‹Œ‰ˆˆ‰„„€|‚†Žˆ‰‰ŠŠ‹ˆˆ††…‰ˆŽŒ”””‘‹ŠŠŒ’Љ‰‡†‡„…‚ƒƒ…|vneTHHKMU`yŠ«ª©‡hZVj’Ðò÷ûù÷õôùýöîÞ˳§¾ÙÛÜÞßáâáÌŠWG==?DNOIFA?Cd¿ÝñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõìâÄ®W2&%%%&&%(&'&%%&&&''&&&('((((((('&&'''&&''&(&&')**(&&&&''&%%$&.3+()-4>AQR[WUQKIF71,%$$#! #-:BMKGNTNF6-)&$! #6EJIJJLKKKKHHNTUVX^]``cccbfcijnkpoqnmjllljoonoposmojnkmklioosnolnjkhhimotonmjjllnmmmmjijllllnlpmkjjjjjjiogiddejmoooosrunlkmllkojkllijhligfgjkigcdffhjjkijfhded[Ycchhhgjkpjdflmtrqqrrqpuuvtttvvvrvrqnonnonmplkmolmmš—˜—˜›œ›ššœ››Ÿ ž˜š’–“›•š ¡ ž›žžž ¡Ÿ›ž Ÿžž›š™™š›žœ›—––˜–”“”—•””–˜••–™šššœ›œ››————“–•“’‹ŠŠ‰ˆˆƒ‚ƒƒƒ„†ƒ‚‚|||||~‚…‡‹ŽŽ‘‘‘‘Ž‰Š‹Ž‘Šˆ…ƒ€€€‚ƒ†‰‹Œˆˆ‡‡†‡…‡‡‡‡Š‹ŒŒŽŒ‘“‹‹ŒŒ‘Œ‹Š‰ˆ††ƒƒ‚ƒƒ|yqgWIIMR[j„¹¢‹s[VWw¯Ýôùûüôìõþû÷êÒ¼´ªÇÚßäèèèëëÝ“WF>@FO]NC?<C_µÒèõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøôëÛÁ¨U0&%$%$%%&&&&&%&'''&&&&''''''(('''''%'&&&&&&&&'(('&%$$$%&$##$-=969=AF@EQVJ?<:5/*'+*29.*&#*AETINJNGA3('%$"!!%;GGIJJKKKJIHLPRSXYZ[]^`abbbciillnmonkilkkknnoooonmnjjijjjkponlpjjgkiijmonkokkkkklmniklllkilllklmkhhfhhjiigfdghikmllmppomkjlmmkkjjiigjhjhfehkgcc`figeeegfggfbee_]_`cdfgjmmehklmspppqqrnqquuutttwqsrqoooonnkmmmlollk™™™˜˜››¤š™šžŸ›™š›ššœž £¤©¤¤¡¢¢¢ £££¢£¤¥¡¤ žž› ›žœŸ š™˜˜˜˜–•••–•”––œ••–›š™™œžŸšŸ—–••‘Ž‘“Š‹ˆ‰€‚‚‚„‡ƒˆ{|yx{€‚†‹Œ‘ޓޓ‘”““‘Ž‹‰Š‹”Žˆ†ƒ‚€ƒ„†Œ‹‘Œ‰‰ˆ‡†‡‡‰‹ŒŽ‘“ŽŽ”’’‘ŽŽŒ‹‡†…ˆ…ƒƒƒƒ‚ƒƒvjVIIMT]pˆ›œ˜lVT]‰Ìî÷ùüøõùýüûóêäÙËÂÆÊÙåâßáãâÚˆSHEMRZVC=<C]©ÓåóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÒÁ¢R/&%%%%&&&&'&&&&'''&&&&(('()')(&''''&'&&&'''&&&'%%%%$$$$###$+5FJJHOSB?FTFA3.*&#"%-7ED::@'1IKLLIG9-($#%'&$" !)?HHJMJKKJIIKQPRSYZ[[^]`afejiijooompljiklonqosptqpmnnmjjlssqnomqjjijknmrooonnonpkkmoilmqnqlnmnkllkiifjhpjjfeglmopqnqmqpnlklpmmllkmjjjjjlhggmihbdegijegfhgffedhde`cccbfgkkkhonposqwrttsqvtzyxvzuxwwsqpssronmpnpppmll™˜™˜——›››•™žž ŸŸŸŸ¡¡¢¤©¦¤¢ ¡¥ žžŸ¡¥ ŸŸŸž››š™™™š™˜—–•’‘ŽŒ‹Œ”‘‘‘’’’••–—››š––’“”‘ŽŒŒ‰ˆ†|€‚ƒƒƒ‚‚~}{{xz|ƒˆŠŽ‘ŽŽŽ“”Œ‹ŠŠŠŠ‰‡…„†‚}€€ƒ…‡ˆŠ‹‹ˆ‡††ˆˆ‡ˆˆŒŽ‹ŽŽŒŽ‘’’ŽŒ‰ŒŒ‰††…ˆˆ†„†††…ƒ~rfVIJNT\w‘œ¯£}ZTXb–ãñøüüýýüýþûöñêâÊ®·ËàÙÊ¿ËÞàÀˆgb]^aWF>:B[ ÖãñúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÐÁ™N.'&&%%&&&&&&&&&'''&&&''&(*(')('''('''&&&&&&%%$$$$%$$#"#$).15C\RRHZL>BE<1+%$"#"&)9KNXJG;+1NA;61,(&!##0F?;-# #-CIIJJKIHHGIKOPSSVXY[\\`bccefhkmllmljjklmoooopprpnjihkklmqspmmlkiiiklmmmmmkllmmqkkkkilmonpoolkijijjifjihgffgimmnmnnrknnnjlllkkklkkkjiihhghhhedbefghiddcbbbbecb`^[[[\^beefhhkmnoqnrqpnrprrrqsrqpooprqnomrponomnmnlmm™šš—–š¢ž£ž¦ ¢ ¡¤¦¦§¤ ¡¢¡ ŸŸ¡¡ ¡ ŸŸš™™šš™——˜—”•‘“ދЋŒ“ŒŽ’•””–Ÿššš›™™“”ŽŽŽ’‘‘ŒŠ‡ƒ~€Š‚†‚‚€}|}~}~„‹‹’”‘’‘’”‹ŠŠŠŒ‹ˆˆ†„…†ƒ‚‚„…ЉŒŒ†„„…‰‰‹“““ŽŽŽ‘‘“’’ŽŒ‹‰‰‡‡‰†ˆ‡Š‹ŠŠ‹†…{qfTHINTm“ °ÆŒ`TRWp¯äòúûýú÷øùùúõðëàÀ®¼ØÐÿ»ÛÞ϶²“}m`N@?CZ—»àîõúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷òéοK.((&&&(((&%&%&&'''(')&''((((((('(('''(*'&&&&$$$$#$$$###-<@ADIYVQJIF4,+)&! !$+/8IRUWVH7,131+%$##$#%/:FE?8)" ! %2FHKIIIIGGHKKOPSSUVXZ\\`addggilnmmmononssspqoqrwoljlkoopoqqolmlklljlmsssllllnrprqqjkipnrqtrtlkiijkjmjpjighgjlpmonnornsmooommklkomqjkjmhkijihddclhjiiccaa`_`dcd^ZYYZ]^abcejillopvptrqmqptqprtqnmkklnrrrnrqoosnpnrqqm–—˜—™›š™š›œœš˜•”“•—•Œ‹‰ŒŽŽŽ‹‰ˆ†ˆ‰‰‰Š‹ŽŽ’”’‘’“••”’’’‘ŽŽ‹‹‹ŠŠŠŠŒ”””‘“•”‘ŽŽŽŒŒŠ‡‡ˆ†…‚€€€‚‚‚€~|}}~€†‹Ž‘Ž‘ŽŽŒŠŠ‰‰‰ˆ„„ƒƒƒƒ‚„†‡ˆ‹‹‡†………ˆŠŒ‘ŽŽŽŽ“’‘”ŒŒ‰‰ˆ‰‡ˆ‰‰‡‰ˆ‰ŠŠ‡‡†……~wl`UKMOd–¡¯±žkURRcºì÷úý÷òóôøýøóñ騫 ²ÁÀÁÉØÙÛÚàÙÓ¤ygVJHPZŽ«Þëñ÷üþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷òè˼‰G.('&&&''(&&&&&&&'((''&'((())**++++****))(((('&%%$$*/+&,;AVP]NTQO=2+&$#"!""(-4<BFO[SNKI0$&(&!"#(,/17DISLD5," '7FHIIGFFEGIKKNOQRUUXY\\`adefgjmljjkjhmmqppnonqqrlkhklooonqpplmmjijknoronklomnopolkjjjnoqpspokkhiljjlkjhgfgdjnmlllmonllkolnooijinjigiihhgghgfbcdlggheba_``__cbc]ZWYZ^_aaceffhjklllhenmmnpppimpnlkeeegiijkkklkkpnopon—™š—œœœœ›˜”‘Œ‡‰‹„|upliikmnnnmlheginponlknquz‚‰ŒŽ‘”””‘‘’“ŒŽ“‹‰ˆˆŠ‹‹ŒŽ‘’’‘Œ‘Œ”””Œ‰‰ˆˆ‡ƒ‚ƒ‚ƒ‚‚‚€~~~}~ƒ‡Ž‘ŽŽŽ“ŽŒ‘ŒŒ‹Œ‹‰‰Š†ƒ~€„ƒ…„„„„…ˆ‡‹‹Ž‰†‡‰‹’’“’–‘”ޔޒ•’’“—”“’”ŒŠ‰ˆ‹ŒŒŒŽ‹Š‰ˆˆˆˆ„ƒyumfZONZ~¡¦¡yZRRVk’Îñ÷ûù÷õôøüú÷ôìßž˜¨¿ÁÓÔØÜàæêÒÈ€taRW_k‡°ÞåäìóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöñèȾ‚E.('(('(''''&&&(((()''''()**.02356531/////.-,+*)+,.3>G*0CGNPPL>50,'"!! %(-4?TJXWWVQIB6)"!! !*7?NIHPXYYB5*#! *?FHHFEFFGJJMNQPQTVVXX[^dbcfghkmljjjjklnvpsnpotuulmnpppprrwppppkhirntptonmnmmowqokklomsoupsonlpjjkkkmiheefggklmlmlmnomkkomnoplmlmiigjhggiijgedefkgfecbdad^^`cab]]ZY]bbdabeifgilhe_[dlnuqupqginqljc\^acdeihlklkqpppnn•›š—–“Іytpmjgfeedcbabcdfhgfk[^TZVdcccbab^_behmtz…‡ŠŽŒ‰Šˆ‡††ƒ‚ƒ‚„†ˆ‡†ƒ‚„†‡‰‹Œ‹Š‰ˆ‡…ƒ‚~|||‚‚€~}{}}ˆŽŽŽŠŽŒ‹‹ˆˆˆ‰Š†‚€~‚‚„„„†††‡‡‰‰‰ŠŒŠ‰Š‹‘ŽŽ”Ž‘””’”••–’ŒŒŠ‰‰ŠŒŒŒŒŠ‰‰‰‰‰‡ƒƒ}zvqo\LTh}ˆ“¤{`ROSXw©Úôúûüøôøûûû÷ñäĦ——¬ÅÊÑÕÝãçßЬƒvqrurrˆÀØÎÃÊéõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöðçÇÀ{B-''''&((('&&&'')(((''')*.2469@=<<<==<998521/3<::=?DK@,.WCIA;4,)$####&*-3;GGHPFKPIB7,)'# !%)/8AKLAIWIA<4+#! !.DEHGFFGHIJKMNQPRUVWXX]_`adfijkmkhjjkloppppmonmnnmmmmmoprtsoomljjjmmsponpqolmomlljlnmlrooonlmkjiijjihfecfgijklmllknmoomlolnqnmmmniiggffgjf\Xfeefjggcccd`]Waec`_\]\\_bcd`beedc`]VSQ\hlntopmjfikkkic_]^_abedliihlmnpol“‹ƒ|vqkeb_`abaa_bdeeeeimoptvmifeccbaaba``b^^^^aaejpw~ŠŒ““”Œ‹‰‹‹‹‰‡~|z|~€}~€ƒ‡ˆ‰‡……„……}}}~}€~||~ƒ‰Œ”’—”ŒŒŒŒŒŒŒ‹ˆ‰ˆ‡…ƒ~‚‚„ƒ„†Š‰‰‰‰‹Ž’‘’‘‘‘‘‘”””—™˜˜—–‘Ž‹‹‘‘’”–•”””’’ŠŠŽ‰‡„ƒ‚‚‚‚wlZNWcoxxwZROPR^‡Èæöøûúùúüùöö÷îÚ·Ÿœ¢¨¦µÓÛâå͹ ‰€‰”’‹w†·âÀ«ÍéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïçÇÀw@-''''&)))'(&&''()()''),5@BCA?AB==?D><:><:::9;CHIZYYL:*,;C4)'%" !%)07;>BLPYTRAC:1+%"! !*7<@DIJPHOB91+'! ! ! !!"1EGGHHHHKJLLNNQQRVXZ\\_``bfjpppkjjmmprtuupqrspropnqrrrsqttsoqmlllnrrspqntqsmlkkkmlpllmroqmllpkjhlijhheffijoksmnmommlonnmpnronnxmnoonnijilZQZfhjjjggdhce`[[ceb__]`^__dddcccc_]ZSOMTdgssuoqjhellkjjdc^^^abgflijjmlkoqmqieba``_^^`deeffghkomlnpryu}yzqqqrheeedcdedcbaa`^\abjp‚‡ŒŽ‹‰…„‚~{yvsqrstuuutrrrsvz|„„ƒ„‚…}|z|z|x~z€}€„Š’’’‘‹ŒŒŽŽ‹Š‰‰ˆˆ…„‚„|~~‚…„€€„„‡ˆ‹‹Š‰‹Š‹ŒŽŽ‘ŽŽ’‹Ž‘“”—›™–˜˜–Š‘‘’••””——•’’‘’Œ‰ŠŠ‰‡‡ˆŠŒ†}tl]PUX_d^SPMPPWaÙîöùûýýþ÷ðöüòäÏÀ¨¤Ÿ®ØÝÝͳ¬Œ‰˜½Ñ”h{ª°³®ßêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïæÆ¾q=,'''''''(''&&'((((((*-8DJOGCCCB>?@?<;<@@ADFMQVPIMNLL3&'('$$"##%'+0<MI[NMJMD:50+'$!! "$'*+8C[NNDDA>6,'"! "$8EGFGGHHKKKLONPQTWZ]]]`bdehlmkkijjmnrrsrqooproonnnpppoopqrsmmkmnoqrnpnnoqmmllmlllklkmnonnjmlkghhkijdddfgjlmknmomommmmmnlmmmmmnnmpqmljhijXIWijkjfgffba^_\]adg`\[[[\__``___`^YYWLDQ_adfhkkhdddca_^^_\Z[[\^cehijkkdWPjk[[\]``ddbbbcefoitnrpupxrsstvxurrqmjhjgpomgkfihh_\\^afs}„‡Š‹ˆ€tpliggiijlnnnnmmnnsvy~‚††‡„„|~}~}}||}€„ˆ…†‡ˆˆŒ’Ž“’ЉЉˆ„…‚„‚ƒ……‡…ŽŒ‹ŒŒ‘ŽŽ’Ž‘’’’‹“˜——™–›˜—’‘•˜—›š›™™™ž™›–˜”““’‹ŠŒ‘’“”–ŽŽ‚€yr_SPPRSOMNOOPVi£Þï÷úüýþ÷ðó÷õòíá³³¦’´ÏÙãàߪ“°×Û¡mf…¥«¨ÌäîöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôíàÆ¹j9+(''&'))(''&(()()((*.8HQWQKHGDABBA>>>FHJQPOQRV^HMIF:,# !!!!"$(.8BGMNJSVKG:5.'$!! !#',3=+/@GNSE;40,%" ! !! !"'?EHFFGHIKLOOPPRSUX\\^_bdghjknkjknkonystppnnoqoonnotpoooqrrsnooooqrsnrstqwmnkllmmnlllppttvjmkjgihljjehggjmnqppnononqnnnnlmkklmmppuomkhhm]LMblnllhiefa`abZ_bgb]YXY^\d__]]]a`\WZSGI]^bbcdheecda`]YYZYYWUWZZacikoprfEFalXY[^^`aaa_acdegijijjihjljjlmmmkhhgfefgghigjfhhfddeimu€€‚‚‚~zvqkc`_bbddfgijihjhlmprvx€€†‡‡‡ƒ|}}~}~~€ƒ…†Šˆ††‡Š‹Ž‘’’‘ŽŽŽ‰‡„„‚‚ƒƒ‚‚„ƒƒ„ƒ…‰‹ŠŒ‹‹‹‘‘‘ŽŒŒŽŒ’Œ‡ŒŽ’”——••–—›˜—•–ššš™™šš››ž˜˜——–—˜“‘‘’“•”…ƒ€yoaROLMMMPOMNP]tåôøûüý÷ðñó÷üôêÒÍØÑÁŸ’Ÿ¯ÄÍÝÉÆÈÝÙ®ukoz€’²Ýàï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþýüúóìÜÆ²c5+)'&&&)((''&&'(((((+/@Ra[TPLKGCCEGFFLZURS^OFJND61-+)%! $+3<>LNWMTFLE>3(%#"!""&*2=?N4)2[C>;8-$" #+AEFGGGJLLMPPPQSUXZ\\_`ceghijojklnjmnrqqnpmnnpmmmnrqnmknrqpnmnmnnpqpnqrrrvnlkmmnkngmmponlkiiiighhllhcggikmlllnmmlonmlnnnkjhllmnppqmnihggQSXemlkjhjbcaaa[V]_\UXVXXZ[\\ZYYZ]_[SSONL[[^__`bbcde_SHB=>?EMKKQSY^ekooqdVOioYZ[[[]bafedbbcccdeeedehhhfdfihfcafdccegijjkmosvy~‚†ŠŽ€ƒ}wrolie``baabcbcegiiiknqptvy{€†‡ˆ‚€‚………„„„ŠŠˆ‰Š‹Ž”•–••”•’ŽŒŒ‹‡†‚‚‚ƒƒ†ƒƒƒƒ…ˆ‰Š‰‹ŽŒ‹‘’˜’•ŽŠ‹‘‘Ž–“™•—–”•˜˜›šœ——™£Ÿ¢ž¡œžš™˜™—š–”•––•”™”–•“‘‘މ‡}wcTMJJMMNMMNSb¾ëôùûýú÷õóõ÷ôñîëé⺤‰†œ²¼Üµ±ËÜÙ¼ˆkk‰}£Ûãï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóëÚǪ^3*(*'&')('''(*()('()+/E_abWTPQLGMRSTUUUnUMPTBA81+%#"! !#" !)7?>GPXOE>6/+'$" !"%/?DFGHN1*5H>/%#! ! $0AFFGHILLNOTRRV]]\\^`ecgflkmjonnnojrrqppoqosotllmnoqnmklnqnmmpqppqonorrtsvnnmpnskomooonlkjjijmkjhlifefillnkkjmlllolkkpopjjhmnnnpprnojkg_YZdpllmnijedbc_Z\e^ZXZYYX[\`ZXYYX\[ZRSNJPZZ`_^^ccidhVF90+)+.39AOQSYcitpqfeappohfeb``aa`aba``abcddeeefffffghfdgjnqtx|~~~ƒ‡Š‹Š€ƒwojfcaa``__`bbbaccceilmmnqqruxy{~€†‡‰ˆ„ƒ††„ƒ„†Š‰‰ŠŽŽŽ‘“”––“ŽŽŽŠ‰‡†‚‚€ƒ„ƒ‚‚‚‚‚‚‚…†‡ˆˆ‰Œ‹Ž‘’‘‘“Œ‰‡‹ŽŽ‘•—–•••”—–•–˜™™™š–˜™Ÿœ™ž››™™—˜–˜š™˜˜—™””‘”””’‘ŽŠƒ}wdSMHJLKKLMORiÇï÷úüýþøóóóôôöøñã·œŠ‹ŽŽ‘–“£ÌÑ×¾žvatƒ|–Íçï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúòëØÄ£X2*''''(('((((((((()+,8Kbs_YSQSTUY`iijLAOVAF=40)%""""#)-,*""0KA?ISJC5-)##"""#%0:?EGNKEFJ+$2J0"! &6DHGGHIKKNPQQUX\\]^adddeeggijjjmjokrppooopoponnnlopqmljllpkmnomlmnmppqqrrpmnnnnnjllopnmmkigjkkkjhgffgikkgjikjnkmlljkkllmjkhjjnmmmmnnjjfb]enmkkhnijfecb]`dc^ZWXXXX\_XSUXWUXWZQRMOSVXYZ]^^^^^ZJ:+*((''(.3AQSV_flnohgfhjŠˆ†ƒ€{vvvqnonnortuwz|zyzz{}€‚‚‚‡Œ’—‰‘‘’‰•“’މzslhd`]\\\]^^_aabbdefhlmplzoppprvvx|…ˆ‹‰‡„††‡„„…‡ŠŽ‘”–••‘‘‘“˜”—””ŒŒ‹†„‚ƒ…‰‰‹ƒ„‚ƒƒ…ˆˆ‰ŠŒ––—”™”–‘’‰†Š‘Ž’•››Ÿ™™•™ššœ™š˜žœœ›ž¡£œ››žœ£™œ›š¡ŸžžŸ™šš˜š˜˜”•ŒˆzeSKHHKJKKLNVt¢×ó÷ûüþûù÷õôòõøñã½›“‘††ƒ‚‹©Ë̽¦ƒus||’½åî÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñéÕÀœR0*''''(((())(+(*(),47@Uqr^WTXghhkmc]T?=@8,++-)&"!"#&/6;>*$#7PFIM>1*$" "%.39FIIKKLXC91%"/*$ ! !);GIIHIIKLNPUUZ[``cdefffijjikkkjmnonrpqosqsttooosstqpmmklmpprpsopnnouttt{rnloppmnjlnvnmnokjinmokliedhkulkkmklkolnmllmllkljkijlroqmnnrkmgghqonjjjnijfhfhhied][[\[[[\[TPQUVUYXXRQRUTVVXY]]^^]]]G0(())((&&+6GVY]dlmnkigli‡‰ƒ„„ƒ†‰ˆˆ‰ˆ‰ˆ‰‹ŒŒ‰‡ˆˆˆˆˆˆ‹ŽŽ‡‹‰‡†ƒ‚€~zvspmid`]\[ZYZ[\\^_`aaaaacefnloolkkopqrvw|…‹‰…‚‚‚‚„„†ˆ””•”””““‘“•”“’‹‹‹ˆ„„ƒƒƒ…†††…ƒ‚‚ƒ„…‡ŠŠŠŠ’“–“’’‘‰‹Œ‹“˜–•š˜šš™•˜––“›™™™Ÿœœ›Ÿœ››œœšš›ž¡¡¡žžŸš››šš›šš˜—•“‘ˆ€weUNFGIKNLLRY}¸àôùûþþþû÷óïôøòçÖµŸ £˜ˆƒ†Ž¬ÏÄÂz{„‰“ÀãíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùñèÓ”O/*'')(()*))))))))*/:BOaroZSY^inif_[KJ4(((&%,356.+058GMT7,($/R;1-*'""!"$+06DHKJXK]NB:3,$" !"! ! ",@GHHHIILNORWY[]abcegghhiijikkklnnnnnoonpqqkspponnqponnknoropopoooooooutsmnjpqnmmknookmmlkjhkkjhhifejlpmlkkkkjkkkjmlllljkjjjloonmjnlkjjilnolnhjiiiifgfgfga_[\]\\[Z\\RHTVVTXXXRRRSSRRVWVSSSTTSG8+)&&'&)''5@[\`ehhijieedŠƒ€‡‡‡‰Šˆ‰‡‘Š‘“‘ŽŽŠ’’’’–‘“Šƒ|zwqmmlfcba`^]Z\]ZWXY\^]bbibfdeacegnkooprsuwxz||‚„ŠŠ‡„€‚ƒƒ„„‡—”Ÿ™œ——˜˜™››š””“—ŽŒŒ‹‰‡„††‰„‰‰‰……„„„‹‡‰ŠŽŒ‘”™•—“”Ž‹ŽŒ‘’“ŒŒ–˜˜˜›š™˜›š›™Ÿ—–—œ›œœ £¢¢¡¢¡¥ŸŸ›™š›ž£¢ª¡Ÿžžž ¡§›œ™˜˜˜™“’ŒŠyhYKEGLMMMOR^ÐéöùüýþýûøôóñðïêâÝÛǵ›ˆƒ‚‹›¶ÏÂÀ€‚¨Êçí÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûùðçÓÄK.*(())())*))*,//-+8DNXgup[X^cujQPUC4-'#!""'2CTB:>HEDOXG61-$(++'#! ")-3?ABJXNGIMQS9*%! !! ! ! #0CFFFGJKNQTW]^^`begilkklnnmnolmnoqsqpowqrqqqttvpnnuoonnntqtqrpsoqprptrxspnnmroomsrsnomrlklmlqjjhhgfinnsnmmqpplmjjlrmmllklinloopnlknkkjjlrssmpjjhkikgghjhg_]\_^]]^_bZRRXVVTYWWTUURPOPWVTQPONOUJB4*%%%%&''4C`cdglgfgjeec~yyz}€€€ƒ‡ytrw}€„~}|‚Œˆ…Š”‰zpmjgea\\\[[\\a^^]_b_\\[\_^]^baaa`acilnpsxyyxwz}}~}}€…‡ˆ‡ƒ‚‚„‚†ˆ‘–––––——™™™˜˜–•’’‹ŠŠ‹‡‡…†…††‡„„„†††…††‡ˆŒŒŒ‘’••’’‹ŒŒŒŒŽŽŽŒ‘•—™™š››ššœ››šœ™š›œžž¡¢¤Ÿ¢¢¡¡¡ŸŸš›š¡¡¡¢žŸ›ŸžŸ ¡¢¡››•˜›šš˜”’ŒŠ†~wfTKEIMMLMMVe•áï÷úüþþþüùòêïôõôìçã϶˜Œ’™šœ¨½µŒ‚ŽÈ׿í÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþþûùðçÓÆ‡I-*)(*)()*+,/39A<4=JW^dstpc]bgeeB.-,)$$"""$+@OMLMMG_PWJ@AB)"#$$!! !-;?B@<DONM<<CJ:+%! ! ! "%5FFEFGKMRUY]^_acgiklljlnnnnonmpqqrrrqpqqpmqrsrplmmnlnnoqspqproroonrqtsqnnnmmmknnponknlljlmnkjgfeedjknosnoopqplkjllqijjkklimmqromllmijilmmnlkjihhihhfillg`[^^^^^^_c`YWUVUVVWWWUXZTPOMNNNOMKJIILE>4**))()+;Jadhkiecaeeecrqtutqnptlfb`djggijggnuvx|ƒ„‚rgecb_a[ZYY[\\^`bc`ddeed^^_^][]]`bfjowy{~~}€€~€~|~„††‡„…‚‚ƒ…‹”£˜œ˜–––˜——––‘”‹‹ˆˆˆˆ‰‹†Š††„‡†‹‰‘“””““”••••’“‹’ŽŽ“”› œŸœžœ¡šœ›Ÿž££¤£¤¤¤¡§£¥ ¥¤¤¡¡Ÿ œžŸ¢¨¡¢¡ ¡¡¤¡¢¡ š›ššœœ–—–›‘ˆ…zycSJFHLLNLNWk§áðøúýþþýûøõö÷ùûøõñæÉ¤œž¥¬‘Ž£²†}~Œ®ÖáîøÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøïåÒÈF,*)))))*.258AUQKMRasjkynh]dXQD0'$##"#%'%%/XO_VRJHLNE;0*)&! "#" !+BFXNLKMB91+/:4*$ ! ! ! #)>EGFGKOT[]_adcggmmplllqprqsoprvssuyyxtsrvtssupolpnnnnnstxpsrrrronorsupmmnnqnomqouqnknllllmokiffcdjupsqtssprqqlllqmplkjkklkpmuqoonnmiklqmlmliihggkjiinmmb[\cccaaaaa_[`XWX\XWWWX^XVRPMLLMLKJJGGHJBC=88965;GTaemigcaaeffeiijkjid`\YUQRSVY``i]ccegvqrppeeeeedceihgdaabbaa_acbbb````a`_`bgmrxwx|€ƒ~~€}|z~|‚…„……„„…†Ž——˜™™™˜—–—˜˜˜—“““‘ŽŽŒ‹Š‹ŠŠŠˆ††††ƒ†…‰‰’”•••”‘““’‘ŽŒŽŽ’”—™š™™šŸœœœ šš›žŸ¤££¡ ¡££¥ŸŸžžž¡ŸŸž¡£¤¤£¢¢¡¡ž£¤£ ¡š˜––—›–‘–—–“Ž‹†|xoaTMFIMLLMP^v°æôøûýþþýþÿýúüÿþýùòã°¯«™‡Š’’€{wƒ‘¯Òîøÿÿÿÿÿÿÿÿÿÿÿÿþþþû÷îåÐÊyB+*))(),6@DEJMW^[Zcusqr|ob`]UJB4.'%$&(.42-*3WMTZNGEC8.)%&''"$'17)!#/CEGK:0//*##$$$" '/@EHIMQVY]_bcffhjmnmlmnooqqrqsuusuwxxwusrurqomiiipnnnmmrtrosusrsoopsupknpnknlmmporqokllmnmmojjfeairropppprppormlmmlnmjikilknmmmpjiihhlpqkljifiggedckjonc\^aaababbb_\ZZZYXZXX[ZY]WWTRMLKMILPIDDEDBBABIKLNNVadghefccbdbfhgimgj`]WTOLLMQSYacicbeipwqlquusrrnmnppyrsciccacaaaacfhijmkjllmosvy}|€ˆ‚~€€|z{~€Š‰‰‰‰Œ•“žœ›™›šššš¡˜›——“”’‘“”””Œ’‹Šˆ†…††‡†‡Š‘’–––—–š”˜’’‘‘“’•˜˜žš›››œ¡œ ŸŸŸŸŸ£¤¥¥¦£¤¢££¥Ÿ¢ Ÿ ¡¢§§ª¤§£££¦¦©§¨©©žŸš—•——›—’‘•—•‘ŽŽŽzvpeVLEGKLNNScƒÀêóùûýýþþÿþýþÿÿþüúïàÚØ¾™‡‹‰†{w}ŒœÚíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷íãÎËr>+,**(),GQYGSV]`biorvxwlbPJLLI;4.+**.5=FL2*3YTRNI7-*(%#*8/**.8EC1%$-ZE;4,'%#! !! " ! " !$-8AGJOSX\_bdgijlrrronmuqrpstttyvzxwx{yytrrutunjhgkpooprqqqqrvuttssvttqonsqqqrrxnpospoopmlmqmnijfegrqvwwvvsrqrprnpprnplhhjklkplllpkmhhjpqrknjfeihgcbemjrd\\bbabfbeef^[\b\\YYYYY]\]Y[XXRPMLLQNJFFGEBBBHJQNZTebkjjcfffffgmlmld^YSQNMLOUWYZ[]ehilxtƒutl€~}uwpqmnopsvlda_`bbbcdeghikmpqomnnqvxz}~€€~}~~{|}‚‰‰‹‘”–˜œœšš›šššš™˜—•”“‘‘‘’’“•ŽŽŒ‰ˆˆ‡†…„„‚†ˆ‹Ž’““”•••–———”“‘‘‘’““–˜˜˜š—™š›œœšžžŸž ¡¢££¤¦¤£¢£¢¦ ¢¤£¡¢¤§¦¦¤¤¢¤¥¦¦¨¦§¢¡Ÿš™–˜˜œ•”“—š—ŽŒ…‚{yfVKCGMMMPUkÅîöùüýþÿÿÿÿÿÿÿÿÿÿöìèåÜ»›‘’˜¢‹z}‹žâðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöìáËÇl<*)(('),?dQIU]]haxmojeb^N9ALNMD:99>CBTMZP5(1693.*'%$#")39;<INTLX=*&)/4-%#! !!! $)1<*! ! ! !%)7DHMSW[_dfgijknpqrqppnurrqtsuvvwxwwvvvtprrrrpmkimsqopqporqrsrortttsrsoonpqpnqrqnqlnopnnmmmmjjhechkrrtssqrrrqqorlnoonnkjiknnloiklkkifjkljjjjkcaijb^chkkdY^bbbccebfib[^^a\\[YYYWYY][[YXXTPONMMLKMPJEDDGHJKOSY__`bccccbgjigjbZTPNLLMPVU[[[XY\`gtspmjkjlnopqqonjihggda^]^``cehjkpruxzwtssttv|~€„ƒ‚€€€€~€‚‚‚ƒˆ†‰”–›¢ ›››œš›šœœœ•’’‘’‘˜“”‘•Œˆ‰†…†‡‡‰Š‹Ž’’˜™˜•–“”•œ—˜”’‘”““”––œœ˜›˜ž˜™›››ž¢¡¢ ¤¦«ªª¥¦¥£¢©¤¦¤£¥ª¬®©¨¦§¤¤¦«ª¨©§¨¢ŸŸžŸ šœœœ˜™šš˜˜”•ŒŒŽˆ†~ydTICFMLOPWsžÕñöúüþþÿÿÿÿÿÿÿÿÿû÷ôòéÕ§™£´¡–~}† æöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúöìßËÂg:**(''(+;dKIaZW\`gnaXY[]C7=Q_XNJJDLJJQLPW1'*0+((#"""#'/C]MFLUPHHG,$#%$" ! ",5$!"+9SE/! ! !! #)3@JPX_acgppoqrrtvyzytvvwuuuuuwx{{{xyxwvsrqrrpononvsspsqxrrswusrrszyyrstuoopuqrpnnqkloqopppnnjjfcfnotqxssrrrsqsoqmnnpnmkljoorlnookljhkplnnnmmd_bid^_fime_]fa_cieeemf`ac_`]^\ZZ\\\Z\\_Z^ZZYZTRPPPQOOJPMKHHJJQRVX\_abcceiijih]WNNNOPRSTUUUUVWX_dffc[ZY\^adeggghfhccbaaa]^`ehjmopruy~|zwttsux|~ƒ}~€‚……ˆ††‡‰Ž‘–˜›››šœš›˜™™š—–•”‘’’‘’Ž‹‰‰…„……†‰Š’””••––••’”•“’’’’’•••—˜š›™š˜™—ž™š›››œœœ ¤¥¥£££¤¥¤¢£¤§¨§¨ª¬«©©¦§£¦©ª©ª¨¨¦§¢¢£¢Ÿž™œŸš™———˜–•’‘‘Ž‹ˆyqbTLCGMNOSY{³ÜóøûþþÿÿÿÿÿÿÿÿÿÿÿüùîݺºÌÄžŒ„‚‚åúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõìÜʾd8)*)'((*:dJFNTNIPVWURRW^G5Hbbf[RKBG`QYFF;+$#$%,0*$*/24>MUZHNLI;94*# !" ! !"""/A0%"&:CCF* #'4?NY^dfimprrsusrvyxwwtvsuvvvvwwwzvxwspsvtrssrppmnourrpppqqssqmqqrsrqqrpnolmmqqonokkklnonnmnnnhgehjmnrqrrsusrtpoopmnnmmmijjmmmlmjmkjimrnkjgfea\aei_`chid]__`_`debegea``c__^]]]\[Z[[[\_[[[ZYYXWWSRQNNHMKJGJNORSTXY__``cfffihibWUVVUVYYZXWUVY\`eeec`[XXY[\_aeihihgddeffgggjmosntsuy|~}|yvvw{€}~‚„~~„„‡†‡ˆ‹‹’™™ŸœŸ››œ›œ˜™™š–”’‘’–’’”‘–“‹‡‡ˆƒ„†‡Š‘““˜˜œ™™™š–••—”““”•™—œ››˜™›Ÿ™œ››šžœž££¢Ÿ¦§¨ª©££££££¥¥¥¦¤¬²«ª©©¬¦¦§«³ª©¬§§¦§§¢¢¡¡ ŸŸŸžœ››™—••‘‹‰ƒ€yqf]MDGONPS^ˆÇåõøüýþÿÿÿÿÿÿÿÿÿÿþüõëäáèìͯ¢œ”œÏðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõëØÉ¹^6(*(&'');@?>>715=TTTIKSYONUbggh^VHDJN?3.*&"""*::547@UOKNUSMA80)('$ ! !! #))&0EA*$#+[MK7% "#(3@P`enoqrxyxw{wvw}zy|uvuyyxyz{{zzvysppsuuuztuqpqtttttqvutvwrpppqtsrrrpnnqnmmrpnnokklmnqmnnomohfhonqqxqsruutsvrvrumpnrmmmmmpqqmmjniikronkkkm`Y[ieg`cfpga`abcbabddhedceac_a^`^^^^_^]a\^_`]\\a`_XXTUQMKLB:AJOUTYYXY__^^dffghhyvf_ZWWXXXY\XW[_djkkhec`^]]\^_bejhhiihjmnprtsrrrrosty}}~€}{zy{}~~€€~€‚ƒ„……†ˆŽ•›šœ››šš››œ—˜–˜—˜—”’’’“’’“ŽŒ‰ˆˆ…‚„„ˆŠŒ“”—™™šš›š˜–‘“““”–———œ™™˜™š›™œœœžž¢¡¡Ÿ¤¥¦¥¥¢£¡¡¡¤¤££¤¥¬ªª©¨¨¬¦©¬®«ª¨§§¬ª©¨¦¤¢ž¡¡Ÿžœš™–•Šˆ†ƒ‚ztgYNDGNOPWa×íöúüýþÿÿÿÿÿÿÿÿÿÿÿûöñïòôìãÙ;¸ÀåôÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÖɰW3(''&&&(3@3,*(''9SUVIGVhjllkijccRK::2+(%$#$%,3GXOEEFPZINB?8.($#"! "(<CB@CC4#%/]QJ5' !(-:FR_eknrtuwwwwwwx|{yyuwtuuxyyzwttuvsroqquxwvvtpmqrqptsrqwuuuspportuqrnmlnooonmrqpoommklmmnjgjkhchjnnqrrrqpqqrqrpppolpmmiiinqlimljgghklmknhhh`WZ`efgaeghc`^abbba`efgc``baa`___`_^^^^^`\_`a__^_^^WWTSSQOJ;<ALOPQWVXYYY__gghhih…vs^^]]]]XZYY\biokkjnoqmjgedbbdgjlqptuvxzzzrqrrrvwz|~‡~~~~}‚‚‡‚ƒ‡„ƒ…†ˆ‹œž š™˜˜™š›››œ˜š™žžž™š”“”™–—“”ŠŠŒ‡‡†ƒ„†‰—™›šš•—›Ÿ ›ž›œ–”•››››ššœšš™™šš™¢Ÿ¡ž¢¢¢¢££§¦¤¥¨¥¦¤¦¤©¤¦¦¬¨ª©®®®ª¨©¬«±°±®®ª©©¬ª³«©©©§¦¤£ ŸŸŸ¡¤›—™‘‘Š‹…ƒ}viZKBFPNPVfžÝîöùüþÿÿÿÿÿÿÿÿÿÿÿýûúøùúôîçÜÓËÉÙóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÔʦR1()&)%&&()(%#"!%5Vj\KR[eemnic`]ND6)'&$###',3<C]VcT\GOLI;/*'$! ! $8aA<KYQJ%'6aZc9(!! ! " $)/6BTXbgllprxwwwywzzz{{zyvzwvuzz|xutxvxsssuuyy{vvqootrrrvpprxwxvspqpwuuqsomloospsoqrqpunpopllihgkhefnnrrssvrrquqsqtpoopoppojkkplijsljhikpmljnkrd[V\dgghglgfa_`ecebbaehna]bfgfedefff^`___`_ccefgaa]^WZYXUUSH=>FMPRTVVZYXY_ciijijjkhdZ\QQQRTTUY_bpollkpsronlkkifgimoqqtz{{ywxqrrssvyƒ€}~~ƒ‚‚ƒƒƒ‚ƒ……††…†‡‹“—™šš™š•˜—šœ›˜˜—™™™™™˜˜””””•’ˆ‡†…ƒ‡…†‡‹Ž“™™™–˜–™œŸœ›››››™˜—š››š›œœ™™–š›š œž¡¡££¤¤¥¤¤¤¤¤¦£££¤¥§§§§ª««ª«©©©©¨¬®®®²³®¬««««³¬ª©¥§§¥¢¢£¢¡¢ž›—˜“‘Œ‹ˆ‡ˆ€yhWLBHKOQ\m¤àð÷úýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷êÛãä×ÝóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÓÊšN/&'&&%%$$%$$#""%3W\cbbdpgkfcWTG92'%$$$&)/8<NDCLIJLJF8.+)&""!!! /DbAH`[]@&+APQJ:% #%-28?JX]aflllprsrwvvvy|{yzuvvzxvu{yxuuuwttstvwwzyxvqmpqqprsokpswwvvrpqqwtqnonmkoprmrprrqnnmmlmkhdgfjehjmosrtuurropoppqpomnlljkklllijknmjhknonmjjhe\XW`hgiiikcbbaabbdbbdhkh\`bcddddccbb_`b__`aabcbaa_][XXXXXY\PDJQSUUUWXZ[[\cfhijkjjlid`]XUSRVWXZ^bhnlpovwywwwwrrppmrpusv{{|}xxwvuuvyˆƒ‚€€€€ˆ‡‡„…†‰ˆ‡††‡‰ŠŽ™•ž—˜™š™›•˜˜š›™ššš››œ˜˜˜™“”“‘†„ƒƒ„†‡ŠŽ”–žš¡šž™›œž›¢œž››œ¢¡¡žœŸ ¡¡¡Ÿšš›¡œ Ÿ¢¢¦¦¦¤£¤¤§¥¦£¢£¥¨««°¯°¬««¬«ªªª«°¯¯·±°¯µ¬¯«´°´¬¬««¬§ª¤¤¤¥Ÿžš˜˜˜‘”‘‘’ŒŠ€ygZNEGONS^v³ãñøûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûòèîóòñøÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÑÉJ-''&%$%$%$"#"""$1UTWZ_bef]TME?90'$$%&.9;>ABNGDUNIA71'$"!!!! ! !!! !'7OfANbHF7%0LQF>-" ! "(.7@FLRackllmnnttttwwvx~{|yxtuw{vvv|{xuvuytvuuvyx~ywrposqqrrpmpwu{wwtssvvwsonnnlloprrsstqropmmmpiedkgjipppq|svvtqpnqpppppposmkikmomllljolkkonommjkhbYY^jiikojkbbccemffedeoieegggcddedda`_`a`_aa`bfddb`]ZZ]YYZ_`^\ZX\YYYZZ]]`clghjplniŠŠ…zurnc^^__`ceijmnux|}}}zvuupnpptuxz||~yyyxxy{ƒƒƒ‚~€€€‚†‡‡…†‡‰‰ˆ‡ˆ‰“”—–—”””—•™”˜˜šš˜ššš›——•”’’Šˆ„„„‡ˆŠŒ’—˜™™š››˜š™™˜šš›™›šœ¡œœž Ÿ ¡œ–™™ ¡¡¢¤£¤£££¢¢¦Ÿ¢£¥¥¨ª¯««¨§¨««¬¬¯®®®¯¯¯¯¯¯¬««¬ª¬¬««¬¨ª¥¥¤¤¢ œ›šš’“’ŽŽŒŠƒ|uk_PDGKPTe‚·èõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòèÎÇ…F-&&&&$$$$$##"###1URRY`^ZWTJ?<93,*)26:@A`PVDO>530,(&%#!" ! !! #%%" "%# $/DXS?He@>-#,T@6,# !'+28AKOSX]cgjkmnppqpstusuxwwuttuvwvssttsuuuuvttsvwyvxusprtrpqrqoqtuv{utsuuvtsqpqpmmmoppqqqpnnlonmmohfeeejlooprsssqpoonrsqonnnnmmjhlonnmllkkjllmmmkkiii[T]eggjkjgc^cfeeedcadefffcddeccbeec``_ab`_aa`_edda_^]\\X[\bf`\WWXXYXZ[]]aehijkmlmf“†~|}ttiljiiijkkmr{x„ƒ„}}vvuqruuz|~}}}~€‚|~‚„‡‰…†‚‚ƒƒŠ‹ˆˆˆˆ‰Š‰‰‰’–—˜™˜˜•–”›–˜˜˜˜›š™ œœ›œ—š“’’–Ž‘‹‰ˆ††ˆŠ”” ŸœŸžž™™—–˜œ››œ¡ ŸŸ¡¢¤ žŸ¡ ¡Ÿ£¡¢žœ›ž ¥¡§¦¨¥¦¥¦¢¡£¦§¨¨¯¯¯ª³±¯«©¨§ª¯±¯¯®°²¸¯°°°¯¯¯¯¬®¯¯¯®¯´®©«¦¦¥¨§§¡¡–““”މ„}n^PEGNPVjŒÄìôùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðçÌÅ|A,''&&%$$$##""###09;;<@BBDC@:76102<IKLJJTUF851+)&$#"!!!"!!!!!! !! "3H1#!)./!(3WZZ:795,"!',+'! !%-4<GNTW^`hgmnonqnuprpxvuuuuttuttuyuuv|utsuuyuvwwu{z{xysrrvtrqtppruu}v|vutxuvttprrtmoopppqrooonmsnpmoiifehrtuqpqxsrpsosstssollqmmkilsornnnolliposmnklij\TZfejiplkc_^dddefbb`eehgjeddiccclecabcda`_da_^deca__aaca^aieb]XX[[[[[Z]_cegjompllg„„}}y~rqlllkjkmosttwx|ƒ|{wvwvvxyy{{{}}|…‰Š†ˆ„ƒ€‚ƒ„Š‹Šˆ‰Š‹Ž‰‰ŠŒ”˜˜˜—––•–“••›—˜™™—šš›™›šš˜•‘ŽŽŒŠ‡ˆ‡‡ˆŽ‘”——˜ž›œœœœœ›š˜˜™™™›žŸŸ¡¡¢¢¥¡¡¢ ŸžœŸž ¡ Ÿ ¡¤¢¦¦¨¦¥£¥Ÿ¢¤¥¥¨©©©«««¨«ªªª««°¬¯®°±²®¯¯¯¯°ª®«®¯±©¯¬¬¯¯©©§¦¦¦§¦¢¡ ž™–•”“’ˆ„ymaRFHMRXp–ÈîöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüõïçËÃu=,(*,))%####""""#,<.(.2336988:;97BUJa_`HMA8/*(##"""!!!!!!! !$'3KH7$"*H)!%4<J9(''$! "!! !#*/7>EQUY[^dfhjmjlmonomqorrrprssrqosuurvvvuutttsrssttttzxusrrrrrponrtwwxvusuuvuvtsqqpnmpqooqtrlllnmnnpkjffdjnrsutrpqqqonnpoqqpomlomnknqqppnoomkkjpmllnjkg[PZfefkijjb\`_`adbb_aadehggeedeccccbcab\_`_^_`_^dfca`_`aa`aceca]\ZZYYWWV]beehkoklljgz~‚~…}€tqqqonnopttwuxwz€ƒ~}{|€ƒ~€}|}‡€‡‡ŒŠ‰ˆˆƒƒƒƒ…‰ˆŒŠŠŽ‹‹Š‰“—¡ ¡˜––™–œ›š—œœœ›ŸŸ œŸ›˜—••ŽŒŒŒŠ‡††‰Š‘—š£Ÿ›œ ¡ž£››š›œœ™žžž¡¥¥¥¥¥¥¥§¨¡Ÿ Ÿ¥¢¥££¤¥¤¤¤¦§¬§«¦¤¤¦§§©°¬«ª¬«©¨«±®¯«¯±°¸²´²³±±°¯®±©®®µ¯±¬¯°°¯±°°¯¯¦§¥¥¥£ŸžžŸ™™˜—‘Œ†…|l`QFHMPXv¡ÓðöûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîçËÂm8+*0-,(&###$##""##"!"%*,--05?LHUOMUMLJJI8-($""!! !!! !!! ! !!!"*59?QY1%#,5&!"#&(&" $)1;BJNV[_bghnnpkmkllqnoopptttppswrqqrtvwwvwv{vysrrssttut{vursrqrrnorzx}wxustxyyuvvutunmmqpoousslmlnoqnpkjeehrsysvvvqtqqpoourvppnonrooovvuqsrqolkooqmnlnim^UWdhkimhgd]\abeedccafggghhgghehdfdicdcc^a_^^^_``ccdbb`dbdchfeb`^^_^[_WWW^cjiijolllkgqrwxz}zuvvtssutrttvsvw|€€€€ƒ„}}~€€€€‚ƒ‡‰Šˆˆƒƒ€ƒ…‡Š‰ŠŠ‹‹‹Œ‹‹‰“˜š™š™—–””•˜˜˜˜˜™››œšš˜——•’ŒŒ‹Š‰‰ˆ†„…†‹”˜ Ÿ››œŸž œ›››š›››™Ÿ ¢£¥§§ª§§§§¥ŸžžŸ ¡£¤¥§¥¤¥¥¦£¤¥§§§§¨©«¬°¬«©ªª©¨«¬±¬¯«¯°±±±±²±¯¯²«®¯®««¬¯¯°¯¯®¯¨©§¦¦¦§¤¡¡ Ÿšš™˜”ˆ†}sjaTIKOSZ{ª×òøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíçÊÀe4++..)&&$$##"$$$$# !',,++-<KGGTWUTDF82,'%#"!! !!!!!!! !!!#%')/7Ga`aF)%%2?% ""! #&.4<CIRW[\`cfikkkljkkkjpmoqqrttsqruqpswtstttssssrrosqtttxutttuqtrqnnlquyxwtuuuvwvxrrrrqoknnonpqursklloqqmqigdhkstwssqqqqppppppppppnmmrmooooqpppqnllnmmkkjje]SX`dhhgggfa]\adccccbafjffggfbededddhbccb]a_`a_\`adab`a_baccghea`^^]\[_XXX_cghiikkihigmqtuuuuvxyyyy{zyztvtvy€‚„Љˆ‡……†€ƒƒ…‚ƒƒ…ˆŠŠ†ˆƒ„ƒƒ…‰ŠŽ‹‹‹ŽŽŠŒš™ ™œ——–—˜Ÿ•›››™™˜žžŸ™™–•’‘Ž‹Šˆˆ‡†ˆŠŽ“—¡Ÿ¥ Ÿ ¥Ÿ Ÿ ¡›Ÿ›ž›ššžŸ¡¡¨©©©ª©ªªª¤£ ¤¡ ¡¡¡¢£§¤¨§ª¥¦¥§¥¥§©«¬®³¯°®®®®¯¯¯¯°°®®°±·°±±±²³¯²°±®´®±±º°®¬±±²¯³±´¯°¯¯¯®««¬®¬ª§¥£¡¡¡¡ •’Šˆˆ~zpdXMKLQ^…¼ßó÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíã˺_4+,/+&%&$$$$$&*-($##&)./**2PN<BdJ:50*&$#"!!"!! !!!!!!!!!! ! %-8LC>ENJJU?'"$<0$ ! #(.6@GRUWZ_abefhooolljnmolpqrqvwwuttuqoqvwwxxsursturqpst|utuuutsvquspmnpwwzyxtvv{xxwxpprtpmnrqppttutskknpqrnriggnpuu}ttstqqppquqrppppnmmrqsoopqqqqqnmnrnolkkl`UT`cghiikjk`__efiekccemgefmfefmchghehabba_a_caa``bdacadfgfedgeea`_`_c^_[[^gglkkkliggmhsstusrsvz|}}~~|wwtttw{€‚…‡‰ˆˆ…„„ƒƒ„…ƒ…†ˆŠ‰†‡………„„„„ˆˆ‰ŠŠ‹‹‰‹‹’•–”“““—•˜™—”–”–”—™žž›—–“‘ŽŒ‹ŠŠˆˆ‡††Œ“–›Ÿžž žŸŸŸžŸžš›™œ›šžž ¡¢Ÿ£¤¤¢¤¤¢Ÿ¢¤£¢¡££¤¤¥¤¥¦¤¢¥¦§¤¦©¯®®°°³®¯®°±±°¯®±²¶°¯«¬«¯®°¯®¬¯®±²´±¯°°°¯±±²±±²²²²±²²±°®¬«©§¥£ ž˜–’Œ†‚~uk[MMNV`ˆÅãôùûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûóìßÍ´^5/-0)&$%%$"%&0:62230,,0)&)6NS=<<9/(&$$##!"!!!! !!!!&+)"""!!#/=ENSRQF?Ci:'""""!&+3:BINVWY\`bdeiijlnkhhimmmlqqqrqpooqppoprssvxussrrpooppsuuutssqtrsqokmnpqwvxrtsustuutqnqrqnqrruqpsrqnnknspppmliijptuvwsssqnppqrrqronnonnnpooopppnmkklmoljomjgbXVVbgggijjjc^`aefdbcdfimeffgeffebddfedbbba___``a`aabaa`cdeeffgdb^``_____]`dfhklmnmggghexuroqty~ˆ††„ƒ|{wxxy~‡‡ŽŽŽ‹‰‰‰„‚‚„†ˆˆŠŠŠŠ‹‰ˆ‰†ˆ‰Šˆ†††‡ŠŠ‘‹Œ‰‘•”””“““”˜– —•–˜—œ–œšŸ™–”ŽŽŠ‹ŠŠ‰‰‰‘–—œŸ¦¡¡¡¢ ¤ ŸžŸžœ››œœ¡œžŸ¡ £ ¢£¤£¢ ¢£¤¥§§§¥¨¤¦¦¦£¢¢¥¥¦§§«¯¯µµ¶±µ¯°¯µ°¯®µµ¶³µ´µ±¶µ¶±³®®¯¶µµ¯°°°²µ³·²±°°³¹²´µµµ¸´½¼¼¼¼»»´¶±±²³¬¬¤§ ŸžŸ˜•”“‹‰xi[OKMSb’ÓèõùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúòêÜÐ^7000(&&(('&'3@FKKNJF.,'"#(1NC=>D0$"""####"!!!!!!!#",45,)($"'?][^`bYD<;;6$! !%,4=HKUY[\abcflehjommjighlsnwpsqxpqnroroopsstr{usstuvpoosquuvuursrussqmkmqttxwwttsxsuutqopwsrrxtsssqwqppqnsqoorkiilovu}vwvwqpputtrrqronnooqqtppnsoponjilrnjinjic]QQ[jhhhligd__adjeddedjjmdjhgeifddfcideedbabd_a`babacbfggeehkghca_ccc_`_caghijnnnnmhjjjf{wqqrx|„‡ˆˆˆ…|wxy~‚ˆ‰‰‹ˆˆ…„‚†‰ŒŽŽŒ‹ˆƒ‡†ˆ‰‰ˆ‡††‡ˆ‰ˆˆ‹‹”’‘••••••–––•——™˜š”••“Ž‹‹‰‡††††‹“—šœŸ£¡ŸŸ Ÿ£¡ žžŸ›œ››œœœœŸœžŸ ž Ÿž¡¢££¦ª©§¦¦¥¢£¡£¢¢¢¥¦§¥«°¯¯±´±®°°°±±°¯±±°±´³³°²±±°³°²µ³²µ°°¯±³µ±²³²²µ¶´±²³´´´´··¼º»¸»µµ²³¶³¯®¦¥¤¢ Ÿ›—’’“‹‚ui[MKKVf–ÜíöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñèÚЧ_:0+*(),151.9EQ[XVXY;+% (*-02=0&#"! !!"""! !"##"$&=e?8=M)"+NJBZMPbA3.*% ")/6<DORWZ]]^aadigdilljifggkmnnqoppoklmmnqnootssrspqppoqppnrruuvqvssrrsrqnlqwvuutssurtsrpompssqrusooorqqmplnnpnnmkeiknpvuwuusqmqptssrrsolnnoqrstqonoopnmjlmlkiinfc\PHXdcfghhif_``beebeedcghgejjfcjeegfchcbadbaab___bdbacbddeaglhdcc_]^^`_``cbfhklmnllkfieif‚~{zy|€„Љ’ˆ‡‚~~‚‰‰‰ŠŽŠ•ŒŒ‹ˆˆ‰‹””“–ŽŠ‰ˆˆ‰ŠŠ‡‰‰‰‚Š‘‘‘‘’’––š—™—š™šœšž˜š”•ŒŒ‹‹‹‰‡††‡‡‡Œ“—¡£¦¡£¡ žŸ£¢¢¡ žŸž žŸ¥ŸšžŸ ¥¤¥¢§§§¦«ªª§¨¦¥¤§¤¤¥§¥©ª®°³³µ®®¯±±²°¶²¹²±°°±²²´²¸··±°±´²·³²²¶±±°¶³µ²´´¶³¼µ³±¶¶¸´¶¶º·¼º¿¹¼¸¹¸·¸¹³°¯¸®«§¥ ž™™•‘‰„woYJHKWm£âï÷úüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøðçÙÑž^8-**+/<KDAGOYgb]VX?+$ "(&&&'&%#"" !!!!! !#+7*(4IgJIKE,%/XOE]B<81)"! $(/8@KQUX]_c_`^bbgfghpmnihhmnoppoupqppkmnnmrqrotsstuprpqpsrrssrwwwvwtssvvuppryxytvuwvuvxronmntrqrtsronnqprnqmpntmqligkowvvvzvyrrosrttvvutnnropqwwwqoonnqpopqnmhhineeRGQfedeffgfcddbcdfeedfglhggjhefjeffgehabbdcbaaacafcccgdcdeeokfcd`^\``f_`adflhppplkkjgiiig„ƒƒƒƒ„†‰‰ŠŒŒ‡‡„„ƒ…‰‹Œ‹‹Šˆ‡‰ŠŠ‹ŒŒ‘“Ž’ŒŠŠŠŒŠŠŠ‰†…ƒ~{„ŽŽ‘‘’’–—š–––š—™™˜—•“‘Ž‹‹‰‰‡‡‡†…†‡Š‹Ž“™¡¤¢¡ žŸžžŸ¡ ¢›œŸŸ žžžŸœœ›žž ž¥£§¦§¥§¨«§¦¦¦¥§§§¦§¨«°²³´¯ª¯¬®°°±¯³±²®±³´³²²³³·±±±°®®¯²±±®®®·³´°³³´²³°±±·µ¶µ¶·¸···º¹º·¶¶¸µ¸µ¶··±¯¬«§¤Ÿœ—“Œ†}rf[NNP^t¨áñ÷úýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøïæØÕ”Y5-*4<GSUXZ[]jffZNC/)&$$$$$$#"##! !""""%2338CTeRfU?,#.^A51,)'$!! #&-29@FPTZ[`abb__`bbgfhikkjhiimllkonolllqmmnnnspposrrnnnpopnnnstsprsssttsqssqprsuwuttsrovuqmmlnoqpsssppponpppmnmonmmkhijprwtuvvtsoomrrtsvtttqoroqrtsqpppnlljnnmnidghihUGRcbdeefeedb`aaccdbddfhiiiijgghhdecfddbcdddc_abccdbddddeeeeniea^Z^]`a`^`aglihlkkjknifhhii‘’‹Œˆ‰ŠŽ‹ŠŠŠŒŽŽŽŽŠ‰Š‡‡Š‹Ž–‘’“‘˜ŽŽ”ŽŒŒ‰ˆ~zzwƒ’“”“–••—š˜ž™›šš™Ÿ”Ž‹‹ŒŒŒ…ƒ††‡ˆŒ’›œ¢¡¥£¡¡¡Ÿ¡¢£¢¦ ¢žž£¤¥¡¦¦¦ŸŸ œ¡ ¡¡ Ÿ¢¡¦§§¨¬¨¨©¬²ªª©«ª«®´²¼´³°¯¬°¯¯°³¯¸°²²·³´³¸²³²¸²´°°¯¶±±®¯²·´´°¶´·²²²´±¸·¹¸¸¸¼½½½½½¾¹»¸¸¸¸¶¶¸¼µ³³¶¯°¦£œœŽƒ{xvbSNP_z´ãñøûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷îåØ×–V6/6FUhih[\\`fibWNB8+ """""!##)+)# !"""##'8@GMKdTHVlA,$,20*%#! "(/6=CNRUY_```dabbcdffghmklhhhmmmnnmpoonokrpsorntqururrnqquopnnptsvpppqrvvvqqpprxrusrszrqswrnllnurttxtqooptnpppppopppliimqzuxuuuytrpqorstsvvwsrrrqtrwrsrsomlklmoqkhhkik[LNaedfjihefedcgghefbceiiijlligjhgehefcccfggdddeeeehikeedkijhnhe`]]daeba`bcmihhokjilljjjikj’”•–“‹‡ˆ‰Ž‰‹Ž‹‰Š‹‰†‰ŠŒ‘‘ޑދŒŒ…ƒ}v|†‘’’“”““‘••˜—˜˜››™™“Œ‹‹Šˆ†……ƒ‚„‡‰‹”–˜Ÿ¢¡ œ ž ¢¢¢£¡¡Ÿ ¡¡¢¢ £¤£ ¡¢¢ŸŸŸ¡¡¢¢¡ ¡¢¨¨©©«ªªª¬©ª«ª©©ªª«¯´´²³¯¯®®«¬¬®®²´²¯¯°±²³°±²±±±±²¯¯®®¯¶±°±´µµ³°³²±±±±³±¹·ºº¸¶¹½ºº¾¾½·¸¸¹º¹·¸¸¸··¸´±®§¤œ—‘Їƒ~wcQPPd³æôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöíãØØ—R87CXmkn`VVX\dc_UQOP4"!"####&)07640)-32/-,26:OOPNCcNKKE?(!$(%#"! !&+17>EJPU[^a`_``_`bbfgijhhhiigggknmklmljnnoknoonqmspqoononqrpnmnppppqqqnqrrppponrsurplrqposztonkotrqssstspqppnooononnmliijpssssstuttrnoosusqqqrqqpqqrsqmppponklmookgijkj^OPUdighighe_[edfcdddbdgjkkkjghhhfgeeddddafcddbaeaefggkffehfhjiee_____bcbacdefhikijfhhjdiikh›› –—Œ‡‡ŠŽŽ””••”‘ŒŠŠ‹Š‰ŠŽŽ‘‘””””–ŽŽ‘„}x€‹›”•–—”“’“–—œ—š˜ ˜•‹‹ŒŒ‹‰‡„ƒ„ƒƒˆ‹“™™ž¦¦§¡žžŸ ¢¢¤¤©§¨¤¤¤¨¥ª£¤¤££¤£ª£¥¢£¥¦§¨¢¢¤¥¨³©®«¬ª¯«¬«¯ª««¬«®¯´µ·µ´¯°±²°¯¯´³¹´±¯²²´²³°®²³°±±±¯±²²¯³°¶²¶±···²²±³³³±±±³´¹¸º¹¹¶·¸¹¹¾½¾¹¹»¿¼½½¼ººº¼·³²´§¨ž—˜š—”Ž‹€vbSOQeƒ¼èóùûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúõìäÙ׈M68IgnrnZQOUY]aXRQTJ<"!%(*--/5?CFDAFKLiI;>VLHSXYPMdLTFB0$ #(.6<BFOR]^^afccac`bbbcjhmjhhfikijjjknjllkjnnpknnpnqlssunqopnuqoooospprurqnursqsoorvsysqprqprvtrnmnssrrusrsvqoppospoopoonlilpxuussrvwwtqqrrxutpnpsonptrzrnovrrponssulhhklmcYJS]iikkkfh`[^fegcfggcfinjhijhhhigkegdefhcfcmdccegihhhlgghkfhjkdebb^^^aceghfjgkklijfhfjiihkgœœ™•’Œ‰‹•••””’Œ‹ŒŽŽ‘““Ž‘’’‘•••Ž„z~€†’‘‘˜”••–”•“”——˜˜—•’ŒŠ‰ˆ†„‚‚‚ƒ‚„…Œ‘–™›ž ¤¡¡¡ žŸ ¤¥¤¥§¡§¤¦¦¨¤££¢¡¡¡££££¤£§©¦¥¥£¥§¨©ªª«ª¬ª¬««««ª¬¬¬¬²³´°¯®±°²±±±²³³³²±±°±°°°¯®°³°°¯¯®¯¯¯¯®²±²¯²²³°°¯¯±±²²³µ¶¶¶¶µ´¶¶¹¹¹¸º¹º»¾¼¼º»»¼»¸¶³°¦¤££¤¡˜”†~ueWSRiнéôøûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôìäÙÜJ3;NfllkVINV[]SOMJHE1"!".951028@K[IGSnPMeZNEQmjihjQME741,#" "&+07@BGKOUX[]acdccac``_ccfhiihfgijhgeghnjiekjlmmjnmmlrmoppnqoonnnpqppqqrtrqqoooonnnruvssstttrtvvrsnoorqsrrrrrvqpoppppppppkgijoutrssrrssrrqprsssqoprpnoqrrsooqsqplnnnnkfijjicXQKYdiiijhfbZ_cffecfhgdgijghiihhijeeegbfedbccddddehhgffhgijfbijfaccb\]^aeeffdihlkkiiddcfgghieŸš˜”’‘“’’–“’’––—”””•••—™““’–“”“““–•”’“‹ˆƒ}‚ˆ‹”‘‘“—˜™–—”–”™˜š•“Œ‹Š‰ˆˆƒ‚‚…†‹Ž–˜Ÿž¢¢¢¢¤¡¡¡¢¥§ ¥¥¤¥¥§¨¨ª§¨££¡ ¢££¤¥¤¤§¨°¨¨§¦¨°««««¬¬¬ª°®¬¬¬®°·³¼³¶±°±´´·´´µµ³¼²°±³³²¬®¯±³³·¯¯¯¯®¯¯µ¸º±µ°³´¶±¯¯±°··¸¶¼¼¼½½µ´¶¹¹¾»»¼¼»¿¾Á½½½ÀÀÁº··¸²®®±²ª£¡žš‹ƒ}gWPRkÅëóùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùòëäÞ݃H>AUgkmaSKMT^UIHG@:3(&'1@BA848?FVSMHW[VTcbcCHVTUKOYA4+%#" "(.4;AEKRTVY___bjihcbccba_ddfhlhhfjilgffggmiifjikljjnmmmqoopqqqnpppptqpqtturppqpnmpmmqxuusqt|vxvzvwssoqrtuvsxswtvqppqswwwoplhfinsstrsrrsxruqpqtsyqposqposrvsspprvponnoqmjiopqg]QNVfhlikiifaaccfeddfgggmihgiiiinilgjefefecacdecgfefhfeeggkgddifdchcb^\^bdggkjjjmloiifefhhkhif——˜’’‘’’“•—“‘’‘““–œ›šœš–”‘‘“’”““““”””•‘‘’‡‡ƒ‰’‘‘“”—™˜”•“•“–˜‰‰‹‹Šˆ‡‡‰„ƒ€~‚„‰Š•šžŸ £¢¢¡£ž¡¡£¦£ Ÿ ¤¦¨¨¨ªª¦¡œŸ ¤¥¤¡¢¢¦¥§¨©©ªª©ª¯¬«««©§©ª¯®«¬²³¶²²³´²³´¶··´´³´²²±°¯¯®©®®®°²³²²¯°±±°±°²´³°¯®®®©¯°·¶¸µ»º»¹¸´µ·º¹ººº»»¼À¾¿½¾¾¼¹¸···¶µ·º¹¸·²®§¥£ š“Œ„{fUSSn•ÈíõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøñêàÝÛ‡H>Lc_\\_UKLOPPFA=3.(''1@N\K>6;CJKNKI_XRVXgCJUST;1/+(#"! "(,28>CGJNTVY[]`cddghecb`bbb_deggfdedghlgfffghiifhhhhhhkllppooooomlmlopppqrsprpqrqnokmnquuuttttuuuutturrorrtuutusrrqpppqssurnlihfmtrrsrrrssrrqnqrttqnpmooonrsuospqqpknmopqlkjllh_SLXegijijghecbbbfeefghhhihffheihlfhffddfd`cadgecefffgffeggkeddhbdec_c^^``acccbjkljkjjgghiijhig˜—˜——“”“”•”•–’”’“”—¡¥Ÿ—™“””˜•——šš›——••’ŠŠ“™‘‘‘˜’––˜˜—˜™™šŽŒˆƒ‰‹ŠŽ‰‡‡ˆ†Šƒ„ƒƒ„‡‹’“œ› ¡¦¡¨¢££¤£¨¢¥£¢ Ÿ¢¨©¯©¨«¢žŸ¥§¯¥©«¨§§©ª«²±°°°¬¬®ª¨©ª«®²µ´´´½´·²´³¶´¹¶¹¸¸´¶¶·²²²³°±°°¯±±³³³³³´´²¸¸¸´µ³±°°°°°°®±±±¯·´¸·¸µ¼º¿¹¹º»ºº¹¹ºº¼¿½À¾Â¿Ã¼º¸··¶º¾¼Â¿Æ¿Á»¹µ²¬«¥ ˜’†weVQTsÏîõúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøðéÝÝ×€GAXVNLM^[KJMNJF?:2(&&,8G^WR>7;FGHGGJfjY@<PpPQ<2,'$#"" %+06=EJPRSSYX[_eeecefhedcbbgbbcjklfdddefglilefghhlfmggfjhkklnpppppmllpmooopsrtuutsqrnnnrswwztstvuuw{vttzrtpwswvwxxstpoprprqqpomjikmvsstwtustsrqqquttrompoooqrytuorqyqplnntpqlolmidULTehlkkkkkleghjiginhjjnjgfegljjikeieddccc_bchhfgljkhihkeigjeffgffcc_b_``aacbbdklpklmnijjmmkihi–”—–•““’’“”“’’’”•˜ œ››˜™˜™“•–—–—˜™™˜—•’Š‹‹Ž•”‘‘’˜’–—˜–˜——‹‚€‡Œ‹‰Š††ƒ„ƒ„„ƒˆŒ”–˜œžŸ¡¡¢¡ž ¥£¥¢¥¢¢¢£¥©ª«¨¥¡Ÿ¢¦§¨©¦©©¬ªª«®¯²±°®ª®««««¬²···¶µ´´³²³´µ´´µ¹°²²²³´±±°±°°¯¯¯°²±°±°²³³³¹··µµ¶°®±±±°¯¯°°°¶³µ´¶´µ¶¹¸¸¸·´¹¹ºº¼½½¼¼¼¼»¼¸¸µ³±¹½¾½½¾À¿¿¼¼»¶²°¨¡™ŽwhYWVv£Ñðøúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ïèÜÜØw@7GQF>O^WKJOHDB>;5+',2@NOQB4348:?BDGjgC+2AEJ7(&$#!! ! #'-2:AGKMORTUVZ\_cfeeehlfbdecbdcddjhfcdcbafegggeeeigkfedddffkjlmqqolkhiillnmpprrtqtrpnnnnorssssqtvrouwvsrqtrrqqrvuuutqpnnnoomlnnljjjnrqprtuuvssqqqsusrtnmmqqppsuvttprppookmmnnmjmlngWLVcfjlmlklme`jkigiiifijjkebfhhhiiheeccccc`_cdefghiijfigkfgfgeeddcbba_`_`_aacddekllkmlljiikmjhhišš›šŸ””‘‘’˜™Ÿœš™˜›˜—˜š—ššš™˜™š›œ›Ÿ˜••’ŒŒŽ”•”““‘–”˜——™œž ›”†|ˆˆ’ŒŒ‹‰…†ƒ„ƒ……†Š““š› Ÿ¢¤¤£¢Ÿ¤£¥¤¨¤¤¥¥¤¦¥¬©¯¦£ £¦«¨ª©«ª¯¬¯³²¶²³³³´´°²±µ°¯±º·º¸ºµ¸³´²¸··µ»¶¹¯±±³³¶±³°²±³°³³³°®¯³³¶¶·³¹¸¸¸¸µ°¯¶±¸³±±±°±±¶¶º¹º³µµ¸¹¹¸··º»¿»Æ¼Â½¼»¹»À¸¸³±¹ÃÃÃÄÄÃÁÁÂÃÅ¿¼¼¼µ¶ª£—Ž…~l^VX{×ñöúüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöîçØÙÚm95@??CTTTMHGA@?=<4-07?ILNE90)(*19=?DoQ/%&+/-'#!!"!! " !%*/5<BJJRSTVZYYZabdilhhhmgedddfghhgfjgddecacgfiffhkjjgkfieeefejjmmqonjhgiklnppvqrstrtqpqstuuuutttqwrqqvvvqopwssrtsvv{ttsropnnmlkonjjlnspooqtuuyssrtswtrssrqrrqrrvwxstqtoonnottvmmlrlq\NScgkkompkngdclkihkjjjmnricelhiikiiedcfb``_`cegejilhiiiijjjgiihddbcaa^^_b`cdkhigpmnmolkjihiggini™˜™™˜•“‘‘“—šŸŸ š—›’•”–•••™•™œœœ›š—‘ŽŒŽŽ”““’‘‘‘•”——™›™šŠ}|ƒŠŠ‹‰………„‚ƒƒ„ˆ‹Ž’•˜›œœžžžžžžŸž¢£¤¥¦¤¤¤¤¤¤£¬¨¦¢¡ ¤§§¦¦¦©©ª¨««®¯±³²±³±²´´´´±±±±±³µ¶´´µ¶´´³²²³²¸µ¶³²®¯°²³²¯¯°°°°¯±±²¯®¬³µ¶µ·³µ´·²´³±°±±´³²°±°±²¶·¹¶¹³³³´²³³¸¹»½½»º¼À¿½º¹º¹·¶µ»ÀÃÂÂÁÄÅÅÄÃÃÄÀ¿¾¼·µ¯§ž˜’‰€p`[Y|®Øò÷úüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöíæÛàÒc839;>EQZXOD@638<82/<AMJIE?9.+%&+7;<=@B*$#$$$"!! #)/5:?CHONNTXYZZY[]cggkkhhggcdedeghiigfjeeeedddffffghhffffceeeffekjlllmjhhhjlnorrsorsrqporusstotrsrqpqoqrqnnnoptsrqtvvsrqrvqmnnnmmmookimopooprtuutttqtsussoqqqooosrrqpntqolonprqoolmmnl\NUbdgjikkjhe`efkjiijijjoolggjlgiiihidcbb_`_beeggehilfikiijihggedadaaa`\^]b_edffhhokkkjjjkjhighjmhœ™›—™”’’’•œŸ§¢£œ›–š›œ—™”“–™—œœŸ›™—“‹ˆŒ’š“””•Ž“’™™šœ£›’t‚…“Š‹‰ˆ…ˆƒ„‚ƒ„…ˆŒ“˜š›œŸ¤ž ž žž ¢¤¦¦«¨©ª«ªª¥¨¦¬¦¤£¤¥¦§ª§®ª°©ª¬±±´¯¹¸···²¼´·µ»´¸²²²¹¸¸´´µ¶µµµ¹··µ¹µµµ´¶¹±±±±³º´µ°°°µ³´´´³´µ¸··³µ´¸´¶²±±±²µµµ²´²²·»ºº¹¹¶¸´µ¶¶·¾½¼½¾»À¾Å¿À¹¼·µºÂÄÆÄÉÃÅÅÊÆÌËËÊÉÉÉÂÀ»¼¶±©£›–‡qaWYºÝòöúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõìæÖßÈ[637;>FP\dJ:0).654/08D?:;<<:86'%)31025/%"!! !!!! !!!!%*07?FKPQRV[\\]_afddekhjjnikfdcldehmlpjjhkefhkedeijmikiheeffdfeffhdljpkkihihjlnppvutqwsqonovvussstrvqnnoqwrsponqsysrswvvsupoppqxnomrstnmmrqqpppvtyxwvutztvsrpsruoos{rqommsqnmpqvsronoppr_PP_cginjkkjgabihjjmjlkkjwnhgklmjjhihieebbbabfelhjjlllioklhhhfglddde`bba`a_bbgfhhmjpkkkkkjjoklikkmh˜—š••”“’”–œŸŸžœ—›—––˜——••–––—˜—˜Šˆ‡Œ’“••’‘Ž“’˜š™ ‰~zq{†…†…„„‚‚‚…ƒ†‡Š“™›œœœœžŸšŸš ¤¦¨¦©©§ªª¨¦¤¢¦¥¥¥¥¥¦¥¥¦©¤¨©ªª¯³µ±®¯°³µ´²²³³³´²¶²³³´°°°±¯¶±µ¶¸·¸µ¶³¶¶¶¶µ±±°±²²²´¯±²´³³²³°´³´±±²µ´³³´²²±²³³³µ±´µ·¾ºµ´²¹·¶¶¶³¶·¾¹¹¸··ººººº¸»´¹¾ÂÂÃÂÅÃÄÆÈÇÇÇÊÊÉÉǾ»»»¹¶¬¤ž˜€o^Z[ƒ¾ÞòøúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõëåÚݪR56=@BFQ]O<-!%*42.+,.,*-08D9@2)&(()('%%#!! $(.5<CINRWUUW\^```bedeffggiiigfdecfdhjkkkkhfedgkfcehkkjjjifcfdcbdfikhdljighghjklnoooooppppomopqrrssssrqqolpsurrqportvstvtrsstnmmpuqlomsqnkopstrnpprqqqqqusttssrorqpmqusspnnnoponqtsqponnop`QTZ_dgjkkjkieeghhjkjhhgjkjieejlkjjggfedebbbddggiilmkijiiiiffefgfcbbbab_a]`_aaffghjihhiiihhhijjhihih™˜š›š˜—œŸŸ›œœ››Ÿšœ™™™š–––——•І‡‰Œ”’–••‘‘ŽŽ‘“š¤•~o|w|ŠˆŠ‰ˆƒ„‚‚€…†‰–—š¡¡¢£¢›œž›ž¡£¤«§¦©©ª°§¤¢££«¥¦§«¨ª¨ªªªªª©±®µ°¸³°¯´²²±µ°±²µ´¶¶·³·®²±³³·µ»¸··¾µ½¸À¹¼µµ´³³³²·´´³¸··´µ¶·³³³³±²²´µ¶··´´±·³³´´´··¹¹º¶¶µººº¶··¼¼¾½½µ´¶¼¼½¹ººº»ÂÂÇÄÅÂÉÇÍÌÌÉÍËÑËÎÇÆÂ¾ÀÆÄľ·¯¯¡™„o^Y^‰Äáó÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôêâÖÜ‹G5<HKMRUL?0#!#*1-+)(&$$%,69:4-'$#$$#""""! "#*07AFNOVX_[Z[\^cchefekkjihjoijeffghihmlnmnigedehfddgirihimgeeeddbcfmjhhkhgfhfikoputtoqpqqppooursrqs{stsrrrrttzswttttu{vxvtttstpqorqnnnosomnvswtuuutuqqqprutttwrqnxppqvuspnotnmnoqwtspposoqdUQ[adhjlnlljhhijkimljjjinllcbfnmpjjgfeccfdedigkhjippnjlmmikfgdiedbcbbbbbabccddefhhkjihliigghkkjhjhhiššššž™——•˜˜ž˜™™›œšš›š˜™”–’Œˆ‡ŠŽ‘’’ŒŽ‘“•’ ‡|vjv‰†……€ƒƒƒƒˆ‰‘—›š žœ™™–™›Ÿ¢£¥«¦¨«ª©¦¡ ž¢£¬¦§¨©¨©§§¥¥¦ª©°¯¯¯®®®±¯¯®¯°´´·µ³±±®²°³´µµ¸º··¶µ¸¶¶´µ±µ·¶´´²µ¶´³·¶·²²°³²²±²°±²µ¶µ²µµ´²±±´¶¶¶¸·¹µº¶¶´·µ¹¶·¸»º¸´³´´¸¸¹·¾ÀÁÀÂÂÃÃÂÂÈÇÉÉÌÆÉËËÈÆÃÂÂÂÂÅÇż·°¥Ÿ•Œ~ob__‰ËäôùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýóéáÓÎq:3CEKQ[M>2&$$%+)('&%#""$(+.,*'""!#""""!!" !!! !#(-4:@FMSTVX[^\]___bbccdekijkjlhdcafeffggllmkifgcegffehghidfedddddbdceghhiikgffggjknpqrpnpqommmoptrsrsuurrrtsssuvvsttusuvuuvwussssqponkmmnpmjnruuvssqploqqprrrqqqroommmrtttsnnnnmnpqrvtqmnllleYST`ijmnqnkjjjjjjjggfgeijkma[dhkkjiiddccdddeehhigjjjjkllklikefddacbbabbbaa``aefedhikmihjihhhhjjighgim¢¡¢ ›š—–—˜šžœ¤ž¡Ÿ›š›œ™™––‹‡ƒ‡’’’‘‘‘‘—šœ›–yixox‡Š†…„…ƒ…€††ˆ‹Ž•—œœœ¡¡œœ™™——¢¥©¦«§®¬¬¥¢¡¡¡§¤¬§¦§«ªª¦¦¤¥§ª«±¯®µ°³¯´¯°¬²³¹¹¹¶·³²±²²²³µ¸»¶¸·¶¶¸µ¾·¹¹¹··¸º·¸¸¸¶´µ¼µ·±²±´´µ¶µ´´µ¶µºº»·¸···½µÂ¶»¸¹ºº¶¸¹»ºº¸»¹¼»º³®°´µ·¹º¾ËÁÃÃÇÂÄÃÁÂÈÇËÊÎÆÉÉÉÉÅÅÊËÍËÈÈÈÁ¿¼º°¡šŒre]aÐåõøûýþÿÿÿÿÿÿÿÿÿÿÿÿþüòéßÓº\319BGUNC3)%$$%&%%$$##%"#%&''%$!!!""#"!!!!!%*07>GKPT\]\]c`_^ba`_cbecdelkojigfcaafflfggmmoigfghjggeffhhiejfgccdghiiijjikjkhiiikpnrrrqonqonmnlprttutzuvtwtvtxuzvwsuwyuxx}wxxwrqrvrsnmlqnrmjlpsuvzuyrqnoquuussrqquqppqqxvztrqrqqnnpusvrpqrlnidST\jkoppommtjlklkjjkjihmnrbX^mkokjigceedfjjjkmllkjkmkkllklikeheeeedeabcfa`_`bifedhimlihijihjklihgkklj£¤¤¤žš™——˜šœœœœœžœ›šššš—”‘‡Š‘‘‘ŽŒ‘’••”–š‡}tdr{‡†…„€€€~‚€†ˆ‹”—˜šš˜œœœœ˜›šššž¡¤§¦¥¦£¤¤¤ ¡¡¢£¤¥¥¦¦¥¤¤¤¤¥¥¦§««¬¬®©¬¬¬¬¬¬®®³µ·¶¶µ³°°®²²´¶·»·´´²µµ·µ´²²°¸¶·¸¸··¶¶¶¶¸µ¯®®²±´³²°³²´µµµ¹¸º¶¶µ··¹µµ¶¸¸¸·¸¶¹º¹···º¸µ²²²±²µµ¸º½ÀÁÂÃÄÁ¾ÆÂÂÃÆÅÉÈÇÁÀ»ÉÉÈÆÉÊËÌÅÁÀÁÁ¿¼¶²«£—teccÓéõùûýþÿÿÿÿÿÿÿÿÿÿÿþüòèÝÕ¦R.-05:>D3'%#$'$$$$#####""####""!! !"!! !%(/5;BHQRSX]^a``bca_ba``bbcdeflnlihecbccdeedghjjhefffffggefdgfedgfecefihiklkjhkkkihhjmnnqsqpoormmmlmprrrusrpututtturrrrruxyswwvwyztorqpmljmlqllimquvvvussrpoqrssvvutsrrrqqrswvusrqqrqjoputvpqljiifUIXajkmllllkjikimjjhiijinpfZ^dijkijhgccceghfiklklkkllklnlllgfeffeabcbabcca`_abiedbfgnkjihhhhjgkhhhfbji©©¨¡œ›™ž›¤œ›œ¢¡¡¤¦ž¢—•‘‘’”‘’’’ŽŽŽ‘“˜—ŸŸŸzkght‹„„„„‚‚‚†Œ‘•œ›Ÿ™šœ¡¡££¤ Ÿ¤¢ª¤¥¥¦¥§£©£¤¡¦¢¤¤¥¥¥¤¦§¨££¥¦§¨«³«««¯®¬®¬¬¬¯³¼µ·¶µµ´´µ²·¶º¹·····µ¼¶¸´´´´´¸·À·¹¸·¶¼¶¶µ´´´´º´·²±³¹¹¹¹¹·¸¸¹¹¹¹¹¹¹¶¶¶¸¸¼º½¾À½»º¿¸»´°±²²º¹¹¹¼¾ÃÀÆÅÅÁÀÁÇÂÈÈÊÅÎÄÆÁÁÄÉÉÒËÐÉËÅÁÂÂÄÇÃÁ½¾µ°¦ž’‡xkcg˜ÜèõøûýÿÿÿÿÿÿÿÿÿÿÿýúðçÛÔ•J+))*/30'$###$######""###"!"""#!!!!"" #'-3:@FPSWYZ]__`bcbddebbbcbcfifglsnlhhddcdehiigghjgffgflgiijeecgddfleefkkriklnmlllllihikmtnqqqopotllkkmpqvtuqpqvtws}twrssttxxyvvwxxzvsrrpokijmlqllmuuzyyuvvwrpqttttywyxxttsrrvvwxwvvvvrqoprwuvpqkkikWJRgclnqnoosllhojmjjijjnkqjb^gejilijggefffhkjjlnkolmmnoolkklhjfgfea``_`bbdbbbiejddfljomqnlklikkkkmhecig¡žžœ››žœ›››œ£¡¡¢žš”ŒŒ‘‘’Ž“”––‚r^joy‡††ƒƒ‚‚€ƒ„„‡‹‘’—™œŸšœ ¢¢¡¢› ¢¤¦¤¤¤¤¤§¢¡¡¤Ÿ¦¡¡ ¡Ÿ¤¤¦¢§£¦¨ªª««©ª««««©««¬°´´´³¯®®°°²³´±·¸ººµ²²±³³³²¸³³±´µ¹¸·¶µ³´²²³´³µ·¸·¸²³±²³¸»¹¸¹¶··¸¸¸¶¸´····¶·½º¾¾¿¿¿º¹´³¯°±³³¹µ¹»¿ÃÂÀÀ½¿¿ÀÂÂÂÇÇÇÅÁ¼ÃÁÇËÊÉÉÈÆÃÁ¿ÄÅÇÉÇÅÄÀ¾¼¸²ª •Ž|lii—ÔéõùüþÿÿÿÿÿÿÿÿÿÿüùïæÙÕ…C*(''''&%$$##"###$##"!"!""!""!!"!" #&+/6<BHKPV[\]^_`abdb`edeccccdhmigjponjfc`dcefghhihfeeeigggdcaieecccfgfdgijiiijkkjjjmolhjjlmnmonooommjkjmnoorupmpqqqssstsrrmtuwuvvwyywzrsqpnmjklmmlimpvvxwxuutvqrsuwvtwvusuuutttuvussprqqoomprutspqklfXJS`adnpnnopmgjikjjjifghmihb``gekjlhgffdeeijjjlpmkojjikonjkjmfeeffcaa___babbccdeeaehklmkpomllgjigehhgdee¡¡¢žŸ ¡¢Ÿ¡¡ ¥¤¥¢£›™’Œ•””’”Ž‘˜˜™›œykf\lƒ‚†‚‚‚‚„‚€€„Љ’”œ˜Ÿ›¡ ¡ ¡£¢¢ ¢¦¤¨¤¥§¬¤§¢¤¤¤¤¦¡¢ ££¤¥¦¦§¨®¬¬¬«°¨©§§«¬ª«®°¸¸¸°¯¯²³µ±¾º¾¶³´¶µµµ¶²¸³µ²¼·¹·¶µ´²¶³·¶¶µ¸¶¶¶·´´¶º¸½¹¸¸¹¶¾»¿¸»·¸·¼¹ºººº½¼¿¿Ã¿Æ¹¸´²³·¶´¶¹ºÀÀÁÁ¿¾¿ÃÁÁÁÆÅÍÂÁ¿¼ÀÃÇÎÌÐÉο¼¿ÀÄÎÈÊÈÈÅÊËÎÅĽ·«¢›}pjn›ÜêõøüýþÿÿÿÿÿÿÿÿüøïåÙÖz=)&%%%&$$$$####$##""""#!"" !!!!!!! $)/38>EIORTZ````abgcgcbbefffffihmmmnonlkjdbbdchhkjokmhfddfhgfbacjfecbdkgeglkjgghqllikkmmljnownqoqnnnokkklmpppprqmntqrqttwssrqqutxvwu~}{xyrxpnnmlqrrnllssww{wyyytuuvvxw{uxvuswwwvww{vtsuutpoprpwtytsoqmo\OP`abipomnoollmnojkjifgimjeb_cgekllggffgjkmmqmnmmlollijmnkkjoihgjebbd`b`baccfflegffinmqmoopllhiigfggkfgh¡ ŸŸŸœžŸŸ ¢¢¡¢¢Ÿ–‹Ž‘“‘ŽŒ’“”•…‚pZefsˆ†……‚€„ƒ„ƒ‚ƒ…‡‹Œ‘”–™›˜Ÿ››œŸ¡ Ÿž¡ Ÿ¡¤¤¢¤£¥¨¦£¦ £ £¢¢ ¡ž¢¢¥¨¨¨©ª¬¬©¦§§¦£¥¤¦¨«§««¯°¯«ªª¬©¬®³²¶°µ¸¶±´´µ¯®®°°´³³³´´²°±¯¯¯²±µ´´´¶´´¯¶µ¸»¹··µ¸·¸·¹¹¸·¼´µ¶ºººº»»½¾¼¼¾¿¼·¶³´µµµ¶¸»½¿¿¿¿¿¼¾¾ÃÂÁÁÆÆ¿·¿»ÂÇÇÌÊÊÇû®½¾ÅÈÉÇÈÄÅÅÊËÌÈǽ´¯§›rlk˜Þìõùûýþÿÿÿÿÿÿÿû÷îåØÓo9(&%%%%$$$$######""#"##!"!!"!!! !"(,27?EIMOQUX]ecbccddfccaccdcfeggijjkmnojheeadhediigfiiigedeegbf_bgeccadffdgmjffefhiiljkkkjjjonnnnmmjklkijlnpomopqpoppoqqsrpooorsutuuuuz|zzuqqpmhlmpspmmptuvvvsusqpuwvvxxzuxutsttvvwwvttsvusoqrrqqqqqpmki_RU]_ahppjkkllljjjiffefdhkml\Taggglhgggehjklnnolnonlnklhjklklhhihhfccbb``_``bbddeeggjmmmnllllljghhhhgeefgh££¦¡¡ §§¦ ¤¤¤¡¢Ÿ›’Ž•‘‘’—‹Ž”•–˜˜ykeYfxŠˆ…ƒ€€…„„„„‡‹’’˜—™™œ› ›Ÿ ¢¡¡¡¨¡¢¡£¢£¢«¥¥¤¤¤¥¤¤£¤£¢ ¡¡§§§§ª±ª¬ª¨©¨¦¥¢§§§¨ª¬±²²®¬¬¬¬®±´²·°¯³··º´´¯°°´±µµºµ·³±¯°¯´¯±²¶´¸´¶µ´µ¶¸¾½»¹¹·¼¸¹¹º¹··½´·¸À¾Ã¿Â½À¼»»º¹¹¹»»º··»¿¿¿¾Æ¾¿¾ÂÁÁÁÂÄÈÇÆ½¸¼ÀÃËÆÆÇÇÄÀ¼¶»ÁÄÌÉËËÑÎÌÍÎÌÎÊÍÇÉÀÀµ©œ}ogkßêöùüýÿÿÿÿÿÿÿúõíäØÏf5('&&%%$%$$$####%""""##!#"!!! !%)06=DKQUYXWX^`ijiegghffffcefheggkkmmoosnoifdddkffgjhffjklfeegghceffdbbbcgfeemkiffefgjimmnkliomqnnmonmihhhlvpqpnosqqqsppqtstpmmmottvvvuyv{ywutuvollqqqnlmpsywyuvtxqoqvwxwzz{vxyysutzyyvtrws|usqusvrtqpppjhaWT]`agropikltlllligefefgklraV[kkoklgffghmlqoroompppnrmmlmmnkqhkihhfdfcc`b__adcddhfgkonqnomlmqmjhjinjihigge¢£££¢¢£¡¡ ¤ Ÿž˜“‘‹Ž‘“ŽŽŽŽ“”‰p\ddp…‚Ї†…€€€‚‚„…‰Š‘’•————š›œžž ¢ž ¡¡ ¡¡¢¢¢£¦¥¥¡¤£ £ Ÿ Ÿ¢£¤¤¦§§©ª¤££¤ ¡¡£¥§¨®±ª§¨©ª¨«¬¬«¯³±°°¬¯±·¸¶³µ°°°°°²±°¯¯¯°®¯¯¯°°°±°°°±µ¸¹¹º·¸¹¸¶·µ¹·¹³µ´µµ¸º¿½¿½¼¹¹º·³²²ºº¹·¸·¼Á¿¼¾¿¾º¿½¼º¿ÀÄÇ¿º¹µ½ÀÅÊÉÅÄÁ¾¸¸ºÁËËÉÉÉËÌÎÏÑÔÐÌÌÊËÈÈÄÃÀ³¦˜‰}ollšÞíöúüþÿÿÿÿÿÿúôìãØÄ\3('(&%$$%$$$#####!""""!!"#%(.3:AGPTY[^]]]]aceghcfggdfffdgjfegillmmopnjieeeddeegjjhggjjgdeeedcceeeaccdeddeffdffghfgiimmjhljolpmlljfedffjmppqqqqoortrnpsrrrmmloppqvutryuxvtqqpoknpqkjilnrtuuutwrroruvyvttttsxuutttvuurrrstttspvttqpooqlfcZWY`ejoomljllnkkjihgdeeghjlaT]fjiiihefgjjlmmnommlllnmmlljkijjjgigghedfcb___`bcddbieiopnmmononpmkgggnihghfgc¦¥©£¤¤¤£¥ ¥Ÿ –‘‘‘‘“’—”~reWetz‹‡ˆˆˆƒ€€~‚ˆ‹’”““–›˜œ™žš›œ¢ ¤¢¢¥¡ ¤¡¢£¤¥§¦©¡¥ £ Ÿž£¡¤£¦¤¦¦§¨©¥¦¦¦Ÿ¡ ¡¢£¦ª´¯±ª§¦©ª¬¬²°¯¯µ¯®®¯°²²ºµ´³´´´³·±²²±±²¯±·°±°´°²²³²±´º·»ºº··¹¾¸¹¹¹¹¹³ºº»¼¾¼¿ÀÁ¼¼º¾·³´·µº¹¹¸¸¼Â¾½½¾¾¿¼»¾ÆÂÉ»µ·¸¼ÈÄÉÉÊÃľ»¿ÆÅÏÉÇÊÍÍÌÍÜÓ×ÒÔÏÐÐÑÌÌÇʾ´¨›‘‚tkn âìõøûýÿÿÿÿÿùóëâÔµS0'''$$$#%$$$$###$"###" "%*08@GNTX[__dbbbbefeeghikhgfjgfgkifgkmrnpmrnkiiddejfhgjjnhjhkgfgjedeffifffgggdccdeeeffjggglmmjghmlonpkkkjdadeflmorwtvpnowtqnpswqqlmmurxqutrqzvyurpoomntqqkjjpqvvwuvuxrqqvvvwwsrtyxxustwxzwvsvr{ttssrvtxrooplkebWW^ciuppkllqmnkljjhkjiiijocXXhijjjhhhghpqspsoqmnllkonmmmlljrjhgjfgffeeba`a_cdkfeehgkppmmmunnnommjkjmigfgghg¥¥¤¡¤¤££¡Ÿš–”ŽŽ‹Œ‹’–Šsbd\lƒ‚Š…‚}~~€‚„ŠŽ’‘”••–—•š˜——š››œŸŸž¥Ÿ¡¡¤¢¤¦¤¤¦¤£ žœ› Ÿ ¡£¢¤¤¥¢¡¡¡ ŸžŸ›¢¢££¥§±¯««§¦¦«¯®±±±®¬°°±³µ²²³²²³´³±°®¯®²¯°¬±¯³®±²³³µ¸¸·¶´µµ¸¹º¸¹¸¶µ´³¹¶º»»»»¸···¸µ²´µ¶´µ²¸·¼À¿¿½½»½¾¼º¾¿Á½¹®´·¾ÁÂÂÂÁ¿»¹ºÁÇÈÈÊÉÉÊËËÍÎÓÐÑÎÏÏÐÑÑÎÌÇÇÄÂÀµ«¢•†uqs áìôøûþÿÿÿÿùòêâУL/'''%%$#%$$$###""!""!! "$*/5;CLPXXZ]``aaaaadeffgiiijhggggghhhhhknnlolkiihebdfgehhiiheeeddgkiefihfeeggdbbbccddghgegeghhfffghmknolijjjbdffgkmpssrtnpqqqpmqqqmnlmnnooppprrttttroooonpnlhjkoqrprsttuqrswvvtsrrrtuvsttwxwuwrsssssotsusstqplhhdWQ\bfkoooimlnmkiiiigggjjigcZ[^gjjjigggijqsropnmjjjlihhjjmllkkkjgheeecabbb___edghhghhmoomlkmmnnnnlhjkkkgefeec¦£¢££¢£¡ž—“‘‘‹Œ–—™ueVbktЇ‰…}„~~„‰ˆ‘•–—™š™™•š™˜—™šžž žžŸ¥ ¤¢¥¢©£¡£¨£¢ ¡žœ››¡£©¤¨¤¦¤©¡¥¢¦žžŸ£¥¨©¬§±¯®®®®²°±±·´¸ª²¯³±±±·±±²¶²´³³®°°±¯²®°ªª®³¯²²¶µ¹³¹¸¸·¶´¹µ¼¼½¸¸¶µ´³µ¹º¿¿¿ºº¹¾µ³±¯³¸·¹¶·¹Á¾ÄÀÁ¾¾¿Ä¼½¼»¿ÇÁüµ¸¾¿ÆÃÌÄż¹¼¿ÂÈÆÍÉÊËÌÎÒÒÑÐÕÔÔÏØÖÕÒÔÐÓÌËÍÐÅÀ¾º©‹|tw£ÝéôøüýÿÿÿøðéâÑ’E.''%$$%%%$%####"" !%*18?GMWZ]aefgghbbaddggjimnojnhjikhhgfhkkllmloigfddcdddgfjhkihfgeffjihfjjlfeemeaaabedhhkjjijjjjjfhfjjmlrljhmklkkknnoptrqqtprrsoporrrqpoonnorppqtstuuuursssttliilosruppr{tvrutwwwsttuuuuztww|wwvxrstvststtzsrrtojiiXNVgglnqnomumolkhmjkfgijilg^\^bgilihhllmmrqrssmkighkkkhiinnplmmmjnffcbbebb_`bedghoikkooqmlklmpnolkkkkkheehege¢ Ÿœ›–“ŽŽŽ‘ŽŒŒŽ‘“ŠudbXhx|‰†ƒ}~~}~~€‚ƒ…‡‰Œ‘•˜š˜˜™—˜”””––™šœžžŸ ¢ £¡¡¡¢ ¡¢£ ¡ œ›ž £¥¤¢¢¢¥¤¤¢¢¡Ÿš ¢¦¨©ª©§ªª««¬°¯°°±±¯®ª¬°°®®¬®°°²²²°´¬««¯¯®¬ª®¦«¯°¯²³µµµ³¹´´±³´·µµµµµ·´³²µ·º»½½¿¶·¶µ¯¯¬°³¶¶·µº¿¿¿¿¼½º¾À¾·º¹¾ÁÁÀ»½ÃÅÆÆ¾¿¿¼µº¼ÄÊÇÆÉÇÊÌÐÒÑÐÐÐÑÑÑÏÙÖÔÐÐÎÏÌÎÏÎÈÆÅ·®¦’xtÜêõúüþÿÿ÷ïè×Ð>,&&%$$$$$$$####" !&*/4;CIQTX[^cifeeeha``ddhhiinmnjjggfffgfgikfllmjiggcbbbabcgfgffghcddggifgfiljhgggcccbafeghhhiekijfffeekmmlkhjimmmlmmnloppqpnpoqqsmpppopoononoqqopqsknnoourqoqnlgjkpsrrtpoottsrrqwusqqqrprsstwyyvutxqssrruxvttqrqplkjZMU_dhmonkkknmifhhhggfjkkhhd`^cgghhfgimnnopnnkjjkigfmnkhhgfffgjiihhgdabc`]c_bedddehhkknnnlkklmnnoikggegffeee`\£ ¡ ¡—•’ŽŒŽ‘˜™š…€hYado‡‡†‡†€}|~ƒƒƒƒƒ†‡‹‹Œ‘–˜œ™˜›—™Ž‘–™šš› žŸ £¡£¡¨¡¢ŸŸŸ£¡ŸŸŸžž¡¦¦¥¤£¢§¢©¥¥¤¨ Ÿ¡ §§«©§§«¬«µ³¸²´¯´®¬¬©³±°®±°±°¶µ´±³ª«¬©§©¬¯±°´³¹µ¹´´´¸¶¶³µ´ºµ¶µ¸µ¹º»»¼º¿º»½¿¶º´±¯°±¶µ¶¸»»Â¾Ç¿Á¿Ã¿¾¼Á¿Æ¿¾ÀÃÄÇÂÉÆÈ¿À»·¼ÇÈÊÇÆÉÍÎÎÑÙÔÕÕÕÖ××רÚÖÕÒÒÑÔÔÔÑÏÑÒÑÑÅÅ´§’ttŸØæõøûýþöïçÑËu:+&&$$$$$$$%"####'+19@EKS\^`aafniifgfgfghjinnmkpoojiggefgihhhlkklmhfghbbbcbbdffifhgheddghjiiiijkjihgggcdcffkkmijkljkfhgjkmopkjjlmrpsoqqqopppppqroqpsotttosopppqvsvttrtnnnnouqpowmkippvs|utouqrssssrwtsqqpwpsrttxxyvuswpsqprvwwtsquqpnq^RR\`eksnmlpkmjgfhhgghgokkhib`bhjlihefinotrroplkikjhhnlkjigdefgjjjijebbeb`_b`eejgiijkonnonkjkrnnmpjlhhhhffhld]_ Ÿœ˜”ŒŒŒŒŽŽ’•”’yjdVguz†„‚€}{{z~€~‚ƒ…‡‰‹Œ‘”—˜šœ›–•“‘‰—œ›™š›¡ž ¡ŸŸžžœš Ÿ £©§¥¤¤¤¢£¡¡¢¢¤£¡ ¡¨©©©©§¨¨©¨¬´±²²°¬¬©¬ªª¨©ª¬«¬«°°°±¯®®©¨¦©§¨¤©«¯±±±³´¶³³°³´¶¶¶³²²´µµ´¶µ¸¹ºººº»··µ¹·º°°°²³µµ¸¹½¿¿¾¿¼À¿¿¼¼¼½¼Â¿¿»¾¿ÂÂÃÂÃÅÀ¹´¹ÂÄÅÇÇÉÌÍÍÑÓÕÓÓÒÓÓÕÓØÙÙÕÕÓÓÓÔÐÒÒÒÒÒÒÏÇļ²¢‘€vq—ÎæôùûýöîæÎÅi7*%%$$#$$##" #%*/5;AJNQV[^aaafljgieffgeghklmmlkkkkhgfffhhiijhmpmlleehc`bbcdefgffefgffedfehhhhhggeiheegbdcefjkjhijjkhdggjiihjjklnoooonpononnpnnoonnnolnnooomorrssqutrponnmopvpqplfiipsssvttpuqqqrqsrsrrqqqpoprssuvzsuqrppprtwtssrqpnmn`PT[^bjmnnlloklhgehdggghomjfb]ciikiggehknpqqpoomkjjiiijjjnc\^`ffkiikkbcce`````ffffjlllnoomljkllkjjkhiihghehkhc`c ™™’Ž‘“Ž’“•—•š‰„l\``l†„††€}{yz{|ƒ‚ˆˆˆ‹Ž“”˜—œ›š›œ••’’“––¡œ›šž¢¢§£¢žžœœœ¢¡¢¡§§¨¨¨§¦¢¥¡¡ ¡¡¡¤£¥¤©©ª©©©±§ª«±®µ°²²¯©©ª¬«®©ª¯ª¬¯®¯«ª¬±¨¨ª«ªª®²²¶¶¶´·µ»²µ°·´µ¶¹²³³¶´¸¶¹¸»»¾¹ºº»¸¹µ¾·¹´´³·µº¹ÀÀÁÁÁ¾¾½¿¿ÁÀÀ¿Â¼ÁÀ¿¾ÁÀÈÆÅÂĽº·³¸ÅÂÃÅÊÉÍÎÓÑØØÙÓÒÔÚÖÙÛÛØÜÖÙÙÚÕ××רÙÙÚÕÕÌÊÄÄ´ª‘qn›Õäôøûôíå̶^2)%%$$#$#" "'+18?HMQTY[abiijlmlkhifffgillpppnnkjjmgfgmgnmmjlkpookkeddaadfihlffggeegkggdfgmiggkgiiifdefcddegjjjijkkigejhohiijloosoonqpuppnmmppuonmpoplnnoooooqustqwsqnopqrssvprlgelnwwwuwvuuvqsstsxxyrpqwqqopr{utu{svqttsswwwsqqrqpppbWVYZbiwpqnllolkkkgggggmlqlib_`iikihgjklmqqrononkkkkiijkiidZSR`eekihhlagfeeeb`bghgionmlqqtmkjpmmlmloijilghhmihaaf•ŽŒ‹ŽŽŽŽŒ’“–’“}oeVdowˆ……„{zy{~}}€ƒ…‡‡‹‘‘‘”––––“••••””–œ™˜˜˜››žŸ¡¡ š™˜š™™™›™›šžŸ¡¢¢¢¥¤¦¡¡Ÿ¦¡ ž¡œŸŸ¡¡¥¥¦¤©¨¦¤©¨«§ª««ªª©¥ª«««©ªª®ªª«¯«°§ª«°¬®ª«¬¬¬¯±³´µ²³³³³µ²±±³³³³³²²²µ´··¹¹»º¸´µ³¹¸º´³³¶µµ³´µ»º¾¾½¹»»¾½½½¿¿¿¿¾¼À½¿¿ÁÁÃÀÀÀ½¶µ¶»¾ÂÂÃÄÉÊÎÏÑÑÑÐÐÐÓÔÕÕÙÚÛØÚÖÚÖ×ÓÔÕÙÙÙ×××ÓÌËÇļ°žŽ{vu™ËåõúóìäǧW/(%%$##!! "&*/4:@HPUYY[]_cdhijkkjiiidghijkklllmlkjihdfhlfikkjklmkjjjfddccgjjhkffggeeeeedcfhgggffeddfdddfbeeghhhiijhgfeejhhhjilnoonnnmoppnnlmnqrpllmmmljmnoppopqsstpppqnoppqrsvqlfffmpuvusvrtssrrssswuuqqqpppnqs{vutssrqrrsttssqppnkjjbXVTY[fonllmmlllmmjfgeghmlmjc]`dfgffghkmmnqnmlkkkhkkkihghhh]J<Taffkihfeabababbdffbhlmklmrnmmlkklmimlniihkfhlmgg\el’Œ‹‹•Ž‘‘“’”™”p_]Xg|Œ‹Š‚€~}{zz}}€€ƒ‡‡‰‹•“˜–›–••˜–—––—š›š™˜ž£ ¥ ¢™š˜™™˜œ £¡¤¤¥¤©¤§¡¢ž¦Ÿž¡¡¢¡¤¦¦ª¤©§¦§©©°¨««¬¬°¬©ª¬««¬¬®¯®ªª«¯¬°«¯®®°±±µ²···´µµµ³¶µ¸¸¸±´³¶´³²¶µµ¶¸¸¾½¾º¸´¹¸¸¸º´·····¶º»Å¹½¼»»¾½Â½À¾ÅÄÿ¾¾À¿ÄÃÂÃÆ½¿½¼¾ÁÃÆÅÅÆÌÉÓÏÔÐÒÓÔÔÔÔÙÙÛ×ÚÚÚØÙØØ×רÛÜÜÜÜ×ÜÕÓÒÓÌÊü¯¤“„xv›ÏâôïëÜæQ.'&&#" !$).39AGLPW[`beghfkhmnolnkmikkljnmmkkkkmrkiighjjjiikmjloplnijfedgfnlnijghgfdeghccdffeffehhhefdcceefimlklliieefeekhghjirrrommpmsrqnomnouppprmplklrnoqstvsxxzprrqqrrsquswmheiltrtutrwwxtrrrsuswtvvwtrrrput|wxttrxruswutttpqnkjldZUYY[bnnnktoonmllkigggllmlme_^dgkgihnnommnqnokkjjjjkooohgggO=CXbghkkkfhcb`fccdhhfemmnkonqnppqmsmnlmmnmnjkjklmgehmmŒ‰‰‰ŒŒŽŠ‘‘‘“‚xiV`dp…ƒƒ‚€y}~|||}„…†‡‰‹Ž“”˜–˜––––———˜›™šž›š™žžžœœ™™™šš™œ ¡£¡¤¥¤¤¤¢¢ŸŸžžšž›¡ ¡¡¦ª©§ª¥¦¦©«©¨§§¨§®©¬««ª«©®°¬«ª©¨«««¬®¯¯®¯±±²±´´µ²²¯³³¶¶¶µ³±³²¶´´³¶¶·¸¸¸¼»¹¶µµ¸¹¹¸¹µµµ·´¶¶ºº¹¸¼º»»¿½½½À»¼¼»»¾¾ÀÀ¿¼¾½º´³µ¿ÃÃÂÃÃÅÇÇÇËÍÐÏÓÌÌÍÔÖÙÙØÕØ×××ÙØØ×ÙÛÜÜÜÜÜÖÛÔÕÔÒÎÍÈÁº´ª™‡~v–ÈáëèÒ¸¦M.&#" !!&(-08>DJNVXZ_ccdeefffgmkkkkimjjhkkmkkjkjknkiighklmjgjllkmqnkheeeffefiiigfedcdbeheacbeeeeeedcgeeeddgiikiedeeedbbbefhgggkjjkklmlmmmlnlmnopompormoilmrnprrsrrvxsoqonlonsrqqkdfflqqrtttsttussssstqsrttuuqnootsttuutrrptsuuqoopnmkjh\YVZ\dknqomonmjkiiihgggmonke_`bflkfhinmlkmmmmlijjjjkkngihheVEJT^gijjjjggb``dcdfhiffghhioonloomjkllkmmmnkic]dhigfghhŽˆ‰ŠŠ‹ŽŒ‘‘’‘—•“wi_Ucv{Œ„„ƒ‚€~~}~||}~‚†„‰ˆ‰‹Œ’“™•—˜œ–—–›™žš™™ššžœœ¡ŸŸ œ›š›œŸž£¡¦£¤¤¨££¢§¢¤Ÿ ŸŸžŸ¢¡¦¦«ªªª§©©¬ª©§§§©§¯®©´°¬®ª´®±«ª¨¨©¬®²³²²²°´²·³µ´´´¹³´²¸´···´³³¶²·¶µµ¶¶ÁÀÀº¼»¹¸½¸¸¸º¹ºµ¶µ···¸Á»»¸¼»Â¾¿¿¿ÀÀÁÁ¼»¼¾¾ÁÀÀ¾À¶²´µºÂÀÃÄÈÄÉÇÎÎÍÍÑÏÓÎÐÑÖÖÚÚÙ×ÚÚÚÚÚÛÛÜÜÜÜÜÜÜÜÜÛÛÛÕÙØØÏËÉȾ±ž|t•ÄÚå̱F)#" #&+07>DKPUX[^cfigfgghiffgnjjkkjmlonolmlolnmljiihhjkolmmmlooonmfcdfgighgkhifedcchggedacaddddieddhfhhkjijljjdfdjedcddfhjjjjlkkklknnnlkmplmnrnmlppsnnnoosostsrpqvroptommsorrumfdhkqssswtvt{uutwststtvstuvsqmoqttssyvwrsqsrxronrolkjheYYY_fqrropponljlipikiijmopf`^cehjkfhkvmljsnomoimjlkokliihj^QLW^ggokkjmgla`cicffjgfghgfhqnnlonmmmmokrnomhd^`chkffgkgЉ‹ŒŠˆ‚o\a]l‚„†…‚€}||~~{{|}ƒˆ‡…‰‰ŒŽ‘’”–”—˜—”–•›–™šš™™šœ››œŸžœ™™šš˜œ›œœžž¡¡¡¢¤¤£¡ ŸŸŸž›ž¢£££¦¦§¥¥¦¨¦©©«©¨§§§¨¨ª©«ª©©««¬«³¬«ª¥¥¦©«¯±±°°²®¯²²µ°³±³¯±±´³´²±±±²´²³°µµ¶¶¹¹¹¶¹»»····¶·¹ºµ´´¸¸º¹»··¸¼¼¼¼½¼»¹··»º¼½½¼¿¾¿¾¶®³µº¾½½ÃÄÇÄÈÈÈÈÈÉËËÍÊÐÕÔÓÓÒÕר×××ÚÛÜÝÝÜÜÛÛÙÛÛÛÚÚÕØØØÔÓÑÏ˾³‰zo޾¼¶ˆc:%%%)-39@FKQU\\]aegjiiiiihhggghghgkhmmnkllkjjjjjkhjkihjjllmjmlmmkge`ceegggidffifeedccccad`baededdbcceeiikkjlkgfcddcacbdeikkjjjjjjilklkkknnommnnmmmnmmlmnnnonppomponloqnknnonpnmhgeknqutsttttttttutsstsusttuommqtqoprrrqoqqtrqmnnoljhheZSXZemnonnoonnkijihhhimoolg]^_dhiijfilolkkkknlmkljkilkmiig_WSP[cffjkkkjgcZbhhcddfdghhggimkllllmmmmnkkknlgccefghffede’‘–‘~udWakt‰ƒ…„…}||~€~‚ƒŠ‰‰Š‘’’••›•˜—˜™šš›››šžšœŸžŸ›£ššššžšœœŸ£¡¥¢££¦¥¤¡¢ž žœœž£¤«¬¬¦¨§¨¨©«««¬¬©«§§©¬¬®ª¬¬¯¯¯¯³¬ªª«¨®®¬´³¸²³²²¯¯²²¶²´´´µµ²¶³´´´³¹²µµ¶¶¶¶º¹¸¸¸¹¸ºÂ¶¾·¹µµ·ºµ·¸¾¹º¹¾·¶¹À¿¾¾½¶µ¶·º¾½À¿¿¼Æ½¿·±´º»ÁÀ¿¾ÅÄÈÅÎÈÈÈÈÈÈÌÐÎÕÔÕÔÓÑÔÕ××ÖØÛÛÜÜÜÜÛÛÛÛÜÛÛÛÛÜÜÜÝÜÜØØÐʺ«™†tnއcK0,-17=BISW[]`cgikllmqmmnnghhkhhiiikjrnpopkkjmjjijllllkkjmkmmnnnlleccieeefgidffhihhgceedddficfegcbbeeefijojjjjhfddcbcfffinmolojojjjlmoqqovornnpqosprmompnnopoponnpnkmrnkmrooqtlhghisrrsusxw{ttsstwsrsutvsuutmmouqpooqupmourxronqotjhhgYSTYbqrrnmnvppljijhiggjppqjc`acjjjiiioooprlmjnlqmoknjrlllnaWVVYfghhkjmlkfa`jiibaeggnjjjjmrllklkulnlrjhjrjgfggjjmhhddcŽŽŽŠ†tgcXgx|ˆ…‚€~~|||~€‚ƒ…†Š‹ŒŒ‘’’“”””““”—˜˜˜˜™™˜˜™œœœš™™š—˜˜˜—›šž£¢¡ £¢£¤¤ Ÿ›œšŸŸ¡¥ª«¬¬¨§¥§¦§§¨§¦§««ª¨§¤§©¬¬«¬¬¯°®®ª©§¨¨®±³²¯´²±®®¯²²²²´´³°°±·´³³³³¸²¶¶·¶¶·¶³¸¶·¶¶µ¶¶µ²µ³µµ´µ·º½¹¹¸¹·¸º½¹·´±¯´µº»½½¾¼½½½¶¶²´¹»¼¾¼¾¿ÄÄÅÂÁÂÂÁÈÅÉÌÍÍÑÏÎËÒÐÐÐÒÑØÚÛÜÜÛÛÚÚÙÛÜÜÛÛÛÜÝÝÝÝÝÝÛÙÖ˾´¦–‚zsnhbC=9=AGNSXZ]^`bdghjlmopmkggggggeeehhjjmoojkiiijhhgiklhkjjjmlmlmokhgbdfgeechhhdffffdaa`aaccdef_aaa`cbcefgjkifffjifddbeeedhjnmmllijjjjmponnnmlqnoppnnnmkmlonprpppnpqqnnqnkmoporuqjheikqqsssqttutspsttpqutssrqomloqplqrqqqppqrrqoponmkigeXNTXbllmmmmnnmmkjikfhejlppke`^bdghhiijllomkgijnlpmoijimmlmeWPMW^eiiiiimmkcccffg^beefggfcltohhhkjjkmhhghhkggfgijkiheaba“‘”‚}j[adn†…„„„~~||}‚ƒ‡ˆˆ‰ŽŽ““”’’’”“““–•œ˜™™˜˜˜™›œžž ˜—˜š˜›˜™š£¡ ¤«££¡¨£££¤ŸŸ ¡¥¦ª©®®®§¨§§©©ª¨§¦°©ª¨§¨©ª¯³²²®¯®«ªª©«¶¨®®¯±°²°¶°¯¯···±´³·³³²···´·³´µ¸µ»º¾¹½¶µµº¶·µµµ¹µ´²µµµµºµ¸¹½º¼¸¹·»º¿µ²±¯µ»»Ä»½½¾¼Å¼¿¶··¸¹½½¿¾ÂÂÈÆËÂÆÂÂÂÉÈËËÖÏÓÎÌËÑÏÏÒÖÕÚÙÛÛÛÙØÙÛÛÛÛÛÛÛÜÜÜÜÜÜÜÛÚÙÑËõ«—ŠyreWNORVY\_cigecddhimlnnqljikjnihefgkikklntilhpkmiijmkkjjjnmqlmllkjfgggfgffchhhhifeccabcgggdfed`````dclmmgmjfedfjhffffjghhnnnnommjllmmnoroonnlrpqqrnpmllpoopssuprqysropnmlnoqsvqojijppqqwwvssuvtrsutsoqrssvqrlkntrpqstrqrpsqyqpqqnmkiggZQQV_mmsoqnqonmnlllkghjtmplg_^afgffhjnlqmolkhhjmmpmoiijlnqkcQIQ_ekinmqknkkeeehgfccddeiggfpominikjiilgigjhpfkghhlihebaeb‘‘“ŒŠxmfZeovƒ}z}}|{{{ƒ€‚„†ŠŒ‹‹Ž‘’’”‘’‘‘’“•–›—˜™™˜šœ››œ›˜—–––š˜™—œ¢ ¥¨¤£¢¢¡¡ŸŸœžŸ¡¢¥§ªª©¨§§£¨¤©¬©©¨¨¦§£©¤¦©««®°±°¬¬«ª©©¨©ª©©®«¬«°®°¯¬¯¯¶³³±±¯¯°²²²³´´¶³¶¶¶´¹¹¹¸¸µµ´´³´´´²²²³²µµ¶¶¶µµµ¹¸º¸·¶¹·´®¯µ¼»»ºº¼»¼»»º¹¶·¸ºº½½½¾ÁÃÃÂÁ¼ÅÁÃÃËÉÉÊËËËËËËÌËÐÕÖ֨רØ×ÕØÙÛÛÛÚÚÛÛÜÜÜÜÜÜÛØÖÑÊÊû´«œ’|qmhggiklmmljfffijllmmnkjhkkkkgegiiijhlmmiihhhijkmmkjhjjnnnlljjgfcgefefcecheeccccccacdghgeddca```adfmiigfeedfgggfghhihijlklonnmkkjiglmnmljmlllprrnnklmpnptsponrppoommkllnorspljgkmqsrsvwvssusqsvusrprqrssqnjosqnpptwrpoorrqppopkjiidYPTV^flmmkklnmljlllkihknnnlh^YaggggfjlmmllmkjijjlmokkijllmlgWLQ\dijjmmlkjhgeefc`edeeeeeeefplhfghighggghfihhegegfeefdcbba˜˜–‡…na`]i~‚„ƒ}~|||}}}{{~ƒƒ††‰ˆŒŽ““”’—–•Ž’˜”š’—•›˜š˜š˜žŸžœ œœ—˜—™™™šŸ¥¤£¡©§¦¥££¢¢¢Ÿ žž¡¢¦¦¦¨«¨§¥¦¢©©«¬¬¬©ª¦¨§§¥¦¨¬²°¯¯ªªªª©©¨ªªª®¬®¬¯¯²±³³´²·³´±²¯³³³³²´¸·¶¶¾¹»¸¾¸¸¸¹´¶¶¶·¹´´²³²¶µµ·½·»º¹µ»º½»Â¹½³°°±µÂ½¿»Á½½¿¿¾½¸¶¸ºº¼¾ÄÁÇÁÀÂÅÀ¿¿ÅÄÅÄÌÈÈËÍÍÎËÐÐÐÏÔÕÙÙÚ×ÕÕÕÕÚÚÚÚÚÚÛÛÛÛÛÜÜÜÛÖÔÐÌËËÃÈÀÁ±«§¦’‚€€}{„€}ytpnoqpoonnnnnmlkjjgghijjniqlnkjimhjlnmommkonnmmlljkgfefhkffeieidedgbecccdehiihhdbaa`abginiifffedlffffipkkkkknkllklmmnjijrqrlljmlmnvsrnmmpnpnrrspnnrqtnmmllmlnmtplihjmqvssuyxxrsrqquuvtwssruswonpxspnqrxuspnpqrrqropkiij[RRW^jkqnnkllqmlklkjihiqmmkj`Y^ggjijkqoonlmmmmnppooolkjjmonl[NJUaonnmnmmlkgghleabdfhiiehgegpkfefgkgihhghhigeejgjfgfhcbbacŽ‹|qhYcfp€}yzzywxxyz~€ƒ…†‡‰‰Ž‘’“‘’‘‹””’’‘—–˜˜˜——˜™™š™˜˜——––˜™›œ £¤££¢¡¡¢¢ œžžŸžžŸ£¤¥¦¦¦¦¢¤£££¨ª«§©§§¦©£¨¨¨¦§¨¬®¬«®§©©©¨©¨ªªª««¬¬«®¯²¯²³³²³±´¯±°³±²°³´µ²µ¶¸¸¹·¸´¶µµ´µ²²°°°³²²²´´¶¹º´¹¸¹µ»ººº¹´±ª°³¶¸Á½¾º¼½º··µ´³¶¸»»¾ÀÂÁÁÁÁÁÁ¾¾¿ÃÂÄÅÆÅÈÃÅÆÆÇÇÉÎÏÒÒÒÒÑÌÎÏÖÔ×ÖÖÔÚÙÛÛÛÛÚÛÜÛÖÍÉÈÈÄÃÃÿ½´®¦¤–‰‡……‚…ˆ‡ƒ}zzyyywusqstronkkjihijlkkimmmmkihhmonmmmmkprokjhhhhgggijhfffeddbbaaacdddefedfedbaaa_abgljhgdebddddffijkkkjighikkjjmplhkmoqollimlooppolmnpnpoqqsnnnnopmmmnnnkmmmlifkppqrqsuyxsnpppqqprrssrrrrmjqwvrpmooqqqoqtrppqnlkiigZOTU]djklllklllkklmigghikkjga[`jeeihknnnpiiinqqpqqqnnlkhlnnp_LJK[fnonmmmmmlgijfacdhihhhec_gkkigefgkffgidgfgeffidfffdcabbaf‰pba\gw|‚ƒ}}~yzxxwvy|……ˆ‰Œ‹‹˜”•‘”’’Ž“““’‘–——˜——žš˜š˜——™™œœ›œ¡ž¦¥§¨¨¢¤Ÿ ŸžœŸ¢¢¢¡¦¤¬¤¥¥§¥¤¥ªª«¨°¬²§¦§§¨©©©©¨ª««°¯¯«®©ª¬°§©¨«ª²²²«®®®°´°µ´´²¶²³¯³°³³µµ·´¶µµµ¸¸¹·¹·¼µ·´¶²³±°°¸³¶³·µ¸·¹·¹¸ººººÀ¶´²°°¸´¼¸À¼½º¹¹»¶¶´²µ¹»Â¿ÄÄÅÂÄÁÆÁÀÁÇÂÄÃÈÆËÊÉÃÆÅÇÈÊÊÍÍÑÑÑÏÎÍÍÏ×ÔÚØØÔÚÚÛÛÚÚÚÚÚÔÐÈÅÇÌÄÅÄýº¶¸©¥™•“‘Œ‰‰ŽŽ„‡…„…†……†„}{}{xwvtrrqqrqpooqrsurpoomspooopssrqpjhimmmihikihhigedecb``bgghhgefffeddfdccddghhhkfefgdfdfiojjkmkihkkmkjjmlkjmmnnnmplmlvomnooqopqstuqrsuonnsnoosrrrrnoigiopwrrqttzspoopppqsrstttrrlinyxvpppopoppqvttponokkkk[PS\]ekrnooplmllkklnhghjlrkjb]_hheehipnnmpiiioovqyuuqqnmmuppgYFCQhlnoqnmnmmmmnjfdihojmmmfddjjjiifmjkkkkjglkkhihjdgfidcdebbdkYabn~~~}|{zyyxuusx|€ƒ†ˆ‰‰‹ŒŒ‹‘’‘‘‘‘‘“”’“““’•–˜š™˜—–——™•––™šœœœœŸž¡¡ ŸŸœ™™™ž›žžŸŸ¢¢£¤¤¤¤¤¤¤¥¦©¨ª¦§§§¦¥¥§¨ª«ª§§§ª¬¬«ª§ªªª¨«¬ª§§¦ª«¯®¯¬¬¬¯±³¯¯¯°°µ³´¯±°³³µµ¶³²¯´´´³¹··¶·´µ³²¯®ª¯°°°°±¶³¸··µµ¶¹¹·¶³®±±³´¶´¶¸»»»º·¶µ²´´µ·º¼Á¾¾½À¿¿½ÁÁÂÃÄÂÂÁÃÃÅÇÅÃÄÅÈÉÊÊËÊËËÊÈÊÍÍÎÏÐÓÒÕÔ×רÙÚÖÔÓÐÉÇ¿ÂÃÄÄľ½¼ºµ¶«¦œ™”’‘Ї„†ˆ‰‹‰ŠŠ‡„ƒ‚‚}{ywvwwwxrpruvwvtsspnrqqoppppqophjkmlmijjjhijihfceec_bdgiifhegfffefecbbeghhhiihilfdddhjkkjihhhhkkjijjjjjjlmnmljklllonllpoqmrusrqprrromlmnooopromigehkoqrppoussoopppqqpmqssstqmhntyuspqqpmopqrrrsoopmikn\OU[_cjoqnoonllllkkkighiklqkc\afgeedgiomlkjiifjknpuuuqonpqrpqbPBM_iponjflkmjmjihgeghiijlifffghiigfihhgjiighhhhihfdeeeea]cccdXcru…€}}~|zyyz{zyuuy‚‰‡‹ŠŒŒ‘‘“””‘’˜˜˜“•”˜“•””–˜›Ÿšœ—•––˜™–™™Ÿž¢Ÿ¡ ¦ ¤¡žžœ›˜˜šžžŸž¡¡¨¦¬¤££©©ª¤ªª©¨«¬¬¤¥¤¥§«ª¯«§¯¬¬±««¨ªª¬¬¬©¨¨ª«±²´³µ®²®²²´®±±²±µ³¶¶¶´¹¹¹¹º²±³»´µ¶»¶¶¸¹¸·²²°°¯±±°²¶´ºµ¶µ»¹¿¶´³°±·¶½´·µµ¹½¼Á··µ´´··¼º¾¼À¾¿½ÂÀÀ½ÅÄÅÃÆÃÄÄÅÁÀÁÂÄÊÈËÉÎÎÎÈÉÉÊÇÊÌÎÎÎÏÕÕÕÔÙ××ÖÙÓÏÍÊÉÊÂÂÃÈÃýÀºº´´«§Ÿ•–‘Œ‰†‡ŠŠ‹Œ‘Ž“…‹„ˆƒ„‚€‚„ƒƒwtv|€~}|}tsrrtuuyusssoonnnqnnnommnpqpigfihhfeglknhijkjmmmjigffkknjjlponigfghlknmnmmmmkjjhikkllllppqqqjlnrqpmlnqqrrsuyrqsxwvollonqopprmjfceilsqursqwrqnoprrvqpopruttnlmwuytsruptopqrsxrtppnmno^QR\`gkuprrsnmmpoplmjgfhlqosf_`fhmhfellnlkkjhhfkkkntuvqpqvuvpqXIHWdpopkhflknmmiihhhhiliiigggjmopjhhjhhiijkikiiimhfghhmd``bcidj||}~~|zyyyxxwwuxv{€ƒ…†‡‰‰ŒŒ‘ŽŽ’’‘”””‘•’“’’’•—šž›–•’“””““”š™›žž¡¡ ¡›——•™˜šœœœœ ¡§§¥¢¢¡¢£¦¤¤¥¦£¥¢ ž¤¢§©«ªªªª§¬«¬¬«¨¬©ªª©§§§¨©¯²²±°°¯¯³¬ª««¯¯´±±±µµ¸¶¶³³°³µ¶´µ¸¶²²±°¯¯¯±²¯®±¯±±²¶´µ±¶µ·¸¶±±°±²¶´´³³³·»º¹·±³³´µ¹¹º¹¾¼¾¾¾ºº»¾½½½ÀÁÀ¿À¼½½½¼ÄÅËÈÇÆÆÃÂÂÄÄÇÇÇÈÏËÎÏÐÑÑÐÑÑÒÑÐÍÈÃÿÃÁÂÂļºµ´¯®©¨œ™‘Ž‹‹ˆ‰‰‹†‹‰Œ‹††‚ƒƒ‚‚ƒ„ƒƒ|xz€€||uvwy|{zyyxxtqqrpoooonnmoqponjhfihhfhjjkjijjiiihjjjjhgijklmnonmhhikkkjnnlhjjllkijkllmmmlppppmjmooonlnorsrrsssptwwxsnliomqopnlhfefhknrpqqqqqnrnnnoooqpnqruokioruuttsruqsqrtsssopppmmn^QSY_ekoppponllkmmpkjigfkopng^bhhihgffmonlkiihhefejlpspoqrttup]KJM_lonliigllllleddefggggijihkmmlljjjjhklljkihgiikeggghgccfdced…|{||zyyzxzvtwy}†‡‡‰ŒŒŒ’ŒŽ’“—“•“”‘–’•—™šš˜›™”•’”””•–”œ™š¡ž¢žœ¡›—––˜žžŸ¢¡¡¡§£¦¤£¢¨¡¢¤¬¤¥§ª¥§¡Ÿ ¦§¯ª¬®«ª«±¬¬¬«¬¬«ª§¦ª°®³³´²²±±¯³²´®®²³´²´µ·¸¹¸¹¶¶³³³¹¶¸µ·µ´®®®®³³³²´°±°²³µ²ºµ¶³¸¶´´´°±³·¶µµµ¶º»»¹¸¶µµµ¶¹¸¿¹º»¿½ÂÂÁ¼¿½Á¿ÂÂÂÀ¿¿Ä¼¾¾¾¿ÅÅËÇÅÅÅÃÅÅÄÅÈÇÇÈÏÍÏÎÎÏÓÑÖÕÕÏÏÈÃÀ¿¿ÆÁÃÂÇÁÁ¼¹¶¶¯®©©š‘”ŽŽŽ’Œ‹‹Œ‰‘‹Š†ˆ„…„„…‰……}y|‚„ŠŠŠ†…~|~„ƒ€…ƒ€€wuuysstursssszusrrljkokikqmklklojmjijllpmjikkkmrqrpmllnponnmmlkjknmllnnprrqqqprxommoornmorsvtutwssu||{urmlmnorpumjedhnooosssqpprnrnnproppsssswlgkvvzvwtssvrsrtsvuupqpppraUPT\dlrprnlonmnkmmqlkijjnpujb_diljhggioookjjjjlfggjkomlovvwwucRKMZpqtmllkhlmoljffefghgfhoklmqqqmnoolkhppokpighkjkfhhigfdhhijjf~}|zzzyxxwyxzwy|€„…‡‰‹ŒŒŽŽŽŽŽ‘‘’’“‘”“–™™™™˜—”“‘’’’••••›˜š››››››œš–˜˜™œžœŸ ¡¡¡¥£££¡££¨ ¢£¤ ¥©¦£¦¡£¥§©¯¨¬«¬ª¬®®¬®ª«ª«¨§¦§§«®°°±±±¯²±¯²¬¯¯²³±±²µ·¸¹¸¶¶´¶°²³µ´´±°«©¬¬³±³³³¯°±²²´²²²²®®¯±²´°³µµµµ¶¶·¶²¯«±±¶µ¶¸¹¶¸¸¼½»ºº¹¹¹¾¼¾½½»½º¿½¾º½¾¿ÀÅÆÆÆÃ¿ÁÂÃÁÅÆÅÄÈÄÑÌÏÌÌÊÍÎÑÎÍÈÈÿ»½¾ÂÂÁ¿À¼¾¼¹¶³¬ª¦£š‘“‹ŒŒŽŽ‘ŒŒ‹‹ŠŒˆ†……ƒ„‚††‚{x„†ˆ‰ˆ‰‡„€ƒ††……„……„‚|wwxxuvxwtuuuuzwvtsnnoooopppooonojjjkklloomjklmnqsqoonppppomnnnmmlnkmmopppqppopooopqqrqlorstsstuusvxvsrqoknqqqrpokhchmmmoptssqpoompoqsrmnmsqpnmhlpttvuwrssursttqqnnmnnmj`SRPW_lurpnkloopmkmmnkkgklnplebafmliffikkkkiigjkkgggijiflpsww|iWPL[inpmjiihilnqkhfffgehhhiiinrpoqkhfnnjhonkjjhhkhfffibcdeceeighe{||yzzzyxx{z{|€ƒŠˆŒŽŽ‘ŽŒ‹Œ‘““““”“–•š˜˜š™ž—•”“”ššš™™—›š›šž™™ššœœ˜——œ› œž §¡¡¡¢¡¡£§¢§¥©¤¥¦¦¦§¤££¥¦¬«µ©¯©¬¬¬ª±¯¯®¯°¯¬«§§§¨ª²°´³±±°±²°¯²±²²²²·¶¶¶·¶¹¹¹µµ´¸´¶¶¹µ¸«¨§§«°®®´²¶²²¯´µµ³´µµ±±±²¯³³´µ¼¶¸µ¹¸½¶¶¬¨¬³±·¶»»»»º»À»¹º¼»¿»½¼Á½¾¼Á¿¿¾¾¿ÄÂÂÀÅÅÆÃÂÁÃÂÇÃÇÃÂÄÈÄÒÐÐÌËÊÏÎØÌËÇÈÀ½ºÁ¾ÄÀÀÀÀÁÁ»»´²®¦¤œ˜“’‹ŒŽ’ŽŽŽ“ŽŒ…ƒ„†…ˆ‡Ž‚}{z‹‰ŠŠŠŠ†……‡‰ŠŠ‰ˆŠ||‚||~}{{{zzxzz~vuuvwxwvwxtutzopnmloooosqpqqpooqonnmnrrusspqqtqspoopptuuptqqqsquwxusrppqrutyxxwzz|wurrooprqpqrpoigiomqorq{sssvrrnuttstorrsqslimqswvyvvvvttuvuxqqopnmmocWNQT[hwvvpokmoonnnnlmkkkpormiefeijmgdfnmmkkiljkljgghmhghmnruyl`ROWnrtolkokpppprjihihljomnjkisrqqrhcdmoiiokihhfkihedek``ehdififleyyywz{zvxx{|~€„…†‡Š‹ŒŒŒŒ‹‹‹’“”•–—™™˜•˜™˜“••—š˜˜–•——™™ššš————–—–——œ›Ÿœ¡£¥¡¡¡¡ ¢£§ ¦¥¨¥¤¤¤££¡£¥¨©©©©©ª©¬¯¬«¯ª©§¦¥«¦§¦ª®¯°®«¬©¯¬®²°²²µ´µ¶¸³µ´´³³²´´¶·µ³©Ÿ©¥«°°¯®®¯¯®®¯®®´²±¯°°±¯°°´¶···²´³¸¶µ²³§«®²±µ´¼º¹·º¼»·¹¸»»»»½¹ºººº½¾¿»¾ÀÂÂýÁ¿¾¾¿ÀÃÃÃÂÃÀÃÄÄÅÉÇÈÊÊÉÉÈÆÂÂÂÀ»½º¾½½½½¾¿º¹¸¶±±ª§¢¡™–““‹Œ’’‘‘“‹‹ƒ„„………†€y{{‚‰ˆ‰‹ŠŠ‡ˆ………‰‰ˆˆ‰ˆˆ„„‚ƒ‚}€†„‚‚‚~{yw{{~wwvwzwuuvxtttsoooomnopqrrqpooppnlnknnrsvvrqrssqqqqsrrtssqqprrrruvvwrqqprrstzxywyzztuqqmptsqrrtomhlqqnnmrqtssssqpnrrsprppqsnmimvuuvuwuuvsptvvtspoooonpdXRMU[gssttpnkopnmmklklkmmoopiheffhildfgmonkjhjjklkgikgeghmloncWPJXgntrklmkjpooolhijjhijopnkjjssoliccdomkjnhhhgcjhhffe\Vbedbcdeffd{{zzzzyz}}~€††‹‹ŒŒŽŒŒŽ‹ŒŽ‘‘Ž•••—››š™—œ˜˜˜˜–›š™—•—›š›œœœ›—™—˜•——š™ž Ÿ¬¤¥¡ ¢ ¥£¨£¦¦©©ª¤£¤¤¤¦©®ªª©®ª¬¬¬¬¬¬®±ª§§¨©ª©©©®®¯®±¯®««¯¯°¯¯¯³±·µ¹´¶µ¹²¶³³²²²¶´·°®ª£¤ªª³³³¯±¬¯µ°¨¨®´¯®±°±°³°º¶¼·¶³¶·¹²±±±³µµµ²¹·¼º¾·»º»¸¹¹À¼¼»¾¸»ºÀº¼½¿¿ÃÁÁÂÃÀÿ¾¾À¿ÅÂÃÃÃÃÇÅÈÆÊÇÈÉËËÌÆÃÁÁÀÀ¼¾½Ä¾¿½½½¿º¸¶·°¯¬§¢žš–•”––œ’””˜’•”ŠŒ‡††‡‡‰€{}‚„‹Œ‹‹‡…„‡‹‹‹‹†…††…‹ƒ‚‚‡‰‹ŠŠ„‚~}|„|}}|{{zwuvxw{utqrqtpprzvtuyturypnlmoutuuwsqtyvtstsutyyxssqrruvwwwvvtrqrsvuxw|yzx}yyuurrsuuwv|sunlnttwrtprrttvvvqsrxtspwtystmllsvwwzuxtrrqqsvxtttuptrrh\SOR[fvvuttomnuqnntklknoppqnnijjlikkkjjjtopkiiklkjlilhfemjmlsdREALclvtolplkjqnumhfkimiijpnnonpspmidabgppnnnmnljikhiij^SUefecbcdeedxwxyzzz|~ƒ…‡Š‹ŽŽŽŒ‹‰‰Š‘Ž‘”•˜™™™™˜———•–––—˜–˜˜˜˜š™š——–—•™–•’–—™™ŸŸ Ÿžž Ÿ¢ ¡££¥¦¥¤¤¡££¦¦¨©©¦¨¨®ª«ª¬¬¬ª¬®«©§ª¬«©©©®¨¯¬¬«¬«ªª¬¬®¬±²¶µ¶´µ³³²°®®±²³³®¥ªª«¬¬¯®¯¬°©«®©¥£ž©®´¬«¬²±±¯±±±±±²²±°«©°¯´²²²¶µ»¹¾·····¹¹¹¹¹¸¾·¹¶¾º»»¿ÁÁÁÁ¿¾¾¾¾¾¼Á¾ÁÁÁ¿ÄÃÃÄÇÆÆÅÅÅÆÆÅÂÁÀ½»»º¿½¾¾½»¾½»·¸´³®ª¥¡œš—•”””˜˜›’““’ŽŽŒŠŠ‡ˆŠˆ†‚}‚‡‰Œ‹‹ŠŠ„‡†ˆŠ‹Š†ƒ…†‡‡Š„„…ˆŠˆˆ†ƒ~}{|z~||||xwuwxytsrrstqsuyyxwxttqqqokoqtttsrpsvwwvttuttwxussqstuvvvwtuqqqstvwxxxyzyyvvvuqtttuvvvqnjnsvwtrrqrruvvvuqrsvvsputsonjnpvywtttsssssrtuspqppoomh^TMTYeqqssqoknqsronnjijoqrrqnlfmnljjhkjklnnliijlihhjihfffjjlkh\C2DUgorslimmkjqmlhgfiijhijollimupmmge^dkljlnmlmlliheilgXSXfefgddefecvvwz{{‚ƒ„†‰Š“ŽŽŽŒŒ‹‘Ž–‘“ŽŽ’•™˜Ÿœ ˜›—–—˜——–˜™˜™ššœš™˜›–˜˜˜•••š™™œ ¢¡ Ÿ¢ž ¢Ÿ¡¡¦¤ª¨¨£¢¢¤¨¯ª©§¥¨¨®©«¯¯¯¯¯¬®®®ª°±²±¯¬±«¬«¬©ª«¬®²®³°¹³º¸·´¶±®¬ª¬®°µµ¶°¬±±²°²²³¯²¬°©«£¡£¢¤«²²²²³²µ®¬®²°¶µ´³±®«©¯µ´´³»²¶·¹¸¾·¸·¹¹¼º½¸º¸¾¹½¼½º¿»ÁÁÂÁÁ¾¾¾ÀÀÁÁÁÁÁÁÈÂÆÄÃÄÌÅÆÆÇÇÆÆÆÀ¿¼ºº½»À¾½½¼»¾¼»·¸³³®¯¯¯¦£Ÿ¡œ›˜˜š¢™›–—“‘ŽŠŒ‰‰…„†‹Œ‰‘Ž“‰Š‰Š‹‘Ž…ƒ„‹ŠŠˆ‹Š‹‹ŒŠ‰ŠŽ‡†~~‰€‚~~~‚y{uvwxxywwv{yzz|€{{vyyzuttuvwxwsqrvw|yyz||}x|xuuwv|ywwzxxuuuvwxyz|‚|}}}zzxwvuuzz{wzvwokjusyvsrsvzv|{|xxtvuyuttytunlntv~||vwv{stv|xwurrrpomnjdVNR[ftuytuomntqrrpnnnmmpqwuunkioooknjklrlpmkhjlmhhimheeiinlmkjL55I]knsnkkrommqkigffhjkghjnlllnnlkkgfffknjkkklolsjgfijcUS\ghhgfeihjfvvy|}~~‚„‡ˆŠŒŽŽŽŽŽŽ‹‹‘‘Ž‘’”—˜—˜˜—–•”•”—™—–••™™šœœš™˜—••”•–—”––™›œžŸž›ŸŸœžžŸŸŸ ¢ Ÿ¢£¨ª¬ª«¦¥¥¨©ª¨ª¨¨©¬«ªª¬©©©ªª°¬ª¬ªª¨«¬®±®°°³³µ±°°°¬«¨«¬¯±±¯°®¯¯°°¯®¬®®±«ª¤ š¢¦ª®®±®²²±°¯««ª°°ª§¦¥£©³±´³¹±µ¸¹µ¸··¶¹º¹··¸¹¸¸·¸¹»»»»¼º»¼¼½¾¾¿½À¿À½ÃÁÁÀÀ¾ÁÄÃÂÅÅÿ¾¾¾¾¿·¸·º¸¸¸º¸¸¸¹·¼µµ±²®ª©¥¤Ÿžœ™šš™™˜––‘’ŒŒ‹‰‹Œ‡‡…Š‹‹‰‹‹‘ˆˆ‰‘І„…†„ЇŒŒ‰‡†‡‡†‚€|}|€ƒƒ€€€}yzwwwvvxwwx{{|}}{|zxvxvvvuuvwy|xtttvw|z{||}{wwwwwyy{{yxyyxvvvwyyz|}}}}{xwxxvwxz{|utrpkkkvttutsvwxv{xxxxtvuvtuwuqqlotwyz{{uuuvuwxzzxwtrsoojiiXNUZesrtusokortqrqqmnknpqrttrkkkosnkmjlmqlmklilmlfiihcbakmmlmkR97;Tjlnmjmlmnnnmhfeffijhehjkkjjkklmlhffhjljlfjjlkjhgfhi`T\geaghfdhhiixy|„‚‚‚†ˆŒ‹ŒŽŽŽŽŒŽ•‘‘’”“˜™˜—™™—––”““™”›˜˜–—•š™œ›™ž–””””••——›š™›Ÿž¢œš›Ÿ¡œŸžŸž ¥ž ¡¡£©©°¯¯ªªªª§¨©¬¬¬¬¨±«ª«³¬¬©¯ª¬ª¯µ¬«¬«ª¬±¯®¯±±µ´³³¸±³®¬«««²¯³±³²³®²³´°¯¬«¯®³§¥¢Ÿ£®®¸±¯°±²·µº¯²®°°¥ ¤§§¨¨¯±²³³´µ¹´µ¸¹·»»½¶¿¸¶·¹º»º¹¸¸º¾½Á»¾¹º»½½ÂÀļÀ¿ÀÀÄÃÂÀÀ¾ÀÁÂÂÊý»¼¿¼½·»·À¿¿»À¸º¸º·¼¼¼µ¸®±««¦§ £¢¢¡¢›™™™–•’Œ‹Š‰‰’‹ŒŒ‹Ž˜‘Ž“‹Œ‰ˆ•“”’І††…‰‡‹ŠŠ…†††ƒ€€~‚ƒ„‡‰„‚~}}~}{{|||~ƒ‚ƒ‚{wxyxyyyy€‚|}xwxy{}{~€|zxwxz}}}}{zz||wyxzy}|‚ƒ~‚|zyx{z{}{}trrrmnqxxxvvv}||y|yyyztwvxwxvurqqvxƒ{|z|wz{}|||zwvrspnjk\QS\eqtzuupnnsruutsrmnnosxrtrpoqoonmmnlopqpqkllpmlmojfcaemmlmp\E27Jgnsnlknmloqnmfefjjnjhgikqkkjkjlllgghmknlkjiimjhgjhmh`_jgeenhddghkk|~‚‚„…‡‰‹‹‹Œ‘Ž‘‘’“’“•••””’‘‘’““’““–•˜––”š˜œššš™“‘‘“”–“–—š›››œ›œ™›œœœœ›žžžž Ÿ¤§«¬°°±««¨©¦¦§¨¦¨©©©ª§«®¬©«¦©ªªª¬®««¬¬¬®¯¯°°°°±¯±°±±¯¬¬«¬¬¬¯¯²²²¬¬°°®«¬«®®ª¡££§¯®¯²²²²´´´´³®®ž–Ž¡¡¨¨ª«±²´´´´µ³¶¸·µ»º¸µ´³¶¶¶´¶µº¹º»¼¼º·º¹¹¹»º½½½¼À¿ÀÀľ¾¾¾¼¿¿ÂÂÁ½¼¹¹¹»º¼·····¸¸¸¸¸¸¸·¼··´´¯®¬«¦£ ¡¡¡ ›™•—••Œ‹Š‰‰‰‹‡Š‹‘˜”Œ‹ˆ‹ŠŒŽ•–“ŽŠ‰‡†…ˆ‡‹‹Š‹‰‰ˆ‡…„‚‚ƒ‡ˆ‡†„ƒ€€€~|‚„………ƒ{{{{{||||~~}|z{{|}{z}zzz{||}}}}}}{}}{xxwz{~~€~~~zzz{yzz{y|zwrqmqqrsvvwwvv{||yyxxuuuwvvvxttrsuy|~€|{zxwz{|xywxwvopqpk[PV]dmsuurtmpqrssspmmmnorturpnqlqoolmmnlmjjklkmnokllljdZejmnno`M;0Gbjqolkjiimoolf_ehiiihiikkljihiikjldfhlkmmlkkjhghhhglgghd`ffggddhike€„††‹‹ŠŠŒŒŒŒŽŽ–‘’’‘–”˜˜—–™”˜”’‘‘”˜”””˜—˜•œ››™››œš™“’‘—–—•œšžž œœžœž›š›£œž¢¤¤¬©²¯´°²««ªª«¬¦©§ª©¨©«¬¬««¨¨©¯¬¬¯¯°®°²µ±´°·³¶°±°µ²¶¯®®¯±®®¬¯¯¸³²±±°¯®°±¬§§¨ª°±³³²²µ´·´´´µ¯’ˆƒ‚“¤©´°°°°±³¶¶¶µ······¾¸¶´³´½µ·¶º¸»ºÁ»º¹·¸¿»¼¼¿¾¾¾¾¼À¿¿ÀÄ¿Á½¾¾¿ÀÅ¿¿½»¸¹¸»»½º¼¼»¸º··¹½¹»¹¼·ºµµ®®¬¤££¤¢¨£¦šœœ“”ŽŒŠŠŒŽŽ’™–“”Ž“Šˆ‡‹Ž‘Ž•““‹ˆ‰…ˆ‰‹‹‹Ž‰‰ˆ…„„Š„……‡„‹‹Šˆˆ‡…‚„ƒˆ†…‚‚‚‡Ž‡Œ…‡€€…ƒƒ‡Œ‡„ƒƒ~‚Š€~‚||}~~€€}}}€‚ƒ„{}|zzzz~~„~~}{{z}|}||}xtrqqru{zyyxyyy{||y~zz{|{|||wytttwy‚‚ƒ‡zxy}||yyy}vuoprv`QS]blr{vwstryv}wxrpmnotsxvxpnnrrsomlnnqpqmmlnowonmpljfcdpooothX@2>[htpnnoihiqqqlbafgkhjhokpllihhijojkddflkpornmigfihjjllkjcbgghfbegilh‚‚…‡ˆ‰ŠŠ‹ŒŒŒŽ‘ŽŽŽŽ’’‘‘’’““•––––”“‘’‘‘“•—•”“—”••š™š˜š™˜–””“‘”“••œšžžžžœœšš››œŸŸŸž£«§¦«©ª«¯±®«©§§¦¦£©¦ª¨©©©©ª¨¬«©©¨¨©¦¬©¯¬®¯²·³±±°°°°°°¯°°±®¬ª««®¦®¯¯°°¯®¯°®«¯®«±²±°°²²²²±±±¯ª§¡‹yƒ’¢©®¯¯¯°±±³µµ¶¶µ´´´µ¸³·¶¶³´µµµ´µ¹¸»º»»»¶¸¸¹º¼½½½½º¾½À¿¿½À½½º¾¾¿Á¾¹¹µ·¶¶¶¼¼»¹»ººµµµ¸º¹¶·¸·´¶µµª¦¦¡¡œŸŸŸŸž™™—–“’ŒŽ”˜˜”‘‘Ž‹‡‰ˆŠŒŽŽŽŽŒˆˆƒ…†‰Š‹‹‹Š‹‰Š††………„ƒ„‡„††ŠŠ‰ˆ„€‚ƒ‡ˆ„ƒ‚‚†‰‰†…‚€ƒ†…Š–˜™Œ„€}†…€|~‚€ƒ‚}~‚‰…‚ƒ|{{{{|}~~ƒ€~|}||{||~€|y}vsmqrvy{{zy{|{{}~|z}|{zzy{zyxxtuuy{~€‚ƒ}wyz}}|zyz|uuopscTU[agpvvvxrsttuvuwqqmpquvwsojmopnnlmnnnpppmmmoqqnnmmkfdimnoopj`D2@Ucmplheiijkqvrkccfeeeghgghhgffgjkifi`dehilmoqmigghijkllkiechihfgkiiii‰‹ŽŽŽŽŽ‘•‘•“”’’•“”“––˜––’“—•••›–™•––—–š™ššžžž˜—•“““”˜’š˜š¡žžžŸœœžœžœ¨ Ÿ¡¦§©¨¨§«««ª¯¬«ª¨¨©©¨¨§ª©®«°¨ª©¬«¨¨©¨ª©°¯°µ´²±±±±°³²²¯¯°³¬«ªª®°®±°µ°±°°¯®®®¯²²²±¸³·±´°²²³²²±±³³–‹‚u}„‘´³²±µ´µ±¸µµµº¶¹µ½·¸µ·¶·µ¸¶¶·ººº¸¾½½¼½¹º¹ºº¾¿Á¾¾¼¿½ÅÀÁÀ޽¾Ä¾¾½½¸¸¶¶¶µ·¾»ºº½¸¸¹ºº¹º»·Á¹º¶¸´µª¨¨©§¦¡¢¡¤›››——˜™••“““š™š—š“‘‘ŒŒŒŒŽŽŽŽ‘ŒŽŽˆŠ††ˆŽŠŒ‹ŠŠ…‡……ƒ‚‚ƒ‡‡ˆ†‹‹‹…„„…†‹…„ƒŠˆˆˆ„ƒ‚ˆ‡†‹œ—§—“„€†…†€‚€ƒ‚‚ˆ…†‚‚‚‚†‰ˆƒ‚€€€…†…ƒƒˆ€}}~€€~‚}}urstx~~{{{€€€€€~}}}}‚|}{ywvvy{€}ƒ…~z{}}}~€|{|utstfZWY_iozvxwywvv{vvuwqrqtu~}}rkkqqtnnnrnnntqqpqrqqroomlfbhtttuvohJ49P`orwkffijmmqrsjdcffffhgffigcdghmjifhfeekimmnpsggilhomrlmihinmnnmlmlppŠ‹‹ŒŒŒ‹‹Ž‘‘’’‘“”“““”–˜’‘Ž““”••“•”–•–––—šš—•”“““’”•—“™˜š›œœœœš›™œœžœž¡¤¨§§§¦¥¦¥ª¦©©ªª§¤¨£¨¥§§§§©¨¨¨¨¨««¨§§§ªª®°®¯°±±³¯°¯¯³³²®°±²¬¬«ª¬¯®°¯°¬¬¬®ª¯¯±²³´²°°³¯³¯¯««©¦}yr‚˜¥«¯²±±±´´µ±±±±²µµ¶´´³²³·¶¶³¶µ¸¸¹¹¹·¸¸¹¹½¹¹¸¸¸¹·º¼¼»½½À½Á¿Á¾¾¿Á½»µµ´µ´¶²µ·½»¹·¶µ¸·¸··¶¸·¶µµ´²¯¬§ª©¨¦¦£¢¢¥››››•–—˜—–•“’”š––—“ޑދЋŒˆˆ‰Š‹‹ŠŒ‹‹Š‰†…„‡……„ƒ€ƒƒ………ƒƒ„ƒ‚„„…†‰‚……‹‰ˆ„ƒ‚‚‚ƒƒ€…Š’••”„„„…ƒƒ€€ƒ…††††‚€€‚„‡‰…ƒ†‡‚~€…ˆ†‚„‡€€€€€}|{wrtu{€}y{||}€~}~~}||{zywvw}€|~~|{z||}{}|{yyy{uvwiYXX^fpvwvwwxywvxwvuvprsvx|}{nmmqrsnooonnnumnlnprqqoolf`gmrrssvmN4=I]nqtoihglmnornkffdeca`dhff]TVXfiihieedccgionnmjehjihkmmlljjkljlmnnnlno‘ŒŒ‹Œ’Ž”˜‘‘’‘’’‘”˜“—““”™‘‘Ž‘””™•––—•š—™™››š•“’”“’““–™–˜–š—››žœŸ›šš›œ œžœ¢ª©°©«¨®¥§¤ª§©©©§§§¨¥¨¨©©ª««§©©©¨«ª¨©¯±®³³²³´³³¯±¯´³³³¶±±²¬¯¯³³²²²®°«¯¬®°°´³µ²°±²²³±³¯±¬š•rww„œŸª«±¯µ²²²´´´´´³³³·····´´µ¸¹º³½¶»¹¹¸¼··¸ºº½¹»¼½»º¹¹»½»À¿Ä½Á¿Ã¿¿ÀÀ¼ºµµ´··¸µµ¸½¸¸µ´¶··¹···º¶´´¶±¯¬¬¯±°¯§¦¦¦¥§Ÿ¤››˜™˜ š™™˜—–•œ–’Ž‘‘˜”•“˜–ŒŠ‹ŒŽŽ““Š‹’Š‹†…„ˆ††…†‡‰…Š…‡‚„ƒƒ‚‰ˆ‡†ˆˆŠˆŠŠƒ‚‚‚ƒ†‚„€…‡Ž•ŒŒ„ˆˆ‰ˆˆ‡††‡‰ŽŠŽ…‚‚‚…„‡‡††‡……„„†‡‰‡†‡‡€…†ˆ‡‡†Š}}xvwy{ˆ‚}}~~ƒƒƒ~|}ƒ‚†}|zyxy|…€‚€}}†ƒ}{yz{{w{obUZ\dn{|{z{y{xwwzxzvvruw|}}zyooprtsrrtuolptmpnnpwqrrsideprwvxwx\>8GZqtwsommmsrrpsmhc_ae`\_bdcfUQNYdikgjefeddfipooliijjihlmnmokllllnmwvvprnŽŒŒŒŽŽŽ‘‘’’“––”“’’‘Ž’”’’“–––”––˜—–“’’‘’’“”–™™——•––›™œš›šš˜›šœŸžœž¢§¨©ª¦§¥¥¦¦¥©¦¥¢¤¤¥¢¢£¨©©¨©©ª§§§¨¨ª¦ª¬®°¯¬®¯®¬®®¯®®¯´±±°®®®®««ªª©¬®¯«²®¬¬¬¯¯°¯³³²±°°³´³¯®ª§¦Œ{qdu€Ž¡£¦¨ª¬³²²¯¶³³²´´³±³²³°¶µµµ´³³³¶¶»½ºµµ¶·¸ººº·¶·¹¶»»»»»º½¿¿½Â¿À¾¼º¹¹¸¶³±²¯¸µ·¸·´¸³µ¸¸·¶µ··º´µ³²««¯®«®¦§£¦£¢œ›š˜˜™™™›œ™––––”’Œ””•’Ž‹ŽŒ‹ŠŒ‰Š‹ŒŠŒŒ‹ŠŠ‡†…†††‡‡‡ˆ„…„„ƒ…†„‚…„††ˆ‡‡‡‡‡†ƒ€‚ƒ…~~~„‡ˆˆˆŠˆ‡…††‰‰ˆ‡††‰‹Š‰…€‚‚€…„…†‡‡‰†…ƒ„„‡ˆˆ„ƒ€€…„„„‡‡Š€|ytwxz|€€~~{}~ƒƒ…}{{}}zyzƒ€ƒ}|}€ƒ‚€}}}{yyuwuncZT\amwy{zz{wyxyzzwxuwrvxyz}vsnruvutrtvtqtwploknpwqrshcdhpstuxzqRC>WoqtvrpnpquwrpnhaX_^a``adfec^Y]`gjkggeeeeefiomokjjigiillmnolmoooolqsvool”’ŽŽŽ•Ž’‘’Ž‘““—˜˜••”•“’””••™—˜–›––˜‘”‘’“•””–œ›š˜–—™›˜œšœ›š™¡ ŸŸž ¤¥§§¨¨¬¦§§§¦«§©¥¤¥¤¤¥¤¤§®©ª¬¬¨©§ª®©ª¬³²´®®¬´¬¬®°°³²²±´±´®¯°°¯¯©«ªª©±°®¯«´®¬¬¬®¶±±°³²°²µ¶¶¶·¯° ž}jf_q‰”¨©®¯°³²²²¶¶·µ¸µ»´¸±´³µ°µ´¶´´³¸¸¹¸¼º»µ·¶½¸ºº»··¶»¹¾¼¿»»º¼¾¿½Â¿Ä½º¸¸·µ³±²µ²·´»¹·µ¹´···¶µµ··¹´¶²²®¬¯®®®®§©©¨¢£œœŸ¡š›› ›››œ––“˜””Œ–––––’Ž‹“ŒŠŠŒ‹Œ“ŽŒŒŽŽ‰Œ†…†‡‰‰‰…‡„…†‹…„„ˆ‰Œˆ‹ˆˆ‡‡…ƒ‚„„…€€ƒ…‡Š‡ˆ‡‡‡ˆŒŒŠ‰‰‰‹‰‡‚‚ƒ…‡†………†‰‡Œ††ƒˆ„ˆˆŽƒ„~†………Œ‹‹‚„{xxˆ‚ƒ‡ˆˆˆ‰ˆˆ~~‚ƒ„„}‚~~|‚‚€ˆƒ€€€ƒ|}~…„„}€zxvyof\VYbnz|~~{|z€zzz{wuxy{z{z}vttwv{uttzvuwvolnnopvstj^_gmvtzy€uiPEOnsvvvvxsuv|xsomf_^`_dfjjiijjjjkksmlhghiffggipoqlnkmgnjoooopmnoroppqrxpplŽ’”””“’’‘’Ž’•————™˜™——–”‘Ž“‘”–•”•–˜—š˜™–˜™™˜˜˜›˜ššž ¢¤¤¥¥¥¤¤£££¢¤£¥¥¥¤£¡££¥¦¦§§¦§¦§¦§¦ª©«ª¬±°®«¬¬ª®¯°°°°¯±±²§¨©§££¥¬©ª¨±¬®¯¬¬ª§®¯¯°°±±°µ´³²°©”…p[acy’™¡§«¬®®²±±²µµµµ¸µµ´´²³²±°µ³³´´³··¸¸·¶µµ¶µ¸¸·¶µµ·¶º¸º»ºººº¼·¹¹º»¼»¸´µ²²°°±²²´³µ²²²²²³³·¶¶´¶µ¶³²±±®®®¬ªª§¦¤¦¦¦Ÿž›žŸŸŸ¡›œ ››™—“–“•”’‹Ž‘–“”‘“’‘ŽŒŒ‹Š‡ŒŽŒ“ŽŽ‹‹ŠŠˆˆ‡‰ˆŒ„†ˆ‰Šˆ†„„†……‡‡„„„ˆ‰Š††‡‰ˆŠ‰…„ƒƒ„ƒƒ€ƒ†‡‡‡ˆ…ˆˆˆˆ‹ŒŒ‹‹ˆ†‡Š‰Œˆ……ƒ…†ˆ‡‡‡…‚†…ˆˆˆ‚ƒ}ƒ„„…………Š}x{|„ˆ‡„„„ˆ‰ˆ„……„‚ƒƒ€€ƒ}~}~ƒ€€‚{~€„„ƒ€€zyy{{yurh]U\am}}~~|{z{{}zxwxwwuyyyyzz{xvtututuvzwv|unkoqrrtrqf`]ksttyywndQVbotuvwwwtuvwwsolba```dkhfhhifilmnpnmfecghhhhgijkhjihffgjjllljmnmmnopqsqqlŽ“’“”ŒŒŒŽ’‘‘‘””›““‘—‘’‘‘‘““”””–œœ›˜™™œ˜™“”’———•››››œ››˜œ™™”“–œœ¢žŸ¥¥ª«ª§§¤©¥§¤¤¢§£¥¤¤¢¢£¤£«ª¨¨¨©©¨¦©§¯«®¬´°µ¯®¬±¯¯²°¯°¶°¯¯²±°¦¦¦¥¤¦§«©¯¬²µ¯®¬©¨©¯¯´±°°º²²²²³¶±©‡q`Q^j|›Ÿ¨¨®³¯²²²°¯³º¶¸¸¸¸·´³²µ²±°µ´¸´¹·»º»¶¶µ·¶··¹¹¹µµµ¸·»¸¸¸»º½¼½¶¶¶·¹¼º¸´¶±±°³³³²¶¶·²µ³¶³´³¸¶º´¼µ´³¯±±±¶®¬««¨§¦¦¦¦Ÿž›£ Ÿ ¢¢¤Ÿ œž˜•”–”•“•”—“———”•‘‘ŒŒ‰ŠŒ’“ŽŽ‹ŒŠ‰ˆ‹‹Šˆ‡†‰ˆ†ƒ‚ƒ‡‡††‡„…†Œ‰Š‡‡‡ˆˆŒ‡††‡……‡†…††‡‹ˆ‡ˆˆŒ‰ŒŒ“‘‹Š‰ˆˆŒŒ‹‹Œ…††‰‡‹‡‰ˆ„‰ˆŒ‰‡ƒ‚‚‰ˆˆ‡Œ…‡†Š€|}‚„މ‹ŠŒ‹Œ‡ˆƒ„…‰†ˆ„€‚ƒ‚‚€€‚‰‚€………‚€‚‡…†€}{z}z{uqaXZemwƒ~{zy}~~ywvxvvx~}}|z~yzvutwv|x|xx}umkssvuwqpcbhyzzx~yxj`^cktuxvzyzvywwxmjafa``ffffjjihipzovoogdekhhiogghjjkfddfflloklkrmlllntrwwxn’”މ‹Œ‹’’”““’’“‘’““““”•–”——™˜–“‘ŽŽ‘’’’—•–•–””•——˜——•”‘“• ŸžŸ¥¦¨¥¨§§¤¤¢¡¡¡ £¢£¢¤¡¢¢££££¥¥¨¤¦§§¦¨§¯«¬¯®¯¬ª®®®¯±¯«®¬®®ª©£¢¡¥¥§¨©ªª«¬«§¦¤¤¤¨ª®¯±²²¯®®¬œ“zc[McwŠž¢¦ª«®®®±±²°²´´´³³³°²±³°³²±°µµµµ·¶¸¶¶²µµ····¶µ¹µµ´´µ¸··³¶¸¹¸·±³³µµµ±±°°®°³´³¶±±¯¯¯´²²²´´´´´´³²²²²±¯««¨ª©¥¢£¡ œ¡£ Ÿ¡¡ ž ˜›”””•“’’‘‘““““‘••”’‹Œ‰Š‰Š‹Œ‹Š‰‰‰ˆˆŠ†‡ˆŠ‡ˆ‰ˆ‡…‚~‚…„ƒ††Š†‰‡‡‡ˆ…‡„††††‡ˆˆ‡††ˆˆ‰‰ˆ„‡‡‡ˆŠ‰‰‡‡‡‡ˆ‹‰ŠŒŽ‰Š‚……‡‡‰‰‰‰ƒ‰ˆŒ‹ˆ‚††‡‡……„…†„}~€†‰ŠˆŠ†‹ŽŒ‡‡€…ˆˆ†…„ƒ„……ƒƒ……ƒ‚‚‚‚„ƒ„ˆƒ„‚~|y{y{qaU\dkwz€ƒ~~z{z{zyvvuxswy||~|x}yzvvtvwyvvv|…‚vnkstusqka[ivz{zz}xpfddjptuvwyy{uwxvqnjg`a`_`edfchgfelsrmkhkhgfhhjkiefiijkdb^ggllljkhiimmmlrqrrsp’’–’ŒŒ‹Ž’’–•–’““”““‘–‘“’•”˜““”•”•–•‘ŽŽ’•–—”——š–˜˜™•˜—›——•””™˜žŸ¤£§¬«ª©ª£¥¡¢ ¡£¤¥§¢¤¢£¢«£¦£¦¥¨¥¥§©©«°¬µ±¯±¬³³³°´´³®®¬¯¯°°¬¨§¤£¤©¦ªªª«««¨¤¡Ÿ¢£¨±´´µ³¶±µ®®Ž~iV[Vgƒ”«««®³²´¯¯°³³´¶·¶¹´µ²³²´³³²·²³³¹µ·µ½¸»¶·µº¶·¸¹¶¶¶º¶¸µµ¶º·····»·¸³µµµ´³´´°°®¯¯¯²¶´¶°±¯´´´³¶µµ¶·µ¸·ºµ³´¸°«®®®§¤¤¦ ¡ŸŸ ¡¡ §¢¢¡¡˜š•š“–”–“•“—–”““š•”‹ŒŽŽŒ‹Š‰ŠˆŠ†ˆ‰ŽŠ‰‡‰„‡‚ƒ†Š‰‰ŠŒˆ‹ˆ‰†ˆ…‰ˆˆ†Š‰‹ˆ‡ˆŒŠŠŠ‹…‡‡‡ˆŒ‰‰ˆŒ†‡‡‹‹’‘‘‰‰ˆˆˆŠŠŠŠ‰Šˆ‘ŠŠƒ…Œ‹‹…„ƒƒ„„€†…Ž‰ŠˆŠŒŽ’ˆ‡„ŠˆŒ‡…‚„…‰‰Œ…ˆ†Ž‡‡„……‹„ƒ„‹Š‰…ˆ„‡ƒ‚€~~|zgWX`ipy|€ƒ~€€||{z|~xyzz{ƒ„}{|yywzyyyzyu†ŒuomstxrraXbuz{|{~umgimstxvyy}z{x‚xulhgf[Y[]_effblfffrqoheenljimmnjgeefghkfbbiikllklhggmlqppqrrsp’ŽŒŽ‹‹ŒŽ’“”’‘‘•‘’’’’’’“‘”’••’ŽŒ”••–””–••”•••“—––––•••™˜ž› ¡£§§¦¦¦¥¦¢¢ŸŸŸ¢¦§§¥¢¢¡¡¡¢¢¢£££¨¦§©©©««¬¬««¬®®®®°¯¨«««¬¬¬ª©¦¦¦§§¤£¢¡¡¡ žœš™˜ž ¦ª´²³²°¯¯¬—}h\LYauŽšª¬¯°±°°¯±±³´··¶¶¸²²°²±²±²²²²³³³²µµµµ´´µ¶·´µ´µ³·µ¶´³³¶¶¶´¶·µ³¶··³°®¬®µ°±¬°°¶²²®¯¯³²³³µ´µ±¶´¶µµ´´´·¯ª®¬¤¤¡¡šŸ¡¢¢¡žž¡›™”“’’””•“”’”•—”“”•‘މŒŒŒŒŒŒŒŠŠŠˆˆ†Š„ˆŠŠŠ‹‹ŠŠ‰……€€‚‡ŠŠŠ‰‰‰‡†††‡†‚‚ƒ†‡‰‰‰†‰ŠŠˆˆˆ‹‚†††‡ˆˆˆˆˆ…‡‡‹Œ‹ŠŠˆˆ†‡ˆ‹Œ‘ŒˆŠˆ‰‡Š‰‰ƒƒ„‹‹…‚€}}„†Šˆ‰ˆŒŽŒ‹’‰‡…‰ˆ‡‡…‚†ˆˆˆˆ†‰ˆŠˆ‡„‡‡ˆ………‡ˆˆ…„…‚‚|~~€‚u__`hox|~€}~~}{zz}zyx{|€€{zyz{xvyyzzz{xv„‡€trpqrqmbVcrx|{|{wpkgntrsuuyyyyywwvshc_[SZZ^`_]`acefgijhcccefijkkkhgfgfhihggfkkkkkkkhgbkknnnlsqso“““ŽŽ‹”“———’’‘‘’”’•”–””’’’’‘”“•’ŽŽ•–•””””™”––––œ–˜•––šš›•™šŸ §¦ª«ª§ª¨©¦§¢£¡¤¥¥¥¬¥£¢¢£¤¢¥¤ª¦¬¦©©¬¬°®²¬°««¬±¬¬«¯¯·¯±¬ª²¬®«©¨¦¦§§¢Ÿ ¡¡¡œ–“‘•šž¤¬¬¯°´²º²¯±³•†kWRIWg{œ¢±«³²·±µ±²±´´·´¾·µ¶·²´´´´µ²²´·¶º¶¶µµ¶·µ¸´µµ½µ·µ·³¸µ·´¶µ¹¶·µ¶µ¶³·¶¶¯®««²°·°°°±°µ²¶²µ¯³°·´µµ¶µ¶µ¶´··¶µ³´¶¯±®¦¨¢ ŸžŸ¥£¦¡¡ £Ÿ ™˜“’“‘˜–˜”—––—•˜˜˜’Ž”‹Š‹Š‰‰‹ŠŠ‡Œ‹‘’ŽŽŒ…………‚„‰‘‹Œ‡ˆ………†…„„‹‹‹‹Œ‰ŠŒŽŽˆŠ„…†ŒŒ“ŒˆŽ‡ŠŠ”Œ‰Š‹Œ††ˆŒ’Œ‹‰Šˆ‡‹‰ˆ††‡Œƒ€…}|~ƒ‹Š‘ŒŒŽŽ“ŒŽŒ‘ˆŒ‰‰ˆ‡ˆ‰‰ŠŒŽˆ‰ŠŠ‹Š‹‹Œˆˆˆ‰†ˆˆˆ‡„„…‚ƒ‚‚€„|qkgjsz†‚‚€ƒƒƒ€‚{}|}~€€…€{zzzxw}{{z{yxv‰‚}twvzqnf]asx~€€xtmjjsrrtvuyyy{€xxurf_[WWZ\`__]^`bbghjgdbcchfijijlgjjjhljjgiiqppmpmmihcjlmnnovrtn“ˆ‹ŒŒŒ’“’Ž‘’“’’’•“’’““‘“’’‘“–—˜””“•”“’—–———–•“–˜™•––™› ££¤¤¤¤¤¥¦§¢£Ÿ¢¡¤¤¥¤¤¤¢ ¡ŸŸ ££¥¥¥¥¨©¬¬¯¬«¨¨¨¨©¬«««¯¯®«±«¬ªªª«§¥¤¤£££¤¤ œœœ™•†…„†‰–ž¤¨¯±²²²³¬©¨Šq^HPQdt‚” §©«°²²²²°°¯´µµ´·¸µ²±²²²´³µ°´¶····´³³±´³¸²µ±µ²¶´µ³µ´µ´¶µµµµ³´²´²±¯¨¨©®®°®±¯±²°°°±²°±¯°°·´µ³³²µ´´³±®¯¯´µµ°°®®¨§¦¥¢¡ ¢£¤£¤ Ÿž–•“’‘‘“”””—––˜–ŽŽ‹Ž’Œ’‘‘ŠŠŠŽŽŠŠ‰‰…‡‡‰ˆŒ‹Œ‹‡†ƒ…ˆ†‚‡ŒŒ‹‡ƒƒƒ…„„ƒ„…‡‡Š‰‹†ˆˆŠ‰‰‰ˆ‰„†‡ŽŽ‹ˆˆ†‰Œ‘’Œ‡„‰‡††‡ˆ‰ŠŒˆˆˆˆˆ‡ƒƒ…‡‡‡Š‚z‚}|{}~ƒ†ŠŠŽŽŽŽŠŠ‰ˆˆŠ‰Šˆˆˆˆ‡†„‡†ŠŒŒŽŠŒ†ˆ‡‰‹‰†ˆˆ‰‰‡„ƒƒƒ‚‚wsnpt{„…ƒƒ€‚„„€€|||~€€€~xzz{yxw{{zy{xwwz{wrwwyqocinw}~~~ttjmoqqtuuuwv{|€xwtoc^WZ^^^_``\^^_^cfgeeabbdefegjifghhhiihghipnmklmnia_jnmmoooopm“ŽŽŒŠ‹Ž‘”‘’’“‘‘’‘•“’“““—–—‘‘”‘’•—›˜š——˜–“’”˜˜˜™••—œ˜™–˜˜ž¢ ¢¤£¤¤£¤§£ª¢¢¢¥¤§£¥¢ ¡¦¨¨©©§¨¨©¨®¯®®««§¨§®¨«¬®¬´¬«¬±¯¯°ªª¦££¤¤¤£¢ Ÿ›—‘Šƒ|xwƒ’¢¦®®µ¶¸²±³¸§¦~eVFOZk‡ž¤®¬±®¯°¼±´°°³»¸½µ·µ³²´²·´¸´µ¶¶µ¸·ºµ³²±³µ´¹²´´»µ¶µ¸¶¶´¸¶º¶¶µ´´¶´º±®«¬°°º®°®µ±²±¯¯¯¯´´´´´³·´¸²²²·³´±°¯³²µµµ°µ¯¦¥¥¥¢££¥¦ª¤¤¢£Ÿ¢žœœž“’•”“’’“—˜˜–•‘’ޓޒ’—’’‹ŽŠŽŠ‹ˆ‰ˆˆŽŒ‘ŽŽŠ‹††……‰‹‹‹‹’ˆ…„‚„†„…ƒ‡†‹‹‹ŠŠ‡ˆŠ‹‹‹‹ˆˆ‰ŠŽ‹‰ˆ‡ˆŽ™‹ˆ…‡‡‡ˆ‹‹Š‹ŽŽŠ‰ˆ‚……ˆˆ„~}ƒ}}}~‚‹Š‘’•ŽŽŽŠŒ‰‰‹Š‹ˆ†‡Š‰Ž‘•Ž‰Š‡Šˆˆˆ‹‰Š‘‰Œ„ˆƒ‡ƒvwsz}…†Š„Šƒ‚‚„„…{~€ƒ‡‡ˆƒ~}{}{|xx{‚{zz{wvw{phq|vytprwy€„€€~tssvwwuzz{x{}ƒz{qmb^\_cgghb``a`cbbekfeabccchfdfigdefgkhhhlipmmlkmodY]kmnnqotpqk‹ŠŠ‹ŠŠŽ‘’‘‘ŽŽ’‘‘‘‘’’’’’•—–“‘ŽŽŽŽ’’•——–—–””““”•˜˜—••–™š™˜š•˜šž¡œ œ¥¢¢¢¢¢¢¢¤¢££¢ ¢¢¡¢¦¦¨©©¨¨¦©©«¬«©§¦¦¤£¦§©¨¨§¦§ª¬®¬¯¨©¤£Ÿ›š˜–—‡xsnmmzƒ’¢§©®±´´´±®¯ŽsXTJ[iz“š£ª°¯¯¯¯¯°°²¶¶¶¶±³³³²´°µ³´´µµ·²´´²²²±²´µ²²²´µ¸¶µ³¸¶¶´·¶¹¶¶µ³³³´°¬¨¬®¯¯°®¯®®¬¯ª¬¬¯¯²±±²·´´±±±²²±¯±±±²µ³²§ ¢¡£¡¤¤¥¦¤¡ ŸŸŸž›™—–’’”–”‘’“”“”“ŠŽŽŽŽ‘‘ŽŠŽ‹‹‰ˆ‡ˆˆ‹ŠŠ‰ŠŒŽˆ†„†‡ˆŒŠ‰‰Šˆ‡†‚~‚…„„„ƒ„†‰‰‰‡Š‡ŠŠŠˆ‰‰‹‡‰Š‰‰ˆ„……ˆ‰ŒŠŠ„‡ˆˆˆŠ‹‡„‡ˆ‰Š‰‡‡†‡‚…†…„†€€~z|~ƒ†‹‹ŽŒŒ‰ˆˆŒˆˆˆ‹ŒŒŒŒ‰‰ˆ‹Œ“‘ŽŠŠ‡‰ˆˆ‡‹ŠŠŠŒˆ…€€€…„}ttu}‚ƒƒ„ƒƒ‚ƒ„…€{~„…‡„„€}y|z{zxvz{{{{zzwvwmcer{vwwvtz}€}}|ytsrvxxvyyyw{‚ƒ…~yupkcegffghgcbaaacceghgdabccb`\_`__aafeeefekiijlglona]]lmnoqmnmke‹ŠŠŠ‘’‘‘’’’’•“““—“™••‘’Œ“Ž••–•™˜™•—’‘’“”™–˜““”–—Ÿ›™˜œœœ£ Ÿ¡§¦§¢¥¦¥¢¨¥§£¤ ¡¢¢¦¦®ª¯«««®©««°«ª£¡¡¢£§¦¥¥¦¦§®¬²±¯¨ª£¤š›––“ˆumfbbew‡“¦¨¯±±¹µ¶¬¬|gSUUay†˜§§®¬¯®´±³³³²¶µµµº¶·³´´¶³µ³¶µ¶´´´¸±µ²±³¸´µ´µ²´³·¶º¶µ³ºµ¶´·¶¹·¼³±²µª«¯²±±®¬°¯´®«°¯°°¯²¸±²²¸³´³·±µ±±±µ²·³µ±²®©¤¤££§¥«ª©¤£¡¢Ÿ¡Ÿžš™–•’’’›››””––“”Ž‹ŒŽŽ“’‘ŽŒŒ‹ŠŠŠ†ˆŠ‘‰‰ŠŽ‡…†ˆŠŠ‰ˆˆˆ‡…„ƒ‚‚……„ƒ‚‡Š‰ˆ‡‹Š‰ˆˆ‰ŒˆŽ‘‰‰‰‰…„†ˆŠ‘Œ‹‹Š‰Šˆ‹‘‰…„††Š‰ˆˆˆ‡†…‹‰‡†…‚}|}„ŒŠ‹Œ’Ž“ŒˆŒ‰ˆˆ‡•Ž“‘‘’ˆ‹Š”ˆ‡€ƒ‚…zstzˆƒŠ‚‡ƒ‚ƒ‰ˆ‰„‡}‚„ˆ‡„†}yw{{{yz€~{|‚|xwodbat{|‚zzz~€€‡zyxxw}}|}~{~~~ƒ‡{vvojikorqqmkgeefedcjhigdbbdhc_\]^bbbaeefdiiihhilinmmfbdlnxrqmnljcŠŠ‹‹‘ŽŽŽŽŽ‘‘“’’‘’’’‘ŒŒŒŽŽŽŽŽ•’••–••”’Ž‘’“””“’“–——ž›š™ž ŸŸŸ £¢¢¢¡¡¡¢¢¤¤£¢¢ ¡££¤§¨¬©ª«¬©©ªª§¦¤¤¥¢¡ ££¡ ¤¦©¨¨©©ª©¨¨£Ÿš•…vmf_\Z`cw‰™¤¨«¬±°¯°£›k_O\fs‹›ž¢¨«¬¬¬°°²´´´µµ²±±±³³´·²°²²³³³³´±±°²±²³µ³³´´²²²´³³³´³µ²´³³´¹µ»²¯®¬¦©©¯³®«ª¥¬«°¬©®«°®¯±±´´°¯®°°±±±°±±±²²²·±°¬¬¬¨¥¢¤¤¤¤¦¦¤¢¡¡¢žœ™›™š””’”•š™š—–’‘“Љˆ‹Œ““ŽŠŒ‹ŒŠ‹Š‹Š‰‡ŠŒŽŒ‹‡ˆ†‰‰ˆ„‡ˆ‰‡Š‰ˆ„~~~‚‚ƒ‚…‡ˆ†…„‹‰‰†ˆ‰‰‡†„‰‰„‰†‡‰‰‹‰‡‡ˆ‰‰‰ˆŠŠ‰„ƒ‚‡…†‡†……ƒ†ˆˆ‰†€…{}~…Š‹ˆŠŒ‘‰‹ŠŠŠŠˆˆ†Žˆ‡‡ŽŠŽŒ‹‘ŽŒŒˆ‡‡‹Žˆ‰ŠŽŽ‰„ƒ‚‚€zuqzƒƒ‚‚‚ƒƒ…ƒ„‚€~ƒ†ˆ„ƒƒ}vyy|y|{||}}{{xvhWJ]w{~€z}~~~€€}wwvxz~~€~{|~„Œ„|vpmimnrvxzupnkiggfedigiifdefffb]]]]^babaccjiiiiihhhhihhhlnpqqkmlhbŒŒ“ŽŽŽŽ”ŽŒŽŽ–“”’‘‘•Œ‘ޑޔ”˜–š””“’‘—–œ”’’‘“š˜šœœžžž¡ ž¡¡¢¡ ¤¢¢ ¡£¬¦©¥¤¡¢¤¥¨®ª´±²¨¨¨©¦¨¢£Ÿž ¡£ª¢¢££¤©¥®©©«¬«©©«›ŽŠˆƒ|vlb\VTV^fz”Ÿ¯®¯®³°²³´›r^[T`q€•šœ¢ª«¬«²¬®«°°·´µ³¸²±°´³¶³²±±±·´¸´·²³²³±³±·µ¸³³µµ³¹µ¶³´³µµ¶³µ³¶µºº¼¯¬ª©ªµ¯¬«¬«¬ª°«ª°®®°²³µ¯°®®¯³¯®±¹±²±·²³«¬©¦¥¥¦©¥¨¤¢¢¥¡¢›šœ›™™™–œš¡šœ™˜•“‘Œ‹Œ™’’‘Ž‹’‘‹ŽŠŽŽŽ’‹‰‡ˆ‡Š‰‰‡Žˆˆ‡Š‰{{|€„ƒƒƒ‚ƒƒ‚†…Œ†…ƒŒŠˆˆŽˆ‰……‡Žˆ‰…‰‰‰ˆ‡†……ˆ‡‰‰ŒŠŠ‹†‡†‡…†‡‹„„„ŒŠˆ†……€~~€„‹’‰‰ŠŽ‹Œˆ‰‰ˆŠ‡Œ˜‰‘’’ŽŒ‡„‡‘ŒŒ‰„ƒƒ„†„€xtw„ƒ„…†„……ˆ…‰„…€„ˆ‡‰‰Š€zxzz}zz{€„€}…~}yv^KGYw|}~†~ƒ~zvuu{{ƒ†€€€‚„‡ztoihrx„†€}wssuttmiiiiiijglhthgee\Z^ccdbedkjjjkiiiijkjpmmmnorqslgbŒŒŒŠ‘ŽŒŒŽŽ‹ŽŽ‘‹‹ŽŽ‹‰•”—“”‘”’““–˜•‘‘•—˜˜˜šžžž¡œ ž¡¢¢žžž¥¢¢ ¡£¥£¥¥££¥§©ª«««ª¬««§¨¦¥£¡ž› ¤¤¤¥££¤¦©§¦¥¦¥£¢¡ž™“Œ„|wrlfbZWQUVcn€”¡¬¯¯¬¬¬ziV]_oƒŠ—› ¢¤ª©ª««©««°°±²³°°¬±²¶±°°±²´²³±°¯³²²°±²·´¹±³µµ³³´³³³³µ²³±´´¶²±²®¦¥¥ª«°¯«ª¨¨§©ª©¨©©¯¯¬®¬¬«®®®«®²´°°°²±²®¬ª¬«ª§§¦©£££££¡Ÿ¡››šœœš˜˜˜œ›ž›™—™™•‘Œ’”‘‘ŒŒ‹‹Œ‹ŽŒ‹‡†‡ŽŒŠ…†„†‡†‡‡†…„„„ˆˆ‡|x„……ƒƒ„„ƒ…†Œ‹‹„…ƒ„‚‡ˆ…„„‚…‡Š‹Ž‡‡†‰ˆˆ‚‚‚‚‚ˆ‡‰‰‰‰ˆ‡Š††‚„…†ˆ‹„……ˆ‰‰†…€~~†Š‹ŠŒŽŽ††‡‡ˆ‡‡‰ˆˆˆ‡‡ŽŽŒŠˆŒŽŽŒ‹ŠŠ‰†‚…ˆŽŒ‹‹Š…‚~……„„~w{|„„„……„…†‡……ƒ‚{€„€„‡ˆ†ƒ~|z{||yzx€€~}}}zrRC;Yw{|~€€~|~}~{ztvvz{€|}‡Œ…zvqmkvˆŠˆ…ƒ‚|zywrrqomjfhgihggfea[Z]^_abddloomlgjkjjkhkmmqnnonmjhcŒŒŠ‘ŒŽ‘‘’Ž‘‘‘‘‘‘‘‘“ŠŠˆ‹‘–•˜’•‘““›››“‘’”™™Ÿž¡¢¢¢¢Ÿ ¡Ÿ¢ ¤¢§¢¤¡¦¥¥¡¡¢§¦¦§««¬ª®¬°¯°«¬ª©§¨¨§Ÿœž¡¡¨§«¥¨¨ª©¨§¦¦¦¦¦£¤ŸšŠƒ}toid_ZUQOQXgv‰¡¦³¯±®®°²¡œlaT\gwŽ— ¢«©©¨«ª©¬««¯¯¶°³³´°²¬°°·³¶±°±²²µ²²²²¯´³´°·²·´¸±´´·³´´¹´³³´´¸¸¸´·¶¶¬¨§ª«²±±®®©©©©¨¨©«ª««¯¯¯¬®¬«¯®¯²¬®®³±µ°°¯µ±±®¯±±¬¬¬§ª£§¦¦¢ Ÿ¡Ÿ ¡¡››™››œœ š˜— ˜—‘Ž““—’’‹Œ‰ˆ‹‘Œ’Œ‹†ˆ‡ŽŠ‡†‰„‡†‡†‰‰Š„‚ƒˆˆˆ~‡†‰……ƒ‰‰‰……‡ŒŒ„…„††„‚‚„„‰‡ŒŒŽˆŠŠ‘ˆˆƒƒ„‰ŠŒ‰‰‡…†Š‡†„ƒ„‡ˆ‰†‡†‡ˆ‰ˆ…~~€…Ž’Ž’ˆŠ†ˆˆ‡ˆ‰ˆŠ‰Š‹ŒŠ‰‹’ŽŒŒ‹†„„‰Š–ŽŠŠ‰†€†‡‰‚~~†……†‹†ˆ‡†ˆ‰†‡‚€„ƒ„‡Š…‚~~}ƒ|yy{€‚ƒ„~}}ymM76[t‚{~€„€}|zyxxy~|€~}|~ƒ‡Œ„~{xuv~ˆ˜“’‘“’††„€}{rnnnlkigeeb_[[[_^bcdbnoxmlkkklkkhiknnmnqpnijdŒ‹ŒŠŠŠŒŒŽŽ’‘ŽŒŒŒ‘‘ŽŽŒ‰ˆ†‹Ž‘’•”’“““’‘Ž‘“”•˜šŸžžž¡¡ Ÿžœ¡ž¢ ¢¡¢¡ ¢¡¤¢¢¢¨¨§§©©ªªª©¨§§§¥¤¥¥£Ÿžšž ¥¥¨§§¦¨©©¨¥£¤¤¢žŸ’Š}tnfc`]XURPNTXl~§©®®¬¬©¨•ˆt`^Vdq•œ¦¦¦©¨¨¨«¦ª¬¬¬®¯¯¯¯««¬°°°°µ±³²²±¯²®¯¯°°³±±±³³²±´²±±³´¶´³³³³¸¶µ³¶¬©¢¨§¬ª¦£¤£©©¨¨ªª«¬®®¬ª««¬®¨ª«¬ª®°®®¬®°±®®¯¬®¦«¦©¤¦¦¦ Ÿž ›š˜ššž ™˜˜—’‘ŽŽŽŽ““•’Ї„†ˆŠ‹‹‹Š„†ˆŽŒŠ‡‡‡‰„„„…†‡‡‡‚„„„„…ƒ„…‡ˆ‰………ˆ‡‡††‡‹ŒŠ„„„…ƒ€‚€„…†„‡†ˆ…ŠŠŒ‡„ƒ…ŠŽ†„‚…†Šˆˆ…„„…І‡€€€~~„‹‘ŒŽŒŒ‡Š†‡…‰‰‰ˆ‰‰ŒŽŒˆ‹ŽŒ‹ˆˆ‰Š‹…€„†ŠŒ‹ŠŠŠŠŽˆ„€†‡‰‚ƒ„…†ˆ‰‡‰ˆ‰ŠŠ‡‡‚}€……„„ƒ€~|}|zv{|€‚‚ƒ‚}}wqM:4Tov{}€}||€|{zzz|{~}~x|†‡…ƒ…†‚~~…’•”””’‘‹‰†…ƒ‚{tssvxqjieca`ZZXZZZZ_agkkkkjjklmkiijmnoorujfhdŽ‹ŽŠŠ‹ŒŽ‘—’’’•””Ž•“Ž‘Šˆˆ‡ˆ’‘•“•‘‘–’‘‘˜–™š›ž¨žŸ£¢¡ŸŸž¡¡¡¡¤¢¥¡¡¢¥¤¤¦§¨¨§¨¦ª«®®¨©§®§§¥¥¢¡žžŸ¦¦§§¦¨§§¦¥¥¤¤¡¢š‹oeb_^]WTQOLLU`rŠ—ªª¯¯³µ¶£Ÿ„uiZ\^h{Š¡©©ª«®©ª«¬©°¯±³²³®¯ª¯°°¯¯°´´´´µ²²±²®¯±´³³±³±°°´³²²¶¶¹¶¸´¹¸¸µ¶³µ¬§§©¬µ®°¬ª¥¡¡§¢¨¨¨¨¯®³±¬ª«««¯®¨«©«¬³®°´°±¬¯³®®¯¹¯©¬§§¦ª¦§¢£Ÿ ¡¥ŸŸ›œ›š¡ ¡˜˜˜˜‘“ŽŽŽ“—“•’ˆ„ƒ„‡‹Š‹Šˆ‰ŠŠŽŠˆ†Žˆ‰†………‡‰†ˆŠ„…„‡…Šˆ‹‹Š‡ŒŒŽˆ‹‡‡‡‹‰„†……‡‰ƒƒƒŠ‡‡†‰‰‹ŠŠŒŽ…ƒ…„ƒ†ŠŒ†„…Œ‹‹‰…„ƒƒ„‰†‡ƒ‚€ŠŒ•’—”ŽŒ‰‰ˆ‹‘Š‹‹‹ŽŽŽŒŽŽŠŠ‡‰ˆ‹…€€…ˆ’Ž‹’Š‹Šˆ……†‡ˆ‰‰‰Š‹Œ‹‹ŠŽŠˆ‚†„Œ……‚{{{‚|yz‚…ƒ†ƒƒ‚‚}ytX><Pisy|}€€€€‚~€|€~ƒ}{€ˆ„‡ˆ…†‹’ ›››˜œ“ŽŽ†„wtu„{rplkaa]ZXZZ[Y^`dipkljjkqmnijjpqqotogemeŠ‹‹Š‹ŽŽ‘“’‘‘‘‘’ŽŽ†††‰‰Ž“‘’‘‹‰‘”–•™šžžŸž¡¤£¢¢¡¢¡¢£££¥¦¨¨¨¨©¦§§§§©§¨££££ ¤ ¡ ¢¤¥§¨¦¦£¤£¢¡ œŸžœœ“Œ€qeWVWYWVQPMQNYcyŽœ§©ª¬°¬ªšŒwb_Uaju€Ž›¡§¨©«««©«¥¬©¯¬¯±±®±«®«¬¬«¯¯°¯±°°¯®©®°±³³³±³°°¯®±²¶µ·´´´¶¶¸µ³°¬§©©«®¯¬ª«£¢ ¢¢¥§©ª¬¬¬ªª¨¬ªªª¨¥§¨¬«®°°°´³¯¬¯®®®®¯¯¨§§¨¦§¥¥££¡£ŸŸž››šœ›š™””’‘ŽŽ‘‘—“Œ‹†„ƒ‡‹ŠŠ‹Š‰‹‹ŒŠˆ‡‰ˆˆ††„‡ˆŠˆ„„…„†††††ƒ…†Š‹Žˆ‡„‡ˆˆˆ…‡‰‡††…ƒƒ††……†‰‹‹ˆ…„„ƒ‚„„„ˆ‰Š‰‡†…ˆˆ‰ˆ‡„‚|‚……‡†„}€‚ƒƒ‰Œ‘‘ŽŽŽ‹Š‰‰‰Œ‹ŒŒŒ‹Œ‹ŒŠŒ‹‰††††††‚€€‰ŒŒŒŒ‰ŒŠŠ‰‹†‰…‡ˆ†„ˆ‰‹ŒŒ‹Œ‹‹‹‹‹ˆˆƒ„ƒ†‰Š…„yu{}~|‚{{|€‚‚‡ƒ‚€~{wfREUcltwz}}}||||~~€}~~{‚‚ƒƒ…‰‘“’Ž‹•œžž™˜—’މ†zxw…„}wtsi`_^\YZ[[[^_beefhhjjkkmikkmlllnjfaa`‹ŒŒŒŒŒŽ‘‘•“’“‘Ž“”ŽŒ‹ŒŒ“‘“’ŽŒ‹‰Œ”“˜˜™œŸ ¢ŸŸŸŸ¤ ¢ ££¤£¦£¤£¥¦§¤ªª¬ª®®¥©¨ª¨¦§¥¦¡¢¡¤£¤¤¨¤¥¦§¦¦£¤¤£œ ™‰‚th]QPQSZSQONOS`ož£²¨®®¬ª~kZZXan|‘–¢£¨¬°¬®ªªª¬¬©°¯²²±°°®¯¯±«¬¯°³®µ°±¯µ¬®·¶¶´¶²³°°°°°µ³¼µ¼´µ´¸·¹²®¬ªª²ª°¯®®®ª«¥¦¢£¤¦ª±°±®¬¬«®§««ª¨¥§¨¬²´´±´°®¬´¯±±°°±©ª§©¥§¥¨¢¤£¤ ¡ž›œœ ¡žž˜™”•‘”‘––—”˜‘ŒŒ†…‚†‹‰‰‰Ž‘Љˆˆˆ‰‡†ˆ‹ŽŒŒ„ƒƒ…„‰ˆ†‡‡‡†…‰‹‡‡„‰ˆŠ‡„„‡‡Š‡†…ƒ‡…„„†‹“Ї…††ˆˆ‡„„ˆ‰‰‰Œ…‡‡‹‡ˆ‚|~†„‹‡†~€„‡Š‰Œ‘‘•ŽŽŽ’ŽŽŽŽŽ•ŒŒ‹‹Ž’‘І†ˆˆ‹†ˆ‡†‡”ŒŽŽŒŒŒŒ…ˆ‹ˆŠ‰™ŽŽŒŒ‹Š‹‹“ŒŠ†‡†Ž‰Š…„ysw‚}}}}…‚‡‡‡„††‡€‚{zob]\agpuy‚€|~|€‚„„…†€€ƒƒƒ„…ƒ‡›šš”‘‘˜›¤¤¦£¤¢™–”•މˆ€€~‡…zwqjda_a\\\\]^`accchhnkjjmllmpmomqieccc‹ŠŠ‹Œ’‘’’‘ŽŽ‹ŒŽŒŒ‹‹ŠŽ“’‘‘ŒŒŠŒŽ“”™™›œž šœšžŸŸ Ÿ¡ ¡¡¤£¤¤¦¢¤¥©ª©¨ª¬¨¥¥¦ª§§¦¦£¥ ¡¡¥¤£¤¤¤¥¢¥¨£¡ œ˜™›‘Š~qg[WOOOSSQNNNTWgv‰ž¤¨¨¨¨£„pdT^cnx€Š—¡£¥©®®ª©©¨¨««¬ª°°®«¨¯¯«¬ªªª®®¯¬®«ªª®®®¯¯²³³²±°°®°±µ³·µ´´´³´²°¬«§«±ª¬«ª©©§¨¥¥¢£¨©¬¬¬«¬«ªª§ªª«©§¥§¨¬¬¯°¯®®¬³®±¯¯®°©©§ª¢¦¤¢ žŸœžœœšœ¡¡¢šž—•‘“‘“’“”“•”˜ŽŒ†…‚……†‡‹ˆ‰‰‹ŽŽ‹‹‹‡†……‡‹‰ˆ†…„„ƒƒƒ„‚„‡‡ƒ……Љ‡ƒƒ„Ї†††……„††‡‡†‚‡ƒƒƒˆ‹‰ˆ…ƒ†‡‡††ƒ†‰Š‰‹ŒŒ…„ƒ†ˆ…|~€‡„„ƒ€}€‡Œ‹‹ŒŽŽŽ‘‘‹ŒŒŒ‰ŒŽ’‘‡††ˆ‡…‚ˆ…ˆ‹ŽŒ‹‰ŽŠ‡‡††…††ˆˆ‹Œ’Œ‹Œ‹‹‹‹ŽŒŒŠ‰ˆ‡ˆŽŠ‰ˆzpw}z}~~„‚‡…†…„ƒ‚~}{wphecinwy~}{|{€ƒƒ„…†€€€‚ƒ‡‰„‚—œ ™—•𢢣¢¡›™–”•’Žˆˆƒƒ‚†‡„}yqmhea`]^___`ab_`_bcilkimmmnpjkjjheccaމˆ‹““™’‘‘’ŒŒŒŒŽŒŽŽŽ“Ž‘••”’’‘‘“”›ššœŸœžžœžžžŸ¡ ¡¡¢¡££¨¨©¦§¤¦¥©©©©§§§¥¦¥ª§§¦¥¦¥¥¥¦¬¤¢£¦£«¢¦£¡Ÿœž–™‰qf]RONNPPXRPLNV^m¨¨¬ª¬©°—xe_X`lx‹”™¥£¦¥«®¬ª©¨¨¯ª¬ª°¬«ª©°¯¯°¬ª¬®®®®¯¬°«¬±±±¯±¯³²³²±±²²²³µ³¸´´µº´·°®««ª®®±ª¬¬«ªª««¨©¢¥§¬¬¬¬®±«®ª«««ªª¨§¨ª¨¬«²µ¯«¬²³³±±±²®°ª©«£¦ ŸŸ¡Ÿ¡Ÿ›œ›¡Ÿ¨¢£˜ž–”“™“”“˜—˜“–——ŽŠ†…„‰ŠŠ‰‹‹‹‹‹Ž‰††‡…‰Š’‰††‡…‡†………‚„‰„‰‡Š…„І…†Š†…………ˆ‡‡„ˆƒ„„‹ˆ‡†…ƒ‰‰Š†……‰ŠŽŒ’Œ…ƒ‚……ƒ~‚ˆ„‡€~~‚†”••“‘‘’ŽŽŽ‘‘‘‘“˜‘Š‹Œ‹’‘ˆ†…†ƒƒ‡ˆ‘ŒŠ‰‹‰ˆˆ‡‰†…‡‰ˆ‹•‘”ŽŒ‹•‹‹ŒŽŒŒŽŠ‹€tt„~||‚‡…„„‡‡ˆ‰Šƒƒ~€~tmjkpxz}}|{||†…„„…†‡‚‚€ˆ†Žˆ„‰—™¢ ¡›š¢Ÿ§¤¥¢ ¢ ž•𓕉ˆ‡‰…yzoifgfgddddacaa^bbhjommmropljhhhhehc‡‰ŒŽ’’’’Ž‘‰Œ‰ŠŠŠ‹ŽŽŽŽ‘“•‘‘‘‘‘‘’”•—™™œœœžž››œžžžž ¡¡¢£¤£¥¥¥££¢¢¤¤££¢¤¤¤¤¦¥§¢¥¦§©¦§¦££¢¤£¡œ›››˜˜™‘Š~qdWSNPNQURPONNNYduˆ”£¨©ª§¤¢|n\_]kxƒ’–›Ÿ££¥¤¥¥§£¦¥¨©ªª©©«¬¦«ª¯¯®«ªª©©©ª«««««®²®°¯°¯³°°²²´¶µ³²³´µ´®¬®ªªª®¬«ªª¨©©ªª¨¥¤£¥¥¨««¬®¦¦¤¤¤§¦©®¨¦¬©«©©¥««®©¬««ªª©¨¤¡žžžžžœœ™›œ¡¡£Ÿœ˜—“““•“““”•–“–’‘Œ†††ˆˆ‰Š‹ŒŒŒŒ‹ŽŒŠ†………†‰‰‰„ƒ……†ƒ‚„……€ƒ‡……†‡†…………‚ˆ†„ƒˆ‚…„„„‡‚‚‡……„……‰Š‹ŒŒ‹€……ƒ€‚ƒƒƒ€{}‡Œ’‘’‘‘ŽŒ‹Œ‘‘“”˜‘‹‹ŠŠ‰‹‹Ž“Šˆ…†‚ƒƒˆ‹ŠŠ‡ˆ†‰‹‰‡‡††‡ˆ‡Œ‘”‹‹Ž‹ŠŠŽ‹‰Š‹Ž‰yxz€}}}ƒ†‡…„…„…ƒ„ƒ€€~}ztqrt{|}~~{}}…Ї…„ƒ„ƒˆ†‰ˆŠ”œžŸ¡žœžŸ¡¢££¢¡¢£Ÿ—š““‹ŒŽŽŠ…{smhklgedddaa`a\\]ffommnmmmmledddcedŽ‘ŽŽŽ‘•މŒ‰Ž‹‘Ž“•”–‘‘’—”•’“”š’“’˜–œœ¢Ÿ ›œœ››œ¡¡¡¡¢¢¥¢¤¥©£¥¡ £¢¡¢¦¡¢¢¤¤¨¤§¤§¨©ª©§§¦¥£§¦¥šš›™ ˜™Š†th\PMJLOPWSRMLLN]j}”›¦¥®¯š’}nfZ^cp†‹›ŸŸ¨¨©£¥¥¥¤§¥««¬¬«°ª¬«¬¨ªªµ¯®®¯¯¯¯§©©©ª±¯´®°°³²±±±°²±´³µµ¶¶º¶¸³¶³¹³³®¯«®¯²ª®ª«ª©©¬ª¯©¦¤¤¦¨¨¨«±¶®¯¨¨£¦¤¬¬ª¦¦¬«¯«ª©¨¨¬«°«¬©°ªª«¬¦¢ œ¡ž ¡¢žœŸ ¥¢¤š˜˜”—”™•——Ÿ–—–—“•†‰‰Œ‹—’ŒŽŽ‰ˆˆ‹‹‹‰Ž‹‹…ƒ„‹†…‚‚‡ƒƒ‚„ƒ……†‚‚†…„…‰†„………„ƒ‡…ƒƒˆ‡‡‡„††‡††…„ƒ‡‡‰ŠŽŒŠ‹ƒƒ‚‹„ƒƒ‚ƒ……†€}}ƒ““““—‘‘‹Š’‘‘—˜š‘ŠŠ‹‹‹Œ’“•‹…†ƒƒ†ŽŽŽŠŠ‡‰‡ŠŠ‰Œ‡ˆ‰‹Œ’‘“””‹Œ‹”ŽŒ‰‘Œ‹‹ŠŒ•‘…|xz~†€††ŠˆŠ‡ˆ…‹†‰ƒ‚ˆ‰Œ€~}}{yz|~€…‰ˆ‹……„„…‰ŠŒŒ‘‘’–Ÿ¡¢¤£¬Ÿ¡¡¨¨©¥¨¢ š™”””™’’𔓋ކ†ypospjhieiba`_]X]dfpntpmntmnfgbcbedŒŠŒŽŽŽŽŽŽŒŠ‰‰ŠŠŠŒŒŽŽŽ““—””’”˜•“““——›Ÿœ››œ™š™› ¡¡£££¢£¥¢ŸœžžŸ ¡¡ ž¢¡¢¢¥£¤¥¥££¤ª¦§¤££ ›–˜”–•’ŽqfXRMLKNPQSQPMJNQar†›Ÿ£¤¤¢žq`^^hox…•™œŸ ¢£¢ ¢¤££¤¤¨¦¨¨©¨¨¥«©©§©©ª«¬¬®¬«©¨§¨©ªª¯®²®°°±±²²±¯±±²²´µ¶¹·´µ³³³²¬ª°¬±«©¦§¥§©©§§§¦£¥¦¨©©©¬®ª¨§¨¤¥¤«¬¦¥¥©«©§¦¦©¨¨©¨¨¨¦©§¨§§£ ž™Ÿ¡ ŸŸŸŸžž¡¥£ ž™—””’”“”“—––’‘’‘‘‡‰Š‹ŒŒŽŒ‹‹ŒŒ†‰ˆˆ‡ˆ‰Š‹‡‡††ƒƒ~ƒƒ‚‚ƒ‚‚‚‚„„ƒ„……„ƒ„††„„†€„ƒ‡‰Œˆˆ„…€ƒ‚†ˆˆ‡ŠŒŠ…„ƒƒ‚†ƒ‚‚……€}}}ƒ‡Ž’’““•ŽŽŒŠŽŽ‘–𖑉ˆˆ‹‹ŒŒŒŒ‹Š†„ƒ‡‰Ž‹‡Š†††‰ˆˆˆŒ†‰ŽŽŽ’ŽŠŠ‹ŽŽ‹‰‰ˆ‰‡‹‹ŒŒˆ}y~‚€‚…†Š†ˆˆˆ…‡††ƒ„…ˆ‡†z||}€‚‚ƒ…‡ˆ‰Š‡†††ƒ†ˆ‹ŒŒ‘„ˆŽœ¡¥¦£§™¢ŸŸ¡¥£¤¡ ›š••”••””—•“Œ‹‹‡~wtuvqkiefa^\_aaadglntuonoljfg```bcŠŒŒ‘Œ‹Œ‘ŠŠ‰ŒŒŒŒ•”ޑޑ™”—˜—––””•——žžžŸŸŸž›œœœ› Ÿ¤¡¡£¦¥¨¢£Ÿœœœž¢¢¤¡¢¡¡¢¢¢¡¡¥¢¥¤¤¢¨¤¬§§¢¥ œœ›–™™š‰‚sf[PMKMPRTUUQOKIPVh}§¦ª±ŸŸ„shZ^al}„‹’œ›ž ¥¡¢ ŸŸ¡¤©ª«©¯§ª¨«¦¨¦ª¨©©¬±ª¬®«¬ª®«°©®¬°±²²²±µ±²±±°´³¶´¹¹¸·¶´ºµ·±°«««±¯²±ªªªª¦¦©«ª«¨«¦©¦ª¯¬±¯³¬®©ª§©¦¨¨®««¨«§¨¦¥¥¥¦©§¨©««¨¬§©¦¨¢ žŸŸŸž¢ ¡¡ž§¦¥¤£›—•••“—•–”—––’”““’‘ŽŽ‹‘‹Š‹Œ‹‹‘ŠŠ‹‹ˆ‡†‡‰‰‹‰ˆ‡‡„‚†…‰‚€…‚†‚‚ƒ‡ƒ†††…‰ˆŽ„‚‚…„„‡‹‹‰ˆ………‡ƒƒƒƒ…ˆŠ‡‰‰Š„„‚‹‚†„ƒƒƒ…†|}ƒ‹Ž–—˜—–“”””ŽŽ’’’’‘’••”Žˆ‡ˆ‹‹ŽŒ‹Œ‡ƒƒ‰‰ŒŽ‘ІЉІ‹‰Šˆ‡Š‹™’’‘ˆ‡ŒŽŒŠ‰ŽŠŒ‰Ž‹ŒŒ†‡„„‚†………†‹ˆŠ‰‹…‰††„‰ˆ‡‡}ƒƒ‡‡…„…‡Œ‰‹‹Š‡‡‡Œ‹‹Š–‘‘‘‘ƒ‡œœ¡¦«¤££¤ŸžŸ©¤¨ ¡ ¢–‘“™š–š–•‘Ž„{zqkik`[Z`cjjhhkoxsqqqkgegbaaabŒŠŒŒŒŠ‰ˆˆˆ‰‰‰‰ŒŒŽŽ’ŠŠ‹Ž‘“’’”•–‘–“–———œšœžž™œœœœŸŸ£¡¢¤¥¤Ÿœ›˜™–œŸ¡¢£¡£¡ £¡¢¢£¢¦¢¢¢¥¤¤¤¢¡ œ™•””‰~qfYTKMLPUTSQQPNNMV_q†“¤¦¦¥¦—‰yhaYcjw‡‹’˜œ £¤¡ŸžžŸ¡¤©¨¨¨¨§¨§§¦¦¦ª¨¨©«ª©ª¨«©¬«¬ª¯¬«®¯²±±±²°°°²±±²²³³³·µµµ±¬ª¨«¬®®®®ª¤¤£ª¨¨§¨¨ª¦ª¦§§§§¬®¬¬«¨¦¦¦§¦©©©©ª§ª§¨¢£¤¦§§§§¦«ª¨§§¨¥§¡ Ÿ›žžœœ¢ž¦¢¡™™–•”–•–••”—’‘‘‘Ž’ŒŒ‘‘ŠŠ†ŠŠŒ‹‹‹Š‡‰ŠŠˆ‡†‰ŠŽˆŠ‰‰‡†…„‚ƒƒ…ƒ‚‚ƒ„‚‚‚ƒ‚„…†‚€……ˆ‰‡‡†‡‡†…„ƒ‚ƒ…‡Šˆ‡†‡ˆˆ€‚„‚„ƒƒƒƒ‚€zz{„Ž’“”––•••““ŽŽŽ’‘“““ŽŠ‰„‡ˆŒŒŒŒŽŠŠŠˆƒ‚„Š‹‹ŠŽ‘ŠŠ‡‡†††Œ‹Š‰‹‡Š‰ŽŽŒ‰„ˆŒŒŒ‹Š‰‰Ž‰‰‰Š‰Œ‹„~ƒ………„‚€†…††‡‡‰ˆŠ„„„…„†‡Œ…„~~ƒ…„…††ˆ‰Šˆˆ‡‡„ˆ‡‰‰‹Œ‘‘•–ƒ}z˜ž¢§¦¥¤¤¢ŸŸ£¡ ¡ž¡˜•“›™™••–”ŽŽˆ‚z}|yutrn^[Zadfghhlowqqplgfddddcb`ŽŒŒ‰Š‰ˆˆŠ‡ˆŠ‘‘’Ž’“ŒŠŠŽ‘““’”““”•–˜•›˜™™›š Ÿ¡žžŸœ›¡Ÿ££«¤§ž˜˜ššž¡¤¥¥¤¤££¢£££¡£¢¤£¦¨©¥©£¨¢¢ Ÿ™–––Š…ui\PLJJNQYWXROMLMP]j{”š¨¦«©©m^YXbr}‘””š™ŸŸ¥¥¥ žžž £¦«¨¬¨¦§¬©ªª«©©ª¨¨©¨«©°¬®¬°¬«°²¸±±±²±´°µ²²²³³´²·³³±¯««ª±®¯®´¬§¤¤¥«¨§¦¨¨©§«§«§¨¦®¬°ª¦£¦¤¦©¨®©©ªª©¯¨§¤ªª«©¨©ª¯«¬¨¯ª¨¤¦ ¨¡¡›£žœœž£¡¦ ¢žž–›“–—š™™”–“”‘‘Ž’’Œ“’‰‰‡‹‰‹‹‰‰ˆ‹‹Š‰‰Œ‹‹‹‡……‡‡ˆ‰‰„†€ƒ‚‡ƒ‰…„…Š‚ƒ…ƒ‚ƒ‡…ŠŠ‡‡‡…Ї‡„ƒ…‹Š‹ŒŒ‰ˆ†ˆ†„„„ˆ……„‰†ˆ€~|‚Š˜•˜–•””––“‘‘‘“’—““ŽŒˆˆˆˆŠŒŒ‰ˆ†‚‡’Ž‘‘’‹“‘‡…‡ŒŒ‹ˆŽŽŽŽ‘ˆ……Œ‹“Œˆ‡‡‰ŽŠŠŠŒŒ‹ƒ‚‰‹’†„…††‡†††‰ŠŠ‰…„…†…‡†Œ†…ƒ†„„†‰…†‰‹Ž‡‰††„ˆ‡Š‰Œ“–Ÿ˜yfw’œ¦¥¤¤³ª¨¡¢¢¦¢¢¡¡¡¢››š›œž–••˜–•Љƒ‚€~~}xra]afffghioowwyojfefigedfb‹ŒŠ‰†…„ˆ‡‡…ˆ‰‹ŒŽŽŒŠŠŽ“’”“““•———˜˜™šœ›žžœžžžž›œ £¤£Ÿž™—–›¡¤¤¥¥¤¥£¢ ¡¡¡ ¥¢¤¤¥£¢¢ ›››š•Ž‹vi\VKKJNQSWWUSNMKQVgu†™ž¦¥¤¡œ†seUZ[hvŒ‘–••ššžŸŸŸŸœžž¡¢¥¦¨¨©§§¨¬««¨ª¤¤¥¨§¨ªª§¥¤¨¨©ª¬¬¬«ª¬®°±²¯±°±°°µ²²±³°²²³¯®ª«©«ª°®¬©§¦¥¤¤¤¦§§¦§§©¨©¦¦¥¦¦§¦¦¥¤ ¢¥¥¤©«ª¨§§¨¨¨¤¤£¦¦ª¥¥¥©°®¬¬©«¬¥¢¢ ¡ž››››œž ¢¡ œ›˜˜˜——›“””™—–”–“”ޑދŒ‹‰Š†‰‰‹‹‹ˆˆ†‹‹‹‹Œ‹ŒŒŒ‹ŒŒŠ……„†…†‡ˆ„„‚‚‚‚‚‚ƒƒ„†‡‡†‰‚€…ƒ„„…ƒˆ‰†‚„…Ї…„…‡†…„„ƒ‚‰†‡ˆ†„…‡ˆ„…†‰„|~}„‹’“”˜”–’”–“’’‘““”’“ŽŒ†‰ˆ‹Œ‰‰‡‰ˆˆ‚€~…ŠŽŒŒ‹ŒŒ‹‘ˆˆˆ‹ˆŽ‹ŒŽŒŠˆ‚„…ŒŠ‹‡†„‡‡ŽŠŠŠŽ‰‡†‰Š‹ˆ‡‡‡ˆ‰‰ˆ‡‡‰ŒŠ†‡††„„ƒ‡†ƒ‚…‚‚‚„…††ˆŒŠ‡†‚‚‚…„††‡ˆŒŒ”˜™™‘qtz•› ž£¢ª©§¤¤¢¥£¢œœœš™˜›——–”“–™–“‘Šˆˆ‡†…ƒreiolgghjkoovwyngbefgggedbŽŽŽ‰‡†„…‰ˆˆ‰‰‰ŒŽŽŒŒŽ’’“‘“““”˜—š›››¢›žžžŸŸŸ¥¥¤ž£žŸ¡¦ ¢¡¡žœš™›£Ÿ§¦¦§ª¦©£¢¡¢¡¢¢¬¤§£¥¤©£¤ ™›››Š„sg^TPKIIMVXXXYTRMKT\n†’¤¤¬§§•zjaUY`k{…‘“˜šš››šŸŸžžž¢Ÿ¨¤©¥§¨ª§§¨®¬«¨©¤§¦¨©¨¨¨¦¥¤¦¨««³®¯ª¬¬±°°±³°±°·±º±µ²´²´²¶±²«®®«±®®§¦¦§¥£¤¦§¨¨¨§ª¨§¦¤ª©§¥ª¡žž¡¤©§««ª§¦¦©¨¨§¦¤§§ª¥¨¥¨¬¬¦¤¤¤££Ÿ¢¡ ž¢¡§§§ ž›››››››œ””•˜˜–––“•Ž‘ŽŒ‘‹Š‰Š‹Œ‰Ž‘‹‹‹‹‹ŒŽŒ’Œ‹ŽŠ‰…‡‡†‡‡‰Œ„‡†††‡†…†‹‹ŠˆŒ‡ˆ‚€€†ƒ……ˆ‡ˆ„„„…‹…„„ˆ‰†…ƒƒ‚Љ‰‡ŽŠˆ‡†Š€~€…‘˜••”™––’”“‘‘˜’“’’’””˜’“ŽŒŠ‰‹Ž‰‰‰Š‰‰€~‚‹‰“‹‰‰Œ‘‰‹ŠŽŽŒŒ‹ŠŽ‘‰…‚„ˆŠ‹‡‡‡ˆŠŽ’ˆ‡‡Š‹‰‹ˆŠŠ‹‹Š‹Œ†ƒ†‰…ƒ‚ˆ„ƒƒ……‹ˆ‡†‹‹ŒŠˆ‡†‚„‚‡…‹‹ŒŒ’’¢›’Œ†‰˜š¤£¤¢ª©¬ªªª«¥£ žœœ ››˜™–•“–—™––‘‘’Ž‹‹‚vtxwvljlooorxxzldaehnjmhee‰ˆ‡‡ˆˆˆˆ†ˆ‰Š‹ŒŒŠ‹‹‹Œ‘‘‘‘”•——šš››››žžž ¡ ¢ž Ÿ ¡¡ Ÿ šžŸŸ¢Ÿ¡¡¤¦¥££¡£¢ž¡£¥¢¢¡¡¡ ŸŸœš™‘ŽtgZTKPLMMOQTVWWTRRPZdw‰—¥¤¤¢¡~qab[bis€ˆ”˜œœšœ˜›œœŸŸ¢¢¤¤§§ª§§¨¨¨¨¨§¤§§¨¥¦¥¨¥¥¦¨©««¬«¨¬¬°¯®¬³¯°°±²±±²³µ±²±±°¨¬«®«¨¨¨§¥¥¦§§¤¢¦¤¥¥§§§¨§§§¢£¢¡ ››œ¢¤§¦¦¦§¦¦¦¤¢¢ž¡¡¦¦ª£¥¥¨«¬§¨§¦¢£¡ ŸžŸ Ÿ£¤¤£¤›š˜™˜™˜™™›”––˜˜•”““ŽŽŽŠ‘‹‹ˆ‰‰‹Š‹‰ŒŠˆ‹‹‹‹Š‰‘‹ˆ‡††„‡‡‡‰Š‹ˆƒ†„……ˆˆ‰‰ˆ‡‡‡Œ†‡ƒ„…†‡‡‡……„……„ƒƒ„‡†ˆ†…‚‚‚„ƒ…‡Š‹Š‰ˆ†……z|…Š“—••”™—•’‘‘“’‘’’”‘ŽŽŽŒ‰ŒŽŽŒŒ‰‹Œ‰„~‚‡ŠŠ‰‰‡…‰Œ’’Œ‹‰Š‹Š‹Œ‹‹‰ŠŠ‡‡†…‚†‹‹ˆ‡„ˆˆŠŽ‹Œ‘І‡†ˆˆŠ‰Šˆ‹‹‹‹ŠŠ…………†‰…„‚‚‚‚…„Љ‡†Š‹Œ‰ˆˆ†ƒ‚ƒ„‰‰‹Œ’”Ÿ ›—––˜šŸŸŸ¢¥©ª©©©¥¥¤¡žžœ››œ˜––•’““•—•”’‘‘‹…z{~xonnooquvvqid`ciiijjigˆ‰‡ŒŠŠŠ‹Œ’ŽŽŒŽ‘–Ž‘’”™–›™ž›œž›¤Ÿ ¥¤¤ ¢¢£¤¤¢£ Ÿž ¢¥Ÿ¨£¢¢¢¢£¦¦¢££¤ ¢ ¥¥¥¤ª¡¢¡¡ š™Œ‡vk]PJFJNPQSSSTUUSQRUam™œ©¦ Ÿ„sk`fkox…Ž˜™œœ£Ÿ¡œœœœ ž£Ÿ¢¢§¦®§¦§¨¨¨©¬««§©¤¥¤§¦¨¨®«««¬¬¬«²¯µ¯¯´¯°°³±µ±²³»°´°°®¬¬¬®«ªªª§§§¨§¨¨¤§¤ª¨«§ª¨°§¨¢ª¢£Ÿž¡£¬¨«©«§ªª«¤¢£¤££¤§¦«¦¦¥¨¨«¦©¨§¢¤ žœ¡ ©¤¤£«¬¬¢£š›—ž™™™œ™š›œ˜œ—•–—‘“’—ŽŽŽ‹ŽŽŽŒ‘Œ‹Š‹ŒŠŠ‹‘ŒŠ‡‡‡††‰‡ŠˆŽ‰†ƒ‹‡ˆ†ŒŒˆˆ‡‹…†‚ƒ„Š‰ŠŠŠ††‡‡‡‹…„‚ƒ…Іˆ‡‡„††‡ƒ……†ˆ‹‰Š„‚~zx|ƒŽ•”˜——•˜˜›”•’–’‘‘˜’•ŽŽ‘‘“““‹‰ƒƒŠˆŠ‡……†‰‹’šŒŽŽ‹Ž‹‹‰ˆˆˆ………‡‰Šˆ‡‡‹ŠŒ”Œ‘Œ‰‡ˆ‡‹ˆ‹Š‘“ŽŒ‘‘‘‹‰ˆ‡†…†Š……ƒ„„„„‰‡‹‹‹‹ŠŠŒ‰‰†‡†ˆ‚‚ƒ‰‰Œ“•žŸª¢¦¤£¤¤Ÿžžžž¡¤ªª««¯¨©¨¨¡¡žœœšŸ˜š”•”“”““——˜˜™‘‹‰†…~~ttttqsx„unii`bflhhimgˆ‡‡†ˆ‰‹Š‹‹ŽŽŒ‹ŠŒŒŒŽŽ‘’“”—˜™šœœœœœ›žŸŸ£¡¢ ¡¡¡¡¢Ÿžž ¡Ÿ¢¤£¢¢¢£§£¡ ŸŸ¡¡£¢£££ Ÿœ™˜”ƒxj]UIGEJRRSQPRSSSRRVZiwˆš £¤˜|ifamx‚ŒŽ’–š›››žžžœ›šœžŸŸž¢¢¥¦¬ª¦§¨¨¨¨§ª«§£¤¡¥¥¦¦§¨«ª«©©§¬ª«««¬®®³±±±³³²®¬«¬¬¬¬¬¬®ª©¨©¥¥¤¤¤¤£¢¢¥¤§§«§©©©¨¨¢¢žžœŸ ¤¥¦¦¦§¨¥¥¥¥¡¢ž Ÿ¤§¨§«¥¥¥¦¥¥¢¢¢£¡¡ž ©¤¥¥ª£¢ ˜™˜œ™˜—™™˜——˜–”“‘—ŽŽ’’’‹‹‹‘ŽŒŒŒŒŒ‹‹ˆˆˆ‰‹‹‹ˆ…ƒ††ˆ‡‰ˆ‡…„ƒ††‡‡Œ‹ŒŒˆˆ‡ˆ†…ƒƒ…ˆˆŠ†„ƒ„…‡†…‚‚ƒ…‡„ƒƒƒƒ„~€„„„„„„„|wxx‡Ž‘””’˜”–˜˜•”‘’’““’‘‘ŽŽŽ‹ŒŽŽŽŽŽ‹‰‡€„†‰ˆ††„†ˆŠ‹’“Œ‡ŠŒ‹ŠŽŒ‹‡‡…„„ˆ‹Š‰‰ˆˆˆ‹‹‹ŒŒŒŒ‹‰‰ˆˆ†††‹ŠŒŒŠŒŒˆˆˆˆˆ‡…‡…„ƒ„‚…†‡††‡ˆ…‡ˆ‰‡…………†‚ƒ†‡‹Œ“–ŸŸ£¢¥££ ¡ Ÿ¡£¦¥¥¥¦§§§§¢¢ Ÿ›šš ˜˜••–•“”““““‘ŽŠ‰‡~~utsrruyzsmfiaddddfhhhЇ‡‡Š‹‹‹‘ŽŒŒ’ŒŽŽ’’“”““—˜˜˜œ›ŸŸŸœŸŸŸŸŸ¡¤£§¡Ÿ £¢¢¢¢¡¨¡¡¡¢ ¢£¤¢§£¤¢ ¡¡¡¡Ÿ£¢¥¥¥¤£ž ™˜ŽŒyo`RLEEFIQV[TQRVVWQQYap„’¨¨©£¦‘„teedožŸŸ ¡¢¡§› œ›››¡ ¡Ÿ£žœŸ¡¤ª¨¬®©¬«°©¨§ª§¦¤§¥«««¦¯¨¬ª°ª©¨«¬®ª±²³¯²±®³²²²³°¯«©ª®¯®°®¯°°¨ª¥¥¥¦¥¨¤££¤¥«ª¬¬«ª¢£Ÿ £¤¦§¨¨§«¨«¥§¡£¢¢¡¨ª¨«§«§¨¦§££¤ª¢¢¡¡£¥£ª§«ª«¢§žœš—¡™˜–›¡—”“”‘—Ž‘˜’””“‘‘‘“““‘ŽŒ‘‹Š‹Œ’‹Šˆ„‚‡†‹ˆ‰…„„„„‡‡ŒŒŒŒŒŒ‹Š‹Š‰‰‹…„€‚Œ†„ƒƒƒ††‰„†„ƒƒƒ€‚‰………ˆƒ„}ywy~‡Œ•‘“’˜”•–˜–—“•’›–—‘Ž’Ž”‹Œ”Šˆ††Œ‰‰‰…‰‰ˆ‰‘”މЖ“Œˆ‰…„†ŽŽŽ‰‰ˆŒŠŽŒŒŒ“ŒŠ‰ˆˆŠ…‡‡Œ‹‘ŒŽŽŒˆŠŠ‘ŒŒ†‹†„„…ƒŠ‡ˆˆˆ‡‹‰‰ŠŒ†……ˆ‡‡‡†‡ˆ†‹™™ ¦¢¦¥§¨©¥§¥¥¥¤¢¨¥¦¤©¨¨©©¥ª¢£ž››¡›œ›š••“““‘‘““”‰‰w|zyuxxxqliiiiffeeglh‹ˆˆˆŠ‹ŒŒŽŽ‹ŽŽ‹Ž’Œ‹ŠŒ‘‘’“””–•˜™œœŸ›œœœœŸŸ ¡¤¤£¢Ÿžžœž£¢£ £££££¡ Ÿ Ÿ ¡¢ š—”’†~n`VJGEHIMQUZXSRRQPQS^fx—¤¥¦ œŒ{qehl{Œ“Ÿ£¥§¨¦¤¤¡¡œœšŸŸŸ¡¡¡Ÿžž ¢¥¦¨¨¨¥¨¨«¬¬ª¨§§£¤£¦¦§§§¦§¨¨¦ª§©«¬¬©°±±®®¯®³±±±¯¯¦ªª¬¬¬¬«¥¥¢£¤£¢¢¡£¤¥¦¬¤¤¥¤£££¢Ÿž£¥§¨¦§¨¨¦¨§¨¦§¢¢¡¢£ªª©§§¦©¨©¦¦££¦¦¢¡¡£¤¥£¦¦¨¥¤ ž™˜——˜˜˜—˜˜š›™•’‘‘‘’“’’’‘‘“”’‘’ŒŒŽ‰ŒŒŽ‹ŠŒŒ‹‹Žˆ‰ƒ‚‚†„……ƒ~‚„……ŽŒŒŒŠŠ‰ŠŠŒ‹‰‹ˆ‡‡†„„‚‚~ƒ…†…„„„ƒ‚‚‚‚‚……ƒ‚‚ƒ€}xxx†‘‘‘’’”“””—–—““’–”‘ŒŒŽ‹ŠŠˆŠ‹ŒŒ‹ˆˆ‰‰‰‰‡‰†‡‡‰‹‘ދЉŒ’‘’Љ†„‚…‡‰†…„†…ˆŠŒ‹‰‹ŒŒ‹‰ˆˆ†‡†‡‡Œ‹ŒŽ‹Œˆˆ‡‹Š†‰ˆ†‚ƒƒ†…ˆ‰ˆ„‰ˆŠ„‹†…„„„…„‡Š‡‚Œ—™œœ¡¢£¢¥ž§¢¦£¤ ¢ ¡¡ ŸŸ¢££¢£££¡ž›ššœš™–˜˜—”‘‹xƒˆˆˆ…€~w{yxvyxqklljhhieacefg‹‹‹‹‹Ž’ŒŽŠ‘ŽŒŽ’‹‰‰‹“”™™˜——˜œ¢žŸ¡œŸ¢Ÿ£¢§£¥¢¡¡ Ÿ ›£¡¦¢¢¡¡ ¤¢¤¤¨££ ¡¡¦¦§§¨¢¥¤¤Ÿ šš•~seWNDEGIMQUX\YWSVONRWdr€•Ÿ¨«œ–†yqhlt˜›¤§¬¬±©«§ª¡£žœ££¥¥¥¦¥¥¥¦¨§ª¦¨©©§¬¨²¬¬«ª§§¥¤¥¦§ª©©§¬©«§¯©«¯¬©®±¶µµ±°³±´®®®°«²¯¯®°«ªª«¥¥¤ª££¡¥¤£¤¦§¬¤¦¤¤£©¡£ ¡¤««¬ª®¦ª©ªª¬©ª¦¦¥££¥§¬ªª§ª©®¨©¦§¥¤¥§¡¡¡©¥¦¥©¨®¤¢žœ˜žœœš›š›¡˜›™—’’•“˜—–––’“’’’™–—’“ŽŒ‘Œ‹ŒŒ‹ˆ‰ƒ‚ƒ‡††ƒ€€‡‡ŠŠ‘Œ‰Šˆ‘ŽŽ‹†‡„……„…†‡‡‚€€ˆ†Š…‡ƒƒƒ‚ƒ„„†„„ƒ‚ƒ~{{{ˆŽ—‘“’’–’™˜˜”œ˜—–š•˜“Ž”’‘Ž‘‹‹ŠŒ‹‹‘“’“ŠŒ‰Ž‡‡ˆŒŒ’ŽŒ‹‘”””‘Œ‹…„…‹Œ…†…ˆ†‰‹ŒŒŒŒŒŒŒŒˆˆˆŽŽŒŽŽŒ‹‹ˆŠŠˆ†ŒŒ†‰ˆˆ‡Š‰Ž‡‰ˆ‹‹‹…†„„„†‡ˆ‡†”œœ›¢¢£¤¥¦§¥¥¤¥¤¦¢ ¤ŸœŸ¡¦¢£¤¨¢¤Ÿ›Ÿž¡œžœ¥žž——Žrq|Šˆˆ„~|{{||€upillomslfdcchi‰‰Š‹’‹Š‰‹Š‹ŒŽŒŒŠŠŠ“•™—˜˜ššœœžžžŸ Ÿ¡¡¡Ÿžžžœ ›¡ ¡Ÿ££¤¡¢¡¡¡£¡£ž¡¡¥¢¡Ÿžž›™•ˆrf\OKEHJORUY[][YTQPMU[k{Œ £¦¤¥—‹€tspy‰•›¡¥ª¯¯¨§¤£ ŸžžŸ£¦©©¦¥¤§¨©¨¨¨¨¥¨¨©§¨§¦££¤¥¥¦¨¨¨¦£¥¥«©©§ªª¯¯««ª®®²²´°°±®©©©¬«¬¬¯°¯ª«¦¤£££¤¢¢¡£¢¤¦§§§¤¥¥¥¢¡ž¢Ÿ¦¬«©ª«¯§ª©ª©©¦¦¤¦¦¦¥¨ª«¦¦¦©©©¨§¦§©¥¥¤¢¢£¨¦¥£¨¦¦¢ ˜šœ›››š™—–”“’””””’‘ŽŽŽ’’“”š–”Š‹ŠŒŽ‹Œ‰‹‹Š‹ˆ‰ƒ„…ˆ†€|‚‡ˆ‰ŒŒŽŠŒ‰‰‰ŒŒŠŠ‰Š…ƒ„…„‚ƒ~€„†„‚ƒƒƒ€ƒ…††…ƒ„‚}zz{€…Œ‘‘’’‘‘”’”––•›˜˜˜™••‘‹”•“ŒŒŠ‹‹Œ‘’‘ŠŒ‰‰ˆˆ‰Šˆ‰Œ‹ŽŽŒŒ…†‡‹‹„……†„Š‹‹‹‹‡ŒŠ‹ŒŒ‡ˆ‡ˆ‰‰‰Œ‹ŒŒ†‰‰‰‰‹†ˆˆˆ†Ž‹‹‡ˆˆ‰‡‰Š‰†‰‡‹Š‹„ƒ‚‚‚‡‰‹…‡‡—œœ››¡¡£ ¡¡ ¥££¢££¡ž ŸžŸœž¤¡¢¢¡ Ÿœœ›œšŸžžž—˜{k‚Љ‰†‚z||}€~qpiikkknmkhfaab‰‰ŽŽŽŠˆ‰Œ‹Œ’‹‹Œ“”™™š˜š››œŸž¡§¤¤¢¡Ÿ¡ žž žžŸ Ÿ£¡¤¤ª¢¦¢¨¤¤¤£¤¦¢¤¢£ Ÿœœ‘~wh[TKGEIOSYZ[]_][SPOPYbsˆ“¦¢¬¥¦’†~usv~”œ¢¢¨©¯¯¯§¥£¡ŸŸŸ¥¢¨¨®ªª¨©ª¯³«©ªª«¬¬¬¦££§¤§¥©¨ª¦¥¤§§ª©«ªª±°µ«®¯®¯²²´µ¸°²¬¬¬¬±³°±²¯©¬¦¤¢¥¦¦¥¥¢¥¥¦¨ª©©©ª¨¦Ÿž £¦±«®¬¬«²¨ª©©©¥¦¨¬¨«§¬ª¯¤§§¯©ª§¦¥¥¤¥¥¥¥¦§¯§ª©¨¦§¡ Ÿžžž¢¡››™—’•”””œ——‘ŽŽ•–Ÿ”š•”ŒŒŽ‘š’‹ŽŽŽŒ‹‹‹Š‡Ž†‰€†€ˆŠ“‘Œ‹ŒŒŠŠŒ‰Š„‡†‡„„€}}‚„„ƒˆ‚‚‚…„‡‡‰†ˆ‚||{{€‡ˆ’‘“™‘–’˜••–™™š˜™šŸ™œ’“““’‘“™——’•ŒŽ‹›•™‘’Œˆˆ‘Ž’Ž•‘‘’ŒŽ‰ˆŠ’ŒŠƒ†‡ŒŽ‘Œ‹Œ‹Œ‹‹‹Œ‰Šˆ‰‹ŒŒ‹ŽŒŒŒ‡‡‡‰‰Œˆ‡‡Œ‰“ŒŽŽŽˆˆ‡Š‡ŒŠŒ…ƒ‚†„ˆˆŠˆŒ’˜¡œœ› ¡¤¡£££¢¥¤¦¦¦¦¦¡¡¡¢ž›¤¤¤¡©¤«£¢œ›œ¡ž¤žŸš™’‡†ˆŒ††|~~|ophfhjlomronc`_‰Š‹‹‹‹Žˆ‰Š‹‡‰‹ŒŒŽ‘”––™™™—››››œœžœ¤ ¡¢ŸŸœšš›Ÿœ›žŸŸ£ ¤¡¢ž Ÿ¢¤¤¤¤¤§ ¡¢¡Ÿ˜——‹sf]QNKJHLPTZZZ[]\ZTNQS`k|˜¢¡¢ {ry}†“˜œœœŸ ¢£§¨©¥¤¢¡ ££¥¦§§©ª«¯®¯©«©¨££¢¢¢§¤¦§ª¢¤¤§§©¥§©ª§¬¬¬°±²²²²²°«¬ª®±««ªª«ª§¨§§££¡¤¥¥¥¤¢¥¦©«ªª§¦£žž›Ÿ¡¨«««««ªª±¨ªªª§§¥¨«¬§¨§¬©¨¤§¨¨©¨¦¥£¤¤¤¢¥¥§¨°§§¦¥¢¡ ŸžœžžŸ › —–’’“‘””—•’ŠŽ“”••“’ދޑ“”‹‡‡ˆŠˆ‰‡Œˆ‰…†…އ„€ƒƒ„†‰‹ŽŽŽ‹Œ‹‹‹‹‰‰‹‡‰ˆ‡ƒ‚†‡†…‚~~|}~ƒ……„„‚‚‚†„„…„……‚}xxv~…ˆŠŽ‘‘’—•–—˜”–—————–“’‘’’“”•–˜ŒŒŒŒ“”“ŽŒŒ‡ŒŽŠ‘ŒŽŽŽ‘ŒŒŒ‰‡„ˆ‰ŒŒ‹ŒˆŠ‹Œ‹‰ƒ‹‰‰ˆ‹ŒŒŒŠ‹Œˆ‰ˆ‡‡ŠŠ‰†‰‰ŽŽŒ‰Ž‰ˆ„‡‡ˆ†ˆˆ‡„ƒ€…„‰‡‡ˆ––™›šœœ›œž™¡¢¢§§¨©¦£¢¡¡žŸŸšœ £¡¢£¢žœœ›žžž›š•ŠŒŽ‘Œˆˆ‚€‚}uljee`klmmqsqfb^‹‹ŠŽŠŒŒ‰‰Œ’‘‘‘”˜˜žšŸ™š™œœ ¡¢¢¢£ ¢žœš™šœŸ¡ž£¢£££¤¥¥¦¢Ÿ£¤¨¨¨¦¨¡¡ žžž””„yj]TKKJJMPWV[[\\\YURPTYgu†™ž«¤¨›™Œ‚~w{‡—›¤ ¡¡¢¨¨«¥§¢¡¡¥¢¨¨¦¥¥¨¯°·±µ°²¯µ´µ®µ°©©¤§¨©©¨¦©§«¥§¨¯¨¨¦¦§ª©®®®¯´°·±µ´´±°¯¬¬³±³¯³ª¬¨©¦«§¨¢¦¤¤¥©¥©¨©ª±°©¨¢ ¡¢¤§³²²ªªª««°«®¬¬©«ª¬¬¬©¬©§¦§¨¨¬¦¦¥¨§§¦¦¥«¨²§¨¤¤£¢ ¤¡ Ÿ¢œ›ž–—‘““—•˜™š•’’˜–š•’‘ŒŽ™˜˜‰††ˆˆŒ‡ŽŒŽŽ‰‰‡ˆ…„‰ŠŒ‹‘‘’ŽŽŒŒ‹ŒŒŒŠˆ‡‹‡Žˆ‡‚†„……„‚‚}}„…‰†ˆ…Š…†…‡†…„…{wx}†„Œ‘”’“”˜—™˜™•————–•‘’’”“›—˜™™’’ŒŽ’Ž‘‘’‘““–Ž’‹‹‘‘‘”““’’•ŽŒ‹‰‡†Œ‰ŽŽŠŒ‡Œ–Šˆ…ЉŒ’Ž’‰ŽŒŠŒˆˆŠŽŽŒŽ‘ŽŠŽ‰ˆ‡Œ‰‹†‡‡‡‡‡ƒ†„‰‰‡‹“•™šž¡Ÿœ ˜›Ÿ¤¡©¦£§ª¤¥£§¡¢¢¢¡ œ Ÿ£¢¦¤©¢£œŸ››™•••“™‘ŒŒ‘ˆ‡ƒ…xpjhedekklnvssjfe‹‹‹Œ‰‰‰ŒŒŠ‹’“—˜˜™˜——˜—›œ ™›š›œ¡žžžšššššœžžŸžŸ ¢¡£Ÿ žžžž¢Ÿ ¡£¢£££¡Ÿ›˜–†{nbVQIKLORUWVWYYXXUSRPZaqŽ¡ ž˜Š…Š‘˜›œœœ›š™Ÿ¢£¤¤¦¤£¡ ŸŸžžŸ§ª°²´¯°°±°³²´®¬¦¤¡§©©§§¥¥¥¥¥§¨©¦¨¦¦¦¨©¯¯¯¯°®¶±µ´±®®¬¬®®¯¯¯¬ª©¨¥¥¦¦¤£¢¤£¥¥¥¦¦¦©¬¬ª©£¢žž¢¤¨«¯±±ªª¦¨©«¨ª©©§ª©«¨¨¨«©¨¤§¨¨¦¦§¨§¦¤¨¦¦¥¥¦©¨©¤¢ ž ž¤¢¢¡¡ ¡šš™ž–•’“”••™™š”’“˜–˜’“ŒŽŽŒŽŽŒ‹ƒ}€ƒ‡‰‹ˆŒŒŽŒŠ‰‡‡‡…„ŠŒŒŒ‘ŒŒ‹Šˆ‡†‰‹Œ‰Œ‰ˆ‡ˆ‡Šˆˆ‚‚€…ƒ‚‚‚‚€ƒ„††„…„‡„……………ƒyzv|†…†ˆ‘’“‘••˜™™˜˜”•——“”•”‘’”•˜––“’’’ŽŽ‘ŽŒ‘’’”Ž‹‹‹ŽŒ‘‘“‰‹‡‡‡ŠŠ‘Љˆ‹‹ŽŠˆ…ˆˆŒŒŽŽ‹‰‹Š‹ˆˆ‰Š‹Ž‹Œ‹‹ŽˆŽ†ˆˆ‰‰‹†‡ƒƒ‚‚‚ƒ‚‚‚‡Œ“•™˜žžŸ›žœœšš—œž¡ –¢¤¤¤¢¢ ¢¢¡Ÿ ›››ŸŸ ¢¡¢ ž™››ž˜’”“”‘‘ˆ†„{pmgfdgjlmmpppmjhg \ No newline at end of file diff --git a/tutorial/tracking/model-based/hybrid/teabox.ppm b/tutorial/tracking/model-based/hybrid/teabox.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b20630a2d581c73b63e358e21c3892c4dbc9897f --- /dev/null +++ b/tutorial/tracking/model-based/hybrid/teabox.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +320 240 +255 +GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^NNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVVNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEENNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNEEE============EEEEEEEEEEEEGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^VVVNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEGGGGGGGGGEEEEEENNNNNNNNNNNNNNNNNNNNNNNNNNNEEE===333333333333333333333333333333333333===EEEGGGVVVVVVNNNEEEEEEGGGGGGNNNVVVNNNNNNVVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^NNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEENNNNNNNNNVVVVVVNNNVVVNNNGGG333===EEEGGGGGGEEE===333---""""""---===GGGVVVNNN333333===333EEEVVVNNNEEEVVV^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^VVVNNNNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVNNNNNNVVVVVVNNNNNNEEEEEEGGGNNNNNNNNNNNNNNNEEE333"""---EEENNNGGG333------333===VVVVVVGGGGGGVVVfff^^^fff^^^fffffffffffffff^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVVVVVVVNNNVVVVVVNNNGGGVVVVVVVVVVVVVVVVVVVVVGGG333""""""333EEE333"""---"""---===VVV^^^VVVGGGVVVffffff^^^fffffffffffffffffffffNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fffffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEVVVNNNVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNGGG===---"""""""""""""""---EEEVVVNNNGGGEEE^^^^^^ffffffffffffffffffffffff^^^NNNVVVNNNVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVNNNNNNGGGNNNVVVVVVNNNNNN===---""""""""""""---===EEE======NNN^^^ffffffffffffffffffffffffppp^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffffffffff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEVVVVVVVVVVVVVVVVVVNNNEEEEEEEEEGGGGGGEEE---""""""333---""""""333---""""""---333EEEGGGNNNfffffffffffffffffffffffffffpppffffffNNNNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEVVVVVV^^^VVV^^^VVVNNNEEE===333===333""""""---333---===333===---"""------""""""333EEEGGGVVV^^^fffffffffffffffffffffpppfffpppfffppp^^^NNNNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEE^^^VVVVVV^^^VVV^^^VVVEEE333------"""""""""""""""""""""---EEEEEE======GGGVVV^^^ffffffffffffpppfffpppfffpppffffffGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^^^^^^^VVVVVVVVVEEE333---"""""""""""""""333333---""""""333NNN^^^ffffffpppfffpppfffppppppppp^^^NNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^VVV^^^^^^VVVVVVGGG===---"""""""""---""""""333EEE333"""---VVVpppfffpppfffppppppfffpppffffffGGGNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEE^^^VVV^^^^^^^^^^^^VVVVVVGGG===""""""""""""EEEEEE---"""333EEEGGG---"""333^^^fffpppfffppppppppppppfffppp^^^GGGNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^^^^^^^VVVNNN===------======"""""""""---===VVVGGG------======"""GGGfffpppfffpppfffpppppppppppppppfffGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^fff^^^^^^VVVVVV==="""333GGGGGG===333333EEEVVV^^^GGG---333---^^^pppfffpppppppppfffpppppppppffffffGGGGGGVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffpppfffffffffppppppfffpppfffpppffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffff^^^fffffffffffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEGGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^NNN---"""======EEE======EEENNN^^^^^^===GGGfffppppppfffppppppppppppppppppppp^^^GGGGGGNNNVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffffffffpppfffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fffffffff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^VVVEEE333333======EEEEEEGGGVVV^^^^^^333333^^^pppppppppppppppppppppppppppppppppfffEEEGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^ffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffffffffffpppfffffffffpppffffffffffffpppfffpppfffppppppfffpppfffpppffffffffffffffffffffffffffffff^^^ffffffpppffffffpppffffffffffffffffffffffffpppffffffffffff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEGGGGGGGGGGGGGGG^^^fff^^^ffffffffffff^^^fff^^^fff^^^VVVGGGEEE======EEEEEEGGGVVVVVV^^^NNNEEE"""===---"""NNNppppppfffpppppppppppppppppppppppppppfffEEEGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^fffffffff^^^^^^ffffffffffff^^^fffffffffffffffpppfffpppfffpppfffffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffffffffffpppfffpppffffffffffffffffffffffffpppffffffpppfffffffffffffff^^^^^^ffffffffffff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGfffffffff^^^fff^^^fffffffffffffffffffff^^^VVVNNNGGGGGGNNNVVV^^^^^^fff^^^^^^GGG---333VVVNNNEEE===EEENNNpppfffppppppppppppppppppppppppppppppwwwfffEEEEEENNNNNNNNNVVVNNNVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffpppfffpppffffffpppffffffffffffpppfffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGG^^^fffffffffffffffffffff^^^fffffffffffffff^^^fff^^^^^^fff^^^fffffffffffffff^^^NNNGGGVVVffffff^^^^^^ffffffppppppppppppppppppppppppppppppwwwwwwwwwfffEEEEEEGGGNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^fffffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffff^^^ffffff^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffffppppppppppppppppppppppppppppppppppppppppppwwwpppwwwpppwwwfffEEEEEENNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEfffffffffVVV^^^ffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwppp======GGGNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppffffffpppfffffffffffffffppppppfffpppffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffGGGNNNffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppfffpppfffpppfffVVVVVVfffpppppppppwwwppppppwwwpppwwwwwwpppwwwwwwwwwpppfff======GGGNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffpppffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffppppppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffffffffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffppp^^^===VVVfff^^^^^^^^^^^^fffffffff^^^^^^^^^fff^^^ffffff^^^^^^fffffffffpppffffffffffffGGGGGGpppppppppwwwppppppppppppffffffwwwwwwwwwwwwwwwwwwfff======GGGGGGNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ffffff^^^fffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEffffff^^^===VVVVVVNNNVVVVVVNNNNNNfffVVVNNNVVVNNNGGGVVVfffNNNVVVVVVVVVNNN^^^fffVVVNNN^^^VVV===GGG^^^VVVppppppVVV^^^^^^^^^VVVpppwwwwwwwwwwwwwwwwwwfff======GGGNNNNNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffppppppfffpppfffpppfffppppppfffppppppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffffffffpppffffffffffffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEGGGEEEEEEEEEGGGGGGppppppVVV===NNNEEEVVVfff^^^GGGNNNVVVEEEVVVfff^^^EEEGGGNNNGGG^^^fffVVVEEE^^^NNNEEE^^^ffffffEEENNNVVVGGGfffVVVGGGfffpppfffpppwwwwwwwwwwwwwwwwwwwwwppp333===EEEGGGNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffffffffff^^^fffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppffffffffffffpppfffppppppfffffffffpppffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEGGGEEEpppfffVVV===GGG===fffpppfffGGG^^^NNNGGGfffpppfffEEEGGGEEENNNppppppVVVEEE^^^EEEGGGpppppppppEEEGGGVVVEEE^^^EEEVVVwwwwwwwwwwwwwwwwwwwwwfff333333EEEGGGNNNNNNNNNNNNVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fffffffff^^^ffffff^^^^^^fff^^^^^^fff^^^ffffff^^^fffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffppppppfffpppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppffffffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEGGGppppppNNNEEENNNGGGfffwwwNNNfffVVVVVVppppppfff===NNNEEENNNfffpppGGGGGG^^^===VVVppppppfffEEENNNNNNEEEVVVEEE^^^wwwwwwwwwwwwwwwwwwwwwppp333333GGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffpppfffpppfffpppfffppppppffffffpppfffpppfffffffffffffffpppffffffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGppppppNNNNNNfffVVVfffpppNNNwwwwww^^^pppfffVVV===VVVVVVNNNfffpppEEENNNfffGGGNNNfffppp^^^===NNNNNNGGG^^^GGG^^^pppwwwppp^^^wwwfff333333EEEGGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffpppfffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffppppppfffpppfffppppppfffpppffffffpppffffffpppfffffffffffffffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEpppppp^^^fffppppppppppppffffffwwwwwwffffffNNN===^^^fffffffff^^^NNN^^^ppp^^^^^^^^^^^^NNNEEE^^^VVVVVVpppVVVVVVffffffVVV^^^wwwwwwfff---333EEEEEEGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppppppppfffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGpppppppppppppppppppppwwwfffpppwwwwwwpppfff^^^VVVGGGpppwwwppppppwwwpppwwwwwwppppppfffffffffppppppppppppwwwppppppppppppfffwwwwww^^^------===EEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^fff^^^^^^ffffff^^^ffffff^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffppppppffffffppppppfffpppfffpppfffpppfffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGppppppwwwpppwwwwwwppppppppppppwwwwwwpppfffppp^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww^^^------======EEEEEEGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffffpppffffffpppfffpppfffpppfffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppfffpppffffffffffffppppppffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGpppppppppppppppppppppppppppwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww’jj¼@@äú��ú��ú��ú��†††^^^""""""333======EEEEEEEEEEEEGGGGGGGGGEEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppfffffffffpppfffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwú��ú��ú��ú��ú��ú��ú��††††††VVV"""---===============EEE===EEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffpppwww‘‘‘––––––†††www^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^ffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffffffffff^^^fff^^^fffpppfffpppfffpppfffpppffffffffffffpppfffpppffffffffffffpppfffffffffppppppffffffpppffffffpppfffpppffffffpppffffffpppfffpppffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffpppffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEGGGEEEEEEEEE^^^^^^fff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffpppfffppppppppppppppppppppppppppppppwwwã´88Œccú��ú��ú��ú��wwwwwwwwwwwwwwwVVVEEEGGGGGGNNNNNNNNNGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVVVVVVVVVV^^^^^^fffpppwww†††‘‘‘‘‘‘–––ªªª°°°¶¶¶¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇǰ°°‘‘‘ppp^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fffffffffpppfffpppfffffffffffffffpppffffffffffffpppffffffpppfffpppfffpppfffpppfffpppffffffppppppfffpppfffpppfffpppfffffffffpppfffpppfffffffffpppfffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppffffffffffffpppffffffpppppppppfffpppppppppú��ú��ú��ú��pppppppppfffppppppfffpppfffVVVVVVú��ú��^^^^^^fff^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGNNNNNNVVV^^^fffwww‘‘‘ ªªª°°°¶¶¶ÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇÇÇÇǰ°°†††ppp^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffpppffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffffffffpppfffpppffffffffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^VVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppppppppppppppú��ú��ú��ú��wwwppppppwwwpppppppppppppppffffffú��ú��pppfffpppfffppppppfffpppfffpppppppppfffpppfffffffffffffff^^^^^^VVV^^^VVV^^^^^^^^^fffppp††† ªªª°°°ÁÁÁÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ××××××ÎÎÎ×××ÎÎÎ××××××××××××ÎÎÎ×××ÎÎÎÎÎÎÎÎÎÎÎÎ××××××ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇǰ°°†††fff^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffffpppffffffpppfffpppfffppppppfffpppfffffffffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffpppfffpppffffffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffff^^^ffffffffffffpppfffffffffffffffpppfffppppppfffppppppppppppú��ú��ú��ú��pppwwwpppwwwwwwwwwpppwwwppppppfffú��ú��fffpppppppppppppppppppppppppppppppppwwwpppwwwwwwpppwwwwww†††––– ªªª°°°¶¶¶ÇÇÇÎÎÎ×××ßßß×××ßßß×××ßßß×××××××××××××××××××××ßßß××××××××××××××××××××××××××××××ÎÎÎ××××××ÎÎÎ×××ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÎÎÎÁÁÁªªª^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffffffffpppfffpppfffpppfffppppppfffpppfffpppfffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffffffff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffpppffffffppppppfffpppfffpppppppppú��ú��ú��ú��pppwwwwwwwwwwwwwwwwwwwwwpppppppppú��ú��pppppppppppppppppppppwwwpppwwwwww‘‘‘–––ªªª°°°ÁÁÁÁÁÁÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß××××××××××××××××××ÎÎÎÎÎÎÁÁÁ¶¶¶––– ÁÁÁÇÇÇÎÎÎ×××××××××××××××××××××ÎÎÎÇÇǶ¶¶ pppVVVNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^ffffffffffffffffffpppffffffffffffffffffpppffffffpppffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppfffffffffpppfffpppffffffpppfffpppffffffffffffffffffffffffpppffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffppppppfffppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwwwwwwwwwwwwwpppfffú��ú��pppppppppwwwwww†††‘‘‘ ªªª¶¶¶ÇÇÇÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß×××ßßßßßßßßßßßß×××××תªª†††fffVVVNNNfffppp ¶¶¶ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÁÁÁªªªwwwVVVGGGGGGNNNNNNGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffpppfffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffff^^^ffffff^^^fffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppppppppppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ÇÇÇ×××ßßß×××ÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎßßßßßßßßßßßßÎÎÎ×××ßßßßßßßßßßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß××××××ÇÇǶ¶¶–––pppVVVGGG===EEE^^^pppffffffppp†††°°°ÎÎÎ××××××ßßßÎÎÎÇÇÇÎÎÎÎÎÎÇÇǰ°°†††NNNEEEEEEGGGNNNGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffpppffffffffffffffffffpppffffff^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffff^^^fffffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppú��ú��ú��ú��wwwpppwwwpppwwwwwwú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ßßßßßßçççßßß¶¶¶†††–––www‘‘‘ÎÎÎßßßßßßÇÇÇ ÁÁÁ×××çççßßßßßßçççßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÎÎζ¶¶ fffNNNEEE===333=========------333===NNNwww‘‘‘wwwwww†††ªªªÎÎÎ×××ßßß×××ÎÎÎÇÇÇÇÇÇÁÁÁ°°°–––NNN======GGGVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffpppppppppú��ú��ú��ú��wwwpppwwwpppwwwpppwwwwwwpppwwwpppú��ú��ÇÇÇççççççççççççççççççßßßßßßçççÇÇÇppppppfff‘‘‘×××çççßßßÎÎΪªª–––ÎÎÎßßßßßßßßßçççßßßçççßßßßßßçççßßßçççßßßçççßßßßßßßßßÁÁÁ wwwNNN===333333---333333333333333333==================GGG^^^www†††††††††¶¶¶××××××ßßß××××××ÎÎÎÁÁÁ°°°–––NNN===333GGG^^^ppppppfffffffff^^^^^^^^^VVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEE^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffpppfffpppfffppppppú��ú��ú��ú��pppwwwpppwwwpppwwwpppwwwwwwppppppú��ú��VVVÁÁÁçççççççççßßßçççççççççßßßßßß‘‘‘VVV^^^ffffffÁÁÁßßßçççßßßÎÎΪªª†††ÇÇÇßßßßßßßßßççç×××ÎÎÎÇÇÇÎÎÎ××××××ßßßßßßßßß¶¶¶wwwGGG333---------------===GGGNNNNNNVVVNNN^^^pppppppppfff^^^^^^^^^VVVVVVppp‘‘‘‘‘‘†††ªªªÎÎÎßßßßßßßßß×××ÎÎΰ°°ªªª–––VVV===333===NNNpppwwwwwwwwwwwwwwwpppwwwpppfffpppffffff^^^^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffppppppppppppfffú��ú��ú��ú��pppwwwpppwwwwwwpppwwwwwwpppwwwpppú��ú��===NNN–––×××ççççççççççççççççççßßß°°°^^^wwwwww†††ÎÎÎßßßçççßßßßßßÎÎÎÎÎÎÁÁÁ×××çççßßß×××ÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎ×××××ב‘‘NNN===333333333===333333333===GGGfff††††††‘‘‘–––ªªª ‘‘‘wwwpppfff^^^fffVVVVVVfffpppppp†††°°°×××ßßßßßßßßßßßß×××°°°ªªª–––GGG333333===EEE^^^wwwwwwwwwwwwwwwpppppppppfffffffff^^^^^^VVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^fff^^^fffffffff^^^fffffffffffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��pppwwwwwwwwwpppwwwwwwwwwú��ú��===EEEGGGwwwÎÎÎïïïçççççççççççççççÇÇÇ‘‘‘^^^pppppp–––ÇÇÇççççççßßßçççßßßÎÎΪªª°°°ßßßçççßßßßßßçççßßßçççßßßÇÇÇfffVVVGGGEEEGGGVVV^^^^^^VVV^^^NNNGGGGGGNNN^^^fff^^^fffppppppppppppfffNNNGGGNNNfffpppfffppp‘‘‘°°°×××ßßßßßßßßßßßß×××ÎÎΰ°°†††GGGGGGVVVNNNGGGNNNfff††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVNNNVVVVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppffffffpppfffpppfffppppppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��wwwpppwwwwwwwwwwwwwwwwwwú��ú��===EEEGGGNNNfff ßßßçççççççççççççççßßßÎÎÎpppffffffNNNwwwÎÎÎßßßçççççççççßßßßßß×××ÇÇÇÎÎÎßßßçççßßßçççßßßßßßÇÇÇ‘‘‘ppp^^^VVVppp–––‘‘‘–––pppGGGNNNNNNVVVNNNVVVEEEVVVwww–––ªªª°°° www^^^GGGEEEGGG^^^^^^^^^pppppp¶¶¶××××××ÎÎÎÎÎÎÎÎÎÇÇǰ°°–––ffffffffffff^^^VVVGGGGGG^^^ppp†††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffffffffff^^^ffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffppppppfffpppfffppppppfffpppppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGGVVVNNNVVVÎÎÎçççççççççççççççßßß×××–––VVVVVVVVVpppÁÁÁßßßßßßçççççççççßßßÎÎΪªª¶¶¶ßßßßßßçççÎÎÎ××××××ÎÎΖ––‘‘‘††††††–––°°°ÁÁÁÁÁÁfffNNNGGGEEEGGG^^^^^^GGG†††¶¶¶ÇÇÇÎÎÎÎÎÎ ppp^^^===GGGppp^^^fff‘‘‘†††wwwwww‘‘‘¶¶¶×××ÁÁÁªªª––––––‘‘‘†††pppfffNNNGGGEEE333333333EEE^^^www††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppfffffffffffffff^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGG^^^NNNGGGVVVpppªªªßßßçççççççççççççççÇÇÇfffGGG^^^fffwwwÇÇÇççççççççççççççççççßßßÎÎΰ°°ÇÇÇßßßßßßçççççç××××××××××××ßßßßßß×××ßßß°°°†††wwwfffVVVNNNwww––– –––‘‘‘pppNNNEEE===EEENNNfffwww‘‘‘fffpppfff––– ‘‘‘pppwwwfffVVV^^^fffVVVGGG------------333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppwwwpppwwwwwwpppwwwwwwwwwwwwwwwwwwppp===EEENNN^^^^^^VVVVVVfff^^^†††ÎÎÎçççßßßçççççççççÎÎΖ––fff‘‘‘––– –––ÎÎÎçççßßßççççççççç×××ÎÎΪªªªªªÎÎÎççççççßßßçççßßßçççßßßçççÎÎΪªªªªª¶¶¶ÎÎΪªªwwwwwwpppVVVVVVVVVNNNGGG===EEE ªªª ªªª‘‘‘‘‘‘‘‘‘wwwpppppppppffffffpppppppppwwwffffffVVVNNNVVV^^^VVVGGGEEE===---------333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwpppppppppffffff^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppffffffVVVNNNVVVppp¶¶¶ßßßççççççççççççßßß¶¶¶–––fffVVVVVVfff°°°ßßßççççççççççççççç×××¶¶¶ªªª°°°ßßßççççççççççççççççççÎÎΖ––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppwwwwwwwww ÎÎÎçççßßßçççßßßßßß×××ÁÁÁªªªªªª¶¶¶ªªª ‘‘‘††††††wwwppp^^^VVV^^^fffVVVNNNGGG===------------333333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppfffffffffffffff^^^^^^VVVVVVVVVNNNNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppfffffffffNNNNNNNNNfff‘‘‘ÎÎÎççççççççççççççç××׆††^^^ppp ßßßçççççççççççççççççç×××¶¶¶ªªªÎÎÎçççßßßççççççßßß××××××¶¶¶°°°°°°ÇÇÇ××××××ßßßßßßßßßßßßßßßßßßçççççççççççççççççççççççççççßßßßßßßßßçççÎÎÎ×××××××××ÎÎÎ pppfff^^^ffffffffffff^^^VVVGGG===333333333=========333333333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwppppppffffff^^^NNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffff^^^fffffffffffffffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppEEEEEENNNfffpppffffffffffffVVVNNNNNN^^^www¶¶¶ßßßççççççççç××ב‘‘VVV===EEENNNwww¶¶¶°°°×××çççççççççççççççßßßÎÎζ¶¶ÎÎÎ×××çççççççççÎÎÎÇÇÇÎÎÎÁÁÁ ––– °°°ÁÁÁ×××ßßßçççççççççççççççççççççßßßççççççßßßççççççççççççççççççßßßçççßßßßßß–––wwwwwwwwwppppppffffffVVVNNNGGGGGGGGGGGGVVVVVV^^^VVV===---333333333===GGGfff†††‘‘‘†††‘‘‘†††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppppp^^^NNNGGGEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffpppffffffffffffffffffpppffffffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppEEEEEEGGGNNNffffffpppppppppffffffVVV^^^NNNfffªªªßßßççççççßßßÎÎΪªªGGG===NNNwww‘‘‘ªªªÇÇÇÇÇÇ×××ççççççççççççççççççßßßÁÁÁÇÇÇßßßçççßßßßßßßßß¶¶¶‘‘‘fffNNNGGGVVV†††ßßßççççççççççççççççççççççççççççççççççççççççççççççççßßßçççßßßÇÇÇ pppVVVVVVppppppfff^^^VVVVVVGGGEEE333333GGGffffffpppNNN333---===333EEEGGGNNNNNNfffppp†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwfffGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffpppfffppppppfffpppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppGGGGGGNNNNNNNNN^^^pppppppppwwwppp^^^VVVNNNNNN^^^‘‘‘ÎÎÎßßßççççççßßß¶¶¶¶¶¶ÇÇǰ°°°°°°°°°°°VVV–––ßßßçççççççççççççççÎÎÎ ªªª×××ççççççßßßÇÇǰ°°ªªª°°°¶¶¶°°°¶¶¶ßßßçççççççççççççççççççççççççççççççççßßßçççççççççççççççßßßßßß fff^^^^^^^^^fffppp^^^GGGEEEGGGGGG333---"""333333=========EEEppp^^^VVVfffppp^^^fff^^^fffppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwpppVVV======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwGGGGGGGGGNNNNNNNNNVVVfffpppppppppfffppp^^^VVVNNNVVVppp ×××çççïïïççççççßßßwww^^^–––¶¶¶ÁÁÁ¶¶¶ÁÁÁ×××ßßßßßßßßßççççççßßß¶¶¶ ßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßßßßÎÎÎÁÁÁªªª†††ppppppwwwwwwpppwwwppp^^^NNNGGG===---"""""""""""""""333NNN^^^‘‘‘ppp^^^VVVVVV^^^VVVNNN^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwfffEEE333333======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGGGGGGNNNNNNNNNNNNNNNVVVwwwppppppwwwwwwwww^^^VVVVVVNNN^^^wwwÇÇÇçççïïïçççççççççÎÎÎÎÎÎçççççççççÎÎΰ°°fff†††‘‘‘ÎÎÎßßßçççßßß×××ÎÎÎßßßßßßßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßÇÇÇ–––†††††††††wwwNNN===VVVppp^^^pppwwwwwwwwwfff^^^NNN===---""""""""""""---333EEENNNpppfffVVVEEE=========EEEEEENNNVVVffffffwww‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwGGG333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffpppfffpppfffppppppfffppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwGGGNNNNNNNNNNNNNNNNNNNNNVVV^^^wwwwwwwwwwwwwwwfffNNNVVVNNNfff–––×××ïïïïïïçççïïïçççççççççççç×××¶¶¶ ‘‘‘‘‘‘pppªªª×××ççççççççç×××¶¶¶ÁÁÁßßßççççççççççççïïïçççççççççççççççççççççççççççççççççßßßÁÁÁ ‘‘‘†††pppNNN333333^^^†††^^^pppwwwpppfffpppVVVEEE===333333333---"""------333===333333GGGEEE333333======EEEGGGNNN^^^fffppp†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††www^^^------333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwww††††††GGGNNNVVVNNNNNNNNNVVV^^^NNNVVV^^^pppwwwpppwwwpppVVVVVVVVVVVV^^^www¶¶¶ïïïïïïçççïïïçççïïïçççççççççßßß¶¶¶ ––– ¶¶¶ ppp°°°çççççç×××ÎÎÎßßßççççççïïïçççççççççïïïççççççççççççççççççççççççßßßÁÁÁpppfff†††††††††††††††––– ‘‘‘^^^www‘‘‘†††pppVVV†††pppppppppVVVNNNGGGNNNNNNEEE333""""""""""""""""""===NNNEEE=========333333======EEENNN^^^ppp‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††fff==="""---333=====================EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNNNNVVVVVVVVVppp^^^NNNNNNVVVVVVfffwwwpppVVVVVVNNNVVVVVV^^^fff‘‘‘×××ïïïïïïïïïçççïïïçççïïïçççççççççßßßÇÇÇ ¶¶¶çççççççççççççççççççççççççççïïïçççççççççççççççççççççßßß××××××ÇÇÇÎÎÎÎÎÎÎÎÎçççßßßÎÎÎÁÁÁÁÁÁ××××××ßßßÇÇÇ‘‘‘††††††wwwNNNpppwwwfffwwwwww^^^^^^VVVVVVGGGEEE======---------------GGG^^^^^^NNNEEE333333333333333===EEEGGGVVVfffppp†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††wwwEEE""""""333===333==================EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffpppfffppppppfffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNVVVNNNVVV^^^wwwfffVVVNNNNNNNNNVVVppppppwwwwwwVVVVVVVVVVVVVVVVVVVVV^^^wwwÁÁÁçççïïïïïïçççïïïçççïïïçççççççççÎÎÎÇÇÇ ßßßççççççççççççççççççççççççççççççßßßßßß×××ßßß×××ÎÎÎÎÎÎÇÇÇÁÁÁ°°°°°°ÎÎÎ××××××ßßßççççççßßßßßßççççççççççççßßßÇÇÇ‘‘‘fffVVV–––^^^ffffff^^^fffffffffVVVGGGGGGGGG===GGGGGGGGGGGG^^^†††^^^NNNNNNEEE333===333333======EEENNNVVVfff†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††VVV---"""---=================================EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=========EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffpppffffffpppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††VVVNNNVVVVVVVVVfffppppppfffVVVVVVfff^^^VVVVVVpppwwwVVVVVVVVVVVVVVVVVVVVV^^^VVVfff‘‘‘×××ïïïïïïïïïçççççççççççççççççççççßßß×××ßßßßßßßßß××××××ÎÎÎÇÇÇÁÁÁ¶¶¶°°°°°°¶¶¶ÎÎÎßßßßßßßßß×××ßßßÇÇǪªª––– –––––– ×××çççççççççÎÎÎÇÇÇßßßççççççççç×××¶¶¶‘‘‘––––––wwwpppfffpppfff^^^VVVVVVNNNVVVGGG^^^^^^VVVGGGGGGNNNfffwwwpppfffGGG===333333===333333======GGGVVVfffwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††ppp333"""---333===333===333========================EEE=========EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppfffpppfffpppfffpppppppppfffppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††VVVVVVVVVVVVVVVfffwwwpppwwwppp^^^ppppppNNNVVVVVVfffVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^¶¶¶çççïïïçççßßß×××ÎÎÎÇÇǶ¶¶°°°ªªª –––‘‘‘†††pppppppppfffpppwww‘‘‘°°°ÎÎÎßßßÎÎÎ †††††† ¶¶¶×××ßßßÎÎζ¶¶ªªªÇÇÇ×××ßßß¶¶¶‘‘‘–––ßßßçççççççççßßß www–––ÁÁÁÁÁÁ –––‘‘‘^^^ffffffwwwppp^^^NNNGGGEEE===GGG^^^^^^pppNNNEEE333===333333===333======NNNfffpppwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††wwwEEE""""""333333======333===333===========================EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEE===EEEEEE======EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppfffpppppppppppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††fff^^^VVVVVVVVV^^^pppppp^^^ppp^^^pppwwwfffwwwVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^fff‘‘‘ÇÇǪªª†††pppfffffffff^^^^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVVffffff–––¶¶¶ÎÎÎÎÎÎ ––– ªªªªªªÇÇÇÎÎζ¶¶°°°ÇÇÇßßßçççÁÁÁ°°°ÇÇÇççççççççççççÇÇÇfffppp–––¶¶¶ÎÎÎÎÎÎÎÎÎÁÁÁªªª––– –––†††ppppppVVVGGGEEENNN^^^www†††pppppp^^^NNNEEE===333===333333333===GGG^^^wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘††††††††††††VVV---"""---333===333===333===333===========================EEE===EEE===EEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEppppppwwwpppwwwppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘www^^^VVVVVVVVVVVVfffwwwppp^^^wwwppp†††VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^pppwwwfff^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^ppp ¶¶¶××××××¶¶¶ –––ÁÁÁßßßßßß××××××ßßßççççççßßßçççççç×××ÎÎÎ×××ÎÎÎwww†††–––ÁÁÁ×××ççççççßßß×××°°°ªªªªªªªªªªªª pppNNNEEE===VVV‘‘‘ªªªªªª‘‘‘‘‘‘††††††ffffffGGG===333=========EEEVVVwww†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††fff333""""""===333===333===333===333=================================EEEEEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEE======EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††––– †††VVVNNNVVVVVVVVV^^^www^^^www†††VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^^^^^^^fff¶¶¶¶¶¶www^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^^^^pppwww‘‘‘ °°°°°°¶¶¶×××ßßßççççççççççççççççççççççççççççççÇÇÇÁÁÁÎÎΰ°°–––‘‘‘ÁÁÁßßßçççççççççÎÎΆ††www‘‘‘–––‘‘‘wwwppp^^^^^^VVVGGGNNNVVVppp‘‘‘ªªªªªª‘‘‘pppffffffNNN===333333333333---EEE^^^www‘‘‘°°°ÁÁÁ¶¶¶¶¶¶–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††EEE""""""333333333===333333===333===333=================================EEE===EEE===============EEE=========EEEEEE===EEE======EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwpppwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘ ¶¶¶ÁÁÁ–––wwwNNNNNNVVVVVVVVV^^^^^^ppppppfff^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^fff†††°°°‘‘‘fff^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffppp–––ªªªÁÁÁÎÎÎßßßßßß××××××çççççççççççççççßßß×××ÎÎÎ×××××××××ççççççççççççÇÇdž††††† ÁÁÁÁÁÁ†††VVVGGGVVVfffVVVNNNVVVGGGVVVfffpppfffNNNEEE======333===333333333---333EEENNNppp––– ªªª‘‘‘‘‘‘ªªªªªª–––‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††VVV---"""---333===333===333333===333===333===333==============================EEE======EEE============EEE======EEE=========EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††† –––‘‘‘†††–––fffNNNGGGVVVVVVVVV^^^wwwwww†††wwwfff^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^ppp‘‘‘ªªª^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNVVVNNNVVVVVV^^^^^^^^^^^^^^^ppp†††°°°ÇÇÇÎÎΰ°° ÁÁÁÎÎÎ×××ÇÇÇÎÎÎßßßççççççßßßççççççßßßßßßßßßççççççßßß×××ÎÎÎ×××ßßßÎÎΪªªppp–––ÇÇǶ¶¶–––pppVVVVVVVVVNNNGGGEEE======333===333333===333333=========GGGfffwwwwww °°°ÁÁÁ°°° –––‘‘‘‘‘‘––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††ppp333""""""333333===333===333333===333===333===333============================================================EEE======EEE=========EEE===EEEEEE=========EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘ †††^^^GGGGGGNNNVVV^^^pppwww^^^wwwfffVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fffwww –––pppVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^ffffff^^^†††‘‘‘www^^^fffwww–––ªªª––– ªªª¶¶¶ÇÇǶ¶¶°°°ªªª°°°¶¶¶ÇÇÇÎÎÎßßß×××ÎÎÎÁÁÁÇÇÇßßßççççççßßß×××ÎÎÎßßß××××××ßßßçççßßß×××ÇÇǪªª †††pppVVVGGGEEE===333333===333333======333=========GGGfff†††ªªª¶¶¶°°°ÇÇǪªª–––‘‘‘‘‘‘––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwEEE""""""333===333333333===333===333===333333===333================================================EEE=========EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ªªªªªªwwwppp–––°°°pppVVVNNNGGGNNNVVVVVV^^^www†††‘‘‘www‘‘‘wwwfff^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^fffppp‘‘‘ªªªwww^^^VVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^fff‘‘‘www‘‘‘†††fff†††ppp^^^^^^fffwww‘‘‘ ªªªÁÁÁ×××ßßß×××ÇÇÇÇÇÇÎÎÎÇÇÇÁÁÁªªª°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇÇÎÎÎßßßßßß×××ççççççßßßçççÇÇǰ°°ªªªªªª°°°°°°ªªª †††wwwVVVGGG===333===333333===333333===EEEGGGfff†††††††††‘‘‘¶¶¶ÇÇÇÁÁÁ¶¶¶°°°–––‘‘‘–––––– –––––––––––––––‘‘‘–––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††VVV---"""---333333===333333===333===333333===333===333===============================================================EEE===EEE=========EEE===EEE======EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ ªªªffffff ¶¶¶–––fffVVVNNNGGGNNNVVV^^^fffppp†††www^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ppp fff^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^fffpppwwwwwwwwwppp††††††fff^^^^^^ppp‘‘‘ªªªÁÁÁ×××ççççççßßßççççççßßßçççßßß×××ÎÎζ¶¶ ªªªªªª––– ÎÎÎççççççßßßççç×××ÁÁÁªªª––– ªªª ––– °°°ªªª‘‘‘www^^^GGGEEE333333===333======GGG^^^‘‘‘††††††‘‘‘ ÁÁÁ¶¶¶ÎÎζ¶¶ªªª ¶¶¶°°° ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppp333""""""333===333===333333333333===333333===333333333===333===========================================================================EEE============EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘––––––ffffff¶¶¶¶¶¶ÁÁÁ†††pppVVVNNNNNNGGGVVVVVVfff†††www†††fff‘‘‘††††††fff^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff‘‘‘ªªªfff^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^fff‘‘‘‘‘‘‘‘‘ppp^^^fffVVVVVVVVVfff–––ÁÁÁÎÎÎ×××ßßßçççÎÎΪªªÁÁÁÎÎÎ××××××ÁÁÁ –––‘‘‘ ßßßççççççßßß×××ÁÁÁªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘––– ªªª †††ppp^^^GGGEEE=========EEEGGGfff†††‘‘‘††††††‘‘‘¶¶¶°°°¶¶¶ÎÎΪªª ÇÇÇÇÇǶ¶¶ ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘EEE""""""333333333333===333===333===333333===333===333333======333==================================================================EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘–––ppp^^^fff¶¶¶ÇÇÇ×××°°°‘‘‘ppp^^^NNNNNNGGGNNN^^^^^^†††www†††fff‘‘‘‘‘‘^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fffVVV^^^^^^fffwww ªªª^^^^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^^^^pppwwwwww††††††VVV^^^NNNNNNVVV^^^www–––ÁÁÁßßßßßß×××ÁÁÁÎÎÎßßßÎÎÎÁÁÁÁÁÁªªª–––¶¶¶×××çççççççççÎÎÎÁÁÁ°°°ªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘†††www^^^NNNEEE===EEENNNfff†††‘‘‘††††††°°°ÁÁÁÁÁÁ°°°ªªª¶¶¶×××ÎÎζ¶¶ ––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††VVV---"""---333333===333333333===333333333333333333333===333======333======333=============================================EEE==================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘wwwffffff‘‘‘ÁÁÁÇÇÇ×××¶¶¶–––ppp^^^NNNNNNGGGNNN^^^^^^pppwwwppp‘‘‘‘‘‘www†††fffffffff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fffffffffppp‘‘‘ÁÁÁfff^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVV^^^fff‘‘‘‘‘‘wwwpppwwwpppfffVVVNNNNNNVVVfffªªªÎÎÎßßßÁÁÁÇÇÇÇÇÇÎÎÎÁÁÁªªªªªª¶¶¶ççççççïïïççç×××ÁÁÁ°°°ªªª –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwwwwwwwwwwww‘‘‘ªªª¶¶¶°°°www===333===EEENNNfff†††‘‘‘††††††–––¶¶¶ÁÁÁ¶¶¶ªªª ÎÎÎßßß×××ÁÁÁªªª–––––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ppp333""""""333333333===333===333333333333===333333333333===333333======333=======================================EEE===EEE===EEE==================EEE===EEEEEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††wwwwww††††††‘‘‘‘‘‘¶¶¶–––ÁÁÁÎÎÎÁÁÁ–––www^^^VVVNNNGGGNNNVVV^^^ffffff‘‘‘–––†††‘‘‘^^^fff^^^fff^^^fff^^^fff^^^fffVVV^^^^^^fffffffffpppwww ‘‘‘pppfffVVV^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^fffpppwww††††††††††††††††††ppp^^^^^^NNNNNNNNN^^^ppp–––¶¶¶ÇÇÇÎÎÎÁÁÁ–––ªªª‘‘‘¶¶¶çççççççççççç×××ÁÁÁªªª –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††wwwwwwpppppppppppp–––°°°¶¶¶ VVV333333333333===EEENNNppp†††‘‘‘††††††‘‘‘°°° ªªª°°°¶¶¶ßßßççç×××ÁÁÁªªª––– ––– ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘EEE""""""---333===333===333333333===333333333333===333333======333======333=============================================EEE===EEE======EEE===EEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††wwwwwwffffff^^^^^^^^^^^^www°°°¶¶¶ ªªª‘‘‘ °°°ÎÎζ¶¶–––www^^^VVVVVVNNNNNNVVV^^^fffwww†††‘‘‘†††‘‘‘‘‘‘www^^^^^^fff^^^ffffffffffffVVV^^^fffffffffffffffpppwwwªªªÁÁÁ‘‘‘ppp^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^^^^pppwwwpppwww‘‘‘††††††www^^^NNNNNNVVVfffªªªÇÇÇ×××××××××ÎÎÎççççççççççççßßßÇÇǶ¶¶°°°ªªª––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††wwwwwwwwwpppwww–––ªªª°°°‘‘‘NNN333333333333===333======EEEVVVppp†††–––††††††‘‘‘°°°ÁÁÁ¶¶¶ÁÁÁÇÇÇßßßççç×××ÁÁÁªªª––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘VVV------333333333333===333333333333333===333333===333===333===333===333===333==========================================EEE======EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEwww††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwpppffffff^^^^^^^^^^^^VVV^^^VVV^^^^^^ppp^^^‘‘‘ÁÁÁ ––– °°°¶¶¶¶¶¶ÎÎÎÁÁÁ–––fff^^^VVVNNNNNNNNN^^^fffpppwww–––†††–––†††‘‘‘††††††fff^^^ffffff^^^fffffffff^^^^^^ffffffffffffffffffwwwÁÁÁïïïçççÇÇdž††^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^–––www†††–––‘‘‘^^^^^^VVVNNNNNN^^^ppp†††°°°ÇÇÇ×××çççççççççççççççßßßßßß×××ÎÎÎÁÁÁªªª ––––––‘‘‘‘‘‘‘‘‘††††††††††††wwwwwwwww–––ªªª°°°‘‘‘VVV333---333333===333======333======GGGVVVwww†††‘‘‘††††††‘‘‘–––ÁÁÁÁÁÁ¶¶¶ÁÁÁßßßßßß×××¶¶¶ªªª––– ––– ––– ––– –––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘––––––‘‘‘ppp333""""""333333333===333333333333333===333===333333===333===333333===333=========333=======================================EEE======EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVVVVVVVV^^^^^^VVVVVVVVV^^^VVV^^^^^^pppNNN^^^––– ‘‘‘–––°°°××× °°°×××ÇÇÇ pppfff^^^VVVNNNNNN^^^^^^ppp††††††–––††††††††††††ffffff^^^fffffffffffffff^^^^^^ffffffffffffpppfff†††ßßßïïïýýýýýýßßß www^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^^^^wwwwww††††††††††††pppVVVVVVNNNNNNVVVppp††† ¶¶¶×××ßßßßßßçççßßßßßßßßßßßßßßß×××ÎÎÎÎÎÎÁÁÁ°°° –––‘‘‘‘‘‘††††††–––°°°°°°–––VVVEEE===EEE============EEEEEEGGGGGG======EEEGGG^^^www‘‘‘†††††† °°°ÁÁÁ¶¶¶°°°°°°ÎÎÎßßßÎÎζ¶¶ªªª–––––– –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘†††EEE""""""---333333333333333333333333333===333333===333===333===333333===333333======333=======================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVVNNNVVV^^^VVV^^^VVV^^^VVVVVV^^^NNNVVVfffVVVGGGfff ††† ªªªÎÎΰ°°ÁÁÁ¶¶¶×××ÎÎΪªª–––†††wwwfffVVVNNNNNN^^^^^^fffwww––– †††††††††††††††ppp^^^ffffffffffff^^^ffffffpppfffffffffppp ïïïýýýýýýýýýýýýïïïÎÎΆ††fff^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^ppp†††–––†††wwwwwwwwwVVVVVVGGGNNNVVV^^^www‘‘‘°°°ÎÎÎßßßßßß×××ßßßßßßßßßçççßßßßßßßßßßßßÎÎÎÁÁÁ¶¶¶ –––‘‘‘†††www†††‘‘‘°°°¶¶¶ªªªfffNNNNNN^^^^^^VVVGGGGGGGGGNNNVVV^^^VVVGGGEEE=========GGG^^^www†††‘‘‘††††††–––ÁÁÁ°°°ªªª°°°¶¶¶¶¶¶××××××ÁÁÁªªª––– ––– ––– ––– ––– ––––––––– ––– ––– –––––––––––––––––––––‘‘‘VVV---"""---333333333333333333333333333333333333333===333===333===333333======333333======333====================================EEE======EEE===EEEEEEEEEEEE===EEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNfffVVVGGG†††ppp‘‘‘¶¶¶¶¶¶°°°ÇÇÇßßß¶¶¶°°°ÎÎÎßßßÁÁÁ°°°–––†††ppp^^^NNNNNNVVVffffff†††††††††‘‘‘‘‘‘†††fffffffffffffff^^^^^^fffffffffppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^www†††–––††††††wwwffffffVVVNNNGGGGGGVVV^^^www‘‘‘¶¶¶××××××¶¶¶ÎÎÎ××××××ßßßççççççßßßçççßßßßßß×××ÇÇÇÁÁÁªªª–––†††††††††ªªª¶¶¶ÁÁÁªªª^^^^^^fffpppppppppppp^^^^^^www‘‘‘†††wwwfffNNNEEE============GGG^^^†††–––††††††°°°¶¶¶ –––°°°ÁÁÁ°°°ÁÁÁ×××ÎÎÎ ––– ––– ––– ––––––––– –––––– ––– ––– –––––– ––––––––––––ppp333""""""333333333333333333333333333333333===333333===333===333333===333333===333===333===333=============================================EEE===EEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††wwwffffff^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVNNNNNNGGGVVVNNNVVVVVVfffNNNNNNfff^^^ °°° ÎÎÎ×××ÁÁÁªªªªªªÎÎÎçççßßßÎÎΰ°°†††ppp^^^^^^VVVVVV^^^fffppp†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘pppffffff^^^fffppppppfffpppfff×××ýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^fff‘‘‘†††††††††‘‘‘†††www^^^VVVVVVNNNGGGGGGVVVfff ÁÁÁÇÇÇÎÎÎ×××ßßßßßß×××ßßßßßßßßßßßßßßßßßßçççßßß×××ÎÎÎÇÇǰ°° –––fff^^^^^^VVVNNNNNNNNNNNN^^^fffppp‘‘‘ªªª †††ppp^^^VVVGGG============EEENNNfffwww‘‘‘‘‘‘††††††††††††ªªªÁÁÁ¶¶¶°°° ªªª ––– ––– ––– ––– –––––– ––– ––– –––––– –––––––––––––––†††EEE""""""---333333333333333333333333333333333333===333===333333===333333===333=========333======333=================================EEE=========EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVNNNGGGGGGGGGGGGNNNNNNVVVVVVVVVfffNNNGGGppppppVVV^^^‘‘‘ ÁÁÁ¶¶¶ÇÇÇÁÁÁ°°° çççïïïßßß×××¶¶¶†††wwwppp^^^^^^VVV^^^fffppp†††‘‘‘†††‘‘‘†††ffffff^^^ffffffpppfffpppppp–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^ppp††††††pppfffffffffppppppVVVNNNGGGGGGNNN^^^ppp‘‘‘°°°××××××ÁÁÁ×××ßßßßßßÎÎÎ×××ççç×××××××××ßßßßßß×××¶¶¶–––pppfffVVVNNNVVV^^^fffVVVGGGEEEGGGGGGNNNVVVfff‘‘‘ffffffpppVVVGGGGGGEEE===333===EEENNN^^^www†††‘‘‘††††††°°°¶¶¶¶¶¶ÇÇǶ¶¶ ‘‘‘––– ––– ––– ––– ––– ––– ––– ––– ––– –––‘‘‘VVV------333333333333333333333333333333333===333333333===333333333333333333333===333======333=============================================EEE===EEE======EEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††pppppp^^^VVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVVVVVVVVNNNNNNGGGGGGNNNNNNNNNNNNVVVVVVVVVVVVNNNfffNNNEEEwww‘‘‘^^^fff–––¶¶¶ÇÇÇÎÎζ¶¶¶¶¶ªªªççççççÎÎÎ×××ßßßÁÁÁ–––‘‘‘ppp^^^VVV^^^ffffffwww†††‘‘‘ppp††††††pppfffffffffpppfffppppppwww¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fffppp‘‘‘www‘‘‘††††††fff^^^^^^NNNGGGGGGVVV^^^www–––°°°ÁÁÁ×××ßßßßßß×××ßßßçççßßßÎÎζ¶¶ªªª–––pppffffff^^^VVVVVVNNNNNNNNN^^^^^^VVVGGGGGGEEEEEEGGGNNNNNN^^^pppwwwwwwwwwppp^^^VVVGGGEEE======333===EEEGGG^^^ppp‘‘‘†††††† ¶¶¶ÁÁÁ¶¶¶ªªª –––––– ––– ––– ––– ––– ––– ––– –––––– ––– –––ppp---""""""---333333333333333333333333333333333333===333333===333===333333===333333===333333===================================================EEE======EEE======EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^^^^VVVVVVVVVNNNNNNNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV^^^GGG333VVV†††††††††ffffffªªª×××ßßß°°°°°°ªªªçççççç×××çççÎÎÎßßßÇÇǶ¶¶°°°–––wwwfffVVVVVVffffffppp †††–––ppppppfff^^^fffppppppfffpppÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwwwfff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^pppwww†††‘‘‘‘‘‘fffVVVNNNGGGNNNVVVfffwww‘‘‘°°°ÎÎÎßßßçççççççççÎÎΪªªwwwffffff^^^fffffffff^^^^^^^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNGGGGGGEEEEEEGGGNNNVVVfffwwwwwwwww^^^NNNGGG======333======EEEGGGNNNVVV^^^www††††††‘‘‘‘‘‘††††††–––ªªª¶¶¶ÁÁÁ ––– ––– ––– ––– ––– ––– †††EEE""""""---333333333333333333333333333333333333333333===333333333333333333333===333333======333333=============================================EEE======EEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNN^^^GGG333GGGfff––– ‘‘‘ffffff ßßß¶¶¶ªªªªªªÇÇÇçççççççççÎÎÎ×××ïïïïïïßßßÇÇǪªªpppVVVVVV^^^pppppp‘‘‘††††††‘‘‘fffffffffpppfffppppppppp†††çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××–––fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff–––‘‘‘†††††††††‘‘‘‘‘‘^^^VVVVVVGGGGGGNNNVVV^^^www†††ªªªÇÇÇßßßßßß fff^^^^^^VVVNNNVVVVVV^^^fff^^^^^^fff^^^^^^VVVVVVVVVVVV^^^VVV^^^VVVNNNEEEEEEGGGNNNNNNVVV^^^ppppppfffVVVGGG===EEEEEEGGG^^^fffppp^^^NNNVVV^^^ppp†††www‘‘‘°°°ÎÎÎÎÎΰ°°‘‘‘––––––––– ––– ––– ––– ––– ––––––‘‘‘VVV"""""""""333333333333333333333333333333333333333===333333===333===333===333333333===333333================================================EEE===EEE======EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††wwwpppfff^^^VVVVVVNNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^NNN333333GGG –––‘‘‘VVV^^^°°°¶¶¶ÇÇÇ ×××ßßß××××××ïïïïïïïïïïïïïïï×××°°°†††ppp^^^VVV^^^ffffffwww‘‘‘–––†††pppfffffffffpppppppppppp ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff^^^^^^^^^^^^^^^^^^^^^^^^fffpppfff^^^^^^^^^fff†††wwwwww††††††fffVVVNNNGGGGGGNNNVVV^^^ppp†††ªªªÇÇÇ ppp^^^^^^VVVVVVNNNNNNVVV^^^^^^^^^^^^VVVNNNVVVVVV^^^ffffffNNNGGGVVVVVVNNNNNNNNNGGGEEEGGGNNNNNNVVV^^^^^^fff^^^NNNGGGVVVwwwwwwwwwffffffNNNNNNVVVpppwwwpppwwwwww °°°°°°ÁÁÁÁÁÁªªª–––‘‘‘–––––– ––– –––––– ––– ––––––ppp---""""""---333333333333333333333333333333333333333333===333333333333333===333===333333===333===333=============================================EEE===EEE===EEEEEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwppp^^^VVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV333---333NNN‘‘‘ªªª^^^NNNNNNfff ÎÎÎ ÇÇÇÇÇǶ¶¶çççïïïïïïýýýïïïïïïïïïßßß°°°†††pppfffffffffpppfffwww–––pppffffffppppppppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××× ppp^^^^^^^^^^^^fffwww††††††‘‘‘ffffff^^^^^^fffwww†††––– –––‘‘‘pppfff^^^VVVGGGGGGGGGNNNVVV^^^ppp‘‘‘ªªª‘‘‘ppp^^^^^^^^^VVVNNNGGGNNNVVVfff^^^^^^NNNGGGVVV^^^^^^^^^NNNGGGNNNNNNNNNVVVNNNNNNVVVNNNGGGGGGGGGNNNVVVVVVfff^^^NNNNNNNNNVVVffffffVVVGGGGGGVVVfffwwwffffffwwwwwwwww†††ªªªÇÇÇ××תªª‘‘‘–––‘‘‘‘‘‘––––––––– ––– ––– ––– ––––––===""""""---333333333333333333333333333333333333333333333333333333===333333333333333===333===333==========================================EEE===EEE============EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV===------===NNNfff†††VVV^^^GGG^^^ ‘‘‘ÎÎÎ××× ßßßïïïýýýççççççïïïßßßçççßßß¶¶¶–––†††wwwppppppfffppppppppppppppppppppppppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎffffff^^^fff‘‘‘‘‘‘†††††††††‘‘‘fff^^^fff––– –––‘‘‘†††‘‘‘––––––fff^^^VVVNNNGGGEEEGGGNNNVVVfffwww–––ªªªªªª‘‘‘wwwfff^^^VVVGGGNNNNNNVVVfff^^^^^^VVVNNNNNNNNNVVV^^^VVV^^^VVVNNNGGGNNN^^^^^^^^^VVVGGGEEEGGGGGGNNNNNNVVV^^^^^^VVVNNNNNNGGGNNNNNN^^^ffffffpppppppppfffVVV^^^fffwwwwww–––°°°ªªªÁÁÁÇÇǪªª‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘NNN"""""""""333333333333333333333333333333333333333333333333333333333333333333333333333===333333============333=============================================EEE===EEEEEEEEEEEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV======333---EEEVVVpppVVV†††‘‘‘VVVNNNVVV‘‘‘×××ççç°°°ßßßïïïïïïïïïÇÇÇÎÎΪªªÁÁÁßßßçççÁÁÁ°°°ªªª°°°ªªª†††wwwppppppfffpppwwwwwwwwwpppwww†††ßßßýýýýýýýýýýýýïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß pppffffffppp††††††‘‘‘‘‘‘†††wwwpppppppppfffpppppp††† ‘‘‘wwwVVVVVVNNNGGGGGGGGGNNN^^^ppp ¶¶¶¶¶¶ wwwffffff^^^^^^^^^^^^NNNVVV^^^fff^^^VVVNNNNNNNNNVVV^^^^^^NNNNNNVVV^^^^^^^^^VVVNNNNNNGGGEEEGGGNNNNNNVVV^^^www‘‘‘ppp^^^ppppppfff^^^wwwwwwwwwwwwpppppp^^^VVV^^^fffwww††† ÁÁÁÇÇÇ pppwww†††‘‘‘––––––––– ––– –––ppp---""""""---333333333333333333333333333333333333333333333333333333333333333333333333333===333===333=========================================================EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEENNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV======333333333GGGGGGEEE‘‘‘–––†††^^^GGG^^^ªªªßßß×××ïïïýýýïïïççç ªªª‘‘‘¶¶¶ßßßïïïççççççßßßÎÎÎÇÇÇÁÁÁ–––pppppp††††††www ïïïýýýýýýýýýýýýÇÇǪªª ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇdž††fff^^^ffffffppp‘‘‘–––†††‘‘‘–––fff^^^^^^ppp††† ªªª–––wwwpppppp^^^^^^^^^VVVVVVGGGGGGGGGGGGVVV^^^www‘‘‘°°°ÎÎÎÎÎΪªªpppffffff^^^VVVVVVNNNVVV^^^^^^VVV^^^VVVNNNNNNVVV^^^^^^ffffffNNNVVVVVV^^^fff^^^VVVNNNGGGNNNNNNVVV^^^ †††wwwwwwfffNNNNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEGGGVVVfffwww‘‘‘‘‘‘wwwwwwwwwwwwwww†††––––––––– ––– ===""""""---333333333333333333333333333333333333333===333333333333333333333333333333===333===333======333================================================EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV===EEEGGG===333333GGGGGGppp––––––‘‘‘EEEVVVppp ÎÎÎïïïýýýïïïïïï‘‘‘‘‘‘×××ïïïïïïïïïïïïçççÎÎÎ××××××ÎÎΪªª††††††‘‘‘ªªªªªª–––††††††°°°ïïïýýýýýýýýýýýýÎÎζ¶¶wwwßßßýýýçççïïïýýýýýýýýýýýýçççÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªppp^^^^^^fff†††‘‘‘–––‘‘‘ffffffwwwwwwfffffffffffffffpppwwwpppfff^^^VVVNNNGGGGGGGGGNNNVVVpppªªªÇÇÇ×××ÇÇǰ°°‘‘‘pppfff^^^ffffffVVVVVVGGGVVV^^^^^^^^^VVVVVVNNNVVVfffffffff^^^VVVGGGVVVVVV^^^^^^^^^VVVVVVVVVVVV‘‘‘fff^^^fff^^^NNNGGG===========================EEEGGGfff†††wwwwwwppp^^^fffppp‘‘‘‘‘‘––––––‘‘‘GGG------333333333333333333333333333333333333333333333333333===333333333===333333333333======333================================================EEE===EEEEEEEEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVV^^^===NNNfffNNN===---333NNN^^^ppp VVV‘‘‘ffffff†††×××ïïïýýýççç fff†††ÎÎÎïïï×××çççïïïïïïïïïïïïÎÎÎ××××××ÁÁÁ¶¶¶ÇÇÇ×××ÎÎÎÁÁÁ –––ÎÎÎïïïýýýýýýýýýýýýýýýççç‘‘‘ççççççwww¶¶¶ÎÎÎßßßýýýýýý°°°GGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††ppp^^^fffwww†††–––‘‘‘††††††wwwffffff^^^^^^^^^^^^wwwwww††††††www^^^^^^^^^VVVNNNGGGGGGGGGVVVfffwww–––¶¶¶ÎÎÎßßßßßß°°°†††wwwpppppp^^^VVVNNNNNNGGGNNN^^^^^^^^^^^^VVVfffffffffffffff^^^VVVNNNVVVffffffffffffwww†††^^^GGGEEEEEEGGGGGGEEE===333333333333333333333333======VVV†††ppp^^^^^^VVVVVVGGGGGGVVVVVVffffffwww†††‘‘‘‘‘‘fff---""""""333333333333333333333333333333333333333333333333333333333333333333333===333===333333===333===333===333====================================EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNNNNVVVNNNVVVNNNVVV^^^EEE^^^ppp^^^EEE333333===NNNfffppp‘‘‘fff‘‘‘–––pppVVVpppÇÇÇïïïïïïÎÎÎpppÎÎÎçççÁÁÁ×××çççýýýýýýççç°°°×××ççççççççççççïïïïïïçççÇÇǶ¶¶ßßßýýýýýýýýýýýýýýýýýýïïï ïïïÎÎÎ^^^–––wwwïïïýýý××× fffýýýïïïÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfffwwwwwwppp†††††† †††ppp^^^^^^^^^^^^pppppp†††wwwwwwwwwwwwppp^^^^^^VVVGGGGGGGGGNNNVVVpppªªªÁÁÁßßß×××¶¶¶°°° pppppp^^^VVVNNNNNNNNNNNNVVVVVVfff^^^fffffffffpppfffffffff^^^fffwww–––¶¶¶ÇÇÇÎÎΪªªpppNNNEEE=========333===333333333333333===---======EEEGGGNNNfffpppfffEEE===333333------NNN^^^^^^^^^^^^fffpppwwwppp333""""""---333333333333333333333333333333===333333===333333333333333===333===333333333===333333===333===333==========================================EEE===EEEEEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNVVVNNNVVVVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVVVVVVV^^^GGG^^^ppppppGGGEEE333333===GGGpppfff^^^–––ªªªªªª^^^NNN^^^ÇÇÇïïïÎÎÎwwwÁÁÁÇÇǰ°°ÎÎÎýýýïïïïïïÁÁÁ–––ßßßïïïïïïïïïçççßßßýýýïïïïïïßßßçççýýýýýýýýýýýýýýýýýýýýý°°°ÎÎÎÎÎÎVVV °°°–––ïïïýýýýýýßßßpppïïïßßßVVVÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††pppfff^^^fff†††‘‘‘ www†††wwwpppfff^^^^^^ffffff††††††‘‘‘^^^^^^^^^VVVNNNGGGGGGGGGVVV^^^ppp†††°°°ÇÇÇßßßßßßßßß°°° †††fff^^^^^^^^^fffNNNGGGNNNNNN^^^^^^ffffffffffffpppppp†††ªªªÇÇÇßßßßßßßßßßßßßßß××× fffNNN======333333333===333=========333333===EEE======NNNNNNGGG333"""------"""333VVV^^^^^^^^^^^^^^^^^^^^^^^^NNN===333---333333333333333333333333333333333333===333333333333333===333333333333===333333======333===333===333==========================================EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^VVVVVVNNNNNNNNNNNNNNNNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^EEEVVVwww^^^EEE======333===NNNpppppp °°°ÁÁÁ–––^^^NNNfff¶¶¶×××ÇÇÇ ¶¶¶ÎÎÎïïïýýýïïï°°°ªªªÇÇÇ×××çççýýýïïïçççÎÎÎÎÎÎßßßïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýªªªwww wwwÎÎÎÇÇÇïïïýýýïïï pppïïïÇÇÇEEEwww––––––ýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfff^^^fffwww†††‘‘‘‘‘‘‘‘‘†††††††††fff^^^^^^^^^ppp†††††††††‘‘‘†††ppp^^^^^^VVVNNNGGGGGGGGGVVVfffwww ÁÁÁßßßççç×××××× ‘‘‘†††pppfff^^^VVVNNNNNNVVVfff^^^fffwww–––°°°ÁÁÁÎÎÎ×××çççßßßçççççççççßßßçççÎÎÎ fffEEE============333333333======333------""""""------""""""333------===VVV^^^^^^^^^^^^^^^VVV^^^VVVVVVNNNGGGGGG======333333333333333333333333333333333333333333===333333===333===333===333===333===333333=========333==========================================EEE===EEE===EEE======EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^EEEGGGVVVwwwfffEEE=========333===GGGppp‘‘‘°°° ‘‘‘VVVfffªªªßßßçççÁÁÁÎÎÎïïïýýýïïïÇÇǶ¶¶ÁÁÁßßßýýýïïïýýýýýýïïï¶¶¶°°°–––ªªªïïïïïïýýýýýýýýýýýýýýýýýýýýý××׆†† www‘‘‘ppp×××ýýýÇÇÇfffªªªýýý¶¶¶=========NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇdž††pppfff^^^fff††††††‘‘‘––––––pppffffff^^^pppwwwwww†††††††††††††††fff^^^^^^^^^VVVNNNGGGGGGNNN^^^www‘‘‘¶¶¶ßßßßßßßßß¶¶¶¶¶¶ÎÎÎ××תªª†††pppfffffffffppppppwww†††ªªªÎÎÎßßßßßß×××ßßßßßßççççççççççççßßßßßßßßß×××°°°‘‘‘GGG===333------"""333EEEEEE333"""""""""------"""---===VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVNNNNNNEEE======333333333333333333333333333333333333333===333333333===333===333===333======333===333================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEENNNNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^EEEEEEGGGfff^^^EEEEEEEEE======333333EEEppp–––°°° ªªª†††VVV^^^ ÎÎΪªªªªªÇÇÇßßßÇÇÇ‘‘‘ªªªÁÁÁçççïïïýýýýýýýýýýýý×××ÎÎΖ–––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎÇÇÇppp×××ýýý wwwçççýýý¶¶¶NNN===333NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß wwwfffffffffppp‘‘‘‘‘‘†††‘‘‘††††††wwwfff^^^fffffffffppppppppppppppp^^^VVV^^^^^^VVVVVVVVVGGGNNNNNNVVVppp‘‘‘¶¶¶ßßß×××¶¶¶ÎÎÎßßßßßß×××ÇÇÇ ††† ªªª°°°¶¶¶ÁÁÁÇÇÇßßßççççççßßßßßßççççççßßßççççççßßßÎÎΰ°° pppNNN===333------------"""""""""---"""""""""""""""""""""===VVV^^^VVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNGGGEEE===333333===333333333333333333333===333333======333===333===333===333===333===================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^ffffffEEEGGGNNNpppNNNEEEEEEGGGEEE======333333EEEwww°°°ªªª‘‘‘wwwNNNwww‘‘‘ªªª °°°ÇÇǰ°°°°°ÎÎÎïïïýýýýýýýýýýýýýýýïïïççç×××ÇÇǰ°°ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççýýýýýý†††NNN–––ïïï°°°^^^NNNVVV^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××׆††ppppppwww‘‘‘––––––‘‘‘†††††††††www^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^NNNNNNNNN^^^ppp‘‘‘¶¶¶×××ÁÁÁÁÁÁÁÁÁ×××ßßßÇÇǪªªppppppwwwªªª¶¶¶×××ßßßßßßßßßçççççç×××çççßßß¶¶¶ªªª‘‘‘fffGGG===333------333333---""""""""""""EEEVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVNNNGGGEEE===333333333333333333===333333===333333333===333======333===333======333===================================================EEE===EEE===EEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^VVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffGGGVVVfffpppVVVEEE===GGGEEEEEE============GGGppp††††††–––‘‘‘wwwVVV †††www ¶¶¶ÇÇÇÇÇÇßßßýýýýýýýýýýýýïïïýýýýýýççççççïïïçççßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýý×××wwwïï着ªwwwwww†††fffÇÇÇ–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýèÇÇËccÔ''êõ÷îÕ%%§TT––––––‘‘‘†††ppppppfff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVNNNNNNVVVppp‘‘‘¶¶¶ÎÎΰ°°‘‘‘–––†††ppp^^^VVV^^^^^^www ªªª ¶¶¶ßßßÎÎΰ°°‘‘‘pppGGG333---"""333GGGGGG333"""---------EEEVVVVVV^^^^^^^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVVNNNNNNEEE===333333333333333333===333333===333===333===333============333=========================================================EEE===EEE===EEEEEEEEE===EEE===EEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^VVV^^^VVVNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppfffffffffffffffppp‘‘‘^^^EEEGGGNNNNNNEEE=========EEEEEEEEEVVVwwwwww†††–––wwwÁÁÁ†††fffppp–––ÁÁÁÇÇÇçççýýýýýýýýýïïïýýýýýýýýýççççççïïïïïïççççççýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýý°°°–––ÎÎΪªªppp××× °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��Ù--“ŽŽ†††‘‘‘‘‘‘‘‘‘pppffffff^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^VVVVVV^^^ppp‘‘‘°°°°°°pppwwwppppppfff^^^^^^VVV^^^VVV^^^ppp^^^GGG333---------===GGGVVVVVVVVVNNN333""""""EEEVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVNNNEEE333======333333===333333===333===333===333======333===333===============================================================EEE===EEEEEEEEE===EEEEEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNNNNGGGNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffpppfffppppppfffpppfffpppfffpppwww‘‘‘ –––^^^VVVwwwNNNEEEEEE===EEEGGGVVVGGGEEEVVVwwwwwwwww °°°‘‘‘^^^^^^†††ÁÁÁßßßçççïïïïïïýýýïïïýýýïïïýýýççççççýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎïïï ¶¶¶www°°° ßßßýýýýýýýýýýýýýýýýýýýýýýýýú��ùû‰‰á³¨¨šVVéú��ú��ú��ºBB‘‘‘‘‘‘‘‘‘‘‘‘––––––†††^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^ppp†††ªªª–––‘‘‘¶¶¶ÎÎÎ ‘‘‘†††fffNNNGGG===333------------333===EEEEEEGGGVVVNNNGGGEEE333""""""""""""""""""NNNVVV^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVNNN======GGGGGGEEE===333===333333===333===333===333======333=========333=========================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^^^^fffffffffffffffffffffpppfffppppppppppppffffffppppppfffpppfffpppfffªªªwww‘‘‘‘‘‘wwwppp†††‘‘‘NNNEEEEEEEEEEEENNN^^^VVVEEEEEEVVVppp^^^fff°°°ÇÇǰ°°fffpppwww†††ÇÇÇ×××ïïïççç×××ßßßýýýïïïýýýççç×××ïïïÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççýýýßßßßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýý––––––‘‘‘GGGwww‘‘‘VVV°°°çççýýýýýýýýýýýýýýýýýýýýýúZZüïïýýýýýýïïï××ײllú��ú��ú��æ‘‘‘––– –––‘‘‘wwwfffú��ú��^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNVVV^^^ppp‘‘‘ªªª¶¶¶¶¶¶NNNEEE===---------333======EEEEEE======EEE333---333333333333------""""""""""""""""""---VVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVVVVVGGG======GGGNNNVVVNNNGGGEEE===333===333333333===333===333======333===333=========================================================EEE===EEEEEE===EEE======EEE===EEEEEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^VVV^^^ffffffffffffffffffffffffffffffpppppppppppppppppppppfffpppppppppppppppfffpppffffffpppwww†††www‘‘‘www‘‘‘†††VVVEEEEEE===EEEGGGVVVNNNEEE===EEENNNfff^^^†††ªªª†††ppp¶¶¶–––‘‘‘wwwÇÇÇçççïïïççççççïïïýýýçççïïïçççÎÎΪªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýççç××תªªVVV†††–––ÇÇÇ‘‘‘ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçÝÝú��ú��ú��ö‘‘‘‘‘‘wwwwwwffffff^^^^^^ú��ú��^^^^^^^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNGGGEEE333===EEEEEEEEEEEEEEE===333333------"""""""""---"""------""""""""""""---""""""---VVV^^^^^^VVVVVVNNNVVVVVV^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVNNN======GGGNNNVVVVVVVVVVVVNNNGGGEEE======333===333===333============333===============================================================EEEEEEEEE=========EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fffffffffffffffffffffpppfffffffffppppppppppppppppppppppppfffppppppppppppppppppffffffffffff^^^www†††‘‘‘†††††† †††^^^EEEEEEEEEEEEEEEGGGGGGEEEEEEEEE===GGG^^^VVVfff–––www¶¶¶°°°^^^fff ×××ÎÎÎçççïïïïïï×××ßßßïï着ª¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××www–––†††ÁÁÁçççfffVVVªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýûÞÞú��ú��ú��ôwwwpppffffff^^^fff^^^^^^ú��ú��^^^^^^^^^VVVVVVNNNNNNGGGGGGGGGGGGNNNGGGNNNGGGEEEEEE=========EEEEEEGGGGGGGGGGGGEEE===333333------""""""""""""""""""""""""""""""---------333"""""""""------""""""===VVV^^^^^^^^^NNNEEEGGGVVV^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVVVVVGGGGGGGGGVVV^^^^^^VVV^^^VVV^^^VVVNNNGGGEEE===333===333======333===333======333=========333==============================EEE=========EEE===EEE===EEE=========EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwpppwwwpppppppppfffffffffffffffpppppppppfffppp‘‘‘†††ppp––– fffGGGEEEEEEEEEEEEEEEEEENNNNNNNNNEEE===GGG^^^VVVVVVfffÁÁÁÇÇǰ°°†††VVV^^^ªªªÁÁÁïïïïïïßßßÎÎÎïïïÁÁÁÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎΆ††×××çççpppfffNNN–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýúwwú��ú��ú��ò†††pppffffff^^^^^^fff^^^ú��ú��VVVVVVNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGEEE======EEEEEEEEEGGGNNNGGGGGGEEE======333------"""""""""""""""""""""""""""""""""""""""NNNEEEGGGNNN"""---333""""""""""""EEEGGG333"""EEE^^^^^^^^^^^^NNNEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVVNNNVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVNNNNNNEEE======333======333=====================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fffffffffffffffpppfffpppppppppfffpppppppppppppppppppppppppppwwwwwwpppwwwpppppppppfffffffffffffffpppppppppppppppVVV^^^fffpppppp°°°–––NNNEEEEEE===EEEEEEEEE^^^ppp^^^NNNGGG===EEEVVV^^^pppÎÎÎÇÇǪªª¶¶¶ ^^^fffwwwªªªçççýýýïïïÁÁÁïïïïïïçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΰ°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççÎÎÎwww†††ppppppïïïýýýýýýýýýýýýýýýýýýýýýýýýü´´ùú��ú��ú��ùIIßßß wwwfffffffff^^^^^^ú��ú��GGGNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEEEEEEEGGGGGGGGGEEEEEEEEE===333333---------""""""---"""""""""""""""---""""""""""""""""""""""""---===GGG---GGG^^^NNNfffGGGEEEVVVVVV333GGGNNN===GGGGGG---"""GGG^^^^^^^^^^^^VVVGGGNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNGGGEEE======333======333===333=========333================================================EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE^^^fffffffff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffppppppppppppfffpppppppppfffpppppppppppppppwwwwwwwwwwwwwwwwwwpppppppppfffffffffpppppppppfffpppfffffffffGGG^^^ffffff wwwªªªVVVEEEEEE===EEEEEE^^^^^^fffVVVEEE===EEENNNwww–––ÁÁÁ×××ÎÎΪªª†††GGGNNNªªªçççïïïÇÇÇïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎwwwpppßßß¶¶¶×××ýýýýýýýýýýýýýýýû°°ùú��ú��ú��ú��û¯¯ýýýçççÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGNNNGGGGGGGGGGGGGGGEEE======333333------------"""---""""""---"""""""""---"""---===GGGEEE"""""""""""""""---333333===GGGVVVVVV^^^333EEEVVV^^^EEEGGGNNN333^^^EEENNNVVVNNNGGG^^^GGG===EEE333""""""NNN^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVV^^^^^^^^^^^^fff^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVNNNNNNGGGEEE======333===333=========333======================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEfff^^^^^^fff^^^fff^^^fffffffffffffffffffffppppppfffppppppfffpppfffppppppfffpppppppppppppppppppppwwwwwwwwwwwwwwwppppppppppppfffpppppppppppppppfffpppfffppppppfffGGGVVVwwwfff‘‘‘°°°–––‘‘‘°°°‘‘‘VVVGGGEEEEEEEEEVVVfffpppwww^^^GGGEEE======GGGffffff†††ÇÇÇÁÁÁÇÇǰ°°VVV===NNNªªªççççççïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°ýýýýýýýýýýýýýýýýýýçççÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶fffppp ^^^pppçççýýýýýýýýýû¦¦ùú��ú��ú��ú��úttýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGEEEEEE=========333------------"""---""""""---""""""---"""""""""---===GGGEEE===VVVpppNNN---333===---===VVVVVVGGGVVVffffff^^^fffGGGVVVEEEVVVEEENNN======NNNGGGfffNNN===GGG^^^333---------VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNEEE==============================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEE^^^fff^^^fff^^^ffffffffffffffffffffffffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwppppppppppppfffppppppfffffffffpppfffpppfffppppppppppppEEEGGGppp†††www–––––––––ªªª¶¶¶–––^^^GGGEEEEEENNN^^^wwwpppfffVVVGGGGGGEEEEEEEEENNN^^^www ¶¶¶ÁÁÁ°°°^^^GGGNNN–––ßßßïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××pppýýýýýýýýýýýýýýýýýýßßß×××ÎÎÎçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ppp–––‘‘‘ÁÁÁÁÁÁªªªûœœùú��ú��ú��ùû‰‰ýýýýýýýýýýýýýýýýýýççç¶¶¶ ú��ú��GGGGGGGGGEEE333333333------------"""---""""""---""""""---""""""---"""---------"""333VVVpppfffVVVpppwwwGGGEEEVVVfffGGGGGGfffEEEEEE^^^NNN^^^NNN^^^GGG^^^GGGVVVEEE^^^333NNNGGGVVVNNN===333EEE^^^===---"""---VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^^^^fff^^^^^^GGGEEE===========================================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEE===EEEffffffffffffffffffffffffpppfffpppppppppppppppfffppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfffpppfffffffffppppppfffpppppppppwwwpppppppppfffGGGGGGVVVwww‘‘‘wwwwwwªªªªªªªªª°°°¶¶¶–––VVVGGGGGGGGGVVVppppppwwwpppVVVNNNGGGEEE======EEENNNfff ¶¶¶ÁÁÁ°°°‘‘‘NNNNNNªªªïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇfffwwwýýýýýýïïïýýýýýýýýýýýýýýýçççÇÇÇ×××ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎζ¶¶ ªªª‘‘‘ªªª¥::ùú��ú��ú��ùû››ýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªú��ú��===333333------"""---""""""""""""---""""""---""""""---""""""------===GGGGGG333---333fffwwwppppppppppppNNN===NNNfffNNNVVV^^^---===^^^NNN^^^VVV^^^NNNfff^^^NNN===VVV333VVVEEENNNfffGGGGGG^^^NNN333"""===^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVV^^^^^^ffffffpppfffffffffVVVGGGEEE==============================================================================EEEEEE===EEE===EEE===EEEEEEEEE===EEEEEE===EEEEEE===EEEEEEfffpppfffpppfffpppfffpppfffppppppfffpppfffppppppppppppwwwwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppwwwpppfffppppppppppppfffppppppppppppppppppppppppwwwpppppppppNNNGGGNNN^^^‘‘‘www†††¶¶¶ÁÁÁ°°°°°°¶¶¶ fffNNNGGGVVVpppwwwªªªfffVVVNNNGGGEEE=========VVVfff °°°°°°ÎÎÎfffEEEVVV×××ïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ^^^wwwïïïýýýïïïýýýýýýýýýýýýýýýýýýïïïÇÇǰ°°ÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶pppfff°°°ÃLLú��ú��ú��ú��ùû§§ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°ú��ú��---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===VVVppppppGGG---===fffppppppfff^^^pppGGGEEEVVVpppNNN^^^VVV---EEE^^^VVVVVVVVVGGG===VVVNNN------==="""333"""333EEE---===GGG333GGG^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^fffpppfffpppppppppfffffffffVVVGGGEEE======================================================EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEE===EEEfffpppppppppppppppppppppffffffpppppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwppppppwwwppppppppppppfffpppfffpppfffpppppppppwwwwwwwwwwwwwwwwwwwwwppppppwwwppp^^^VVVNNNVVVfffwww††††††ªªªÁÁÁÁÁÁ°°°°°°ÁÁÁÁÁÁfffNNNNNN^^^ppp°°°VVVNNNGGGGGGEEEEEE======NNNNNN ÇÇÇççç–––EEEEEE ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß‘‘‘fffïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýý×××ÎÎÎßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßpppVVVÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýý××× ú��ú��"""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVV^^^^^^pppGGG---EEEfff^^^ppp^^^VVVpppGGGVVVfffpppNNN^^^NNN---EEENNNGGG===333------""""""GGG^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^fffpppfffpppfffppppppppppppffffff^^^NNNGGG======================================================EEE======EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEE===EEEEEE===EEEpppppppppwwwwwwpppppppppfffpppfffpppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppp^^^^^^VVVVVV^^^VVV–––‘‘‘–––¶¶¶ÇÇǰ°°°°°×××°°°pppNNNVVVVVVwww–––‘‘‘VVVNNNGGGGGGGGGGGGEEE=========GGG†††ßßßÁÁÁpppEEE†††ýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎ^^^VVV‘‘‘ïïïïïïïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÎÎÎßßßïïïýýýýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁßßßú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýýÎÎΖ––ú��ú��""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""---NNNEEENNNfffEEE---===pppVVVVVVNNN^^^pppGGGfffffffffEEEGGG===""""""---""""""""""""NNN^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffffffffpppfffpppfffpppfffppppppfffffffffVVVGGGEEE===========================================================================EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEEppppppppppppppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwpppppppppffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwppppppppp^^^www†††VVV^^^NNNVVV†††ªªª‘‘‘–––ÁÁÁÁÁÁ°°°°°°ÁÁÁ¶¶¶NNNVVVfff‘‘‘†††VVVNNNNNNGGGNNNGGGGGGGGG===333===NNN‘‘‘–––‘‘‘fff–––ïïïççç×××ýýýýýýýýýýýýýýýýýýýýýýýýßßßfffVVVppp ×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎÁÁÁßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççwwwßßß‘‘‘–––çççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––fff333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===^^^VVV---"""EEEpppVVVEEEEEE^^^^^^===EEEGGG===------""""""""""""""""""333"""---VVV^^^^^^^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffffpppfffffffffpppfffpppfffpppffffffpppffffff^^^^^^VVVGGGEEE==========================================EEE============EEE======EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEppppppfffpppfffpppfffpppppppppppppppppppppppppppwwwpppwwwpppppppppppppppfffppppppfffpppffffffpppppppppwwwwwwwwwwwwwwwwwwppppppppppppwwwpppppppppppp^^^pppªªªfffNNNNNNppp‘‘‘–––––––––¶¶¶ÇÇǰ°°°°°ÎÎÎÎÎÎwwwVVVNNNpppwww^^^GGGGGGNNNGGGNNNGGGNNNGGGGGG===EEEfff†††www^^^–––ïïïßß߆††çççýýýýýýýýýýýýýýýýýýýýýïïï–––^^^NNNNNN^^^ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁßßßwwwÁÁÁªªªpppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ–––fff---"""""""""""""""""""""""""""""""""""""""""""""---------"""""""""---NNN^^^EEE333---EEEpppNNN333333GGGEEE---------"""""""""""""""""""""""""""""""""""""""------------333---333^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffpppfffpppffffffffffffpppffffffffffffpppfffpppfff^^^^^^NNNEEE=========================================================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppppppppwwwpppwwwppppppppppppwwwpppppppppppppppppppppppppppffffffpppfffpppfffpppppppppppppppwwwwwwwwwwwwwwwppppppppppppwwwwwwpppwwwwwwpppffffff¶¶¶¶¶¶wwwVVVGGG^^^www–––ªªª––– ¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇdž††VVVVVVffffffNNNGGGNNNNNNGGGNNNNNN^^^†††^^^GGGVVVwww†††VVVpppßßßÇÇÇwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççNNN^^^‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýçççpppÇÇÇfff^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁ‘‘‘VVV---""""""""""""""""""""""""""""""""""""""""""---GGGVVV==="""""""""EEEfffVVVNNNGGG---===VVVEEE---------"""---""""""---"""""""""""""""""""""""""""333"""---333333333======""""""333333---===VVVfff^^^^^^^^^^^^fffVVVVVV^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffffffffffffffffffffffpppfffpppffffffffffff^^^VVVGGGEEE======================================================EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEwwwwwwwwwwwwwwwppppppppppppppppppppppppfffpppfffpppfffffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwfffppp¶¶¶¶¶¶–––^^^VVVVVVppp °°° ªªªÎÎΪªª ÁÁÁÁÁÁppp^^^fffNNNGGGGGGNNNNNNNNNNNNwww¶¶¶ ^^^EEENNNppp^^^ppp‘‘‘††††††¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýïïï–––ªªª×××ïïïýýýýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýïïï°°°ÁÁÁVVVNNN^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ†††NNN---"""""""""""""""""""""""""""""""""---------EEE^^^NNN===""""""---VVVwwwpppfffEEE------------""""""---"""---"""---""""""""""""""""""""""""""""""""""""===EEE======EEE===------""""""333333---"""GGG^^^^^^^^^fff^^^^^^NNNVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^VVVGGGEEE==========================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEwwwwwwwwwpppwwwppppppwwwpppfffpppppppppppppppfffpppppppppfffpppfffpppppppppwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwww†††^^^^^^ªªª¶¶¶ªªªfffVVVfffppp––– ––––––¶¶¶°°°ªªª–––ªªª¶¶¶ªªª^^^^^^NNNGGGGGGNNNVVVNNN^^^ªªª×××¶¶¶†††NNNEEEGGGNNNfff‘‘‘°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýýÁÁÁ‘‘‘ÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßßýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßfff–––¶¶¶ßßß°°°×××ýýýýýýýýýýýýýýýýýýççç¶¶¶†††GGG---""""""""""""""""""""""""""""""333GGGVVVEEEGGGppp^^^GGG---"""333^^^fffVVVEEE---""""""""""""---"""------"""---""""""""""""""""""""""""""""""------"""""""""---===EEE===333---"""---======333---NNN^^^^^^^^^^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffff^^^NNNEEE=============================================EEE===EEEEEEEEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEwwwwwwwwwwwwpppppppppfffppppppfffpppffffffpppppppppfffpppppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwpppwwwwww††††††www^^^^^^††† –––fff^^^fffpppwww‘‘‘ªªª ––– ÁÁÁ ‘‘‘ªªªÎÎΆ††wwwNNNNNN^^^fffppp^^^fffÇÇÇÎÎÎÁÁÁ–––pppGGGGGGGGGfff†††‘‘‘ªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýïïï°°°†††ªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýïïï‘‘‘¶¶¶çççÎÎÎVVV†††ýýýïïïýýýýýýýýýýýýççç¶¶¶‘‘‘EEE""""""""""""""""""---===333"""---GGG^^^pppGGG===NNNppp^^^---"""---EEE===---""""""""""""---""""""---""""""---""""""---""""""---------""""""---===EEE===---333---"""---""""""=========333VVV^^^^^^^^^VVVNNNVVV^^^^^^^^^^^^^^^^^^fffVVV^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffpppfffpppfffppppppffffff^^^VVVGGGEEE====================================EEE===EEE===EEEEEEEEEEEEEEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwpppppppppppppppppppppppppppppppppfffpppppppppwwwwww†††wwwwwwppppppwwwwwwwwwppppppwwwwww†††††††††††††††ppp^^^^^^––––––†††fffffffffpppwww‘‘‘ ‘‘‘ªªª°°°ªªª–––ªªª°°°¶¶¶fffVVVpppwwwwwwpppfff¶¶¶ÎÎÎÎÎÎ †††fffVVVfffppp‘‘‘ ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ °°°×××ýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶×××ýýýýýýýýýýýýýýýýýýýýý××׆††ÁÁÁÁÁÁppp†††çççÇÇÇçççýýýýýýýýýççç¶¶¶†††===""""""""""""------EEE^^^==="""333^^^fffVVV===EEENNNfffNNN---""""""---""""""---"""""""""""""""---""""""---"""------------"""---=========---333EEEEEE333"""---""""""333======333^^^^^^^^^NNNNNNVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppffffffffffffpppfffffffff^^^NNNEEE========================EEE==================EEE===EEE===EEE=========EEE======EEE===EEEwwwwwwwwwwwwpppwwwpppwwwpppppppppppppppfffppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwww††††††††††††wwwppp^^^VVV†††ªªªªªª‘‘‘†††^^^ppppppwww‘‘‘ªªª–––††† ¶¶¶°°°–––°°°ÎÎΰ°°ppppppfffpppwwwfff °°°ÁÁÁªªª†††www‘‘‘fffppp‘‘‘–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ÁÁÁ¶¶¶×××ýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýßßßßßßýýýïïïýýýïïïýýýýýýýýýýýýïïï××תªªppp¶¶¶ççç ïïïýýýýýýççç°°°===---"""---333======NNNppp333"""===pppfffGGGGGGGGGfffNNN==="""""""""""""""---"""""""""""""""---""""""---"""------333===------===GGGEEE333---333333---""""""""""""---======EEEVVVVVVNNNNNN^^^^^^VVV^^^VVV^^^^^^fff^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffpppfff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffffffffffffffpppffffffffffff^^^VVVGGG==================EEE===============EEE=========EEEEEE===EEE=========EEE===EEE===EEEwwwwwwwwwwwwwwwwwwppppppwwwppppppfffpppppppppwww††††††wwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††ppp^^^^^^‘‘‘¶¶¶°°°ªªªppp^^^ppppppwww‘‘‘ªªª‘‘‘†††–––ÁÁÁ¶¶¶––– ÎÎÎÇÇÇwww^^^VVVfffwwwfffwww–––†††www–––VVVppp––––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççççç–––×××ççç×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïßßßÁÁÁïïïýýýýýýýýýýýýïïïVVV°°°çççÁÁÁ‘‘‘çççýýýýýýçç窪ªEEE333---333NNNNNNGGGVVVppp333"""333fffppp^^^GGG===333333""""""""""""""""""""""""""""""""""""""""""---333333---333EEEGGG===333GGGEEE===---"""""""""""""""""""""""""""---"""===333GGGVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffpppfffffffffffffffpppfffffffffpppfffffffff^^^fff^^^^^^GGGEEEEEE===EEE==============================EEE======EEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwwwwppppppfffwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwww††††††††††††††††††www^^^fff––– ¶¶¶†††ffffffpppwww‘‘‘‘‘‘ †††–––ÁÁÁ¶¶¶‘‘‘‘‘‘¶¶¶¶¶¶wwwffffffwww^^^ppp ppp^^^ffffff‘‘‘–––^^^VVV––––––×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßß߆††×××ýýý×××çççýýýýýýýýýýýýïïïýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïççç°°°çççýýýýýýýýýýýýýýý¶¶¶^^^°°°ÎÎÎÎÎÎ×××ýýýýýýýýýßßߪªªwwwGGGEEE======^^^GGGEEENNNpppEEE333EEENNN^^^NNN---""""""""""""""""""""""""""""""""""""""""""---"""---===EEEEEEGGGGGGGGGEEE333---333---"""""""""""""""""""""""""""---333=========---------NNN^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffffffffffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fffffffff^^^VVVEEEEEE===EEE===EEE========================EEE======EEE======EEE======EEE===EEE†††wwwwwwpppppppppwww††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘††††††www^^^^^^–––°°°°°°–––wwwfffpppppp†††°°°‘‘‘††† ÁÁÁ°°°‘‘‘†††°°°ªªªpppffffffpppwwwfff^^^^^^ wwwGGG–––ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßßßß¶¶¶ßßßïïïÎÎÎçççýýýýýýýýýïïïÇÇǰ°°ßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇǶ¶¶çççßßßýýýýýýýýýïïïßßßçççßßßßßßýýýýýýýýýýýýßßß pppEEEEEE======NNNEEE333GGGppp^^^VVVNNN333---"""""""""""""""""""""""""""---""""""---"""---333333EEEEEEGGGGGGGGGEEE===333------""""""---"""""""""""""""""""""""""""333======EEEEEE===333"""---VVV^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^VVV^^^ffffffpppfffpppffffff^^^GGGEEE=======================================EEE===EEE======EEE===EEE===EEE†††††††††wwwwwwwwwwww††††††††††††††††††††††††wwwwwwwwwwwwpppwwwpppwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††www^^^fff†††¶¶¶ÁÁÁ°°°pppfffpppwwwwww†††‘‘‘–––†††ªªª¶¶¶ªªªwwwwww‘‘‘pppwww^^^www†††††††††fffpppppp‘‘‘‘‘‘GGGppp–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°°°°çççççç×××ßßßýýýýýýýýýÎÎΆ††††† ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××ÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýý××תªªpppEEE333======EEE===333EEEpppfffNNN===""""""------"""------"""""""""---""""""---333EEEEEEGGGGGGGGGGGGEEE333333---""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""333======EEEEEE===333""""""333VVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^^^^^^^ffffff^^^^^^^^^fff^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^VVV^^^fffpppfffppppppfffpppppppppfff^^^NNNEEE=======================================EEE===EEEEEE===EEE===EEE===†††www†††‘‘‘‘‘‘†††‘‘‘††††††††††††wwwwwwwwwpppppppppppppppwwwwww††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††www^^^^^^†††ªªª°°°–––pppfffpppwwwpppªªª ‘‘‘‘‘‘ªªª†††wwwppppppwwwwwwppp††††††www‘‘‘†††‘‘‘–––^^^VVV °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç熆†¶¶¶ªªªçççßßßïïïïïïýýýççç°°°‘‘‘¶¶¶ÇÇǪªªÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªpppEEE333=========333---333GGGEEE---"""---===EEE======333EEE------333333333"""333GGGEEEEEE===333------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------333333333"""""""""===VVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^ffffffffffff^^^fffffffffpppffffffffffff^^^ffffff^^^fffffffff^^^^^^VVV^^^^^^fffffffffppppppfffppppppfffppppppppppppfffNNNGGG=========EEE===========================EEE===EEE===EEE======EEE††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††wwwwwwwwwpppfffppppppwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††wwwfff^^^‘‘‘¶¶¶¶¶¶fffppppppppp–––°°°†††††††††°°°†††††††††fffwwwpppwww––––––‘‘‘–––†††NNN–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁ^^^†††wwwfffÁÁÁïïïçççßßßýýýÎÎÎÁÁÁ¶¶¶ÇÇǪªªÁÁÁ ßßßýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýïïï×××ßßßÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïï××× pppEEE333333===------"""""""""""""""333EEENNNNNNGGGGGGEEEEEEEEENNNNNNEEE333EEE===------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---EEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffffVVV^^^^^^ffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffff^^^^^^VVV^^^^^^ffffffpppfffpppfffpppppppppppppppppppppppppppfff^^^GGGEEE======EEE=====================EEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwwwwwwwfffppppppwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††wwwfff^^^†††°°°†††–––wwwfffppppppppp°°°ªªª††††††°°°°°°–––ªªª‘‘‘ppppppfff^^^www†††‘‘‘–––‘‘‘–––pppÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï–––NNNfff–––ÁÁÁßßßÎÎÎ ïïïççççççÁÁÁ°°°°°°ßßßÁÁÁ¶¶¶ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ×××çççýýýýýýýýýýýýýýýýýýïïï××× pppEEE333""""""""""""---333333======GGGVVVVVVNNNGGGGGGEEEEEE===---333===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^ffffff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffpppfffffffff^^^fffffffffffffff^^^^^^VVVVVVfff^^^ffffffpppfffffffffpppfffpppfffpppppppppppppppppppppfff^^^GGGEEEEEE===EEE===EEE======EEE======EEE===EEE===EEE=========‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppfff‘‘‘‘‘‘ªªªªªª‘‘‘pppppppppppp†††°°°ªªª†††‘‘‘ªªª‘‘‘ †††wwwppppppVVVpppwww‘‘‘‘‘‘‘‘‘wwwfffÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïppp^^^‘‘‘°°°ÁÁÁ×××ßßß çççïïïïï着ª‘‘‘‘‘‘¶¶¶ßßßÎÎÎ×××ýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïï×××çççýýýýýýýýýýýýýýýýýýïïï××× ^^^===333---"""---333GGGGGGNNNEEE===EEEGGGGGGGGG===333------"""""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^^^^ffffffffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppffffff^^^^^^ffffffffffff^^^VVV^^^^^^ffffffffffffffffffpppffffffpppfffpppppppppfffppppppppppppppppppfffNNNEEEEEE===EEE===EEE======EEE=========EEE============EEE‘‘‘–––‘‘‘–––‘‘‘†††‘‘‘†††††††††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††‘‘‘‘‘‘–––––––––‘‘‘fff^^^wwwªªª¶¶¶°°°†††fffffffffppp†††ªªª–––wwwwww†††pppfffwwwpppwww^^^pppfffpppwww†††‘‘‘pppwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎppp‘‘‘°°°–––¶¶¶×××^^^¶¶¶ïïïççç¶¶¶†††ppp‘‘‘ ïïïýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––NNN======333333---""""""EEENNNVVVVVVGGG===EEEEEE===333---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""---""""""""""""---333VVVfff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^ffffffpppfffpppfffpppfffffffffffffff^^^fffffffffffffffffffffffffff^^^ffffffffffff^^^^^^VVV^^^^^^ffffffpppfffffffffffffffppppppfffpppfffpppfffpppppppppppppppwwwppppppfffVVVEEEEEE===EEE===EEE=========EEE======EEE============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwwwwwwwwww†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––– ––––––†††ppp^^^°°°¶¶¶ªªªpppffffffpppwww–––wwwwwwwwwpppfffwwwpppwww†††ppp^^^pppwww†††‘‘‘‘‘‘‘‘‘°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁffffffppp–––ÁÁÁ‘‘‘VVV°°°ßßßçççßßß¶¶¶wwwppp^^^wwwÎÎÎýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ–––NNN=========333333""""""===NNNVVVGGGGGGEEE======333333333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""EEE^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^VVV^^^^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffpppfffffffffffffffffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppVVVGGGEEE===EEE===EEE===EEE======EEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘wwwwwwpppwww†††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– –––‘‘‘ffffff°°°¶¶¶ wwwffffffpppwwwwwwwwwpppwwwwwwpppwww†††www–––wwwfffpppwww†††‘‘‘–––‘‘‘ªªªÁÁÁÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇNNN^^^VVVNNN^^^¶¶¶–––^^^††† ßßßïïïßßß‘‘‘^^^^^^fff^^^†††ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ NNN333======333333---"""---333======333============333---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""""""""---""""""""""""""""""---"""EEEVVV^^^^^^fffffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^fffVVVVVV^^^^^^fff^^^fffffffffpppfffffffffffffff^^^fff^^^ffffffffffffpppfff^^^ffffff^^^fffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppfff^^^GGGEEE===EEE===EEE===EEE======EEE======EEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwww†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– –––‘‘‘fff^^^ªªª°°°°°°wwwffffffpppwwwwwwwwwppppppwwwpppwww†††fff‘‘‘‘‘‘wwwfffppp‘‘‘†††–––––– çççýýýýýýýýýýýýýýýýýýýýýýýý×××NNNGGGGGGVVV^^^www°°°†††VVVfffpppÇÇÇßßß××× fff^^^^^^VVV^^^×××ýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ NNN======333333------===333333333333333333---""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""---"""---GGGffffffffffff^^^ffffff^^^fffffffff^^^fff^^^fffffffffVVVNNN^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppfffffffffppppppfffppppppfffpppfffpppppppppwwwpppwwwpppwwwpppwwwwwwwwwpppppppppfff^^^NNNEEEEEE============EEE===EEE=========EEE –––††††††††††††††††††††††††‘‘‘†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––‘‘‘ppp^^^°°°ÁÁÁ–––wwwffffffpppwwwwwwppppppwwwpppfffppp^^^††††††‘‘‘wwwppp†††–––‘‘‘‘‘‘‘‘‘‘‘‘ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïfffEEEGGGfff†††www NNNVVVppp¶¶¶ïïïÁÁÁªªª–––www^^^NNNNNN ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ NNN===333------""""""---333333333---""""""""""""""""""""""""""""""""""""""""""---"""---""""""---"""""""""---""""""""""""""""""---"""""""""---""""""---""""""""""""---""""""""""""---"""---"""""""""---VVV^^^ffffffffffff^^^fffffffff^^^ffffff^^^fff^^^ffffffNNNNNN^^^fff^^^ffffffffffffffffffpppffffff^^^^^^fffffffffffffffpppffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffffffffppppppfffpppfffppppppppppppppppppwwwpppwwwwwwwwwwwwpppwwwppppppwwwppppppfffNNNEEE======EEE===EEEEEE======EEEEEE===‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††††††††††††††wwwwwwwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––‘‘‘fff^^^†††ªªª¶¶¶¶¶¶wwwffffffppppppppppppppppppffffffVVVppp‘‘‘‘‘‘‘‘‘pppwww‘‘‘‘‘‘–––wwwªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁNNNNNNppp¶¶¶–––pppGGGNNNppp‘‘‘××× °°°†††^^^NNNNNNpppßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ VVV===---"""---------"""---------""""""""""""""""""""""""""""""""""""---"""---"""------------"""""""""""""""""""""---""""""""""""---""""""---""""""---""""""""""""""""""---333===---"""""""""333^^^ffffff^^^fff^^^fffffffffffffff^^^ffffff^^^ffffff^^^GGG^^^^^^fffffffffffffffpppffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppppppppwwwwwwpppfffpppppppppppppppfffNNNEEE======EEE===EEE===EEE===EEE===‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––– –––†††wwwfff^^^www¶¶¶ÁÁÁªªªwwwffffffppppppppppppppppppfffNNNVVV‘‘‘†††‘‘‘fffVVVwww†††–––†††ppp‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýçççfffGGG××׆††‘‘‘wwwNNNGGGpppfff‘‘‘††† www^^^VVVVVVNNNÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶ NNN===---"""---333---""""""------""""""""""""""""""---""""""---"""------"""---"""------333======---""""""---""""""---""""""---"""---""""""---""""""---"""------"""---333333333===333---""""""---===^^^^^^ffffff^^^ffffff^^^fff^^^ffffff^^^ffffffffffffNNNGGG^^^^^^ffffff^^^fffffffffffffffffffff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffffffffpppffffffffffffpppffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppwwwppppppfffpppfffpppfffpppppppppfffNNNEEEEEE=========EEE===EEE======†††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––– –––––––––‘‘‘†††fff^^^wwwªªª¶¶¶ªªªffffffpppppppppppppppfffVVVNNNVVV†††wwwpppGGGNNNfffwwwwww^^^†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïpppNNN ×××¶¶¶fff†††wwwNNN^^^VVVVVV†††ªªª–––wwwVVVNNNNNNGGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶‘‘‘GGG333"""------333---"""""""""---"""---"""------""""""---"""------333------333===---======EEEEEE""""""---""""""---""""""---""""""""""""---""""""---------333===---333EEEEEE===EEE===---"""---"""EEE^^^^^^^^^^^^ffffffffffff^^^ffffff^^^ffffff^^^fffVVVNNNVVVffffff^^^ffffffffffffpppffffffffffff^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffff^^^ffffffffffffpppffffffpppfffpppffffffpppfffpppppppppppppppfffpppppppppwwwwwwpppwwwwwwpppfffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††www††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††fffVVV†††°°°ÁÁÁ¶¶¶ffffffpppfffpppwwwppp^^^NNNNNNfffpppEEEEEEGGGVVVppp^^^pppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççfff^^^¶¶¶ßßßÎÎÎwwwpppVVVVVVªªª°°°†††wwwVVVEEEEEEEEE^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°†††EEE------333---------"""---"""------"""---------------"""------"""---------333333======EEE===EEE======EEE---"""""""""---"""---""""""---""""""---""""""---333EEE333EEE===333======333333EEEEEE---""""""---GGG^^^fffffffff^^^fffffffffffffff^^^fff^^^^^^fffVVVNNN^^^^^^fffffffffffffffpppffffffffffffffffff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffpppfff^^^ffffffppppppfffpppffffffpppppppppppppppwwwwwwwwwwwwpppfffffffffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE=========†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘fffVVVwwwÁÁÁ¶¶¶°°°www^^^ppppppfffwwwwwwfff^^^NNNGGG^^^pppVVV======GGGNNNfff†††–––ïïïýýýýýýýýýïïïïïïýýýýýý×××VVV^^^ÁÁÁßßßçççÇÇÇppp†††www––––––°°° wwwfffNNNEEE======GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°www===------333------------"""---------------333===333---------------===333333EEE===EEE333GGGEEEEEE=========""""""---"""---""""""---"""---"""---"""---------===EEE333GGG333======333333========="""---"""---NNN^^^^^^^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffffpppffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffff^^^ffffffppppppffffffppppppfffpppfffpppwwwwwwwwwwwwpppffffffffffffffffffffffffffffffpppfffppppppfffNNNEEE===EEE============–––‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††wwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘www^^^VVVwwwªªªªªª–––ffffffpppfffpppwwwfff^^^pppGGGNNN^^^GGG===EEENNN^^^ppp†††‘‘‘†††ßßßïïïýýýïïïÎÎÎ×××çççïïï GGG^^^ÁÁÁçççßßßßßß°°°†††pppppp‘‘‘ªªª–––fffVVVGGGEEEEEE===EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªfff333---------------"""---------------"""---======333333333===333EEE===333===GGG===EEE===EEE===EEE---333333---""""""---"""---"""---""""""---"""333======---EEE===---GGG333EEEEEE===333===333---"""""""""333VVV^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^fff^^^fffffffffffffffffffffffffffpppfffffffffffffffppp^^^^^^ffffffpppfffpppfffpppfffpppfffpppppppppwwwwwwwwwwwwfffffffff^^^^^^^^^fff^^^fffpppfffffffffpppfffppp^^^NNNEEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††fffVVVfff‘‘‘ªªª^^^pppfffpppfffppppppppppppNNNVVVVVVNNNEEEEEENNNpppwww†††wwwÇÇÇïïïççççç窪ª ßßßÇÇÇpppEEEVVVÁÁÁççççççççç××ב‘‘www^^^VVVfffpppfffVVVGGGEEEEEEEEE======pppïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß ^^^333---"""------"""333------------------333GGGEEE=========EEE333EEEEEE===EEEEEE===GGG======---333---"""---"""---"""---""""""---"""333===333333======GGG===EEE333333EEE---EEEEEE==="""---"""---"""""""""333^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fffffffff^^^fff^^^fff^^^fffffffffpppffffffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffpppfffpppffffffpppfffpppfffpppppppppwwwppppppfffffffff^^^^^^ffffffffffff^^^ffffffffffffpppfffffffff^^^NNNEEE============†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘wwwfffVVVppp°°°ffffffpppfffffffffpppppp^^^^^^^^^NNNNNNGGG===GGGfffwwwfffÇÇÇïïïççççççÁÁÁ°°°ÎÎÎ ^^^EEEGGG¶¶¶çççïïïïïïßßß‘‘‘^^^fffNNNNNNNNNVVVGGGEEE======EEEEEE===NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªªNNN------------"""---333EEE333333===EEE===333GGG===EEEEEE===GGG333===EEEEEE===EEE---333333---"""---"""---"""---"""---"""---333=========GGGEEE333EEE333GGG333GGG333333GGG333---------""""""""""""""""""===^^^^^^fff^^^^^^^^^VVVVVV^^^fffffffffffffff^^^^^^fff^^^ffffffffffffffffffpppfff^^^^^^fffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fffpppfffpppffffff^^^ffffffffffff^^^ffffffpppffffffffffffpppfffpppfffppppppppppppppppppfffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffNNNEEE=========‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††wwwfffVVVfff^^^fffpppfffffffffpppfff^^^VVVVVVVVVGGGEEE===EEENNN‘‘‘––– –––×××ïïïßßßçççÇÇǶ¶¶ ^^^EEEGGGwwwÎÎÎïïïïïïççç–––VVV^^^^^^NNNGGGGGGEEE==================EEEÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG------"""---------EEEGGG===EEEEEEEEEEEE===GGG===GGGEEEEEEEEE===EEE===333333------------"""---"""---"""---"""---333333333EEE===GGG===EEE===EEEEEE===EEE333GGG===---------"""""""""""""""""""""GGG^^^^^^^^^fff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffffffffpppfff^^^pppfffpppfffffffffpppffffffpppfffpppfffppppppppppppfffffffffpppfffffffffffffffpppppppppppppppppppppppppppffffffppppppfffNNNEEE======‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††wwwfffVVV^^^fffpppfffpppffffffppp^^^NNNNNNGGGEEEEEE======EEEfff°°°¶¶¶¶¶¶çççïïïßßßÎÎΰ°°‘‘‘ ÎÎÎpppNNNGGGfff ßßßçççççç°°°VVVGGGffffffNNNEEE===333333333333333333EEE°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG---"""------------EEEGGG===GGG===EEEEEE===GGG333GGGGGGEEE===---333------------"""------===------------------=========EEEEEE333GGG333EEE===EEEGGGEEE333---------"""---""""""""""""""""""""""""NNN^^^fff^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^ffffffpppffffff^^^fff^^^ffffff^^^^^^fff^^^^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppfffffffffffffffffffffpppfffffffffffffffpppppppppwwwpppppppppppppppppppppffffffNNNEEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††fffVVVVVVfffppppppfff^^^fffpppVVVNNNGGG===============NNN ªªª°°°ÎÎÎçççÁÁÁ–––––––––ªªª×××–––^^^GGG^^^–––ÁÁÁççççççÁÁÁfffEEEVVVwwwVVVGGG===333333333333===–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––EEE------------------EEEGGG===GGG===GGGEEE===EEE333333===333---------------333333------EEEGGG333333===EEE===333GGG---333EEEEEE===GGG333GGG333333===333---"""---""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffff^^^ffffffffffffpppffffffffffffffffffffffffpppfff^^^fffffffff^^^fff^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppffffffffffffffffffffffffpppfffppppppfffpppffffffffffffffffffffffffffffffffffffpppfffpppppppppwwwwwwwwwwwwppppppwwwppppppffffffGGGEEE‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††fffNNN^^^ppppppfffffffffpppfffpppVVVEEEEEE333===EEEVVV†††‘‘‘ªªªÎÎζ¶¶†††‘‘‘ °°°‘‘‘pppVVVVVV†††ªªª×××çççÇÇdž††GGGGGGppp¶¶¶ÇÇÇ pppNNNEEE333333333===wwwïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΑ‘‘===------------------GGGGGGEEEGGG333EEE===333333------------------"""---333GGGEEE---333GGGNNN333======GGG======EEE------GGG======EEE---===---"""------"""---"""""""""""""""""""""""""""""""""333VVV^^^^^^^^^fff^^^fffffffff^^^^^^^^^VVV^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffffppppppffffffpppfffpppfffpppfffppppppfffppppppffffffffffffffffffffffffffffffffffffpppfffpppppppppppppppwwwppppppwwwwwwppppppfffpppfff^^^GGG†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††fffNNNVVVpppppppppfffpppppp†††ªªªwwwGGGEEE===EEEfff†††††††††°°°×××××× †††††††††–––†††^^^VVVpppªªªÎÎÎßßßÎÎΖ––NNNVVV°°°ßßß×××°°°–––wwwGGG===333===333^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††===------------------GGGEEE333===333---------------------------"""------===NNNGGG333===NNNGGG333======GGG===EEEEEE------EEE333333333------"""---"""""""""""""""""""""""""""""""""""""""""""""EEE^^^^^^^^^^^^^^^ffffffffffff^^^fffVVVVVV^^^^^^ffffffffffffpppffffffffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppfffpppfffffffffffffffffffffpppppppppffffffpppffffffpppfffpppffffffffffffffffffffffffpppppppppppppppwwwpppppppppppppppfffppppppfffpppfff^^^†††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††fffVVVVVVppppppffffffppp‘‘‘¶¶¶ ^^^GGGEEEEEE^^^‘‘‘°°°–––ªªªÁÁÁ×××°°°‘‘‘‘‘‘†††–––°°°‘‘‘ppp–––ppp–––ÁÁÁßßßÎÎΖ––^^^www×××ßßß×××°°°°°°wwwNNNEEE=========VVVïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ333---------"""------333333---------------------------------------------===NNNNNN===EEEGGGGGG333======GGG===EEEEEE------333------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""EEEVVV^^^^^^ffffff^^^fff^^^^^^ffffffVVV^^^^^^fff^^^ffffffffffffffffff^^^ffffffffffffppp^^^ffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppfffpppffffffpppffffffffffffppppppfffpppfffpppfffffffffpppfffffffffffffffffffffffffffffffffppppppppppppppppppppppppppppppfffpppppppppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††pppVVVVVVpppffffffppp†††ªªª VVV^^^wwwVVVfff ††† ÎÎζ¶¶ ––– ‘‘‘¶¶¶ÁÁÁ‘‘‘ÇÇÇpppwww–––×××¶¶¶–––°°°ßßßçççÎÎΰ°°°°°VVVEEE=========NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ333---------------------------------------333===------------======------EEEGGGNNNGGGEEEGGGEEE===GGGEEEEEE---===333"""---"""---"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffffpppfffffffffpppffffffpppfff^^^ffffffpppfffppppppfffpppfffffffffpppfffpppppppppfffffffffffffffffffffffffffffffffppppppppppppwwwpppfffpppppppppppppppwwwpppppp†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††pppVVVVVVpppffffffwww†††‘‘‘^^^wwwªªªwwwppppppwwwªªªÁÁÁ¶¶¶ªªª–––††† ÁÁÁ××× °°°^^^°°°ªªª°°° ÇÇÇçççßßßÇÇÇ ªªªwww^^^GGGEEE======GGG×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇwww333---------------------------------------======333===============------EEEEEEGGGNNN===GGGEEE===EEE===333---"""------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---NNN^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffpppfffpppffffffpppffffffppppppfffpppfffpppffffffffffff^^^fffffffffppppppfffpppffffffppppppppppppwwwppppppfffppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††††††††pppVVVVVVffffffwww†††ppp–––†††pppppp‘‘‘pppªªª¶¶¶ªªª‘‘‘‘‘‘‘‘‘–––ÎÎζ¶¶‘‘‘wwwNNNfff ÁÁÁ–––×××çççßßßÁÁÁ –––www^^^GGGGGGEEE===GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁfff333---------------------------------333333EEEEEE===EEE======GGG===------EEE===EEEEEE333EEE333------------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---VVV^^^fff^^^fff^^^^^^ffffff^^^^^^^^^fff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffppp^^^fffffffffffffffffffffpppffffffpppfffffffffpppfffffffffpppfffpppfffpppffffffffffffffffffppppppfffppppppfffpppfffpppfffffffff^^^^^^^^^fffpppfffpppffffffffffffpppppppppwwwppppppfffffffff‘‘‘†††‘‘‘‘‘‘†††‘‘‘–––––– ‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††www†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††pppVVVNNNfffwww‘‘‘^^^www‘‘‘fffppp†††ªªª†††ªªª°°°ªªª‘‘‘‘‘‘ ‘‘‘–––×××ÇÇÇpppVVVVVVppp °°°×××çççßßß¶¶¶ ‘‘‘www^^^VVVGGGEEE===EEE¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶^^^------------------------------------===EEEGGGEEEEEE=========EEE333------GGG333---333------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""333^^^fffffffff^^^fffffffff^^^^^^^^^ffffff^^^fff^^^fffpppffffffffffffffffffffffff^^^^^^^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffpppffffffpppffffffpppfffpppfffffffffpppffffffffffff^^^^^^fffffffffpppffffffpppppppppfffpppfffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppwwwpppfffffffffffffff†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††fffNNNNNN^^^www‘‘‘fffGGGVVVªªªwwwfffwww‘‘‘ –––––––––ÁÁÁÁÁÁ –––ÁÁÁªªª†††pppVVVpppªªª wwwÇÇÇçççÎÎΪªªªªª‘‘‘pppVVVNNNGGGEEE===EEEªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------333EEE===EEEEEEGGG===333===333---------333---------"""------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""EEE^^^ffffff^^^fffffffff^^^fffffffff^^^fff^^^^^^ffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffff^^^ffffffpppffffffpppfffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffff^^^fff^^^pppfffppppppfffpppppppppppppppfffpppffffff^^^ffffffpppfffffffffffffff^^^ffffffpppppppppppppppfff^^^fffppp†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––––––––––––––––––‘‘‘––––––––––––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††wwwfffNNNNNNwww†††GGGNNN–––†††ppp^^^fff‘‘‘wwwppp–––ßßßçççÎÎΰ°°°°°ªªª‘‘‘‘‘‘‘‘‘VVVppp ppp°°°ÁÁÁÁÁÁ°°°¶¶¶‘‘‘^^^NNNGGGEEE======EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------===EEEEEE===333EEE333------------------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""GGGfff^^^^^^ffffff^^^ffffff^^^fff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^^^^fffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffppppppffffffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffpppfffpppppppppppppppppppppffffffffffffffffffppppppfff^^^ffffffffffff^^^ffffffppppppppppppffffffpppfff‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– ––––––––––––––––––‘‘‘‘‘‘––––––––––––––– ––– ––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††www††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘†††††††††††††††††††††www^^^VVVpppwwwVVVVVVppp†††pppVVVNNNpppwwwfffVVV‘‘‘ßßßýýýïïïÎÎζ¶¶–––‘‘‘–––†††VVVfff†††–––ppp†††ÁÁÁ××××××¶¶¶^^^NNNGGGEEE======EEE ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªGGG---------------------------------333EEE===333------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---NNN^^^ffffff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffffffffpppfffpppfffffffff^^^^^^^^^ffffffpppfffppppppfffpppfffpppfffpppfffpppfffffffffffffffpppfff^^^^^^ffffffffffffffffff^^^ffffffpppppppppppppppppp‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––––––‘‘‘–––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††wwwVVVNNNfff^^^NNNNNN^^^^^^NNNEEENNNfff^^^^^^×××ýýýýýýççç××תªª¶¶¶ wwwffffffppp‘‘‘fff ßßß×××ÇÇǶ¶¶^^^GGGGGGGGGEEE======‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªEEE------------------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------"""""""""------"""""""""""""""""""""""""""""""""---VVVfff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffff^^^ffffff^^^ffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppppppppffffff^^^^^^ffffffppppppffffff^^^ffffffpppfffpppfffpppfff‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––––– ––––––––––––––––––‘‘‘––– ––––––‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††pppVVVNNN^^^^^^NNNNNN^^^GGGEEEGGG^^^†††www‘‘‘¶¶¶çççïïïýýýçççÇÇÇÁÁÁ†††wwwppp^^^wwwfff°°°ÇÇÇÁÁÁ°°°ªªª‘‘‘^^^GGGGGGGGG======EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß–––EEE---------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------===333---"""------------"""""""""""""""""""""---"""""""""333^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppffffffpppfffpppffffffffffff^^^^^^fff^^^fffpppffffffffffffpppfffpppfffpppfffffffffpppffffffpppffffffVVVNNNVVVfffpppfffpppfffffffff^^^fffffffffpppfffffffff––––––‘‘‘–––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––––––––––––––– ––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††ppp^^^VVV^^^NNNNNNVVVNNNEEEGGGwww‘‘‘www‘‘‘¶¶¶ÎÎÎçççýýýýýýçççÁÁÁ†††ªªªÁÁÁwwwpppppp°°°ÇÇǰ°°––––––†††fffGGGEEE=========EEE†††ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß‘‘‘===------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333EEEEEEEEE---EEE===333333333333---"""""""""""""""---333===333"""""""""===^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffpppffffffpppfffpppffffffffffffpppfffffffffppppppfffpppfffpppfffpppffffffpppfffpppfffffffff^^^^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffffffffppppppffffff^^^GGG===NNNfffpppfffpppffffff^^^^^^^^^fff^^^fffpppffffff‘‘‘–––––––––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––––––––––––––––––––––– ––– –––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††wwwVVVVVVVVVNNNGGGGGGGGGEEEfff††††††ÇÇÇßßßßßßïïïýýýýýýÎÎΖ––‘‘‘ ¶¶¶–––°°°¶¶¶ªªª¶¶¶ªªª–––†††pppGGG============EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===---------------------------------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------"""---333EEENNN^^^^^^NNN333GGG===EEENNNNNNEEE---""""""------333EEEEEEGGG==="""""""""EEEVVV^^^fff^^^^^^fff^^^fff^^^ffffff^^^^^^ffffffffffffffffffffffff^^^ffffffffffffffffffpppfffffffffffffffffffffpppfffffffffffffffffffffppppppfffffffffffffffppppppfffffffffffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffpppfff^^^GGG===EEEffffffppppppffffffffffffffffffffffffpppfffffffff‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––– ––– ––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘††††††††††††wwwVVVNNNGGGGGGGGGGGGEEEGGG^^^www‘‘‘°°°ÎÎÎ×××ïïïýýýýýýççç××תªª ¶¶¶‘‘‘ªªª¶¶¶ ªªª–––‘‘‘pppfffGGG============EEE‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGGGGGEEEGGGNNN^^^fff^^^^^^GGG333333333GGG^^^VVVEEE------333===GGGGGGEEEEEE333---"""""""""GGGVVV^^^^^^^^^^^^^^^^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppfffffffffffffffpppfffffffffpppffffffpppfffppppppfffffffffffffff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffffffffppppppffffffGGG333===^^^pppfffpppfffpppffffffffffffpppfffffffffpppffffff–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––– ––– ––– ––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††pppVVVGGGEEEGGGGGGGGGEEEEEENNN ÁÁÁÁÁÁïïïýýýýýýýýý×××°°° ‘‘‘†††††† –––wwwwwwfffVVVGGGEEE=========EEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††333---------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333333EEE^^^VVVVVVVVV^^^^^^^^^VVVEEE===333---333EEENNNGGG======GGGGGGNNNGGG===---"""---"""""""""---GGGVVVVVVVVVVVV^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffppppppfffpppfffpppffffffpppfffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppffffffNNN===EEEVVVppppppppppppffffffffffffffffffpppffffffpppfffffffff‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––––– ªªª ––– ––– ––– ––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††‘‘‘‘‘‘†††wwwpppVVVEEEGGGGGGGGGGGGGGGGGGfff°°°¶¶¶¶¶¶ßßßýýýýýýýýýïïïßßßÇÇÇ–––‘‘‘www^^^fff††††††pppVVVNNNGGG===EEEEEEEEEEEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎwww333------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---333===NNNNNNVVVfff^^^fffNNNGGGGGGEEE======333333333===EEENNNNNNEEEGGGNNNGGG333---"""""""""""""""""""""---NNNNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffpppffffff^^^fffffffffffffff^^^fff^^^ffffffffffffffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppfffppppppffffffffffffffffff^^^ffffffpppffffffpppfffpppfffpppfffffffffffffffffffff^^^ffffffffffffpppVVV===EEE^^^pppppppppppppppffffffpppfffpppffffffpppfffppppppfffppp‘‘‘‘‘‘––––––––––––––––––––––––––– –––––––––––– ––– ––– –––––– –––––– ––––––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††wwwpppVVVGGGGGGNNNNNNGGGGGG^^^–––°°°ÇÇǶ¶¶ïïïýýýýýýïïïïïïçççÎÎΪªªpppfff†††ppp^^^NNNGGGGGG===NNNVVVVVVGGGªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇppp---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""333===GGGGGGVVVVVV^^^^^^VVVGGG===333333333333333333======EEEGGGVVV^^^NNNEEEGGG===---"""""""""""""""""""""""""""---NNNNNNVVVVVVVVV^^^^^^^^^^^^^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppffffffffffff^^^fffpppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffffffffpppVVV======VVVpppfffpppppppppppppppfffpppfffpppfffffffffppppppfffpppfff–––––– ––– ––– ––– ––– –––––– –––––– ––– ––– ––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwpppVVVGGGNNNGGGNNNGGGNNNppp–––ÁÁÁªªª×××ýýýýýýïïïýýýïïïïïïßßߪªªÎÎζ¶¶wwwfffVVVNNNGGGEEE===NNNwwwwwwNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ^^^"""""""""""""""""""""""""""""""""""""""""""""""""""---""""""---""""""EEE======EEENNNVVVVVVNNN^^^VVVEEEEEE------"""------------333======GGGNNNVVVNNNGGGEEE333333---333======"""""""""""""""""""""333GGGNNNNNNVVVVVV^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^fffffffff^^^fffffffffffffff^^^ffffffpppfffffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffffffffffffff^^^fffpppfffpppfffpppfffpppfffpppffffffffffffffffff^^^ffffffffffffffffffGGG===VVVfffpppfffpppfffpppfffppppppfffpppfffpppfffpppppppppppppppfff–––––– ªªª ––– ––––––––– –––––– ––– ––– ––– ––– ––––––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††wwwwwwVVVGGGNNNNNNNNNGGGNNNpppªªª¶¶¶ ßßßýýýýýýïïïýýýïïïßßß¶¶¶×××ßßß¶¶¶‘‘‘fffNNNNNNGGG======GGGfffNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN"""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVVVVVVVV^^^^^^^^^NNN===333333""""""""""""""""""---"""333GGGNNNNNNNNNGGGEEE333---------333EEEEEEEEEEEE---""""""""""""""""""===GGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^fff^^^ffffff^^^°22ú��ú��ú��ú��fffffffff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffpppfffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffNNNEEENNNffffffpppfffppp^^^pppppppppppppppfffpppfffpppfffppppppppppppppp–––––– ––– ––– ªªª–––‘‘‘‘‘‘‘‘‘†††‘‘‘ ––––––––– ––– ––––––‘‘‘–––––––––––– ––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††wwwVVVGGGGGGNNNNNNGGG^^^†††ªªª–––¶¶¶ïïïýýýïïïýýýýýýçççÎÎÎÁÁÁÇÇÇßßßÎÎÎNNNGGGEEE======EEE^^^wwwVVVªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN""""""""""""""""""""""""""""""""""""""""""---""""""---""""""""""""---^^^fffVVV^^^fff^^^===---""""""""""""""""""------333"""===NNNGGGGGG===""""""------======EEENNNEEE------"""""""""""""""""""""EEEGGGGGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^ffffffRR÷ú��ú��ú��ú��fffffffffffffffffffffffffffpppffffffffffffpppfffffffffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffVVVNNNNNNfffffffffpppfff^^^^^^ppppppppppppppppppfffppppppffffffpppfffpppfff ––– –––––––––––––––––––––––––––––– ––– –––––––––––––––––– –––––– –––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††††††††††††††ppp^^^GGGNNNNNNNNNppp–––‘‘‘www×××ýýýïïïïïïýýýïïïßßß°°°ÇÇÇ×××××תªªVVVGGGEEE======EEENNNpppNNNªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÁÁÁGGG"""""""""""""""""""""""""""---""""""---""""""---"""""""""---""""""333VVVNNN===EEEVVVEEE---"""""""""""""""---333GGGGGGEEE---333333---""""""---333EEEEEEGGGEEE===333"""""""""""""""""""""EEEGGGGGGNNNNNNNNNNNNVVVVVV^^^^^^^^^fff^^^fffhddÞú��ú��ú��ú��ú��fffffffffffffffffffffffffffpppfffpppfffpppppppppffffffpppfffpppffffffpppfffpppfffffffffffffff^^^ffffffppppppffffffpppfffpppfffffffffpppfffffffffffffffffffffffffff^^^fff^^^VVVVVVfffffffffffffff^^^^^^fffwwwppppppppppppppppppppppppfffpppfffppppppppp–––––––––––– –––––– –––––– –––––– ––– ––– ––– ––– ––– ––––––––––––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘––––––––– ––– ––––––––––––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www^^^GGGGGGNNNppp–––ªªªwwwVVV‘‘‘çççýýýïïïýýýýýýçççÎÎΰ°°ÎÎÎ×××ÎÎÎÁÁÁªªªVVVEEE===EEEGGGGGGGGG–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶GGG""""""""""""---"""""""""---""""""---"""---""""""---"""---"""""""""---333---"""------""""""---"""---333===GGGNNNGGGGGG===---"""""""""---===EEEGGGGGG===333---""""""""""""""""""---EEEú��ú��GGGGGGGGGNNNNNNVVV^^^^^^^^^^^^ffffffµ77ú��çú��ú��ú��ú��fffppppppfffffffffppppppfffpppfffffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffffffffffffff^^^pppfffpppppppppfffpppffffffpppffffffffffffffffff^^^fffffffffffffff^^^^^^VVVffffffffffffffffffpppppppppppppppppppppppppppppppppfffpppfffpppfffpppfff–––––––––––– –––––– ––––––––––––––– –––––––––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––– –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††wwwwww†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††fffGGGNNNVVV††† †††^^^VVV¶¶¶ïïïýýýïïïýýýýýýßßßÇÇÇÁÁÁÎÎÎßßßßßßçççÇÇÇ^^^EEEGGGVVVEEE===ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE"""""""""""""""---"""---""""""---""""""---"""---"""""""""---"""---"""""""""""""""""""""---EEEEEEEEEEEEGGGGGG===333"""---333===333---EEEGGGGGGEEE333""""""""""""""""""""""""333EEEú��ú��GGGGGGNNNNNNVVVVVVVVV^^^^^^ffffffSS÷ø��‰MMú��ú��ú��ú��pppfffpppffffffpppfffppppppfffffffffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffpppffffffffffffpppfffppppppffffffpppfffpppfffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^fffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppppppppfffppppppfff–––––––––––– ––– ––– ªªª ––– ––– ––– ––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††www†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††wwwfffGGGNNN^^^–––°°°pppNNNppp×××ýýýýýýýýýýýýýýýïïï×××¶¶¶ÎÎÎÎÎÎÁÁÁ×××°°°†††www^^^EEEEEEwwwÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªª===""""""---"""---"""""""""---""""""------"""---"""---"""---""""""""""""""""""""""""---======NNNGGGNNN333333---""""""---333NNNNNNEEE---===333---""""""---EEE333"""""""""""""""""""""===GGGú��ú��GGGGGGGGGNNNVVVVVVVVV^^^^^^fffhddÞú��¾00fffú��ú��ú��ú��ppppppfffpppfffppppppffffffffffffppppppffffffppppppffffffppppppppppppffffffpppffffffffffffffffffpppfffppppppfffpppffffffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffppppppppppppfffppppppppppppfffpppppppppfffppppppfffpppppp‘‘‘–––––– ––––––––––––‘‘‘‘‘‘††††††wwwwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††www^^^NNNVVV–––ªªª†††NNNVVV‘‘‘çççýýýïïïïïïýýýýýýçççÁÁÁ ¶¶¶ÇÇÇ××××××ßßßÁÁÁwwwNNNNNNwwwÇÇÇçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""---""""""---"""---"""---"""---------333------------------"""------333---EEEGGGNNNGGG===---"""""""""---===EEENNNNNNGGG==="""""""""---======GGGNNNEEE"""""""""""""""""""""EEEEEEú��ú��GGGGGGNNNNNNNNNVVVVVV^^^^^^fff°22ú��ækbbfffú��ú��ú��ú��pppfffpppfffppppppppppppfffpppppppppfffpppfffppppppfffpppfffpppppppppfffffffffpppffffffffffffpppfffpppfffpppfffppppppfffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVVVVV^^^^^^ffffffffffff^^^fffpppppppppfffpppfff^^^^^^ffffffffffffpppfffppp‘‘‘‘‘‘†††wwwpppffffff^^^^^^fff^^^ffffffpppfff^^^VVV^^^^^^^^^^^^^^^^^^fffpppwww†††‘‘‘‘‘‘†††††††††††††††††††††††††††wwwwwwwww†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwfffNNNfffVVVNNN^^^°°°ïïïýýýýýýýýýýýýïïï××תªª °°°ÎÎÎßßßßßß°°°wwwÇÇÇÁÁÁ×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––==="""---"""---"""---"""---"""---"""333=====================333333333EEEEEEGGGEEE---EEE===333"""""""""---333===GGGNNNEEEEEE333"""""""""---333EEEGGGGGGGGG==="""""""""""""""""""""---EEEEEEú��ú��GGGGGGNNNNNNVVVVVV^^^^^^^^^SSöø��ŠMMppppppú��ú��ú��ú��pppppppppppppppppppppfffpppfffppppppppppppffffffpppfffpppppppppfffpppfffffffffffffff^^^ffffffpppfffpppfffppppppfffppppppfffpppfffffffff^^^^^^fffffffff^^^fff^^^^^^^^^^^^VVV^^^^^^fff^^^fff^^^VVVNNNVVVfffppppppfffffffffppp^^^^^^^^^^^^fffffffffpppffffff^^^^^^^^^^^^^^^^^^ffffffppppppppppppwwwwwwpppppppppfffffffffffffff^^^^^^VVVfffwww‘‘‘†††wwwppppppfffpppppppppppppppppp†††wwwwwwwww†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††pppNNNVVVVVVNNNNNNNNNfffÎÎÎýýýýýýýýýïïïýýýïïïÎÎΰ°°ªªª–––ÎÎÎ×××ÇÇÇ‘‘‘¶¶¶¶¶¶ppp ªªªßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––333---"""---"""---"""---"""------===GGGGGGEEEEEE=========EEEEEEGGGNNNNNNGGGGGG==="""""""""""""""333EEEGGGNNNGGGEEE333---"""""""""------===NNNEEE===333"""""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^^^^fffàú��»++pppppppppú��ú��ú��ú��fffppppppfffppppppfffppppppppppppppppppfffpppfffpppfffpppppppppffffffffffffffffffffffffppppppfffppppppfffpppfffpppfffppppppfffpppfffVVVNNNffffffffffff^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^VVVVVVNNNNNN^^^ffffff^^^^^^^^^^^^VVV^^^VVVVVV^^^fffppppppVVVVVVVVVVVV^^^^^^^^^^^^fff^^^fffffffff^^^fffffffffffffff^^^fffffffffffffffpppppppppwwwpppfff^^^^^^^^^fff^^^fffffffffpppppp†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––– ––– ––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppVVVGGGNNNGGGNNNNNNßßßïïïýýýïïïïïïýýýïïïßßß×××°°°‘‘‘°°°ÎÎζ¶¶ÎÎÎÇÇÇpppwwwÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††---"""---""""""---"""---"""------GGG^^^NNNNNNGGGGGGGGGNNNVVVNNNNNNGGGEEE------""""""""""""333EEEGGGNNNEEE===333"""""""""---===EEE===---GGG333---""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^ffffffú��èlbbfffppppppú��ú��ú��ú��ppppppfffppppppppppppfffppppppppppppppppppfffpppfffpppfffpppffffffpppfffffffffffffffffffffpppfffpppfffpppfffffffffpppfffppppppffffffVVV^^^pppffffff^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^NNNNNNNNN^^^VVV^^^ffffffNNNEEE333333EEEGGGNNN^^^fffppp^^^^^^wwwwwwpppfffpppfffffffffffffffpppppppppppppppppppppwww††††††‘‘‘†††wwwppp^^^^^^^^^VVV^^^^^^^^^fff^^^pppppppppppppppwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––––––––– ––– ––– –––––––––––––––––––––––––––‘‘‘‘‘‘wwwVVVGGGGGGGGGNNNVVV–––ïïïýýýýýýýýýïïïïïïýýýçç窪ª††††††‘‘‘‘‘‘ÁÁÁÇÇÇ‘‘‘fffªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇwww------"""---"""---"""---"""---333VVVfffVVVVVVVVVfff^^^NNNGGGEEE333---"""""""""---333"""---GGGEEEEEE333"""""""""---333EEEEEEGGGEEE333---333""""""""""""""""""""""""""""""EEEEEEGGGú��ú��NNNVVVVVV^^^^^^ffffffffffffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffpppfffppppppfffppppppppppppfffppppppfffppppppfffffffffpppffffffffffffpppffffffpppfffpppffffffpppffffffppppppfffpppfff^^^fffpppfffpppfffffffff^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVV^^^VVV^^^EEE"""---"""---333NNNVVVfffpppffffff††††††††††††††††††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘††††††wwwwwwfffffffffVVV^^^VVVVVVVVV^^^^^^^^^^^^^^^ffffffpppfffppppppwwwwww††††††††††††‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘–––––––––––––––––––––––– ––––––––– ––– ––––––––––––‘‘‘wwwVVVGGGGGGNNNGGG^^^ÁÁÁïïïýýýýýýýýýïïïïïïïïïÎÎÎÁÁÁªªª†††–––ÇÇǶ¶¶†††°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎfff---"""---"""---------------===GGGppp^^^VVVfff^^^VVVGGG===""""""---======333===GGGEEE---"""===---"""""""""333===GGGGGGGGGNNN===---""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��VVV^^^^^^ffffffffffffpppfffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ppppppppppppppppppppppppppppppfffppppppfffpppfffpppfffpppfffffffffffffffpppfffppppppffffffpppfffpppffffffpppfffpppfffffffffppppppfffffffffffffffffffff^^^^^^^^^^^^^^^VVVNNNNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNEEE---""""""------NNN^^^ffffffffffffwwwpppwwwwwwwwwwwwfffppppppppppppwww†††wwwfff^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^ffffffpppwwwwwwwwwwww††††††††††††‘‘‘–––––––––––––––––––––‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––– ––– ––– ––– ––– ––––––––––––‘‘‘‘‘‘pppVVVGGGGGGNNNNNNpppÎÎÎýýýýýýýýýýýýïïïïïïýýýïïïßßß°°°––– –––°°°†††ÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff------"""------333===EEEEEEVVVfffwwwfff^^^^^^GGG---""""""""""""===NNNNNNGGGNNNGGG===---""""""""""""---EEEEEEEEEEEE========="""""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��^^^^^^fffffffffppppppppppppppppppppppppppppppú��ú��ú��ú��pppfffpppppppppppppppfffpppppppppfffpppfffpppppppppfffpppfffpppffffffffffffpppppppppppppppfffpppfffffffffpppfffppppppfffpppfffpppffffffffffffffffffffffff^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGG===333333---===VVVffffff^^^^^^ffffffffffff^^^VVVNNNNNNNNN^^^^^^^^^fffpppppppppfffpppffffffpppfff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppwwwwwwwww†††††††††‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– ªªª ––––––––––––‘‘‘–––‘‘‘†††fffVVVGGGGGGGGGNNN†††ßßßïïïýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁªªª††††††www–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇVVV------"""333GGGGGGNNN^^^^^^wwwppppppVVVNNNEEE333---"""333===---EEENNNNNN===333------"""---333333"""===EEE===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""===EEEGGGVVVú��ú��ffffffppppppppppppppppppppppppwwwwwwwwwppppppú��ú��ú��ú��fffpppppppppppppppppppppppppppppppppppppppfffppppppfffppppppffffffffffffppppppppppppppppppfffppppppfffffffffpppfffpppfffffffffppppppfffffffffffffffppp^^^^^^^^^^^^^^^fffVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNGGGNNNGGGGGGEEEEEEEEEGGGNNNVVV^^^ffffff^^^fffffffff^^^VVVNNNNNNNNNVVVVVVVVVffffffppppppfffwwwpppppppppfffpppfff^^^^^^^^^fffffffffpppppppppppppppwwwwwwwww†††‘‘‘‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ªªª ªªª ªªªªªª ªªªªªªªªªªªª ––– –––––––––––––––‘‘‘‘‘‘†††wwwNNNGGGGGGNNNVVV–––çççýýýýýýýýýýýýýýýýýýýýýïïïïïïÎÎΖ–– –––‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÇÇÇNNN---"""---333VVVNNNVVV^^^ppp^^^^^^NNN===NNNGGGEEEEEEEEEGGGNNN333---333""""""""""""EEEEEEGGGNNNGGG---"""---"""""""""""""""---""""""===333"""""""""""""""""""""333GGGNNN^^^fffú��ú��ppppppppppppppppppppppppwwwwwwwwwppppppppppppú��ú��ú��ú��pppppppppppppppppppppwwwppppppfffppppppppppppfffppppppfffpppfffffffffpppppppppppppppppppppfffpppfffpppfffpppfffpppfffffffffpppffffffffffff^^^^^^ffffff^^^^^^fff^^^fff^^^^^^^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGGGGGNNNVVV^^^^^^fff^^^ffffffVVVNNNNNNVVVVVVVVVVVVVVV^^^fff^^^VVVVVV^^^^^^fffffffff^^^fff^^^^^^ffffffppppppwwwwwwwwwpppwwwwwwwww†††††††††‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ––– ––––––––––––‘‘‘‘‘‘†††pppVVVGGGGGGNNN^^^¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎ××תªª‘‘‘‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÁÁÁGGG------"""333GGGGGGEEEEEENNNNNNNNNNNNGGG^^^fffNNNEEENNNGGG===""""""---333---===EEENNNNNNEEE333333""""""""""""""""""""""""333333"""===EEE333""""""""""""""""""333GGG^^^fffppppppwwwpppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppppppppfffppppppffffffpppfffppppppffffffffffffpppppppppwwwppppppppppppfffpppfffppppppfffffffffppppppffffff^^^^^^fff^^^ffffff^^^^^^^^^^^^ffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGEEEEEENNNVVVVVVVVV^^^^^^ffffffppp^^^VVVVVVVVVVVVVVVfffffffffpppfff^^^^^^^^^ffffffpppppppppwwwppppppppppppwwwwww††††††‘‘‘––––––––––––––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘––– ––––––‘‘‘‘‘‘–––––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘††††††pppVVVGGGGGGNNNfffÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýççç×××ÁÁÁÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE---""""""---333""""""333VVVNNNVVVfffffffff^^^NNN===---"""""""""333EEEVVVGGGGGGNNN===333"""""""""""""""""""""EEE===GGGEEE"""GGGNNN333"""""""""""""""""""""---===NNNfffppppppwwwwwwpppwwwwwwwwwwwwpppwwwwwwwwwpppwwwpppppppppwwwwwwppppppppppppppppppwwwpppppppppppppppfffpppppppppfffpppppppppfffpppffffffffffffppppppppppppppppppppppppfffppppppfffffffffpppffffffpppffffffppp^^^VVV^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^VVVVVVNNNNNN===GGGNNNNNNVVVVVV^^^^^^ffffffffffffVVVVVVNNNVVVVVV^^^pppfffpppppppppppppppfffpppppppppwwwwwwwwwppppppwwwwww†††††††††‘‘‘‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––––––––––– ––– –––––– ––– ––– ––– ––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªª°°°°°°ªªªªªªªªª –––‘‘‘‘‘‘‘‘‘††††††wwwVVVGGGNNNNNNwww×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççççççßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß°°°==="""""""""""""""""""""---VVV^^^^^^fff^^^VVVEEE333""""""---333===GGGGGGGGGGGG333---"""""""""""""""""""""""""""---NNNGGGVVVEEE---GGGGGG---""""""""""""333===GGG^^^ffffffpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppwwwpppppppppppppppfffppppppppppppppppppfffpppfff^^^ffffffppppppppppppppppppfffpppppppppffffffpppffffffpppfffpppfffpppffffffVVV^^^fffffffff^^^^^^fff^^^fffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffpppfff††††††wwwwwwppp^^^fff^^^ffffffwwwwwwwwwwwwpppppppppwwwwwwwwwwww†††††††††††††††††††††‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘–––––– ––––––––––––––– ––– ––– –––––– ªªª ªªªªªªªªª°°°ªªª ªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘†††www^^^GGGGGGVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""""""""""""""---EEEGGGNNNGGGGGG===333---======EEENNNGGG===------""""""""""""""""""""""""""""""333""""""---"""EEENNNEEEEEE---"""===---"""""""""---===EEENNNVVVffffffppppppppppppwwwpppwwwwwwpppwwwpppwwwppppppwwwpppwwwwwwwwwwwwpppppppppppppppppppppwwwpppwwwpppppppppppppppfffpppppppppfffppppppffffffffffffpppppppppppppppppppppppppppfffpppffffffppppppffffffpppfffpppffffffVVV^^^^^^fffppp^^^^^^ffffff^^^^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVfff^^^VVVVVVVVVVVV^^^^^^ffffffpppfffwwwppppppfffpppppppppwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘†††††††††‘‘‘––––––––––––‘‘‘–––––––––––– ––– ––––––†††‘‘‘††††††††††††‘‘‘–––––– ––– ––––––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª°°°°°°ªªªªªª ªªª –––––––––‘‘‘‘‘‘www^^^GGGNNNVVV çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––333------""""""""""""""""""---"""333333333EEEEEEEEEGGGVVVGGGEEE---"""""""""""""""""""""""""""""""""---333GGG333"""333"""---333"""""""""""""""""""""---===GGGNNN^^^ffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppwwwwwwpppwwwwwwpppwwwpppfffppppppppppppfffpppfffpppfffffffffwwwppppppppppppppppppppppppfffpppppppppppppppppppppfffpppfffpppfff^^^VVVfffffffff^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVV^^^VVVVVV^^^fffpppfffffffffpppwwwwwwwwwwwwwwwpppwwwppppppwww††††††††††††††††††††††††‘‘‘†††‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘††††††††††††††††††––– ––– –––––– ––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªªªªªªªª ––– ‘‘‘†††www^^^NNNNNN^^^°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘---------""""""""""""""""""""""""------===EEENNNGGGEEE333---"""""""""""""""""""""""""""""""""333333EEENNNNNN"""------""""""""""""""""""333EEEGGGVVV^^^^^^fffpppffffffpppfffpppppppppppppppppppppwwwpppwwwppppppppppppwwwpppwwwppppppppppppfffpppwwwwwwpppwwwwwwwwwppppppppppppfffpppppppppfffpppppppppffffffpppwwwppppppppppppppppppppppppfffppppppfffpppppppppfffpppfffpppfff^^^VVV^^^fffffffff^^^^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^VVVVVV^^^fffffffffffffffwwwwwwppppppwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘ ––– ––– ––– ––– ªªª ªªª ªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª ––––––‘‘‘†††fffNNNNNNfffÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333---""""""---"""333EEE======------"""===EEE===333"""""""""""""""""""""""""""333"""""""""EEENNNVVVGGGEEEEEE"""""""""""""""""""""---===EEENNNVVV^^^^^^ffffffpppffffffpppppppppppppppppppppppppppppppppwwwppppppppppppppppppwwwppppppppppppfffpppwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppffffffppppppwwwppppppppppppppppppfffppppppppppppppppppffffffpppfffpppfff^^^NNN^^^ffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffppppppffffffwwwpppwww†††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘††††††††††††††††††––– ––– ––– ––– ––– ––– ªªªªªª ªªª ªªª ªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°ªªªªªª ––––––‘‘‘fffNNNGGGpppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333------=========NNNVVVVVVEEE""""""---------"""""""""""""""""""""---"""===NNNEEE==="""NNNNNNGGGEEE---"""""""""""""""---===GGGNNNVVV^^^^^^^^^ffffffpppfffffffffpppppppppffffffpppfffppppppppppppppppppppppppppppppwwwpppppppppwwwppppppwwwpppwwwppppppppppppfffppppppppppppppppppfffppppppfffffffffppppppwwwppppppppppppppppppppppppfffpppppppppfffppppppfffpppfffffffffNNNVVV^^^fffffffff^^^^^^fff^^^^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^fff^^^^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffpppffffffffffff†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††www†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘†††††††††††††††††††††‘‘‘––– ––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°ªªª°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªª –––†††wwwfffVVVNNN×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp333333NNNVVVVVV^^^fff^^^GGG---""""""""""""""""""""""""""""""""""""333EEEGGGVVVVVV==="""EEE---""""""""""""""""""333===GGGVVVVVV^^^^^^^^^^^^fffffffffffffffpppfffpppfffppppppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwpppppppppfffppppppppppppppppppfffppppppfffpppffffffpppwwwpppwwwpppppppppppppppwwwpppppppppppppppppppppffffffpppfffffffffVVVNNN^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^ffffffffffffpppfffffffff‘‘‘––––––††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘††††††††††††††††††‘‘‘––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª¶¶¶°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°° ––––––‘‘‘†††pppVVVNNN†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß×××ppp333NNNpppfffNNNVVV^^^VVVNNNEEE"""---"""333=========EEE======EEENNNNNNVVVGGGGGG---""""""""""""""""""---333EEEGGGVVV^^^^^^^^^^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppffffffppppppppppppwwwppppppppppppppppppfffppppppppppppppppppppppppfffffffff^^^NNN^^^fffffffff^^^^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffff^^^VVV^^^^^^fffffffffffffffffffffffffff––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘––––––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘†††††††††††††††–––––– ªªª ªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªªªªª ‘‘‘†††wwwVVVVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××fff===VVVfff^^^GGGVVVNNNGGGEEE333"""333===333===GGGNNNGGGVVVVVVVVVGGG^^^VVVVVVEEE333---""""""""""""333===EEEGGGVVV^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffpppfffppppppfffpppppppppfffppppppppppppppppppfffpppfffpppwwwppppppwwwpppwwwppppppppppppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppppppppppppppfffpppffffffppp^^^VVVVVVfffffffff^^^^^^^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffff^^^^^^^^^^^^fff^^^ffffffpppfffffffffffffff––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘††††††‘‘‘††††††††††††‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªª°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ªªªªªª–––‘‘‘wwwVVVVVV–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××^^^GGGVVVVVVVVVGGGNNNGGGEEE---"""333GGGNNN======GGGGGGVVVfffGGG^^^GGGGGG===---"""""""""""""""---===EEEGGGNNNVVV^^^^^^^^^ffffff^^^^^^^^^fffffffff^^^fffffffffffffffffffffffffffffffffpppppppppfffpppfffpppppppppppppppppppppfffpppwwwppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppffffffpppppppppppppppppppppppppppppppppfffppppppppppppppppppfffppppppfffpppfffNNNVVVffffffpppfffffffffffffffffffffffffffffffffpppfff^^^^^^fff^^^ffffff^^^fffffffffffffff^^^fff^^^fff^^^VVV^^^^^^^^^fffpppffffffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––††††††‘‘‘†††††††††–––––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªª°°°¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°° †††www^^^^^^ çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß×××VVVEEEEEEGGGVVVGGGGGGEEE===333---===GGGGGG333---======VVVGGG---===333"""""""""""""""""""""333===GGGGGGNNNVVVVVV^^^ffffffffffff^^^ffffff^^^ffffff^^^^^^fffffffffffffffffffff^^^fffffffffpppffffffffffffppppppppppppppppppppppppppppppppppppppppppwwwpppppppppppppppwwwpppppppppfffpppfffpppfffppppppppppppwwwpppppppppppppppppppppppppppppppppppppppfffppppppfffpppfffVVVVVVffffffpppfffffffffffffffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^ffffffffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘ ––––––––––––––––––––––––––––––†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––†††††††††††††††††††††‘‘‘–––––– ––– ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶°°° ‘‘‘^^^^^^°°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎGGG333===GGGVVVGGG333333333333===EEEEEE===333"""---333===333""""""""""""""""""""""""---===EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffpppfffffffffpppppppppppppppppppppppppppppppppwwwpppppppppppppppfffwwwppppppwwwpppppppppppppppfffppppppfffpppppppppwwwwwwpppwwwppppppppppppppppppppppppppppppfffppppppppppppppp^^^NNNfffffffffffffff^^^ffffffffffffpppfffffffffffffffffffff^^^^^^^^^fffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^^^^fffffffffpppffffffpppffffffppp––––––––––––‘‘‘––– ––– ––––––––––––‘‘‘––––––‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘––––––†††wwwwww††††††††††††–––––– ––– ––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁ¶¶¶ÁÁÁ°°°ªªª–––^^^fff¶¶¶çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßß¶¶¶EEE===EEENNNNNN---"""------------"""333===333"""""""""""""""""""""""""""""""""333EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffffffffff^^^fffffffffpppffffff^^^ffffffffffffffffff^^^^^^^^^fffffffffffffffffffffppppppfffppppppfffppppppppppppppppppppppppppppppppppppwwwpppwwwpppppppppfffpppfffppppppppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfff^^^VVV^^^ffffffpppffffffffffffpppfffffffffffffffpppfffffffff^^^fff^^^fffffffffpppfffffffffffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffff––– ––––––––––––––– ––– ––––––––––––†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––wwwwww†††††††††‘‘‘–––––– ––– –––––– ªªªªªªªªª ªªª ªªªªªª ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ°°°ªªª–––^^^fffÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××–––333EEEGGGNNN333"""""""""---""""""""""""---""""""""""""""""""""""""""""""---===EEENNNVVVVVV^^^^^^^^^^^^^^^fffffffffffffff^^^ffffffffffffpppffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffppppppppppppfffpppppppppppppppwwwpppppppppwwwwwwpppwwwpppwwwwwwpppppppppppppppfffpppfffppppppwwwppppppfffppppppppppppppppppfffpppwwwpppppppppfffppppppppppppffffffVVVVVVfffppppppfffffffffffffffffffffppp^^^^^^ffffffffffff^^^fffffffffffffffpppffffffpppffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffpppfffffffffffffffffffffªªª –––––– –––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––pppppp†††††††††‘‘‘‘‘‘–––––– ––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ¶¶¶ªªª–––†††fffpppÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp------333333"""""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ppppppffffff^^^fff^^^fffffffff^^^fffffffffffffff^^^ffffff^^^fffffffffffffffpppffffffpppppppppfffppppppfffpppppppppppppppwwwwwwppppppppppppwwwpppwwwwwwpppppppppffffffpppfffppppppwwwpppppppppppppppwwwpppwwwpppppppppwwwppppppppppppppppppwwwpppffffffVVVVVVfffpppfffpppfffpppfffpppffffffpppfff^^^ffffffffffff^^^fffffffffpppfffpppfffpppfffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppfffffffff ––– ––– –––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––fffpppwww†††††††††‘‘‘––– ––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÎÎÎÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ°°° †††pppwwwÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýççç×××^^^---"""---""""""""""""""""""""""""""""""""""""""""""""""""333===EEEGGGVVV^^^^^^^^^^^^^^^fffffffff^^^fffpppfffpppfff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^^^^fffffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppfffpppppppppppppppppppppppppppwwwwwwwwwwwwwwwppppppfffppppppfffppppppwwwwwwwwwppppppwwwwwwpppwwwpppwwwpppwwwpppwwwpppppppppppppppppppppfff^^^NNN^^^fffppppppffffffpppfffffffffffffff^^^^^^fffpppfffffffffffffffpppfffpppffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffpppppppppffffffffffffffffff ––––––†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ffffffwww†††††††††‘‘‘‘‘‘–––––––––––– ªªª ªªª ªªªªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÇÇÇÇÇÇÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÏ££àbbí11õø÷óæ))¾DD}eewwwÎÎÎïïïýýýýýýýýýýýýýýýýýýçççÎÎÎNNN---""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^fff^^^ffffff^^^fff^^^fffffffffpppfffpppfff^^^fffffffffffffffffffffffffff^^^^^^fff^^^fff^^^ffffffffffffffffffpppffffffpppfffpppfffffffffppppppppppppppppppppppppppppppfffpppppppppwwwwwwwwwwwwppppppfffppppppfffpppwwwpppwwwppppppwwwwwwwwwwwwppppppwwwwwwpppwwwpppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffpppffffff^^^fffppp^^^^^^ffffffffffffffffffppppppfffppppppffffffpppfffffffffffffff^^^^^^^^^^^^^^^fffffffffpppffffffpppfffffffffffffff^^^ –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ppp^^^www†††††††††‘‘‘‘‘‘‘‘‘–––––– ªªª ªªª ªªª ªªªªªª°°°ªªª ªªªªªªªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ñ “]]wwwÎÎÎïïïýýýýýýýýýýýýïïïçççÇÇÇEEE"""""""""""""""""""""""""""""""""""""""---333EEEGGGVVVVVV^^^^^^^^^ffffffffffffffffffffffffffffffpppfffpppfff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fffffffffpppffffffpppfffpppfffffffffffffffpppppppppppppppfffppppppfffpppppppppwwwpppwwwppppppfffppppppffffffpppwwwpppwwwpppppppppwwwppppppwwwpppwwwpppwwwppppppwwwppppppwwwpppfffppp^^^VVV^^^fffpppffffffpppffffffffffffffffff^^^VVVfffffffffffffffppppppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^^^^fff^^^ffffffpppfffppppppfffpppfffpppfffffffff –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††ppp^^^ppp†††www†††‘‘‘‘‘‘–––––––––––––––––– ––– ªªª –––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎð++àxxÕªªÏÇÇØÏÏ×¢¢í00ú��ú��ú��åwwwÎÎÎïïïýýýýýýýýýïïïççç°°°===---"""""""""""""""""""""""""""""""""---===EEENNNVVV^^^^^^^^^^^^ffffffffffffffffffffffff^^^ffffffpppfffffffff^^^fff^^^ffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^fffffffffffffffffffffffffffpppffffffffffffffffffpppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffpppppppppwwwpppppppppwwwppppppwwwpppwwwwwwwwwppppppwwwppppppwwwpppppppppfffVVVVVVfffpppfffppppppfffffffffffffffffffffVVVfffffffffffffffpppppppppffffffffffffffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffppppppffffffffffff^^^fff –––‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††ppp^^^ppp†††††††††‘‘‘––––––––––––‘‘‘‘‘‘–––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªª°°°ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××ÎÎÎ××××××ÎÎÎÎÎÎÒ··ú��ú��ú��÷ªªªÎÎÎïïïýýýýýýïïï×××ú��ú��"""""""""""""""""""""""""""---333===GGGVVV^^^^^^ffffffffffff^^^ffffffppppppffffff^^^ffffffffffffpppffffff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffffffffpppffffffppppppfffppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppffffffpppwwwpppppppppppppppwwwppppppppppppppppppwwwpppwwwpppppppppwwwpppppppppfff^^^NNN^^^fffpppfffffffffffffffffffffffffff^^^^^^fffffffffpppppppppffffffpppffffffpppffffff^^^ffffffffffff^^^^^^^^^^^^ffffffffffffpppfffpppfffpppffffffffffffffffff^^^ –––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††www^^^fffwwwwwwwww†††‘‘‘‘‘‘––––––––––––‘‘‘†††––––––––– ––– ––––––––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ°°°¶¶¶ÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ×××××××××ÎÎÎÎÎÎÚÂÂú��ú��ú��ð%%¶¶¶ªªªwwwÇÇÇïïïýýýïïï×××ú��ú��"""""""""""""""""""""---===EEENNNVVV^^^ffffffffffffffffffffffffppppppfffffffffffffffffffffppppppfff^^^fff^^^fffffffff^^^fffffffff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppppppppfffppppppfffpppfffpppfffpppfffpppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppwwwwwwppppppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffffffffffffffpppffffff^^^ffffffffffffppppppppppppfffffffffffffff^^^VVV^^^fffffffff^^^fff^^^^^^fff^^^fffpppfffpppffffffpppfffpppfffffffffffffff^^^‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^fffwww†††wwwwww†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘––––––––– –––––––––––– ªªª ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××××××××××××××××××ÔÔÛ··ïBBú��ú��÷ Ù™™ÇÇǶ¶¶ ÁÁÁïïïïïïÎÎÎú��ú��"""""""""""""""---333EEEGGGVVVVVV^^^fffffffffpppfffffffffffffffpppfffpppfffffffffffffffpppfffpppfffffffff^^^fffffffffffffff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffpppfffffffff^^^fffffffffpppppppppfffppppppfffpppppppppfffpppfffppppppppppppwwwppppppppppppppppppffffffppppppwwwpppwwwpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwppppppfffffffffVVVVVV^^^pppppppppfffpppffffffffffffffffff^^^fffffffffpppfffpppffffffffffffpppfffffffffNNNGGGffffffffffff^^^^^^fff^^^ffffffpppfffpppppppppfffpppfffpppfffffffffpppfff^^^†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^^^^www†††wwwwww††††††‘‘‘‘‘‘‘‘‘–––––––––––––––––– –––––– ––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎ×××××××××××××××ú��ú��ú��ú��ùëYYÚ¼¼×××ÎÎÎÇÇÇÁÁÁªªª†††ÁÁÁßßßÁÁÁú��ú��"""---333===GGGNNNVVV^^^^^^ffffff^^^fffffffffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffffffffffffffffffff^^^fff^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppfffpppppppppfffffffffpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwpppwwwppppppppppppfffpppfffVVVVVVfffpppfffpppffffffffffffffffffpppfff^^^^^^pppfffpppfffpppfffpppfffpppfffpppffffffVVVNNNVVVfffpppffffff^^^fff^^^fffffffffffffffpppfffpppfffpppfffpppffffff^^^fffffffff†††‘‘‘†††‘‘‘‘‘‘†††fff^^^ppp†††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ×××ÎÎÎ×××ú��ú��ú��ú��ú��øèhh×ÖÖ××××××ÎÎÎÇÇǰ°°†††ppp°°°†††ú��ú��333333EEENNNVVV^^^^^^fffpppfffffffffffffffffffffppppppfffpppffffffpppffffffffffffpppfffpppffffff^^^^^^ffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^ffffffffffffffffff^^^^^^fff^^^ffffffpppfffpppfffpppfffppppppfffppppppfffpppfffppppppfffpppfffpppfffppppppfffpppfffffffffpppwwwpppwwwppppppwwwppppppwwwpppwwwfffppppppppppppppppppppppppfffpppffffffVVVVVV^^^pppfffppppppffffffffffffppppppfff^^^^^^fffffffffppppppfffpppfffpppfffpppffffff^^^VVVVVVfffffffffppp^^^^^^fff^^^ffffffffffffpppffffffpppfffpppfffpppfff^^^fffffffff^^^‘‘‘‘‘‘‘‘‘††††††ppp^^^ppp†††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––––––––– ––– ªªªªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ××××××××××ÑÑÞ©©ó((ú��ú��ú��æxxßßß××××××ÎÎÎú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffppppppffffffffffffffffffffffffpppfffffffffffffffpppfffpppfffpppfffffffff^^^ffffff^^^ffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^fffffffffffffffffffffffffff^^^fffpppfffpppffffffpppppppppfffpppppppppfffpppfffppppppppppppppppppppppppfffpppppppppffffffppppppppppppwwwpppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffVVVNNN^^^pppppppppfffpppfffpppffffffpppfff^^^^^^fffffffffpppffffffffffffppppppffffffpppfffNNNVVV^^^fffpppfffffffffffffff^^^^^^fffffffffpppfffffffffpppffffffffffffffffffffffff^^^‘‘‘‘‘‘†††www^^^fffwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––– ‘‘‘––––––––– ªªª ––– ªªªªªª ªªªªªªªªªªªªªªª ªªª ªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶°°°°°°°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÇÇÇÎÎÎÎÎÎ××××××××××××××××××ݬ¬ú��ú��ú��ö!!××××××ÎÎÎÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��fffpppfffpppfffpppffffffffffffpppfffpppfffffffffpppfffffffffpppfffpppffffffffffff^^^ffffff^^^fff^^^^^^^^^fffffffff^^^^^^^^^^^^ffffffffffffffffff^^^fffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffppppppppppppppppppffffffpppfffppppppffffffppppppwwwwwwpppppppppwwwppppppppppppppppppppppppppppppppppppppppppfffffffffVVVVVV^^^fffpppfffpppffffffpppffffffpppfff^^^^^^fffffffffpppfffpppfffpppfffpppffffffpppfffVVVNNN^^^pppfffpppfffffffff^^^^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^^^^†††www^^^fffwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÇÇÇÎÎÎ×××××רÏÏú��ú��ú��ø×××ÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ªªª‘‘‘ú��ú��††††††wwwwwwwwwpppppppppfffpppfffpppppppppfffppppppppppppppppppfffffffffffffffffffff^^^fff^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffffffffff^^^^^^ffffffpppfffffffffffffffpppffffffppppppffffffpppfffpppfffpppppppppppppppfffpppppppppppppppfffffffffpppppppppwwwppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfffpppVVVVVVVVVfffpppfffpppfffpppfffffffffffffff^^^^^^fff^^^ppppppfffffffffppppppppppppfffpppppp^^^GGGVVVfffppppppfffpppfffffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^pppwwwwwwwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– ––– ªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÞ¥¥ú��ú��ú��ôÇÇÇÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶°°°ªªª–––ú��ú��†††††††††††††††††††††††††††wwwwwwpppwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffpppfffpppfff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffpppfffpppffffffffffffpppfffpppfffpppppppppfffppppppppppppppppppppppppfffpppfffpppfffffffffpppppppppppppppfffpppppppppfffwwwppppppfffwwwppppppppppppppppppppppppfffpppVVVVVV^^^fffpppppppppfffpppfffffffffpppppp^^^^^^ffffffffffffpppffffffffffffpppwwwppppppppppppNNNGGGfffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^^^^^^^wwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁî++ÛvvΨ¨ÈÁÁÐÂÂÚ““ôú��ú��ú��Ý__ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª–––ú��ú��†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwppppppppppppppppppppppppfffffffffffffffffffffffffffpppfffffffffppppppppppppffffffpppfffpppffffffpppfffppppppppppppppppppfffpppfffppppppppppppppppppwwwwwwfffpppppppppffffffffffffppppppppppppppppppfffppppppppppppfffppppppwwwppppppppppppppppppppppppppp^^^NNN^^^fffppppppfffpppfffpppfffffffffpppfff^^^ffffffffffffpppfffffffffffffffpppfffppppppwww^^^GGG^^^pppfffffffffpppffffff^^^ffffffffffffppppppffffffffffffpppfffffffffffffff^^^ffffffffffffwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––––––––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁú��ú��ú��ú��ú��ú��ú��ú��ú��è<<·±±ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwppppppwwwpppwwwpppwwwpppfffpppfffpppppppppfffppppppfffpppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwwwwwwwwwwppppppppppppppppppffffffppppppppppppppppppppppppppppppppppppfffppppppwwwpppwwwppppppwwwppppppfffppp^^^NNNVVVfffwwwpppfffppppppfffpppfffppppppffffffffffffffffffffffffffffffpppffffffffffffpppwwwfffNNN^^^pppfffpppfffppppppffffffffffffffffffpppppppppffffffpppfffffffffffffffffffff^^^fff^^^ffffffwwwwwwwwwwwwwwwwww†††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––––– ––––––––––––––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°ªªª ªªª°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁË››ßXXï))öù÷ òåEEщ‰¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††www†††‘‘‘†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwpppppppppfffppppppwwwpppppppppfffpppppppppppppppppppppppppppwwwpppppppppppppppwwwppppppppppppwwwwwwwwwwwwppppppppppppppppppfffpppppppppppppppppppppppppppppppppppppppfffppppppwwwwwwpppppppppwwwpppppppppppp^^^NNNNNNfffppppppppppppfffpppfffpppfffpppffffffffffffffffffppppppffffffpppfffffffffffffffppp^^^GGGVVVfffppppppfffppppppffffffffffffppppppfffppppppfff^^^fffppppppfffffffffffffffVVV^^^^^^^^^fff^^^wwwwwwwww†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––––––––––––––‘‘‘––––––––––––––––––––––––‘‘‘–––––– ––– ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°ªªªªªª°°°°°°ªªª°°°ªªª °°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°ªªªªªª –––––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwpppwwwwwwpppwwwwwwwwwwwwwwwpppwwwpppppppppwwwppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwpppppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwppppppppppppwwwppppppwwwwwwwwwpppppppppwwwppppppppppppwwwwwwppppppwwwppppppppppppffffffVVVNNNfffwwwppppppfffpppppppppfffppppppppppppfffpppffffffpppfffpppfffffffffffffffffffffffffffGGGEEE^^^ppppppfffpppfffffffffffffffffffffpppfffpppffffff^^^pppffffffppppppfffffffffVVV^^^ffffff^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘––––––––––––––– –––––––––‘‘‘–––––––––––– –––––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°ªªª °°°°°°ªªª°°°°°°°°°ªªªªªªªªªªªª ªªªªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°ªªª ––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppfffpppwwwpppwwwwwwwwwwwwwwwpppwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwppppppfffVVVVVVfffpppppppppppppppppppppfffpppppppppppppppfffppppppfffppppppfffpppfffffffff^^^fffppppppVVV===GGGfffpppfffpppppppppfff^^^fffffffffpppfffffffffpppfffffffffffffffffffffffffff^^^^^^^^^ffffffffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘––––––––– ––– –––––– ––– ––– ªªª°°°°°°ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªªªªª ªªª°°°°°°°°°°°°¶¶¶°°°ªªª–––†††–––ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ªªªªªªªªª –––‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^pppwwwpppppppppwwwpppppppppppppppppppppppppppfffppppppffffffpppppppppffffffffffffppp^^^EEEEEEfffpppfffffffffpppfff^^^ffffffpppffffffffffffpppfff^^^fffppppppfffffffffffffffffffff^^^fff^^^ffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––‘‘‘‘‘‘––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°–––†††‘‘‘ªªª°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘–––‘‘‘†††www††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^ppppppwwwpppwwwwwwppppppppppppwwwpppfffpppfffpppfffpppppppppppppppfffpppffffffppppppfffGGG===^^^pppfffffffffppppppfff^^^ffffffffffffffffffffffffffffffffffffppppppfffffffffpppfffffffffffffffffffff†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––––––––– –––––– ªªªªªªªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª †††www†††–––ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªªªªª ªªª ––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††–––‘‘‘†††††††††††††††††††††††††††wwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppfffpppppppppppp^^^fffppppppwwwVVV===VVVppppppfffpppppppppfff^^^fff^^^^^^^^^^^^NNN^^^fffffffff^^^^^^pppfffffffffffffffppppppffffffpppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°° †††fffwww‘‘‘ °°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ––– –––––– –––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwww†††www†††wwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwppppppwwwppppppfffppppppfff^^^ppppppwwwpppGGGNNNpppwwwppppppwwwpppppp^^^^^^^^^^^^fffffffff^^^fffffffffffffffffffffppppppffffffpppfffpppppppppfffppppppppp†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––––––––– ––– ªªªªªª ªªª ªªªªªª ªªª ªªªªªªªªªªªª°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°° ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°†††fff^^^–––ªªª°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°ªªªªªª ªªª ––– ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††††††††††††††††††††wwwwwwwwwppp^^^VVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwppppppppppppppp^^^fffwwwwwwwwwfffVVVppppppwwwwwwpppwwwwwwfff^^^^^^^^^ffffffffffffppppppppp^^^fffffffffffffffffffffffffffffffffpppfffpppppppppppp‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––––––––––––––‘‘‘––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°ªªªªªª‘‘‘ppp^^^fff‘‘‘ ªªª°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªª ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwfffVVVfffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††pppppppppppp^^^fffwwwwwwwwwpppffffffwwwpppwwwwwwwwwwwwffffff^^^^^^^^^ffffff^^^fffpppfffffffffffffffffffffffffff^^^^^^fffpppfffffffffpppfffpppppp‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘–––––– ªªª ªªª ªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªª ªªª ––––––‘‘‘–––ªªªªªª°°°°°°°°°°°°–––wwwVVVVVVwww ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªªªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††www^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfff^^^wwwwwwfffpppppppppwwwwwwwwwwwwpppfffVVVVVV^^^^^^VVV^^^fffffffff^^^fffffffffpppfffffffffffffffpppfffpppffffffffffffppppppppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘–––––––––‘‘‘–––––––––––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª –––‘‘‘†††––– °°°°°°°°°°°°ªªªVVVNNNfff††† ªªªªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††wwwwww†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwppp†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwpppfffwwwwwwpppppppppwwwwwwwwwwwwwwwppp^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffffffff^^^ffffffffffffpppffffffppp^^^ffffffppppppppp††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––––––––––– ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªª –––––––––†††wwwffffffwww‘‘‘ªªªªªª°°°°°°ªªª–––fffVVV^^^––– ªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªªªªª –––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††pppwww†††††††††wwwwwwwwwwwwwwwwwwfff^^^wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffpppfffffffff^^^fff^^^ffffff^^^fff^^^VVV^^^^^^^^^ffffffffffffffffffppp^^^fffppppppffffff†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª–––‘‘‘†††ppp^^^VVV^^^www ªªªªªª°°°°°° www^^^^^^www‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª ªªª ––– –––‘‘‘––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††wwwwww†††††††††††††††wwwwwwwwwwwwwwwVVVNNNwwwwwwwwwwwwwwwwww†††wwwfffppppppwwwpppppppppfffffffffffffffffffff^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppppppppfff††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– ªªª ªªªªªªªªªªªªªªª ªªª ––– ªªª ªªªªªª †††pppfff^^^NNNVVVfff††† °°°ªªª°°° †††fffVVVppp‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªª°°°ªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††www††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††wwwwwwwwwwwwwwwwwwGGGGGGwwwwwwwwwwwwwwwwwwwwwwww†††wwwpppwww†††‘‘‘††††††wwwwwwwwwffffffffffff^^^^^^VVV^^^^^^^^^fffppppppfffpppffffffpppfffpppffffff‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– ––– ––– ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª †††wwwfff^^^VVVVVVNNNNNNppp‘‘‘ªªª°°°°°° †††ppp^^^fff††† ªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶ªªª °°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªªªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††wwwwwwpppGGGEEEpppwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘–––––––––‘‘‘††††††wwwwwwwwwpppfff^^^VVVVVVVVV^^^^^^fffffffffpppffffffppppppppppppfff†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––– †††www^^^NNNVVVVVVNNNNNNVVV ªªªªªªªªª‘‘‘ppp^^^^^^www‘‘‘ ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°ªªªªªªªªªªªª°°°ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°ªªª°°°°°°ªªª°°°°°°ªªªªªª ªªª –––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††wwwwwwwww†††www^^^NNNfffpppwww‘‘‘–––‘‘‘‘‘‘––– ––––––‘‘‘††††††wwwwwwfff^^^^^^VVV^^^VVV^^^ffffffffffffpppfffpppfffppp^^^^^^††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ––– ªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––––––‘‘‘www^^^VVVNNNNNNNNNNNNNNNfff‘‘‘ ªªªªªª–––^^^^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°ªªªªªª ––– ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††wwwwww††††††wwwffffffpppwwwwww††††††††††††––– –––––– ––––––‘‘‘††††††††††††wwwfff^^^^^^^^^^^^^^^^^^^^^ffffffpppppppppffffffffffff‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––– ––– ªªª ªªªªªªªªªªªª ªªª –––––––––‘‘‘www^^^NNNNNNNNNNNNNNNNNNVVV–––ªªªªªª–––†††fff^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ––– ––– –––––– –––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††www††††††‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www††††††††††††wwwwwwwwwwww†††††††††††††††‘‘‘––– ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppppppfffffffff^^^^^^VVV^^^fffpppfffpppppp^^^^^^^^^††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––––– ªªª –––––––––‘‘‘www^^^NNNGGGNNNNNNNNNNNNNNN^^^‘‘‘ ªªª †††ppp^^^fff‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªª ªªª ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www††††††–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘††††††‘‘‘††††††‘‘‘†††††††††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘––––––ªªª –––––––––‘‘‘–––‘‘‘††††††wwwwwwffffff^^^^^^^^^ffffffpppppppppffffff^^^^^^‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––––– ªªª ªªª ––––––†††www^^^NNNGGGNNNVVVNNNNNNNNNVVVwww––– ªªª‘‘‘ppp^^^^^^www‘‘‘‘‘‘––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªª ªªªªªª ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––– ––––––––––––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘www††† ªªª ––––––––––––‘‘‘‘‘‘‘‘‘wwwwwwppp^^^^^^fffffffffwwwpppffffffffffff^^^†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––– ªªª ªªª ––––––†††wwwfffNNNGGGNNNVVVVVVNNNNNNNNNfff‘‘‘ –––^^^VVVfff†††‘‘‘––––––––– ––– ªªªªªª ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ªªª ªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––––––––––––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘––––––www††† ªªªªªª ªªª –––––––––––––––‘‘‘––––––‘‘‘‘‘‘†††‘‘‘†††wwwppppppffffffppppppwwwfff^^^fffffffff†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ªªª ªªª –––‘‘‘wwwfffNNNGGGNNNNNNNNNVVVNNNNNN^^^www–––ªªª †††ppp^^^fffwww‘‘‘‘‘‘––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªª ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª ––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††www‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘––– ––– ––– ªªªªªªªªª ªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppppppppwwwpppfff^^^fffffffff††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘wwwfffNNNGGGGGGNNNNNNNNNVVVNNNVVVppp‘‘‘ ‘‘‘wwwfffppp†††––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª ªªªªªª ªªª ªªª ªªªªªªªªª ªªªªªªªªª ––– –––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘––– ªªª ªªªªªªªªª ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwpppwwwwwwpppfffffffffffffff†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––‘‘‘fffNNNGGGGGGNNNVVVVVVVVVNNNNNN^^^–––ªªª †††pppfff––– ªªªªªªªªª ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªªªªªªªªªªª°°°ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªª ªªª ªªª ––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††–––––– ªªª ––– ––– ––––––––––––‘‘‘www††††††wwwwwwwwwwwwpppfffpppffffff^^^^^^††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––––– ––– ––– ––– ––– ––––––‘‘‘pppVVVGGGGGGNNNVVVVVVVVVVVVNNNVVVppp‘‘‘ ªªª‘‘‘ppp‘‘‘––– °°°°°° ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªªªªªªªªªªª°°°ªªªªªª ªªª ªªªªªªªªªªªª °°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ––– ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘†††‘‘‘†††‘‘‘–––‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘–––––– ªªª –––––– ªªª ––– –––††††††††††††wwwpppffffffpppffffff^^^†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––––––––––– ––– ––––––––– ––– ––– ªªªªªª ––––––†††pppVVVGGGGGGNNNVVVVVVVVVVVVVVVNNN^^^––– †††www†††––– ªªª ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª ªªª ªªªªªª ªªªªªª ªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ªªª ––– –––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††www††††††††††††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††††††††–––––– –––––– ––– ªªª ªªª –––––– –––‘‘‘‘‘‘‘‘‘‘‘‘†††wwwffffffffffffppppppfff \ No newline at end of file diff --git a/tutorial/tracking/model-based/hybrid/teabox.xml b/tutorial/tracking/model-based/hybrid/teabox.xml index d25cdf11000c9d66a4fc1e055c604cc7f6aa9568..41a1afbf8e84411727a564d89507b0e32e571735 100644 --- a/tutorial/tracking/model-based/hybrid/teabox.xml +++ b/tutorial/tracking/model-based/hybrid/teabox.xml @@ -18,13 +18,6 @@ <step>4</step> <nb_sample>250</nb_sample> </sample> - <face> - <angle_appear>70</angle_appear> - <angle_disappear>80</angle_disappear> - <near_clipping>0.1</near_clipping> - <far_clipping>100</far_clipping> - <fov_clipping>1</fov_clipping> - </face> <klt> <mask_border>5</mask_border> <max_features>300</max_features> @@ -41,5 +34,12 @@ <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> diff --git a/tutorial/tracking/model-based/hybrid/tutorial-mb-hybrid-tracker.cpp b/tutorial/tracking/model-based/hybrid/tutorial-mb-hybrid-tracker.cpp index 1e5b22c145aeaf8ec5a2544e83931a8505a24a2b..3bdd6ed0c1bbbdc38ce6b40c1c65ca1fc8f5255e 100644 --- a/tutorial/tracking/model-based/hybrid/tutorial-mb-hybrid-tracker.cpp +++ b/tutorial/tracking/model-based/hybrid/tutorial-mb-hybrid-tracker.cpp @@ -1,83 +1,131 @@ /*! \example tutorial-mb-hybrid-tracker.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpImageIo.h> +#include <visp/vpIoTools.h> #include <visp/vpMbEdgeKltTracker.h> +#include <visp/vpVideoReader.h> -int main() +int main(int argc, char** argv) { -#ifdef VISP_HAVE_OPENCV - vpImage<unsigned char> I; - vpCameraParameters cam; - vpHomogeneousMatrix cMo; +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) - vpImageIo::read(I, "teabox.pgm"); + try { + std::string videoname = "teabox.mpg"; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--name") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + std::string parentname = vpIoTools::getParent(videoname); + std::string objectname = vpIoTools::getNameWE(videoname); + + if(! parentname.empty()) + objectname = parentname + "/" + objectname; + + std::cout << "Video name: " << videoname << std::endl; + std::cout << "Tracker requested config files: " << objectname + << ".[init," +#ifdef VISP_HAVE_XML2 + << "xml," +#endif + << "cao or wrl]" << std::endl; + std::cout << "Tracker optional config files: " << objectname << ".[ppm]" << std::endl; + + vpImage<unsigned char> I; + vpCameraParameters cam; + vpHomogeneousMatrix cMo; + + vpVideoReader g; + g.setFileName(videoname); + g.open(I); #if defined(VISP_HAVE_X11) - vpDisplayX display(I,100,100,"Model-based hybrid tracker");; + vpDisplayX display(I,100,100,"Model-based hybrid tracker");; #elif defined(VISP_HAVE_GDI) - vpDisplayGDI display(I,100,100,"Model-based hybrid tracker");; + vpDisplayGDI display(I,100,100,"Model-based hybrid tracker");; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV display(I,100,100,"Model-based hybrid tracker");; #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpMbEdgeKltTracker tracker; + vpMbEdgeKltTracker tracker; + bool usexml = false; #ifdef VISP_HAVE_XML2 - tracker.loadConfigFile("teabox.xml"); -#else - vpMe me; - me.setMaskSize(5); - me.setMaskNumber(180); - me.setRange(8); - me.setThreshold(10000); - me.setMu1(0.5); - me.setMu2(0.5); - me.setSampleStep(4); - me.setNbTotalSample(250); - tracker.setMovingEdge(me); - tracker.setAngleAppear(70); - tracker.setAngleDisappear(80); - tracker.setMaskBorder(5); - vpKltOpencv klt_settings; - klt_settings.setMaxFeatures(300); - klt_settings.setWindowSize(5); - klt_settings.setQuality(0.015); - klt_settings.setMinDistance(8); - klt_settings.setHarrisFreeParameter(0.01); - klt_settings.setBlockSize(3); - klt_settings.setPyramidLevels(3); - tracker.setKltOpencv(klt_settings); - cam.initPersProjWithoutDistortion(839, 839, 325, 243); - tracker.setCameraParameters(cam); - tracker.setNearClippingDistance(0.1); - tracker.setFarClippingDistance(100.0); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + if(vpIoTools::checkFilename(objectname + ".xml")) { + tracker.loadConfigFile(objectname + ".xml"); + usexml = true; + } #endif - tracker.setDisplayFeatures(true); - tracker.setOgreVisibilityTest(true); - tracker.loadModel("teabox.cao"); - tracker.initClick(I, "teabox.init"); + if (! usexml) { + vpMe me; + me.setMaskSize(5); + me.setMaskNumber(180); + me.setRange(8); + me.setThreshold(10000); + me.setMu1(0.5); + me.setMu2(0.5); + me.setSampleStep(4); + me.setNbTotalSample(250); + tracker.setMovingEdge(me); + tracker.setMaskBorder(5); + vpKltOpencv klt_settings; + klt_settings.setMaxFeatures(300); + klt_settings.setWindowSize(5); + klt_settings.setQuality(0.015); + klt_settings.setMinDistance(8); + klt_settings.setHarrisFreeParameter(0.01); + klt_settings.setBlockSize(3); + klt_settings.setPyramidLevels(3); + tracker.setKltOpencv(klt_settings); + cam.initPersProjWithoutDistortion(839, 839, 325, 243); + tracker.setCameraParameters(cam); + tracker.setAngleAppear( vpMath::rad(70) ); + tracker.setAngleDisappear( vpMath::rad(80) ); + tracker.setNearClippingDistance(0.1); + tracker.setFarClippingDistance(100.0); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + } + tracker.setOgreVisibilityTest(true); + tracker.loadModel(objectname + ".cao"); + tracker.setDisplayFeatures(true); + tracker.initClick(I, objectname + ".init", true); - while(1){ - vpDisplay::display(I); - tracker.track(I); - tracker.getPose(cMo); - tracker.getCameraParameters(cam); - tracker.display(I, cMo, cam, vpColor::red, 2, true); - vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); - vpDisplay::flush(I); + while(! g.end()){ + g.acquire(I); + vpDisplay::display(I); + tracker.track(I); + tracker.getPose(cMo); + tracker.getCameraParameters(cam); + tracker.display(I, cMo, cam, vpColor::red, 2, true); + vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); + vpDisplay::displayText(I, 10, 10, "A click to exit...", vpColor::red); + vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; - vpTime::wait(40); - } + if (vpDisplay::getClick(I, false)) + break; + } + vpDisplay::getClick(I); #ifdef VISP_HAVE_XML2 - vpXmlParser::cleanup(); + vpXmlParser::cleanup(); #endif -#ifdef VISP_HAVE_COIN - SoDB::finish(); +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + SoDB::finish(); #endif - + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV and rebuild ViSP to use this example." << std::endl; #endif } diff --git a/tutorial/tracking/model-based/keypoint/CMakeLists.txt b/tutorial/tracking/model-based/keypoint/CMakeLists.txt index 164edb88380913d2debf4a4e017eed45d44311fe..9723f386ed876edc88c1a571cc34053e649c25ff 100644 --- a/tutorial/tracking/model-based/keypoint/CMakeLists.txt +++ b/tutorial/tracking/model-based/keypoint/CMakeLists.txt @@ -3,25 +3,26 @@ project(tutorial-tracking-mb-keypoint) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-mb-klt-tracker tutorial-mb-klt-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-mb-klt-tracker.cpp) -# copy the data -get_target_property(target_location tutorial-mb-klt-tracker LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.pgm" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox-triangle.cao" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-mb-klt-tracker - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.mpg" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.xml" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.cao" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox-triangle.cao" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.init" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/teabox.ppm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-mb-klt-tracker.cpp ${data}) endforeach() diff --git a/tutorial/tracking/model-based/keypoint/teabox-triangle.cao b/tutorial/tracking/model-based/keypoint/teabox-triangle.cao index a63b13eefa1ff322e7a2bed58e11167234073b1b..e8053825aa74ded63dfec8a4fd1ad56c5f11ab03 100644 --- a/tutorial/tracking/model-based/keypoint/teabox-triangle.cao +++ b/tutorial/tracking/model-based/keypoint/teabox-triangle.cao @@ -1,17 +1,21 @@ V1 -8 -0 0 0 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z 0 0 -0.08 0.165 0 -0.08 0.165 0 0 0.165 0.068 0 0.165 0.068 -0.08 0 0.068 -0.08 -0 0.068 0 -0 -0 -12 -3 0 1 2 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +12 # Number of faces +3 0 1 2 # Face 0: [number of points] [index of the 3D points]... 3 0 2 3 3 0 3 7 3 3 4 7 @@ -22,5 +26,8 @@ V1 3 5 3 2 3 5 4 3 3 7 6 1 -3 7 1 0 -0 +3 7 1 0 # Face 11 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/tracking/model-based/keypoint/teabox.cao b/tutorial/tracking/model-based/keypoint/teabox.cao index 4e6dd5c45d46f8971d2800f333a116dacd0e7078..83c1fdf783e68e78dfa381d4c326f07d88fa8d3f 100644 --- a/tutorial/tracking/model-based/keypoint/teabox.cao +++ b/tutorial/tracking/model-based/keypoint/teabox.cao @@ -1,20 +1,27 @@ V1 -8 -0 0 0 +# 3D Points +8 # Number of points +0 0 0 # Point 0: X Y Z 0 0 -0.08 0.165 0 -0.08 0.165 0 0 0.165 0.068 0 0.165 0.068 -0.08 0 0.068 -0.08 -0 0.068 0 -0 -0 -6 -4 0 1 2 3 +0 0.068 0 # Point 7 +# 3D Lines +0 # Number of lines +# Faces from 3D lines +0 # Number of faces +# Faces from 3D points +6 # Number of faces +4 0 1 2 3 # Face 0: [number of points] [index of the 3D points]... 4 1 6 5 2 4 4 5 6 7 4 0 3 4 7 4 5 4 3 2 -4 0 7 6 1 -0 +4 0 7 6 1 # Face 5 +# 3D cylinders +0 # Number of cylinders +# 3D circles +0 # Number of circles diff --git a/tutorial/tracking/model-based/keypoint/teabox.init b/tutorial/tracking/model-based/keypoint/teabox.init index f326e929605734e745c9e9dff9c19939d67d9f75..f109800a0eab96b97050f4872839f09b0a0c2bd3 100644 --- a/tutorial/tracking/model-based/keypoint/teabox.init +++ b/tutorial/tracking/model-based/keypoint/teabox.init @@ -1,6 +1,5 @@ -4 -0.000 0 0 -0.165 0 0 -0.165 0 -0.08 -0.165 0.068 -0.08 - +4 # Number of points +0 0 0 # Point 0 +0.165 0 0 # Point 3 +0.165 0 -0.08 # Point 2 +0.165 0.068 -0.08 # Point 5 diff --git a/tutorial/tracking/model-based/keypoint/teabox.mpg b/tutorial/tracking/model-based/keypoint/teabox.mpg new file mode 100644 index 0000000000000000000000000000000000000000..50fb8ad2d9e245e2bcc06b94d22763a07d9b6191 Binary files /dev/null and b/tutorial/tracking/model-based/keypoint/teabox.mpg differ diff --git a/tutorial/tracking/model-based/keypoint/teabox.pgm b/tutorial/tracking/model-based/keypoint/teabox.pgm deleted file mode 100644 index 41833d705da80194d014d5e4a442d1a060ddd435..0000000000000000000000000000000000000000 --- a/tutorial/tracking/model-based/keypoint/teabox.pgm +++ /dev/null @@ -1,73 +0,0 @@ -P5 -640 480 -255 -IKJKKJJJLKJKKJJKKKKKKLLMMMNNOOQONNOPPQPQQQQQTSSSTSUTWUTTWVWWXXYZYYZZ[\[\]]]\`^^_```^^`aababa`__ZUQPPQQSRQRUUVVUUWVWWXVUUVWWUVVVVXVVVYWVVVVWWUUVUUWWWVXWX[YXZ\[[\\[[[[[[XXYXWYXXXXYYWXWWXZZZZ\[]\\]_]]^_^_`c__bcacbaa``a`_`^^b`__aaaaa`b```a```____aababacc`ababaabbaaaabc```a___`bcbbbdddcccfbc``___^_aacbb``^]^^_d```b_`baaeee``acbcccbaeffeddeda`aa`abfdbbbcdddcdddfheffggfddefdcbb`abbbe`_`a`ccaaaaba``_accddeddcccgbc`aa`abbcca`bb````bbbaa__ac`_``^_^`]_`_^^_^\\\\\\YZXZ[[Z]Z[XYZYXXYYXZXXYZ[]\[[[\`\[[\[[[\YXYYW\WUUWVWWYVUVYXXXZWVVVUUUSTWTRRVTVUTSSSURRRSSTSVSSSRRQRTSSSRSUTVSTRQQQPPPQPOOPMNLKMOMNMLLLLKLMLLKJLKKKJLKKJKKMLKJIIIIIHHGGGHHIGGGFEGGGFEEFEKMKKLKKJKLKLLLKKLLLMLLMMMMNNOOPOPRPQQOPQQQQQRSSSSSUTUUUUWVVWXXYZZZZZ[\]]]_^]^__a`a`__`aaaababa_]VQQPQQRSRSTUUVUTWVWWXVVWWVWWWWXWXUVWVUVUWVWXWWVUVXVUVWVW[ZZZ[Z[YZZ[\ZZYXYZXWWVWZWXWVWWWVXXZY\\\\\]^\]_^a^^^__bbabcaaadba``_^___^`aaaa``_`aaa`_``__``abbabbbbaaa```baa`abba`_`___`ab`abccbbccbbca`_`a`abaccb``^^a^`aa`__^_aaabbb```abc_aabddceedbbaaa`_`bddcbccegecddeigfeefddccdddedc`bbbdd`````acb_bbbbcccbcddddedcccbbaabbbcccd`baaaaa`cb_aa```a```__^^]^^_b___\^_][\]\YYXXXXXYYXWYWXWY[ZYXWXYZ\\_^^]\]\\\\[[ZZZZYYXXWWVWWWWYVUTW[XXXWVVUUVUUUVUTSVTUVWWSTSRRRSSSSSSSSRQQRTQRTQTTUTRSRQQRPPPQOOONMLKLMMLMKKKLLLNNMLKLMLLKJKJJJKIJKKJJJHHIHGGGFGGIGHHGEFFFGEEEELLLLOMNMNNNNPLLLNMLMMMNNNNOOPOOOSQPPQPSSSSSRSTVVWUUUUVVVWWWWXXZZ[[[[]\]]]^_^___``acbbaaabccbaa`_VRRQRQQQTTWVVVUTVWXWXVVWXWVYXWXW\WVWVVWVXWWWVVVVUUUUUVUW[[\[[[ZYZ[[[[ZZXXXXV[WWWWWWXYWXXXY\[\\\[^^]^^^^^]]``_^_accbabbaaabd_`_`__`baa```abbaa`__``bcccfba`caaa````bac``bbbb^_``_bbb``bdcabdbbbca``cbb`aafffad`__^_aac_^^`bbabba`aaabd``abciceefcaab`__`acbacdddeedfeefdejegeeddccefdddfbbbc__````bbcccecdccbdefdddfcdbbbdbcdgccdeddc`ac```aaaabaa```b`__``a^_`___\^^^\[\\ZYXYXYY[YXXYWXX[\\XWXYZZZ\^^__\^]\\\[\YYZ[ZYZYXWVWXXXYWWVVWWXYYXXWWXWXUXWXTUTTUUTSSRRSRUSSSTRSSSQRRTRQQPPRSSSURQQRQQQQOONMNOONLLLMLNMLLLLNLLLMMMMKJJJJJKIHIKIJIIGGHHHHGGHIHHIIGGGFEEFHELLLMNMNONMNNNMMMMMNNNNNPNNNNOPPOOOOONNPQTTSSTVUVVWWWWVWXWWXYXXZZZ[[\\\]]^^^^^^`aaabbaaaaacddcca`YSSPRQRPRUUVUUUUVVWWWVVWWXXXYWVVWVWWWWWVWXWVVVVWUUUVVWWWY[\\[[ZY[[[][YZXXWWVZWWWWVWYYWWXZ[ZY[\[[]_]\^^^^^^_`__`ba`aaa`aaba`_`____`aaa`acbbbbaa`aaabbcccba``_``aaaab```aaaeb_`a```___`abbbababccaaacebaabbbbbbc`a`_`ab___aca_cbbbbcbadcadccecddedbbb```_`cabdddeeecfeeedddefededcchedccb`baa`abaaaca`abdbbbbab`bbdccceaaaecccccdgdddecba`````aa`_````aa``_^a^___]]\^^]\\[ZZYYXXYZ[YXXWWXYZ[[WXXYYZZ\^]]\\]]\[\ZZYZZZZYYYWWVWWXWWWVUWXXXXYYYYYWVWUVUUTVUUTTSSRRSRQSUUVSRSSRQQRSRQQQPQSRTSQQRPOPPPOPNNONMMKLLMKKJLJKKLLLNMLLMKKJIJKJIHIIIJGHFGGGFHGGIIHHHGFGEFEFFHENNONOPOOOPPOONONPOQOPNMNOMJKLKJIHIJKLLPQVTTSVVUVVWWWXWWXWWXY\Y[Z[Z\\^^]^^^`___caaacbaacbbbacddb`[VSQRQQQRVUUTTWWXVXXYYYWXXZYYX_WVVWWXWWWVXVWWVWXUVXXXWZYY[_\\[[[\[[[][ZYYXZX[YZZYWVYYXXY][ZZ\ZZ[]]\[^]]^a____````__aaaaacbaa`_`__`abbbbcfdfdgfeccbdcfdbacab```aaebca`_caabba_`abbaaaaaabebcacbdcbadcabcdecccbaabb`cbbccbaa`adbbbbbcadcbdccfgheddccedd```bbdccefefffedffffdddddccddddhcc`eabbbbbabbaa`adbcbddccccdcdeeedddcccccedccddeaa``a``aa__``caaa_a`_`___^]^^^]]]^[Z[_YXXYZ[Z[XXX[Z^Z[[[\]Z]Z\\\\[[\^`[\ZZZZZZYXXXXWWXXZZZWVVVX[XXY\[]YWXYVWWXVWVWTSSTRRSSQRSTTRRTSURRRTRQQRPQQQQQQQPOOSPPQQNNNNNMLLLMLKKLKKJKLMMNMMLLJKJIIKJHGGHJIIGGGJGGGFGIIJHHGIIIFGFHGNNNMNQPPOOOPPPPOONOPPPHHA==>@CBABCCDFGJMOPPPQRSWWXXXYXXYWWWWXXXYYYZ[\]\\\]^```a``aacb`bdccccdgee]VSRQQQQSVUWUSWVWWXXYYYWWWWWWXXXWWVVWWWXVXXXXWWXWVWWWWXYZZ\\[[\Z[[[[ZZZXYYYWYYYZYXXYYYZZZZZZ\YZ\\^\[^]]___`aaaaa`a_`aa`_bbaa```___`cbcbbccdddeedcbbbdecbc```bcaabbbb`_`abbbaaabbaaaabbbddbbabadeaababeccceddcbcca`d_aaaba``aabbdabbbdeedbbfffeeedcefd`aabcccdeeeeefheeeedcddeccdddddcbcaeabbbfabbbbbbaaaaacecbcddcefebccccdedddbccdddaaaaaa`ba`_``abaeaa`a`___^]^_^][Z^Z[[[[YXYYYZYXXXXYZZ[[[\[XYY[[\\\^]`^\[ZZ[YXXXXYYZWXXXYXXWWWWWWXXXZZXWWXXWWUWVVUVTSRSSRRRRSSTRRRTRQQRSRRRQPOPPPQQQQPOOPPPQQONOOONMLJLKKJJIKKKLLNMLLKJJJIIIIIHGGHHIIJHFIHHGGGHGHIHHHHIFEFIGOOQONQQQRPOPPQRQQOPOLD@;53257889;?A?=?GDBDHKLMOQRTXWYYYYXVSSUWZXVVZ[\\][Z\^_a`bacaabdbbdccccdfge_VURQRTSSTUUUVVVVWYXZZ[XWVVWWXXWWWVWZXXWVVVXYXXWWWWXYXXY[Z\\[[\[Z[\[ZZZZZ[]Y]ZYXWY\Z[[\[[ZZZ\Z[\[[]]^]]_a`aafddb`_``bac`cbaac`_`a````bdbddddcccdebbbdddccadddcbaaacaa``bbabbdcdb``abbbcbcdccdaecaaaaddccbdeecdedaaddda```````abaabcdfeebbcgggffffdgeeeeeefefeddegeefeegggdddddabcdbbbbccfabbbbabddddeaaabbceffgddcifecccfeeddddbfcddccdccccbbba`_`abbbaaba`_b_`^^^_][\^[\\\\ZZ[XXZYYZYYZZZ[\[Z[Y[[^[\\\]]^_^_[ZZXXYXXXWWWWXWYWXWWWVVWWWX[YWVXWXXYUWWUUWWWSSTTQRTWTTRQRTRRQQQQRUQPPOOPQRQQQPPQPPOPPOONNQOONMLKJKJLKKKLLMNNLOJLJIIIJHHJHIHHHIIIIKGGHIHGHHHHHIGFGHFONQQPPRSRSQQQRRRONFC=8644010333368887666789:<>CHLORUW\Z[PEDELSNIMRV\\WTRW\]aa``_caabddccbbccdeff^VSQQSTTTTUUUUVVWXWXY\ZYWVWXXXWVXWXYXWWVVUVYWVWWWWXXXZYXY[[Z[\[ZZ][Z[\ZZ[\ZXZZZXYYZZ\]\^\\\^[Y[[[[]]^^__^^``ceca`__^_```c`aca_```c````abcddedddccbbbcdcbc`accebaa````_`bbaaabcbb``acabcbcecbcabbcdb`bbcecceggfddbbdebcbaac``a`abbbceeeeabcefghgffddddddcddddecccdddeeefdefeheecbddccccccccbaaaaabccdcbb`bcdedcdedceeecccddecddccedcccecccedcb_`aaccea`aab`````____^]^^][\^\\[[YXXZXXY[[Z[\\\[ZYXZZYY\]\_^]\Z[[[^YZYXX[XXWWVVWWWWWWWWWWXXZWWVXWVUUUWWUUUUWSTUTQRTSRSSRQRTRQQQQQRQQQPOQQSTQQQRQQPOOPPPNMQPONMLKJJIJKKLKKLONKKJJKKJIJIIIHHHHGIJIHHHGGHJHJHHHIIHHGFEQPPQRRSSRRRRSSTQOD:415578633320334422.*)()*+-04:@FNRUYY[J;6=GII>>ITZ\TKLUY]`bbbccacbecccccbcdefe^VRQQRSTVTRSTUUUTVXVVYZYWWZYYXWXXWWYWVVVVUVWWVYXYYYYXXZXXYYZ[]][Z[[[[[Z[[\YYY[ZX\[[Z`^\]^]a^[Z[\^\]]]]]_caaacda```a_^`a`c`aa``aaaa``__abeeeeedccccbbcccbd``cbba``````_`bda`_abbccba`bddcbcecedddfeebbcabeffffffcccecacddcc_`cccdddddefecbdefghggghhdddfehefdeddccdefgefeedceeeddgddceeecdda``aaabccbbccbeeddccdeccddedccccecebbdhcccdbbcccdca``aaabb```aa`_`aab``^]]]^]\\\[\[ZYYYXXXW[Z[]Z[[\ZYYYXXX\\]]]\[[[[Z^YYXYXXXWVUUVVWWVXXXXWXZYYWWVXUTTUUWVUUVWXUUTTRRSSRRRSQQRQRSQSSRQSQPQVSTRQPQRQQQQQPPPQQQQRONMKJJKNLKKKKKMOMNJJJIIIJKIIHGGHFJHHIIIJGHGGGGHHHHHIGGFRRRRRSTSSRSSSTSPD:506:@FEDCA=86432.)%" #(,5?ENRWX[G656:>:7:?OYXRJ@JUZ^`cbaaabbbbcddcccegfd^URQQTSSSSSSTUUUUUUSUWWXVVVUWXWYXVWYWVUUUVVWVVXWXYZVXXXYXXYY[^][ZYYYYZZZ[\\[[[ZXZZ[Z[\\[\[]\\[]_^[]]^^]_bbabcdbaaa`__`a^`_`_`___bc`___aabdeeecccbgbbcdaa`^`bba`__`a```a````^abbabca`bdcbbbcbcddeedeccccbcefhfdecccdccbcccb__`bbdecegehfeeefefgfeeegcdffdddfdefedeffihegfedcfeeeddbbcedecdecaa`bcbbcbbbbbeedbcddfeedceefeeddbcabddbcdbbbabbcbaa`aa_bb`aabbda`aaa_^]\[]]]\]][\ZZZXXXXXWYZ[ZYZZZYYZZXXXZ[\\][]^[Z[][YYZXVVUVSVUVWWWWWWXXWWVWXWVVUUTTTUUTWUYWWUTSSSSRQQRRRQQRTSRSRRQRQQRRRRQQQQQRRPPQQPOOONNNNNNKJJLLKKKLLKLLKKJKKHHIGIIIJIHHFGGHKJIIHIFFFGHGFGGIGFFSSTSSSVVUUVTSTSI=64:@GNONOOMKHD;42+%#+4;EQWXYE60122257<IWZUI@@LY]_bdbacecccfefdddfgfe^USRRSSSSTWWVVWVUUTTYWVVVVVVVVUVUWXXWVZUWUVVZXXX[ZZY\XWXXXYYZ\][[YZYZ[]\\]\[[\]YZZ\[[\]\^^]\\\^^]]]^_^^`aabcabdbabe`a````_a``__`c``___a`acfegbbbbcabca``abbbca__bcc`dbbaaaa`abeabbbbcccceedcbdeegdededcbcegffffeccfedbdbb````abdeehgfgggifgffgeeiehedfhddcegffeefgghhgjfgdcdfefeedhdedddddddcbdbabddfcfciedcbeeeeeccefffgedddbccddecbccacccba``baaaaaabbaab_aa`]\\\[_]]\[\[[YZ[\\YXWVWZZ[YZZ\ZZ[[YXYZ[^\][^]\\\\]]^ZXVUTVTVUVWYXVWXXZVWWWWWVVVWUVUUTSTUWXVUUTSSSRRRRRSTRSSTRUSRRRRURSRSSSSSRRQQPRPPOPPOOOOPMLKJJLKKKJKLKIJKJKIHHHHIHIIJHIHGGJJKIIIIGEEHGGGHGIGIESSTUTTTVUSVTTTLC>9@IMRSTTSSTOMIC<4-%$,4=IVTUB60/.-.36<FU[XPGDDO[_bccddfdcbgeeeeefgff^RSSSRTWVUUUUUUTUVTUUUWVVVVVVVUVVWYXWWWUVUVVXYXXYZYYYWXWXZYXYZZ[ZXYYZ[\ZZZYYZZYY[[\\\[\\\\]\]]]]]]]]__````bccbdbaba_``_``___`__`c]^]]_a``afefbb`bccbdaada`aaccaabbb`dbbbaaaaaabaaabdcbcddccfdheddbfdedcccdedefeedccecbbbc_``aabcddfffgfffffgggfeedefedddccccefedghiihggfffddeeeeddfdcbcdecdedddabbdddcdcfeeeffeedddbeeegfeedcbccdddbb_`ab`bba_`aaaaaac`a`_]]^^]\\\\\\[\\]\\\[ZZ[[ZYVVUZZ[YYYYZ[][ZZZ[[\\\[^\[[\ZZ[ZYXVUSSSUUVWWWWWWVWVWVWXXWWWWUUTTSSTTTTTVYVUSRSQQQRUSRRRRRSSSSRRRRRQQPPPQRQPQNOOONNMNMMOMLLKJILKKKKKKJIIIJKHHIIHHFGGHHHIGFHIIIIHHIEEHFFFHGIFFFTUUUUUUUUUVTWUJC?FMRVWWVUVVUTSRLG:3( &-6BMVK<101.)).49CU\\[RGBH[`ceeddfegggghghgfhgg]TVTTTUVWVUUUVUTTUUUUUWUVVWWXUVWXWXXWWXWWVWXXXXXXYYX[XXW[YXXXXXYYYYZ\\]\\[[[]YZ[\\\]\\\]]]]^^]`]\\_^^^`__`baaadcab``__ccc`_^````b_^]\]ba`beefffbbbdbdabcbaabcccddcbbdbdbbaaabceedbbcddgddbacddcccdffeccddddeeddddddccdgdccbaaabdedhffeefifkhihggggffhdccccddegefkikihgggeddeffeeedfdcccdddcefedddceeddefgeeeddeecdfedddefefdedgfffhcb_^acbbbc```cba`ac_^^]\]^\[\_]^]\\\]_^`]\\`]\ZZXXXY[Z[[XXZ\][\[[\\_]\[^\[[^ZZZXXWVYUTUUUUWWXZXXVYVXWVWVWZXXVTTVSTSSSSSVUUUVSSSRRTSRRSRTSSSSSSRRQRQPPQPQQOPPOOOPNNMNMLLLLMLLJMLNJJJKJJIIIJGIIIILHFFIIHIIGHIIHIJJIEFGGGGHIIFGFUVUTUWVUUUVUUUMHLPSXWVWXWWWXYZRQD=1*" '/:EBB2(+2*&')05DV_`^\OEIYadeeedeefgfigfhiihhh_TTTUUVUUVVVVUUVUTUUVVWUVVWVYVWXXWY[[ZYXYXXYYYXXXXXXXWWWWXWXWXYYYYZZ[[[[\[[ZZYZ[\\\]]\]]]^]^^]]]]]]]^]^^^_b_aabeaa``_^`aaba_`a``b`_^^^``acbbbbbbbabbbbbbbbbcbbcccccceabaabbbccdccccbcccbbbbbbbdadgfedcdgffdedcccb`acdddccbbabbddddeeefffhggghdhggfeeeedcddefffffggghggfifddeghgeeedbccceddegfeeedbegdcegffdddceheedddddcdeeccddddeedb`aadecbca`_a_`dab`_^]]`^[[\]\]`_^]]__`\]]]\[[ZYXYZ\ZZ[XYYZ][\[Z\]]^\[]\[[[[Z[YXWVVUUUUUUWVXZVVUUUWXWWVWXXVUTSTTTRRRSRTTTSTTSSRRTSRRRQRRRTRRRRRQQQPPOOOPOOOQNPOLNMMMMLLLLLKJKKKJJKKIIJIHIHHIIIIHGGHJJJIHHIILIHIHHGGFFFGFEEFFWVVUVWWWVVVVVRQQQUZYYWZYYWWWXWSJB80)$")3<@0'('&$!"(-6EZaac\WHJZaffehggffhgghhjiiijh`TRTUVZWZXVVXUUVUUVVWVWUVWWWXWYYYXZZYYYY[\\[[ZXXWWYWWWYVVWVXWY[Y\[[YXYZ[^\]ZZ[\\_[\]]\^\\]\\^\\]\]a__^^``_a`baabb`^`^``aaad`aa__caa`_`aa`bcbaaaabbccbccddeffhdecbccceccaacdcedddfddbbccbeddabcddeegdceeeffdcbbdbaabddddccdddcdhdcddeeeefiihghiiihgieffgggfgffffgffhihfeegdcehgheeedccccdfegfdggfeddeedgfgfddddfeeeddfeecddddhdcccdddf`abdedccbaaa```aaa`^^]`^[\[^\\^b^]]c_`]_]\\[[[YYYZZYZ[ZZXXZZ\[Z\]]^^]_\\[[[ZYZXWVWWVUVUUUVWZVXWVUWWWWZXXWUTWSRTTRUTTTUUWTUUUSTRRRSRQQSQQQRRRQQQRQQQOOOOONNNNOPMNMQMNMLLLLNJKJLLLKKJIJHIJHIJLIKIHHGHIJIHHHHHIIHHIGHFFFGGEEEFVVVYYYWWUVVUSQRTTXXWWWYYZWVVUSJA92)# (1*$$*%#!%).9I]^aVSNHH[dfffgfggghgghijjjiig`SSTUVVVWXVVVTUUUVUUUWWVWXXYYXYYXXYYYZZZZ\\\ZYYYYWWXWVWVWXWWWYZYYWYXYZZ[\]]Z[[[ZYY[]\\\[\]\[[[\_]]]^______`^`aaa`__`_babaab``a`_a`aca`aabbccbaaaabdedcdeddeeedebbacccdcbccccddeeebccdddcddcbcddedcdcegfffedcbaaaaaceeedcddeeedddefdddeeegjgfghikhggffeffedgffceefffffeeeeedegffeffdcccddecdfggfeeeedecddegfccdfddieddcddeeddeccccgddeabbcedccdacb`__adbaa```^[[[]]]]^^^]_^`\_^\[[]ZZYZZZ[[ZZZWXYZ[[[[^]]\\\[[[[[ZYZWVUUUVUUVVUVVVVWVWSWYWWXXWVUUUSSSSRRSSSTSWTUUTTTSSQSQQPQPPORRQQPPPPQQPOOMNNMMNNNMNMNMNPNLLKJJJJKKKJJJJIIKJHJJJJIHHHHGHIIIHGHHIIHGGGGEFGGFFEEEWWWWXVUVVWXTSQSTVW[VYWYYZWVPLG@6/(" ##"(*/;OW\PG@<?G_eeegighiiihhhiijllkh`SRTWUVVWWWWWXWTSTUTTUVXZX\[]YZXXZ^ZZZ\ZZ[[ZYY]ZZYXWWXXWXXYWXYZZ[[[\]ZY[[\]\\\[[\ZZ[]\][\\\[[\\]\\]^`___bbb_abb```___`aaaaa``aa``aaaabcbgdcdddddeedfeedddegeddebbbfcdccbbcdddeeffdddgffeeeedcddeicddhgggheebbbdaabefigeeffefggfffedddddefggfghiiigfghffgcdheecdfifgefdeeefhfffffffddfddeeddefedegeiddedddfiecdfccefffcedeedddegccddddddcbcccbbbbca^_bcccdba`_^b\^^``_^`^b^`_`]ZZ[]YYYZ\[[\^ZZYYX[[][[\]]`\\[\[[ZZYZWUTTUXUUVXVWUUVVVWVWWWWYXXWWUUSTSTRSSSSSTWUTUTUWUTSSSSQRPRRQRQQPOPQOPSPONNMLMMLLMPMKKMOOLNLKJIJNKIJNLLJJIJIJJJJIJIIJHHHIIGHIIJIHGGGGFFGGFIFGGWXWVVVVWWWVSPNOORSTUWWWWUTMG?:0)# !#$$#'*-=CLD><5?AQbdefiihhiihhjihijkkkiaSRRVTUUVUVUUVUTSSSTTUVYYXZZZYZWY[[ZZZZZZZZZYZZ[[[[ZYYYXXXWWXXZZZY\][Z[[[[[[[Z]_\\Z[[[[[\Z[[ZY]^]Z^^_^_`_]__a_`````_^^_`aaabbba`abbabcccedeeedddeeefefddefhdddcbcdcaeccabcddefeeeeeffdegffeecfefebdehggggceabcbbccefhifefiffhifdeedddgfgggfeghighjggghfhddecdcdeddeeddfdeeffgigifedddeeeefeeeadefeecegfceeeeeeeaceegdcdddfeeeedddfdcefcbbbcecaaabaaabccddbaa_^^\]^``_]^^_]^^]\[^\]ZWZYYY[\[ZYYXWXYZZZZZ[[Z[XYYZZZYYWUSTUWTUUWWVUUVVVVXWVVWXYXWWUVSSSSSSTSSTUUUTTTTTSRQRRRRQPQSRQQPPPPPONSQONOMMNMKLLLKKKKKLMMMLJJJLKJIKLKJIHHHIKJJJJIIIHHHHJIIIIIIHGGIGFFGGGHFFFYXXWWWYXXXVSMIGKPQSSXWUSPJD90)" - - $%&$ "&',596556<DRadihhhhhkkkiiiiikkklnhaRQRVTVVWWXUTTUTTTTUVVVXYZ[YYYZYYZZYYZZZ\YZZZZ[ZZYYYZ[ZYYXXXXYYZ[Z\[[[\[[]^]]___^][ZZ\Y[][\[\[^]^\^^aaaa``_`a_aab`b^]^^`dbbb``bbedfabccdfeeegddddefddedejfgdfdcbdebaeeebbcddefieeehhjfefgffeceeffedeifgghedbcddeheffkgffehfgihfedffdcdceeeeehhkjkiggefffdeddfddebcedbcfddegghiggfeifdegfefeddcdehdffgfmdgfefeeeccdddcccddeffhgkceedcfdcbbbcccacadaaafddddb``^]^^_]^`_^^]_\]\\[Z[[\[YZYZZ[[[\^YWWWZZYYY[[YY[XZZ^ZZY]WVVWVXUTUVVTUVVUUXXWVYVWWXVWVUTTRSTTRRSUUTTTTTSRRQQQQUUTSTSSQQPPOMMMNQQRNONNNMLNLLKJKKKLLKLLKLKNKJIIKLIIJLIJJJJHHHIHGHHIIHHIIJHHHJIGGGGJILGIFYWXYXXYYY[WSLEEGFGIKORLHB=2(" """ - -#.,,'!""$%,189>AEJR_dggghhhgjjjiijjjkkllkhbQQSTTVVWWVUUTUUTWWWVVVWWWXXY[[YYZYYYZZYYYZ[[ZZ[ZYXXYYZYYYXXYYZZZY[ZZ\\^^]^^]]^]]]\[[[[\[[[[[Z^\\[^^a`ab`_``a_`_``_^^^^^ccba`_aacbbbccdeffecccddddeddeecfegbedccddbbdccbbbdeedeeffhhhhhggdfgegfgffdceeeffeedegggggfgkhhghhgggfggeeeedeedeeeegfffhhhffghiffddedeebcdddbfdeegghigfeefgedefffedddddeeeeggfeghfffeebcdefdbcdeeeffffcdddbccccbdb`a_``aaa`dfffcbab^\]^_]_ca__]_Z\[[ZZ[ZYZYYY[][\\\]ZYWXYYXXZZZYYYXYY[\ZXWVWWXVVTTUWUUUVUUTTUWUVVWWWVVVUUURSTSRSTTUSSTTTRSUQPRQTTSRRRQQPOOONMNNOOONNMMNMKLKJJJKJJKKKLKKJJLMKIIKJIIIIIJJJKIHHIHHHHHHHGHIKIIJIHHIHGGGGFFFYYYYYY[ZZZYUKDDB@?@CFED<4* #(+4:>843/& - -%07+(#!"%*.7>ELQWZbejgggjhhgiiijkjjkklnlkhdPPRUTXXWVUUUVVUTWWXYWWWWWWXXZ\YXXYYY[\YXY\[]ZYYYZ[YYZZYYYZYYY[[\[ZZZ[[]`]^___^]]]^\[[_\[ZYZZ\]]__^^a`aabbb`aba_a``_a___cbbaabb`cbebeeeeggfccdgdddhdedefgfheeefefdddccbcddhdegfefhhhhhgfffhffggfieddfddfeeifhgfgggghkihgghihgfifeehfiehefddeheeehhiffhjigfddefecacffgffffffhgffeddeeeeeeeekghggfeflfjhgfiggeeeeddfgedbdefedfifgffdgcdbfcbbbbba`````accdcccc`]]]^``bab`_^_]\[[ZZ[ZY[YZZ[][\\[][[YXY[WYXXXWWXYZYZ\[YXXZXWVVUUVVWXVXUVUUVVTTUWXXVVUTUVUVSSSSTTSSTTSTSVSQPSRUSSRRSURQPQOPPOMNNONMMLLMLOLMLKKLJJKKKKJJJLMMKJJIIJJJIJJJJJIHHHIJHIHIGGHKJJIIHGHIHGFGGGFZ[ZYZZ[ZZZZVNDA<:9:<<<6/%&*5.00B?=>>:8( - &*0+)%$$&.5@GMX[]^ceghfhhghhhijjkklmnnnmlkhdOOQSTUTVUUVVVVWVVVWWWWVXYYXYYZZXXXXY\ZXXYZ[[ZZZZYZZZ][YYYZZZZZZYY[\[[\]]]]]]^^^^^]]\[[ZZ]\[\^^]]]^__``ab``a````_^^_a_``abbbbbaaaabbegefgffddegcddeeddeffffeeeeddcccdfeedddcffffgihihhhhhhgbfjgffdeefccgeefddehiiiiiijhhhiiihfieedhgfefdddedeeeegfhhgiihhgfdfeebcdegfdfigghjgeeeeffceffffeefffffffffihgffffeedeefhfeecddeedefffefbcdcbddbabbbbaa`_aaccbabca___^^_`bbbb_]^]\[[[[[[\\YZZ[\\\\]][ZZYYXXXXXVVTXWXXY[[XXXYXWVVUVVXYWVUUVVVVUTTUUUVVUTTUUSSRRSTUTUSRSSSRRRQOPQRSRQRSTRRQPNNLOMNMNNNNLKLLKKKMKJJJIJKKKKJKLJMKJKIHIKIHHHHHIIHGIIJIHHHHHHIIJJIIHHHJGFGGHF[[\\]]][][ZXQF?9544563,!'*31--.370/6>/)(& %0.)" %)/9FWLXY[\cdhgjjjhhiijjkkkllmmnmmnnicNNPRUUVYXYVWVVWYUVWWWYZ[XXX[ZYYYYYYYYYXXY[[ZZZZYYYZ[\]Z\ZZ[]Z[ZYY\[[\_^_^^]]]^]^_]]]\[[Z\\\]^^]]]____^`c`a`__\`_\\^aabbbbccccabbbbcedefgfgeefgggeffgdgfifgddeedeccdhhhhhfedfffeiihhhiiilhhghhiffdhfedcffffeefhhhhihjiihjkkhgghffehfgffedeedfeefjghgghhhjihffeeeeeefgefgghiigggfffgghfgfgfeeffgffghgigggfffffdefhgffhfgfgedefffefbdcbceffcdbbbcbb`abcccbaa``_`^^_`fbbb^]`^\\\[[[[\][YY[[[\]]]^][ZXXYXXXXZVWXXXXZZZYXYXXVVVVWZXWUTUWWVVUTTUVTUVUTUVVTTRSRVUUTRQSRSQSQQPPQRQPPTTSRTQPPOOONRNNNNOMLLLQKJMKIHIIJKLLLKLMMMKJJJIHHHHHHIIKIHHIIKHHIIIKHIILIHIKHIHGGIHHF\Z[[[[[[^\[ZSG?84001/,# #&($ #(%"%*#! '*&!#%,<DSIEGGOW[^bffgiiikkkklllllmkmmllmjbMMOQTUVVVVVVVVXWVWYXWXZXXXXYZYYXYYYXXXWYZYYZYZ\ZYYZ[[[Z[Z[[[Z[\ZZ[[[\_]__]^]\]]^^]\]]]^]\]]]]]]]]^___^__`a____`_\]^``aa`_baccccbbccfddffffeeefffeefedgffddcdefdcbbcdefffeeegggffehhhmiikghegfffeeffgiiigeefggghggiijhhgjkkhgffffdeeedgfffededeefgghhggghkgffbeeeeeeeeefhiihgffffdffgefgffedefggghggggghffghgeeeeeeggfeeeeeggfecdbbccecccbbbcdc_aaabbbbbaa`a`_^`_ab_`a_^_`\[\]\[\\\[ZYZ\\\[\[[][ZYYZYWWXWWXYYYYZYYYXYVWWWWWXXVWTUUWVVVUTTUUTTUTTTTTTTSSSSSTRRQSRRQSPPPQPPPQNPQQRQPPOONONNNNNMNNOLKKKKMKIIHJJJIIHIJJJJJJIIGHGHHHHIIIIIHHHHHIGIHHGHHIIHGHGGFFFFEFF`_^\]\\\^\[[UG?82.-,*#!'")!&%#&((/;NE:*$*4@MU[^eelihikjklmkklpppmommibLJOPTUTTUVVVVVXXWWXXXXWWWWXXXXXXXWWXYXXXYYZ\Z[[ZZ\[]]]Z[\][_\^\[\^]^__^_^\\__^]_^]\\\\^a^^^_]]^aa`^^^^_`aa_]^`____`aacab`ccgffddbcdfddeffeejeggheeefeffheeegeecccbccdgeeeeejhigffhhgiiikkmhhfeefgihhgghjgfffggijhiiighgjkjhgffejefeedgddddeefgdefgijhggghffffffeedfhhhhhiggjfffffgfffffhfedefggigfddgggggkhhggggddfhedefefgkfedhdgcdeaccbbccdd`aacb``ea```aa^^^_ac`^`a`^]\[^^]]^]\[]ZZ[[[[]Z[^[Z[\ZZWXXXWXXYY[ZYY][ZZZWWWXWXVVUUVWWWVVUXVTTSTTSTTTSTUSSRSSRTRSSRQSQPQTQOPQQRQQPPPROPOONNNOMMORPMMOLKKJKLJJKMIKIIIKIJJKJHHGGGHHHIJJIJHHHJJIHHIIIKJJJIIIGHGFEFFHH]\]]]^^^^\[\SI@92.+*$ - #'" $',..021/&%09DSY_eiiijjkmllllpmmnpnniaKJPQTTSSTUUVWWXWWWWXWXUWWXVXXXXXXXXXYXXXYYZ\ZZZ[ZZ[\[[[\]^[[\]]]]\\]`^]]]]\]^^]]]]]\\\_^]^__^^^^^____^^`ca`_^`__a`ca`c`aaccefecccddddeeeeeeeeffeeddedfggfedeeecccccceeeejefeefhhfghhhhijehihgfeghhhhgfggggggfgjihijiggghiiihggceefdedgcedfgfffdeehiiiijhggffffgfeffhhhhgjgfgggffgggfcddfffdddffffedefggggihhghhfeeeedeffffghfedddfcccbfcabbbceaaabba`b```aba^_^_`ceaa`_^\\\\[[[^[[[]ZZZZZ[\[Z\[[\][ZWXYXXXXXYZYZYZ[ZXXWWWXVVVVUVWWVVWVVXUTTSSSRSUSRSRSSRQRRRRQPQQRRQPRRQQOMOPPPPOOOONOPOOOONNOPOONLLLKIJJKKJIJJIIIIJKJJIHGGGHHHHHJHIHGGGHIHHKJIIIJKIHHHGFFFFGGG]^^]]^^]^\^ZRKB:2-*& - - - !&*$"%*3::4'#"#-;NYcimjnlllmlmmpnppponlaJJOSTVSSTTUVVVVWWWWXYYWWWYYYXXWWXYYYYYY[Y[Z[ZZ[]]^\\[\]`]^[[[\\\]]\\\\\\]^]]]^^]\]]]]]]^]_`a_a]]]^_^^^_`a`````____```cafccddeebhcgdhefeeeeeeefehecdffffeeeeeffghdcccddeeeeeeeehggggjhihjghgfgkehijihhggggjhgffhiijjiiighiiiighhhhhgeeggifjihgiggfhinkkjkgjhgghgggihjiihghfehhihiiiilccegffeeeefeeefgfkghiiiihhhigfeefgfeegfiffdddgccbabcbbadceaabbcb`dababab``__`bba`_^^\`\][[\^\[[]\[ZYZ\\[Z]\_\^\^WZYXXYX\[[Y[ZZ[\YXYYXXXXVVVXWYWXWXXYUUTSSURTRQRUSSSRQQPPQQQQPUQRRRRRPNOOPQPPOOOPNOPQOMMNNMPOPPMMLLMNLLKKKJJIJKJIIIIJIHHHIIHHHIIIHFFHHIGIJJIIIKJJIHGFFGGGGGG]]^]]^_]][ZXTNF<5-*$ #$)'!#*2<<?0%! - -$<Sfikjmlllllmnnmnooonn`JJNTUURSTTUUWUUXVVUUVWXWYXWWVVVWWXXZZZYYXZZ[\\[]]]\\[\]]\\[[Z]]]\\]]\\\\\]^]\]]]]]]]]]^^^__```]]^^a_^^``a`````__^`a`_`abbddeeebccdcddeefhgfffgeeddddcgeeeegfkgfgccddeefedddddfhghgfhhhhhggfghiefihihihgghhhgfffgghghhghijjiigggghiiffgghfgihfihgfhjjjjkkhijhggghijhijjjhgfeghhhijihjeefggfhedffgfffgegghjikjjhffffefgfeeddcheedddcabbaab`a`dbb`acbaaaaab`baa_````aaa`_]]\]\\\\\\]\\\]\[Z[\^[Z[\\\\[ZXXYXWYX[Z[XYYZZZ[YXXWXXWYVVWWVVVVXVVTTUTVTQQRRRSTRRQQQPPQRRQPOORQRSSOOOPPQOPPPOONOPNMMONNOONMNNMLMMMLKKKJJLJJJIIIIIIJJGGGIHHHHHHIFIGHHHHHIKIIIIIJHFGGGFGEGH]^^^]^^^`\ZYYTLB80*$ - #1-)'(&!$)2:EJ<.*( -0Wgikjmlommmooonnnmnnl_JIOUUUSRSSUUTTTWVVVZVXWXXWVWWWWWXXYZZZYYYYZZ\^\`]]\]\__^\\[[\]]`\]]^^^\\]]^^]\\]]^]]]__]_``a``aa^^``_^_```abaa___ab```adbgedeeciefdbegefggggggfgdddcdheeeedeghhhccdeeeeddeefdggfhgfihhhlhghjihgffhihhhgghfgggggghihggkkjjmmnjjggikighkjjhfgjhiggghklkjkkkkjkjjghiiiikhhigffiiijkjjiiiijigfgeehgghihjihgiiijjjiihffghgffifddifeeeecbabcaaaccdccaabbaaa`bebccdae`a``ac`_^^]^^^\\\\]]]`^][[[\\[[Z\^^^\[Y\Y[WXY[[\ZZY[[[[ZXYXWWVVVVWWVUXVYVVSVSSTTSSRRRTSRRSQRSRQQRRPPQRRRRSNQQQQQPSSSOONNNNLMNNNQNLLPOQNOOPLJJLIJJJIJJJIIIIJJJJJIHHHHHIHGHGGGHIHGIIHIIIIIHJGGGIIJI^`^^^``_`]\\[[SNB6.& - $2@J8,&!%/4=ESI6*.$ - -8Zjjjjjjonnnnooponnook_KMORTTTUUTUUXTTUVWYXVVWWWWVWXXYYZZZZZYXYYYYZ\\\\\\\\]__]\\Z[]]]`[][[Y[\\\]_^^\[\\]a^^___``aa``__^_`_a`_`_aaaa`_``aa`a`adbdfddddedecceeffgghhigeecdgfffeedfeefgigddcehfcdeeeedgggkhfghhhihgghhhigegfgggfhjgjighihhihgfghjijjjiighijjijigjjhgghgefihiihjkkkiijjkfhiihikgghggfijjljhjkjijjihggggggghhghhgfgfiijiiigggiigghhiffffeeehdbbbbaaabccbaaaabba`aaa`aaaaaaaa``ba`_``^]][\\\]]^_`^\\_]]\\\[\\]\[ZZXZWY[[YYZYXYYZYZZYXWYVUTTUXUTWVUTUSSSTUTSRRSSSRQOPQRRSQRRRPPTSSRQQOOOPOPSSSQOPNNNMMNNNMMLLJLMNNMLLKJKJHJKJJIHIIIHIIIIIIHIIHHIIHHHHJHHIIIIHHILIIIHHHHHHHHG^^^^^```a_^^^]ZUN@5-%(.)!!%$-DXPE0$)-=BQRJ-') 'C]ljjlllnnooqpqpoonook^JFNQUTUXTRUTTTTUVXWWVVWWVVWXX[[\YZY[YXXXY[YZ[\\\\^[\]^][[\[[]^^`[][[[[[^____\[\]\]^^_^_^`_`aa`ab`___`c_aacaaaa``aaa```bccbdeegggeicdefgghegjigfeefegeeeffffjhggfeffefhcdeeeedfghihgfhihhhjgghiikhliggjjjifgiihhijihhgghjjjjiihijijmmjhiijlhfffedgghiijjkjiimkmhjijhhihhhhifiiijjkkkiimmlhhgigfghgfgljkfhfjiiiiifgihhggffgfffeeeeedbbbcbaaacbbabbbba``````a``bdbaaa`bcc_a__\`[[[\]_^a``^]]^]\]]]`]^]^[]XZZYYZYWYXWXXY[\[ZXWWVUUTTTUTXUTSUSUSTTTRSRTTTSQPORRSSRRRQPPQQSSQRRQQQNOPOOQPPNLMMNNNPLLLLKKLMMMLLKJJIIIJJJIHHHJHHHIIIIHIJHHIJIJHJIHHJIIIHIKJIIKLKIJJIHHI__`__b```aaa``]\TLC:2)*67:-#""" #,**=WUVC3 )4BFQ:&## -5Qkkkllllnoooopppopoook`IHLQUUUVTTTTTUVVVWVWUUXWVVWXXZ\[WZY[XXXYX[YZ\\^]]\\]^]]\[[[]]]a^[[[[[[ZZZ\[[[\\\\]^^_^^_bacb``cb`aa``a_aabaaaabbbba``adcccbdeffeedddefhghffhigigeefgfffeffgihhhghfffeecceddeefjhjhghhifgghggfiiihijhghjijhfjlijjjijihhhihiiiiijiiknmjhkkkkhgddfeefghhijjijjjjmhgghhiiihghighiijkmmmjijjighhhfgggggfghgeffgjiihhhgghgffefgfeeeeeeedbbcccaabcccbaabb``````caaaaaaa``````^___\\\[[]\^^^_^^]]_^^]]^^]]]\YZYYXXXZYYYXWXXZZ[VYXWTTTTSSSSTTTTSTSSSTUSRRRRSSSRRRRSSSTRSQQQRRSSQQQQPONNNNNOQQMMMMMMMMLLMMMMLLLLLKKJHIJIIJJHHGHGGHHIIIJJIJHHIIHHGHHHIHHIIIIJJJKJJJHIJHGHF``b`_aaaba``cba][VSIA4'#,9CLD9.))((*-1<N\aSC/*28;0%# - - %HeknnoonoonooopqoopqoolaIFLRUUTUUWTUUUUVVZWWUUUVWWWWYZZZYYZ\YZXZY\YY[[]^]^]___]^[\\a]]^\\[[]\\[[\][\\]]]\\]__a`_`aaa``accbbbcdbaabbbbbbccdbaabbbcgbceghlgffffgggggggggfiikghhggfffglihhhhjghfdccddcdefihhhhhhihghihhfiiihgghhhiiijjjjijljiijjjiiikjjjjlnkklmjhilmlghecdeeegiiijniliijmhggijjkkhhghggijjkjjjjijjhijhhgggggffhhgfffegihhgffegffdegghegdeefdedccbbbbedgdea``a_`a```ababab`a`b`b``____\\[[[]]^]^^^^]]___]]^_]\\]YZZZYZY[YYYXXZY\ZZXYVWTSTUTSRRSTTSTTSSSSSSRSSUTVTTRVRSSRRRRSRTRRRTTURSOPOOONNNOQNONNMPNMMLLKLMMLLLLJJKIHHHHJHGGJHGGHHJJKIIJJHIHJHGGHIGGIHKIJIJJKKJJJJMHGHJF``aaaaaabbbacba_^[YWKA0#19LLOC<61.-28@LYgbQA) - - -)2<2# - -4[ilnonnnppnnmpoooooponlaHFJQSUTTUVTUVUTVVVVVTUUUVWWXYYYZYYXYXXXXXYXZ\\^]\]]]]]\\[\]^^]][Z[]]\]]]^^Z]\]\\[\]^^^\__`ab_``dgcacdcdbabcbbbbbbcaaabcddccdehjigfddeffhhgigghdhfgghjhigfggjjhjihhffgeddddedefihhhgghjkihhhhgijhgggiiiihhjljijjkkiijjkmjhhhijkllkklmjjikjigiffdeeefhiiikiihiiighhiijmkhhgihhiijiijiihijjjjjighhhggighgeffffggggfffffgeeeefefdddeeddcccbccddddcabab`aca``_c``_``a`aa`____^^]\[ZZ\_]\^_^[\\^]]\\]]]\\ZXY[YYYYYXYYXWYYYYYVVUTSTUTSRQRSUVVVUTTSRRRRSSSTTTTRQQRQQQRRRQRRQSRRRQRNNNOMMLMMNNNMMMNNNNMLLLKJLMLLLLJJIHIIHGGGHHGGGGKIIIIIIHHGGGGHHHFFHHHIIJJLKKJIIJIFGHHGa`aa`abcbaabcbaab``\WK<'$1:GMKJDE;79<DQYdedQ@ )27% - %@^mlpnmnlpponnoprpqpqonkaGCJPSUUVVVUUUUTVVWVVVWWXXWWXY[YZZ[YXXXY[XZZZ\\\\\\]\]]\\\]^_^]][[[\^\a^^^`\\]^^_\\]]]]^`_bbcbbbdeeccccegccbbbbbgbcaaaabdedcgfihggifeeffhgggfghhhfhgggfghhhhjhgikigfffffeccfefgihhhhhhjjkhgghhijgfgilkkjiikjjkojjkijklkllhihjjmmrllmmjkkljiijjjfedhgjiijmjijihihihjjiijjiiihiiijjjlihhgikijjliijkhggffffggdeeghgfegffefeddgefdedeffdbdfdhdeeddcceccadcb```d`a`_aa```_____^_^_[^Y[[[]]]][[\]]\\\\\[[\YXYYYYXX[Z[[ZX\ZZZYVVUVVVSSRTQQSTTTVUUVSRQQRSRRRRSTSSSSRRRQRSRWQQPQRPQQNNOPNNNMLMNONOOPONNOOOLKLLMMMLKJJKIIHHHHHHHGGIIKIJJIIHHIGGGFFFFFFGHIIIJJIIJKHIJIGFIHHaaaaabbccaaabbcdba``[UC.%&2=ABBBA@>:;BLVace`L2#&- 1OllmmmnomppppopppppppoojcGHJOQTXWYVVVUUTVVWWVVWWWWWYXXYYZ[ZZYXXYYXYYZ\[Y\\]^]_]_]\]^_^]]\\\\]]a_]\]]]\\\\]]]]]]__]`bbbbdcccccbcedccbccccdcbaaaaaddddgeiggggffeffggggfffeheffgegghhhhhgghkkffeeeeedeffggkiggghhjjjiihighigghikjkkjijjjkjijkkklnklkfihjklmmllkjjjkkjjjiiigfehgjiilkiiihihgghhfjjjkihhhifghijiihhhiiiijjiiiiihgfegfgeddfgggffgfeefddddcdddbcdddddddgbbcdeddeccaaaaaaaa`abaa`_`aaa_]^^^]\[ZZ[[[]\\[Z[\]^^^\\[[Z[YYXYXWWWXXXVWXXXXWVVUUUTSQQQTQRRRSTVTTTTSQQSSRQQRQRRRRRSQRPPPORQQPQTRQQOOONNNNMKMOOPNNOPONPONLLLLNNNLKJIIJHGHHGHHJIGHHJIIJIIHHHHHGFFFEGKHHIIIIJIIIKHIIIIIIIHbbbbbcecdbbaacfcccfc_YK8*&)4@==?=@?=;@FN[adf]C( &FdmnmmnpompoproqqqqqqqpplcGBIMPRUWWVWXUVUVVWWXVWWVVVWXXYYZZYYZXXYYZYYZZZZ\\\^]^^^]]^^__\[]\]^_^`^^^^^]\a]^^^]\]]^___baadcccddeeddddcddeedddbbcbedcchegejijhgfiiiffgigffiehfeegfjghhjhhhhhkjffeeeeeeefifgijfgfgijjmkoiihhiihjijjiknlokkkkjkllkmnkjiiijmkkmmmlkkjmkjjmlkihijghhkiijjiihhgggghiijkmjkjiijhghhiiiiiliiikkjjkjjiigffgghdeeffggggggjgeddddcdccbbcdcdccdgdccgfhdddccbaaafaa`___aa`_`db`^^^^^\\[[\[\]\\[\^\\]\^][[ZZ[[YXXXVWVVYWVWXZXVVVUVVVSRQURTRRQQQTTSSVSTRQRRSPQSRRQQPPPQQOOPQSSVQRQQQQONMMNOOMMMMMMNNMNOOPOMLLLLLLLKJKIHJIHHHHGHIJGHIJIHIIIIIHHHGGGGGGHIHJHHIJHIIKKJILIJIJIccccdccccbbbbefccdeea\QB6,.4:;=?@ECADHLS[bcgZ<& - - 7_gnopoooporpppoqpppqsppqmcHAGLPRTUUVWVTTTVVWWXWWWVWVUYZYZYYYYZXYZZZZZZ[ZZ[]]^^^^]^^^]__]\\]^a_]^^^]^`][[[\]]]\]]]___aabbbbeccdfdddedddcddddddcbcdcbdeeeihhggfgiiffghhgeeefffdddfefhggggggijghfffffgffgfffeefefhijjjjihghigghiijjjlkjiiijjiijkkmjijljkkjjmnnmnkjjjkkklkjijjghhhhjjjhihhggfgihijlljjjjijihhhhhhhgggiijklkkkkiiggfgdeddefdfhghggggfefgeddccbbccccgdbdedddddddfcdbbaba``__^^^_a`````_^c``^\\[[[[]]]]]^[[\\^][[YZZZYYXXWWWWYWVWWWVUVVVUTTRSQRTSRQQRRRRRRQQRSQQRRRRRRQPPOPPPPPPQRQQSQQPQQQONMNPNNNMMMMMNLNMNNONMLLKLKLLKJJIIJIHGHHGHIHHIJIJHHIIJJIIHGGFFFGFJGHHHHHHHIHFGGHGHHIJddecdddcccdccefddegffa]NA8478:==BDIHMMSV^_b`V>(! - - - -*HbnnqooopoopqqpqrqpqqqqqpmdG@GKPSSTUUUUSTTVWXYYWVWVVWWZYZYYYYYYY[\[[\[ZZ[[[\_^_^^^`^^^a_^^a^^__]_^[^__]]\[[\_]]]^^a__`bbbbbbbbccddddeeeffeedddebbeeccdieihhghffhhgggiiihgfffheedddegggjhgfhhhhghigghiiigjffefgggijiikhgghhghiiilmkljijjkllpihjjjjklljkhhjmonlmkjkkkkjkkkjjmghhhillkjjighhgghhjkmmkllklkjkhfhihlhiiiijjklmmmjijifhggecdfeefjhhhfgfffffeeddbacdccdfbbcedfffdddcbaabb``a`_^]^_`a`c_``_`a_`]]\[[[^^^]^^[[[\^`\[YYZZZYXYXXWXWVVYXXXXVXVUTTSSRQSTSSPTSRQPQQQRRQQRRTRSRQQPPPQQQRQRQPPSQQPPPPNNMNNMNQNNNMNOMNMMNOORLLLMLMMMJIIJIJHIHIIJIHHMJIHJHIJLJJJJHIGFGHHJFIGGHIGGHHHHGGGLGIIdddcddddddeeeeedeefeed_YPGB>=9:;?BEHIKOVY[\]\JD?0" 0*% $=WpnnonnnpppnqsssrqqrroqqqndH@GJPQQRRTTTSTSUVWYYWVWVUWWXXYYYYYZZYZ[\\\]\[[]\[\\^^^_^]____^^^]]]^]^``___]\]]\\\\]]^_`a```abcbbbbccccddddddfgfdeeecdddddddeiggeffghijgigfghffeeffdcddfdgegigfhgghhhifggggiggfeeddddgiiihhgfggfeggimmjklkjkmlllkjjjjkjkkkkhfjlmnmolklnklkjkkjjihilijkljjjiiiihhgimlnmkkkkjkiihhhhhihhhilkjknlkljjjhghifeddefddghikggfefeeeddddcdcacbbbccfcdeedcccaa`ac`aaa__^_^```c^_^____`]\\[[\^]`^^]\[[\\\\\ZYZZZYYXWWWWWWXWVXYWUVVVSSSTURRRRRPRRQOOOQSRRQQRRRQQPQRPPQQQPPPPOOOPSPQPPPNNNNNOOONNMMMMMMMNONNMMMLLLMKKJJGHGHHIIIHIMHHIHHHHHIIIIJJIHHHFGGGGGGGGGIFGGGGGGGGGHIIfddefddefeffjefegegfgdfa_VTKE?<<>@BFFKMTW\^_`YXTM3 #AS>,! 4ToonnnooorrspqrpsrrqrrruttqeF?EJORQRTWVVUSTUVWXZWVWXXXXXXYYYYYYZYZ[]\\\][[\][\]]]]_^]a_^^^^^^]]^]___`^_\]]^]\]\^\^`bbba`accaaabgdddededddfffdefgggggffefghggffggijhhhgffgggifefddfefegdegihifhhhhhgigfgiihfdebdeefgiiiggfgeeeihhjmjimmjmmmlnlmlkjkjlkjiihjklmmmnmmljjjjljmjihljijiijigghjjijjjknmmjjkkjljfhmjkhjhiiilkjkllknjhiihhhffedddcehgjggggghfgfgdedcccbccbcdcfdfdddbcdaa`______```]]^``b___`ab`_^`\\[_]]]\\]^^^^]^\\[\Z[XWWWWXXXWYWWVWZVVWVZUTTTTSRSTSRSRPOOPRRQQRRSRRRRPQQQQQPPOMNOONOOPOPOPPOPNMMPPOONNOMMMONNNLMMMMMNLMLKKJJLHLKKIIHIJHHIHGGHHJIKKKIJHGGGFFGGGFHHGIHHGGGFGGFFHKIgeeeeddefeefgffffeffheedb^[XQKHFEFIKMOTX]`becba`VJ6%6VTVC6/*+-3:NlnnnmnqqpporpqqrtrqrssstuuseE>DJNPPRUVVWWTVWVVVWWXXVUXXYVXXXWZYYYYZ[[[\\Y[\\Z\_^\]`^]_`^_^]^a^]^\_^```a`^^^^[\[[\^aabbbbbbcbaaacddddcdeedeefdeffeffggecffgghgffhhjghgggffhghgfeddddedddfggggghhhihghfggiihfeeffffgghkigfeedfefiggjijllkmmmlmlkkkkljjjihiijkmmmmmmllkjjjkjjkjiigikjhjihhjkjjkkkkkkjiijkjjkiijjigjhhkikjjjllkjhhiiggggggjgedeeeeefffegffffddccbbcdeeedbbcdedfbba_`__^^]_ca_^\\\_`````_aab`^]]\\_]]\\\]^]\\\\\\[ZYXXXXXXVUXWYUVVWZVVVUUUVWUUTSSTSQRRQQQRRRQPQSSQPOROPPPPRPPOOOOOOOOQPSPOOOONNNOOOONNNNNLNONNLLMOMNMKLLLKJIKGHHIJIHIIIIIJFFGGJGHHHHGGFEFFGHGGFGGFHHGFGGGGFFGHIIgfhggffgjedefghghhgghgffged_^[XURRSUW\`agehhhhiebVQ<+3N^f_ZTOMKQVZimsorooqrpsrqrrsustsstvvwvxteE=CINOPUUUUTTTVUVWVVWYWVUYYZWXXXYYYZZ_Z[Z]\[[[[\Z\]_\]]^]__^^_]]]]]^^__`__``_^__]^]]\`abbbbbcbbdccccdeccddeeegffffefedeeeffffihihgghijiiggghgggjgeffffeeddegijghhhhiinhkhijjhjfeeefighhhiifeeeeffeefgiilkllmmnmnmkjkllkjjihljjlmmppqmolmkmkmkkkmjjiijmhkkikpmnllkmlljiijjnkjjliiihhihgiiijihjmjkiijlgggjhhhgfeeeeeeifgfhfhffedcdcccfdddeccddcccaaa_`___````a_^^`_^__`b`__``a^]]^\`]\]]]^]]\[\]\[[YYXXZYXVVVXXYVUUZWVVVUUUVVUTUTUTSQTRSQQQPPQPPSRQPPRQQQQRRPPPQOPPPPPPPPPNMMMMLLLLLNOOONPLLMONLLLLKMNLLLMLLJJHHHIIIIKIJHHJFFFGIIIIJHHFFFHIIHHGFFFGGFGFHGGHIGIIHHffgigfghhffhhighhgghhiihhgebab_\]^__`abdffhiihggc]XSB4=J[khgc`_\`chknprnooppposqqtsusrsstuuvvvvveF=AIMOQSRUTTTTVUUUUWWVVVVUUUVXWY[[[ZYZZZZ\^[[ZZ[Z[[\[\\]^^]]\\]^__]^\_`______``^\]^]\``bbcbbccddccdcccbcddfegffffeefffjgeeeeeghihhjhiiihffffffggefgefffefeehihghhiiijjfhiiiihjfeeefffhjiiheeffffffefhhijkkllmmnnnllkkkjjlkhjiklmmonmklllkkjkkkmlljjiiiiklmnlllllkllkiiiihijjhkiiihhjkkkiihghiihijjljgghgfihgfedegfefeefffgfffffdcdcfdccccb`bbcba____`^__^__a^^____^_bb_^^`__^]_\Z[[]\\[\[[Z\_^]\\YYXXYYXWWWWVWWUVWUUUTTUVUUUTTSSSSQRRTPPOOOQPQRQQQPPPPOONPPPOOOONPOQOONONLKKKLMLLLNMMMNMMMMMKLLLKKLLLLLLKKJJHHIIIIJHGHHHIGIFFHIHIJHGFGGFFGGHHFFGIGFFFGGHGGFGGGHhgggfgfihfggggfgeghhhhhhjhhgfffeeghgghjjiimmljniidc]ZVT^kkljijjjjllnsssoppsqqqrqqttsssuuvuzwyyxsgF=AHNOQTSUTTUUTUUXXYWVWXVVVVUYXYZ\\ZY[Z\Z[\\ZZ[^\\[^\[\^__^\\\]``_^____^_``_^_^]]]^^^a`cccceceddcdddbabcbcdceffffefifggghhhhglllhiiiikihhigfeeghffeeeeffeefhhhhjiiijjkihikihhjhfeddgfjiiihgffgffgfghgihijjjklmmmnlkkjnjjkpjjimlmlnmmlklmmmjkkllkjjkljijnlomllommkijjjiilhghkhjijjljjkhikihghhijnkkjihhhhfihgeeeeefffeigfgjfhgffffgegcddedebbceabbc_a^^___`_a^_`b_`__`b^_]`_^^]]\\\\][[[_Z\Z\]^]][ZY[[ZZZXXXWVVVUUWUUUTTTUUUWTTRRRSRRSSPRPQQRQQRQQPPOPRPNNQPROONNNPPQPOOPMKJJLNLLLKLKLMMMMMMMMMMLLKKLLKLMKJJJKKKKIHHGGGHKIHIGGHIHJLHJGIFEEIIJGFFFGHFEEFGIGIHHHHHhhhhhhgggegfgghhhgihiiiijiilhhhhhhgfghiijkllliiiijfd_]akklmnopllmpporqqopqqqrsrqrstrtuuuuvvvxwxphE<@EMPQRRRRTUUSUUWZXYWWWVWYWUXYYZ][ZZZZZZ[[ZY[[\\\[^\\\\\]]]\\\\]]\]^^^^\`__^_]^^_____```ccccbacbcccbbdcdceddfgffffffffggggggiikfgfiihiijiggfffffeedeffffgghhhijkjijjiljihhhhijgfddfffgikihgffeeeghhhhjjjjjkmmmmrkijjjjkjjjjijmlllllmoomlljjjlkjijkkijkjjkkkjollkjjjiihhhggihhijjllllighhhgghiklkjiiihhgeggfeedeeeggefffggffgfffedcfaddeecbcbbaa_`__^_`_^_`b__`a`___`___\^^_`^\\\\]\[[[\ZYYZ[\^]\[YZ^ZWWWWXWVVUUTUUTTUTTTUUWTTRRSRRRRQPQPPQRPQSPPQRPPOPONNMOPNMOOOONMONNLLLLLLKKKKJJLLLMLLLKIKKLNKKKLKMLJIJJKKJKIIHHGGHHIIIIIIIIJIHHGFEEEEEEEEEFFFFEDFGHGIJHHHHiilijhligeigihhhkiiijjjjkjjihimikiggkihjkkklmjkjjijhfjmllnpomnppoqsrrruuurrstssstssstuxxyvvvxwwshD;>CKPPRQSQSTTTUVWZXXXXZXXXXXXYYZ\[ZZ]YZ[[[[Y][[[\\^]\\\\\]^\\\b]^]]^^^__a___^^`^^____`aacbbbabdcecbbccccbecehggfggfgkffffgiiihjgigjihhjiigdfeeeeeedehgffgikhhiijmikihjjkjijhghgfdefgghkkkiihfghhghiiijjijikmkmnnkjmlomlkkjjjijkmmlllmnmlllmmmkjiimmkllkjnkjjolmkkkihhgghghkjijkkkkijjiiihhhikkjjiihhhgfeggffeeeddfgedefggffgkhhfddefgeeebbdbcb`_b`ba``a__`bab`cab__]]_^^_^_`a``]\\\[[[]YYYYZ[]_\[ZZZYXZXXVVVVUWTVVVSRSUTUUXTUSSQQQRQPPRPPQRQSRPQRRPONNONNMNNMMPONMMNOOOMLMPNLLKLKJJKLKLLMKKKLKLKJKKJJKKJIIJJKKKKLJJHHHHIIILJGHHIJJKGHEEEFFFFEFFFFGHEFGHGGHGHGGiijigeb_begghhhijiijjkjkllljkkjijjihiiijjjkjkjkkkjjkkkklmoooljklpqrprstusqsttssttttsuvxvvuvvvvwvfC:=CIMOPPPRUUUTVYXZXXXXXXXXXXXZYX\ZZZZYYZZ[ZYZ[[[[\\\\[\\][]]\\]]]^^]]^^______^^]^a```_`abgcbbcccdbccbbccdeeeheffgfghggfeffggihgfffggihhhigffeeggfgggghhgghhghjiiihijijjjiijhhhggfgghhiiijiiiiijjihijiiiiijkllmmmljjjlkkkjjjjjjkmmkkkmnnmlmmmmjiiknmjjikjnijkmlllllihhhghhhjkjllkkkjjjiiiiihjiiihijhgheeegjhifeegdffeeeeghhhggggfeeeffeeecbddba`_a`baabba``a```aa`_`]^_^`^]___^^]\[\[[[]ZYYZ\[[^\Z[Z[ZXXWWUVWUTTTTRSRRRSTTTTTTSQPPQQOOOPQPPPPPPPPQQPNNNONOPNMNNNMNMNOMLNMMMNNMNNNLKKKKKKKKLKMKKLLKLJIIIJJIHJJKKKJJJIHHHIIJIJJHHHHHHHGGEEEFFFGGGFFGGFEFGGGGGHFFHjikifZVZ_hjimiiilkmjllnkomnkjjijlkkjjjjknlllnklkllmkkklmmmmnnjdjoqrrssttsrsttsstvuwuywywxwyvwwyveC9<BJMPPOPSUVVWXWWXXW[YXWWWXXZYZY\ZYY[ZXYYZ\ZZ[\\[[\]\[\]^[^^\\^^^^^]_^_`a___b_````c``````cdbcbbcdbfcbcdcefeehffffgghhgiehfefihggggggjiiiiffddehfgiijjhhhhhhhjjjiihijiinjjiihhiihjffhjiijmiiihilllikkijjjjkjklllmniijkkjjjjjijklmooollnpmlmpnnkjknnmjijjjnijknnnlmlhhghhiihhkkkkklmljjjkijihiiikijjjhiefegggghfffefffeedgkknghgjhhfehfffedddbaa``bbbbdceb``a`d```a_d^^]]^]]_`_^^]\\_[]Z\\[Z[\[Z][ZZZZZXXWWUUUUTTTUSTRRQRSTTTTTRQRSRROOOPPPPRPQQRPPPQMMMOOQPMMOONNONPOMLNMLMMLLMPNMKKJJKJKJJKKKLLKJJHIIIIJKILKJKLKKJIHIHJHGHJIGGIHGHGGJGGEFFFGGHFFIGFFFGGGHFGFFFkjjhcNKKaijijjiikkkjkkkkmkkkjjjkjjjkjlllmmllmllllmllmmnonmle[T]ipsrusrsrsstsssttuutuwvwwxwwvwwvteB8<CILSOOPSUWVYWWWWWW[ZXWWVWXYYZZZZYXZ_YYYZZZ[Z[Y[]]]]\\[[[\^]\]]^\]]_^^_____`_````a``bbbaaabbaaacbcddccdeffefhfhfhffgfeeffdefegihdegffggffgddehggjighihhhhihjkkhhhiiiiihihihhhhhhghhjiijjijkjjkjjjkkkkkkkklmlkkikjjikjjjjijhjjllmmmlllmlmnnnmkjmlmkkkmljjjjjklkkklihhiijjiikigkiklkjjijijihhiiihhhihgffefffffefjhgggfdefhiihgdijggggfddeeddbbdb`aabbcdcbbdbaa_`````^^]^_______^]\\\[[Y[^]\\\[ZZZZZYYYXXWXTUVUTTTTRRQQQRSSSSTTQQSSSRNNMOOOOROPPPPPPPLMMOOOONMONNMNNOONLMMMMMLLLLLLJJJKKJJIILMLLKKKJJJIHIJKIJKKKKJJIIIJHIHGGHHGGGFGIHGHGFFFFGHGGGGHHHHFGGFFEFFFFnkmj_F=JclnjkkiijinmmmlmpjkkjjiiijkkjkmklllmonnmnllnooopolkaJJUjpsqrssuvuttsttuvvvvu{wxwzy|xzxvudA7;DGKONOOSUWVVWWWXXX[ZZXXXXXYZ[ZZZ\[[ZZXW[ZZZZ[[[^^^^\\[Z[[^^\\^]]^]`^a^^^aa`a````b``aaa`abbbbaafbbeecbdgfffefffefgggeddeeddfehghddeefghgfhdffjhhhjhfgiihhkijlkhjhjjkjjiiiiijiihhilijjijkjjjjkmknnlkllllnmpnnlnkplkkkjjjkkkkkkmmnnnlooompnmmllkkjjjkjlnjlkkkkmpkjkkjiilkmkjihhliikjjkkkjlihhjjigghhhggffhfefjggiighhgfffghiigfigggjfdeddfddccdgcccbbbcaabccab_c`aaaab__^_^`^^]]]\]][ZZ\\]\_[\[[ZZYYZYYYXYUUUWUVUTSTQQRRSURRRSQQRSRQNQMOOQPROOPQQPPPNNNNNOOPMMMLMPPPPPNMMMMMLNLMLLLNKKJLKJILLMLLKLJKJIJIJJJKKMKJJIIKIKHHHGGGHIGGFFHGHGGFGHGHHHHHHHFFHGGGGJGFFHFlkjl]@>Jfollf`bdccgjhgmnmkgcdeddefhlkkkkhdfgghjnmllolkjijjc\D<UjqtsrrtvvuuusqqomllqsvwvwyyzxzzwucA7:DGJONNPTUWVVVUVXXXXXYYXXXWXZZ[[YZZZYYXX[ZZZY[]]]]\]\\\\\\]]\\]]\]]^^_]]^^_a`aba```aaaaacbbcdbabbcdccdfgfeefgfeeefgfeefegefeddefeddeffffeedefgggghhggiihghhhhhhhikiihiiiiiijijjiiihjkjlkiiijllikllllkmmnmmmljjjlllkkjjjklklkjmmnnnklmmmnpmmmllkjkkjjmljkllllnmkjlkjjjlikkjihhjkjjjhigkijihihfgggjhijjihhefeiiiihghhgghghkijhfghggifffdddccccdgdcbbbbdaaaaa``_`^aa```^__^]]^^_^\\\\\[Y\Z[Z\\\[ZZYYYZYXVVXTUUUUVXTSURSTRSSSRQQQQQQQONOMONONNOONQRQRONOONNONNMNONMMMMMONMMMMLLLKMLLKKLJIKKJILKLLKHIIJJJJJKJHIIJLJHIIIIIHHKHGGIHGGGGHGGGHGGGFGHHIHHHFGHGGGFGHGFEEmkolZ<8LigiYSWXZ`ce`^enll^YYZ]`bcdeimgd_\^behggfemnkkfbehebK9<Vjsokmquyssokmonnjfipuwwyyyyyxyyyua@68CFJNNNQSUTSTUUUWZX\XYYXXXWWYY[\[[ZXYYYZZZZ\YZ]_]\\^]Z[\\^]]\\]a\_]]]_^a_^_babbabb_ababccddcdbbbbedcdeegeeefggggfffeeefeeeeedfeiefddgfeffeddejhhhhijgkhighihggghilihiiihikjjikkjihhijikhikhhjkkkkilnmpppnnllmqmmlmllkjjjkklkkmmqnomlmollnnmommkkkmkkmljjllllllkjlmjjklmmlnighkkjjnjjillmjiggghghhjjijkihgghiihhhhigggggihghkhhhgfhfefddccccedfddbcccdaabac`aaa_aabab``a`^]^^^_]_]_\[Z\Y[[]\][ZYYY]YXVUVVUUVZWWVTUVRWSRSUSRPQPPQQPOONNPOPNOOOPOPQPOOPOOOONNMNNOMNMMNPMMMMLKLMKNLMLKJJJJJKKKKMKKIJIKKKKIIJJJHILJJKIJHHHHHHGGHHGIGGGHGGGGGIHIIHHHIIGHGGGGGGHFFFFkkllW98QfdTIRVXSVTQNXdijWLMPWRQPRU]gd^TN^TXWY_[ZfoifXTZ]][SB8>Vidafjrvqmb_dhec]X^evyyxyyyxyyyxytb?58BFKMNOPRSSSSUUVWWWWXXYXYXWXYYZ[[[YYZY[ZYZZYYZ\\\\\\][[\[\\]_\\\[\]]]^^_`````a_____`aabb`a`cdcccccddfeeeeeefffffffiegeeeeeccdcdedddcgfefhedeeghijiihfghggghhggghhhhhljjiijjjiihikihkkjjihkiiikkkllmmmoopnnklmlklllllnnmkmmllkllmmnonmmjlmnmmomkllnjllklkmllllmmmlkjkjjjjjjghhkljkkjjijkkkifgggeifggjijjjjjjiiilihgggggggfghiiggggffefeddcccdccccdbaddbca`a`aab_`baaabcc`a^]^^]\\\[YZ[ZY[[[[[ZZYZVXXXWVWWXWVWWWVVUVRSSSRRRSPQPQNPPPQNNNOONOMNNONOPONPPPPOOOONNMLMLMNNMMLMMMMLKLLLOKJJMKJJKJJJJKIIHHIKJJIKIIIILJIIIIHHIHGGGHFHGIGGFFFGHGGGGHHHHGEGGGGGFGFGHHGGGlkjkU78TeOBJ]_d\WQDHTijRCHQ[gZPHAJXa`LER^\YVNJHWihdODQV__\QE9@VjZSRhurnYP[^ea_TVZl{yzyzz{yzz|z{tb>36AEJMQQPQSSSSVUWVXYYX\XYXYWWXY[[ZZYYYXYZY\[ZZZ[\\]^^\[\_[[[^^\]]\[\a_^^_``a``a__`a_a`aacbdbecdceegefdcehgfeeehgggggedeffddcbdccfcddccceffffhfegkijihfggffgiihhhjiihhhhikijikijiiiijkkklllkilijjikmmooopponmmmlkmlmmnmmllmonnkklmmmmpnlllnsmlmolmlnlplllklnmnmmmlkkjnlljjkkkmjkljkklmkjkojiiiikgjhhhjjlkjjiiiijljihhlhjfhfghhggffgjfdeefeeechcdccebbdebaaabbaab``baa`_^```_^^^]\[\ZY[\ZZ]Z[[[Z\Y[XWWXWWXXXYYYWYXWWVUUSSRRRSQRQQPQQRQNNMOPOONNOPPPONMPPOPOONOOMMLNNMMOMNNMLLLLLLLLLLJJJJJNKKJJKKIJHHIKJJJKIHILJIIJIJHHHGGIGHHHGIGHGGGFGHHHHLHHIGGJIIGGFHGIHJKJHnmkiQ5:VPA?N_ife\OBC^lU@AI[igdVD=?ZeM<I_diebP@DVkfO<FS`mhgZM;AZmTAMflmWFQ\gqmlb_hy{zzzzzzyz{{{wsc=25@EJMORPPQQRTUSUVWYXXXXXWXWWWX[ZYYZYYYYYXXWYYZZ[]^\\\[[[Z[\]]][]^\\__^^`a`bbc`__```````a`aaaabcdeedddddgeddfeihhihgffeeeddfddcbcacbccccefffggeehiihffghggiiiiihhhhigghjkiihiiijiiijjjjkkkkiiiiijjkkmnnqponlkkjjkkllkjkklkkjllllkkklnolkmoollmmklllklkklllomnnnnmklikkkjkkkjjhkmjkllmmmkkijjjhggiiijjklkjnljjkklkkihhfgfhgghggghggffffeeeddcccccdecbdccba`bbbba``bbb`__```ab_^]^]\\Z\_[YZZZ[[Z[XZXXXXWVWXXYYXVVTUWUTSSSQQQRRRQPNQRQQOOOPOLPOQQOPOMNNNNOOOMOPNLKKNMMKLLNKLKLLLMLMMKLKJIKLNJKKJIIIJHIJKJJJIHIIIHIIJHGGGFGFFFGHHHGFHGGIFGGFGGHFHIHFHIHFFFEEIHHGHIonoiM4;VO=>VnkkieI:Jj]J<?WokjfcE7@[O?=RbnllcT@B`nR>;J_nmokhP>A]\OCIiqVCEUhzwvqovzz~{{{|||||{{|vsc;14?DIMNPOPRQRUUTUVWXXXYWWWZXWVVVWXYY\Z[YXXYX[[^ZZ\^\\\[[[[\\^]_\]]]]]]^_`aabbbaabaaab```c_aaaaddddeefehefeeeffihhhjeefedggfeccdcbbeccceceehhgeeefghffffhhgigggghhhhggghijiiijikkljlhijjlmjkjhhkilkmkkklmpppljjijkkllkkllmmmkllmljkpnnmlmonmmmmmllljklkkklmommmnmmmljjlonnkiikjljhjmkklnllkllmigiliijllljjmqklkkljkkggghiiiihjhgggfgggdcdhffdbbdddcccccebaaccbc`_`ac`a`b_``ca`^^^_]_]\\\ZZZYZ]Z[XZXXXYWXVWXYXWVUUUTSSRRSRQQQRRQQQRSRQPOROOLPPQONNPNPNONOOROOONMLLNMLJKLNMNOOMLLLLMKLJIILMMKJJJIIILIIJJHHHHHIIIHIIKHGGGFIFEFGGGFFFHGGGFGIFFGGHGGIHGIIGFFEFIHHHHIomoiJ4=VC4DcnmorfFDR`\D5JhnomicE6B[J>>[lnooeK7Eh]G;=VqpqppkS>@\`L;Ii]HAG`xuuvvvwz|}|}|}|||}~||ytd:02=CHMNOOORRRTTVVVWXXXWVVVXYWVUUXXWWYYXXXXYY[[[ZZ[[Z[[\[\[\^\\]]\\]_]]^_```bbca`bc```__``^bb```acccefeedeeeeffifghgeffeegffeeedbcccccedcddeffeeffegeffghgfghgiijhghegghijkkmjijjjiihijjlkijiihhhkkjjihmklllkkjjmkllllllnmllklmlkklllmlmpommpmonmlllllkllmmmmmmlkmmljkmlkkkjijjjiiijjkkmlkjkllliilijjkijikmmkklkmkjiggghgigffghgfffggfdddefeebabbcccdcba`aba`aba__`b_a_^^`aa`_^___]]]\[\[[ZZZZZ[XXXWWWVVUVXXXWYVVTSSQRRRRQPQPQRQQRRRQQORNMMPNONNNNNNNNMNNNONNMLMMMMLJKLMLLLMMMMLLKJJJJIJKKMJKIHIILJJKHGHIHGIHHHHIIHHHGFFFFGGGGFFFGFFFFGGGGGGHGGHHHHHHGGFFIGHJIKsoohG4>BA=JbmprqeIEZqY=DYgurtldC6E\F8CdjrpqcE>Ig`>4GiptrspoQ=@[aJ@Ii]B9Qrwyxz{|||{|}€}€€€}€~~}{vc:/1:DGKLMOOQRSRSVWVVVVVVVZVWWVVUVWVVWYYYWXY[YZZZY\[\\\[^]^\\[[\\]\[\]]]`_b`_aabaaea_acba`__ba`_^_fccefeecgedfggigggihhfefhgffhfddfdcccccdcccdeffghgghiijhgffgghiijghglhhjkkkjijkkkimmkjjkkjlhoiihkkjiijmkmnmmmkljilkllkkmollmpljjjjjkllnnmlkmmoqnonnmmlooommnnmlllllllmmlllmknnmjjiijokmkkklllkjjlmmjnjlmmmnjjlklmjjhjjjhhhigggifdeefhfeeeedcbadddccbbcbaaaa``ac___c_a_a^___`b^a___`][[\[\[\]]ZZYYZYWVVUUTUUUUWVUTUURRRSRUPRPPPRRRRSRSSRNMNONOOOOPNNNPNONNOMONLLMMLLLLLMLKLKLLLKKJIHIJJKJJIHIHHKLLJHHHGGGHGIIHHHIMJIHGFFGGGGGHFFGFFEFFHHGGGGGHHHHIIIHIGFGIGFGHHqppgE4E[G;Kapv{„oNQiiYLJ\hsqrsaA;GPEEF_hqqq`B:Nh^9=QgpqqqqmQ9?^dH:IfW9=]pwwwz}{{|}}}}}|}}}}zxa9/1:EHJLLOOPRQQRVVUUVVVWWVUVUUUUWVVVWXXYWWZZXYZ[X[YYXYZ\[[]\[[[\^\[\\]\^^_``aaaaa`__a`^____`a`___bdegfdddeefggggggeghheeghifeeeedddcedccddedeefhhggghiggghggghjiijfffhhilkkkiiijikijkklkjjihhhhihlkjhiijkkkkkkiigilkkkjklmmkqnkkkkjklllnnmmmmoppnnnnllllmnnmlmllmlllkkllllmmkkljhjkigjkkkklkkkjkjkkjjlkkkllkkkkkjljihhghhhghhhhhfgiggggfeededcbedcccbcdbbbbba`^```_`^^^^]__^`_^__`__\\\[Z\Y[\\ZYYWUXXVXVUUUUVVWUTTRRRRSSQQPQPPORSSSRRQPOMNOONNONNNNNLONNNNOONNLLQLKKJKLLKLLKMLLKKIIIIIIIIJHHJIHIIJJIHHGFGGGHIIIIIJIJHGFFGGIHHGHFGGFFFGIGGGGGFGGGGIHHHHGGHHFGHHHqqs`C>JXQLL`tz…†yPSlzhSTajopsp_:5L`L?D[honqX?:Ui[>=OemoprqlH5@`bF9IeRABZns{y~|{yuwy~~‚€€~|y_8.1;DIIKMNOOPQRRSTUUVUUXVVUVTSSSUVVVVXXYVVYZXXY[Z[XXWWY[ZYZZ[]]]^\\^]]]]_^``abbaab`__``__^```a```acgfffggffhhhghhifkhgeeeeedefegegecegffdefgeffkhggggkknijgghiiljkggfgghjjjjjkikkjjjjijlijiiiihmmljjjjggjmlnlljiiiijknmmmmlllkkklnljlnmooomnorpponnplnllmnnolnnnoolslnlklonmkkkihjiihjjklmmkklknkjjjkommlmmkklkkkljiiihgijihhihhhiigggggeeegdcceccccdddbbbafa`^`````_a_]]__^_`^b`a`_]`\[[[YZ[[YXXWW[XVVVVWTVVZWUUURSRRQRPQQQPPQRRSRRRQONMOONNNNNNMNPOONNNNNNNOKLLMKJKKKLMMLKKKKJIIIKIHHHGIIIIJJJIIJLHHGIHKJIIIIIILIJIIGFGIHJHHHFFIGFFGGGGGGFGGGFFHHFGHHHHJIJJIIrqqYC=Sk\RV_o€ƒgMVozƒgWasponmS57P\]METiomnQ<:[icCCMailmorY?9Be_E:LdUACWkpyz{|ohmq|€€€€}|y^7-09AFIJONQOOPSQRTUTSTTTUUUTTSSTUVWVVWWVVVYXXXXYXXXXXXY[ZZYZZZY[^\]^]]__^____`a`aa``^`a`````aaaaabcefffhkfgfffgfffeeffeeheeedededeffffccdefeeefhiihggjjjihghhhhkikhgggghiiijmjijkkkjiiiiijhjkjijljhhhiegikklmlljjiijkkknolllllnlllmlomkmlllnllmoponmllllmnmnmlklllllllmkklllkklhfgghhijlnmqlklllkjjjjllllmmkjllkjkljljjjijiiggghiiihghhgfffgccccccbbdcbbcc_`a`]__`_a___^]^_^^`^___^]]\\[ZZZZ[YWXXWWXWVUTTTTUTVVUTSRRQRQPOOPQPQRQRRQRSQOOMLMMMNPONNNNONNNONMNNOLLKKKKMKJKKKJKKLJIIIJJGHHHFIIIIJHIGHHHHHFGFGEIKIIHGGHIHHGGFGGHIHIGFGHGFGGGFFEFGGGFEFGGGHHHHGHHFIJsrp\ENdjkfaelxr\VYsƒƒzjcholk\H4;Vib[TQ_olmM:GahiSFK]dnml`N94IjaDCQbfPFRinwvvl`Z[o€€€€‚€y\5+.7BDGJNNNONPPPQUTTRSSSSSTSTTSUUUXXWYVVVUVUVVWYWXXYY[[ZZYY[ZYZZ[[]^]\\^^a``^_``__`aaa```cbbaaabcgeefffhgffgfffffgeefifeeeefdeeeeeedeeehffeeeffghhghhliiihhhhlkjijihhighijijjjiiikjiiihhhjkkkkljjihkgiilkjlklkkjjijilkknmllnnnnnlkmmmmkoklmnllmrppnnmllmlmmrmklmlmmmmlllllklkkkgghgghijnmmmlknllllkjkklnlplkjlkkjijikiihhjijhjhhikijhihiffffbfdeccbcccaabc`]_`_b```a`a_`]]^`^`^^^^^]][[\\[[ZZYXXXWXYWXTTSUUVUVVUTSRQRSQONOPRQSQPRTPRSQOOMLMONNOPNNNMMNOONMMNNOMMLMMMLJKLKJJJKLJJJLIJGIIIIIIIIIJKIHHHHJGHIIGHIIHGGHIIHHGGGGGIIIIIGGGHHGGGGIEGGIFGEEFHHHHJHHHGGIIssra_\iqnkmnpssi`Wex€†~ynegfjWG5>Zgjfc_^negKKOcskaWM]^cd_VM8BRbcJEYqi]TN]knsh`YQap~€€€€€€wZ3*-7CDFHNMMMMNOOPRRRQQRSRRRSSSSTUUWVUVVVXVYWWVWXWXYXXYYYZZXXXXZYY\[]][[\]]]^^^_^^___`a__aaaaa``acedegffgfeegfghgffeeffefeedeeegfffhgfffffefeegghhghhhhgijhjiijiikjhhgggggggjiiiihjjigghhhiikijkjjiggfhjklkklllkjijkklllllklljjjkkkllllkljlonmnnnnnnnoonnomllkllmknnoplllljgijjjjjhghihjmnllmllllmkkkkkkllmmljkjkhiiijjiihiiihhhihihihhihghhfcfbcaabceca`aaa_^^^__`_____`^]^^]^^]]]\\][[[Z[^ZYYZYYWXWVUTSSTSSTVUUUSSRRRQPMNPQQQPPPPOPRPOOLLLMMNONNMMMNNOONMMMMMNMLLLLLLKLLKIJKKJIIIGGGHHHHHIIIJJJIIHHGGGHHGGHIIGGGGFGGGHGGHGIGHHGGGFFEFGGGGFFEFFFHFEGHGFHHIJHHIIsssigjutsrrrxxylimqv€‚~~khgh\G;AdupoljjkiaZWbsqtlld^^_eXUMLO`rgQXgpsjc]Zlff]^Ubqx‚€€€€€€€€€wX2(,9?DEGHIKLMONMOQQQPPOPRQRSSTTTTUUVVVUUVTVYWWW[XXWWYYXXY]WVWVWYY\[\[[[]\\]___^]^`^^_a^^__`bacaaceegfefgfeegffghfeeeeefifeeefffffffeefeeeegggggkggggghgihghhiihhiighggffgiihihijhmjihhhkhiikihhhihfffgjkkklllmjjjjkklmnnlkkpjkjlkkllllmmkklklnnnmroonlmonmlllnlmlonmmlknjhiijihihhhiihikmnmmmolkkjkllooomnmnjkjkjjiikkjkikilihhkiiijgghhilggggddefcccca`ac`_`_^_`c`e___`^^]^^^]_^^\[\Z[\ZZZ[[\[[YXXXVTTSRVTSTVVUVTSQRTQQNNPQQQPPPQPOPRPONPLLMMNNMMMQPQPPMLLOLMMMLKKJKKLNKLJJKLJIHHHIIIHGGHHGHKKKIJHHHHGJGHGHIKHHHGFFGHGHIIIIHHGGGGGGGFFGGGGGFFFFGGGGGFGJKKIHHGJssqlnrtssrrrtvummoruyz{|sjgie`J;Mlrsrpqrtvkhouvwupnmkhgfc_`bjqmkkiqzvsqomlkie`eqy€~€€€€€€wU1'*6=CCFGHJKLLMLMNOPQONOQQRRSRRSUTTSSTWUVUUUUTTTUVWWYWVWVWWVXVVWYZ[[[[[]\\\\Z\\\]]\]]^]^^_`a```abdddfdegfeddefefgddcbgefffffhfeeeeeeeeeeediffffgggffffeffghiihghhhggggfffjghiijjgmjijhgghjjhggggggggjiijkkkljkkjiknkkmomlkikkjjkjknmkknmklnmlmpolnmmmllmnnlmnlkllmmmmljkjiiihgggfghigiijkmmmkollklmllopmnnlkjiijkiijkjjihhijihgiiiihhgghhieeefedcccbccbaab``__^_`_^a``__^]]]\\\]]]^\\[[[[ZYYXWWXXWWWWVWSSUUTUWWWVTSSRRRPNOOOPPPPPPQPOPQONPKNNNNNKMMMMNNNMMMMLLLLKKKKLKKKKKIJJLJIHHKIIHHGGHHHHHHJJIHHIIFHGHFGHHIHGFFFFGGHJHFIHHIGGGHGGFFFHHGGGGGGFGGGIGGKIIHHIIIstwvttvsvvvtsssooqutwy}xkebhb^NPWqzvuuuvwtsvxw{z{wusrrrqopqsuv|ttu~{xwwvvxzvsuwz€€€ƒ‚…xS.%(3<ABEFGHIJKLLLKKKMNNNOPOOONPSTTUURSUUUTUVUTTUTTTTUVUWXYWVVUUUWYXXY[[\[\\][[ZZ^_\\]]]_^^`a`a`cceccbbdgggdddfedcabbcgehgggggfffeddeegfeddeeeeffefgfddeeeegkjjhighgjhggigjhiiiijgliiiiiiijhgfighgigghhijkklnjilijjkjkonmmnkjkpjlkkmnljnlllklllnpnpnrmlklmplmlllmkllmmnjlkljlghhkhjikjkjjkmmmlnmmlomlmonmmnlmiijijkkkjjihhhihhmhhhkihhhgihifeegfcdbbeeebbbd`b``_____aab`a_]]]\]]\]\\\\ZZZZYYZYWWWWVVUWUWSTVTTUWWXUUSRSUQOOPPQQRQQPPPPPPPOOOMNMMMMMNNMMONNMONNLPKLLKKMKLKLKIHHJKJJHIIGHHHHIIIKHJIJJIIJHIFGGHGGFGIKGEFFGFGIIGFJIHHGGFFFFGFFFHGGGGGGGIGHHHHKIIIJIHIstutttutvuututspqruuvxyunbfgf_]]gvwwvuxyxuwxxxxxz{xvuuutuvvvwyywx|}}|{zz{{yyz}}‚€€€€€ƒ‚‚ƒ‚ƒ€xP+%&1:=@BEEFGHHIJKJIJJJKLMMMNNNNPSRQQRRSTSSRSTTTSSSUTSUWWWVVVWVUUUVUVWY[[[ZZZZ[ZZ]\Z\\\]]]^a````bcddbbcedbcdddedcabcbceegfghhhgfeeddedcddccdcbfggdefeddgeeefffgggggghgfffeffieiihgiiiijjjghggfffghhhgghhjnmlmijllkkjkkonnnmjijjjkjklmllnnnllllmnnmmmomnnkjplmllijjjkllnjmlljighhihiiihkjjkkklkmlmmmlmmmmmmllmijhjjlkjgjiihhhhgihhhiihhgfgihgffffeebbddcccccaba`__^_a_^^^`a][\Z]^[Z\]]\[\\\YXY[WYWWWWUVUWTSSSTUUVVUUURSRPPPPRQPPPOOOOORPQQPNMNMMMMONNMLMMMLMONJKKLHJJMKJIIJIIIIIIIGHGGIIIIKIHHHHHIJIGGGHGGFHHHGGGGFGGFGGJHIHGGGGGHIGFGEGFFFHGGGHHGFGGGIIHHHHKIIHGtuutwtutwvvtussrvvuuvxxurrrrrolruwzxz{|z|{{{{yxz~{|{{{{||}}||{{|}}}}ƒ€‚‚€€‚ƒ€€‚ƒ‚‚‚ƒ‚wM)"$-7:>ABCDFGHHKHGIIJIIKLMMMMPNOPQPOQSSSRRRRRRSSQQSSRTTTUUUVSRTTTTTUWYYYXXXXY\ZZZ\[]]]\\]^__`__bbbbbacddaacfffccbbbbbedjggggghffegcccccccbceagggdeeeddefgfefeeeggkkkfhefffeihhhihjjjiijjgigffggggffggkhhihlmljklklmlkoopmmklkljkjlmmmmmlkklolononnmpmnlkkommlljjjikllmklmojjjiikiiijjjjjljjllmmnmlmmmnmmlqmmkkkjklkjhjkliihhhkjjjihgggggghgfggfeeccfcbdfccccab`a``_^^a_`^\[\[_[YZ]\_]\[ZZYXXWWWXVTUUUTWUSTVWUUVVVSRRSSQPPPQSQPOONNOOPOPOONMLKMMNMMMLLKLOLMMMKNLKIJKMKIILJKIIHHHIHHGGHHHGIIIJHHHHIIGGGHHHFHHIGJGHGGFEEEGHIJHHGGHHIJGGGGFGFHHGHHHHGJGGHJIGGIHFGHHrrrssrsstvtsrrrssstwwxvtqrsuroqwwyywzyyz|{{{{yz||{{{|}{z|~~}}|}~}~~‚€€€‚‚‚‚‚„‚ƒƒ‚‚ƒ…uJ'!#+48<>?@ACDDDDDEIGFGIHGHJIJKKLNLLLLMMOQQPONNOOONNPRRRRRSVSRRTSTTTUWWWWWWWWWY[[Z[[[[[[\]^^^^^__````_abb`abbbbaacbabbdeedddfggefcfccdbccccdcaddcbcddgdddddcdddceefggeedffeehjhghhihiigfiggeffhjhhgehggeghimmnlkklllkklmlkjjlkjijjlmmlmmkkklklmmoomlnmmjkkllljjjjljnllkkkkkkkliijjjjijjjjkkjkllklllnnllmllqmlmkkkkkkjgkkkiighhhfffggghgfggffgigeeedccccddccbaaa^^_`]]]^]^]\[ZZ[XYY[Z[][[ZZZZXVVVVUUTVUTVTTTXVUUVUUSQRPQPPPQQRQPRPPOONMNNNNNKLKLLMLMMLKKKKKLLLJKKJJJKKJIHIIJIHIHGHHHIIIHGGHHHHGGGHHHGGFHHHGHFGGGGFDEEFEFGGJJHIFGIHFFGGIGFFFHHHHHHIGGGGHHJGFGFFFHEqqqqrssrqqrqoprrtsrssstsqrvvxwwvwwywzz{{|}|~{}}€~~~|}}~~€}€€~€‚‚‚„ƒƒƒƒ‚‚ƒ‚‚…ƒ†††„ƒƒ…„rG%"*67<;>=>?BBABBCEGGFEFEEEHHKIIJJJMLLKMMLMNNOOOLKLMOPQRQPQPPPQRSTUUVWUUVXVVVWXXY[\[ZZ[[\`]]^_^aaa`__cba_bcdcd`abaacddddddfghhgggfeccb``abbbbdcbbcdddddhcededcegeeffffefehhhiigghgghhffhfffggkhhhigihieghjmplnkjmlmnmmnlkjjlkjjjjlmomlkjkkklmpmonlknmlkklmmlkjjijjkllrmqkjjmjjkllojlijkkkgjmklkmllmolllnnqmmljkkkmlmllllllgjihihhhhhhjghgihghideegcacgdedcaaab^]^`^[^__^]\]_[]]\[[ZZ]YZ][[ZXWZVVUWWVVTTTTTTUUTTTTTTURRQPPPPRPPQOOMMMMMMONNLKLNNPKMMOKJJLLMLKKJJJJLKKIIIJJLJIHIGIHHHIIIGFGGIHFHGIGFGGFHHHIIFFFGFEEEEFEHGGHIIIFGGGGIIHGFFGFGGGHJJIGGGGGHGGGIGFFHFijjjklllllmnmmooooqqpoprsrsvrqrttuvwwwx|{zzzyyyzzz{}{z{||}}}~~~~~€„‚…ƒƒƒ„ƒƒ‚‚ƒƒ‚ƒ„†……€pD$"#-69==>>>??@@AAAABBCBCCBBCCDDEFGHIJJJJIIIIIJJIHIIKMMMMMNPOOOOQQQRRRSTTTTTTUWWWVY[YYYXYYZ[[\\\^^][]]^^__`a```abedbffhhknswxz{|{zslhcb```````aabccdcccddbeddbcfedfcddfeefhghhhhggfeheegfegffgjfghgefgffggkkljlkknmmmllpljjkkjjjjklllmlkkjkjklllmlkklklklooolkkjjjkkllllmlkjmijklkkkkjjkkljjjikknnmllkllmmmmmljiijkmljjklnkghjihijhgggfgggggggebdefbcddddecaa`b^^\`__^]]]^]\\[[[[ZZZ[\ZYZ[[YXVVUUUVVVWUVTSSSTSTTTTSUTRRQQQPORPPQPONMMPLKNPMLKKNMMKKKKKKJKKLLLKJJJJJJKIIIJLKKIHHGHGGGGGHGGHGHGGGGGFFGGFHHGGGFFFFEEFEDEEHFGGHHIEFHHIHGGFFEFFFFGGHGJGGGGHHHHFGHGEEEabceeeeeeeffffffghjklllkknppoopqvuuuuuvvwxxyyyyyzyxxyxz{{{~~‚~~€~€‚„€€€ƒ‚………„…‚kB'#+19<=>@A@@?@BBBBBABBCBBBBAABCCDEEFGHFEFHHGGGHGGGHIJKKLMMNNOPOOPPRRQPQSSRRSSVVWWVWXXWXZXXYZZY[_]\\]_````accdeefiklpv{‚ˆ’ ¤§®µµ´¨™†rjccbab`c_aaadecbbbbbbeddcddffgdeegeefkghgghgefdhddeddgfffkggggdcddfghkknklllnnnnllllkjkljijjkpkklnnmjjkjjlmolkjllnllnnmklijnjklpmlmooollkjknklllknkklkklkklnmmlmkmlnmmmnljilijjjkllkkjggkkkjjgghijjjggfggeefeeeeecdfgfca`b_^]a^\\\]\\]\[[^^^[ZZ][ZZ[[ZXVWWUVVUUVUTUUUTSTUUTUTSSSSRRQPPPRQSRROQNMOLLNNLLLLMLLKKJKLLJJKLLNJKKKIIIKILJJJIIIIIJHGEFHHHGHHGGGGHHHGGGFGHGGFGGFFFEEFFDFEGGJFHHJGFGHIGFFFGFFGHFGFIHJHGGKGHHIHGHHEEFbedgdcdgecfefffffdeffffghghhhgjjjijklnnoppqrrrrrstttvwwwxwy{xwwxwwy||{|}{{||}}~~}~~}}}}~{eE06=AEFGGHHHFEFGGFFFEEEEEFEDCBCCDCDDEEEEEDDDDCCDDDEEFGHHIHIJKLKJKLLMMLLMOOOOOPQRQQSTUUVVWXYYYY\_acdfhknqstvxyz„‰”™ ¨±ºÀÇÇÇÈÉÉÈÈȼ²–€tjf`__`__^acbaacaabbbcbbddfacdefefeeggggfffeedccdgddehedfhgdecddefghjkkjljlnlkmmkjkkjjiijkkkllllkjiiikjjlnnmkjkllkmnnmlljiljkkonnmnlmnlkkklkkkklmkkmlklllllkkjklllmmmmlkjiiiikjlkjkkkiilkjkihfgghhiefffgffecedeeeffdddb```]]^]]\\]\\[ZZ[\^\[ZZZZZZZ[YXXWVUWWUTTTTTTSTUTTTRSPTSRRRQQPOOPQQRQOONNNNOONMLLJMKKJJJJJJJJJKKJJJIJIIHIIIHIJIHIIIIHHFFGGGGHFGGGGGGGGHIHGHFGIGGFEFFFGGDEEHGGFGGHHHHHHGGGGGHGGGEFFFGGHHGGGGGHHGGFEEGcdfededfgfgfgffffeiflihfigegighijhiiijklllmnmlkkkkmllmnmmnppppqqqrtuwuuuxwvxzz{||||||}|}}}||||y^HIMTYZ[ZZXVWWVTRQOMMMKIIJIHGFFFEEEEFGGFEEEDCCBEDDDDDDEFFFFGHIHHIIIKJJJJKLLMMNOOPSQRTUUVXY\^`cglrx~ƒ‰•š¢ª¯®®¸ÂÇËÎÐÐÐÏÎÎÏÐÒÍÍËÐÌËÉÊÇŪ›„nea``__^abb`a``acccbaadefabceefdddgfefgffehcddcdeddefeefhfgghefgiikjkjlkklmknjiiijmjiilkhkklkjkkjihiijmmmllkklolmnqnmljjmjmkpprmnlllmlmllkmjjkljjnpmmlnlklmjklonmmlllkkijhhijjklmkkkkkkjjjkfffgihfffhhhfeefegfgffcdab`^]\]\\^\[[_\[YY\[[[[ZZ\ZZZZZXXZX[UXWVVWTTTTTUUTTUSSQTRQQRRQQOOQPPPQPONPNNOOMLLKJMKKJJIHIKKMJKKJJIIJJJIIIHHIHHGGGGGFGHGFFFFHGHHIGHGGHKIIHHGGGGGHEGFGFGEHGGGHGIGHGIIHFGHIIHHHHKFGEDFEGGGGGGGHGFGGFEFededebdeecddfefeeeefghgfghgfgfhfhhhhikkkkmmmkjkjjlmkllllmmoppooopotqrrsqsprooppoonmlmmmmmmmmmmgZRQV\]^`abccbbb`_^^^_^^[XWVUSRRPONNNMLLKKJHGFEDEEFEEEEEFGFEEEFFHFIHHHIHJJNMNOQSUXY[_chlqw|‚‡“𥰹ÁÂÄÇÊÌÎÐÐÏÎÏÍÏÑÎÍÍÎÎÎÍÍÍÎÎÏÎÍÍÍÍËÊÉÆÅ»¯“}tfd_^^_^```_`aabbaabaaaabbbbaabcdddeedddcacbbbcccdfgeddeeeeefiihiiiikkjijjihhhijkkijkjjkkmjjknljiijjjjkkkklomlllmnnmkkljmkmpqllllklllkkjljjklhkoommllmmmmjkkljkikjkjihiiihiikkkklmllkjjihggghhggfdddfefffeeeeeeaa_`_^]\\[[\\[[\\[ZY\[Y[][ZZYYYZZXXZVVTTTTSSSTSSSSUTQRRQQRQRSRURQOOPPQQPQOONNNMNMMLLJJJKIJIIIIIJIKJJJIIJKJIJKIIJIHHGGGFGGGFEEFFFFGHGGGGGGHGIJHGGGGGHEFFGFGEGHGFGGHFGIHGFEGIHGGFGGGFFEFHGGGGGFGFGHGFGGFGifhdebddedfdgfffeedfgidgfghhhghhhhihljjkkkkkjkkjjlonnmmmsmppopqptrvrssvvuuuvvuuusssqpomnqmjjika\W]cbacdeefgghihikkkihggghgedcddcb`]][XVTSRPNKJIHHIHHIHHIIHGHHHHHIIIJKMNORVZ_djpx‰‘™£¯»ÃÊÏÓÔÕÖÖÕÔÖרØÖÔÒ×ÒÑÓÔÒÐÒÔÓÒÒÓÒÒÎÓÑÖÕÔÔÔÑÑÑÑÐÏÏÏÅ©•mg`_a_a_`a`_^_^_`___`bcaaaa`bbddedddeca`faabedededdeddhefefjiiiiljljjjjijhghljkihikiiiiijiikmjmkljjjjklllmmmmknnplmmljmllmqlllkklllkmmmmnllmmlnnnlmlmmlklllmmlmmmkjimiiiiilkkjomllmkjihghhhgfgfeeejeghhffeeddba``^_]\[Z\][Y[\]^[Z\[[\\\YYZZY\YXXYVUTUSSSUTTUUSSSTSSQQQSRRRRRRQPPOPRQPPOOPONNOOPNOKJJKJIGHHIIKKJIIIIIJJJKKJHIJIIHGHGGIHIGGGIGFFGIFGFGHGHIHHHHIIIGHFGFEEGFGFDGHHHGIHFFFFGHHGGGFHGFFFHHHFFGHGGFFGHHHGIGfeeedcdcecddeeeffffhgifggfgigggggghhiijjkjiiihhgkkklnmnmmmooooqpqrrqsstssssruutttttsstuusqrsnlhefgedfghklljjkkkjklkkkkkkkkkkjiikjijihgecb``_][YWUTTUTRSRSTTTTUWY[^adhlt|„Œ” ©´»ÂÉÐÒÔÓÒÓÔÔÔÔÔÓÓÓÔÖØÖÓÖÓÔÓÓÔÔÔÔÒÒÓÓÓÓÒÑÑÑÎÎÏÕÓÔÕÔÑÒÒÑÑÏÍÍÍÌ͸¬’|rca^]]]\\[]`^]^^]\]^^^aa`_```abaacba```a`bbbccdccddcdeffgjihighiijjiijhfghjiihggijihjkiijkjiljkjkkjkllmnnqmlnomjjjjjknlkkkllkkmmkjkiklnnnnmmmmmkllmmllkllkkjkklijjjiiijhijjjnmlkkkihhhhhhjkligffgehigffefgdcba_]]]\]\^][\[\Z\\\[[[[ZYYYXXYYXXXXVVTSSSRSSSRRRRRSRQPQQRRRTSSQPPPOOONPPNOOOONOLLLKKKIJJIFHIHIJJIIIKIIIIJHIJHHIHIHHIHGGGGGGFFGFFGIGGGFGGHHHHHHHHHFHFFEEEFFFFFHIGGGIHGIFGGIHGGGHHGEEGFEFGFFFFEEFFFGFEHHeeeefdddedeeefhgggggggggheefgghgghhgjikjljihhikhkklmolllnoqopoqprrsrsssrrsstuuuvvuuuuuuuutssrrrqoihijklmonooonnnqqpnnprrqqrstsqpoopnmnomlmlmoliiijljihhijkmorx€ˆ™¦¯¸ÃÎÑÓ×ÚÚÚÛÜÙÖÙÛÚÙÛÜ×ÕØÝØÚØÝØØØÚÖ×ÖÜרØÙÖÖÔÓÕÚÕÔÔÕÓØ××ÖÖÖ×××ÖÕÔÔÓÓÕÖÔÒÐÓÅÄ¥|ic^\[\\\\\]]]\\]]^^^a_^^_^_`d`_`a`____``aabbbcddgcefgfggghhghijjlknhgfiijhhglghhijjjhiliiimjljijmkmmmnoonnnllkjjlkjknllkkklkqmkklllmonnmmlronllmmmmlkkikjjkllkkkjijijiiijlnmnkljihiijhiikkjgjgjfggffffffedca^_b^\\\]\\\[\[\[ZZ[\\Z[YYXWYZXYY\WWUTTTRRRSRSRQQTQRRRRRRRRRQPORPONNOPONNMMOMNLLKKKLJJJKIHIIHIJLHIKJHHHJHHHJIIIJIJHHHHGHHHGFGFFFFFGJGGGHGGGIGJHGFGHGEEFHGFFGHIHHGIGGGHHGGGGHGGFFDDEEFFGHFGFHFFFFFFFGFfhfeedeeedeefghgfefffgggfefgffghhffgghiilhiiijjhjjkllkklnopoonnnsqsqrqsssutvuwvvvuuwvvwxvuuttusqojiijjlnnnonoooopqppprrqqqqrrrrrrrrqrssssssttvwxz|ƒ‡Œ‘–œ¤®·ÁËÔרØÙÛÜÜÜÜÜÛÛÙÙÙÙÙÙÙÙÙרØÙÙÙÙØØÚÙÙר××Ö×ÖØØØ×ÖÕÕÖ×ÕÕÕÕÒÔÔÓÑÒÐ×Ô××××רÖÔÔÕÔÑÐÑËۦ‹ul_]YYYZ[ZYYYXXYX\^]\\\\[]^^__^_`^^^^^_`___`bbbbcdfeeefeeggikjihijhfefhgggggghmikjjihkgiikkigijjjknllmmmlkjkkkjjjijklljllkkkjjijklmonmlllnnnomqolnnkjlkkjiilkkkjijiiiijklkjkjlihhhghhhhikjhgfgeggeeffeeeecaaa`_`a]^]Z\[\Y\\\]ZYYXXXYXYYXWYXXUVWUTTRRRTRQQQOQPQRPPQPRQQPNNOONNNOOMMLLMNNLLKJLLJIIIJJIIIGIIIHIJHHHIJIHHIIIJIIIIGGGGGGGFGGFFFEEFGHGGGGGGHGGGGFGEEEFFFGGGGHIGHFIFFFFEGGGGGGGGGDDDEDEEFEFFFGFFFFFFFFgfffeffedefefgigffefgghgffjgihfgihliiiijlkkkjjijkkkkmlpmmosopnopsrrrrqssrstttuuuuuvvvwxxzuwvvvwqpkjklmnmopposqrqpqrrtrrrrsuuvvvvwwwxyyyz{}‚‡Œ‘—ž§±¸¿ÇÏÕÜÛÜÞÞÞßßßààààßßÞÞÞÞÞàÜÞÝÜÚßÛÛÝÞÞÞÜàÜàÛÛÜÞÚÚÛÝÜÛÙÝÚÛØÚÙÙ××ÖÙÕÖÖÕÕÔÒÕÒØÕØ×ÚØØ×ÚÚÙØÛÕÕÔÓÍȼ·š„sb\YXYXYXWWXXXWZZY[]][Z_]]Z[]_][[[\\^__^^^^_`bbeeeddfeegfkkjgghjieegggfefffhiijjihhkikkljiiihiijkllnnmllkmkkihghiklljnmljjkkjnmpmnmmlopomnpmnomnmjjjkkjijlkllmiklljijkklkkjkijiiimihghijihgggggeeefgfheecba_^^`]\^Z\[[Z\\`\YXWXXXYYYXWWVVWVXVUUTSTRSQQQQQRQPPOOQRRRRRRNPONNNNOMMKMMOMLLKJKKJIJKKLKIJJIIIIJHHGLKKJKIHHIIIJJHGFGGIGGGGGIFGFGGHGFGIGGHHGHHHGGFFEGFHGGGIHHHHGJGGFFFFFJIIGGGGFGDDEFFJFHFJHHGHGGGFFfffhfffffgfefggfefeefffgggggilhgghiiihijkijijijjkkjkmmnonooooooprrrrrqsrrrsuuuuuuuvvwwwvwvwzxvvrqlllmnnnoppprqrrsutstttttuuuvxyz|~€‚…ˆŒ‘—œ¢©³¾ÊÕÛÞßàáââáâáàÞÞÝÝÝßÞßàßÞßÞßáßÞßÝÞÞÝÚßÛÜÝÝÝÝÛÜÛàÛÝÞÞÛÛÛÜØÛÚÝÚÛØÚÛÔÔÓÑÑÑǽ½ºÅÏÍËÐÓÒÓÖÙØØØØÙØÚ×Ö×ÕÓÍÇÁ½¦—~g_WVTTUUUTSUXVTUVWVXYZZYWY[[XXYZ[[\^__```^^_`accccfddefhihffghieedeecddegfegjjffglijljihghhiiijklllkjkkkjkgggihhihhkokjkkkjmmlllllkokmlmommommkjhjmkllkkkkijikkkkjhjijijiijjiihjihhhikmiggfggefegfefdddcb_]__^\\Z\][Z[[[YXWXXXXXXWWWVVUVVUUUUUSRRSPRRQQSOPSPOPQQPPPPNMLNONMMMLKLLKKLJJIKJJHHHIIIIHHGGHIHGGGHHHHIHGGGGIGFFGGGGIFFGFFFFFFFGGGGGHGHIGGHJHFFFFGFFFFGHHHHHHGGFGFFGGFHJIHHGFFEDEEFFFEFGHGHHGFGGFFefhgfffghfffhgfffeeegfghjjkhiiihhiijlhiikjjjnjjknkklmnqptnnooonpqsttttvrsrsuuvwuzyyy|yyxxyyyxvvsrnmmooqopqutttstuuuvvvvvvxz}€ƒˆŽ”š¡ª²¾ËÓÚÞÜÝÞÞÞßààáááââââââââââááááááááááááááßáàßßÞßáàßÞáÞàßßßàÞàÝÜÜÜÛÛÛÝÜߨÛÎÍÈø®§ž‚s~”£¯¼ÈÎ×××ÖÕÕÞØÛÙàÚÛÛÛÔÖÐÉÄı§†k^TSRPPPQQQSTTTUVTVTUWYWXZZYXYXY[Z[]^\\\]]^]^_`aa`abddhgggfdcdebbccdddddddfhjffhkhghjimhkkkighkihjjjlljijhjjmhhimhjkkknmmmmlllonnnolmlmmmnnoollmmjklljiknklkkkklkkokljjknjiijhkilihjkihghgffeecgeffdbceb_^_`b\]]]]]\[Z[[[YXXZWXWWVVWWUVUTTUUUSRSSRQRPQTOORPPTPPPOOPOMMONNMLLNKKLKKKIIJKJJIJHHIKIHGEGGGGFFFGGKKKGFGGGIHFFIGIHHFFFEEEEFGGFFGGHJHHGFGIHGGFEFFEEFGGIIHGHIGGGHGHHHFGIIIIGEEEEGGGFFFGGIGIHGGIFGGffhfgffhfefffgffffffghhhhihhihiihjjjihihikkjkkkklllmoqpnnooooopssrsqrtssstuvvwvuxwxxyyyyxxxwwvvsqnnnooppqqrsstvwyz{{}€ƒ…ˆ’˜¡©³ÁËÖÝßßßàááâââáââââââââââââââãããââáàáâââááááââáßâáááááááàÝßÞáàßÞßÝÞÜÞàßÞÜÛÝÜÜÙ×¹ª›‘zuqdVVXr|ƒš¿ÄÈÏÖÓÓØØÙÚÚÚÚÚÚÖØØÒÌǾ´«ŽvdPMKKKMNOPQQQRRRQQRTSSUVUTUUUWVVXZYXZZZYZ\\\^a__`aa`bccbbbaa```a``aaaabcdffefjgfghiggffffgffghhhjghgjihfiiighhhhhilklmkiklllmknmmkmlmmlnmjkjjkmikkkiijklkjkkklkjlkjijmkjiiihhhjkkjiggfghgdefegfffedcdba`_^a]\[[[[\[Z[ZZYXXYVWUWVVWVTTTTTSRSSSSSVRQQQQPPQPPPPPRPOOPMMONNMMLLLLLKIIIJIJHHHHHHHHHGGGIHGGGFFFGHIHGHJGGHIGGGGHGGHHHGGFEFGFEFFFFGGGFGIHGHIGFFFFEGHIIGGGHGFFHGEFFFFFIHHGFEGFEFFFEFFFGGGGGGFFFFGgehghggffefgggeffffgggjhhihhiijhgiljjiihikllllmlmmonpqpooppooprsstvsrtxwwwwxxxxx|yxxz{zxwwyxwvurqoonoqrrrrswyz|~†‹‘—¡«³¾ÇÏÚÛÜÝÝÞßàáâãããââââââââââââââââãããããããâââââââââáâââáââââááááááàßàááááàààààààßßßßÜÝØÖ©’ƒrcXOGHN]nrlio}Œ´¾ÔÖØ×ÞÛßÜÝÜÛÛÛÛÝÔÕÊÆºµ˜cJFHJLKLLLLMLLNNOPONQTTTTTSSSTTWVVVYYYYY[]]\]]^_ab`_`abaa```^]]^^____aabceefddeihjfhegddgjefgggififjhggihkkkhgghikkjkkjkllmnlnnmmnlmmlmljmmmlnmljjjjkkljknlkkkkkljjjjjiiijjjhjjjjhhjfhhifefffgfffecdba`_^`^\[ZZZ\Z[[ZYXXXZZZVWVUUVTSTTTQQQRURRRRQQQRPQQQPPQPPPQOONNQOOOPKMLMKLJKJMJLHHHHHGHHHHGGHIGFFEFFHGHIGKHFFHHGGGHHGGHJGHGFFGGEEFFIFFFFGGHJHJHHGGFGGGGGGFGGHGGGGGFGFEEGGFFGGFGEEFEEEHGGGFFFHGFFEFGgegfgefdfeehghgffhhhgghhiiiiihhhhhiiihillklmlnnmllmlnnpoonpqooqprtttstuuvwwxxxxxyzxxyzyxwuuuwvurqoooqsuwz}€„Š–¥®ºÇÖÜÝÝÚרÙÜßßÞÝÝßâããäãââàÞÞßáââããããââãããããããããããâãããââââââââáââââââáááâááàáááßáàßÞÞÞÜÛÚÙØÒ®¥|w_RJ@>=IqnŠhefhlr…”§ÀÆÑÔÖÛÛÜÞÞËÏÍÏÒÐÑÍĽ³œ…gIC>AGILLNMKKKNMMMMMOOOOPOOOPQRSSSSSTTUUVWXYYYYYYY\_^]\\][[\\\\[[\^^_``^`acdcceieccccccgfeedegffgeeefgihhhhffgghhhjkjjkmlnllnnmmlmlnmmjiigijjjjkkikiklkmmlkklllkkjjjijiiiiihijjjihhgggfgehgffeeecbcba``a`^\[ZZZ[[Z[ZYXWWWWXVVUUTTTTTTSRRQQQQQQQQQPPPQQQPPOOOQMOPNMLLLLMKKLKJJIIIIHHGHHHGGHHHHGHHGHFFFFFHGGIGGGFEFGGGHFFFHHHFGFFGGGFFFFFEFGGGGGHHHGGFGFGHHHGFFJHGHIGFFEEFEDGEFEEDFGEEFDDDGGGHFFGGGFGGGIgfghhggeedceggggghihihghkkjikllhghjhmjjjjkmlklmlmmlmnnspqqpppoqppqrsstvuwvwxxxxyyywxzyxwxx|uwvvssppouy‡›¦³ÃÑÜßÝÞßàáâãâѱœŸ©¸½´®¹ÊÓàáããããÖÉÀÊÙÛââããããâãäãããããããããââãããããââââââââââââáááâááàáàßßÞÞÝÝÜ×̾±£“€o^PLFC5007DQ\cijnlihhr|”³ÁÍÐÚÙÜÝà×ÒÐÅÇËÑÜÏË¿¶¢–jI=8<CGMNNMLLMMKKKLMNNONONMMNOOOOOOPQVSSTUVWXXXWWYZ[[Z[\YYYZ[ZYXZ^^^_`^]^`aabaaaaaacccccdcbbcbdfffecgkiggfeffighiijkiiiiiklnkklkkkklljjjiiijhiklkjkkmkkilklpmmkjkljihhiijiijjmiihigfffgdfgghfecaaccdaca`^\[\[[[_Z[ZYXWWVVYVUUUUUSSSSSSQRQQQQQSQQPPOOPPQQOOORONNNMNLKKNKPLKJNIIHIJIIJHLGGGGFGHIHGFGGGHHGGGIGGGIGGGIHHGFFJHHHHGGFFFFFFFEEGHIFHGIHGGGGHGHHIGGGFHIIIHGFEEEFECFEEEEEEEEEGDIDFGGFFGHGGGHGHHgfffgggdedeeghggggihgfhgiiiihghggjjhijjjkklklllmnolmnmnnoponooppooqrrsttuxvvwxvvwxvuwwwvvwvuvttrrqqov…§ÆÕßßàâäååååääåååãßœ–y«¤™†’²ÊÝàãäã߿”ÊÕáãääãâãääããããããããâââãããããââââããâââââáááááßÞÛØØÕɽ«˜†wi\QHA::;?EBD3--.37;@Ojh}Ž|ptt†“¥ºÃÒ×ÜÝààßÒÈÆÇÊÏÉȾ´§˜pM@59=CMPQRQQQQPONOPPPQPOLNOOOOMONNOOOPPQQQQRSRRTUVWWWWWVVVVVVUUWXZ\[[[\\]`c_^___a``aa`aada`abcccdefeeffeeeffffggggghiiikkjjjjjjiijljhjhhghgikkkjkkmlkilklmmnjjiihhlijiiijihliihhfhkggdeeddfdba`a``_b`_^]\\[\ZZZYYYXVTUUUUTSTTTSTRRSQQQPQSQQRQPPPNNMMMMMNNPONMMLLKKKLKMLKKLJIIIIIHHHHHHGHJHHGFGGGIHHGFGGHHHGHGGGGIGGFFFFGGGEFFFGFEFGFFFGGFFGHHHGGFGGGGGGGGGGGGGHFFFFEDEDEEDCEEEFEEEDDDFFGGFHGGGFGGHHgeefjggdeegfggghkhihggihjiiighjgghghkiiillmknlllklllnmommnnmnorooopqrstttuuuwwvvvvvvwwwuuwxuvtsrrrqqv†ÕàææææææææææååååæäãÝΣˆƒ|mnb\f¦ÛâääãÖȬ›¨½ÇÞáääääääãääãããäãããããããããââââãâââââáááàßÞÛ×È®”{hWI@:511/-,,-138>;3-*,+,,/4=J`ž„|sm|‚¢¸×ØÙÜßßßÙ×ÌÅÆÆÈ̾¸« sR>359DUT][YVTTSRSTSQQQPQSSTQOOOOONNNNNOPOOOOOPQRRSTTVTSTUUUTSSTVWYYXXY[[^^\\]^^]\\a^^_accaabbccccccceeeeeddeeegghgdfghjiiijighhhjklikghfggjjjjkjknnliknllmnmmjjghhiikjiiihlinhgghfddcdecccbbbbe`a`b`_^^]_\\Y[YYYXVUTVUXUUSTUTSTRQPPQQQRQQQQQPOPNMMMMNORNPPPMNLKKJKKKNLJKJJKKJIIILIJIJHHIHHGGGFFFFHGGHIJHHHIHJGGGEEGFHHGGFFFEEEDDFFFFGGGGGHHHIIJGGGJHHHHGJGHGHFEFFEDDFDDDECFEGFGEFEEEFGGGFFFGGGJHHHfeefffedddffgghhhhiggghhiiiihhiggghijgiijjmkkkllllllllmlllmmnnnnoqqqrrrqsttsuuututuuvuwuvwxvwtturrqprz¦Øçìêéèçççççææååæåååâמ]j_jwtgg‚¹àåååäáܵœž”´ÛáäåäääääääãäääãääãããããããâããâââââßÛ򯃵”wbNB9520.//00110///22897566533235=@]zƒ‚w…‘¦¸ÊÙÜÜÞàÜÙØÕÐËÉÀº¹©œvSA357APUijlifca__][[[YWWXWVUTSRRRRQQPPPPOOOOOPPPNTPPPQPPOOPOMPRSTTSUVXYYYXXXWXYYZYY[`_]\\]^_c`_`aaaaabbbbbbcedcdeddeffffeggfefggggggffgghgggijjknmmjjlnllkjjjihhghgilihiglihhgffdeedccbbebaabaa`_`_^^]]\[ZYYXXZYVUUUUUUUSSSSTSRQQPPQRRQQQRTPOONNOMMMPNMMMMLLLKJKLLKKLKKKKKIIKJIJHHHHHHHHIGGHFFGFHGGHJIHHIIHGGHGFEFFGGGHFFFEEEEDEFFFGGGFGIIIIFGGGFGHHGGGGFGFFEEGEDDCDDDDDBDEFFFEEDDDEEEEFEGHHGGGHHffhggeeffefeigiihiiijjjiiikihhjghihijjjillmllkllnlolmmnlllnmonmnppqrqrsqrtuuutstuuvwwuxvvvxxwuwutssqnlk™ÙçìëééèççççææææææååâÖ‹yw}€¨rgj•Èääåääãȸ£•Œ©ÐÖãääääääääääããããããããããããããââááÞÖ·‚eP>5/-,--./0269766777657=EIJJHEB???>>?AGPbt¥—ty½ÍßÝÜÝßàààßÖËźµ³©¡yU?3329AQjnzwtvwutstrolhgea^\[YWVVUTTUTSSRQQRTQQRTSSQRQONOOONOPPQQRRSWXVVUVWVUUUWZWXXWY\]]\\]]^^`a^^^_acaaccccbbcddddfeheffeddehggfiggggffffgljkjikkjkllmlllihglhjhhgghigkgghlhfeddfcdaabea`_``b_`_^]^\[ZYYYXWXYWVVWUTUUTUSRSSRRQPPQQSQQQSPOOONMLNMLMMMMMOLPMMJNLMJKKLKMKLJIJLIHHHGGHHHFGGFHFGFFFGGGGHIIHIHHGEFFGHFGGGFFGFFFFFFFFIHHGHHGHFGJHGGIFHGHHGFFGGFEEFEDDECDEGFHDDEFEEDEEDDEFEEEFGGJHGFGGfgededeeeefeffgghgiihhhhhhihhhjghiiiijjikkkkljjkllljllollllmnnnnopooprqqrttuttttuuuvvuvvvwwxwtuuuusrpi\T‡ÔâìëêéèçççççççççæååàŸuNQUmvfJãäåååäÚÛÂ²Ž¸ÔãååääåååäÛÒÒÓÕØÚÜààáâããâáÞÛÅÁŽjQ<4.,))(*+,-149>BDFGGGGHHHTbdgdb]XVTRQQPNMOSdz‚œ™œƒyˆ›±Ã×ÝÛàààáááÚØÅ´®®ŸŸyTA5206;K_gxy{yxxyxwxxwwurrrqomifdb`^][ZYYXWWWWVUUSRSSSTRPOOPPPQPPQQRSSRRRQPQSSTSSTUVVVWXXYZZZZZZ[\\\\\\]^]^^^^__`abbbbbbccddccbccfgggfdedeeghhhhkjiijiiihigfhgfiggggghgfeedgheedceadaaabaa`___]^\]]][ZYXWYXXWWWVVVXTTTSSSSSRQRPQPOOPPPOOOOONMLKNNLMMONNMLLMLKKKKJJJKKKKKJKJIGHHHGGHGHFHGFHFFFFFFGGGHJIHHHGGFFFIHGGGGGGFFEEEFGFFGGGGHGGHGFGHGGIFHHGGGFFGGEEDDCDDCDEEEDEEEFFEDCEBEEEEFHFFFFGGGEFGeeedhdddhghefgghhiiighjihhhhhhjhhhiijjjikjpkljokkklkmmnmmlmmooqmopqooppqssrsstuutuxvuuxuvwvvwxxuvtutqoRBGÌÚêëìêéèççççççççæåÞ¹eb][ea_v›ÛáååååäàÖ½™‹‹¶Ò×àâåååååäÓÈÄÄÆÉÌÎÏÐÒÙÞáàßÚ¯g?5...00,*)**+-47EMX\_dgebfilqx~…ŒŠ…|twxsmppke\YVZu ‘Ž„…~¥ÀÝÙàààààááÝØÃ®¥Ÿ¢¤yS=41135AL_€~‡…~~|~€~{|~{xyzxwxyxxxwsomjedb_]][XWWVTUUTSRQQQRSTUTRSRQQPPPOPOPQPQPPPQSWVTVXWUVYZ\[[\\[Z[]^^]^___a___^^aabcfa`abbfcbdfeeefefghghggggghihhjfeeeeigfggffeeedceeedebcbdaaaa_]`_`_^_\\\^YZYXWYYYVVVYVTUUTTSTUVSRQRQRPOOQPTOOONMMMMMMLLMLNMMMMNMLLLKLKKKJJIIIIJIIGGHGGIHGGFGGGHFGFFGHHHGIIJKKHGGGGFFHHHHHGIIIIIGHFFGHGGHIHHHGGGGGGHHGGFGIGGGGFEDDCCCCDFEEEEEEFHEEDDEEFFEFFGEEFHHHEFFefedeccdhffegihhhhihgfijiijjighhhhihiijijjkkkjnikkkklllnnnnmnnnnoqpnoopqrrrstttwuuvvuvxuvxwvwvuuutttosU;?N{ŸÄçìíêéèèèèèèçççæÞÑ…rb€xrnkq{¨ÜäåæææåàßÒļ½¾¾ÌÝãåååæãÐÀ¿¾ÉÃÎÈÉÈÑÔÝÞÑÀ›qW86211244321/-.048@QOso}}|zw|€†‰‘–›—’Œr‚s‚mieb]QUZepv‚sjwƒš«ÅÙáàààààááÝÝ륣 uM<22245:AWptƒƒƒ‚€€‚‚€~~}|zyxxyzxuvwsonnjfecba_^]\[ZYYXWWWVUUUSQRRRQQPPPROOOOOPPRTTUSRTTTTUUUUWWXXYYYYZ\\[\]\[[[^^__`_`````ababaceeffgffeeeefhffeddddceffeeffccabcdeddcbbaa`a`_^^__^__^\[ZZWWWWXWVVUUVVTSRSRSURQRRRSQQRNOOPPOOOMMNMLLKKKKKLNMNMMMMLLLJKJJIJJIKIIJHHHGGGFGGGGGFGEEEEEFGHHHHJIJHHHGGGHFFFFGHHGGGGFHGHGGHIEFGHHGGGJGGGHHGGFFFGGGFFGEEDCDEEDFEEEEEFFHEEEEGEEDDEEFEEEHFHEFFeeededgggghhihhhhhhhiihijhhijhhhhhhhijkjikmkjkmjklmmllllmnpoonnnopqoooopprvuutuwwwwuuvxxxxwxxvvvxwwvwsV;6AOh‰»ãëîìêéèèèèèèèçßÕ„nO_z€’w‰¶ÝâææææåäãâááßʾÍàâãäååÐÌÉÊÌÍÏÍÌÔÚÞäâÌ•fN9:;;;889;>@>;876567@HYsv’ˆžŽ§ˆ±¸·¶³°•”ymgcb_dUanXqYSe_mgiu€•¡¹Ïßàáááááááßàĵµ¶« oI9445567:JWi‡‡„‚………„„‚‚ƒƒ„†ƒ„„„……ƒ‚ƒƒ‚€€~}}|{z{||xutsqolieba`][ZXWVVVVVTSSSSRQRQRPQPQSRRPQSRQRUTUUTSTVXWVWXXVUVX\ZYY^\\]]\]^^^``_`aaabcdeggfedddffeedcbceeddeedefcd``bdddddabab`a_^^a^_]`__[ZZXXVW[XXVXUUTTUVTTRRSSSSSPOOQSSROPPPOPMMNMLNKLKLKMMLLLLNLLLLJKIIILJJJKKKGIHHGGGGGGGGGIFDEEEEFHHJJIILIJHIHIHGFGFFGHGGGIHIHHHIHIFFGHHGGGGGGGHHHHEEFFGGEEFEEDDDEFEFEEEFFFFHFGFDGEEHGGGGFFFHGHEGGfeecdeeefhhggeighjihilihihhhjghkihhiikkmmmlkkkjjkmlolkkkllmnnnnopnononppqrwtutuvvuutuwxvxxwwwwwwwwvvvtW;:<GLcw¨ÛêïìêèèèèèèèçãଛiQh}{qrh}´×áæææææææææäÞ½¡¢¬Ëãååãââââáâãããäååâ·taEC@AA@?ELQWUTSQNLIFCBCESYdqz‚ƒ€‰Ž‰…~klbdfdcbbVHT`Uff{nnnq’¡¼ÍÝàáâááßááßÛ×Ñô¦˜gC>=DKJFCAILb{~‚„…„ƒƒ‚ƒƒ…„…„ƒ„…ƒ‚ƒƒƒƒ„„‚‚ƒ„„ƒ~}}~~{xyyxuvxtpnjgecb`_\Z[[ZXWWVVTRQPSTTSRRRRRQRQTSTQSSSRRRUUSSSTSSSTWXXXYZYXY[\]]]]]^_`aaba`aabbaabcbcabaaa``acdcaa^_aa``___a^`__]]]]^]]\[[ZZYXXXXYWXUUVTTTTTSRPPPQQQRPOOOOOOONNPONMMQMKKJKKJJJKLNLKLKLKKJKJIIIIIHHGHHIGGGGGFEGHGHFEEEEFEFGGIJIHHGHGGHHHHHFFFGGFGGGGIHHIHGGGFIHHHGGFGHHIHGHEEEFGGEEFFEDDDDDDDEFFFFFEHGGGGHFEHGGFFFFFHGFEEEeeeegeeeehihggighhjhhhiikhjhjiiikjmkjjlkklklmkjjjklmmlmmmlmnrnnoonnnqppqrsxuuuvvzuxvxwxx}xxxyw{www{xvvX;5<FGJWkßëíìëêééèèéèçåȯƒti~|vqoz‚Œ¯ÐáææççççççæãÁ°¥ª¸Öãääåæååååååååååà¢sjfaUKIFHU^sssehmqrs[cRRLFJCHOQKB@CHR^tu{|}€„}whiXMFDDJ[kmpidfmz…˜¯ÈÜàââáááááÛßÓÑ¿°š‹ZEJVg}c[RKHHV`l„‡‰„„…‡ƒƒ…†…†…†„ƒƒ††…††…ˆ…„„…„†‡ˆ†…„…ƒƒ…€~|{zz{zxxwutrqnjgeb_][ZXWVWVVUUTTSSTSTTTTTSSQPRSSRRRQPQRSSSUUTUWWVX[ZYZ\\\\]]^^^]]_ba_``aa___````cddccbb^````a_a_a^`_^\[\`_^\[[\YXXXVTVZWWVUTTTVTRRQPPPPQPQPONNNNMNMNROMMMMMKKJKKJIJJKLKKMLMLLJLJJJJJIHIIJHIGFFGFFEIHFGFDFEDEFFIGHKIIHHJGGHIHGGEFHGFFFFGGIHGGGGIGEIGGHIIHIIIIHGHEEEEEGEGGIFFDDDDEIFGGFFGFGHHGGGFHGGGFFEFFHHFFGFfeeffdffgkhhggggihihiiijighghiiijjkkkkkjkklmmkkjjjjjmlllllmnooonmmnoqqrqrssstuutvuwvvvxwwvwwxxyyxwxzvxY<;=HGJNYc‹ÍêììêéèéééèèãÞÜØ¦~eXpnjWPQ—ØäççèèççççåáÞÝÖÐÎÛÝáæççææææååäääÞªyt…}aTOLVaz˜†px„|eSPONYVSKDKOM<?=Q[q}‡’—œš–‘†ub\NF???NWm`acd]kmw—«ÊÞââàÝÝÞÛÜÕÔÉÅ«›‘j[YbromljYWOKJN_uy}„…„……………„†‡……‡‡†…††††‡‡…„†…††„„„„„ƒƒƒ„ƒ„„„ƒ‚€~}}|{zyyywuuuspmkhfddba_]\[[ZXVYXWVVTSRSUUTSRRRQQRRRRTSRQRSSSUWWWWXWWYZ[\\[[Z\^\[\_]\]^]]]]]]]]^_]\]^^^_^^]^^^]\Z[\\]\ZZZYXVUVTTTTUUVUSSRVTSTRSPOPQPOONNNNOMMMNNMMNLNMJIIKJIIJIJKJJKKKJJIJHHHHHHIIHHGGFFFGFFFGHFFFEEEEEFFFGGKHHHIIFHGGHGFFFFFFFFEFGGGGGGGGFGJFGHHHHIHIIIGGFEDEEEEEEEFEDDDDEEFGJHFFGHGGGGGGGGFGGFEFFFGGFFFifeeffjggghhiihihhhikjihhhhhhjiiijkjlkkjllmlkklkjjkkmnommmonpppmnnnprqqrsstttuxwwwvwywzwwx{xxy||{yxzvy[=6>IHJLKQZv˜ÃåëíëééééèèèççåÖ–VippiXM]ްØáæçèççççæåääßÝÁ¯¶ÈÒãåææææåÜÖÙâ×Â~ˆ¥}tqn^]nƒ³ª¤±¹w_OEMONONeUNTkLEYq†™¤»ÃËÈÈ®©€ukeJ><@PT`o\[_eo‚sq|‡žÃÔàÜÐÇÃÆÏÎÌĽ²ª›—}pnifaafa[\[F:9<AK\v}‡ƒ‰†‡†…„‡†ˆ‡ˆ‡‡‡‡††‡ˆ‡††††‰††…ˆ‡‡…†„„ƒˆ†‰††‡‰ˆ……‡…„ƒ‚€~~~}{zzzxwvvtpljgeca^\ZYYXVWXWVUUUTSTSRRSRQQQQRRRQQQSSSUXYZWVVWXXXWVXZ[[\\[\\]^[[[ZZZ[[\\]^_^^_``\[Y^__ZWX[XWVVUUTUSSUTTTSRRVTSRQRROOOPONNMNONMLMLKLNLKMNKLIKJIJLIJKJJKJKIHHJIFFGHIHIHJGGFIGHFEFEEEFGFGFFEFFFFFGHHIGHGKHHGFFFEFGGGGEEFHGIHIGFEGHGGHGGHIIHIJGHHGGGEEEHEEEGDEDDFEGGGIFEGHGIGGGFGHFGGGFGFFGGGGFhhfgfgggggfeiiikhhhhijihhhijijijiijjjkjjjjmkkkkllllmlmmmnnnnnmnnmmopqqrssststuwtwvvwvvxxyzzyyz||{}yzvy\=9?JINZNNTWmƒ®Ýêîêêéééééééèá×¹œ€m]^Ygiq—ÓÛæçççççèèççåâÁ´˜©¯ËãææææåÒÐÌ×ÕÑϨ‡ƒ€€}sx’¢ª¬¹Ê¶uaPNNF>>?TjYXVRDx¹µÈÉÖÔÒȲ–yuuYD>@S}riaXiw{€€twz‘¦ÀÍÛÔµ·«¨§¦¡››„{}nh`TNJLRKB50349@Tksƒ„…†Š†…ˆˆ‡…‡†††‡‡†††„…„††‡……„†…†…………ƒ‡„………††…„ƒƒƒƒƒƒ„…†ƒ‚€€}|zzz|yvvuusqpmjgeca`_^\\\ZYWVWXVTTTTTTSRRRRRRRSSSRRSSUVTSTTUXWWWWWXXWVVXYXXXZYXZ][ZZZZYYXYYZZZWWVWXVTSRSTSRRUSSTURRRRRRRQQNNNNOMLNKLLLLLKKKKKKLKKJIIIIIIIJLJJJIIIHHJKGEFGGGHGGFGEEEEEEEEDFIGEEDFEEEFHFFFFFEFFFFFGFFEEFGGFFEEEEFIGHFFFFFGGGGGGIHHGHGFFFFFFEDEEEEEDDCDFFFGEFGGGGGGGGHGGGGGFFFFFFGGIGEgeefffhgggffhhhhghjhghijiiiihiijkjjiiiiijkmjkkkljlllllrmnnnmmmponnrotsstxstsutwwwv{wwxzz{{z{}z|{zzy{y{^=8?JHR`iQMNQ^q¢ÝëíìêêééééééèåܽˆZFNVSSY`ŒÂÓåæçèèèèèçæåâÞÁ´²¹ÒáãååæÝÕÚáäåáݺ¬Ÿ•”—œ §«±»ËàαteSJJCFHLTd`i€JCo¦°¾ÂÎÑÖÔÖ«ŒteYK?:>Ydk`^c}”°šz€ut|‰œ¶ÉÝÄÅ«£•‹‘šˆ‚}{xvrogVGE>;95/.///49HWhƒ†ˆ‰ˆˆ‰††Š†‰‡†‡†…‡†‰‡ˆˆ‰ˆˆ†‹†ˆ…ŠŠŠ†ˆ„†…‰††††„„„‡‡ˆ…І…„†‡ˆ„†‡ˆ‡†††††€€~~~}{zxxxz|zxvuromigda^][YXYXWVVWUUTTUVTUSRSTTSSRQSTQSSUTTTTTTRSUTTUUVVWXZYXYZ[XXXXYYYZWVVUVUSSRSRRQQRRRQQQQRONMLNOMMMMMLLMJMMMLMKKLJJKKLJJHGHHHHIIIKJIIKHJHKJHFFGHGIGFGGGGFFEGFEEGGGFEFGEEEGGGFFEEEGGFFGGFFEEHGGFEEFEDFIGGGGFEFHGGHHGIGFGHGFFFFIFEEEEFDDDCCFEEFGGGGGGGFGGGHGGIIIIIFGFFFFFFFeeefffgggfgggghhhhhihhiijhhghkjjjkjiiiiikkkjkjllmmlmmllmopommmopppqoqrstvrtsuuvvxuwwxyyyyyz{}yyy{z{|x|]>:?JIR`\RMKNR`l“¾ÕìëêéééééééèåÛ¡REBLYZ\aa³ãåçèéééèçèèçåãâ๺Ä×áåææææçççåÛÔ×ÙÕÑØßßßÔËÒààÔ¥ytaZUdyvtvzeb]HQl—¨¯µ´³´¶¬£ydQC?69:GMWZfl}žŸ€pqqolt€–³»Â¹®˜Žw|‰}og`adjyf^QQ=1.,--.//135@HYnuƒƒ…†ˆ‡†‡†‡††‡†…†††…‡‡‡†‡„„…†„‡‡‡……„„…††‡‡†„„„„……†‹††„„…‡„††‡†††…„„„„‚„„„„‚€€~{|{zyxyxvusrrmhhgda_^^^]\[ZYXWWUUWXUSSRSTSSSTTTSRTSRQQQRRSSTWVYVVWWVUUUUUUWWWUUTTSQQQQPPPPQQPONOOONNNLOMLMMMNLMKJJJKKKKKLJIJKIHIHHHHGHIIIIIIJIIHGGHHFFFGHGFFEFFGEFFFGFEEEFFFFFEEDDEEEFEEEGFFFHGFFFEEFFFFFEFFFFGGGGFFFFFGHHFFFFGHHHHGFFFFEFEFEEFDCEEFFFEEFFGFFGHGGHGHJHGFFFFFEFFFGkfgffgfgjfhglggiihgiiiijjhhhhikkjjiigihilknkkkmlklmmmnomoppmmnoopppoqsstuttuvvzyyyyy|||z}|}{}~|~{|{v|]?8?IHSahRLILQU_`}¢ËåçèèçèèééèèäÕ™`ND\ilpx†½Ýãâåèèéèçèèèèçå㺴¬ºÎáäæçèèçäÔÙÙÚÛàâãããâááàß¹Ÿœ Ÿœ•”ŒŠ‰p`TKJVf‘Ї†…}ulaUaB?><=@DMRdt}„ƒzpiemtkbo|‚ˆ‡……ƒ|thdoudZUQW]ge]TP<1,,,-../01116<K^l‚†‡ˆ‰ˆˆ‡‡†ˆ‡Š†ˆˆ‰‡‹‡ˆ†Š†‰‰Š†ˆ‡‹‹‹‡‰†…‡‰ˆ…„…„…†‡†‹†‰ˆ‰…ˆ†ŠˆŠ‰‰‡ˆ„ˆ‡†ƒ‰……„‹†ˆ‡††‡†„„…„ƒ€~~|{||{zzyxxwutrpmigc`^[[[ZXXWWWVTTWVTSSTTSRQQRSRSRQSSRTUWSSSRRRRSSRRRRRQPPOPRRROOOOPNNOOPMMNLLLLONMLKKIIIJKKKKJJJIJIHIJHIIIGIILIHHJIHHIFGHKFEEFGFFFFFFHDEFFFHFHFFFFGGFEEGEEDEEEFGFEFHFEGEEFFFFFFFECEEFHGHEEFGGGHHFFFFGGGFHIGHFEEEEGEEEDEFEGFFFDEEFFFGFGGHHHHHGFGEFFFIGGGfffggigggggggghiijjiiiihjhhiihiihiihhihhjjjkkmmkllmmmnmlonnlmnoopnoortsstxtuuvvvwvxyz{|z||{|||{z}z|{x}^?;?JHTaXYXORS`r^Zw¶ÜãçæçèèéèèèâÚ«~i^e››¿¦É¾½Ýäèéèèèèèèçæãξ¶¢Ÿ¥¾ÝäçèèèåãääääååæçææäÔÆ³¦¨µÍÊÅ•€„ŠxlcROMW\YUQLIFEEKSansvvvvwrmt~ˆ–‹‹zookklnfcanstmutvnva`ce[WPNNYf`\VQD8420-.-./011136?JYjt‡ˆŠ‰ˆ‡…‡‡Š†ˆˆˆ‡‡‡‡†Š„‡††…‡‡‡‡ˆ†††‡ˆˆˆ‡……………†††…††‡…‡…††…„………„†ˆ…ƒ„……………‡„………„…„„„…†‡‡†…€€~}||{|}|zywwvspnligeccb`]\[[[YWWWVVUUTSSSSRSTTSTTSQSSRSQPPPOOPRPPPPNNOOOONNOPPPONNLMNLLKKJJJJJJKKJJIHIKJIIIIHHHHHHFHGHIIIHHHHGFGFGHGEEDEFEHFFFFFEEEEFFEFGFFGIHHFGHDDCEEFFFEEEEDEFEIFEEEEEEDDEDEFEFEFFFGGFFEFFFGGFFGGGGFFGDDEEEEEEDDFFFFEEFFFFFFGGHIHHGFFGFEFFGGHEgfegghhhjhjgghiiiiihhhijjihijihhgiliiiiihiiillmmnmqnmmoonmmnnosoppqqttsrrsstvuwwxwwy{{}{|€|}}{{||~|{}_?:@KJTbsfh]TUineTWg¯ÝåçççèèèèèéæãȰ|c|šŒ‚xov‰±×áæçèèéèèèçæäãáǪ˜·ÜãæçèçççæççææçççæÞ¸¢”žÄ¼ÉÇÙ~^lkszZecb\URHD:95?KÃÕÜÛÚÛÛÝÙȶ© ˜ortxyvsoiijrnqsuwvxrmppcWRMRXYWVXQQQOC;.,++,./0001149HWg}‚‹ˆ‡ˆ‰Š‰‰ŠŠŒŒ‰‹‹ŠŠŒˆ‰††‡‡‡Šˆˆ†Šˆˆ‰‰‡‡‡‰†ˆˆ‰‡‡‡Š†††‡‡†…‡‡Œ…†‡†…Œ†‡†††‡ˆ‡†††ˆˆ‰†ˆ‡‡‡ˆ‡†††‡‰‰ˆ‡†…„‚‚}~~}{zzyxz{ywvtsrpmjfc`_][XWWWWVTTTUWWXURSSSRSQQMNMNNMKLMNMMNMMMMMLLLMPOKLMMMKJIIIJIIJJKKIHHHIIIKIHHIHGGHGHHHHGGIIHHGFGGFFFEFDEEEEFFFEFEEDDEFFFFGGGGHGFFGEFFGFGFFDCEFEGEDFFEDEHEFEDDDDEEFFFFFFIFEEGEIGGFIGGGFEHFDDDDDEFDDEFFFEFGGFGGGGHHIIIGGFEFGFFGGGHHeffggiiihghhijiijjihhiiiiiiiijhhgiiiihjlllkjkjlmnmnomlmnnlnpooppqtqqqqsrssssuuvvwzzyzz{{|||~~}}}z{|y~`?<ALKUbpeiwbZbfXMRTdtÑ×äåèèèééééèæÏ«smieeejpŒ¶ÑæééééèèèèèèçãȬ¤•¬¾ÐåèèéèèèéèèèçççÒ¿‹‹Šº·¯ž‰‘„yŒc^\addc]VPLT_ˆµÓäææåæææãßÞÜÀ¬¤Š††’›š™“Œ…vvxxx||u{”s‚poXZZ[^`^VVOOSQN6-++,,-.03100135<FWms†…‰‰‰‰‰‰ŠŠŠ‰ˆˆ‰Šˆ‹‰‰ˆˆˆˆ‡‰ˆ‡††‡ˆ‰ˆˆ‡‡‡†‡†‰‡‡ˆˆ†ˆŠˆ‰‡†‡‡††ˆŠ‡††‡‡ˆ‡†‡ˆˆ‡‡‡‡‡ˆ†ˆ‡‡‡ˆˆ†…†††††‡……†‡††ƒ‚‚€~}}||{zyxwuvwuspoonjgdcb`_^]]^^[YXWWVUUUWQONNNKKKKLMKMMLKLLKJKJMOLLKJJJJHHIIJJHIIHHHFGGGGIHIHHHGGHFEEEEFGIIHGFFGEEDEDCDDEEDEFEDDDDEEEEEFEEEGGGFFFGEEDFDFEEDDEEDDCDEEEEGFCEFECDFDEFEEEEEEEEEEFGFHFGFFGFEHFEDDEEDDCDFEEEDGCFHGGGGHIIIHGFFFEDDEEHGGGefiggikihgghjjlijjjjjiijojjhhhhhhhhikjkljkmlnlqnnnnmnmmnnonopooqrqqqsrssstvutuxwwyyz{||||}~~}~ƒ{|}z~b@;@LKVcqprqoojaOLNOPYf޹ÍáæéééééééèèäݬaYdPMM_Hƒ·ÕäæèèèèéèèèèæäÔʲ›¢¯ÍâåèèèèéèèèèççÜÓµ–‹„ƒ‚‚‚’š®À¯¤¦£’†‰’—š›š™¢¯ÌÝáåææææææææåäãââÞÏ¿ÂÇÔÝÁ¼¹¶¯¦ š’ˆy€}phcaWXd`qombaQDA83/,+.0.-/03322111138GUg‚ˆ‹ŠŒŠŠ‹‹Š‰‰Š‰Šˆ‹‰‰ŠŒ‡ˆ‰‹‰ˆ‡ˆ‡ˆ‰Œ‡‡ˆ‰ˆŠŠ‰ˆˆˆˆ‰‹‰Š‹Š‡†‡‰†Œ‰‰‡‰ˆˆ‡‡ˆ‰ˆ‹‡ˆ‡Šˆ‰‡ˆˆ‹ˆ‹ˆˆ†ˆ†…†‡†††ˆ…„†‡‡‰……ƒ„„„„„ƒƒƒ„‚€‚‚}|zxusqsvuutsqnnlkieb`][YXVUTRQONMLKKKMMMLKJKLJIMIKKKKJIMIJIHHIIJIIIGGGFGEHHHIGHMHGGHFEEEEFFIHHGEEGDDDEFFDDDDDEFHEEDDEGEFEEEFFGGIFEFGEDDFFGDCDEEDDFDEEHEEEFDDDFDDDEFGEDEHFGEEEHFGGGGGFHFFFGFEDDCCDDDEFFEEEGFEFGHHGHIKIHGFGIFFEEFIGGGgggegihhhgghhhhgijkjhiikjghhhhhhhgijkhjmkjmllmmmmmnjnommmnnooopssqqpsrssstvuuuwxxxy}}|}}|~}~~}}}|}{~bA;@LKVepirdmijh\PPOOQZb€™»ÝåééééêêêêéåÞÎd\ep|nbgmÄ×àåèèéééèèèèççäÝÁ¥©©¿Þåèèèèèèèèçæäâßűž‹”¤¸ÅÐÏÐÖÝÜÜßÜÜÜÞßßßßàãæççççççççççççççççåãááäåäÍÉËÌË×ÖÙÊÁ»‰sqndYcd`]`dl|ifaWOC=83/001123567765420.24?FXlt‡‡ŒŠ‰‹‹Š‰‰ŠŠˆ‹‰‰Š‹‡‰‰Š‹‰ˆˆˆ‰Š‰‡ˆ‰ˆˆ‰ŠŠ‹ˆˆ‰‰‰‰‰†Š‡‡ˆ‰‡‰‡ˆ‡ˆˆˆ‡ˆˆˆˆ‡‡‡†ˆˆˆ‡‡‡‰‡‡ˆˆ†……†††††††††ˆ†…†……„„…„ƒ‚€‚ƒƒ‚ƒ†€}}|{zzyyxyzwuttttrppplhfda^[XVSQPNJKIIIKLKHJKJIIIIJJKIIIIIIGFFGGGHHFFFEEDFGHHGGIHGGHEEEEEEEIHGFEDDCDFEBDECDDDDDFEEDDEEEEEEEEFFFFEEHEDDDDDDCCDEEDCDDEEEEEEFBCDDDDCEEFFDDEEFDDDEEFGGGHEFEFFFFEEEBCEFGEDFEEEEGEEGHGGGGHHHFFFFFEGFFHGGGigggjgfhihhhhggiiiihghhkkiihihjihhijljjlmmmmmmmnnnnooomlmpsttpqstrrrrsttstxvux{z{xx{}|~}|}}~~}}}~~aC;@LJUdpkraonpo`VPNNPRSYpŒ´áçèèéêêêêééåÚ¡š˜˜–eb`bmÛáåçèéèèèéèèèèæåÑÀ¨š½ÞãåæççèèèèåÙÓÚâÉÑÌ̹ÜÑÞßààààáâãääåæææçççççççççèçççèçççççççææåæçæåãàááââââÜÙ²‹yonedeepp€nfg\[`ZgQKIF;68;<=>?>?@?=:4000235:JXi‡ŒŠŒŠŠŠ‰Š‹ŒŒ‰‰Š‹‰Œ‹‹ŠŠ‰Š‰‹ŠŠŠ‹‰‰ˆ‰‰Šˆ‡ˆ‹‰‰‰‰‰ŠˆˆŠŠˆ‡Š‰ŠŠ‹‹Š‰ŒˆŒ‡‹‹‹‰ŠˆŠˆŠ‡‰ˆˆˆ‹Š‰††‡ˆ‡‹ˆ‹ˆ†‡Š††…„„‡…………ƒƒ…„ƒ…„„…†‚~|}}{}~~}{zz{wttusrolhd^YTPLKHGHJKKIIIHGGHHHJIIIMHGHHEFGGFFFFFFEEEGGIHHHKGGHHEFDEFEDHGHFGDECBDFCCCCCEDCDGDEEEEEEDEFEDDDEGDDECCFDCCEDDDGEEDEFFEEEDEECDCEFFDEEEEEDEDGGFEEEEFHHHFGGHFGFGDDCCFFGEEEEFEEEEEFFFGHGHGHFGEEFEEGFHGFGhfghgggiiijjhghlhgiiijkkjjihhkiihghjjjjlmmlklmnpnmmmmmmmnoppqqqtrrrsrrttttuvvzzyzyz|}||z||||~€~~~~}„cE>@JJTcojpboiooij\VRPRSXZm{ŸÌÖçèéêêêêéçϳ‚lZJECOY\q‚ËÍ×ÜâæèèééééééééèÙô¦¿Ë×ãåçèééèåÐÊÇÇÇÈǽª´¿ÉÍÏÓ×ÛÝáææçèèèèèèèèèèèèèèçèèèççççççççççèèççççæççææäÙ¯‰…r}spljrtvwjmaVVXVRJKCEJMOQPOPQQTMA7401244479DJ\my†‡‰ŠŠŠ‹‹ŠŠ‡ŠŠŠŠŠ‰‹‰‰‰‰‰‰‰‰‹Š‰‰‰ˆˆ‰ˆ‰ˆ‰ˆ‰ˆˆ‡ˆ‡‰‰ŠˆˆˆŠ‰Šˆ‰ˆˆˆˆˆ‡‡ŠŠŠ‰‰ˆˆ‡Šˆˆˆˆˆ‰ˆˆ‡‡†ˆ†ˆ‡‡‡†ˆ‰†††‡‡††„‚„„„„„‚‚‚‚‚‚‚€€|}}~~~~~}}}||yxxwusrqonf_YQMGFEFGHIIIHGGGGGJHHHHGHGFFFFFGEEFEEEEEGEGHIJIGHHHDDCDDDDFGFEEDDCCDEECCCCCCCCDEEFECDDDHEDDGDDDCDFCBDFCBDDDDDDEEFCEEEFDEECCCCDFDFEEEEDEEEFFGEFEDFFGFFGFFGEDDDEDFFGEDFFFFFFEEFFFFFGFFGFFFEDDDEEFFGHhiihgghhhihhkkkihgiiiikjhiihhiiihghkkjmmmmmllnonnnomnnoppppqrrrsrrrrutwvvuuvxwwz||}||||}…|~€€€€‚eE?AIJT_rqqgphnllld^SOSVVWW^k¸ÔçéëêêêéèŹ]_<:-5GMVfœ¥»ÞåèèééééééééèäÞÊÃÇËÔÓßçèéèæØÇÉÍÔÚѸ¦Œˆqpt…›§³ÊàáççèèèèèèèèèèèèèèèèèèçççççççççèèèèççææææäãÛÜ£wpw~‚|vqda`gkTRNKLKIGHJLV[`csqkb^;0--043768:;=ASht††Ž‹‹‹‹‹‹‹‹ŠŒŒ‹ŠŠ‹Š‰‡ˆ‰‰‰‰Š‰ŠŠ‹ŠŒ‡‹‹ŠŠŠ‰‹‰‹‰‰ŠŠ‹Š‹‰‰ˆ‡ˆ‡ŠŠŒŠˆˆ‹ŠŠˆˆˆŠ‰Œ‰‹ˆŠˆˆ‡‰‡‡‡‡ˆ‰‡Šˆ‡‡‡………‰…„ƒ‡„ƒ„„„…‚‚ƒ„ƒƒ„…€€~~~€}}~}yyyztunj]OFEEFGGGGGFGIHIHKGIHGHJGGFGFFEEEFFFFDFGFFHHIHGJHGEDDECCDGGFEGEDCCCDDCCEDCCGDEFDEEDDDDDDDCDDEDDEEECCDDDEFFDCDGEFEEEGEDDFDEDGGFEFEFEHDEEEFGHEEEEGGIFFFFFFDDDDEEEHFEFGGGGIFGFGGIFFFFFHFFEEDDDDEGFIGggijhghggihhihighhhfiiijihjkijjjjjkmkknnmkmnmnpmnnnmnopqppqrrrsustsrttvuvvvwwwyz{|||||}~~~|€~~~€€~‚gF@BKMPT\dcejilkortwd]\W[kWR\d…ªËåêëêêééÙɰ rL1,7:[axs†n–¤ØÜááåéééêêêêêéèæåã×ÌÓÝåçèèçäâââãßÚ¹¼ŠŠbRNNT[j|ªÖæçèèéèèèèèèèèèèèèèèèççççççèèèèèèèçæææåäãÏË«‰nYVVf‡ˆˆyseYX\][QHEC=766<MTrijn€kiO5.'.11278?EECDHYbox}†‡ˆ‹‹Œ‹ŠŠŠŠ‰‹ŒŒ‹‹‰Š‰‰‰ŠŠŠ‰‹‡‹‹Š‰‰ˆŠŠŠŠŠˆˆˆ‰‰ŠŒŒ‰‰‰Š‹Š‹Š‰Šˆ‰‰‰‰ˆ‡‰ˆ‰ˆ‰‰ˆˆ‰ˆˆ‡ˆˆ‡†ˆ‡ˆ‰ˆ‡ˆ†‰‰‡‡ˆ………ˆ„ƒƒ…………†‡†ƒ…†…€€‚‚‚€€}~}|{{zzzzwwxpiYJFBBBCEEEEFEEGFFFGGGHJGFEGEFEEDEDEGFFEEFHGIIGGGFEEEECDDDEEEFFDCDCCBCCEDCBCDDEEEDDEEDDDECCDCCBDEDCBCCBCBCCCEFDEDDEDDCCDCDCDDEEEEEEEDEDEFFGFEEFFGFEFFFEECDEEEEDDEFGGFFEEEFEFGFEEDEEEEEEEEDDDDEFFFfghhhgjgghiikillmihgjiiikijiijjikjkkkmoommpmlnpnrpopppqqrsusrrtusuttwvyvywxzzz{{€~}}}€€~€‚€€‚‚hG@CMLLKQ]mhsstkqsqrrqg_bcYQRV]|¥ÈãèééêééæãѸm::ARdw‰›³ÏÚÜØ¾¹ÍÚáåçééêêééééèçàϘª½ÓäæèèçççæÝ¹’„jgVSJLKIMpˆÙåèèèéèèèèèèèééééèééèèèèèçèèèèèèèèççççäÓ¿£•„vd[SNPX`enjiaZ\_[UPKI99*),:Q_ke_ejYH2,-5=A?=AHVbZTRT_agnv„†‹Œ‹‹ŒŒŠŠŠŠŠ‹Œ‹ŒŒŒ‰‹ŠŠ‰Š‹ŠŠŠŒ‰Š‰ˆˆŠ‹‹‹‹‰ˆ‰‰ŠŒŠŒŒ‹‰ŠŠ‹‰Œ‹ŒŒŠ‰‰‹‰Œ‰ˆˆŠŠ‹‹‹ˆ‰ˆ‹ˆ‰ˆ‡ˆ‰‡‡‡‡†‡‡Š‡‹†††‡†ˆ††††‚„„‚‚‚‚€€€~~~}{||z|zuniVGA?@ABBCDDDEHGFEHGGHJGFEGEGDDDEEEGEEEEEEFHKGGGFEFFEDFEEDEEEDDDEDDBBBEDDDECCDDDDEFEECCCBCDCCCCCCCBBCDDDCCCFFDFCCCCDCCGCFCCDFEDDDEFEEDDEFFEFFFFEEDGFIEHEEFEEFEDEGGGFHEGGGFEFFEGDEEEDFEDDDDEEEFGFgfhgggggggiijiiiiihhijihjhjhijjijjmnnnnonlonnmonqppppppprtttsrtuttuuvvuuzwz~{||{}}~}~~~~~€€€~ƒiHABMPMNRSX]ejlkqsq€q€e_d]XPRSW\x•»ÞäèéêêêëèÌŸ‡˜«ÁϽÐÕÚÜÙ¹{[|š½ãçéêêééééééåÚ´“¡°Èáçééééçᮣ‚{uuw†™Š|~…ÖæèééèéééèéééééééééééèèèèçèèèèèèèèèèççãÜ£“ƒ{ebZVRTV[Vb`gdYMKKMOML?<,%%&-69:<AEI@522DhfbTMRalxjabfecdfpx}††‰Š‹ŒŒ‹Œ‹ŒŒŒ‹‹‰‰ŠŠ‰‰‰ŠŠŠ‰‰ŒŠŠ‹Š‰ˆ‰‰‰ˆŠ‰‹‹‹‹‡‰ŠŠŠŠ‰Œ‡ŒŠŠŠŠ‰‹Š‹‰ŠŠŠ‰‰ˆŠˆˆˆŠ‰Š‰‹‡ˆˆˆ‰‰‡ˆ‰‡ˆˆ‡…‡ˆˆ‡‡‡‡…‡‡‡‡†……ƒ„…ƒƒƒƒ„„ƒ‚€€~}~{|}{}~yum`M=<<>AAABDEDDBDDFGGGGFFEFFFDEFEGEFEDDGEEFGHGGHFFEFEDFFFEEFEDDDDCCCBBCECCEACFEDEEFDECCCBDDDCBCCCBBBBBBCCEEEEDDCBEEFCDECEBCCCCDEDDDDEDDDEEDDEEEDDEEEEEEEEDEFFGEEHFFEFEFFFFFFEDEDEGDDEDEEDDDEFGFFgfiggfhgggiimiiiijjhhjkkjjjjkjjjkknnonnnooonomqqqpppqqsrrsvttsssssvvwwwwzw||z|}}ƒ€€€€ƒ~€‚„jIBEMPNNNLPXhjtrsrqvrpmic_VQQRRUYp‹¬ÏØåèëëëéèãÞßàß׫ ‹’—˜’…dXF‰ÛãçééééééêééäÙ®œœ¦ÂÞäèèéèçäàâßÞÜâßààáââáåèéééééééééééééééééèèèèéèèèèèèèèèçççæåãâ´’Œ|qkhgk~mjmptwkZJ>:>EMKC7.+*$!#%&')-5>BBFQrŽyj]Z^tx{eggka_^ahoz‰ŒŽŽŽŒ‹ŒŒŒŒŽ‹‹‹Ž‹Œ‹ŽŠŠ‹Š‰‹Š‰Š‰‰Œ‹‹‹‹Š‰ŒŠŠ‹ŒŒŠŒŒ‹Œ‹‹‰‰ŠŒ‰‰‰‰‹‰ŠŠŠŠŒ‰‰ŠŠŠˆ†ˆ‰ˆŒ‰ˆˆ‹‰‰ˆˆˆŠŠŠ‡‡ˆ‰‰ˆ…†……†ˆ…„„…„„‚€~~€||yxhT=99=????@ABBBDEFEEEFEEEIIIGGEEFFFFDDDEEFFHGGHGFGGGEEFFFFFGEFDDCDCCBBDBCEDCDEDEEFEECECBCCCCBEDDDCBEBDCCCCDCCEBBBCCCDFDDCCCDCCCCDEDGFFEEEGEEFGEGEGFEEEEEEEFIGFFHGFFDEEFEFHFFFFEDDDDFEEFDDDEGFEFffhhgfgggghhjiihghhhhkjjjljjkikkkkmnnnooonnnnnppqqqprssssssstutstsuuwwwxxy|||~~}~}€€€€€€€€€‚€„kJCFOONNNNRQVZ`fmrsywxlhkwf^WQSTVZft¬ÃßçëêêëëëëêçÛyiKl‡—££¡—‹–¬Æáäæåçéééêêêèá´©žœº×áèêêééééèçèéééêéééêêêêêééééééééééééééèèèèèéééèèèèèèçæåäâáàÑᣇwzxy{zytuswyqfVNIGEDB8-*(''"!"%$$)/:db\f‰xjg``aacffc[XX]bgjrx|€„ˆ‰Š‹ŒŽŒ‹ŽŽŒŒŒŒŒ‹‹‹Œ‹‹Š‰‹‹ŠŒŒ‹Š‹Š‹‹ŒŒ‹ŠŒ‹ŒŠŠ‹Œ‰ŠŠŒ‹ŠŠ‰‹ˆŠŠ‰‰Š‡‹‰Š‰ŠŠŠˆŠŠ‰Š‹ŠŠˆ‰‰ˆ‰‰‡ˆˆ‰‡‰‰ˆ‡ˆˆˆ‰‰Šˆ‡ˆ…†ˆ‡‡‡††…„„„„„„€€€}|{xp[B:479;>??@@ACCBCCDEEDEFFEFEEFFEFFFDDDEHEFGGGHHEGFGFFFFDFGEDFDDBBBBBBECCDCCCDDEEEDDDDCCCCBBACCDDCBBBBBBCCCCBCCCDCBCEDDDDCDCBCBCDEDEEEEEDEFFGFEEEFFEDEEFEFGHGFFGGGFFEEFFEEDFHGGEDDDEHEEFEDDGFEEgfghgfhhgggijjihihhhhijjiiikjknmnmqnonoooonnoorqssrruuusttvtxuuuxuwx{wyz|}|||€€€€€„‚ƒ„……€€~‡mKDFOOOOOPPMNRZbpvqtjoieruongVSSTTV[cz‘¶ÞéìíîíìëëéÞ£mflޤÉàààßÞßßáâßÛÚàààäéééèåÏ»±ž›¡ÛÝãçèééêêêêêêêêêêéêêêêêêééêêêêééééééééééééééééèèèèæåáÝßÕÅ·²ª¢™‰ˆgnooruz|trty|~mgfbQC</)&#$$$ $)06CKUr|œŠˆx{b^QHMU\cRNQUZ^^ahmry†ŠŽŽŽŽŽŽŽ‘Ž‹ŠŒŽŒŒŒ‹‹‹Œ‹‹Ž‹‹ŽŒŒŒŒŠŠ‹ŒŽ‹Ž‹Œ‹ŠŠ‹ŽŠŠ‹‹‹‰Œˆ‰ŠŠ‹Š‰‰ŠŠŠ‹‹‹ŠŠŠ‰Š‡ˆ‰‡ˆˆˆ‰†‡‡‡ˆ‡‰‰†‡ˆ†…†ˆ…‡„†…‚‚ƒ€€‚‚ƒ~~}ujO;3469:<>??@@@ABBDDDDDEEEEFGFKGHFFECDEEEFGGHHIEHGGEFEFEEFDCFDCCCCCBBCCDDDDCDDDEDDDDCDDCCBCCCBFDCBCBCBCCEDCBCCDDBBCCCDDDCCCCDEEDFDDEFFFDDEEFEEDEEEFEEEEFFFFGGGGGIFGFIFGFFGFGFFFDECEDDDDCDEGFEDgfhihghjhkiiihggilihhhklijjjkklmmnqoomooprqqppppsrrstttstttstuvvvuyz{wzz||{z||~€€€€ƒ€‚ƒ‚‚€ˆoLEFQQPOOOOOPPSTe}twrskhw}}hb]WWUUUY]mz¤ßéïñïíìëëåÝÝʺ°ÃâêêëêèåáÜ¿€{Š«ÜåæèéèäâÙȺÂÝÝÞàåèéêêêêêêêêêêêêêêêêéêêêëëêêééééééèéêééééèèèèèæãÏ»§˜‘Œ‰†ƒsgVAHRejiijjrt~…{uokf_TE>70+(%&!! !#(+/2;BR]l~~~oiXI?:;<>??ADHNUZ`chnpuy}ƒ‡ŽŽŒŒŒŒŒ‹‹ŠŒŒ‹ŒŒŠŒ‰Œ‹ŒŒŒŒŒ‹ŒŠŒ‹Œ‹ŠŠŠŒ‹‹ŒŒŒ‹‹ŒŠ‹‹‹‹ŠŒˆŠŒŽŽŒ‹ŠˆˆˆŠ‹ŒŠŠŠ‰ˆˆ‰ˆ‡ˆˆˆˆ‰†‰‰‡‡‡ˆˆ‡ˆ‰‡†‡†ˆˆˆ……„†…„ƒƒ‚‚ƒ~€~zmX?00158:<==>>?@ABBBCGDDDFFGHFFFEEEGDDFEEEFFGFFEGEFEFEFEEFECDDCCBAB@BBCEDEDCDDDDEFDEDDCBBBBBAAABCBBBAACBCBBBBBBCBBBCCDEGDBDDDDEDEDDCDEDDDDDFFEDDEDEFEEFFFFFFGHHGGGGEHFHGFFFGFDEEDDDDDDDDDEFFEFjiijjhgjhiiiihghiijjkhlkjjkkmmmnmnpooorrqqrrrrqqsrutttttuvztuwxvyxyz|z|}~~||}€‚ƒ‚‚ƒƒ„‚‚ƒ‚€‡qMFGSQQPPPOOPOON]tŒvptsvz|~}uka[WUSSUVV_k“ÁÛïñòðîìëéçåãããçêêëëçफ़”dC„ž†{²ÝÝÛáåææåäãâåçáÛâàäçéêêêêêêêêêêêêêêêêêêêêêêééééééééééééèçæåãàÞ¿¥Žz…‡‰ŠŒ‹zqM7+/Iuvqg^dqsˆ}zvvpnol_TNF:1*(%$##$'*--2;@DHWdb[ULB85142669;>@GMQX\_cfjov‰”“”ŽŒŒŒŒŒŽŒ‘Œ‹ŒŒŒŒŽŽ‰ŠŽŽŒŒŒŒ‹ŒŠŒ‹ŒŒŒŒŒŒ‹‹Œ‹Œ‹‹‰ŠŠ‰ˆˆˆ‰ŠˆŽˆ‰‰ˆ‡ˆ‰‹‡ˆˆˆˆ‡†ŠŠ‰‡‡‡‡‡†„„ƒ‚‚…‚‚~{zbB0,-269:<<<<>>>@ABBCCCBEFFIFGFFDEEDDFFGDEFHFGFGGHHIFFDDEFDDCCBAACBEBCDCEGEGDEEEFDEEDCBAABA@@ABBB@AAADBBBCCCCCCDCCDEEEEEDDDDDDDDDDDCEDCECCFIDEDCCDFHFGFFFFFGGHHGHHHHIHGEFFFHEFFEEEDEEEDDDDEEFhghkhghkijiihhgjjjjjkhjjkklllmnmnooooppppppqrrrrssttuttuuvvuw{xvxxyz{{{|}~|}}~‚‚‚„ƒ‚‚‚‚„€€€ƒ…pOGHSSRRRSPOPPPPXZ[ajxvxz|xxxxwsgc]SUWVV]f†¤ÄçòôñîëëêëìììëêëëëéÛ´‹hu𣧢¡„ Ãâçééêêêêäת¾ÔâçêêêêêêêêéêêêêêêêêêêêéééééééééééééèãÞßܶ§šŽ‡vz~…ƒ‚}p`N4.%4KxŠ€{l^flrvvvtromouf[OC90121/.0//*')-0136?JA;74:??=:67688:;?CIOTX^cgjqxŒŒŒŒŽ‹‹ŒŒŽ‹ŽŒŒŒŽŒŒŒŽŒŒŽŠŒŒŒŒŽŒ‹ŒŒŠŒ‹ŒŒŒ‹‹‹ŒŒ‹‹‹‹‹‹‹‰‹Š‹ˆŒŠ‰ˆˆ‰Š‡‰‡ˆŠ‰ˆ‰Š‹‡‡‡‡ˆˆ‡‡†‡†‡†‡†………„„‚…‚…ƒ‚€}zjI0*(-269:;;:<=>@@@AABCCEEEFFEEEDEFEDFEDCDGFFEEFGEDDDGDDCEDDBBBBBBAAACCDEEEDDEFEEDCCCCCBABCAABCBCAAAAAABECCCADDDECCDGDCEGCCDEEFDEDDDEDCCCDFFDDDDDDDDDEEFHFEFEFFFHGFIHHGFFFFFEGHFFFDEEEDDGEEEFhhhhhhiihjihhggjkkkkkkmllllmmnqpporopqsstrtrvsttvttuxxxxwvvwyyxxxz|{|}}~~}}~€€ƒ‚‚„ƒƒƒ‚‚‚ƒ‚„„…„ƒ‚ƒ‡qOGISSTRRQPPPPPPQNOWiz†‚€|uqpxz~–vdVUUUUUZ_l„²áðóòñîììììììëëëëëæáâàÒÁyzzrj]J”£ÆßáàäèéêæÚ½®°¹ÕæèêêêêêêêêêêêêêêêêêêêêêêêêêêêéééèåÚ®”uz|€†€‡…„ycYC710;W}Œˆh`dt~‚„ˆumjmsw_YKA<?AA?:850'"!"""'+-*'(/EV_MB;9888789;>AFKQV[^cjt€ŒŒŒŽŒŽŽŽ‘ŒŽŽ‹‹‹ŽŽŒŒ‘ŒŽŒŒ‹‹‹ŒŒŽŽŒ‘‹‹ŠŠŠŠŠ‹ŒŒ‡ˆ‰ŒŠŠŠŒ‹‹‡†ˆ‰‰‰ˆŒ‡‡ˆŒˆŠ‰ˆ†‰…„„ƒƒ…‚ƒ€|nQ4)'(-6889;:;=>>>@@@CBFEEDGEEEEEFEEEGDDDDEDDEDEEEEEDGEDDEDDCBBBBBAAAEDEEFEFDDDDEDDCCECECABBBBBCBBABCDBBCCDCCEDDECCDDCDFDCCDDEDCDDDFDDDCCDDEEEDEEEEFEFEFFFEGGHEFFFFIHHFFHGFFFGFFFEEFEEEEFFFFFhlhhhhhhhjilknhikkkklmmnnpnnnmoppopopqrrsrssusttuuuuvtxwwwwxxwyzzz{y|}}~~|~~~€€‚ƒ‚‚‚ƒ„ƒ‚ƒ‚„ƒ‚‚ƒ†sOHITSSSRRQPRSX^UQSSaqknw{vqs…}vseVXUVYXX[]kwŸÒàôöòíìëììììììëëëëëêæÍœ‹nlgfi~˜¨ž…šÌÜèëèàÖÊÀÁÉáæëëëëëëëëëêêêêêéêêêêêêêêêêêêééèæß³qhPiux|}}|zzzz{xvtn]KUb‚’‹†r`Wiƒƒ‰wogool^TKHEFINSIB>8-#! ! !!"$"!&.BdVKB><<;:867788;>CHOX^bkp|Š’ŽŽŽŽŽ‹ŒŽŒŽŒŽ’ŽŽŽŒ‹Œ‹‹‹ŽŒŽŽŽŒŽŒŒŒ‹‹ŒŽŠ‹‹ŠŠŠŠŠŠŠ‰Šˆ‰ˆˆˆˆˆ‰ˆ‰ˆˆˆˆ†ˆ‰ˆ‡ˆˆŠ…Šˆˆ„………‰ƒƒ†€‚‚€pY;,$&*1688::;<===??>?@ABCDDDDDDBDDDDDDDBDFDDCCDDDDDDCCDEDCCBBBBBBAAABCCDDDFCDDDDDDBCEBCDBBBBBAAABBCCCGDBCEBCCBCECCCCCDDCCCCCDCDCDDDDDCCCCCDDEDFEFGEEEEEEFEFEEFFFFFGGFEFEFFGGFEFFEDEFFEEEFFFEiiiijjjiiiijjiijkkmklnonnnooonnqssspqqrtusrsuuwvuvyw{wxwywwy|y||{{||||}~€‚‚€†‚„„ƒ‚ƒƒƒƒ„ƒ†‚ƒ‡uQIITSTTRRRQTXbeXPQQUW\fz{ytt†z~tsw\ZUVWWWXZ^dl°ÕîñóñïíììììììììëëìëéåÜÒÉÈÆØ×ÚÔŸˆ_O„žäêéèçæäâäæèëëêëëëëëëêêêêêêêêêêêéééééèèççæãЦ…lu„’™—•˜™Š”´¹½ÄÎǺƒhr‰™‹‰|aR`v‰‡ŠyxtrsmbUN\TOQZUUGIA3($"#$$"! !%1@OKG?@?>>;:6556789<?CJQW]cmx†’Ž’ŽŽŽ‘’‘“ŽŽŽŽŽŽŒ’ŽŽŽŽ‘ŽŒ‹Š•‹‹‹‹ŒŠ‰‰ˆ‹‰ˆˆŒ‰‰‰‹ˆ‰‰ˆ‡‹‰‡‡‰ˆŠ‰Šˆˆ††………„„†‚„ƒƒvlE/%$'0488:::<==<=>>???@BCCCCDGDEEGEDDDEDDDDGEECCDFCCCDCDDDDCBBBBBB@CCCCDDEDGEHCBBBCECCCCCEBAABACCCCBCDCBCBCCCCCCDEDEEDDDCBCDDDDDDDDCCDDDDDEEEFEGFEEGEEEGFGEEFGGGFGGFFIDEFHFFFFEEDEEHFFFHFEEiijjjjjjiiijjjjjjkkjlppnnoooppprqoqqrqsttvssuuwvvvvvwwwvwwx{{yzz{|~~}|}~~€€€ƒ‚‚ƒ‚ƒ€ƒ…ƒƒ„ƒ†‚‚‡wRKJUXTVTSTU\agrWPRRTVYX_gp~†|upl^ZUUVVVXY[\bh‚˜¼åóõòïíìììííììììììììêçæäáÞÕǨ€Zmˆ´àéêëëëëêêëëëëêëëëëëêëëëëêêêêêéééèçæåäããàØÒÍÁ´¹½ÎáàÞÕÌÉŸ®¾ÖÑÐØàÔÆ›|†Š‰ˆ‰„gLYi€woorvzri]U\YUQOLF@??:6/(((((&&'(-7HQRQMIE@>86544766689<>ELT]dgq{Ž“‘ŽŽ‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽŽ‘ŽŽŽŽŽŒ‹‹‹Œ‹‹‹Œ‹Š‰ˆˆˆ‰‰‰ˆŠ‰‰‰ˆ†‰Š‰‡‰‰ˆ‡ˆ‡ˆ†ˆ‡‡‡‡†………„„ƒ„ƒƒyrP5%#$+1979::<;;=;<@?>??@AABBDCCDDDEDDDDDBCBEFDBCCDCCCCCCCDCBAA@CBBAABCDDEEDDCDCCCCCDCDCCBBBBBBABBCCCCCCCCCCCECCCDDDDFDDDCCCDEDDDCDECCDCDCCDEEDDGFEEEDDEFFEEEGGGFEEEFEFDEFEEEEEDEDEEFFFFFFEDhjkkjjjjihjkjkljjlllmnpprpqqqqpqpqurusutsttuvvvv{vvwxxwwxxyz}}}||}~~}€~€€…‚ƒƒ„‚‚‚‡„„ƒƒ„‚†„…ŠwSLKWVTVVTUWetsnXQRRSSRQS[j€‰{‚ur}]ZVUWVWXZX[[_cq„²áðóòòïíííííììììììììëëéÜ×—xŒ|ˆÂÅßäêëëëëëêêêëëêêêêêêêêêêéééèèèçççææäâÜÕÐËÆÅÈÔãäããäåæåäÝ×ÏÆÄÈÚææåæçäá²’ŠŠ‹ƒrNRjzxhcgqˆvgdd_[\^`J<=DACHC3/-,,,.015Hbkusoa\IA<76666766789;>CHOWYbio|Œ–Ž‘‘‘ŽŽ’‘‘’‘‘‘‘ŽŽ‘‘ŽŽŽ“ŽŽŽ“ŽŽŽ’ŽŽŽ“Ž–‹‘ŽŒ‹‹ŽŠŒŠŽ‰‰†‰ŠŒŒŒ‰Š‰Œ‰Š‰‹ˆ‰ˆ‰†ˆ…†……ƒ‡„„~w^<'#$(-98:;::;;>;;;<>?>???@@BBBDDDCCCCDDBCBCDCAFFFCCCDDFDDCCBAACCECDBCCCDFCDBECCDDCDBBBCCCBBBBAABCDCCCEECDCCCCCBCECCDDEDCGDDDFDCCDCCCDCDCDDEEHGGFFEDDFEEEEEEFFGHFEDFEFEEEDEHEFEEEFFFFEEGFEDijjljjjjjikmmllklmmmmmpopoqrrsqrqrurttutttuuvvvvvvwwxyxxxxz{||}|}~~€~|„‚‚ƒƒ‚‚‚ƒ‚‚ƒ‚ƒƒ„„…‹zTMMWVUVVTXZj‰nm^TTSSTSSUVdysz„utz\YVVWVZZ[\[[[^bny Ðßôöòîííííììììíííìììêáѽ°¤–““®ÙåéìëëêêëëëêêëêêêéééééèçææåååàÝÙÖÔÓÓÉÅÁÄȸ´ÀßââããåèééèçæáááåæçççèééæáÎÀ¤‰ƒwaD[†‰‹q_alpxg]adefekd[KLIHLRA79A@@?>>>F[j‘ŽŒ{qWKIGB=:88777777::;?DKRZZ^gu‚Š‘’Ž‘‘ŽŽ‘‘ŽŽŽŽŽŽŒŽŽŽŽŽŒ‘ŽŒŒŽ‰‹‹‹‰‰‰Œ‹Š‰‡ŠŠŠŒŠ‰‰‰Š‰‰‰Šˆˆ‡ˆ‡†…†…„ƒƒƒ‚ƒzjD*$!&+38;:::;9;:;<====>>>@@@AAABBABBBBBBBBCCCBCDCCCDDDGBCDCBAACCCDDBCCCCCCCBDCDDDCEABBAAAAAAABBABACBCDCCCBBBCDCCCCCCDFDBDCDBCCCCCCCDCCCCCCEDDDEEFFEEEEEEEEEDEEFFEEEFFFEEEEFDDDEEEFFHFDEEFEiiijjjkjmlonmlllmmnoqnppsssroppqqrutyuvuyvuuvwzwxw|xyyyy{y}}}}~}€~}}~…€€†ƒƒ€ƒ‚…‚ƒƒ‚‚‚ƒƒƒƒ„‚‰†‡……‹{UNNWVUVUTZ]ruoo`YVQSTUSQQYdjkq{tuw[YVXWWXWXXXXZZ\^em°ÕïòôñïîíììììììììëëêéæãâßÃ¥™›ÚäèêêéééééèèèèèççææææåäÞÙ×ÕÒÒÔØÎÔÀÏÒÍÆ¿Ã®¡¥²¸¾ÈÓÐÐáãæèèèçæçèçæææèéééçåÚ׬ˆ}_]j”¶’{e[cco\[_hsuqnl^SPRKOL@<BQ^[VRNOSUnwˆ„€qURUY^J?96777666899:=AHOPXbft|€‹‹“‘”‘’‘’’’•‘’’‘‘Ž‘‘“Ž’‘“’‘ŽŽ“ŽŽŽŽ‘‘ŽŒŒŽŽŒŒŠŒ‹‹Š‹‰Š‰‰‹Œ‹‰Š‹ˆ‰ˆ‹ˆ‰‰‰‡†‡ˆ…†ƒ}qN/&!#(28;:::;:;::::;=<<==>>??AAABBBAAABBBCDDDCBCBCDCCDGCCDCCBBECBCDCEDDDEDCCCCDDDDEBAA@@?@AAABBADBBBBCBCCBCCCDDDEDDCDDDCEDFBDCEDCCCCBCBCDDEEEDFFFFFEEEGEEFEDEEEEEEEFEEEEFGHGGDEEFEEFGFFFFEjjjjiikkkkllmmllmnnopnoqqqrrqpqsssssttstvvwvwxwwwwxxy{zyzy|||}}}~}~€€€€‚‚‚ƒƒ‚€‚ƒ‚‚ƒƒ„„„†ƒƒ†……„…Š}WQOVVVUUU[^qtrqqr^RTSY_\\\ZZZi…tvw\YVXWXXYYXYZZZ[\^cj‚™¼çôõñïììììëëëëëëëêêêêêèäÝÉÑÝáåæåääãâàßàÞÛÖÖÔÏËÈÄÂÂÆÊÒÓØÛÞäÜØØÝßßµ¬››Šœ™šœš–›¤»ÜæééèèééæâÙâçééééèèçà½©š‰•Ÿ›Œvlgjlgachovi^[YTPVLPNI@IU_k^ZWQNKPX`gpqidipbQ><887777687789;?CHSY_ips}…‹•’‘‘‘‘‘‘‘‘ŽŽŽŽ’‘’ŽŽŽŽŽ’ŽŽŽŽŽŽŽŒŒŒŠŠŠŠ‹Œ‹‰ŒŠŠŠ‰‰‰‰Š‰‰‰‰ŠŠˆŠ†ˆŠˆ………†‡ˆ„†ƒ€xX7*!"'/789:::::9:89:::;;=>>>>@A@@?AA@@ABAEDBDCDCBECBCECBCDEEBBCBCBCBBABBCDDDBAAABBBBA@@??@AABBAACBBCBCCCCBCDBBCCDEEDDEDBCCCBCDDFDACCCCDDDDDCDDEDFFFFEDFEEEDDECEGEEEFFFDDEFGDEDEDEDEEEEFFEEjjpiikolollllnnmnoppooopqqrsurssstutwvyvvxuvwxyw{yyx{{||||{|||~~~}}~‚€€‚€„ƒƒ‚ƒƒƒ„†………„ƒƒ‚„ˆ†………Œ[SRVWWVWX\`qsxsrueZUT]ewjZSSU]elxz[ZWWXYYYYXYYYYZZZ[`cqƒ³ãðòòñïíìêêêééééèèèèççæãáàâãââߨÔÏÌÉÄ¿¸±®¨£žž ¤¬¸ÆãßåææåäãáÞÞ;¬¤Ÿž¥««©¡›—’—Êâçççèçäл°¸âçèéééééæàÌÅ–œ’”¡¦˜ˆ‚€€wpopnc\[^d_\[[^_[Y\ac^WbMJEADJUmp”yulQ=;8887766666788:;?IPZgpux}„‘Ž•—“–‘••••”‘‘‘‘‘‘“““‘‘“’“‘“‘’’’’’‘‘’’’•‘ŽŽŒ‹Œ‘ŒŒ‹Œ‹‹‹‹‰ŠŠŠŠ‹ŠŠŒ‰‰ˆ‰ˆ‡†…‰‰‰„‡†ƒ{lB.#"%,668:;;:<9:9<::9=;>=====>?@?AABA@A?BDCDCBBBBBBBDCCEEBAACCCDCDBCBCB@ABCBBBAABBAA@A@B@AAEBCBCCCCBCDEECBBBBDDEEHEDDCBBCCCCCCCDBCCCCCDCDEDDEFEGFFGGGFFHEFFFFFFEEEEDCCEFFGEFEGEFEEEEEFEEEkkkikllknnmlnpoppppooqqprrsutrstuuuuuvwwwxvvxyyw{yxy{}|{{{|}}|}~~~}|€€€ƒ€ƒ‚ƒƒƒ„„…ˆ††…ƒƒ„†††††Ž‚^XUV\YYWXZ`pvvrqrmjbYbfp}_TXZ[YdudYYWWXYYXYY[[\ZZYYZ__`mz¡ÎÞòôñíëèæäãáàßÞÝØÓÎÌÉþ¼ºµ¯«¨£ž™–Їƒ|ywwx|Œ–£µÅßåææÜ˶§™šœ £©³¾ßááÞÐÉĬ¬¦¹àäåæãÕ»¤–ƒÚååééêêééáÕœ|‡ž³¾Ì«—•–‡{zyn]ZYacfiq|{wslgd[SNKFD>:=CWmpqu{mUF>;98876686677779:CJXdkqyvyx‰’’’‘’‘’”‘‘’‘“‘‘‘‘‘’ޑޑ’Ž‘“‘‘‘ŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹Š‰‰ˆ‰ŠŠˆˆ‰Š‰‹‰ŠŠŠŠˆ††††„‡‡ƒ}rN4$""(258889999888888::;<=<<===>>>>??@B@AAABABCCBBDDEDDDGBABCCBBBBBBBCD@@AAAAAAA@B@@@@@BAABECBABACCBBDDDBBCCCCDDDECDDCCCBBCDCCCDBBCCDCCDEECDEDDFGFGFEFEGDEEEGEDEFEDDCDEFFFEEDGFFEEEDDEDEEolmllmmmrnnmnoopqpqppqqqssuuvvuvxuywzxwwyxwx|y{{{z~|{|}~~~~~}€~~~€‚ƒ€‚‚ƒƒƒƒƒƒ„„„„„„„†‡††…†„…†‰‰‡Žƒa[XUWXXWWY^q}t{z{z}`bh€…eTYYVTXb_YYXXXXXZZXYWZYY[ZZ\[]^enޱÔíïðëãâàÖÌ·±¬¥Ÿš–’Œˆ„~zwtqnjigdba__`abdgovŠ™§ºÎÙâܺ«ˆ‰lq}”¯°ÈÎßàåÒᾯ¨¨³×ØÙæßÃŒ„ªäçèêêêêêçØ¡tlq‰¼±ÓÐ˳¨§§œ‰‡‰‰qbhoqtz‰ˆ†…ˆzyk[NJIBA;94CNaZTQck]OGC;8897679677879:@FVbhqz|~|{€„†““”š”––—’“’’’‘’“““‘•”•‘“’‘’’’”‘‘’‘‘‘’‘•‘‘‘’‘‘”““’‘Ž‘ŽŽŽŒŽ‹Š‹Ž‹ˆ‹ŠŠŠˆŠ‹‰‹Šˆ‰‰‡ˆ‰‹†Š††~z[9&"!%,4798:9;9879889::;<=<<=<<;<>>>?@@?ABAEBCCEABDECBBBBABCCEBDBBBAAFB@ABBBCBBDABA@@@ABABCEEDAABECABCCDBBCCCCCEEFGGDCCCCBDDDDCDCBCDDCCDDEDEDDEFFHFEFFEGDFEFEDDFEEEFCDEGGGDDDFFEFFEDDDEFDmmllmnnnroomosqqqqqqqrssssuuuuuvuuyvwyxwyyxzzy{zzz~|}~}~~}~}€~~~~~}€€ƒ‚ƒƒ‚ƒ„„……†…†…………………ƒ……†…‡…eb]YVVWXXYZh|rovbmulafiŠiWf{gVZ[YYY[XYXWXZ[\YYYX\[[^[\\^dj„™»ßçâ¶¥›”…€|yvrnlihfdba``_^^^]]]]]\[[\]^`ekpxƒ›¬»ËÚÕа“‡x†Œª¬®°¼ÓÑÐν³¨ª¯ÕßàåãÀ©˜¡«Ååêêêêêëêä´wkcu‹¤®ÍÕÏÈÆÄ¾·«ž™”‘’™—‹‡…„{wx|zzVLKKEIIHKUm}p^^ehe[OE><:976676678778?CR_hq{~|zyyz‚…‹“““’’—’’‘‘’“”’’’’’•““’’’’“‘’‘‘‘’’‘‘‘ŽŽŽ‘‘‘‘ŽŒ‹Ž‹ŒŽ‰‹ŠˆˆˆŒ‹ŠˆŠ‰ŒŠŠŠŠ‹‰ˆ‰‰Š…‡…„€~kB*#!$(268788998888889:;=;;<?<;;<=??>>>?@@@@ACCBABCCCBDBCBBBABBBABBAA@@AABBBCBBBAAAA@AABABCBABABBCCBBBBBBCEEDCBBBDEDCCCDEDDDDCCCCCCCDCBDCDCDDEEFFHEFGFFFDEDEDDDDEDCCCEEDDDDEEFFEDDDDDDFDDqqqssrrssqqrrsrssstqruwuuvvuwwxx{wyxwxxy{yyyzy{{|~|}|}~~€‚‚€‚|„„„„„„„‚……‡†††…†…„„…‰†……‡‡ˆ‰ˆŒ†pjgaZTUXXYW^gjoreenh]ck‚nhrwWXWWXXXXWXXXYXYXYXY\[[[ZZZ]]`es¢³ž‚|jfcbb_]]]]\]YZZZZZZ[[ZYYZ[Z[ZZZ[\\Z^`ehqy…‘¡³ÂÛÐßĵ«¡¡£¢Ÿ›ž¶ËËÌɼº½ÀÐÞåæçÚËÆÆÖáæèèçççççä¢sip~’Ÿ¯ÉÒÙÛâäãØÇ½·©£¥²´£œ•‡…urpt_TJB=?CO}xs‚Ÿš˜€uy}sh\JEB@=97666678777=AQ^hq}€‚‚ƒ‚~}‚ƒ‰“”–“˜’“”˜’–“’’’’”••““’““•’’““’’’˜‘‘‘’‘’’‘“‘•‘’‘–‘‘‘“’’“ŽŽŽŽ‘Ž‹‹ŒŽŽŒŒŒŒ‹‹ŠŒŠ‹‹ŒŒŒŒŒ‰‰Š‹‡‰„sK/%""&158899987878:989:;::;<<;<;<<<=>>?@@?@@CCBBBCCCCDAA@@AACBCBDA@@@@CB@@AAAABBBCBBDCCBDBBCBBBCCBBBAABCCCDDCCCBCCCCFDFEDDDCCCCDDCCCDDDDDDFEGEHGHGFGGFFDFEFEEEDDCCCDEDDCEEGFFEEEDDDDDCCDrrssssttsssstuttuwtrtvvuvvwvwwxxyxzyz{yy{zzzz{}|||||~~}||~~~‚~~ƒ‚~‚‚ƒƒƒ…„‡ƒ…‡…ƒƒ„„…ˆˆ†…‡†‡†‰‘ˆwxwobYSUYYX\^del~qpf^elƒ’s^yvWYXYYYXY[[[XYXZYYZZZZ[\[ZZ_\\`cmvyy}wne`[[[[[[][\\[ZZZZ[[[[YZ[\\\[[ZY[\\\^`bels~‰—¤¶ÈÔÞàÜ˼±¢›Š«Ò×ÞãÑÉËÍ×ÞåçèçåàååæççåÚÐØáàâ®yr{‹¥ ¯ÍÑÛáåçèçÝÕÇ·»¹¦¥§±³°¨¡|qbLL==;ARnŸ”¬®°¢“Œˆ‰†}umfa[QG>876778887<>KYfq}‚ƒ„„ƒ‚ƒ†‹‰Ž””’“–•’’’““”•””“““‘““’’“•’““’“‘’‘’‘’’’’“‘‘‘‘‘’‘‘’‘‘‘ŽŽŽ’ŽŽŽŽŽŽ‹ŒŒŒ‹Œ‹‰‹ˆŠŠŠ‰‰‰Š‰Šˆ‡†‚{W5(!!%,38788878888:9899:::;:;;;;<><;=>>=>??@@ACCCCBBBC@@@@@@@@BAAAAA@@AB@@@CCCBABBBAABBBCBBBCBBCBBBBAAACCBCDCBBBCDBBFCDCCBBBCCCBBBBCCECCDDFCGFFEFFFHFEECEEFEEFEDCCDDCDDCDDEEEEEGDCDEDCCDwuuvwxyyyxxxwvvvyyxwvxyyyyyy||}||{~}}|{|~}~~‚~|}€€~~~ƒƒ‚‚‚‚ƒ‚ƒ‚‚ƒƒƒƒ„„„†…‡†…††…†‡Š…‡ˆ‡ˆ‡†‡ˆ‰“—†‡†‚scYSUYXXZ\`ltwxpcel„†yx’yW[YYY[YZYXYXXXYYZ]\[[Z[ZZZ\[[\]_flqƒ‘—€e_\[ZZYZZ[[Z[[Z[\ZYZY[[[ZZZ[ZZ[[Z[]]]`chmv~Š•¤´ÁÛäÞéšžªÀÖàææåæççççèéêéèèèééééçÔÉÈÑÛÛÎ’t|“©Ò×ãåçèèééäᳫ§¤¥£¢¤«´¡«…–v|]RJB>@Lbx–¶¬•”“’“™žp‡‚€_F987888899=>DSgq€‚‡«œ•› ¦¯¡“ˆ‹‘Ž•““”–““”••™•—””’””—“““‘“”“’’’“““””””•”“‘’–’’‘’’’‘‘‘‘‘Ž”‘‘’ŽŽŒŽŽŽŽ‹‹‹ŒŠŽ‰‹ŠŒ‹‹‹‹ŠŽ‡Š~f?,"!$*38888879888::88;::;<:::<;;;;;<===>>@?A@CCAAAABB@@AAB@@@C@EACAAA@@@@ABCBEADABA@BBBDCEBCBABBBCBCAABDCDEEBDCCAABFCDCDBBBCCCBBBDCCCCDGEEEGFFFGGFGEDEEHFFEFFDDCDECCDEEDDEEFEEDDDDDCDDDvuvyxwyzxwwxxyyyy{xwxzzyz{{{||||}|}{}~~~~}}~€~~€€€ƒ‚€‚ƒ‚‚‚ƒƒƒƒ„„„…†…„†…‡Š†††††‡†…‡‹ˆ‡‡‡‡‡Œ—•Ž•˜‘Šs_UQTZY[[\_am|pgdk‚ƒzk‚›yW[ZZZZYZXYZXXXZZZ[]\\[\YZZ][[\]^`dhx»À¼‘mc\[\[ZZZZZZ[Z][YZ[[[[[[[[\[\ZYZZZZ\^_`dhmt}†™¤°¹Å·³µµÀàÜåçççéëëëëëëëêêêêëëëëéÝÊÁ¹ÈŲ̀”†‚“›ÕÞãèêéééêçÇ™Šˆ––”’Œ…ƒ„€wkb_ZMCFHSWep‰¤¨©§ª¡†|lo€rmE:776677884/8CTgoz}ˆ ¹¼ÃÆÈ»¼©œ’…”‹Ž’“—–””””“•••””’“•–“““““”“”•’’“‘’“’•’“““““‘‘‘‘’““‘‘‘‘‘ŽŽŒŽŠŽ‹‹‹ŒŠŒŠ‰‰ŒŠ‹‹ŒŠ†Š‚pK2#""(1467987887777979:989:99::;;;;;<=>=<??@@ACAB@@A@@@A?A????@@AA@@??@@BAAB@AAA@C?@BAABBBBCAABAAAAAABBCDCBBBDCD?@AABBBDBBDCEBBBBBDDDDDDDDEFFFFGDFGFGFFECEEEEFFEDDDEEEEEFFDFEEDDEEFFFFFwvxyzw|yyy{zzz|yyywz~}}{€€€€€~~€€„€‚€€ƒ‚‚‚ƒƒ‚ƒƒ‚‚„„„„ƒ‚ƒ„…„„…‡‡‡†††Š†ˆˆ‡†ˆˆˆ†…†‡‰‰ˆ‡‡ˆ‰Ž˜¢§¯±²™„lYRMRZYYZXXevael~€€{–xXYYYYZYZY[YYXXYZZZZX\[[YZ[[[\]]^_`cm’À»Ï{l]]\[[ZZZZZZZZZ[[ZZZYX[\ZZZZZYYXYZZZ[\^`cglrx‡š¤©¯®ËÔÞâãååæèèééêéééééêêêêêééÛÆÀ¿ÕÉÅÀ·« œ³×âèéêêêé騶‰oowŽœžž™‚ƒ_ZYXZ`jbXSSRSTQZew–ž»µ®‘ƒtcZTWZO@968666678.+-5AP_ny‚”¦ªµÇÁº¼¾¡§˜›”‡Š‘‘œ•—•—–•–—•—““–˜”ž—•••“—”“’“”š“’’•’–“””“’““—“”“‘’“‘’’‘‘”‘””•‘’‘‘‘‘Ž”ŽŒŽŽ‹ŒŽŽŒŒ‹‹‹Œ‰‹…zZ9%""%,4677798797779878989999:::;=;;;==>???@???@@??A@BA@AB?A?>?@AAA@??@@AAAD@DAA@C@@@@ACBAABAAAAABABACBACBBCCCCDBAABBDCCBBABBBBDCBCFFEEDDEFGFFFGEFFFFGFEEGFFFFFEDDDFEEEFEEFGFFEFEGFEEEEwwwwwwyxzzz{{{{yz{||}|}}~}~€€€€ƒ‚„‚€‚…„‚ƒ„ƒƒƒƒ„……†„‚ƒƒƒ„„„„ƒ…††…††‡ˆ‡†††††ˆˆ‡†ˆŠ‰‡‡Œ‰‰‰‰ˆ‡ˆ‰Žš§°¾È´¡zfUMKPXZ[ZX`e\Zdm|||…Šyafga[[YYYZ[ZZYYZ[ZZY]\[Z[[Z[__^^^_`fl‘¨Ä¢wd_\]\[[ZZZZZ][\\[[YY[[ZZ[[[YXYXWWWWXZYYZ\`cgkosy„‰–¦±»ÂÊÒÛßåæççæãâåéêééééêëéæäÜÔÒËÎÛÖÏÏÍÜçêëëëëéèη„{pƒ¢¥®´¾¥‹mQLIMQac]OPQTZQJRThy†€unhSFCA>;97777866660+/3=DOYdr†«³¾Ç¬¡•Œ”š¨£•ˆ‹“•–—————–—“•–••˜—–“”””““’“–•’’‘”“”““”“‘’’”““’“““‘“““’”‘‘‘‘‘’ŒŽŒ‰‹‹‹‹‰‰‰€kB)$"$(367789888776667788889899::;<;:=<<=====?>?>>?@@BA@A@?@??>@@@?@AA@@@ACD@@???@?@@@?BCA@BBA@AAA@@@ABABCBBBBCCCCCBBDBBDB@BCCCBCCBCBEEEDCCDDFGEDFFFGFIGGFFFFFCEDEEFFEDEEFFFFFDEEGEEGFFyyzxwxzx~|{{}}}}}}|}~€‚~€€ƒ€ƒ‡ƒƒ…‚………„ˆ„„„…†‹……„…†…†‹‰ˆ…††‡‡Š‡ˆ‡ˆ‰ˆ‰Œ‹Š‰ˆˆ‰‹‰‰Š‹Šˆ‹‹ŠŠŠŠŠ‰ŠŽš¯±ÖÈ» –Šr_QLIPYY\YYYWYdn€‚…‡†‰|{‚yh\\ZYYZZ[\[Z\\ZZY[ZZZ[[[[\\^^_^`_acky–º¥¤…g_\[[[[ZYZZ[ZZ[[[Y[YYYXYWYYVUUUTSSRSSTTTUYZ\]^`bdgmy†•¤°ÁÉÑÛåáÛÕÇÃÈÝèèéèèèéèèèççæßÚØÙÜßäæçééêêëêéÙ½§–’“¥®¿ÖÕÓ¯mOHDM]lo`SQRUUJOPSZaa_\YQC?<:87777787678633258:>EO^pŒ‘’‘ŽŠ…‘›£»³²¥¬˜†‹–˜š˜˜—————˜–—•˜———˜––”““”••––”˜”—“”””••––””““““”—’’’“‘‘’“’’”•’’Ž“‘”“”‘‘‘’ŽŒŽŽŒŒ‘ŒŒŒŒŠ…tL.&#"&15778897777776:888889999:99:;<<<<<====?>>>>??ABA@???B????@A@@B>@@@ABD@B???AAA@B?ABA@AA@@BAA@A@@BABDBBABBABBCAADBBCBABCCCBBBBDDDDECCCDDGFEDEEEEFHIGFFFFFFEEGEIGFEEFGFHEFEFEFFFFGFzzyyyyyx{{{|}}{{|z~|}}€~€€…ƒ‚ƒ„ƒ‚………ƒƒ„…‡…†‡……††‡ˆˆ‰ˆˆ†‡‡‡ˆˆ‡‰‡ˆŽŒŠŠ‰‹ŒŠŠ‹ŽŽ‹ŠŠ‹‹Œ‹‰‹‹‹‹‹‹™¥©¢—”‘’§‡j[NJINVYZYY[[douƒ‘…ˆyiz•sa`\[Z[Z[\ZZ[]\\\\[[[[\^\\\__^_``abbin† ¥½’rg]\\[YY[ZZZZZYYWWXWVUTTTSSRQQPQQRRTUVXWXZ[\]a`caeens‚ ²½ËÕÙÐË·«²ÔááåæáÞàãæèéêêéèæåæçççççæçéëêêèäßÛÍÈÁÉÏØÙ×̸›}ZX]Œ¾°Ÿ‡sia]LQRS[YYYYNF@>;:987787777876777798;?FO^jqvy}~€ƒœ©ªºÕ¶®©‘ˆ’‘––š™——–––˜”™––——•–—••””–˜••–”–’’“””””•–”’“”””•’’’“‘’”“’”“”“’‘“‘‘ŽŽŒŽŽŒŽŠ…{W4)#$%,47878:677777677889688889::9:;<;;<<<<<>???>=>@@@@@?>>=?>????@B??@@AA@@?>?AA?A???@@ABA@AAB@@@@@@AAABBAAAAA@BCAABBCCBCCCCCCBBBBBCECBCCCCDEEDFEEEFGGFFGFFFFEEEDFHFFFFFFFDFEEEEDFEEEyyyy}yy{z}||{z{{|~}}~~€€ƒƒ‚ƒƒ‚…ƒƒƒ…†‡…††Š……†ˆ‡ˆˆ‰ˆˆˆ‰ˆŒŒ‹‡ˆ‰‰ˆŠˆˆ‹‹ŠŠ‹‹ŒŽŽŒ’ŽŽŽ˜£šŽŒŽ‰‡š |cVNJHNTW[ZYZcqƒx†ˆˆynƒ‡‚od^\[[[[ZYZ[\\\\\[[[\]\]\\]\]``aa```ej|Œ¤¿šp_\[\[YXXWWWWWVUUUSRRPTQRQRRSTTTVXX\\[ZYZ\^adeda_]ahw…—©¹ÑË•œ§¹ÍÑÍ·°ÂÐÕâååååææçèèèçÛ×ÖÜæçèèèçææåÛÖÛãäåäâÑÓ|œÚàáàßδ—€oa\VVKMJGC?<<<9:998877789778788889;>CKWjp}‚ƒ”´§¼º¼¸µ«¢¨±Ÿ‰Œ’œ—–––—™–š—˜˜—–™–˜•”•–•”•—”–“—•™””””•—“–––”™”““”’“••“”•””““”’’‘’“‘‘’’ŽŽ““‘‘‘‘‘ŽŽ‘Ž‹Œ}h>-##$*27888:778:798779898;98899889;<;;<=<=>=====<<=?>=?>>>=??@@@??@??A??@@@@??@@@@@??A@@@AABBB@@?@AA@@@ABAA@AA@BBBBBBECBCDCECECBBCCEDCDDCFCDEFFFDDEFGHGFFGGFFGGGEEFFFFFGGGGGEDDGDGEEExyyzzyz{{{||{{{{|}~~~~~}€‚‚„ƒƒƒ„†„ƒ‡††…†…†‡‡††…‡…‡ˆŠ‡‡‡ˆ†‰ŠˆŠŠŠŠŒŒŒ‹‹‹ŒŽŒŽŽŒ‹‹ŽŽŽ‘˜š‡xˆ…™–Œr]TMJHKTW\ZYbqkn{Š‹‹x`js„}vvkda[[ZZ\\^]\]^^^\^]]]^\`]]```bbbadegs~–¹Ÿ–{d^XXXXWWUTUTSSRRPRQRSSTUVXXYZ[[[Z]^cgc^gq~Š‚{ywnadfq|Œ ªµ©•›—š“™Ì¼Ï«¬°µ¾ÉÕßáâäååä×ÉËÇÌÓÚåèêêééåàÜÙØÝåæåÙÓÕÙâççæäáÑÕ®¥™~pdXPID@><>89888777778888:999999;=DM\nw†}~ƒ‰œºÔ²³Åƹ–‰‰Š’™—›˜–—•———˜––—–––•••”””•””“•”–••–•“”“••–””••““’“’’““’”•“‘’”’‘’“’‘”“‘‘Ž‘“Ž‘‹‹sJ3#"#(0677577777788778888888888889;::;<<;<=====<<<<==>?>=>>?=??@>?A@?B??C@B@A@BBA@??AA@A@AAABA@@?@AA?@@AAACA@@??@BBBBCBCCCCDCCDCCCBECCCCDEDDDFGFEEEEGGGEDEGFEEDDDEFFGFEHEEEFFEDDDEEEHvyz{|{{{{|}|€{}||~‚~}~~~€€ƒƒ‚„ƒ†ƒ„…„„ˆ‡‡†ˆˆˆ‡‡†††‹‡‰‰Š‡‰‡ŠŠŠŒŽŽŒŽ‹ŒŽŒŽŽ’ŒŽŽ‘ŒŒ“’‘‘“›¦¡…x†{y“ºŸ‚jZRMJGLOU[Zamzyyx{Žwaew…€Žsd[\[Z\\^]\_^\\\\]]]]\`^^_```_`aabcdksª¥‡h_UUUUSSQUQSSTTUWWXYZZ\[YZ][Z[[[^dnvv_lƒ¡œ’y…‹m\Zairƒ˜¡œ«°¶¼ÄËÊËÉÅÀ¬©¨¦´§¦¬¼ÅÐßÜÛÖÔÁ»»ÃÓãåææçææßÑÎÏÙÖ×ÛÞâåçççæäÞÔɲ¹»¾³§•‚tgZOHB=:998889667988998887:9;>ELWku‡……‡…††”´±²®Æ¿Þ¸¨§¨—Ž’”—š™˜—™˜—––——˜˜˜˜™™˜˜””•™™š•˜•™–˜•——™˜—•”–™”•”•“™–˜•””“’’““’’““’‘‘”‘–‘““’’’Ž’ŽŒ„}Z9%""%+5786878787:888999887778988899:;:;;:;;<<;;;>=>>>=>>????A?@?@A@@B?>@???@@BB@@???@@A@BA@CA@?@@@BBBAAABA??A@@@ECCDDDECBBEBBDCCCBEDDCCCFEGEFFFEGEEEFGFEEFEEEEEDGFFFGFJGFEFFEDEEEFFFzyyzz|{z{{{{||}|}~~€€€€‚€ƒ‚„ƒ…ˆ…‚ˆ„„„‡‚‡‡‡†††††‰‰ˆ†‡‡‡ˆŠˆ‰‰Š‹‹ŠŒŒŒŒŒ‹‹ŒŒŒŽŽŽ’‘‘”ž§«•yvstŒ±¹•vfYRMJHJNU[^`_alvy’{n|‰…˜ƒ~nbb^]\\`]\]^]]]`]^^^]`_]___`__aabccejn‚›¡º’reXVTTTTUUVWY[[[[\\[]\\\[\^]][^_dp–ydx¥|_w˜‚x^Xccilw‹ŸŸ®°²¸×ÐâÜäãá˶¯®ºÄ¼¤¦©ÆŸ¢§º¹»¼´¥Âº¿ÄÊÑÛâáàÚäáߨÆÞäèèçççåã×ɺ²ª±³¶®´²¯£•„th]RIC=;99768887877777:89<>CIVgyŽŠ†€€…œ®Ë¾É»¾Ö¯Ÿ™“Œ“›™šš˜–˜˜˜˜˜—˜˜˜––––––•••˜–™•–”–——•˜–••”””””““”””””“‘‘’“’•’‘‘‘‘‘‘Ž‘‘‘‘‘ŽŽŽ‰ƒiB)#!#(356677777776888987887777899:9:99:::::;;;;;><<<<<>=>A??@?@@@@@@??@@@???@BA@@?@@@@??AA@BA@?>??@@C?BAA@@?@@@ABCCCCCCCBBCBCDDDCACDDDCCDEEEFFFFFFFDFGFFEGFDEEEEGFFFFEFFFFEDDDEEEFFF|zzyy|~{z|{z}~}|}€€€~~€€ƒ‚†……„„„†‡ˆ„ˆ„„…ˆƒ††ˆ‡‰‡ˆˆ‰‡‡‡ˆˆˆˆŠ‡‰‰‰‹ŒŒŒ‹‹‘’ŽŽ•“Ž“‘•žµ²²x}mp‰¬»«ˆqbXRMKHJMS[Z\^hlv”€››•~˜„~yqid^\\_]]]^_^]]]]^^]^`___``_`abaabccdhw„½›‚p\YXY[[[\]]][Z[[\[]Z[[\[[[[[\_dy‹“{„Œ¨p`k}†zhfhd`_cjs‹’šŸ¥°»ÉÒÛåååãâãäää忨ÊÀ¶¶»¾»º°µÃ¶§¦´¸¶³¾ÔâàãáãäæèçççæäâÒ³©¨¨«¬¬¬«««¶¶µ°«Ÿ“}k\PG@;77677778;778899<?CHUbt‹‰”Šˆ‡„ŠŒ¤Ì¾Ø¾¿Â¶½×’”—›«£žœ™›™š˜™™˜———š—••—•˜—™™™–›———™••––•˜––”””•”—”““•’’“““‘‘“’“–––’•’””•’‘–’”““ޓЉtK.%!"&16677778686788889788899888988999;9;:::;;<;><<<;<><<=>???A@?>@?????@???AAAAAAB@@???@A@AABB@D??@CBCA@@AAA@?BABCCCCBBBAEEDDEDDBBCCDDCBEEEFFFEFFGGFFEEEFGFEEFEHGGGFGGGHFEDDDFEFFHF|zzyz||z{{}||}}}}}€€€~€€‚‚ƒ†………„„„ƒ„ƒ……††…‚††‡‡ˆ‡‡‡†„‡‡ˆˆˆ‡‡‡ˆˆ‰ŒŠ‹Œ‹ŠŒŒŒŒŽŽŽ‘•ª·vjcq‹¬¹°™‚l`YSQLIJLRZ__`bp”‚{€}€šqu‚xxl``_^_``_^^]]^_`^^_``aaaadbcbbbccdedjs¸¢~e_\\]\\\\[[[\\]\^Z[[][\[[[\]]jtux}€ylqsvvs}~€vgd^ehnt~‰‘—¢¯¸ÂÒáäççèèçèéééèåãáâãÙÔËÆÆ»¯ ¡¨°¢–¥²ÀÞåèèèçæææãØÊ½¯¦ £¦¦¥¤£ ¢¥¥¥¥®¬¤›Š{m`ULE?<:9778797689<?CHS`q†–’‡†Œ–¢ºÄÅǽÐ׺¨ž™•“˜¨¸°£Ÿ››š™—˜–˜—˜——––•••—–––˜•—˜˜—˜”–—••–˜–“••”’—””“”’’’’’‘‘’’“‘””•‘‘‘’’““’‘‘‘“‘“‘Œ~X4(!"%,67778777889777777889677777677899:;:::::;:;<<=;<<<==>>>@?@???>>>???????>??@@?>@A????@@@@@@???@AA@?@@A?@@@AAAAABBBBAACCCCEDCCCCCCDEDFDDDEEEEEEFFFFEEEGHFEFEGHGGFFFFFEEDDCCDEEFE~|zz||}}~}~|}~~~‚~€€€€…‚ƒ‚„‡‡‡„ƒƒ…„††……„……„‡‡‡ˆ‰†††‡…ŠˆŠ‡ˆ‡†ˆ‰ˆŠŠ‰‰Š‹Ž‹‹ŒŽŽŽŽŽ‹ŒŽŽŽ“’’•›§§ªqabr–°·½«’zi`XTOMKJKR[Z\^lw{€†{ˆ|€Œwit’¨„wg_^]]^_`^^^````__``_`bbabbccbcbadea\blŠª§µ‹md^]\]^][[\[\\\\ZZZ[Z\[ZZ\^]^^`cfjt†ŽŒ‡vsrvšŸœ|bY[\]`cks}‹”¨¬¾ËÙÞååäááåèèèæåäãäåäãåàÍ´©£³¨ž’”ÇßçéèèçæåäÙÌÁµ¬¤ £¤¨£Ÿœ˜™š›Ÿ§¼¾¿¸°¥™„pbTJB<76668688;<=BGPZj~…”Ž”‹‰†•¨²¿²¨ÏÖ¾¾¿½¶´¡°ÐÀº¡œ˜˜˜–œ™œ——–šš›œš—™—˜——™Ÿ—˜—š–”•–—š–˜–˜˜—–™“•”””š•”’˜’““””•“’‘–“–•–“–”“’“”–‘’ŽŽƒk>+"!$)18998778887666677999778978777889;;:=:;<<;<<=<:;<<?==>=>>?A@A>@>??>???@?@?????@??????@@@@@@?BAA@@?BAA?@ABAAADCCBBBCBCBCCECDCBBCCDDDDDCDEEEEEEEHGFFGEHFEFHGFFHGFFHFEEEDDDGFFEGE{}{{|{||||}|}€}}~~€€€€€ƒ„‡ƒ„ƒƒƒ†‚…††‡…………††ˆŠ‡…†††…‡‡Š†‡ˆ‰‰‰ˆ‰Š‰ˆŠ‹Œ‹ŒŽ‹ŒŒŒŽŽŽŽŽŽŽ”™¦Ÿ o_aq¢º¸½É¨Šxjb[VQNKJIPY\^cfox†~ƒ|€œwav—€vie^]^^__`___^__`a`_`cbbcddbbbcdeb[]cj€–£¹’wlb_^^^^]\[\]]]]][\[\[Z[[\\]]^^abs•Œ|wxz~€•¨Š\Sbeb\[Y\ajt€Œš§¶ÊÒáÙÑßáææåãȰ¹ÅÎÖàãäáÌÆ¯ ¡›”ŒŠ©ÛãèéèçæåãÚËÁ¶®§¤žŸ £££¢ ›˜•“’‘‘—œ¦««ª®´¶¸˜}k]SJC<;:9899:=?BJRat‘›‘Œ†€„„‡œÑų®«ÉØÀž—Ÿ´ÓÏÔ½©¢œš˜˜—˜—˜–—˜˜———˜—˜˜˜™š———––—––•–•—–˜––––“”••“““”’“’““”“•“’’’–““‘”•“‘“Ž’’’‡vK2#""'-3588888886667998967778887777789<:::::9;;::;<;:;;====>>>@>>?>?>>@???@@??=>=??@?????@@@A@??@@@A@@?BAA?@BABABCCBBBBBBCCCCCBBBCCCBDDDDDDDFEFEFDDEFFFFFFFFFFFFFFGFEEEEGEEEEEFEDEF{||}}{~||||||}}~~~€ƒ„‚†ƒˆ‡ˆ„ˆƒ„„††…†‰†††‡‡Šˆ‹ˆ†…††‰…‡‡ŠŠ‰ˆŒ‰Œ‰’ŒŽŒ‹ŠŒŒ‹ŒŒŒ‘Ž‘‘‘’˜¦••h_`q¦ÜÃÂà‡yme]WTOLKJOWY_]ds‹•z€›w_s˜‡€”›—{h^^_a_^__```bbba```aacccdccccde`Z[^bgx‡¹™†xe_\]]]\\][[\\[Z[Z\[[]\\\\[\[]^o‚”ˆ~}•І–„r`comd[WUSTUZajtƒ¢¶ÄØÓáäççåᱤ’ÂÀØ»ÍÓÑÑÄǤ¦—–‰¢ÀåéééèçåãÙÏø°¨¤¦¡£¢«©¨§¦›—“‘’•—›œœ™—›Ÿ¡¢¥¥¢ž–n_SKD>:9888;=CKXdr‚‡‘‘”Їˆ„†ˆ¡°°±¦ÙÓÆÀ·¨±ÉËÝп²¡š˜˜˜˜—˜™•›™›™›™šš›œœšš™—˜——————––––””•—–˜•••”“““–”•”“”•”–”““””•’“““’•‘“‰€\9(#!%+45778778878678889666779777777779999999::::::;::;<=>>@@?==>@>@>>?@@??>>>>????AA@?A@A?@@??@??@CABABAAA@@@@ABCCCBBCBBDCCCEBCCECEDDDDDFDEFFFFEDDEFHGIEGGGFEEFFGGEEEEFEEEFEEEFDEF{z|||}|||}|||~}}~€~}€€€‚‚‚ƒ‚‚‚ƒ‡„…………††……†ƒ…††‡‡‡†‡‡‡‰…††ŠˆˆˆŒˆ‰‰ŠŠ‹‹‹‹‹‹‹‹ŒŒŽŒŒŒ‹ŒŠŒŒŽŽ‘’˜”t`_^p¨×ÃÇÚͶžŒ~qg^XSOMKHMSZ`bev”‚vw`t™‡u—Žr^ccb`_^`ca`baaaaabdcecddgdecfc`ZZ]beis{‘²®¥Žwk^^]]\\[[[[[\\[[\\\\\\[[[\]]elqxy{~„zz{‰rwx„s^ZYXTROQU_gt€‘ ²ÄÑãæççÜÆµÆÒÙÌÇÍÀ½¾Â¨¤™™ ¯Â×èëêéèçãÕÈ¿·±«¦¡¡¡Ÿš˜˜˜˜™“’’“”’Œ‹ŠŠŒŠ£Œ”ˆ{maWMF@=;<9=BLVev—Š…~…Š•¡ÃÓÆÆ¼²¦š¨³ÏäÞϼ«£›™œ™™—˜–˜———›–š˜š˜˜—™™˜—––—˜˜—————––•––•–••––”””””””””””•”“’’““’““’’’‘‘…lA*$!#(05667778767788787665677667666678899999:<:;;:::::<<===<<<>?>>>>>????@>>?@?>??A@@?@?A@?????>?@A@B@AAABA?@AADBBBDCCCCCCCCCBBCCBCCCCCCCDDFEEEDDDEGFHGEFEEFFFFFFFEFEEEDEEEFEEEEEF|{||}||~€|}}}}}~€~€‚ƒ„‚ƒƒ…„‡‡ˆ‡‡ˆ‰…†††„…††‡ˆ††‡‰ˆŠŠŠ‰‰‡ˆŠˆ‰ŠŒ‹‹‹ŒŠ‰‹ŽŽŽŒŽŒŒ‘‹ŒŒŒŽ‘ŽŽ‘•—wfa^^pœÒÆËÕã˳¡€qg^XRPLJHMPV]]ey|}Žvauš†uˆƒ“}adda```aaaaaa`acabbcdcccdddcdb`Y\^bdghls‰¥¡ÉŠn]\]]\[]]\]Z[[[[\^]\\\[Z\\\[[]dhxzuuy}ƒˆƒ¨“‹bZ`_YTPLNOV]ht‚‘¤¸ÍäæçæâÇÏÍæåäØáÎÀ¾¾²¢—ÁàæèéêééæäØÆ½´¯¬¨§¡¤Ÿž› ˜”’’“–•“’’Žˆˆ€€‚…‰Œ“¤¤¤Ÿ˜„ti^PC;57;CKYfq…’‹‘Ї†„††˜Á½»½À¾À«§«¬ÄÜßâÓÅ·¤žœ››™™™›œœœœ˜™™š™™—™——ž–™˜™˜Ÿ˜˜—•••–™••–˜˜——š–••———••”–”’“—”•“™“—“’‘“‘‰vJ.%#"&-676777777779777667566966665568899;99:::::99:;<<<<>=<<<=???>>=>>>@@@??A@???@A@??@@A@??A?@?DBBABAAA@ADAAAABABBCFCDCCCDDDBBCCCCDFDECCCDEEDCDFDEFFEEFGDEFGGHFFEEEEEEEEEGFFEEEFE{||{||||~€}}}}€~~~€‚~‚ƒƒ€‚‚‚ƒ…‰††‡„ƒƒ„†††‡‰‰ˆ†…†‡†‡ˆˆˆ‡ˆ†‰‹‹ˆ‰ŠŠŠ‹Š‹‰ŠŠ‹Š‹Œ‹‹‹‹‹ŒŒŽŒŒ‘•tnda`mŒªÇÅÆÒáϸ¥’ƒti_YUPMKIKPW`dgffu›sbw›†v‘…ˆ”jyƒugfdegecabbaaaabdddfeedddgc`\]`ffhghkp‚”±Ó°‚l_^]]\\_\]\^\\\\^_]^^]]\\]\]^blz„ŒŠˆˆ|}{{ufozph_WSMLKQVakyˆš«ÂØÝå䯯È×áØÓ×ݽ²´Ã¥˜¹âèêëëêéèåØÈ½µ°¨©¦§œš™šš™”’‘Ž‘‘”‘“’‘ˆƒ€xwxz|zyzzƒˆ—£¦´³±—„a@4047>CNWds~Œ™Š„}}‹—¨ÎÎȾ²¤¢¡µ×åêêßÀ®§¢Ÿ››š—˜™™˜˜›š˜™˜˜———™–™™™•™˜˜––—–—˜–•––•–—™–––—“—”–“”””’’’““““–“““‘ŒV3(#"$+5666787777876666766656655766679978899:;:99;::;<<<=>=<<===?>>=>??@@??AA@@@@?A@@@@>???@@???@AA@BBBBA@A@AABCDCCFECCBBCCDDACCCCCDCCCCCDCBDDCCDDEFFFEEEDDEEDDDFFFEFFFFFFFEFEEEED€}€||}€~~ƒ~~€€€€€ƒ‚‡€…ƒ‚‚ƒƒƒ…†††‚ƒ†……ˆ‡††††ŠŠ‰‡‰ˆ‡‡ˆˆ‹‹‹‰‰‰‰ŠŠŒ‰Œ‹‹‹ŠŽŒŒŽ‹ŒŒ‰‡‰‹‘“€|pegm~’ÃÃÁÄÚãÏÀ©•„sh`ZUQOLJLOU^^aarwrbxœ†w‘‚€˜‰…—‘skhmoiabcdeecbccddddeedfgc`\]bdggfihilz‰¬É „ufb_^[^]]]]\]]][_]\[\[][\[\^aqw˜›˜˜˜…xqorlrqw†‘—yaV[SNMJNR\dq£¹ÏÙáɲÀ¿¿»Áͺª¦¥¤¨ÀßçëëêêéçåÜξ´°®¥¥œ–—š˜—“‘”‘”“š‘‘ŽŒŠ…€}wywvutsuuw}‚Œ”£®¸ÐÂdž€<0/148;?FMYeq€…“‰‡‡ƒ‹Ž£¹¼¾½Á«³ž—³ÅÐáãäÕɽ¦Ÿœœ™˜ ™™™™šœ˜™š™˜š™›˜˜™›—š˜—˜—–——›–•——–—™šš˜——˜”—–••™”•”““’’–“˜“”’ƒi;+#"#(06667877777866666666676767666689888::<:9999:::::;===<<<=>B??>>>?????@@??@@@@@A@@???@??@@@@@ABBBBBCBAAAADCBBCDEDECDCCCDDDCDCCCDCBCEDCCCDEEEEHFGFHFFDDEDEFEFFFFGGFFHFEFFEEFIF||}{{{}~~~~ƒ€€€€‚‚€€„ƒƒƒ„„„„†„„‚ƒ†……ˆ…†…†††‡†‡†‡‡‡ˆ‰‰‰Š‰‰ˆ‰Š‰ˆ‰ˆŽ‹Š‹‹‹‹ŠŒŽ‹Š‰‰‡‡ˆ‰…‚‚„†„†ŠŠƒ~~|“¾¿šªÅÎÖÎÄ—ƒsi`[WSRMJKMT`baejecx†x‚›‹w‹¤ƒyp{naeeeeeebbcdefeeedefdb[\adhiiihhknw–´ž˜}mga^^^]]^^^]]\_]\[\\][\\\]_gmrwz}{x‚†ŠŽ‚˜„|bW`^[WPKNQX`kv‰›°ÈÍÓÂÂÃÀ·›Ÿ¢¨”œ¾ßçëíëééèåÛÑÁ³©«§¥¡–—š–˜”•‘Ž‘‘Œ‹ˆ…„|zwxwtsrmoqvz…™¤²¼»º¡W4/.2456:<?DOXet€‘”—‰„~~Š•¦ÅµŸššœŸ¼¯Ææðîëáï§¡žššš™šš™š˜˜–––˜˜˜˜™™˜–›˜™š˜———˜–––————————––—’—˜–•””•—”’’’•”•““’†vG1$""&,26677777778575676766767665766688888898889:99:::9==<><<=?>=>@>>??????????@AA@A@@???@??@@AAABCCBBBBBBAAAAABCCBDCDBCDCCCCCBCECCBBACCCDGCDEEEEEEFFFDFDDDEEEEEEFGFFFFGFFGFDFGFF‚~}~~~~~ƒ~€‚‚ƒ‚„„ƒ…ƒ…ˆƒ„„‡…„„‡…Š„„…‡‡ˆ…ˆ†‰††‡††ŠˆŒˆ‹Š‹‰Š‰ˆ‰‰ŠˆŠ‰ŽŒŠ‹‹ŠŒŠ‡‡‡‡…‚€{tpmjjw„„ŽŽ‘›«œ—¬¼”©½ÂãÕǬ“‚sjb]WTPMKLLTZ\_cdeyŽ…y€ƒœ†›’‡‡}vededcccbccdfecedddee_Z^aeggghhijjjmtŽª—•ˆvmca^^]]\^]\[\]][[[[[\[[\\^]^cht†˜šž•‘Œ‡ƒ‚|jgie`[WQOONU[eq’§½ÄÎÖáÔ¹¥œ¤¦•»ßçêëëêèæäÙÌÁ·°¨¤ Ÿžž™–—œ˜˜—•‘ŽŒŒ“ˆ†‡ƒƒzyyyusrqonty‹–£®¿·µ›~V70/13457889=AGO]jw„ˆ“‘“‹†…„‰ž±›»Á¿À·±ÊâåçæäÕͼ¦ šœœœ™œžŸš˜™˜™›™™™œ™š™™—™˜—•–—˜˜›–˜˜˜–––——˜˜—–––••——•”™•—•˜’•ŠƒZ8&""$)259898:8778876666666788687777778:898::988889;:98=<;::<=====>>>>?@?@@@??>?ACCCBA???@@BBA@BBDDDBDBBBDBBAAABCCCDDDCFEEBEDDBBCCCBBBDCCEDCDFEEEEEGFEFEEEDFFFFFDGFGFFFIFEFGHHFEE~}~|~~~}~~€~~€‚€‚ƒ‚ƒ„ƒ‚ƒƒ„…„…„‡…„ƒ……„„ƒƒ†††††………‡‡‡‡‡‡ˆ‡ˆ‰Šˆˆ‡ˆŠ‰Š‰ˆ‰ˆ‹ŽŠŠ‡……ƒƒ…„ƒ€}vpkgedbaabek‚z‡±²±®¬š£½–‘ŸŸ ÁãËé“„ume^YURPNMMRY^ffdp’~yƒ~ƒ›Ž‚Š„ˆ’„zŽot~laccdheeddehhheeg^[_aeeehghkiiiknt‡˜«¶¢…xkd`^^^_^\\\]_\[[[\[[\\\\\^`fnzŒ†„ƒ‚urƒ…‡…………‚}vri]XROMRV`ky‡š½ÕÝâоËà˽ÂÞçêêêêéçãØÊʺ·°«¤¡šš—–•–——˜˜‘‘’”•’ދЋŒ†„ƒƒzyyyutttsvz‰•¢ª³°®•zV60.1456678889:;BHS_l}ƒ’𑉅– ©ÃÅÂĵµÇÌÕèïìèàî¦ žœœ—™˜˜˜™š™™™››šš™™˜™—˜–™™—•–—˜•———————–—•———–––––––••˜”•”““•ŠgA)#!#&0556667877776665686777777677788888889;998888;;98;;;;;<<==?====>@?>>=>>???@A?@????@@@BC@@BAABCCBABBDABAACBBBCCBBBBCDCBBCBBACCCDDCCCCCDDDEEEEFFEEEDBCCDEEEEDEEGEFFHEFHGFGDEF~}~…„‚‚€‚€‚ƒ„…ƒ‚ƒŠƒ‡„…„‰…‡„ƒƒ…„ƒ„‰…‡……††……†Š‰‰ˆ‰‡‹‡ˆ‰Œˆ‡‡ˆˆ‡‰ŒŒŽ‡Šˆ‡†ƒ„…€|tnifdaaa`^]]^``bjƒ{¤ÃÂıŒ˜¼šŸ£¤´ÇÏÊÁª•…xne`[XUSPONRV[dbiotz—~ƒžŒ…œ}˜Š• ‰‰Žˆrbcddeeeddfiefffh_[_befghhhiiihjkoxšÅÖä¹–„oha_]^_]]^]^\\\]\\[\\\[\]_bgoqrrpkcq‘œŒ–†Š†‡‡’ŽfXUVROQQYbp}Ž¡´ÊÖåäãããääçêêêêéèçäÙÓËž¹²¬§š—””‘”•–™”–••–—ŒŠ‰ˆˆ†„ƒ‚‚‚}zywvuxz~‚‰•¡«³±¯”vS7/-0357677988978;>AKTany‡Š—”“‹……‚„‡œÅÀ¿¸°³ÐȽÎÜãæåå×ѽ¦Ÿœ››š™™™™˜œ›››šššš™š˜˜˜——œ—˜––—››œ™š——˜š—™—™–›˜™––——•™––––’vJ.%!"%-4765567977655676666779876779888998888889888::99<;<;><<==<====<=>>>==>??>?A@C@@@>@@AB@@ABABBCBAABBCBBBBBBBBBFBCBBBFCDBCBDCCCECCCCCDCGDDDEFGFFEGEDDDCGEFEFDDEGEFFHHGHIGHFEE~}€€~ƒ‚€€€€€€ƒ„„ƒƒƒƒ„„„„„ƒ„ƒ„ƒƒƒ„„…„ˆ††…………‡‰ˆ‰‡‡†‹†‡‡‹‡†ŠŠŠ‡‡…‚€~{yrmieddb`_]][[Z[[]]__birbn”°ÈŵŒ¢žš°®¯°±½ÑÌ«˜Š{qhb^\XUSPMNS[afhpts|¢‹…‰|žw‘¡†€‹—uceedffhdefigfgjga^`cihhhhijjiijlp~¢ÔëîêÏ·›xmc``_]]^]^\\\\\][[]^]`_`__bedddcfx„¥’|zŠz‡Š–¢€lVW\]VPONV^it‚‘¤¹ÇÙßãâçêêêêééèçæåßÝ×ÒÌÇÀ¸±¥ œ›š˜–••”“‘Ž’–“‹ˆŠ………„‚‚~~{{{zzyw|„Š•¡¬¶²“wU61-/24577778:999999<=CJVamz†“”š‡‚}}‚•ž¢¢¥±Ñýº¹ÀÔêñîìäÁ¬£žœšš››š™™œ››˜ššššš™™š™˜˜˜š˜˜™™˜˜•–—˜—˜›š———™•˜—–•••—”–—––•““‚X5(!!#*35666667666556766666688787578899;8987888778:9:8889:;;;==;=<<<<<=<===>?A>>@@@@@AA@@AB?@AAAABBBBBBDCBBCBBBBCAAACBBACCBBBACCCBCBBBCCCCCDCCFGFFFEEEDCCCEDFEECEFEEFFGGGGGGFFEE…€€€€€ƒ€‚€€€ƒ†ƒ„ƒ…ƒ„„ƒƒ†ƒ„„†„†……„ˆ‡‡‡†……‡‰‡ŠŠŠ„…ˆŠŠˆ‡‡‹…‡†Š…ƒ„„„„‚|zqkgcaababb`_^]]^[YZZ[]^``bi€^Wu›´È±Ž’¥¡«½ÄÉ®´ÄÜÓÁ¯œŒsieb_[YTPMOSYbafntx¡‹‚•}ƒ “ƒ…¡vcedceeeddegggiif`]adgjjjiijjijkls…´çñöøù÷é¬sgd`_]]]]]]\\\\\\^^]]]]\\\\\\[]et’–‘}~†Š“Œ§—‹lZ^`_ZTSQOSW^fuƒ”§µÊÏÔßççççèçççæåäãâââÝØÍüµ°«©ª£žš—”‘‘‘ŽŽ‡Š†‡‡ˆƒ„~}{zz{{|~…Œ—¥®º´²•zU91.0145799:889:;:::;<;9>BMTbr|Š‹•ŒŽ‰††…Š’™¬Ô¿Â¼¶¾¾ÊÔßââáÒÁ³¢››œœ™œ›ššœ›œš›™˜˜œš™šš——š™™—˜˜˜™šš—™˜›š›™—––•—–—˜™••••‡i>,#!"(/567686768886777656668767877899888887788878:::9;889;;;;<;>==<<=>>==?>??>>@?A@@@@AAABBB@AAFCDCBCADCDCCCDDBCACCEBBBCCDDDCCCCCCCBBCCCCDDCDFEFEEEFEECBCFFFFEDFEDDEGFEGGGGFEEE‚‚€€€‚‚…„‚„„„ƒ„„ƒƒ…‚„……ƒ……†„……†ˆ†…†ˆˆˆˆ‡‡…†ˆ‰Š‰ˆ‡ˆ‡…†„ƒ„}|{slhc`^\YXVY[]_`_^\[[ZZ[[[\]^^^`i|ZTXu ¯±š“•¥§©ÀåÔ¢§¯ÂáÙʲž€wokgda[VRNNPW`ehggw…}„Ž£’z•ƒ™¢xeihgefffggfghijd`]ddfgijjiiijjkov’Ìëóöûÿþøè»”€mfa__^^\\\\\]]]]^]]]^]]\]\[[\bgv}}{††‰ˆ‰‡†‹quvw{gZZYTONOW_itƒ¸ÎÓáâäåæææææååääããßÞÙÔÌÆÀ»¸¶¯¨¢™–’““‘Ž‹ˆˆ†‡‡ˆ‚€~}}||}~€Œ—¤®·³±—yZ>:89::::9889:;<?@BCB@?=;9<?ELXcp‡Ž—ˆƒ~…~Ž®¯ÆÅ¼½·®¬²ÅßëçÜÒºª£Ÿšš˜™™ššœ™œ™™˜—–™˜™—››™–›™™–—˜˜—šœ›˜˜—›–›™˜––•——˜˜—–••“ŒxK3$""&+15555555666555555666877777779:9889887876778998:899:9::;;;;=;<<==>=@>?>>>>???@@@AA@@@B@AABBDECCCDCCCBCBBAAABCCBBCDCCCCCCEEEDDBADCCBDEEEDDFEFDEEDCCCDEEEEEEDDGFFFEGHGFGGED…„„€‚‚‚„‰„†ƒƒ„„‚†„ƒƒƒƒƒ„…„………„„…ˆ…†…†††‡‡‡‡ˆˆ‡‡ˆˆˆ‰‰‰‰ˆ‡‡€~~{tmhc`^]]]\ZWUSVSYZ]a^][[ZZZ[\^\\\]]_hz[JMY{¡§œƒ‘–¡Ä×Þ¤›£ÎâÚ̶¢’…}wrnic^YTOOOV_`bcoy~}‘‡‹›–ƒ’Ž€|““~jkkgfedeeeghiikd_]eegghhjjjjjjkq|ŸÙìõøûþÿÿúï̧Œrjb`_^\]^]\]\]\\\]_]\]\\]\\\\^`hkptw|ƒƒ†‡ˆˆ˜™™}o^ZYZTPNKQU]ep{‡“£³ÁÒÙäåæææåääääääãããââââàÝÔÉÁ¸¯¦ œš•‘ŒŠ‹ˆ†‡‰…}~„•§°¸µ—~\C?@EFGFDA><;<>@BEJPSUPJE@<9:;@DNWcox…ˆ’‘’‰†„‚Š’¡ÉÁºº¸µ´¬ª¶ÁÎßßÞÍ÷¥Ÿœ›œ› œœ™™¡›œšŸ›œœœ›ž™™™›™˜˜œœ›œœš™™š›š˜™™š—œ™—–œ—™†^:&""$(/354455555666545567687667788898898:8887788:898;9:9:9::<:::=:;<<=>>@?@@@>=>>?@@@@@@@@BAAABBEEECCCBCBBCBBAA@BBBBBCDDFDCBCDDDDDBADCCCEBBDDEGFFDHHHDDDDEFEEEDDEEFFGGHGFFHGED‚ƒƒ‚„ƒƒƒƒƒ…‰ƒ…ƒ„„„‚„ƒƒƒƒ††…„†…„„„……††‡………‡‹ˆˆ‰‰ˆ†ˆ‰†„„„ƒ‚|zwnhd`^]\[[YZYZXWRTWWX[\\\\Z[\\ZZZZZ[YXW[az`KCQ_}«¡Œ“˜™´Óä§«³¹ÇäÖλ§šˆ|tmh`ZUPOOT]aejmoo–š©‘|’Š~–‹~s‚Œzliheefghiije_^dfjjnjjiijkjlsƒ°çðöùûþþÿÿüôÚÁ }peb__`_`^^^^]^]_^]]]\[\`]`^`^`bdgiy‡–’‘…‡ƒ|uilnh\XSOJLNUZdks~œ¬¾ÊØÞåæáÝØÝäååäääåååääããáÝÔÊÁ¸°ª£œ–‘Ž‹‰†„€{}~ƒ…’¢²º¸±¤dFEGPXYZVSMGDACEHJOUZ`[WONGB>:::<>FMXbo}…‘’—އƒ}~‚•¨»»¹°¤¡¢´³´»ËãçÞÁ°¨¡ž››š™š™›››š›››™š˜˜™™˜™›š™™™ššœœ››šššš™—™œš˜œ›˜–™—–’‹oB)$!#&-444455555555744566667667789998888788877888888889:99:::9::::;<=====>======>>>>AA@@??AAAABBCBDDCCCCBBBBBCBAABBBBBDCDECCCDEDDDBBEDCCCBBDDEEEFDDDEEDDEEEDEDDDDDFFFFGFFEFFEF‚ƒƒ‚…‚‚„†„‡…‰……„…†ˆ‡‡‡†…„…ˆ…„†‡††…‡‡‡†‡†‰†‡Š‰……‡‰…ƒ…‡„}xslgca_^]\[[ZZZZ[[^YWVWXY[^^_\\\\\_YZZZZ]XVUW_{fLBKdpˆ¥Œ‡Ÿ±¦ªÀ࿺ÐݼÆÛÜÙÁ±¥š“Œ„yrha[UQPOUZ^dehj€™ª¥~“Š~˜‰|}£›“vjffgjhiijjg`^dhiijjihjkkjmv‘Æìòøúüþÿÿÿÿýû÷鲇wie``_a__^_`^]^^]]]\\\]\\\\\\]\^ar…¤ž”‡~|{{}€ƒŠw[YXYQKLINSW]go}‡™ª»ÔÙäãÝ×Ñ×ãäåäääååååååäääãâÜÒɾ·°§Ÿ˜’Œ„‚€}|}~…Œ’™£´ÆÁ¾«™qYST\djnomibZQMKKOTZbhgc^WTOIC>:98:;AFOXes|‰‹”’”‹ˆ‡„Š‘¶Öî§¢¡Ç¹É¸¯ÂÝÝÝÕϼ¥ ¡œŸœ›ŸŸžœ›œ›žš™™™šœš œœš›››š›››š›™™ž˜•”{J-%!"%+3355555755544555566666667889888:8978777888888887:99::9::::::;==>===>>>>=====>ABAA@>@@@ACCDDDBABCBBCDBBBDCEBAABCDDCCCCEDFDDDBBFEECCCCCDDFEGCDDDEHDEDGDEDDDEDHFGFGFFEFFFE‚ƒƒ‚ƒ‚ƒ„„„„ƒ……†„…………‡ƒˆˆ„„…‡„†‡‡†ƒ……‡†‡†ˆ…†Š†ƒ„…ƒ‚{wqlgda^\\\\Z\[[ZZ[[\[[[ZZYZ[[\^^]]]`]\[XWWWWUTTRV]jiLEMˆ{q{’š·º½¬¡½ÂÊèåǶ¿ÔÜÛ˼±¨ ˜‚wld[WTRPSX_hhgwŽŽœ‰•‰€™‹…–…„vjlmkhhiikea`cgijkjiikllmo|žØîôùûýÿÿÿÿÿÿÿÿùê¿™†qhb`a`_`a`^_`_^^a]]]]^_]]]\\\]^jz†™‡‡Š‹‘†€p^bda\TMJGJLRX_fq|‰–ª¼ÎäßÛÐÀÉÜÛÙÛàâååæççççæååäÞÝÖÎǺ²©œ•މƒ‚ƒŽ˜£¯¸ÅÂÁ¡gdbgkjxpywuuuf\WT]gr}}yokd[PIC?<98::>AHO[htˆ’˜ˆƒ}†³ÊÙ¬ª¤Ÿ£¬³·²¢²¼Ôäãâ¹¥¡žœ›››œ ›œœœ œ™™™šššš››œšœœžš›œ›™š™™™œšš˜˜˜˜˜’…Y3(!!#)12655554345534555566666667777789776777877899889999999999999:;==>===>>><><==>>?@???>@?@AA@ABBAAACBBCCBBBBABABDCCCGDCDCCCFDCCCCCBCCCCDEDBDEDCCCCCEDEDDDDDDDDDFFFFFEEEFGFD†‚ƒ‚…ƒ„ƒƒ…ˆ……†††††Š†ˆ†‹†„………„†ˆ‡†…‰…†‡Š‰‰„ƒ„†ƒ{xojgdb`_^^\[\\\\__]\\\\\\[[\^^^]^\_^d]^__ZZXUVWTSTTSU\pcKJPxuj|Ÿ¯Ûȳ˜´ÇÐàåÕ››¥Àâèæ×ÍÄ»°¢•‡zne^ZUTRUW_eckyz{˜†‚š‡Ž“†…Š{onmjhhhhkcaadlloljjklmmnq‚°éòöùûýþÿÿÿÿÿÿÿÿûñѱ”vlcbaaaa`````__`^^_^^^`^]]]]^^biqyƒžŸŒ‹„€~ztttrla[SQMHFGLQX_hp}Œž¯ÅßØÔ¼¾ÈÐÎØÜÝÜàåæçææåäääãââáßÚÓÊ¿³«£™‘ŽŒ‘™¦µ¼Îºº “}j_ZZ]_efgijnsq~orkk|’ž§š‰ƒsnYOIE@;:989:<CIS^k|Œ“‘‰‡±©¦¡”±¡¶Ú¾ª¶ÀËÙÒ̪¤ ŸŸŸŸž›œœœœ› œ›œœ›ž›Ÿš™šœœžž š›š™™Ÿšššš˜›™–Šm:,"!"&,245554444463444466676667786788899777667788777889::::;889:9=<==>===@>>=====?>@?>>??B@@@@@AAABDBCCBBCBBBABCCBCCBCCEDEDDCECCCECCCDCCDDDDDDEECDCDDDDGEFDDDDDFDDEGEFECEHFEE‚‚‚‚†ƒ„†††††„‡††„„„†……„…†‡‰„†ˆˆ‡…†…‡‡„€€€€upjda_]\\\\\]]]`]]]]^_]]]]]]]^^_]\^]][\]^_YVSQPNQSTRTUUUW\f^IJQl‹|c_x¨»Ò¯˜«ÙÏÑâÝ¡—§»Îéèâà×μ«œ‰{nga\YWTUX]eimmoy}…Šˆ…šƒƒ˜‘…ƒxŒ{llnjiebagjknmmmlmmlptŒÆêô÷ùûýþþÿÿÿÿÿÿÿÿüöâÖ¨sfdba`aa`____`^__`_^^^^__^]_abfiu~Œ£‘€…ƒ„…‡ŒŠŒi_YYYXPJGEILSYajw„”¥ºÎËû¹ÍÖØÚÜÝÝ×ÝáåææäãäååäääããáÝØÐÉ¿µ«£ ž¤°º©š‰wmdbZZUXVUVUUWX[_fonxz–§§¨›—vj`YTOHC><999:;?CKTany„‰“—˜Š††ˆ‹ˆ…Œ¶²µÙجµ´¢ Ÿœœž Ÿ››žœ››››šœœœ™›šš›œœ›››œž›œšššš™š™š›š˜˜™–ŽzF2#""%)0234534444443844556866777888888977786566777777889::;9899:9;<<<<<<<=>>==?====>>?=??B>?@@@@CBBBAAABAAABCCBBBBBCCBBCDECEDDDCBCDCCCCDEEDDFEGEBBBDEEDEDDDCBDEDDDCEEEDDDDEDD‚„…„†††„…††‡Š„‡‡‹‹Œ†ˆˆ‰‡††††…†Šˆ‡†‰ƒƒ|xslfb_]\\\`\\[\]^^^`__`^^^]]^]^\__^]\\^^^]\[ZWSOLHEGIUVVWWZZ\^waIGOi•o[X[zš¡§›§ÓÆÈÝÜ©žÄ¯ ¸äíîîëâÚųž‹|qjd`\ZWWV\ddffpwŽ…„š…œ–¤ ‰¤›‘vnknkhbahilmnnllnnnrz˜Õëõøûýþþÿÿÿÿÿÿÿÿÿÿþü÷æ¸zlgb``a`_____^___`^^^^^^^]_`___eo€‹‘‡‰Ž“–˜—–’ta[Z][[SMIEHJOT]fq|‹œ°ÆÆÒÕãäääääåßÜßâÛÒ×ääääããããããââáââÛÓ¾ÀžªŸ”‰€wha]]abc_[UPNLMLOQRU\`gzƒ£¸¨©“€ogfgc\QJD@<:9888<=DKWcmyšŒ‰ƒ„ˆš¬Ï¿µ³²ª¥°À®‘œž¢œ¢¡ ›£œžžžžž¢œœœœ›Ÿœ››œžœœœžœ š›œ›šš™†[8%"!"'/233555443443445745666866798788887777677779787::999999::99;;<<<;><>=><<=====>>?=@?C>??@@?@AAAABAAAABDBAAAABCCCBCECDCEDDDFBBCCCDEEEFCDDDEEBBCDEFCEEECCDEEDDGDFEGDDDEDDD„‡…„„ƒ…††ˆ†……ƒ‡††‡ˆ………†ˆ††††……†‡ƒƒ€}zwlfc`][[[[Z\[\Z\\]]^^^`^^^]_^][\]]\_`]]\[ZYXUSRNKIDGFIKNUVXXXYY[_saGDKgnhbVVYt™›•ªÍÃÊܲ¥ÎÇ›š¾ÜîðîéÝßË´ Œsnjhe`[XVZafjmn|–~ˆ„šŒˆ‘Ÿ††œ’ˆ€vkllgbbgimmnlllmnot€§æï÷úüþþÿÿÿÿÿÿÿÿþÿÿÿÿÿùëÄžˆpibac`__`_____^___^^`_^^__^^^adqy~ˆƒŠ‹†|wroljdddgidaYSLGIIMRYakw…“¦¹ÈãääØÊÐ×ßåßàâÑÌÉääãØØÙÝâããããâàÛÝÿ¤•‡{smhd\UWX]edf`YTHFAEKMOPRUWbk{š¢•Š}igdiqeXQKGDB?<8999:>BLU`nw„‹’šŠ„~~…Š‘ »Õ±°³ÀËʇ “˜œž žœœœžŸœžœžœœœœ››œœœ›œ šœœœœ›š™›“‹k@'#!"$-22233334344444444566677687778878879767767777789988999:999:;<;;<<<==<<<<====>>?<>=>>>??@@AB@@@ABAAABBBBBA@AACCADDBCCDCCCBABCBBDBEDCCDEEEECCDDEDCDEDDDDDDDDECCCEDDEECDDˆ†…„‹†‰†…„…………‡…††‰…„…†‡‡‰‹„„‚€~wpid`\\[[ZZZ[\\]]`_^_`]`_^\]^a^`__\\]]\^^]]\[ZVSPKGAACGJLNPRUWY[WWX[^qaEEId›€{kWW]z™œ®¹Ã¶Æà»¶ÕÆ›“¬çíïí騨æÎµ¡ƒyurnjd]ZV[_djipy€€—†‚’‘~{~…‰ ŽŠ‰zlmmecbfikkomllnoovЏìòøûüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÕ²”vlcca____________^^_b___`^]___agjr|ƒ‰‹ƒn_a`beimt{}yf]SWPKJHKMT[en|‹›°ÄÜÕÍÁ¶ÐÙåÛßáÔËÓäæçÞÑÏÑ×ÝááàÝÔɰŽwokecaZQNLQZ^ffjbXOFB?CDGKNPRTX^lsz„~rkkmotgYTMMOGB=:9899;=DJT]iv}‰”Œ’ˆ†…„…—¸³²±ÂËÞÇ®«§™’•šœžžŸŸŸœ žŸžž ŸŸ ¡¢ ›ž›¡¡ Ÿ œœ ››™–yI,$""$+/3355554334546666665676666688777877777767666777888899:9<9:;<<<<<<<=;<???=??>>?>>>>??>A@ABC@C@@@@AAAAAABCAAACBABDDECDCCCCCBBBBDCDDFDDEEFFDEEBCCDEDDCEDDDFEEDECDDEFFCDD…„„„…„†„…„…‡‡‡……††††††…„ƒ‚~{{{qlf`][ZYXV[ZZ[\\\Z]]]\]_]]][[\]_^^^^]]]]]Z[\YVUTOLLIGFIPQPPPRUVYZYYTUVY\nbC<Fb˜†•j^_c| ¦®¸´Íá°Åפ“¯çîîëåÐÊèåл§–†|vpia[WY^dkmpvw€‰Œ‡{~s~„‹ˆ†wlmlgdcfjmmonnmqpqz•Éíôùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷áѧ€rfc`__a`______^^^_^^_a__```aabdfop€—‹nopx|€†‰Œsj_X^^VNKGIKRXaiu ²ÂÀ½ÃËÛãââáÛÕØæéëèÛÌÈÁÂÆÃÁ®žŽ€wplhgec_[VRMLKQUZbcm_[PGEDDADEJQPWXY^cmwz}{wttuseZTPRQHA>;97889:=@HOZeozŽŠƒ}}Ž•±±ÂÊÉʼǩœ—•““š£ žžœ››ž Ÿžœœœ Ÿšœœœ œ Ÿžœœ›››œœœ’ƒV1&!"#(.2342434234455555566566656687768977886:6756687777778:98:9::;:;=<======<?>>?>=>>><=<>=>?@?@@A@@@@@AA@@AACCABCBAABCEBCCCCCCCCDCCBDEDDDEEGFDEEEDCDEECBCDDCFDDCDDDCDDDCDD„„††‡‡Š†ˆ………††„†‰ˆˆ‡†††‚€}ylida^[ZZYXXXY\Z^\]^``_]\]]_\\\\]]^_^^]]_]\]]Z[XUPLIFKKNOPQTWUTUYVVWXXXUUUW\m^@9A^˜‡ž‘of`_j‚™²ÈÆÕãɰ·ÈªŸ´éðíâÓ¿ÒèêäÑ¿®£›”އvkb]XZ[`gfkq|…‰Œž…wnt~”Žzvmnlgeehjmnnpolqpr¤Ùñöúýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþü÷縌xie```ab```__]^^__^aa__`__``__`chwˆ™…‹’™Ÿ•‰ˆ…{rgefb]VRMIJJNTZaktƒž¯µÍÐâåæçèâÛáçèéçå×ɯ¥˜†|sliggggedb]VVXYWQNRQSW\a]_QPKEDBDFGHIJMSUY_dlyy€~xojeb^RODC>;988888:<?CNT^hr„‹‡Œ…††…“µ±Ä¿½³¨¤šž´¥™˜—™žŸ¢ ž Ÿ¡ žž Ÿ¡Ÿ¤Ÿœ Ÿ¡ŸžŸ ŸžœŠf9)"!"&+246663433434555556965676668766:98766776556677777777888:99:;:;;==;;=<<=?===>?>>A=><A>>?@@B@C?@@AAA@@@AABAABDBAABCEBBBBCECDDDEFBCCCCDDDDFGHEDDCCCCBBDDDDFEDDDCDCDDDEED……………ˆ‡††„„…‡‡ˆ‰ˆˆ„~ytmhe\[ZYXWUWXYYZ[\[^\^__^^]][]^\ZZZ][^_]]][[Z\\XTROMIKHKMPSSUUVVWWWXVVVVVVVVTX\`V83=Us‡•t`af»ÁÎßçÕ³²Î¢œ´ëóíÞÐÓåçåáãÙź°ª¥›‘†xof`ZXX^jlkrz{|œ’ˆt…£ˆvtmmmhdfkkonnnpppps†²æó÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúì܇rjdbba```__^^__`^```a`_^a````bcmuz‚‘‰…„‚‡„„ƒ‚„xunhaYRKIHLPU[emv€Žœª»Êàåæèéèèèçåäá׬“ƒtmhfdedffgjgdb\ZY^g_ZXWRIOOSXVTUXTPLGGBBCBAFKPUZ]biu„…†zvmd]SLEC?<:98878:;<ABILU[clw†Š‹‹‡Š™£˜‰„Ž’›ž´²³²«–™™¢¡ ¢ ŸžžŸ žžžžŸŸŸžŸžžžžœœœžŸ›œ’uD/#"!$(133332543433445565555666556776888777766667777667777888::9899:;<=:9;;<==;=<==>>>==;=>>>??@@@@@@ABAABDBABABABAABBBBBBDCBCBBBAABBCDDBCCCCDDDDCCCBCCBBDDDFDCDECCCCDDDEED†††„„†…†ˆ„„†ˆ‰Š‡‡„‚usjd`^\[ZZYXXWXXYZ[__`]^^a_`^]]^]]]]ZZ[][]]\]]Z\WUUTPLKJNSSUVWWWY[WXWXWYUXVVUVVZZZ[gW32:N\w”†w`_e‡®Èæé㹯°œž³ÝïîàÝáêçÕ¸ÖèÞÔʽµ¨šŒshaYWV^edghmw’¦³”ˆ|˜™xqmomjdgjkonoooppqvÄéõùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûóÔ·›ymcbaaa``_^__`__``___^]_^`a`_`achpwzyx{„„‡ŒŽ¡žš…ŠŠŠ}i^]RNKGJKQW[biq~‰™©·ÆÓäççèéèäϲ›„wida`ddccjeefgdc_`^cejgf`[RONKKNTWZ[ZXSNJCB<==DINTUW[`jpuƒ|obZMHD@=:999878<DIKKMPQQW_hr{…„މŒ‘‡„‚…Œ µ¶ØÙÖ¶¨£š™žžŸŸ ¡£ž¡ž¢ŸŸž¢¡ Ÿ¢Ÿž£œœœŸœœœ”…X6%"!#&033333543333444595445666556788777766766677776667778788:88999:;;;::;;==<;==>>>=>?><=>?>??@@CABAAAABBCCCDBCBAAAACBBBBBCBEABBBBCCBCDCDCBCCDDDDBBBCCCBDDEDCCCCCCDCFDDEHD††††††††ˆ…†‡„‚€}ytkb^ZYYYXYXXXXXY[[[[^\[\\__^]]]][ZZZZZZZ[[ZZYYYXWURPMOOORSSTUVWX[YYYWWWWWWUUVUTVVVVWZfZ5,5MRf}—–™Ÿ©zbca}¦Íèå½µ´¤¡¯ÐßîîèèíéÏÊÏêììéçÞÒǵ¡‘‚vjaZXV[dhpnm€ ”–ˆƒˆ„ƒvmnnhdgllonqpqpqty–Öìöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ñ⮈xgdbaa``_cab``aa`__^]`^b`__```beilrsƒ—“‘Œ‘“‰‰Š•yb]\XRMHHILPUZ`frz‰”¡¯ÂØÛæèè⸒tmgcd_\XSW]_baadfca`_adcdfg\ZXUQLRW_g_\Za]ZQKF@AAIJOVUTW\_clw|€pjZOLD@<=====?JT\bdfb[TIQW_dn}ƒ‘‹Œ„~|{€„‡£ÀÏÔÒÌÎÅŠ’”ššœœžŸ ž¢Ÿž›œž¡ Ÿžžœœœœœ››—Œe>'"!"$+245435444453545444564666666777776666778787876777667787899::::;:;;;;<<;=<;<<=>>>===>>>??@@C@@@BCAAB@BCDBBBAABBBBBCBABBCBAABCDDBBCBDCBEDDDEDBCCCCCCDDCBCCDBCCCCFCDEDD†‡‹†††‡‰‰…ˆ„urjc_\ZYXWXXY[Y\YYZ\\\\\]\[]]``_[\\^[\ZZYYZ^ZZ[[ZZWVTQPMMPQSSVV[ZZYYYYZ\WXWYWWUWVVVWWWWW[e^7-0@NWk‘ª¦±¡†sp_`| ÇÞ··²¨«®µÈâîìêìèÑÈÕèðòòòðìá×¼¦”„vib\XU\bfjhq„‡€„”Š¡‘{lonfeglmmnpoprru¡äï÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõ隃icaabaaaaa`aba`__^]^^`____`_^]^agn˜ª‡Š…‚ލ›–iZ\`[WPJJGIKOSY`fmyƒ‘ž¯ÄÒåçß·leb_^^XQNLRYYY^edcbaaace]UX`Zd\[\[`hg`XVY]ZdXTLHHHJKJIMPSZZ\`eluhpc_SGDDEFGGM`iorru€o_TP\YYZbmu}‚‡x‚€ƒ‚ˆš¢¸×ÊÚµ¤ ›žŸ›™˜š›œœ Ÿ§¡Ÿ£¢£žŸŸŸž¡ žžžžŸ›¡™“wF+#!"#)0346456545433445445777656666678868667976677777766678878;;;:;:>:;<<;;::;=<<=====<<=>?>?@BACA@AA@?ABBBCDCBACCCDCCCBAABCDBABCCDCCCCCDCBDFDCEDCCDDCBCDCBBCCDBDDEDEFFEFE„„†††‡…ƒ€yrkc_\ZXWUVWXWYZZXYYZ\]^]]]^\[[[[ZZY\]\[ZZZYYZZYXWWTTTSQSVRORRSTVWZYYXYYY[[WYXWUVVWWXWWWWY[]ga;..1<I]…’©¦™z_[]u’£³º¼¹¸· ±ÓæèæâÞÔÚæêîòóõôñïåÚ«—ƒwld\YV[`fkpspqŽŠŽ—™|lmngehlnropoorrw†¯èòøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷íβ“qiccbbaaa`aaa```__abdegikgcba__di{‡…„‰†~{ƒ‹ŒŽ„qonmjb[TOJEFGLOUZ`fpx„ŽŸ¬¿×Ô½…nhb`[]UUONMNOS]_eedcccceYSMT[[[Ydciij`RPNQ[^bUXQPNLLDCBHLPVVVXX\dfsghZUQNLIJVc‰ƒ~~€qcaa\TPNT\elrx}{yxz}ŒŸºÒÀ®²Àʲ–’”––—˜™š›œžžžœŸŸœž œžžŸŸžžœš›•…Q0&!!"&-23545344454344554456555567677665556566657777766767667778999:99<<<:;;;9<==>=<===<>>>=@@@@ACA@@?@@ABAABCAABBAABACBABBCCBBCCEDBCBBCCBBCDBCDDBCCDDCCCCCCDCCBDDDEEEEEDDƒ†…ƒ€}voh`^\ZXXVUUVVWYY[[\YXY[\__^\\\_\[[\ZZZ\]\\[XWWYZ[XWWXSQTSSVUUVWVUVYWYYZY[YYZZXYXVVVWZXWW[YXYZ]id>2-+/=Jat–«˜™ ¥fNQXkz”¼º¼ÍÜŸ¡ËàÜÑÓÚØéïñòôôõôóòðîèÈ®˜†ynf`]\`afjkgjv‰¤¤®—€mmnhgilnppppqtsxÀìôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõðà§}nfcaaaa`aaa``_`bdgjmtywob_^__`bhpw„‚€xhxz‹…‡…ˆ’”rf[]WTMGFGILOSY_emwŽ·Ó¼}oha_\]`]ZVROMTVX[^aafed[RNNTWXY^efgj_WNIFLOSWWVSVQMICBBGHNRQPPQSW\cconn`UPFNao‹tos€„srkfSIFIT[ekrqptopw}€ƒ˜Ÿ§°·µØÅÔº°ª•‘‘“™™šœ£žžžŸ œŸžŸžŸŸ¢¢¢žœ¡š™‰_8("!!$)23856344554464554445556666658665556666666777976566678788=:;;:;<<<<<;<;;<==?=>>?<B??>@@>>?CA@@@AA@AAAAAABCAAABACBABDCCABBDDDCCBECCBBBEBEDDDDDFDDCDDEDDDDCCCEFFEEEED‚‚€}woi`\YXWVUUVVTVWXYYZZZZYZZZZ\[[ZZZ[\[\[[[[\\\\YWWVYWVUUSSQSUTUWXUWXXWWYWXYYWWWWVWWXXXYYXXYWWXXYY[^hgA50+,.>Pf–˜˜¦ªqNGRO]h‚š·åÕ ¤ÅÌØÄ·ÈÛìòõöööõôóóòñìæÉ°›ˆ|qkfdbcfiokkswˆ§–{nnnhgjlopqqqrtt}™Óïöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûòâ¶}hebbbbdccabhnsy~„‹‘odcadca`cgpt}‚}ƒŽ“”˜—–ŒŽui\ca^WPJGFGGLOTX^eow…•©ºž‚ungeccdcdZTRQNIOTX[aie_\YVSPINUZadkcbUIHHIJPRRQTZSVQKKJMNNNLJKPPQSV\aepb[SGOPVYZ_j}xukjZQEALU]dn~smljptzzxx{€‚‡ ¾ÇÊÐÖÕ˘„“‹“’”———™šœŸŸžŸŸžœžžŸžž›ššš›‹kC.#!!#&02344344544444445546555456656655556666787776666666777789:9:;:<;;;;;;;;;;<<<<=<====?>?@?>?CB??>>>@@AAAAACBAABAABBBBBABABBCCCCCABCCBBBCBDCCCDDCCDBCCDCCBBBCCCDFEEEEDysg`][ZXVVWWVWVWW[[[Y^[[[ZXXZZZ[Z[ZZY]ZYZZ[\ZZYYYXXYZZUTSTRRSTVVW[WVWZYYYZXXXXXXWWWWVUVWYZYYXXYZZZ[]^fhD73.++2FT_l›¢¯lQRVSPS\|°É΢¥ÊÃÅ·²¸ÚíòõõõóññññòñïëæÊ±‹~uqnljjimkknowŒ’vopoijlnpqqqqsuu¦çó÷ûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöìÊ¥‰leabaccbbcp™Ÿ‰—‘Œ„xqiijc`^`bhry…Š– ¨¦¤š™“‰{qoohbUYPLJFFEHKOSY_hn|Š™§ ¥˜Šojgfdc`ZXSIGGIJRZ_jjldaYPKGMPU\af``VPRUQPTGLTTSTY[[[ZWTOFDCFMNOPSW[`jer[TPFIBIPVYXZULG?DP[djqvvmghqn{wssuy}ƒŒ’—±ÙÕØ·žžžœ“ŒŽ•””•™™™›Ÿž£ŸžžžŸŸžž žŸ›œƒQ3%!!"%023343745444446575555574456555657556788797755566788777:9;99:::9:<;:;=;=;<<<<<<>===>=>?B>@AB@B@??@@AACBBBABCBEDDBCCCCCCDBBCDDCAACEBCDDCDDDDFDEDDCCCDDCCBCCCDDFGGEFDja]ZYWWUVUVWWWWUWXYYYYYXYYYXYZZY[[[[[YYXYZ[XYWXZZZZ]YWVTSSUSUVWXWWWWWVWWXWWWXWWWWXWVVVVVXXYYYYYYYYZ\\]dhE952.+/6FS[bœŠaO…u\QLPV¿«Ž¡ÏÎм™®ØìñôôõñëíïðïìíîìèÍ´ ކ€}yxxwurmoosvqtsopqmmotrqqrqsvw†²êôøûÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûñÕÀ™ukeddcdbboˆŽ†‰…‰ƒ„…††sfb_bbjƒ—“™¦œŸ‘ŒŒŽŽŽrn^W][WQLFDDGHMQW\bitŽž¢³¦€vnjfc_\[PHIJKKRV^cfhedWOONNNSTUX]e[\[ZWUPKLLMQUa[ea`ZSNFC>DIJLNPQRW\bmadWOJCFIKBHJKKKMYediknromkmlh```fipuy~‡™²Æ¸«ª¼ÍÛ¶—’…Œ‹““”•–™›œœšžŸ šž žœœš™”Š`;&" "#+13333335542354444555664565555555555667676655567657777788899:<::;;:;;;;;<<<<<<?====<==>>?????>>@@?@AA@@AAABABBBABBAADCBBBABBBABCCCBDCBBBDDDCDDDBDCCDCDDDDCDCDDFEED[ZYXWWWVVUUWWXXY[Z\\\ZYY[Z[ZZYZY\[[Z\ZYYYZ\ZZVVX\]\[ZVVSTTUW\[[XXXXY[XXYYWWWWXYWWWWWWVUW[WZZZYYYYXXY[\biF;741.,/<MVYjˆfMx•s[MHLY|‘ˆžÏâãä ¤ÒìòõôóòïëêíëÚÔàìîè禛”‹‰‰‰…~xqnmkinpooooqrrrssrqsvx‹¿éõùûþÿÿÿþþýýýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷óçƒsedcbacpœ˜Š‰Œ’–˜‰€na_ccm{“›žŸš–‡„†Œ’œª©¥Œ{maaed_XXOKHDEGKOSW\bmw„‘Ÿ±¯½¦›‹xoha^ZWVTQMMNVZcdefaa\XY[URRHQ]]\[abb`]YVMGJMT\fihg_\ODA>CBEJKMMPSV]cbqcc][XQJFO]adffgeceovx{vomc^]]`ekosv~‰‘˜Ÿ¦«³×ÐÎżªƒ|€“–––˜›šŸ›Ÿž£ŸžžŸž œž—‘oC*#!!"'/443243444344333455456445667665556666867666555665999777789:;::;<;:;;;@;>==<<<>==<<<??>>>??@?>>?@AAAA@BCDEDCCBBAABDBDBBBDA@BDABCBCBBDCDBCCEDDDDDDDCCCCBCDDDEFFFEEDXWWWWWVVWWWXY[\]Z[[Z\[ZZZZYYZ[ZZZZZYZZZZXXWTTTUVXXXYZVVTUUWXYY\YY\YY[Z[[YXYYZYXWXXXXXXWWZXYYYXYYXWWWY[ajF<9853/,5CPY]m~}MmŒsOGEMVk† Íêîë¦Ííóöôòóï×ÞåÚ½¿ÇÕêíéÓ¾²§¢žŸŸžœ’ˆ~uronopoppqux{xvvuuux{”Ñì÷úüýþÿÿÿýúúûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ井kgccdkqˆ‡Š†‘ˆŠŒŽ’‹…zvqolppuzz{yxxx}€”œ¨ž˜Œ~{rvy|‚f\[YTNIBDEILPTY_ho|‡–¦°¿½ºŸ€keaagda[WZ^_aZUVY\fgicc[SPNOOSUVW\b_[WPLHMP\feghha\QIHGDAFFHKMPPRTW^ib|„Šwf`]_mrypoedhqwyyƒw{tlnlkkf^^aiqv|€‡™³º¿ÌàÅÐ|xsvzƒ‹’”•–˜™š›œœœŸŸš–„N/&!""%-443223344333375556446445566665666677657655666555677788899999:;;<;;;:;;<<<<<<==<;;;=??>>????>>???@@A@AABCCBBAAABBBABABCBABBBBBBBCBABBCCCCCDDEDDDDEFCBBBCDDEEEEEEEWXYWVVVXZ[Z[[\\\[\]]\\[[[YXZZ[\[\[[[[WVVWUUSSTVWYXXXZXVVXXXYZZ\[ZZZZa[[[ZYZYXXXXYYYY_XZY[Z\Y[WWWXXXXY[`lF;99:53-.9KUUa}VK~´ž•ogUJNUtÁåîêÂ×ñôöôòòêÄ·Ãʤ’©µÔìðëÙÊ¿¹¹º¾Ã»¯ …xrmnnoprv{~~}|{ywvzžáï÷ûüþþÿÿÿõêíîíêóüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷îΩogccdfkt{€Œ‡…Œ—ž–‡‰†|okdcddccgov†”¡¨¦œ’…‚…‘†‘€q][[^XSKGFEFIMPV\bit~Žœ¬ÃÀÐÁ³™tkiiiifddkdQNMPSZacfbj\VSQPOOORZ\g]^XTQOW]ab\Y]g\_]ZXSMHG@HKIJOORT[_k‚•¤‡yjdpwxqfd_mn|xwuwrznpd]OJNYcms|ˆŽ“ªÐÈç‹€zwvvvy~Œ”–˜˜™šŸžŸœ¢Ÿ š›‰\5("""$)3334633455433455556464456666756645666576556665865687997889<:<;;::;==<<<<<<<<?=<<<<=???=>?>>>@?@@BBBBABDBBBBAABCCCBBBBCBBCCGCCBBCDDCCCCCCCDDDCCCCECCCBBCDDEEEFFFEWWWWWYYYYYZ[\]\\\]]][ZZY[ZZ][ZZYZZXVUTTSTSSRSTVWXXXXY[XWWWWZ\\\[Z[ZZYYZ\YXXXXUXVYXYYYXZYYYYXXVWWXXXXY\_lG<::96420/;NRQK6G‘¡§—ƒ{eSKN[{¦Îìí×åíôööõóò楡¿¯’–©»ÉëññìâßÜÙÚÞÆÎüª™‰{topqst}ƒ…†„‚|zw{ƒªêòøûýþÿÿÿýé¶½¿ÀÓöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüó×Ãxoeddgimppq€Œ–Ÿ“‹‡„…Ž–€me`ddddq}‰œ ª¤¥”‹€…}ytqod[]a__ZYQKGCDFJMSX^dny†“¤´ÃÜÙÖ´œ‰upljggfigZRQOSSRRbbaaZZ[]YSPMQRW]^`aacdccbRSTW^]gbma^XRMINCJNPRSUWZo›”~stuvveWRNTZ\_[Zblgba`]XNFDDINU\epuzƒ™œ”‚}|{zvysrr{{ƒ‹Ž’”•—›› ž›Ÿš—Œg=-#"!$'.135443355333344545355566566656555555566656455666587899888999::::9:;;;<<<;<<<<<<=>>???>>>>>?@>@@AAACBAAABBBCBBCABBBDCCCBCACCCBCCCCCCCBBBCDDCDFDDDBCCCCCDDEEEFFGBWWXYZY]\\[\[_]^[\\[Z[Z[[^\Z[[\]YYWUUSSSTTTSSUVYX]XWWX[ZXYVVY_]aZZZZXXYYXXXXYXXXWYYYXXXZXWWXX[VWYZYZYY[_nH<::99873.0?QOD>Euœ›šz^JDSe…¥ÎçÖÛðöööõôóã †˜ª”š¶ÉÝïòóòñðïíéÙÉÇÒÍŲž~xsru|‡‘Ž‹…}{‹µìóùüýÿÿÿÿûæ“’¾‘mËôûþþýþÿÿÿÿÿÿÿÿÿýûúøüÿÿÿÿÿÿÿÿÿÿÿÿþû÷òä°‡vfdbbbaafw‹¨Œ…†’¥§¤sa_a`bdz€›°¬©—Žˆ‚|tpkhda^]^___]\]TNJDDDHKPTY_itŒ°ÄÛÖÜ̸›‰qldejg``aZVTRV]_ea]\bai]TQOQMSUWc_lkkfaZSRMSU[bgleka]VPMMRSSSTVV^…Äœƒv}wwq_WLGDBBBBCDEFFEFFDA??>>ADIQ]ir{…‰‡„~€}…~€|{{{ywst{Ž‘”•›™› žŸ ¡‚J1%"!$&/034445553345444545396566577677678856555566476678889899888999::;=;::=<==<;;<==??>>>>====>>>?@?@@AAABBABBAABCCCBBAABCEDEDDBBCCBDDDCDCDDECCCGDFFFDDCDDDDGDEDEEEEFDY[ZZZXYY\Z[[\]\ZZ\ZYZYZZ[\YZYYWSRPQQRQSUWYVUXYYYZXXZXZXWWUWXXXYVXYXXXXXYX[Z[XXXWXVYVXXWVWVWWXWXZZXVTTV\nG=<<?A@=7112<MHDIh‹¨œŽ•xFBN_i ÂÔåñ÷öõööô䢄†ŠœÍÞîòòòóôóòñëÕÊÁÓàÑÒ»¤“†|ƒ”¡¤§Ÿ—‹„‡“Åíõùüþÿÿÿÿý꫈ÁŒhÃôúüøùýÿÿÿÿÿÿÿÿÿûñèéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñå½™ƒkgcbbbdqƒŠ“„€†‹Žš™œwsnnlgdp{ƒ’‰†€{tnkhhhjeggknnlhec`_^XVMHEEGJMPW^go{ˆ˜©¼ÔÝáÕÀ® tkiga`dfca][[[TMRY^`cfc_ZSRRVMU]`hjlji`YVTVKOTZ^bjcf`\[XWUUTUT]x¢£yqrpljccXSLB@<;:98889::;<;;:<@@AGLWar{}€€~~}||{y}€|„ogefrvŽ’“”˜™šœš”ŠZ9'" "$+/354445433346443333666565667765666565555444767778767;:9889:989;:;::;;;;;;;;>=>==>>===>>?@??@?ABAAAABABBA@CBBBBCAAAABCCBDDCCBBCEDCDBCCDDCCDDDCDCCCCDDDDDDEEEEDGDZZ]]\Z\\^[[[\ZYYYZYZ\\[[\[YWUTROMOOQRRSWYZZX_XWXYYYXWWWWXYYY[W[XYXXXXWWXYY[ZXWXXZWYWXXWVXWWWYX[ZZWUTUTZpG==AIT`J<62/1=JNP]u„š–—™žE?X`gl™¹áíõõôõöô娃||…žËèðòðïòõóñðêÙÐÛèçÒØßÀªœ’Ž™ª¯³²®¦›”Œ‡‹ŸÔðöúüþÿÿÿÿÿôÉÇ͘iÓöøóæÔê÷øúûüþÿÿÿó¤›ÃïùÿÿÿÿÿÿÿÿÿÿÿÿÿýûöïÒ°qhdbacirzƒ‚‘‘‘—••Љ‘wgaflqsrokgdb`_cfhffm{‚vvnf_^^^ZVQLHDFGJOU[civŸ´ÎÛâ×ÕÓѵˆtlcfjlqom`XXKJJMPT_`j`o`_ZWQNUV`jknlrfc]WOIRPUY]`dgca_\[WVWZ`y¨kc_]\`ddcV]LH?:765956677:889::<<=@GU|ˆ”…„‚~€}‚~yˆyrnn`\_eegrz‚–––—™š›•”l@*#!!$)/3334444443444554446667767666765565856666677666666687789889::9::;9:<<<;;;;<>=@==>?>@=????@AAAAAABDCDABBDBCBBBBAAADBABCCDCEEHBCEFDDDCCDDDDDCDDFCBCCDFDDDDEEEFEGEZZ\[ZY[[ZYYWXXYZZZZZZZZ[YXUSRQPOOPRSTVX[[\YXXWWWYYXWXUVVYYZZYWWWWWVUXVWXXXXXXXXWWVWVYWWWYYXWXWWVUTSRST[qH<>ETecVB95110=Z\Zac…¹¨»˜KX‡vhjhu†´æóôóõöô範ksÈèðïäëðôñïðîéíïîåÐÛÙ×Å´¬§«±¿ÉÉÇÀ¸¢™˜¬âò÷úýþÿÿÿÿÿûòì×¥qàøõé‹„¡èíííõüÿÿýç’H\{¾òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûóÛÌ¡|rgeeghqw}~‚†‘–”•’ŠŽˆ}shllnnifedecbafsoqry‰™}sy…o`aa``]\UOJDFFJNSZ_gr}‹˜ÁÍØÖããßɲ’~upppommb]VSLKKLHOUZ_`b]e]WVUZ^ihijkllmaXVTTPQS\effeeeecciqx€[HHIIILQRSQNGB:6655556757884689:;<CRn™•–‰‚{vsolgedfe^QRSSUZ\aeimx}„Ž‘••–‘€K-&!!#'/43345554444453544465776666666865556556556666667766877887788799:;999:;:;=<<<;<<=>?>>>>@??@AA@@AABBAAAAAAACEBBBBAABBBBBACCDDCCCEDCECCBDDEEDCDFECCCDFFCEFDEEEEDEFZ[\[ZY[YXYZZZZ\[]Z\\ZXWVVUTQPQQRSSUWYZ[[[WWXYX[WVVVWWWVWZXWWWWWWWVUWXV[XXXXXXXVVWWXXXWWX\ZYUTVUTSRRRSV\rI=@O`jp^H=631.3EZ[lfo‰˜¡‘ch”¬mg_i~±àêñôöõ꿉kj{™¿ÞïìÙÇßîêçóõõõõîÑ·ÍçæãÏÃÄÈÔáæëæßϾ²§Ÿ¡ºéôøûýþÿÿÿÿÿýúøë·xäúòØtVƒÒγÀÚòþÿûãuSC`œíÿþüûùüÿÿÿÿÿÿÿÿÿÿþû÷òâ´‹xecdchtwzz€”–žŽŒ‰‰‹Œˆƒ}tkha`aaaa`ahs‰~{ƒ§—€†…ia__^^]^XSNHFEHLPT[cmwƒ’£³ÄÜâçæãϯ˜•‹‚|qnndcdeYTRQHGNPUY]^affdb_aggfggimmoda^[VPVZeefgikqyˆ˜›š‡raHB?>>>???@AA@>:765555576678225789;>HSet€‘|xpg[RPTSTTTC=;@QY\]`behmrx†•’ƒU2'"!"$,44346585444565444555677556655665555586856666777667878888::9888:99989;;;;<;;;<;?>???>@?>????@@@BBBAAA@ACBCBBBCBAAACDBBCBCCDCDDEDDDCBBEDCEFDFEDCCDFEFEHEEEEEFEED[\\[YXYYZ[ZZZZ[[YXYXWVUTTSRRRRTVWXZ\\\ZVWVVVVVVVVUWVWXVXWUVVWUXWWWWWWVZWWUVVWWWVWXXWWYXWXVUSTVTSRRRSTW\rJ>E[dmqoTB:63134BT`klj~¨–_m©—Žl^S\k›ßîòööðç¤oiu”¸áíëÓÂÍééìôùúú÷쾺Ãèììéåàçéëîïîíê×Ǻ®´ÇìõøûþÿÿÿÿÿÿÿÿýñÇzèûðÂhaešÂ…Y™çýþýè¥}iTèÿüôìîùÿÿÿÿÿÿÿÿÿÿÿÿÿüôçÀž„kgdixz†sq}ˆ„…ƒ{’ ”ƒ„|sg^bca```dmu}x…Œ„s€{morolg`^^\ZQJFEGJNSX^go|Š™¨ºÐØåæâĨ¥¢œ”…xsijlhcb_VKKMPQURT\^`bhgdfffdgijimlkkdadfffhkqx…•£»Åܹ–‚cXGC@>;<;<8:999866755557568821489=>EHKOZfq|sleMD<>@>=92,(8MY]^^_`bcehms|…„†`:.%$"$)0344555556555543555578657765655555667666677876666688878889888898999::9<;;<;;;;>===>=???A?>@??@AAAABDAAAAAAAABBBCCCCCCBCCDEEDDGEDCCCCCDDEDCEDDCCEEEEEDDDDDEFEED]\[YXZZ[\[_[[ZZXWWXVTTTSTUTTTUVY\]a\[YXVXWWWVWWUUVWWZUTUWVVVVWWWWVYWYWZVVUXWYYYXXZ[WWWXWVTSSTTTTVVUUUW[rK?Jalmwq\G=964204CUbmik˜aq‡‚™ÇjWISb•ÌãóôôîÁzjr¼çìçʰÁçïôöøøøô䮟ÈëññðïïïïïðòôöôíÝÏÅÃÖíõùûþÿÿÿÿÿÿÿÿþóÌ}àùï°\d`’¼ff…ëýþÿôÙÜÌeoçþøè·¬ÙúýÿÿÿÿÿÿÿÿÿÿÿþüøñÖµ“rikt~qopsy„‚rx—§¬…œƒk_b`_aa``eimqu~Љ|zy{€†‰|i^^^`][TQKFEFKOTZais}Œ›¾Ñåá×ÏÅ¿º¹¹—†{spocb\^TPRSUXSHMRV_bfeiefghhklmkiijihghlr…’¦½ÇÏÌÝÞà¶—zfPHA=<;<987777776565566687315=;EAUJKNU_mplkeF:31.,,+(#$/MY^b___aaadihilpnkD3*'&%(0375556766655555456576667766756666677688878766679788:88787788999;::::;<;;<?;<;?<==?>>?A?>>?>>@A@CCBBAA@AAAAABCABEBCCDBFFEDEDDFGCCDECDDGDDDEEFDDDDEGEDDEDDDDDFD]\\YZ[[\\[ZXXWVTTTUTTSSTVWWWXX[]]_][YXVUUVVTUUUTVVVVVSTUWUUTVVVWWWYVVVVVUUVWXXXXX[YWWUURWUTRTUUUUUUUWX[sM@LffiwseOC>985246AO]pokd^m˜Ÿµ‘dMKS[ŠÖéóô칃rpˆÁ×åÑÁ±´çóùøöõòèÁ’›ÑíóôóòòòðäÝïõùøóîâ×ÜâïöùüþþþÿÿÿÿÿÿþõÔ•Òôí²PTg¤Æ„xªïýþÿûõðßulçüóÜls®ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿýõÞÑ£|torwkehiu…‘}}š² y|ŠŒugklifdbacegiiw‡…Œz|}‡•’l\cbb_][XSMGFFINRV\blv„‘¢²ÅÚÙæåáââʱ¦›Œted^^`][^`]VLFLMQUZ_bfebefjklkknjlor{†˜°ÂÚßàÙÑßåäâàݳ”x\PFB=<:9767777665779:::855<=>BEB@=<HSZ`_^M:0''&)))$$$4M\_a``_a]__``accb]NB62,**/4755655645555554445556886645566676554767666666776778888968899999899:<;;;:;;<:<<==>>>??>>>@>>?@@@@BBAAAAABAABBBBBACCDBBCDDDDDCCCDEDCCDDCDDEEFDDFEEEEDDDDEEDCDD__`]]^]]\XWUTUVSSSSTTVYXX[__^\a]\\\ZXWUUWWWUVUWUZWUVVVVUXVUVVWWVVWYVVVVUUVWYYXYYXWWWYWVVWVXWWUUVWWZZZY\rNEN_fmyws]I@=985324?Ndyqpag £¬®È‚WOKP^“Öáîè¶‘wx¤»¶¬§Ìíõù÷óïçÅ©šÐêðñòóóòðÚÖêòõõõôñèæêñ÷úüþþÿÿÿÿÿÿÿþõÞu¨åß²W]¥ÝâxÆíþÿÿýûñÚflæýòÁX@ˆÞíïñôúÿÿÿÿÿÿÿÿÿÿÿûøòã¶{jecbcdkv‡‹‹…¡Ÿ™yp‚ˆ…‡‡{slc`ababaclx‰‹‡„‡ŠŒ“˜‘ukigc`^]_YUPIHDGIMQV^fn{‡—§»ÐÚæçèåßÅ»·³š‰wkhgfeddegZQNLKILPVYeeefgilmlkkmx€‘¦¸ÉÌßäååååäæçæåãßǯ”wfMD><:89877689:;:@@A;;974349>?811=DGFC@90'!!(.*)**-9R]`aaa`b___^^^`ba`VUIB91.04677645556665644434756776865556666656766676668777887889978899:999999:<<;::;;;<=?>??=>>>??@>>?A@@ACAAACBCBCBDBDBAACCDCBBCDEDEDEDFEDDDDEDDDDEFEEEEEEDDDDEFEEDDD__^\]]YUUSRQRSSQQRTUWZZXY^^]\[[\ZXXYWWUUUUUTTTUUUTSSSQUTWVVVWXXWVVYWVWYYXXY[[[YXWWWWXXWVVVWWWWWYXXWWWY]rOAJ[csz|zoRC=;:;7455AScomjn¤¨ºªÍš|]RLU`‹ÈÜæËž‘”š¨®³¹Êéôøùöîdz¡¨«¾ÑÞàìñôóòðßÊ×çéêïôõòðñôøûýþÿÿÿÿÿÿÿÿþ÷Þr{¥Î£`uÖèâªtëÿÿÿÿøâ˜Vyæýð°L?^²Ð°©ÉôÿÿÿÿÿÿÿÿÿÿÿÿÿüôèÁ„mhddehkv‚„Š…‡š‘ƒ‡~tgfbbbabho{‰‚‡ˆ‹Œ‹~wyzsoic`_^]TNIFGGJLSY`gs~ŽŸ±ÂÕæééåÏÎÙʯ‘‘‚|vsnjijf^XUQMKBTSZefgfjimprv{…’¡´ÂÛÎÏäææçèèçèéèçæåãá׳˜xZOB?:;;;<=>9::8879<:6432-02.*&#).///.-)&%0/,('-5BW^aa_______`\\\[[[XVQNJA<;;::9765665544445555555565676555666667777677777788997888878988999::::::;;=;;;;;<===>?>>>>>???@@??AAAACCAAABAA@@@AAABBBBABBDEDCDDDDDDDEDCDDDFGFEEEEEDDDEGFEDFEDb_^YYWUTTRPQRSRSSTUWY\^ZZ\\\\[]ZZXVVUUVUUUUUVTUTSRRSTTTUWVWWXYYZZYYW\\^]\Z]YYYYY[YZXXYZZZYYXXXYYXXWVV[_uP@DP`jzzˆz[E=<<::7736DRc{zx’±©¥¶Î¨¯u\UPWc¼Ðçëç´ÆÝîò÷÷øò浕£´ÍççâãíôøöõóêÉÂÄÄÍàîîòóôõøûýÿÿÿÿÿÿÿÿÿþùãs_a„™ƒ²ÛÑǃ{áûýÿúè¦ccëþïžE0I”u`p—íÿÿÿÿÿÿÿÿÿÿÿÿÿþüøñÓ¬Žrjeddegmpu~ˆ“˜™˜—“Љš˜sebea`adir~‡‡‘Š›”“‰„„„…„se`]^]]WVOIHFHKPT\dmx‡—ªÂÖåèèßÜáß¹–”–œ¢–‹qnm`\ZYSOQWY\dfffgis|‹š©½ÂÄÃÒÝËÏâååçèééééçâååæçäݸŒiRD>@AB@?;70*()07DB@<<=/*% "#$'('%#)36+')/7EW`ab`c_``_^a\\\][\\\WYSRRPGB?<:8766554464555654456545756556666778:7889998:8:8789998788889::99<<;<;<=<;<;;<===>>>>=>?>@?>??@@AABBA?@AAA@@ACAAAABCCCBDCCCDEDDCCCDDCEDDEGFFEEFFEFCFFGEEEFE\YXUTRRQRRRSTUUUWXXXZ]ZYYZZYZZZXXWVUUUVTUUUTSSRRRRSSTSUVVVWX[\[[ZZYX[][[ZYYXX]YYYYZXYZZYYYYZXWYZYYXXY[_wO@AFPZiw~„^G><<<<:8566AM`vx‚€„¤É²–˜ƒxe[TZe¤Íìïé×ÈÅÁÙñö÷ø÷òëʰ°´Çßñôïìñ÷ûúø÷í¹±µ¯¢Ëíôõõøûýÿÿÿÿÿÿÿÿÿÿüé¤`\kyvou’œ’‡Åõûÿô»q\m³ðÿî“@59EKA;{çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôØÂœzqigfggjlu|„‰ˆ‹Š‰“˜¨‰xd`gebadhp€}„„‚ŠŽˆ†„‚…‡‹˜wf_[^_^^]ZRMIDGJNSZ`kv…“©½Óåèçåå弩 ±¼Éظ ~whea_^^_befhjknqz†£ÃÊÙßÝÕÎÖÙÕÐäåæçèêëêéæãÓäæèçÝ̹ºs^GA?;840-' *.9GGH>@4' !&&'$$)4/(',07EW___^]]]\\\\\]Z[Z[\ZXXVTSQQNJGB?;976455545566667765435554566888887776888877:8889<99899888:999:;;<<<;;;;;=>>=<=>>>>===>????@AAA?CA@@AA@AAAAABABBBBBBBBCCBCDCCDDDCDDDEEFEEFFDFEECFDDDEDFDVVVTSSTTSTUUVWWXZ\]Y[XWXYYYYYZ\YZXVUWVZTTTTQQQQRUUXUUUXXWWWY^^^\[[\\[YXYXYZWXYYYZZYZZ[ZY[[ZZXXY[]ZYZ[\_vOA?BHP[kwu^G>>@@@<:8546>I[q~„¤¬°ª«˜¥žšlTY`¤ÎëìæÄÁÄâíóôöóèÆ·±Í·ÑÚïôöõõøüûúùî˪²ªšˆ’ªêòõöùüýÿÿÿÿÿÿÿÿÿÿÿö輯Ïݧju†~a[»òûÿî—gf¬é÷ÿí‹C7550-4kæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷êß®†wkhfeddhq|~~ƒˆ“—››™‘{hhlid`dhtxxutx‡Š‰‰‹‹ŒŠ“ˆi`]a_^_`\YTPLGHIMRYajt‚”§¼Ôåæåáܺ¿ÊרäâÜİ –„wrruxwvutuv|ƒ ³ÛãååäåæåäåãææçèééêëêéæãÚââáÔÓª¤’}R?71.-,++&!!,/:ACC@;0! "$$'&)+,%&*15EX]_^^^\]\]\\\][\[Z\]Z\[\\ZVUVWNHB>;:766656666766666756565776667987799888877:888997778:::99::99:;=>;;::<;<>>>=<?@@@>>>>>>?@@BAABCAAABA@AAACACBCA@AABBBBCCCDEFDCCCDFEFFGEEFFFFGGFGDDEEDFDURTUTSUUUVWZ[[ZZ[]]XXVWVXWYXYYYYZXVUUUUTUSUOQRSSUUUUUVW[XVXZ^^]]\[[[ZXYZYYWUXYYYYZZZYYZYYZ\\\\\\^ZZ[[\auPA@@DFWckv]F?@ABA><;8654:AWt|‚Œ“¥¸«š ¥¢ƒjWW]zžÈåÜ»ªÁáêëî鯛ž®±µÏÎíò÷ööúýýýúóèËÉ»¢„¦èñôöùýþÿÿÿÿÿÿÿÿÿÿÿÿüõñðî㵞·Õ—Og¹ôûúßvU“åúþÿë†H>7/+/5kåýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüóæ¼–…rliefginty~ˆ•‘‘ŒŽ‹”}|~mcegjlkkjjrwwxyzzzyyqd`^^^^a^]]]ZWOIIIMRX^hr‚’¦»ÐåãÜÒ¶»ÛÔÜææã×ÉĶ¥‹„‡ ŸŸ¡ Ÿ ¤²ÄÌÒáéëèåæçäæçèèéêêêëëëêèãÒÍÖÙćk]PB5.)''*+--("%%'*,-...# $"$')+%"(,3FZ]^^]\Z][]]\[ZYZZZ[[[[Z[[ZYYXXTROIC@=;9855567766654555576667867777798888877:888::8:8889999<;:9:;>=;<=<=<;>=>@>>?>>>>>??????AA@BAAAAAAAAAAAABAAAAABCBCBCCDDEDCCCCEEDDEEDEFEEEFEDDDDDEDEDVTTUUVWXZZYYZ]`\\]]Z]WWXZXYY]ZZYZXWUVVVTTTUUTTTUUVXWWWWZYYZ[]]\[\ZYYYYZZ\YXYZZYYXYWXXZ[\^_``___^^[\^_bdwRBABDESba^ZE@AGFF@><;86437B[wƒ…‰œ¥²³¸˜qTT]{œ¿Ñ°•™Ÿµ¹ÌÚ·¬ ÀÓåôöùøøúýýýúöïÚØÙ¶–¦æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿýûûûöòñðíÓ˜…ÏúüôÓ]M²Øï÷ýèZmE.28;måýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøïа–wogfcdgkmw‹”‘‡Š‰Œ’•˜š€uieca`_``adgilnmkkjhc`^]]^^^]^^]_ZXSNLILOU\fr‘¦¼Ðåà˵µÈÕÛååæáÝÛÖ³’‹ˆ”®¯ÚÙ×ÝÜÕÖßæèëéççæßÛçéèèæäåæäâáàȯ˜„wjWE:3201-,+*.-1.+#!%! -%""%*(!%1I[^^_\\[]]`]][YXXY\\`__[]\\[]\]YYXXUPGA=:88877778654658676668767778798;8:788:9989888:988889:<::;<<<;=<<<<<>=?===?===???????@AA@@BBBAAAB@CAAABBDAABCBBCABBCCCDBBBCDCCEDGFGFEEEEEDDDEDEDFDUTVWXZ[\\][[[]\[ZYYXWUWZZYYXYWXWXWVUTTUTUTTSTTVWXXYYYYYZ[\\_[Z[[ZYYZZZZZ\\\\[ZZYZZZY[\^a_^___^^^^_acdegxSBBDEFTtfXNC@AEGFB?>==9654:A[€€‚ƒvލ¯²ª|£›\LUc—¡¯ 𥮲ºÊÀª“¤µÈåð÷úûúúúûüýýùôïâ×ÕÑ®˜´æïõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüóäáóþýó´UBrÁðûå}UVQLMHCqäüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøåÖ¦ujhhouvx…ˆ‘‘“—™”‹‹‰ƒ~uuvkcbaaacdeeefeeeeb`_]]^^_^`_^][\aXSNJLNT[fq’¥¹ÎäÖ¸¹ÀÉÍÓâäåßå˼—}|†•·ÀÇÍÜÞÞååãæêëêèæ×ÝæêèåÞÆ¼ÉÒ©‘~k\NE<60,)224630,'&$$# - %'#"0M[]_\Z[\^\_[\ZYXXY\\]]]Z[[\[\\\ZYXXYVRMHD?<:9876655555656776777777767777888::998:788999:9::::9:<;<<;<<====><<<=====>>=???>?@AB@@BBBABBA@AAAABBBBBBCABBA@BBBACBBACEDDDEFFFFFFEEFFECDEDDEEWXY\^``^_\\[Z\]YWWWWVW[YYZZXZVVVUTTUTUWUWWWVYYYZZ[\Z^\`_^[[[ZZZ[ZYY[\\^^^__^]\_]]___``cbb__aabbchhhiiijyTBCN\NUrkZHB?AEHNE@??=<98557C`‚„Šƒ£Ò©¥‡ª“_JQhŒŽ‰™¬¯«±Æáë³Ôâñõøúûûûúùúüüü÷ðîëãáÕÁ¿ÅéðõøûýþÿÿÿÿÿÿÿÿÿÿÿüúüþÿÿÿÿÿÿþüøóùÿþôÕWFAc‡ëúãyX‘`KZlXuÝðïíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùê¶‹{nozƒ‹…„‡˜¤œ¡‹‰‹‰Œ’•ƒsba_`bbb`__`___`___^\]]]]^^^]\\^][YRNMNTZdq’¦¼ÎàÈÀÀʼ³ÁÜâãÈ»§£whddirˆ›¦ÀÍãßãÙÕßäèéçåÛââãߨ·•Œ…s`M<40///.-*)54@770*" - - !(% 1P[]\ZZ`^^^^]^[\Z]^_\\\][[\\\^^`_`\^^^ZYSRLE@=;97755566656687677877877788:899;::99888:99::99:;:<<;<>??<>>>=?=>=?=>==>>=????CAAA@@BBBA@@@@BCCCCBDCCBBAAA@@BBBBDCDDDDGDEEEEFFGEEFFEFDCCDDGF[\^aab][[YZZZ[ZXUTWVWXYYZZWVVTVUUTUVVUVUWYYWYZZ[\\\[\[]]\ZZYYW[Z[Z[[\]^]___`_]__`bbaabaab_acdffefghijkjzUCGTVUapmgG?>?BGLMD@?==;:876>C\‚ƒ„…‹” ŸqŸ[K^u™~Œ¤ª°ÁÐÍÉËÎäõûûûûüûûùøùûüûóåâìíéã×ÑÝîñõøûþþÿÿÿÿÿÿÿÿÿÿÿúöùüþÿÿÿÿÿÿÿÿÿÿÿÿûãfYEƒæùáw[‚qp vTzÖÇ¡±çûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú랊x|…€†~ƒˆŒ’…|‡’•˜Ž‘†ƒwgkliedba`___``____```^]]_^_]\]^]][[VRPPTYer’¥ºÊÔÉ«–¤³¨œŽƒwkaY[]djr{”±¿Î÷±°Íãæã×ÏÍɽ±oWC;4-''(16@H@6.(+.01-'! - - - ''(!3Q[[\[Z^^`a_]\[\Y^[\\\[[[[[\\^^`]_[]^^ZYWWVNJGC?;855876656567787776798789977788999:97:::::99:::;<<<<<<<<<><<<==>==<====>?>>???>@@A@@@@@@ABDCBBBCBBBA@BA@?ACBBBABBDDDCDDEEFFEDFFEEEDCCDDFD_cjcd^[Z[Z^\^XWSRUWVZZZYXVUTTTVUXVUVVWWXZ[\[[\_^c_^_^^_\[[ZXYZ\\]^]\^]abc````accdbfaaa`abcbdeghinmljkkjxUIJYioxysjHA??AGKKGA@??=<;;::;BZn{Ž…|ˆ™š›]Rp Ênow‹¥ªÚÆÂÇÍãòöúûûûûúúúùúûûùîÙäîñòñîëìðòõùûþÿÿÿÿÿÿÿÿÿÿÿþõåôüþÿÿÿÿÿÿÿÿÿÿÿÿÿöç»šŽ˜íúáu\¦€ƒ|c€Õ²šv·îùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÒ°–ƒ~{{yyxwƒ•§š˜‹ˆƒƒ~}ztec`a`b`_```___^^^^^]_]^^]^]]]\\]XUTRWZeo€¥¿É×Äšs|~„†pjea[YWZY\_fo~Љ†‰ŸÅÙÙÖÀ”~ocSA3'" !#'-5>I`^X</$! #$" - - %)%!4SZ[\_[__cab^\[\Z^[]\_[ZZ^[\]]^`\^_``b[_[YXTTTNG@9668775555688886777887999:;899;:9:;9:9=:::>;;;>=?<=<<==>=<<<==@==<<=?>?>>>?????@B@@AE@CBBCDBCBDBCBABBAA?@BDCFAECDCEDDDEEFEEDFFEEEDDCCDEE`gc_]ZZZZZZZUOMLPVXWZYXWVTUTTTTUWWVVXYZ\^a__]]^^`_`a^]^\[Y[YZ[]^^^^^^]_abbcdddcccbabcdddeefhijkkllkjjkjw`TY`sˆƒxoM?BEHJPYJB@@?>==>?ACCBQar†}ytyž¯pZŽÑÄqfio€³¾ÅÒÐìó÷úûüûúùùúûûûûùëèëïòóôôîëîðöùüþÿÿÿÿÿÿÿÿÿÿþüíáîûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôíèçõüã{_·ÅÈÊyR‹ÛΩ‡ŒãóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöÞŦ‡…wtqpnmnqq~™‘•ކŒ‡‰‹”Ž‹viiffdb``c`a_^^]_b^___^]^]]]]\\\]]\ZXWX\fq€£¶¼ÆŸyutqnywsmibaa`_]\]]`deegiu’€r^L>1,(&$**3:@ELQTU\dL:+!! - - - - $#""6TZ[[[[^^^[[ZZYZY][[Z_[ZY[[\^]^]]\\]]]\\[ZZWUTSNH=76:9888667877767877777789879899988998:::::99:<<>;=<<?==<<<==<===;<<>===?>>@?????@@AAACBBBBABBBABAAAAAA@AAAAABBBCCDEDGEEEEEDEDEGECCCCCDD``_[YX\\\YYSMECLNY[YZYXVVVVVWXZW[YWZ^^^_a___aaa__`_^\]^\]\\\]]_``____^_dhffedeffedcfhijijkkknnolqlnjjkjnfkxy’¯“}rQFLQY[XULC@@C?=>AFPMH@@N]n{znužµ\°¶Ÿys_`ez«Ï¿¿Ûîô÷øúùøøùúûûûúøôðíìðóö÷ñÜéðöúüÿÿÿÿÿÿÿÿÿÿÿþùáÎÚíõûýÿþýþÿÿÿÿÿÿÿÿÿüú÷õúýꉂÃÜåØuS™à䮘Îèîòõúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûïä³~omfgddhm{Ÿ•ŒŒ”˜œ›š“’€yrpmfb_`a`a^^^^a`^]]_]]^^]]^\\]]\\[YXX[^hs€¢¶·Šxsmy‹“…uv{wqla\[YWUSPOUZVRH<2)#!( &,19>OXk`e\a^[O@3'!! - - - - $$!$<V[^[]^]]_Z\YZZZX\\]^_[[[]\\]^^_]]\^^^]]]]\]YZUTKC649;;:9977777;7777787879887989999:9;;;:99<;=;<;>;==<==<<<>>==>=@=<=?<?@?>=>?@>???A@@ACBCBDAABBBCAAAAABBBBBABBCCBCCDCCCFIGHEFDDEFCDCECCD[[ZYWUVVUSMFNBJOTZYYYYXUWWX[ZYXX\^_a__`aa__`bb`_^\]]\Y]\^__^_`abbbbbbcfiihhhffffghiiiijllllllmmlpjlkjll{wˆ‚€‹•§ ‡pUKYdqƒjWLCA@A@@?CHS^SHC?JWj}wr‰¯q]¨š¤¤¢Ÿv__\oˆ¤¹ÆÕêîôö÷÷ö÷øùûûûúø÷÷ðéæðöøîÜçðöúüÿÿÿÿÿÿÿÿÿÿÿýúáϼÑë÷ûÿýûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþðºÔîóÝlS£Û̳zg¯Õ»°ÊðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúëŠunhgefgs|‚‰‡Ž“‘ŠŒxga]`a``________]^]]]]^^]\\[]]]^^^^]]`hqŸ²©•Ї•¬Ä¶©–žw~_]ZWJGC>8420*&)!#&)+2;>CFGLca_WROLE;4+" ! - !## (EX\][]]^ZZZZXXVZY][\\][[\]]^]]\[[][[Z^\]Z[[[YXXLB:1;CDDA>;888887777888778888798999999999999::;;;::;;<<=<;<<<<<==<====<<>>>=>>>?????@?@ACCBABAA@ABAAABAAAAA@BABBBABDDDBCDFFFFEEEDDECBBFDDCYYZXUTTSPNFJMRWW[YXZZ[\\[Z[Z[[^`abcdcadcd_bahb`]\ZY\[\^_b``accfbcdfhkinhlijgghiiklnllllnpnsnpnompppkkmnu{™®„s’¹£•v[\n}‰Žz[LCBBBA@@DIZ``RF=<J`qƒwth^Yž™®¸Òªde_^s†¤ÕÛçéíòõóðòóôöùüùöøúóÙÙëôõßËãðöúüÿÿÿÿÿÿÿÿÿÿÿþýöëØÏáí÷ÿ÷íòöúýþÿÿÿÿÿÿÿÿÿÿÿøñîíõôÒcV¶‘nNW“°†‚’¿íøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûòÖ¸š|rjgfegnsz‰‘’Ž’“–› ¢¢¡“Œlb_a`_``_]_____]]^]]]\[[]]\\^_^]\\ZY[^gp}Š˜¦Ÿ¢¨¯¾áãÝ®”…zf\JD=73+)''&&'),06:@AGPWFDFIOTPKIE@71+( ## - - - - -"!&$&!!,M[]]^_]_Z\ZZXVWZ[][_]`\\\\]^]`\]]][\[^\\[`[^XVOE:5:GMSROG@=<<;977878777;8888799:9;:99:9::99:9;<;:<<<<<<<<>==<<<<<>=?<==?=?>>>??@??@@ACACCCBAA@@@@AAAADAABBBBBAACACCEDBCFDDDFEEEEEECBBEDDDXYWVVTUSSPSXZ[YZ[[[\\\[ZZZZZ]^_`cdcba`aab_`a_^]ZZZ]_`abbbbcccdedffhihihhihhhjlllnonnnnnnljmlmmmmmmmjkoo}© ˆqž¥Ž~idu•‹„^KCBFABAADHWd`\M@>>KZkzvh[U}œ³½Ð·›jeomj„§âÚÐçîòçââßÞî÷üøôøüõÛÊéòðÍÄáð÷úüþÿÿÿÿÿÿÿÿÿÿÿÿýúòäÜåòÿðßåíôúýÿÿÿÿÿÿÿÿÿÿÿÿÿýûý÷¼i]º±rEDN˜ÂwKf„ÙðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøæØ¬ˆ{mighjos…Š”–˜™š£”›•’‡ƒwic_``aa`^_a`a__`b``^]]_Z]]]\`[YXWUSTUY_fny„›Ÿ¯ªÏÑÚǯ‚k`WMD=1-)+'()-04;=@BDEEBFIMF>50258:85672-+)$#)# - - !#&%&" 1T\\^^\\[YZZXWXX[[[[]\\\\[\]\[[Z]\][\\]]\[[[YSJA:4=HMRRSRRKFD?=:9877777787888788889999999899:9;::;<<<;;<<<<<=<=?=<<==<====>>>=>>??@AAAAABCCBA@@@@@@@BAA?BAAAAAAABABBCCBDCDDCDDDCDDCCCBCCEEWWVWYYXY[^`\[ZZ\]^_`a]^[Z[\^``abfdd``abbf__ZYZZ\^`deffjihccddfhhjjjhliiijkmnnpsttrsoqmkkkknopmnmnnnkmnoz‡ ´ˆrŒ…€spzˆ‹ŽŒ`JCBAABEBBGTah_TE?<?JXitka]q˜ÒÒÖ²³ihvz„~¤ËÌéðóëçáÑÕéõú÷ó÷úôÜÛëðè¼®ãð÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿþýú÷òíõýñÚØÚèñùÿÿÿÿÿÿÿÿÿÿÿÿÿþýþùǑʷ^TZW—k`fÂÖéóûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøê½–ƒrlhhkp€ŸŸ§¤¤Ÿ˜„|vrlfb`aaba``a`^^^^aaa_]]]\YYXXYWUROMKMNQUY^djt‰’§«¤™vXJ;620.-,.037<ACEDCCCA><;77870'$$&+--.//.,3*)'((# - - - !$%&&&"! 6V]__]\ZYZYXWWZY`]]^__^]]]`\\[^]]\]\_]^^^\[XXM@529GOTSVXZXXRPHB=;:8877978788;7888999<::99999;:;:>;><;;;;==<<>>==><==<<?>=>>>>>>???AABA@@ACCBBBC???@@BBBBBAFAA@@@BABBBBBBCCEEEDCDFDDDDCDCEDXYZ[\\\\]_^]\[\^_a^]\\\\\\_aaaabbaa`aabb`\_V[\_bcfghhggghbefghhhjjjiijklmnooqqrrrpnlpkllmnoopmmmmmljkjnwƒˆ„|{}}€•„t}ŒŽ‡eJDAAAACCBEO[[_PHB?>>IT_pf]i¥¯®’fs¼€«tv•ÍãòöóñíÞÒêóùöòô÷óçåìè˨°äðøüýÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿúôøûòÖ¹¶ÀçôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüîØÀÃÅka\`¡¯kg–¬®©¹ÜíûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúíÊ©‘yqjlp}˜”œ‰„|xtnligdcbbabbab````^]]]^_]\\[XURONLJHIIIKMNRUX]aiklnmjgb]PC?333579<@DGJKLCEB>;8631/-,+*(# ()*'*++++*'$'# - - - -!#%&'(%"!!:W\^_]]YZUTSTTVY]\]^___]]]_[\Z[Y\\]\]]][\[YWMA8/9EKRSTWYZZXVUTLFB?=:9897767788899:9::;::9::<:9:;;;<;::;:::==<;<<<<<<=<>===>>>>>>??A@A@@@BCDAAA@>??@@BCBAAAAABBAAAAAABCCADCDDDDCCCCCBDCCCCCYZ\\\]^\__`^^^a`_^^]]^bccagddacaabcbabdaa``acdegllkkkggghiijkjjjnllnnopprrwwwrtpnmkloqrqvswponnmlmllljju}…–„ƒ…‚zw”™ƒ„“’bJFFBCBBBBDIPTQLGEA><>EL[mc^lz‹³{o}ʱ§ªlho—ÌÙíðòîêíñóõôòîíîðñêÏ´®µêòøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúúùï̸²Íéöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôõòå€_R^¨©l‚ÚèÜ’cŒÑãøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÜÁŸ€upsŒ–ƒ|uolheccbaabb_bbbba___^^__\[ZYTRPMLKKHKHIJIIKMMOQSRQOMKHEB@>><;;;=AFILJIHHEA?<9642/,*(&%##" &&'%'(+**)%$&" - -&')*((%"!$?[]__^\ZYRQRSTUY]^caaaa_`__Z\]]Y`]_^_^`_`[ZQE:25CMRTUX[[[[\[ZXVURJC?<:98777778;999:::;<::::::99:::<;::;<<;=<;;;<=<;===A=?>??>>?>>?@@B@EDDDDB@@?>@@C@AAAACAAACAAAAAEBBCGCDDFECCCCEDDCDCBCGCZ[]_^^^]_```^^_Z_^____aabbcdcaaabcefcbbabbcegjjhjijijhjkkkkljjijmnoppqrtsttrunnmmknoqsrrrprpnnlmlkheddhty|†€†Š‰‚Œ‹Š¦šŒ€cQGDBCCCBBCFILMIFECA@?>@DUmd\W[Êp‚Æ®®©¡i]bh†¦ÈçìæäìôóñòñÝ×ßñò踧¡¿îóùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøíÔÅÍäíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíhX_µl›èòä†QLzµÙ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùçÚ°‚v{~zyunljigedecbab``caaa`_``b]_\YTQOLKJIHIIKLKKLOLNMMJFB?<987<9;=BGHJMOONILIFB=:7410/-)(&%$#"! "#$"$$$$$% ,$%)!"'!&)**)(%!!'H^]aab_[ZPMGHKRY\^abb_a_^[ZZ[ZZZ\]^[_^^\\[SIG<?BMUVWY[ZY[]][ZYYXTTMHD?=<978888999999989:9:::::9:;;:::9::;;<<<=<<<<<<====?>>????>>??AA@AAABBB@@@>@@@@@@AAAABBC@BBBBCBBBDDDDDFGHCCDDCBBCBCCC\^b`___`ccc`^^^^__abdccbhbbbbabbddedccbceffimjkhkhhjnnnnolkkilqqprtuutwvvtvttonoqtxsusrrutuoojjheefeeghrtx“”™ƒ‡ŒŒ‹‹ ¨”‚jVGDDCCDBBBDEGGFGHFCCB>;<DYqdXSq‡›u†ÄŸ³¹£ma[\b|•¸Ü¾µÔîððóñåÈÖìñ麟¥ÌîôøüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïéÞáæïöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøç©t‹Ãm©íòá}HENu§ÏòùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøëÄ£Žzynlgeeeedccdccbb`abaa```_^ZWTROLKKJHIIHJKLOTINKIEA?<;:::<>@AEIKMMLMMKHFDA<952/,*)(('%#"!!!! ! "# ! - -,F:**6=C%-%'.00.*(%!!+O]_agcd_\TLBCFPW\^bbf`a^]]_Z]ZZ[]\^^_^b^`ZPKILRRZ[_`aa`\\]_\ZZ\[XXZSQHB>;:9889<;;999989:99;9999:;<;::;;<=<==<<>=?======>@?AAB??????@@@AABBBB@@A@@@?@BACAAACCDBBBBBDCBCEDEDCEEDBCDCCCBABCDD`aaaa`bcbcba`_`bcefgcbbcbbbabcccdefdddeefihjjihgmikklkmmmmmmopqrsvvuussstrrourttvvursqqpomljhffdeehhjno{pjop•™‚‡Š|l|š£ Œp[HDCCBBBCEDDEGFNOPNLIC?<?CSl`R\e^ZƱÓÁ©ŽlUZ_p€š¦±»ØíïôöîÏÆÜñíĪÒîôøüþÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüñâ¿ÂÌßòüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúê¿´›qµðòævf…^HfŒÄóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÕº‚xnkifeefcddgddc`bc`]]]WTRPOKKJJJKLLMNNKOOOLGC==8;9<>@BGJOQRSRRJIFCA@;8530.,*)'&&%$##"""!! !! ! *GHJ51Jf@'2-# %/=A;4+&$!/S\`aabcc^WM<=>IT[\aab``__`_[\[\\\[\]^^b^\WSPRRUWZ[]]^_____]]\\\\ZXYUTPKGD?<::99999::989::99;::::::::::;<<:;<;;<===<<==>????>?????@@@@@@AABBB@?@??@@@@ABABBBBDDBBBBCCCDDCDDDFEBBDCBCCBBBCDEccccfccddccdeegeedhecbfcccdeefgeeefffffghigghigjnmqlllmmnnspqswutuvuurqsvssuwu}utttuvpqqpjhgfedegkqrturzmhhhv…œŠtfjб®£eODCCAABCCCCCDGQ^]\WRKE@=<BUjXRUNT|ϯÖо hYZ\jz“¶¶ÉîòöùôÕÎÑïð׺À×ðõøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøï×ÁÅËâêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóóè¯x¾òïÛspriKImŸíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷æÕ«Š{nkhgffcdcdddbad^\VSQONLLJKKMOONOOLMIGGFEA@??@ABEIJKIQHQNLIECA?;8641/,+)('&&%$$#$##!"""!!! "$:WjP<Hfh@%"/A?5(#).-)'*=WWY6+&#!4X]dababaaYOB:=CUY\bce`a`b__^^]__^]a^^_b^[YZWVX[[\\^]]^^^^^]^]^_\\ZZ[\WYVSIB>;;:9899:98<<;9:::;;::::::::;<;:;:;><;<??>>??????B@@??>>??@@A@ADBAAA@?@BAA@DACCBBCCCBCBCCFDECCCEEDBACCBBBDBCDCEfgededdedcdedbbbbbbbccdccccbdddeeeghhhjkkljiknnonnnmmlnppoqqrstusrsrrrrsvuvwvvwstttvsomkiffeeeikmqppqqrzkWZ^djkl|ydkzš¿«˜{XICBAAABBCCCCHUeuj`YRMFA>?@KWY_YS|××ØÂ¬§®¢k[[ajv“ºÅíôøûöãÉíóîçØâñõùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôéסÀëûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøßÆËôíÄo~¶nVWZ‰èþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷꺕…tnjhfffcbdd`^[WSOMLNLMMOPQRR`POLJFCF?CBBCDEHLNONMKJIGEC@=:8531/-+*)('&&&%%$$$$$$$$$$$#!!! " &2?5%$1Oo`SNaek>-;SYjD*0<FPA/7LSWI5("#:Y^_____]\ZRKIGMWXYZZ[[^___`^^^___^__`ab]\[ZZ[[[[[[[Y[[[ZZ[^]^^]\\\[[\XWUSOLID@>;:9::989999989999:<;;::::<<;<;;;<<<>@>@>>?>@??????=>???@A@@A@@@@@@??@AAAABBBBBBBBBBCCDDDCCCDCCBBBAABBBBCDCFkggghhhfgcefhbbbbbccecgdcccdeddddfijkjmklnpqrnonnnppppqqtqstwsrrqqtssuvwvwvuvwtxtrqpljgeefjlorttqtoqonokSRZlh]eu‰yvxˆŸ°§ŸmQIBBBAABCCBBHWpzwc]\[NE@=;>J^bph€ÑÑź°ÁËȾ^cgjrˆÓîõûøìÅÄîø÷öôòôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷é˰¨×õûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôèóûî¬lŽ–msšp{åúöóøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùíɧ‘zrkiffd`][XVTSPNMNOPRSTSSTTSPNHKFFGIJJJJKHMKKHEB?><;97630.,,+*)(''''&'%%&%%$#$$%'*,*&#!! " !##%(,18=(.D_`a=)7gd[SMdnR;?ZSgjT8=Qf]a7>KTPB0& %AZ^```__\[ZVVTUXYXYXYYZ\\\]a_cacbebcbebb]^]a\]\[[][ZY_\[YZ[^]_^\\]\^\[ZZXVTTQOHA=;;;::99999:9999:;;::::::<;:;;;>;=<==<>>>?>@?>>@>?>>???BA@@@@@@AA@@CCCBBAABBBCCDBBBCDEDCDDCECCBBBABBCDCBDCDfffffgfedccba```bcddccgdddddddfghiknlkmmnppnoooooqoooopqsqrtrnnnqquwxz{zzvttuxtrqpnkjhkfiknrrrpppooonmlpkIMWgg_[o¡ž v|‚’±§ƒiRIDCBBBBCBBJYo†veca]WLD@=>>HQ_q†Äšx™ÂÌçáᾤžoQMQw´ÔòûúòÍÃðúûüùö÷÷úýþÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúèÑÅÌñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýþÿïªkœdpxovÞîäÜæõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñ× ƒwmhfa]ZVURRRRRTXZ[VTQNNMNQMLLKIOKLKMKHECA@><;963210/.,+()))(('''&&&&&&&'&%%$'+7?@C0$"!!!!! "#$%),**8A@CGJVcQ9)(Dadh_P,:cbB,GfW?=NQTakJ8Mg_]L9=HIK4&")H\^_]^\_ZZZXWWXYZZYYZ[[[Z[[]\^``bbaabdcd^]]`\]]]Z[\[Y\[ZYZ\[[[[ZZ[[\\\^YYWVTRQPJFC@?<;:::9999:;:9:::::9:<;;;;;;;<====>>??>=>???>>>=>>>>@@@A@?@AAA@@@AAABAA@A@@@CCBBCDDECCCBDCCCCDBBBCCCBECCgedfjghdccb```^aceeedehggfefhikmmkknopqqppqnopqpqooooppqsqsqpnnqtv}|€z{||{{tvtqpomjjhjklrrxsuqoomlkkkkinjHKUvoeckœ£¨Šzwx—®›‰jRJEDBBACBBKYqzxjilvaQIDAA<<DNg„xq‰§ÜÖÕݽµ®«zJ7:Ix§ÒðôñØÅðúûüùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùóôöûÿÿÿÿÿýúôîòöøúöòøþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿó³‹sZx®msÚܨ€¦ßôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü÷çÔ®‹yie_]ZXVUUVWWWYdYWUPOOONNMKJJIJJIHD@>;:86420.--,+*))('''''()&&&&&%&''(*+)'&+;OZgQ7&"!"""!!#*05:C<6=X]aholijlB/=aSJZpO/=`[.%BgV:CWb`^S?:ahJA:3355/'!.N^_c_^\_\\ZZ[\[``````_]][[\^\]^_acaabdde^a_`\^]]\\\\[[[ZZZYYZZZ[ZZZ\]]ZYYYWWVVUUTRJC?=<::9999;;::::::<::;::;:::<<>=@=@??@>>>?>>>==<<=>>@??@@?@@A@?@A@?AABBBC@BACBBBCCDCCCFCDCCBADDBBBACABCDgeeeeedddbc`a`bcfigeffgggghjkllmmnoonqooolrnopooonoprsrnsppopqsxyyyyzzzxwwutsqqnollllooppsrqpnnommlllkmqiGIQgsmi— ¸°£Šxz‰ž¶¦nRKFDCCECBIUcpnmnpvfVKGEB?>=CKd|kds¬ÕÓÜ»ÎÈÔŠPA3>Kq¡Ðíñéæñùûüùõööúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷翵ÔöÿÿÿÿÿÿÿþýüûøéÑÕðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Ð fWŠisϨi]]¡êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúòå¼}mgba`^\\ZYWUYXXTTONLJIHHJFCA@=;:964210/-,+*))(('''''''''&&&&&&&'&)-4;==3+:Q`sjd:'()-1-('':NQTX\FK]jkkjdfikG@RXIDT^L;DVP(#OgH7Oma_QG69]jP4.*40+# 2T^_`_^\^]\[\`\[_]]]^^^^][[\\\]]___`aaa`d^`__]]]]_][[Z[[[ZYYZ\[ZZXZZ\ZZXXXYWXXWVWXVUPKG@?=;999;;::;<;:<::;;;;:;;;<<;==>=?B>>>>>>>==<=>>>>>?>??A?@@@@@@@BAABBA@AABBBCCCDBBBBBBBBBBCCBBABCBABAhfedccddecddcdgihijgghihhhklopqnonsmkmnnnnsooonoopqsvssrqqqrxz{xxywx{{{wxtsrqppoomrooosrqqrnlmmmkmpqqpsqhFGL^trk„’œ¶¡›Š‰–°Å©‘pWNGEDDCDGQWansvvwh\QJHGCA>=@GR\dou¬Æ½¨ÈÂÛ•z`G7?Mx²Òêêèò÷úýùöö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûì²|Ž·òÿÿÿþýþÿÿþþýóÕÁ¾äíõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûõÛЃYy†lu£„^Wb{Ç÷÷öôñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøëœ‰vqnjfb][ZWRTZTQNKFCBBA?><;975430.--,**)(''''''''&&&''''&&&&&'''',<LWbSA>F^rtuc=./;HJI9,3O^mWdNFWnaZdr\SgjHCVe@7YhE7LhB#*Yd@CbebM?3(3Xb]:,,4-(7X^_b`^_b^___^][^]^_c_a_]\\\\\a_c^^_ccbcd``_`^^]]_`\^]^\\Z[Y[\_\]]][\YYZ^XYW[Y[Z^^^`c[TKEA?;;::::;;<=;<;<;;<<;<<><<<==@=??>>>>@>A=>=?>@??>?@AAA@@@@@BBBABAAAA@@BAACCCCCBDBBAABCBBBCBDABBBBCCfdfbccccddddffhiklkhhhggiilnpqpnnmmjlnpspoqpqrqppqruurrrttuvxz{yyyxwytutspppooonolnmmmnlnnnkkknmnpqrrrrveDEHWny‡~„…}š •™§´¾®•w\OGEEDEGNSZlyvsqkbUPLJIFC@>BAKU\er~•ÃÈÊÀÚ¾”cF=GPu¥Àêðóöúþúö÷÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÌ†om¥îþÿÿýûýÿÿÿÿÿîϹÉÒÙìùýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøß©•œy]¶x]ƒš„¬éêÒÄÖñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùì˲ŸŽ…{smdYVVNMLKIGB=;9985320//.-,+*))('''''''''''('&&'')'())(''&''3O^nlnUFWoruv_?3BUZk\R<8VodQG>CabY\_`PPeeFNfa;D__>A\d:65a`=BeeN:;,.1LhcB/-/+#;[]`a__`b_^^_[[Z\\]^^^__^^^^^]^^_]]]]]]]__`]]]]]^]]\]\\\[Z[[[[\[\[]\[XYZZXXXYYZZ^_ceee^[SKF>=;;:;<<<;<<;;;<>;:;<>==?=<=>>>>=>>>====<===>>>?A??@@@@@@@?@@CBAAAA@BA@BFCABBBBBCAABBBABBCABABCCBeefedegeddiejjjjllkihhgjlnsopnmmlllmpprqqqtvvwxwusssuuzzzvxxx{z~}}xxssrronnqnpoppolkkmkmmlklmooprusvrrubDCGO_m~…Љuw†“¤¤ ž¦®ºÐ²y[OGFEEHKOWjuyqnnmaURQNKFB@><>EKU\]dƒ‘´½¬ÁņWKFHNl‰Àèð÷úüùõö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ¾tdjšîþþÿûøûÿÿÿÿÿõãìíä½Õëòöûÿþüþÿÿÿÿÿÿÿÿÿÿÿÿÿüúàͯwe¶rmŦx“ÛšŒ“ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùïÜ̾µ–„}r^PJFDCA?=:5210.-,*)((((('''&&&&'&&&('&&''('&'''(+./-*(&'):Zrtwqeehnyuw_@8[Sdhr]A>`o^C13BeiQHbfLP_^JR`]JLbW=@bb6*AjV:GhgE=::ICHhi?2.1' #B]acc_`aabb`_]]]`]_]]^_`aacab```aa`^_``\]^`]_^_^_]]]a]\\]]]\^]]\^^]][[[Z[ZYYZ[]^adiilhhedZSID@><;<=<;<<<<<<<<<<<>>>??===@>>=@>>========>??@??@@@@@@@@@AABBBACA@ABBBBBBAABBBAABDCCABCCABBBCCBgggghihgfdffijjkklkjkklpqspnolllnnopqrrrssvxuvwytsssvxyvzwz{{z{yyxvutponnmnmmmmkkjnmmllkllllopsuttutsrswaGFGNUc„•Šqss‰µ©£¥¨¬²¼À°£yXNIGFHJOVepspprtucWUTNJFDA>><>BLY\ez¶´Õ½Ý¼·¢hKHGišÍíøúûøõööúþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòªhcj“îÿþþùôúÿÿÿÿÿüøûýðÔÊÎÛæöÿüùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöâ¢w¢»u{ÕËvÔžqM„çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøóíåЬ¡£†cMB>:7542/-+)))(&&%&&&&&&'(&%&&'&&&&'&%%%&'''*,6=AD;1+'(+>dqvvvmhkmqtu^?;IOXbieDDdpT4,,FhfHPg_JWmT@Sm_TYeD0Eh\3.TZLLIdiVS?BNPVgQ7-*'"'K_abc_aaa``______]]]^_acabaaa`aaaa_]][\\]]_]__^^^]]]^^]]\[[[\]]\^^^^\[[Z[[\]\]`behiijhhggb]YQHD@>;;;<<<<<;;;;;<;========?>>==?><<<<==<=>????????@@@@@AACCCAAAA@@BAAABABAAABAABBBBBBDCBBBBBBBikohjjllkiiinkklpppprsrrrponopqotsstux{vxvxwuttsstvwxz}v{{{{{z{zywvuupnnpnnpqmnjjjmnonommnprvsxttstssrty`IIFMRZjy¢ƒ{oœ§Ã°¨«°·½²Ÿr[RJIIINS`lvqqu‡„pb^YRMJGFB>;9:>JS[_}‘±´³µ¯Ë²˜cFBQÀàöøú÷õö÷úþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþñ gahíþýüøôøüþÿÿÿþüýÿøïÛ¾ÉÒàðððøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüóÞäßÒyyЪo€Ö^F}ç÷õóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùöðçä¡eI:520.,*('%%&&%%$$$$$%%&&&'%&&''&&&&&%%%%'').;IUb_[B/((-Bfquxvxvsqpqr[>9AORirdGHgnJ/',MicGRiZ[^jNOSinomQ6,NXX.2Oa?.@dgiN@CeijWA-$""-S`ebcabbbab`ecb__^^^a_cbbbbceeeba_^^\\]^`^``a`b^^^]]^_^^`[`]]]]]^_`]]]][_]^^`bgijknmoomkqjkd`WNGB?>>=<<<=>=<><=>===<<=?@>>A===>;<<<<=>>>????>@A?A@?@AAAAA@@BAAA@BBAACBCBDCCAABBAEBBCCBAAABBCjmojkjkjkllkkkklooosrrnnmnnnooqqrqstw{wvtsttssssuxvwy{yvxyyxxuuuvuuqpmnnpooromlhjkmnnnnortuvurtssssqrrtxeRLGLPW`m€‹ƒq{›§Ç»²ª®µÅŠzaTKJILQ\ivvwwŒlc_TNLJIHC@<8:;G]_cyµ½¼«šÁÞ±vL<GZŠÝñöù÷õö÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿýï¦hagŽíýüùöóöùüÿÿÿÿÿÿÿÿýòÚÌ÷¶¸äóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöñî݆r„‘m…×ÄcJ{ÛëááñýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóéÉ¢§˜eB1,)(&&%$$####$%##$$$%&&&&&&&(''&&%%%$$$%&*.>O\knseT6))/Fhqstttth\lqrX=3DR^oqbHMjlG,)/Tk`JXlVM]iD1FfglS<,&=R:&*H<,(1H\K;52JbSH0!3Wbcbbaabbbc`bbb`aa_^_^aabdcccddab`_`^]_``______]^[]]_\\\\\``^]]]__^]]Z[[_\^_chijjkkklmlkkkjigg[TOEB?>===<=<<<<<<=>=>>>??>=<<==><<;<=<=>????>?@A>??@BAAAAA@@A@?AA@?AAABBABBA@BDAAABBBBBBAABBBlmomklnnooopoorooopnmljmonqqvqrsvrxwwurrqrsttszwwxwxzz{vvw|wvwwvystpoopponoommonoooqstwxyvvtttttussssru{j\RIMPU\brŽŠ™~x£ÍÃ͝«·»¶²‰fVMJJOXfy}{“®²vg_VOMKKIFC@;97<L_\`|“§®µ¾ßÚŠT=;F]¤äòú÷óõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿüöß³k_fíýûøõôö÷ûÿÿÿÿÿÿÿÿÿúõä̼¦¿ãñúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûúìšjq\T•Ö®cgƒ©¥“ãúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøð彟™–Z;,&$$#""""#""#####%$%%'&%%%%%%%%%%$%%$$%%',<TbpkvuxZ<+*0Iksmipur`ZirpS<>T\fks]HQmkC*&2\d]Sg]NMaO516IYN>,!0+$!!$% $/1.(&798. #7\adcbaebbbccbbfbccccddfffcdccceada^_abbaca`_a`a]_]`__``_b`___`a^_]]^]\\\^^ccgjnkpmqorrssqqqmjhddaTJDA?==<<;;<<===>==?????????=>==<<<==>?AAABAA@???@ABA@ACAAAC@BA@@@A@@AABBAABBAABBEBBBCAAAABmmmnonpqppppqrpokikkllnponpprrrrrqrrssrrsstttt{zzxy|yxvtvvwvwxtsqooonmnmnoonnnnooprtvxwwxvvuuvuttssstsv}l_XQPPTY^cr„‰ƒ€‹œ¶ÂÄÆ¸®¯²´ÂÇ“hVKKLUbirz}º±‚m`WOMKKKHEC@=;;;HWWYn¡·áÌáç£oE<<PuÛíûöñôöúýþÿÿÿÿÿÿÿÿÿÿÿÿÿùßÄ‘l_f•ìýú÷ööõõúÿÿÿÿÿÿÿÿÿÿÿùéÞÌÈÛîôøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò¶jR@]Ù˜dª®~QsÌöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷îÔ±™‹xM4&$#""!!!!!""#####$$$%&&%'%%%%%%%%$$$$$%&'0L^sb^itu]<+*1LllfjnmqZQhroPFJ[ffiqXMTljA('3UjSINU@=<;)'/10( " %=\_aca`a`abbbbbdcbbbabcdcdcccccb`aa_^aaa`dca_^\]]^^`b_^^^_^_``__]^]]\\\\]^_bdilmkmlmmoopppppolljgdbWOJEA>=<;;<<<==>==>?>>===>>=>==<<<==??A@@@B>?>??AAA@@AB?@@@@@@@A@CAAAABBA@BDBBBABABAC@ACBBmmnnoqstvwzuxrplfhjkomrqqpsqtqqpppoqqtyuuvzyxx|{wxxvtrsxxytrqponoooomnmmoooqooqwwxy{x~xxxwuzzyyxuutxuyymb`[TLRX\`bkw…‘‰ˆ•±µÆÄ¸°®®³áØÐ“fVLKPZbkrz¦¶ŒtbWNMMNLJHFCB><9<FMJLu·ÁÄâéÊ[>9AeÈìû÷òõ÷úýþÿÿÿÿÿÿÿÿÿÿÿÿÿöÅkc`kîüúø÷öõõùüþÿÿÿÿÿÿÿÿÿüùê×ÔÉ×åïòöùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿøåžVYr¾ÝŒe¥ÞÂtHdÔöüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþú÷į̀’‡vF/$##!!!!!!!!"######$$%'%$$$%%%%%%%%%$%$$&'5Tk`OQ`tuU<**2OnudVbsiOQgsmLFWnj^lnRESnc?('/OIB@?6*('"'C\_a```bcccecechdcbfceeeeddfcdcccc`_`caaadba^_\^_bcca`_e^`___b``_c`_^_`aa`bfeponkmllmnoqptpqonormkgf^[OFA=<;<==========>??=<<=>>===><==B?@?A@C@>>??AAB@AABAA@A@??AA@AABBABBDCBBCCBBDBBBCAABCCnoopruvvwwutpllklkklmmosppqqqqqopqstvwwwwwyyyz{{zvuusppopponnnnnnmmlmmnmoqqpqprsx|zzzxxxxxwuyzywwuututyunaa^[TTUZb]YfyŽž”™ž«ÂÂÌ¿³¬«ÃßÖÊgXKNV[\esƒ”Ÿ˜zbVNMLMKKJHFDB@=<9<?BFgåëÞ§vT:B\Äëû÷óöøûýþÿÿþÿÿÿÿÿÿÿÿÿþó·h\Zap¨îüúùøöõõ÷øüÿÿÿÿÿÿÿÿÿÿÿü÷ìÒ¾Éßåìòöùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿøà¢…âß‚iÏìÜxJiÙ÷üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúöêÄ¢‘ƒk?-$##"""!!!"$""""###$$$$$$$$%$'%%%&%'%%$$%'2XQNECdseM6)*2PpteV[]`PSltkHGejkkmfLOPSZ5&&)/3.)'$"!)H]`````bababaabdddbccdeeeedcba^d__```baaab`a\]]`abbb``_a__^_^_^___`__`abaddefojjjmklooonnnnpoonomljgb_ZRIE?><;:<<===<===??>=<=>>=======>>@??@@@???>??@@@?@A@?????AAABACBBBCCBBBBBBBCBBBBAABCApquvxv}xytpnmnplnmqpponoppqrpqqrvy{zy{xxz{|{{zytrqonnnnnpnnnnoolmlmmnpstuw|zzzzyy{x}xxxzyyyywwvzttuyum`caeaYSY`\UWk”Ÿ˜“‘–ª¼ËÕ»«ª³ÉâѧŒn[OR[WZnyŒªž‚bUOOOOMLKJIIFFA?96869DcŽÆãÍÁ”pOD\¿ëûöñóôùýþÿþþÿÿÿÿÿÿÿÿÿÿôgWXap»ãóòðòõö÷÷øûþÿÿÿÿÿÿÿÿÿÿýüöìÓÀ¾ÄÕÙë÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöííîíÞ{oâðãviwéöûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö꿟’€a;*$$#""""!!""""""#####$#$$$##$%%%%%%%$%%$$%.AO?9JfffD0(*3RpteKObQHWpuiDF[rqnb`C6;B<,$""##! #,,,M]_aab`ebcbfcbcdegefdjfedeedaa^d_`adaca`ac``\_`hbeeebc`b_``a_aaa```a_ccefkkkinjmknlkmmmonmmqppppnollfgbaVMFB><<=<>=><<==>>>@@?><===>=?>@@@?A@@@@?A?>?A@@?@@A???CBCABABBCCCDDBCCDDECDBBBCACCDAqsuvvtsrqpokmpokmmqpponmprrsssuvwzyyyyzyy|yxwwvtroonmllmmlmmmllllklmoprsvvwx}zz}{xyvxxywxxyvwxwvuttttuuup`cc{–rV[a\WVXl„™°’–ªÇÈ˸¯±³Â׿țv`ORTWer‡œ|cVOOLMNLLMMKIHFB?;753>Jp¤¨©¤¡ƒk[aÆëúôçáë÷ýýþþÿÿÿÿÿÿÿÿÿÿÿõÀmXX^k”ÐʾËÙðö÷÷÷úýþÿÿÿÿÿÿÿÿÿÿÿÿüïÕ·ÃÂÅãôûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõâvußîÑle¥èõøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúö黓Y7)%$#"#"#!!""""""#####$##$$##$$%&$$$$$$%$$$)024ATjkP7-'*3SpsfH?AAE^ouY>@OV_VOC3.,+*'""""!! (2-$3R]^aaaabbbageeeddcagdffecdca__^``abdaa____]]]_aaa``bbc__^a_`^aa`^`aa`cdgjjkkjkijjkkllmkljmlmkllmmollgfca_XSMEB?><>=======>>>=>=====><?>?=>?@AA>@?????@?@@@@@@?>@@@@AAAABDCDECCCDDDCBABBBBCCCAxuyrppopsoolnnnoqpqqsqqqrsxxyvzzyyzyyyzyzzyxwsqpollmllompmnmlkkmmnqqrtwy|z~~}{{~{}yxx|wzy~vuuuvsuwtssuuqade•‘’d^^`VPPZt¡§•—«¾ÆÈ»±®°·¾ÉÉ£}`OPTZgžŒ{dVNNLLLMMMMNLLJHD@;66:DWx‰ŒŽ‘•pkmÍëùóãÒÈïüýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Öy[Z\^dvyxªçóøøùûüþÿÿÿÿÿÿÿÿÿÿÿÿÿúõðåɶÑíøÿÿÿþýþÿÿÿÿÿÿÿÿÿÿÿÿÿöã€{àé¥cÙççäåñûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõè·”R5($$#""""!"""""""$#$#$#"##$$#&$%%$$$%$%%$$$%'+6I_nW@2)&)4SqrhF858C]qbP:28>>:5/*&%%$$"!!!! !!#-44*"<X]_aacbbafdieggfcebgeffgcea`]^^bbcccaa__]]^abbadbbaddd`__`aa`aadafaabffjjmnmkmjljonnmnkmkollmmknmnlliihgdd_^RJDA?>=<==>==@=====>>>=B>@?@>>>>?A?@??>B?@@A@@??@A??@A@AAAABCDDGEDDEDDCBABCCBCDFAtqplkknpnkmlmlnopqqqrrrrrtttttuvwwxyxwwztpqrrpokllnpmlmlmkkjkkmoootvwy|~}|~}}|}~~||}{{ywuutrrowvwwvrtpuurbcd~ ›rgb[WOLUf~šš™•“—¨¿ÇÒ¿³³²ºÐÒß©x_PQS_px}{eXNMMLLMPNNMMLMKIGC@9;>Oeyƒ„zjotÂëùòÙ‡ èüýÿÿÿÿÿÿÿÿÿÿÿÿÿùêr_YPRWWWi‰áñøùúûüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôÛÍÊêöÿÿÿýúýÿÿÿÿÿÿÿÿÿÿÿÿÿ÷í–§áåb˜àÝ©€—Èóûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôç¶N2'$#""""""$"$#%"""""######$$$$$%&'((('%%$$&%&1?VoZH<0,'*6VptkF2//=ZTS>/-+,-+)(&%%$$$#""!! !!##&,' '.54."%B\]_aabaaaedhedbc```bdfec`a^]]]^bccdcaa^_\^_``aaccba``a^ada___a`aafbbdffjjkklkkijijihgjjkkkjjhhimklmkjjgggfca[UMIDA>><=====<==>><<<<====>>>>>>??@>>>???@A??=?AA@@AAAAAABBCCDDEDEDCBABBBCCCCCDBonmnnnmnmnpppqqpqtwr|vutqsssrrsuwvwwwustqpppponknmronmmmmlllmnooptz|~{€}~~~|~~‚~~{{{{yvolsqsxwuuvsssuuqccdr¦Çœ›x]SLKQ_q†›—°œ’’˜«ÂÊж®®²¿ÓÜÚ¢zcPPVcot{gZOJJKLMNNNNMNOOOPRQF<=H]xƒ‰uaTf¶èøïÎxtÉøûþÿÿÿÿÿÿÿÿÿÿÿÿüñÄ™wWNLOQTe§çõøúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïïñô÷ôñððøÿÿÿÿÿÿÿÿÿÿÿÿÿûöîØëâ„c°Ý¯l_]—áóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþøò嵚zK1&$##""#"""""""""##""######%$$$&*.461*&%$$$&+8Oh_MC:72)*5YnwdE0++6;<9.*(((''&&%$%%$$#"!!!!! !!'&5*##%0;8600#,376/%&H\baddd`aaedidbad^]]aekeccc`b]_`cdddca`^^^c`fbdddbbaaaddddcaa`a`cceejjjhkkmmokmjnklkigkillljiihhmklmjjnnmmmhihf[SLFAA>>=>==<?>;<<<>?>==>B==>>>>>@?@>???AAABAA@C@@ABBBBABDCECCCCDDBBBBBDCEEECBBmnmkmmoprtssrqrrsvtruvvtrsqortsrrrssqopqomnmmnnlllnmmmmonmnnopstvw{}z}}}~~}}~}~}ywvvroqlquuvwxvwvuustvzfcdq«¹«¨‡bVOIO\mx†¢« –—™«¿½º³µ´¯¼ØÓЫƒfPTZblmj]RMJKMLONNNNNOPU`xl]L?ERf|…“~_SU˜ãóé»xq½ñùýþÿÿÿÿÿÿÿÿÿÿÿþúðÌ \NORX_ÜòøùúüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûøöòäÒźãóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûøóà|fÅÉ\P_l¥ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ïÚ²˜‰uH/&%##""#$""""!!""##""""####$$%&-5CMKJ0'%$$$(0Gee[PFJU8+*2Pka[<.*)+.-,*(''('''&&%%%$$##"#"!! (5*%$ %/11000C9A3/%%/1441&(L\bacba`abcbcca`ZVZ]ceddbaa__^`bbbcba``^^`aaccc`bacbbbcdb`aaa``accefgfhhkimlnijjjjjjigkijiihfdigllllkjkkjiigfed`\XPHE@?>>==<<==<<=>>>>>??==>>>>>>=@>@??@@@@@A@@@@ACDCBBBBCCCCCBBBBBCCBBCCBCCBBlnnoquz|{wuvuttsussruuvrqsrprrqqqrronnnmmmomllnnmmonmmnoppqtwyyyzx‚~~ƒ~}‚€~~xvtpnkrzyyxzwwvwxxvzvvwkddr¯ÉÈÎh]QIMXkq{ˆ «²¥š–°Á·´º»®©¸ÈÐ݆eS[[cjj`TOLJKLNNNNNOOQ[i°…cODJVk…„cT^ƒÅåʦ„t¥êöýþþÿÿÿÿÿÿÿÿÿÿÿþúê bY[eršÇéûúùúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüûôÛϨ»ãòûýÿÿÿÿÿÿÿÿÿÿÿÿÿþüöá‚kÉÓwFM†hêüþÿÿÿÿÿÿÿÿÿÿÿÿÿþýöíÕ®˜„pE.%%$#####""#"!!""#"""""####$$&+6KcdfJ6(%%%$*<YjpcY_iY<+)/HJKA3+)(((((''('''&&&&%%$$$###""!! "!75/,**,6<=;23::93''19762' ,O\abfbabgbdbbbbXRTY`defcbaa``abbcbd`]^__`chggcbaebddcbdcaaabc`abeehgihjjjjlkniniiijjlllijjjhfehillnnmlmlkhggffieea]UMGB@?>====>>==?>?>====>>B>>>=>@>@?>???@AAAA@@BDDCCCBBCECDCBBDBBBAABCCCGCBBqrw|}~}zyvuurspussquqppprpnooqlnnnlkkljlmlmllmnmmonmmnppquxz|zxxy||~~~}~€€}{xtqpmqqu|{{{yyyz|{zxxwwxzjcev²»¶½¶rbXPPTcpw|ާ¬¾©˜›¡«´±ÙƵ§¤±ÊËÊ¥{op]^ckcWOLJJMPONORPPTb~Ÿ¨¦€cOCKUhvi]a}œ¢œ“މ”åôýþþþÿÿÿÿÿÿÿÿÿÿþýð©xk°Ìéóúÿüúúùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿ÷åÐÆÆÝñöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøè’•ÑÈgINUUtçùüÿÿÿÿÿÿÿÿÿÿÿÿþýüõëÏ—„jB,&%$$##$""""""""""""#""$%&(''+0Ga]\RG2'%%%&0F`xrlmmeZ7*(*494/,(((((('''''('((&&%%$#$$$$#""!! ")=J:5426???9//72.%*39754("2Uc`aaabccaba`_VNV\^dd``a`_``bbbaba`^^^_`bcdcccbadbcabbbbbabbcbbceghgihhiijijiiiiijjiihiihhhhhhhiijiijjjhgfffgggffecaYUNEC?>=====>=>>>>=>=>>>>>>=>?>=?>??@A@?@AAABDDDDCDFCCCCCCBABAAAAABDCCCDCB}|„„…}|zyxwttswqtttuuqnnoqonoormmlllllnkkkklpllllmpmnoqtxz}~~}|}~~}~}|}€€ƒƒ‚{{okmpt|~}‚€|zy|z}hceyªË·Â¼}ld[RR_lsw~œ°¾£œœ£ÆÑ³¥Ÿœ¬»ÃË™‡ƒc]^gkUMLJJPQRPOPSSYu˜¼Ü¶—y]JBHRcma\cx|‡‚……áòýþþÿÿÿÿÿþÿÿÿÿÿÿýóÂŒ«åîðõúüÿüúòéêêïôúÿÿÿÿÿÿÿÿÿþüþÿÿÿÿÿÿÿû÷åËÅÆÞï÷ÿþüüüþÿÿÿÿÿÿÿÿÿûõÑÀÁ»g?FQ]qåóóò÷ýþÿÿÿÿÿÿÿÿÿýüôê̬”…`?*%%####$"""$"!!$#"####$'+0561.7Zgi[JD.'%%%'6Toxzn{ieG1(''())(''&'''*((('()(''%%%%'%$%$%#"""#!!!!! #*4NKACECCDD98)%" -5:775*%:[```bbedcad^ZSNS`acdc_aaa^`bfdeed_a^`adffdgddcddededdb`bedeefeeeggjhjiijkiihhiiimjjiihkhggjjjhhijjlihjjhggihlimlmklgfc^QGBA>====@>>>>==>>>??>>>>?>>>A@@@C@@??@AADDDDECDEEBCDDCAABADAAADCCCCCCB~ƒ€||xxvurssspstsqrqmlounooprmmnlnmlmmljjkplmnnlmmrtvx{|}~}}}}||}~}yvsqks{}~zzyz{|€€ƒ„ƒ€€€~~~~ia^m¥º¸»ÂˆqhbYU^kptw{‹£¥¦™—¦Á¿²¨¨ ™ž¦²«·ÂZ\eaRMKJIKQTUSPQSf‡³ÛÙÅ©pSGBEJNSWcu|†x…’‡‰äðýþÿÿÿÿþþþÿÿÿÿÿÿþùëâèóùúüýþÿüù繟¯ÙêôÿÿÿÿÿÿÿÿÿýúüÿÿÿÿÿÿÿÿÿúóçÓÑèôÿüøùùüÿÿÿÿÿÿÿÿÿÿþ÷òì×l\wxz§âæØáè÷ýÿÿÿÿÿÿÿÿÿþüóèȪ“Y;(%$$####"""""""#"#$##$'1;HULE;=Zsl]TF8)&%%):`mwrn`VE5,&%%&('&'''''(((((((((''&%%%%&%%$$##"!"!"$')&" #%)+5HJEG<763/+&"/67788-!*F]_``ccba``\WTPW^`dbbb^^]_^acdbba^]__abddfcgddceccbdecbccdadcccdefgkhjijkjiihijjjjhihgfffgfgfighhjjlijjjihghijhlikkkihgdaUKGA?=<<?>>===>>>=?A?@?>>?????@>??@>?@AAABCCCCCBBABABCCCAABAAABBCECCCB€ƒ~||}wxxxxwtutytsnmmmloomorqsnommnppppqmnmpppppqsvy|}‚‚}~~}~}zmhsqw†}ƒywut{z‡‚ƒ‚‰…‡‚…†‡†‚l`[bй¼½Ã•™xc][`onqt|Œ£¡Ÿ™™œ«½¯ªªš‘¸²ÎÇ r[dcRNKJIGQY\]UPTp¡ÏÕÕ¿·˜†eK?>@@GOcq|†ˆ‘ˆÞîýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõøúúùúûýÿü÷àŽ†…—ªËî÷ÿÿÿÿÿÿþûùûýþÿÿÿÿþÿÿýúôïðñøÿöíóùüÿÿÿÿÿÿÿÿÿÿÿýúôà}g¡ÚáæèÕœ°æöÿÿÿÿÿÿÿÿÿýüòæÆ©•}T7'%%%$##""%###"%&%####%/=OchlS?<]hsplYE.'%&*>[ne[PF=0+'%$$%&&&(''''''((((('&&&'&&%%%&%%%$#""!"%-=2.,+$$&(*,3FFG=3/+'"#19?;;:6(1P^``aca``e[TORXaaccbab^__bbcchca^\]ccddfegehghdecedddcbgcdeedhcjfhhjjkilkkjjkkkkjjhjihhhhhghiihkjjillmjjjkimmmllkonpnrmmhgZPGB@>=?>>>>>??>>???@?>@@A@@??>>?A?DBCAABCBFDBBBBBAAA@@@@CAAABBBCCCBB}|{}wxxxwvvsrrqpmmmmlnnnopqolllnoppqnnmnnpqrrwyz{~‚}~€~~~}}||}}}|yxqknhpvw~|{vrwqw|ƒ‚‚ƒ‚………‚†‡ˆ…‡ƒ‚uj]_dn{¡Ä”’‚m`]_gnpsuy†š®¤¢œš¢¦§Íº§™’—žÂݾ‡qifRNKOSUXami`UUwÇÒÐË»½¡}ZCDHEAN_nЇ}‡‘‰Ììþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüú÷ùúüÿüöË€}Ž›Ãîÿÿÿÿÿþüúøùúýÿÿÿþþþÿÿÿþýûùüýíàìøüÿÿÿÿÿÿÿÿÿÿÿÿÿùè’m»Ùð÷íºb`s³ïÿÿÿÿÿÿÿÿþýüòåĪ™€Q5'&&$$$##"##$%)-/0($##,:Odcqk`=8K^bqnoL2'%%)6VMI@72,)&&%%%%%&%&'&&'('''((''(&&&&%&%%&&*/*%$##$+3:9;@0&-=1***-1/-' #/:>:;>8'7W^`aba__[WPRQYa`aa``_a^``aac__^]\]^cddcfdgcededcbdfffdcgddeededfdfgiihgigghjjkjjihgiefefegfghiihfjijikjjkjikklllmmmnllljhgbZRMGC@?==<<=?>>???>?>>???>@A?>??A>@@BAABBBDDDCBCBAA@@???@@@AAAACCBBB‚}~|||}~xwrppqpporpoprnoopomkknqoqprnmnpqswz|†„‚ƒ„‚ƒ‚~„}|{{{{{zxtmgnw{~z{vqmjqvy€ˆƒƒ€‚†„„ƒˆˆ‰‰Š„„|ye][`t—“‘•»‘zpcc`bmlnsy‚£ª¢–•š¤¾Ê¯¢˜“—·ÏÝ™‹‰‰[OMS`klmt|k[Vy¼ÝÕËÄæ“‚iVURKJP^n|‡ˆˆ‘“Ž»ëýþþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿþýúøùúüþùñË’††Žœ«Ö÷ûÿÿÿýûúùùúüÿÿÿýüüüþÿÿþýüýúáÉÖéñ÷ûÿÿÿÿÿÿÿÿÿÿÿüñ·wŠºÑïè¤aQn’ìúöò÷ýþÿþýüûñäĬ—‚N4&&%$$%#$###%,5DM<,$#%0Ide^inQ96>HOitnT2($%(.584,)(&$%&%'%%%%%'&&&''''''''(('&&&&'%%&).10+(%#(0:AEKC6'/88($##"!".7==<=</>X]ada___TMNVZbafa`_baa`ecdce^\]]]cchefcfffffggcdddeeeidgejhlefelddehhggkjojnklllgggiiifgfhgjikjjfkjkinkkkliolmlklmloopmjjlig^ZRHC@>>=>??>>>>>>>>??>>>B@?>A@B@C?BBAAABBBCBAABAB@B???@@@ABAABBBDB}|||}}~||wrqppoqppoqqqsqnopplkknooooopopqsv{ƒƒ‚‚‚€€€€€}~}{{|zytoqorzy|wusnols{|‚‚€€†‚ƒƒˆ‡‰‡ˆ„…~{ne[\[|¨†~´˜—–rbadhkmqtx† ¡¢Ÿ›–’¸··¯¥“¡¯¯§±ÁœgTNXpv~uxz|fYr°ÊàÑËĪ—•wZcfTX]py|xˆ“‘’²éüýþþþþÿÿÿÿþÿÿÿÿÿÿÿÿþýûùùùûýöíʤ‰‰—žÅî÷ÿÿÿüùúúùùüÿÿÿüúúùüÿÿÿÿÿýúÞ̸ÀÑë÷ÿÿÿÿÿÿÿÿÿÿÿÿúä¥n‡¢àܬifyŽÜòéâíøýÿþýûùðãÄ«–€K2&%%%%%#$$$%+6JbWI/%%'8\deeiXD86=BQ`jmJ0'$%%),+)('&%%%%%%%%%%&&%&&'&'(()('())())'&&&*-:F81.*+,9AEPFC0&+8-%!'3;;==9+BZ^c`_^]SJPU[_`a`^^]]^badbba_\\^^^abccfbfdccddddddeeecccedigfeeddcdcighgjiihhiihgeeeffhggffgihkjjgihjjmkjjkijjkkkkjjiiklkkjigda`VMGA@>>?>>>@>>>>??@>>?@?>>>????@??@@AAAACAAAB@??????AA@@ABAAAABA€}|€{xusqsqqruqrrrssrpnnopnrsspoppqtwyzƒ„„„‚†„‰€€ƒ€€ƒƒ‰}~}}|yworz|}zxwtsnqv|€~„‚ƒ‚…ƒ‚†„†…ˆˆ‰‰Œ„†~xrcZY`ily¯£¡–tjjdakmptx„Œ¶¨œ“›£¬¾·°•ŸªÆÅÀ†_V\q€|yz|~pfl¦¿ÏÒÔÙ³‘‚ms‹„h[[lkvxˆ”•“èüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿþþüùúúüýõìÕ²š“¤ºººÈìöÿÿÿýúúúúúüþþÿýúùùüÿÿÿÿÿþýöèÆªÂæöÿýûýÿüúýÿÿÿÿÿõ⯎²Ê½ºn†˜çëÅ£ÉëöþþýûùðãÁ«”|G1'&%%%%%&*-+/@bgjF1%%)?dpliYJ>:AYWYemW=,&%%$%&&&&&&%%%%%%$&%&&(&&&%&&((((((*+/3.)''*.9IC=>@-+1KJIFE6)$#$$!!-:=?>7-I^da^^^RKNZ]caf``^^]_`bbgbc_^[\_aafcdcedgdccedeeefjfededghihfggbacghiikilihghhifgeeglfigjjkjoijiijkjonnmmlnkmkojkijjkijlmlkijkkgh[QHB?>>>>@???????@@A@B>====?>A@A??@CCD@CCDBB@??@>??AAA@AAAADBBB~}}|}{vurrrrrtttqrrrprqqqnnooooppqqrtx{~€‚‚‚‚‚€€€€€~€€€|~~|{wwwwwvvvwvvvvx{||€‚€€‚…„„…††ˆˆŒ…„€}voe[\\cs¥«¥¥™„„¡magnqrvxŽ¢·©›‘Œ’𮯵©£™™›ÉÝÆ®ˆeelooqru€vjk›´Îý¾»¥„qt–‹o`gpuv‰”–“©èüýýýþþþÿÿÿÿÿÿÿÿÿÿÿþÿÿüúûûüýóéÛÅ ž±¶»ÂÊêõÿÿÿýúúúûûüüþÿýúùøûÿÿÿÿÿÿÿýøèǺçôþúöûÿúôúÿÿÿÿÿþøëãß˦º~m¬Ãç覓¥Åïüýþüúð㿨’vF2))(%$')/5649HfugC/%&+AjptePF?ADRllnXE4'%%%$%&%%%%&%%$%&$$%&%%%&&&&''''(((-57;93+'-4<JJDA?7/*667660+%!"! '7=??51N`^^\ZRKQV[]]^]\]^^]_abbbb_\[Z]_``cbccdcgccbcaeceffgfbdehhikfbbbddefijjjlhgfffheffgjhfihihiikijjkkkjlkmgiinkmijhkjjjiijmnjiiiihhgc\TMEB>???????>>>???>>=>@>===??@@????@@ABBAAA@@?=>>??A@BAA@AAAC~~}yxyxvtw||{zyyyqrtuuutwpmmposqqqqvx{€†‚ƒ‚‚ƒ„„ƒ†……‚„€‚€‚}~wvvvuy{~}}{{}|†ƒ…~ƒ†ƒ†„Ї‰†‰‰Ž„ƒ‚ƒ}xuf\W[l˜¢º°º¢™œ}hcdqputvƒ”©µ¡•Œƒ ®»¼¼©œ›ÐÏÏɽ“|refcelz‡okƒ¥ŸžÑ²©‡zwŒ•‰nabqr‹”—“¥æûüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûüýýþóéåç§¡¸âɲÑçóÿÿÿýûûüüüüüþÿýûùøúüþÿÿÿÿÿþýöïñóöùöôôôðíóùüÿÿÿÿþùõïÓ¨³uj•Þìé¶ˆŠ›æùûþüúðâ¾§’oH40/'#%,4:EVB;Kjvg@-$$+CjxuaRGIOOQZbSC5*%$%%%%%%%%&$$%$$$$$%%%%%%&&&)+)''),18<CE7-,3?JKKHF:1*(.5.*(&%!!" !5>A;35T^_]XPLP[]^^]\[[]]]^ccccb_\[\]a__`dbbbebhcccffedefggfchfkhhhffghhgjimmmmmhffeehfihhgffhhljlinmqmqkkjmkmikjnklillljiihijnokmllllllhkb\OFA@@??@@A@@@@@B??>B@@?>>??C@???@@ABBBAA@@@>?@@??AABBBAABAA{zzxyzzy{||yxxwuursutttrqlmlnopnsuy€~€‚€ƒ€€ƒƒ†ƒ‚€€}~~~{zwvvvuy|}~~||{}|‚„€€ƒ„ƒ„………††ˆ…†ƒ‚‚‚€~zth\[^x¥°¼¼½¾‘kbbjsttvx†™ž¦ š†‡—¨¾ÂĨ™ §µÞÍ̧€g[]_afproox…’§œ—“‰Œy}Šš€l\fj}••“¡åúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿýûýÿþþóèé쨟Áéäá×ßñÿÿÿþüüýýüüüýÿþüúøùùüÿÿÿÿÿÿÿþýüû÷óòñíÖ¸Ýíóùÿÿÿÿÿÿÿöå’xir…ãïëÍ¥~Ñöùýüúðã¼£‘qL=50)'(7JLPPPDLjvf?-$&+AjutfY\cF8<C?;2)'$$&%%%%%&%%%%$$$$$$$$$%%%&&,31.+).06<CNG;0.;JJOKH;1+&&&&%%%$#! " """ +=?62:V\[[WNRV[^]Z^[[[\\^_a`^]]ZZZ\^a````ab```bbceedddeedbcbgggggggfhhhhjjjjjihgfdedededefgghfhhhijklkjjjjjjjhigijlhiijjhghijnljjihffggffg`]SIFBBA@@@@??@@A@>>???A>?>??@???BAA@@AA@@AB@?@@??@@AAAABBB@|zzzz{}~}|€zyvuuvvvv{uuonllmnruw}€‚€ƒƒƒ„‚‚‡‡‡„ƒ€€}}~‚„~{yyxwuvxz€…„€‚ƒ…†‡‚„„‰„„„„…†‡ˆ„†‚…†‡„„„„~zf\Za{Œ©Ê²¡ª…ghderqww{…Ž¡¹šˆˆˆ‹˜²ÑƲ¢—”™·Õ×Öšq`X[[]gxiddi¤ˆnryuxƒ•¤•x]]`s’Ô÷ûþþÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþýþÿÿþòæèꙌÂîíìâÛîûýÿþþþþþþýýþÿÿþûùùùüÿÿÿÿÿÿÿÿþþýùöòîìηÏìöúÿÿÿÿÿÿÿúé¨cNFwàæèÏЗçúûýûúï亡ŽsN@;90+2CSYSeL@Ikvf@-&'/=[utrfhM:0,--)&$$$$&%$%&&%%&&%$$$$$#$$$$%%&*1;<50259=AILT>3/JIJ@830+&$$$##$##"! ! #(,.1/+%%;?21?Y\\ZWXZ[^\\Y^[^]accbe`^\[Z\[\`cceabaa`caddeefddeecbccdggfgighhllljlkljjhgfeehegefceglhieighhijokkimmmiiiihijlhlinighmjjkmhiijdd_\_aggdbVLGCA@@CA@@?@A@>?A???>>>???A>?@@@?@A@@@BBA@@@@@@@D@@ADCEA}|}~~~~{|zzxvuxzvusrplllqrux{€€€€‚€€€‚‚‚‚‚~}|}}~€|xwuvuyy{}}|~€€€}€ƒƒ„……ƒ„„ƒƒ„ƒ~€€„…‡…‡‰ˆƒ}si^__w¥¯±«²´œpfcaipvyy|‡™›œ—³–‚‹³È¾²¢’—©Áƹ—ogkaYi}ubchv”«e_fkiex‘›¥†dZQk”›Ïôúÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñäæãˆ‰Åóõöêßë÷ûÿÿÿÿÿÿÿþþÿÿÿÿýúúùüÿÿÿÿÿÿÿÿÿÿÿüùòêëिèöûÿÿÿÿÿÿÿýöÓ€EPpÚØÕÏ˺ÈðþýýûùïḞ‹jNBA>766Ee\RMHAEgvhC3+37?PashhN9.&'(&$$$$$%&%$%&&$%$$$$$$%$$%%%%'(,1<KB>@BFJJIGEB?//4952.+)&%$$%$#%!!!! "',/26===6.$ 90)2G[]][ZY[\[ZZY^[]]`aa`_]\[[[]]_aabb`b```a`dcddddddbacceffggfihijjikikmljiggffegcfegefhhfgfffhhiihgggiihhhffegjihhhighihhhgfedca\^[_behgeccYQLECAB?@A@@@@@@@?>>>>>>?>?>?>@B?@@@@@@@@?@@@@@@@@@@BBBA€…†‡ƒ†€~|}{zw|xurqonprv{~€‚ƒ„ˆƒ‚‚‚ƒ‡ƒƒ€€ƒƒƒ|wss}{{ƒ}{zxwutuy||}}~€‚€‚ƒ„ƒƒ„‡„…‚‚‚†‚‚|{~}}€„‡‹‹‘‘‘‹Š|j`\b–œµÈ¤vlkdfuswy~ˆ““–§¢‹ŽµÐÀ¸œˆ¤Æ»Ê”uogju{vdfs«zb^]\\]m‰™ž tYO^…ž™™¾ìõþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýðâæß•vÈóòñæÎâóùþÿÿÿÿýûôíîðõúúúúúüþÿÿÿÿÿÿÿÿÿÿþüøôòéÀ´Îéôüùöûÿÿÿþýä±€cœáÙÆÌßëïöþýýûùîÜ´›ˆdMDEI>99GZaOI?:BcmiM:;DDCEMRWG6*"$(&$$$$%%%%%%%%%$%$$$$$#$$%&'(*,/4?NHEGHJRIFFG?>3,+-,*(&&%%$$$$$#$!!!! +6;=@@@IDD9.!))%4O^`a\[Z][ZZ[Z^^aagac_^\]]]`baeaaababafbcbecbdccidbdfdihhgijiimkjimijknhhhighfhdfglfiggfhhlhjilhffjikhigkghffijjihihjfggfefd_]]^ceghnmlljiiaYOIDB@@@A@@@A@AAA?@>>>C?@???BA?@A@A@BAC?@@@@A@A@@@BACAƒ„„„…‚‚}}|||wtuvspqqsuz~€‚ƒƒ…ƒƒ‚‚ƒ‚‚|}}~}‚~|yuvmt{zyyyyxy{xwy|~}}}~€‚ƒ‚‚‚€‚ƒƒƒ‚€€€}~€~{€…‰ŠŒŽ‘Š…€vlaacs¢º¸·Ÿ–‚sjdjpvyz}„†’¬«—ŒŠž²Æ¾«ŒŠˆš¹»¿soonoi`o~„Š‚zpib\[Zhz£ƒ`DXw˜˜š§æñþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüîàæäµÌôðìâÉÚïöýþÿÿÿû÷èÙÉÞêõøûûûüýþÿÿÿÿÿÿÿÿÿÿÿÿþùñѺ²Çê÷òíöÿÿÿÿÿùßÈÌÚìçÐÊßòûüþýýûøí×°›„cJCHCA;:CYSG@78=`sk[RLWgE7551,'!#%&$#$#$%%$%%%%&$$$%&%%&&),,,37:>BLJFHNJJHGB>83/)('('''&%%%$$$"""!!! 46===?ADFD?6.%"#$7V]`^\ZWWWYZZ[^_````_]^[\]`bbbe```a`aaccccdbcfdcdefhfdhfhhhkjijjjiiiihhggfgeddddfikefggfghkgkjlhgghghgidjggggggffggfecdcbb]Y]\afgghhnlkiklkjc\SIFBA@?????>@@@A?=>>???@>>??????@@@@@?@?@@@@@AA@AAA@‹ŒŒ„†‚‚ƒƒƒ{xwutrrsw{ƒƒ„ƒ‰‡†‡Š…‡„„‚„€€|}|||||zvsuuwzywxxx{zyz{zz|€‚~€‚…„ƒ‚„ˆ„††‡‚€…††~‚†ŒŒŽ‘‰‰€{kb[_~§¦»¹º©ž”|pffstxz|ˆ£Á˜‘†¸Õ¿¸œ‰‚•¯±¢vtpifab„‡…€~}wm`]Z_m‡˜¨‘hAIi“™Ÿ¡ãïþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúàÎ×Þµ¾ÞöðêáÆÖîöþÿÿÿÿöêÀ“”œ¿æìò÷ýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúæÈ»«áëßÙôÿÿÿÿÿüùôïóøïßæêóüýþýýúøíÓ°›‡bIA=;=;;?JOE=639\r{nfjmZJ/%! !#$&%$%##%%%%%%%&%%$%&'*.19869?BMMKMMMNKH?;5.-,*('(''&%&'%$%$$%"""!!!!! +8=A@AACDE@@:6+$ !$:X]^]ZXXXX[\``dcb`_^^^^]cbhdfcda````abddedibddfffhmjkihhmhhijiiijkkhggifdfgdcdffgikfiijiijkglllikijghfiijiihhgggggkeddeda\X[acjijkkkpmmkonppohcTJDB@????@???@@>????@????@???@?C@@@@@B@?@B@ABBAAAA@І‡„…~}{{yysvuxz}ƒ„…†††…ˆ‹‡†‡……ƒƒƒƒ~}}}||{zywuvyzzwvwxxy{|{|}||€}€ƒ€ƒƒ‚ƒ„‚‚€‚€~~‚‚…„„„†ˆˆ‰ŒŒŒŒŠ…wl`_av…¿¾¿Â”ngcktxyzz“•’¤…“¦·Â¹´˜|xˆ™yr}labd€ˆ‹Ž‡ƒ‚zjgigg|›¦xEIZ‹››×îþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûôƼÁ¹´Æí÷ðèÚÇÒíöÿÿÿÿÿðÇ–ƒ{’®ÅÜôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøäÓËÐʼ×ñþÿÿÿÿÿÿÿÿÿÿöíêëôüýþýýú÷ìаœ‰aI=47=<;>DDD;556UptzceTJ8($!#&(%"%&&&%%$%%%%%%%%%$$).389;DMLJJIKPQSA8894/,*)''''''&%%%%%%%%###"!! !+48;<=ACE@??;9, $&=ZZ\\XXWXY[]abaa_]\]][]_abdddccaaba`bcebccdbddfdghhfghgfffghkiihihgfedeffhecddfgghkfhiihikhgijiihfgfeeefhhhfghedeec`_^]Za[_bfhijlnlkmlmlnnooolgbWMHBA@???>???????@??????????@@@@@?@@BAAAAAAB@@@@@AŠŠŠ„…€~{z|yyy{„…†‰ŽŒ‹ŒŠˆ‡‰‡‰ƒ„„ƒ€}}~}€xywvv{trqruwy~{{~}}}€€ƒƒ„‚†‚‚……„ƒ€€‚‚ƒ„†Š‰Š‹ŠŽŒ’“””–‘Šˆ€yja\avŠ Ä¼Ãµ°…omfiwuywx†‹‘¡š‹Š—¦¹³«„yroƒ‹spuvkfm€ƒ…Š‘†‚ˆ‡qqrspz‹ž£ˆTBO‚š¤žËíþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøìº¬±¬£¨ÐïîíàÏÞïöýþÿüøÜ¯‰{‰œŸ£³ÁÞöúÿÿÿÿÿÿÿÿÿÿÿþýýþþÿûøëÞÑ··Ýëôøýþÿÿÿÿÿÿÿúôôóøýþþýýú÷ëͯž‰`H;56==>>@B>:5/2OduiaN?6+'$%(/82+)((*.1*%$%&&&&&&%%'.:CFJFUKMIDGJD?93/-+)('&&&&&%%%$$$$$$$$$"#"" !"##"$"##*05::?BFBEDC95#"!')BYZ[[ZZZ\\^_caa_^\[\````bbfdjeeabaabfdecbbcbddfdkhgghhjfefhhlhfgjhfdbcffggfffdgghglhhikhhhhfjkkhffgfeefghghgkgdceca__\Z[_bghjmrmpoqpqnoororpronjh[OGB@???@??A@??>????????>>>@@A@B@@@DBBAAAB@@@@@BA……ƒ€€|z{|~€…ŽŽŽŒŒ‹Ž‹‰‰ˆˆŒˆ†…ƒƒ‚„€}{{yxwruvvvqnmmsxz{}~€~~~}}~€‚„€€€‚ƒƒ‚‚‚‚€ƒ„†‡‡‡‰Š‹‰Œ‹‘ŽŽ‹Š…wmc`^p‡£·®®·€kffmvxxvvx|‹´¢’“ ²ˆ|uoigqvx{ooy‚†ƒ‚€€…uxІ~†‰“¢‘ePFn™žžÈìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõç›Í·‡ˆ£ÏíòêâéðöûýÿøéËŸƒŒ¸Ïµ£«Èæöÿÿÿÿÿÿÿÿÿÿÿýúûüþÿÿÿüöé;ÀÊÛòúýÿÿÿÿÿÿÿýüûûýþþþþýùöëÈ®Ÿ‡^F;77<?C>?<974--A^VQD60(((.39A><73326>:/)&(*-001,&'*9OMMCBBACF9433/,*''&&%%%%%%$$$$#######"!! !####%$####""!"#(,-.4<;:993.)"##',HYXYZZZ[[\_````]\\]]^]_```baaba`babcdccccccbdcdeghgggggeefhhgedcbafbcdfgfffeedgfgfffijjggefffggeeeefeefggghffccbba\X`\^`dhijkmomoopoooonpmroppomjg]TLDA>????@@@@@@??@@?=?>>??>?>?@A@AABAA@A?@@@@@@‡‚€€€€~|‚†‘’““‘‘Œˆ‹‡††‡„ƒ‚‚„~|{wuvuswsrmknorx{~~~ƒ‚…~„€‚„‚€€€€‚‡…ˆ„ƒƒƒƒŠŠŠŠ‡ˆŠ‹‹‹Ž‘ŽŽŽ•ŠŠ‚}nbZ^u…›¢³¾§°ymojjurtssu}™°ª—ŒŒ‘ °‰‰†smos{{qoxƒ†…‚}ƒ}€¡‘—’‘šœyWGa—ª ÆìþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÓ|€§ÂŠk}©ÓìêéìðñóùÿòÞ»œš¶ÞÓͯŸ´ÛëúýÿÿÿÿÿÿÿÿÿüùùúüÿÿÿýüöðÚ½ÆÄëóôôúÿÿþÿÿþýýýþÿÿþþýùõêÆŸˆ]F=88;C@><8860*(09<8.*%$&+8?NLKHC@<=?BM7.-/7@A@?2'(2EJKA9<@620,*))'&$%$$$%$%$$$$##%######! !!""%$$$$$$$$$$###"!"%&'*/220,(&""#$'0MXXZ__``ecc`aa`]`\]^a^gbdcc`ba``cafdedgccccdhcfdffgggggcdfmhfcb`^`ebdfkhifgfifgfggggjjogmegffghdegjefefgjglfea``a]XZ_`fgijoknnrmooqpuopooprprtwrqmmcYMEA@????@A@?@??A@??@?@?A????@CAAACABBB@@??@@@~€€€~€€ƒ†‹‘““‘“‘ŒŒˆ†‚ƒƒ‚‚ƒ~~}{|vwysqonriotuwy|}~~}~~€€€ƒ€€€€‚€~€€‚ƒƒ„…„„„…†‡ŠŠŒˆˆŒŠŠ‹‹ŒŠŒŒŽŽŽŽ‹‡‚|na_^p’£¹¾¯«„w‹vgkuvsrsy…˜¥«ž‹Œ‰°—~}€„ˆ…‡xhksƒƒtuy€~ƒ’›••“”–iC[’¬ŸÎìýþÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþí©ioŒ˜fW·åëïïïãæõÿìIJ±±¯Èȱ¶Ãª™±åõúÿÿÿÿÿÿÿÿþú÷÷øûÿÿÿÿÿÿþõÛÊÉÝê×Ýôÿÿþÿÿÿÿÿÿÿÿÿþþýøó鯬Ÿˆ^F>8559>:82.+)$ %)))'$#!'.8ELTQQMIHHECB==<?KPUST4)/9FE=41.,*(''&&&%%$%$$$$$###"""#"""""!!! !""$##$$$$%&%%$$$$$%%$$$%$%%&'&%%$$#$$)5OWYZ``aaa`a``^^\\]_`a^^^____`baacafcdddcccddccddgfhhheb_effgfa]X_bbcfghggfggggffhgijigohgehbcdgcehiegfggjgfbaa^\_[]^acgjjkkkllllomnnnnpopqqqstvrqnnic[RHD@@>??AA@>????>>???>?>??@@AAAAAAAAA@?>>@?A€…‚‚ƒ†‰—‘‘‘•”˜‘‘‘‹‹Œ‡„‚€€ƒ}|||z}}|tqkhoqu{y~z{|€~€}~}}~€€€‚‚‚€€€ƒ„…ƒƒƒ„…Š……†ˆ‹‹Œˆˆ‰‹‹ŒŒŒŒ‘ŒŒ‘Ž‘‘‘„|lbZ^}¢¤Â¾ÄŠt‹‰ogiuqrsqw~¢³½‰†…†”±¶™‘’‘‰…jbn~„„jkq…’£™›˜”“¡™†VV„¯ ×îýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýê\Yrc^j¦ÔçïêææÜðÿí·Êá˵¿Î¨§ÆÇª›Äèóÿÿÿÿÿÿþþýú÷÷÷ûþÿÿÿÿÿÿúöçÞæéÌÉíöøûýÿÿÿÿÿÿÿÿþþýøóèÆ«ž†^G?910036/+&" !$##$%*29AKOUWVUTKMEBDEGKM]PME635@G90,(&&%%$$$$%##$$$$##"""!!!"!!!!!! !!"$#%%%%%%%%&&$%&%%$%%$$$%%##""""##$$%%$+:QZ\\aceefcd`_``]_`cbb`a^^_a``bcccbfffeeefffccccehhjjjdaaggghi_Z\`deejiihhghhjgfehimjigohffhbbbfdegiffgihjfe`^][\_adceglkoopkjkqpppppqpoprqwtttwsuqsonhfUJB@?B?ABA?>?@@A>A??@@??@CABAAABAAB@@?>>>?@€…†ˆŒŽ‘‘ŽŠŠ‰‰†ƒ~‚€~}}~€|{{zxurokrjqzz{z{{z{||{{{|yyz|}}}~}€ƒ…„ƒ‚‚‰…†††‡Š‡ˆ‡ˆ†‹…††††ˆ‰‹‹‹‹‘‘ŽŽˆ€xnaabn~™ÃމŒˆqhflpturrv}“¹¹²˜†ƒ²¯«£˜˜›žž‡qihrunadi„ŠŽ’˜¡˜Œ’Ÿ‰hpw”¡æðýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ãwRT\oxsoŠ ÊãîåÄœ¹ëÿîÞæêÛ±´¼¨¼áÜ»«ªÈìÿÿÿÿÿþýýüùö÷÷úýþÿÿÿÿÿÿÿûöòì¸Èâêò÷ûÿÿÿÿÿÿÿÿþþþøóèŪ…]H?90'(*('$$)**+,379<BIRbYZSMLEHLLLMLGA=:1-7L9.*'&%%$$#$&$$##$$$###!! !!"###%%#%%$$%'%%%%$$%&%$$%%&%%%$$$$$$$$&$%%$-AU\\]beffeba```__`````__^^_a``bbbcbcdeeeafffbbcdehhijhdccgihif\`dcddegfjggfihjggdgimkigojgfdbbbgfedihggigfcc[YW\^abcdgjjklkkhjillljmmmlnrqrrstsrqrprrnkhaVJFAC?@@@@AA@@A>@@@@@?@@@@A?@@BAAA@@?>>??@‡ŠŽ‘‘“‘‘•ŽŽŽ‹‹‹‰‡„‚‚ƒ€~}~|}xwsnmemsy~„~||y{{}||{{xwy{}€~€‚‚‚‚€„†…†ƒ‚ƒ…‚‰…†‡‡†‰ŠŠ‡‰†…‡„„†Š‰‹Š“•–’–Žˆ„€pf_cv—•œy”ªŽwpllqprqotƒ Á³¤–†„£Çº½›œ¬¼Ÿ‹wknunc^cj|††“”—ž‹‹”¢}gnšžáòýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúï¯dQN[h…”¡¿ÅËäéÔ¾“¢èýòâèë˪ªÄ¥ÆÖäÓÄ¿Þ÷ûÿÿÿþüüüù÷÷÷ùüýÿÿÿÿÿÿÿýûøôéÕÍÅßòùÿÿÿÿÿÿÿÿÿþþøóèé~[G=7.&!!"$" +2877:>;78;AHOTVQMVFVGEED@<5/,)+=8-($$%%$$%$$$$$$$$###"! !""#$$$$$%%%%%%%%%%%$%$$$%%$%%%&%%$%$$#$$$$&$$&$/GY^baffgedbbcfcdccceaa`___`bbabhbcccdgfdbefgggeienikiggkkliigcahijfhfhfjgfghijkljjjmmmlnkhfddcdgfefkihimgcccXVY^bgghfmlqlmklijkoookmnrmmorrrrspoqrsrqqnojhYLEBA@@@@@AB@A@C@?@@@A@?@@?BAAAAAA@??A@?@”’’’’’’’‘’“’‰‰ˆ†„†ƒ††…ƒƒƒ|{zyxsomjnosxz|~€ƒ~}||y{{|}|{{yz{||}}}{{|€~€€‚‚‚€€‚‚†…ƒ‚ƒ‚…„……„„‰‰‰‡†„„……„††ˆ‰Šˆ‹‹““•“’‘ŽŠ…~tifauŠ«¹}¦§upplqrpnny†œµ³¶›ˆ‚‘¦¸¨™¾«¢Šqmroe[`kw~†w”Ž“‡ˆ‘ke|œäóþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøè`T]gˆµÌÅàÞàæ½‡åûöòðìÀ¡”—®¼ÕãçäÃÒðøÿÿÿýûûüú÷÷÷ùúýÿÿÿÿÿÿÿÿÿþüöäÈÅÏìöÿÿÿÿÿÿÿÿÿÿþøòèç›wT?:60(%"%'%!%6FEDFGEC;5<<CGSVNMIE@:751-+('&'(*,($$$$$$#$%#""""!"""""" !"#$$#%$$$&%%%$%$%%%%%%%%%%%&%$%$%%%%&%%%$$$#%&%$%%4N]`abgedccbbccddcdddccba``aaaabebcddbbbdbeedcddeefgkihhhhiiigedhiideefeffgjhkkiihjjllkjkiggfeedefgghihhgc_[ZW[^acfghflillkijijkmmljlmmmmnqqqnomopqqpoooonkgZOICB@ABABA@??@@??@@@A????@AA@@@@@???AA@™™¡˜˜“••˜’”‘‘‰†ƒ…‰ˆˆ†‰„ƒ|ywtokjgpz{ƒ„†€‚ƒ~||||}{{~}|~}|}|{{|}…„ƒ‚†€…‚………„„‚†„…„‡„Šˆ‰‡…„„…ˆˆ‰‡‰‹ˆŽ‹’“˜–™•—’‰…wnad|ž“}“Ѽ³³°kholnmpx†ž¸¶¯˜‚…Žš§›”Œ„}}rnptlY]mvzƒuy‰Ž––›ƒyspcm£æõþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôà~_cp“Á®Ä»²Äßßâ¬w}Äòóóðê¾—’}ƒ¡¼ÌáÊÉÈäòÿÿÿýûûûùøøøùùûýþÿÿÿÿÿÿÿÿþûøòìïòùÿÿÿÿÿÿÿÿÿÿþøòéÀ¥™oM>9870**-,+#.HINRYYZL@@?@CGOLGFB;5/+)'&$##$$$%$#"$$$$$###"""!!!!!! !!!"""$$##$$$$$%%%%%%%&$$%%%&%'$%%%&&%%%%%&%%%%%&&%&%%%&9Taccbgcbbbbdefefehffffcc`bbdbeciccdfaaadcfedccdeeihjijjlkkjmjjijhgehiieeejhgimigimlllqjkhggjedcffhigghhi`ZXZ\cdfeijkilhkklkllllollkonnmonqqunplrqsqqqqswsrmj^RIDBA@??@@@?CAB?@@AA??@??@BA@@@@??>?@@•–––—“””“ŽŽŒŒ‰ˆ††‡‰Š‰‡…€|wspnhotx}}~€€~€|}}z|}}}}||{~||z{{~~~~€~„…„…ƒƒƒ†………†…†‡‰‰‰ˆˆˆˆ‡‰‰‘’”–•––—–”ˆ‚wkfbtƒŒÏÑ®©¤Ÿnehqqnoq|е«±†|}‚‚„…r`r†sfs€t^]tlhlrs{‚ˆ’—‡}lge{¯ëöþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð»qkw”ƹ¯š–ªÖàã˜sYo¡æïôðêÀ¢–hxˆ• ¢¡¢¡½ìÿÿÿýúúúùøøøøøùúýÿÿÿÿÿÿÿÿÿÿþú÷÷÷ûÿÿÿÿÿÿÿÿÿÿÿøñä¾£“gG8;@<86310,'):KLRXXXXTA<?PHKB>>?90,''$$#####$%#"""#%#"##"""!!!! ! "#$$$#"%&$#$$%%%%$$%&%%%%%&&%&%%$%%%&&%%%%%%%%&%%&%%%%%&(?[bccba^aaaaeeeefchffcdccabccbcaccddeaa`aaa`abdeeeihkgjkkkkjkihghgfefffdefigghlghiiiijiihffffcedfghihfeb_ZZZ^adfffjmkgkhkhhhjklmnllknlmmmlnoonnlqptrrrtuvutpmh_TMDB?@?@@@@@@@@A?B???A@??AA@@@@????@?“•—–—–—‘ŽŽŒŠˆˆ‰ˆ‰‹Š‰‡†„~{yplkls}€†††~}}}€ƒ€€}||}~~€|}{z{~|{{}|„‡€}€€‚}€€‚ƒ‚„ƒ…„‡€‚„„…†Šˆ‹ˆŠ‰ˆŠ‡‡ˆŠŒ‘‘•”™™›œœœœ“‰‚sj_bjm„ʾ¬¸Îªvpfflgjns|‡— ¬ˆ|xxz€Švccl…tqwk`zkelgjsx‚†¦Œ€wtz‹®èòüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþî¨kuƒ««‘|zÍÎÛ˜jUVàêðíéѳŸ‚pr|}‰—®–Õúýÿýüúùùøøøøøúûýÿÿÿÿÿÿÿÿÿÿÿýûûûýÿÿÿÿÿÿÿÿÿþþ÷ðâ½¢aA:>@A994522*##5FPVYVTVYLA=?EF=56882+'$$#!###$###"!!"""""#"""" !! !""#"$#"#%#$%$$$%%%%$$$%%&&%%%&&&%%%%&%%%%%%%&&'&&&$%&%$%&%&+H]eeeb``ddgffefefchggffdddgdeeeddddedacaa_]_abgghfpjlimmljkjnilhkgghgfffffjhhhkhijkiijlihgeffdeefgljkhea\Z^`feffhinmlllikgkjijklsnpoopqmqnoonnnorptsvt{wwuxtsqpeWLEA@@B@AA@@A@A@B?@A@@??AAA@B??>?@@?“••–“ŽŽŽ‹‹Š‰ˆˆ‡†‡‰‡„ƒƒ~|{vuylsx~‚„†………€}}€~|{||~}~}}|{xyz|{|y}|}~}|}|~}‚€€€ƒ€€€€„„…†ˆ‡‡‡‡†ˆˆˆ‡Š‹Ž’’’“™™š››šœ–“†{sie_gz¥¿Ç½´qgcflmmpu}„’¨Œyvx}rhcn„slt‚ƒwu…}ofaiqu|€…Š‹ŠŒ‹¨ÐîùûýþþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿþìždoŒ“†sliwŒ°Ç»¢yY[‡àæëéçåÑ´‘qq|mct‹|‹Çòúÿþýúøøùøøøøúûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöîß¼¢Ž]A7<A=964340+#!+BTQaUPMJKD?9<>>72175.(&$$""#"""#""""!"!""""""!! !! !!"###$##$$$$#$&%$%&&&%%%%%%&&&&%%%&&%&&&$$%%%%%%%%%%&%%%%%%%%'.N_^_``abcdddddddfcdddcddeeeeecdeeeefeba_\[^`abgffgggjfliiikhjihhkfffgddceejdihihjjjjiihggjgfgeddghljiif^][addefgijmiiggggghfjjkjsnmkomnmonoonnprrqrsvuzwwvvuvurrdYPEC@A?A@AAA@@?A>A@???@@AA@@@?>?@@?““”’’’’””Žˆ…‚…ˆ‰ˆ‰ˆ†…‚}|zxqntx|‚‡††………„„…„ƒ~}}}}{{{|}€~~zywwy||}}}||~}}~~€€†‚‚„€€ƒƒ‡„„…ŠŒ†………†‡ˆŠŒŽ”••–™™›œœœ›œ˜™”ˆ†vn`ap‹ž¬··¹¤ˆvpffhfnrw|†”€ywwxwsnqtsqot~…~Ž—zi^gqux}„Œ”—’ »Úîñôùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþí™`kx~ypbZeo|¥Å¶¢Š`a‚ÕÖáæëêèÓ´Œrka^arwpo¤äôÿÿþûøøùùøùùúûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõìÚº¢]A6:::97544/*#!%0XVRQMIDDF@=;;>?82361*%$$$##"""!""!!!!!!!!! !!!""#"$$$$$$&%&$$$$$$%&%%&'&&%%%&&&&%%%%%&'''&&$$%%%%%'%%%%%&%%%%%%%'2S_a___adhhhhieecfcedecdehgfefdgedeifeb`ZV[dcedgfjgjjjkljjjlhkkkijehghgfeigjjkjnmmjqkigfefhhfghgfmkmjjggaabhfggghkjmjihhhhihijkmnunnmpnpmsqrrqptturst{zzwywywtturqh`OFBA@@ABAAAB@AAA??>?@AA@??@????@?“Ž‘‘‘“”•ŽŠ‚ƒƒƒƒ‚€€{wurwnv|€„„……„………„ƒ‚€}}z}|z{{{zyyzxxvxz||{z{|}~~}~|}~€}€‚„‚„ƒ…………†‡ˆ……†ˆ‰Š‹ŒŽ‘“”••—–—•˜˜œ››™˜—•‰€vjeaqƒ˜º¹À’‡rjfegnptw‡„ywxuvytsturu~‹v}’™‡o]kwtr}„‘Œ‘Ÿ™’˜³ÈÉÍàõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþíž[[edc]SXgcb¢Â±Ÿc`z¥µ¿ãîîìêÓ¦zf^X`ffdgzµîÿÿÿüøùùùùùùúúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýôêÖº¢—^@5;:;98774/)%"%37<>AA@=79><<<>@=885-'"#$$##"""""! !!"###%%%$$$&%%$$$$$$$$$$$$%%&%%%%%%%&%&%%%%%&&&&%$$%%%%%%%%%&&%%%$%%&&&&)6S____aceghfdgcfccadedddefgecdcddddffdaYQX``accgffghhhhhiiihfimjggeedeeefffeekjmjjjkgffeefdfeghgghhhhhffbdfggghhihikkjihilojjlmnqpnnnpnnntqrsrqtttstuxxxwwvzxvuusqle]SIDAABAAA@A@@@@@@@??@@A>?@?@A@@?“’“–”–“ƒ‡‚‚~~€}|{wtqtw|ˆŠŒŒŒ‡……†…†ƒƒ‚€}||}€}}z|{yyzyzz{{{}{zz{|}~}€~~~€€€‚ƒ……Š„„ƒ†‡††……‰†…ˆ’“””•’“•”˜•——˜˜™š›Ÿ ŸŸ—‘‹…vi_bs~š°¸Á©´{rgejituy~~|zxwttxxwvrpwˆ‹}t{•›umqolm~‰“‘“—–•ž¹ÀÊÎïüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿï¦YOSUSOOVfX[ŸÂ¯’e`p’ŒÅîëèæáe\]dgl_[l”éúüþûùùùùùùùúúûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóêØº£^@5::;;:::2.(("#&)+.9<;6127;==>B?=74*$""####! ! ! !""#$%&&%$%%%%%$%%%$$$$$$%##$%%%&'%$%&&'''&%%$%%%&&&%%%$$&%%%&%%%%%%%$$%%%%%%*:U^_adeffgfeegeebbceddehffeedhdedffgdd[SS_``bhdgeegjijgnmmhhhijihhehdddffhgiikkmjlkmgjegfhghiiihgjhjhihljiikioiikqhkkllllrnklnopprnpoporqustuvtxxysvuzy}x{xzx}xysqoogbTHB@@@AABB@A?@@@@?@C@B@@@??ABAA“ŽŽŒ‘——“’ŽŠ„‡€€}||}xxy~………ˆ‰ŠŠ‰‡‡‡‡…„ƒƒ€}}}}}|zyyyyyyxxzyzz|}~z{z{{~~~}~~~€‚€‚„†††„„„††…ƒƒ„Šˆˆ‰‘“”““••“•––•—™™™›œœ›œœ—’Š€tid_nƒ˜º³°›’vkddhquw|{{zyussvw}xqovƒŸmu—••pnkfazŽ”’Ž™˜š¡¨±¨›¸éùüþþÿÿÿÿÿÿÿÿÿÿÿÿÿð¾ZJIIIMQ]g\ZšÃª—d\_qr~«êèãÝÖµ‹h`^egaYVVxÚòùýûúúùùúùùúúúúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûóêÙ·¤Ÿ_@5:<:7662,)'$! .7<;4/1368::8754-&#""#"!!" ! !"###$%&&(&'&&%%%%%%%%$$$$$$$#$%%%%&&%%&&&&&&&%%%%%%%%%&%%%$%%%%%%%&%%%%%%%$%%%&,?Y^bdeggfgdgeeffadeeeeggdfeeddddeghhaZQRW`bbceeffggghjhjjigiijjighegacfffffijjjnjjjhdfeeehffddegfhggfggkjjiifmjkllhiilhlmqmmnmlnnnnponorquttuutxwwttuvvvv{wzyxwwtrppkfaVKFBBBAAA@@?@@@@@@@@A@@@??A@A@˜“’’’•›‘‘‰†…ˆ‡†‚‚~||~†‹Ž’ŒŽ‹ŠŠ‡ˆ‰‰„‚‚‚‚‚‚}{zzyxxxyyyyzyz|~‚{{{}}‚€€…ƒƒ€‚„†††…‰†…††††…„†ˆ‡ŒŒ’‘‘”•˜˜—”””•–•–šš››œ›¤ž Ÿœœ–”ˆƒuj_ar|–§«ÍÊË“tpeejirvz{yxtsstvy|soq{ƒgnˆ•’Ž|ofgw‰™Ž‹©˜›žœš™¤Îï÷þþþÿÿÿÿÿÿÿÿÿÿÿÿóÕcCABELUifb[–«¥“w^QVngd äêêâÖ´˜|fdea\VRSc¨èõûûûúúúúúúúûûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüóéÖ´¤¢aB9<;9653.($!"08=74121221221.+(#!!!"!! "#$$%&'''&&&&&&&'&%%%%%%$$$$$$$$%%%&&%%%%&&&(&%%%%%%&&%%%%%%%%&%&%%%%%%%'&%%&&&%%%/E]bhhghighhhfffgagekegggeieecfghhigi_TPR\bafffeghjglijikjjiojmhffidgbchjffgiijknjjhffffgfhglgfglkkgfefglkljhillmlmililhqmqmpnnnsmporonossuvvtutyw|twvwvvu|y|yyxxsuuunomjZMEBAAA@@C?@@A@AAABB@A@>@BAA@š™”ŽŽ‹ˆ‰…†…†„„‚€„ŠŠŽ‹‰ˆˆ‡‡†††…‚ƒ‚‚€}zzzyxxxxyyyzz{|~~||}~~~~€€‚ƒƒ‚‚‚„†…„……†……ƒ…†‡…†ˆ‰ŠŒ’•••””‘““•––—™—š™š›žŸŸ —”Œˆwkf`m}ªÈ¶ÌŸ„vlddgqswxyxutsrtwwvshm}x_h‚Œ‹Š‡ztlvƒ†Š‘ŸŸ“’•˜•Š›¶ÖòþþþþÿÿÿÿþÿÿÿÿÿÿöçrI@BFOZm“t]‰«›ŒuVOPjgsçëðç»°¢’qj`[ROMY…ÞðúûüûúúúúûûûüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüóèÒ²£žcE;;9410/,&$ %2:62232/*%$$&(%$#"!"!!!! !""#$&&&&('('''(&&&%&&%%%%%$%$$%$%$&%%&&&$$$&'''&&&&%%%%&&&%%%%%%%%%%%%%%%&&''&%%%%%%&2L_ehkieiggffcffhbeegegeddecdddefgihg\NGU_bafddehjhgkiihhgiikijfecdbcbfiifghihjknijgfefegggggghiihhffefggehijmmmmklhjiiimmnlllnnmllmqooorssrrqtqttttuuwwwvxwwuvvtqstroqsmf[NHAA@@????@@BAAA@@?@@@@BBA@¤œšŽŠŠ‰‰‹‡ˆ‡†ƒƒ„ˆ‰Š‹ŒŽ‘‹‰‡ˆ‡ˆ‰†††„…ˆ‚„‚€€~{{z||}z~|{z{|~}€…€‚ƒ„€€ƒ‚‚…ƒ†…‡……„‡‰†……ŠŠŠ‹“’’•’š•–•––—“”•˜™™™šš ž¢Ÿ¦œœœ™–ˆzqaanxœÌ¸Ä¦Ž†|qfekkvwzwwtspquyvuicfnYax‘“œ„†wwy{ƒŽ›‘‹’‘‘†«ÇïüýþþþÿÿÿþþÿÿÿÿþùéœSDCJU\s•r©™†sMGLfks—Üãîß™©¦ˆxbZQMLPoÀëùûýûúúûûüüüüýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòçβ¢šeG=;4/,-,*('#$3200133.% ""!!" !!!!! """"#$$$%(&&''''(&''''&&&&&&%&%%%&$$%&%$&&&&&&%'%'&&&%&&&&%&'(&'%&&%%%%%%%%$%%&'))(&%%%%%%'6UafgijfiggejhigiiigifgffggfgddefgihfRGLZ`bchfgehgggjijghiokpileeefeeeihhhmhihnnnijhgghfhgfhljkijjjhhenhgfgillmnnlmmmjlknnqpsomkklmnqqtqqrtrsquvvsxuvv{zyyzwyuwrrsstqquttrp^OEBAA?@?CA?AAAE@@@?ACAABB@—•‘ŒŠŠ†‡†„‚‚‚…†ˆŠŒŒ‹‹Š‰ˆˆˆˆˆˆ……†……„‚ƒ€€}}{{{|}{y}||}||}}€„€ƒ„‚‚€€„‚†„„ƒ„„‹†‰ˆ††‹‹‹ŒŒ’““‘‘’•‘“”•““””’“”š›œžž¥›Ÿ ›˜“Œ„ykc[j…¥¬³±»ÅvjbchruvwtrsoptvvtkcdjU\l‚’˜’ˆ‰†}kq{…‹‹‹Ž˜¤”†œ¼ìúüþþþÿÿÿþþþÿÿÿþýöØ€MJMS[|ÀŸu‚¨—lGFH\trŒ°ãèÔƒo¢È§•zdZROMN[šäöûþüùúûüýýüýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüòæÊ³£šeI>:0,(()+-+*%')((-/.-%!!! ! !!"""##$$$$$$%%&&&'''(((()(()))))(&%%%%%%$%%&&%&&'&%&%&%%%&&&(&&&&&('&'%&%%%$%%%%%%&),171.)&%%%%&'<]begggffeeefefegggeiffcfbffedfffgiiXEIW]`bdefgchfhhjiighijiiihedddafgghhhhhihhiiihhhhheggiiiiigggghgfnihghkkhmjjjjiijklmmnnnnmklmnoppqqqqqqrqrrstyuuttuwwwuusqnorqsqrsrtspj]PIBA@??@B@AAAAA@@@BBBB@AA˜ŒŒˆŠ„ƒƒ„ƒ‰Š‹’Ž‹Œ‘ŒŠŠ‹Š††…„†ƒ„„ƒƒ€~}}~}}{}|}~~}„ƒƒƒ„…†€~‚‚‚„‚†…‡‡‰ŠŠˆ‹‰‰‰ŒŒ’Ž“–’’’’‘‘‘•‘‘“˜“•”•’“•œ›œœžž ž¥¤¤¡¡ ¡—“Œ†rgZ\rŒ—³·¹Áº”xqeejiortqsqoruttndefTTaŒ›•‘Š–Ž…ojpv}ŠŒ—˜ š—xz“¦çøûÿþþþÿÿþþÿÿÿÿþýüå½rQMOZ‡»ÈŽ€‘–}dNDDVwq}ºÝ¼qf˜¨~dZYWRMSyÅñùþüûûüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûñåǶ§šdI=8/)&&(+030/&!&+./-&#""" "#"#$$$%&''&&%%&'''''''(((()(*+-/.+)'%$%&%%%%&&&&&&&&%%&&'(&&&&&&&'&%&%&'&&&%%$%%%%%&'-4<@E3,&'%%&')A^ceifhghejefeheifffifffgffeeejghij`SAL[c`edofgehhjikjlkkkkjpiiegdd`hhhgmhhhihjiiihhiihdiinjjjjfefijmfnijijjjgmiiilijkonmmmmnmmnnoppwqspppurrqqrwxyvvtutwwyuwqqlmoqqqqtrwttpo`QGB@??@@@ABBBAAAAAAAB@BAŽŒ‹ˆ‡†‰‚„ƒ„„‰‹ŒŒ‹‰‰‰ŒŒ‹Š‰‰ˆ‡†††††„ƒ‚‚~~~~|z~}~~~~|~„†ƒ‚‚€‚‚‚ƒ„„‚†…††‰††‡‡ˆ‰ŠŒŒ’Ž“’’’’‘‘‘’’“—’“’’“”•–˜š›œœžž ¢¡¡ž ˜”‡xoe_Zdr˜É½Ùº©’uibchosrqrrpqrssqiggVSVi}Œ•Š—‡pe^dm„Š’Ÿž“ƒry‰ŸÕ÷ûÿÿþþþÿÿÿÿÿÿÿÿþý÷ÐPGO^˜ØÏ˜ƒ‹‘~jVKBQwqjuŒ±¦og¢¯˜ƒzc[YZ[QKe¬éöýýýýüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúñ䯳“aH<7.'$'),024.+!%(+-/(%$#""""#$%%&(&'''''&&())('%''(((()*-05;:=3*(%%&%%%%&'&'''&(&%&(&&'&&'&&&&&&&&&%&&*,(&&&*-,-,)4@=<84*%%%%&'+F`ccfffffeeceefeedffgeffedfefegfhid[QCP^``cdeeedgghhjjlhjkigjifdcdc`degghfffihhggfffgfhehhjjjffeefiihgjiiijghghhiijjkkljmkllmlnpoppqvqpoopqppqsswwvtttttuutsplkkmmmmnoprrrssol^QJBA>@C?@@@@@AA@@AAA@@?“މ‡„‚†ŠŠŠ…‡Š‹Š‹’’’‘“ŽŠŠŒŠ‹ˆˆ†………ƒ„€€„~€€€†ƒ‚ƒ„…†…‡„…„„…††ŠŠ‰‡‰‡Œ’‘‘’’’’’’‘•“•—’““–“•–———•–—››¡›Ÿž¢¡¦¡¢¡¡™•Š|qg[[h€ ¹µµ¸½zrefhgmnopoopsvrkieXQP]n}ŠˆrbTWe|‚…‘¢ŒxitŠÙ÷ûÿþþþþÿÿÿÿÿÿÿþþþùèŠMEMmªÚÖ¨{}¥‰qeUHLhd^u§ˆp§¤•ƒub[Y`[XIV”àóüýþþýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúð䯱«‹^F;8+%#'+-131,)#!%()*('&%&%$%''''((&')'&&&&+12,)((())((*.4;?>IE:+(%%%%&'&&&&&'&&'''&&&&'&''(&%&%&&&&&&)-0,''*/6;81/=A742-(&&%%%'.K`ddfeddheefjjifeeffkghgfejgffhfhfeWNKV_ecffhiifggkillljjiifngfcbbcbedffijjfhhhhhggghfhgghljkfgeiiiiihmjmjihkhjilllkpkmmlkonqmoprrrrvqqnvrrrvu{w|vvstvwvwtuqplnkqmmmmmnoqssrqlpaSGCAA@???A??BA?@BAA?@@‰ˆ……„‡‰‹‹Šˆ‹Ž‘ŒŒ‰ŒŽŽŽ‘ŠŒŒŽ‡‹Œ‹‰ˆˆ‡ˆ†‚…ƒƒ€€€€€€€~~„…………ƒ†‚„†††…†‡‡‰‡‘‘‘‘‘–’’’””•––––”—˜™› ›œ››œœ››˜œš–‹‡ƒ{tjc\k|‘¯µ¤²œ‘ymcbfnqqrpoprvrnjf[RMRZn‚ƒ„‡€zweNOTclrz†ƒo^l‰—Þ÷ûÿþþþÿÿÿÿÿþþþþþÿûë‘LB[~¶ÚÔÉg|•~xfNM_a`WWov…Ÿ£’†vaWQTYPGOr½ïúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðãÆ¯ªƒYA9/&%%+//11/+&"$$((''(((&')))(((('''&'''',;3/+*-013/,05;C<;GM;,(%%%&&&)'''&''&''&&&&&&&''''%&&&),*().7@4)-06?@F60BL@74+'&&&&&)2Q`bbcdccdfeiiighfefffgfcfeghffhfeaWMOQZbddffghhegghglkjjiiifeefbb`bbabdfggjeedhfgfgeffghhfhhgefdighghhhiljjihfiiklljkjjjkjklmkqproqtqoonnpqrtttuvrwuvzxwvtpkkjlkllnnllmmnlnnmklj]RKCA?@A@@@@BB@@@@@?@?‡…ƒ†ˆ‹“Ž‘ŽŽ‘’“‘Ž‹‹‹ŽŒŒ‹‹ŒŠ‰Š‡ˆ‡††…ƒƒƒ‡‚†€‚‚ƒ…‚€€‚~€€‚ƒ…‚‰‡‡…‡ƒ†††††‡‡…‹‰‰ˆŽ‘–‘•’‘–•—–——›••—š—šŸŸ¡šœœ˜–—˜š‘‡…xn][fq•»¶Ñ±½ž…vefjgrqqooqutsngaTKKPcx…ƒŽzygIDIRZ_l}zg]fzŠËõùþþÿþþþÿþþþÿþþþÿúéKHeŽÃÛÝΩmi}‡ˆ„`OZt`LKYz ¯¸€w`PLKLLFH_§ëúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúðâŬ¨zS=1*&&*20/01-*%#! !"#%&())))**))))(('(('''('''.79/--38??;23<IC98FM>+(&%&&&&'('''''&&*''&&&&&'&'&%%&'+13,,6HE=/28=?JI;1IKIB:+'&'&&&*6WagcddhdddeghgggffffgfeegedfffhedYOMS[ccdeiiiikghhjgmkjijiiihdfbeccbaacfiijffehgjgggijihmhjihgidifjjjikillojhhljnmmlniijmlllnnrprqqpoosnppsrvssuyxywyy|wuqljkjokklplllmmnlomkjlki`SHB@@@@@BAB@@@A@??A?††ˆ‹Ž“‘’ŽŽŽŽŽŽŠ‹‰Œ‹‰‹‹Šˆˆ††‡††…ƒƒ‚‚€‚‚‚€€€€€€€€€€‚„ƒ…††„‡‚‚‚‡„„„……ˆ‰‰‰ŒŽŽŽŽ‘””–”–—–“–˜˜™™™™™™••––’“”•—”’’’‘‘‹†€vh`XfŸ¼»¬¾Æ¦€mcbempoonorxxvmkYNMLXgs€†‡~tfI@?GNRVj~gWfmz´ðøþþÿþþþÿþýþÿþýþþùç‹LMm™ÎÝàÜ·|ns‚€xwf\h`LD_…¾°—‡zv^NDDDCADN‡åúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùïãé¦qM8,)'+/00//-*(%%%$"#&'''()*))*++,+***))'(''())((&/6:/238<<G?7:BH@97GK<)(&&&&''&''''''&&'''('&&('&&&%&'*5D:15=DE8.6?<9>E5,8FFO<,)&&&&%,;YbaacdedeeffhegjfdfeeeeedccdffhdYNQS\bbcefiiighhhhihjjihiiidgde`eea`cceghhhfeefgggggihiijhjdgjidighiigihmkmihhlklkkeeeghjjjknoonnklmnlmmpqrrsuuuvvvvxwxtpljhhikjkkkjlklmllmmlkllkh^RKBA@@AA@@???A@??@?‰Š–‘”“’’“‘‘“—“”ŽŽŽŒŒŠŒ‹‹ˆˆ‡†‡‡ˆ‡…„ƒ‚ƒƒ}}~~‚†€ƒ„…„…‡‡‡‚‚„‡‡ˆ„…†‡ˆ‰ˆ’‘ŽŽŽŽ‘’’‘’˜——•–•”–˜š ™ššš”–“““”“’’‘’•“˜“˜‡ƒugYZo„›¼¹ÙÂÈ™€vdfhmponnovzwrm]UUNOWas„…‡pcF<:@EHL]oifilqŸàòýþÿÿþþýüúûûüýýþ÷âMQs¢ÙßãàÚ£ro‡zz„||eNSkŸÍµ©€xu]LBAAB??EnÞ÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúïã¾§ hF3,*,2471//,*((+*+*()**,+*)**,041-+*)))'''')+,+**09925<C:;JM><FI=78GJ9)'&&''&&'('''''(''(('&&%&&&'&&&)0@FE49EM@60?A;7=7/,09EJ@,*&&&'%-@\addcdgfiggfifjhfggelghdfbcceei^ROW^jeeehfliihljkklkkkkllihhidddfc`bgflkkggefefggghhkiiimhkghijfihkjjhijnlmllillmikedcijmlqpoommmllmompnrrsrrtwwwy}{xzrljkhhilihhkjmkklomnnnmqoooocUICAA@A@@@B@A???@@Š‹‘Љˆ‰Š”‘“–”““‹‰ŽŒŽ’ŽŒ‹‹Œ‡ˆ†‡…†Š‡…††„…‚€€€€||z}}~|‚~{€€€€‚€‚‚„ƒ„…†‰ˆ„†‡‡ˆ‰‡ˆˆ‰‰ŽŽ‘Ž‘’’’“””‘—”•“–˜˜š›™˜——•”“•’’’“‘’“’‘’’–‘–‘І~tfa[dq“µÍù£–‚hacjppnlotz}um`ZXXNKT`o{s`SC;9BFIJWpnr}†~´ëýþÿÿþýüùö÷øúüýþõÇqKRt¨ÞáåäàÜ™ix’z’ŒymhÁ¸¨˜…zsi\J????<<>\ÀðúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúïṤ™a?3-./062/--+))(**))****+**)+.4;78.+*)**)'*+05692-7D;3:FA59IL@@GH;::GI7('&&''('&''''''(('''&&&&''((((',8BM>16IJ:74=C94413444CI8*(%&&'%0G\`a_bdeeiffdeeecdcccfefba_cbfeaYXW^ccdddefggjhkjkiiiihihhgfedbddebcdffkigdfffffgghhhhhjghhgfgggefghhhgjlmjjjigmkifdacdjiklnnplkjjhkmmnplqoooqswvxy{{{wslhfgfhihgfdggiijlljklnmnopppn`SLDB@@@??@????@@?‘Žƒ€ˆ‰Ž•”—“‘’’‹‰ŽŽ“‘“‘Ž‹Œˆ‰‡Š‡†††……„„ƒƒƒ…~~||}‚‚‚‚ƒ‚ƒ€€€‚„…Іˆ…‡‰‰‰‰ˆˆˆ‰‡Š‡ˆ‰“ŽŽŒ“—”••™”––——˜–œ—˜šœ••–—“‘‘”“˜’”’’“”‘“’–••“”‘‘‰…vnZXfz£×¼³«Ò pgbkponnnszz|oc^\\OEMWdqkWK?:;BHTQUbp}—’Ž‚œëûúúûüûúóíïòô÷ùüî¸_IRt¨àâæåäÜËŒq€ƒ{~˜–¦µ²²¢‘}rkdVH?>?><9:N›ç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùîÛ¶¢[>5--.0//-,+**)))))***++++*)-4>?@60,***,,+.29<B>:4AEC?AD@26IL@>IG77;IB4(''('''''''('''&''''&&&&'(,..,*/;KL:/4IH639GB51115>EA?=2)'&(&&%3N]`cbbcgfieideeeeeddcgfea``cdgfb\^`idedhdffjjkjljkiihmhiggggdccggfcdfjjlhgeihjfggiiihhhkgihgehgggfggilkkllijjifnkhcbbcfkkllonplljlkpornqpqopqttvw}zz{}vqifeeehfeeddeghhijmjkjnnnowqtnl`TICAB@A@@?@???@>“•Žƒ†ŠŽ’”•”““Ž’ŒŽ““ŽŒ‹Šˆˆ†ˆ‰ˆ‡‡††ƒ„€€€~|{z||~€€€|‚€€€€‚‚„……†‡ƒ†ˆŠ‹Š‹‰‰‰†‡†ˆ‰Š‹ŠŠŠŠ‹‹‘’••˜””•˜˜˜–™——““’’”“‘ŽŽŽ’”‘‘Ž””•“‘Š€s`[Ug|¤¶“°¶xi`kqkmnnot{}pf_aj\FIPZd\QG<=@DKVY\aqŽ™‡’àøöô÷úùõéÝàèïòööáLFPo¥áãçççãÒ¯‚ƒsqu‰¦“¹©¨—ƒqh`YPF@AEA=;9I|åóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøí×´ †U;2,,*,+++++*())*+)**++-++**08<=842/.-295/5=;;;=73CNB9DN@2;JC9<JC4497;0(''((&'(('&'&'('''''''')+28:>6,/:II8*4JH22>>B=6424C?>74-&&%&&&&7T^`cbbcgffeddda``abbaabbaaaceefc_aceddcedeeghihmjiiiilhieffedcehifdfgjjhggdeffeffgfgghhhggfgfihhghjjiigkkifhghgjid_bdfhkllkomljkkkjonnnnnmnpqtvvv|zzzupkgfgeabbbacefhghigmiihjjlmnoomkc\TLCB?@@@?@>@>>=š“‘Ž’’“’•••”•‘“‘—‘‘Ž’ŽŽŽ‡‡‡ˆˆ‹‡‰‡‡ƒ„~}||z|€€„€€‚€‚‚‚‚ƒ‡…‹ˆ‡‡‰ŠŠ‰‰‹ŒŒ‹‹‹‹‹‰ˆˆˆˆŠŒŠŽ‘–˜™™™—œ›ž—š™˜‘‘‘’‘—‘ŒŽ““”’’Ž‘‘‘’”•›šœ‘”„{lbXZhu‘µ·µº~j^jqmmnklqw|sjefqtJGMTZXNE??BKRX_dkv…ŠŒ‘ŒˆÀôóóòñòîÒ½¿ËèîïéµgGENj¤àäèèèæãЯ“•xdgo‘™ž“‰‚xg^WPJDCDEC>=;DhÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷ìÓ²žP70+*+****+*))))**)****++*++2?D?5455418AB5;DH>8744EOC:GOA:=ED7:A@0-131+(('''&&(''''&(((&&'((('+2==GIF20<GE5)5LF/1AMHCB@0.6;70*&%%%&&&%:Y^acbdcgeecbceaa_^`baccbbcdeflfecgefdgdfdiefgijmkjinnmgidfedcdhkgffhikhffiddegggfhfiiighgjijhjhllmlljigkiggkkklkeb`eghhknqmollknjqmoonmmnppsrwvxw{{ysqlhghgf_ccbbfffghhnmmjkillkmnmllkcd_UHCAB@??@?@>?>’‘Ž‘‘‘‘•‘“’’‘ŠŒŒŽŽŽ‹Ž‡ˆ†‰ŽŠˆ‹„ˆ…„‚~}€€‚ƒƒƒƒƒƒ„‚‚‚‚‚ƒƒ…†……†…‹‰ˆ‡Š†ˆ‡ˆ‹‹†…„„…ˆŠˆ‡ˆˆŠ‰ŠŠŽ‘“‘’•”—™—’‘‘ŽŽŽ‘‹ŽŽ‹‘Œ‘’“”””“†€wof_Wbov}¬½‹k]iqmmnkknuvvlimrrRJUWWTPKDACIQ[how‡ˆ‡ƒv|¬ïñóíçêæ¶£˜µßçäÆ[DDKh£ÞäêêêèæãÇ›…„l]^`jsttqngZVPJDB@DIE?=;AVŸìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöëÒ°ŸvL3.+*)*****)*,-.--,+*++,++--7HED<8;A81<LC39FFDA<:7GO@8DPF<>E9/194,++++)('''''&('''''''().211-)2=::@PH78?B?3)7ME//?FOAD6**++,*'&%%&%%$%>\_ccceacccaa``_\Z]_bbbbdefffeeedccccbfceddeggjjkkjjjjjhebfeeegjkggghihffdddccddfeeeeefffehiihhhkiliigfegghijigdb`ccgjiimpojniljjknlmklkmnprsqwtxxwurnmjhefcf`cfeefggdccgfhhiijilmmllmkbcg\RJBB@?>@>?>>>ŽŽ‘‘“–“•“˜““‘“ŒŒŽŽ‹ŽŠ‹‹ŽŒ‹ŒŒ‡ˆ„‚‚…€‚€€€‡„‹Š‰‡ˆƒ†ƒƒƒ…„Š‹Œˆˆˆ‰†‹‰ˆˆ‹ˆ‰‰Š‡‡‡††‡‰ŠŠŽ‹ŒŽ“’‘’••‘—•––—•”ŽŽŽŒ‘ŒŠ“‘’”’œ”˜’’ŒŒƒpeXVbov§¯¥o\fqmmkjjjnqpnowooWN[jVUSTIB@FO]jvˆ‰‡„~or¦íîïìêì㺔”«ÛàЧzUECIf£ÞæìíîëéåÛ£ut}dVVZ_cddc`UOIC@?@DHHA>;>L„èýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùõëÒ° qH0+)()))****+-1450-++,-.../1;LKB>>IF=4DKG48AMJNC=:JO>7CJO@C:2+*,,)'''''(''''''(''''((),07?A;2-8CD7:NM87CK<1)<IC2.6GID9/'%$$'&&%$#"!!!&Cacdidedfbc```a\Z\abfdcbfgjhjgidcdecdbfdfgkjjijjmmnkkhgedcfehgkllghgkhgfeegdcbcdfeeegghfeegjlhoklimmlffehfkklheb`bjimlnkonmmnkljonqnpnolpnrstqwuxvsqnnnjlhjfgcefhimihgeefgjhljmkkklkjkidddd^THC@??@>A>>>ŽŒŽŽŽ”’‘‘‘”’’ŒŽŽŽŒŒŒŒ‹‹‹‹‹ŒŒŒŒŠ†…ƒƒƒƒ‚‚‚‚…„†…‰††„…ƒ„„„„‡ˆ‰‰‡‡ˆ…‡ˆˆ‡ŠŠŠ‰Š‡‰††ˆˆˆ‰‰‹ŒŽ‹ŒŽŽ’’’’’‘“”“ŒŒŒŽŠŠˆŠŠŒŽŠŒ‘“””–‘ŽŒ‹ŽŒ”Œ†€xpe_X^hŽÃµt\crkmjihinnoqstjc\W]aXTROKB<BMWhny|||{wcm ëëêìííèѧ“¶ÙÔÆ‘lUDCG_¢áèîðñïìèÞ¬oehk[RSUWYYXVPMDA=>?BECB>;=DsäûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýýùõëЯ mE.+))))**+**,09D;31.147:9963<OJ>@?IL=5EOD489@EKJ=<GN:/5>;;61-())(''''''(''((''(()*/3468::JFF7/;IA0:KK68FJ:2)=QD6-+061-*'&%&&%%#"! !(Ldceecdccaa__\ZX\_bcddddhhifeedbbbecdcddfgiiihjjjkkkjedaccefghihhffddddddegeeddeffeffceddehjjhkjjjiihdddggkjifb]cfiijkmjlnkikkjhnlmlmlojmnqssqwtsqokigjjjiigfcefhhkjjhhghhhhijkjkkkkkkhcfhgf\RJA@??>?>>=ŽŽ‘‘—‘’‘Ž‹‹Œ‹ŠŠŠ‘ŒŽŠˆ‡†ˆ‰†ˆƒ„ƒ…†‡Š‰‰‰‰‰……„‰††‡ˆ‡ˆ‰‰Š‡‡ˆˆˆˆˆˆŠŠŒ‹ŠˆŠ††ˆŒ‹‹‹ŽŠŠ“‘“ŽŽŽŒ‹Š‹‡‡‰ŽŽ‘•™ž–—Ž‹‹“–’‰†ƒ€skYW^r—²n\_slmkiihhjmsqpg`a[_eYUVRI?:<CN[f|Ž„v€tl©ìîïëçêêÚ¹½ÆÉÈ©„mTD@DXŠÎÞìïòðîêß´ldfjaXQOOPQTUMKA?<<>@BCB?::?fÉùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýùôêЯ fB-*)))**+*)*-2DHF5349<CDMD;4>OJ:9ALJ=6FOB777:BLE>=EB6,,/00.+)''''''&('&'((((&'()-17=EGH>@FPH<7EK<-9NK39JJ<2)@GG4-'')''&'''(#"! ".Tcbdebgccbc`a\X[adheffihliiffddbddeeheefiilkmjjkllmjjecbedhiojkjkfidcceccdgddddcbcddecfcdejjkgmlpjiifddejjljieabkjmjplmllkjimlijmkomnloloptstrwtsnlhgfklokmjieeglikkmknnnnnlllnmmkolmjheknrlg_QEA?>>?>>=Ž‹’‘ŒŽŒŒŒŒŒŒŠŠ‰‰‰ˆ‰Š‡‡‡‡‡‡ˆ†„„‚„„…„†‡‡†…‚„„„„†‡‰ŠŠŠŠŠŠŠ‡‚ˆ†ˆ‰‰ˆ‰ŠŒ‰‡ˆ…‡‡Š‹‹ŠŠŠˆŠ‹Œ“ŒŽŽŽŒŒŠŒŒŒŒŽ‹‹‰‰„‡†‘‘”•”’‘މŒŒŽŽŽŽ‹Šˆƒ{pd^Vcomh]^uonkklihikopog^`WYZYQLIF?9;?HQ`‡™—ŽŽ•š²îñóêáæêâ¿Î»³©¡ŒoSEBCSlÚêïóòðëâ¾vTXgdaUNLJIJIHC><:;;==?A=99:Z¼óûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùóêί¡`=,*)))+**)**.5FRF6:>?CDDLL>4>OK89CMG?9HOA7<ECA>=74471+***++*))('((''')''((,-/20.6?==AMG:>?NF=:HL<.<O@07LHB3(093.*&'''''&$#! $5Wbbccbbaccb``^``dgedfghhhhiefdcaeefehffgijjijkknhehifcddeehjojjhkeecccfbcccddb`\badccbbcdfjhggijjgihgeffjiiiebeijimkmllljhjjomjjmknmlllkopqoppsuojifggkllkliigghiiiikkmmnonmnnnmmlmmljhcjpqnli]PH@@?>>??•‘Ž”’‘‘’”Ž“ŽŽŒŽŽŒ‹ŠŠ‰‹‹Œ‰ˆ†„„……ŠŠ‰ˆ‡ˆ‡††‡ˆ‹†‡ˆŒŠŽŠ‹‰ˆ‰ˆˆˆ‰ŽŒŒŒ‹‰‰‡‹†‡ˆ‰ŠŠ‹ŠŒŒ‹ŒŒ’ŽŽŽ‘ŽŽŠŠ‰Ž’Ž’‹‹ˆˆ‡‡‰’’–––”””˜’‘ŽŽŽŒŠ‹‘“‡ˆ~ypj[Y_fl^]umolklihgjnnmh^XQUeYMHFC?;:=DGPsª£¦´ ÎóóóéßàâØ»¡ž¡¥¨wQICBNb€·ßèðððëäÇ€XOVg`ZUMIFFGB=;9:;:;;<<:988PŸìùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòé˯¡\8,)))*******/8IRG7=GPA>CNOB8>MK69HLI@@KJ@9BCH@<3/-,-+)(((()***)))(((('''),16=<86ADC@BPJ63?LC=?LK;5AG=/5CK@5)(**('%%&'%$" ';Zadedccaddeabcdbjgeeiimmnghffcccffhghhhhkjkikklheekheehfkgjjojkhjeddddgdedeeib__abdeechdghkhjghhgfjghgkjjhoidcikommlokiiijkkoljjlkomrlnmsptprouqlijhjgmmnkkkklmhrkjjkltqwxxussropqvpoopnlptnrmm_QEA?>>?>‘’‘ŽŽ‹ŒŠŽ‹‹ŒŒŠŒŒ‹Œ†…†ŠŠ‹Œˆ…„‚†ˆ‰ˆ‡‡ˆˆˆ‡ˆ‰‰ˆˆ‰‰ˆˆ‰ŒŠŒŒ‹‰‰‰‰‰‰‹Š‰ŠŒ‹ˆ‡„…†‡†‡‡‰ˆŠ‰Š‰Œ‹ŒŒŒŒŽŒŽ‹Šˆ‰‰ŽŠŠ†ˆˆ‰Š’’””•—“‘‘Ž‘ŠŽŽŠ…‡‰‹Œ“އ†‚}xrhaX[_Z]vmmmlkjhgikonj`RMOUVIBAA?::;=@Ib„§´¸´±ºìùöòèÝÛÑͧ””‘¯Ö½ƒYSHAL[w—¾áíîðëåЋ`NIR\`bZNIA?<:87677668:9876IŠå÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ðèɰ Z6+)))*******0;IRF7?OL=??NPA4AMH58MMLFCBE9/4;752.-+-**)(()*162/+(((++++**079<:H99CM>2@OI34DE?=ALLD;BC6(-6460)((''&%&'#! *C]`bcbab`a`dccccceedcdeeegfedddccefhehgghjjjhiiifffgfeeffgfkjkjiggddeedccdefeb_acccdcddedgikfffgffffghhljjhjhhfijmqnkljjijkkjnjjjmkomolmmpppoqnnlifgghgghjijjkllhhhkjjkoorstuuwurrsvqpppmopsnnmke[OG@?=>>‘””“““Œ‹Š‹‹’‹ŒŠ‹‡‡‰Œ‡„„…‰Ž•ދЉ‹‹‹ŠŠ‹ŽŽŽ‹‹Œ‹‹ŒŒŒŒ‰ŠŒŠ‰ŠŒ‰‡†…‡‹‡Š‰ŒŒŽ“ŽŠŠŠ‰ˆ‡‡‡ŠŽŒŽŽŽŠ‰‰‹Œ‘Ž“•••—–’’“•Ї„…‰ŽŠŒ‡ˆtm[W[V]ulromlnhffglok`TKMOMD>>@A<99;>CTlœÏ¸¶µ¹ÛòòñçÝ×Ç®’ŠŠ”ÃÝÒ“eZLEIXq‘©ËæêîêçÖ–kOEEV‰|n^SK?;86665543344675DzÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðèɰžU5+*))*)*****1<IRE7APJ:9?LN@5CKF48CPLLDC:2,+-.-+**)+*))((*/;AA2,()+.01/++6BH==<<>NK;/>PG04HN<6=NNKDF9/&&))))((%%%&#"!! !!!".I_aedeac`abeeedgggehcfefdieccefgdefhghhmmlklknijhiggehfhhihljmijfgceegfhdfhkcabfefdeddefelllkmglfffjkmimlljlhhhijmoqlkjojmmpknikkmmnnsoqpppsrrlmjgfhhlhigljmlklljmkkjqjnnpqttvw{zzuvsrqvvuttpppqjk^NB@=>=•’‘’‘‘ŽŒ‹ŒŒ‹‹‹‹Œ‰‹‹‹‹Œ‰‰‰ˆˆ‰ŠŠ‰Œ„ˆ‹Œ‘‹ŒŒ‹Š‹‹ŒŒ‹‹ŒŒ‹Š‹ŠŒ‹‹ˆŒ‹‹Š‹ˆˆŒŠˆ‰‰ŠŒˆ‡‡ˆ‡‡‰‰ŒŒŒŽŽŽŒ‹†‹ˆ‰‡ˆ…†‰ŒŽ‹‹ŠŒ‹ˆŒŒŽŽŽ‹ŽŽŽ‹†‡…‡†ˆ‹ŠŠŠ‰ŠŠŠ‹Š‰„€zsg`WSWflrpomnigeejmme_TLJIC<=>><9:;>CIb޳¸´ÊÈÇéíëß¿¥žœ”ŠŽ™½ÝÕ£€sSHLUm¤¶ÓåëéçÜvVCBP{šqpmh\RH?<97542244555AlÝòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüöïèʱ“Q3+********++2?KSD7BQJ99ANL?5CPB138;A=951/++,+++*,-.0-)))/5ALD4.+159@;<3-;LE3108@MI:0BPC/5LM943>LA?7/,''((''('&%$" !!!"#2O]aaab_a``afddeeefddbddcaabbbeffddehfhhjgijjjjjhfecgeeeghhhkjmhgddcdeeeecgje_bdeeedddffffhgggifhfffiiihliihhgikkklkiikjjjllkijijkmnnnnnnlnnnkjffchgiikfffhhhhiijijiiijjmmqqrsuuwxyvvttrsssrrppkpkkkZKE>><–“—‘’’’ŽŒ‹‹ŒŒŒ“‹ŒŒ‘ŽŽŽ‰‹‰ŽŒ‘ЉЋޑ‘’ŽŒŽŽŽ–ŒŒ‹‹Œ’‹ŒŒŒŒ‹‹‡†ˆŠŠŠ‹Š‰‡‡‡†‰ŒŒ‘‹Š‰‹‹‹ˆ‡††ˆŒŒ‹‹Š‹‘‘‘’”’™Œ‹ŠŠ‹Œ•ŽŽ‰††ˆŠ‰Š‡†‡ŠˆŠ“’“Œ‰†…ƒwr]QQ\crqponkjgeilmmd`VLJB=<><:8;<?DM]„¶¤œž¢³ßéâÃ¡Ž„šœ¢©£·ÛÖ¤’wZQNUj‡Ÿ®Êâêèæà¥‚_FAGljo‡¨Ÿ–}hUFA:752222555?aÌïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüöïçʉN2+,++++*++**3CKSC7CQJ79DOM=7EE?/-02420-+*)))))*+/5861+**2?OQH5017=CIPA71EKB/))1DLG81EP@.7ML6.,2862-*)'(&'%$##" !!!"#$6U[_ccdabbebgehhhejegcdcc^]_fehfffgfhhlkphhjqlmjgeefhfjgkklkkknihdcdhefggfihedfffffdefkhhhjghglfifehlhjhlhhghjmmpmpjiikkpjkjjjlilmmornooonsmmjheefiijkkfhehhkiiioikiljmkplqqsttuwx|xzyyvvtwvvrqopnomlZLA==’‘‘‘Ž‹ŒŠŒŒŒŒ‹ŒŒŒŒŠŠ‰‰‰‰Š‹ŠŒŠŽ‘ŽŠ‘‘‘ŽŒŠˆ‰Š‹ŒŒ‹‡ˆ‡ˆ‡Œˆ‰„†‡ˆŠŠ‰‰ˆ‡‡ˆ‰ŠŠŠ‹ŠŠ‹ŒŒŒŒ‹‹Š‡‰ˆ‰‹‹‰‰‡‡‡‰ˆ‡ˆŒŽ’”•˜’ŽŠŠˆŠ‹Œ‹‹Šˆ‡ˆ†††ˆŠˆ……†…ˆ‰‹Ž‹Œ‹ˆ†„vj\OQZiqqsomkjhhlpojkm_TK@?>=99:<CHQay‘’“–‘•ÌàÓ¹¤ˆ†›˜¨ ©Õ¹›‹}eWTUe~š¤ºÞéçåÞ²ŽmLB?OZzŸ·Æ¶¨…j\PHA;5433556=X®íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõïæÄ¨€K2+++++**+-+*4FJRB7FRI67FFK8/5<5-,+-.--,***))))+,5BAF9--+7JOVF56;<@@JNC83EOA/,(1HLB82HQ>-2J=2,'*-,*)((''%%&" !""""$$&<Y]^^^a_bbebfeghgegeea`^^[\^ecgedcfghhiiiiikihhhffgggfhgigjijjjigcddfdfghhhgedeedcedfgihhijdddfffdfikfgeggggknnnpljiijkkkkkiiikjmlmpomonppommjhfghjjjihfffhhhhihjhjjjilkplpopqsvwvwtssuvvtutsqpmnmnmkdWIC=”“”‘‘ŽŽŒŽŽŽŽŽŒŠ‹‰Ž‰‹Ž‹”“•’Ž’‘–‘“’˜Œ‹Š‰‹’ŒŠ‹Ž‹‹ŠŠˆ‹‰‡‡‡‡‡‡‡‰‰‡‡‡‰‹ŠŽ’Š‹ŠŠŒ‹Šˆˆ‡‡ˆ‰Š“‹ŽŽŽŠŠŠŠ‹‘’”™™˜’‹ŠŠŒ‘ŒŽ‰ˆ‡†‡ˆŠŽŽŽ†„„…††‡Š‹ŒŒ’Ž‘‹‹†‚vm[MR[erqponkhimoronuzocSCCD<:<>ALYjyƒ†„„“¾ÒÊò™‰Ž’˜¨šœ¤¤’†|k\TW_v•¤Íæåäß¿›UB=Fh½Ù×ܶšƒth_ND:534685;Q“êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõîæÀ§vH1++****++,,,5IOQA8JLH45<C=4../.+))********+))),.=KTM>/..;OUTF25>G<:LOB96GO?.+'1JL>74KI<./45.*&%((''&''&#" ! !!!""#"$$##%)D^^_`^d_abdcffiiihjde`_^`Z\^ecgfeefhljniljjigedffhggiiljmmnjliihjfgegdjjjkkfeeedddggghkhjjjffeheeejikhiiigikoopnqkjijjkmmlkjjjnlnlmppmonsrpopjjimkkjniifllkhjjjgmilmmlppqmtqrrttyvxttstuvuztsrpnonpmmifWI?ŽŽŠ‹‹ŽŽŽŒŒŒŠŠˆˆ‡‰ˆ‹Œ‹ŽŒŒ“‘‘‘‘‘Š„‹ŠŠŒŽŠ‹‹‰‰‰ˆ†‡‡Œˆ‰Š‡†‡„†…ˆ‰‰ŠŠŠŒŒŠŠŠ‹ŒŒŠ‰‰‰‡‰Š‹‹‹‹‰ˆŒ‹ŒŽŽ–‘Ž‹ŠˆŠ‹ŒŒ‹ˆ…ˆ…†ˆŒ‡ƒ‚‚‚…‡ˆˆ‰ŠŠŠŽŽ‹‹‹Šˆ„|shZMQZirrtpnjiklomo|Žªr[GD><=@AJavˆ…„ˆˆ„—ÉâÔâË©“‹‰‹ŒŽ—š‹‡Œu`XM_h…§«ÃãããßĦ‰]F?Zƒ´ÜÛÜÛÁž˜dTE<866875;J†èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüôí忦pE1+*****-+,,,4KKN=5=I>133220,,-+******-++**+))*,/?RRRB232?PNQC/3;;;AMM?8:IN=-+(3LD:53:A4(+-+))''''('&#"! !!"""""""#$#%&-Ja^^^^`_aacdeggggffcd^__XT]^acdceegjlhiilkjggceggehgijihlkjihfffggffgekihefeffedeeghgfgghjifffheefggjihfggjmonmlljjijkknjhhehjoknmorqlllmlljjhjjjkkkkihflhgdffgfihkkkkonnnqqqqrrxuuutsttsrqpolmnnnmlljgaTEŽ“‹Š‹’ŽŒŒŽˆŒˆ‹ŠŽ‘ŽŽŽ’’‘“•‘Š…‡‰‹ŒŠŠ‹‰ŠŒ‰ˆˆ†…„‡ŽŠŠŠ‡†‡„ŠˆŒŒˆ‡ˆŠŠŠ‰Š“‹ŒŠŠŠŠŒŒŒŠˆŠ‹Œ—”ŠŠ‰ˆ†Š‹ŒŒ‰‡…ƒ…††‡ˆŠŠŠ…ƒƒ†‰ŒŠŽ‰‰ˆ‰‹ŽŽ”ŒŠ‹ŽŽ‰„xnZMR\gtsqnjihkooq™°¹–…aKB=?DAH\€‡’ˆ…ÏÖÔÜà±”Œˆ‰‰ˆ‡’”’f\^chxž¬¼àáãßɧ’aJMj§ÝÞßÝÙ»£¡¦ƒlVH>:99877;G|çþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¿¢jA0+++**+***+,4EME:7886/,,+,,+**+******++*)**)**,0BSWUG54:GMRQ@..:==DNL;8=KO;,,(7EH6//10,('((')'(&%$"! !""!""""##"$####$'2O`b^abbbdcgefgigkfhbbdh^UU\^acgcefkjlhlknkihgehhhhhgiiijlkkgffihigffjhmihhhfjffdeehhhfhgffhhighfghkhjjkfihnmrnlllllkjlmiggighjontpqpqlkkkkmiijmlpknkkhiklhgchggejhlkplqnuuvsqrwwxsrsustrqpqpplnnpnnnolkhfSŒŒŽŒŒ‹ŒŒŽ‘ŒŠŒŒ‰ˆˆŒŒŽŽŽŽ‘’’’’ŽŒŠ‡ŽŠ‹ŒŒ‹Š‹ŒŠ‰ŒŠ‰ˆ‡ˆˆˆ‡‡ˆ‰†…†…ˆ‡ˆˆˆ†…„……‡‡ˆ‰ŠŠŠ‰‰‰‰‹Š‹ŒŒŒ‹ˆ‹Œ‹ˆ‡‡‡ˆ‰‹‡…ƒ€‚€‚ƒ†‰†„…ƒ„ƒƒ„†‰Œ‡‡ˆ…‰‡Š‹ŒŒŒˆŒ‰ŒŽˆ€vi[OS\ittrnkijmrsƒ£Áº®}QFBA@@EWm„–¥’”ž½×ÙÙÕ»™ŽŒ‰…šž¡Ÿ™€mqˆykt•«´ÐßâÞÃ¥’gVS‡ÏàáâÝÙ¶À¨†oWJA=::988;Duæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõíå¼ f>/*++******,+/=9;4..0/,,,,++,,++**+*,,,+*+,+**,.1ESSUK:<CEIQP?.5;<;AOK87?NM:++'/<6.,)*+**((('''%#"! !!!!!!"$#"""##$#$$$#(6S`^^abbbcddeffhfdbc`eie\ZW^_abbbefijjhhhhhebhfgghihffgijkifdffggheffjhlffeggffeceeijgegdeehhgfgfhhihhfffiimmnkmijjklifhfgghgijpopponpikkkkigilkkmklkkhjkhdddikhfhhiijilnrttsrrvtsqrssrsrqmmkmlmnnoqspmmli^Ž‹ŠŠŒŒŒ‹ŒŒ‘‘ŽŒŒ’”“›•˜’‘‘”•™•–‰‹ˆ‹‘Ž’‘‹ŒŒŠŽŽŽŽŠ‰ˆ‡‡†‡ˆˆˆ‰‰‰†ƒƒŠŠ‹‰Š‹ˆˆˆ‰‡‡ˆŽŒŒ–—˜‹‰ˆˆŠ‹‹‘Œ“Љ†ƒ€~„…†‡‡†„ƒ„ƒˆˆˆ‰Ž†ƒ†‡ˆ‰‰ŽŽŒ‹‹Œ‹ŽŽŽŽ…um^SU\fqpoliikot…µ¼°”qWHILLGCXi˜¬Ê© ¬¯¶ÍÞÈŸ˜—”†Š˜«Î§„v}¥¾vf€ª®ÂÛàܽ ’r]n¢ÜãããÝÚ³«©©‹s]MC@=;::9<Bnçüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôíå¹¢c<.+****-+*+,+,210.,-,,+++++++,--+*+*++++,--++)),3IOPRPBABDHPN>8;CD:?QJ56ARF8**(*-,*(((())(&%$$"! !##""$"""#$%$$$#"%$&$$$)9W]^_dbcdefheffibaaeelhe^___addheeffhjijggccdiggfjjkklmlmmgddihkhifggjjlfgfjgffeehfligelgffkgffhhjjjgffjiikqnolnijkojihhghilkppppqnllpomlmjigmkklonmlkkojfdiijiihigghlllmqrututwsttsststrqnolpmnorsttvvuuvjŽŠŠŠ‹ŒŒŒ‹ŒŽŽŽŽ‘‘‘‘’”””““““”••“”‹‹‘‘’‘Ž‹‹ŒŒŒŒŽŒŠ‰†‰‰‰ˆ††…„ƒ‚‚ƒ„‹ˆ‰‰‰‰‰ˆ‡ƒ‰…†‡Œ‘ŽŒŒŠ‰ˆ‰Š‹Š‰ˆ†„ƒ‚„„€€‚ƒˆ†ˆŠ‰‰‡ƒ„…ˆŠ‰ˆ‡„„…‰ˆ‹‹ŒŽŽ‹Žˆ„}vqaSU[irqlihjmt„—ª²²gSL]whQOZj{²²²Ÿ’”¬Áäצ–›¡¦©“…–±·¶ž“ÈÉ‚blŠ›°ÕßÖ²›”}w|¹âæåäÞа¯¶«wcPEA><;:9;@gåúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôìä¹£`;.+**++++++++,.-,-+,,,+,+++,-283,++,,,,036;4-,*-6KMPRQGEEGGOL>4@MD6APA24A;=3*)&()*)))((((%#! """"!######$$##$##$#"$###$#*<X\_`aacedffccb`]acefecb___abeeeeeeeegggfdaefgfhghiiiiiihgeeeihjhgfggijgdgfgfggghhffggfffggffffhjjijgedhikkllkiiikljfffggikllmmoonllkllmljjiimjklonnolklifeijjggggggehhjkklnppqqpsutssspmmkmmnnprtuutvssrqnŽ‹’ŽŒŒ’”Ž‘’•‘˜’’‘’“™—œ—š™™—›–”“““”–—‘™’•’ŽŽŒŒ•‘‘‹‹Ž‹’“’Œ†„‚‚‚„‡†Œ‡‰‰‰ˆˆˆˆˆ‡ˆˆ‘“‘ŽŽŽŒŒŒŒŽŠ‰ƒ‚‚‚‚‚„‚…€~~€‚…‹‰ˆ‡‰‰‰‡†‡ˆ‹Œ‹……‚„ˆ‹Œš‘•ŽŒŽŠŒƒ~zvcUU]fqmjhgls}†—¤’cLZv‰‘lYblzŠ”žœ›Š˜¥½åΫ¥¨Ÿ–¥–—¸×ÖÑ›™ÁÏŠ_fƒžÄÜ£˜Žƒ¢ÞäèæãÝǰ¯±{bSGD>===9:?aÚøüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôí帟]9.*,+,+++,+,--+*+,,-,,,,,++-.8>>.,----.3;AA?/,*/9LJCMTPOA=HNI=8LMEDHH@.,230-)(''')('((%$#" !!""####$$$%$$$%#$###%#%#$#$#,AX]ccbadedefcd^[^ehjeedccb`eeeefeeehegffdcdjjiekhhikkkijhfegeihmijhjiiheegfghjhniihgfggjgigihjjjjmkjgdfkkpknlkimlljiefgjjmlroompopkmlmmpjhjjkmjllpponnmmighlkkhlgghhhiiihiilmqrrpttuvvpolmlqnpqrsvtssxwvqooŽŒŒŽŽŽŽ’ŽŽ‘‘“”•‘‘‘““’’“™—œ—˜˜˜–•““’‘’’•‘”’•‘“ŽŽ‹Œ”‰†ƒƒ„…†‡‡ˆˆŠˆ‰Š‰‰‰‰‡†‡‡ˆˆˆˆ‹‰ŒŒ’Œ‡„ƒƒ‚€ƒƒ…†‚€€|€‚ƒ…ˆˆ‰‰††††‡ˆ‰ˆ††……ƒ…„…‰ŒŒ‘ŽŒŽŒ‹ŠŒ†ƒ€ztbRTZgrlihkry†“ “bSb‰¥›‡mlozz{‚‰‡ƒ”ºÉÇ²Â§š†–¢¼ÔÝÚ§š¼³–eYo„•·´ª£¤¥§©¯âçêæâݽ ¨¸©‡t`UJF@??>;<@]ÇõüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúôíÞµšY6-)+++++*++,---++-,,,,,,,+,.0:H:1476752:C>?7.-*0;LGIHRTK;<KJE<8DSC?>?5****+*)))'(()%&#"!! !!!""#####$&%$$$$$$####""#####$#/I\^`_aaefedb_\X_ehkc^]]baabdeedffedffgggcfghgfeffijkjjjifedeeiglikghgffeegfhiihiegggfgehggehjkijjkkjghikkmkmgkillkhjfhijjmmmmnmmklkkilkjfhjkklkkjqpqkmnmijkkkmghgfehcefiegfllmmqnnnomlkllllooruuuussponponmŒ‹Œ‘Ž“““Ž‘—”˜˜™”•””•–——““•™™™˜—š•–•š’“’—”•”—••‘’—’“Š‰ŠŒŠ‰‰‰…‡†‰††ˆ‰Œ“’‘‘ŠŒ…†……†Š‹‘”—‰†ƒ‚€€‚„Žƒ‚€‚„‰ˆ‰ŠŠ‰‰…‹‰ŽŠŒ‡……†ƒƒ„…‡Š‹Š‹Œ‘ŽŽ‹‹‹Œ‰Š…}ubUTZdnhhjpz|‚ˆoVf §«vrqqprs|zxz–ɽ»³¬¨œ–‹Ž—¦¯ÏÖÓ³œ¦»•lVct†¢º £³Ã£¹äçéæâÜ´Ÿ¢¤¦‚paWNJEB@><??YºòûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÚ³‘V5-+*+++*++,,++,+,,,,--,,,+,/6DH;8:?HB;9EF>>3..*0?LE;BRRI8:LQA74@FG=73,(''(()))('&%#! !!!!""#$$#$%%%$%%%%$$$##$$#%###$#$#2R_aaaffefhdc_Z\ehle]Z[^abffggffgfjgfhlihfniiedehfmlnjihieefkfihlkkhkffeffkhmijhiefhgglgghjfhlqkjimjjijlmmnjmijiolkhjkllkkmnmlqmmnoonnpkjfjkppqlmkqpqmmmmmmnqkoklgfghcfeigkkkmpqropppkkknnqrttuw|vzstoompnnlŽ‹‹ŒŽŽ’‹“””—••”–••””••”–š™˜˜™——˜•––˜’’“–••“”’‘“’‘ŠŒŽ‘Œ‹Šˆ†††‡‰ˆ…†‡‰ˆˆ‰Œ‘”’ˆ…ƒ‚…†ŒŒŠ‰‰‰…„„„ƒ„†……„„||yy{††‡‰ˆˆ…‡††…Šˆˆ‡‡………………†‰‹‹‰‰ŠŽŒ‘ŽŽŒ‹ŒŒŠ‰ˆ‡‡}rcSSYdjihm‚€}|{Šnf†¬¢”slkpt~„€ns–ÇÆµª®™›•|…ˆ“”°ÑÍÅ¥ŒlSYdw‹«É¾ŸžÂæçèåâÖ¯“§¨–}rbZOLHEB@=>>V¬ïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúóëÔ²‡R3-**,+,+,++,,,+,,,-,.,,--028=BG=9<>BH;8AND>4,-*2C@B89LO@16KB;2+/421/,+)('('('&%#! !!!""""####$$%%%%%%%$$$#"""""#######$7X`bbccbededb_`bfif_]\_abcdefffggegghijihghfeeefhgjjifgfhbefgfhhiffgheedfghfiikeeegihgkfgiifilkkjilkjikllikjjiiiiihgiilomnnqlkmjgeimkjigffjkonojkkqoollmnmnpnkljkhgcbbccefhilpmkmllkjfhhlpqrssuwwuurolkkjiig‘‹Ž“““”•—œ••”˜•–““”•—ššš—–––—ž˜˜˜˜˜™•••››œšš‘‘‘“”•’“‘ŒŒŽŽŽ‰ˆ‡‡…„…‡‡Š‰‹ŠŽŽ““—–”‘Œ‰„‚ƒ„†‹‰ŠŠ†„……†ŠŒŒŒˆ‰…„‚}||tr|ƒ‡Š‡Š‰‡‡†‡ˆ‰ˆ‡…†††‡ŠŠŽ‡ŒŒ‹ŒŽŽ‘ŽŽ‹‹ŒŠŠ€xcUTW_ifjzŠ}{‰“muŽ™‘…rjluŽ—Ÿ{q“Å»±¬ª›†ˆŽ‰„ˆ—’¤ÑÕÍ®‰˜oTNZjz€·É»”ŠÉçèèåáÒ¬ž¢‘{ub\TOKHEC???RœëøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòêÒ²M2,+*+,,++++,,-+-,,,,,,,.049?FKB=@E@B@;8@INA5,.,3IJ?55HD8.2;;3-)()))((*))&'%$$#! !!!"""""###%%%%&&%%'&%%$%$#$##"$"#######&<\bcdeedffffffgfjhd^^`cdfdieefghieihqjjiiggfghlhkgihhfgggfggggjikefggfiiigifkilegfgikgkhghlkkkjjkilkkjnjklmkkkojmiihlkmllnomlkqiddiihgffiinnnnnmllpopmlmqnpnllmkjgicbbbbeflkmmllljijieggjoqswvvwvvwqniihhiigŽ‹Œ‹‘‘’’•——˜™––”””–’•–—™˜š—•••–—š˜˜”˜–——–”˜–›—–“’’‘‘‘ŽŒ‹‹‰ˆ…ˆ…‡ˆ‡†ˆˆ‰‰ŠŠŽ’””’’‰‰Š‰‰‰ƒ„†‡‡†„ƒ‚‚‚‚„‡‡‰ŒŠ‰ˆ…„€{xyvz„…‡†‡‡ˆ†……‡ƒƒƒ‚ƒ„…‡ˆ‰Œ‡Š‹ŽŽŽŽŽŒŒ‰Š‹‰ŠŠ‹Š‰ƒ|ubSPS^fis|~„‘”}eg}Љrhht’¨ ‡†¿¼¯²¬œŽ…ž–‡”‘©ÏÒÐ¸Š‚spXOQfsy‹ºË¬€…ÑçèèäáΪ˜ŸœŒzuc]VROHFB???Pç÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñéÏ«wH0,+++,,,+++,,,+,--,,-,,04;CBLJ@AHA<<;;8AB?@3,+*3LJ;2,5<3),0/.,***)))('&%$#! !!""!!##$###$$$$$%%%%&'&&%%$#$##$"!""######$(A`acdefeededfffffed`adcefefedcddfehijgjhhfhggghghggfheedcbcdfgiilfgggfgggggghge`gggghgiiiikjkkkjjhlkljlijmllllojjiihljlkmkkiihhdccdefffejloonmkgmnpppklmommmlklljedccdddfhlkjhhihhgeeeffjnopssvrqmmmhcddjjji“Œ‹‹Œ•’”˜—žŸ –—”””–™›™š——•••””–—žœŸ˜˜—œ›š¤———–•’‘‘“‘”ޑދ‰‡……ˆ‡ŒŽŽŽŽ•””’Œ‰‰‹ŒŒŠˆƒŠŠŒƒ€‚ƒ…ŠŒ‰‰‰‰‡†ƒ‚‚€{}ƒ…‡††„ˆˆ‡ˆˆ†‹‡‡ƒ‚‚ƒ…ЇЉ‹ŒŽ“’”ŽŽŽŒŒ‰‰ŠŠŠ‹ŠŒˆ‡‡†}s`RPS[glvy‹‘“‚u]Ucyxhiq‘ž³‰Šœ¼¹©ª¬—‘’™¨µ”“¥ÕÞÌÆŒ€vqaQPblq½´yÉæèéãÞ¦¦©Ÿ‰zwb^VXOID@>??M‡ãöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñèË¥pD/+++,,,,,,,+,,++,------2<F?@NK@EF?;99;>@BD:1++*4HL70).0.)(**)*)*()('%#" ! !#"!"""!##$$##$&$%%'&&%%&&%%%%%%#$#$##""###""%*HbedhfhfhfiflllfedeffdcehefddeechekkkkkghhhhliiikhhgheccdcdehjkjmgigkfmllkmgifddhflkjkljliokoonklgllllpiklolnnnjlkkhkkpmnklihgfccdeeegjjlmqonkklnpspomnnpmmmlkkjjdedhhihllmjifghhfeeeeihkooottwrqlljecbdkknmŽŠ‹‹ŒŽ”•˜™›œ››˜•””––˜˜›•••˜“••”“••˜šœ›™—˜–™š›——”––•‘‘ŽŽŽŒŠ††…ˆ‡ŒŒ’ŽŽŠˆˆˆŠ‹ŠŠŒ†‚€€€~~ƒ†ˆ‰Š‰ˆˆˆ†‡‚†‚‚ƒƒƒ„„„…„ƒ„ƒˆ‡‡Œ‡…†‡‡‚‚€‚†ˆ‰ŒŽ‹‹ŽŽ’ŒŽŒ‹‹‹‰‹‹ŠŠ‹ˆ‰ˆ‡‰†wk^QMO[gjn†¢’u`NJLk¨›€khnw‰‹Š’¢¹ž –˜¨»Ã²—‘ŽœÚͳ¥Œ€xxw^Q^lq±šu~ÂäèêãѶž•¤¡‰|n_XSQNH@?<=>JàõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøñæÈ¨jA.+,,,,,,,..,-,,++,.-./.8GE<@MD<GME>:59DAA92/++)/@72.)+,+)*+*)*)((&%"! !"!!#""##$$$$$$$$$$$$$&%%$%&&%%%%#""$#"""#""$$##%-Mcdbhffffefehhgdccdbdeddfdfba`dddejjihgefegiiijjjiidcccadbegjkkiifgfeefgkhhffdegigkijjjijjkkoiiijilmkkliklmkllljhgiilkmlmihfgfgaccccfjkmlmqplimmoqqqpmmnpkkkmjigfdechghimnkgfefggefcefhhkpoovqrpommjd^belkmnŒŽŽ‘••™šš›˜•˜žžšž˜š”•–™–›•–•˜•˜ššš–ž–ššœ™š˜ž™œ”““•ŽŽŒŒ‹‹Œ’’˜Œ‹‰‰…†ˆ‡ˆ‹”ŽŒ‰„€}{~ƒ‰‹ŒŠŠ‹ˆ‡‡ˆ………†ˆ‹ŠŠˆˆŠ…‡…‡‡ˆ‡†††……‡ˆ„ƒ‚‡‹Œ‹‹”‘ŽŒŽ‹ŠŠŠ‹‘ŒŠ‰‰Šˆˆˆˆ„um]NJLV`n…‘Œ{[KAFb¤³Šploonw‘›™™“‹˜”Ÿ½àÛɯªš—³·®«“{¢‰iZ]lrÅ•u{µáããÒË©•¥Ã¢‰whZROMJF>?;<=H{ÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøðåÇ©f?.+,.,+,,,-,,,,,,,,,,,-/<JE;@GC=KOLC@44:;62.+*+*,01/,)))))((*('&$##! !!""""#$##$%%%%$$&$$$$$&&%%%%(''%%###$#""##%#$###&1Scgeiiiffeiejggdccfghegdidfaaajglfjgffgfhefjmlplmjkefcdddehkmllkjfhggeikliiffefhigkiljjkkktmnjjjlmoljjllklnlnloighjlqknkmhiglfgcdddegjmmonqmjltstrtrqqqnpkmmnihfddeeijnlrlkffehgjfhceekghkpowvusqrske`chlllmŽŽŽ’’‘˜–™š™˜šœ›šš™™–—•–™—”•”––—”™šš˜˜•—–š™™˜™—š™š–”’•‘‘ŒŽ”ˆˆˆ‰‰ˆ‡‡……†Šˆ‚€~~|€…‡Š‡†‡‡†…††‡ƒ†…‡ˆŠŽŠˆˆˆ‡‡†……„………„„„„ƒ…†ˆ††…†‡‹Š‹‰ŒŽŒŠŠ‰‹ŠŠˆŠ‹‹ŠŠŠŠŠ‰ˆˆ‡ˆ…†‰€yri[MJLZn„‰†iLDE^ž›•vmom^^}—•‹‡zo|¦ÐìêÙǾ§¦®³®° ƒ~°vZ]ku¬©•uu§ÍÞɺާ¸Ã£ƒoaTNIHGB=<;;<GtÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûøðäÆªc=-,,-.,-,,,,,,,,,,,+,-00?LG<AJ>4>LC@8/011/.,,,+)+--,+))+))''&%#! ! !""$#$$$$$$$%$%%$#&$$$$$&%&&%$$#$%$#####""""#"####(6WcgdhcccfefeddeedcehgedccbcbbbifhffeedffffgjkkjjiggdedcbdfiklggeffffgdjiiggffdghigjjlkkmlllmljkkmmojiikklmlknjjiijkkkkkjigggecgcdafkjjlmnmnlllspqqqooopmpkkkmgfedceeikmnrifbdfffgeddedgghilnooquwxsngbhmmllkŽŽ‘’’‘˜˜˜œ¢¢£œš™™˜˜™˜š———™šš—™—šš›˜›–œœ™™˜œ ™Ÿ——•–••••’‘‘”‘‘‘‰†‰™†‰ƒƒƒ‚„„ƒ‚„…ˆ…‰‡†……„„„„ˆ‹‹‰‹‰ˆ‰‰‰‰ˆ†…††……„„„„…‡Šˆ‰‡‡‰Œ‹‘•Œ‹Š‰‰‰‹ŒŒ‹ŒŽ‹Ž‰‰‰‰ˆ‰ˆˆ„„ƒ‚{xtpbUOPbƒ‚[GL[„¬œ}syhOUpƒƒxspjj„ªÚïððìàÄ·°±¨”…‰œžƒYXkx‰¸©–toµ²³ÂÁ¾¾½À™}j\QLJJHB=<;;=FoÙòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýúöïãê^;-,,,,,,++,,-----,-,,.00CKGFFB945:;61,,,,,-++++***+****)(%$#" """ ""!"#$%%%%%$%%'$%%$$%$%$&%'''&%$%$$$$#####""######$$*<\dhehdefeeffgdeehdeghdcbcbeeeejhkffeedjggfiillmihgggggfffgjjlgieffjfggljijighfjiiilmmlmmololqonooppkjjoksmlknllkkjmkjjiiihhecbhdeeikjjlnrnnmpmtqurrowrtppmnkllldefjhnmxosgcbcglhllkihghgkillllpszutljmqppnmkŽŽŽ‘““””“™œšœ›››™™—šžš—–”—‘˜—––™šš—™“™š—™š›˜–“—••’––——”’’‘‘”Œ‹‹ŒŽ‹„€}}y€‚‡‡‰Š†…‡…„„„„ƒ‚…†ˆ‰Š‹Œ‹Š‰‰ˆ‡†ˆˆˆˆ†…„‚‚ƒ…………ˆ‰‰ŠŠ‰‰‡‡ˆ‹ŒŽ‹ŽŽŒŠ‰‡ˆ‰‰ŠŒ‹ŒŒ‰Šˆ‰‰Šˆ†ƒ‚‚‚‚ƒ~„~yiXTUhzz~~bRPXl‡¢„rjcLN`w{}rkfX_{¨áîõúôêÜõœ˜‹”›Ÿ}WUjt€œ¥–sm|™©ÃÝßÝÎÄ´xfZPMKJHC?=<<=FjÞñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõíâÀ¢Y9-,+,,-+++,,--,,-+,,-...=OEB<:4,/11/-+,,,+-++,,,*,*)**'&$"! !"!""" !!""##$$$$%%%$%%%$%%%%%$$$%%')(&%$$#$$$###"!""#######$,C\cdceeedddfedccccbfdcba`bbegffgghefdedgfffjihfffheggfdfeghjiigfcfffghhhhijighdggjkklljmnnkklpmmnokkkkkljjjjijkjjjjmmhfghhdedbbcbegjkjjmnonmmnnnnooooonoonmmjjhheffghnnqokda_dimghhjjhghceeeehlmlnoolmppoppol‘‘•”›–—“œ›™›œ›š˜˜˜šœš™˜˜˜˜’”•–•••š››™š˜ŸŸžœ››žš˜•””—“•“˜—š˜—“–’’’“‘‘•‘Ž‹ˆ…ƒ€{z{€ˆŠ’ˆ††…‡„ƒƒ‚ƒ„†‹ŠŠ‰‰ˆˆ‰‡‡ˆŽŽ‡…ƒƒ††‹‹’‹ŒŒ‹Šˆ‡‡Š‹ŽŒŽŽ•‘ŠŒ†‡‰ŠŠŽ‹ŒŽ‹‰‰ŠŠ†„‚…ƒ„†Œ†ƒn\UWbjryeUSU]j~ngaIISerrlgcUSr¨ÚîöûøôåÍÅÅ¥’”Ÿž‰w_Shoyžž–tk{ŸÄßßßÕØÀ©ŽraYPNLMFD@><==FhÖðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôí὚V7-.-,--,,+++,----,,,--..9?A;62/+++,,,+++++,+++,**)(&%#"" !!!!##"#"#%%%$%&%$%%%%%%%&'''%%$$$$'++)&&$%%%$%#""""$$$#$###$#/J\cgffffcdehdccddbbfdd`_affffiggfieffeekhhhkiffjfhgghkfggjknijgfdgghhljkjijkiiiklmnpllkmnllnopntmoijknkkikjjijjmkqnnjffffiffcaaeelinjiksqpnqrroqoqoooonppnnojliiklklnonrlga_`ghmiikmljhhbedbcginnomrnqqrqqqql‘‘‘”””“’’’’“•–————˜–—˜˜——–”‘‘’’“–”••˜˜ššš™œœ›š›››—™””“”’‘”“–•—’–““””““‹ˆ†‡„„„ƒ‚‚…‹ŠŠŠ‡…€ƒƒ…„„„…†ˆŠ‹ŠŒ‹ˆˆ‡‡†††‡‡†…ƒ€}ƒ„‡ˆ‹‹‹‹Œ‡ˆ‰‹Š‰†Š‰ŽŒ‹ŒŠŠ‡‰ŒŠŠŽŒ‹‡‰‰ŒŠˆ‡†ƒƒ‚ƒƒ„‡‰ŠŒŒ‰…‚l[TS]gih[USPXcjjc`H?IVdqlebW]j•Õí÷üüûîßØÊ¯› ½Ã•€{dfhfe‚¡‘sk³àãàÚÔ˾¬v^XPMKIFCA?<<<EdÏðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùóìá¼–S5------,,+++,.---,,-----/3220.-++,,,+++,++++++*)&$#"! !!!!!!!!###"##$#$$%$$$$$%%%&()('&%%$$$&(,.*%%$%#$####""#$$$"###"$#3P^cgdffgccfeddcbba`aa_^`bb`ecefdcccehgejhhhhgffeehhhggghhjjjghgfcghihhiigjkkfijklmomkmmmmllmopklljejjlkjhihhijjkjmomhecdceegaaadekilkklooplmlonnmnmmlmnnlnkjjjkjkkknpolkgc^_begjijnormiibdccdggonnlkkmnmlmklk•”“‘’—’’’”•˜––—™˜™—›•’“’‘’•—”–•––œ–™šš™›¥œžœœ˜š”“”𔕕–—˜™™˜—“™”•Ї„ƒƒ†ˆŠ’’’“’‹ŠˆŠ‡…ƒƒ„Љ‰ˆŒŽŽ”ŽŒŒˆˆŠŠ‡‰‡†ƒ‚€€„…ŒŽŠŠ‹Œˆˆ‰ŽŠ‹ŠŽŒ’’’‹‹‹ŠŠŠŽ‹Š‰ŠˆŒ‰‡†„‚ƒ…‰Ž’‘‰‰}hWOOZic]XRPNXfb`ZG>AKUbkgdgtyŠºçòöùûõïéÛµ ·Ô®z~k`ab`uŒnkŠÅÓàÜ×ÉÅ·¯˜}fWNKIIIIA@=>=DaÄïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùôìâºO3,+.----,--,--.-,,,-,++++-,,+++++,,,++++++++*'%$"! !!!""###"####$$$##$%%$$$$%&'')-./*'&&%$&')0-)$%$%$%#$##"$$%$%###$##$8V_dgfhggcdefefbaa``c_]_`bbbedecbbbcfiigjjlhgfhfgghhkhjhmhkjjhiihgghlhnjnkqkljnmpmnlkkpkjklloppjmkiijkkjiikilmmjklmllgedhdeeibccjhljokmmmnplmnqnnnommlpmnmnkkjjjjllmrqqmkdb]``bdnjkoqqpjkffccejjpnsljiwnomnjpj‘“’““”“‘‘’’”•–•––™’”“’‘’”’Ž”–”“••––––šš›™™™˜—™™˜”™’”••–—˜›Ÿœœš›“‹‡ƒ‚~ƒ„‰–“Œ‹Š‰‡ˆ‰ˆ………‰‰ˆ‡ŒŠ†‡Š‰‡…‚‚ƒ‚‚€€„…‰‹Žˆˆ‡Š‰ˆˆŠŒ‹ŠŠŒ‹‹‰Œ‹ŽŽŠŠ‡‰‰‰‰Š„……‡ˆ…„…‚„ˆ‹ŽŽŽŒˆŠ‰‰ˆˆ€}pbSPQ[ca\TPNOYb[TE?ABKXl}vu~Œ‘§ÑëðöúúûòãÀºÃÁž}z{sqoh^lˆyhm•ÆÌÊž¹³¬§¡‡lYLJGILF@>===E^ÂîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøôìจL3,,.------,,-.--,,-.,+,.,-,++++++-,-+,*+*)'%$#! !!!!!#"$"$#######$$$$%$$%%$$&)((*,150+)'''''(*+,)$%###$#$"""$$$%%#####$&<\adfffegcebfdcba``_^\`aaaccdba_a`cdfhigjiiggeffgiifhfhhhhgfhgffhghhihkiiijgkjmmmkljjjjgkjllljjiijjjkllmjhijlmlklmlihdeedddcbacchhhhkkljlkkkmnmmllplkkmmlknhjiiiklmmoomje_ZVYY\`nkloolpkjgfccbeikmmliikmkijjihšššš›”———‘“–™———ššš”’Ž‘•™˜–•”“––™™˜š¡š ˜——˜—›˜——˜˜˜˜™œ¢£¢›˜’‡‚‚~‚†Œ•‘”’ŽŽŒŒ‹ŒŒ‘Ž‰ŠˆŽ‰Œ‰Ž’ŽŽ’Їˆ…ƒƒ‚‚†‚……†‡‡…††ŠŠ‰‰‹’Љˆ‹Ž‹Š‹‹ŒŒŒ‘‘ŠŠ‡‰‡‡ˆŠ…†„†„‚ƒ…†Š‹ŽŽŽŒ‹‹‹Š‡…€}wpdWQPZnaYROQV[YSDDECF[q†’€‚Šœ¬ÃÜêñöùüõêØÐϽ™{zƒƒ‡xkalneo™ÉÊÎÆ²¬§¨¨œ“o[KGFIGD?=<=>D`ÁîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøóëÚ±~I4.----.-,-..-..--,--,,,,,,++,++,+,+**)('%$#"!! "!!"""##"###"$##$###$$%$$$%%&()((+,+,8RB4+*)**))+***)$%$&$%###$%)(((&###%#%(A`ceffgfgcecfdccdbb_``fdecfgga``dceefgigoijjkgihkjjglikijhggkkkfkiokkimikkjgljqnnkllljiiklpnmjkippppqnpljhllqmnnrmkhhgheeefdecghhhjhmklllijkomnlmlpkrkmmlknjjjljmmqpqnohc\WLJKPamkonnnpkkhiefceceglhgjrkjijjji—˜˜˜–”””“““˜––———•”•‘‘“••”••”““”›š›œ›ž˜—–˜”›˜šššššš š–—˜–‘’Œ„„†‰‘‘ŒŒŒ‹ŒŠŠŠŽŒŠŠ‰ŠŠŠ‰Žˆ†„ˆ‚‚€‚‚ƒƒ‚„…†„††‡†ˆˆˆˆŠŠŠ‰ŠŠ‰ˆ‰‰‹ŒŒ‹Œ‰‰ˆŠ†……‡‡†‚…„†€€…†Š‹‹‹Š‰‡…ˆˆˆ‡…‚€}xufXSS_l`TPPRZYRGFHHGa”…Ÿ¸ÄÎÚëð÷þøðêàÖ¬„|‰–«¼Ÿofk]q›ÇÉÈÇž›“…r^KE@ACA><<=>Ea½îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýýøòêÖªwE30.-----------..-,--.--,,,,,+++,,,*)&%$"! !!!""##"##############$$%$$%%&&/:63<K9./=IF4,./01/-,*)(&$$#$$$#$$')-04;+$###"%*Gabffdgggceafcdedbb```dccba``]`acdfefdggihgddeggiegghhihhhhijihgjjnkjimijiigjjlkkiihjijklmoplijjnlllllliigmlnlnnmjihgcieebcccdffgehhmjmijikkkkllllmjjjjijhijjjljmnqpmjhd]VH;AEVenkllmlmkkfedebaacbdafijhiiiiih–—š–”–˜˜™•’“••œ›˜“’‘’š™˜••••–•”••œ¢œŸœž™œ——–››Ÿœž ›š˜••“—–—›˜–—––•ŽŒ‹‹Š‹Œ‹‹•“’–‘ŽŒ‹‹’ŽŒŽ•‘Ї‡…‰‚‚ƒ„„…‰„„†‹‡ˆ‡Š‰‰‹ŠŒŽ‰‹‰‹Šˆˆ‰‰‰‹“Ž’ˆŒ‰ŠŠŽ‰ˆˆˆƒ‰„„……„Œ‹Š‰ˆˆ‰‰‰ˆˆ‰‹ˆ…ƒ‰ƒ€~{i\TS\hYRPMR[QIGGEHcœ“‹u‚µÇÌÒàëôúù÷ôìÙ£‹t‘ÕÐË‹}tns|›ÄÅÆ¸ªš™˜˜‹tcMD==?><;=AAF`¸îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷òéÒªqC1.----.-,-/-,-...--,,-.,,,,,,*(('&&%$""! !!!!""!!!#!"##"##"#$$$$$$$$$$%%$$$%&&(.:NOSYQ@13CSA438:=??70,+''%##$$$$%(-8FHJ>0&$#$$&-M_cdefjghdeageidebc`achcb``aabdcffjgedkhlhgdifkghfgilikjkilmmjjhkjnjlknikjlinjmjiiihnmmknmrolkonoknmllmkmlmnqmplkihglgjeebbchhhgggllmknilkmkskmkjkqkkkkklghjmmllqrrnlhf]XJ;6;Ncgolmmllnjkgfdeaccddfefjkklikihi—•™”—™˜˜™—”‘‘’’“””••’••˜–”•–›žœ™š›—””•——šœ›š—ž˜˜—˜—𡡣𛕓’’‘‹‰ˆŠŠŒ”””ŒŽ‰‹Œ“““ŽŒŒŒŒŒ‹‰†…ƒ‚‚ƒƒ†ˆ†††„„„‹‡‡ˆŠŠ‰‡‹„‹‰‰…‡Š‡ƒˆ…‡‡‰ŠŠŠ‰‰‰ˆŠ‰‰‰ˆˆˆ†ˆƒ„„†‡‡†Œ‰‰Šˆ‡†„‰†‡‰‰ˆ†„„„„„yj[TQZ^UPLLMPKGFCF_˜ˆ€”±ÔèÝÜãñ÷úýùôß±Œ‡‡¹Îº”“³°®¦ž±Å»¬¢™”Ž‹ƒ|udNE<<<;:;=>?E_¾îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ñè̪nB1/---------,,,,---.,--,,,,+*))%%"!!!! !!! !#!!!!!!!!! "##"#$###$$$$$%&''&&%$&'.28=OpR\[]A27HP><:CKOQIC6-+'&&$#(+)(16=HFOI?6(%###'0Q[^`ggecbad`fdebcaaacfga_]acdeddfgjhgdjffefdeekggghhhgggiikkkljhhhihhiihjijijiihhhhhhgijnmmmmlllkkkikmlilmoomkkjihhgiffccadehhhhhgkkkjiikkllmjjhhhhgijjljehhiilmnnnljg]RH=63Lfeilkopmkjijgfccbegggffhiijihjghi––™˜ž›œ—™••‘Ž“’—•™““”—“•–——™•–—œ›š™˜–”•—˜œš™››œœœŸ ¡Ÿ§ž£¢¢™—•”’‘‘“ŽŽ•–––—’ŒŽŽ’Ž••˜’“ŒŠŠ‰‹ŽŠ‰†…„„…ˆ‰‹‡‡†‰‰ˆ‡Œ‰‰Œ‘Š‹‰Œ‡ŠŠ‹……††…ˆˆ‰ˆŒŒŒŠ‰ˆŠ‰‹ŠŽ‰‡‡ˆ†‡††‡ŒŒ‹ŒŒŠ‰ˆ‡†‡‡Š‡†‰‰‰‰‰Ž‰‹‡†€}jZROSZQKKJNLKIDCTqu{€‘ŒáäæÝàñöùýûùè͹§š‘™°¶°–“¯¿¿×«¶½¨–’Ї€|seOE>=;:::<>?EaÅîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷ñèǬk@1------,/-.,--.---.--,-++*)%#"! !"$""""""""!""""!"##$#$$###$%%%%&,124,()+2?I^`bnV\naC69BN:8>RX_WWJ;.+('&%(/117GCOKHLM@B(%$$#(4TZ^_idccbadbeegccdeefffb_`ggggggkilijekfgdjfggklllkhghhimkokmklmmhkhhiihnnnjlikhjhhgghijnmllrllkijlgokkkpounmjkiihohlfgbedihiijjkjlkkkjioooklkkgihgfhiiijfihjkqqqnpklaVF>51Bfkiiompookihjhggffkknkihkirmoijhjk–—˜˜˜˜—””‘‘’’’’•••“““““•”–—–••˜˜—–•™•˜›››žžŸŸ¡£¢¢ŸžŸŸšœœœ™˜—––——˜š–“’’‘’ŽŽ‘‘“’’’’“‡‰ŠŠŠŠˆˆ…†ˆˆ‡ˆ‰ˆ††‡‡ˆ‰ŠŠŠ‹Žˆˆˆˆ‡‰‰‰‚…„……†‡‡‡††‡‡‡‡Šˆ‡‡†…„„…„ˆ‰‰ˆ‹Š‰ˆŒŠ‰‡‡‡‡ˆˆˆˆŠŠŠŠŠ‹Š‰‡‡ƒ~xgXQKQTJFHOMQIDABVjs|ˆ’‘¦ÈáÜØàñôùþüûñäßÎ³š“®´¶˜Ž©´º¿¦¢®°£˜‹…yuobPE?=;;;;;=?FbÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýü÷ñèÅ®i>0-----.,---,,,-,,,--++*'%$"! !"""""#"##""""##$$#"#"$%$$$%&'%%/CAA=48@?ANV_dfk[ZbdC458868AU^]WSO<.,)))+/;FEEG@GNCG=51'$###)8UY[]^^_]__`aedcaddeefdfbbbedfgghkgihgefffejffhiiihkiiijkmiiiihlmifhihhiinnmiighgjghhikkkkkllkkkjihhhjhjloonoljiiiihhhedbefiiijjjkjlkkgkijhjiijhehggeihhigefgkmmnnlkjdXF63.@^goljnlnoniihiigeghjknlifjfkklijhih——™–––˜”•’’“”•–š——–•“““•”˜˜––••––•–™™Ÿ¢£¤ ª ¨§¥¡žŸ›œ¦¥£¢¢¡›œ—š“‘‘•”•––™œ››“—“‘‘’”“‡‰‹‹Š‹‰‘ŠŠ‰ŽˆŠ†††‡Š–’‹ŒŒŒŽ‰‰‰‰‡†‡ˆˆ‰………„………ˆ‰ˆŠ…„„„„…‡ˆ‰ŠŒ‹‹ˆŒŠŠˆŒŠ‹‹ŒŠŠ‰ŽŠ‰ˆ‹…}ygYOIHJDEIKJIE@BHMQeˆ‘»ÃÊÌÓïôùýýýöðçÔ«¡ž²¹Ÿ‹’²¶¼œ™¦²¢”ŒŠxtsh`PF?=;<<;;=>FbÉïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûöðçįf<0--.-..----++--++**)('%#!! !#"$$#######""""###$#$$$$%$$$%&('&'5P]QNKLS_bggjfpjZYaRA6/.048CZ_`XUJ<0.-/4@GNNYNT>B>61.+(&###$+<WY][\]a]_^_adcbdiijgjeecdchefglkkhkiifhfkjjgfgkjllmnnjkknilkjilkhgjjrkmjonmjjjjjjjkjkmqkljqlkjkjjgjknjnlsoqljjjjjjighfdeiglklkmmmlpkjiljkklihhghhgjghhigeefhonrrrllhdK92/6Lipponmmnmnkplmiggjkolmlijigllpkjiih–––”–•’‘‘““•™—š™˜“Ž–““’•”—›—•”“–——š›œž ¢¢¡ ¡ ŸŸŸžœŸ ŸŸžžžžžŸš›˜˜–•–•““””’•›™›™—•’‘Ž‘‘’ŒŒˆŒŒ‹‰‰‰‰Š‡……………ˆŒŒŒŒ‹‡†………„„†…†‡‰Œ‰†††…†…„‡‰ˆ†…‚‚‚ƒ‡Š‹‹‹‹Š‰Š‰‰ˆŠ‰‰†ˆˆŒŠ‹ŠŒŒŠˆ‡ˆ‰‰‰ˆ†‚~ysgYNDDDDFKJJHDBGFHQh‚Œ–¨¶Ê½ÉïõùýþþûøçÁ§§£¢¤¤‰{”“„•¤¡¡’…~xsmh`XOF?=;=@?>=>GcÍïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüûõðç¯b:/--.-,---,-+,/+)('%#"! ! !"""#$$%$$##########$$$&%%%$$$&*-/,-Fk[XTRU^]\eVgegiSOHC<70,/47BLSQNHB;29?@BGRNKJA:///,'&%$$#"$$,BVXYYZ[[\]]_`bbdggfggfddbddheggkjiggfhdfeggiggejjjihfgghihghfjhhhhgijlijikiiiiiiijjkkmmlikkmkkjkjjhijjjnklllijljjjkifeegijgihkklmmllkjjkiihhhgfhjhfffhfgeddhklmmllljeN;98=E[oopoonmnnlikklhhhjkjjmlkiiilkkjiiihš”––•’“•™™˜——–”“‘“˜”““”•————š“––—œ¢¢¢¢¢ £¢¥ ¤¡ ž§¢£œžššš™š¡¢¢™›˜œ•–—˜™™”’‘‘’‘’”މЉˆ†…†ˆ‰ŒŒ”“ŒŒŒ‹‡‰„ƒƒ‚ƒ‡…‰‡ŒŒ†‡‡‡††‡†ˆˆ†‡ƒ‚„ˆˆŽŽ‘ޒЉˆ‡Šˆˆˆ‰ŠŒ‹‹’Œˆˆˆ‰„ƒ€zwhXKBBDEIJLJJHFEBJXnƒ˜¦¶É¹ÁíõùüýþüúïÍÁ»¡˜•‡w~yry”–˜–’ƒwtqhe\VMF@=<>?>==?HdÐðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõðæÀ¨^9/-,-----.+,)(''$""!! ! !!"!!"""###$$$$#"####$$%%%&&%(&%%$%(4A73@Z\]ac`[VQ]hYf\WPD?=:761./36<ETRHA?9;LQUOMQRCE5-)%%%$%$###$'%.FVXZYYZ[[]]aaabegigngfefdhghhhjpjmggefghfgglgihkhgfeefeijjjjjiiijjjjjmknmmijihinikknntnnjmlonnkklnmnoonnnnmkjkkllmihghfjjommiqknnrlnmpmokkhmhfggggghhighfgdmnnmnkmkkS>7:BM[qqrqvqrmplkilllkkjlkijqmmjklpklihilg“‘Ž‘‘”–——˜—–••““”•—˜••˜–––”––—”—˜œž£©¥¢¢ Ÿ¡¢¥¡ ŸŸ Ÿšžž¢¢£œœœ›žžžž›™™–š’–”™•˜“””’‘’’ކ‡……„†‡ŠŠ‹ŠŒŽŒ‡‡‡‡‡ƒ…„ƒ‚ƒƒ†„†‡ˆ‡‡‡†‡‡…†‚‚ƒ…†‡……‡ŠŒŽŽŽŽŠŒˆ‡…ˆ†‰‹ŒŽŠ‰‰Š’’‹‡††„ƒƒ€€|yvfWLACEKKLJJJHGFFO\|ª³¹¿¸½êóùüýþýüøïâÍ¿³ —Œ‹sziefy‹Œ‹ƒ{sp`[UQKE@?@BEA??@HfÒðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõð໡[8/--.-,,-+*'%#" !!!""#"""###$$$%$$$##$$$$$$$$%%$&&()*+6BEDHUWpnnno_UZ\SLLKE@>:977531347:JUUGGGECQ\ZZH?9/+''#$#####$##$$%2LUVVVYX[Z\]^_abffhfggfcdeefhjjjkjhdheffffgffgihgefceeedigiijgjiijiikkmjkijijhhgmjllooolkikkooliklnmmnnmmkjgkkjdhggffeggjkkkmilkmlmllllllkjijiijggghgghfffffjiigmjmjS>:8DP_moqqovqqmojkhhhjjjjljjjlkjjlmomnhijmf‘’––—™ššŸ——–—•”•˜—›˜˜˜š™—–˜–•–˜šž¤¡¤¦©¢¥¡££¥¢¥¡§ žžŸžžž¤¤¥¤¥Ÿ¥Ÿ£££œ›š™˜™˜™”–“˜˜˜™™——”’“—–™‘ŽŠ‰ˆ‡†‡‹ŽŠ‹ŽŽ‡Ž…‡†††‡†„‚‡ƒŠƒ‡†‹‹‹Š‰ˆ†††‚ƒ…‡‰‹’‘‘‘‘ŽŠ‹‡‡ˆ‹‘ŒŽŠˆˆˆˆ–‘’ˆ†ƒƒ€€€|xxgXKCELKNNOLJIGFIWy¦ÄÂÀ¸¯ËêõûüþþýøôîäØÎ°˜Œ}wcZ_j~ŒŒ‚qn\UQOIC>?GMHDDBAHjÕðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîß·šW6.---,)('%$"! "!!!""#"""""##$$$$%$#%&$$$$$%$$%%$$%%&&*-7?AFTie`WXa_^\aiNB@BBA?;::7777789::<F][USSD<J_QGA<1*'%%&"$###$$%$%$#%6MSTTUZY][____eegfjhiihceegfiillligghfhghiighilfddeeeegghhiijhjjkkllmlmkmmliiijjmmnmpnolljkkqlkkqmnmnoppqkkilljfjgeddegjqmllnlnmrnmklmrmlmmjkjjhffghffjfffnilikhmkpYB44@Qbvnvttrvqupoljiijoopkmlliokjjqqsnohjkog‘‘’•™š™™™™˜˜˜–––—˜˜˜š›ššš™˜”––—™œžŸŸ¡¡¤££¡¢¢£¥£¢¢ Ÿžžœ žŸŸŸŸŸžšœš™™œ™›š›™™˜š””•””ššššœœœ››˜—••’‘ŽŽŽŠŠ‰‰Œ‹‰‹ŠŒŽŒ‹†………†††‡…„‚‚ƒŠ…††‡††‚„‡ƒ€€€‚ƒ‡ŠŽŽŽŒŒŽŽ‡Š‰‰ŠŒŠŠ‰ˆ††„…„ˆŠ‰‰ˆƒ€€‚‚€|~xwsfYODGLNPONLJIFHSq£©¯¸Èº²ÑñúüþþþùôóïçØÈ²•…zfc^gu‡ƒ{tkaUQMJFD=APPQRTNDJmÝñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúôíݹ’S4-+)'&$#! !!!!!""#""""##$$$%%%%&%&$$&%%%%&%&%&(+.14@OLSX][[\Z]fedLEA;7568656865569@<=>BJQ]d][P@;XOA70+''$%##"#####$$#%%%&8NMOSSVWYZ\\_`bdedeeebgdfgggiiiihfffgfffhigfhifddbeeddffhfgghhjgljkgmjjjkjiijkjjlmnkllnjjkkkkhjjjjllnoollkjgggggggddeeilllmnnklnmmlkkkkkmnjgihhgfdfhhhdbfggghhiimhZF;2?L^nmnooqqqqrpomlkklmllkjjkjihkjqpqmlilnng“”—›¡›ŸŸž™š™™¤œ¢œššŸ£››Ÿ™š™žž¥ ŸŸ ¢££¡¡£¤¤£¤££Ÿžž›© ¢Ÿžžž››œžŸœ¢œžž š˜™›››žœ›››¡ž¦ž˜˜••––“‘‘•’”Œ‹‰Š‹‹ŠŽ‹’Ž‹‰†ˆ…ˆŠˆ‡„ƒ„…†‹ŒŒ‡‰†‡‚…‚€‚‚ƒ‡Ž‘™’’Ž’ŽŒŠŠ‹Ž‹‰‰‡‡…„ƒ†…‰ˆ†……„ƒ‚‚‚ƒ…‚ƒ|~zywtj_NFGPORONMMIHNf‰ £®ÂÔ´¼ã÷úýþþ÷ðóõðêæßŶ”ydr‹ˆ€vldZSOJHFF=CMddghbLLpÜòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúóìÛ¼‰M.(&$#"" !"!!"####"#$#%""#$%%%'&%%%%'$$%%'))('''(0:FTW[TMTo\UXs[dPE<40.--021/./23477<>=ERQ]\]^aZQ<:HF5*)%&'''$$$%$&$$###%$$'9MPNRSUVYY]\badbfeeeddfehiliihiheeigiffejgfgheddedhdcdhhhhiglkjimjkjnlrkkiijkmpjmlokkkljollkjjojjjpmqppklllgghlkpecefhnmmlommkklnlllokjjmjgflhhhhefhjeabmikhiiijq`O;6;I\nosnqoqpssspooppopplmlpmmihiomqqppqqrnmi—˜š›œžš˜›œŸ£¡›¡ žŸ¢™™™ŸœœœŸ¡ ¡££££ŸŸ¢ Ÿ¥ ¢—™•œš›œ¢ Ÿž¡¢£ŸŸŸžžžœššœœŸœœšššŸŸ œ—–••–—–’‘‘ŒŒŠŠ‹ˆ†‰ŠŠŠ‰ˆ‰†‡ƒ‚‚‚ƒ‚„…„…‡‰Šˆˆ„„€~…‡‰Œ’’’’ŒŽ’މ‰‡Š††„„‡†………„…„ƒ€‚‚ƒ|||||zyj\PDHLOPNNMJHIZy†‘§½Â¯®ÕðøüýþõìóúöòðìåΤ¡Çƺsz‡~qg\XROIGDA<BJ`lt{gQNrÜóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùóìÙ¼E(#!! !!"!! !"!!!""#########$#$$%'&&&&&%%%%%%%'.6=3/49:>N_WQYVSQm\PHOYH92-*''',0.,+*.2479:?BFLTUZOOPQSB45G6*,-,-5A0&$##""""""###$)=JPNSSTWXY[\```_aba_cdddihhiigfeeefeddefggghhceeeefdefihiefgggkijjkiiikkkhjmmmlijkokkkkjllifhikjjjlkljkhgghghhkihcehiklmmmmllkkklkkklkjhhdeflhheheeddbcdihjggfkmcRD8>FXmklnllmnnooomoqpponmlllmmmeikmnqqqpqmqnlh šœŸŸžž™œ¤¥¦£¡¡¡¡¡žŸž£¢¢œ ¤¡§¦¦§§¡¡¡¢žœ ¥ŸŸ £¡ œ£¢£¡ ¡¡¢¥¢¢¢¢£¤¦¦Ÿ£œœ¡žžžžžžžž¤ ¡›œ–—–š–™”“‘’ŽŒ‘Šˆ†ˆŠŠŠŠŠŠ‰‰€}ƒ„ˆˆ‰„†…†‡‹ˆ„…}ƒˆŒ‘•’“’’Ž‘‘’’“’““ŒŽˆˆ‡‹‡‰„…„‡††……††„„ƒˆƒ„…‚ƒ„€}{vj[NDGONOONLJKN`m€ ºÉ·Ÿ¹âñùûýøòõùøøôñìݪ´ÒâØÎr‚|mbXVQOJHB?;>H]m‡€vVQuÝôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùòë׺w?%! !!"!!!!!"!#######$&%$#$$$$$%&&&&'&&&&%%%&%)>`L?DTQMSe^_WmQ<IpYM;861,&%$$%'++*)((-38=BCFMRP]\]KD@<72//.+,05;=HD6+%#%"#"!"##$#$+@JQNSSSUXX[[c_^_a``_dcceiiiiiffeeegecdhghgjgfdihlehhjjjjjfffiglkkjnjkkkkkkllrmkkpmnkqkjkljfegilllmmklmnhggjhjilgdbfimllmomnlqlnkkkrlmkmhjbcglijihfkdfeeelijgghlf^H=>HWlmnkqlkmonmmmnsssppnmlmmnmlmpnppsqrqsrrnkh—˜Ÿ ŸžžŸ¢¤¥¥£¥£¡¢œœœœ›˜š™›ž¢ œœ›žœžžŸ¡¡ ¡¡ žŸŸ Ÿ ¡¢££¤¢ ¡¡ œœ›¢œœœ›œŸžžžžžžžž›œ••””•˜’ŒŒ‹ŠŠˆ†‡‡‡Š†„ƒƒ‚~‚ƒ†††……ƒ„„„…‡‡‡„„„Š’“’‘”‘’’”—“ŽŒŒŒˆ„„……‡…ƒƒ„……†‡†ƒ…ƒ‚‚‚„„ƒ‚‚‚ƒƒ„|yvi[PDHOOQQPLJLPZf‰´»Â«š¼éöúüú÷øøûý÷òéà®´ÛÞãÕª’„|i_WSONID?=;=DThwyYRxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûøòëÕ¼q<#!!!!"""$"!""#&%%%$$$%$$#$$$%%&&'&&&%%%%%$$$$-F]X[]Tt[__]TLML97?F7*)('%%$$$%'((('()2<GQNLMSQNLID>60-+*)+*26>HFEEE;1&"""""""#!"#%.BIMNPPSTUVZZ[\]__^^^dbcdeeefecedfeffeeffffgfedhggehghhhhgggfhgiikkkjkjkjkklllikkmmnkkkllmhfehilklllklmkhhhihhigddcimmjljllnlmjnlllmlljiic_dfhgggiddcdeeehggghhj]O@ADUejklkmllnnfhjmnpppppnnmlinlllnmqppoqorsrmlh˜šŸ¡ ¤¡¥¤©©¨¤££¤£¥žœ™˜’“•›¢ ¡œž ¤¢¢¤¥¤©¤¨¢¡¡¡Ÿ¤ Ÿ ¡§¢¢ ¥ Ÿ¢ŸŸ¤£¢Ÿ¢¢¢›œž£¢¢¡¡ŸŸŸŸšœ—–•”•˜‘ŽŽŒ‹‹ˆ‰Ž†Š…„ƒƒƒƒ‚ƒ…ŒˆŠ††ƒƒ„Іˆˆˆƒƒ†‹•“™–•“”’’’”––“‹‹‹†‚ƒ„ƒƒ‚…‰†††‰ƒ„‚ƒƒˆ‚„…‡†Œ‹Š‰‡…ˆ„{zk]NEHPOPOOLKLO[u–¯¸¿›Ìïöûúù÷ôøýú÷îÚ¾¶ÊÕÝßÞ×´Žr[SOONFB><;<AL^o€~ZSxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷ðéÓ¾m9#""""##"######$$$$$$%&%$%&%%&(&(''&&%%%%%%$$$0Qv]j[WbYaicVKW=-,-,(%$$$$%$$$$%&&'''*<I]RdYVSRCA82-)''*,-/3BGPLDDUFB.'!#"!!!!"!""&2CJNORPSSTVYZ[Z]]]\\^cbddeeffedjgfffffffimijffeihhgfhfhmllgigkhhjpkljmnmkomlkmjkjmnnnnmpmlhfgiillrlllpjjefhkkkhgfegppolllmlnmonomooplmjke_blhjklgjeecefifigggmhiVGCFRffjjnmmklnnbbippqrrpsoqoqlnnnnomrpppqoqrroljš››œŸžžž¡¤ ›¥¢£¦¤Ÿ›—•”“‘‹ŒŠ–—¤¡¡Ÿž¡¡¡£¢¢¢££¢ ž›š™š›£ž ŸŸŸ¡ ¡žŸž¡ŸŸ¡Ÿžžžž›———˜™™™™š›ž™˜—˜™”’”ŽŽŽŽŒŒ‹‰ˆ‡…ƒ‚„‡†…†ˆ‹‹‹…††…‚ƒ„…„„‚‡…‰Ž’•’”“’’”ŽŒŒ‰‡†„‚|~€‚ƒ…‡‡…………ƒƒƒ„„„ƒ„…‡ˆ‹ˆ‰ˆ†„…„‚€}xj[PEIOOOPNLJMTh†›®«¢ ØóùúûöðöýüüñßÎÈÄûÕÞåݳ†dOLJHDA><;<>HVm{~[RxÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïçѽh8$"""""##$####$$%&%$%%%%%%%&&&&&'''&&%%%%%%%$%3Ye_bWVo]igq^G:-)%&&%%$#$$$##$&'+-+&(-AMMNSXJ@80,('&(%,149;<HWQU=6760)$!! !! "!!!"(8FIJKMMPSTXWVYZZZ[[\^cbbbcccceefgggghfehkkigefdjihfghgfffhggfkijkkkliiiiionllkkjjmmnlllkkliiiijmlmiiiiefcghiigeeehlkjjhmkllnkmlmlnjjjkifbdffgikjdfedcegfdffghgbZLJHSaefihnlmjih]Sbkoppnrprnnnnlmmnnnmmmmlnopqronnœž¥ž£¡Ÿ›¤¢¥¦¦™“‘Œ’’’ŽŽ˜ª©¨žŸ ¡¡§§§¢££¢Ÿšš˜™›ž£ ¡ Ÿ¡¢££¥ŸŸŸŸ š”’’’’—•——˜—˜¢œ ™˜—•”““–ŒŒŒŽ’Š‹‡…†€ƒŒŒ‘ˆ‹Œ‹Š„„„‰†…†ƒ„ˆ‘““—’’“”‘–“•”™““’“ŽŽ‹Œ‰‡„……ƒ~}{}‚ˆ‰‰‡‹ˆˆ……„†„„„‡ˆŒ‹Š‰ŠŠŠ†…„…ƒ†ƒ„…†{k^QGIPOQOOLMYczŒž°¥Ž—Ãíöùû÷òóôùýôëäÝÆÃÄÏÛßÜÛ©nOIHGDA=;;:=DRl}}[QyÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöïæÍ»d7%#""#$#"##$$$%%%%'%&%%&'&''&&''&&&&'''%%&'&(&6`seqVUTUiefe?-(&$%$$###$#"#%-75787('.SRSEDA=1($"#$)-17>E?>FPRQ?/+*'%#! " !!!)<HIKKOMPRUVVUYYXXY[^]cbbbdbbeghggghkhgfkkkigfggjjkhiilghfiffgkkmjjkliiijlonmllkjjonnllkjjllljqlmlnjiiifffihihfefhllkkljmlmmnkmlpknnnimhfekijiihhffedfjgfdggjhh]TNOVbejihglmmii]SXlquutsrrrssssnpopnpnnlklponoroomœžž ˜ž›Ÿ¡¢¡Ÿ›“’’““–—•‘’’™›šžŸžžžžžž£ Ÿžœ››››Ÿ¢š™—˜™¡¡ ¤œ››œœœ˜——–•—™—›˜œ™—š™œœš˜••””“”ŽŒ‹‹‹Œ‹‹‡†‡…‚ƒƒ‡‹‰†ŒŽŠ†…‚……………„‰Ž‘•”“”’’‘’’‘Œ‹ŠŠ‰ˆ†„‚€}}{€†ˆˆˆ†ˆˆˆ„„„„„„…‡ˆ‰‰ˆˆ‡†…„„ƒƒƒ„„ƒƒ…~{wkaTGIMOQONPhpz‹”¡¦Šw™ãð÷ûøõðëõÿøðæÔµ´ÄÕÙÙÚÙÕyQLGEC@=;:;=ENb}tZOwÞôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöîäʹ`5%$###$%%&$$%&%%%%&&&&&'&''('&&&&&&&&&&%%'&&&(6f`WSECEOhZTB/)%&#$$#!##$$&*29@EIO8+*.;;72.*'%$"&(2BCF=WMC>QB:2+'"!!! +?IIIILMQPRSRQSTVWZ\]]dbbbcadihihgijlgggjjkhgghhjjiiihhfgfgghhkkkijjjiiilnnnmkkgjjllnokgiimmljollllhiihggfhgfdeejmmmllkilllknjklkkkkmilgggggjjighfffghgggcfffc`ZVRZbdgghhflmkecT]hnsvqrppqrppqsnoopnplllllonnnponm ¢¢¡ Ÿ Ÿ££¤Ÿ›šœžŸžžœ›¢š™›Ÿª¢Ÿ¢ž¤¡¦œœ£››™™——––™™œž¥¤¤›œ›žžž˜›œœœ™˜—˜œ™šœš›Ÿš›—š—˜’”ŽŽŒ‹ŠŒŒŒ‹Š‹‡Š„ƒ„„†‰‰ŽŠ‡‡†‚ƒ‚†„„†‰Œ““—”••˜”–‘“ŽŒŽ‘‹Š‰‡ˆ‰……€€~~€…‡Š‡ˆ‰‰ˆˆ…„†ˆˆ‰‰ŠŒ‹‹ˆˆ‰ŒŠŠŠ‹ƒ„…ˆ„ƒ€~{zpeRFHMNOOTfƒ†˜¢“ksãòùùùôîöýúöìϺ©¼ØÕÛÚÚÙ†p]QGD@=;:;?FLXfpVNqØðüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõíãÉ·]3%%#$$$$%%$%%%&&%&&&&&%&&'(''&'&'&(&''&&&&%%&(6OWGC817LON=/(%$&$$###$&(,1:JIZNMG;2)*60)&#"!"%'-6?IKFALW@?93,$" " .BIHIJLMQORQOOPRUX_]b`ebddccihmllnommimgnkjjkhijmmnjkiigghklmkkjplpkiijkpnuoqkkjjkolnmkikiqnqmomlllhkjjhighfecfjsnponmnlllllnirkiikklijiogiinllgifefliihmdfff`_ZY]hgigjjjkllje^_nostƒqwvvqrszttsrpppqmnllmommntqun››œ™™˜œžžœšš››Ÿœ›› ›š››œ—š—™š œžŸžœ››š—”–—˜˜—••–———————–”“””””•–œ™™˜›œœ›š›œš———˜–“ŒŒ‹‹Š‰‡‡‡ˆ‡†……„ƒ€ƒ†…€~‚‚‚„„‰‘‘”••”••—’’ŒŠŒ‹‹‹Š‡‡„…†ƒ~|‚‚‚‚ƒ„†ˆŠ‡‡‡†ƒƒƒ†‡Š‹Œ‘Љˆ‰ŠŠŠŠ†Š„……‡„„‚€€}wl_SGIMNOTe€Œš¥§ˆn\f€¹ëöúý÷ñ÷üüüòÞθ«¸ËÜÚØÙµ£œŒtNF>=<>BHKNPMIHjÓìùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõíâȳZ3&$#$&&%%&&&%%&%&('&&'&&&&('''&&&%&&'&&%&%%%&(1A:0-+**182+(%%%%%%#&(,2:AEGKMLLHC99'%%$#!""*/37<LMWGDDB=9.'%"! !1CIHIJKKMOMIKMRUVX^]___`bacciimklmmmmhihmkkkjgklnmojjjjhiilmlllkkjjhhgjlooppmijjlmmlniihihklllonkkkgjijggghddchnmnnoppomlkllljlhiiihhgkihfhilmhcdefhhhhhhdedc`\U\dfffejilniggghimortrpvtvrstttsqqponnmnllmolmnommmœ™š˜™šž£žž››£¡§šš›››™˜›››žŸœšœžžœœœœŸžœšœž™•–—˜™™˜–”••——–—””•–“••œ–™–œ Ÿ žœ››œ™˜———š““’Œ‰ˆ‡††Š…„……„‚€‚‚‚~}‚„…‡Š’š”—–•–––˜’–ŽŽ‹ŒŒŒ‹Œ‰ˆˆ‰„„€|‚†Žˆ‰‰ŠŠ‹ˆˆ††…‰ˆŽŒ”””‘‹ŠŠŒ’Љ‰‡†‡„…‚ƒƒ…|vneTHHKMU`yŠ«ª©‡hZVj’Ðò÷ûù÷õôùýöîÞ˳§¾ÙÛÜÞßáâáÌŠWG==?DNOIFA?Cd¿ÝñøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùõìâÄ®W2&%%%&&%(&'&%%&&&''&&&('((((((('&&'''&&''&(&&')**(&&&&''&%%$&.3+()-4>AQR[WUQKIF71,%$$#! #-:BMKGNTNF6-)&$! #6EJIJJLKKKKHHNTUVX^]``cccbfcijnkpoqnmjllljoonoposmojnkmklioosnolnjkhhimotonmjjllnmmmmjijllllnlpmkjjjjjjiogiddejmoooosrunlkmllkojkllijhligfgjkigcdffhjjkijfhded[Ycchhhgjkpjdflmtrqqrrqpuuvtttvvvrvrqnonnonmplkmolmmš—˜—˜›œ›ššœ››Ÿ ž˜š’–“›•š ¡ ž›žžž ¡Ÿ›ž Ÿžž›š™™š›žœ›—––˜–”“”—•””–˜••–™šššœ›œ››————“–•“’‹ŠŠ‰ˆˆƒ‚ƒƒƒ„†ƒ‚‚|||||~‚…‡‹ŽŽ‘‘‘‘Ž‰Š‹Ž‘Šˆ…ƒ€€€‚ƒ†‰‹Œˆˆ‡‡†‡…‡‡‡‡Š‹ŒŒŽŒ‘“‹‹ŒŒ‘Œ‹Š‰ˆ††ƒƒ‚ƒƒ|yqgWIIMR[j„¹¢‹s[VWw¯Ýôùûüôìõþû÷êÒ¼´ªÇÚßäèèèëëÝ“WF>@FO]NC?<C_µÒèõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüøôëÛÁ¨U0&%$%$%%&&&&&%&'''&&&&''''''(('''''%'&&&&&&&&'(('&%$$$%&$##$-=969=AF@EQVJ?<:5/*'+*29.*&#*AETINJNGA3('%$"!!%;GGIJJKKKJIHLPRSXYZ[]^`abbbciillnmonkilkkknnoooonmnjjijjjkponlpjjgkiijmonkokkkkklmniklllkilllklmkhhfhhjiigfdghikmllmppomkjlmmkkjjiigjhjhfehkgcc`figeeegfggfbee_]_`cdfgjmmehklmspppqqrnqquuutttwqsrqoooonnkmmmlollk™™™˜˜››¤š™šžŸ›™š›ššœž £¤©¤¤¡¢¢¢ £££¢£¤¥¡¤ žž› ›žœŸ š™˜˜˜˜–•••–•”––œ••–›š™™œžŸšŸ—–••‘Ž‘“Š‹ˆ‰€‚‚‚„‡ƒˆ{|yx{€‚†‹Œ‘ޓޓ‘”““‘Ž‹‰Š‹”Žˆ†ƒ‚€ƒ„†Œ‹‘Œ‰‰ˆ‡†‡‡‰‹ŒŽ‘“ŽŽ”’’‘ŽŽŒ‹‡†…ˆ…ƒƒƒƒ‚ƒƒvjVIIMT]pˆ›œ˜lVT]‰Ìî÷ùüøõùýüûóêäÙËÂÆÊÙåâßáãâÚˆSHEMRZVC=<C]©ÓåóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÒÁ¢R/&%%%%&&&&'&&&&'''&&&&(('()')(&''''&'&&&'''&&&'%%%%$$$$###$+5FJJHOSB?FTFA3.*&#"%-7ED::@'1IKLLIG9-($#%'&$" !)?HHJMJKKJIIKQPRSYZ[[^]`afejiijooompljiklonqosptqpmnnmjjlssqnomqjjijknmrooonnonpkkmoilmqnqlnmnkllkiifjhpjjfeglmopqnqmqpnlklpmmllkmjjjjjlhggmihbdegijegfhgffedhde`cccbfgkkkhonposqwrttsqvtzyxvzuxwwsqpssronmpnpppmll™˜™˜——›››•™žž ŸŸŸŸ¡¡¢¤©¦¤¢ ¡¥ žžŸ¡¥ ŸŸŸž››š™™™š™˜—–•’‘ŽŒ‹Œ”‘‘‘’’’••–—››š––’“”‘ŽŒŒ‰ˆ†|€‚ƒƒƒ‚‚~}{{xz|ƒˆŠŽ‘ŽŽŽ“”Œ‹ŠŠŠŠ‰‡…„†‚}€€ƒ…‡ˆŠ‹‹ˆ‡††ˆˆ‡ˆˆŒŽ‹ŽŽŒŽ‘’’ŽŒ‰ŒŒ‰††…ˆˆ†„†††…ƒ~rfVIJNT\w‘œ¯£}ZTXb–ãñøüüýýüýþûöñêâÊ®·ËàÙÊ¿ËÞàÀˆgb]^aWF>:B[ ÖãñúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÐÁ™N.'&&%%&&&&&&&&&'''&&&''&(*(')('''('''&&&&&&%%$$$$%$$#"#$).15C\RRHZL>BE<1+%$"#"&)9KNXJG;+1NA;61,(&!##0F?;-# #-CIIJJKIHHGIKOPSSVXY[\\`bccefhkmllmljjklmoooopprpnjihkklmqspmmlkiiiklmmmmmkllmmqkkkkilmonpoolkijijjifjihgffgimmnmnnrknnnjlllkkklkkkjiihhghhhedbefghiddcbbbbecb`^[[[\^beefhhkmnoqnrqpnrprrrqsrqpooprqnomrponomnmnlmm™šš—–š¢ž£ž¦ ¢ ¡¤¦¦§¤ ¡¢¡ ŸŸ¡¡ ¡ ŸŸš™™šš™——˜—”•‘“ދЋŒ“ŒŽ’•””–Ÿššš›™™“”ŽŽŽ’‘‘ŒŠ‡ƒ~€Š‚†‚‚€}|}~}~„‹‹’”‘’‘’”‹ŠŠŠŒ‹ˆˆ†„…†ƒ‚‚„…ЉŒŒ†„„…‰‰‹“““ŽŽŽ‘‘“’’ŽŒ‹‰‰‡‡‰†ˆ‡Š‹ŠŠ‹†…{qfTHINTm“ °ÆŒ`TRWp¯äòúûýú÷øùùúõðëàÀ®¼ØÐÿ»ÛÞ϶²“}m`N@?CZ—»àîõúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü÷òéοK.((&&&(((&%&%&&'''(')&''((((((('(('''(*'&&&&$$$$#$$$###-<@ADIYVQJIF4,+)&! !$+/8IRUWVH7,131+%$##$#%/:FE?8)" ! %2FHKIIIIGGHKKOPSSUVXZ\\`addggilnmmmononssspqoqrwoljlkoopoqqolmlklljlmsssllllnrprqqjkipnrqtrtlkiijkjmjpjighgjlpmonnornsmooommklkomqjkjmhkijihddclhjiiccaa`_`dcd^ZYYZ]^abcejillopvptrqmqptqprtqnmkklnrrrnrqoosnpnrqqm–—˜—™›š™š›œœš˜•”“•—•Œ‹‰ŒŽŽŽ‹‰ˆ†ˆ‰‰‰Š‹ŽŽ’”’‘’“••”’’’‘ŽŽ‹‹‹ŠŠŠŠŒ”””‘“•”‘ŽŽŽŒŒŠ‡‡ˆ†…‚€€€‚‚‚€~|}}~€†‹Ž‘Ž‘ŽŽŒŠŠ‰‰‰ˆ„„ƒƒƒƒ‚„†‡ˆ‹‹‡†………ˆŠŒ‘ŽŽŽŽ“’‘”ŒŒ‰‰ˆ‰‡ˆ‰‰‡‰ˆ‰ŠŠ‡‡†……~wl`UKMOd–¡¯±žkURRcºì÷úý÷òóôøýøóñ騫 ²ÁÀÁÉØÙÛÚàÙÓ¤ygVJHPZŽ«Þëñ÷üþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýû÷òè˼‰G.('&&&''(&&&&&&&'((''&'((())**++++****))(((('&%%$$*/+&,;AVP]NTQO=2+&$#"!""(-4<BFO[SNKI0$&(&!"#(,/17DISLD5," '7FHIIGFFEGIKKNOQRUUXY\\`adefgjmljjkjhmmqppnonqqrlkhklooonqpplmmjijknoronklomnopolkjjjnoqpspokkhiljjlkjhgfgdjnmlllmonllkolnooijinjigiihhgghgfbcdlggheba_``__cbc]ZWYZ^_aaceffhjklllhenmmnpppimpnlkeeegiijkkklkkpnopon—™š—œœœœ›˜”‘Œ‡‰‹„|upliikmnnnmlheginponlknquz‚‰ŒŽ‘”””‘‘’“ŒŽ“‹‰ˆˆŠ‹‹ŒŽ‘’’‘Œ‘Œ”””Œ‰‰ˆˆ‡ƒ‚ƒ‚ƒ‚‚‚€~~~}~ƒ‡Ž‘ŽŽŽ“ŽŒ‘ŒŒ‹Œ‹‰‰Š†ƒ~€„ƒ…„„„„…ˆ‡‹‹Ž‰†‡‰‹’’“’–‘”ޔޒ•’’“—”“’”ŒŠ‰ˆ‹ŒŒŒŽ‹Š‰ˆˆˆˆ„ƒyumfZONZ~¡¦¡yZRRVk’Îñ÷ûù÷õôøüú÷ôìßž˜¨¿ÁÓÔØÜàæêÒÈ€taRW_k‡°ÞåäìóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöñèȾ‚E.('(('(''''&&&(((()''''()**.02356531/////.-,+*)+,.3>G*0CGNPPL>50,'"!! %(-4?TJXWWVQIB6)"!! !*7?NIHPXYYB5*#! *?FHHFEFFGJJMNQPQTVVXX[^dbcfghkmljjjjklnvpsnpotuulmnpppprrwppppkhirntptonmnmmowqokklomsoupsonlpjjkkkmiheefggklmlmlmnomkkomnoplmlmiigjhggiijgedefkgfecbdad^^`cab]]ZY]bbdabeifgilhe_[dlnuqupqginqljc\^acdeihlklkqpppnn•›š—–“Іytpmjgfeedcbabcdfhgfk[^TZVdcccbab^_behmtz…‡ŠŽŒ‰Šˆ‡††ƒ‚ƒ‚„†ˆ‡†ƒ‚„†‡‰‹Œ‹Š‰ˆ‡…ƒ‚~|||‚‚€~}{}}ˆŽŽŽŠŽŒ‹‹ˆˆˆ‰Š†‚€~‚‚„„„†††‡‡‰‰‰ŠŒŠ‰Š‹‘ŽŽ”Ž‘””’”••–’ŒŒŠ‰‰ŠŒŒŒŒŠ‰‰‰‰‰‡ƒƒ}zvqo\LTh}ˆ“¤{`ROSXw©Úôúûüøôøûûû÷ñäĦ——¬ÅÊÑÕÝãçßЬƒvqrurrˆÀØÎÃÊéõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûöðçÇÀ{B-''''&((('&&&'')(((''')*.2469@=<<<==<998521/3<::=?DK@,.WCIA;4,)$####&*-3;GGHPFKPIB7,)'# !%)/8AKLAIWIA<4+#! !.DEHGFFGHIJKMNQPRUVWXX]_`adfijkmkhjjkloppppmonmnnmmmmmoprtsoomljjjmmsponpqolmomlljlnmlrooonlmkjiijjihfecfgijklmllknmoomlolnqnmmmniiggffgjf\Xfeefjggcccd`]Waec`_\]\\_bcd`beedc`]VSQ\hlntopmjfikkkic_]^_abedliihlmnpol“‹ƒ|vqkeb_`abaa_bdeeeeimoptvmifeccbaaba``b^^^^aaejpw~ŠŒ““”Œ‹‰‹‹‹‰‡~|z|~€}~€ƒ‡ˆ‰‡……„……}}}~}€~||~ƒ‰Œ”’—”ŒŒŒŒŒŒŒ‹ˆ‰ˆ‡…ƒ~‚‚„ƒ„†Š‰‰‰‰‹Ž’‘’‘‘‘‘‘”””—™˜˜—–‘Ž‹‹‘‘’”–•”””’’ŠŠŽ‰‡„ƒ‚‚‚‚wlZNWcoxxwZROPR^‡Èæöøûúùúüùöö÷îÚ·Ÿœ¢¨¦µÓÛâå͹ ‰€‰”’‹w†·âÀ«ÍéõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïçÇÀw@-''''&)))'(&&''()()''),5@BCA?AB==?D><:><:::9;CHIZYYL:*,;C4)'%" !%)07;>BLPYTRAC:1+%"! !*7<@DIJPHOB91+'! ! ! !!"1EGGHHHHKJLLNNQQRVXZ\\_``bfjpppkjjmmprtuupqrspropnqrrrsqttsoqmlllnrrspqntqsmlkkkmlpllmroqmllpkjhlijhheffijoksmnmommlonnmpnronnxmnoonnijilZQZfhjjjggdhce`[[ceb__]`^__dddcccc_]ZSOMTdgssuoqjhellkjjdc^^^abgflijjmlkoqmqieba``_^^`deeffghkomlnpryu}yzqqqrheeedcdedcbaa`^\abjp‚‡ŒŽ‹‰…„‚~{yvsqrstuuutrrrsvz|„„ƒ„‚…}|z|z|x~z€}€„Š’’’‘‹ŒŒŽŽ‹Š‰‰ˆˆ…„‚„|~~‚…„€€„„‡ˆ‹‹Š‰‹Š‹ŒŽŽ‘ŽŽ’‹Ž‘“”—›™–˜˜–Š‘‘’••””——•’’‘’Œ‰ŠŠ‰‡‡ˆŠŒ†}tl]PUX_d^SPMPPWaÙîöùûýýþ÷ðöüòäÏÀ¨¤Ÿ®ØÝÝͳ¬Œ‰˜½Ñ”h{ª°³®ßêõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüõïæÆ¾q=,'''''''(''&&'((((((*-8DJOGCCCB>?@?<;<@@ADFMQVPIMNLL3&'('$$"##%'+0<MI[NMJMD:50+'$!! "$'*+8C[NNDDA>6,'"! "$8EGFGGHHKKKLONPQTWZ]]]`bdehlmkkijjmnrrsrqooproonnnpppoopqrsmmkmnoqrnpnnoqmmllmlllklkmnonnjmlkghhkijdddfgjlmknmomommmmmnlmmmmmnnmpqmljhijXIWijkjfgffba^_\]adg`\[[[\__``___`^YYWLDQ_adfhkkhdddca_^^_\Z[[\^cehijkkdWPjk[[\]``ddbbbcefoitnrpupxrsstvxurrqmjhjgpomgkfihh_\\^afs}„‡Š‹ˆ€tpliggiijlnnnnmmnnsvy~‚††‡„„|~}~}}||}€„ˆ…†‡ˆˆŒ’Ž“’ЉЉˆ„…‚„‚ƒ……‡…ŽŒ‹ŒŒ‘ŽŽ’Ž‘’’’‹“˜——™–›˜—’‘•˜—›š›™™™ž™›–˜”““’‹ŠŒ‘’“”–ŽŽ‚€yr_SPPRSOMNOOPVi£Þï÷úüýþ÷ðó÷õòíá³³¦’´ÏÙãàߪ“°×Û¡mf…¥«¨ÌäîöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûôíàÆ¹j9+(''&'))(''&(()()((*.8HQWQKHGDABBA>>>FHJQPOQRV^HMIF:,# !!!!"$(.8BGMNJSVKG:5.'$!! !#',3=+/@GNSE;40,%" ! !! !"'?EHFFGHIKLOOPPRSUX\\^_bdghjknkjknkonystppnnoqoonnotpoooqrrsnooooqrsnrstqwmnkllmmnlllppttvjmkjgihljjehggjmnqppnononqnnnnlmkklmmppuomkhhm]LMblnllhiefa`abZ_bgb]YXY^\d__]]]a`\WZSGI]^bbcdheecda`]YYZYYWUWZZacikoprfEFalXY[^^`aaa_acdegijijjihjljjlmmmkhhgfefgghigjfhhfddeimu€€‚‚‚~zvqkc`_bbddfgijihjhlmprvx€€†‡‡‡ƒ|}}~}~~€ƒ…†Šˆ††‡Š‹Ž‘’’‘ŽŽŽ‰‡„„‚‚ƒƒ‚‚„ƒƒ„ƒ…‰‹ŠŒ‹‹‹‘‘‘ŽŒŒŽŒ’Œ‡ŒŽ’”——••–—›˜—•–ššš™™šš››ž˜˜——–—˜“‘‘’“•”…ƒ€yoaROLMMMPOMNP]tåôøûüý÷ðñó÷üôêÒÍØÑÁŸ’Ÿ¯ÄÍÝÉÆÈÝÙ®ukoz€’²Ýàï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþýüúóìÜÆ²c5+)'&&&)((''&&'(((((+/@Ra[TPLKGCCEGFFLZURS^OFJND61-+)%! $+3<>LNWMTFLE>3(%#"!""&*2=?N4)2[C>;8-$" #+AEFGGGJLLMPPPQSUXZ\\_`ceghijojklnjmnrqqnpmnnpmmmnrqnmknrqpnmnmnnpqpnqrrrvnlkmmnkngmmponlkiiiighhllhcggikmlllnmmlonmlnnnkjhllmnppqmnihggQSXemlkjhjbcaaa[V]_\UXVXXZ[\\ZYYZ]_[SSONL[[^__`bbcde_SHB=>?EMKKQSY^ekooqdVOioYZ[[[]bafedbbcccdeeedehhhfdfihfcafdccegijjkmosvy~‚†ŠŽ€ƒ}wrolie``baabcbcegiiiknqptvy{€†‡ˆ‚€‚………„„„ŠŠˆ‰Š‹Ž”•–••”•’ŽŒŒ‹‡†‚‚‚ƒƒ†ƒƒƒƒ…ˆ‰Š‰‹ŽŒ‹‘’˜’•ŽŠ‹‘‘Ž–“™•—–”•˜˜›šœ——™£Ÿ¢ž¡œžš™˜™—š–”•––•”™”–•“‘‘މ‡}wcTMJJMMNMMNSb¾ëôùûýú÷õóõ÷ôñîëé⺤‰†œ²¼Üµ±ËÜÙ¼ˆkk‰}£Ûãï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúóëÚǪ^3*(*'&')('''(*()('()+/E_abWTPQLGMRSTUUUnUMPTBA81+%#"! !#" !)7?>GPXOE>6/+'$" !"%/?DFGHN1*5H>/%#! ! $0AFFGHILLNOTRRV]]\\^`ecgflkmjonnnojrrqppoqosotllmnoqnmklnqnmmpqppqonorrtsvnnmpnskomooonlkjjijmkjhlifefillnkkjmlllolkkpopjjhmnnnpprnojkg_YZdpllmnijedbc_Z\e^ZXZYYX[\`ZXYYX\[ZRSNJPZZ`_^^ccidhVF90+)+.39AOQSYcitpqfeappohfeb``aa`aba``abcddeeefffffghfdgjnqtx|~~~ƒ‡Š‹Š€ƒwojfcaa``__`bbbaccceilmmnqqruxy{~€†‡‰ˆ„ƒ††„ƒ„†Š‰‰ŠŽŽŽ‘“”––“ŽŽŽŠ‰‡†‚‚€ƒ„ƒ‚‚‚‚‚‚‚…†‡ˆˆ‰Œ‹Ž‘’‘‘“Œ‰‡‹ŽŽ‘•—–•••”—–•–˜™™™š–˜™Ÿœ™ž››™™—˜–˜š™˜˜—™””‘”””’‘ŽŠƒ}wdSMHJLKKLMORiÇï÷úüýþøóóóôôöøñã·œŠ‹ŽŽ‘–“£ÌÑ×¾žvatƒ|–Íçï÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúòëØÄ£X2*''''(('((((((((()+,8Kbs_YSQSTUY`iijLAOVAF=40)%""""#)-,*""0KA?ISJC5-)##"""#%0:?EGNKEFJ+$2J0"! &6DHGGHIKKNPQQUX\\]^adddeeggijjjmjokrppooopoponnnlopqmljllpkmnomlmnmppqqrrpmnnnnnjllopnmmkigjkkkjhgffgikkgjikjnkmlljkkllmjkhjjnmmmmnnjjfb]enmkkhnijfecb]`dc^ZWXXXX\_XSUXWUXWZQRMOSVXYZ]^^^^^ZJ:+*((''(.3AQSV_flnohgfhjŠˆ†ƒ€{vvvqnonnortuwz|zyzz{}€‚‚‚‡Œ’—‰‘‘’‰•“’މzslhd`]\\\]^^_aabbdefhlmplzoppprvvx|…ˆ‹‰‡„††‡„„…‡ŠŽ‘”–••‘‘‘“˜”—””ŒŒ‹†„‚ƒ…‰‰‹ƒ„‚ƒƒ…ˆˆ‰ŠŒ––—”™”–‘’‰†Š‘Ž’•››Ÿ™™•™ššœ™š˜žœœ›ž¡£œ››žœ£™œ›š¡ŸžžŸ™šš˜š˜˜”•ŒˆzeSKHHKJKKLNVt¢×ó÷ûüþûù÷õôòõøñã½›“‘††ƒ‚‹©Ë̽¦ƒus||’½åî÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñéÕÀœR0*''''(((())(+(*(),47@Uqr^WTXghhkmc]T?=@8,++-)&"!"#&/6;>*$#7PFIM>1*$" "%.39FIIKKLXC91%"/*$ ! !);GIIHIIKLNPUUZ[``cdefffijjikkkjmnonrpqosqsttooosstqpmmklmpprpsopnnouttt{rnloppmnjlnvnmnokjinmokliedhkulkkmklkolnmllmllkljkijlroqmnnrkmgghqonjjjnijfhfhhied][[\[[[\[TPQUVUYXXRQRUTVVXY]]^^]]]G0(())((&&+6GVY]dlmnkigli‡‰ƒ„„ƒ†‰ˆˆ‰ˆ‰ˆ‰‹ŒŒ‰‡ˆˆˆˆˆˆ‹ŽŽ‡‹‰‡†ƒ‚€~zvspmid`]\[ZYZ[\\^_`aaaaacefnloolkkopqrvw|…‹‰…‚‚‚‚„„†ˆ””•”””““‘“•”“’‹‹‹ˆ„„ƒƒƒ…†††…ƒ‚‚ƒ„…‡ŠŠŠŠ’“–“’’‘‰‹Œ‹“˜–•š˜šš™•˜––“›™™™Ÿœœ›Ÿœ››œœšš›ž¡¡¡žžŸš››šš›šš˜—•“‘ˆ€weUNFGIKNLLRY}¸àôùûþþþû÷óïôøòçÖµŸ £˜ˆƒ†Ž¬ÏÄÂz{„‰“ÀãíöÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùñèÓ”O/*'')(()*))))))))*/:BOaroZSY^inif_[KJ4(((&%,356.+058GMT7,($/R;1-*'""!"$+06DHKJXK]NB:3,$" !"! ! ",@GHHHIILNORWY[]abcegghhiijikkklnnnnnoonpqqkspponnqponnknoropopoooooooutsmnjpqnmmknookmmlkjhkkjhhifejlpmlkkkkjkkkjmlllljkjjjloonmjnlkjjilnolnhjiiiifgfgfga_[\]\\[Z\\RHTVVTXXXRRRSSRRVWVSSSTTSG8+)&&'&)''5@[\`ehhijieedŠƒ€‡‡‡‰Šˆ‰‡‘Š‘“‘ŽŽŠ’’’’–‘“Šƒ|zwqmmlfcba`^]Z\]ZWXY\^]bbibfdeacegnkooprsuwxz||‚„ŠŠ‡„€‚ƒƒ„„‡—”Ÿ™œ——˜˜™››š””“—ŽŒŒ‹‰‡„††‰„‰‰‰……„„„‹‡‰ŠŽŒ‘”™•—“”Ž‹ŽŒ‘’“ŒŒ–˜˜˜›š™˜›š›™Ÿ—–—œ›œœ £¢¢¡¢¡¥ŸŸ›™š›ž£¢ª¡Ÿžžž ¡§›œ™˜˜˜™“’ŒŠyhYKEGLMMMOR^ÐéöùüýþýûøôóñðïêâÝÛǵ›ˆƒ‚‹›¶ÏÂÀ€‚¨Êçí÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûùðçÓÄK.*(())())*))*,//-+8DNXgup[X^cujQPUC4-'#!""'2CTB:>HEDOXG61-$(++'#! ")-3?ABJXNGIMQS9*%! !! ! ! #0CFFFGJKNQTW]^^`begilkklnnmnolmnoqsqpowqrqqqttvpnnuoonnntqtqrpsoqprptrxspnnmroomsrsnomrlklmlqjjhhgfinnsnmmqpplmjjlrmmllklinloopnlknkkjjlrssmpjjhkikgghjhg_]\_^]]^_bZRRXVVTYWWTUURPOPWVTQPONOUJB4*%%%%&''4C`cdglgfgjeec~yyz}€€€ƒ‡ytrw}€„~}|‚Œˆ…Š”‰zpmjgea\\\[[\\a^^]_b_\\[\_^]^baaa`acilnpsxyyxwz}}~}}€…‡ˆ‡ƒ‚‚„‚†ˆ‘–––––——™™™˜˜–•’’‹ŠŠ‹‡‡…†…††‡„„„†††…††‡ˆŒŒŒ‘’••’’‹ŒŒŒŒŽŽŽŒ‘•—™™š››ššœ››šœ™š›œžž¡¢¤Ÿ¢¢¡¡¡ŸŸš›š¡¡¡¢žŸ›ŸžŸ ¡¢¡››•˜›šš˜”’ŒŠ†~wfTKEIMMLMMVe•áï÷úüþþþüùòêïôõôìçã϶˜Œ’™šœ¨½µŒ‚ŽÈ׿í÷ÿÿÿÿÿÿÿÿÿÿÿÿÿþþûùðçÓÆ‡I-*)(*)()*+,/39A<4=JW^dstpc]bgeeB.-,)$$"""$+@OMLMMG_PWJ@AB)"#$$!! !-;?B@<DONM<<CJ:+%! ! ! "%5FFEFGKMRUY]^_acgiklljlnnnnonmpqqrrrqpqqpmqrsrplmmnlnnoqspqproroonrqtsqnnnmmmknnponknlljlmnkjgfeedjknosnoopqplkjllqijjkklimmqromllmijilmmnlkjihhihhfillg`[^^^^^^_c`YWUVUVVWWWUXZTPOMNNNOMKJIILE>4**))()+;Jadhkiecaeeecrqtutqnptlfb`djggijggnuvx|ƒ„‚rgecb_a[ZYY[\\^`bc`ddeed^^_^][]]`bfjowy{~~}€€~€~|~„††‡„…‚‚ƒ…‹”£˜œ˜–––˜——––‘”‹‹ˆˆˆˆ‰‹†Š††„‡†‹‰‘“””““”••••’“‹’ŽŽ“”› œŸœžœ¡šœ›Ÿž££¤£¤¤¤¡§£¥ ¥¤¤¡¡Ÿ œžŸ¢¨¡¢¡ ¡¡¤¡¢¡ š›ššœœ–—–›‘ˆ…zycSJFHLLNLNWk§áðøúýþþýûøõö÷ùûøõñæÉ¤œž¥¬‘Ž£²†}~Œ®ÖáîøÿÿÿÿÿÿÿÿÿÿÿÿÿþþûøïåÒÈF,*)))))*.258AUQKMRasjkynh]dXQD0'$##"#%'%%/XO_VRJHLNE;0*)&! "#" !+BFXNLKMB91+/:4*$ ! ! ! #)>EGFGKOT[]_adcggmmplllqprqsoprvssuyyxtsrvtssupolpnnnnnstxpsrrrronorsupmmnnqnomqouqnknllllmokiffcdjupsqtssprqqlllqmplkjkklkpmuqoonnmiklqmlmliihggkjiinmmb[\cccaaaaa_[`XWX\XWWWX^XVRPMLLMLKJJGGHJBC=88965;GTaemigcaaeffeiijkjid`\YUQRSVY``i]ccegvqrppeeeeedceihgdaabbaa_acbbb````a`_`bgmrxwx|€ƒ~~€}|z~|‚…„……„„…†Ž——˜™™™˜—–—˜˜˜—“““‘ŽŽŒ‹Š‹ŠŠŠˆ††††ƒ†…‰‰’”•••”‘““’‘ŽŒŽŽ’”—™š™™šŸœœœ šš›žŸ¤££¡ ¡££¥ŸŸžžž¡ŸŸž¡£¤¤£¢¢¡¡ž£¤£ ¡š˜––—›–‘–—–“Ž‹†|xoaTMFIMLLMP^v°æôøûýþþýþÿýúüÿþýùòã°¯«™‡Š’’€{wƒ‘¯Òîøÿÿÿÿÿÿÿÿÿÿÿÿþþþû÷îåÐÊyB+*))(),6@DEJMW^[Zcusqr|ob`]UJB4.'%$&(.42-*3WMTZNGEC8.)%&''"$'17)!#/CEGK:0//*##$$$" '/@EHIMQVY]_bcffhjmnmlmnooqqrqsuusuwxxwusrurqomiiipnnnmmrtrosusrsoopsupknpnknlmmporqokllmnmmojjfeairropppprppormlmmlnmjikilknmmmpjiihhlpqkljifiggedckjonc\^aaababbb_\ZZZYXZXX[ZY]WWTRMLKMILPIDDEDBBABIKLNNVadghefccbdbfhgimgj`]WTOLLMQSYacicbeipwqlquusrrnmnppyrsciccacaaaacfhijmkjllmosvy}|€ˆ‚~€€|z{~€Š‰‰‰‰Œ•“žœ›™›šššš¡˜›——“”’‘“”””Œ’‹Šˆ†…††‡†‡Š‘’–––—–š”˜’’‘‘“’•˜˜žš›››œ¡œ ŸŸŸŸŸ£¤¥¥¦£¤¢££¥Ÿ¢ Ÿ ¡¢§§ª¤§£££¦¦©§¨©©žŸš—•——›—’‘•—•‘ŽŽŽzvpeVLEGKLNNScƒÀêóùûýýþþÿþýþÿÿþüúïàÚØ¾™‡‹‰†{w}ŒœÚíøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷íãÎËr>+,**(),GQYGSV]`biorvxwlbPJLLI;4.+**.5=FL2*3YTRNI7-*(%#*8/**.8EC1%$-ZE;4,'%#! !! " ! " !$-8AGJOSX\_bdgijlrrronmuqrpstttyvzxwx{yytrrutunjhgkpooprqqqqrvuttssvttqonsqqqrrxnpospoopmlmqmnijfegrqvwwvvsrqrprnpprnplhhjklkplllpkmhhjpqrknjfeihgcbemjrd\\bbabfbeef^[\b\\YYYYY]\]Y[XXRPMLLQNJFFGEBBBHJQNZTebkjjcfffffgmlmld^YSQNMLOUWYZ[]ehilxtƒutl€~}uwpqmnopsvlda_`bbbcdeghikmpqomnnqvxz}~€€~}~~{|}‚‰‰‹‘”–˜œœšš›šššš™˜—•”“‘‘‘’’“•ŽŽŒ‰ˆˆ‡†…„„‚†ˆ‹Ž’““”•••–———”“‘‘‘’““–˜˜˜š—™š›œœšžžŸž ¡¢££¤¦¤£¢£¢¦ ¢¤£¡¢¤§¦¦¤¤¢¤¥¦¦¨¦§¢¡Ÿš™–˜˜œ•”“—š—ŽŒ…‚{yfVKCGMMMPUkÅîöùüýþÿÿÿÿÿÿÿÿÿÿöìèåÜ»›‘’˜¢‹z}‹žâðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöìáËÇl<*)(('),?dQIU]]haxmojeb^N9ALNMD:99>CBTMZP5(1693.*'%$#")39;<INTLX=*&)/4-%#! !!! $)1<*! ! ! !%)7DHMSW[_dfgijknpqrqppnurrqtsuvvwxwwvvvtprrrrpmkimsqopqporqrsrortttsrsoonpqpnqrqnqlnopnnmmmmjjhechkrrtssqrrrqqorlnoonnkjiknnloiklkkifjkljjjjkcaijb^chkkdY^bbbccebfib[^^a\\[YYYWYY][[YXXTPONMMLKMPJEDDGHJKOSY__`bccccbgjigjbZTPNLLMPVU[[[XY\`gtspmjkjlnopqqonjihggda^]^``cehjkpruxzwtssttv|~€„ƒ‚€€€€~€‚‚‚ƒˆ†‰”–›¢ ›››œš›šœœœ•’’‘’‘˜“”‘•Œˆ‰†…†‡‡‰Š‹Ž’’˜™˜•–“”•œ—˜”’‘”““”––œœ˜›˜ž˜™›››ž¢¡¢ ¤¦«ªª¥¦¥£¢©¤¦¤£¥ª¬®©¨¦§¤¤¦«ª¨©§¨¢ŸŸžŸ šœœœ˜™šš˜˜”•ŒŒŽˆ†~ydTICFMLOPWsžÕñöúüþþÿÿÿÿÿÿÿÿÿû÷ôòéÕ§™£´¡–~}† æöûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúöìßËÂg:**(''(+;dKIaZW\`gnaXY[]C7=Q_XNJJDLJJQLPW1'*0+((#"""#'/C]MFLUPHHG,$#%$" ! ",5$!"+9SE/! ! !! #)3@JPX_acgppoqrrtvyzytvvwuuuuuwx{{{xyxwvsrqrrpononvsspsqxrrswusrrszyyrstuoopuqrpnnqkloqopppnnjjfcfnotqxssrrrsqsoqmnnpnmkljoorlnookljhkplnnnmmd_bid^_fime_]fa_cieeemf`ac_`]^\ZZ\\\Z\\_Z^ZZYZTRPPPQOOJPMKHHJJQRVX\_abcceiijih]WNNNOPRSTUUUUVWX_dffc[ZY\^adeggghfhccbaaa]^`ehjmopruy~|zwttsux|~ƒ}~€‚……ˆ††‡‰Ž‘–˜›››šœš›˜™™š—–•”‘’’‘’Ž‹‰‰…„……†‰Š’””••––••’”•“’’’’’•••—˜š›™š˜™—ž™š›››œœœ ¤¥¥£££¤¥¤¢£¤§¨§¨ª¬«©©¦§£¦©ª©ª¨¨¦§¢¢£¢Ÿž™œŸš™———˜–•’‘‘Ž‹ˆyqbTLCGMNOSY{³ÜóøûþþÿÿÿÿÿÿÿÿÿÿÿüùîݺºÌÄžŒ„‚‚åúýÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúõìÜʾd8)*)'((*:dJFNTNIPVWURRW^G5Hbbf[RKBG`QYFF;+$#$%,0*$*/24>MUZHNLI;94*# !" ! !"""/A0%"&:CCF* #'4?NY^dfimprrsusrvyxwwtvsuvvvvwwwzvxwspsvtrssrppmnourrpppqqssqmqqrsrqqrpnolmmqqonokkklnonnmnnnhgehjmnrqrrsusrtpoopmnnmmmijjmmmlmjmkjimrnkjgfea\aei_`chid]__`_`debegea``c__^]]]\[Z[[[\_[[[ZYYXWWSRQNNHMKJGJNORSTXY__``cfffihibWUVVUVYYZXWUVY\`eeec`[XXY[\_aeihihgddeffgggjmosntsuy|~}|yvvw{€}~‚„~~„„‡†‡ˆ‹‹’™™ŸœŸ››œ›œ˜™™š–”’‘’–’’”‘–“‹‡‡ˆƒ„†‡Š‘““˜˜œ™™™š–••—”““”•™—œ››˜™›Ÿ™œ››šžœž££¢Ÿ¦§¨ª©££££££¥¥¥¦¤¬²«ª©©¬¦¦§«³ª©¬§§¦§§¢¢¡¡ ŸŸŸžœ››™—••‘‹‰ƒ€yqf]MDGONPS^ˆÇåõøüýþÿÿÿÿÿÿÿÿÿÿþüõëäáèìͯ¢œ”œÏðùÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùõëØÉ¹^6(*(&'');@?>>715=TTTIKSYONUbggh^VHDJN?3.*&"""*::547@UOKNUSMA80)('$ ! !! #))&0EA*$#+[MK7% "#(3@P`enoqrxyxw{wvw}zy|uvuyyxyz{{zzvysppsuuuztuqpqtttttqvutvwrpppqtsrrrpnnqnmmrpnnokklmnqmnnomohfhonqqxqsruutsvrvrumpnrmmmmmpqqmmjniikronkkkm`Y[ieg`cfpga`abcbabddhedceac_a^`^^^^_^]a\^_`]\\a`_XXTUQMKLB:AJOUTYYXY__^^dffghhyvf_ZWWXXXY\XW[_djkkhec`^]]\^_bejhhiihjmnprtsrrrrosty}}~€}{zy{}~~€€~€‚ƒ„……†ˆŽ•›šœ››šš››œ—˜–˜—˜—”’’’“’’“ŽŒ‰ˆˆ…‚„„ˆŠŒ“”—™™šš›š˜–‘“““”–———œ™™˜™š›™œœœžž¢¡¡Ÿ¤¥¦¥¥¢£¡¡¡¤¤££¤¥¬ªª©¨¨¬¦©¬®«ª¨§§¬ª©¨¦¤¢ž¡¡Ÿžœš™–•Šˆ†ƒ‚ztgYNDGNOPWa×íöúüýþÿÿÿÿÿÿÿÿÿÿÿûöñïòôìãÙ;¸ÀåôÿÿÿÿÿÿÿÿÿÿÿÿÿþýøôêÖɰW3(''&&&(3@3,*(''9SUVIGVhjllkijccRK::2+(%$#$%,3GXOEEFPZINB?8.($#"! "(<CB@CC4#%/]QJ5' !(-:FR_eknrtuwwwwwwx|{yyuwtuuxyyzwttuvsroqquxwvvtpmqrqptsrqwuuuspportuqrnmlnooonmrqpoommklmmnjgjkhchjnnqrrrqpqqrqrpppolpmmiiinqlimljgghklmknhhh`WZ`efgaeghc`^abbba`efgc``baa`___`_^^^^^`\_`a__^_^^WWTSSQOJ;<ALOPQWVXYYY__gghhih…vs^^]]]]XZYY\biokkjnoqmjgedbbdgjlqptuvxzzzrqrrrvwz|~‡~~~~}‚‚‡‚ƒ‡„ƒ…†ˆ‹œž š™˜˜™š›››œ˜š™žžž™š”“”™–—“”ŠŠŒ‡‡†ƒ„†‰—™›šš•—›Ÿ ›ž›œ–”•››››ššœšš™™šš™¢Ÿ¡ž¢¢¢¢££§¦¤¥¨¥¦¤¦¤©¤¦¦¬¨ª©®®®ª¨©¬«±°±®®ª©©¬ª³«©©©§¦¤£ ŸŸŸ¡¤›—™‘‘Š‹…ƒ}viZKBFPNPVfžÝîöùüþÿÿÿÿÿÿÿÿÿÿÿýûúøùúôîçÜÓËÉÙóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÔʦR1()&)%&&()(%#"!%5Vj\KR[eemnic`]ND6)'&$###',3<C]VcT\GOLI;/*'$! ! $8aA<KYQJ%'6aZc9(!! ! " $)/6BTXbgllprxwwwywzzz{{zyvzwvuzz|xutxvxsssuuyy{vvqootrrrvpprxwxvspqpwuuqsomloospsoqrqpunpopllihgkhefnnrrssvrrquqsqtpoopoppojkkplijsljhikpmljnkrd[V\dgghglgfa_`ecebbaehna]bfgfedefff^`___`_ccefgaa]^WZYXUUSH=>FMPRTVVZYXY_ciijijjkhdZ\QQQRTTUY_bpollkpsronlkkifgimoqqtz{{ywxqrrssvyƒ€}~~ƒ‚‚ƒƒƒ‚ƒ……††…†‡‹“—™šš™š•˜—šœ›˜˜—™™™™™˜˜””””•’ˆ‡†…ƒ‡…†‡‹Ž“™™™–˜–™œŸœ›››››™˜—š››š›œœ™™–š›š œž¡¡££¤¤¥¤¤¤¤¤¦£££¤¥§§§§ª««ª«©©©©¨¬®®®²³®¬««««³¬ª©¥§§¥¢¢£¢¡¢ž›—˜“‘Œ‹ˆ‡ˆ€yhWLBHKOQ\m¤àð÷úýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷êÛãä×ÝóÿÿÿÿÿÿÿÿÿÿÿÿÿþýùôêÓÊšN/&'&&%%$$%$$#""%3W\cbbdpgkfcWTG92'%$$$&)/8<NDCLIJLJF8.+)&""!!! /DbAH`[]@&+APQJ:% #%-28?JX]aflllprsrwvvvy|{yzuvvzxvu{yxuuuwttstvwwzyxvqmpqqprsokpswwvvrpqqwtqnonmkoprmrprrqnnmmlmkhdgfjehjmosrtuurropoppqpomnlljkklllijknmjhknonmjjhe\XW`hgiiikcbbaabbdbbdhkh\`bcddddccbb_`b__`aabcbaa_][XXXXXY\PDJQSUUUWXZ[[\cfhijkjjlid`]XUSRVWXZ^bhnlpovwywwwwrrppmrpusv{{|}xxwvuuvyˆƒ‚€€€€ˆ‡‡„…†‰ˆ‡††‡‰ŠŽ™•ž—˜™š™›•˜˜š›™ššš››œ˜˜˜™“”“‘†„ƒƒ„†‡ŠŽ”–žš¡šž™›œž›¢œž››œ¢¡¡žœŸ ¡¡¡Ÿšš›¡œ Ÿ¢¢¦¦¦¤£¤¤§¥¦£¢£¥¨««°¯°¬««¬«ªªª«°¯¯·±°¯µ¬¯«´°´¬¬««¬§ª¤¤¤¥Ÿžš˜˜˜‘”‘‘’ŒŠ€ygZNEGONS^v³ãñøûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûòèîóòñøÿÿÿÿÿÿÿÿÿÿÿÿÿþýøóéÑÉJ-''&%$%$%$"#"""$1UTWZ_bef]TME?90'$$%&.9;>ABNGDUNIA71'$"!!!! ! !!! !'7OfANbHF7%0LQF>-" ! "(.7@FLRackllmnnttttwwvx~{|yxtuw{vvv|{xuvuytvuuvyx~ywrposqqrrpmpwu{wwtssvvwsonnnlloprrsstqropmmmpiedkgjipppq|svvtqpnqpppppposmkikmomllljolkkonommjkhbYY^jiikojkbbccemffedeoieegggcddedda`_`a`_aa`bfddb`]ZZ]YYZ_`^\ZX\YYYZZ]]`clghjplniŠŠ…zurnc^^__`ceijmnux|}}}zvuupnpptuxz||~yyyxxy{ƒƒƒ‚~€€€‚†‡‡…†‡‰‰ˆ‡ˆ‰“”—–—”””—•™”˜˜šš˜ššš›——•”’’Šˆ„„„‡ˆŠŒ’—˜™™š››˜š™™˜šš›™›šœ¡œœž Ÿ ¡œ–™™ ¡¡¢¤£¤£££¢¢¦Ÿ¢£¥¥¨ª¯««¨§¨««¬¬¯®®®¯¯¯¯¯¯¬««¬ª¬¬««¬¨ª¥¥¤¤¢ œ›šš’“’ŽŽŒŠƒ|uk_PDGKPTe‚·èõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùóôöùýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýøòèÎÇ…F-&&&&$$$$$##"###1URRY`^ZWTJ?<93,*)26:@A`PVDO>530,(&%#!" ! !! #%%" "%# $/DXS?He@>-#,T@6,# !'+28AKOSX]cgjkmnppqpstusuxwwuttuvwvssttsuuuuvttsvwyvxusprtrpqrqoqtuv{utsuuvtsqpqpmmmoppqqqpnnlonmmohfeeejlooprsssqpoonrsqonnnnmmjhlonnmllkkjllmmmkkiii[T]eggjkjgc^cfeeedcadefffcddeccbeec``_ab`_aa`_edda_^]\\X[\bf`\WWXXYXZ[]]aehijkmlmf“†~|}ttiljiiijkkmr{x„ƒ„}}vvuqruuz|~}}}~€‚|~‚„‡‰…†‚‚ƒƒŠ‹ˆˆˆˆ‰Š‰‰‰’–—˜™˜˜•–”›–˜˜˜˜›š™ œœ›œ—š“’’–Ž‘‹‰ˆ††ˆŠ”” ŸœŸžž™™—–˜œ››œ¡ ŸŸ¡¢¤ žŸ¡ ¡Ÿ£¡¢žœ›ž ¥¡§¦¨¥¦¥¦¢¡£¦§¨¨¯¯¯ª³±¯«©¨§ª¯±¯¯®°²¸¯°°°¯¯¯¯¬®¯¯¯®¯´®©«¦¦¥¨§§¡¡–““”މ„}n^PEGNPVjŒÄìôùüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùúûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöðçÌÅ|A,''&&%$$$##""###09;;<@BBDC@:76102<IKLJJTUF851+)&$#"!!!"!!!!!! !! "3H1#!)./!(3WZZ:795,"!',+'! !%-4<GNTW^`hgmnonqnuprpxvuuuuttuttuyuuv|utsuuyuvwwu{z{xysrrvtrqtppruu}v|vutxuvttprrtmoopppqrooonmsnpmoiifehrtuqpqxsrpsosstssollqmmkilsornnnolliposmnklij\TZfejiplkc_^dddefbb`eehgjeddiccclecabcda`_da_^deca__aaca^aieb]XX[[[[[Z]_cegjompllg„„}}y~rqlllkjkmosttwx|ƒ|{wvwvvxyy{{{}}|…‰Š†ˆ„ƒ€‚ƒ„Š‹Šˆ‰Š‹Ž‰‰ŠŒ”˜˜˜—––•–“••›—˜™™—šš›™›šš˜•‘ŽŽŒŠ‡ˆ‡‡ˆŽ‘”——˜ž›œœœœœ›š˜˜™™™›žŸŸ¡¡¢¢¥¡¡¢ ŸžœŸž ¡ Ÿ ¡¤¢¦¦¨¦¥£¥Ÿ¢¤¥¥¨©©©«««¨«ªªª««°¬¯®°±²®¯¯¯¯°ª®«®¯±©¯¬¬¯¯©©§¦¦¦§¦¢¡ ž™–•”“’ˆ„ymaRFHMRXp–ÈîöúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüõïçËÃu=,(*,))%####""""#,<.(.2336988:;97BUJa_`HMA8/*(##"""!!!!!!! !$'3KH7$"*H)!%4<J9(''$! "!! !#*/7>EQUY[^dfhjmjlmonomqorrrprssrqosuurvvvuutttsrssttttzxusrrrrrponrtwwxvusuuvuvtsqqpnmpqooqtrlllnmnnpkjffdjnrsutrpqqqonnpoqqpomlomnknqqppnoomkkjpmllnjkg[PZfefkijjb\`_`adbb_aadehggeedeccccbcab\_`_^_`_^dfca`_`aa`aceca]\ZZYYWWV]beehkoklljgz~‚~…}€tqqqonnopttwuxwz€ƒ~}{|€ƒ~€}|}‡€‡‡ŒŠ‰ˆˆƒƒƒƒ…‰ˆŒŠŠŽ‹‹Š‰“—¡ ¡˜––™–œ›š—œœœ›ŸŸ œŸ›˜—••ŽŒŒŒŠ‡††‰Š‘—š£Ÿ›œ ¡ž£››š›œœ™žžž¡¥¥¥¥¥¥¥§¨¡Ÿ Ÿ¥¢¥££¤¥¤¤¤¦§¬§«¦¤¤¦§§©°¬«ª¬«©¨«±®¯«¯±°¸²´²³±±°¯®±©®®µ¯±¬¯°°¯±°°¯¯¦§¥¥¥£ŸžžŸ™™˜—‘Œ†…|l`QFHMPXv¡ÓðöûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûõîçËÂm8+*0-,(&###$##""##"!"%*,--05?LHUOMUMLJJI8-($""!! !!! !!! ! !!!"*59?QY1%#,5&!"#&(&" $)1;BJNV[_bghnnpkmkllqnoopptttppswrqqrtvwwvwv{vysrrssttut{vursrqrrnorzx}wxustxyyuvvutunmmqpoousslmlnoqnpkjeehrsysvvvqtqqpoourvppnonrooovvuqsrqolkooqmnlnim^UWdhkimhgd]\abeedccafggghhgghehdfdicdcc^a_^^^_``ccdbb`dbdchfeb`^^_^[_WWW^cjiijolllkgqrwxz}zuvvtssutrttvsvw|€€€€ƒ„}}~€€€€‚ƒ‡‰Šˆˆƒƒ€ƒ…‡Š‰ŠŠ‹‹‹Œ‹‹‰“˜š™š™—–””•˜˜˜˜˜™››œšš˜——•’ŒŒ‹Š‰‰ˆ†„…†‹”˜ Ÿ››œŸž œ›››š›››™Ÿ ¢£¥§§ª§§§§¥ŸžžŸ ¡£¤¥§¥¤¥¥¦£¤¥§§§§¨©«¬°¬«©ªª©¨«¬±¬¯«¯°±±±±²±¯¯²«®¯®««¬¯¯°¯¯®¯¨©§¦¦¦§¤¡¡ Ÿšš™˜”ˆ†}sjaTIKOSZ{ª×òøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíçÊÀe4++..)&&$$##"$$$$# !',,++-<KGGTWUTDF82,'%#"!! !!!!!!! !!!#%')/7Ga`aF)%%2?% ""! #&.4<CIRW[\`cfikkkljkkkjpmoqqrttsqruqpswtstttssssrrosqtttxutttuqtrqnnlquyxwtuuuvwvxrrrrqoknnonpqursklloqqmqigdhkstwssqqqqppppppppppnmmrmooooqpppqnllnmmkkjje]SX`dhhgggfa]\adccccbafjffggfbededddhbccb]a_`a_\`adab`a_baccghea`^^]\[_XXX_cghiikkihigmqtuuuuvxyyyy{zyztvtvy€‚„Љˆ‡……†€ƒƒ…‚ƒƒ…ˆŠŠ†ˆƒ„ƒƒ…‰ŠŽ‹‹‹ŽŽŠŒš™ ™œ——–—˜Ÿ•›››™™˜žžŸ™™–•’‘Ž‹Šˆˆ‡†ˆŠŽ“—¡Ÿ¥ Ÿ ¥Ÿ Ÿ ¡›Ÿ›ž›ššžŸ¡¡¨©©©ª©ªªª¤£ ¤¡ ¡¡¡¢£§¤¨§ª¥¦¥§¥¥§©«¬®³¯°®®®®¯¯¯¯°°®®°±·°±±±²³¯²°±®´®±±º°®¬±±²¯³±´¯°¯¯¯®««¬®¬ª§¥£¡¡¡¡ •’Šˆˆ~zpdXMKLQ^…¼ßó÷ûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûôíã˺_4+,/+&%&$$$$$&*-($##&)./**2PN<BdJ:50*&$#"!!"!! !!!!!!!!!! ! %-8LC>ENJJU?'"$<0$ ! #(.6@GRUWZ_abefhooolljnmolpqrqvwwuttuqoqvwwxxsursturqpst|utuuutsvquspmnpwwzyxtvv{xxwxpprtpmnrqppttutskknpqrnriggnpuu}ttstqqppquqrppppnmmrqsoopqqqqqnmnrnolkkl`UT`cghiikjk`__efiekccemgefmfefmchghehabba_a_caa``bdacadfgfedgeea`_`_c^_[[^gglkkkliggmhsstusrsvz|}}~~|wwtttw{€‚…‡‰ˆˆ…„„ƒƒ„…ƒ…†ˆŠ‰†‡………„„„„ˆˆ‰ŠŠ‹‹‰‹‹’•–”“““—•˜™—”–”–”—™žž›—–“‘ŽŒ‹ŠŠˆˆ‡††Œ“–›Ÿžž žŸŸŸžŸžš›™œ›šžž ¡¢Ÿ£¤¤¢¤¤¢Ÿ¢¤£¢¡££¤¤¥¤¥¦¤¢¥¦§¤¦©¯®®°°³®¯®°±±°¯®±²¶°¯«¬«¯®°¯®¬¯®±²´±¯°°°¯±±²±±²²²²±²²±°®¬«©§¥£ ž˜–’Œ†‚~uk[MMNV`ˆÅãôùûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûóìßÍ´^5/-0)&$%%$"%&0:62230,,0)&)6NS=<<9/(&$$##!"!!!! !!!!&+)"""!!#/=ENSRQF?Ci:'""""!&+3:BINVWY\`bdeiijlnkhhimmmlqqqrqpooqppoprssvxussrrpooppsuuutssqtrsqokmnpqwvxrtsustuutqnqrqnqrruqpsrqnnknspppmliijptuvwsssqnppqrrqronnonnnpooopppnmkklmoljomjgbXVVbgggijjjc^`aefdbcdfimeffgeffebddfedbbba___``a`aabaa`cdeeffgdb^``_____]`dfhklmnmggghexuroqty~ˆ††„ƒ|{wxxy~‡‡ŽŽŽ‹‰‰‰„‚‚„†ˆˆŠŠŠŠ‹‰ˆ‰†ˆ‰Šˆ†††‡ŠŠ‘‹Œ‰‘•”””“““”˜– —•–˜—œ–œšŸ™–”ŽŽŠ‹ŠŠ‰‰‰‘–—œŸ¦¡¡¡¢ ¤ ŸžŸžœ››œœ¡œžŸ¡ £ ¢£¤£¢ ¢£¤¥§§§¥¨¤¦¦¦£¢¢¥¥¦§§«¯¯µµ¶±µ¯°¯µ°¯®µµ¶³µ´µ±¶µ¶±³®®¯¶µµ¯°°°²µ³·²±°°³¹²´µµµ¸´½¼¼¼¼»»´¶±±²³¬¬¤§ ŸžŸ˜•”“‹‰xi[OKMSb’ÓèõùüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúòêÜÐ^7000(&&(('&'3@FKKNJF.,'"#(1NC=>D0$"""####"!!!!!!!#",45,)($"'?][^`bYD<;;6$! !%,4=HKUY[\abcflehjommjighlsnwpsqxpqnroroopsstr{usstuvpoosquuvuursrussqmkmqttxwwttsxsuutqopwsrrxtsssqwqppqnsqoorkiilovu}vwvwqpputtrrqronnooqqtppnsoponjilrnjinjic]QQ[jhhhligd__adjeddedjjmdjhgeifddfcideedbabd_a`babacbfggeehkghca_ccc_`_caghijnnnnmhjjjf{wqqrx|„‡ˆˆˆ…|wxy~‚ˆ‰‰‹ˆˆ…„‚†‰ŒŽŽŒ‹ˆƒ‡†ˆ‰‰ˆ‡††‡ˆ‰ˆˆ‹‹”’‘••••••–––•——™˜š”••“Ž‹‹‰‡††††‹“—šœŸ£¡ŸŸ Ÿ£¡ žžŸ›œ››œœœœŸœžŸ ž Ÿž¡¢££¦ª©§¦¦¥¢£¡£¢¢¢¥¦§¥«°¯¯±´±®°°°±±°¯±±°±´³³°²±±°³°²µ³²µ°°¯±³µ±²³²²µ¶´±²³´´´´··¼º»¸»µµ²³¶³¯®¦¥¤¢ Ÿ›—’’“‹‚ui[MKKVf–ÜíöúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüùñèÚЧ_:0+*(),151.9EQ[XVXY;+% (*-02=0&#"! !!"""! !"##"$&=e?8=M)"+NJBZMPbA3.*% ")/6<DORWZ]]^aadigdilljifggkmnnqoppoklmmnqnootssrspqppoqppnrruuvqvssrrsrqnlqwvuutssurtsrpompssqrusooorqqmplnnpnnmkeiknpvuwuusqmqptssrrsolnnoqrstqonoopnmjlmlkiinfc\PHXdcfghhif_``beebeedcghgejjfcjeegfchcbadbaab___bdbacbddeaglhdcc_]^^`_``cbfhklmnllkfieif‚~{zy|€„Љ’ˆ‡‚~~‚‰‰‰ŠŽŠ•ŒŒ‹ˆˆ‰‹””“–ŽŠ‰ˆˆ‰ŠŠ‡‰‰‰‚Š‘‘‘‘’’––š—™—š™šœšž˜š”•ŒŒ‹‹‹‰‡††‡‡‡Œ“—¡£¦¡£¡ žŸ£¢¢¡ žŸž žŸ¥ŸšžŸ ¥¤¥¢§§§¦«ªª§¨¦¥¤§¤¤¥§¥©ª®°³³µ®®¯±±²°¶²¹²±°°±²²´²¸··±°±´²·³²²¶±±°¶³µ²´´¶³¼µ³±¶¶¸´¶¶º·¼º¿¹¼¸¹¸·¸¹³°¯¸®«§¥ ž™™•‘‰„woYJHKWm£âï÷úüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøðçÙÑž^8-**+/<KDAGOYgb]VX?+$ "(&&&'&%#"" !!!!! !#+7*(4IgJIKE,%/XOE]B<81)"! $(/8@KQUX]_c_`^bbgfghpmnihhmnoppoupqppkmnnmrqrotsstuprpqpsrrssrwwwvwtssvvuppryxytvuwvuvxronmntrqrtsronnqprnqmpntmqligkowvvvzvyrrosrttvvutnnropqwwwqoonnqpopqnmhhineeRGQfedeffgfcddbcdfeedfglhggjhefjeffgehabbdcbaaacafcccgdcdeeokfcd`^\``f_`adflhppplkkjgiiig„ƒƒƒƒ„†‰‰ŠŒŒ‡‡„„ƒ…‰‹Œ‹‹Šˆ‡‰ŠŠ‹ŒŒ‘“Ž’ŒŠŠŠŒŠŠŠ‰†…ƒ~{„ŽŽ‘‘’’–—š–––š—™™˜—•“‘Ž‹‹‰‰‡‡‡†…†‡Š‹Ž“™¡¤¢¡ žŸžžŸ¡ ¢›œŸŸ žžžŸœœ›žž ž¥£§¦§¥§¨«§¦¦¦¥§§§¦§¨«°²³´¯ª¯¬®°°±¯³±²®±³´³²²³³·±±±°®®¯²±±®®®·³´°³³´²³°±±·µ¶µ¶·¸···º¹º·¶¶¸µ¸µ¶··±¯¬«§¤Ÿœ—“Œ†}rf[NNP^t¨áñ÷úýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüøïæØÕ”Y5-*4<GSUXZ[]jffZNC/)&$$$$$$#"##! !""""%2338CTeRfU?,#.^A51,)'$!! #&-29@FPTZ[`abb__`bbgfhikkjhiimllkonolllqmmnnnspposrrnnnpopnnnstsprsssttsqssqprsuwuttsrovuqmmlnoqpsssppponpppmnmonmmkhijprwtuvvtsoomrrtsvtttqoroqrtsqpppnlljnnmnidghihUGRcbdeefeedb`aaccdbddfhiiiijgghhdecfddbcdddc_abccdbddddeeeeniea^Z^]`a`^`aglihlkkjknifhhii‘’‹Œˆ‰ŠŽ‹ŠŠŠŒŽŽŽŽŠ‰Š‡‡Š‹Ž–‘’“‘˜ŽŽ”ŽŒŒ‰ˆ~zzwƒ’“”“–••—š˜ž™›šš™Ÿ”Ž‹‹ŒŒŒ…ƒ††‡ˆŒ’›œ¢¡¥£¡¡¡Ÿ¡¢£¢¦ ¢žž£¤¥¡¦¦¦ŸŸ œ¡ ¡¡ Ÿ¢¡¦§§¨¬¨¨©¬²ªª©«ª«®´²¼´³°¯¬°¯¯°³¯¸°²²·³´³¸²³²¸²´°°¯¶±±®¯²·´´°¶´·²²²´±¸·¹¸¸¸¼½½½½½¾¹»¸¸¸¸¶¶¸¼µ³³¶¯°¦£œœŽƒ{xvbSNP_z´ãñøûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷îåØ×–V6/6FUhih[\\`fibWNB8+ """""!##)+)# !"""##'8@GMKdTHVlA,$,20*%#! "(/6=CNRUY_```dabbcdffghmklhhhmmmnnmpoonokrpsorntqururrnqquopnnptsvpppqrvvvqqpprxrusrszrqswrnllnurttxtqooptnpppppopppliimqzuxuuuytrpqorstsvvwsrrrqtrwrsrsomlklmoqkhhkik[LNaedfjihefedcgghefbceiiijlligjhgehefcccfggdddeeeehikeedkijhnhe`]]daeba`bcmihhokjilljjjikj’”•–“‹‡ˆ‰Ž‰‹Ž‹‰Š‹‰†‰ŠŒ‘‘ޑދŒŒ…ƒ}v|†‘’’“”““‘••˜—˜˜››™™“Œ‹‹Šˆ†……ƒ‚„‡‰‹”–˜Ÿ¢¡ œ ž ¢¢¢£¡¡Ÿ ¡¡¢¢ £¤£ ¡¢¢ŸŸŸ¡¡¢¢¡ ¡¢¨¨©©«ªªª¬©ª«ª©©ªª«¯´´²³¯¯®®«¬¬®®²´²¯¯°±²³°±²±±±±²¯¯®®¯¶±°±´µµ³°³²±±±±³±¹·ºº¸¶¹½ºº¾¾½·¸¸¹º¹·¸¸¸··¸´±®§¤œ—‘Їƒ~wcQPPd³æôùüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûöíãØØ—R87CXmkn`VVX\dc_UQOP4"!"####&)07640)-32/-,26:OOPNCcNKKE?(!$(%#"! !&+17>EJPU[^a`_``_`bbfgijhhhiigggknmklmljnnoknoonqmspqoononqrpnmnppppqqqnqrrppponrsurplrqposztonkotrqssstspqppnooononnmliijpssssstuttrnoosusqqqrqqpqqrsqmppponklmookgijkj^OPUdighighe_[edfcdddbdgjkkkjghhhfgeeddddafcddbaeaefggkffehfhjiee_____bcbacdefhikijfhhjdiikh›› –—Œ‡‡ŠŽŽ””••”‘ŒŠŠ‹Š‰ŠŽŽ‘‘””””–ŽŽ‘„}x€‹›”•–—”“’“–—œ—š˜ ˜•‹‹ŒŒ‹‰‡„ƒ„ƒƒˆ‹“™™ž¦¦§¡žžŸ ¢¢¤¤©§¨¤¤¤¨¥ª£¤¤££¤£ª£¥¢£¥¦§¨¢¢¤¥¨³©®«¬ª¯«¬«¯ª««¬«®¯´µ·µ´¯°±²°¯¯´³¹´±¯²²´²³°®²³°±±±¯±²²¯³°¶²¶±···²²±³³³±±±³´¹¸º¹¹¶·¸¹¹¾½¾¹¹»¿¼½½¼ººº¼·³²´§¨ž—˜š—”Ž‹€vbSOQeƒ¼èóùûýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúõìäÙ׈M68IgnrnZQOUY]aXRQTJ<"!%(*--/5?CFDAFKLiI;>VLHSXYPMdLTFB0$ #(.6<BFOR]^^afccac`bbbcjhmjhhfikijjjknjllkjnnpknnpnqlssunqopnuqoooospprurqnursqsoorvsysqprqprvtrnmnssrrusrsvqoppospoopoonlilpxuussrvwwtqqrrxutpnpsonptrzrnovrrponssulhhklmcYJS]iikkkfh`[^fegcfggcfinjhijhhhigkegdefhcfcmdccegihhhlgghkfhjkdebb^^^aceghfjgkklijfhfjiihkgœœ™•’Œ‰‹•••””’Œ‹ŒŽŽ‘““Ž‘’’‘•••Ž„z~€†’‘‘˜”••–”•“”——˜˜—•’ŒŠ‰ˆ†„‚‚‚ƒ‚„…Œ‘–™›ž ¤¡¡¡ žŸ ¤¥¤¥§¡§¤¦¦¨¤££¢¡¡¡££££¤£§©¦¥¥£¥§¨©ªª«ª¬ª¬««««ª¬¬¬¬²³´°¯®±°²±±±²³³³²±±°±°°°¯®°³°°¯¯®¯¯¯¯®²±²¯²²³°°¯¯±±²²³µ¶¶¶¶µ´¶¶¹¹¹¸º¹º»¾¼¼º»»¼»¸¶³°¦¤££¤¡˜”†~ueWSRiнéôøûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùôìäÙÜJ3;NfllkVINV[]SOMJHE1"!".951028@K[IGSnPMeZNEQmjihjQME741,#" "&+07@BGKOUX[]acdccac``_ccfhiihfgijhgeghnjiekjlmmjnmmlrmoppnqoonnnpqppqqrtrqqoooonnnruvssstttrtvvrsnoorqsrrrrrvqpoppppppppkgijoutrssrrssrrqprsssqoprpnoqrrsooqsqplnnnnkfijjicXQKYdiiijhfbZ_cffecfhgdgijghiihhijeeegbfedbccddddehhgffhgijfbijfaccb\]^aeeffdihlkkiiddcfgghieŸš˜”’‘“’’–“’’––—”””•••—™““’–“”“““–•”’“‹ˆƒ}‚ˆ‹”‘‘“—˜™–—”–”™˜š•“Œ‹Š‰ˆˆƒ‚‚…†‹Ž–˜Ÿž¢¢¢¢¤¡¡¡¢¥§ ¥¥¤¥¥§¨¨ª§¨££¡ ¢££¤¥¤¤§¨°¨¨§¦¨°««««¬¬¬ª°®¬¬¬®°·³¼³¶±°±´´·´´µµ³¼²°±³³²¬®¯±³³·¯¯¯¯®¯¯µ¸º±µ°³´¶±¯¯±°··¸¶¼¼¼½½µ´¶¹¹¾»»¼¼»¿¾Á½½½ÀÀÁº··¸²®®±²ª£¡žš‹ƒ}gWPRkÅëóùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùòëäÞ݃H>AUgkmaSKMT^UIHG@:3(&'1@BA848?FVSMHW[VTcbcCHVTUKOYA4+%#" "(.4;AEKRTVY___bjihcbccba_ddfhlhhfjilgffggmiifjikljjnmmmqoopqqqnpppptqpqtturppqpnmpmmqxuusqt|vxvzvwssoqrtuvsxswtvqppqswwwoplhfinsstrsrrsxruqpqtsyqposqposrvsspprvponnoqmjiopqg]QNVfhlikiifaaccfeddfgggmihgiiiinilgjefefecacdecgfefhfeeggkgddifdchcb^\^bdggkjjjmloiifefhhkhif——˜’’‘’’“•—“‘’‘““–œ›šœš–”‘‘“’”““““”””•‘‘’‡‡ƒ‰’‘‘“”—™˜”•“•“–˜‰‰‹‹Šˆ‡‡‰„ƒ€~‚„‰Š•šžŸ £¢¢¡£ž¡¡£¦£ Ÿ ¤¦¨¨¨ªª¦¡œŸ ¤¥¤¡¢¢¦¥§¨©©ªª©ª¯¬«««©§©ª¯®«¬²³¶²²³´²³´¶··´´³´²²±°¯¯®©®®®°²³²²¯°±±°±°²´³°¯®®®©¯°·¶¸µ»º»¹¸´µ·º¹ººº»»¼À¾¿½¾¾¼¹¸···¶µ·º¹¸·²®§¥£ š“Œ„{fUSSn•ÈíõùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøñêàÝÛ‡H>Lc_\\_UKLOPPFA=3.(''1@N\K>6;CJKNKI_XRVXgCJUST;1/+(#"! "(,28>CGJNTVY[]`cddghecb`bbb_deggfdedghlgfffghiifhhhhhhkllppooooomlmlopppqrsprpqrqnokmnquuuttttuuuutturrorrtuutusrrqpppqssurnlihfmtrrsrrrssrrqnqrttqnpmooonrsuospqqpknmopqlkjllh_SLXegijijghecbbbfeefghhhihffheihlfhffddfd`cadgecefffgffeggkeddhbdec_c^^``acccbjkljkjjgghiijhig˜—˜——“”“”•”•–’”’“”—¡¥Ÿ—™“””˜•——šš›——••’ŠŠ“™‘‘‘˜’––˜˜—˜™™šŽŒˆƒ‰‹ŠŽ‰‡‡ˆ†Šƒ„ƒƒ„‡‹’“œ› ¡¦¡¨¢££¤£¨¢¥£¢ Ÿ¢¨©¯©¨«¢žŸ¥§¯¥©«¨§§©ª«²±°°°¬¬®ª¨©ª«®²µ´´´½´·²´³¶´¹¶¹¸¸´¶¶·²²²³°±°°¯±±³³³³³´´²¸¸¸´µ³±°°°°°°®±±±¯·´¸·¸µ¼º¿¹¹º»ºº¹¹ºº¼¿½À¾Â¿Ã¼º¸··¶º¾¼Â¿Æ¿Á»¹µ²¬«¥ ˜’†weVQTsÏîõúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøðéÝÝ×€GAXVNLM^[KJMNJF?:2(&&,8G^WR>7;FGHGGJfjY@<PpPQ<2,'$#"" %+06=EJPRSSYX[_eeecefhedcbbgbbcjklfdddefglilefghhlfmggfjhkklnpppppmllpmooopsrtuutsqrnnnrswwztstvuuw{vttzrtpwswvwxxstpoprprqqpomjikmvsstwtustsrqqquttrompoooqrytuorqyqplnntpqlolmidULTehlkkkkkleghjiginhjjnjgfegljjikeieddccc_bchhfgljkhihkeigjeffgffcc_b_``aacbbdklpklmnijjmmkihi–”—–•““’’“”“’’’”•˜ œ››˜™˜™“•–—–—˜™™˜—•’Š‹‹Ž•”‘‘’˜’–—˜–˜——‹‚€‡Œ‹‰Š††ƒ„ƒ„„ƒˆŒ”–˜œžŸ¡¡¢¡ž ¥£¥¢¥¢¢¢£¥©ª«¨¥¡Ÿ¢¦§¨©¦©©¬ªª«®¯²±°®ª®««««¬²···¶µ´´³²³´µ´´µ¹°²²²³´±±°±°°¯¯¯°²±°±°²³³³¹··µµ¶°®±±±°¯¯°°°¶³µ´¶´µ¶¹¸¸¸·´¹¹ºº¼½½¼¼¼¼»¼¸¸µ³±¹½¾½½¾À¿¿¼¼»¶²°¨¡™ŽwhYWVv£Ñðøúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ïèÜÜØw@7GQF>O^WKJOHDB>;5+',2@NOQB4348:?BDGjgC+2AEJ7(&$#!! ! #'-2:AGKMORTUVZ\_cfeeehlfbdecbdcddjhfcdcbafegggeeeigkfedddffkjlmqqolkhiillnmpprrtqtrpnnnnorssssqtvrouwvsrqtrrqqrvuuutqpnnnoomlnnljjjnrqprtuuvssqqqsusrtnmmqqppsuvttprppookmmnnmjmlngWLVcfjlmlklme`jkigiiifijjkebfhhhiiheeccccc`_cdefghiijfigkfgfgeeddcbba_`_`_aacddekllkmlljiikmjhhišš›šŸ””‘‘’˜™Ÿœš™˜›˜—˜š—ššš™˜™š›œ›Ÿ˜••’ŒŒŽ”•”““‘–”˜——™œž ›”†|ˆˆ’ŒŒ‹‰…†ƒ„ƒ……†Š““š› Ÿ¢¤¤£¢Ÿ¤£¥¤¨¤¤¥¥¤¦¥¬©¯¦£ £¦«¨ª©«ª¯¬¯³²¶²³³³´´°²±µ°¯±º·º¸ºµ¸³´²¸··µ»¶¹¯±±³³¶±³°²±³°³³³°®¯³³¶¶·³¹¸¸¸¸µ°¯¶±¸³±±±°±±¶¶º¹º³µµ¸¹¹¸··º»¿»Æ¼Â½¼»¹»À¸¸³±¹ÃÃÃÄÄÃÁÁÂÃÅ¿¼¼¼µ¶ª£—Ž…~l^VX{×ñöúüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöîçØÙÚm95@??CTTTMHGA@?=<4-07?ILNE90)(*19=?DoQ/%&+/-'#!!"!! " !%*/5<BJJRSTVZYYZabdilhhhmgedddfghhgfjgddecacgfiffhkjjgkfieeefejjmmqonjhgiklnppvqrstrtqpqstuuuutttqwrqqvvvqopwssrtsvv{ttsropnnmlkonjjlnspooqtuuyssrtswtrssrqrrqrrvwxstqtoonnottvmmlrlq\NScgkkompkngdclkihkjjjmnricelhiikiiedcfb``_`cegejilhiiiijjjgiihddbcaa^^_b`cdkhigpmnmolkjihiggini™˜™™˜•“‘‘“—šŸŸ š—›’•”–•••™•™œœœ›š—‘ŽŒŽŽ”““’‘‘‘•”——™›™šŠ}|ƒŠŠ‹‰………„‚ƒƒ„ˆ‹Ž’•˜›œœžžžžžžŸž¢£¤¥¦¤¤¤¤¤¤£¬¨¦¢¡ ¤§§¦¦¦©©ª¨««®¯±³²±³±²´´´´±±±±±³µ¶´´µ¶´´³²²³²¸µ¶³²®¯°²³²¯¯°°°°¯±±²¯®¬³µ¶µ·³µ´·²´³±°±±´³²°±°±²¶·¹¶¹³³³´²³³¸¹»½½»º¼À¿½º¹º¹·¶µ»ÀÃÂÂÁÄÅÅÄÃÃÄÀ¿¾¼·µ¯§ž˜’‰€p`[Y|®Øò÷úüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöíæÛàÒc839;>EQZXOD@638<82/<AMJIE?9.+%&+7;<=@B*$#$$$"!! #)/5:?CHONNTXYZZY[]cggkkhhggcdedeghiigfjeeeedddffffghhffffceeeffekjlllmjhhhjlnorrsorsrqporusstotrsrqpqoqrqnnnoptsrqtvvsrqrvqmnnnmmmookimopooprtuutttqtsussoqqqooosrrqpntqolonprqoolmmnl\NUbdgjikkjhe`efkjiijijjoolggjlgiiihidcbb_`_beeggehilfikiijihggedadaaa`\^]b_edffhhokkkjjjkjhighjmhœ™›—™”’’’•œŸ§¢£œ›–š›œ—™”“–™—œœŸ›™—“‹ˆŒ’š“””•Ž“’™™šœ£›’t‚…“Š‹‰ˆ…ˆƒ„‚ƒ„…ˆŒ“˜š›œŸ¤ž ž žž ¢¤¦¦«¨©ª«ªª¥¨¦¬¦¤£¤¥¦§ª§®ª°©ª¬±±´¯¹¸···²¼´·µ»´¸²²²¹¸¸´´µ¶µµµ¹··µ¹µµµ´¶¹±±±±³º´µ°°°µ³´´´³´µ¸··³µ´¸´¶²±±±²µµµ²´²²·»ºº¹¹¶¸´µ¶¶·¾½¼½¾»À¾Å¿À¹¼·µºÂÄÆÄÉÃÅÅÊÆÌËËÊÉÉÉÂÀ»¼¶±©£›–‡qaWYºÝòöúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõìæÖßÈ[637;>FP\dJ:0).654/08D?:;<<:86'%)31025/%"!! !!!! !!!!%*07?FKPQRV[\\]_afddekhjjnikfdcldehmlpjjhkefhkedeijmikiheeffdfeffhdljpkkihihjlnppvutqwsqonovvussstrvqnnoqwrsponqsysrswvvsupoppqxnomrstnmmrqqpppvtyxwvutztvsrpsruoos{rqommsqnmpqvsronoppr_PP_cginjkkjgabihjjmjlkkjwnhgklmjjhihieebbbabfelhjjlllioklhhhfglddde`bba`a_bbgfhhmjpkkkkkjjoklikkmh˜—š••”“’”–œŸŸžœ—›—––˜——••–––—˜—˜Šˆ‡Œ’“••’‘Ž“’˜š™ ‰~zq{†…†…„„‚‚‚…ƒ†‡Š“™›œœœœžŸšŸš ¤¦¨¦©©§ªª¨¦¤¢¦¥¥¥¥¥¦¥¥¦©¤¨©ªª¯³µ±®¯°³µ´²²³³³´²¶²³³´°°°±¯¶±µ¶¸·¸µ¶³¶¶¶¶µ±±°±²²²´¯±²´³³²³°´³´±±²µ´³³´²²±²³³³µ±´µ·¾ºµ´²¹·¶¶¶³¶·¾¹¹¸··ººººº¸»´¹¾ÂÂÃÂÅÃÄÆÈÇÇÇÊÊÉÉǾ»»»¹¶¬¤ž˜€o^Z[ƒ¾ÞòøúýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõëåÚݪR56=@BFQ]O<-!%*42.+,.,*-08D9@2)&(()('%%#!! $(.5<CINRWUUW\^```bedeffggiiigfdecfdhjkkkkhfedgkfcehkkjjjifcfdcbdfikhdljighghjklnoooooppppomopqrrssssrqqolpsurrqportvstvtrsstnmmpuqlomsqnkopstrnpprqqqqqusttssrorqpmqusspnnnoponqtsqponnop`QTZ_dgjkkjkieeghhjkjhhgjkjieejlkjjggfedebbbddggiilmkijiiiiffefgfcbbbab_a]`_aaffghjihhiiihhhijjhihih™˜š›š˜—œŸŸ›œœ››Ÿšœ™™™š–––——•І‡‰Œ”’–••‘‘ŽŽ‘“š¤•~o|w|ŠˆŠ‰ˆƒ„‚‚€…†‰–—š¡¡¢£¢›œž›ž¡£¤«§¦©©ª°§¤¢££«¥¦§«¨ª¨ªªªªª©±®µ°¸³°¯´²²±µ°±²µ´¶¶·³·®²±³³·µ»¸··¾µ½¸À¹¼µµ´³³³²·´´³¸··´µ¶·³³³³±²²´µ¶··´´±·³³´´´··¹¹º¶¶µººº¶··¼¼¾½½µ´¶¼¼½¹ººº»ÂÂÇÄÅÂÉÇÍÌÌÉÍËÑËÎÇÆÂ¾ÀÆÄľ·¯¯¡™„o^Y^‰Äáó÷ûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôêâÖÜ‹G5<HKMRUL?0#!#*1-+)(&$$%,69:4-'$#$$#""""! "#*07AFNOVX_[Z[\^cchefekkjihjoijeffghihmlnmnigedehfddgirihimgeeeddbcfmjhhkhgfhfikoputtoqpqqppooursrqs{stsrrrrttzswttttu{vxvtttstpqorqnnnosomnvswtuuutuqqqprutttwrqnxppqvuspnotnmnoqwtspposoqdUQ[adhjlnlljhhijkimljjjinllcbfnmpjjgfeccfdedigkhjippnjlmmikfgdiedbcbbbbbabccddefhhkjihliigghkkjhjhhiššššž™——•˜˜ž˜™™›œšš›š˜™”–’Œˆ‡ŠŽ‘’’ŒŽ‘“•’ ‡|vjv‰†……€ƒƒƒƒˆ‰‘—›š žœ™™–™›Ÿ¢£¥«¦¨«ª©¦¡ ž¢£¬¦§¨©¨©§§¥¥¦ª©°¯¯¯®®®±¯¯®¯°´´·µ³±±®²°³´µµ¸º··¶µ¸¶¶´µ±µ·¶´´²µ¶´³·¶·²²°³²²±²°±²µ¶µ²µµ´²±±´¶¶¶¸·¹µº¶¶´·µ¹¶·¸»º¸´³´´¸¸¹·¾ÀÁÀÂÂÃÃÂÂÈÇÉÉÌÆÉËËÈÆÃÂÂÂÂÅÇż·°¥Ÿ•Œ~ob__‰ËäôùûýþÿÿÿÿÿÿÿÿÿÿÿÿÿþýóéáÓÎq:3CEKQ[M>2&$$%+)('&%#""$(+.,*'""!#""""!!" !!! !#(-4:@FMSTVX[^\]___bbccdekijkjlhdcafeffggllmkifgcegffehghidfedddddbdceghhiikgffggjknpqrpnpqommmoptrsrsuurrrtsssuvvsttusuvuuvwussssqponkmmnpmjnruuvssqploqqprrrqqqroommmrtttsnnnnmnpqrvtqmnllleYST`ijmnqnkjjjjjjjggfgeijkma[dhkkjiiddccdddeehhigjjjjkllklikefddacbbabbbaa``aefedhikmihjihhhhjjighgim¢¡¢ ›š—–—˜šžœ¤ž¡Ÿ›š›œ™™––‹‡ƒ‡’’’‘‘‘‘—šœ›–yixox‡Š†…„…ƒ…€††ˆ‹Ž•—œœœ¡¡œœ™™——¢¥©¦«§®¬¬¥¢¡¡¡§¤¬§¦§«ªª¦¦¤¥§ª«±¯®µ°³¯´¯°¬²³¹¹¹¶·³²±²²²³µ¸»¶¸·¶¶¸µ¾·¹¹¹··¸º·¸¸¸¶´µ¼µ·±²±´´µ¶µ´´µ¶µºº»·¸···½µÂ¶»¸¹ºº¶¸¹»ºº¸»¹¼»º³®°´µ·¹º¾ËÁÃÃÇÂÄÃÁÂÈÇËÊÎÆÉÉÉÉÅÅÊËÍËÈÈÈÁ¿¼º°¡šŒre]aÐåõøûýþÿÿÿÿÿÿÿÿÿÿÿÿþüòéßÓº\319BGUNC3)%$$%&%%$$##%"#%&''%$!!!""#"!!!!!%*07>GKPT\]\]c`_^ba`_cbecdelkojigfcaafflfggmmoigfghjggeffhhiejfgccdghiiijjikjkhiiikpnrrrqonqonmnlprttutzuvtwtvtxuzvwsuwyuxx}wxxwrqrvrsnmlqnrmjlpsuvzuyrqnoquuussrqquqppqqxvztrqrqqnnpusvrpqrlnidST\jkoppommtjlklkjjkjihmnrbX^mkokjigceedfjjjkmllkjkmkkllklikeheeeedeabcfa`_`bifedhimlihijihjklihgkklj£¤¤¤žš™——˜šœœœœœžœ›šššš—”‘‡Š‘‘‘ŽŒ‘’••”–š‡}tdr{‡†…„€€€~‚€†ˆ‹”—˜šš˜œœœœ˜›šššž¡¤§¦¥¦£¤¤¤ ¡¡¢£¤¥¥¦¦¥¤¤¤¤¥¥¦§««¬¬®©¬¬¬¬¬¬®®³µ·¶¶µ³°°®²²´¶·»·´´²µµ·µ´²²°¸¶·¸¸··¶¶¶¶¸µ¯®®²±´³²°³²´µµµ¹¸º¶¶µ··¹µµ¶¸¸¸·¸¶¹º¹···º¸µ²²²±²µµ¸º½ÀÁÂÃÄÁ¾ÆÂÂÃÆÅÉÈÇÁÀ»ÉÉÈÆÉÊËÌÅÁÀÁÁ¿¼¶²«£—teccÓéõùûýþÿÿÿÿÿÿÿÿÿÿÿþüòèÝÕ¦R.-05:>D3'%#$'$$$$#####""####""!! !"!! !%(/5;BHQRSX]^a``bca_ba``bbcdeflnlihecbccdeedghjjhefffffggefdgfedgfecefihiklkjhkkkihhjmnnqsqpoormmmlmprrrusrpututtturrrrruxyswwvwyztorqpmljmlqllimquvvvussrpoqrssvvutsrrrqqrswvusrqqrqjoputvpqljiifUIXajkmllllkjikimjjhiijinpfZ^dijkijhgccceghfiklklkkllklnlllgfeffeabcbabcca`_abiedbfgnkjihhhhjgkhhhfbji©©¨¡œ›™ž›¤œ›œ¢¡¡¤¦ž¢—•‘‘’”‘’’’ŽŽŽ‘“˜—ŸŸŸzkght‹„„„„‚‚‚†Œ‘•œ›Ÿ™šœ¡¡££¤ Ÿ¤¢ª¤¥¥¦¥§£©£¤¡¦¢¤¤¥¥¥¤¦§¨££¥¦§¨«³«««¯®¬®¬¬¬¯³¼µ·¶µµ´´µ²·¶º¹·····µ¼¶¸´´´´´¸·À·¹¸·¶¼¶¶µ´´´´º´·²±³¹¹¹¹¹·¸¸¹¹¹¹¹¹¹¶¶¶¸¸¼º½¾À½»º¿¸»´°±²²º¹¹¹¼¾ÃÀÆÅÅÁÀÁÇÂÈÈÊÅÎÄÆÁÁÄÉÉÒËÐÉËÅÁÂÂÄÇÃÁ½¾µ°¦ž’‡xkcg˜ÜèõøûýÿÿÿÿÿÿÿÿÿÿÿýúðçÛÔ•J+))*/30'$###$######""###"!"""#!!!!"" #'-3:@FPSWYZ]__`bcbddebbbcbcfifglsnlhhddcdehiigghjgffgflgiijeecgddfleefkkriklnmlllllihikmtnqqqopotllkkmpqvtuqpqvtws}twrssttxxyvvwxxzvsrrpokijmlqllmuuzyyuvvwrpqttttywyxxttsrrvvwxwvvvvrqoprwuvpqkkikWJRgclnqnoosllhojmjjijjnkqjb^gejilijggefffhkjjlnkolmmnoolkklhjfgfea``_`bbdbbbiejddfljomqnlklikkkkmhecig¡žžœ››žœ›››œ£¡¡¢žš”ŒŒ‘‘’Ž“”––‚r^joy‡††ƒƒ‚‚€ƒ„„‡‹‘’—™œŸšœ ¢¢¡¢› ¢¤¦¤¤¤¤¤§¢¡¡¤Ÿ¦¡¡ ¡Ÿ¤¤¦¢§£¦¨ªª««©ª««««©««¬°´´´³¯®®°°²³´±·¸ººµ²²±³³³²¸³³±´µ¹¸·¶µ³´²²³´³µ·¸·¸²³±²³¸»¹¸¹¶··¸¸¸¶¸´····¶·½º¾¾¿¿¿º¹´³¯°±³³¹µ¹»¿ÃÂÀÀ½¿¿ÀÂÂÂÇÇÇÅÁ¼ÃÁÇËÊÉÉÈÆÃÁ¿ÄÅÇÉÇÅÄÀ¾¼¸²ª •Ž|lii—ÔéõùüþÿÿÿÿÿÿÿÿÿÿüùïæÙÕ…C*(''''&%$$##"###$##"!"!""!""!!"!" #&+/6<BHKPV[\]^_`abdb`edeccccdhmigjponjfc`dcefghhihfeeeigggdcaieecccfgfdgijiiijkkjjjmolhjjlmnmonooommjkjmnoorupmpqqqssstsrrmtuwuvvwyywzrsqpnmjklmmlimpvvxwxuutvqrsuwvtwvusuuutttuvussprqqoomprutspqklfXJS`adnpnnopmgjikjjjifghmihb``gekjlhgffdeeijjjlpmkojjikonjkjmfeeffcaa___babbccdeeaehklmkpomllgjigehhgdee¡¡¢žŸ ¡¢Ÿ¡¡ ¥¤¥¢£›™’Œ•””’”Ž‘˜˜™›œykf\lƒ‚†‚‚‚‚„‚€€„Љ’”œ˜Ÿ›¡ ¡ ¡£¢¢ ¢¦¤¨¤¥§¬¤§¢¤¤¤¤¦¡¢ ££¤¥¦¦§¨®¬¬¬«°¨©§§«¬ª«®°¸¸¸°¯¯²³µ±¾º¾¶³´¶µµµ¶²¸³µ²¼·¹·¶µ´²¶³·¶¶µ¸¶¶¶·´´¶º¸½¹¸¸¹¶¾»¿¸»·¸·¼¹ºººº½¼¿¿Ã¿Æ¹¸´²³·¶´¶¹ºÀÀÁÁ¿¾¿ÃÁÁÁÆÅÍÂÁ¿¼ÀÃÇÎÌÐÉο¼¿ÀÄÎÈÊÈÈÅÊËÎÅĽ·«¢›}pjn›ÜêõøüýþÿÿÿÿÿÿÿÿüøïåÙÖz=)&%%%&$$$$####$##""""#!"" !!!!!!! $)/38>EIORTZ````abgcgcbbefffffihmmmnonlkjdbbdchhkjokmhfddfhgfbacjfecbdkgeglkjgghqllikkmmljnownqoqnnnokkklmpppprqmntqrqttwssrqqutxvwu~}{xyrxpnnmlqrrnllssww{wyyytuuvvxw{uxvuswwwvww{vtsuutpoprpwtytsoqmo\OP`abipomnoollmnojkjifgimjeb_cgekllggffgjkmmqmnmmlollijmnkkjoihgjebbd`b`baccfflegffinmqmoopllhiigfggkfgh¡ ŸŸŸœžŸŸ ¢¢¡¢¢Ÿ–‹Ž‘“‘ŽŒ’“”•…‚pZefsˆ†……‚€„ƒ„ƒ‚ƒ…‡‹Œ‘”–™›˜Ÿ››œŸ¡ Ÿž¡ Ÿ¡¤¤¢¤£¥¨¦£¦ £ £¢¢ ¡ž¢¢¥¨¨¨©ª¬¬©¦§§¦£¥¤¦¨«§««¯°¯«ªª¬©¬®³²¶°µ¸¶±´´µ¯®®°°´³³³´´²°±¯¯¯²±µ´´´¶´´¯¶µ¸»¹··µ¸·¸·¹¹¸·¼´µ¶ºººº»»½¾¼¼¾¿¼·¶³´µµµ¶¸»½¿¿¿¿¿¼¾¾ÃÂÁÁÆÆ¿·¿»ÂÇÇÌÊÊÇû®½¾ÅÈÉÇÈÄÅÅÊËÌÈǽ´¯§›rlk˜Þìõùûýþÿÿÿÿÿÿÿû÷îåØÓo9(&%%%%$$$$######""#"##!"!!"!!! !"(,27?EIMOQUX]ecbccddfccaccdcfeggijjkmnojheeadhediigfiiigedeegbf_bgeccadffdgmjffefhiiljkkkjjjonnnnmmjklkijlnpomopqpoppoqqsrpooorsutuuuuz|zzuqqpmhlmpspmmptuvvvsusqpuwvvxxzuxutsttvvwwvttsvusoqrrqqqqqpmki_RU]_ahppjkkllljjjiffefdhkml\Taggglhgggehjklnnolnonlnklhjklklhhihhfccbb``_``bbddeeggjmmmnllllljghhhhgeefgh££¦¡¡ §§¦ ¤¤¤¡¢Ÿ›’Ž•‘‘’—‹Ž”•–˜˜ykeYfxŠˆ…ƒ€€…„„„„‡‹’’˜—™™œ› ›Ÿ ¢¡¡¡¨¡¢¡£¢£¢«¥¥¤¤¤¥¤¤£¤£¢ ¡¡§§§§ª±ª¬ª¨©¨¦¥¢§§§¨ª¬±²²®¬¬¬¬®±´²·°¯³··º´´¯°°´±µµºµ·³±¯°¯´¯±²¶´¸´¶µ´µ¶¸¾½»¹¹·¼¸¹¹º¹··½´·¸À¾Ã¿Â½À¼»»º¹¹¹»»º··»¿¿¿¾Æ¾¿¾ÂÁÁÁÂÄÈÇÆ½¸¼ÀÃËÆÆÇÇÄÀ¼¶»ÁÄÌÉËËÑÎÌÍÎÌÎÊÍÇÉÀÀµ©œ}ogkßêöùüýÿÿÿÿÿÿÿúõíäØÏf5('&&%%$%$$$####%""""##!#"!!! !%)06=DKQUYXWX^`ijiegghffffcefheggkkmmoosnoifdddkffgjhffjklfeegghceffdbbbcgfeemkiffefgjimmnkliomqnnmonmihhhlvpqpnosqqqsppqtstpmmmottvvvuyv{ywutuvollqqqnlmpsywyuvtxqoqvwxwzz{vxyysutzyyvtrws|usqusvrtqpppjhaWT]`agropikltlllligefefgklraV[kkoklgffghmlqoroompppnrmmlmmnkqhkihhfdfcc`b__adcddhfgkonqnomlmqmjhjinjihigge¢£££¢¢£¡¡ ¤ Ÿž˜“‘‹Ž‘“ŽŽŽŽ“”‰p\ddp…‚Ї†…€€€‚‚„…‰Š‘’•————š›œžž ¢ž ¡¡ ¡¡¢¢¢£¦¥¥¡¤£ £ Ÿ Ÿ¢£¤¤¦§§©ª¤££¤ ¡¡£¥§¨®±ª§¨©ª¨«¬¬«¯³±°°¬¯±·¸¶³µ°°°°°²±°¯¯¯°®¯¯¯°°°±°°°±µ¸¹¹º·¸¹¸¶·µ¹·¹³µ´µµ¸º¿½¿½¼¹¹º·³²²ºº¹·¸·¼Á¿¼¾¿¾º¿½¼º¿ÀÄÇ¿º¹µ½ÀÅÊÉÅÄÁ¾¸¸ºÁËËÉÉÉËÌÎÏÑÔÐÌÌÊËÈÈÄÃÀ³¦˜‰}ollšÞíöúüþÿÿÿÿÿÿúôìãØÄ\3('(&%$$%$$$#####!""""!!"#%(.3:AGPTY[^]]]]aceghcfggdfffdgjfegillmmopnjieeeddeegjjhggjjgdeeedcceeeaccdeddeffdffghfgiimmjhljolpmlljfedffjmppqqqqoortrnpsrrrmmloppqvutryuxvtqqpoknpqkjilnrtuuutwrroruvyvttttsxuutttvuurrrstttspvttqpooqlfcZWY`ejoomljllnkkjihgdeeghjlaT]fjiiihefgjjlmmnommlllnmmlljkijjjgigghedfcb___`bcddbieiopnmmononpmkgggnihghfgc¦¥©£¤¤¤£¥ ¥Ÿ –‘‘‘‘“’—”~reWetz‹‡ˆˆˆƒ€€~‚ˆ‹’”““–›˜œ™žš›œ¢ ¤¢¢¥¡ ¤¡¢£¤¥§¦©¡¥ £ Ÿž£¡¤£¦¤¦¦§¨©¥¦¦¦Ÿ¡ ¡¢£¦ª´¯±ª§¦©ª¬¬²°¯¯µ¯®®¯°²²ºµ´³´´´³·±²²±±²¯±·°±°´°²²³²±´º·»ºº··¹¾¸¹¹¹¹¹³ºº»¼¾¼¿ÀÁ¼¼º¾·³´·µº¹¹¸¸¼Â¾½½¾¾¿¼»¾ÆÂÉ»µ·¸¼ÈÄÉÉÊÃľ»¿ÆÅÏÉÇÊÍÍÌÍÜÓ×ÒÔÏÐÐÑÌÌÇʾ´¨›‘‚tkn âìõøûýÿÿÿÿÿùóëâÔµS0'''$$$#%$$$$###$"###" "%*08@GNTX[__dbbbbefeeghikhgfjgfgkifgkmrnpmrnkiiddejfhgjjnhjhkgfgjedeffifffgggdccdeeeffjggglmmjghmlonpkkkjdadeflmorwtvpnowtqnpswqqlmmurxqutrqzvyurpoomntqqkjjpqvvwuvuxrqqvvvwwsrtyxxustwxzwvsvr{ttssrvtxrooplkebWW^ciuppkllqmnkljjhkjiiijocXXhijjjhhhghpqspsoqmnllkonmmmlljrjhgjfgffeeba`a_cdkfeehgkppmmmunnnommjkjmigfgghg¥¥¤¡¤¤££¡Ÿš–”ŽŽ‹Œ‹’–Šsbd\lƒ‚Š…‚}~~€‚„ŠŽ’‘”••–—•š˜——š››œŸŸž¥Ÿ¡¡¤¢¤¦¤¤¦¤£ žœ› Ÿ ¡£¢¤¤¥¢¡¡¡ ŸžŸ›¢¢££¥§±¯««§¦¦«¯®±±±®¬°°±³µ²²³²²³´³±°®¯®²¯°¬±¯³®±²³³µ¸¸·¶´µµ¸¹º¸¹¸¶µ´³¹¶º»»»»¸···¸µ²´µ¶´µ²¸·¼À¿¿½½»½¾¼º¾¿Á½¹®´·¾ÁÂÂÂÁ¿»¹ºÁÇÈÈÊÉÉÊËËÍÎÓÐÑÎÏÏÐÑÑÎÌÇÇÄÂÀµ«¢•†uqs áìôøûþÿÿÿÿùòêâУL/'''%%$#%$$$###""!""!! "$*/5;CLPXXZ]``aaaaadeffgiiijhggggghhhhhknnlolkiihebdfgehhiiheeeddgkiefihfeeggdbbbccddghgegeghhfffghmknolijjjbdffgkmpssrtnpqqqpmqqqmnlmnnooppprrttttroooonpnlhjkoqrprsttuqrswvvtsrrrtuvsttwxwuwrsssssotsusstqplhhdWQ\bfkoooimlnmkiiiigggjjigcZ[^gjjjigggijqsropnmjjjlihhjjmllkkkjgheeecabbb___edghhghhmoomlkmmnnnnlhjkkkgefeec¦£¢££¢£¡ž—“‘‘‹Œ–—™ueVbktЇ‰…}„~~„‰ˆ‘•–—™š™™•š™˜—™šžž žžŸ¥ ¤¢¥¢©£¡£¨£¢ ¡žœ››¡£©¤¨¤¦¤©¡¥¢¦žžŸ£¥¨©¬§±¯®®®®²°±±·´¸ª²¯³±±±·±±²¶²´³³®°°±¯²®°ªª®³¯²²¶µ¹³¹¸¸·¶´¹µ¼¼½¸¸¶µ´³µ¹º¿¿¿ºº¹¾µ³±¯³¸·¹¶·¹Á¾ÄÀÁ¾¾¿Ä¼½¼»¿ÇÁüµ¸¾¿ÆÃÌÄż¹¼¿ÂÈÆÍÉÊËÌÎÒÒÑÐÕÔÔÏØÖÕÒÔÐÓÌËÍÐÅÀ¾º©‹|tw£ÝéôøüýÿÿÿøðéâÑ’E.''%$$%%%$%####"" !%*18?GMWZ]aefgghbbaddggjimnojnhjikhhgfhkkllmloigfddcdddgfjhkihfgeffjihfjjlfeemeaaabedhhkjjijjjjjfhfjjmlrljhmklkkknnoptrqqtprrsoporrrqpoonnorppqtstuuuursssttliilosruppr{tvrutwwwsttuuuuztww|wwvxrstvststtzsrrtojiiXNVgglnqnomumolkhmjkfgijilg^\^bgilihhllmmrqrssmkighkkkhiinnplmmmjnffcbbebb_`bedghoikkooqmlklmpnolkkkkkheehege¢ Ÿœ›–“ŽŽŽ‘ŽŒŒŽ‘“ŠudbXhx|‰†ƒ}~~}~~€‚ƒ…‡‰Œ‘•˜š˜˜™—˜”””––™šœžžŸ ¢ £¡¡¡¢ ¡¢£ ¡ œ›ž £¥¤¢¢¢¥¤¤¢¢¡Ÿš ¢¦¨©ª©§ªª««¬°¯°°±±¯®ª¬°°®®¬®°°²²²°´¬««¯¯®¬ª®¦«¯°¯²³µµµ³¹´´±³´·µµµµµ·´³²µ·º»½½¿¶·¶µ¯¯¬°³¶¶·µº¿¿¿¿¼½º¾À¾·º¹¾ÁÁÀ»½ÃÅÆÆ¾¿¿¼µº¼ÄÊÇÆÉÇÊÌÐÒÑÐÐÐÑÑÑÏÙÖÔÐÐÎÏÌÎÏÎÈÆÅ·®¦’xtÜêõúüþÿÿ÷ïè×Ð>,&&%$$$$$$$####" !&*/4;CIQTX[^cifeeeha``ddhhiinmnjjggfffgfgikfllmjiggcbbbabcgfgffghcddggifgfiljhgggcccbafeghhhiekijfffeekmmlkhjimmmlmmnloppqpnpoqqsmpppopoononoqqopqsknnoourqoqnlgjkpsrrtpoottsrrqwusqqqrprsstwyyvutxqssrruxvttqrqplkjZMU_dhmonkkknmifhhhggfjkkhhd`^cgghhfgimnnopnnkjjkigfmnkhhgfffgjiihhgdabc`]c_bedddehhkknnnlkklmnnoikggegffeee`\£ ¡ ¡—•’ŽŒŽ‘˜™š…€hYado‡‡†‡†€}|~ƒƒƒƒƒ†‡‹‹Œ‘–˜œ™˜›—™Ž‘–™šš› žŸ £¡£¡¨¡¢ŸŸŸ£¡ŸŸŸžž¡¦¦¥¤£¢§¢©¥¥¤¨ Ÿ¡ §§«©§§«¬«µ³¸²´¯´®¬¬©³±°®±°±°¶µ´±³ª«¬©§©¬¯±°´³¹µ¹´´´¸¶¶³µ´ºµ¶µ¸µ¹º»»¼º¿º»½¿¶º´±¯°±¶µ¶¸»»Â¾Ç¿Á¿Ã¿¾¼Á¿Æ¿¾ÀÃÄÇÂÉÆÈ¿À»·¼ÇÈÊÇÆÉÍÎÎÑÙÔÕÕÕÖ××רÚÖÕÒÒÑÔÔÔÑÏÑÒÑÑÅÅ´§’ttŸØæõøûýþöïçÑËu:+&&$$$$$$$%"####'+19@EKS\^`aafniifgfgfghjinnmkpoojiggefgihhhlkklmhfghbbbcbbdffifhgheddghjiiiijkjihgggcdcffkkmijkljkfhgjkmopkjjlmrpsoqqqopppppqroqpsotttosopppqvsvttrtnnnnouqpowmkippvs|utouqrssssrwtsqqpwpsrttxxyvuswpsqprvwwtsquqpnq^RR\`eksnmlpkmjgfhhgghgokkhib`bhjlihefinotrroplkikjhhnlkjigdefgjjjijebbeb`_b`eejgiijkonnonkjkrnnmpjlhhhhffhld]_ Ÿœ˜”ŒŒŒŒŽŽ’•”’yjdVguz†„‚€}{{z~€~‚ƒ…‡‰‹Œ‘”—˜šœ›–•“‘‰—œ›™š›¡ž ¡ŸŸžžœš Ÿ £©§¥¤¤¤¢£¡¡¢¢¤£¡ ¡¨©©©©§¨¨©¨¬´±²²°¬¬©¬ªª¨©ª¬«¬«°°°±¯®®©¨¦©§¨¤©«¯±±±³´¶³³°³´¶¶¶³²²´µµ´¶µ¸¹ºººº»··µ¹·º°°°²³µµ¸¹½¿¿¾¿¼À¿¿¼¼¼½¼Â¿¿»¾¿ÂÂÃÂÃÅÀ¹´¹ÂÄÅÇÇÉÌÍÍÑÓÕÓÓÒÓÓÕÓØÙÙÕÕÓÓÓÔÐÒÒÒÒÒÒÏÇļ²¢‘€vq—ÎæôùûýöîæÎÅi7*%%$$#$$##" #%*/5;AJNQV[^aaafljgieffgeghklmmlkkkkhgfffhhiijhmpmlleehc`bbcdefgffefgffedfehhhhhggeiheegbdcefjkjhijjkhdggjiihjjklnoooonpononnpnnoonnnolnnooomorrssqutrponnmopvpqplfiipsssvttpuqqqrqsrsrrqqqpoprssuvzsuqrppprtwtssrqpnmn`PT[^bjmnnlloklhgehdggghomjfb]ciikiggehknpqqpoomkjjiiijjjnc\^`ffkiikkbcce`````ffffjlllnoomljkllkjjkhiihghehkhc`c ™™’Ž‘“Ž’“•—•š‰„l\``l†„††€}{yz{|ƒ‚ˆˆˆ‹Ž“”˜—œ›š›œ••’’“––¡œ›šž¢¢§£¢žžœœœ¢¡¢¡§§¨¨¨§¦¢¥¡¡ ¡¡¡¤£¥¤©©ª©©©±§ª«±®µ°²²¯©©ª¬«®©ª¯ª¬¯®¯«ª¬±¨¨ª«ªª®²²¶¶¶´·µ»²µ°·´µ¶¹²³³¶´¸¶¹¸»»¾¹ºº»¸¹µ¾·¹´´³·µº¹ÀÀÁÁÁ¾¾½¿¿ÁÀÀ¿Â¼ÁÀ¿¾ÁÀÈÆÅÂĽº·³¸ÅÂÃÅÊÉÍÎÓÑØØÙÓÒÔÚÖÙÛÛØÜÖÙÙÚÕ××רÙÙÚÕÕÌÊÄÄ´ª‘qn›Õäôøûôíå̶^2)%%$$#$#" "'+18?HMQTY[abiijlmlkhifffgillpppnnkjjmgfgmgnmmjlkpookkeddaadfihlffggeegkggdfgmiggkgiiifdefcddegjjjijkkigejhohiijloosoonqpuppnmmppuonmpoplnnoooooqustqwsqnopqrssvprlgelnwwwuwvuuvqsstsxxyrpqwqqopr{utu{svqttsswwwsqqrqpppbWVYZbiwpqnllolkkkgggggmlqlib_`iikihgjklmqqrononkkkkiijkiidZSR`eekihhlagfeeeb`bghgionmlqqtmkjpmmlmloijilghhmihaaf•ŽŒ‹ŽŽŽŽŒ’“–’“}oeVdowˆ……„{zy{~}}€ƒ…‡‡‹‘‘‘”––––“••••””–œ™˜˜˜››žŸ¡¡ š™˜š™™™›™›šžŸ¡¢¢¢¥¤¦¡¡Ÿ¦¡ ž¡œŸŸ¡¡¥¥¦¤©¨¦¤©¨«§ª««ªª©¥ª«««©ªª®ªª«¯«°§ª«°¬®ª«¬¬¬¯±³´µ²³³³³µ²±±³³³³³²²²µ´··¹¹»º¸´µ³¹¸º´³³¶µµ³´µ»º¾¾½¹»»¾½½½¿¿¿¿¾¼À½¿¿ÁÁÃÀÀÀ½¶µ¶»¾ÂÂÃÄÉÊÎÏÑÑÑÐÐÐÓÔÕÕÙÚÛØÚÖÚÖ×ÓÔÕÙÙÙ×××ÓÌËÇļ°žŽ{vu™ËåõúóìäǧW/(%%$##!! "&*/4:@HPUYY[]_cdhijkkjiiidghijkklllmlkjihdfhlfikkjklmkjjjfddccgjjhkffggeeeeedcfhgggffeddfdddfbeeghhhiijhgfeejhhhjilnoonnnmoppnnlmnqrpllmmmljmnoppopqsstpppqnoppqrsvqlfffmpuvusvrtssrrssswuuqqqpppnqs{vutssrqrrsttssqppnkjjbXVTY[fonllmmlllmmjfgeghmlmjc]`dfgffghkmmnqnmlkkkhkkkihghhh]J<Taffkihfeabababbdffbhlmklmrnmmlkklmimlniihkfhlmgg\el’Œ‹‹•Ž‘‘“’”™”p_]Xg|Œ‹Š‚€~}{zz}}€€ƒ‡‡‰‹•“˜–›–••˜–—––—š›š™˜ž£ ¥ ¢™š˜™™˜œ £¡¤¤¥¤©¤§¡¢ž¦Ÿž¡¡¢¡¤¦¦ª¤©§¦§©©°¨««¬¬°¬©ª¬««¬¬®¯®ªª«¯¬°«¯®®°±±µ²···´µµµ³¶µ¸¸¸±´³¶´³²¶µµ¶¸¸¾½¾º¸´¹¸¸¸º´·····¶º»Å¹½¼»»¾½Â½À¾ÅÄÿ¾¾À¿ÄÃÂÃÆ½¿½¼¾ÁÃÆÅÅÆÌÉÓÏÔÐÒÓÔÔÔÔÙÙÛ×ÚÚÚØÙØØ×רÛÜÜÜÜ×ÜÕÓÒÓÌÊü¯¤“„xv›ÏâôïëÜæQ.'&&#" !$).39AGLPW[`beghfkhmnolnkmikkljnmmkkkkmrkiighjjjiikmjloplnijfedgfnlnijghgfdeghccdffeffehhhefdcceefimlklliieefeekhghjirrrommpmsrqnomnouppprmplklrnoqstvsxxzprrqqrrsquswmheiltrtutrwwxtrrrsuswtvvwtrrrput|wxttrxruswutttpqnkjldZUYY[bnnnktoonmllkigggllmlme_^dgkgihnnommnqnokkjjjjkooohgggO=CXbghkkkfhcb`fccdhhfemmnkonqnppqmsmnlmmnmnjkjklmgehmmŒ‰‰‰ŒŒŽŠ‘‘‘“‚xiV`dp…ƒƒ‚€y}~|||}„…†‡‰‹Ž“”˜–˜––––———˜›™šž›š™žžžœœ™™™šš™œ ¡£¡¤¥¤¤¤¢¢ŸŸžžšž›¡ ¡¡¦ª©§ª¥¦¦©«©¨§§¨§®©¬««ª«©®°¬«ª©¨«««¬®¯¯®¯±±²±´´µ²²¯³³¶¶¶µ³±³²¶´´³¶¶·¸¸¸¼»¹¶µµ¸¹¹¸¹µµµ·´¶¶ºº¹¸¼º»»¿½½½À»¼¼»»¾¾ÀÀ¿¼¾½º´³µ¿ÃÃÂÃÃÅÇÇÇËÍÐÏÓÌÌÍÔÖÙÙØÕØ×××ÙØØ×ÙÛÜÜÜÜÜÖÛÔÕÔÒÎÍÈÁº´ª™‡~v–ÈáëèÒ¸¦M.&#" !!&(-08>DJNVXZ_ccdeefffgmkkkkimjjhkkmkkjkjknkiighklmjgjllkmqnkheeeffefiiigfedcdbeheacbeeeeeedcgeeeddgiikiedeeedbbbefhgggkjjkklmlmmmlnlmnopompormoilmrnprrsrrvxsoqonlonsrqqkdfflqqrtttsttussssstqsrttuuqnootsttuutrrptsuuqoopnmkjh\YVZ\dknqomonmjkiiihgggmonke_`bflkfhinmlkmmmmlijjjjkkngihheVEJT^gijjjjggb``dcdfhiffghhioonloomjkllkmmmnkic]dhigfghhŽˆ‰ŠŠ‹ŽŒ‘‘’‘—•“wi_Ucv{Œ„„ƒ‚€~~}~||}~‚†„‰ˆ‰‹Œ’“™•—˜œ–—–›™žš™™ššžœœ¡ŸŸ œ›š›œŸž£¡¦£¤¤¨££¢§¢¤Ÿ ŸŸžŸ¢¡¦¦«ªªª§©©¬ª©§§§©§¯®©´°¬®ª´®±«ª¨¨©¬®²³²²²°´²·³µ´´´¹³´²¸´···´³³¶²·¶µµ¶¶ÁÀÀº¼»¹¸½¸¸¸º¹ºµ¶µ···¸Á»»¸¼»Â¾¿¿¿ÀÀÁÁ¼»¼¾¾ÁÀÀ¾À¶²´µºÂÀÃÄÈÄÉÇÎÎÍÍÑÏÓÎÐÑÖÖÚÚÙ×ÚÚÚÚÚÛÛÜÜÜÜÜÜÜÜÜÛÛÛÕÙØØÏËÉȾ±ž|t•ÄÚå̱F)#" #&+07>DKPUX[^cfigfgghiffgnjjkkjmlonolmlolnmljiihhjkolmmmlooonmfcdfgighgkhifedcchggedacaddddieddhfhhkjijljjdfdjedcddfhjjjjlkkklknnnlkmplmnrnmlppsnnnoosostsrpqvroptommsorrumfdhkqssswtvt{uutwststtvstuvsqmoqttssyvwrsqsrxronrolkjheYYY_fqrropponljlipikiijmopf`^cehjkfhkvmljsnomoimjlkokliihj^QLW^ggokkjmgla`cicffjgfghgfhqnnlonmmmmokrnomhd^`chkffgkgЉ‹ŒŠˆ‚o\a]l‚„†…‚€}||~~{{|}ƒˆ‡…‰‰ŒŽ‘’”–”—˜—”–•›–™šš™™šœ››œŸžœ™™šš˜œ›œœžž¡¡¡¢¤¤£¡ ŸŸŸž›ž¢£££¦¦§¥¥¦¨¦©©«©¨§§§¨¨ª©«ª©©««¬«³¬«ª¥¥¦©«¯±±°°²®¯²²µ°³±³¯±±´³´²±±±²´²³°µµ¶¶¹¹¹¶¹»»····¶·¹ºµ´´¸¸º¹»··¸¼¼¼¼½¼»¹··»º¼½½¼¿¾¿¾¶®³µº¾½½ÃÄÇÄÈÈÈÈÈÉËËÍÊÐÕÔÓÓÒÕר×××ÚÛÜÝÝÜÜÛÛÙÛÛÛÚÚÕØØØÔÓÑÏ˾³‰zo޾¼¶ˆc:%%%)-39@FKQU\\]aegjiiiiihhggghghgkhmmnkllkjjjjjkhjkihjjllmjmlmmkge`ceegggidffifeedccccad`baededdbcceeiikkjlkgfcddcacbdeikkjjjjjjilklkkknnommnnmmmnmmlmnnnonppomponloqnknnonpnmhgeknqutsttttttttutsstsusttuommqtqoprrrqoqqtrqmnnoljhheZSXZemnonnoonnkijihhhimoolg]^_dhiijfilolkkkknlmkljkilkmiig_WSP[cffjkkkjgcZbhhcddfdghhggimkllllmmmmnkkknlgccefghffede’‘–‘~udWakt‰ƒ…„…}||~€~‚ƒŠ‰‰Š‘’’••›•˜—˜™šš›››šžšœŸžŸ›£ššššžšœœŸ£¡¥¢££¦¥¤¡¢ž žœœž£¤«¬¬¦¨§¨¨©«««¬¬©«§§©¬¬®ª¬¬¯¯¯¯³¬ªª«¨®®¬´³¸²³²²¯¯²²¶²´´´µµ²¶³´´´³¹²µµ¶¶¶¶º¹¸¸¸¹¸ºÂ¶¾·¹µµ·ºµ·¸¾¹º¹¾·¶¹À¿¾¾½¶µ¶·º¾½À¿¿¼Æ½¿·±´º»ÁÀ¿¾ÅÄÈÅÎÈÈÈÈÈÈÌÐÎÕÔÕÔÓÑÔÕ××ÖØÛÛÜÜÜÜÛÛÛÛÜÛÛÛÛÜÜÜÝÜÜØØÐʺ«™†tnއcK0,-17=BISW[]`cgikllmqmmnnghhkhhiiikjrnpopkkjmjjijllllkkjmkmmnnnlleccieeefgidffhihhgceedddficfegcbbeeefijojjjjhfddcbcfffinmolojojjjlmoqqovornnpqosprmompnnopoponnpnkmrnkmrooqtlhghisrrsusxw{ttsstwsrsutvsuutmmouqpooqupmourxronqotjhhgYSTYbqrrnmnvppljijhiggjppqjc`acjjjiiioooprlmjnlqmoknjrlllnaWVVYfghhkjmlkfa`jiibaeggnjjjjmrllklkulnlrjhjrjgfggjjmhhddcŽŽŽŠ†tgcXgx|ˆ…‚€~~|||~€‚ƒ…†Š‹ŒŒ‘’’“”””““”—˜˜˜˜™™˜˜™œœœš™™š—˜˜˜—›šž£¢¡ £¢£¤¤ Ÿ›œšŸŸ¡¥ª«¬¬¨§¥§¦§§¨§¦§««ª¨§¤§©¬¬«¬¬¯°®®ª©§¨¨®±³²¯´²±®®¯²²²²´´³°°±·´³³³³¸²¶¶·¶¶·¶³¸¶·¶¶µ¶¶µ²µ³µµ´µ·º½¹¹¸¹·¸º½¹·´±¯´µº»½½¾¼½½½¶¶²´¹»¼¾¼¾¿ÄÄÅÂÁÂÂÁÈÅÉÌÍÍÑÏÎËÒÐÐÐÒÑØÚÛÜÜÛÛÚÚÙÛÜÜÛÛÛÜÝÝÝÝÝÝÛÙÖ˾´¦–‚zsnhbC=9=AGNSXZ]^`bdghjlmopmkggggggeeehhjjmoojkiiijhhgiklhkjjjmlmlmokhgbdfgeechhhdffffdaa`aaccdef_aaa`cbcefgjkifffjifddbeeedhjnmmllijjjjmponnnmlqnoppnnnmkmlonprpppnpqqnnqnkmoporuqjheikqqsssqttutspsttpqutssrqomloqplqrqqqppqrrqoponmkigeXNTXbllmmmmnnmmkjikfhejlppke`^bdghhiijllomkgijnlpmoijimmlmeWPMW^eiiiiimmkcccffg^beefggfcltohhhkjjkmhhghhkggfgijkiheaba“‘”‚}j[adn†…„„„~~||}‚ƒ‡ˆˆ‰ŽŽ““”’’’”“““–•œ˜™™˜˜˜™›œžž ˜—˜š˜›˜™š£¡ ¤«££¡¨£££¤ŸŸ ¡¥¦ª©®®®§¨§§©©ª¨§¦°©ª¨§¨©ª¯³²²®¯®«ªª©«¶¨®®¯±°²°¶°¯¯···±´³·³³²···´·³´µ¸µ»º¾¹½¶µµº¶·µµµ¹µ´²µµµµºµ¸¹½º¼¸¹·»º¿µ²±¯µ»»Ä»½½¾¼Å¼¿¶··¸¹½½¿¾ÂÂÈÆËÂÆÂÂÂÉÈËËÖÏÓÎÌËÑÏÏÒÖÕÚÙÛÛÛÙØÙÛÛÛÛÛÛÛÜÜÜÜÜÜÜÛÚÙÑËõ«—ŠyreWNORVY\_cigecddhimlnnqljikjnihefgkikklntilhpkmiijmkkjjjnmqlmllkjfgggfgffchhhhifeccabcgggdfed`````dclmmgmjfedfjhffffjghhnnnnommjllmmnoroonnlrpqqrnpmllpoopssuprqysropnmlnoqsvqojijppqqwwvssuvtrsutsoqrssvqrlkntrpqstrqrpsqyqpqqnmkiggZQQV_mmsoqnqonmnlllkghjtmplg_^afgffhjnlqmolkhhjmmpmoiijlnqkcQIQ_ekinmqknkkeeehgfccddeiggfpominikjiilgigjhpfkghhlihebaeb‘‘“ŒŠxmfZeovƒ}z}}|{{{ƒ€‚„†ŠŒ‹‹Ž‘’’”‘’‘‘’“•–›—˜™™˜šœ››œ›˜—–––š˜™—œ¢ ¥¨¤£¢¢¡¡ŸŸœžŸ¡¢¥§ªª©¨§§£¨¤©¬©©¨¨¦§£©¤¦©««®°±°¬¬«ª©©¨©ª©©®«¬«°®°¯¬¯¯¶³³±±¯¯°²²²³´´¶³¶¶¶´¹¹¹¸¸µµ´´³´´´²²²³²µµ¶¶¶µµµ¹¸º¸·¶¹·´®¯µ¼»»ºº¼»¼»»º¹¶·¸ºº½½½¾ÁÃÃÂÁ¼ÅÁÃÃËÉÉÊËËËËËËÌËÐÕÖ֨רØ×ÕØÙÛÛÛÚÚÛÛÜÜÜÜÜÜÛØÖÑÊÊû´«œ’|qmhggiklmmljfffijllmmnkjhkkkkgegiiijhlmmiihhhijkmmkjhjjnnnlljjgfcgefefcecheeccccccacdghgeddca```adfmiigfeedfgggfghhihijlklonnmkkjiglmnmljmlllprrnnklmpnptsponrppoommkllnorspljgkmqsrsvwvssusqsvusrprqrssqnjosqnpptwrpoorrqppopkjiidYPTV^flmmkklnmljlllkihknnnlh^YaggggfjlmmllmkjijjlmokkijllmlgWLQ\dijjmmlkjhgeefc`edeeeeeeefplhfghighggghfihhegegfeefdcbba˜˜–‡…na`]i~‚„ƒ}~|||}}}{{~ƒƒ††‰ˆŒŽ““”’—–•Ž’˜”š’—•›˜š˜š˜žŸžœ œœ—˜—™™™šŸ¥¤£¡©§¦¥££¢¢¢Ÿ žž¡¢¦¦¦¨«¨§¥¦¢©©«¬¬¬©ª¦¨§§¥¦¨¬²°¯¯ªªªª©©¨ªªª®¬®¬¯¯²±³³´²·³´±²¯³³³³²´¸·¶¶¾¹»¸¾¸¸¸¹´¶¶¶·¹´´²³²¶µµ·½·»º¹µ»º½»Â¹½³°°±µÂ½¿»Á½½¿¿¾½¸¶¸ºº¼¾ÄÁÇÁÀÂÅÀ¿¿ÅÄÅÄÌÈÈËÍÍÎËÐÐÐÏÔÕÙÙÚ×ÕÕÕÕÚÚÚÚÚÚÛÛÛÛÛÜÜÜÛÖÔÐÌËËÃÈÀÁ±«§¦’‚€€}{„€}ytpnoqpoonnnnnmlkjjgghijjniqlnkjimhjlnmommkonnmmlljkgfefhkffeieidedgbecccdehiihhdbaa`abginiifffedlffffipkkkkknkllklmmnjijrqrlljmlmnvsrnmmpnpnrrspnnrqtnmmllmlnmtplihjmqvssuyxxrsrqquuvtwssruswonpxspnqrxuspnpqrrqropkiij[RRW^jkqnnkllqmlklkjihiqmmkj`Y^ggjijkqoonlmmmmnppooolkjjmonl[NJUaonnmnmmlkgghleabdfhiiehgegpkfefgkgihhghhigeejgjfgfhcbbacŽ‹|qhYcfp€}yzzywxxyz~€ƒ…†‡‰‰Ž‘’“‘’‘‹””’’‘—–˜˜˜——˜™™š™˜˜——––˜™›œ £¤££¢¡¡¢¢ œžžŸžžŸ£¤¥¦¦¦¦¢¤£££¨ª«§©§§¦©£¨¨¨¦§¨¬®¬«®§©©©¨©¨ªªª««¬¬«®¯²¯²³³²³±´¯±°³±²°³´µ²µ¶¸¸¹·¸´¶µµ´µ²²°°°³²²²´´¶¹º´¹¸¹µ»ººº¹´±ª°³¶¸Á½¾º¼½º··µ´³¶¸»»¾ÀÂÁÁÁÁÁÁ¾¾¿ÃÂÄÅÆÅÈÃÅÆÆÇÇÉÎÏÒÒÒÒÑÌÎÏÖÔ×ÖÖÔÚÙÛÛÛÛÚÛÜÛÖÍÉÈÈÄÃÃÿ½´®¦¤–‰‡……‚…ˆ‡ƒ}zzyyywusqstronkkjihijlkkimmmmkihhmonmmmmkprokjhhhhgggijhfffeddbbaaacdddefedfedbaaa_abgljhgdebddddffijkkkjighikkjjmplhkmoqollimlooppolmnpnpoqqsnnnnopmmmnnnkmmmlifkppqrqsuyxsnpppqqprrssrrrrmjqwvrpmooqqqoqtrppqnlkiigZOTU]djklllklllkklmigghikkjga[`jeeihknnnpiiinqqpqqqnnlkhlnnp_LJK[fnonmmmmmlgijfacdhihhhec_gkkigefgkffgidgfgeffidfffdcabbaf‰pba\gw|‚ƒ}}~yzxxwvy|……ˆ‰Œ‹‹˜”•‘”’’Ž“““’‘–——˜——žš˜š˜——™™œœ›œ¡ž¦¥§¨¨¢¤Ÿ ŸžœŸ¢¢¢¡¦¤¬¤¥¥§¥¤¥ªª«¨°¬²§¦§§¨©©©©¨ª««°¯¯«®©ª¬°§©¨«ª²²²«®®®°´°µ´´²¶²³¯³°³³µµ·´¶µµµ¸¸¹·¹·¼µ·´¶²³±°°¸³¶³·µ¸·¹·¹¸ººººÀ¶´²°°¸´¼¸À¼½º¹¹»¶¶´²µ¹»Â¿ÄÄÅÂÄÁÆÁÀÁÇÂÄÃÈÆËÊÉÃÆÅÇÈÊÊÍÍÑÑÑÏÎÍÍÏ×ÔÚØØÔÚÚÛÛÚÚÚÚÚÔÐÈÅÇÌÄÅÄýº¶¸©¥™•“‘Œ‰‰ŽŽ„‡…„…†……†„}{}{xwvtrrqqrqpooqrsurpoomspooopssrqpjhimmmihikihhigedecb``bgghhgefffeddfdccddghhhkfefgdfdfiojjkmkihkkmkjjmlkjmmnnnmplmlvomnooqopqstuqrsuonnsnoosrrrrnoigiopwrrqttzspoopppqsrstttrrlinyxvpppopoppqvttponokkkk[PS\]ekrnooplmllkklnhghjlrkjb]_hheehipnnmpiiioovqyuuqqnmmuppgYFCQhlnoqnmnmmmmnjfdihojmmmfddjjjiifmjkkkkjglkkhihjdgfidcdebbdkYabn~~~}|{zyyxuusx|€ƒ†ˆ‰‰‹ŒŒ‹‘’‘‘‘‘‘“”’“““’•–˜š™˜—–——™•––™šœœœœŸž¡¡ ŸŸœ™™™ž›žžŸŸ¢¢£¤¤¤¤¤¤¤¥¦©¨ª¦§§§¦¥¥§¨ª«ª§§§ª¬¬«ª§ªªª¨«¬ª§§¦ª«¯®¯¬¬¬¯±³¯¯¯°°µ³´¯±°³³µµ¶³²¯´´´³¹··¶·´µ³²¯®ª¯°°°°±¶³¸··µµ¶¹¹·¶³®±±³´¶´¶¸»»»º·¶µ²´´µ·º¼Á¾¾½À¿¿½ÁÁÂÃÄÂÂÁÃÃÅÇÅÃÄÅÈÉÊÊËÊËËÊÈÊÍÍÎÏÐÓÒÕÔ×רÙÚÖÔÓÐÉÇ¿ÂÃÄÄľ½¼ºµ¶«¦œ™”’‘Ї„†ˆ‰‹‰ŠŠ‡„ƒ‚‚}{ywvwwwxrpruvwvtsspnrqqoppppqophjkmlmijjjhijihfceec_bdgiifhegfffefecbbeghhhiihilfdddhjkkjihhhhkkjijjjjjjlmnmljklllonllpoqmrusrqprrromlmnooopromigehkoqrppoussoopppqqpmqssstqmhntyuspqqpmopqrrrsoopmikn\OU[_cjoqnoonllllkkkighiklqkc\afgeedgiomlkjiifjknpuuuqonpqrpqbPBM_iponjflkmjmjihgeghiijlifffghiigfihhgjiighhhhihfdeeeea]cccdXcru…€}}~|zyyz{zyuuy‚‰‡‹ŠŒŒ‘‘“””‘’˜˜˜“•”˜“•””–˜›Ÿšœ—•––˜™–™™Ÿž¢Ÿ¡ ¦ ¤¡žžœ›˜˜šžžŸž¡¡¨¦¬¤££©©ª¤ªª©¨«¬¬¤¥¤¥§«ª¯«§¯¬¬±««¨ªª¬¬¬©¨¨ª«±²´³µ®²®²²´®±±²±µ³¶¶¶´¹¹¹¹º²±³»´µ¶»¶¶¸¹¸·²²°°¯±±°²¶´ºµ¶µ»¹¿¶´³°±·¶½´·µµ¹½¼Á··µ´´··¼º¾¼À¾¿½ÂÀÀ½ÅÄÅÃÆÃÄÄÅÁÀÁÂÄÊÈËÉÎÎÎÈÉÉÊÇÊÌÎÎÎÏÕÕÕÔÙ××ÖÙÓÏÍÊÉÊÂÂÃÈÃýÀºº´´«§Ÿ•–‘Œ‰†‡ŠŠ‹Œ‘Ž“…‹„ˆƒ„‚€‚„ƒƒwtv|€~}|}tsrrtuuyusssoonnnqnnnommnpqpigfihhfeglknhijkjmmmjigffkknjjlponigfghlknmnmmmmkjjhikkllllppqqqjlnrqpmlnqqrrsuyrqsxwvollonqopprmjfceilsqursqwrqnoprrvqpopruttnlmwuytsruptopqrsxrtppnmno^QR\`gkuprrsnmmpoplmjgfhlqosf_`fhmhfellnlkkjhhfkkkntuvqpqvuvpqXIHWdpopkhflknmmiihhhhiliiigggjmopjhhjhhiijkikiiimhfghhmd``bcidj||}~~|zyyyxxwwuxv{€ƒ…†‡‰‰ŒŒ‘ŽŽ’’‘”””‘•’“’’’•—šž›–•’“””““”š™›žž¡¡ ¡›——•™˜šœœœœ ¡§§¥¢¢¡¢£¦¤¤¥¦£¥¢ ž¤¢§©«ªªªª§¬«¬¬«¨¬©ªª©§§§¨©¯²²±°°¯¯³¬ª««¯¯´±±±µµ¸¶¶³³°³µ¶´µ¸¶²²±°¯¯¯±²¯®±¯±±²¶´µ±¶µ·¸¶±±°±²¶´´³³³·»º¹·±³³´µ¹¹º¹¾¼¾¾¾ºº»¾½½½ÀÁÀ¿À¼½½½¼ÄÅËÈÇÆÆÃÂÂÄÄÇÇÇÈÏËÎÏÐÑÑÐÑÑÒÑÐÍÈÃÿÃÁÂÂļºµ´¯®©¨œ™‘Ž‹‹ˆ‰‰‹†‹‰Œ‹††‚ƒƒ‚‚ƒ„ƒƒ|xz€€||uvwy|{zyyxxtqqrpoooonnmoqponjhfihhfhjjkjijjiiihjjjjhgijklmnonmhhikkkjnnlhjjllkijkllmmmlppppmjmooonlnorsrrsssptwwxsnliomqopnlhfefhknrpqqqqqnrnnnoooqpnqruokioruuttsruqsqrtsssopppmmn^QSY_ekoppponllkmmpkjigfkopng^bhhihgffmonlkiihhefejlpspoqrttup]KJM_lonliigllllleddefggggijihkmmlljjjjhklljkihgiikeggghgccfdced…|{||zyyzxzvtwy}†‡‡‰ŒŒŒ’ŒŽ’“—“•“”‘–’•—™šš˜›™”•’”””•–”œ™š¡ž¢žœ¡›—––˜žžŸ¢¡¡¡§£¦¤£¢¨¡¢¤¬¤¥§ª¥§¡Ÿ ¦§¯ª¬®«ª«±¬¬¬«¬¬«ª§¦ª°®³³´²²±±¯³²´®®²³´²´µ·¸¹¸¹¶¶³³³¹¶¸µ·µ´®®®®³³³²´°±°²³µ²ºµ¶³¸¶´´´°±³·¶µµµ¶º»»¹¸¶µµµ¶¹¸¿¹º»¿½ÂÂÁ¼¿½Á¿ÂÂÂÀ¿¿Ä¼¾¾¾¿ÅÅËÇÅÅÅÃÅÅÄÅÈÇÇÈÏÍÏÎÎÏÓÑÖÕÕÏÏÈÃÀ¿¿ÆÁÃÂÇÁÁ¼¹¶¶¯®©©š‘”ŽŽŽ’Œ‹‹Œ‰‘‹Š†ˆ„…„„…‰……}y|‚„ŠŠŠ†…~|~„ƒ€…ƒ€€wuuysstursssszusrrljkokikqmklklojmjijllpmjikkkmrqrpmllnponnmmlkjknmllnnprrqqqprxommoornmorsvtutwssu||{urmlmnorpumjedhnooosssqpprnrnnproppsssswlgkvvzvwtssvrsrtsvuupqpppraUPT\dlrprnlonmnkmmqlkijjnpujb_diljhggioookjjjjlfggjkomlovvwwucRKMZpqtmllkhlmoljffefghgfhoklmqqqmnoolkhppokpighkjkfhhigfdhhijjf~}|zzzyxxwyxzwy|€„…‡‰‹ŒŒŽŽŽŽŽ‘‘’’“‘”“–™™™™˜—”“‘’’’••••›˜š››››››œš–˜˜™œžœŸ ¡¡¡¥£££¡££¨ ¢£¤ ¥©¦£¦¡£¥§©¯¨¬«¬ª¬®®¬®ª«ª«¨§¦§§«®°°±±±¯²±¯²¬¯¯²³±±²µ·¸¹¸¶¶´¶°²³µ´´±°«©¬¬³±³³³¯°±²²´²²²²®®¯±²´°³µµµµ¶¶·¶²¯«±±¶µ¶¸¹¶¸¸¼½»ºº¹¹¹¾¼¾½½»½º¿½¾º½¾¿ÀÅÆÆÆÃ¿ÁÂÃÁÅÆÅÄÈÄÑÌÏÌÌÊÍÎÑÎÍÈÈÿ»½¾ÂÂÁ¿À¼¾¼¹¶³¬ª¦£š‘“‹ŒŒŽŽ‘ŒŒ‹‹ŠŒˆ†……ƒ„‚††‚{x„†ˆ‰ˆ‰‡„€ƒ††……„……„‚|wwxxuvxwtuuuuzwvtsnnoooopppooonojjjkklloomjklmnqsqoonppppomnnnmmlnkmmopppqppopooopqqrqlorstsstuusvxvsrqoknqqqrpokhchmmmoptssqpoompoqsrmnmsqpnmhlpttvuwrssursttqqnnmnnmj`SRPW_lurpnkloopmkmmnkkgklnplebafmliffikkkkiigjkkgggijiflpsww|iWPL[inpmjiihilnqkhfffgehhhiiinrpoqkhfnnjhonkjjhhkhfffibcdeceeighe{||yzzzyxx{z{|€ƒŠˆŒŽŽ‘ŽŒ‹Œ‘““““”“–•š˜˜š™ž—•”“”ššš™™—›š›šž™™ššœœ˜——œ› œž §¡¡¡¢¡¡£§¢§¥©¤¥¦¦¦§¤££¥¦¬«µ©¯©¬¬¬ª±¯¯®¯°¯¬«§§§¨ª²°´³±±°±²°¯²±²²²²·¶¶¶·¶¹¹¹µµ´¸´¶¶¹µ¸«¨§§«°®®´²¶²²¯´µµ³´µµ±±±²¯³³´µ¼¶¸µ¹¸½¶¶¬¨¬³±·¶»»»»º»À»¹º¼»¿»½¼Á½¾¼Á¿¿¾¾¿ÄÂÂÀÅÅÆÃÂÁÃÂÇÃÇÃÂÄÈÄÒÐÐÌËÊÏÎØÌËÇÈÀ½ºÁ¾ÄÀÀÀÀÁÁ»»´²®¦¤œ˜“’‹ŒŽ’ŽŽŽ“ŽŒ…ƒ„†…ˆ‡Ž‚}{z‹‰ŠŠŠŠ†……‡‰ŠŠ‰ˆŠ||‚||~}{{{zzxzz~vuuvwxwvwxtutzopnmloooosqpqqpooqonnmnrrusspqqtqspoopptuuptqqqsquwxusrppqrutyxxwzz|wurrooprqpqrpoigiomqorq{sssvrrnuttstorrsqslimqswvyvvvvttuvuxqqopnmmocWNQT[hwvvpokmoonnnnlmkkkpormiefeijmgdfnmmkkiljkljgghmhghmnruyl`ROWnrtolkokpppprjihihljomnjkisrqqrhcdmoiiokihhfkihedek``ehdififleyyywz{zvxx{|~€„…†‡Š‹ŒŒŒŒ‹‹‹’“”•–—™™˜•˜™˜“••—š˜˜–•——™™ššš————–—–——œ›Ÿœ¡£¥¡¡¡¡ ¢£§ ¦¥¨¥¤¤¤££¡£¥¨©©©©©ª©¬¯¬«¯ª©§¦¥«¦§¦ª®¯°®«¬©¯¬®²°²²µ´µ¶¸³µ´´³³²´´¶·µ³©Ÿ©¥«°°¯®®¯¯®®¯®®´²±¯°°±¯°°´¶···²´³¸¶µ²³§«®²±µ´¼º¹·º¼»·¹¸»»»»½¹ºººº½¾¿»¾ÀÂÂýÁ¿¾¾¿ÀÃÃÃÂÃÀÃÄÄÅÉÇÈÊÊÉÉÈÆÂÂÂÀ»½º¾½½½½¾¿º¹¸¶±±ª§¢¡™–““‹Œ’’‘‘“‹‹ƒ„„………†€y{{‚‰ˆ‰‹ŠŠ‡ˆ………‰‰ˆˆ‰ˆˆ„„‚ƒ‚}€†„‚‚‚~{yw{{~wwvwzwuuvxtttsoooomnopqrrqpooppnlnknnrsvvrqrssqqqqsrrtssqqprrrruvvwrqqprrstzxywyzztuqqmptsqrrtomhlqqnnmrqtssssqpnrrsprppqsnmimvuuvuwuuvsptvvtspoooonpdXRMU[gssttpnkopnmmklklkmmoopiheffhildfgmonkjhjjklkgikgeghmloncWPJXgntrklmkjpooolhijjhijopnkjjssoliccdomkjnhhhgcjhhffe\Vbedbcdeffd{{zzzzyz}}~€††‹‹ŒŒŽŒŒŽ‹ŒŽ‘‘Ž•••—››š™—œ˜˜˜˜–›š™—•—›š›œœœ›—™—˜•——š™ž Ÿ¬¤¥¡ ¢ ¥£¨£¦¦©©ª¤£¤¤¤¦©®ªª©®ª¬¬¬¬¬¬®±ª§§¨©ª©©©®®¯®±¯®««¯¯°¯¯¯³±·µ¹´¶µ¹²¶³³²²²¶´·°®ª£¤ªª³³³¯±¬¯µ°¨¨®´¯®±°±°³°º¶¼·¶³¶·¹²±±±³µµµ²¹·¼º¾·»º»¸¹¹À¼¼»¾¸»ºÀº¼½¿¿ÃÁÁÂÃÀÿ¾¾À¿ÅÂÃÃÃÃÇÅÈÆÊÇÈÉËËÌÆÃÁÁÀÀ¼¾½Ä¾¿½½½¿º¸¶·°¯¬§¢žš–•”––œ’””˜’•”ŠŒ‡††‡‡‰€{}‚„‹Œ‹‹‡…„‡‹‹‹‹†…††…‹ƒ‚‚‡‰‹ŠŠ„‚~}|„|}}|{{zwuvxw{utqrqtpprzvtuyturypnlmoutuuwsqtyvtstsutyyxssqrruvwwwvvtrqrsvuxw|yzx}yyuurrsuuwv|sunlnttwrtprrttvvvqsrxtspwtystmllsvwwzuxtrrqqsvxtttuptrrh\SOR[fvvuttomnuqnntklknoppqnnijjlikkkjjjtopkiiklkjlilhfemjmlsdREALclvtolplkjqnumhfkimiijpnnonpspmidabgppnnnmnljikhiij^SUefecbcdeedxwxyzzz|~ƒ…‡Š‹ŽŽŽŒ‹‰‰Š‘Ž‘”•˜™™™™˜———•–––—˜–˜˜˜˜š™š——–—•™–•’–—™™ŸŸ Ÿžž Ÿ¢ ¡££¥¦¥¤¤¡££¦¦¨©©¦¨¨®ª«ª¬¬¬ª¬®«©§ª¬«©©©®¨¯¬¬«¬«ªª¬¬®¬±²¶µ¶´µ³³²°®®±²³³®¥ªª«¬¬¯®¯¬°©«®©¥£ž©®´¬«¬²±±¯±±±±±²²±°«©°¯´²²²¶µ»¹¾·····¹¹¹¹¹¸¾·¹¶¾º»»¿ÁÁÁÁ¿¾¾¾¾¾¼Á¾ÁÁÁ¿ÄÃÃÄÇÆÆÅÅÅÆÆÅÂÁÀ½»»º¿½¾¾½»¾½»·¸´³®ª¥¡œš—•”””˜˜›’““’ŽŽŒŠŠ‡ˆŠˆ†‚}‚‡‰Œ‹‹ŠŠ„‡†ˆŠ‹Š†ƒ…†‡‡Š„„…ˆŠˆˆ†ƒ~}{|z~||||xwuwxytsrrstqsuyyxwxttqqqokoqtttsrpsvwwvttuttwxussqstuvvvwtuqqqstvwxxxyzyyvvvuqtttuvvvqnjnsvwtrrqrruvvvuqrsvvsputsonjnpvywtttsssssrtuspqppoomh^TMTYeqqssqoknqsronnjijoqrrqnlfmnljjhkjklnnliijlihhjihfffjjlkh\C2DUgorslimmkjqmlhgfiijhijollimupmmge^dkljlnmlmlliheilgXSXfefgddefecvvwz{{‚ƒ„†‰Š“ŽŽŽŒŒ‹‘Ž–‘“ŽŽ’•™˜Ÿœ ˜›—–—˜——–˜™˜™ššœš™˜›–˜˜˜•••š™™œ ¢¡ Ÿ¢ž ¢Ÿ¡¡¦¤ª¨¨£¢¢¤¨¯ª©§¥¨¨®©«¯¯¯¯¯¬®®®ª°±²±¯¬±«¬«¬©ª«¬®²®³°¹³º¸·´¶±®¬ª¬®°µµ¶°¬±±²°²²³¯²¬°©«£¡£¢¤«²²²²³²µ®¬®²°¶µ´³±®«©¯µ´´³»²¶·¹¸¾·¸·¹¹¼º½¸º¸¾¹½¼½º¿»ÁÁÂÁÁ¾¾¾ÀÀÁÁÁÁÁÁÈÂÆÄÃÄÌÅÆÆÇÇÆÆÆÀ¿¼ºº½»À¾½½¼»¾¼»·¸³³®¯¯¯¦£Ÿ¡œ›˜˜š¢™›–—“‘ŽŠŒ‰‰…„†‹Œ‰‘Ž“‰Š‰Š‹‘Ž…ƒ„‹ŠŠˆ‹Š‹‹ŒŠ‰ŠŽ‡†~~‰€‚~~~‚y{uvwxxywwv{yzz|€{{vyyzuttuvwxwsqrvw|yyz||}x|xuuwv|ywwzxxuuuvwxyz|‚|}}}zzxwvuuzz{wzvwokjusyvsrsvzv|{|xxtvuyuttytunlntv~||vwv{stv|xwurrrpomnjdVNR[ftuytuomntqrrpnnnmmpqwuunkioooknjklrlpmkhjlmhhimheeiinlmkjL55I]knsnkkrommqkigffhjkghjnlllnnlkkgfffknjkkklolsjgfijcUS\ghhgfeihjfvvy|}~~‚„‡ˆŠŒŽŽŽŽŽŽ‹‹‘‘Ž‘’”—˜—˜˜—–•”•”—™—–••™™šœœš™˜—••”•–—”––™›œžŸž›ŸŸœžžŸŸŸ ¢ Ÿ¢£¨ª¬ª«¦¥¥¨©ª¨ª¨¨©¬«ªª¬©©©ªª°¬ª¬ªª¨«¬®±®°°³³µ±°°°¬«¨«¬¯±±¯°®¯¯°°¯®¬®®±«ª¤ š¢¦ª®®±®²²±°¯««ª°°ª§¦¥£©³±´³¹±µ¸¹µ¸··¶¹º¹··¸¹¸¸·¸¹»»»»¼º»¼¼½¾¾¿½À¿À½ÃÁÁÀÀ¾ÁÄÃÂÅÅÿ¾¾¾¾¿·¸·º¸¸¸º¸¸¸¹·¼µµ±²®ª©¥¤Ÿžœ™šš™™˜––‘’ŒŒ‹‰‹Œ‡‡…Š‹‹‰‹‹‘ˆˆ‰‘І„…†„ЇŒŒ‰‡†‡‡†‚€|}|€ƒƒ€€€}yzwwwvvxwwx{{|}}{|zxvxvvvuuvwy|xtttvw|z{||}{wwwwwyy{{yxyyxvvvwyyz|}}}}{xwxxvwxz{|utrpkkkvttutsvwxv{xxxxtvuvtuwuqqlotwyz{{uuuvuwxzzxwtrsoojiiXNUZesrtusokortqrqqmnknpqrttrkkkosnkmjlmqlmklilmlfiihcbakmmlmkR97;Tjlnmjmlmnnnmhfeffijhehjkkjjkklmlhffhjljlfjjlkjhgfhi`T\geaghfdhhiixy|„‚‚‚†ˆŒ‹ŒŽŽŽŽŒŽ•‘‘’”“˜™˜—™™—––”““™”›˜˜–—•š™œ›™ž–””””••——›š™›Ÿž¢œš›Ÿ¡œŸžŸž ¥ž ¡¡£©©°¯¯ªªªª§¨©¬¬¬¬¨±«ª«³¬¬©¯ª¬ª¯µ¬«¬«ª¬±¯®¯±±µ´³³¸±³®¬«««²¯³±³²³®²³´°¯¬«¯®³§¥¢Ÿ£®®¸±¯°±²·µº¯²®°°¥ ¤§§¨¨¯±²³³´µ¹´µ¸¹·»»½¶¿¸¶·¹º»º¹¸¸º¾½Á»¾¹º»½½ÂÀļÀ¿ÀÀÄÃÂÀÀ¾ÀÁÂÂÊý»¼¿¼½·»·À¿¿»À¸º¸º·¼¼¼µ¸®±««¦§ £¢¢¡¢›™™™–•’Œ‹Š‰‰’‹ŒŒ‹Ž˜‘Ž“‹Œ‰ˆ•“”’І††…‰‡‹ŠŠ…†††ƒ€€~‚ƒ„‡‰„‚~}}~}{{|||~ƒ‚ƒ‚{wxyxyyyy€‚|}xwxy{}{~€|zxwxz}}}}{zz||wyxzy}|‚ƒ~‚|zyx{z{}{}trrrmnqxxxvvv}||y|yyyztwvxwxvurqqvxƒ{|z|wz{}|||zwvrspnjk\QS\eqtzuupnnsruutsrmnnosxrtrpoqoonmmnlopqpqkllpmlmojfcaemmlmp\E27Jgnsnlknmloqnmfefjjnjhgikqkkjkjlllgghmknlkjiimjhgjhmh`_jgeenhddghkk|~‚‚„…‡‰‹‹‹Œ‘Ž‘‘’“’“•••””’‘‘’““’““–•˜––”š˜œššš™“‘‘“”–“–—š›››œ›œ™›œœœœ›žžžž Ÿ¤§«¬°°±««¨©¦¦§¨¦¨©©©ª§«®¬©«¦©ªªª¬®««¬¬¬®¯¯°°°°±¯±°±±¯¬¬«¬¬¬¯¯²²²¬¬°°®«¬«®®ª¡££§¯®¯²²²²´´´´³®®ž–Ž¡¡¨¨ª«±²´´´´µ³¶¸·µ»º¸µ´³¶¶¶´¶µº¹º»¼¼º·º¹¹¹»º½½½¼À¿ÀÀľ¾¾¾¼¿¿ÂÂÁ½¼¹¹¹»º¼·····¸¸¸¸¸¸¸·¼··´´¯®¬«¦£ ¡¡¡ ›™•—••Œ‹Š‰‰‰‹‡Š‹‘˜”Œ‹ˆ‹ŠŒŽ•–“ŽŠ‰‡†…ˆ‡‹‹Š‹‰‰ˆ‡…„‚‚ƒ‡ˆ‡†„ƒ€€€~|‚„………ƒ{{{{{||||~~}|z{{|}{z}zzz{||}}}}}}{}}{xxwz{~~€~~~zzz{yzz{y|zwrqmqqrsvvwwvv{||yyxxuuuwvvvxttrsuy|~€|{zxwz{|xywxwvopqpk[PV]dmsuurtmpqrssspmmmnorturpnqlqoolmmnlmjjklkmnokllljdZejmnno`M;0Gbjqolkjiimoolf_ehiiihiikkljihiikjldfhlkmmlkkjhghhhglgghd`ffggddhike€„††‹‹ŠŠŒŒŒŒŽŽ–‘’’‘–”˜˜—–™”˜”’‘‘”˜”””˜—˜•œ››™››œš™“’‘—–—•œšžž œœžœž›š›£œž¢¤¤¬©²¯´°²««ªª«¬¦©§ª©¨©«¬¬««¨¨©¯¬¬¯¯°®°²µ±´°·³¶°±°µ²¶¯®®¯±®®¬¯¯¸³²±±°¯®°±¬§§¨ª°±³³²²µ´·´´´µ¯’ˆƒ‚“¤©´°°°°±³¶¶¶µ······¾¸¶´³´½µ·¶º¸»ºÁ»º¹·¸¿»¼¼¿¾¾¾¾¼À¿¿ÀÄ¿Á½¾¾¿ÀÅ¿¿½»¸¹¸»»½º¼¼»¸º··¹½¹»¹¼·ºµµ®®¬¤££¤¢¨£¦šœœ“”ŽŒŠŠŒŽŽ’™–“”Ž“Šˆ‡‹Ž‘Ž•““‹ˆ‰…ˆ‰‹‹‹Ž‰‰ˆ…„„Š„……‡„‹‹Šˆˆ‡…‚„ƒˆ†…‚‚‚‡Ž‡Œ…‡€€…ƒƒ‡Œ‡„ƒƒ~‚Š€~‚||}~~€€}}}€‚ƒ„{}|zzzz~~„~~}{{z}|}||}xtrqqru{zyyxyyy{||y~zz{|{|||wytttwy‚‚ƒ‡zxy}||yyy}vuoprv`QS]blr{vwstryv}wxrpmnotsxvxpnnrrsomlnnqpqmmlnowonmpljfcdpooothX@2>[htpnnoihiqqqlbafgkhjhokpllihhijojkddflkpornmigfihjjllkjcbgghfbegilh‚‚…‡ˆ‰ŠŠ‹ŒŒŒŽ‘ŽŽŽŽ’’‘‘’’““•––––”“‘’‘‘“•—•”“—”••š™š˜š™˜–””“‘”“••œšžžžžœœšš››œŸŸŸž£«§¦«©ª«¯±®«©§§¦¦£©¦ª¨©©©©ª¨¬«©©¨¨©¦¬©¯¬®¯²·³±±°°°°°°¯°°±®¬ª««®¦®¯¯°°¯®¯°®«¯®«±²±°°²²²²±±±¯ª§¡‹yƒ’¢©®¯¯¯°±±³µµ¶¶µ´´´µ¸³·¶¶³´µµµ´µ¹¸»º»»»¶¸¸¹º¼½½½½º¾½À¿¿½À½½º¾¾¿Á¾¹¹µ·¶¶¶¼¼»¹»ººµµµ¸º¹¶·¸·´¶µµª¦¦¡¡œŸŸŸŸž™™—–“’ŒŽ”˜˜”‘‘Ž‹‡‰ˆŠŒŽŽŽŽŒˆˆƒ…†‰Š‹‹‹Š‹‰Š††………„ƒ„‡„††ŠŠ‰ˆ„€‚ƒ‡ˆ„ƒ‚‚†‰‰†…‚€ƒ†…Š–˜™Œ„€}†…€|~‚€ƒ‚}~‚‰…‚ƒ|{{{{|}~~ƒ€~|}||{||~€|y}vsmqrvy{{zy{|{{}~|z}|{zzy{zyxxtuuy{~€‚ƒ}wyz}}|zyz|uuopscTU[agpvvvxrsttuvuwqqmpquvwsojmopnnlmnnnpppmmmoqqnnmmkfdimnoopj`D2@Ucmplheiijkqvrkccfeeeghgghhgffgjkifi`dehilmoqmigghijkllkiechihfgkiiii‰‹ŽŽŽŽŽ‘•‘•“”’’•“”“––˜––’“—•••›–™•––—–š™ššžžž˜—•“““”˜’š˜š¡žžžŸœœžœžœ¨ Ÿ¡¦§©¨¨§«««ª¯¬«ª¨¨©©¨¨§ª©®«°¨ª©¬«¨¨©¨ª©°¯°µ´²±±±±°³²²¯¯°³¬«ªª®°®±°µ°±°°¯®®®¯²²²±¸³·±´°²²³²²±±³³–‹‚u}„‘´³²±µ´µ±¸µµµº¶¹µ½·¸µ·¶·µ¸¶¶·ººº¸¾½½¼½¹º¹ºº¾¿Á¾¾¼¿½ÅÀÁÀ޽¾Ä¾¾½½¸¸¶¶¶µ·¾»ºº½¸¸¹ºº¹º»·Á¹º¶¸´µª¨¨©§¦¡¢¡¤›››——˜™••“““š™š—š“‘‘ŒŒŒŒŽŽŽŽ‘ŒŽŽˆŠ††ˆŽŠŒ‹ŠŠ…‡……ƒ‚‚ƒ‡‡ˆ†‹‹‹…„„…†‹…„ƒŠˆˆˆ„ƒ‚ˆ‡†‹œ—§—“„€†…†€‚€ƒ‚‚ˆ…†‚‚‚‚†‰ˆƒ‚€€€…†…ƒƒˆ€}}~€€~‚}}urstx~~{{{€€€€€~}}}}‚|}{ywvvy{€}ƒ…~z{}}}~€|{|utstfZWY_iozvxwywvv{vvuwqrqtu~}}rkkqqtnnnrnnntqqpqrqqroomlfbhtttuvohJ49P`orwkffijmmqrsjdcffffhgffigcdghmjifhfeekimmnpsggilhomrlmihinmnnmlmlppŠ‹‹ŒŒŒ‹‹Ž‘‘’’‘“”“““”–˜’‘Ž““”••“•”–•–––—šš—•”“““’”•—“™˜š›œœœœš›™œœžœž¡¤¨§§§¦¥¦¥ª¦©©ªª§¤¨£¨¥§§§§©¨¨¨¨¨««¨§§§ªª®°®¯°±±³¯°¯¯³³²®°±²¬¬«ª¬¯®°¯°¬¬¬®ª¯¯±²³´²°°³¯³¯¯««©¦}yr‚˜¥«¯²±±±´´µ±±±±²µµ¶´´³²³·¶¶³¶µ¸¸¹¹¹·¸¸¹¹½¹¹¸¸¸¹·º¼¼»½½À½Á¿Á¾¾¿Á½»µµ´µ´¶²µ·½»¹·¶µ¸·¸··¶¸·¶µµ´²¯¬§ª©¨¦¦£¢¢¥››››•–—˜—–•“’”š––—“ޑދЋŒˆˆ‰Š‹‹ŠŒ‹‹Š‰†…„‡……„ƒ€ƒƒ………ƒƒ„ƒ‚„„…†‰‚……‹‰ˆ„ƒ‚‚‚ƒƒ€…Š’••”„„„…ƒƒ€€ƒ…††††‚€€‚„‡‰…ƒ†‡‚~€…ˆ†‚„‡€€€€€}|{wrtu{€}y{||}€~}~~}||{zywvw}€|~~|{z||}{}|{yyy{uvwiYXX^fpvwvwwxywvxwvuvprsvx|}{nmmqrsnooonnnumnlnprqqoolf`gmrrssvmN4=I]nqtoihglmnornkffdeca`dhff]TVXfiihieedccgionnmjehjihkmmlljjkljlmnnnlno‘ŒŒ‹Œ’Ž”˜‘‘’‘’’‘”˜“—““”™‘‘Ž‘””™•––—•š—™™››š•“’”“’““–™–˜–š—››žœŸ›šš›œ œžœ¢ª©°©«¨®¥§¤ª§©©©§§§¨¥¨¨©©ª««§©©©¨«ª¨©¯±®³³²³´³³¯±¯´³³³¶±±²¬¯¯³³²²²®°«¯¬®°°´³µ²°±²²³±³¯±¬š•rww„œŸª«±¯µ²²²´´´´´³³³·····´´µ¸¹º³½¶»¹¹¸¼··¸ºº½¹»¼½»º¹¹»½»À¿Ä½Á¿Ã¿¿ÀÀ¼ºµµ´··¸µµ¸½¸¸µ´¶··¹···º¶´´¶±¯¬¬¯±°¯§¦¦¦¥§Ÿ¤››˜™˜ š™™˜—–•œ–’Ž‘‘˜”•“˜–ŒŠ‹ŒŽŽ““Š‹’Š‹†…„ˆ††…†‡‰…Š…‡‚„ƒƒ‚‰ˆ‡†ˆˆŠˆŠŠƒ‚‚‚ƒ†‚„€…‡Ž•ŒŒ„ˆˆ‰ˆˆ‡††‡‰ŽŠŽ…‚‚‚…„‡‡††‡……„„†‡‰‡†‡‡€…†ˆ‡‡†Š}}xvwy{ˆ‚}}~~ƒƒƒ~|}ƒ‚†}|zyxy|…€‚€}}†ƒ}{yz{{w{obUZ\dn{|{z{y{xwwzxzvvruw|}}zyooprtsrrtuolptmpnnpwqrrsideprwvxwx\>8GZqtwsommmsrrpsmhc_ae`\_bdcfUQNYdikgjefeddfipooliijjihlmnmokllllnmwvvprnŽŒŒŒŽŽŽ‘‘’’“––”“’’‘Ž’”’’“–––”––˜—–“’’‘’’“”–™™——•––›™œš›šš˜›šœŸžœž¢§¨©ª¦§¥¥¦¦¥©¦¥¢¤¤¥¢¢£¨©©¨©©ª§§§¨¨ª¦ª¬®°¯¬®¯®¬®®¯®®¯´±±°®®®®««ªª©¬®¯«²®¬¬¬¯¯°¯³³²±°°³´³¯®ª§¦Œ{qdu€Ž¡£¦¨ª¬³²²¯¶³³²´´³±³²³°¶µµµ´³³³¶¶»½ºµµ¶·¸ººº·¶·¹¶»»»»»º½¿¿½Â¿À¾¼º¹¹¸¶³±²¯¸µ·¸·´¸³µ¸¸·¶µ··º´µ³²««¯®«®¦§£¦£¢œ›š˜˜™™™›œ™––––”’Œ””•’Ž‹ŽŒ‹ŠŒ‰Š‹ŒŠŒŒ‹ŠŠ‡†…†††‡‡‡ˆ„…„„ƒ…†„‚…„††ˆ‡‡‡‡‡†ƒ€‚ƒ…~~~„‡ˆˆˆŠˆ‡…††‰‰ˆ‡††‰‹Š‰…€‚‚€…„…†‡‡‰†…ƒ„„‡ˆˆ„ƒ€€…„„„‡‡Š€|ytwxz|€€~~{}~ƒƒ…}{{}}zyzƒ€ƒ}|}€ƒ‚€}}}{yyuwuncZT\amwy{zz{wyxyzzwxuwrvxyz}vsnruvutrtvtqtwploknpwqrshcdhpstuxzqRC>WoqtvrpnpquwrpnhaX_^a``adfec^Y]`gjkggeeeeefiomokjjigiillmnolmoooolqsvool”’ŽŽŽ•Ž’‘’Ž‘““—˜˜••”•“’””••™—˜–›––˜‘”‘’“•””–œ›š˜–—™›˜œšœ›š™¡ ŸŸž ¤¥§§¨¨¬¦§§§¦«§©¥¤¥¤¤¥¤¤§®©ª¬¬¨©§ª®©ª¬³²´®®¬´¬¬®°°³²²±´±´®¯°°¯¯©«ªª©±°®¯«´®¬¬¬®¶±±°³²°²µ¶¶¶·¯° ž}jf_q‰”¨©®¯°³²²²¶¶·µ¸µ»´¸±´³µ°µ´¶´´³¸¸¹¸¼º»µ·¶½¸ºº»··¶»¹¾¼¿»»º¼¾¿½Â¿Ä½º¸¸·µ³±²µ²·´»¹·µ¹´···¶µµ··¹´¶²²®¬¯®®®®§©©¨¢£œœŸ¡š›› ›››œ––“˜””Œ–––––’Ž‹“ŒŠŠŒ‹Œ“ŽŒŒŽŽ‰Œ†…†‡‰‰‰…‡„…†‹…„„ˆ‰Œˆ‹ˆˆ‡‡…ƒ‚„„…€€ƒ…‡Š‡ˆ‡‡‡ˆŒŒŠ‰‰‰‹‰‡‚‚ƒ…‡†………†‰‡Œ††ƒˆ„ˆˆŽƒ„~†………Œ‹‹‚„{xxˆ‚ƒ‡ˆˆˆ‰ˆˆ~~‚ƒ„„}‚~~|‚‚€ˆƒ€€€ƒ|}~…„„}€zxvyof\VYbnz|~~{|z€zzz{wuxy{z{z}vttwv{uttzvuwvolnnopvstj^_gmvtzy€uiPEOnsvvvvxsuv|xsomf_^`_dfjjiijjjjkksmlhghiffggipoqlnkmgnjoooopmnoroppqrxpplŽ’”””“’’‘’Ž’•————™˜™——–”‘Ž“‘”–•”•–˜—š˜™–˜™™˜˜˜›˜ššž ¢¤¤¥¥¥¤¤£££¢¤£¥¥¥¤£¡££¥¦¦§§¦§¦§¦§¦ª©«ª¬±°®«¬¬ª®¯°°°°¯±±²§¨©§££¥¬©ª¨±¬®¯¬¬ª§®¯¯°°±±°µ´³²°©”…p[acy’™¡§«¬®®²±±²µµµµ¸µµ´´²³²±°µ³³´´³··¸¸·¶µµ¶µ¸¸·¶µµ·¶º¸º»ºººº¼·¹¹º»¼»¸´µ²²°°±²²´³µ²²²²²³³·¶¶´¶µ¶³²±±®®®¬ªª§¦¤¦¦¦Ÿž›žŸŸŸ¡›œ ››™—“–“•”’‹Ž‘–“”‘“’‘ŽŒŒ‹Š‡ŒŽŒ“ŽŽ‹‹ŠŠˆˆ‡‰ˆŒ„†ˆ‰Šˆ†„„†……‡‡„„„ˆ‰Š††‡‰ˆŠ‰…„ƒƒ„ƒƒ€ƒ†‡‡‡ˆ…ˆˆˆˆ‹ŒŒ‹‹ˆ†‡Š‰Œˆ……ƒ…†ˆ‡‡‡…‚†…ˆˆˆ‚ƒ}ƒ„„…………Š}x{|„ˆ‡„„„ˆ‰ˆ„……„‚ƒƒ€€ƒ}~}~ƒ€€‚{~€„„ƒ€€zyy{{yurh]U\am}}~~|{z{{}zxwxwwuyyyyzz{xvtututuvzwv|unkoqrrtrqf`]ksttyywndQVbotuvwwwtuvwwsolba```dkhfhhifilmnpnmfecghhhhgijkhjihffgjjllljmnmmnopqsqqlŽ“’“”ŒŒŒŽ’‘‘‘””›““‘—‘’‘‘‘““”””–œœ›˜™™œ˜™“”’———•››››œ››˜œ™™”“–œœ¢žŸ¥¥ª«ª§§¤©¥§¤¤¢§£¥¤¤¢¢£¤£«ª¨¨¨©©¨¦©§¯«®¬´°µ¯®¬±¯¯²°¯°¶°¯¯²±°¦¦¦¥¤¦§«©¯¬²µ¯®¬©¨©¯¯´±°°º²²²²³¶±©‡q`Q^j|›Ÿ¨¨®³¯²²²°¯³º¶¸¸¸¸·´³²µ²±°µ´¸´¹·»º»¶¶µ·¶··¹¹¹µµµ¸·»¸¸¸»º½¼½¶¶¶·¹¼º¸´¶±±°³³³²¶¶·²µ³¶³´³¸¶º´¼µ´³¯±±±¶®¬««¨§¦¦¦¦Ÿž›£ Ÿ ¢¢¤Ÿ œž˜•”–”•“•”—“———”•‘‘ŒŒ‰ŠŒ’“ŽŽ‹ŒŠ‰ˆ‹‹Šˆ‡†‰ˆ†ƒ‚ƒ‡‡††‡„…†Œ‰Š‡‡‡ˆˆŒ‡††‡……‡†…††‡‹ˆ‡ˆˆŒ‰ŒŒ“‘‹Š‰ˆˆŒŒ‹‹Œ…††‰‡‹‡‰ˆ„‰ˆŒ‰‡ƒ‚‚‰ˆˆ‡Œ…‡†Š€|}‚„މ‹ŠŒ‹Œ‡ˆƒ„…‰†ˆ„€‚ƒ‚‚€€‚‰‚€………‚€‚‡…†€}{z}z{uqaXZemwƒ~{zy}~~ywvxvvx~}}|z~yzvutwv|x|xx}umkssvuwqpcbhyzzx~yxj`^cktuxvzyzvywwxmjafa``ffffjjihipzovoogdekhhiogghjjkfddfflloklkrmlllntrwwxn’”މ‹Œ‹’’”““’’“‘’““““”•–”——™˜–“‘ŽŽ‘’’’—•–•–””•——˜——•”‘“• ŸžŸ¥¦¨¥¨§§¤¤¢¡¡¡ £¢£¢¤¡¢¢££££¥¥¨¤¦§§¦¨§¯«¬¯®¯¬ª®®®¯±¯«®¬®®ª©£¢¡¥¥§¨©ªª«¬«§¦¤¤¤¨ª®¯±²²¯®®¬œ“zc[McwŠž¢¦ª«®®®±±²°²´´´³³³°²±³°³²±°µµµµ·¶¸¶¶²µµ····¶µ¹µµ´´µ¸··³¶¸¹¸·±³³µµµ±±°°®°³´³¶±±¯¯¯´²²²´´´´´´³²²²²±¯««¨ª©¥¢£¡ œ¡£ Ÿ¡¡ ž ˜›”””•“’’‘‘““““‘••”’‹Œ‰Š‰Š‹Œ‹Š‰‰‰ˆˆŠ†‡ˆŠ‡ˆ‰ˆ‡…‚~‚…„ƒ††Š†‰‡‡‡ˆ…‡„††††‡ˆˆ‡††ˆˆ‰‰ˆ„‡‡‡ˆŠ‰‰‡‡‡‡ˆ‹‰ŠŒŽ‰Š‚……‡‡‰‰‰‰ƒ‰ˆŒ‹ˆ‚††‡‡……„…†„}~€†‰ŠˆŠ†‹ŽŒ‡‡€…ˆˆ†…„ƒ„……ƒƒ……ƒ‚‚‚‚„ƒ„ˆƒ„‚~|y{y{qaU\dkwz€ƒ~~z{z{zyvvuxswy||~|x}yzvvtvwyvvv|…‚vnkstusqka[ivz{zz}xpfddjptuvwyy{uwxvqnjg`a`_`edfchgfelsrmkhkhgfhhjkiefiijkdb^ggllljkhiimmmlrqrrsp’’–’ŒŒ‹Ž’’–•–’““”““‘–‘“’•”˜““”•”•–•‘ŽŽ’•–—”——š–˜˜™•˜—›——•””™˜žŸ¤£§¬«ª©ª£¥¡¢ ¡£¤¥§¢¤¢£¢«£¦£¦¥¨¥¥§©©«°¬µ±¯±¬³³³°´´³®®¬¯¯°°¬¨§¤£¤©¦ªªª«««¨¤¡Ÿ¢£¨±´´µ³¶±µ®®Ž~iV[Vgƒ”«««®³²´¯¯°³³´¶·¶¹´µ²³²´³³²·²³³¹µ·µ½¸»¶·µº¶·¸¹¶¶¶º¶¸µµ¶º·····»·¸³µµµ´³´´°°®¯¯¯²¶´¶°±¯´´´³¶µµ¶·µ¸·ºµ³´¸°«®®®§¤¤¦ ¡ŸŸ ¡¡ §¢¢¡¡˜š•š“–”–“•“—–”““š•”‹ŒŽŽŒ‹Š‰ŠˆŠ†ˆ‰ŽŠ‰‡‰„‡‚ƒ†Š‰‰ŠŒˆ‹ˆ‰†ˆ…‰ˆˆ†Š‰‹ˆ‡ˆŒŠŠŠ‹…‡‡‡ˆŒ‰‰ˆŒ†‡‡‹‹’‘‘‰‰ˆˆˆŠŠŠŠ‰Šˆ‘ŠŠƒ…Œ‹‹…„ƒƒ„„€†…Ž‰ŠˆŠŒŽ’ˆ‡„ŠˆŒ‡…‚„…‰‰Œ…ˆ†Ž‡‡„……‹„ƒ„‹Š‰…ˆ„‡ƒ‚€~~|zgWX`ipy|€ƒ~€€||{z|~xyzz{ƒ„}{|yywzyyyzyu†ŒuomstxrraXbuz{|{~umgimstxvyy}z{x‚xulhgf[Y[]_effblfffrqoheenljimmnjgeefghkfbbiikllklhggmlqppqrrsp’ŽŒŽ‹‹ŒŽ’“”’‘‘•‘’’’’’’“‘”’••’ŽŒ”••–””–••”•••“—––––•••™˜ž› ¡£§§¦¦¦¥¦¢¢ŸŸŸ¢¦§§¥¢¢¡¡¡¢¢¢£££¨¦§©©©««¬¬««¬®®®®°¯¨«««¬¬¬ª©¦¦¦§§¤£¢¡¡¡ žœš™˜ž ¦ª´²³²°¯¯¬—}h\LYauŽšª¬¯°±°°¯±±³´··¶¶¸²²°²±²±²²²²³³³²µµµµ´´µ¶·´µ´µ³·µ¶´³³¶¶¶´¶·µ³¶··³°®¬®µ°±¬°°¶²²®¯¯³²³³µ´µ±¶´¶µµ´´´·¯ª®¬¤¤¡¡šŸ¡¢¢¡žž¡›™”“’’””•“”’”•—”“”•‘މŒŒŒŒŒŒŒŠŠŠˆˆ†Š„ˆŠŠŠ‹‹ŠŠ‰……€€‚‡ŠŠŠ‰‰‰‡†††‡†‚‚ƒ†‡‰‰‰†‰ŠŠˆˆˆ‹‚†††‡ˆˆˆˆˆ…‡‡‹Œ‹ŠŠˆˆ†‡ˆ‹Œ‘ŒˆŠˆ‰‡Š‰‰ƒƒ„‹‹…‚€}}„†Šˆ‰ˆŒŽŒ‹’‰‡…‰ˆ‡‡…‚†ˆˆˆˆ†‰ˆŠˆ‡„‡‡ˆ………‡ˆˆ…„…‚‚|~~€‚u__`hox|~€}~~}{zz}zyx{|€€{zyz{xvyyzzz{xv„‡€trpqrqmbVcrx|{|{wpkgntrsuuyyyyywwvshc_[SZZ^`_]`acefgijhcccefijkkkhgfgfhihggfkkkkkkkhgbkknnnlsqso“““ŽŽ‹”“———’’‘‘’”’•”–””’’’’‘”“•’ŽŽ•–•””””™”––––œ–˜•––šš›•™šŸ §¦ª«ª§ª¨©¦§¢£¡¤¥¥¥¬¥£¢¢£¤¢¥¤ª¦¬¦©©¬¬°®²¬°««¬±¬¬«¯¯·¯±¬ª²¬®«©¨¦¦§§¢Ÿ ¡¡¡œ–“‘•šž¤¬¬¯°´²º²¯±³•†kWRIWg{œ¢±«³²·±µ±²±´´·´¾·µ¶·²´´´´µ²²´·¶º¶¶µµ¶·µ¸´µµ½µ·µ·³¸µ·´¶µ¹¶·µ¶µ¶³·¶¶¯®««²°·°°°±°µ²¶²µ¯³°·´µµ¶µ¶µ¶´··¶µ³´¶¯±®¦¨¢ ŸžŸ¥£¦¡¡ £Ÿ ™˜“’“‘˜–˜”—––—•˜˜˜’Ž”‹Š‹Š‰‰‹ŠŠ‡Œ‹‘’ŽŽŒ…………‚„‰‘‹Œ‡ˆ………†…„„‹‹‹‹Œ‰ŠŒŽŽˆŠ„…†ŒŒ“ŒˆŽ‡ŠŠ”Œ‰Š‹Œ††ˆŒ’Œ‹‰Šˆ‡‹‰ˆ††‡Œƒ€…}|~ƒ‹Š‘ŒŒŽŽ“ŒŽŒ‘ˆŒ‰‰ˆ‡ˆ‰‰ŠŒŽˆ‰ŠŠ‹Š‹‹Œˆˆˆ‰†ˆˆˆ‡„„…‚ƒ‚‚€„|qkgjsz†‚‚€ƒƒƒ€‚{}|}~€€…€{zzzxw}{{z{yxv‰‚}twvzqnf]asx~€€xtmjjsrrtvuyyy{€xxurf_[WWZ\`__]^`bbghjgdbcchfijijlgjjjhljjgiiqppmpmmihcjlmnnovrtn“ˆ‹ŒŒŒ’“’Ž‘’“’’’•“’’““‘“’’‘“–—˜””“•”“’—–———–•“–˜™•––™› ££¤¤¤¤¤¥¦§¢£Ÿ¢¡¤¤¥¤¤¤¢ ¡ŸŸ ££¥¥¥¥¨©¬¬¯¬«¨¨¨¨©¬«««¯¯®«±«¬ªªª«§¥¤¤£££¤¤ œœœ™•†…„†‰–ž¤¨¯±²²²³¬©¨Šq^HPQdt‚” §©«°²²²²°°¯´µµ´·¸µ²±²²²´³µ°´¶····´³³±´³¸²µ±µ²¶´µ³µ´µ´¶µµµµ³´²´²±¯¨¨©®®°®±¯±²°°°±²°±¯°°·´µ³³²µ´´³±®¯¯´µµ°°®®¨§¦¥¢¡ ¢£¤£¤ Ÿž–•“’‘‘“”””—––˜–ŽŽ‹Ž’Œ’‘‘ŠŠŠŽŽŠŠ‰‰…‡‡‰ˆŒ‹Œ‹‡†ƒ…ˆ†‚‡ŒŒ‹‡ƒƒƒ…„„ƒ„…‡‡Š‰‹†ˆˆŠ‰‰‰ˆ‰„†‡ŽŽ‹ˆˆ†‰Œ‘’Œ‡„‰‡††‡ˆ‰ŠŒˆˆˆˆˆ‡ƒƒ…‡‡‡Š‚z‚}|{}~ƒ†ŠŠŽŽŽŽŠŠ‰ˆˆŠ‰Šˆˆˆˆ‡†„‡†ŠŒŒŽŠŒ†ˆ‡‰‹‰†ˆˆ‰‰‡„ƒƒƒ‚‚wsnpt{„…ƒƒ€‚„„€€|||~€€€~xzz{yxw{{zy{xwwz{wrwwyqocinw}~~~ttjmoqqtuuuwv{|€xwtoc^WZ^^^_``\^^_^cfgeeabbdefegjifghhhiihghipnmklmnia_jnmmoooopm“ŽŽŒŠ‹Ž‘”‘’’“‘‘’‘•“’“““—–—‘‘”‘’•—›˜š——˜–“’”˜˜˜™••—œ˜™–˜˜ž¢ ¢¤£¤¤£¤§£ª¢¢¢¥¤§£¥¢ ¡¦¨¨©©§¨¨©¨®¯®®««§¨§®¨«¬®¬´¬«¬±¯¯°ªª¦££¤¤¤£¢ Ÿ›—‘Šƒ|xwƒ’¢¦®®µ¶¸²±³¸§¦~eVFOZk‡ž¤®¬±®¯°¼±´°°³»¸½µ·µ³²´²·´¸´µ¶¶µ¸·ºµ³²±³µ´¹²´´»µ¶µ¸¶¶´¸¶º¶¶µ´´¶´º±®«¬°°º®°®µ±²±¯¯¯¯´´´´´³·´¸²²²·³´±°¯³²µµµ°µ¯¦¥¥¥¢££¥¦ª¤¤¢£Ÿ¢žœœž“’•”“’’“—˜˜–•‘’ޓޒ’—’’‹ŽŠŽŠ‹ˆ‰ˆˆŽŒ‘ŽŽŠ‹††……‰‹‹‹‹’ˆ…„‚„†„…ƒ‡†‹‹‹ŠŠ‡ˆŠ‹‹‹‹ˆˆ‰ŠŽ‹‰ˆ‡ˆŽ™‹ˆ…‡‡‡ˆ‹‹Š‹ŽŽŠ‰ˆ‚……ˆˆ„~}ƒ}}}~‚‹Š‘’•ŽŽŽŠŒ‰‰‹Š‹ˆ†‡Š‰Ž‘•Ž‰Š‡Šˆˆˆ‹‰Š‘‰Œ„ˆƒ‡ƒvwsz}…†Š„Šƒ‚‚„„…{~€ƒ‡‡ˆƒ~}{}{|xx{‚{zz{wvw{phq|vytprwy€„€€~tssvwwuzz{x{}ƒz{qmb^\_cgghb``a`cbbekfeabccchfdfigdefgkhhhlipmmlkmodY]kmnnqotpqk‹ŠŠ‹ŠŠŽ‘’‘‘ŽŽ’‘‘‘‘’’’’’•—–“‘ŽŽŽŽ’’•——–—–””““”•˜˜—••–™š™˜š•˜šž¡œ œ¥¢¢¢¢¢¢¢¤¢££¢ ¢¢¡¢¦¦¨©©¨¨¦©©«¬«©§¦¦¤£¦§©¨¨§¦§ª¬®¬¯¨©¤£Ÿ›š˜–—‡xsnmmzƒ’¢§©®±´´´±®¯ŽsXTJ[iz“š£ª°¯¯¯¯¯°°²¶¶¶¶±³³³²´°µ³´´µµ·²´´²²²±²´µ²²²´µ¸¶µ³¸¶¶´·¶¹¶¶µ³³³´°¬¨¬®¯¯°®¯®®¬¯ª¬¬¯¯²±±²·´´±±±²²±¯±±±²µ³²§ ¢¡£¡¤¤¥¦¤¡ ŸŸŸž›™—–’’”–”‘’“”“”“ŠŽŽŽŽ‘‘ŽŠŽ‹‹‰ˆ‡ˆˆ‹ŠŠ‰ŠŒŽˆ†„†‡ˆŒŠ‰‰Šˆ‡†‚~‚…„„„ƒ„†‰‰‰‡Š‡ŠŠŠˆ‰‰‹‡‰Š‰‰ˆ„……ˆ‰ŒŠŠ„‡ˆˆˆŠ‹‡„‡ˆ‰Š‰‡‡†‡‚…†…„†€€~z|~ƒ†‹‹ŽŒŒ‰ˆˆŒˆˆˆ‹ŒŒŒŒ‰‰ˆ‹Œ“‘ŽŠŠ‡‰ˆˆ‡‹ŠŠŠŒˆ…€€€…„}ttu}‚ƒƒ„ƒƒ‚ƒ„…€{~„…‡„„€}y|z{zxvz{{{{zzwvwmcer{vwwvtz}€}}|ytsrvxxvyyyw{‚ƒ…~yupkcegffghgcbaaacceghgdabccb`\_`__aafeeefekiijlglona]]lmnoqmnmke‹ŠŠŠ‘’‘‘’’’’•“““—“™••‘’Œ“Ž••–•™˜™•—’‘’“”™–˜““”–—Ÿ›™˜œœœ£ Ÿ¡§¦§¢¥¦¥¢¨¥§£¤ ¡¢¢¦¦®ª¯«««®©««°«ª£¡¡¢£§¦¥¥¦¦§®¬²±¯¨ª£¤š›––“ˆumfbbew‡“¦¨¯±±¹µ¶¬¬|gSUUay†˜§§®¬¯®´±³³³²¶µµµº¶·³´´¶³µ³¶µ¶´´´¸±µ²±³¸´µ´µ²´³·¶º¶µ³ºµ¶´·¶¹·¼³±²µª«¯²±±®¬°¯´®«°¯°°¯²¸±²²¸³´³·±µ±±±µ²·³µ±²®©¤¤££§¥«ª©¤£¡¢Ÿ¡Ÿžš™–•’’’›››””––“”Ž‹ŒŽŽ“’‘ŽŒŒ‹ŠŠŠ†ˆŠ‘‰‰ŠŽ‡…†ˆŠŠ‰ˆˆˆ‡…„ƒ‚‚……„ƒ‚‡Š‰ˆ‡‹Š‰ˆˆ‰ŒˆŽ‘‰‰‰‰…„†ˆŠ‘Œ‹‹Š‰Šˆ‹‘‰…„††Š‰ˆˆˆ‡†…‹‰‡†…‚}|}„ŒŠ‹Œ’Ž“ŒˆŒ‰ˆˆ‡•Ž“‘‘’ˆ‹Š”ˆ‡€ƒ‚…zstzˆƒŠ‚‡ƒ‚ƒ‰ˆ‰„‡}‚„ˆ‡„†}yw{{{yz€~{|‚|xwodbat{|‚zzz~€€‡zyxxw}}|}~{~~~ƒ‡{vvojikorqqmkgeefedcjhigdbbdhc_\]^bbbaeefdiiihhilinmmfbdlnxrqmnljcŠŠ‹‹‘ŽŽŽŽŽ‘‘“’’‘’’’‘ŒŒŒŽŽŽŽŽ•’••–••”’Ž‘’“””“’“–——ž›š™ž ŸŸŸ £¢¢¢¡¡¡¢¢¤¤£¢¢ ¡££¤§¨¬©ª«¬©©ªª§¦¤¤¥¢¡ ££¡ ¤¦©¨¨©©ª©¨¨£Ÿš•…vmf_\Z`cw‰™¤¨«¬±°¯°£›k_O\fs‹›ž¢¨«¬¬¬°°²´´´µµ²±±±³³´·²°²²³³³³´±±°²±²³µ³³´´²²²´³³³´³µ²´³³´¹µ»²¯®¬¦©©¯³®«ª¥¬«°¬©®«°®¯±±´´°¯®°°±±±°±±±²²²·±°¬¬¬¨¥¢¤¤¤¤¦¦¤¢¡¡¢žœ™›™š””’”•š™š—–’‘“Љˆ‹Œ““ŽŠŒ‹ŒŠ‹Š‹Š‰‡ŠŒŽŒ‹‡ˆ†‰‰ˆ„‡ˆ‰‡Š‰ˆ„~~~‚‚ƒ‚…‡ˆ†…„‹‰‰†ˆ‰‰‡†„‰‰„‰†‡‰‰‹‰‡‡ˆ‰‰‰ˆŠŠ‰„ƒ‚‡…†‡†……ƒ†ˆˆ‰†€…{}~…Š‹ˆŠŒ‘‰‹ŠŠŠŠˆˆ†Žˆ‡‡ŽŠŽŒ‹‘ŽŒŒˆ‡‡‹Žˆ‰ŠŽŽ‰„ƒ‚‚€zuqzƒƒ‚‚‚ƒƒ…ƒ„‚€~ƒ†ˆ„ƒƒ}vyy|y|{||}}{{xvhWJ]w{~€z}~~~€€}wwvxz~~€~{|~„Œ„|vpmimnrvxzupnkiggfedigiifdefffb]]]]^babaccjiiiiihhhhihhhlnpqqkmlhbŒŒ“ŽŽŽŽ”ŽŒŽŽ–“”’‘‘•Œ‘ޑޔ”˜–š””“’‘—–œ”’’‘“š˜šœœžžž¡ ž¡¡¢¡ ¤¢¢ ¡£¬¦©¥¤¡¢¤¥¨®ª´±²¨¨¨©¦¨¢£Ÿž ¡£ª¢¢££¤©¥®©©«¬«©©«›ŽŠˆƒ|vlb\VTV^fz”Ÿ¯®¯®³°²³´›r^[T`q€•šœ¢ª«¬«²¬®«°°·´µ³¸²±°´³¶³²±±±·´¸´·²³²³±³±·µ¸³³µµ³¹µ¶³´³µµ¶³µ³¶µºº¼¯¬ª©ªµ¯¬«¬«¬ª°«ª°®®°²³µ¯°®®¯³¯®±¹±²±·²³«¬©¦¥¥¦©¥¨¤¢¢¥¡¢›šœ›™™™–œš¡šœ™˜•“‘Œ‹Œ™’’‘Ž‹’‘‹ŽŠŽŽŽ’‹‰‡ˆ‡Š‰‰‡Žˆˆ‡Š‰{{|€„ƒƒƒ‚ƒƒ‚†…Œ†…ƒŒŠˆˆŽˆ‰……‡Žˆ‰…‰‰‰ˆ‡†……ˆ‡‰‰ŒŠŠ‹†‡†‡…†‡‹„„„ŒŠˆ†……€~~€„‹’‰‰ŠŽ‹Œˆ‰‰ˆŠ‡Œ˜‰‘’’ŽŒ‡„‡‘ŒŒ‰„ƒƒ„†„€xtw„ƒ„…†„……ˆ…‰„…€„ˆ‡‰‰Š€zxzz}zz{€„€}…~}yv^KGYw|}~†~ƒ~zvuu{{ƒ†€€€‚„‡ztoihrx„†€}wssuttmiiiiiijglhthgee\Z^ccdbedkjjjkiiiijkjpmmmnorqslgbŒŒŒŠ‘ŽŒŒŽŽ‹ŽŽ‘‹‹ŽŽ‹‰•”—“”‘”’““–˜•‘‘•—˜˜˜šžžž¡œ ž¡¢¢žžž¥¢¢ ¡£¥£¥¥££¥§©ª«««ª¬««§¨¦¥£¡ž› ¤¤¤¥££¤¦©§¦¥¦¥£¢¡ž™“Œ„|wrlfbZWQUVcn€”¡¬¯¯¬¬¬ziV]_oƒŠ—› ¢¤ª©ª««©««°°±²³°°¬±²¶±°°±²´²³±°¯³²²°±²·´¹±³µµ³³´³³³³µ²³±´´¶²±²®¦¥¥ª«°¯«ª¨¨§©ª©¨©©¯¯¬®¬¬«®®®«®²´°°°²±²®¬ª¬«ª§§¦©£££££¡Ÿ¡››šœœš˜˜˜œ›ž›™—™™•‘Œ’”‘‘ŒŒ‹‹Œ‹ŽŒ‹‡†‡ŽŒŠ…†„†‡†‡‡†…„„„ˆˆ‡|x„……ƒƒ„„ƒ…†Œ‹‹„…ƒ„‚‡ˆ…„„‚…‡Š‹Ž‡‡†‰ˆˆ‚‚‚‚‚ˆ‡‰‰‰‰ˆ‡Š††‚„…†ˆ‹„……ˆ‰‰†…€~~†Š‹ŠŒŽŽ††‡‡ˆ‡‡‰ˆˆˆ‡‡ŽŽŒŠˆŒŽŽŒ‹ŠŠ‰†‚…ˆŽŒ‹‹Š…‚~……„„~w{|„„„……„…†‡……ƒ‚{€„€„‡ˆ†ƒ~|z{||yzx€€~}}}zrRC;Yw{|~€€~|~}~{ztvvz{€|}‡Œ…zvqmkvˆŠˆ…ƒ‚|zywrrqomjfhgihggfea[Z]^_abddloomlgjkjjkhkmmqnnonmjhcŒŒŠ‘ŒŽ‘‘’Ž‘‘‘‘‘‘‘‘“ŠŠˆ‹‘–•˜’•‘““›››“‘’”™™Ÿž¡¢¢¢¢Ÿ ¡Ÿ¢ ¤¢§¢¤¡¦¥¥¡¡¢§¦¦§««¬ª®¬°¯°«¬ª©§¨¨§Ÿœž¡¡¨§«¥¨¨ª©¨§¦¦¦¦¦£¤ŸšŠƒ}toid_ZUQOQXgv‰¡¦³¯±®®°²¡œlaT\gwŽ— ¢«©©¨«ª©¬««¯¯¶°³³´°²¬°°·³¶±°±²²µ²²²²¯´³´°·²·´¸±´´·³´´¹´³³´´¸¸¸´·¶¶¬¨§ª«²±±®®©©©©¨¨©«ª««¯¯¯¬®¬«¯®¯²¬®®³±µ°°¯µ±±®¯±±¬¬¬§ª£§¦¦¢ Ÿ¡Ÿ ¡¡››™››œœ š˜— ˜—‘Ž““—’’‹Œ‰ˆ‹‘Œ’Œ‹†ˆ‡ŽŠ‡†‰„‡†‡†‰‰Š„‚ƒˆˆˆ~‡†‰……ƒ‰‰‰……‡ŒŒ„…„††„‚‚„„‰‡ŒŒŽˆŠŠ‘ˆˆƒƒ„‰ŠŒ‰‰‡…†Š‡†„ƒ„‡ˆ‰†‡†‡ˆ‰ˆ…~~€…Ž’Ž’ˆŠ†ˆˆ‡ˆ‰ˆŠ‰Š‹ŒŠ‰‹’ŽŒŒ‹†„„‰Š–ŽŠŠ‰†€†‡‰‚~~†……†‹†ˆ‡†ˆ‰†‡‚€„ƒ„‡Š…‚~~}ƒ|yy{€‚ƒ„~}}ymM76[t‚{~€„€}|zyxxy~|€~}|~ƒ‡Œ„~{xuv~ˆ˜“’‘“’††„€}{rnnnlkigeeb_[[[_^bcdbnoxmlkkklkkhiknnmnqpnijdŒ‹ŒŠŠŠŒŒŽŽ’‘ŽŒŒŒ‘‘ŽŽŒ‰ˆ†‹Ž‘’•”’“““’‘Ž‘“”•˜šŸžžž¡¡ Ÿžœ¡ž¢ ¢¡¢¡ ¢¡¤¢¢¢¨¨§§©©ªªª©¨§§§¥¤¥¥£Ÿžšž ¥¥¨§§¦¨©©¨¥£¤¤¢žŸ’Š}tnfc`]XURPNTXl~§©®®¬¬©¨•ˆt`^Vdq•œ¦¦¦©¨¨¨«¦ª¬¬¬®¯¯¯¯««¬°°°°µ±³²²±¯²®¯¯°°³±±±³³²±´²±±³´¶´³³³³¸¶µ³¶¬©¢¨§¬ª¦£¤£©©¨¨ªª«¬®®¬ª««¬®¨ª«¬ª®°®®¬®°±®®¯¬®¦«¦©¤¦¦¦ Ÿž ›š˜ššž ™˜˜—’‘ŽŽŽŽ““•’Ї„†ˆŠ‹‹‹Š„†ˆŽŒŠ‡‡‡‰„„„…†‡‡‡‚„„„„…ƒ„…‡ˆ‰………ˆ‡‡††‡‹ŒŠ„„„…ƒ€‚€„…†„‡†ˆ…ŠŠŒ‡„ƒ…ŠŽ†„‚…†Šˆˆ…„„…І‡€€€~~„‹‘ŒŽŒŒ‡Š†‡…‰‰‰ˆ‰‰ŒŽŒˆ‹ŽŒ‹ˆˆ‰Š‹…€„†ŠŒ‹ŠŠŠŠŽˆ„€†‡‰‚ƒ„…†ˆ‰‡‰ˆ‰ŠŠ‡‡‚}€……„„ƒ€~|}|zv{|€‚‚ƒ‚}}wqM:4Tov{}€}||€|{zzz|{~}~x|†‡…ƒ…†‚~~…’•”””’‘‹‰†…ƒ‚{tssvxqjieca`ZZXZZZZ_agkkkkjjklmkiijmnoorujfhdŽ‹ŽŠŠ‹ŒŽ‘—’’’•””Ž•“Ž‘Šˆˆ‡ˆ’‘•“•‘‘–’‘‘˜–™š›ž¨žŸ£¢¡ŸŸž¡¡¡¡¤¢¥¡¡¢¥¤¤¦§¨¨§¨¦ª«®®¨©§®§§¥¥¢¡žžŸ¦¦§§¦¨§§¦¥¥¤¤¡¢š‹oeb_^]WTQOLLU`rŠ—ªª¯¯³µ¶£Ÿ„uiZ\^h{Š¡©©ª«®©ª«¬©°¯±³²³®¯ª¯°°¯¯°´´´´µ²²±²®¯±´³³±³±°°´³²²¶¶¹¶¸´¹¸¸µ¶³µ¬§§©¬µ®°¬ª¥¡¡§¢¨¨¨¨¯®³±¬ª«««¯®¨«©«¬³®°´°±¬¯³®®¯¹¯©¬§§¦ª¦§¢£Ÿ ¡¥ŸŸ›œ›š¡ ¡˜˜˜˜‘“ŽŽŽ“—“•’ˆ„ƒ„‡‹Š‹Šˆ‰ŠŠŽŠˆ†Žˆ‰†………‡‰†ˆŠ„…„‡…Šˆ‹‹Š‡ŒŒŽˆ‹‡‡‡‹‰„†……‡‰ƒƒƒŠ‡‡†‰‰‹ŠŠŒŽ…ƒ…„ƒ†ŠŒ†„…Œ‹‹‰…„ƒƒ„‰†‡ƒ‚€ŠŒ•’—”ŽŒ‰‰ˆ‹‘Š‹‹‹ŽŽŽŒŽŽŠŠ‡‰ˆ‹…€€…ˆ’Ž‹’Š‹Šˆ……†‡ˆ‰‰‰Š‹Œ‹‹ŠŽŠˆ‚†„Œ……‚{{{‚|yz‚…ƒ†ƒƒ‚‚}ytX><Pisy|}€€€€‚~€|€~ƒ}{€ˆ„‡ˆ…†‹’ ›››˜œ“ŽŽ†„wtu„{rplkaa]ZXZZ[Y^`dipkljjkqmnijjpqqotogemeŠ‹‹Š‹ŽŽ‘“’‘‘‘‘’ŽŽ†††‰‰Ž“‘’‘‹‰‘”–•™šžžŸž¡¤£¢¢¡¢¡¢£££¥¦¨¨¨¨©¦§§§§©§¨££££ ¤ ¡ ¢¤¥§¨¦¦£¤£¢¡ œŸžœœ“Œ€qeWVWYWVQPMQNYcyŽœ§©ª¬°¬ªšŒwb_Uaju€Ž›¡§¨©«««©«¥¬©¯¬¯±±®±«®«¬¬«¯¯°¯±°°¯®©®°±³³³±³°°¯®±²¶µ·´´´¶¶¸µ³°¬§©©«®¯¬ª«£¢ ¢¢¥§©ª¬¬¬ªª¨¬ªªª¨¥§¨¬«®°°°´³¯¬¯®®®®¯¯¨§§¨¦§¥¥££¡£ŸŸž››šœ›š™””’‘ŽŽ‘‘—“Œ‹†„ƒ‡‹ŠŠ‹Š‰‹‹ŒŠˆ‡‰ˆˆ††„‡ˆŠˆ„„…„†††††ƒ…†Š‹Žˆ‡„‡ˆˆˆ…‡‰‡††…ƒƒ††……†‰‹‹ˆ…„„ƒ‚„„„ˆ‰Š‰‡†…ˆˆ‰ˆ‡„‚|‚……‡†„}€‚ƒƒ‰Œ‘‘ŽŽŽ‹Š‰‰‰Œ‹ŒŒŒ‹Œ‹ŒŠŒ‹‰††††††‚€€‰ŒŒŒŒ‰ŒŠŠ‰‹†‰…‡ˆ†„ˆ‰‹ŒŒ‹Œ‹‹‹‹‹ˆˆƒ„ƒ†‰Š…„yu{}~|‚{{|€‚‚‡ƒ‚€~{wfREUcltwz}}}||||~~€}~~{‚‚ƒƒ…‰‘“’Ž‹•œžž™˜—’މ†zxw…„}wtsi`_^\YZ[[[^_beefhhjjkkmikkmlllnjfaa`‹ŒŒŒŒŒŽ‘‘•“’“‘Ž“”ŽŒ‹ŒŒ“‘“’ŽŒ‹‰Œ”“˜˜™œŸ ¢ŸŸŸŸ¤ ¢ ££¤£¦£¤£¥¦§¤ªª¬ª®®¥©¨ª¨¦§¥¦¡¢¡¤£¤¤¨¤¥¦§¦¦£¤¤£œ ™‰‚th]QPQSZSQONOS`ož£²¨®®¬ª~kZZXan|‘–¢£¨¬°¬®ªªª¬¬©°¯²²±°°®¯¯±«¬¯°³®µ°±¯µ¬®·¶¶´¶²³°°°°°µ³¼µ¼´µ´¸·¹²®¬ªª²ª°¯®®®ª«¥¦¢£¤¦ª±°±®¬¬«®§««ª¨¥§¨¬²´´±´°®¬´¯±±°°±©ª§©¥§¥¨¢¤£¤ ¡ž›œœ ¡žž˜™”•‘”‘––—”˜‘ŒŒ†…‚†‹‰‰‰Ž‘Љˆˆˆ‰‡†ˆ‹ŽŒŒ„ƒƒ…„‰ˆ†‡‡‡†…‰‹‡‡„‰ˆŠ‡„„‡‡Š‡†…ƒ‡…„„†‹“Ї…††ˆˆ‡„„ˆ‰‰‰Œ…‡‡‹‡ˆ‚|~†„‹‡†~€„‡Š‰Œ‘‘•ŽŽŽ’ŽŽŽŽŽ•ŒŒ‹‹Ž’‘І†ˆˆ‹†ˆ‡†‡”ŒŽŽŒŒŒŒ…ˆ‹ˆŠ‰™ŽŽŒŒ‹Š‹‹“ŒŠ†‡†Ž‰Š…„ysw‚}}}}…‚‡‡‡„††‡€‚{zob]\agpuy‚€|~|€‚„„…†€€ƒƒƒ„…ƒ‡›šš”‘‘˜›¤¤¦£¤¢™–”•މˆ€€~‡…zwqjda_a\\\\]^`accchhnkjjmllmpmomqieccc‹ŠŠ‹Œ’‘’’‘ŽŽ‹ŒŽŒŒ‹‹ŠŽ“’‘‘ŒŒŠŒŽ“”™™›œž šœšžŸŸ Ÿ¡ ¡¡¤£¤¤¦¢¤¥©ª©¨ª¬¨¥¥¦ª§§¦¦£¥ ¡¡¥¤£¤¤¤¥¢¥¨£¡ œ˜™›‘Š~qg[WOOOSSQNNNTWgv‰ž¤¨¨¨¨£„pdT^cnx€Š—¡£¥©®®ª©©¨¨««¬ª°°®«¨¯¯«¬ªªª®®¯¬®«ªª®®®¯¯²³³²±°°®°±µ³·µ´´´³´²°¬«§«±ª¬«ª©©§¨¥¥¢£¨©¬¬¬«¬«ªª§ªª«©§¥§¨¬¬¯°¯®®¬³®±¯¯®°©©§ª¢¦¤¢ žŸœžœœšœ¡¡¢šž—•‘“‘“’“”“•”˜ŽŒ†…‚……†‡‹ˆ‰‰‹ŽŽ‹‹‹‡†……‡‹‰ˆ†…„„ƒƒƒ„‚„‡‡ƒ……Љ‡ƒƒ„Ї†††……„††‡‡†‚‡ƒƒƒˆ‹‰ˆ…ƒ†‡‡††ƒ†‰Š‰‹ŒŒ…„ƒ†ˆ…|~€‡„„ƒ€}€‡Œ‹‹ŒŽŽŽ‘‘‹ŒŒŒ‰ŒŽ’‘‡††ˆ‡…‚ˆ…ˆ‹ŽŒ‹‰ŽŠ‡‡††…††ˆˆ‹Œ’Œ‹Œ‹‹‹‹ŽŒŒŠ‰ˆ‡ˆŽŠ‰ˆzpw}z}~~„‚‡…†…„ƒ‚~}{wphecinwy~}{|{€ƒƒ„…†€€€‚ƒ‡‰„‚—œ ™—•𢢣¢¡›™–”•’Žˆˆƒƒ‚†‡„}yqmhea`]^___`ab_`_bcilkimmmnpjkjjheccaމˆ‹““™’‘‘’ŒŒŒŒŽŒŽŽŽ“Ž‘••”’’‘‘“”›ššœŸœžžœžžžŸ¡ ¡¡¢¡££¨¨©¦§¤¦¥©©©©§§§¥¦¥ª§§¦¥¦¥¥¥¦¬¤¢£¦£«¢¦£¡Ÿœž–™‰qf]RONNPPXRPLNV^m¨¨¬ª¬©°—xe_X`lx‹”™¥£¦¥«®¬ª©¨¨¯ª¬ª°¬«ª©°¯¯°¬ª¬®®®®¯¬°«¬±±±¯±¯³²³²±±²²²³µ³¸´´µº´·°®««ª®®±ª¬¬«ªª««¨©¢¥§¬¬¬¬®±«®ª«««ªª¨§¨ª¨¬«²µ¯«¬²³³±±±²®°ª©«£¦ ŸŸ¡Ÿ¡Ÿ›œ›¡Ÿ¨¢£˜ž–”“™“”“˜—˜“–——ŽŠ†…„‰ŠŠ‰‹‹‹‹‹Ž‰††‡…‰Š’‰††‡…‡†………‚„‰„‰‡Š…„І…†Š†…………ˆ‡‡„ˆƒ„„‹ˆ‡†…ƒ‰‰Š†……‰ŠŽŒ’Œ…ƒ‚……ƒ~‚ˆ„‡€~~‚†”••“‘‘’ŽŽŽ‘‘‘‘“˜‘Š‹Œ‹’‘ˆ†…†ƒƒ‡ˆ‘ŒŠ‰‹‰ˆˆ‡‰†…‡‰ˆ‹•‘”ŽŒ‹•‹‹ŒŽŒŒŽŠ‹€tt„~||‚‡…„„‡‡ˆ‰Šƒƒ~€~tmjkpxz}}|{||†…„„…†‡‚‚€ˆ†Žˆ„‰—™¢ ¡›š¢Ÿ§¤¥¢ ¢ ž•𓕉ˆ‡‰…yzoifgfgddddacaa^bbhjommmropljhhhhehc‡‰ŒŽ’’’’Ž‘‰Œ‰ŠŠŠ‹ŽŽŽŽ‘“•‘‘‘‘‘‘’”•—™™œœœžž››œžžžž ¡¡¢£¤£¥¥¥££¢¢¤¤££¢¤¤¤¤¦¥§¢¥¦§©¦§¦££¢¤£¡œ›››˜˜™‘Š~qdWSNPNQURPONNNYduˆ”£¨©ª§¤¢|n\_]kxƒ’–›Ÿ££¥¤¥¥§£¦¥¨©ªª©©«¬¦«ª¯¯®«ªª©©©ª«««««®²®°¯°¯³°°²²´¶µ³²³´µ´®¬®ªªª®¬«ªª¨©©ªª¨¥¤£¥¥¨««¬®¦¦¤¤¤§¦©®¨¦¬©«©©¥««®©¬««ªª©¨¤¡žžžžžœœ™›œ¡¡£Ÿœ˜—“““•“““”•–“–’‘Œ†††ˆˆ‰Š‹ŒŒŒŒ‹ŽŒŠ†………†‰‰‰„ƒ……†ƒ‚„……€ƒ‡……†‡†…………‚ˆ†„ƒˆ‚…„„„‡‚‚‡……„……‰Š‹ŒŒ‹€……ƒ€‚ƒƒƒ€{}‡Œ’‘’‘‘ŽŒ‹Œ‘‘“”˜‘‹‹ŠŠ‰‹‹Ž“Šˆ…†‚ƒƒˆ‹ŠŠ‡ˆ†‰‹‰‡‡††‡ˆ‡Œ‘”‹‹Ž‹ŠŠŽ‹‰Š‹Ž‰yxz€}}}ƒ†‡…„…„…ƒ„ƒ€€~}ztqrt{|}~~{}}…Ї…„ƒ„ƒˆ†‰ˆŠ”œžŸ¡žœžŸ¡¢££¢¡¢£Ÿ—š““‹ŒŽŽŠ…{smhklgedddaa`a\\]ffommnmmmmledddcedŽ‘ŽŽŽ‘•މŒ‰Ž‹‘Ž“•”–‘‘’—”•’“”š’“’˜–œœ¢Ÿ ›œœ››œ¡¡¡¡¢¢¥¢¤¥©£¥¡ £¢¡¢¦¡¢¢¤¤¨¤§¤§¨©ª©§§¦¥£§¦¥šš›™ ˜™Š†th\PMJLOPWSRMLLN]j}”›¦¥®¯š’}nfZ^cp†‹›ŸŸ¨¨©£¥¥¥¤§¥««¬¬«°ª¬«¬¨ªªµ¯®®¯¯¯¯§©©©ª±¯´®°°³²±±±°²±´³µµ¶¶º¶¸³¶³¹³³®¯«®¯²ª®ª«ª©©¬ª¯©¦¤¤¦¨¨¨«±¶®¯¨¨£¦¤¬¬ª¦¦¬«¯«ª©¨¨¬«°«¬©°ªª«¬¦¢ œ¡ž ¡¢žœŸ ¥¢¤š˜˜”—”™•——Ÿ–—–—“•†‰‰Œ‹—’ŒŽŽ‰ˆˆ‹‹‹‰Ž‹‹…ƒ„‹†…‚‚‡ƒƒ‚„ƒ……†‚‚†…„…‰†„………„ƒ‡…ƒƒˆ‡‡‡„††‡††…„ƒ‡‡‰ŠŽŒŠ‹ƒƒ‚‹„ƒƒ‚ƒ……†€}}ƒ““““—‘‘‹Š’‘‘—˜š‘ŠŠ‹‹‹Œ’“•‹…†ƒƒ†ŽŽŽŠŠ‡‰‡ŠŠ‰Œ‡ˆ‰‹Œ’‘“””‹Œ‹”ŽŒ‰‘Œ‹‹ŠŒ•‘…|xz~†€††ŠˆŠ‡ˆ…‹†‰ƒ‚ˆ‰Œ€~}}{yz|~€…‰ˆ‹……„„…‰ŠŒŒ‘‘’–Ÿ¡¢¤£¬Ÿ¡¡¨¨©¥¨¢ š™”””™’’𔓋ކ†ypospjhieiba`_]X]dfpntpmntmnfgbcbedŒŠŒŽŽŽŽŽŽŒŠ‰‰ŠŠŠŒŒŽŽŽ““—””’”˜•“““——›Ÿœ››œ™š™› ¡¡£££¢£¥¢ŸœžžŸ ¡¡ ž¢¡¢¢¥£¤¥¥££¤ª¦§¤££ ›–˜”–•’ŽqfXRMLKNPQSQPMJNQar†›Ÿ£¤¤¢žq`^^hox…•™œŸ ¢£¢ ¢¤££¤¤¨¦¨¨©¨¨¥«©©§©©ª«¬¬®¬«©¨§¨©ªª¯®²®°°±±²²±¯±±²²´µ¶¹·´µ³³³²¬ª°¬±«©¦§¥§©©§§§¦£¥¦¨©©©¬®ª¨§¨¤¥¤«¬¦¥¥©«©§¦¦©¨¨©¨¨¨¦©§¨§§£ ž™Ÿ¡ ŸŸŸŸžž¡¥£ ž™—””’”“”“—––’‘’‘‘‡‰Š‹ŒŒŽŒ‹‹ŒŒ†‰ˆˆ‡ˆ‰Š‹‡‡††ƒƒ~ƒƒ‚‚ƒ‚‚‚‚„„ƒ„……„ƒ„††„„†€„ƒ‡‰Œˆˆ„…€ƒ‚†ˆˆ‡ŠŒŠ…„ƒƒ‚†ƒ‚‚……€}}}ƒ‡Ž’’““•ŽŽŒŠŽŽ‘–𖑉ˆˆ‹‹ŒŒŒŒ‹Š†„ƒ‡‰Ž‹‡Š†††‰ˆˆˆŒ†‰ŽŽŽ’ŽŠŠ‹ŽŽ‹‰‰ˆ‰‡‹‹ŒŒˆ}y~‚€‚…†Š†ˆˆˆ…‡††ƒ„…ˆ‡†z||}€‚‚ƒ…‡ˆ‰Š‡†††ƒ†ˆ‹ŒŒ‘„ˆŽœ¡¥¦£§™¢ŸŸ¡¥£¤¡ ›š••”••””—•“Œ‹‹‡~wtuvqkiefa^\_aaadglntuonoljfg```bcŠŒŒ‘Œ‹Œ‘ŠŠ‰ŒŒŒŒ•”ޑޑ™”—˜—––””•——žžžŸŸŸž›œœœ› Ÿ¤¡¡£¦¥¨¢£Ÿœœœž¢¢¤¡¢¡¡¢¢¢¡¡¥¢¥¤¤¢¨¤¬§§¢¥ œœ›–™™š‰‚sf[PMKMPRTUUQOKIPVh}§¦ª±ŸŸ„shZ^al}„‹’œ›ž ¥¡¢ ŸŸ¡¤©ª«©¯§ª¨«¦¨¦ª¨©©¬±ª¬®«¬ª®«°©®¬°±²²²±µ±²±±°´³¶´¹¹¸·¶´ºµ·±°«««±¯²±ªªªª¦¦©«ª«¨«¦©¦ª¯¬±¯³¬®©ª§©¦¨¨®««¨«§¨¦¥¥¥¦©§¨©««¨¬§©¦¨¢ žŸŸŸž¢ ¡¡ž§¦¥¤£›—•••“—•–”—––’”““’‘ŽŽ‹‘‹Š‹Œ‹‹‘ŠŠ‹‹ˆ‡†‡‰‰‹‰ˆ‡‡„‚†…‰‚€…‚†‚‚ƒ‡ƒ†††…‰ˆŽ„‚‚…„„‡‹‹‰ˆ………‡ƒƒƒƒ…ˆŠ‡‰‰Š„„‚‹‚†„ƒƒƒ…†|}ƒ‹Ž–—˜—–“”””ŽŽ’’’’‘’••”Žˆ‡ˆ‹‹ŽŒ‹Œ‡ƒƒ‰‰ŒŽ‘ІЉІ‹‰Šˆ‡Š‹™’’‘ˆ‡ŒŽŒŠ‰ŽŠŒ‰Ž‹ŒŒ†‡„„‚†………†‹ˆŠ‰‹…‰††„‰ˆ‡‡}ƒƒ‡‡…„…‡Œ‰‹‹Š‡‡‡Œ‹‹Š–‘‘‘‘ƒ‡œœ¡¦«¤££¤ŸžŸ©¤¨ ¡ ¢–‘“™š–š–•‘Ž„{zqkik`[Z`cjjhhkoxsqqqkgegbaaabŒŠŒŒŒŠ‰ˆˆˆ‰‰‰‰ŒŒŽŽ’ŠŠ‹Ž‘“’’”•–‘–“–———œšœžž™œœœœŸŸ£¡¢¤¥¤Ÿœ›˜™–œŸ¡¢£¡£¡ £¡¢¢£¢¦¢¢¢¥¤¤¤¢¡ œ™•””‰~qfYTKMLPUTSQQPNNMV_q†“¤¦¦¥¦—‰yhaYcjw‡‹’˜œ £¤¡ŸžžŸ¡¤©¨¨¨¨§¨§§¦¦¦ª¨¨©«ª©ª¨«©¬«¬ª¯¬«®¯²±±±²°°°²±±²²³³³·µµµ±¬ª¨«¬®®®®ª¤¤£ª¨¨§¨¨ª¦ª¦§§§§¬®¬¬«¨¦¦¦§¦©©©©ª§ª§¨¢£¤¦§§§§¦«ª¨§§¨¥§¡ Ÿ›žžœœ¢ž¦¢¡™™–•”–•–••”—’‘‘‘Ž’ŒŒ‘‘ŠŠ†ŠŠŒ‹‹‹Š‡‰ŠŠˆ‡†‰ŠŽˆŠ‰‰‡†…„‚ƒƒ…ƒ‚‚ƒ„‚‚‚ƒ‚„…†‚€……ˆ‰‡‡†‡‡†…„ƒ‚ƒ…‡Šˆ‡†‡ˆˆ€‚„‚„ƒƒƒƒ‚€zz{„Ž’“”––•••““ŽŽŽ’‘“““ŽŠ‰„‡ˆŒŒŒŒŽŠŠŠˆƒ‚„Š‹‹ŠŽ‘ŠŠ‡‡†††Œ‹Š‰‹‡Š‰ŽŽŒ‰„ˆŒŒŒ‹Š‰‰Ž‰‰‰Š‰Œ‹„~ƒ………„‚€†…††‡‡‰ˆŠ„„„…„†‡Œ…„~~ƒ…„…††ˆ‰Šˆˆ‡‡„ˆ‡‰‰‹Œ‘‘•–ƒ}z˜ž¢§¦¥¤¤¢ŸŸ£¡ ¡ž¡˜•“›™™••–”ŽŽˆ‚z}|yutrn^[Zadfghhlowqqplgfddddcb`ŽŒŒ‰Š‰ˆˆŠ‡ˆŠ‘‘’Ž’“ŒŠŠŽ‘““’”““”•–˜•›˜™™›š Ÿ¡žžŸœ›¡Ÿ££«¤§ž˜˜ššž¡¤¥¥¤¤££¢£££¡£¢¤£¦¨©¥©£¨¢¢ Ÿ™–––Š…ui\PLJJNQYWXROMLMP]j{”š¨¦«©©m^YXbr}‘””š™ŸŸ¥¥¥ žžž £¦«¨¬¨¦§¬©ªª«©©ª¨¨©¨«©°¬®¬°¬«°²¸±±±²±´°µ²²²³³´²·³³±¯««ª±®¯®´¬§¤¤¥«¨§¦¨¨©§«§«§¨¦®¬°ª¦£¦¤¦©¨®©©ªª©¯¨§¤ªª«©¨©ª¯«¬¨¯ª¨¤¦ ¨¡¡›£žœœž£¡¦ ¢žž–›“–—š™™”–“”‘‘Ž’’Œ“’‰‰‡‹‰‹‹‰‰ˆ‹‹Š‰‰Œ‹‹‹‡……‡‡ˆ‰‰„†€ƒ‚‡ƒ‰…„…Š‚ƒ…ƒ‚ƒ‡…ŠŠ‡‡‡…Ї‡„ƒ…‹Š‹ŒŒ‰ˆ†ˆ†„„„ˆ……„‰†ˆ€~|‚Š˜•˜–•””––“‘‘‘“’—““ŽŒˆˆˆˆŠŒŒ‰ˆ†‚‡’Ž‘‘’‹“‘‡…‡ŒŒ‹ˆŽŽŽŽ‘ˆ……Œ‹“Œˆ‡‡‰ŽŠŠŠŒŒ‹ƒ‚‰‹’†„…††‡†††‰ŠŠ‰…„…†…‡†Œ†…ƒ†„„†‰…†‰‹Ž‡‰††„ˆ‡Š‰Œ“–Ÿ˜yfw’œ¦¥¤¤³ª¨¡¢¢¦¢¢¡¡¡¢››š›œž–••˜–•Љƒ‚€~~}xra]afffghioowwyojfefigedfb‹ŒŠ‰†…„ˆ‡‡…ˆ‰‹ŒŽŽŒŠŠŽ“’”“““•———˜˜™šœ›žžœžžžž›œ £¤£Ÿž™—–›¡¤¤¥¥¤¥£¢ ¡¡¡ ¥¢¤¤¥£¢¢ ›››š•Ž‹vi\VKKJNQSWWUSNMKQVgu†™ž¦¥¤¡œ†seUZ[hvŒ‘–••ššžŸŸŸŸœžž¡¢¥¦¨¨©§§¨¬««¨ª¤¤¥¨§¨ªª§¥¤¨¨©ª¬¬¬«ª¬®°±²¯±°±°°µ²²±³°²²³¯®ª«©«ª°®¬©§¦¥¤¤¤¦§§¦§§©¨©¦¦¥¦¦§¦¦¥¤ ¢¥¥¤©«ª¨§§¨¨¨¤¤£¦¦ª¥¥¥©°®¬¬©«¬¥¢¢ ¡ž››››œž ¢¡ œ›˜˜˜——›“””™—–”–“”ޑދŒ‹‰Š†‰‰‹‹‹ˆˆ†‹‹‹‹Œ‹ŒŒŒ‹ŒŒŠ……„†…†‡ˆ„„‚‚‚‚‚‚ƒƒ„†‡‡†‰‚€…ƒ„„…ƒˆ‰†‚„…Ї…„…‡†…„„ƒ‚‰†‡ˆ†„…‡ˆ„…†‰„|~}„‹’“”˜”–’”–“’’‘““”’“ŽŒ†‰ˆ‹Œ‰‰‡‰ˆˆ‚€~…ŠŽŒŒ‹ŒŒ‹‘ˆˆˆ‹ˆŽ‹ŒŽŒŠˆ‚„…ŒŠ‹‡†„‡‡ŽŠŠŠŽ‰‡†‰Š‹ˆ‡‡‡ˆ‰‰ˆ‡‡‰ŒŠ†‡††„„ƒ‡†ƒ‚…‚‚‚„…††ˆŒŠ‡†‚‚‚…„††‡ˆŒŒ”˜™™‘qtz•› ž£¢ª©§¤¤¢¥£¢œœœš™˜›——–”“–™–“‘Šˆˆ‡†…ƒreiolgghjkoovwyngbefgggedbŽŽŽ‰‡†„…‰ˆˆ‰‰‰ŒŽŽŒŒŽ’’“‘“““”˜—š›››¢›žžžŸŸŸ¥¥¤ž£žŸ¡¦ ¢¡¡žœš™›£Ÿ§¦¦§ª¦©£¢¡¢¡¢¢¬¤§£¥¤©£¤ ™›››Š„sg^TPKIIMVXXXYTRMKT\n†’¤¤¬§§•zjaUY`k{…‘“˜šš››šŸŸžžž¢Ÿ¨¤©¥§¨ª§§¨®¬«¨©¤§¦¨©¨¨¨¦¥¤¦¨««³®¯ª¬¬±°°±³°±°·±º±µ²´²´²¶±²«®®«±®®§¦¦§¥£¤¦§¨¨¨§ª¨§¦¤ª©§¥ª¡žž¡¤©§««ª§¦¦©¨¨§¦¤§§ª¥¨¥¨¬¬¦¤¤¤££Ÿ¢¡ ž¢¡§§§ ž›››››››œ””•˜˜–––“•Ž‘ŽŒ‘‹Š‰Š‹Œ‰Ž‘‹‹‹‹‹ŒŽŒ’Œ‹ŽŠ‰…‡‡†‡‡‰Œ„‡†††‡†…†‹‹ŠˆŒ‡ˆ‚€€†ƒ……ˆ‡ˆ„„„…‹…„„ˆ‰†…ƒƒ‚Љ‰‡ŽŠˆ‡†Š€~€…‘˜••”™––’”“‘‘˜’“’’’””˜’“ŽŒŠ‰‹Ž‰‰‰Š‰‰€~‚‹‰“‹‰‰Œ‘‰‹ŠŽŽŒŒ‹ŠŽ‘‰…‚„ˆŠ‹‡‡‡ˆŠŽ’ˆ‡‡Š‹‰‹ˆŠŠ‹‹Š‹Œ†ƒ†‰…ƒ‚ˆ„ƒƒ……‹ˆ‡†‹‹ŒŠˆ‡†‚„‚‡…‹‹ŒŒ’’¢›’Œ†‰˜š¤£¤¢ª©¬ªªª«¥£ žœœ ››˜™–•“–—™––‘‘’Ž‹‹‚vtxwvljlooorxxzldaehnjmhee‰ˆ‡‡ˆˆˆˆ†ˆ‰Š‹ŒŒŠ‹‹‹Œ‘‘‘‘”•——šš››››žžž ¡ ¢ž Ÿ ¡¡ Ÿ šžŸŸ¢Ÿ¡¡¤¦¥££¡£¢ž¡£¥¢¢¡¡¡ ŸŸœš™‘ŽtgZTKPLMMOQTVWWTRRPZdw‰—¥¤¤¢¡~qab[bis€ˆ”˜œœšœ˜›œœŸŸ¢¢¤¤§§ª§§¨¨¨¨¨§¤§§¨¥¦¥¨¥¥¦¨©««¬«¨¬¬°¯®¬³¯°°±²±±²³µ±²±±°¨¬«®«¨¨¨§¥¥¦§§¤¢¦¤¥¥§§§¨§§§¢£¢¡ ››œ¢¤§¦¦¦§¦¦¦¤¢¢ž¡¡¦¦ª£¥¥¨«¬§¨§¦¢£¡ ŸžŸ Ÿ£¤¤£¤›š˜™˜™˜™™›”––˜˜•”““ŽŽŽŠ‘‹‹ˆ‰‰‹Š‹‰ŒŠˆ‹‹‹‹Š‰‘‹ˆ‡††„‡‡‡‰Š‹ˆƒ†„……ˆˆ‰‰ˆ‡‡‡Œ†‡ƒ„…†‡‡‡……„……„ƒƒ„‡†ˆ†…‚‚‚„ƒ…‡Š‹Š‰ˆ†……z|…Š“—••”™—•’‘‘“’‘’’”‘ŽŽŽŒ‰ŒŽŽŒŒ‰‹Œ‰„~‚‡ŠŠ‰‰‡…‰Œ’’Œ‹‰Š‹Š‹Œ‹‹‰ŠŠ‡‡†…‚†‹‹ˆ‡„ˆˆŠŽ‹Œ‘І‡†ˆˆŠ‰Šˆ‹‹‹‹ŠŠ…………†‰…„‚‚‚‚…„Љ‡†Š‹Œ‰ˆˆ†ƒ‚ƒ„‰‰‹Œ’”Ÿ ›—––˜šŸŸŸ¢¥©ª©©©¥¥¤¡žžœ››œ˜––•’““•—•”’‘‘‹…z{~xonnooquvvqid`ciiijjigˆ‰‡ŒŠŠŠ‹Œ’ŽŽŒŽ‘–Ž‘’”™–›™ž›œž›¤Ÿ ¥¤¤ ¢¢£¤¤¢£ Ÿž ¢¥Ÿ¨£¢¢¢¢£¦¦¢££¤ ¢ ¥¥¥¤ª¡¢¡¡ š™Œ‡vk]PJFJNPQSSSTUUSQRUam™œ©¦ Ÿ„sk`fkox…Ž˜™œœ£Ÿ¡œœœœ ž£Ÿ¢¢§¦®§¦§¨¨¨©¬««§©¤¥¤§¦¨¨®«««¬¬¬«²¯µ¯¯´¯°°³±µ±²³»°´°°®¬¬¬®«ªªª§§§¨§¨¨¤§¤ª¨«§ª¨°§¨¢ª¢£Ÿž¡£¬¨«©«§ªª«¤¢£¤££¤§¦«¦¦¥¨¨«¦©¨§¢¤ žœ¡ ©¤¤£«¬¬¢£š›—ž™™™œ™š›œ˜œ—•–—‘“’—ŽŽŽ‹ŽŽŽŒ‘Œ‹Š‹ŒŠŠ‹‘ŒŠ‡‡‡††‰‡ŠˆŽ‰†ƒ‹‡ˆ†ŒŒˆˆ‡‹…†‚ƒ„Š‰ŠŠŠ††‡‡‡‹…„‚ƒ…Іˆ‡‡„††‡ƒ……†ˆ‹‰Š„‚~zx|ƒŽ•”˜——•˜˜›”•’–’‘‘˜’•ŽŽ‘‘“““‹‰ƒƒŠˆŠ‡……†‰‹’šŒŽŽ‹Ž‹‹‰ˆˆˆ………‡‰Šˆ‡‡‹ŠŒ”Œ‘Œ‰‡ˆ‡‹ˆ‹Š‘“ŽŒ‘‘‘‹‰ˆ‡†…†Š……ƒ„„„„‰‡‹‹‹‹ŠŠŒ‰‰†‡†ˆ‚‚ƒ‰‰Œ“•žŸª¢¦¤£¤¤Ÿžžžž¡¤ªª««¯¨©¨¨¡¡žœœšŸ˜š”•”“”““——˜˜™‘‹‰†…~~ttttqsx„unii`bflhhimgˆ‡‡†ˆ‰‹Š‹‹ŽŽŒ‹ŠŒŒŒŽŽ‘’“”—˜™šœœœœœ›žŸŸ£¡¢ ¡¡¡¡¢Ÿžž ¡Ÿ¢¤£¢¢¢£§£¡ ŸŸ¡¡£¢£££ Ÿœ™˜”ƒxj]UIGEJRRSQPRSSSRRVZiwˆš £¤˜|ifamx‚ŒŽ’–š›››žžžœ›šœžŸŸž¢¢¥¦¬ª¦§¨¨¨¨§ª«§£¤¡¥¥¦¦§¨«ª«©©§¬ª«««¬®®³±±±³³²®¬«¬¬¬¬¬¬®ª©¨©¥¥¤¤¤¤£¢¢¥¤§§«§©©©¨¨¢¢žžœŸ ¤¥¦¦¦§¨¥¥¥¥¡¢ž Ÿ¤§¨§«¥¥¥¦¥¥¢¢¢£¡¡ž ©¤¥¥ª£¢ ˜™˜œ™˜—™™˜——˜–”“‘—ŽŽ’’’‹‹‹‘ŽŒŒŒŒŒ‹‹ˆˆˆ‰‹‹‹ˆ…ƒ††ˆ‡‰ˆ‡…„ƒ††‡‡Œ‹ŒŒˆˆ‡ˆ†…ƒƒ…ˆˆŠ†„ƒ„…‡†…‚‚ƒ…‡„ƒƒƒƒ„~€„„„„„„„|wxx‡Ž‘””’˜”–˜˜•”‘’’““’‘‘ŽŽŽ‹ŒŽŽŽŽŽ‹‰‡€„†‰ˆ††„†ˆŠ‹’“Œ‡ŠŒ‹ŠŽŒ‹‡‡…„„ˆ‹Š‰‰ˆˆˆ‹‹‹ŒŒŒŒ‹‰‰ˆˆ†††‹ŠŒŒŠŒŒˆˆˆˆˆ‡…‡…„ƒ„‚…†‡††‡ˆ…‡ˆ‰‡…………†‚ƒ†‡‹Œ“–ŸŸ£¢¥££ ¡ Ÿ¡£¦¥¥¥¦§§§§¢¢ Ÿ›šš ˜˜••–•“”““““‘ŽŠ‰‡~~utsrruyzsmfiaddddfhhhЇ‡‡Š‹‹‹‘ŽŒŒ’ŒŽŽ’’“”““—˜˜˜œ›ŸŸŸœŸŸŸŸŸ¡¤£§¡Ÿ £¢¢¢¢¡¨¡¡¡¢ ¢£¤¢§£¤¢ ¡¡¡¡Ÿ£¢¥¥¥¤£ž ™˜ŽŒyo`RLEEFIQV[TQRVVWQQYap„’¨¨©£¦‘„teedožŸŸ ¡¢¡§› œ›››¡ ¡Ÿ£žœŸ¡¤ª¨¬®©¬«°©¨§ª§¦¤§¥«««¦¯¨¬ª°ª©¨«¬®ª±²³¯²±®³²²²³°¯«©ª®¯®°®¯°°¨ª¥¥¥¦¥¨¤££¤¥«ª¬¬«ª¢£Ÿ £¤¦§¨¨§«¨«¥§¡£¢¢¡¨ª¨«§«§¨¦§££¤ª¢¢¡¡£¥£ª§«ª«¢§žœš—¡™˜–›¡—”“”‘—Ž‘˜’””“‘‘‘“““‘ŽŒ‘‹Š‹Œ’‹Šˆ„‚‡†‹ˆ‰…„„„„‡‡ŒŒŒŒŒŒ‹Š‹Š‰‰‹…„€‚Œ†„ƒƒƒ††‰„†„ƒƒƒ€‚‰………ˆƒ„}ywy~‡Œ•‘“’˜”•–˜–—“•’›–—‘Ž’Ž”‹Œ”Šˆ††Œ‰‰‰…‰‰ˆ‰‘”މЖ“Œˆ‰…„†ŽŽŽ‰‰ˆŒŠŽŒŒŒ“ŒŠ‰ˆˆŠ…‡‡Œ‹‘ŒŽŽŒˆŠŠ‘ŒŒ†‹†„„…ƒŠ‡ˆˆˆ‡‹‰‰ŠŒ†……ˆ‡‡‡†‡ˆ†‹™™ ¦¢¦¥§¨©¥§¥¥¥¤¢¨¥¦¤©¨¨©©¥ª¢£ž››¡›œ›š••“““‘‘““”‰‰w|zyuxxxqliiiiffeeglh‹ˆˆˆŠ‹ŒŒŽŽ‹ŽŽ‹Ž’Œ‹ŠŒ‘‘’“””–•˜™œœŸ›œœœœŸŸ ¡¤¤£¢Ÿžžœž£¢£ £££££¡ Ÿ Ÿ ¡¢ š—”’†~n`VJGEHIMQUZXSRRQPQS^fx—¤¥¦ œŒ{qehl{Œ“Ÿ£¥§¨¦¤¤¡¡œœšŸŸŸ¡¡¡Ÿžž ¢¥¦¨¨¨¥¨¨«¬¬ª¨§§£¤£¦¦§§§¦§¨¨¦ª§©«¬¬©°±±®®¯®³±±±¯¯¦ªª¬¬¬¬«¥¥¢£¤£¢¢¡£¤¥¦¬¤¤¥¤£££¢Ÿž£¥§¨¦§¨¨¦¨§¨¦§¢¢¡¢£ªª©§§¦©¨©¦¦££¦¦¢¡¡£¤¥£¦¦¨¥¤ ž™˜——˜˜˜—˜˜š›™•’‘‘‘’“’’’‘‘“”’‘’ŒŒŽ‰ŒŒŽ‹ŠŒŒ‹‹Žˆ‰ƒ‚‚†„……ƒ~‚„……ŽŒŒŒŠŠ‰ŠŠŒ‹‰‹ˆ‡‡†„„‚‚~ƒ…†…„„„ƒ‚‚‚‚‚……ƒ‚‚ƒ€}xxx†‘‘‘’’”“””—–—““’–”‘ŒŒŽ‹ŠŠˆŠ‹ŒŒ‹ˆˆ‰‰‰‰‡‰†‡‡‰‹‘ދЉŒ’‘’Љ†„‚…‡‰†…„†…ˆŠŒ‹‰‹ŒŒ‹‰ˆˆ†‡†‡‡Œ‹ŒŽ‹Œˆˆ‡‹Š†‰ˆ†‚ƒƒ†…ˆ‰ˆ„‰ˆŠ„‹†…„„„…„‡Š‡‚Œ—™œœ¡¢£¢¥ž§¢¦£¤ ¢ ¡¡ ŸŸ¢££¢£££¡ž›ššœš™–˜˜—”‘‹xƒˆˆˆ…€~w{yxvyxqklljhhieacefg‹‹‹‹‹Ž’ŒŽŠ‘ŽŒŽ’‹‰‰‹“”™™˜——˜œ¢žŸ¡œŸ¢Ÿ£¢§£¥¢¡¡ Ÿ ›£¡¦¢¢¡¡ ¤¢¤¤¨££ ¡¡¦¦§§¨¢¥¤¤Ÿ šš•~seWNDEGIMQUX\YWSVONRWdr€•Ÿ¨«œ–†yqhlt˜›¤§¬¬±©«§ª¡£žœ££¥¥¥¦¥¥¥¦¨§ª¦¨©©§¬¨²¬¬«ª§§¥¤¥¦§ª©©§¬©«§¯©«¯¬©®±¶µµ±°³±´®®®°«²¯¯®°«ªª«¥¥¤ª££¡¥¤£¤¦§¬¤¦¤¤£©¡£ ¡¤««¬ª®¦ª©ªª¬©ª¦¦¥££¥§¬ªª§ª©®¨©¦§¥¤¥§¡¡¡©¥¦¥©¨®¤¢žœ˜žœœš›š›¡˜›™—’’•“˜—–––’“’’’™–—’“ŽŒ‘Œ‹ŒŒ‹ˆ‰ƒ‚ƒ‡††ƒ€€‡‡ŠŠ‘Œ‰Šˆ‘ŽŽ‹†‡„……„…†‡‡‚€€ˆ†Š…‡ƒƒƒ‚ƒ„„†„„ƒ‚ƒ~{{{ˆŽ—‘“’’–’™˜˜”œ˜—–š•˜“Ž”’‘Ž‘‹‹ŠŒ‹‹‘“’“ŠŒ‰Ž‡‡ˆŒŒ’ŽŒ‹‘”””‘Œ‹…„…‹Œ…†…ˆ†‰‹ŒŒŒŒŒŒŒŒˆˆˆŽŽŒŽŽŒ‹‹ˆŠŠˆ†ŒŒ†‰ˆˆ‡Š‰Ž‡‰ˆ‹‹‹…†„„„†‡ˆ‡†”œœ›¢¢£¤¥¦§¥¥¤¥¤¦¢ ¤ŸœŸ¡¦¢£¤¨¢¤Ÿ›Ÿž¡œžœ¥žž——Žrq|Šˆˆ„~|{{||€upillomslfdcchi‰‰Š‹’‹Š‰‹Š‹ŒŽŒŒŠŠŠ“•™—˜˜ššœœžžžŸ Ÿ¡¡¡Ÿžžžœ ›¡ ¡Ÿ££¤¡¢¡¡¡£¡£ž¡¡¥¢¡Ÿžž›™•ˆrf\OKEHJORUY[][YTQPMU[k{Œ £¦¤¥—‹€tspy‰•›¡¥ª¯¯¨§¤£ ŸžžŸ£¦©©¦¥¤§¨©¨¨¨¨¥¨¨©§¨§¦££¤¥¥¦¨¨¨¦£¥¥«©©§ªª¯¯««ª®®²²´°°±®©©©¬«¬¬¯°¯ª«¦¤£££¤¢¢¡£¢¤¦§§§¤¥¥¥¢¡ž¢Ÿ¦¬«©ª«¯§ª©ª©©¦¦¤¦¦¦¥¨ª«¦¦¦©©©¨§¦§©¥¥¤¢¢£¨¦¥£¨¦¦¢ ˜šœ›››š™—–”“’””””’‘ŽŽŽ’’“”š–”Š‹ŠŒŽ‹Œ‰‹‹Š‹ˆ‰ƒ„…ˆ†€|‚‡ˆ‰ŒŒŽŠŒ‰‰‰ŒŒŠŠ‰Š…ƒ„…„‚ƒ~€„†„‚ƒƒƒ€ƒ…††…ƒ„‚}zz{€…Œ‘‘’’‘‘”’”––•›˜˜˜™••‘‹”•“ŒŒŠ‹‹Œ‘’‘ŠŒ‰‰ˆˆ‰Šˆ‰Œ‹ŽŽŒŒ…†‡‹‹„……†„Š‹‹‹‹‡ŒŠ‹ŒŒ‡ˆ‡ˆ‰‰‰Œ‹ŒŒ†‰‰‰‰‹†ˆˆˆ†Ž‹‹‡ˆˆ‰‡‰Š‰†‰‡‹Š‹„ƒ‚‚‚‡‰‹…‡‡—œœ››¡¡£ ¡¡ ¥££¢££¡ž ŸžŸœž¤¡¢¢¡ Ÿœœ›œšŸžžž—˜{k‚Љ‰†‚z||}€~qpiikkknmkhfaab‰‰ŽŽŽŠˆ‰Œ‹Œ’‹‹Œ“”™™š˜š››œŸž¡§¤¤¢¡Ÿ¡ žž žžŸ Ÿ£¡¤¤ª¢¦¢¨¤¤¤£¤¦¢¤¢£ Ÿœœ‘~wh[TKGEIOSYZ[]_][SPOPYbsˆ“¦¢¬¥¦’†~usv~”œ¢¢¨©¯¯¯§¥£¡ŸŸŸ¥¢¨¨®ªª¨©ª¯³«©ªª«¬¬¬¦££§¤§¥©¨ª¦¥¤§§ª©«ªª±°µ«®¯®¯²²´µ¸°²¬¬¬¬±³°±²¯©¬¦¤¢¥¦¦¥¥¢¥¥¦¨ª©©©ª¨¦Ÿž £¦±«®¬¬«²¨ª©©©¥¦¨¬¨«§¬ª¯¤§§¯©ª§¦¥¥¤¥¥¥¥¦§¯§ª©¨¦§¡ Ÿžžž¢¡››™—’•”””œ——‘ŽŽ•–Ÿ”š•”ŒŒŽ‘š’‹ŽŽŽŒ‹‹‹Š‡Ž†‰€†€ˆŠ“‘Œ‹ŒŒŠŠŒ‰Š„‡†‡„„€}}‚„„ƒˆ‚‚‚…„‡‡‰†ˆ‚||{{€‡ˆ’‘“™‘–’˜••–™™š˜™šŸ™œ’“““’‘“™——’•ŒŽ‹›•™‘’Œˆˆ‘Ž’Ž•‘‘’ŒŽ‰ˆŠ’ŒŠƒ†‡ŒŽ‘Œ‹Œ‹Œ‹‹‹Œ‰Šˆ‰‹ŒŒ‹ŽŒŒŒ‡‡‡‰‰Œˆ‡‡Œ‰“ŒŽŽŽˆˆ‡Š‡ŒŠŒ…ƒ‚†„ˆˆŠˆŒ’˜¡œœ› ¡¤¡£££¢¥¤¦¦¦¦¦¡¡¡¢ž›¤¤¤¡©¤«£¢œ›œ¡ž¤žŸš™’‡†ˆŒ††|~~|ophfhjlomronc`_‰Š‹‹‹‹Žˆ‰Š‹‡‰‹ŒŒŽ‘”––™™™—››››œœžœ¤ ¡¢ŸŸœšš›Ÿœ›žŸŸ£ ¤¡¢ž Ÿ¢¤¤¤¤¤§ ¡¢¡Ÿ˜——‹sf]QNKJHLPTZZZ[]\ZTNQS`k|˜¢¡¢ {ry}†“˜œœœŸ ¢£§¨©¥¤¢¡ ££¥¦§§©ª«¯®¯©«©¨££¢¢¢§¤¦§ª¢¤¤§§©¥§©ª§¬¬¬°±²²²²²°«¬ª®±««ªª«ª§¨§§££¡¤¥¥¥¤¢¥¦©«ªª§¦£žž›Ÿ¡¨«««««ªª±¨ªªª§§¥¨«¬§¨§¬©¨¤§¨¨©¨¦¥£¤¤¤¢¥¥§¨°§§¦¥¢¡ ŸžœžžŸ › —–’’“‘””—•’ŠŽ“”••“’ދޑ“”‹‡‡ˆŠˆ‰‡Œˆ‰…†…އ„€ƒƒ„†‰‹ŽŽŽ‹Œ‹‹‹‹‰‰‹‡‰ˆ‡ƒ‚†‡†…‚~~|}~ƒ……„„‚‚‚†„„…„……‚}xxv~…ˆŠŽ‘‘’—•–—˜”–—————–“’‘’’“”•–˜ŒŒŒŒ“”“ŽŒŒ‡ŒŽŠ‘ŒŽŽŽ‘ŒŒŒ‰‡„ˆ‰ŒŒ‹ŒˆŠ‹Œ‹‰ƒ‹‰‰ˆ‹ŒŒŒŠ‹Œˆ‰ˆ‡‡ŠŠ‰†‰‰ŽŽŒ‰Ž‰ˆ„‡‡ˆ†ˆˆ‡„ƒ€…„‰‡‡ˆ––™›šœœ›œž™¡¢¢§§¨©¦£¢¡¡žŸŸšœ £¡¢£¢žœœ›žžž›š•ŠŒŽ‘Œˆˆ‚€‚}uljee`klmmqsqfb^‹‹ŠŽŠŒŒ‰‰Œ’‘‘‘”˜˜žšŸ™š™œœ ¡¢¢¢£ ¢žœš™šœŸ¡ž£¢£££¤¥¥¦¢Ÿ£¤¨¨¨¦¨¡¡ žžž””„yj]TKKJJMPWV[[\\\YURPTYgu†™ž«¤¨›™Œ‚~w{‡—›¤ ¡¡¢¨¨«¥§¢¡¡¥¢¨¨¦¥¥¨¯°·±µ°²¯µ´µ®µ°©©¤§¨©©¨¦©§«¥§¨¯¨¨¦¦§ª©®®®¯´°·±µ´´±°¯¬¬³±³¯³ª¬¨©¦«§¨¢¦¤¤¥©¥©¨©ª±°©¨¢ ¡¢¤§³²²ªªª««°«®¬¬©«ª¬¬¬©¬©§¦§¨¨¬¦¦¥¨§§¦¦¥«¨²§¨¤¤£¢ ¤¡ Ÿ¢œ›ž–—‘““—•˜™š•’’˜–š•’‘ŒŽ™˜˜‰††ˆˆŒ‡ŽŒŽŽ‰‰‡ˆ…„‰ŠŒ‹‘‘’ŽŽŒŒ‹ŒŒŒŠˆ‡‹‡Žˆ‡‚†„……„‚‚}}„…‰†ˆ…Š…†…‡†…„…{wx}†„Œ‘”’“”˜—™˜™•————–•‘’’”“›—˜™™’’ŒŽ’Ž‘‘’‘““–Ž’‹‹‘‘‘”““’’•ŽŒ‹‰‡†Œ‰ŽŽŠŒ‡Œ–Šˆ…ЉŒ’Ž’‰ŽŒŠŒˆˆŠŽŽŒŽ‘ŽŠŽ‰ˆ‡Œ‰‹†‡‡‡‡‡ƒ†„‰‰‡‹“•™šž¡Ÿœ ˜›Ÿ¤¡©¦£§ª¤¥£§¡¢¢¢¡ œ Ÿ£¢¦¤©¢£œŸ››™•••“™‘ŒŒ‘ˆ‡ƒ…xpjhedekklnvssjfe‹‹‹Œ‰‰‰ŒŒŠ‹’“—˜˜™˜——˜—›œ ™›š›œ¡žžžšššššœžžŸžŸ ¢¡£Ÿ žžžž¢Ÿ ¡£¢£££¡Ÿ›˜–†{nbVQIKLORUWVWYYXXUSRPZaqŽ¡ ž˜Š…Š‘˜›œœœ›š™Ÿ¢£¤¤¦¤£¡ ŸŸžžŸ§ª°²´¯°°±°³²´®¬¦¤¡§©©§§¥¥¥¥¥§¨©¦¨¦¦¦¨©¯¯¯¯°®¶±µ´±®®¬¬®®¯¯¯¬ª©¨¥¥¦¦¤£¢¤£¥¥¥¦¦¦©¬¬ª©£¢žž¢¤¨«¯±±ªª¦¨©«¨ª©©§ª©«¨¨¨«©¨¤§¨¨¦¦§¨§¦¤¨¦¦¥¥¦©¨©¤¢ ž ž¤¢¢¡¡ ¡šš™ž–•’“”••™™š”’“˜–˜’“ŒŽŽŒŽŽŒ‹ƒ}€ƒ‡‰‹ˆŒŒŽŒŠ‰‡‡‡…„ŠŒŒŒ‘ŒŒ‹Šˆ‡†‰‹Œ‰Œ‰ˆ‡ˆ‡Šˆˆ‚‚€…ƒ‚‚‚‚€ƒ„††„…„‡„……………ƒyzv|†…†ˆ‘’“‘••˜™™˜˜”•——“”•”‘’”•˜––“’’’ŽŽ‘ŽŒ‘’’”Ž‹‹‹ŽŒ‘‘“‰‹‡‡‡ŠŠ‘Љˆ‹‹ŽŠˆ…ˆˆŒŒŽŽ‹‰‹Š‹ˆˆ‰Š‹Ž‹Œ‹‹ŽˆŽ†ˆˆ‰‰‹†‡ƒƒ‚‚‚ƒ‚‚‚‡Œ“•™˜žžŸ›žœœšš—œž¡ –¢¤¤¤¢¢ ¢¢¡Ÿ ›››ŸŸ ¢¡¢ ž™››ž˜’”“”‘‘ˆ†„{pmgfdgjlmmpppmjhg \ No newline at end of file diff --git a/tutorial/tracking/model-based/keypoint/teabox.ppm b/tutorial/tracking/model-based/keypoint/teabox.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b20630a2d581c73b63e358e21c3892c4dbc9897f --- /dev/null +++ b/tutorial/tracking/model-based/keypoint/teabox.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +320 240 +255 +GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^NNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVVNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEENNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNEEE============EEEEEEEEEEEEGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^VVVNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEGGGGGGGGGEEEEEENNNNNNNNNNNNNNNNNNNNNNNNNNNEEE===333333333333333333333333333333333333===EEEGGGVVVVVVNNNEEEEEEGGGGGGNNNVVVNNNNNNVVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^NNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEENNNNNNNNNVVVVVVNNNVVVNNNGGG333===EEEGGGGGGEEE===333---""""""---===GGGVVVNNN333333===333EEEVVVNNNEEEVVV^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^VVVNNNNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVNNNNNNVVVVVVNNNNNNEEEEEEGGGNNNNNNNNNNNNNNNEEE333"""---EEENNNGGG333------333===VVVVVVGGGGGGVVVfff^^^fff^^^fffffffffffffff^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEGGGEEENNNVVVVVVVVVNNNVVVVVVNNNGGGVVVVVVVVVVVVVVVVVVVVVGGG333""""""333EEE333"""---"""---===VVV^^^VVVGGGVVVffffff^^^fffffffffffffffffffffNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^fff^^^fffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fffffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEVVVNNNVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNGGG===---"""""""""""""""---EEEVVVNNNGGGEEE^^^^^^ffffffffffffffffffffffff^^^NNNVVVNNNVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^fff^^^^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVNNNNNNGGGNNNVVVVVVNNNNNN===---""""""""""""---===EEE======NNN^^^ffffffffffffffffffffffffppp^^^NNNVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffffffffff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEVVVVVVVVVVVVVVVVVVNNNEEEEEEEEEGGGGGGEEE---""""""333---""""""333---""""""---333EEEGGGNNNfffffffffffffffffffffffffffpppffffffNNNNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^fff^^^ffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^fff^^^ffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEVVVVVV^^^VVV^^^VVVNNNEEE===333===333""""""---333---===333===---"""------""""""333EEEGGGVVV^^^fffffffffffffffffffffpppfffpppfffppp^^^NNNNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEE^^^VVVVVV^^^VVV^^^VVVEEE333------"""""""""""""""""""""---EEEEEE======GGGVVV^^^ffffffffffffpppfffpppfffpppffffffGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fffffffffffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffff^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^^^^^^^VVVVVVVVVEEE333---"""""""""""""""333333---""""""333NNN^^^ffffffpppfffpppfffppppppppp^^^NNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEVVV^^^VVV^^^^^^VVVVVVGGG===---"""""""""---""""""333EEE333"""---VVVpppfffpppfffppppppfffpppffffffGGGNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffff^^^fff^^^^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEEEEEEE^^^VVV^^^^^^^^^^^^VVVVVVGGG===""""""""""""EEEEEE---"""333EEEGGG---"""333^^^fffpppfffppppppppppppfffppp^^^GGGNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^^^^^^^VVVNNN===------======"""""""""---===VVVGGG------======"""GGGfffpppfffpppfffpppppppppppppppfffGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^^^^^^^^^^^^^fff^^^^^^VVVVVV==="""333GGGGGG===333333EEEVVV^^^GGG---333---^^^pppfffpppppppppfffpppppppppffffffGGGGGGVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffpppfffffffffppppppfffpppfffpppffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffff^^^fffffffffffffff^^^^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGEEEGGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^NNN---"""======EEE======EEENNN^^^^^^===GGGfffppppppfffppppppppppppppppppppp^^^GGGGGGNNNVVVVVVVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffffffff^^^fff^^^ffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffffffffpppfffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fffffffff^^^^^^^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^VVVEEE333333======EEEEEEGGGVVV^^^^^^333333^^^pppppppppppppppppppppppppppppppppfffEEEGGGNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^ffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffffffffffpppfffffffffpppffffffffffffpppfffpppfffppppppfffpppfffpppffffffffffffffffffffffffffffff^^^ffffffpppffffffpppffffffffffffffffffffffffpppffffffffffff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEGGGGGGGGGGGGGGG^^^fff^^^ffffffffffff^^^fff^^^fff^^^VVVGGGEEE======EEEEEEGGGVVVVVV^^^NNNEEE"""===---"""NNNppppppfffpppppppppppppppppppppppppppfffEEEGGGNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^fff^^^ffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^fffffffff^^^^^^ffffffffffff^^^fffffffffffffffpppfffpppfffpppfffffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffffffffffpppfffpppffffffffffffffffffffffffpppffffffpppfffffffffffffff^^^^^^ffffffffffff^^^fffffffff^^^^^^^^^^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGfffffffff^^^fff^^^fffffffffffffffffffff^^^VVVNNNGGGGGGNNNVVV^^^^^^fff^^^^^^GGG---333VVVNNNEEE===EEENNNpppfffppppppppppppppppppppppppppppppwwwfffEEEEEENNNNNNNNNVVVNNNVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffffffffffffffffffffff^^^fffffffff^^^fff^^^fffffffffffffffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffpppfffpppffffffpppffffffffffffpppfffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffffffffffffffffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGG^^^fffffffffffffffffffff^^^fffffffffffffff^^^fff^^^^^^fff^^^fffffffffffffff^^^NNNGGGVVVffffff^^^^^^ffffffppppppppppppppppppppppppppppppwwwwwwwwwfffEEEEEEGGGNNNVVVNNNVVVNNNVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^fff^^^fff^^^^^^fff^^^^^^fffffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffff^^^ffffff^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffff^^^ffffffppppppppppppppppppppppppppppppppppppppppppwwwpppwwwpppwwwfffEEEEEENNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffffffffff^^^ffffff^^^fff^^^fff^^^ffffffffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEfffffffffVVV^^^ffffffffffffffffffpppfffpppffffffpppfffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwppp======GGGNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppffffffpppfffffffffffffffppppppfffpppffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffff^^^ffffff^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffGGGNNNffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppfffpppfffpppfffVVVVVVfffpppppppppwwwppppppwwwpppwwwwwwpppwwwwwwwwwpppfff======GGGNNNNNNNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^fff^^^ffffff^^^ffffff^^^fff^^^^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffpppffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffppppppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffffffffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffff^^^ffffff^^^^^^fff^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffppp^^^===VVVfff^^^^^^^^^^^^fffffffff^^^^^^^^^fff^^^ffffff^^^^^^fffffffffpppffffffffffffGGGGGGpppppppppwwwppppppppppppffffffwwwwwwwwwwwwwwwwwwfff======GGGGGGNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ffffff^^^fffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVNNNVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEffffff^^^===VVVVVVNNNVVVVVVNNNNNNfffVVVNNNVVVNNNGGGVVVfffNNNVVVVVVVVVNNN^^^fffVVVNNN^^^VVV===GGG^^^VVVppppppVVV^^^^^^^^^VVVpppwwwwwwwwwwwwwwwwwwfff======GGGNNNNNNNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^fff^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffppppppfffpppfffpppfffppppppfffpppfffpppfffppppppfffppppppfffpppfffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffffffffpppffffffffffffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVNNNVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEGGGEEEEEEEEEGGGGGGppppppVVV===NNNEEEVVVfff^^^GGGNNNVVVEEEVVVfff^^^EEEGGGNNNGGG^^^fffVVVEEE^^^NNNEEE^^^ffffffEEENNNVVVGGGfffVVVGGGfffpppfffpppwwwwwwwwwwwwwwwwwwwwwppp333===EEEGGGNNNNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^ffffffffffff^^^fffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppffffffffffffpppfffppppppfffffffffpppffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffpppffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEGGGEEEpppfffVVV===GGG===fffpppfffGGG^^^NNNGGGfffpppfffEEEGGGEEENNNppppppVVVEEE^^^EEEGGGpppppppppEEEGGGVVVEEE^^^EEEVVVwwwwwwwwwwwwwwwwwwwwwfff333333EEEGGGNNNNNNNNNNNNVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffff^^^fffffffff^^^ffffff^^^^^^fff^^^^^^fff^^^ffffff^^^fffffffff^^^ffffffffffffffffffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffppppppfffpppfffpppfffpppfffpppfffffffffpppfffpppfffpppfffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppffffffffffffffffff^^^fff^^^fff^^^ffffff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNVVVNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEGGGppppppNNNEEENNNGGGfffwwwNNNfffVVVVVVppppppfff===NNNEEENNNfffpppGGGGGG^^^===VVVppppppfffEEENNNNNNEEEVVVEEE^^^wwwwwwwwwwwwwwwwwwwwwppp333333GGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffpppfffpppfffpppfffppppppffffffpppfffpppfffffffffffffffpppffffffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGppppppNNNNNNfffVVVfffpppNNNwwwwww^^^pppfffVVV===VVVVVVNNNfffpppEEENNNfffGGGNNNfffppp^^^===NNNNNNGGG^^^GGG^^^pppwwwppp^^^wwwfff333333EEEGGGGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^fff^^^fff^^^ffffff^^^fff^^^fff^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffpppfffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffppppppfffpppfffppppppfffpppffffffpppffffffpppfffffffffffffffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNVVVNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEpppppp^^^fffppppppppppppffffffwwwwwwffffffNNN===^^^fffffffff^^^NNN^^^ppp^^^^^^^^^^^^NNNEEE^^^VVVVVVpppVVVVVVffffffVVV^^^wwwwwwfff---333EEEEEEGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppppppppfffpppfffpppfffpppfffpppffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^fff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNNNNVVVVVVVVVNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGpppppppppppppppppppppwwwfffpppwwwwwwpppfff^^^VVVGGGpppwwwppppppwwwpppwwwwwwppppppfffffffffppppppppppppwwwppppppppppppfffwwwwww^^^------===EEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^fff^^^^^^ffffff^^^ffffff^^^^^^fff^^^fff^^^fffffffff^^^fff^^^fff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffppppppffffffppppppfffpppfffpppfffpppfffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffpppffffffffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGppppppwwwpppwwwwwwppppppppppppwwwwwwpppfffppp^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww^^^------======EEEEEEGGGGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVNNNNNNNNNVVVNNNNNNVVVNNNVVVNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVVVVV^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffffffffpppffffffpppfffpppfffpppfffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppfffpppffffffffffffppppppffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGpppppppppppppppppppppppppppwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww’jj¼@@äú��ú��ú��ú��†††^^^""""""333======EEEEEEEEEEEEGGGGGGGGGEEEGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppffffffpppffffffpppfffpppfffpppfffpppffffffpppfffffffffpppfffpppfffffffffpppfffpppffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwú��ú��ú��ú��ú��ú��ú��††††††VVV"""---===============EEE===EEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffpppwww‘‘‘––––––†††www^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^ffffff^^^fff^^^ffffffffffffffffff^^^fff^^^ffffffffffff^^^fff^^^fffpppfffpppfffpppfffpppffffffffffffpppfffpppffffffffffffpppfffffffffppppppffffffpppffffffpppfffpppffffffpppffffffpppfffpppffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffpppffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEGGGEEEEEEEEE^^^^^^fff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffpppfffppppppppppppppppppppppppppppppwwwã´88Œccú��ú��ú��ú��wwwwwwwwwwwwwwwVVVEEEGGGGGGNNNNNNNNNGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGGGGGGGGGGNNNGGGNNNNNNNNNNNNNNNVVVVVVVVVVVV^^^^^^fffpppwww†††‘‘‘‘‘‘–––ªªª°°°¶¶¶¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇǰ°°‘‘‘ppp^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^fffffffff^^^fff^^^ffffff^^^ffffff^^^fff^^^fffffffffpppfffpppfffffffffffffffpppffffffffffffpppffffffpppfffpppfffpppfffpppfffpppffffffppppppfffpppfffpppfffpppfffffffffpppfffpppfffffffffpppfffpppfffffffffffffffpppfffpppfffffffffffffffffffff^^^ffffff^^^fff^^^ffffff^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppffffffffffffpppffffffpppppppppfffpppppppppú��ú��ú��ú��pppppppppfffppppppfffpppfffVVVVVVú��ú��^^^^^^fff^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNNNNGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGNNNNNNVVV^^^fffwww‘‘‘ ªªª°°°¶¶¶ÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇÇÇÇǰ°°†††ppp^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffpppffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffffffffpppfffpppffffffffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^VVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppfffpppppppppppppppú��ú��ú��ú��wwwppppppwwwpppppppppppppppffffffú��ú��pppfffpppfffppppppfffpppfffpppppppppfffpppfffffffffffffff^^^^^^VVV^^^VVV^^^^^^^^^fffppp††† ªªª°°°ÁÁÁÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ××××××ÎÎÎ×××ÎÎÎ××××××××××××ÎÎÎ×××ÎÎÎÎÎÎÎÎÎÎÎÎ××××××ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÇÇǰ°°†††fff^^^^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffffpppffffffpppfffpppfffppppppfffpppfffffffffpppffffffpppfffpppfffpppfffpppfffpppfffpppfffpppfffpppffffffffffffpppfffffffffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffff^^^ffffff^^^ffffff^^^ffffffffffffpppfffffffffffffffpppfffppppppfffppppppppppppú��ú��ú��ú��pppwwwpppwwwwwwwwwpppwwwppppppfffú��ú��fffpppppppppppppppppppppppppppppppppwwwpppwwwwwwpppwwwwww†††––– ªªª°°°¶¶¶ÇÇÇÎÎÎ×××ßßß×××ßßß×××ßßß×××××××××××××××××××××ßßß××××××××××××××××××××××××××××××ÎÎÎ××××××ÎÎÎ×××ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÎÎÎÁÁÁªªª^^^VVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffffffffffffffffpppfffffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffffffffpppfffpppfffpppfffppppppfffpppfffpppfffffffffpppfffffffffffffffpppfffffffffffffffffffffffffffffffff^^^ffffff^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffpppffffffppppppfffpppfffpppppppppú��ú��ú��ú��pppwwwwwwwwwwwwwwwwwwwwwpppppppppú��ú��pppppppppppppppppppppwwwpppwwwwww‘‘‘–––ªªª°°°ÁÁÁÁÁÁÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß××××××××××××××××××ÎÎÎÎÎÎÁÁÁ¶¶¶––– ÁÁÁÇÇÇÎÎÎ×××××××××××××××××××××ÎÎÎÇÇǶ¶¶ pppVVVNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^ffffffffffffffffff^^^^^^^^^^^^ffffffffffffffffffpppffffffffffffffffffpppffffffpppffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppfffffffffpppfffpppffffffpppfffpppffffffffffffffffffffffffpppffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGGGGEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEGGGEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffppppppfffppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwwwwwwwwwwwwwpppfffú��ú��pppppppppwwwwww†††‘‘‘ ªªª¶¶¶ÇÇÇÎÎÎ×××ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß×××ßßßßßßßßß×××ßßßßßßßßßßßß×××××תªª†††fffVVVNNNfffppp ¶¶¶ÎÎÎÎÎÎ××××××××××××××××××ÎÎÎÁÁÁªªªwwwVVVGGGGGGNNNNNNGGGNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppffffffpppfffpppfffffffffpppffffffpppfffffffffpppffffffffffffffffffffffffffffff^^^ffffff^^^fff^^^^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffff^^^ffffff^^^fffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppppppppppppppppppppppppppú��ú��ú��ú��wwwwwwwwwwwwwwwpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ÇÇÇ×××ßßß×××ÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎßßßßßßßßßßßßÎÎÎ×××ßßßßßßßßßßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß××××××ÇÇǶ¶¶–––pppVVVGGG===EEE^^^pppffffffppp†††°°°ÎÎÎ××××××ßßßÎÎÎÇÇÇÎÎÎÎÎÎÇÇǰ°°†††NNNEEEEEEGGGNNNGGGGGGNNNGGGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffpppffffffffffffffffffpppffffff^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEGGGEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffff^^^fffffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppú��ú��ú��ú��wwwpppwwwpppwwwwwwú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ßßßßßßçççßßß¶¶¶†††–––www‘‘‘ÎÎÎßßßßßßÇÇÇ ÁÁÁ×××çççßßßßßßçççßßßçççßßßçççßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÎÎζ¶¶ fffNNNEEE===333=========------333===NNNwww‘‘‘wwwwww†††ªªªÎÎÎ×××ßßß×××ÎÎÎÇÇÇÇÇÇÁÁÁ°°°–––NNN======GGGVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNNNNNNNVVVNNNVVVVVVNNNVVVVVVNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fff^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffpppppppppú��ú��ú��ú��wwwpppwwwpppwwwpppwwwwwwpppwwwpppú��ú��ÇÇÇççççççççççççççççççßßßßßßçççÇÇÇppppppfff‘‘‘×××çççßßßÎÎΪªª–––ÎÎÎßßßßßßßßßçççßßßçççßßßßßßçççßßßçççßßßçççßßßßßßßßßÁÁÁ wwwNNN===333333---333333333333333333==================GGG^^^www†††††††††¶¶¶××××××ßßß××××××ÎÎÎÁÁÁ°°°–––NNN===333GGG^^^ppppppfffffffff^^^^^^^^^VVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEE^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffpppfffpppfffppppppú��ú��ú��ú��pppwwwpppwwwpppwwwpppwwwwwwppppppú��ú��VVVÁÁÁçççççççççßßßçççççççççßßßßßß‘‘‘VVV^^^ffffffÁÁÁßßßçççßßßÎÎΪªª†††ÇÇÇßßßßßßßßßççç×××ÎÎÎÇÇÇÎÎÎ××××××ßßßßßßßßß¶¶¶wwwGGG333---------------===GGGNNNNNNVVVNNN^^^pppppppppfff^^^^^^^^^VVVVVVppp‘‘‘‘‘‘†††ªªªÎÎÎßßßßßßßßß×××ÎÎΰ°°ªªª–––VVV===333===NNNpppwwwwwwwwwwwwwwwpppwwwpppfffpppffffff^^^^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffppppppppppppfffú��ú��ú��ú��pppwwwpppwwwwwwpppwwwwwwpppwwwpppú��ú��===NNN–––×××ççççççççççççççççççßßß°°°^^^wwwwww†††ÎÎÎßßßçççßßßßßßÎÎÎÎÎÎÁÁÁ×××çççßßß×××ÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎ×××××ב‘‘NNN===333333333===333333333===GGGfff††††††‘‘‘–––ªªª ‘‘‘wwwpppfff^^^fffVVVVVVfffpppppp†††°°°×××ßßßßßßßßßßßß×××°°°ªªª–––GGG333333===EEE^^^wwwwwwwwwwwwwwwpppppppppfffffffff^^^^^^VVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fff^^^fff^^^fffffffff^^^fffffffffffffffffffffpppffffffpppfffffffffffffffpppfffpppfffpppfffpppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��pppwwwwwwwwwpppwwwwwwwwwú��ú��===EEEGGGwwwÎÎÎïïïçççççççççççççççÇÇÇ‘‘‘^^^pppppp–––ÇÇÇççççççßßßçççßßßÎÎΪªª°°°ßßßçççßßßßßßçççßßßçççßßßÇÇÇfffVVVGGGEEEGGGVVV^^^^^^VVV^^^NNNGGGGGGNNN^^^fff^^^fffppppppppppppfffNNNGGGNNNfffpppfffppp‘‘‘°°°×××ßßßßßßßßßßßß×××ÎÎΰ°°†††GGGGGGVVVNNNGGGNNNfff††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVNNNVVVVVVNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNVVVNNNNNNVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGGGGGGGGGGNNNGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffff^^^fffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppffffffpppfffpppfffppppppú��ú��ú��ú��ú��ú��ú��ú��ú��ú��wwwpppwwwwwwwwwwwwwwwwwwú��ú��===EEEGGGNNNfff ßßßçççççççççççççççßßßÎÎÎpppffffffNNNwwwÎÎÎßßßçççççççççßßßßßß×××ÇÇÇÎÎÎßßßçççßßßçççßßßßßßÇÇÇ‘‘‘ppp^^^VVVppp–––‘‘‘–––pppGGGNNNNNNVVVNNNVVVEEEVVVwww–––ªªª°°° www^^^GGGEEEGGG^^^^^^^^^pppppp¶¶¶××××××ÎÎÎÎÎÎÎÎÎÇÇǰ°°–––ffffffffffff^^^VVVGGGGGG^^^ppp†††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^^^^VVVVVVVVVVVVVVVVVVNNNNNNNNNNNNNNNNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNVVVNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffffffffff^^^ffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffppppppfffpppfffppppppfffpppppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGGVVVNNNVVVÎÎÎçççççççççççççççßßß×××–––VVVVVVVVVpppÁÁÁßßßßßßçççççççççßßßÎÎΪªª¶¶¶ßßßßßßçççÎÎÎ××××××ÎÎΖ––‘‘‘††††††–––°°°ÁÁÁÁÁÁfffNNNGGGEEEGGG^^^^^^GGG†††¶¶¶ÇÇÇÎÎÎÎÎÎ ppp^^^===GGGppp^^^fff‘‘‘†††wwwwww‘‘‘¶¶¶×××ÁÁÁªªª––––––‘‘‘†††pppfffNNNGGGEEE333333333EEE^^^www††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppfffffffffffffff^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNNNNVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGGGGNNNGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fff^^^ffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff===EEEGGG^^^NNNGGGVVVpppªªªßßßçççççççççççççççÇÇÇfffGGG^^^fffwwwÇÇÇççççççççççççççççççßßßÎÎΰ°°ÇÇÇßßßßßßçççççç××××××××××××ßßßßßß×××ßßß°°°†††wwwfffVVVNNNwww––– –––‘‘‘pppNNNEEE===EEENNNfffwww‘‘‘fffpppfff––– ‘‘‘pppwwwfffVVV^^^fffVVVGGG------------333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppffffffffffff^^^VVVVVVVVVVVVNNNVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNVVVVVVVVVVVVNNNVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEfff^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppwwwpppwwwwwwpppwwwwwwwwwwwwwwwwwwppp===EEENNN^^^^^^VVVVVVfff^^^†††ÎÎÎçççßßßçççççççççÎÎΖ––fff‘‘‘––– –––ÎÎÎçççßßßççççççççç×××ÎÎΪªªªªªÎÎÎççççççßßßçççßßßçççßßßçççÎÎΪªªªªª¶¶¶ÎÎΪªªwwwwwwpppVVVVVVVVVNNNGGG===EEE ªªª ªªª‘‘‘‘‘‘‘‘‘wwwpppppppppffffffpppppppppwwwffffffVVVNNNVVV^^^VVVGGGEEE===---------333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwpppppppppffffff^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNGGGNNNNNNNNNGGGNNNGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffffffffpppfffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppffffffVVVNNNVVVppp¶¶¶ßßßççççççççççççßßß¶¶¶–––fffVVVVVVfff°°°ßßßççççççççççççççç×××¶¶¶ªªª°°°ßßßççççççççççççççççççÎÎΖ––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppwwwwwwwww ÎÎÎçççßßßçççßßßßßß×××ÁÁÁªªªªªª¶¶¶ªªª ‘‘‘††††††wwwppp^^^VVV^^^fffVVVNNNGGG===------------333333333333EEE^^^www†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwppppppfffffffffffffff^^^^^^VVVVVVVVVNNNNNNNNNGGGGGGNNNGGGNNNGGGGGGGGGGGGNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwppp===EEENNNfffpppfffffffffNNNNNNNNNfff‘‘‘ÎÎÎççççççççççççççç××׆††^^^ppp ßßßçççççççççççççççççç×××¶¶¶ªªªÎÎÎçççßßßççççççßßß××××××¶¶¶°°°°°°ÇÇÇ××××××ßßßßßßßßßßßßßßßßßßçççççççççççççççççççççççççççßßßßßßßßßçççÎÎÎ×××××××××ÎÎÎ pppfff^^^ffffffffffff^^^VVVGGG===333333333=========333333333333EEE^^^††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwppppppffffff^^^NNNNNNNNNGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEGGGGGGEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffff^^^fffffffffffffffffffffpppffffffffffffffffffffffffpppfffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppEEEEEENNNfffpppffffffffffffVVVNNNNNN^^^www¶¶¶ßßßççççççççç××ב‘‘VVV===EEENNNwww¶¶¶°°°×××çççççççççççççççßßßÎÎζ¶¶ÎÎÎ×××çççççççççÎÎÎÇÇÇÎÎÎÁÁÁ ––– °°°ÁÁÁ×××ßßßçççççççççççççççççççççßßßççççççßßßççççççççççççççççççßßßçççßßßßßß–––wwwwwwwwwppppppffffffVVVNNNGGGGGGGGGGGGVVVVVV^^^VVV===---333333333===GGGfff†††‘‘‘†††‘‘‘†††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppppp^^^NNNGGGEEEGGGGGGGGGGGGGGGGGGGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGGGGGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^fffffffffffffffffffffpppffffffffffffffffffpppffffffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppEEEEEEGGGNNNffffffpppppppppffffffVVV^^^NNNfffªªªßßßççççççßßßÎÎΪªªGGG===NNNwww‘‘‘ªªªÇÇÇÇÇÇ×××ççççççççççççççççççßßßÁÁÁÇÇÇßßßçççßßßßßßßßß¶¶¶‘‘‘fffNNNGGGVVV†††ßßßççççççççççççççççççççççççççççççççççççççççççççççççßßßçççßßßÇÇÇ pppVVVVVVppppppfff^^^VVVVVVGGGEEE333333GGGffffffpppNNN333---===333EEEGGGNNNNNNfffppp†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwfffGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffffffffffffffpppfffppppppfffpppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppGGGGGGNNNNNNNNN^^^pppppppppwwwppp^^^VVVNNNNNN^^^‘‘‘ÎÎÎßßßççççççßßß¶¶¶¶¶¶ÇÇǰ°°°°°°°°°°°VVV–––ßßßçççççççççççççççÎÎÎ ªªª×××ççççççßßßÇÇǰ°°ªªª°°°¶¶¶°°°¶¶¶ßßßçççççççççççççççççççççççççççççççççßßßçççççççççççççççßßßßßß fff^^^^^^^^^fffppp^^^GGGEEEGGGGGG333---"""333333=========EEEppp^^^VVVfffppp^^^fff^^^fffppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwwwpppVVV======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^ffffff^^^fffffffffffffffffffffpppffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwGGGGGGGGGNNNNNNNNNVVVfffpppppppppfffppp^^^VVVNNNVVVppp ×××çççïïïççççççßßßwww^^^–––¶¶¶ÁÁÁ¶¶¶ÁÁÁ×××ßßßßßßßßßççççççßßß¶¶¶ ßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßßßßÎÎÎÁÁÁªªª†††ppppppwwwwwwpppwwwppp^^^NNNGGG===---"""""""""""""""333NNN^^^‘‘‘ppp^^^VVVVVV^^^VVVNNN^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwfffEEE333333======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGGGGGGNNNNNNNNNNNNNNNVVVwwwppppppwwwwwwwww^^^VVVVVVNNN^^^wwwÇÇÇçççïïïçççççççççÎÎÎÎÎÎçççççççççÎÎΰ°°fff†††‘‘‘ÎÎÎßßßçççßßß×××ÎÎÎßßßßßßßßßççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççßßßßßßÇÇÇ–––†††††††††wwwNNN===VVVppp^^^pppwwwwwwwwwfff^^^NNN===---""""""""""""---333EEENNNpppfffVVVEEE=========EEEEEENNNVVVffffffwww‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwGGG333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffffffffffffffffffffpppfffpppfffppppppfffppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwGGGNNNNNNNNNNNNNNNNNNNNNVVV^^^wwwwwwwwwwwwwwwfffNNNVVVNNNfff–––×××ïïïïïïçççïïïçççççççççççç×××¶¶¶ ‘‘‘‘‘‘pppªªª×××ççççççççç×××¶¶¶ÁÁÁßßßççççççççççççïïïçççççççççççççççççççççççççççççççççßßßÁÁÁ ‘‘‘†††pppNNN333333^^^†††^^^pppwwwpppfffpppVVVEEE===333333333---"""------333===333333GGGEEE333333======EEEGGGNNN^^^fffppp†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††www^^^------333333===============EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffffffffpppfffpppfffpppfffppppppfffppppppppppppppppppppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwww††††††GGGNNNVVVNNNNNNNNNVVV^^^NNNVVV^^^pppwwwpppwwwpppVVVVVVVVVVVV^^^www¶¶¶ïïïïïïçççïïïçççïïïçççççççççßßß¶¶¶ ––– ¶¶¶ ppp°°°çççççç×××ÎÎÎßßßççççççïïïçççççççççïïïççççççççççççççççççççççççßßßÁÁÁpppfff†††††††††††††††––– ‘‘‘^^^www‘‘‘†††pppVVV†††pppppppppVVVNNNGGGNNNNNNEEE333""""""""""""""""""===NNNEEE=========333333======EEENNN^^^ppp‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††fff==="""---333=====================EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffffffffffffffffpppfffpppppppppppppppppppppppppppppppppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNNNNVVVVVVVVVppp^^^NNNNNNVVVVVVfffwwwpppVVVVVVNNNVVVVVV^^^fff‘‘‘×××ïïïïïïïïïçççïïïçççïïïçççççççççßßßÇÇÇ ¶¶¶çççççççççççççççççççççççççççïïïçççççççççççççççççççççßßß××××××ÇÇÇÎÎÎÎÎÎÎÎÎçççßßßÎÎÎÁÁÁÁÁÁ××××××ßßßÇÇÇ‘‘‘††††††wwwNNNpppwwwfffwwwwww^^^^^^VVVVVVGGGEEE======---------------GGG^^^^^^NNNEEE333333333333333===EEEGGGVVVfffppp†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††wwwEEE""""""333===333==================EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffpppfffppppppfffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††NNNNNNVVVNNNVVV^^^wwwfffVVVNNNNNNNNNVVVppppppwwwwwwVVVVVVVVVVVVVVVVVVVVV^^^wwwÁÁÁçççïïïïïïçççïïïçççïïïçççççççççÎÎÎÇÇÇ ßßßççççççççççççççççççççççççççççççßßßßßß×××ßßß×××ÎÎÎÎÎÎÇÇÇÁÁÁ°°°°°°ÎÎÎ××××××ßßßççççççßßßßßßççççççççççççßßßÇÇÇ‘‘‘fffVVV–––^^^ffffff^^^fffffffffVVVGGGGGGGGG===GGGGGGGGGGGG^^^†††^^^NNNNNNEEE333===333333======EEENNNVVVfff†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††VVV---"""---=================================EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=========EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEffffffffffffpppffffffpppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††VVVNNNVVVVVVVVVfffppppppfffVVVVVVfff^^^VVVVVVpppwwwVVVVVVVVVVVVVVVVVVVVV^^^VVVfff‘‘‘×××ïïïïïïïïïçççççççççççççççççççççßßß×××ßßßßßßßßß××××××ÎÎÎÇÇÇÁÁÁ¶¶¶°°°°°°¶¶¶ÎÎÎßßßßßßßßß×××ßßßÇÇǪªª––– –––––– ×××çççççççççÎÎÎÇÇÇßßßççççççççç×××¶¶¶‘‘‘––––––wwwpppfffpppfff^^^VVVVVVNNNVVVGGG^^^^^^VVVGGGGGGNNNfffwwwpppfffGGG===333333===333333======GGGVVVfffwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††ppp333"""---333===333===333========================EEE=========EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppfffpppfffpppfffpppppppppfffppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††VVVVVVVVVVVVVVVfffwwwpppwwwppp^^^ppppppNNNVVVVVVfffVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^¶¶¶çççïïïçççßßß×××ÎÎÎÇÇǶ¶¶°°°ªªª –––‘‘‘†††pppppppppfffpppwww‘‘‘°°°ÎÎÎßßßÎÎÎ †††††† ¶¶¶×××ßßßÎÎζ¶¶ªªªÇÇÇ×××ßßß¶¶¶‘‘‘–––ßßßçççççççççßßß www–––ÁÁÁÁÁÁ –––‘‘‘^^^ffffffwwwppp^^^NNNGGGEEE===GGG^^^^^^pppNNNEEE333===333333===333======NNNfffpppwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††wwwEEE""""""333333======333===333===========================EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEE===EEEEEE======EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppfffpppppppppppppppppppppppppppppppppwwwpppwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††fff^^^VVVVVVVVV^^^pppppp^^^ppp^^^pppwwwfffwwwVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^fff‘‘‘ÇÇǪªª†††pppfffffffff^^^^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^VVVffffff–––¶¶¶ÎÎÎÎÎÎ ––– ªªªªªªÇÇÇÎÎζ¶¶°°°ÇÇÇßßßçççÁÁÁ°°°ÇÇÇççççççççççççÇÇÇfffppp–––¶¶¶ÎÎÎÎÎÎÎÎÎÁÁÁªªª––– –––†††ppppppVVVGGGEEENNN^^^www†††pppppp^^^NNNEEE===333===333333333===GGG^^^wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘††††††‘‘‘†††‘‘‘††††††††††††VVV---"""---333===333===333===333===========================EEE===EEE===EEEEEEEEEEEEEEE===EEE===EEEEEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEppppppwwwpppwwwppppppwwwpppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘www^^^VVVVVVVVVVVVfffwwwppp^^^wwwppp†††VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^pppwwwfff^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^ppp ¶¶¶××××××¶¶¶ –––ÁÁÁßßßßßß××××××ßßßççççççßßßçççççç×××ÎÎÎ×××ÎÎÎwww†††–––ÁÁÁ×××ççççççßßß×××°°°ªªªªªªªªªªªª pppNNNEEE===VVV‘‘‘ªªªªªª‘‘‘‘‘‘††††††ffffffGGG===333=========EEEVVVwww†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††fff333""""""===333===333===333===333=================================EEEEEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEE======EEEEEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††––– †††VVVNNNVVVVVVVVV^^^www^^^www†††VVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^^^^^^^fff¶¶¶¶¶¶www^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^^^^pppwww‘‘‘ °°°°°°¶¶¶×××ßßßççççççççççççççççççççççççççççççÇÇÇÁÁÁÎÎΰ°°–––‘‘‘ÁÁÁßßßçççççççççÎÎΆ††www‘‘‘–––‘‘‘wwwppp^^^^^^VVVGGGNNNVVVppp‘‘‘ªªªªªª‘‘‘pppffffffNNN===333333333333---EEE^^^www‘‘‘°°°ÁÁÁ¶¶¶¶¶¶–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††EEE""""""333333333===333333===333===333=================================EEE===EEE===============EEE=========EEEEEE===EEE======EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwpppwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘ ¶¶¶ÁÁÁ–––wwwNNNNNNVVVVVVVVV^^^^^^ppppppfff^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVVVVV^^^VVV^^^^^^fff†††°°°‘‘‘fff^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffppp–––ªªªÁÁÁÎÎÎßßßßßß××××××çççççççççççççççßßß×××ÎÎÎ×××××××××ççççççççççççÇÇdž††††† ÁÁÁÁÁÁ†††VVVGGGVVVfffVVVNNNVVVGGGVVVfffpppfffNNNEEE======333===333333333---333EEENNNppp––– ªªª‘‘‘‘‘‘ªªªªªª–––‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††VVV---"""---333===333===333333===333===333===333==============================EEE======EEE============EEE======EEE=========EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††† –––‘‘‘†††–––fffNNNGGGVVVVVVVVV^^^wwwwww†††wwwfff^^^VVVVVV^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^ppp‘‘‘ªªª^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNNNNNNNVVVNNNVVVVVV^^^^^^^^^^^^^^^ppp†††°°°ÇÇÇÎÎΰ°° ÁÁÁÎÎÎ×××ÇÇÇÎÎÎßßßççççççßßßççççççßßßßßßßßßççççççßßß×××ÎÎÎ×××ßßßÎÎΪªªppp–––ÇÇǶ¶¶–––pppVVVVVVVVVNNNGGGEEE======333===333333===333333=========GGGfffwwwwww °°°ÁÁÁ°°° –––‘‘‘‘‘‘––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††ppp333""""""333333===333===333333===333===333===333============================================================EEE======EEE=========EEE===EEEEEE=========EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘ †††^^^GGGGGGNNNVVV^^^pppwww^^^wwwfffVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fffwww –––pppVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^ffffff^^^†††‘‘‘www^^^fffwww–––ªªª––– ªªª¶¶¶ÇÇǶ¶¶°°°ªªª°°°¶¶¶ÇÇÇÎÎÎßßß×××ÎÎÎÁÁÁÇÇÇßßßççççççßßß×××ÎÎÎßßß××××××ßßßçççßßß×××ÇÇǪªª †††pppVVVGGGEEE===333333===333333======333=========GGGfff†††ªªª¶¶¶°°°ÇÇǪªª–––‘‘‘‘‘‘––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwEEE""""""333===333333333===333===333===333333===333================================================EEE=========EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ªªªªªªwwwppp–––°°°pppVVVNNNGGGNNNVVVVVV^^^www†††‘‘‘www‘‘‘wwwfff^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^^^^fffppp‘‘‘ªªªwww^^^VVVVVVVVVVVV^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^fff‘‘‘www‘‘‘†††fff†††ppp^^^^^^fffwww‘‘‘ ªªªÁÁÁ×××ßßß×××ÇÇÇÇÇÇÎÎÎÇÇÇÁÁÁªªª°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇÇÎÎÎßßßßßß×××ççççççßßßçççÇÇǰ°°ªªªªªª°°°°°°ªªª †††wwwVVVGGG===333===333333===333333===EEEGGGfff†††††††††‘‘‘¶¶¶ÇÇÇÁÁÁ¶¶¶°°°–––‘‘‘–––––– –––––––––––––––‘‘‘–––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††VVV---"""---333333===333333===333===333333===333===333===============================================================EEE===EEE=========EEE===EEE======EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ ªªªffffff ¶¶¶–––fffVVVNNNGGGNNNVVV^^^fffppp†††www^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^fff^^^^^^^^^fff^^^ppp fff^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^^^^fffpppwwwwwwwwwppp††††††fff^^^^^^ppp‘‘‘ªªªÁÁÁ×××ççççççßßßççççççßßßçççßßß×××ÎÎζ¶¶ ªªªªªª––– ÎÎÎççççççßßßççç×××ÁÁÁªªª––– ªªª ––– °°°ªªª‘‘‘www^^^GGGEEE333333===333======GGG^^^‘‘‘††††††‘‘‘ ÁÁÁ¶¶¶ÎÎζ¶¶ªªª ¶¶¶°°° ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppp333""""""333===333===333333333333===333333===333333333===333===========================================================================EEE============EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘––––––ffffff¶¶¶¶¶¶ÁÁÁ†††pppVVVNNNNNNGGGVVVVVVfff†††www†††fff‘‘‘††††††fff^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff‘‘‘ªªªfff^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^fff‘‘‘‘‘‘‘‘‘ppp^^^fffVVVVVVVVVfff–––ÁÁÁÎÎÎ×××ßßßçççÎÎΪªªÁÁÁÎÎÎ××××××ÁÁÁ –––‘‘‘ ßßßççççççßßß×××ÁÁÁªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘––– ªªª †††ppp^^^GGGEEE=========EEEGGGfff†††‘‘‘††††††‘‘‘¶¶¶°°°¶¶¶ÎÎΪªª ÇÇÇÇÇǶ¶¶ ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘EEE""""""333333333333===333===333===333333===333===333333======333==================================================================EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘–––ppp^^^fff¶¶¶ÇÇÇ×××°°°‘‘‘ppp^^^NNNNNNGGGNNN^^^^^^†††www†††fff‘‘‘‘‘‘^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^fffVVV^^^^^^fffwww ªªª^^^^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVV^^^^^^pppwwwwww††††††VVV^^^NNNNNNVVV^^^www–––ÁÁÁßßßßßß×××ÁÁÁÎÎÎßßßÎÎÎÁÁÁÁÁÁªªª–––¶¶¶×××çççççççççÎÎÎÁÁÁ°°°ªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘†††www^^^NNNEEE===EEENNNfff†††‘‘‘††††††°°°ÁÁÁÁÁÁ°°°ªªª¶¶¶×××ÎÎζ¶¶ ––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††VVV---"""---333333===333333333===333333333333333333333===333======333======333=============================================EEE==================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘wwwffffff‘‘‘ÁÁÁÇÇÇ×××¶¶¶–––ppp^^^NNNNNNGGGNNN^^^^^^pppwwwppp‘‘‘‘‘‘www†††fffffffff^^^^^^fff^^^^^^fff^^^fff^^^fff^^^^^^fffffffffppp‘‘‘ÁÁÁfff^^^VVV^^^VVV^^^VVV^^^^^^^^^VVV^^^VVV^^^fff‘‘‘‘‘‘wwwpppwwwpppfffVVVNNNNNNVVVfffªªªÎÎÎßßßÁÁÁÇÇÇÇÇÇÎÎÎÁÁÁªªªªªª¶¶¶ççççççïïïççç×××ÁÁÁ°°°ªªª –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwwwwwwwwwwww‘‘‘ªªª¶¶¶°°°www===333===EEENNNfff†††‘‘‘††††††–––¶¶¶ÁÁÁ¶¶¶ªªª ÎÎÎßßß×××ÁÁÁªªª–––––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘–––––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ppp333""""""333333333===333===333333333333===333333333333===333333======333=======================================EEE===EEE===EEE==================EEE===EEEEEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††wwwwww††††††‘‘‘‘‘‘¶¶¶–––ÁÁÁÎÎÎÁÁÁ–––www^^^VVVNNNGGGNNNVVV^^^ffffff‘‘‘–––†††‘‘‘^^^fff^^^fff^^^fff^^^fff^^^fffVVV^^^^^^fffffffffpppwww ‘‘‘pppfffVVV^^^^^^VVVVVV^^^VVV^^^VVVVVV^^^^^^fffpppwww††††††††††††††††††ppp^^^^^^NNNNNNNNN^^^ppp–––¶¶¶ÇÇÇÎÎÎÁÁÁ–––ªªª‘‘‘¶¶¶çççççççççççç×××ÁÁÁªªª –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††wwwwwwpppppppppppp–––°°°¶¶¶ VVV333333333333===EEENNNppp†††‘‘‘††††††‘‘‘°°° ªªª°°°¶¶¶ßßßççç×××ÁÁÁªªª––– ––– ––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘EEE""""""---333===333===333333333===333333333333===333333======333======333=============================================EEE===EEE======EEE===EEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††wwwwwwffffff^^^^^^^^^^^^www°°°¶¶¶ ªªª‘‘‘ °°°ÎÎζ¶¶–––www^^^VVVVVVNNNNNNVVV^^^fffwww†††‘‘‘†††‘‘‘‘‘‘www^^^^^^fff^^^ffffffffffffVVV^^^fffffffffffffffpppwwwªªªÁÁÁ‘‘‘ppp^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^^^^pppwwwpppwww‘‘‘††††††www^^^NNNNNNVVVfffªªªÇÇÇ×××××××××ÎÎÎççççççççççççßßßÇÇǶ¶¶°°°ªªª––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††wwwwwwwwwpppwww–––ªªª°°°‘‘‘NNN333333333333===333======EEEVVVppp†††–––††††††‘‘‘°°°ÁÁÁ¶¶¶ÁÁÁÇÇÇßßßççç×××ÁÁÁªªª––––––––––––––––––––––––––––––––––––––––––‘‘‘––––––––––––––––––‘‘‘–––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘VVV------333333333333===333333333333333===333333===333===333===333===333===333==========================================EEE======EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEGGGEEEEEEwww††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwpppffffff^^^^^^^^^^^^VVV^^^VVV^^^^^^ppp^^^‘‘‘ÁÁÁ ––– °°°¶¶¶¶¶¶ÎÎÎÁÁÁ–––fff^^^VVVNNNNNNNNN^^^fffpppwww–––†††–––†††‘‘‘††††††fff^^^ffffff^^^fffffffff^^^^^^ffffffffffffffffffwwwÁÁÁïïïçççÇÇdž††^^^^^^^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^–––www†††–––‘‘‘^^^^^^VVVNNNNNN^^^ppp†††°°°ÇÇÇ×××çççççççççççççççßßßßßß×××ÎÎÎÁÁÁªªª ––––––‘‘‘‘‘‘‘‘‘††††††††††††wwwwwwwww–––ªªª°°°‘‘‘VVV333---333333===333======333======GGGVVVwww†††‘‘‘††††††‘‘‘–––ÁÁÁÁÁÁ¶¶¶ÁÁÁßßßßßß×××¶¶¶ªªª––– ––– ––– ––– –––––––––––––––––––––––––––––––––––––––––––––‘‘‘–––‘‘‘––––––‘‘‘ppp333""""""333333333===333333333333333===333===333333===333===333333===333=========333=======================================EEE======EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVVVVVVVV^^^^^^VVVVVVVVV^^^VVV^^^^^^pppNNN^^^––– ‘‘‘–––°°°××× °°°×××ÇÇÇ pppfff^^^VVVNNNNNN^^^^^^ppp††††††–––††††††††††††ffffff^^^fffffffffffffff^^^^^^ffffffffffffpppfff†††ßßßïïïýýýýýýßßß www^^^^^^^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^VVV^^^^^^wwwwww††††††††††††pppVVVVVVNNNNNNVVVppp††† ¶¶¶×××ßßßßßßçççßßßßßßßßßßßßßßß×××ÎÎÎÎÎÎÁÁÁ°°° –––‘‘‘‘‘‘††††††–––°°°°°°–––VVVEEE===EEE============EEEEEEGGGGGG======EEEGGG^^^www‘‘‘†††††† °°°ÁÁÁ¶¶¶°°°°°°ÎÎÎßßßÎÎζ¶¶ªªª–––––– –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––‘‘‘†††EEE""""""---333333333333333333333333333===333333===333===333===333333===333333======333=======================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVVNNNVVV^^^VVV^^^VVV^^^VVVVVV^^^NNNVVVfffVVVGGGfff ††† ªªªÎÎΰ°°ÁÁÁ¶¶¶×××ÎÎΪªª–––†††wwwfffVVVNNNNNN^^^^^^fffwww––– †††††††††††††††ppp^^^ffffffffffff^^^ffffffpppfffffffffppp ïïïýýýýýýýýýýýýïïïÎÎΆ††fff^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVV^^^ppp†††–––†††wwwwwwwwwVVVVVVGGGNNNVVV^^^www‘‘‘°°°ÎÎÎßßßßßß×××ßßßßßßßßßçççßßßßßßßßßßßßÎÎÎÁÁÁ¶¶¶ –––‘‘‘†††www†††‘‘‘°°°¶¶¶ªªªfffNNNNNN^^^^^^VVVGGGGGGGGGNNNVVV^^^VVVGGGEEE=========GGG^^^www†††‘‘‘††††††–––ÁÁÁ°°°ªªª°°°¶¶¶¶¶¶××××××ÁÁÁªªª––– ––– ––– ––– ––– ––––––––– ––– ––– –––––––––––––––––––––‘‘‘VVV---"""---333333333333333333333333333333333333333===333===333===333333======333333======333====================================EEE======EEE===EEEEEEEEEEEE===EEEEEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††††††††wwwpppfff^^^^^^VVVVVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVVNNNVVVNNNfffVVVGGG†††ppp‘‘‘¶¶¶¶¶¶°°°ÇÇÇßßß¶¶¶°°°ÎÎÎßßßÁÁÁ°°°–––†††ppp^^^NNNNNNVVVffffff†††††††††‘‘‘‘‘‘†††fffffffffffffff^^^^^^fffffffffppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^www†††–––††††††wwwffffffVVVNNNGGGGGGVVV^^^www‘‘‘¶¶¶××××××¶¶¶ÎÎÎ××××××ßßßççççççßßßçççßßßßßß×××ÇÇÇÁÁÁªªª–––†††††††††ªªª¶¶¶ÁÁÁªªª^^^^^^fffpppppppppppp^^^^^^www‘‘‘†††wwwfffNNNEEE============GGG^^^†††–––††††††°°°¶¶¶ –––°°°ÁÁÁ°°°ÁÁÁ×××ÎÎÎ ––– ––– ––– ––––––––– –––––– ––– ––– –––––– ––––––––––––ppp333""""""333333333333333333333333333333333===333333===333===333333===333333===333===333===333=============================================EEE===EEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††††††††††††††††††††wwwffffff^^^VVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^^^^VVV^^^VVVNNNNNNGGGVVVNNNVVVVVVfffNNNNNNfff^^^ °°° ÎÎÎ×××ÁÁÁªªªªªªÎÎÎçççßßßÎÎΰ°°†††ppp^^^^^^VVVVVV^^^fffppp†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘pppffffff^^^fffppppppfffpppfff×××ýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^^^^^^^fff‘‘‘†††††††††‘‘‘†††www^^^VVVVVVNNNGGGGGGVVVfff ÁÁÁÇÇÇÎÎÎ×××ßßßßßß×××ßßßßßßßßßßßßßßßßßßçççßßß×××ÎÎÎÇÇǰ°° –––fff^^^^^^VVVNNNNNNNNNNNN^^^fffppp‘‘‘ªªª †††ppp^^^VVVGGG============EEENNNfffwww‘‘‘‘‘‘††††††††††††ªªªÁÁÁ¶¶¶°°° ªªª ––– ––– ––– ––– –––––– ––– ––– –––––– –––––––––––––––†††EEE""""""---333333333333333333333333333333333333===333===333333===333333===333=========333======333=================================EEE=========EEE===EEEEEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVV^^^^^^^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^VVVVVVVVVNNNGGGGGGGGGGGGNNNNNNVVVVVVVVVfffNNNGGGppppppVVV^^^‘‘‘ ÁÁÁ¶¶¶ÇÇÇÁÁÁ°°° çççïïïßßß×××¶¶¶†††wwwppp^^^^^^VVV^^^fffppp†††‘‘‘†††‘‘‘†††ffffff^^^ffffffpppfffpppppp–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^ppp††††††pppfffffffffppppppVVVNNNGGGGGGNNN^^^ppp‘‘‘°°°××××××ÁÁÁ×××ßßßßßßÎÎÎ×××ççç×××××××××ßßßßßß×××¶¶¶–––pppfffVVVNNNVVV^^^fffVVVGGGEEEGGGGGGNNNVVVfff‘‘‘ffffffpppVVVGGGGGGEEE===333===EEENNN^^^www†††‘‘‘††††††°°°¶¶¶¶¶¶ÇÇǶ¶¶ ‘‘‘––– ––– ––– ––– ––– ––– ––– ––– ––– –––‘‘‘VVV------333333333333333333333333333333333===333333333===333333333333333333333===333======333=============================================EEE===EEE======EEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††††††††pppppp^^^VVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^VVVVVV^^^^^^VVV^^^^^^VVVVVVVVVNNNNNNGGGGGGNNNNNNNNNNNNVVVVVVVVVVVVNNNfffNNNEEEwww‘‘‘^^^fff–––¶¶¶ÇÇÇÎÎζ¶¶¶¶¶ªªªççççççÎÎÎ×××ßßßÁÁÁ–––‘‘‘ppp^^^VVV^^^ffffffwww†††‘‘‘ppp††††††pppfffffffffpppfffppppppwww¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘fff^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fffppp‘‘‘www‘‘‘††††††fff^^^^^^NNNGGGGGGVVV^^^www–––°°°ÁÁÁ×××ßßßßßß×××ßßßçççßßßÎÎζ¶¶ªªª–––pppffffff^^^VVVVVVNNNNNNNNN^^^^^^VVVGGGGGGEEEEEEGGGNNNNNN^^^pppwwwwwwwwwppp^^^VVVGGGEEE======333===EEEGGG^^^ppp‘‘‘†††††† ¶¶¶ÁÁÁ¶¶¶ªªª –––––– ––– ––– ––– ––– ––– ––– –––––– ––– –––ppp---""""""---333333333333333333333333333333333333===333333===333===333333===333333===333333===================================================EEE======EEE======EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVVVVV^^^VVV^^^^^^VVVVVVVVVNNNNNNNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV^^^GGG333VVV†††††††††ffffffªªª×××ßßß°°°°°°ªªªçççççç×××çççÎÎÎßßßÇÇǶ¶¶°°°–––wwwfffVVVVVVffffffppp †††–––ppppppfff^^^fffppppppfffpppÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç窪ªwwwfff^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^^^^pppwww†††‘‘‘‘‘‘fffVVVNNNGGGNNNVVVfffwww‘‘‘°°°ÎÎÎßßßçççççççççÎÎΪªªwwwffffff^^^fffffffff^^^^^^^^^^^^^^^VVVNNNNNNNNNVVVVVVNNNGGGGGGEEEEEEGGGNNNVVVfffwwwwwwwww^^^NNNGGG======333======EEEGGGNNNVVV^^^www††††††‘‘‘‘‘‘††††††–––ªªª¶¶¶ÁÁÁ ––– ––– ––– ––– ––– ––– †††EEE""""""---333333333333333333333333333333333333333333===333333333333333333333===333333======333333=============================================EEE======EEEEEEEEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE†††††††††††††††wwwpppfff^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^^^^VVV^^^VVVVVVVVV^^^VVVVVVVVVVVVNNNNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVNNN^^^GGG333GGGfff––– ‘‘‘ffffff ßßß¶¶¶ªªªªªªÇÇÇçççççççççÎÎÎ×××ïïïïïïßßßÇÇǪªªpppVVVVVV^^^pppppp‘‘‘††††††‘‘‘fffffffffpppfffppppppppp†††çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××–––fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff–––‘‘‘†††††††††‘‘‘‘‘‘^^^VVVVVVGGGGGGNNNVVV^^^www†††ªªªÇÇÇßßßßßß fff^^^^^^VVVNNNVVVVVV^^^fff^^^^^^fff^^^^^^VVVVVVVVVVVV^^^VVV^^^VVVNNNEEEEEEGGGNNNNNNVVV^^^ppppppfffVVVGGG===EEEEEEGGG^^^fffppp^^^NNNVVV^^^ppp†††www‘‘‘°°°ÎÎÎÎÎΰ°°‘‘‘––––––––– ––– ––– ––– ––– ––––––‘‘‘VVV"""""""""333333333333333333333333333333333333333===333333===333===333===333333333===333333================================================EEE===EEE======EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE††††††wwwpppfff^^^VVVVVVNNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVV^^^NNN333333GGG –––‘‘‘VVV^^^°°°¶¶¶ÇÇÇ ×××ßßß××××××ïïïïïïïïïïïïïïï×××°°°†††ppp^^^VVV^^^ffffffwww‘‘‘–––†††pppfffffffffpppppppppppp ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff^^^^^^^^^^^^^^^^^^^^^^^^fffpppfff^^^^^^^^^fff†††wwwwww††††††fffVVVNNNGGGGGGNNNVVV^^^ppp†††ªªªÇÇÇ ppp^^^^^^VVVVVVNNNNNNVVV^^^^^^^^^^^^VVVNNNVVVVVV^^^ffffffNNNGGGVVVVVVNNNNNNNNNGGGEEEGGGNNNNNNVVV^^^^^^fff^^^NNNGGGVVVwwwwwwwwwffffffNNNNNNVVVpppwwwpppwwwwww °°°°°°ÁÁÁÁÁÁªªª–––‘‘‘–––––– ––– –––––– ––– ––––––ppp---""""""---333333333333333333333333333333333333333333===333333333333333===333===333333===333===333=============================================EEE===EEE===EEEEEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEwwwppp^^^VVVVVVNNNVVVNNNVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV333---333NNN‘‘‘ªªª^^^NNNNNNfff ÎÎÎ ÇÇÇÇÇǶ¶¶çççïïïïïïýýýïïïïïïïïïßßß°°°†††pppfffffffffpppfffwww–––pppffffffppppppppppppwwwÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××× ppp^^^^^^^^^^^^fffwww††††††‘‘‘ffffff^^^^^^fffwww†††––– –––‘‘‘pppfff^^^VVVGGGGGGGGGNNNVVV^^^ppp‘‘‘ªªª‘‘‘ppp^^^^^^^^^VVVNNNGGGNNNVVVfff^^^^^^NNNGGGVVV^^^^^^^^^NNNGGGNNNNNNNNNVVVNNNNNNVVVNNNGGGGGGGGGNNNVVVVVVfff^^^NNNNNNNNNVVVffffffVVVGGGGGGVVVfffwwwffffffwwwwwwwww†††ªªªÇÇÇ××תªª‘‘‘–––‘‘‘‘‘‘––––––––– ––– ––– ––– ––––––===""""""---333333333333333333333333333333333333333333333333333333===333333333333333===333===333==========================================EEE===EEE============EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVV===------===NNNfff†††VVV^^^GGG^^^ ‘‘‘ÎÎÎ××× ßßßïïïýýýççççççïïïßßßçççßßß¶¶¶–––†††wwwppppppfffppppppppppppppppppppppppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎffffff^^^fff‘‘‘‘‘‘†††††††††‘‘‘fff^^^fff––– –––‘‘‘†††‘‘‘––––––fff^^^VVVNNNGGGEEEGGGNNNVVVfffwww–––ªªªªªª‘‘‘wwwfff^^^VVVGGGNNNNNNVVVfff^^^^^^VVVNNNNNNNNNVVV^^^VVV^^^VVVNNNGGGNNN^^^^^^^^^VVVGGGEEEGGGGGGNNNNNNVVV^^^^^^VVVNNNNNNGGGNNNNNN^^^ffffffpppppppppfffVVV^^^fffwwwwww–––°°°ªªªÁÁÁÇÇǪªª‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘NNN"""""""""333333333333333333333333333333333333333333333333333333333333333333333333333===333333============333=============================================EEE===EEEEEEEEEEEEEEE===EEEEEE===EEEEEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNVVVVVVVVV^^^VVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV======333---EEEVVVpppVVV†††‘‘‘VVVNNNVVV‘‘‘×××ççç°°°ßßßïïïïïïïïïÇÇÇÎÎΪªªÁÁÁßßßçççÁÁÁ°°°ªªª°°°ªªª†††wwwppppppfffpppwwwwwwwwwpppwww†††ßßßýýýýýýýýýýýýïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß pppffffffppp††††††‘‘‘‘‘‘†††wwwpppppppppfffpppppp††† ‘‘‘wwwVVVVVVNNNGGGGGGGGGNNN^^^ppp ¶¶¶¶¶¶ wwwffffff^^^^^^^^^^^^NNNVVV^^^fff^^^VVVNNNNNNNNNVVV^^^^^^NNNNNNVVV^^^^^^^^^VVVNNNNNNGGGEEEGGGNNNNNNVVV^^^www‘‘‘ppp^^^ppppppfff^^^wwwwwwwwwwwwpppppp^^^VVV^^^fffwww††† ÁÁÁÇÇÇ pppwww†††‘‘‘––––––––– ––– –––ppp---""""""---333333333333333333333333333333333333333333333333333333333333333333333333333===333===333=========================================================EEE===EEEEEE===EEEEEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEENNNVVVVVVVVVVVVVVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVNNNVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV======333333333GGGGGGEEE‘‘‘–––†††^^^GGG^^^ªªªßßß×××ïïïýýýïïïççç ªªª‘‘‘¶¶¶ßßßïïïççççççßßßÎÎÎÇÇÇÁÁÁ–––pppppp††††††www ïïïýýýýýýýýýýýýÇÇǪªª ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇdž††fff^^^ffffffppp‘‘‘–––†††‘‘‘–––fff^^^^^^ppp††† ªªª–––wwwpppppp^^^^^^^^^VVVVVVGGGGGGGGGGGGVVV^^^www‘‘‘°°°ÎÎÎÎÎΪªªpppffffff^^^VVVVVVNNNVVV^^^^^^VVV^^^VVVNNNNNNVVV^^^^^^ffffffNNNVVVVVV^^^fff^^^VVVNNNGGGNNNNNNVVV^^^ †††wwwwwwfffNNNNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEGGGVVVfffwww‘‘‘‘‘‘wwwwwwwwwwwwwww†††––––––––– ––– ===""""""---333333333333333333333333333333333333333===333333333333333333333333333333===333===333======333================================================EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVV^^^VVV^^^VVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVV===EEEGGG===333333GGGGGGppp––––––‘‘‘EEEVVVppp ÎÎÎïïïýýýïïïïïï‘‘‘‘‘‘×××ïïïïïïïïïïïïçççÎÎÎ××××××ÎÎΪªª††††††‘‘‘ªªªªªª–––††††††°°°ïïïýýýýýýýýýýýýÎÎζ¶¶wwwßßßýýýçççïïïýýýýýýýýýýýýçççÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªppp^^^^^^fff†††‘‘‘–––‘‘‘ffffffwwwwwwfffffffffffffffpppwwwpppfff^^^VVVNNNGGGGGGGGGNNNVVVpppªªªÇÇÇ×××ÇÇǰ°°‘‘‘pppfff^^^ffffffVVVVVVGGGVVV^^^^^^^^^VVVVVVNNNVVVfffffffff^^^VVVGGGVVVVVV^^^^^^^^^VVVVVVVVVVVV‘‘‘fff^^^fff^^^NNNGGG===========================EEEGGGfff†††wwwwwwppp^^^fffppp‘‘‘‘‘‘––––––‘‘‘GGG------333333333333333333333333333333333333333333333333333===333333333===333333333333======333================================================EEE===EEEEEEEEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVV^^^===NNNfffNNN===---333NNN^^^ppp VVV‘‘‘ffffff†††×××ïïïýýýççç fff†††ÎÎÎïïï×××çççïïïïïïïïïïïïÎÎÎ××××××ÁÁÁ¶¶¶ÇÇÇ×××ÎÎÎÁÁÁ –––ÎÎÎïïïýýýýýýýýýýýýýýýççç‘‘‘ççççççwww¶¶¶ÎÎÎßßßýýýýýý°°°GGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††ppp^^^fffwww†††–––‘‘‘††††††wwwffffff^^^^^^^^^^^^wwwwww††††††www^^^^^^^^^VVVNNNGGGGGGGGGVVVfffwww–––¶¶¶ÎÎÎßßßßßß°°°†††wwwpppppp^^^VVVNNNNNNGGGNNN^^^^^^^^^^^^VVVfffffffffffffff^^^VVVNNNVVVffffffffffffwww†††^^^GGGEEEEEEGGGGGGEEE===333333333333333333333333======VVV†††ppp^^^^^^VVVVVVGGGGGGVVVVVVffffffwww†††‘‘‘‘‘‘fff---""""""333333333333333333333333333333333333333333333333333333333333333333333===333===333333===333===333===333====================================EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVNNNNNNNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVVVVVNNNNNNVVVNNNVVVNNNVVV^^^EEE^^^ppp^^^EEE333333===NNNfffppp‘‘‘fff‘‘‘–––pppVVVpppÇÇÇïïïïïïÎÎÎpppÎÎÎçççÁÁÁ×××çççýýýýýýççç°°°×××ççççççççççççïïïïïïçççÇÇǶ¶¶ßßßýýýýýýýýýýýýýýýýýýïïï ïïïÎÎÎ^^^–––wwwïïïýýý××× fffýýýïïïÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfffwwwwwwppp†††††† †††ppp^^^^^^^^^^^^pppppp†††wwwwwwwwwwwwppp^^^^^^VVVGGGGGGGGGNNNVVVpppªªªÁÁÁßßß×××¶¶¶°°° pppppp^^^VVVNNNNNNNNNNNNVVVVVVfff^^^fffffffffpppfffffffff^^^fffwww–––¶¶¶ÇÇÇÎÎΪªªpppNNNEEE=========333===333333333333333===---======EEEGGGNNNfffpppfffEEE===333333------NNN^^^^^^^^^^^^fffpppwwwppp333""""""---333333333333333333333333333333===333333===333333333333333===333===333333333===333333===333===333==========================================EEE===EEEEEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^VVV^^^VVV^^^VVVVVVVVVNNNVVVNNNVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNVVVNNNVVVVVVNNNNNNVVVNNNVVVNNNVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVNNNVVVVVVVVVNNNVVVVVVVVVVVV^^^GGG^^^ppppppGGGEEE333333===GGGpppfff^^^–––ªªªªªª^^^NNN^^^ÇÇÇïïïÎÎÎwwwÁÁÁÇÇǰ°°ÎÎÎýýýïïïïïïÁÁÁ–––ßßßïïïïïïïïïçççßßßýýýïïïïïïßßßçççýýýýýýýýýýýýýýýýýýýýý°°°ÎÎÎÎÎÎVVV °°°–––ïïïýýýýýýßßßpppïïïßßßVVVÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††pppfff^^^fff†††‘‘‘ www†††wwwpppfff^^^^^^ffffff††††††‘‘‘^^^^^^^^^VVVNNNGGGGGGGGGVVV^^^ppp†††°°°ÇÇÇßßßßßßßßß°°° †††fff^^^^^^^^^fffNNNGGGNNNNNN^^^^^^ffffffffffffpppppp†††ªªªÇÇÇßßßßßßßßßßßßßßß××× fffNNN======333333333===333=========333333===EEE======NNNNNNGGG333"""------"""333VVV^^^^^^^^^^^^^^^^^^^^^^^^NNN===333---333333333333333333333333333333333333===333333333333333===333333333333===333333======333===333===333==========================================EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^VVVVVVNNNNNNNNNNNNNNNNNNVVVVVVVVV^^^VVVVVVVVVVVVVVVNNNVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^EEEVVVwww^^^EEE======333===NNNpppppp °°°ÁÁÁ–––^^^NNNfff¶¶¶×××ÇÇÇ ¶¶¶ÎÎÎïïïýýýïïï°°°ªªªÇÇÇ×××çççýýýïïïçççÎÎÎÎÎÎßßßïïïïïïïïïýýýýýýýýýýýýýýýýýýýýýªªªwww wwwÎÎÎÇÇÇïïïýýýïïï pppïïïÇÇÇEEEwww––––––ýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªwwwfff^^^fffwww†††‘‘‘‘‘‘‘‘‘†††††††††fff^^^^^^^^^ppp†††††††††‘‘‘†††ppp^^^^^^VVVNNNGGGGGGGGGVVVfffwww ÁÁÁßßßççç×××××× ‘‘‘†††pppfff^^^VVVNNNNNNVVVfff^^^fffwww–––°°°ÁÁÁÎÎÎ×××çççßßßçççççççççßßßçççÎÎÎ fffEEE============333333333======333------""""""------""""""333------===VVV^^^^^^^^^^^^^^^VVV^^^VVVVVVNNNGGGGGG======333333333333333333333333333333333333333333===333333===333===333===333===333===333333=========333==========================================EEE===EEE===EEE======EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVVVVVNNNVVVNNNVVVNNNNNNNNNVVVNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^VVVVVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^^^^EEEGGGVVVwwwfffEEE=========333===GGGppp‘‘‘°°° ‘‘‘VVVfffªªªßßßçççÁÁÁÎÎÎïïïýýýïïïÇÇǶ¶¶ÁÁÁßßßýýýïïïýýýýýýïïï¶¶¶°°°–––ªªªïïïïïïýýýýýýýýýýýýýýýýýýýýý××׆†† www‘‘‘ppp×××ýýýÇÇÇfffªªªýýý¶¶¶=========NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇdž††pppfff^^^fff††††††‘‘‘––––––pppffffff^^^pppwwwwww†††††††††††††††fff^^^^^^^^^VVVNNNGGGGGGNNN^^^www‘‘‘¶¶¶ßßßßßßßßß¶¶¶¶¶¶ÎÎÎ××תªª†††pppfffffffffppppppwww†††ªªªÎÎÎßßßßßß×××ßßßßßßççççççççççççßßßßßßßßß×××°°°‘‘‘GGG===333------"""333EEEEEE333"""""""""------"""---===VVV^^^VVV^^^VVVVVV^^^VVVVVV^^^VVVVVVVVVNNNNNNEEE======333333333333333333333333333333333333333===333333333===333===333===333======333===333================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEENNNNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNNNNVVVNNNVVVVVVVVVVVVVVVVVV^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVV^^^VVVVVV^^^^^^^^^EEEEEEGGGfff^^^EEEEEEEEE======333333EEEppp–––°°° ªªª†††VVV^^^ ÎÎΪªªªªªÇÇÇßßßÇÇÇ‘‘‘ªªªÁÁÁçççïïïýýýýýýýýýýýý×××ÎÎΖ–––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎÇÇÇppp×××ýýý wwwçççýýý¶¶¶NNN===333NNNïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß wwwfffffffffppp‘‘‘‘‘‘†††‘‘‘††††††wwwfff^^^fffffffffppppppppppppppp^^^VVV^^^^^^VVVVVVVVVGGGNNNNNNVVVppp‘‘‘¶¶¶ßßß×××¶¶¶ÎÎÎßßßßßß×××ÇÇÇ ††† ªªª°°°¶¶¶ÁÁÁÇÇÇßßßççççççßßßßßßççççççßßßççççççßßßÎÎΰ°° pppNNN===333------------"""""""""---"""""""""""""""""""""===VVV^^^VVVVVV^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVVVVVVVNNNGGGEEE===333333===333333333333333333333===333333======333===333===333===333===333===================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNVVVNNNVVVVVVVVVVVVVVVVVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^ffffffEEEGGGNNNpppNNNEEEEEEGGGEEE======333333EEEwww°°°ªªª‘‘‘wwwNNNwww‘‘‘ªªª °°°ÇÇǰ°°°°°ÎÎÎïïïýýýýýýýýýýýýýýýïïïççç×××ÇÇǰ°°ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççýýýýýý†††NNN–––ïïï°°°^^^NNNVVV^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××׆††ppppppwww‘‘‘––––––‘‘‘†††††††††www^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^NNNNNNNNN^^^ppp‘‘‘¶¶¶×××ÁÁÁÁÁÁÁÁÁ×××ßßßÇÇǪªªppppppwwwªªª¶¶¶×××ßßßßßßßßßçççççç×××çççßßß¶¶¶ªªª‘‘‘fffGGG===333------333333---""""""""""""EEEVVV^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVVVVVNNNGGGEEE===333333333333333333===333333===333333333===333======333===333======333===================================================EEE===EEE===EEE===EEEEEEEEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^VVV^^^VVVVVVNNNVVVVVVVVVVVVVVVVVVNNNVVVVVVNNNVVVVVVVVVVVVVVV^^^^^^^^^VVV^^^VVVVVVVVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffGGGVVVfffpppVVVEEE===GGGEEEEEE============GGGppp††††††–––‘‘‘wwwVVV †††www ¶¶¶ÇÇÇÇÇÇßßßýýýýýýýýýýýýïïïýýýýýýççççççïïïçççßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýý×××wwwïï着ªwwwwww†††fffÇÇÇ–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýèÇÇËccÔ''êõ÷îÕ%%§TT––––––‘‘‘†††ppppppfff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVVNNNNNNVVVppp‘‘‘¶¶¶ÎÎΰ°°‘‘‘–––†††ppp^^^VVV^^^^^^www ªªª ¶¶¶ßßßÎÎΰ°°‘‘‘pppGGG333---"""333GGGGGG333"""---------EEEVVVVVV^^^^^^^^^VVVVVVVVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVVNNNNNNEEE===333333333333333333===333333===333===333===333============333=========================================================EEE===EEE===EEEEEEEEE===EEE===EEE======EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^VVV^^^VVVNNNGGGNNNVVVVVVVVVVVVVVVVVVVVVNNNVVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVV^^^^^^^^^^^^fff^^^fff^^^fffffffffffffffpppfffffffffffffffppp‘‘‘^^^EEEGGGNNNNNNEEE=========EEEEEEEEEVVVwwwwww†††–––wwwÁÁÁ†††fffppp–––ÁÁÁÇÇÇçççýýýýýýýýýïïïýýýýýýýýýççççççïïïïïïççççççýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýý°°°–––ÎÎΪªªppp××× °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��Ù--“ŽŽ†††‘‘‘‘‘‘‘‘‘pppffffff^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^^^^VVVVVV^^^ppp‘‘‘°°°°°°pppwwwppppppfff^^^^^^VVV^^^VVV^^^ppp^^^GGG333---------===GGGVVVVVVVVVNNN333""""""EEEVVV^^^VVV^^^VVVVVVVVV^^^VVV^^^VVVVVVVVV^^^VVVVVVVVV^^^VVV^^^VVVVVVNNNEEE333======333333===333333===333===333===333======333===333===============================================================EEE===EEEEEEEEE===EEEEEEEEE===EEEEEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVVVVNNNNNNGGGNNNNNNVVVVVVVVVVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffpppfffppppppfffpppfffpppfffpppwww‘‘‘ –––^^^VVVwwwNNNEEEEEE===EEEGGGVVVGGGEEEVVVwwwwwwwww °°°‘‘‘^^^^^^†††ÁÁÁßßßçççïïïïïïýýýïïïýýýïïïýýýççççççýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎÎïïï ¶¶¶www°°° ßßßýýýýýýýýýýýýýýýýýýýýýýýýú��ùû‰‰á³¨¨šVVéú��ú��ú��ºBB‘‘‘‘‘‘‘‘‘‘‘‘––––––†††^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVV^^^ppp†††ªªª–––‘‘‘¶¶¶ÎÎÎ ‘‘‘†††fffNNNGGG===333------------333===EEEEEEGGGVVVNNNGGGEEE333""""""""""""""""""NNNVVV^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^^^^VVV^^^VVV^^^VVVVVVVVVNNN======GGGGGGEEE===333===333333===333===333===333======333=========333=========================================================EEEEEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVVNNNVVVVVVVVVVVV^^^VVV^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^^^^fffffffffffffffffffffpppfffppppppppppppffffffppppppfffpppfffpppfffªªªwww‘‘‘‘‘‘wwwppp†††‘‘‘NNNEEEEEEEEEEEENNN^^^VVVEEEEEEVVVppp^^^fff°°°ÇÇǰ°°fffpppwww†††ÇÇÇ×××ïïïççç×××ßßßýýýïïïýýýççç×××ïïïÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççýýýßßßßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýý––––––‘‘‘GGGwww‘‘‘VVV°°°çççýýýýýýýýýýýýýýýýýýýýýúZZüïïýýýýýýïïï××ײllú��ú��ú��æ‘‘‘––– –––‘‘‘wwwfffú��ú��^^^^^^^^^VVV^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNNNNVVV^^^ppp‘‘‘ªªª¶¶¶¶¶¶NNNEEE===---------333======EEEEEE======EEE333---333333333333------""""""""""""""""""---VVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVV^^^VVVVVVGGG======GGGNNNVVVNNNGGGEEE===333===333333333===333===333======333===333=========================================================EEE===EEEEEE===EEE======EEE===EEEEEE======EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVVVVVVVV^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^VVV^^^ffffffffffffffffffffffffffffffpppppppppppppppppppppfffpppppppppppppppfffpppffffffpppwww†††www‘‘‘www‘‘‘†††VVVEEEEEE===EEEGGGVVVNNNEEE===EEENNNfff^^^†††ªªª†††ppp¶¶¶–––‘‘‘wwwÇÇÇçççïïïççççççïïïýýýçççïïïçççÎÎΪªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýççç××תªªVVV†††–––ÇÇÇ‘‘‘ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçÝÝú��ú��ú��ö‘‘‘‘‘‘wwwwwwffffff^^^^^^ú��ú��^^^^^^^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGGGGGGGNNNNNNNNNVVVVVVVVVVVVNNNGGGEEE333===EEEEEEEEEEEEEEE===333333------"""""""""---"""------""""""""""""---""""""---VVV^^^^^^VVVVVVNNNVVVVVV^^^^^^^^^^^^VVV^^^VVVVVV^^^VVV^^^^^^VVVNNN======GGGNNNVVVVVVVVVVVVNNNGGGEEE======333===333===333============333===============================================================EEEEEEEEE=========EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^fffffffffffffffffffffpppfffffffffppppppppppppppppppppppppfffppppppppppppppppppffffffffffff^^^www†††‘‘‘†††††† †††^^^EEEEEEEEEEEEEEEGGGGGGEEEEEEEEE===GGG^^^VVVfff–––www¶¶¶°°°^^^fff ×××ÎÎÎçççïïïïïï×××ßßßïï着ª¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××www–––†††ÁÁÁçççfffVVVªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýûÞÞú��ú��ú��ôwwwpppffffff^^^fff^^^^^^ú��ú��^^^^^^^^^VVVVVVNNNNNNGGGGGGGGGGGGNNNGGGNNNGGGEEEEEE=========EEEEEEGGGGGGGGGGGGEEE===333333------""""""""""""""""""""""""""""""---------333"""""""""------""""""===VVV^^^^^^^^^NNNEEEGGGVVV^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVVVVVGGGGGGGGGVVV^^^^^^VVV^^^VVV^^^VVVNNNGGGEEE===333===333======333===333======333=========333==============================EEE=========EEE===EEE===EEE=========EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffpppfffpppfffppppppppppppppppppppppppwwwppppppwwwpppwwwpppppppppfffffffffffffffpppppppppfffppp‘‘‘†††ppp––– fffGGGEEEEEEEEEEEEEEEEEENNNNNNNNNEEE===GGG^^^VVVVVVfffÁÁÁÇÇǰ°°†††VVV^^^ªªªÁÁÁïïïïïïßßßÎÎÎïïïÁÁÁÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÎÎΆ††×××çççpppfffNNN–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýúwwú��ú��ú��ò†††pppffffff^^^^^^fff^^^ú��ú��VVVVVVNNNGGGNNNGGGNNNGGGNNNGGGGGGGGGEEE======EEEEEEEEEGGGNNNGGGGGGEEE======333------"""""""""""""""""""""""""""""""""""""""NNNEEEGGGNNN"""---333""""""""""""EEEGGG333"""EEE^^^^^^^^^^^^NNNEEEEEEVVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^VVVNNNVVVVVV^^^VVV^^^^^^^^^VVV^^^VVVVVVVVVNNNNNNEEE======333======333=====================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffffffffffffff^^^fff^^^^^^^^^fff^^^fff^^^fff^^^fffffffffffffffpppfffpppppppppfffpppppppppppppppppppppppppppwwwwwwpppwwwpppppppppfffffffffffffffpppppppppppppppVVV^^^fffpppppp°°°–––NNNEEEEEE===EEEEEEEEE^^^ppp^^^NNNGGG===EEEVVV^^^pppÎÎÎÇÇǪªª¶¶¶ ^^^fffwwwªªªçççýýýïïïÁÁÁïïïïïïçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΰ°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççÎÎÎwww†††ppppppïïïýýýýýýýýýýýýýýýýýýýýýýýýü´´ùú��ú��ú��ùIIßßß wwwfffffffff^^^^^^ú��ú��GGGNNNGGGNNNNNNVVVNNNNNNGGGEEEEEEEEEEEEGGGGGGGGGEEEEEEEEE===333333---------""""""---"""""""""""""""---""""""""""""""""""""""""---===GGG---GGG^^^NNNfffGGGEEEVVVVVV333GGGNNN===GGGGGG---"""GGG^^^^^^^^^^^^VVVGGGNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVVNNNGGGEEE======333======333===333=========333================================================EEE===EEE===EEE===EEE===EEEEEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEE^^^fffffffff^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffppppppppppppfffpppppppppfffpppppppppppppppwwwwwwwwwwwwwwwwwwpppppppppfffffffffpppppppppfffpppfffffffffGGG^^^ffffff wwwªªªVVVEEEEEE===EEEEEE^^^^^^fffVVVEEE===EEENNNwww–––ÁÁÁ×××ÎÎΪªª†††GGGNNNªªªçççïïïÇÇÇïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎwwwpppßßß¶¶¶×××ýýýýýýýýýýýýýýýû°°ùú��ú��ú��ú��û¯¯ýýýçççÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGNNNGGGGGGGGGGGGGGGEEE======333333------------"""---""""""---"""""""""---"""---===GGGEEE"""""""""""""""---333333===GGGVVVVVV^^^333EEEVVV^^^EEEGGGNNN333^^^EEENNNVVVNNNGGG^^^GGG===EEE333""""""NNN^^^^^^VVVVVVVVVVVVVVV^^^VVV^^^^^^VVVVVVVVV^^^^^^^^^^^^fff^^^^^^VVV^^^VVVVVV^^^VVVVVVVVVVVVVVVVVVVVV^^^VVVVVVVVVNNNNNNGGGEEE======333===333=========333======================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEEEEEEEEEEEEEEEEEEEEEEEEEE===EEEfff^^^^^^fff^^^fff^^^fffffffffffffffffffffppppppfffppppppfffpppfffppppppfffpppppppppppppppppppppwwwwwwwwwwwwwwwppppppppppppfffpppppppppppppppfffpppfffppppppfffGGGVVVwwwfff‘‘‘°°°–––‘‘‘°°°‘‘‘VVVGGGEEEEEEEEEVVVfffpppwww^^^GGGEEE======GGGffffff†††ÇÇÇÁÁÁÇÇǰ°°VVV===NNNªªªççççççïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°ýýýýýýýýýýýýýýýýýýçççÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶fffppp ^^^pppçççýýýýýýýýýû¦¦ùú��ú��ú��ú��úttýýýýýýýýýýýýú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��GGGEEEEEE=========333------------"""---""""""---""""""---"""""""""---===GGGEEE===VVVpppNNN---333===---===VVVVVVGGGVVVffffff^^^fffGGGVVVEEEVVVEEENNN======NNNGGGfffNNN===GGG^^^333---------VVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVVVVV^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNEEE==============================================================================EEE===EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEE===EEEEEE===EEEEEE^^^fff^^^fff^^^ffffffffffffffffffffffffpppfffppppppfffppppppppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwppppppppppppfffppppppfffffffffpppfffpppfffppppppppppppEEEGGGppp†††www–––––––––ªªª¶¶¶–––^^^GGGEEEEEENNN^^^wwwpppfffVVVGGGGGGEEEEEEEEENNN^^^www ¶¶¶ÁÁÁ°°°^^^GGGNNN–––ßßßïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××pppýýýýýýýýýýýýýýýýýýßßß×××ÎÎÎçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ppp–––‘‘‘ÁÁÁÁÁÁªªªûœœùú��ú��ú��ùû‰‰ýýýýýýýýýýýýýýýýýýççç¶¶¶ ú��ú��GGGGGGGGGEEE333333333------------"""---""""""---""""""---""""""---"""---------"""333VVVpppfffVVVpppwwwGGGEEEVVVfffGGGGGGfffEEEEEE^^^NNN^^^NNN^^^GGG^^^GGGVVVEEE^^^333NNNGGGVVVNNN===333EEE^^^===---"""---VVV^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVV^^^VVVVVVVVV^^^^^^fff^^^^^^GGGEEE===========================================================================EEE===EEE===EEE===EEE===EEE===EEEEEE===EEE===EEEEEE===EEEEEE===EEEffffffffffffffffffffffffpppfffpppppppppppppppfffppppppppppppppppppwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfffpppfffffffffppppppfffpppppppppwwwpppppppppfffGGGGGGVVVwww‘‘‘wwwwwwªªªªªªªªª°°°¶¶¶–––VVVGGGGGGGGGVVVppppppwwwpppVVVNNNGGGEEE======EEENNNfff ¶¶¶ÁÁÁ°°°‘‘‘NNNNNNªªªïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇfffwwwýýýýýýïïïýýýýýýýýýýýýýýýçççÇÇÇ×××ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎζ¶¶ ªªª‘‘‘ªªª¥::ùú��ú��ú��ùû››ýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªú��ú��===333333------"""---""""""""""""---""""""---""""""---""""""------===GGGGGG333---333fffwwwppppppppppppNNN===NNNfffNNNVVV^^^---===^^^NNN^^^VVV^^^NNNfff^^^NNN===VVV333VVVEEENNNfffGGGGGG^^^NNN333"""===^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^VVVVVV^^^VVV^^^^^^^^^VVV^^^^^^VVV^^^VVV^^^VVV^^^^^^VVVVVVVVV^^^^^^ffffffpppfffffffffVVVGGGEEE==============================================================================EEEEEE===EEE===EEE===EEEEEEEEE===EEEEEE===EEEEEE===EEEEEEfffpppfffpppfffpppfffpppfffppppppfffpppfffppppppppppppwwwwwwpppppppppwwwpppwwwwwwwwwwwwwwwwwwwwwpppwwwpppfffppppppppppppfffppppppppppppppppppppppppwwwpppppppppNNNGGGNNN^^^‘‘‘www†††¶¶¶ÁÁÁ°°°°°°¶¶¶ fffNNNGGGVVVpppwwwªªªfffVVVNNNGGGEEE=========VVVfff °°°°°°ÎÎÎfffEEEVVV×××ïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ^^^wwwïïïýýýïïïýýýýýýýýýýýýýýýýýýïïïÇÇǰ°°ÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶pppfff°°°ÃLLú��ú��ú��ú��ùû§§ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°ú��ú��---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===VVVppppppGGG---===fffppppppfff^^^pppGGGEEEVVVpppNNN^^^VVV---EEE^^^VVVVVVVVVGGG===VVVNNN------==="""333"""333EEE---===GGG333GGG^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^VVV^^^VVV^^^^^^^^^fffpppfffpppppppppfffffffffVVVGGGEEE======================================================EEE===EEE======EEE===EEE===EEE======EEE===EEE===EEE===EEEEEEEEE===EEE===EEEfffpppppppppppppppppppppffffffpppppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwppppppwwwppppppppppppfffpppfffpppfffpppppppppwwwwwwwwwwwwwwwwwwwwwppppppwwwppp^^^VVVNNNVVVfffwww††††††ªªªÁÁÁÁÁÁ°°°°°°ÁÁÁÁÁÁfffNNNNNN^^^ppp°°°VVVNNNGGGGGGEEEEEE======NNNNNN ÇÇÇççç–––EEEEEE ïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß‘‘‘fffïïïýýýýýýïïïýýýýýýýýýýýýýýýýýýýýý×××ÎÎÎßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßpppVVVÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýý××× ú��ú��"""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVV^^^^^^pppGGG---EEEfff^^^ppp^^^VVVpppGGGVVVfffpppNNN^^^NNN---EEENNNGGG===333------""""""GGG^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^VVV^^^^^^^^^fffpppfffpppfffppppppppppppffffff^^^NNNGGG======================================================EEE======EEE===EEE===EEE======EEE===EEE===EEEEEE===EEEEEE===EEEEEE===EEEpppppppppwwwwwwpppppppppfffpppfffpppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffppppppwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppp^^^^^^VVVVVV^^^VVV–––‘‘‘–––¶¶¶ÇÇǰ°°°°°×××°°°pppNNNVVVVVVwww–––‘‘‘VVVNNNGGGGGGGGGGGGEEE=========GGG†††ßßßÁÁÁpppEEE†††ýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎ^^^VVV‘‘‘ïïïïïïïïïýýýïïïýýýýýýýýýýýýýýýýýýýýýçççÇÇÇÎÎÎßßßïïïýýýýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁßßßú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ýýýýýýýýýýýýýýýýýýÎÎΖ––ú��ú��""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""---NNNEEENNNfffEEE---===pppVVVVVVNNN^^^pppGGGfffffffffEEEGGG===""""""---""""""""""""NNN^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffffffffpppfffpppfffpppfffppppppfffffffffVVVGGGEEE===========================================================================EEE===EEE===EEEEEEEEEEEE===EEEEEEEEE===EEEEEEppppppppppppppppppfffppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwpppppppppffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwppppppppp^^^www†††VVV^^^NNNVVV†††ªªª‘‘‘–––ÁÁÁÁÁÁ°°°°°°ÁÁÁ¶¶¶NNNVVVfff‘‘‘†††VVVNNNNNNGGGNNNGGGGGGGGG===333===NNN‘‘‘–––‘‘‘fff–––ïïïççç×××ýýýýýýýýýýýýýýýýýýýýýýýýßßßfffVVVppp ×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎÁÁÁßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççwwwßßß‘‘‘–––çççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––fff333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---===^^^VVV---"""EEEpppVVVEEEEEE^^^^^^===EEEGGG===------""""""""""""""""""333"""---VVV^^^^^^^^^ffffff^^^fff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffffpppfffffffffpppfffpppfffpppffffffpppffffff^^^^^^VVVGGGEEE==========================================EEE============EEE======EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEppppppfffpppfffpppfffpppppppppppppppppppppppppppwwwpppwwwpppppppppppppppfffppppppfffpppffffffpppppppppwwwwwwwwwwwwwwwwwwppppppppppppwwwpppppppppppp^^^pppªªªfffNNNNNNppp‘‘‘–––––––––¶¶¶ÇÇǰ°°°°°ÎÎÎÎÎÎwwwVVVNNNpppwww^^^GGGGGGNNNGGGNNNGGGNNNGGGGGG===EEEfff†††www^^^–––ïïïßß߆††çççýýýýýýýýýýýýýýýýýýýýýïïï–––^^^NNNNNN^^^ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßçççýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁßßßwwwÁÁÁªªªpppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇ–––fff---"""""""""""""""""""""""""""""""""""""""""""""---------"""""""""---NNN^^^EEE333---EEEpppNNN333333GGGEEE---------"""""""""""""""""""""""""""""""""""""""------------333---333^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffpppfffpppffffffffffffpppffffffffffffpppfffpppfff^^^^^^NNNEEE=========================================================EEE===EEE===EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEfffpppppppppwwwpppwwwppppppppppppwwwpppppppppppppppppppppppppppffffffpppfffpppfffpppppppppppppppwwwwwwwwwwwwwwwppppppppppppwwwwwwpppwwwwwwpppffffff¶¶¶¶¶¶wwwVVVGGG^^^www–––ªªª––– ¶¶¶¶¶¶¶¶¶°°°ÇÇÇÇÇdž††VVVVVVffffffNNNGGGNNNNNNGGGNNNNNN^^^†††^^^GGGVVVwww†††VVVpppßßßÇÇÇwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççNNN^^^‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýçççpppÇÇÇfff^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïÁÁÁ‘‘‘VVV---""""""""""""""""""""""""""""""""""""""""""---GGGVVV==="""""""""EEEfffVVVNNNGGG---===VVVEEE---------"""---""""""---"""""""""""""""""""""""""""333"""---333333333======""""""333333---===VVVfff^^^^^^^^^^^^fffVVVVVV^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffffffffffffffffffffffpppfffpppffffffffffff^^^VVVGGGEEE======================================================EEE===EEE===EEEEEEEEEEEEEEEEEEEEEEEE===EEEEEEEEEEEEwwwwwwwwwwwwwwwppppppppppppppppppppppppfffpppfffpppfffffffffpppfffpppfffpppfffppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwfffppp¶¶¶¶¶¶–––^^^VVVVVVppp °°° ªªªÎÎΪªª ÁÁÁÁÁÁppp^^^fffNNNGGGGGGNNNNNNNNNNNNwww¶¶¶ ^^^EEENNNppp^^^ppp‘‘‘††††††¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýïïï–––ªªª×××ïïïýýýýýýïïïïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýïïï°°°ÁÁÁVVVNNN^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ†††NNN---"""""""""""""""""""""""""""""""""---------EEE^^^NNN===""""""---VVVwwwpppfffEEE------------""""""---"""---"""---""""""""""""""""""""""""""""""""""""===EEE======EEE===------""""""333333---"""GGG^^^^^^^^^fff^^^^^^NNNVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^ffffff^^^ffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffffffffffffffffffffff^^^VVVGGGEEE==========================================EEE======EEE===EEEEEEEEEEEEEEEEEEEEE===EEE===EEEEEEEEEEEEwwwwwwwwwpppwwwppppppwwwpppfffpppppppppppppppfffpppppppppfffpppfffpppppppppwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwww†††^^^^^^ªªª¶¶¶ªªªfffVVVfffppp––– ––––––¶¶¶°°°ªªª–––ªªª¶¶¶ªªª^^^^^^NNNGGGGGGNNNVVVNNN^^^ªªª×××¶¶¶†††NNNEEEGGGNNNfff‘‘‘°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýýÁÁÁ‘‘‘ÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßßýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßßfff–––¶¶¶ßßß°°°×××ýýýýýýýýýýýýýýýýýýççç¶¶¶†††GGG---""""""""""""""""""""""""""""""333GGGVVVEEEGGGppp^^^GGG---"""333^^^fffVVVEEE---""""""""""""---"""------"""---""""""""""""""""""""""""""""""------"""""""""---===EEE===333---"""---======333---NNN^^^^^^^^^^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffpppfffpppfffffffffffffffpppffffff^^^NNNEEE=============================================EEE===EEEEEEEEEEEEEEE===EEE===EEE===EEE===EEEEEEEEEwwwwwwwwwwwwpppppppppfffppppppfffpppffffffpppppppppfffpppppppppwwwwwwwwwwwwwwwwwwpppppppppwwwwwwpppwwwwww††††††www^^^^^^††† –––fff^^^fffpppwww‘‘‘ªªª ––– ÁÁÁ ‘‘‘ªªªÎÎΆ††wwwNNNNNN^^^fffppp^^^fffÇÇÇÎÎÎÁÁÁ–––pppGGGGGGGGGfff†††‘‘‘ªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýïïï°°°†††ªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý×××çççýýýýýýýýýýýýýýýýýýýýýïïï‘‘‘¶¶¶çççÎÎÎVVV†††ýýýïïïýýýýýýýýýýýýççç¶¶¶‘‘‘EEE""""""""""""""""""---===333"""---GGG^^^pppGGG===NNNppp^^^---"""---EEE===---""""""""""""---""""""---""""""---""""""---""""""---------""""""---===EEE===---333---"""---""""""=========333VVV^^^^^^^^^VVVNNNVVV^^^^^^^^^^^^^^^^^^fffVVV^^^^^^^^^fff^^^fff^^^fff^^^^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppffffffffffffffffffffffffffffffffffffffffffpppfffpppfffppppppffffff^^^VVVGGGEEE====================================EEE===EEE===EEEEEEEEEEEEEEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwpppppppppppppppppppppppppppppppppfffpppppppppwwwwww†††wwwwwwppppppwwwwwwwwwppppppwwwwww†††††††††††††††ppp^^^^^^––––––†††fffffffffpppwww‘‘‘ ‘‘‘ªªª°°°ªªª–––ªªª°°°¶¶¶fffVVVpppwwwwwwpppfff¶¶¶ÎÎÎÎÎÎ †††fffVVVfffppp‘‘‘ ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶‘‘‘ °°°×××ýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶×××ýýýýýýýýýýýýýýýýýýýýý××׆††ÁÁÁÁÁÁppp†††çççÇÇÇçççýýýýýýýýýççç¶¶¶†††===""""""""""""------EEE^^^==="""333^^^fffVVV===EEENNNfffNNN---""""""---""""""---"""""""""""""""---""""""---"""------------"""---=========---333EEEEEE333"""---""""""333======333^^^^^^^^^NNNNNNVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^fff^^^fff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppffffffffffffpppfffffffff^^^NNNEEE========================EEE==================EEE===EEE===EEE=========EEE======EEE===EEEwwwwwwwwwwwwpppwwwpppwwwpppppppppppppppfffppppppppppppwwwwwwwwwwwwpppwwwwwwwwwwwwwww††††††††††††wwwppp^^^VVV†††ªªªªªª‘‘‘†††^^^ppppppwww‘‘‘ªªª–––††† ¶¶¶°°°–––°°°ÎÎΰ°°ppppppfffpppwwwfff °°°ÁÁÁªªª†††www‘‘‘fffppp‘‘‘–––ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ÁÁÁ¶¶¶×××ýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýßßßßßßýýýïïïýýýïïïýýýýýýýýýýýýïïï××תªªppp¶¶¶ççç ïïïýýýýýýççç°°°===---"""---333======NNNppp333"""===pppfffGGGGGGGGGfffNNN==="""""""""""""""---"""""""""""""""---""""""---"""------333===------===GGGEEE333---333333---""""""""""""---======EEEVVVVVVNNNNNN^^^^^^VVV^^^VVV^^^^^^fff^^^VVV^^^^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffff^^^ffffffffffffpppfff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffffffffffffffpppffffffffffff^^^VVVGGG==================EEE===============EEE=========EEEEEE===EEE=========EEE===EEE===EEEwwwwwwwwwwwwwwwwwwppppppwwwppppppfffpppppppppwww††††††wwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††ppp^^^^^^‘‘‘¶¶¶°°°ªªªppp^^^ppppppwww‘‘‘ªªª‘‘‘†††–––ÁÁÁ¶¶¶––– ÎÎÎÇÇÇwww^^^VVVfffwwwfffwww–––†††www–––VVVppp––––––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççççç–––×××ççç×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïïïïßßßÁÁÁïïïýýýýýýýýýýýýïïïVVV°°°çççÁÁÁ‘‘‘çççýýýýýýçç窪ªEEE333---333NNNNNNGGGVVVppp333"""333fffppp^^^GGG===333333""""""""""""""""""""""""""""""""""""""""""---333333---333EEEGGG===333GGGEEE===---"""""""""""""""""""""""""""---"""===333GGGVVVVVVVVVVVVVVVVVV^^^VVV^^^^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffpppfffffffffffffffpppfffffffffpppfffffffff^^^fff^^^^^^GGGEEEEEE===EEE==============================EEE======EEE===EEE=========EEE===EEEEEEwwwwwwwwwwwwwwwppppppfffwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwww††††††††††††††††††www^^^fff––– ¶¶¶†††ffffffpppwww‘‘‘‘‘‘ †††–––ÁÁÁ¶¶¶‘‘‘‘‘‘¶¶¶¶¶¶wwwffffffwww^^^ppp ppp^^^ffffff‘‘‘–––^^^VVV––––––×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßß߆††×××ýýý×××çççýýýýýýýýýýýýïïïýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïççç°°°çççýýýýýýýýýýýýýýý¶¶¶^^^°°°ÎÎÎÎÎÎ×××ýýýýýýýýýßßߪªªwwwGGGEEE======^^^GGGEEENNNpppEEE333EEENNN^^^NNN---""""""""""""""""""""""""""""""""""""""""""---"""---===EEEEEEGGGGGGGGGEEE333---333---"""""""""""""""""""""""""""---333=========---------NNN^^^VVVVVV^^^VVV^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^ffffffffffffffffffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^^^^fffffffff^^^VVVEEEEEE===EEE===EEE========================EEE======EEE======EEE======EEE===EEE†††wwwwwwpppppppppwww††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘††††††www^^^^^^–––°°°°°°–––wwwfffpppppp†††°°°‘‘‘††† ÁÁÁ°°°‘‘‘†††°°°ªªªpppffffffpppwwwfff^^^^^^ wwwGGG–––ÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßßßß¶¶¶ßßßïïïÎÎÎçççýýýýýýýýýïïïÇÇǰ°°ßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÇÇǶ¶¶çççßßßýýýýýýýýýïïïßßßçççßßßßßßýýýýýýýýýýýýßßß pppEEEEEE======NNNEEE333GGGppp^^^VVVNNN333---"""""""""""""""""""""""""""---""""""---"""---333333EEEEEEGGGGGGGGGEEE===333------""""""---"""""""""""""""""""""""""""333======EEEEEE===333"""---VVV^^^VVVVVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^ffffffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^^^^^^^VVV^^^ffffffpppfffpppffffff^^^GGGEEE=======================================EEE===EEE======EEE===EEE===EEE†††††††††wwwwwwwwwwww††††††††††††††††††††††††wwwwwwwwwwwwpppwwwpppwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††www^^^fff†††¶¶¶ÁÁÁ°°°pppfffpppwwwwww†††‘‘‘–––†††ªªª¶¶¶ªªªwwwwww‘‘‘pppwww^^^www†††††††††fffpppppp‘‘‘‘‘‘GGGppp–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï¶¶¶°°°°°°çççççç×××ßßßýýýýýýýýýÎÎΆ††††† ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××ÇÇÇÇÇÇïïïýýýýýýýýýýýýýýýïïïïïïýýýýýýýýýýýý××תªªpppEEE333======EEE===333EEEpppfffNNN===""""""------"""------"""""""""---""""""---333EEEEEEGGGGGGGGGGGGEEE333333---""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""333======EEEEEE===333""""""333VVVVVVVVVVVVVVV^^^^^^^^^VVV^^^^^^^^^^^^ffffff^^^^^^^^^fff^^^^^^fff^^^ffffffffffff^^^ffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^VVV^^^fffpppfffppppppfffpppppppppfff^^^NNNEEE=======================================EEE===EEEEEE===EEE===EEE===†††www†††‘‘‘‘‘‘†††‘‘‘††††††††††††wwwwwwwwwpppppppppppppppwwwwww††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††www^^^^^^†††ªªª°°°–––pppfffpppwwwpppªªª ‘‘‘‘‘‘ªªª†††wwwppppppwwwwwwppp††††††www‘‘‘†††‘‘‘–––^^^VVV °°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçç熆†¶¶¶ªªªçççßßßïïïïïïýýýççç°°°‘‘‘¶¶¶ÇÇǪªªÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªpppEEE333=========333---333GGGEEE---"""---===EEE======333EEE------333333333"""333GGGEEEEEE===333------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------333333333"""""""""===VVVVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffff^^^^^^^^^ffffffffffff^^^fffffffffpppffffffffffff^^^ffffff^^^fffffffff^^^^^^VVV^^^^^^fffffffffppppppfffppppppfffppppppppppppfffNNNGGG=========EEE===========================EEE===EEE===EEE======EEE††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††wwwwwwwwwpppfffppppppwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††wwwfff^^^‘‘‘¶¶¶¶¶¶fffppppppppp–––°°°†††††††††°°°†††††††††fffwwwpppwww––––––‘‘‘–––†††NNN–––¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁ^^^†††wwwfffÁÁÁïïïçççßßßýýýÎÎÎÁÁÁ¶¶¶ÇÇǪªªÁÁÁ ßßßýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýïïï×××ßßßÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïï××× pppEEE333333===------"""""""""""""""333EEENNNNNNGGGGGGEEEEEEEEENNNNNNEEE333EEE===------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---EEEVVV^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^fff^^^fff^^^fffffffff^^^fffffffffVVV^^^^^^ffffffffffffffffff^^^fffffffffffffff^^^fff^^^fffffffffffffff^^^^^^VVV^^^^^^ffffffpppfffpppfffpppppppppppppppppppppppppppfff^^^GGGEEE======EEE=====================EEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwwwwwwwfffppppppwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††wwwfff^^^†††°°°†††–––wwwfffppppppppp°°°ªªª††††††°°°°°°–––ªªª‘‘‘ppppppfff^^^www†††‘‘‘–––‘‘‘–––pppÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï–––NNNfff–––ÁÁÁßßßÎÎÎ ïïïççççççÁÁÁ°°°°°°ßßßÁÁÁ¶¶¶ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ×××çççýýýýýýýýýýýýýýýýýýïïï××× pppEEE333""""""""""""---333333======GGGVVVVVVNNNGGGGGGEEEEEE===---333===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^ffffff^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^fff^^^^^^ffffff^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fffffffffffffffffffffpppfffffffff^^^fffffffffffffff^^^^^^VVVVVVfff^^^ffffffpppfffffffffpppfffpppfffpppppppppppppppppppppfff^^^GGGEEEEEE===EEE===EEE======EEE======EEE===EEE===EEE=========‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppfff‘‘‘‘‘‘ªªªªªª‘‘‘pppppppppppp†††°°°ªªª†††‘‘‘ªªª‘‘‘ †††wwwppppppVVVpppwww‘‘‘‘‘‘‘‘‘wwwfffÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïppp^^^‘‘‘°°°ÁÁÁ×××ßßß çççïïïïï着ª‘‘‘‘‘‘¶¶¶ßßßÎÎÎ×××ýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýïïï×××çççýýýýýýýýýýýýýýýýýýïïï××× ^^^===333---"""---333GGGGGGNNNEEE===EEEGGGGGGGGG===333------"""""""""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^^^^ffffffffffff^^^^^^^^^fff^^^^^^fff^^^^^^^^^fff^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppffffff^^^^^^ffffffffffff^^^VVV^^^^^^ffffffffffffffffffpppffffffpppfffpppppppppfffppppppppppppppppppfffNNNEEEEEE===EEE===EEE======EEE=========EEE============EEE‘‘‘–––‘‘‘–––‘‘‘†††‘‘‘†††††††††††††††††††††wwwwwwpppfffpppwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††‘‘‘‘‘‘–––––––––‘‘‘fff^^^wwwªªª¶¶¶°°°†††fffffffffppp†††ªªª–––wwwwww†††pppfffwwwpppwww^^^pppfffpppwww†††‘‘‘pppwwwÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÎÎÎppp‘‘‘°°°–––¶¶¶×××^^^¶¶¶ïïïççç¶¶¶†††ppp‘‘‘ ïïïýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––NNN======333333---""""""EEENNNVVVVVVGGG===EEEEEE===333---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""---""""""""""""---333VVVfff^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^^^^fff^^^fff^^^^^^^^^^^^^^^ffffffpppfffpppfffpppfffffffffffffff^^^fffffffffffffffffffffffffff^^^ffffffffffff^^^^^^VVV^^^^^^ffffffpppfffffffffffffffppppppfffpppfffpppfffpppppppppppppppwwwppppppfffVVVEEEEEE===EEE===EEE=========EEE======EEE============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwwwwwwwwww†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––– ––––––†††ppp^^^°°°¶¶¶ªªªpppffffffpppwww–––wwwwwwwwwpppfffwwwpppwww†††ppp^^^pppwww†††‘‘‘‘‘‘‘‘‘°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁffffffppp–––ÁÁÁ‘‘‘VVV°°°ßßßçççßßß¶¶¶wwwppp^^^wwwÎÎÎýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ–––NNN=========333333""""""===NNNVVVGGGGGGEEE======333333333"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""EEE^^^^^^^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^fff^^^VVV^^^^^^fffffffffffffffffffffffffffffffff^^^fffffffffffffffpppfffffffffffffffffffff^^^fffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppVVVGGGEEE===EEE===EEE===EEE======EEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘wwwwwwpppwww†††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– –––‘‘‘ffffff°°°¶¶¶ wwwffffffpppwwwwwwwwwpppwwwwwwpppwww†††www–––wwwfffpppwww†††‘‘‘–––‘‘‘ªªªÁÁÁÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýÇÇÇNNN^^^VVVNNN^^^¶¶¶–––^^^††† ßßßïïïßßß‘‘‘^^^^^^fff^^^†††ïïïýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ NNN333======333333---"""---333======333============333---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""---"""""""""""""""---""""""""""""""""""---"""EEEVVV^^^^^^fffffffff^^^^^^fff^^^ffffff^^^fff^^^fff^^^fffVVVVVV^^^^^^fff^^^fffffffffpppfffffffffffffff^^^fff^^^ffffffffffffpppfff^^^ffffff^^^fffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppwwwwwwwwwwwwwwwwwwpppfff^^^GGGEEE===EEE===EEE===EEE======EEE======EEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††wwwwww†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– –––‘‘‘fff^^^ªªª°°°°°°wwwffffffpppwwwwwwwwwppppppwwwpppwww†††fff‘‘‘‘‘‘wwwfffppp‘‘‘†††–––––– çççýýýýýýýýýýýýýýýýýýýýýýýý×××NNNGGGGGGVVV^^^www°°°†††VVVfffpppÇÇÇßßß××× fff^^^^^^VVV^^^×××ýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ NNN======333333------===333333333333333333---""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""""""""""""""---"""""""""""""""---"""""""""""""""---""""""""""""""""""---"""---GGGffffffffffff^^^ffffff^^^fffffffff^^^fff^^^fffffffffVVVNNN^^^^^^fff^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppfffffffffppppppfffppppppfffpppfffpppppppppwwwpppwwwpppwwwpppwwwwwwwwwpppppppppfff^^^NNNEEEEEE============EEE===EEE=========EEE –––††††††††††††††††††††††††‘‘‘†††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––‘‘‘ppp^^^°°°ÁÁÁ–––wwwffffffpppwwwwwwppppppwwwpppfffppp^^^††††††‘‘‘wwwppp†††–––‘‘‘‘‘‘‘‘‘‘‘‘ÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýïïïfffEEEGGGfff†††www NNNVVVppp¶¶¶ïïïÁÁÁªªª–––www^^^NNNNNN ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ NNN===333------""""""---333333333---""""""""""""""""""""""""""""""""""""""""""---"""---""""""---"""""""""---""""""""""""""""""---"""""""""---""""""---""""""""""""---""""""""""""---"""---"""""""""---VVV^^^ffffffffffff^^^fffffffff^^^ffffff^^^fff^^^ffffffNNNNNN^^^fff^^^ffffffffffffffffffpppffffff^^^^^^fffffffffffffffpppffffffffffff^^^fffffffffffffffffffffffffffpppfffpppfffffffffppppppfffpppfffppppppppppppppppppwwwpppwwwwwwwwwwwwpppwwwppppppwwwppppppfffNNNEEE======EEE===EEEEEE======EEEEEE===‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††††††††††††††wwwwwwwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––‘‘‘fff^^^†††ªªª¶¶¶¶¶¶wwwffffffppppppppppppppppppffffffVVVppp‘‘‘‘‘‘‘‘‘pppwww‘‘‘‘‘‘–––wwwªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýÁÁÁNNNNNNppp¶¶¶–––pppGGGNNNppp‘‘‘××× °°°†††^^^NNNNNNpppßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁ VVV===---"""---------"""---------""""""""""""""""""""""""""""""""""""---"""---"""------------"""""""""""""""""""""---""""""""""""---""""""---""""""---""""""""""""""""""---333===---"""""""""333^^^ffffff^^^fff^^^fffffffffffffff^^^ffffff^^^ffffff^^^GGG^^^^^^fffffffffffffffpppffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppfffpppfffppppppppppppppppppppppppwwwpppppppppwwwwwwpppfffpppppppppppppppfffNNNEEE======EEE===EEE===EEE===EEE===‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––– –––†††wwwfff^^^www¶¶¶ÁÁÁªªªwwwffffffppppppppppppppppppfffNNNVVV‘‘‘†††‘‘‘fffVVVwww†††–––†††ppp‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýçççfffGGG××׆††‘‘‘wwwNNNGGGpppfff‘‘‘††† www^^^VVVVVVNNNÁÁÁýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶ NNN===---"""---333---""""""------""""""""""""""""""---""""""---"""------"""---"""------333======---""""""---""""""---""""""---"""---""""""---""""""---"""------"""---333333333===333---""""""---===^^^^^^ffffff^^^ffffff^^^fff^^^ffffff^^^ffffffffffffNNNGGG^^^^^^ffffff^^^fffffffffffffffffffff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffffffffpppffffffffffffpppffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppwwwppppppfffpppfffpppfffpppppppppfffNNNEEEEEE=========EEE===EEE======†††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––– –––––––––‘‘‘†††fff^^^wwwªªª¶¶¶ªªªffffffpppppppppppppppfffVVVNNNVVV†††wwwpppGGGNNNfffwwwwww^^^†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïpppNNN ×××¶¶¶fff†††wwwNNN^^^VVVVVV†††ªªª–––wwwVVVNNNNNNGGG‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶‘‘‘GGG333"""------333---"""""""""---"""---"""------""""""---"""------333------333===---======EEEEEE""""""---""""""---""""""---""""""""""""---""""""---------333===---333EEEEEE===EEE===---"""---"""EEE^^^^^^^^^^^^ffffffffffff^^^ffffff^^^ffffff^^^fffVVVNNNVVVffffff^^^ffffffffffffpppffffffffffff^^^fff^^^^^^ffffff^^^ffffff^^^ffffffffffffffffff^^^ffffffffffffpppffffffpppfffpppffffffpppfffpppppppppppppppfffpppppppppwwwwwwpppwwwwwwpppfffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE============††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††www††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††fffVVV†††°°°ÁÁÁ¶¶¶ffffffpppfffpppwwwppp^^^NNNNNNfffpppEEEEEEGGGVVVppp^^^pppÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýçççfff^^^¶¶¶ßßßÎÎÎwwwpppVVVVVVªªª°°°†††wwwVVVEEEEEEEEE^^^çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç°°°†††EEE------333---------"""---"""------"""---------------"""------"""---------333333======EEE===EEE======EEE---"""""""""---"""---""""""---""""""---""""""---333EEE333EEE===333======333333EEEEEE---""""""---GGG^^^fffffffff^^^fffffffffffffff^^^fff^^^^^^fffVVVNNN^^^^^^fffffffffffffffpppffffffffffffffffff^^^^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffffffffffpppfffffffffpppfff^^^ffffffppppppfffpppffffffpppppppppppppppwwwwwwwwwwwwpppfffffffffffffffpppfffpppfffpppfffpppfffNNNEEE===EEE===EEE=========†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††www†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘fffVVVwwwÁÁÁ¶¶¶°°°www^^^ppppppfffwwwwwwfff^^^NNNGGG^^^pppVVV======GGGNNNfff†††–––ïïïýýýýýýýýýïïïïïïýýýýýý×××VVV^^^ÁÁÁßßßçççÇÇÇppp†††www––––––°°° wwwfffNNNEEE======GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß°°°www===------333------------"""---------------333===333---------------===333333EEE===EEE333GGGEEEEEE=========""""""---"""---""""""---"""---"""---"""---------===EEE333GGG333======333333========="""---"""---NNN^^^^^^^^^ffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^ffffffpppffffffffffffffffff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffffffffffpppfffffffffffffff^^^ffffffppppppffffffppppppfffpppfffpppwwwwwwwwwwwwpppffffffffffffffffffffffffffffffpppfffppppppfffNNNEEE===EEE============–––‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††wwwwww††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘www^^^VVVwwwªªªªªª–––ffffffpppfffpppwwwfff^^^pppGGGNNN^^^GGG===EEENNN^^^ppp†††‘‘‘†††ßßßïïïýýýïïïÎÎÎ×××çççïïï GGG^^^ÁÁÁçççßßßßßß°°°†††pppppp‘‘‘ªªª–––fffVVVGGGEEEEEE===EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßߪªªfff333---------------"""---------------"""---======333333333===333EEE===333===GGG===EEE===EEE===EEE---333333---""""""---"""---"""---""""""---"""333======---EEE===---GGG333EEEEEE===333===333---"""""""""333VVV^^^^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^ffffff^^^^^^fff^^^ffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^fff^^^fffffffffffffffffffffffffffpppfffffffffffffffppp^^^^^^ffffffpppfffpppfffpppfffpppfffpppppppppwwwwwwwwwwwwfffffffff^^^^^^^^^fff^^^fffpppfffffffffpppfffppp^^^NNNEEE===============‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††fffVVVfff‘‘‘ªªª^^^pppfffpppfffppppppppppppNNNVVVVVVNNNEEEEEENNNpppwww†††wwwÇÇÇïïïççççç窪ª ßßßÇÇÇpppEEEVVVÁÁÁççççççççç××ב‘‘www^^^VVVfffpppfffVVVGGGEEEEEEEEE======pppïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß ^^^333---"""------"""333------------------333GGGEEE=========EEE333EEEEEE===EEEEEE===GGG======---333---"""---"""---"""---""""""---"""333===333333======GGG===EEE333333EEE---EEEEEE==="""---"""---"""""""""333^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^fffffffff^^^fff^^^fff^^^fffffffffpppffffffffffff^^^fffffffffffffffffffff^^^fff^^^fff^^^fffffffff^^^ffffffffffffffffffffffffffffff^^^fffffffffpppfffpppffffffpppfffpppfffpppppppppwwwppppppfffffffff^^^^^^ffffffffffff^^^ffffffffffffpppfffffffff^^^NNNEEE============†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘wwwfffVVVppp°°°ffffffpppfffffffffpppppp^^^^^^^^^NNNNNNGGG===GGGfffwwwfffÇÇÇïïïççççççÁÁÁ°°°ÎÎÎ ^^^EEEGGG¶¶¶çççïïïïïïßßß‘‘‘^^^fffNNNNNNNNNVVVGGGEEE======EEEEEE===NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªªNNN------------"""---333EEE333333===EEE===333GGG===EEEEEE===GGG333===EEEEEE===EEE---333333---"""---"""---"""---"""---"""---333=========GGGEEE333EEE333GGG333GGG333333GGG333---------""""""""""""""""""===^^^^^^fff^^^^^^^^^VVVVVV^^^fffffffffffffff^^^^^^fff^^^ffffffffffffffffffpppfff^^^^^^fffffffffffffff^^^fff^^^fff^^^fff^^^fff^^^^^^^^^fffpppfffpppffffff^^^ffffffffffff^^^ffffffpppffffffffffffpppfffpppfffppppppppppppppppppfffffffffffffffffffffffffffffffffpppfffpppfffpppfffffffffffffffNNNEEE=========‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘†††wwwfffVVVfff^^^fffpppfffffffffpppfff^^^VVVVVVVVVGGGEEE===EEENNN‘‘‘––– –––×××ïïïßßßçççÇÇǶ¶¶ ^^^EEEGGGwwwÎÎÎïïïïïïççç–––VVV^^^^^^NNNGGGGGGEEE==================EEEÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG------"""---------EEEGGG===EEEEEEEEEEEE===GGG===GGGEEEEEEEEE===EEE===333333------------"""---"""---"""---"""---333333333EEE===GGG===EEE===EEEEEE===EEE333GGG===---------"""""""""""""""""""""GGG^^^^^^^^^fff^^^^^^^^^fffffffffffffffffffff^^^fff^^^fffffffffffffffpppffffffffffffffffffffffffffffff^^^fff^^^^^^fff^^^^^^^^^fff^^^fffffffffffffffffffffffffffpppfff^^^pppfffpppfffffffffpppffffffpppfffpppfffppppppppppppfffffffffpppfffffffffffffffpppppppppppppppppppppppppppffffffppppppfffNNNEEE======‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††wwwfffVVV^^^fffpppfffpppffffffppp^^^NNNNNNGGGEEEEEE======EEEfff°°°¶¶¶¶¶¶çççïïïßßßÎÎΰ°°‘‘‘ ÎÎÎpppNNNGGGfff ßßßçççççç°°°VVVGGGffffffNNNEEE===333333333333333333EEE°°°ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××תªªGGG---"""------------EEEGGG===GGG===EEEEEE===GGG333GGGGGGEEE===---333------------"""------===------------------=========EEEEEE333GGG333EEE===EEEGGGEEE333---------"""---""""""""""""""""""""""""NNN^^^fff^^^^^^^^^fff^^^fff^^^^^^ffffff^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^ffffffpppffffff^^^fff^^^ffffff^^^^^^fff^^^^^^fffffffffffffffffffffffffffpppffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppfffffffffffffffffffffpppfffffffffffffffpppppppppwwwpppppppppppppppppppppffffffNNNEEE===‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††fffVVVVVVfffppppppfff^^^fffpppVVVNNNGGG===============NNN ªªª°°°ÎÎÎçççÁÁÁ–––––––––ªªª×××–––^^^GGG^^^–––ÁÁÁççççççÁÁÁfffEEEVVVwwwVVVGGG===333333333333===–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––EEE------------------EEEGGG===GGG===GGGEEE===EEE333333===333---------------333333------EEEGGG333333===EEE===333GGG---333EEEEEE===GGG333GGG333333===333---"""---""""""""""""""""""""""""""""""---VVV^^^^^^^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffff^^^ffffffffffffpppffffffffffffffffffffffffpppfff^^^fffffffff^^^fff^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppffffffffffffffffffffffffpppfffppppppfffpppffffffffffffffffffffffffffffffffffffpppfffpppppppppwwwwwwwwwwwwppppppwwwppppppffffffGGGEEE‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††fffNNN^^^ppppppfffffffffpppfffpppVVVEEEEEE333===EEEVVV†††‘‘‘ªªªÎÎζ¶¶†††‘‘‘ °°°‘‘‘pppVVVVVV†††ªªª×××çççÇÇdž††GGGGGGppp¶¶¶ÇÇÇ pppNNNEEE333333333===wwwïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΑ‘‘===------------------GGGGGGEEEGGG333EEE===333333------------------"""---333GGGEEE---333GGGNNN333======GGG======EEE------GGG======EEE---===---"""------"""---"""""""""""""""""""""""""""""""""333VVV^^^^^^^^^fff^^^fffffffff^^^^^^^^^VVV^^^fff^^^fffffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffff^^^fff^^^ffffffffffffffffff^^^ffffffffffffffffffppppppffffffpppfffpppfffpppfffppppppfffppppppffffffffffffffffffffffffffffffffffffpppfffpppppppppppppppwwwppppppwwwwwwppppppfffpppfff^^^GGG†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††fffNNNVVVpppppppppfffpppppp†††ªªªwwwGGGEEE===EEEfff†††††††††°°°×××××× †††††††††–––†††^^^VVVpppªªªÎÎÎßßßÎÎΖ––NNNVVV°°°ßßß×××°°°–––wwwGGG===333===333^^^ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††===------------------GGGEEE333===333---------------------------"""------===NNNGGG333===NNNGGG333======GGG===EEEEEE------EEE333333333------"""---"""""""""""""""""""""""""""""""""""""""""""""EEE^^^^^^^^^^^^^^^ffffffffffff^^^fffVVVVVV^^^^^^ffffffffffffpppffffffffffffffffffffffffpppfffffffff^^^ffffffffffffffffff^^^ffffff^^^ffffffffffff^^^fffffffffffffffpppfffpppfffffffffffffffffffffpppppppppffffffpppffffffpppfffpppffffffffffffffffffffffffpppppppppppppppwwwpppppppppppppppfffppppppfffpppfff^^^†††††††††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††fffVVVVVVppppppffffffppp‘‘‘¶¶¶ ^^^GGGEEEEEE^^^‘‘‘°°°–––ªªªÁÁÁ×××°°°‘‘‘‘‘‘†††–––°°°‘‘‘ppp–––ppp–––ÁÁÁßßßÎÎΖ––^^^www×××ßßß×××°°°°°°wwwNNNEEE=========VVVïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎ333---------"""------333333---------------------------------------------===NNNNNN===EEEGGGGGG333======GGG===EEEEEE------333------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""EEEVVV^^^^^^ffffff^^^fff^^^^^^ffffffVVV^^^^^^fff^^^ffffffffffffffffff^^^ffffffffffffppp^^^ffffffffffffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffpppfffpppffffffpppffffffffffffppppppfffpppfffpppfffffffffpppfffffffffffffffffffffffffffffffffppppppppppppppppppppppppppppppfffpppppppppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††pppVVVVVVpppffffffppp†††ªªª VVV^^^wwwVVVfff ††† ÎÎζ¶¶ ––– ‘‘‘¶¶¶ÁÁÁ‘‘‘ÇÇÇpppwww–––×××¶¶¶–––°°°ßßßçççÎÎΰ°°°°°VVVEEE=========NNNçççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇ333---------------------------------------333===------------======------EEEGGGNNNGGGEEEGGGEEE===GGGEEEEEE---===333"""---"""---"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGG^^^^^^^^^^^^fff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffff^^^fffffffffffffffffffffpppfffffffffpppffffffpppfff^^^ffffffpppfffppppppfffpppfffffffffpppfffpppppppppfffffffffffffffffffffffffffffffffppppppppppppwwwpppfffpppppppppppppppwwwpppppp†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††pppVVVVVVpppffffffwww†††‘‘‘^^^wwwªªªwwwppppppwwwªªªÁÁÁ¶¶¶ªªª–––††† ÁÁÁ××× °°°^^^°°°ªªª°°° ÇÇÇçççßßßÇÇÇ ªªªwww^^^GGGEEE======GGG×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇwww333---------------------------------------======333===============------EEEEEEGGGNNN===GGGEEE===EEE===333---"""------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---NNN^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffffffffffpppffffffpppffffffffffffpppfffpppfffpppfffpppffffffpppffffffppppppfffpppfffpppffffffffffff^^^fffffffffppppppfffpppffffffppppppppppppwwwppppppfffppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††wwwwww††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††††††††pppVVVVVVffffffwww†††ppp–––†††pppppp‘‘‘pppªªª¶¶¶ªªª‘‘‘‘‘‘‘‘‘–––ÎÎζ¶¶‘‘‘wwwNNNfff ÁÁÁ–––×××çççßßßÁÁÁ –––www^^^GGGGGGEEE===GGGÇÇÇýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÁÁÁfff333---------------------------------333333EEEEEE===EEE======GGG===------EEE===EEEEEE333EEE333------------"""---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---VVV^^^fff^^^fff^^^^^^ffffff^^^^^^^^^fff^^^ffffff^^^fffffffffffffffffffffffffffffffffffffffffffffffffff^^^fffffffffppp^^^fffffffffffffffffffffpppffffffpppfffffffffpppfffffffffpppfffpppfffpppffffffffffffffffffppppppfffppppppfffpppfffpppfffffffff^^^^^^^^^fffpppfffpppffffffffffffpppppppppwwwppppppfffffffff‘‘‘†††‘‘‘‘‘‘†††‘‘‘–––––– ‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††www†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††pppVVVNNNfffwww‘‘‘^^^www‘‘‘fffppp†††ªªª†††ªªª°°°ªªª‘‘‘‘‘‘ ‘‘‘–––×××ÇÇÇpppVVVVVVppp °°°×××çççßßß¶¶¶ ‘‘‘www^^^VVVGGGEEE===EEE¶¶¶ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶^^^------------------------------------===EEEGGGEEEEEE=========EEE333------GGG333---333------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""333^^^fffffffff^^^fffffffff^^^^^^^^^ffffff^^^fff^^^fffpppffffffffffffffffffffffff^^^^^^^^^ffffffffffffffffffffffffffffff^^^ffffffffffffpppfffpppffffffpppffffffpppfffpppfffffffffpppffffffffffff^^^^^^fffffffffpppffffffpppppppppfffpppfffpppfffffffff^^^fffffffffffffffffffffffffffffffffpppwwwpppfffffffffffffff†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘–––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††fffNNNNNN^^^www‘‘‘fffGGGVVVªªªwwwfffwww‘‘‘ –––––––––ÁÁÁÁÁÁ –––ÁÁÁªªª†††pppVVVpppªªª wwwÇÇÇçççÎÎΪªªªªª‘‘‘pppVVVNNNGGGEEE===EEEªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------333EEE===EEEEEEGGG===333===333---------333---------"""------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---"""""""""""""""""""""""""""EEE^^^ffffff^^^fffffffff^^^fffffffff^^^fff^^^^^^ffffffffffff^^^ffffffffffffffffff^^^fffffffffffffffffffffffffffffffff^^^ffffffpppffffffpppfffffffffpppfffpppffffffpppfffffffffpppfffpppfffffffff^^^fff^^^pppfffppppppfffpppppppppppppppfffpppffffff^^^ffffffpppfffffffffffffff^^^ffffffpppppppppppppppfff^^^fffppp†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––––––––––––––––––‘‘‘––––––––––––––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††wwwfffNNNNNNwww†††GGGNNN–––†††ppp^^^fff‘‘‘wwwppp–––ßßßçççÎÎΰ°°°°°ªªª‘‘‘‘‘‘‘‘‘VVVppp ppp°°°ÁÁÁÁÁÁ°°°¶¶¶‘‘‘^^^NNNGGGEEE======EEE ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç¶¶¶NNN---------------------------------===EEEEEE===333EEE333------------------------"""---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""GGGfff^^^^^^ffffff^^^ffffff^^^fff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^^^^fffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffppppppffffffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffpppfffpppppppppppppppppppppffffffffffffffffffppppppfff^^^ffffffffffff^^^ffffffppppppppppppffffffpppfff‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– ––––––––––––––––––‘‘‘‘‘‘––––––––––––––– ––– ––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††www††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘†††††††††††††††††††††www^^^VVVpppwwwVVVVVVppp†††pppVVVNNNpppwwwfffVVV‘‘‘ßßßýýýïïïÎÎζ¶¶–––‘‘‘–––†††VVVfff†††–––ppp†††ÁÁÁ××××××¶¶¶^^^NNNGGGEEE======EEE ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªGGG---------------------------------333EEE===333------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""""""""""""""""""""---NNN^^^ffffff^^^fff^^^^^^^^^fff^^^^^^fff^^^fff^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^fffffffffffffffffffffpppfffpppfffpppfffpppffffffpppffffffffffffpppfffpppfffffffff^^^^^^^^^ffffffpppfffppppppfffpppfffpppfffpppfffpppfffffffffffffffpppfff^^^^^^ffffffffffffffffff^^^ffffffpppppppppppppppppp‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– –––––––––‘‘‘–––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††wwwVVVNNNfff^^^NNNNNN^^^^^^NNNEEENNNfff^^^^^^×××ýýýýýýççç××תªª¶¶¶ wwwffffffppp‘‘‘fff ßßß×××ÇÇǶ¶¶^^^GGGGGGGGGEEE======‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçç窪ªEEE------------------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------"""""""""------"""""""""""""""""""""""""""""""""---VVVfff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffpppfffpppffffffpppfffpppfffpppfffpppfffpppffffffpppffffffpppfffpppffffff^^^ffffff^^^ffffffffffffpppfffppppppfffpppfffpppfffpppfffffffffpppppppppffffff^^^^^^ffffffppppppffffff^^^ffffffpppfffpppfffpppfff‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––––– ––––––––––––––––––‘‘‘––– ––––––‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††pppVVVNNN^^^^^^NNNNNN^^^GGGEEEGGG^^^†††www‘‘‘¶¶¶çççïïïýýýçççÇÇÇÁÁÁ†††wwwppp^^^wwwfff°°°ÇÇÇÁÁÁ°°°ªªª‘‘‘^^^GGGGGGGGG======EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß–––EEE---------------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---------===333---"""------------"""""""""""""""""""""---"""""""""333^^^^^^ffffff^^^^^^^^^fff^^^^^^^^^^^^fff^^^^^^^^^^^^fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppffffffpppfffpppffffffffffff^^^^^^fff^^^fffpppffffffffffffpppfffpppfffpppfffffffffpppffffffpppffffffVVVNNNVVVfffpppfffpppfffffffff^^^fffffffffpppfffffffff––––––‘‘‘–––‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––– ––––––––––––––– ––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††ppp^^^VVV^^^NNNNNNVVVNNNEEEGGGwww‘‘‘www‘‘‘¶¶¶ÎÎÎçççýýýýýýçççÁÁÁ†††ªªªÁÁÁwwwpppppp°°°ÇÇǰ°°––––––†††fffGGGEEE=========EEE†††ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß‘‘‘===------------------------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333EEEEEEEEE---EEE===333333333333---"""""""""""""""---333===333"""""""""===^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^ffffff^^^ffffffffffff^^^fff^^^ffffffffffffpppffffffpppfffpppffffffffffffpppfffffffffppppppfffpppfffpppfffpppffffffpppfffpppfffffffff^^^^^^fffffffffffffffpppfffpppffffffpppfffffffffpppfffffffffppppppffffff^^^GGG===NNNfffpppfffpppffffff^^^^^^^^^fff^^^fffpppffffff‘‘‘–––––––––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––‘‘‘–––––––––––––––––––––––– ––– –––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††wwwVVVVVVVVVNNNGGGGGGGGGEEEfff††††††ÇÇÇßßßßßßïïïýýýýýýÎÎΖ––‘‘‘ ¶¶¶–––°°°¶¶¶ªªª¶¶¶ªªª–––†††pppGGG============EEE‘‘‘ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===---------------------------------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""------"""---333EEENNN^^^^^^NNN333GGG===EEENNNNNNEEE---""""""------333EEEEEEGGG==="""""""""EEEVVV^^^fff^^^^^^fff^^^fff^^^ffffff^^^^^^ffffffffffffffffffffffff^^^ffffffffffffffffffpppfffffffffffffffffffffpppfffffffffffffffffffffppppppfffffffffffffffppppppfffffffffffffffffffff^^^ffffffffffffpppffffffpppfffffffffffffffffffffffffffpppfffpppfff^^^GGG===EEEffffffppppppffffffffffffffffffffffffpppfffffffff‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––– ––– ––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘†††††††††††††††‘‘‘††††††††††††wwwVVVNNNGGGGGGGGGGGGEEEGGG^^^www‘‘‘°°°ÎÎÎ×××ïïïýýýýýýççç××תªª ¶¶¶‘‘‘ªªª¶¶¶ ªªª–––‘‘‘pppfffGGG============EEE‘‘‘ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××׆††===------------------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---GGGGGGEEEGGGNNN^^^fff^^^^^^GGG333333333GGG^^^VVVEEE------333===GGGGGGEEEEEE333---"""""""""GGGVVV^^^^^^^^^^^^^^^^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffffffffffpppffffffpppfffffffffpppffffffpppfffpppfffffffffffffffpppfffffffffpppffffffpppfffppppppfffffffffffffff^^^ffffffffffffpppffffffffffffffffffffffffffffffffffffffffffppppppffffffGGG333===^^^pppfffpppfffpppffffffffffffpppfffffffffpppffffff–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––– ––– ––– ––––––––––––––––––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††pppVVVGGGEEEGGGGGGGGGEEEEEENNN ÁÁÁÁÁÁïïïýýýýýýýýý×××°°° ‘‘‘†††††† –––wwwwwwfffVVVGGGEEE=========EEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††333---------------------""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""333333EEE^^^VVVVVVVVV^^^^^^^^^VVVEEE===333---333EEENNNGGG======GGGGGGNNNGGG===---"""---"""""""""---GGGVVVVVVVVVVVV^^^^^^fff^^^fffffffff^^^ffffffffffffffffff^^^ffffffffffffffffffffffffffffffffffffffffffpppffffffffffffffffffppppppfffpppfffpppffffffpppfffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppffffffNNN===EEEVVVppppppppppppffffffffffffffffffpppffffffpppfffffffff‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––––––––‘‘‘–––‘‘‘–––––– ªªª ––– ––– ––– ––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††‘‘‘‘‘‘†††wwwpppVVVEEEGGGGGGGGGGGGGGGGGGfff°°°¶¶¶¶¶¶ßßßýýýýýýýýýïïïßßßÇÇÇ–––‘‘‘www^^^fff††††††pppVVVNNNGGG===EEEEEEEEEEEE–––ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎÎwww333------------"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---333===NNNNNNVVVfff^^^fffNNNGGGGGGEEE======333333333===EEENNNNNNEEEGGGNNNGGG333---"""""""""""""""""""""---NNNNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffpppffffff^^^fffffffffffffff^^^fff^^^ffffffffffffffffffpppfffpppfffffffffpppfffpppfffpppfffpppfffpppfffppppppffffffffffffffffff^^^ffffffpppffffffpppfffpppfffpppfffffffffffffffffffff^^^ffffffffffffpppVVV===EEE^^^pppppppppppppppffffffpppfffpppffffffpppfffppppppfffppp‘‘‘‘‘‘––––––––––––––––––––––––––– –––––––––––– ––– ––– –––––– –––––– ––––––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††wwwpppVVVGGGGGGNNNNNNGGGGGG^^^–––°°°ÇÇǶ¶¶ïïïýýýýýýïïïïïïçççÎÎΪªªpppfff†††ppp^^^NNNGGGGGG===NNNVVVVVVGGGªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÇÇÇppp---""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""---""""""333===GGGGGGVVVVVV^^^^^^VVVGGG===333333333333333333======EEEGGGVVV^^^NNNEEEGGG===---"""""""""""""""""""""""""""---NNNNNNVVVVVVVVV^^^^^^^^^^^^^^^fff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^ffffffffffffffffffffffffpppffffffpppffffffpppfffpppfffffffffffffffpppfffpppfffpppffffffffffff^^^fffpppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^ffffffffffffpppVVV======VVVpppfffpppppppppppppppfffpppfffpppfffffffffppppppfffpppfff–––––– ––– ––– ––– ––– –––––– –––––– ––– ––– ––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††wwwpppVVVGGGNNNGGGNNNGGGNNNppp–––ÁÁÁªªª×××ýýýýýýïïïýýýïïïïïïßßߪªªÎÎζ¶¶wwwfffVVVNNNGGGEEE===NNNwwwwwwNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇ^^^"""""""""""""""""""""""""""""""""""""""""""""""""""---""""""---""""""EEE======EEENNNVVVVVVNNN^^^VVVEEEEEE------"""------------333======GGGNNNVVVNNNGGGEEE333333---333======"""""""""""""""""""""333GGGNNNNNNVVVVVV^^^^^^^^^^^^^^^^^^ffffffffffff^^^fff^^^^^^fffffffff^^^fffffffffffffff^^^ffffffpppfffffffffpppfffpppfffpppfffpppfffffffffffffffpppfffpppffffffffffffffffff^^^fffpppfffpppfffpppfffpppfffpppffffffffffffffffff^^^ffffffffffffffffffGGG===VVVfffpppfffpppfffpppfffppppppfffpppfffpppfffpppppppppppppppfff–––––– ªªª ––– ––––––––– –––––– ––– ––– ––– ––– ––––––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††wwwwwwVVVGGGNNNNNNNNNGGGNNNpppªªª¶¶¶ ßßßýýýýýýïïïýýýïïïßßß¶¶¶×××ßßß¶¶¶‘‘‘fffNNNNNNGGG======GGGfffNNNªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN"""""""""""""""""""""""""""""""""""""""""""""---""""""""""""""""""---VVVVVVVVV^^^^^^^^^NNN===333333""""""""""""""""""---"""333GGGNNNNNNNNNGGGEEE333---------333EEEEEEEEEEEE---""""""""""""""""""===GGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^fff^^^ffffff^^^°22ú��ú��ú��ú��fffffffff^^^fffffffffffffffffffffpppfffpppffffffpppffffffpppfffpppffffffffffffpppfffpppfffffffffffffffffffffpppffffffpppfffpppfffpppfffffffff^^^ffffffffffff^^^fffffffffffffffNNNEEENNNffffffpppfffppp^^^pppppppppppppppfffpppfffpppfffppppppppppppppp–––––– ––– ––– ªªª–––‘‘‘‘‘‘‘‘‘†††‘‘‘ ––––––––– ––– ––––––‘‘‘–––––––––––– ––––––––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††wwwVVVGGGGGGNNNNNNGGG^^^†††ªªª–––¶¶¶ïïïýýýïïïýýýýýýçççÎÎÎÁÁÁÇÇÇßßßÎÎÎNNNGGGEEE======EEE^^^wwwVVVªªªýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇNNN""""""""""""""""""""""""""""""""""""""""""---""""""---""""""""""""---^^^fffVVV^^^fff^^^===---""""""""""""""""""------333"""===NNNGGGGGG===""""""------======EEENNNEEE------"""""""""""""""""""""EEEGGGGGGNNNNNNVVVVVVVVVVVV^^^^^^^^^^^^^^^ffffffRR÷ú��ú��ú��ú��fffffffffffffffffffffffffffpppffffffffffffpppfffffffffpppfffpppfffpppfffpppfffpppffffffffffffffffffffffffffffffpppfffpppfffpppfffpppfffffffffffffffffffff^^^ffffff^^^ffffffVVVNNNNNNfffffffffpppfff^^^^^^ppppppppppppppppppfffppppppffffffpppfffpppfff ––– –––––––––––––––––––––––––––––– ––– –––––––––––––––––– –––––– –––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††††††††††††††††††††††††††††††††††††††ppp^^^GGGNNNNNNNNNppp–––‘‘‘www×××ýýýïïïïïïýýýïïïßßß°°°ÇÇÇ×××××תªªVVVGGGEEE======EEENNNpppNNNªªªïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÁÁÁGGG"""""""""""""""""""""""""""---""""""---""""""---"""""""""---""""""333VVVNNN===EEEVVVEEE---"""""""""""""""---333GGGGGGEEE---333333---""""""---333EEEEEEGGGEEE===333"""""""""""""""""""""EEEGGGGGGNNNNNNNNNNNNVVVVVV^^^^^^^^^fff^^^fffhddÞú��ú��ú��ú��ú��fffffffffffffffffffffffffffpppfffpppfffpppppppppffffffpppfffpppffffffpppfffpppfffffffffffffff^^^ffffffppppppffffffpppfffpppfffffffffpppfffffffffffffffffffffffffff^^^fff^^^VVVVVVfffffffffffffff^^^^^^fffwwwppppppppppppppppppppppppfffpppfffppppppppp–––––––––––– –––––– –––––– –––––– ––– ––– ––– ––– ––– ––––––––––––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘––––––––– ––– ––––––––––––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www^^^GGGGGGNNNppp–––ªªªwwwVVV‘‘‘çççýýýïïïýýýýýýçççÎÎΰ°°ÎÎÎ×××ÎÎÎÁÁÁªªªVVVEEE===EEEGGGGGGGGG–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß¶¶¶GGG""""""""""""---"""""""""---""""""---"""---""""""---"""---"""""""""---333---"""------""""""---"""---333===GGGNNNGGGGGG===---"""""""""---===EEEGGGGGG===333---""""""""""""""""""---EEEú��ú��GGGGGGGGGNNNNNNVVV^^^^^^^^^^^^ffffffµ77ú��çú��ú��ú��ú��fffppppppfffffffffppppppfffpppfffffffffpppfffpppfffpppfffpppfffffffffpppfffpppffffffffffffffffff^^^pppfffpppppppppfffpppffffffpppffffffffffffffffff^^^fffffffffffffff^^^^^^VVVffffffffffffffffffpppppppppppppppppppppppppppppppppfffpppfffpppfffpppfff–––––––––––– –––––– ––––––––––––––– –––––––––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––– –––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††wwwwww†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††fffGGGNNNVVV††† †††^^^VVV¶¶¶ïïïýýýïïïýýýýýýßßßÇÇÇÁÁÁÎÎÎßßßßßßçççÇÇÇ^^^EEEGGGVVVEEE===ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE"""""""""""""""---"""---""""""---""""""---"""---"""""""""---"""---"""""""""""""""""""""---EEEEEEEEEEEEGGGGGG===333"""---333===333---EEEGGGGGGEEE333""""""""""""""""""""""""333EEEú��ú��GGGGGGNNNNNNVVVVVVVVV^^^^^^ffffffSS÷ø��‰MMú��ú��ú��ú��pppfffpppffffffpppfffppppppfffffffffpppfffpppfffpppfffpppffffffpppfffpppffffffffffffpppffffffffffffpppfffppppppffffffpppfffpppfffffffffffffffffffff^^^fff^^^ffffff^^^fff^^^^^^^^^^^^fffpppfffppppppppppppppppppwwwwwwwwwwwwpppppppppppppppfffppppppfff–––––––––––– ––– ––– ªªª ––– ––– ––– ––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘––––––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††www†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††wwwfffGGGNNN^^^–––°°°pppNNNppp×××ýýýýýýýýýýýýýýýïïï×××¶¶¶ÎÎÎÎÎÎÁÁÁ×××°°°†††www^^^EEEEEEwwwÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýý××תªª===""""""---"""---"""""""""---""""""------"""---"""---"""---""""""""""""""""""""""""---======NNNGGGNNN333333---""""""---333NNNNNNEEE---===333---""""""---EEE333"""""""""""""""""""""===GGGú��ú��GGGGGGGGGNNNVVVVVVVVV^^^^^^fffhddÞú��¾00fffú��ú��ú��ú��ppppppfffpppfffppppppffffffffffffppppppffffffppppppffffffppppppppppppffffffpppffffffffffffffffffpppfffppppppfffpppffffffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^ffffffppppppppppppfffppppppppppppfffpppppppppfffppppppfffpppppp‘‘‘–––––– ––––––––––––‘‘‘‘‘‘††††††wwwwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘††††††††††††www††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††www^^^NNNVVV–––ªªª†††NNNVVV‘‘‘çççýýýïïïïïïýýýýýýçççÁÁÁ ¶¶¶ÇÇÇ××××××ßßßÁÁÁwwwNNNNNNwwwÇÇÇçççïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""---""""""---"""---"""---"""---------333------------------"""------333---EEEGGGNNNGGG===---"""""""""---===EEENNNNNNGGG==="""""""""---======GGGNNNEEE"""""""""""""""""""""EEEEEEú��ú��GGGGGGNNNNNNNNNVVVVVV^^^^^^fff°22ú��ækbbfffú��ú��ú��ú��pppfffpppfffppppppppppppfffpppppppppfffpppfffppppppfffpppfffpppppppppfffffffffpppffffffffffffpppfffpppfffpppfffppppppfffpppffffffffffffffffff^^^fffffffff^^^^^^^^^^^^^^^^^^VVVVVV^^^^^^ffffffffffff^^^fffpppppppppfffpppfff^^^^^^ffffffffffffpppfffppp‘‘‘‘‘‘†††wwwpppffffff^^^^^^fff^^^ffffffpppfff^^^VVV^^^^^^^^^^^^^^^^^^fffpppwww†††‘‘‘‘‘‘†††††††††††††††††††††††††††wwwwwwwww†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††wwwfffNNNfffVVVNNN^^^°°°ïïïýýýýýýýýýýýýïïï××תªª °°°ÎÎÎßßßßßß°°°wwwÇÇÇÁÁÁ×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––==="""---"""---"""---"""---"""---"""333=====================333333333EEEEEEGGGEEE---EEE===333"""""""""---333===GGGNNNEEEEEE333"""""""""---333EEEGGGGGGGGG==="""""""""""""""""""""---EEEEEEú��ú��GGGGGGNNNNNNVVVVVV^^^^^^^^^SSöø��ŠMMppppppú��ú��ú��ú��pppppppppppppppppppppfffpppfffppppppppppppffffffpppfffpppppppppfffpppfffffffffffffff^^^ffffffpppfffpppfffppppppfffppppppfffpppfffffffff^^^^^^fffffffff^^^fff^^^^^^^^^^^^VVV^^^^^^fff^^^fff^^^VVVNNNVVVfffppppppfffffffffppp^^^^^^^^^^^^fffffffffpppffffff^^^^^^^^^^^^^^^^^^ffffffppppppppppppwwwwwwpppppppppfffffffffffffff^^^^^^VVVfffwww‘‘‘†††wwwppppppfffpppppppppppppppppp†††wwwwwwwww†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††pppNNNVVVVVVNNNNNNNNNfffÎÎÎýýýýýýýýýïïïýýýïïïÎÎΰ°°ªªª–––ÎÎÎ×××ÇÇÇ‘‘‘¶¶¶¶¶¶ppp ªªªßßßïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΖ––333---"""---"""---"""---"""------===GGGGGGEEEEEE=========EEEEEEGGGNNNNNNGGGGGG==="""""""""""""""333EEEGGGNNNGGGEEE333---"""""""""------===NNNEEE===333"""""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^^^^fffàú��»++pppppppppú��ú��ú��ú��fffppppppfffppppppfffppppppppppppppppppfffpppfffpppfffpppppppppffffffffffffffffffffffffppppppfffppppppfffpppfffpppfffppppppfffpppfffVVVNNNffffffffffff^^^^^^VVV^^^^^^VVV^^^^^^^^^^^^^^^VVVVVVNNNNNN^^^ffffff^^^^^^^^^^^^VVV^^^VVVVVV^^^fffppppppVVVVVVVVVVVV^^^^^^^^^^^^fff^^^fffffffff^^^fffffffffffffff^^^fffffffffffffffpppppppppwwwpppfff^^^^^^^^^fff^^^fffffffffpppppp†††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––– ––– ––––––––––––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††pppVVVGGGNNNGGGNNNNNNßßßïïïýýýïïïïïïýýýïïïßßß×××°°°‘‘‘°°°ÎÎζ¶¶ÎÎÎÇÇÇpppwwwÁÁÁçççýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïÎÎΆ††---"""---""""""---"""---"""------GGG^^^NNNNNNGGGGGGGGGNNNVVVNNNNNNGGGEEE------""""""""""""333EEEGGGNNNEEE===333"""""""""---===EEE===---GGG333---""""""""""""""""""ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��^^^^^^ffffffú��èlbbfffppppppú��ú��ú��ú��ppppppfffppppppppppppfffppppppppppppppppppfffpppfffpppfffpppffffffpppfffffffffffffffffffffpppfffpppfffpppfffffffffpppfffppppppffffffVVV^^^pppffffff^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVVVVVVV^^^NNNNNNNNN^^^VVV^^^ffffffNNNEEE333333EEEGGGNNN^^^fffppp^^^^^^wwwwwwpppfffpppfffffffffffffffpppppppppppppppppppppwww††††††‘‘‘†††wwwppp^^^^^^^^^VVV^^^^^^^^^fff^^^pppppppppppppppwwwwww††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––––––––– ––– ––– –––––––––––––––––––––––––––‘‘‘‘‘‘wwwVVVGGGGGGGGGNNNVVV–––ïïïýýýýýýýýýïïïïïïýýýçç窪ª††††††‘‘‘‘‘‘ÁÁÁÇÇÇ‘‘‘fffªªªçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇwww------"""---"""---"""---"""---333VVVfffVVVVVVVVVfff^^^NNNGGGEEE333---"""""""""---333"""---GGGEEEEEE333"""""""""---333EEEEEEGGGEEE333---333""""""""""""""""""""""""""""""EEEEEEGGGú��ú��NNNVVVVVV^^^^^^ffffffffffffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffpppfffppppppfffppppppppppppfffppppppfffppppppfffffffffpppffffffffffffpppffffffpppfffpppffffffpppffffffppppppfffpppfff^^^fffpppfffpppfffffffff^^^^^^VVVVVVVVVVVVVVVVVVVVVVVVVVVNNNNNNVVVVVVVVV^^^VVV^^^EEE"""---"""---333NNNVVVfffpppffffff††††††††††††††††††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘††††††wwwwwwfffffffffVVV^^^VVVVVVVVV^^^^^^^^^^^^^^^ffffffpppfffppppppwwwwww††††††††††††‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘–––––––––––––––––––––––– ––––––––– ––– ––––––––––––‘‘‘wwwVVVGGGGGGNNNGGG^^^ÁÁÁïïïýýýýýýýýýïïïïïïïïïÎÎÎÁÁÁªªª†††–––ÇÇǶ¶¶†††°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎfff---"""---"""---------------===GGGppp^^^VVVfff^^^VVVGGG===""""""---======333===GGGEEE---"""===---"""""""""333===GGGGGGGGGNNN===---""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��VVV^^^^^^ffffffffffffpppfffú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ppppppppppppppppppppppppppppppfffppppppfffpppfffpppfffpppfffffffffffffffpppfffppppppffffffpppfffpppffffffpppfffpppfffffffffppppppfffffffffffffffffffff^^^^^^^^^^^^^^^VVVNNNNNNVVVVVVNNNNNNNNNNNNVVVNNNNNNNNNEEE---""""""------NNN^^^ffffffffffffwwwpppwwwwwwwwwwwwfffppppppppppppwww†††wwwfff^^^^^^VVVVVV^^^^^^^^^^^^^^^^^^^^^^^^VVV^^^^^^ffffffpppwwwwwwwwwwww††††††††††††‘‘‘–––––––––––––––––––––‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––– ––– ––– ––– ––– ––––––––––––‘‘‘‘‘‘pppVVVGGGGGGNNNNNNpppÎÎÎýýýýýýýýýýýýïïïïïïýýýïïïßßß°°°––– –––°°°†††ÇÇÇçççýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇfff------"""------333===EEEEEEVVVfffwwwfff^^^^^^GGG---""""""""""""===NNNNNNGGGNNNGGG===---""""""""""""---EEEEEEEEEEEE========="""""""""""""""""""""""""""""""""""""""---EEEEEEGGGú��ú��^^^^^^fffffffffppppppppppppppppppppppppppppppú��ú��ú��ú��pppfffpppppppppppppppfffpppppppppfffpppfffpppppppppfffpppfffpppffffffffffffpppppppppppppppfffpppfffffffffpppfffppppppfffpppfffpppffffffffffffffffffffffff^^^^^^^^^^^^VVV^^^VVVVVVVVVVVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGG===333333---===VVVffffff^^^^^^ffffffffffff^^^VVVNNNNNNNNN^^^^^^^^^fffpppppppppfffpppffffffpppfff^^^^^^^^^^^^^^^^^^^^^ffffffffffffpppwwwwwwwww†††††††††‘‘‘–––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– ªªª ––––––––––––‘‘‘–––‘‘‘†††fffVVVGGGGGGGGGNNN†††ßßßïïïýýýýýýýýýýýýýýýýýýýýý×××ÁÁÁªªª††††††www–––ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýçççÇÇÇVVV------"""333GGGGGGNNN^^^^^^wwwppppppVVVNNNEEE333---"""333===---EEENNNNNN===333------"""---333333"""===EEE===---"""""""""""""""""""""""""""""""""""""""""""""""""""""""""===EEEGGGVVVú��ú��ffffffppppppppppppppppppppppppwwwwwwwwwppppppú��ú��ú��ú��fffpppppppppppppppppppppppppppppppppppppppfffppppppfffppppppffffffffffffppppppppppppppppppfffppppppfffffffffpppfffpppfffffffffppppppfffffffffffffffppp^^^^^^^^^^^^^^^fffVVV^^^VVVVVVVVVVVVVVVVVVVVVNNNGGGNNNGGGGGGEEEEEEEEEGGGNNNVVV^^^ffffff^^^fffffffff^^^VVVNNNNNNNNNVVVVVVVVVffffffppppppfffwwwpppppppppfffpppfff^^^^^^^^^fffffffffpppppppppppppppwwwwwwwww†††‘‘‘‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ªªª ªªª ªªªªªª ªªªªªªªªªªªª ––– –––––––––––––––‘‘‘‘‘‘†††wwwNNNGGGGGGNNNVVV–––çççýýýýýýýýýýýýýýýýýýýýýïïïïïïÎÎΖ–– –––‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýïïïçççÇÇÇNNN---"""---333VVVNNNVVV^^^ppp^^^^^^NNN===NNNGGGEEEEEEEEEGGGNNN333---333""""""""""""EEEEEEGGGNNNGGG---"""---"""""""""""""""---""""""===333"""""""""""""""""""""333GGGNNN^^^fffú��ú��ppppppppppppppppppppppppwwwwwwwwwppppppppppppú��ú��ú��ú��pppppppppppppppppppppwwwppppppfffppppppppppppfffppppppfffpppfffffffffpppppppppppppppppppppfffpppfffpppfffpppfffpppfffffffffpppffffffffffff^^^^^^ffffff^^^^^^fff^^^fff^^^^^^^^^VVVVVVVVVVVV^^^VVVVVVVVVVVVNNNNNNGGGNNNGGGGGGGGGGGGNNNVVV^^^^^^fff^^^ffffffVVVNNNNNNVVVVVVVVVVVVVVV^^^fff^^^VVVVVV^^^^^^fffffffff^^^fff^^^^^^ffffffppppppwwwwwwwwwpppwwwwwwwww†††††††††‘‘‘–––––– –––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ––– ––––––––––––‘‘‘‘‘‘†††pppVVVGGGGGGNNN^^^¶¶¶ïïïýýýýýýýýýýýýýýýýýýýýýýýýçççÎÎÎ××תªª‘‘‘‘‘‘çççýýýýýýýýýýýýýýýýýýýýýýýýýýýßßßÁÁÁGGG------"""333GGGGGGEEEEEENNNNNNNNNNNNGGG^^^fffNNNEEENNNGGG===""""""---333---===EEENNNNNNEEE333333""""""""""""""""""""""""333333"""===EEE333""""""""""""""""""333GGG^^^fffppppppwwwpppwwwwwwwwwwwwpppwwwwwwwwwwwwwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppppppppfffppppppffffffpppfffppppppffffffffffffpppppppppwwwppppppppppppfffpppfffppppppfffffffffppppppffffff^^^^^^fff^^^ffffff^^^^^^^^^^^^ffffff^^^^^^^^^^^^^^^^^^VVV^^^^^^^^^VVVVVVVVVVVVNNNNNNGGGEEEEEENNNVVVVVVVVV^^^^^^ffffffppp^^^VVVVVVVVVVVVVVVfffffffffpppfff^^^^^^^^^ffffffpppppppppwwwppppppppppppwwwwww††††††‘‘‘––––––––––––––– ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘––– ––––––‘‘‘‘‘‘–––––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘††††††pppVVVGGGGGGNNNfffÎÎÎïïïýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýççç×××ÁÁÁÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß¶¶¶EEE---""""""---333""""""333VVVNNNVVVfffffffff^^^NNN===---"""""""""333EEEVVVGGGGGGNNN===333"""""""""""""""""""""EEE===GGGEEE"""GGGNNN333"""""""""""""""""""""---===NNNfffppppppwwwwwwpppwwwwwwwwwwwwpppwwwwwwwwwpppwwwpppppppppwwwwwwppppppppppppppppppwwwpppppppppppppppfffpppppppppfffpppppppppfffpppffffffffffffppppppppppppppppppppppppfffppppppfffffffffpppffffffpppffffffppp^^^VVV^^^fff^^^fff^^^^^^^^^^^^^^^fff^^^^^^fff^^^^^^^^^^^^^^^^^^VVV^^^fff^^^^^^VVVVVVNNNNNN===GGGNNNNNNVVVVVV^^^^^^ffffffffffffVVVVVVNNNVVVVVV^^^pppfffpppppppppppppppfffpppppppppwwwwwwwwwppppppwwwwww†††††††††‘‘‘‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘†††††††††††††††‘‘‘‘‘‘–––––––––––– ––– –––––– ––– ––– ––– ––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªª°°°°°°ªªªªªªªªª –––‘‘‘‘‘‘‘‘‘††††††wwwVVVGGGNNNNNNwww×××ýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççççççßßßýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß°°°==="""""""""""""""""""""---VVV^^^^^^fff^^^VVVEEE333""""""---333===GGGGGGGGGGGG333---"""""""""""""""""""""""""""---NNNGGGVVVEEE---GGGGGG---""""""""""""333===GGG^^^ffffffpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppwwwpppppppppppppppfffppppppppppppppppppfffpppfff^^^ffffffppppppppppppppppppfffpppppppppffffffpppffffffpppfffpppfffpppffffffVVV^^^fffffffff^^^^^^fff^^^fffffffff^^^fff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^VVVVVVVVVVVVVVVNNNVVVVVVVVVVVV^^^^^^fffpppfff††††††wwwwwwppp^^^fff^^^ffffffwwwwwwwwwwwwpppppppppwwwwwwwwwwww†††††††††††††††††††††‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––– ––––––‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘–––––– ––––––––––––––– ––– ––– –––––– ªªª ªªªªªªªªª°°°ªªª ªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªªªªªªªªªªªªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘†††www^^^GGGGGGVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××× ==="""---"""""""""""""""---EEEGGGNNNGGGGGG===333---======EEENNNGGG===------""""""""""""""""""""""""""""""333""""""---"""EEENNNEEEEEE---"""===---"""""""""---===EEENNNVVVffffffppppppppppppwwwpppwwwwwwpppwwwpppwwwppppppwwwpppwwwwwwwwwwwwpppppppppppppppppppppwwwpppwwwpppppppppppppppfffpppppppppfffppppppffffffffffffpppppppppppppppppppppppppppfffpppffffffppppppffffffpppfffpppffffffVVV^^^^^^fffppp^^^^^^ffffff^^^^^^ffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVVfff^^^VVVVVVVVVVVV^^^^^^ffffffpppfffwwwppppppfffpppppppppwwwwwwwwwwwwwwwwwwwww††††††††††††‘‘‘†††††††††‘‘‘––––––––––––‘‘‘–––––––––––– ––– ––––––†††‘‘‘††††††††††††‘‘‘–––––– ––– ––––––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª°°°°°°ªªªªªª ªªª –––––––––‘‘‘‘‘‘www^^^GGGNNNVVV çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï×××–––333------""""""""""""""""""---"""333333333EEEEEEEEEGGGVVVGGGEEE---"""""""""""""""""""""""""""""""""---333GGG333"""333"""---333"""""""""""""""""""""---===GGGNNN^^^ffffffpppfffpppfffppppppppppppppppppppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppwwwwwwpppwwwwwwpppwwwpppfffppppppppppppfffpppfffpppfffffffffwwwppppppppppppppppppppppppfffpppppppppppppppppppppfffpppfffpppfff^^^VVVfffffffff^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^^^^^^^VVV^^^VVVVVV^^^fffpppfffffffffpppwwwwwwwwwwwwwwwpppwwwppppppwww††††††††††††††††††††††††‘‘‘†††‘‘‘––––––––––––––––––––––––––– ––––––‘‘‘‘‘‘††††††††††††††††††––– ––– –––––– ––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªªªªªªªª ––– ‘‘‘†††www^^^NNNNNN^^^°°°çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïï××ב‘‘---------""""""""""""""""""""""""------===EEENNNGGGEEE333---"""""""""""""""""""""""""""""""""333333EEENNNNNN"""------""""""""""""""""""333EEEGGGVVV^^^^^^fffpppffffffpppfffpppppppppppppppppppppwwwpppwwwppppppppppppwwwpppwwwppppppppppppfffpppwwwwwwpppwwwwwwwwwppppppppppppfffpppppppppfffpppppppppffffffpppwwwppppppppppppppppppppppppfffppppppfffpppppppppfffpppfffpppfff^^^VVV^^^fffffffff^^^^^^ffffff^^^ffffffffffffffffff^^^fff^^^^^^^^^^^^^^^^^^^^^^^^^^^fff^^^ffffff^^^^^^^^^VVVVVV^^^fffffffffffffffwwwwwwppppppwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘ ––– ––– ––– ––– ªªª ªªª ªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª ––––––‘‘‘†††fffNNNNNNfffÁÁÁïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333---""""""---"""333EEE======------"""===EEE===333"""""""""""""""""""""""""""333"""""""""EEENNNVVVGGGEEEEEE"""""""""""""""""""""---===EEENNNVVV^^^^^^ffffffpppffffffpppppppppppppppppppppppppppppppppwwwppppppppppppppppppwwwppppppppppppfffpppwwwppppppwwwppppppppppppppppppppppppppppppppppppppppppffffffppppppwwwppppppppppppppppppfffppppppppppppppppppffffffpppfffpppfff^^^NNN^^^ffffffffffff^^^fff^^^fffffffffffffff^^^fff^^^ffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffppppppffffffwwwpppwww†††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘††††††††††††††††††––– ––– ––– ––– ––– ––– ªªªªªª ªªª ªªª ªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°ªªªªªª ––––––‘‘‘fffNNNGGGpppÎÎÎýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××333------=========NNNVVVVVVEEE""""""---------"""""""""""""""""""""---"""===NNNEEE==="""NNNNNNGGGEEE---"""""""""""""""---===GGGNNNVVV^^^^^^^^^ffffffpppfffffffffpppppppppffffffpppfffppppppppppppppppppppppppppppppwwwpppppppppwwwppppppwwwpppwwwppppppppppppfffppppppppppppppppppfffppppppfffffffffppppppwwwppppppppppppppppppppppppfffpppppppppfffppppppfffpppfffffffffNNNVVV^^^fffffffff^^^^^^fff^^^^^^fffffffffffffff^^^ffffff^^^^^^fff^^^^^^^^^fff^^^^^^fffffffff^^^^^^^^^^^^^^^^^^ffffffpppffffffffffff†††††††††††††††††††††††††††††††††‘‘‘†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††www†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘†††††††††††††††††††††‘‘‘––– ––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°ªªª°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªª –––†††wwwfffVVVNNN×××ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp333333NNNVVVVVV^^^fff^^^GGG---""""""""""""""""""""""""""""""""""""333EEEGGGVVVVVV==="""EEE---""""""""""""""""""333===GGGVVVVVV^^^^^^^^^^^^fffffffffffffffpppfffpppfffppppppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwpppppppppfffppppppppppppppppppfffppppppfffpppffffffpppwwwpppwwwpppppppppppppppwwwpppppppppppppppppppppffffffpppfffffffffVVVNNN^^^fffffffff^^^^^^fff^^^ffffffffffffffffffffffff^^^^^^fff^^^^^^^^^fff^^^fffffffff^^^ffffff^^^^^^^^^^^^^^^ffffffffffffpppfffffffff‘‘‘––––––††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘††††††††††††††††††‘‘‘––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª¶¶¶°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°° ––––––‘‘‘†††pppVVVNNN†††ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýßßß×××ppp333NNNpppfffNNNVVV^^^VVVNNNEEE"""---"""333=========EEE======EEENNNNNNVVVGGGGGG---""""""""""""""""""---333EEEGGGVVV^^^^^^^^^^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppfffppppppfffpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppffffffppppppppppppwwwppppppppppppppppppfffppppppppppppppppppppppppfffffffff^^^NNN^^^fffffffff^^^^^^fff^^^fff^^^ffffffffffffffffff^^^ffffff^^^ffffff^^^ffffffffffffffffffffffff^^^VVV^^^^^^fffffffffffffffffffffffffff––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘––––––‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘†††††††††††††††–––––– ªªª ªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªªªªª ‘‘‘†††wwwVVVVVV‘‘‘ßßßýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××fff===VVVfff^^^GGGVVVNNNGGGEEE333"""333===333===GGGNNNGGGVVVVVVVVVGGG^^^VVVVVVEEE333---""""""""""""333===EEEGGGVVV^^^^^^fff^^^fff^^^^^^fffffffffffffffffffffffffffffffffpppfffppppppfffpppppppppfffppppppppppppppppppfffpppfffpppwwwppppppwwwpppwwwppppppppppppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppppppppppppppfffpppffffffppp^^^VVVVVVfffffffff^^^^^^^^^fff^^^fffffffffffffffffffffffffff^^^fff^^^^^^fff^^^ffffff^^^ffffffffffff^^^^^^^^^^^^fff^^^ffffffpppfffffffffffffff––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘††††††‘‘‘††††††††††††‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªª°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ªªªªªª–––‘‘‘wwwVVVVVV–––çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïççç×××^^^GGGVVVVVVVVVGGGNNNGGGEEE---"""333GGGNNN======GGGGGGVVVfffGGG^^^GGGGGG===---"""""""""""""""---===EEEGGGNNNVVV^^^^^^^^^ffffff^^^^^^^^^fffffffff^^^fffffffffffffffffffffffffffffffffpppppppppfffpppfffpppppppppppppppppppppfffpppwwwppppppwwwpppwwwpppppppppppppppwwwppppppppppppppppppffffffpppppppppppppppppppppppppppppppppfffppppppppppppppppppfffppppppfffpppfffNNNVVVffffffpppfffffffffffffffffffffffffffffffffpppfff^^^^^^fff^^^ffffff^^^fffffffffffffff^^^fff^^^fff^^^VVV^^^^^^^^^fffpppffffffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘––––––––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––††††††‘‘‘†††††††††–––––– ªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªªªªª°°°¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°ªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°° †††www^^^^^^ çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßß×××VVVEEEEEEGGGVVVGGGGGGEEE===333---===GGGGGG333---======VVVGGG---===333"""""""""""""""""""""333===GGGGGGNNNVVVVVV^^^ffffffffffff^^^ffffff^^^ffffff^^^^^^fffffffffffffffffffff^^^fffffffffpppffffffffffffppppppppppppppppppppppppppppppppppppppppppwwwpppppppppppppppwwwpppppppppfffpppfffpppfffppppppppppppwwwpppppppppppppppppppppppppppppppppppppppfffppppppfffpppfffVVVVVVffffffpppfffffffffffffffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^ffffffffffffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffpppfffffffffffffff–––––––––‘‘‘‘‘‘‘‘‘ ––––––––––––––––––––––––––––––†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––†††††††††††††††††††††‘‘‘–––––– ––– ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶°°° ‘‘‘^^^^^^°°°ïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýïïïßßßÎÎÎGGG333===GGGVVVGGG333333333333===EEEEEE===333"""---333===333""""""""""""""""""""""""---===EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffff^^^fffffffffffffffffffff^^^ffffffffffffffffff^^^fffffffffpppfffffffffpppppppppppppppppppppppppppppppppwwwpppppppppppppppfffwwwppppppwwwpppppppppppppppfffppppppfffpppppppppwwwwwwpppwwwppppppppppppppppppppppppppppppfffppppppppppppppp^^^NNNfffffffffffffff^^^ffffffffffffpppfffffffffffffffffffff^^^^^^^^^fffffffffpppfffffffffffffff^^^fff^^^^^^^^^^^^^^^fffffffffpppffffffpppffffffppp––––––––––––‘‘‘––– ––– ––––––––––––‘‘‘––––––‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘––––––†††wwwwww††††††††††††–––––– ––– ––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁ¶¶¶ÁÁÁ°°°ªªª–––^^^fff¶¶¶çççýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýçççßßß¶¶¶EEE===EEENNNNNN---"""------------"""333===333"""""""""""""""""""""""""""""""""333EEEGGGNNNVVVVVV^^^^^^^^^ffffffffffffffffff^^^fffffffffpppffffff^^^ffffffffffffffffff^^^^^^^^^fffffffffffffffffffffppppppfffppppppfffppppppppppppppppppppppppppppppppppppwwwpppwwwpppppppppfffpppfffppppppppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfff^^^VVV^^^ffffffpppffffffffffffpppfffffffffffffffpppfffffffff^^^fff^^^fffffffffpppfffffffffffffff^^^^^^^^^^^^^^^^^^^^^fff^^^fffffffffffffffffffffffffff––– ––––––––––––––– ––– ––––––––––––†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––wwwwww†††††††††‘‘‘–––––– ––– –––––– ªªªªªªªªª ªªª ªªªªªª ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ°°°ªªª–––^^^fffÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýýýýççç×××–––333EEEGGGNNN333"""""""""---""""""""""""---""""""""""""""""""""""""""""""---===EEENNNVVVVVV^^^^^^^^^^^^^^^fffffffffffffff^^^ffffffffffffpppffffff^^^ffffff^^^fffffffff^^^fffffffffffffffffffffffffffppppppppppppfffpppppppppppppppwwwpppppppppwwwwwwpppwwwpppwwwwwwpppppppppppppppfffpppfffppppppwwwppppppfffppppppppppppppppppfffpppwwwpppppppppfffppppppppppppffffffVVVVVVfffppppppfffffffffffffffffffffppp^^^^^^ffffffffffff^^^fffffffffffffffpppffffffpppffffff^^^fff^^^^^^fff^^^^^^fff^^^ffffffpppfffffffffffffffffffffªªª –––––– –––––– –––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––pppppp†††††††††‘‘‘‘‘‘–––––– ––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ¶¶¶ªªª–––†††fffpppÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýýýýççç×××ppp------333333"""""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^^^^^^^^^^^^^^^^^^^fff^^^ppppppffffff^^^fff^^^fffffffff^^^fffffffffffffff^^^ffffff^^^fffffffffffffffpppffffffpppppppppfffppppppfffpppppppppppppppwwwwwwppppppppppppwwwpppwwwwwwpppppppppffffffpppfffppppppwwwpppppppppppppppwwwpppwwwpppppppppwwwppppppppppppppppppwwwpppffffffVVVVVVfffpppfffpppfffpppfffpppffffffpppfff^^^ffffffffffff^^^fffffffffpppfffpppfffpppfffffffffffffff^^^^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppfffffffff ––– ––– –––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––fffpppwww†††††††††‘‘‘––– ––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°¶¶¶°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÎÎÎÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁÁÁÁ°°° †††pppwwwÇÇÇïïïýýýýýýýýýýýýýýýýýýýýýççç×××^^^---"""---""""""""""""""""""""""""""""""""""""""""""""""""333===EEEGGGVVV^^^^^^^^^^^^^^^fffffffff^^^fffpppfffpppfff^^^^^^fffffffffffffff^^^ffffff^^^fff^^^^^^fffffffffffffffpppfffpppfffpppfffpppfffpppfffpppffffffpppfffpppppppppppppppppppppppppppwwwwwwwwwwwwwwwppppppfffppppppfffppppppwwwwwwwwwppppppwwwwwwpppwwwpppwwwpppwwwpppwwwpppppppppppppppppppppfff^^^NNN^^^fffppppppffffffpppfffffffffffffff^^^^^^fffpppfffffffffffffffpppfffpppffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffpppppppppffffffffffffffffff ––––––†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘ffffffwww†††††††††‘‘‘‘‘‘–––––––––––– ªªª ªªª ªªªªªªªªªªªª ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÇÇÇÇÇÇÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÏ££àbbí11õø÷óæ))¾DD}eewwwÎÎÎïïïýýýýýýýýýýýýýýýýýýçççÎÎÎNNN---""""""""""""""""""""""""""""""""""""""""""""""""---333EEEGGGNNNVVV^^^fff^^^ffffff^^^fff^^^fffffffffpppfffpppfff^^^fffffffffffffffffffffffffff^^^^^^fff^^^fff^^^ffffffffffffffffffpppffffffpppfffpppfffffffffppppppppppppppppppppppppppppppfffpppppppppwwwwwwwwwwwwppppppfffppppppfffpppwwwpppwwwppppppwwwwwwwwwwwwppppppwwwwwwpppwwwpppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffpppffffff^^^fffppp^^^^^^ffffffffffffffffffppppppfffppppppffffffpppfffffffffffffff^^^^^^^^^^^^^^^fffffffffpppffffffpppfffffffffffffff^^^ –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘ppp^^^www†††††††††‘‘‘‘‘‘‘‘‘–––––– ªªª ªªª ªªª ªªªªªª°°°ªªª ªªªªªªªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÇÇÇÁÁÁÁÁÁ¶¶¶ÇÇÇÇÇÇÇÇÇÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ñ “]]wwwÎÎÎïïïýýýýýýýýýýýýïïïçççÇÇÇEEE"""""""""""""""""""""""""""""""""""""""---333EEEGGGVVVVVV^^^^^^^^^ffffffffffffffffffffffffffffffpppfffpppfff^^^ffffffffffffffffffffffff^^^ffffff^^^fff^^^fff^^^fff^^^fffffffffpppffffffpppfffpppfffffffffffffffpppppppppppppppfffppppppfffpppppppppwwwpppwwwppppppfffppppppffffffpppwwwpppwwwpppppppppwwwppppppwwwpppwwwpppwwwppppppwwwppppppwwwpppfffppp^^^VVV^^^fffpppffffffpppffffffffffffffffff^^^VVVfffffffffffffffppppppfffpppfffpppfffpppffffffffffffffffff^^^fff^^^^^^fff^^^ffffffpppfffppppppfffpppfffpppfffffffff –––‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††ppp^^^ppp†††www†††‘‘‘‘‘‘–––––––––––––––––– ––– ªªª –––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎð++àxxÕªªÏÇÇØÏÏ×¢¢í00ú��ú��ú��åwwwÎÎÎïïïýýýýýýýýýïïïççç°°°===---"""""""""""""""""""""""""""""""""---===EEENNNVVV^^^^^^^^^^^^ffffffffffffffffffffffff^^^ffffffpppfffffffff^^^fff^^^ffffffffffff^^^ffffffffffff^^^fff^^^^^^^^^fffffffffffffffffffffffffffpppffffffffffffffffffpppppppppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffpppppppppwwwpppppppppwwwppppppwwwpppwwwwwwwwwppppppwwwppppppwwwpppppppppfffVVVVVVfffpppfffppppppfffffffffffffffffffffVVVfffffffffffffffpppppppppffffffffffffffffffpppfffpppfff^^^^^^^^^^^^^^^^^^ffffffffffffpppffffffppppppffffffffffff^^^fff –––‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††ppp^^^ppp†††††††††‘‘‘––––––––––––‘‘‘‘‘‘–––––– ªªª ––– ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªª°°°ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××ÎÎÎ××××××ÎÎÎÎÎÎÒ··ú��ú��ú��÷ªªªÎÎÎïïïýýýýýýïïï×××ú��ú��"""""""""""""""""""""""""""---333===GGGVVV^^^^^^ffffffffffff^^^ffffffppppppffffff^^^ffffffffffffpppffffff^^^^^^^^^ffffffffffff^^^ffffffffffffffffffffffff^^^fffffffffffffffffffffffffffpppffffffppppppfffppppppppppppppppppppppppppppppppppppppppppppppppfffppppppppppppffffffpppwwwpppppppppppppppwwwppppppppppppppppppwwwpppwwwpppppppppwwwpppppppppfff^^^NNN^^^fffpppfffffffffffffffffffffffffff^^^^^^fffffffffpppppppppffffffpppffffffpppffffff^^^ffffffffffff^^^^^^^^^^^^ffffffffffffpppfffpppfffpppffffffffffffffffff^^^ –––‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††www^^^fffwwwwwwwww†††‘‘‘‘‘‘––––––––––––‘‘‘†††––––––––– ––– ––––––––– ––– ªªª ªªª ªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁ°°°¶¶¶ÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎ×××ÎÎÎ×××ÎÎÎ×××××××××ÎÎÎÎÎÎÚÂÂú��ú��ú��ð%%¶¶¶ªªªwwwÇÇÇïïïýýýïïï×××ú��ú��"""""""""""""""""""""---===EEENNNVVV^^^ffffffffffffffffffffffffppppppfffffffffffffffffffffppppppfff^^^fff^^^fffffffff^^^fffffffff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffffffffppppppfffpppppppppfffppppppfffpppfffpppfffpppfffpppppppppppppppppppppppppppffffffppppppwwwpppppppppppppppppppppppppppppppppwwwwwwppppppppppppppppppppppppfff^^^VVV^^^fffpppfffpppfffffffffffffffpppffffff^^^ffffffffffffppppppppppppfffffffffffffff^^^VVV^^^fffffffff^^^fff^^^^^^fff^^^fffpppfffpppffffffpppfffpppfffffffffffffff^^^‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^fffwww†††wwwwww†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘––––––––– –––––––––––– ªªª ––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎ×××××××××××××××××××ÔÔÛ··ïBBú��ú��÷ Ù™™ÇÇǶ¶¶ ÁÁÁïïïïïïÎÎÎú��ú��"""""""""""""""---333EEEGGGVVVVVV^^^fffffffffpppfffffffffffffffpppfffpppfffffffffffffffpppfffpppfffffffff^^^fffffffffffffff^^^fff^^^ffffff^^^fffffffff^^^^^^fffffffffpppfffffffff^^^fffffffffpppppppppfffppppppfffpppppppppfffpppfffppppppppppppwwwppppppppppppppppppffffffppppppwwwpppwwwpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwppppppfffffffffVVVVVV^^^pppppppppfffpppffffffffffffffffff^^^fffffffffpppfffpppffffffffffffpppfffffffffNNNGGGffffffffffff^^^^^^fff^^^ffffffpppfffpppppppppfffpppfffpppfffffffffpppfff^^^†††††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www^^^^^^www†††wwwwww††††††‘‘‘‘‘‘‘‘‘–––––––––––––––––– –––––– ––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°ÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎ×××××××××××××××ú��ú��ú��ú��ùëYYÚ¼¼×××ÎÎÎÇÇÇÁÁÁªªª†††ÁÁÁßßßÁÁÁú��ú��"""---333===GGGNNNVVV^^^^^^ffffff^^^fffffffffpppfffpppfffpppfffffffffffffffpppffffffpppfffpppfffffffffffffffffffff^^^fff^^^fff^^^^^^fff^^^fff^^^fffffffffffffffffffff^^^^^^fffffffffffffffffffffpppfffpppfffpppfffpppppppppfffpppppppppppppppppppppfffpppppppppfffffffffpppppppppwwwpppppppppppppppwwwpppppppppppppppwwwpppwwwppppppppppppfffpppfffVVVVVVfffpppfffpppffffffffffffffffffpppfff^^^^^^pppfffpppfffpppfffpppfffpppfffpppffffffVVVNNNVVVfffpppffffff^^^fff^^^fffffffffffffffpppfffpppfffpppfffpppffffff^^^fffffffff†††‘‘‘†††‘‘‘‘‘‘†††fff^^^ppp†††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶°°°¶¶¶ÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ×××ÎÎÎ×××ú��ú��ú��ú��ú��øèhh×ÖÖ××××××ÎÎÎÇÇǰ°°†††ppp°°°†††ú��ú��333333EEENNNVVV^^^^^^fffpppfffffffffffffffffffffppppppfffpppffffffpppffffffffffffpppfffpppffffff^^^^^^ffffffffffffffffff^^^fff^^^fff^^^fff^^^^^^ffffffffffffffffff^^^^^^fff^^^ffffffpppfffpppfffpppfffppppppfffppppppfffpppfffppppppfffpppfffpppfffppppppfffpppfffffffffpppwwwpppwwwppppppwwwppppppwwwpppwwwfffppppppppppppppppppppppppfffpppffffffVVVVVV^^^pppfffppppppffffffffffffppppppfff^^^^^^fffffffffppppppfffpppfffpppfffpppffffff^^^VVVVVVfffffffffppp^^^^^^fff^^^ffffffffffffpppffffffpppfffpppfffpppfff^^^fffffffff^^^‘‘‘‘‘‘‘‘‘††††††ppp^^^ppp†††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––––––––– ––– ªªªªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶°°°¶¶¶ÁÁÁ¶¶¶ÁÁÁÇÇÇÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎ××××××××××ÑÑÞ©©ó((ú��ú��ú��æxxßßß××××××ÎÎÎú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ffffffppppppffffffffffffffffffffffffpppfffffffffffffffpppfffpppfffpppfffffffff^^^ffffff^^^ffffff^^^fff^^^^^^fff^^^fff^^^^^^^^^fffffffffffffffffffffffffff^^^fffpppfffpppffffffpppppppppfffpppppppppfffpppfffppppppppppppppppppppppppfffpppppppppffffffppppppppppppwwwpppppppppppppppppppppfffppppppppppppppppppppppppppppppffffffVVVNNN^^^pppppppppfffpppfffpppffffffpppfff^^^^^^fffffffffpppffffffffffffppppppffffffpppfffNNNVVV^^^fffpppfffffffffffffff^^^^^^fffffffffpppfffffffffpppffffffffffffffffffffffff^^^‘‘‘‘‘‘†††www^^^fffwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––– ‘‘‘––––––––– ªªª ––– ªªªªªª ªªªªªªªªªªªªªªª ªªª ªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶°°°°°°°°°ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÇÇÇÎÎÎÎÎÎ××××××××××××××××××ݬ¬ú��ú��ú��ö!!××××××ÎÎÎÇÇÇú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��ú��fffpppfffpppfffpppffffffffffffpppfffpppfffffffffpppfffffffffpppfffpppffffffffffff^^^ffffff^^^fff^^^^^^^^^fffffffff^^^^^^^^^^^^ffffffffffffffffff^^^fffffffffpppfffpppfffpppffffffpppfffpppfffpppppppppfffppppppppppppppppppffffffpppfffppppppffffffppppppwwwwwwpppppppppwwwppppppppppppppppppppppppppppppppppppppppppfffffffffVVVVVV^^^fffpppfffpppffffffpppffffffpppfff^^^^^^fffffffffpppfffpppfffpppfffpppffffffpppfffVVVNNN^^^pppfffpppfffffffff^^^^^^ffffff^^^ffffffffffffffffffffffffffffffffffffffffff^^^^^^†††www^^^fffwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––––– ––– ªªª ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°°°°°°°¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÇÇÇÎÎÎ×××××רÏÏú��ú��ú��ø×××ÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ªªª‘‘‘ú��ú��††††††wwwwwwwwwpppppppppfffpppfffpppppppppfffppppppppppppppppppfffffffffffffffffffff^^^fff^^^^^^fffffffff^^^fff^^^^^^^^^^^^ffffffffffff^^^^^^ffffffpppfffffffffffffffpppffffffppppppffffffpppfffpppfffpppppppppppppppfffpppppppppppppppfffffffffpppppppppwwwppppppppppppppppppppppppppppppppppppfffppppppppppppppppppfffpppVVVVVVVVVfffpppfffpppfffpppfffffffffffffff^^^^^^fff^^^ppppppfffffffffppppppppppppfffpppppp^^^GGGVVVfffppppppfffpppfffffffffffffffffffff^^^ffffffffffffffffffffffffffffff^^^fff^^^fff^^^^^^^^^pppwwwwwwwwwwwwwwwwww†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––––– ––– –––––– ––– ªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÎÎÎÎÎÎÎÎÎÎÎÎÞ¥¥ú��ú��ú��ôÇÇÇÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶°°°ªªª–––ú��ú��†††††††††††††††††††††††††††wwwwwwpppwwwwwwwwwwwwppppppppppppppppppfffpppfffpppfffpppfffpppfff^^^fff^^^fffffffffffffffffffffffffffffffffpppfffffffffffffffpppfffpppffffffffffffpppfffpppfffpppppppppfffppppppppppppppppppppppppfffpppfffpppfffffffffpppppppppppppppfffpppppppppfffwwwppppppfffwwwppppppppppppppppppppppppfffpppVVVVVV^^^fffpppppppppfffpppfffffffffpppppp^^^^^^ffffffffffffpppffffffffffffpppwwwppppppppppppNNNGGGfffpppffffffpppfffffffffffffffffffffffffffpppfffffffffffffffffffffffffffffffff^^^^^^^^^wwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ‘‘‘‘‘‘‘‘‘––––––––– ––– ––––––––––––––– ––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÇÇÇÁÁÁÇÇÇÁÁÁÁÁÁî++ÛvvΨ¨ÈÁÁÐÂÂÚ““ôú��ú��ú��Ý__ÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª–––ú��ú��†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwppppppppppppppppppppppppfffffffffffffffffffffffffffpppfffffffffppppppppppppffffffpppfffpppffffffpppfffppppppppppppppppppfffpppfffppppppppppppppppppwwwwwwfffpppppppppffffffffffffppppppppppppppppppfffppppppppppppfffppppppwwwppppppppppppppppppppppppppp^^^NNN^^^fffppppppfffpppfffpppfffffffffpppfff^^^ffffffffffffpppfffffffffffffffpppfffppppppwww^^^GGG^^^pppfffffffffpppffffff^^^ffffffffffffppppppffffffffffffpppfffffffffffffff^^^ffffffffffffwwwwwwwwwwwwwwwwwwwwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘––––––‘‘‘–––––––––––––––––––––––– ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°°°°ªªª°°°ªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ªªª°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁú��ú��ú��ú��ú��ú��ú��ú��ú��è<<·±±ÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††www†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwppppppwwwpppwwwpppwwwpppfffpppfffpppppppppfffppppppfffpppppppppppppppppppppfffppppppppppppppppppppppppppppppppppppwwwwwwwwwwwwppppppppppppppppppffffffppppppppppppppppppppppppppppppppppppfffppppppwwwpppwwwppppppwwwppppppfffppp^^^NNNVVVfffwwwpppfffppppppfffpppfffppppppffffffffffffffffffffffffffffffpppffffffffffffpppwwwfffNNN^^^pppfffpppfffppppppffffffffffffffffffpppppppppffffffpppfffffffffffffffffffff^^^fff^^^ffffffwwwwwwwwwwwwwwwwww†††††††††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘––––––––––––––––––––––––‘‘‘––––––––– ––––––––––––––– ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°ªªª ªªª°°°°°°ªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁË››ßXXï))öù÷ òåEEщ‰¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶°°°ªªª –––ú��ú��‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††www†††‘‘‘†††††††††††††††††††††††††††wwwwwwwwwwwwwwwpppwwwwwwpppppppppfffppppppwwwpppppppppfffpppppppppppppppppppppppppppwwwpppppppppppppppwwwppppppppppppwwwwwwwwwwwwppppppppppppppppppfffpppppppppppppppppppppppppppppppppppppppfffppppppwwwwwwpppppppppwwwpppppppppppp^^^NNNNNNfffppppppppppppfffpppfffpppfffpppffffffffffffffffffppppppffffffpppfffffffffffffffppp^^^GGGVVVfffppppppfffppppppffffffffffffppppppfffppppppfff^^^fffppppppfffffffffffffffVVV^^^^^^^^^fff^^^wwwwwwwww†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘––––––––––––––––––‘‘‘––––––––––––––––––––––––‘‘‘–––––– ––– ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°ªªªªªª°°°°°°ªªª°°°ªªª °°°ªªª°°°°°°ªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÇÇÇÇÇÇÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶°°°ªªªªªª –––––––––––––––‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwpppwwwwwwpppwwwwwwwwwwwwwwwpppwwwpppppppppwwwppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwpppppppppwwwwwwwwwwwwwwwpppwwwpppwwwwwwppppppppppppwwwppppppwwwwwwwwwpppppppppwwwppppppppppppwwwwwwppppppwwwppppppppppppffffffVVVNNNfffwwwppppppfffpppppppppfffppppppppppppfffpppffffffpppfffpppfffffffffffffffffffffffffffGGGEEE^^^ppppppfffpppfffffffffffffffffffffpppfffpppffffff^^^pppffffffppppppfffffffffVVV^^^ffffff^^^fffpppwww††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘––––––––––––––– –––––––––‘‘‘–––––––––––– –––––– ––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª°°°ªªª °°°°°°ªªª°°°°°°°°°ªªªªªªªªªªªª ªªªªªª°°°°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°ªªª ––––––––––––‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††††††††www†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppfffpppwwwpppwwwwwwwwwwwwwwwpppwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwppppppfffVVVVVVfffpppppppppppppppppppppfffpppppppppppppppfffppppppfffppppppfffpppfffffffff^^^fffppppppVVV===GGGfffpppfffpppppppppfff^^^fffffffffpppfffffffffpppfffffffffffffffffffffffffff^^^^^^^^^ffffffffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––––––‘‘‘‘‘‘‘‘‘––––––––– ––– –––––– ––– ––– ªªª°°°°°°ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªªªªª ªªª°°°°°°°°°°°°¶¶¶°°°ªªª–––†††–––ªªªªªªªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ªªªªªªªªª –––‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^pppwwwpppppppppwwwpppppppppppppppppppppppppppfffppppppffffffpppppppppffffffffffffppp^^^EEEEEEfffpppfffffffffpppfff^^^ffffffpppffffffffffffpppfff^^^fffppppppfffffffffffffffffffff^^^fff^^^ffffff††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘––––––––––––––––––‘‘‘‘‘‘––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°–––†††‘‘‘ªªª°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶ÁÁÁÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªª ––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††‘‘‘–––‘‘‘†††www††††††wwwwwwwwwwwwwwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppp^^^VVV^^^ppppppwwwpppwwwwwwppppppppppppwwwpppfffpppfffpppfffpppppppppppppppfffpppffffffppppppfffGGG===^^^pppfffffffffppppppfff^^^ffffffffffffffffffffffffffffffffffffppppppfffffffffpppfffffffffffffffffffff†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––– ––––––––– –––––– ªªªªªªªªª ªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°ªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°ªªªªªª †††www†††–––ªªª°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶ÁÁÁ¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°ªªªªªªªªª ªªª ––––––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††–––‘‘‘†††††††††††††††††††††††††††wwwpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwpppwwwppppppwwwwwwwwwpppppppppppppppppppppfffpppppppppppp^^^fffppppppwwwVVV===VVVppppppfffpppppppppfff^^^fff^^^^^^^^^^^^NNN^^^fffffffff^^^^^^pppfffffffffffffffppppppffffffpppppppppppp‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘––––––––– ––– ––– ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°° †††fffwww‘‘‘ °°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ¶¶¶¶¶¶ÁÁÁÁÁÁÁÁÁ¶¶¶¶¶¶¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ––– –––––– –––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††wwwwww†††www†††wwwwwwwwwwwwwwwwwwpppVVVVVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwppppppwwwppppppwwwppppppfffppppppfff^^^ppppppwwwpppGGGNNNpppwwwppppppwwwpppppp^^^^^^^^^^^^fffffffff^^^fffffffffffffffffffffppppppffffffpppfffpppppppppfffppppppppp†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––––––––––––––––– ––– ªªªªªª ªªª ªªªªªª ªªª ªªªªªªªªªªªª°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°° ªªª ªªªªªªªªª°°°ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°†††fff^^^–––ªªª°°°ªªª°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ÁÁÁ°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°¶¶¶°°°°°°°°°¶¶¶¶¶¶¶¶¶¶¶¶°°°°°°°°°ªªªªªª ªªª ––– ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††††††††††††††††††††wwwwwwwwwppp^^^VVVpppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwpppwwwwwwwwwppppppppppppppp^^^fffwwwwwwwwwfffVVVppppppwwwwwwpppwwwwwwfff^^^^^^^^^ffffffffffffppppppppp^^^fffffffffffffffffffffffffffffffffpppfffpppppppppppp‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘–––––––––––––––‘‘‘––– ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°ªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªª ªªªªªª°°°°°°°°°ªªªªªª‘‘‘ppp^^^fff‘‘‘ ªªª°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶°°°¶¶¶°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªª ––– –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwwwwfffVVVfffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww†††pppppppppppp^^^fffwwwwwwwwwpppffffffwwwpppwwwwwwwwwwwwffffff^^^^^^^^^ffffff^^^fffpppfffffffffffffffffffffffffff^^^^^^fffpppfffffffffpppfffpppppp‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘–––‘‘‘–––––––––‘‘‘–––––– ªªª ªªª ªªª ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªªªªªªªªªªª ªªª ––––––‘‘‘–––ªªªªªª°°°°°°°°°°°°–––wwwVVVVVVwww ªªª°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°ªªªªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††www^^^fffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppppppppfff^^^wwwwwwfffpppppppppwwwwwwwwwwwwpppfffVVVVVV^^^^^^VVV^^^fffffffff^^^fffffffffpppfffffffffffffffpppfffpppffffffffffffppppppppp‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘–––––––––‘‘‘–––––––––––– ªªª ªªª ªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªª –––‘‘‘†††––– °°°°°°°°°°°°ªªªVVVNNNfff††† ªªªªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°¶¶¶°°°°°°ªªªªªª°°°ªªª°°°°°°°°°ªªª°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘††††††††††††‘‘‘††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††wwwwww†††‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘‘‘‘††††††††††††††††††††††††wwwppp†††wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpppwwwwwwpppfffwwwwwwpppppppppwwwwwwwwwwwwwwwppp^^^VVV^^^^^^^^^^^^^^^^^^ffffff^^^^^^fffffffff^^^ffffffffffffpppffffffppp^^^ffffffppppppppp††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘––––––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘–––––––––––– ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªª –––––––––†††wwwffffffwww‘‘‘ªªªªªª°°°°°°ªªª–––fffVVV^^^––– ªªª°°°°°°°°°°°°°°°¶¶¶°°°°°°¶¶¶°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°¶¶¶°°°¶¶¶¶¶¶°°°°°°°°°ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªª ªªªªªª –––––––––‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘†††††††††pppwww†††††††††wwwwwwwwwwwwwwwwwwfff^^^wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffpppfffffffff^^^fff^^^ffffff^^^fff^^^VVV^^^^^^^^^ffffffffffffffffffppp^^^fffppppppffffff†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª–––‘‘‘†††ppp^^^VVV^^^www ªªªªªª°°°°°° www^^^^^^www‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°°°°¶¶¶¶¶¶°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª ªªª ––– –––‘‘‘––––––––––––‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘††††††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘†††wwwwww†††††††††††††††wwwwwwwwwwwwwwwVVVNNNwwwwwwwwwwwwwwwwww†††wwwfffppppppwwwpppppppppfffffffffffffffffffff^^^^^^^^^^^^^^^ffffffffffffffffffffffffpppppppppfff††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– ªªª ªªªªªªªªªªªªªªª ªªª ––– ªªª ªªªªªª †††pppfff^^^NNNVVVfff††† °°°ªªª°°° †††fffVVVppp‘‘‘ ªªªªªªªªªªªª°°°°°°°°°°°°ªªª°°°¶¶¶°°°°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶°°°¶¶¶°°°°°°°°°¶¶¶°°°¶¶¶ªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªª°°°ªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††www††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††www†††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††www†††††††††††††††wwwwwwwwwwwwwwwwwwGGGGGGwwwwwwwwwwwwwwwwwwwwwwww†††wwwpppwww†††‘‘‘††††††wwwwwwwwwffffffffffff^^^^^^VVV^^^^^^^^^fffppppppfffpppffffffpppfffpppffffff‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– ––– ––– ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª †††wwwfff^^^VVVVVVNNNNNNppp‘‘‘ªªª°°°°°° †††ppp^^^fff††† ªªªªªªªªªªªªªªªªªª°°°°°°ªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°ªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°¶¶¶ªªª °°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°ªªª°°°ªªª°°°ªªªªªªªªªªªªªªª ––– –––––– –––––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††wwwwwwpppGGGEEEpppwwwwwwwwwwwwwwwwww†††††††††††††††‘‘‘–––––––––‘‘‘††††††wwwwwwwwwpppfff^^^VVVVVVVVV^^^^^^fffffffffpppffffffppppppppppppfff†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––– †††www^^^NNNVVVVVVNNNNNNVVV ªªªªªªªªª‘‘‘ppp^^^^^^www‘‘‘ ªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°ªªªªªªªªª°°°°°°°°°°°°ªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶°°°¶¶¶¶¶¶°°°ªªªªªªªªªªªª°°°ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª°°°ªªª°°°°°°ªªª°°°°°°ªªªªªª ªªª –––––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††wwwwwwwww†††www^^^NNNfffpppwww‘‘‘–––‘‘‘‘‘‘––– ––––––‘‘‘††††††wwwwwwfff^^^^^^VVV^^^VVV^^^ffffffffffffpppfffpppfffppp^^^^^^††††††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––––– ––– ––– ªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªª ––––––‘‘‘www^^^VVVNNNNNNNNNNNNNNNfff‘‘‘ ªªªªªª–––^^^^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°ªªªªªª ––– ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––††††††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††††††††www††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††wwwwww††††††wwwffffffpppwwwwww††††††††††††––– –––––– ––––––‘‘‘††††††††††††wwwfff^^^^^^^^^^^^^^^^^^^^^ffffffpppppppppffffffffffff‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––– –––––– ––– ªªª ªªªªªªªªªªªª ªªª –––––––––‘‘‘www^^^NNNNNNNNNNNNNNNNNNVVV–––ªªªªªª–––†††fff^^^ppp†††––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°¶¶¶°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ––– ––– –––––– –––‘‘‘–––‘‘‘–––‘‘‘–––‘‘‘††††††††††††‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘††††††www††††††‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††††††††‘‘‘‘‘‘††††††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘‘‘‘‘‘‘www††††††††††††wwwwwwwwwwww†††††††††††††††‘‘‘––– ªªª –––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††ppppppfffffffff^^^^^^VVV^^^fffpppfffpppppp^^^^^^^^^††††††‘‘‘†††‘‘‘‘‘‘†††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘–––––– ––– –––––– ªªª –––––––––‘‘‘www^^^NNNGGGNNNNNNNNNNNNNNN^^^‘‘‘ ªªª †††ppp^^^fff‘‘‘––– ªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°¶¶¶¶¶¶¶¶¶°°°°°°ªªªªªªªªª°°°ªªªªªª ªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªªªªª ªªª ªªª ªªªªªªªªªªªª ªªª ––– ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www††††††–––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––‘‘‘††††††‘‘‘††††††‘‘‘†††††††††‘‘‘†††††††††††††††††††††‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www††††††‘‘‘††††††††††††‘‘‘‘‘‘––––––ªªª –––––––––‘‘‘–––‘‘‘††††††wwwwwwffffff^^^^^^^^^ffffffpppppppppffffff^^^^^^‘‘‘†††‘‘‘†††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––––– ªªª ªªª ––––––†††www^^^NNNGGGNNNVVVNNNNNNNNNVVVwww––– ªªª‘‘‘ppp^^^^^^www‘‘‘‘‘‘––– ––– ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªª ªªªªªª ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––– ––––––––––––––––––‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††www‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††††††††‘‘‘‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††‘‘‘†††††††††††††††††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘www††† ªªª ––––––––––––‘‘‘‘‘‘‘‘‘wwwwwwppp^^^^^^fffffffffwwwpppffffffffffff^^^†††‘‘‘†††††††††††††††‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––––– ––– ––––––––– ªªª ªªª ––––––†††wwwfffNNNGGGNNNVVVVVVNNNNNNNNNfff‘‘‘ –––^^^VVVfff†††‘‘‘––––––––– ––– ªªªªªª ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªª°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªª ªªª ªªª ªªªªªª ªªªªªª ªªªªªª ªªªªªªªªªªªª ––– ––––––––––––‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††††††††††††††††††††‘‘‘‘‘‘––––––www††† ªªªªªª ªªª –––––––––––––––‘‘‘––––––‘‘‘‘‘‘†††‘‘‘†††wwwppppppffffffppppppwwwfff^^^fffffffff†††††††††††††††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘––––––––– ––– ªªª ªªª –––‘‘‘wwwfffNNNGGGNNNNNNNNNVVVNNNNNN^^^www–––ªªª †††ppp^^^fffwww‘‘‘‘‘‘––– –––––– ––– ªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°ªªª°°°°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªª ––– ªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª ––––––––––––––––––––––––‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘†††‘‘‘††††††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘†††www‘‘‘‘‘‘–––‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘†††††††††††††††††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††††††††‘‘‘††††††††††††‘‘‘––– ––– ––– ªªªªªªªªª ªªª ––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘wwwpppppppppwwwpppfff^^^fffffffff††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––– ––– ––– –––‘‘‘wwwfffNNNGGGGGGNNNNNNNNNVVVNNNVVVppp‘‘‘ ‘‘‘wwwfffppp†††––––––––– ––– ––– ––– ªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªªªªªªªªªªªªªªªªª°°°ªªª°°°°°°°°°°°°°°°ªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªª ªªªªªª ªªª ªªª ªªªªªªªªª ªªªªªªªªª ––– –––––– ––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††††††††††††††††††††‘‘‘†††††††††††††††††††††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††††††††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘††††††‘‘‘††††††††††††‘‘‘‘‘‘††††††††††††††††††‘‘‘†††‘‘‘††††††††††††††††††††††††††††††††††††††††††††††††‘‘‘––– ªªª ªªªªªªªªª ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††wwwpppwwwwwwpppfffffffffffffff†††††††††‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘––––––––– ––– ––– ––– –––‘‘‘fffNNNGGGGGGNNNVVVVVVVVVNNNNNN^^^–––ªªª †††pppfff––– ªªªªªªªªª ––– ªªªªªªªªªªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªªªªªªªªªªªªªª°°°°°°ªªª°°°°°°ªªªªªªªªªªªª°°°ªªªªªªªªª ªªªªªª ªªªªªªªªªªªªªªªªªª ªªªªªª ªªªªªªªªª ªªª ªªª ––––––––––––––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘†††‘‘‘†††‘‘‘†††‘‘‘††††††††††††‘‘‘††††††‘‘‘†††‘‘‘†††††††††††††††††††††wwwwww‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘–––––––––‘‘‘‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††††††††‘‘‘†††‘‘‘††††††††††††‘‘‘†††‘‘‘††††††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††‘‘‘†††††††††††††††††††††††††††††††††††††††††††††††††††–––––– ªªª ––– ––– ––––––––––––‘‘‘www††††††wwwwwwwwwwwwpppfffpppffffff^^^^^^††††††‘‘‘‘‘‘††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘–––––––––––– ––– ––– ––– ––– ––––––‘‘‘pppVVVGGGGGGNNNVVVVVVVVVVVVNNNVVVppp‘‘‘ ªªª‘‘‘ppp‘‘‘––– °°°°°° ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªªªªªªªª°°°ªªªªªª°°°°°°°°°°°°ªªªªªªªªªªªªªªª°°°ªªªªªª ªªª ªªªªªªªªªªªª °°°ªªªªªªªªªªªªªªª ªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ––– ––– –––––––––‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘‘‘‘–––‘‘‘‘‘‘††††††‘‘‘‘‘‘‘‘‘†††‘‘‘††††††††††††††††††‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††††††††www†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘––––––––––––‘‘‘‘‘‘‘‘‘‘‘‘–––‘‘‘†††‘‘‘†††‘‘‘–––‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘†††‘‘‘††††††‘‘‘†††††††††‘‘‘††††††‘‘‘††††††††††††‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘††††††††††††††††††††††††‘‘‘–––––– ªªª –––––– ªªª ––– –––††††††††††††wwwpppffffffpppffffff^^^†††‘‘‘†††††††††‘‘‘†††‘‘‘†††‘‘‘‘‘‘–––––––––––– ––– ––––––––– ––– ––– ªªªªªª ––––––†††pppVVVGGGGGGNNNVVVVVVVVVVVVVVVNNN^^^––– †††www†††––– ªªª ªªªªªª°°°ªªª°°°°°°ªªªªªªªªª ªªª ªªªªªª ªªªªªª ªªªªªªªªª°°°°°°°°°°°°°°°ªªªªªªªªª°°°ªªªªªªªªªªªªªªª ªªª ªªªªªªªªªªªª ––– ªªª°°°ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª ªªªªªª ªªª ªªªªªªªªª ªªª ––– –––‘‘‘‘‘‘‘‘‘–––‘‘‘‘‘‘†††‘‘‘–––‘‘‘‘‘‘†††‘‘‘‘‘‘‘‘‘†††††††††††††††‘‘‘†††††††††††††††‘‘‘†††‘‘‘††††††‘‘‘†††††††††††††††www††††††††††††wwwwww††††††‘‘‘‘‘‘‘‘‘‘‘‘––––––‘‘‘–––––––––‘‘‘‘‘‘‘‘‘––––––‘‘‘†††‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘††††††‘‘‘‘‘‘†††‘‘‘†††‘‘‘‘‘‘‘‘‘†††‘‘‘†††††††††‘‘‘††††††‘‘‘†††††††††‘‘‘‘‘‘†††‘‘‘†††††††††††††††‘‘‘††††††‘‘‘‘‘‘††††††††††††††††††††††††††††††–––––– –––––– ––– ªªª ªªª –––––– –––‘‘‘‘‘‘‘‘‘‘‘‘†††wwwffffffffffffppppppfff \ No newline at end of file diff --git a/tutorial/tracking/model-based/keypoint/teabox.xml b/tutorial/tracking/model-based/keypoint/teabox.xml index 0937e9ee703b9a39de7c128e040d7961a1d3568b..6e1762b42c5ba2da16e3f001b0b62c9e9d64b294 100644 --- a/tutorial/tracking/model-based/keypoint/teabox.xml +++ b/tutorial/tracking/model-based/keypoint/teabox.xml @@ -1,12 +1,5 @@ <?xml version="1.0"?> <conf> - <face> - <angle_appear>70</angle_appear> - <angle_disappear>80</angle_disappear> - <near_clipping>0.1</near_clipping> - <far_clipping>100</far_clipping> - <fov_clipping>1</fov_clipping> - </face> <klt> <mask_border>5</mask_border> <max_features>300</max_features> @@ -23,5 +16,12 @@ <px>839.21470</px> <py>839.44555</py> </camera> + <face> + <angle_appear>70</angle_appear> + <angle_disappear>80</angle_disappear> + <near_clipping>0.1</near_clipping> + <far_clipping>100</far_clipping> + <fov_clipping>1</fov_clipping> + </face> </conf> diff --git a/tutorial/tracking/model-based/keypoint/tutorial-mb-klt-tracker.cpp b/tutorial/tracking/model-based/keypoint/tutorial-mb-klt-tracker.cpp index 46582dedb46b7983439f42ec9fe6bb1c84138fcb..b2620b638ace086427eeb01f8cf3352193cfa4ee 100644 --- a/tutorial/tracking/model-based/keypoint/tutorial-mb-klt-tracker.cpp +++ b/tutorial/tracking/model-based/keypoint/tutorial-mb-klt-tracker.cpp @@ -1,73 +1,127 @@ /*! \example tutorial-mb-klt-tracker.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpImageIo.h> +#include <visp/vpIoTools.h> #include <visp/vpMbKltTracker.h> +#include <visp/vpVideoReader.h> -int main() +int main(int argc, char** argv) { -#ifdef VISP_HAVE_OPENCV - vpImage<unsigned char> I; - vpCameraParameters cam; - vpHomogeneousMatrix cMo; +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) - vpImageIo::read(I, "teabox.pgm"); + try { + std::string videoname = "teabox.mpg"; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--name") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + std::string parentname = vpIoTools::getParent(videoname); + std::string objectname = vpIoTools::getNameWE(videoname); + + if(! parentname.empty()) + objectname = parentname + "/" + objectname; + + std::cout << "Video name: " << videoname << std::endl; + std::cout << "Tracker requested config files: " << objectname + << ".[init," +#ifdef VISP_HAVE_XML2 + << "xml," +#endif + << "cao or wrl]" << std::endl; + std::cout << "Tracker optional config files: " << objectname << ".[ppm]" << std::endl; + vpImage<unsigned char> I; + vpCameraParameters cam; + vpHomogeneousMatrix cMo; + + vpVideoReader g; + g.setFileName(videoname); + g.open(I); #if defined(VISP_HAVE_X11) - vpDisplayX display(I,100,100,"Model-based keypoints tracker");; + vpDisplayX display; #elif defined(VISP_HAVE_GDI) - vpDisplayGDI display(I,100,100,"Model-based keypoints tracker");; + vpDisplayGDI display; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV display; #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; + return 0; #endif - vpMbKltTracker tracker; + display.init(I, 100, 100,"Model-based keypoint tracker"); + + vpMbKltTracker tracker; + bool usexml = false; + //! [Load xml] #ifdef VISP_HAVE_XML2 - tracker.loadConfigFile("teabox.xml"); -#else - tracker.setAngleAppear(70); - tracker.setAngleDisappear(80); - tracker.setMaskBorder(5); - vpKltOpencv klt_settings; - klt_settings.setMaxFeatures(300); - klt_settings.setWindowSize(5); - klt_settings.setQuality(0.015); - klt_settings.setMinDistance(8); - klt_settings.setHarrisFreeParameter(0.01); - klt_settings.setBlockSize(3); - klt_settings.setPyramidLevels(3); - tracker.setKltOpencv(klt_settings); - cam.initPersProjWithoutDistortion(839, 839, 325, 243); - tracker.setCameraParameters(cam); - tracker.setNearClippingDistance(0.1); - tracker.setFarClippingDistance(100.0); - tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + if(vpIoTools::checkFilename(objectname + ".xml")) { + tracker.loadConfigFile(objectname + ".xml"); + usexml = true; + } #endif - tracker.setDisplayFeatures(true); - tracker.setOgreVisibilityTest(true); - tracker.loadModel("teabox-triangle.cao"); - tracker.initClick(I, "teabox.init"); + //! [Load xml] + if (! usexml) { + //! [Set parameters] + tracker.setMaskBorder(5); + vpKltOpencv klt_settings; + klt_settings.setMaxFeatures(300); + klt_settings.setWindowSize(5); + klt_settings.setQuality(0.015); + klt_settings.setMinDistance(8); + klt_settings.setHarrisFreeParameter(0.01); + klt_settings.setBlockSize(3); + klt_settings.setPyramidLevels(3); + tracker.setKltOpencv(klt_settings); + cam.initPersProjWithoutDistortion(839, 839, 325, 243); + tracker.setCameraParameters(cam); + tracker.setAngleAppear( vpMath::rad(70) ); + tracker.setAngleDisappear( vpMath::rad(80) ); + tracker.setNearClippingDistance(0.1); + tracker.setFarClippingDistance(100.0); + tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING); + //! [Set parameters] + } + tracker.setOgreVisibilityTest(true); + tracker.loadModel(objectname + "-triangle.cao"); + tracker.setDisplayFeatures(true); + tracker.initClick(I, objectname + ".init", true); - while(1){ - vpDisplay::display(I); - tracker.track(I); - tracker.getPose(cMo); - tracker.getCameraParameters(cam); - tracker.display(I, cMo, cam, vpColor::red, 2, true); - vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); - vpDisplay::flush(I); + while(! g.end()){ + g.acquire(I); + vpDisplay::display(I); + tracker.track(I); + tracker.getPose(cMo); + tracker.getCameraParameters(cam); + tracker.display(I, cMo, cam, vpColor::red, 2, true); + vpDisplay::displayFrame(I, cMo, cam, 0.025, vpColor::none, 3); + vpDisplay::displayText(I, 10, 10, "A click to exit...", vpColor::red); + vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; - vpTime::wait(40); - } + if (vpDisplay::getClick(I, false)) + break; + } + vpDisplay::getClick(I); #ifdef VISP_HAVE_XML2 - vpXmlParser::cleanup(); + vpXmlParser::cleanup(); #endif -#ifdef VISP_HAVE_COIN - SoDB::finish(); +#if defined(VISP_HAVE_COIN) && (COIN_MAJOR_VERSION == 3) + SoDB::finish(); #endif - + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +#else + (void)argc; + (void)argv; + std::cout << "Install OpenCV and rebuild ViSP to use this example." << std::endl; #endif } diff --git a/tutorial/tracking/moving-edges/CMakeLists.txt b/tutorial/tracking/moving-edges/CMakeLists.txt index 93d9a6ded309d0a901a1e43d845d005868ebb30e..590ef19aca73f298e6c2d7502da19f041fc4676f 100644 --- a/tutorial/tracking/moving-edges/CMakeLists.txt +++ b/tutorial/tracking/moving-edges/CMakeLists.txt @@ -3,10 +3,15 @@ project(tutorial-tracking-me) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-me-ellipse-tracker tutorial-me-ellipse-tracker.cpp) -add_executable(tutorial-me-line-tracker tutorial-me-line-tracker.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-me-ellipse-tracker.cpp + tutorial-me-line-tracker.cpp) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() diff --git a/tutorial/tracking/moving-edges/tutorial-me-ellipse-tracker.cpp b/tutorial/tracking/moving-edges/tutorial-me-ellipse-tracker.cpp index 319762e0498e32a8b4af522a653bd2c2224b000d..38d5bc13ede2ffd4b2ee2a8039047069bc026609 100644 --- a/tutorial/tracking/moving-edges/tutorial-me-ellipse-tracker.cpp +++ b/tutorial/tracking/moving-edges/tutorial-me-ellipse-tracker.cpp @@ -3,52 +3,59 @@ #include <visp/vp1394TwoGrabber.h> #include <visp/vpV4l2Grabber.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpMeEllipse.h> int main() { #if (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) || defined(VISP_HAVE_V4L2)) - - vpImage<unsigned char> I; + try { + vpImage<unsigned char> I; #if defined(VISP_HAVE_DC1394_2) - vp1394TwoGrabber g(false); + vp1394TwoGrabber g(false); #elif defined(VISP_HAVE_CMU1394) - vp1394CMUGrabber g; + vp1394CMUGrabber g; #elif defined(VISP_HAVE_V4L2) - vpV4l2Grabber g; + vpV4l2Grabber g; #endif - g.open(I); - g.acquire(I); + g.open(I); + g.acquire(I); #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Camera view"); + vpDisplayX d(I, 0, 0, "Camera view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Camera view"); + vpDisplayGDI d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::display(I); - vpDisplay::flush(I); - - vpMe me; - me.setRange(25); - me.setThreshold(15000); - me.setSampleStep(10); - - vpMeEllipse ellipse; - ellipse.setMe(&me); - ellipse.setDisplay(vpMeSite::RANGE_RESULT); - ellipse.initTracking(I); - - while(1) { - g.acquire(I); vpDisplay::display(I); - ellipse.track(I); - ellipse.display(I, vpColor::red); vpDisplay::flush(I); + + vpMe me; + me.setRange(25); + me.setThreshold(15000); + me.setSampleStep(10); + + vpMeEllipse ellipse; + ellipse.setMe(&me); + ellipse.setDisplay(vpMeSite::RANGE_RESULT); + ellipse.initTracking(I); + + while(1) { + g.acquire(I); + vpDisplay::display(I); + ellipse.track(I); + ellipse.display(I, vpColor::red); + vpDisplay::flush(I); + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/tracking/moving-edges/tutorial-me-line-tracker.cpp b/tutorial/tracking/moving-edges/tutorial-me-line-tracker.cpp index 4881a04fb26904e190e3d1392f6f171e93bbe10a..5c6657edb7ed1a6f271acd209c9801f8d9d27da4 100644 --- a/tutorial/tracking/moving-edges/tutorial-me-line-tracker.cpp +++ b/tutorial/tracking/moving-edges/tutorial-me-line-tracker.cpp @@ -3,52 +3,59 @@ #include <visp/vp1394TwoGrabber.h> #include <visp/vpV4l2Grabber.h> #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpMeLine.h> int main() { #if (defined(VISP_HAVE_DC1394_2) || defined(VISP_HAVE_CMU1394) || defined(VISP_HAVE_V4L2)) - - vpImage<unsigned char> I; + try { + vpImage<unsigned char> I; #if defined(VISP_HAVE_DC1394_2) - vp1394TwoGrabber g(false); + vp1394TwoGrabber g(false); #elif defined(VISP_HAVE_CMU1394) - vp1394CMUGrabber g; + vp1394CMUGrabber g; #elif defined(VISP_HAVE_V4L2) - vpV4l2Grabber g; + vpV4l2Grabber g; #endif - g.open(I); - g.acquire(I); + g.open(I); + g.acquire(I); #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Camera view"); + vpDisplayX d(I, 0, 0, "Camera view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Camera view"); + vpDisplayGDI d(I, 0, 0, "Camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::display(I); - vpDisplay::flush(I); - - vpMe me; - me.setRange(25); - me.setThreshold(15000); - me.setSampleStep(10); - - vpMeLine line; - line.setMe(&me); - line.setDisplay(vpMeSite::RANGE_RESULT); - line.initTracking(I); - - while(1) { - g.acquire(I); vpDisplay::display(I); - line.track(I); - line.display(I, vpColor::red); vpDisplay::flush(I); + + vpMe me; + me.setRange(25); + me.setThreshold(15000); + me.setSampleStep(10); + + vpMeLine line; + line.setMe(&me); + line.setDisplay(vpMeSite::RANGE_RESULT); + line.initTracking(I); + + while(1) { + g.acquire(I); + vpDisplay::display(I); + line.track(I); + line.display(I, vpColor::red); + vpDisplay::flush(I); + } + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } #endif } diff --git a/tutorial/tracking/template-tracker/CMakeLists.txt b/tutorial/tracking/template-tracker/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d2b69576da54202d6bc665d4e656586ca3b3dee5 --- /dev/null +++ b/tutorial/tracking/template-tracker/CMakeLists.txt @@ -0,0 +1,23 @@ +project(tutorial-tracking-template) + +cmake_minimum_required(VERSION 2.6) + +find_package(VISP REQUIRED) + +# set the list of source files +set(tutorial_cpp + tutorial-template-tracker.cpp) + +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/bruegel.mpg" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-template-tracker.cpp ${data}) +endforeach() diff --git a/tutorial/tracking/template-tracker/bruegel.mpg b/tutorial/tracking/template-tracker/bruegel.mpg new file mode 100644 index 0000000000000000000000000000000000000000..3fa49c5a51766b2d04b422cf32bbc8a922034d42 Binary files /dev/null and b/tutorial/tracking/template-tracker/bruegel.mpg differ diff --git a/tutorial/tracking/template-tracker/tutorial-template-tracker.cpp b/tutorial/tracking/template-tracker/tutorial-template-tracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..921e5622399153dae088a0d70f4e7286c399a805 --- /dev/null +++ b/tutorial/tracking/template-tracker/tutorial-template-tracker.cpp @@ -0,0 +1,88 @@ +/*! \example tutorial-template-tracker.cpp */ +#include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> +#include <visp/vpVideoReader.h> +//! [Include] +#include <visp/vpTemplateTrackerSSDInverseCompositional.h> +#include <visp/vpTemplateTrackerWarpHomography.h> +//! [Include] + +int main(int argc, char** argv) +{ +#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x020100) || defined(VISP_HAVE_FFMPEG) + std::string videoname = "bruegel.mpg"; + + for (int i=0; i<argc; i++) { + if (std::string(argv[i]) == "--videoname") + videoname = std::string(argv[i+1]); + else if (std::string(argv[i]) == "--help") { + std::cout << "\nUsage: " << argv[0] << " [--name <video name>] [--help]\n" << std::endl; + return 0; + } + } + + std::cout << "Video name: " << videoname << std::endl; + + vpImage<unsigned char> I; + + vpVideoReader g; + g.setFileName(videoname); + g.open(I); + +#if defined(VISP_HAVE_X11) + vpDisplayX display; +#elif defined(VISP_HAVE_GDI) + vpDisplayGDI display; +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV display; +#else + std::cout << "No image viewer is available..." << std::endl; +#endif + + display.init(I, 100, 100, "Template tracker"); + vpDisplay::display(I); + vpDisplay::flush(I); + + //! [Construction] + vpTemplateTrackerWarpHomography warp; + vpTemplateTrackerSSDInverseCompositional tracker(&warp); + //! [Construction] + + tracker.setSampling(2, 2); + tracker.setLambda(0.001); + tracker.setIterationMax(200); + tracker.setPyramidal(2, 1); + + //! [Init] + tracker.initClick(I); + //! [Init] + + while(1){ + g.acquire(I); + vpDisplay::display(I); + + //! [Track] + tracker.track(I); + //! [Track] + + //! [Homography] + vpColVector p = tracker.getp(); + vpHomography H = warp.getHomography(p); + std::cout << "Homography: \n" << H << std::endl; + //! [Homography] + + //! [Display] + tracker.display(I, vpColor::red); + //! [Display] + + if (vpDisplay::getClick(I, false)) + break; + + vpDisplay::flush(I); + } +#else + (void)argc; + (void)argv; +#endif +} diff --git a/tutorial/visual-servo/ibvs/CMakeLists.txt b/tutorial/visual-servo/ibvs/CMakeLists.txt index a7134b807801bd7d1f2f1eed83a653a7f2191c7c..06edb2c835322fd7609f693a11867d737a3fde72 100644 --- a/tutorial/visual-servo/ibvs/CMakeLists.txt +++ b/tutorial/visual-servo/ibvs/CMakeLists.txt @@ -3,31 +3,34 @@ project(tutorial-visual-servo-ibvs) cmake_minimum_required(VERSION 2.6) find_package(VISP REQUIRED) -if(VISP_FOUND) - include(${VISP_USE_FILE}) -endif(VISP_FOUND) -# build the examples -add_executable(tutorial-ibvs-4pts tutorial-ibvs-4pts.cpp) -add_executable(tutorial-ibvs-4pts-display tutorial-ibvs-4pts-display.cpp) -add_executable(tutorial-ibvs-4pts-image-tracking tutorial-ibvs-4pts-image-tracking.cpp) -add_executable(tutorial-ibvs-4pts-ogre tutorial-ibvs-4pts-ogre.cpp) -add_executable(tutorial-ibvs-4pts-ogre-tracking tutorial-ibvs-4pts-ogre-tracking.cpp) -add_executable(tutorial-ibvs-4pts-plotter tutorial-ibvs-4pts-plotter.cpp) -add_executable(tutorial-ibvs-4pts-wireframe-camera tutorial-ibvs-4pts-wireframe-camera.cpp) -add_executable(tutorial-ibvs-4pts-wireframe-robot-afma6 tutorial-ibvs-4pts-wireframe-robot-afma6.cpp) -add_executable(tutorial-ibvs-4pts-wireframe-robot-viper tutorial-ibvs-4pts-wireframe-robot-viper.cpp) +# set the list of source files +set(tutorial_cpp + tutorial-ibvs-4pts.cpp + tutorial-ibvs-4pts-display.cpp + tutorial-ibvs-4pts-image-tracking.cpp + tutorial-ibvs-4pts-ogre.cpp + tutorial-ibvs-4pts-ogre-tracking.cpp + tutorial-ibvs-4pts-plotter.cpp + tutorial-ibvs-4pts-plotter-gain-adaptive.cpp + tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp + tutorial-ibvs-4pts-wireframe-camera.cpp + tutorial-ibvs-4pts-wireframe-robot-afma6.cpp + tutorial-ibvs-4pts-wireframe-robot-viper.cpp +) -# copy the Ogre3D data -get_target_property(target_location tutorial-ibvs-4pts-ogre LOCATION) -get_filename_component(target_location "${target_location}" PATH) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/sphere/Sphere.mesh" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/sphere/Sphere.material" ) -list(APPEND data2copy "${CMAKE_CURRENT_SOURCE_DIR}/target_square.pgm" ) -foreach(data ${data2copy}) - add_custom_command( - TARGET tutorial-ibvs-4pts-ogre - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${data}" "${target_location}" - ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/sphere/Sphere.mesh" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/sphere/Sphere.material" ) +list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/target_square.pgm" ) + +foreach(cpp ${tutorial_cpp}) + visp_add_target(${cpp}) + if(COMMAND visp_add_dependency) + visp_add_dependency(${cpp} "tutorials") + endif() +endforeach() + +# Copy the data files to the same location than the target +foreach(data ${tutorial_data}) + visp_copy_data(tutorial-ibvs-4pts-ogre.cpp ${data}) endforeach() diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-display.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-display.cpp index e46daed8a484246240fb37dc797840e327c2df58..ccae30d6d32ad59bcd4bdd9aa94c21009ad33a89 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-display.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-display.cpp @@ -3,10 +3,14 @@ #include <visp/vpServo.h> #include <visp/vpSimulatorCamera.h> #include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayGDI.h> #include <visp/vpProjectionDisplay.h> #include <visp/vpServoDisplay.h> +void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, + const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam); + void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam) { @@ -27,83 +31,91 @@ void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &p int main() { - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., - vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., + vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; - vpImage<unsigned char> Iint(480, 640, 255) ; - vpImage<unsigned char> Iext(480, 640, 255) ; + vpImage<unsigned char> Iint(480, 640, 255) ; + vpImage<unsigned char> Iext(480, 640, 255) ; #if defined(VISP_HAVE_X11) - vpDisplayX displayInt(Iint, 0, 0, "Internal view"); - vpDisplayX displayExt(Iext, 670, 0, "External view"); + vpDisplayX displayInt(Iint, 0, 0, "Internal view"); + vpDisplayX displayExt(Iext, 670, 0, "External view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI displayInt(Iint, 0, 0, "Internal view"); - vpDisplayGDI displayExt(Iext, 670, 0, "External view"); + vpDisplayGDI displayInt(Iint, 0, 0, "Internal view"); + vpDisplayGDI displayExt(Iext, 670, 0, "External view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV displayInt(Iint, 0, 0, "Internal view"); + vpDisplayOpenCV displayExt(Iext, 670, 0, "External view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif #if defined(VISP_HAVE_DISPLAY) - vpProjectionDisplay externalview; - for (int i = 0 ; i < 4 ; i++) - externalview.insert(point[i]) ; + vpProjectionDisplay externalview; + for (unsigned int i = 0 ; i < 4 ; i++) + externalview.insert(point[i]) ; #endif - vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); - vpHomogeneousMatrix cextMo(0,0,3, 0,0,0); + vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); + vpHomogeneousMatrix cextMo(0,0,3, 0,0,0); - while(1) { - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - } - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + while(1) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - display_trajectory(Iint, point, cMo, cam); + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + display_trajectory(Iint, point, cMo, cam); - vpServoDisplay::display(task, cam, Iint, vpColor::green, vpColor::red); + vpServoDisplay::display(task, cam, Iint, vpColor::green, vpColor::red); #if defined(VISP_HAVE_DISPLAY) - externalview.display(Iext, cextMo, cMo, cam, vpColor::red, true); + externalview.display(Iext, cextMo, cMo, cam, vpColor::red, true); #endif - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); - // A click to exit - if (vpDisplay::getClick(Iint, false) || vpDisplay::getClick(Iext, false)) - break; + // A click to exit + if (vpDisplay::getClick(Iint, false) || vpDisplay::getClick(Iext, false)) + break; - vpTime::wait( robot.getSamplingTime() * 1000); + vpTime::wait( robot.getSamplingTime() * 1000); + } + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - task.kill(); } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-image-tracking.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-image-tracking.cpp index a0c9d8a859771397ca32965d06cb9656ebc5dfe0..09ac68f4b2455d150a44f495973058460f271c4f 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-image-tracking.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-image-tracking.cpp @@ -1,5 +1,6 @@ /*! \example tutorial-ibvs-4pts-image-tracking.cpp */ #include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayGDI.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpImageIo.h> @@ -8,6 +9,8 @@ #include <visp/vpServoDisplay.h> #include <visp/vpSimulatorCamera.h> +void display_trajectory(const vpImage<unsigned char> &I, const std::vector<vpDot2> &dot); + /*! Given an image of a target, this class provided virtual framegrabbing capabilities in order to retrieve an image of a @@ -22,6 +25,7 @@ public: \param cam : Intrinsic camera parameters. */ vpVirtualGrabber(const std::string &filename, const vpCameraParameters &cam) + : sim_(), target_(), cam_() { // The target is a square 20cm by 2cm square // Initialise the 3D coordinates of the target corners @@ -78,92 +82,99 @@ void display_trajectory(const vpImage<unsigned char> &I, const std::vector<vpDot int main() { -#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - vpImage<unsigned char> I(480, 640, 255); - vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); - - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpVirtualGrabber g("./target_square.pgm", cam); - g.acquire(I, cMo); +#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV) + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + vpImage<unsigned char> I(480, 640, 255); + vpCameraParameters cam(840, 840, I.getWidth()/2, I.getHeight()/2); + + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpVirtualGrabber g("./target_square.pgm", cam); + g.acquire(I, cMo); #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Current camera view"); + vpDisplayX d(I, 0, 0, "Current camera view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Current camera view"); + vpDisplayGDI d(I, 0, 0, "Current camera view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Current camera view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::display(I); - vpDisplay::displayCharString(I, 10, 10, - "Click in the 4 dots to initialise the tracking and start the servo", - vpColor::red); - vpDisplay::flush(I); + vpDisplay::display(I); + vpDisplay::displayText(I, 10, 10, + "Click in the 4 dots to initialise the tracking and start the servo", + vpColor::red); + vpDisplay::flush(I); - vpFeaturePoint p[4], pd[4]; - std::vector<vpDot2> dot(4); + vpFeaturePoint p[4], pd[4]; + std::vector<vpDot2> dot(4); - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); - dot[i].setGraphics(true); - dot[i].initTracking(I); - vpDisplay::flush(I); - vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); - - task.addFeature(p[i], pd[i]); - } + dot[i].setGraphics(true); + dot[i].initTracking(I); + vpDisplay::flush(I); + vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; + task.addFeature(p[i], pd[i]); + } - for (; ; ) { + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; + wMo = wMc * cMo; - g.acquire(I, cMo); + for (; ; ) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; - vpDisplay::display(I); + g.acquire(I, cMo); - for (int i = 0 ; i < 4 ; i++) { - dot[i].track(I); - vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); + vpDisplay::display(I); - vpColVector cP; - point[i].changeFrame(cMo, cP) ; - p[i].set_Z(cP[2]); - } + for (unsigned int i = 0 ; i < 4 ; i++) { + dot[i].track(I); + vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); - vpColVector v = task.computeControlLaw(); + vpColVector cP; + point[i].changeFrame(cMo, cP) ; + p[i].set_Z(cP[2]); + } - display_trajectory(I, dot); - vpServoDisplay::display(task, cam, I, vpColor::green, vpColor::red) ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + vpColVector v = task.computeControlLaw(); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; + display_trajectory(I, dot); + vpServoDisplay::display(task, cam, I, vpColor::green, vpColor::red) ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) + break; - vpTime::wait( robot.getSamplingTime() * 1000); + vpTime::wait( robot.getSamplingTime() * 1000); + } + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - task.kill(); #endif } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre-tracking.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre-tracking.cpp index 183aa06999a8214d30fa4d9c58994ed82cbe24cd..2253fb2581979c37203bfd171bf3d94b0b3f1267 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre-tracking.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre-tracking.cpp @@ -1,5 +1,6 @@ /*! \example tutorial-ibvs-4pts-ogre-tracking.cpp */ #include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayGDI.h> #include <visp/vpAROgre.h> #include <visp/vpFeatureBuilder.h> @@ -8,6 +9,12 @@ #include <visp/vpServoDisplay.h> #include <visp/vpSimulatorCamera.h> +void display_trajectory(const vpImage<unsigned char> &I, const std::vector<vpDot2> &dot, unsigned int thickness); +#if defined(VISP_HAVE_OGRE) +void ogre_get_render_image(vpAROgre &ogre, const vpImage<unsigned char> &background, + const vpHomogeneousMatrix &cMo, vpImage<unsigned char> &I); +#endif + void display_trajectory(const vpImage<unsigned char> &I, const std::vector<vpDot2> &dot, unsigned int thickness) { static std::vector<vpImagePoint> traj[4]; @@ -23,10 +30,7 @@ void display_trajectory(const vpImage<unsigned char> &I, const std::vector<vpDot #if defined(VISP_HAVE_OGRE) void ogre_get_render_image(vpAROgre &ogre, const vpImage<unsigned char> &background, - #if VISP_VERSION_INT > VP_VERSION_INT(2,7,0) - const - #endif - vpHomogeneousMatrix &cMo, vpImage<unsigned char> &I) + const vpHomogeneousMatrix &cMo, vpImage<unsigned char> &I) { static vpImage<vpRGBa> Irender; // Image from ogre scene rendering ogre.display(background, cMo); @@ -40,140 +44,113 @@ void ogre_get_render_image(vpAROgre &ogre, const vpImage<unsigned char> &backgro int main() { -#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) - unsigned int thickness = 3; - - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - // Color image used as background texture. - vpImage<unsigned char> background(480, 640, 255); - - // Parameters of our camera - vpCameraParameters cam(840, 840, background.getWidth()/2, background.getHeight()/2); - - // Define the target as 4 points - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - // Our object - // A simulator with the camera parameters defined above, - // and the background image size - vpAROgre ogre; - ogre.setCameraParameters(cam); - ogre.setShowConfigDialog(false); - ogre.addResource("./"); // Add the path to the Sphere.mesh resource - ogre.init(background, false, true); - //ogre.setWindowPosition(680, 400); - - // Create the scene that contains 4 spheres - // Sphere.mesh contains a sphere with 1 meter radius - std::vector<std::string> name(4); - for (int i=0; i<4; i++) { - std::ostringstream s; s << "Sphere" << i; name[i] = s.str(); - ogre.load(name[i], "Sphere.mesh"); - ogre.setScale(name[i], 0.02f, 0.02f, 0.02f); // Rescale the sphere to 2 cm radius - // Set the position of each sphere in the object frame - ogre.setPosition(name[i], vpTranslationVector(point[i].get_oX(), point[i].get_oY(), point[i].get_oZ())); - ogre.setRotation(name[i], vpRotationMatrix(M_PI/2, 0, 0)); - } +#if defined(VISP_HAVE_OGRE) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) + try { + unsigned int thickness = 3; + + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + // Color image used as background texture. + vpImage<unsigned char> background(480, 640, 255); + + // Parameters of our camera + vpCameraParameters cam(840, 840, background.getWidth()/2, background.getHeight()/2); + + // Define the target as 4 points + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + // Our object + // A simulator with the camera parameters defined above, + // and the background image size + vpAROgre ogre; + ogre.setCameraParameters(cam); + ogre.setShowConfigDialog(false); + ogre.addResource("./"); // Add the path to the Sphere.mesh resource + ogre.init(background, false, true); + //ogre.setWindowPosition(680, 400); + + // Create the scene that contains 4 spheres + // Sphere.mesh contains a sphere with 1 meter radius + std::vector<std::string> name(4); + for (int i=0; i<4; i++) { + std::ostringstream s; s << "Sphere" << i; name[i] = s.str(); + ogre.load(name[i], "Sphere.mesh"); + ogre.setScale(name[i], 0.02f, 0.02f, 0.02f); // Rescale the sphere to 2 cm radius + // Set the position of each sphere in the object frame + ogre.setPosition(name[i], vpTranslationVector(point[i].get_oX(), point[i].get_oY(), point[i].get_oZ())); + ogre.setRotation(name[i], vpRotationMatrix(M_PI/2, 0, 0)); + } - // Add an optional point light source - Ogre::Light * light = ogre.getSceneManager()->createLight(); - light->setDiffuseColour(1, 1, 1); // scaled RGB values - light->setSpecularColour(1, 1, 1); // scaled RGB values - light->setPosition((Ogre::Real)cdMo[0][3], (Ogre::Real)cdMo[1][3], (Ogre::Real)(-cdMo[2][3])); - light->setType(Ogre::Light::LT_POINT); + // Add an optional point light source + Ogre::Light * light = ogre.getSceneManager()->createLight(); + light->setDiffuseColour(1, 1, 1); // scaled RGB values + light->setSpecularColour(1, 1, 1); // scaled RGB values + light->setPosition((Ogre::Real)cdMo[0][3], (Ogre::Real)cdMo[1][3], (Ogre::Real)(-cdMo[2][3])); + light->setType(Ogre::Light::LT_POINT); - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); - // Image used for the image processing - vpImage<unsigned char> I; + // Image used for the image processing + vpImage<unsigned char> I; - // Render the scene at the desired position - ogre_get_render_image(ogre, background, cdMo, I); + // Render the scene at the desired position + ogre_get_render_image(ogre, background, cdMo, I); - // Display the image in which we will do the tracking + // Display the image in which we will do the tracking #if defined(VISP_HAVE_X11) - vpDisplayX d(I, 0, 0, "Camera view at desired position"); + vpDisplayX d(I, 0, 0, "Camera view at desired position"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI d(I, 0, 0, "Camera view at desired position"); + vpDisplayGDI d(I, 0, 0, "Camera view at desired position"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV d(I, 0, 0, "Camera view at desired position"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpDisplay::display(I); - vpDisplay::displayCharString(I, 10, 10, "Click in the 4 dots to learn their positions", vpColor::red); - vpDisplay::flush(I); - - std::vector<vpDot2> dot(4); - vpFeaturePoint p[4], pd[4]; - - for (int i = 0 ; i < 4 ; i++) { - // Compute the desired feature at the desired position - dot[i].setGraphics(true); - dot[i].setGraphicsThickness(thickness); - dot[i].initTracking(I); - vpDisplay::flush(I); - vpFeatureBuilder::create(pd[i], cam, dot[i].getCog()); - } - - // Render the scene at the initial position - ogre_get_render_image(ogre, background, cMo, I); - - vpDisplay::display(I); - vpDisplay::setTitle(I, "Current camera view"); - vpDisplay::displayCharString(I, 10, 10, "Click in the 4 dots to initialise the tracking and start the servo", vpColor::red); - vpDisplay::flush(I); - - for (int i = 0 ; i < 4 ; i++) { - // We notice that if we project the scene at a given pose, the pose estimated from - // the rendered image differs a little. That's why we cannot simply compute the desired - // feature from the desired pose using the next two lines. We will rather compute the - // desired position of the features from a learning stage. - // point[i].project(cdMo); - // vpFeatureBuilder::create(pd[i], point[i]); - - // Compute the current feature at the initial position - dot[i].setGraphics(true); - dot[i].initTracking(I); + vpDisplay::display(I); + vpDisplay::displayText(I, 10, 10, "Click in the 4 dots to learn their positions", vpColor::red); vpDisplay::flush(I); - vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); - } - - for (int i = 0 ; i < 4 ; i++) { - // Set the feature Z coordinate from the pose - vpColVector cP; - point[i].changeFrame(cMo, cP) ; - p[i].set_Z(cP[2]); - - task.addFeature(p[i], pd[i]); - } - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; + std::vector<vpDot2> dot(4); + vpFeaturePoint p[4], pd[4]; - for (; ; ) { - // From the camera position in the world frame we retrieve the object position - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; + for (int i = 0 ; i < 4 ; i++) { + // Compute the desired feature at the desired position + dot[i].setGraphics(true); + dot[i].setGraphicsThickness(thickness); + dot[i].initTracking(I); + vpDisplay::flush(I); + vpFeatureBuilder::create(pd[i], cam, dot[i].getCog()); + } - // Update the scene from the new camera position + // Render the scene at the initial position ogre_get_render_image(ogre, background, cMo, I); vpDisplay::display(I); + vpDisplay::setTitle(I, "Current camera view"); + vpDisplay::displayText(I, 10, 10, "Click in the 4 dots to initialise the tracking and start the servo", vpColor::red); + vpDisplay::flush(I); for (int i = 0 ; i < 4 ; i++) { - dot[i].track(I); + // We notice that if we project the scene at a given pose, the pose estimated from + // the rendered image differs a little. That's why we cannot simply compute the desired + // feature from the desired pose using the next two lines. We will rather compute the + // desired position of the features from a learning stage. + // point[i].project(cdMo); + // vpFeatureBuilder::create(pd[i], point[i]); + + // Compute the current feature at the initial position + dot[i].setGraphics(true); + dot[i].initTracking(I); + vpDisplay::flush(I); vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); } @@ -182,21 +159,59 @@ int main() vpColVector cP; point[i].changeFrame(cMo, cP) ; p[i].set_Z(cP[2]); + + task.addFeature(p[i], pd[i]); } - vpColVector v = task.computeControlLaw(); + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; + + for (; ; ) { + // From the camera position in the world frame we retrieve the object position + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; - display_trajectory(I, dot, thickness); - vpServoDisplay::display(task, cam, I, vpColor::green, vpColor::red, thickness+2) ; - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + // Update the scene from the new camera position + ogre_get_render_image(ogre, background, cMo, I); - vpDisplay::flush(I); - if (vpDisplay::getClick(I, false)) - break; + vpDisplay::display(I); - vpTime::wait( robot.getSamplingTime() * 1000); + for (int i = 0 ; i < 4 ; i++) { + dot[i].track(I); + vpFeatureBuilder::create(p[i], cam, dot[i].getCog()); + } + + for (int i = 0 ; i < 4 ; i++) { + // Set the feature Z coordinate from the pose + vpColVector cP; + point[i].changeFrame(cMo, cP) ; + p[i].set_Z(cP[2]); + } + + vpColVector v = task.computeControlLaw(); + + display_trajectory(I, dot, thickness); + vpServoDisplay::display(task, cam, I, vpColor::green, vpColor::red, thickness+2) ; + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + + vpDisplay::flush(I); + if (vpDisplay::getClick(I, false)) + break; + + vpTime::wait( robot.getSamplingTime() * 1000); + } + task.kill(); + } + catch(vpException e) { + std::cout << "Catch a ViSP exception: " << e << std::endl; + } + catch(...) { + std::cout << "Catch an exception " << std::endl; + return 1; } - task.kill(); #endif } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre.cpp index 742eb1ceb054f90a06171c29379e250e3300072c..0328d7aea020de3b551b8fa3c4d102dcfe8c17ab 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-ogre.cpp @@ -1,4 +1,5 @@ /*! \example tutorial-ibvs-4pts-ogre.cpp */ + #include <visp/vpAROgre.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpServo.h> @@ -6,86 +7,94 @@ int main() { - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - // Define the target as 4 points - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); + // Define the target as 4 points + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); #if defined(VISP_HAVE_OGRE) - // Color image used as background texture. - vpImage<unsigned char> background(480, 640, 255); + // Color image used as background texture. + vpImage<unsigned char> background(480, 640, 255); - // Parameters of our camera - vpCameraParameters cam(840, 840, background.getWidth()/2, background.getHeight()/2); + // Parameters of our camera + vpCameraParameters cam(840, 840, background.getWidth()/2, background.getHeight()/2); - // Our object - // A simulator with the camera parameters defined above, - // and the background image size - vpAROgre ogre; - ogre.setShowConfigDialog(false); - ogre.setCameraParameters(cam); - ogre.addResource("./"); // Add the path to the Sphere.mesh resource - ogre.init(background, false, true); + // Our object + // A simulator with the camera parameters defined above, + // and the background image size + vpAROgre ogre; + ogre.setShowConfigDialog(false); + ogre.setCameraParameters(cam); + ogre.addResource("./"); // Add the path to the Sphere.mesh resource + ogre.init(background, false, true); - // Create the scene that contains 4 spheres - // Sphere.mesh contains a sphere with 1 meter radius - std::vector<std::string> name(4); - for (int i=0; i<4; i++) { - std::ostringstream s; s << "Sphere" << i; name[i] = s.str(); - ogre.load(name[i], "Sphere.mesh"); - ogre.setScale(name[i], 0.02f, 0.02f, 0.02f); // Rescale the sphere to 2 cm radius - // Set the position of each sphere in the object frame - ogre.setPosition(name[i], vpTranslationVector(point[i].get_oX(), point[i].get_oY(), point[i].get_oZ())); - } + // Create the scene that contains 4 spheres + // Sphere.mesh contains a sphere with 1 meter radius + std::vector<std::string> name(4); + for (int i=0; i<4; i++) { + std::ostringstream s; s << "Sphere" << i; name[i] = s.str(); + ogre.load(name[i], "Sphere.mesh"); + ogre.setScale(name[i], 0.02f, 0.02f, 0.02f); // Rescale the sphere to 2 cm radius + // Set the position of each sphere in the object frame + ogre.setPosition(name[i], vpTranslationVector(point[i].get_oX(), point[i].get_oY(), point[i].get_oZ())); + } - // Add an optional point light source - Ogre::Light * light = ogre.getSceneManager()->createLight(); - light->setDiffuseColour(1, 1, 1); // scaled RGB values - light->setSpecularColour(1, 1, 1); // scaled RGB values - light->setPosition((Ogre::Real)cdMo[0][3], (Ogre::Real)cdMo[1][3], (Ogre::Real)(-cdMo[2][3])); - light->setType(Ogre::Light::LT_POINT); + // Add an optional point light source + Ogre::Light * light = ogre.getSceneManager()->createLight(); + light->setDiffuseColour(1, 1, 1); // scaled RGB values + light->setSpecularColour(1, 1, 1); // scaled RGB values + light->setPosition((Ogre::Real)cdMo[0][3], (Ogre::Real)cdMo[1][3], (Ogre::Real)(-cdMo[2][3])); + light->setType(Ogre::Light::LT_POINT); #endif - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } - - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; - - for (unsigned int iter=0; iter < 150; iter ++) { - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; + vpFeaturePoint p[4], pd[4] ; for (int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); point[i].track(cMo); vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); } + + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; + + for (unsigned int iter=0; iter < 150; iter ++) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } #if defined(VISP_HAVE_OGRE) - // Update the scene from the new camera position - ogre.display(background, cMo); + // Update the scene from the new camera position + ogre.display(background, cMo); #endif - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpTime::wait( robot.getSamplingTime() * 1000); + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + vpTime::wait( robot.getSamplingTime() * 1000); + } + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } + catch(...) { + std::cout << "Catch an exception " << std::endl; } - task.kill(); } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp new file mode 100644 index 0000000000000000000000000000000000000000..beef4d088ad28ce69a86b86c58d41949dd9b50ae --- /dev/null +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp @@ -0,0 +1,102 @@ +/*! \example tutorial-ibvs-4pts-plotter-continuous-gain-adaptive.cpp */ +#include <visp/vpFeatureBuilder.h> +#include <visp/vpServo.h> +#include <visp/vpSimulatorCamera.h> +#include <visp/vpPlot.h> + +int main() +{ + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + + vpAdaptiveGain lambda(4, 0.4, 30); + task.setLambda(lambda); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } + + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; + +#ifdef VISP_HAVE_DISPLAY + vpPlot plotter(2, 250*2, 500, 100, 200, "Real time curves plotter"); + plotter.setTitle(0, "Visual features error"); + plotter.setTitle(1, "Camera velocities"); + + plotter.initGraph(0, 8); + plotter.initGraph(1, 6); + + plotter.setLegend(0, 0, "x1"); + plotter.setLegend(0, 1, "y1"); + plotter.setLegend(0, 2, "x2"); + plotter.setLegend(0, 3, "y2"); + plotter.setLegend(0, 4, "x3"); + plotter.setLegend(0, 5, "y3"); + plotter.setLegend(0, 6, "x4"); + plotter.setLegend(0, 7, "y4"); + + plotter.setLegend(1, 0, "v_x"); + plotter.setLegend(1, 1, "v_y"); + plotter.setLegend(1, 2, "v_z"); + plotter.setLegend(1, 3, "w_x"); + plotter.setLegend(1, 4, "w_y"); + plotter.setLegend(1, 5, "w_z"); +#endif + + unsigned int iter = 0; + while(1) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(iter*robot.getSamplingTime()); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + +#ifdef VISP_HAVE_DISPLAY + plotter.plot(0, iter, task.getError()); + plotter.plot(1, iter, v); +#endif + if (( task.getError() ).sumSquare() < 0.0001) + break; + + iter++; + } + std::cout << "Convergence in " << iter << " iterations" << std::endl; + + task.kill(); + +#ifdef VISP_HAVE_DISPLAY + plotter.saveData(0, "error.dat"); + plotter.saveData(1, "vc.dat"); + + vpDisplay::getClick(plotter.I); +#endif + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +} + + diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-gain-adaptive.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-gain-adaptive.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3d0a7b806d3a7bd27eb004c93c48eed4a1cfd2a --- /dev/null +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter-gain-adaptive.cpp @@ -0,0 +1,102 @@ +/*! \example tutorial-ibvs-4pts-plotter-gain-adaptive.cpp */ +#include <visp/vpFeatureBuilder.h> +#include <visp/vpServo.h> +#include <visp/vpSimulatorCamera.h> +#include <visp/vpPlot.h> + +int main() +{ + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + + vpAdaptiveGain lambda(4, 0.4, 30); + task.setLambda(lambda); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } + + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; + +#ifdef VISP_HAVE_DISPLAY + vpPlot plotter(2, 250*2, 500, 100, 200, "Real time curves plotter"); + plotter.setTitle(0, "Visual features error"); + plotter.setTitle(1, "Camera velocities"); + + plotter.initGraph(0, 8); + plotter.initGraph(1, 6); + + plotter.setLegend(0, 0, "x1"); + plotter.setLegend(0, 1, "y1"); + plotter.setLegend(0, 2, "x2"); + plotter.setLegend(0, 3, "y2"); + plotter.setLegend(0, 4, "x3"); + plotter.setLegend(0, 5, "y3"); + plotter.setLegend(0, 6, "x4"); + plotter.setLegend(0, 7, "y4"); + + plotter.setLegend(1, 0, "v_x"); + plotter.setLegend(1, 1, "v_y"); + plotter.setLegend(1, 2, "v_z"); + plotter.setLegend(1, 3, "w_x"); + plotter.setLegend(1, 4, "w_y"); + plotter.setLegend(1, 5, "w_z"); +#endif + + unsigned int iter = 0; + while(1) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + +#ifdef VISP_HAVE_DISPLAY + plotter.plot(0, iter, task.getError()); + plotter.plot(1, iter, v); +#endif + if (( task.getError() ).sumSquare() < 0.0001) + break; + + iter++; + } + std::cout << "Convergence in " << iter << " iterations" << std::endl; + + task.kill(); + +#ifdef VISP_HAVE_DISPLAY + plotter.saveData(0, "error.dat"); + plotter.saveData(1, "vc.dat"); + + vpDisplay::getClick(plotter.I); +#endif + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } +} + + diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter.cpp index 54afccf712faf7ed74bfa523cd5ef3336f6fc46b..df01839ddb190d6b5ed8da353f9d496460a97792 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-plotter.cpp @@ -6,84 +6,95 @@ int main() { - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; #ifdef VISP_HAVE_DISPLAY - vpPlot plotter(2, 250*2, 500, 100, 200, "Real time curves plotter"); - plotter.setTitle(0, "Visual features error"); - plotter.setTitle(1, "Camera velocities"); - - plotter.initGraph(0, 8); - plotter.initGraph(1, 6); - - plotter.setLegend(0, 0, "x1"); - plotter.setLegend(0, 1, "y1"); - plotter.setLegend(0, 2, "x2"); - plotter.setLegend(0, 3, "y2"); - plotter.setLegend(0, 4, "x3"); - plotter.setLegend(0, 5, "y3"); - plotter.setLegend(0, 6, "x4"); - plotter.setLegend(0, 7, "y4"); - - plotter.setLegend(1, 0, "v_x"); - plotter.setLegend(1, 1, "v_y"); - plotter.setLegend(1, 2, "v_z"); - plotter.setLegend(1, 3, "w_x"); - plotter.setLegend(1, 4, "w_y"); - plotter.setLegend(1, 5, "w_z"); + vpPlot plotter(2, 250*2, 500, 100, 200, "Real time curves plotter"); + plotter.setTitle(0, "Visual features error"); + plotter.setTitle(1, "Camera velocities"); + + plotter.initGraph(0, 8); + plotter.initGraph(1, 6); + + plotter.setLegend(0, 0, "x1"); + plotter.setLegend(0, 1, "y1"); + plotter.setLegend(0, 2, "x2"); + plotter.setLegend(0, 3, "y2"); + plotter.setLegend(0, 4, "x3"); + plotter.setLegend(0, 5, "y3"); + plotter.setLegend(0, 6, "x4"); + plotter.setLegend(0, 7, "y4"); + + plotter.setLegend(1, 0, "v_x"); + plotter.setLegend(1, 1, "v_y"); + plotter.setLegend(1, 2, "v_z"); + plotter.setLegend(1, 3, "w_x"); + plotter.setLegend(1, 4, "w_y"); + plotter.setLegend(1, 5, "w_z"); #endif - for (unsigned int iter=0; iter < 150; iter ++) { - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - } - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + unsigned int iter = 0; + while(1) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); #ifdef VISP_HAVE_DISPLAY - plotter.plot(0, iter, task.getError()); - plotter.plot(1, iter, v); + plotter.plot(0, iter, task.getError()); + plotter.plot(1, iter, v); #endif - } + if (( task.getError() ).sumSquare() < 0.0001) + break; - task.kill(); + iter++; + } + std::cout << "Convergence in " << iter << " iterations" << std::endl; + + task.kill(); #ifdef VISP_HAVE_DISPLAY - plotter.saveData(0, "error.dat"); - plotter.saveData(1, "vc.dat"); + plotter.saveData(0, "error.dat"); + plotter.saveData(1, "vc.dat"); - vpDisplay::getClick(plotter.I); + vpDisplay::getClick(plotter.I); #endif + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-camera.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-camera.cpp index e9e96e4257791e284f208e0926d37d9943f9e3e2..5378468a66a2e054367e62160c4a95cbfa160308 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-camera.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-camera.cpp @@ -3,11 +3,15 @@ #include <visp/vpServo.h> #include <visp/vpSimulatorCamera.h> #include <visp/vpDisplayX.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayGDI.h> #include <visp/vpProjectionDisplay.h> #include <visp/vpServoDisplay.h> #include <visp/vpWireFrameSimulator.h> +void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, + const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam); + void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam) { @@ -28,85 +32,93 @@ void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &p int main() { - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; - vpImage<unsigned char> Iint(480, 640, 0) ; - vpImage<unsigned char> Iext(480, 640, 0) ; + vpImage<unsigned char> Iint(480, 640, 0) ; + vpImage<unsigned char> Iext(480, 640, 0) ; #if defined VISP_HAVE_X11 - vpDisplayX displayInt(Iint, 0, 0, "Internal view"); - vpDisplayX displayExt(Iext, 670, 0, "External view"); + vpDisplayX displayInt(Iint, 0, 0, "Internal view"); + vpDisplayX displayExt(Iext, 670, 0, "External view"); #elif defined VISP_HAVE_GDI - vpDisplayGDI displayInt(Iint, 0, 0, "Internal view"); - vpDisplayGDI displayExt(Iext, 670, 0, "External view"); + vpDisplayGDI displayInt(Iint, 0, 0, "Internal view"); + vpDisplayGDI displayExt(Iext, 670, 0, "External view"); +#elif defined VISP_HAVE_OPENCV + vpDisplayOpenCV displayInt(Iint, 0, 0, "Internal view"); + vpDisplayOpenCV displayExt(Iext, 670, 0, "External view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); - vpHomogeneousMatrix cextMo(0,0,3, 0,0,0); - - vpWireFrameSimulator sim; - sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - sim.setCameraPositionRelObj(cMo); - sim.setDesiredCameraPosition(cdMo); - sim.setExternalCameraPosition(cextMo); - sim.setInternalCameraParameters(cam); - sim.setExternalCameraParameters(cam); - - while(1) { - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - } - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); + vpHomogeneousMatrix cextMo(0,0,3, 0,0,0); + vpWireFrameSimulator sim; + sim.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); sim.setCameraPositionRelObj(cMo); - - vpDisplay::display(Iint) ; - vpDisplay::display(Iext) ; - - sim.getInternalImage(Iint); - sim.getExternalImage(Iext); - - display_trajectory(Iint, point, cMo, cam); - vpDisplay::flush(Iint); - vpDisplay::flush(Iext); - - // A click in the internal view to exit - if (vpDisplay::getClick(Iint, false)) - break; - vpTime::wait(1000*robot.getSamplingTime()); + sim.setDesiredCameraPosition(cdMo); + sim.setExternalCameraPosition(cextMo); + sim.setInternalCameraParameters(cam); + sim.setExternalCameraParameters(cam); + + while(1) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + + sim.setCameraPositionRelObj(cMo); + + vpDisplay::display(Iint) ; + vpDisplay::display(Iext) ; + + sim.getInternalImage(Iint); + sim.getExternalImage(Iext); + + display_trajectory(Iint, point, cMo, cam); + vpDisplay::flush(Iint); + vpDisplay::flush(Iext); + + // A click in the internal view to exit + if (vpDisplay::getClick(Iint, false)) + break; + vpTime::wait(1000*robot.getSamplingTime()); + } + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - task.kill(); } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-afma6.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-afma6.cpp index 590aa30998b6a5d54a80a154ace304c3adc36ed2..016b07c1f24f80b720d94f14c8351f1c5065fc13 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-afma6.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-afma6.cpp @@ -1,14 +1,18 @@ /*! \example tutorial-ibvs-4pts-wireframe-robot-afma6.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpServo.h> #include <visp/vpSimulatorAfma6.h> +void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, + const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam); + void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam) { - int thickness = 3; + unsigned int thickness = 3; static std::vector<vpImagePoint> traj[4]; vpImagePoint cog; for (unsigned int i=0; i<4; i++) { @@ -27,10 +31,11 @@ void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &p int main() { #if defined(VISP_HAVE_PTHREAD) - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(-0.15, 0.1, 1., vpMath::rad(-10), vpMath::rad(10), vpMath::rad(50)); + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(-0.15, 0.1, 1., vpMath::rad(-10), vpMath::rad(10), vpMath::rad(50)); - /* + /* Top view of the world frame, the camera frame and the object frame world, also robot base frame : @@ -53,102 +58,108 @@ int main() c_x <-- */ - vpHomogeneousMatrix wMo(0, 0, 1., 0, 0, 0); - - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } + vpHomogeneousMatrix wMo(0, 0, 1., 0, 0, 0); + + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } - vpSimulatorAfma6 robot(true); - robot.setVerbose(true); + vpSimulatorAfma6 robot(true); + robot.setVerbose(true); - // Get the default joint limits - vpColVector qmin = robot.getJointMin(); - vpColVector qmax = robot.getJointMax(); + // Get the default joint limits + vpColVector qmin = robot.getJointMin(); + vpColVector qmax = robot.getJointMax(); - std::cout << "Robot joint limits: " << std::endl; - for (unsigned int i=0; i< 3; i ++) - std::cout << "Joint " << i << ": min " << qmin[i] << " max " << qmax[i] << " (m)" << std::endl; - for (unsigned int i=3; i< qmin.size(); i ++) - std::cout << "Joint " << i << ": min " << vpMath::deg(qmin[i]) << " max " << vpMath::deg(qmax[i]) << " (deg)" << std::endl; + std::cout << "Robot joint limits: " << std::endl; + for (unsigned int i=0; i< 3; i ++) + std::cout << "Joint " << i << ": min " << qmin[i] << " max " << qmax[i] << " (m)" << std::endl; + for (unsigned int i=3; i< qmin.size(); i ++) + std::cout << "Joint " << i << ": min " << vpMath::deg(qmin[i]) << " max " << vpMath::deg(qmax[i]) << " (deg)" << std::endl; - robot.init(vpAfma6::TOOL_CCMOP, vpCameraParameters::perspectiveProjWithoutDistortion); - robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); - robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - robot.set_fMo(wMo); - bool ret = true; + robot.init(vpAfma6::TOOL_CCMOP, vpCameraParameters::perspectiveProjWithoutDistortion); + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); + robot.set_fMo(wMo); + bool ret = true; #if VISP_VERSION_INT > VP_VERSION_INT(2,7,0) - ret = -#endif - robot.initialiseCameraRelativeToObject(cMo); - if (ret == false) - return 0; // Not able to set the position - robot.setDesiredCameraPosition(cdMo); - - vpImage<unsigned char> Iint(480, 640, 255); + ret = + #endif + robot.initialiseCameraRelativeToObject(cMo); + if (ret == false) + return 0; // Not able to set the position + robot.setDesiredCameraPosition(cdMo); + + vpImage<unsigned char> Iint(480, 640, 255); #if defined(VISP_HAVE_X11) - vpDisplayX displayInt(Iint, 700, 0, "Internal view"); + vpDisplayX displayInt(Iint, 700, 0, "Internal view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI displayInt(Iint, 700, 0, "Internal view"); + vpDisplayGDI displayInt(Iint, 700, 0, "Internal view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV displayInt(Iint, 700, 0, "Internal view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); - robot.setCameraParameters(cam); - - bool start = true; - for ( ; ; ) - { - cMo = robot.get_cMo(); - - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - } + vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); + robot.setCameraParameters(cam); + + bool start = true; + for ( ; ; ) + { + cMo = robot.get_cMo(); + + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + + vpDisplay::display(Iint); + robot.getInternalView(Iint); + if (!start) { + display_trajectory(Iint, point, cMo, cam); + vpDisplay::displayText(Iint, 40, 120, "Click to stop the servo...", vpColor::red); + } + vpDisplay::flush(Iint); - vpDisplay::display(Iint); - robot.getInternalView(Iint); - if (!start) { - display_trajectory(Iint, point, cMo, cam); - vpDisplay::displayCharString(Iint, 40, 120, "Click to stop the servo...", vpColor::red); - } - vpDisplay::flush(Iint); + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + // A click to exit + if (vpDisplay::getClick(Iint, false)) + break; - // A click to exit - if (vpDisplay::getClick(Iint, false)) - break; + if (start) { + start = false; + v = 0; + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + vpDisplay::displayText(Iint, 40, 120, "Click to start the servo...", vpColor::blue); + vpDisplay::flush(Iint); + vpDisplay::getClick(Iint); + } - if (start) { - start = false; - v = 0; - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpDisplay::displayCharString(Iint, 40, 120, "Click to start the servo...", vpColor::blue); - vpDisplay::flush(Iint); - vpDisplay::getClick(Iint); + vpTime::wait(1000*robot.getSamplingTime()); } - - vpTime::wait(1000*robot.getSamplingTime()); + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - task.kill(); #endif } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-viper.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-viper.cpp index f0a3b377998d1c9c4f1e8ae37a54ee7e4614d29d..adb8ade575dffb1e80aa01e66112a317162816e8 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-viper.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts-wireframe-robot-viper.cpp @@ -1,14 +1,18 @@ /*! \example tutorial-ibvs-4pts-wireframe-robot-viper.cpp */ #include <visp/vpDisplayGDI.h> +#include <visp/vpDisplayOpenCV.h> #include <visp/vpDisplayX.h> #include <visp/vpFeatureBuilder.h> #include <visp/vpServo.h> #include <visp/vpSimulatorViper850.h> +void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, + const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam); + void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &point, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam) { - int thickness = 3; + unsigned int thickness = 3; static std::vector<vpImagePoint> traj[4]; vpImagePoint cog; for (unsigned int i=0; i<4; i++) { @@ -27,11 +31,11 @@ void display_trajectory(const vpImage<unsigned char> &I, std::vector<vpPoint> &p int main() { #if defined(VISP_HAVE_PTHREAD) + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - /* + /* Top view of the world frame, the camera frame and the object frame world, also robot base frame : --> w_y @@ -53,114 +57,120 @@ int main() c_x <-- */ - vpHomogeneousMatrix wMo(vpTranslationVector(0.40, 0, -0.15), - vpRotationMatrix(vpRxyzVector(-M_PI, 0, M_PI/2.))); - - std::vector<vpPoint> point(4) ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } + vpHomogeneousMatrix wMo(vpTranslationVector(0.40, 0, -0.15), + vpRotationMatrix(vpRxyzVector(-M_PI, 0, M_PI/2.))); + + std::vector<vpPoint> point(4) ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); + } - vpSimulatorViper850 robot(true); - robot.setVerbose(true); - - // Enlarge the default joint limits - vpColVector qmin = robot.getJointMin(); - vpColVector qmax = robot.getJointMax(); - qmin[0] = -vpMath::rad(180); - qmax[0] = vpMath::rad(180); - qmax[1] = vpMath::rad(0); - qmax[2] = vpMath::rad(270); - qmin[4] = -vpMath::rad(180); - qmax[4] = vpMath::rad(180); - - robot.setJointLimit(qmin, qmax); - - std::cout << "Robot joint limits: " << std::endl; - for (unsigned int i=0; i< qmin.size(); i ++) - std::cout << "Joint " << i << ": min " << vpMath::deg(qmin[i]) << " max " << vpMath::deg(qmax[i]) << " (deg)" << std::endl; - - robot.init(vpViper850::TOOL_PTGREY_FLEA2_CAMERA, vpCameraParameters::perspectiveProjWithoutDistortion); - robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); - robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); - robot.set_fMo(wMo); - bool ret = true; + vpSimulatorViper850 robot(true); + robot.setVerbose(true); + + // Enlarge the default joint limits + vpColVector qmin = robot.getJointMin(); + vpColVector qmax = robot.getJointMax(); + qmin[0] = -vpMath::rad(180); + qmax[0] = vpMath::rad(180); + qmax[1] = vpMath::rad(0); + qmax[2] = vpMath::rad(270); + qmin[4] = -vpMath::rad(180); + qmax[4] = vpMath::rad(180); + + robot.setJointLimit(qmin, qmax); + + std::cout << "Robot joint limits: " << std::endl; + for (unsigned int i=0; i< qmin.size(); i ++) + std::cout << "Joint " << i << ": min " << vpMath::deg(qmin[i]) << " max " << vpMath::deg(qmax[i]) << " (deg)" << std::endl; + + robot.init(vpViper850::TOOL_PTGREY_FLEA2_CAMERA, vpCameraParameters::perspectiveProjWithoutDistortion); + robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL); + robot.initScene(vpWireFrameSimulator::PLATE, vpWireFrameSimulator::D_STANDARD); + robot.set_fMo(wMo); + bool ret = true; #if VISP_VERSION_INT > VP_VERSION_INT(2,7,0) - ret = -#endif - robot.initialiseCameraRelativeToObject(cMo); - if (ret == false) - return 0; // Not able to set the position - robot.setDesiredCameraPosition(cdMo); - // We modify the default external camera position - robot.setExternalCameraPosition(vpHomogeneousMatrix(vpTranslationVector(-0.4, 0.4, 2), - vpRotationMatrix(vpRxyzVector(M_PI/2,0,0)))); - - vpImage<unsigned char> Iint(480, 640, 255); + ret = + #endif + robot.initialiseCameraRelativeToObject(cMo); + if (ret == false) + return 0; // Not able to set the position + robot.setDesiredCameraPosition(cdMo); + // We modify the default external camera position + robot.setExternalCameraPosition(vpHomogeneousMatrix(vpTranslationVector(-0.4, 0.4, 2), + vpRotationMatrix(vpRxyzVector(M_PI/2,0,0)))); + + vpImage<unsigned char> Iint(480, 640, 255); #if defined(VISP_HAVE_X11) - vpDisplayX displayInt(Iint, 700, 0, "Internal view"); + vpDisplayX displayInt(Iint, 700, 0, "Internal view"); #elif defined(VISP_HAVE_GDI) - vpDisplayGDI displayInt(Iint, 700, 0, "Internal view"); + vpDisplayGDI displayInt(Iint, 700, 0, "Internal view"); +#elif defined(VISP_HAVE_OPENCV) + vpDisplayOpenCV displayInt(Iint, 700, 0, "Internal view"); #else - std::cout << "No image viewer is available..." << std::endl; + std::cout << "No image viewer is available..." << std::endl; #endif - vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); - // Modify the camera parameters to match those used in the other simulations - robot.setCameraParameters(cam); - - bool start = true; - //for ( ; ; ) - for (int iter =0; iter < 275; iter ++) - { - cMo = robot.get_cMo(); - - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - } + vpCameraParameters cam(840, 840, Iint.getWidth()/2, Iint.getHeight()/2); + // Modify the camera parameters to match those used in the other simulations + robot.setCameraParameters(cam); + + bool start = true; + //for ( ; ; ) + for (int iter =0; iter < 275; iter ++) + { + cMo = robot.get_cMo(); + + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + + vpDisplay::display(Iint); + robot.getInternalView(Iint); + if (!start) { + display_trajectory(Iint, point, cMo, cam); + vpDisplay::displayText(Iint, 40, 120, "Click to stop the servo...", vpColor::red); + } + vpDisplay::flush(Iint); - vpDisplay::display(Iint); - robot.getInternalView(Iint); - if (!start) { - display_trajectory(Iint, point, cMo, cam); - vpDisplay::displayCharString(Iint, 40, 120, "Click to stop the servo...", vpColor::red); - } - vpDisplay::flush(Iint); + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); + // A click to exit + if (vpDisplay::getClick(Iint, false)) + break; - // A click to exit - if (vpDisplay::getClick(Iint, false)) - break; + if (start) { + start = false; + v = 0; + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + vpDisplay::displayText(Iint, 40, 120, "Click to start the servo...", vpColor::blue); + vpDisplay::flush(Iint); + //vpDisplay::getClick(Iint); + } - if (start) { - start = false; - v = 0; - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - vpDisplay::displayCharString(Iint, 40, 120, "Click to start the servo...", vpColor::blue); - vpDisplay::flush(Iint); - //vpDisplay::getClick(Iint); + vpTime::wait(1000*robot.getSamplingTime()); } - - vpTime::wait(1000*robot.getSamplingTime()); + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; } - task.kill(); #endif } diff --git a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts.cpp b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts.cpp index 0ecad7c9739c3be692dd9c3251483a136f9c4139..1d676a727b8daced0d6740b3a4a1a129eb1d3a5f 100644 --- a/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts.cpp +++ b/tutorial/visual-servo/ibvs/tutorial-ibvs-4pts.cpp @@ -5,47 +5,52 @@ int main() { - vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); - vpHomogeneousMatrix cMo(0.15, -0.1, 1., - vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); - - vpPoint point[4] ; - point[0].setWorldCoordinates(-0.1,-0.1, 0); - point[1].setWorldCoordinates( 0.1,-0.1, 0); - point[2].setWorldCoordinates( 0.1, 0.1, 0); - point[3].setWorldCoordinates(-0.1, 0.1, 0); - - vpServo task ; - task.setServo(vpServo::EYEINHAND_CAMERA); - task.setInteractionMatrixType(vpServo::CURRENT); - task.setLambda(0.5); - - vpFeaturePoint p[4], pd[4] ; - for (int i = 0 ; i < 4 ; i++) { - point[i].track(cdMo); - vpFeatureBuilder::create(pd[i], point[i]); - point[i].track(cMo); - vpFeatureBuilder::create(p[i], point[i]); - task.addFeature(p[i], pd[i]); - } - - vpHomogeneousMatrix wMc, wMo; - vpSimulatorCamera robot; - robot.setSamplingTime(0.040); - robot.getPosition(wMc); - wMo = wMc * cMo; - - for (unsigned int iter=0; iter < 150; iter ++) { - robot.getPosition(wMc); - cMo = wMc.inverse() * wMo; - for (int i = 0 ; i < 4 ; i++) { + try { + vpHomogeneousMatrix cdMo(0, 0, 0.75, 0, 0, 0); + vpHomogeneousMatrix cMo(0.15, -0.1, 1., + vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)); + + vpPoint point[4] ; + point[0].setWorldCoordinates(-0.1,-0.1, 0); + point[1].setWorldCoordinates( 0.1,-0.1, 0); + point[2].setWorldCoordinates( 0.1, 0.1, 0); + point[3].setWorldCoordinates(-0.1, 0.1, 0); + + vpServo task ; + task.setServo(vpServo::EYEINHAND_CAMERA); + task.setInteractionMatrixType(vpServo::CURRENT); + task.setLambda(0.5); + + vpFeaturePoint p[4], pd[4] ; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cdMo); + vpFeatureBuilder::create(pd[i], point[i]); point[i].track(cMo); vpFeatureBuilder::create(p[i], point[i]); + task.addFeature(p[i], pd[i]); } - vpColVector v = task.computeControlLaw(); - robot.setVelocity(vpRobot::CAMERA_FRAME, v); - } - task.kill(); + vpHomogeneousMatrix wMc, wMo; + vpSimulatorCamera robot; + robot.setSamplingTime(0.040); + robot.getPosition(wMc); + wMo = wMc * cMo; + + for (unsigned int iter=0; iter < 150; iter ++) { + robot.getPosition(wMc); + cMo = wMc.inverse() * wMo; + for (unsigned int i = 0 ; i < 4 ; i++) { + point[i].track(cMo); + vpFeatureBuilder::create(p[i], point[i]); + } + vpColVector v = task.computeControlLaw(); + robot.setVelocity(vpRobot::CAMERA_FRAME, v); + } + + task.kill(); + } + catch(vpException e) { + std::cout << "Catch an exception: " << e << std::endl; + } }